diff --git a/.gitignore b/.gitignore
index 96214f7a18cd5cd618c43f91b02e940897be0c42..cbb38e68280b1b395ec831e9bd3c0d1c158a258b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,3 +13,4 @@ GRTAGS
 GTAGS
 /.clangd/
 /.cache/
+/doc/lisp/elpa/
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6839589707785a95c48e15b2de4750cb33ee084b..0e95e1f1f7da7af3d8a859cf6ad603972cb04188 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -592,10 +592,15 @@ target_link_libraries(
   stdc++fs
   )
 
-# ---------------------- Doxygen ----------------------
+# -------------------- Documentation --------------------
+
+include(PugsDoc)
+
+# -------------------- Doxygen --------------------------
+
 include(PugsDoxygen)
 
-# ------------------- Installation --------------------
+# -------------------- Installation ---------------------
 install(TARGETS
   pugs
   PugsMesh
@@ -669,13 +674,19 @@ endif()
 if(CLAZY_STANDALONE)
   message(" clazy-standalone: ${CLAZY_STANDALONE}")
 else()
-  message(" clazy-standalone: no found!")
+  message(" clazy-standalone: not found!")
 endif()
 
 if (DOXYGEN_FOUND)
   message(" doxygen: ${DOXYGEN_EXECUTABLE}")
 else()
-  message(" doxygen: no found!")
+  message(" doxygen: not found!")
+endif()
+
+if (EMACS)
+  message(" emacs: ${EMACS}")
+else()
+  message(" emacs: not found!")
 endif()
 
 message("================================")
diff --git a/cmake/PugsDoc.cmake b/cmake/PugsDoc.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..7a5694f096503b0c977c6961de863fe7e39bb551
--- /dev/null
+++ b/cmake/PugsDoc.cmake
@@ -0,0 +1,70 @@
+# -------------------- Documentation ----------------
+
+# check for Emacs since the documentation is writen in org-mode
+find_program(EMACS emacs)
+
+# check for LaTeX
+find_package(LATEX COMPONENTS PDFLATEX)
+
+add_custom_target(userdoc)
+add_custom_target(doc DEPENDS userdoc)
+
+if (EMACS)
+
+  add_custom_command(
+    OUTPUT "${PUGS_BINARY_DIR}/doc"
+    COMMAND ${CMAKE_COMMAND} -E make_directory "${PUGS_BINARY_DIR}/doc"
+  )
+
+  add_custom_target(pugsdoc-dir DEPENDS "${PUGS_BINARY_DIR}/doc")
+
+  set(ORG_GENERATOR_FILES
+    "${PUGS_SOURCE_DIR}/doc/lisp/build-doc-config.el"
+    "${PUGS_SOURCE_DIR}/doc/lisp/userdoc-html.el"
+    "${PUGS_SOURCE_DIR}/doc/lisp/userdoc-pdf.el"
+    "${PUGS_SOURCE_DIR}/doc/lisp/share/pugs.el"
+    "${PUGS_SOURCE_DIR}/doc/lisp/share/ob-pugs.el"
+  )
+
+  add_custom_command(
+    OUTPUT "${PUGS_BINARY_DIR}/doc/userdoc.html"
+    COMMAND
+    ${CMAKE_COMMAND} -E env
+    PUGS=${PUGS_BINARY_DIR}/pugs
+    HOME=${PUGS_SOURCE_DIR}/doc/lisp
+    PUGS_SOURCE_DIR=${PUGS_SOURCE_DIR}
+    PUGS_BINARY_DIR=${PUGS_BINARY_DIR}
+    ${EMACS} -Q --script ${PUGS_SOURCE_DIR}/doc/lisp/userdoc-html.el
+    DEPENDS "${PUGS_SOURCE_DIR}/doc/userdoc.org" pugs pugsdoc-dir ${ORG_GENERATOR_FILES}
+    WORKING_DIRECTORY ${PUGS_BINARY_DIR}/doc
+    COMMENT "Built user documentation in doc/userdoc.html"
+    VERBATIM)
+
+  add_custom_target(userdoc-html DEPENDS pugsdoc-dir "${PUGS_BINARY_DIR}/doc/userdoc.html")
+
+  add_dependencies(userdoc userdoc-html)
+
+  if (LATEX_FOUND)
+
+    add_custom_command(
+      OUTPUT "${PUGS_BINARY_DIR}/doc/userdoc.pdf"
+      COMMAND
+      ${CMAKE_COMMAND} -E env
+      PUGS=${PUGS_BINARY_DIR}/pugs
+      HOME=${PUGS_SOURCE_DIR}/doc/lisp
+      PUGS_SOURCE_DIR=${PUGS_SOURCE_DIR}
+      PUGS_BINARY_DIR=${PUGS_BINARY_DIR}
+      ${EMACS} -Q --script ${PUGS_SOURCE_DIR}/doc/lisp/userdoc-pdf.el
+      DEPENDS "${PUGS_SOURCE_DIR}/doc/userdoc.org" pugs pugsdoc-dir  ${ORG_GENERATOR_FILES}
+      WORKING_DIRECTORY ${PUGS_BINARY_DIR}/doc
+      COMMENT "Built user documentation in doc/userdoc.pdf"
+      VERBATIM)
+
+    add_custom_target(userdoc-pdf DEPENDS pugsdoc-dir "${PUGS_BINARY_DIR}/doc/userdoc.pdf" )
+
+    add_dependencies(userdoc userdoc-pdf)
+
+  endif()
+
+  add_dependencies(doc userdoc)
+endif()
diff --git a/cmake/PugsDoxygen.cmake b/cmake/PugsDoxygen.cmake
index b0bacf92f6db23784bbbcfc46e73c9054f025c5d..85ba88743b397c0fda9b3127cfdcc9a32e6ede63 100644
--- a/cmake/PugsDoxygen.cmake
+++ b/cmake/PugsDoxygen.cmake
@@ -4,26 +4,23 @@
 # define the option
 option(BUILD_DOXYGEN_DOC "Build Doxygen documentation" ON)
 
-# this is very much inspired by https://vicrucann.github.io/tutorials/quick-cmake-doxygen/
+# this is very much inspired from https://vicrucann.github.io/tutorials/quick-cmake-doxygen/
 
 # check if Doxygen is installed
 find_package(Doxygen)
 if(DOXYGEN_FOUND)
-    # set input and output files
-    set(DOXYGEN_IN "${PUGS_SOURCE_DIR}/doc/Doxyfile.in")
-    set(DOXYGEN_OUT "${PUGS_BINARY_DIR}/Doxyfile")
+  # set input and output files
+  set(DOXYGEN_IN "${PUGS_SOURCE_DIR}/doc/Doxyfile.in")
+  set(DOXYGEN_OUT "${PUGS_BINARY_DIR}/Doxyfile")
 
-    # request to configure the file
-    configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)
-    message(STATUS "Configuring Doxygen")
+  # request to configure the file
+  configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)
+  message(STATUS "Configuring Doxygen")
 
-    # note the option ALL which allows to build the docs together with the application
-    add_custom_target(doxygen
-        COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
-        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
-        COMMENT "Generating API documentation with Doxygen"
-        VERBATIM )
+  add_custom_target(doxygen
+    COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
+    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+    COMMENT "Generating API documentation with Doxygen"
+    VERBATIM)
 
-    # building doc builds doxygen
-    add_custom_target(doc doxygen)
 endif()
diff --git a/doc/lisp/.emacs.d b/doc/lisp/.emacs.d
new file mode 120000
index 0000000000000000000000000000000000000000..945c9b46d684f08ec84cb316e1dc0061e361f794
--- /dev/null
+++ b/doc/lisp/.emacs.d
@@ -0,0 +1 @@
+.
\ No newline at end of file
diff --git a/doc/lisp/build-doc-config.el b/doc/lisp/build-doc-config.el
new file mode 100644
index 0000000000000000000000000000000000000000..e302caf7b0afd5f4247d3971e890479dfb893e5b
--- /dev/null
+++ b/doc/lisp/build-doc-config.el
@@ -0,0 +1,195 @@
+;;; build-doc-config --- Pugs' doc generation helper
+;;; Commentary:
+;;; configuration piochee sur le net
+;;; Code:
+
+(setq max-lisp-eval-depth 10000)
+(setq max-specpdl-size 10000)
+
+(require 'package)
+(setq package-enable-at-startup nil)
+
+(add-to-list 'package-archives
+	     '("melpa" . "http://melpa.org/packages/"))
+(add-to-list 'package-archives
+	     '("gnu" . "http://elpa.gnu.org/packages/"))
+(add-to-list 'package-archives
+	     '("org" . "https://orgmode.org/elpa/"))
+
+;; Bootstrap `use-package'
+(unless (package-installed-p 'use-package)
+  (package-refresh-contents)
+  (package-install 'use-package))
+
+(package-initialize)
+
+(use-package auto-async-byte-compile
+  :ensure t
+  :hook
+  (emacs-lisp-mode-hook  . 'enable-auto-async-byte-compile-mode))
+
+(setq auto-save-default nil)
+
+(use-package org
+  :ensure t
+  :pin org)
+
+(use-package htmlize
+  :ensure t)
+
+(custom-set-variables
+ '(org-export-html-postamble nil)
+ ;; By now pugs is not known by Pygments raw text is better
+ '(org-latex-minted-langs '((pugs "Text")))
+ '(org-confirm-babel-evaluate nil)
+ '(org-html-validation-link nil)
+ '(org-src-fontify-natively t)
+ '(org-html-htmlize-output-type 'css)
+ '(org-latex-listings 'minted))
+
+(setq org-latex-minted-options
+      '(("linenos=true") ("breaklines")))
+
+(defun org-export-output-file-name-modified (orig-fun extension &optional subtreep pub-dir)
+  (unless pub-dir
+    (setq pub-dir (substitute-in-file-name "${PUGS_BINARY_DIR}/doc"))
+    (unless (file-directory-p pub-dir)
+      (make-directory pub-dir)))
+  (apply orig-fun extension subtreep pub-dir nil))
+(advice-add 'org-export-output-file-name :around #'org-export-output-file-name-modified)
+
+(setq org-latex-listings 'minted
+      org-latex-packages-alist '(("" "minted"))
+      org-latex-pdf-process
+      '("cd ${PUGS_BINARY_DIR}/doc; pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
+        "cd ${PUGS_BINARY_DIR}/doc; pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
+        "cd ${PUGS_BINARY_DIR}/doc; pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
+
+(setq python-indent-guess-indent-offset-verbose nil)
+
+(setq org-babel-python-command "python3")
+(add-hook 'org-babel-after-execute-hook
+          'org-redisplay-inline-images)
+
+(load-library "${HOME}/share/pugs.el")
+(require 'pugs-mode)
+(add-to-list 'auto-mode-alist '("\\.pgs?\\'" . pugs-mode))
+(load-library "${HOME}/share/ob-pugs.el")
+(require 'ob-pugs)
+
+(org-babel-do-load-languages
+ 'org-babel-load-languages
+ '((python . t)
+   (emacs-lisp . t)
+   (shell . t)
+   (pugs . t)
+   (C . t)
+   (gnuplot . t)
+   (js . t)
+   (ditaa . t)
+   (dot . t)
+   (org . t)
+   (latex . t )))
+
+(eval-when-compile
+  (require 'easy-mmode)
+  (require 'ox))
+
+(use-package ox
+  :config
+  (define-minor-mode unpackaged/org-export-html-with-useful-ids-mode
+    "Attempt to export Org as HTML with useful link IDs.
+  Instead of random IDs like \"#orga1b2c3\", use heading titles,
+  made unique when necessary."
+    :global t
+    (if unpackaged/org-export-html-with-useful-ids-mode
+        (advice-add #'org-export-get-reference :override #'unpackaged/org-export-get-reference)
+      (advice-remove #'org-export-get-reference #'unpackaged/org-export-get-reference)))
+
+  (defun unpackaged/org-export-get-reference (datum info)
+    "Like `org-export-get-reference', except uses heading titles instead of random numbers."
+    (let ((cache (plist-get info :internal-references)))
+      (or (car (rassq datum cache))
+          (let* ((crossrefs (plist-get info :crossrefs))
+                 (cells (org-export-search-cells datum))
+                 ;; Preserve any pre-existing association between
+                 ;; a search cell and a reference, i.e., when some
+                 ;; previously published document referenced a location
+                 ;; within current file (see
+                 ;; `org-publish-resolve-external-link').
+                 ;;
+                 ;; However, there is no guarantee that search cells are
+                 ;; unique, e.g., there might be duplicate custom ID or
+                 ;; two headings with the same title in the file.
+                 ;;
+                 ;; As a consequence, before re-using any reference to
+                 ;; an element or object, we check that it doesn't refer
+                 ;; to a previous element or object.
+                 (new (or (cl-some
+                           (lambda (cell)
+                             (let ((stored (cdr (assoc cell crossrefs))))
+                               (when stored
+                                 (let ((old (org-export-format-reference stored)))
+                                   (and (not (assoc old cache)) stored)))))
+                           cells)
+                          (when (org-element-property :raw-value datum)
+                            ;; Heading with a title
+                            (unpackaged/org-export-new-title-reference datum cache))
+                          ;; NOTE: This probably breaks some Org Export
+                          ;; feature, but if it does what I need, fine.
+                          (org-export-format-reference
+                           (org-export-new-reference cache))))
+                 (reference-string new))
+            ;; Cache contains both data already associated to
+            ;; a reference and in-use internal references, so as to make
+            ;; unique references.
+            (dolist (cell cells) (push (cons cell new) cache))
+            ;; Retain a direct association between reference string and
+            ;; DATUM since (1) not every object or element can be given
+            ;; a search cell (2) it permits quick lookup.
+            (push (cons reference-string datum) cache)
+            (plist-put info :internal-references cache)
+            reference-string))))
+
+  (defun unpackaged/org-export-new-title-reference (datum cache)
+    "Return new reference for DATUM that is unique in CACHE."
+    (cl-macrolet ((inc-suffixf (place)
+                               `(progn
+                                  (string-match (rx bos
+                                                    (minimal-match (group (1+ anything)))
+                                                    (optional "--" (group (1+ digit)))
+                                                    eos)
+                                                ,place)
+                                  ;; HACK: `s1' instead of a gensym.
+                                  (-let* (((s1 suffix) (list (match-string 1 ,place)
+                                                             (match-string 2 ,place)))
+                                          (suffix (if suffix
+                                                      (string-to-number suffix)
+                                                    0)))
+                                    (setf ,place (format "%s--%s" s1 (cl-incf suffix)))))))
+      (let* ((title (org-element-property :raw-value datum))
+             (ref (url-hexify-string (substring-no-properties title)))
+             (parent (org-element-property :parent datum)))
+        (while (--any (equal ref (car it))
+                      cache)
+          ;; Title not unique: make it so.
+          (if parent
+              ;; Append ancestor title.
+              (setf title (concat (org-element-property :raw-value parent)
+                                  "--" title)
+                    ref (url-hexify-string (substring-no-properties title))
+                    parent (org-element-property :parent parent))
+            ;; No more ancestors: add and increment a number.
+            (inc-suffixf ref)))
+        ref))))
+
+(defun org-export-deterministic-reference (references)
+    (let ((new 0))
+       (while (rassq new references) (setq new (+ new 1)))
+       new))
+ (advice-add #'org-export-new-reference :override #'org-export-deterministic-reference)
+
+;;; Local Variables:
+;;; byte-compile-warnings: (not free-vars)
+;;; End: (provide 'build-doc-config)
+;;; build-doc-config.el ends here
diff --git a/doc/lisp/share/ob-pugs.el b/doc/lisp/share/ob-pugs.el
new file mode 100644
index 0000000000000000000000000000000000000000..6c3d72956d519bea339b63cb883c57f9bf1cd000
--- /dev/null
+++ b/doc/lisp/share/ob-pugs.el
@@ -0,0 +1,195 @@
+;;; ob-pugs.el --- org-babel functions for pugs evaluation
+
+;; Copyright (C) your name here
+
+;; Author: your name here
+;; Keywords: literate programming, reproducible research
+;; Homepage: https://orgmode.org
+;; Version: 0.01
+
+;;; License:
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;; This file is not intended to ever be loaded by org-babel, rather it is a
+;; pugs for use in adding new language support to Org-babel. Good first
+;; steps are to copy this file to a file named by the language you are adding,
+;; and then use `query-replace' to replace all strings of "pugs" in this
+;; file with the name of your new language.
+
+;; After the `query-replace' step, it is recommended to load the file and
+;; register it to org-babel either via the customize menu, or by evaluating the
+;; line: (add-to-list 'org-babel-load-languages '(pugs . t)) where
+;; `pugs' should have been replaced by the name of the language you are
+;; implementing (note that this applies to all occurrences of 'pugs' in this
+;; file).
+
+;; After that continue by creating a simple code block that looks like e.g.
+;;
+;; #+begin_src pugs
+
+;; test
+
+;; #+end_src
+
+;; Finally you can use `edebug' to instrumentalize
+;; `org-babel-expand-body:pugs' and continue to evaluate the code block. You
+;; try to add header keywords and change the body of the code block and
+;; reevaluate the code block to observe how things get handled.
+
+;;
+;; If you have questions as to any of the portions of the file defined
+;; below please look to existing language support for guidance.
+;;
+;; If you are planning on adding a language to org-babel we would ask
+;; that if possible you fill out the FSF copyright assignment form
+;; available at https://orgmode.org/request-assign-future.txt as this
+;; will make it possible to include your language support in the core
+;; of Org-mode, otherwise unassigned language support files can still
+;; be included in the contrib/ directory of the Org-mode repository.
+
+
+;;; Requirements:
+
+;; Use this section to list the requirements of this language.  Most
+;; languages will require that at least the language be installed on
+;; the user's system, and the Emacs major mode relevant to the
+;; language be installed as well.
+
+;;; Code:
+(require 'ob)
+(require 'ob-ref)
+(require 'ob-comint)
+(require 'ob-eval)
+;; possibly require modes required for your language
+
+;; optionally define a file extension for this language
+(add-to-list 'org-babel-tangle-lang-exts '("pugs" . "pgs"))
+
+;; optionally declare default header arguments for this language
+(defvar org-babel-default-header-args:pugs '())
+
+;; This function expands the body of a source code block by doing things like
+;; prepending argument definitions to the body, it should be called by the
+;; `org-babel-execute:pugs' function below. Variables get concatenated in
+;; the `mapconcat' form, therefore to change the formatting you can edit the
+;; `format' form.
+(defun org-babel-expand-body:pugs (body params &optional processed-params)
+  "Expand BODY according to PARAMS, return the expanded body."
+  (require 'inf-pugs nil t)
+  (let ((vars (org-babel--get-vars (or processed-params (org-babel-process-params params)))))
+    (concat
+     (mapconcat ;; define any variables
+      (lambda (pair)
+        (format "%s=%S"
+                (car pair) (org-babel-pugs-var-to-pugs (cdr pair))))
+      vars "\n")
+     "\n" body "\n")))
+
+;; This is the main function which is called to evaluate a code
+;; block.
+;;
+;; This function will evaluate the body of the source code and
+;; return the results as emacs-lisp depending on the value of the
+;; :results header argument
+;; - output means that the output to STDOUT will be captured and
+;;   returned
+;; - value means that the value of the last statement in the
+;;   source code block will be returned
+;;
+;; The most common first step in this function is the expansion of the
+;; PARAMS argument using `org-babel-process-params'.
+;;
+;; Please feel free to not implement options which aren't appropriate
+;; for your language (e.g. not all languages support interactive
+;; "session" evaluation).  Also you are free to define any new header
+;; arguments which you feel may be useful -- all header arguments
+;; specified by the user will be available in the PARAMS variable.
+
+;; SDP: This is a very simple version.
+;; SDP: I kept the original example just after if needed in future.
+(defun org-babel-execute:pugs (body params)
+  "Execute a block of pugs code with org-babel."
+  (let ((in-file (org-babel-temp-file "n" ".pgs"))
+        (verbosity (or (cdr (assq :verbosity params)) 0)))
+    (with-temp-file in-file
+      (insert body))
+    (org-babel-eval
+		(format "${PUGS} --no-preamble --no-color %s"
+			(org-babel-process-file-name in-file))
+		"")))
+
+;; SDP: original `org-babel-execute:pugs' example.
+;; (defun org-babel-execute:pugs (body params)
+;;   "Execute a block of Pugs code with org-babel.
+;; This function is called by `org-babel-execute-src-block'"
+;;   (message "executing Pugs source code block")
+;;   (let* ((processed-params (org-babel-process-params params))
+;;          ;; set the session if the value of the session keyword is not the
+;;          ;; string `none'
+;;          (session (unless (string= value "none")
+;;                    (org-babel-pugs-initiate-session
+;;                     (cdr (assq :session processed-params)))))
+;;          ;; variables assigned for use in the block
+;;          (vars (org-babel--get-vars processed-params))
+;;          (result-params (assq :result-params processed-params))
+;;          ;; either OUTPUT or VALUE which should behave as described above
+;;          (result-type (assq :result-type processed-params))
+;;          ;; expand the body with `org-babel-expand-body:pugs'
+;;          (full-body (org-babel-expand-body:pugs
+;;                      body params processed-params)))
+;;     ;; actually execute the source-code block either in a session or
+;;     ;; possibly by dropping it to a temporary file and evaluating the
+;;     ;; file.
+;;     ;;
+;;     ;; for session based evaluation the functions defined in
+;;     ;; `org-babel-comint' will probably be helpful.
+;;     ;;
+;;     ;; for external evaluation the functions defined in
+;;     ;; `org-babel-eval' will probably be helpful.
+;;     ;;
+;;     ;; when forming a shell command, or a fragment of code in some
+;;     ;; other language, please preprocess any file names involved with
+;;     ;; the function `org-babel-process-file-name'. (See the way that
+;;     ;; function is used in the language files)
+;;     ))
+
+;; This function should be used to assign any variables in params in
+;; the context of the session environment.
+(defun org-babel-prep-session:pugs (session params)
+  "Prepare SESSION according to the header arguments specified in PARAMS."
+  )
+
+(defun org-babel-pugs-var-to-pugs (var)
+  "Convert an elisp var into a string of pugs source code
+specifying a var of the same value."
+  (format "%S" var))
+
+(defun org-babel-pugs-table-or-string (results)
+  "If the results look like a table, then convert them into an
+Emacs-lisp table, otherwise return the results as a string."
+  )
+
+(defun org-babel-pugs-initiate-session (&optional session)
+  "If there is not a current inferior-process-buffer in SESSION then create.
+Return the initialized session."
+  (unless (string= session "none")
+    ))
+
+(provide 'ob-pugs)
+;;; ob-pugs.el ends here
diff --git a/doc/lisp/share/pugs.el b/doc/lisp/share/pugs.el
new file mode 100644
index 0000000000000000000000000000000000000000..36830ffa5036575b65b08690cb22d986b34473a9
--- /dev/null
+++ b/doc/lisp/share/pugs.el
@@ -0,0 +1,75 @@
+;;; pugs -- simple major mode for pgs files
+;;; Commentary:
+;;; simple major mode for pugs
+;;; Code:
+
+(define-derived-mode pugs-output-mode compilation-mode "pugs-output"
+  "Define a special mode for pugs process output."
+)
+
+(defun pugs-run-file ()
+  "Execute the current pugs script.
+Ask to save the buffer if needed"
+  (interactive)
+  (when (buffer-modified-p)
+    (save-buffer))
+  (let ((output-buffer "*pugs-output*")
+	(pugs-binary "~/src/pugs/build/pugs")
+	(pugs-file (buffer-file-name (current-buffer)))
+	(pugs-buffer (current-buffer)))
+    (when (get-buffer output-buffer)
+      (kill-buffer output-buffer))
+    (get-buffer-create output-buffer)
+    (follow-mode)
+    (display-buffer output-buffer)
+    (set-buffer output-buffer)
+    (pugs-output-mode)
+    (setq-local inhibit-read-only t)
+    (insert (format "started: %s\n" (current-time-string)))
+    (insert (format "binary : %s\n" pugs-binary))
+    (insert (format "script : %s\n\n" pugs-file))
+    ;;; run the process
+    (process-file pugs-binary nil output-buffer nil pugs-file "--no-color")
+;    (start-process pugs-binary nil output-buffer t pugs-file "--no-color")
+    ;;;
+    (insert (format "\nfinished: %s\n" (current-time-string)))
+    (insert (format "binary : %s\n" pugs-binary))
+    (insert (format "script : %s\n" pugs-file))
+    ))
+
+(define-derived-mode pugs-mode prog-mode "pugs"
+  "pugs mode is a major mode for editing pugs files"
+  ;; define pugs keywords
+  (defvar pugs-keywords
+    '("import" "let" "Z" "N" "B" "R" "string" "and" "or" "xor" "not" "true" "false" "let" "do" "while" "for" "if" "else" "break" "continue" "cout" "cerr" "clog" "+"))
+
+  (defvar pugs-special-symbols
+    '(":" "," ";" "{" "}" "->" "<=" ">=" "=" "+" "-" "*" "/"  "<" ">" "^"))
+
+  (defvar pugs-font-lock-defaults
+    `((
+       ;; strings
+       ("\"\\.\\*\\?" . font-lock-string-face)
+       ;; keywords
+       ( ,(regexp-opt pugs-keywords 'words) . font-lock-keyword-face)
+       ;; special symbols
+       ( ,(regexp-opt pugs-special-symbols) . font-lock-constant-face)
+       )))
+
+  (setq font-lock-defaults pugs-font-lock-defaults)
+
+  ;; for comments
+  (setq comment-start "//")
+  (setq comment-end "")
+
+  (modify-syntax-entry ?\\ "\\" pugs-mode-syntax-table)
+  (modify-syntax-entry ?/ ". 124b" pugs-mode-syntax-table)
+  (modify-syntax-entry ?* ". 23" pugs-mode-syntax-table)
+  (modify-syntax-entry ?\n "> b" pugs-mode-syntax-table)
+
+  (local-set-key (kbd "C-c C-c") 'pugs-run-file)
+  )
+(provide 'pugs-mode)
+
+;;; (provide 'pugs-mode)
+;;; pugs.el ends here
diff --git a/doc/lisp/userdoc-html.el b/doc/lisp/userdoc-html.el
new file mode 100644
index 0000000000000000000000000000000000000000..501d34cdb9e00d3b1c59e1a8cc7e44126da41708
--- /dev/null
+++ b/doc/lisp/userdoc-html.el
@@ -0,0 +1,15 @@
+;;; userdoc-html --- builds userdoc (html)
+;;; Commentary:
+;;;
+;;; Code:
+
+(load-library "${HOME}/build-doc-config.el")
+
+(with-current-buffer
+    (find-file-noselect (substitute-in-file-name "${PUGS_SOURCE_DIR}/doc/userdoc.org"))
+  (org-html-export-to-html))
+
+;;; Local Variables:
+;;; byte-compile-warnings: (not free-vars)
+;;; End: (provide 'userdoc-html)
+;;; userdoc-html.el ends here
diff --git a/doc/lisp/userdoc-pdf.el b/doc/lisp/userdoc-pdf.el
new file mode 100644
index 0000000000000000000000000000000000000000..c28342e75f6a8e2e2cf4a3c7df9d79e670892a80
--- /dev/null
+++ b/doc/lisp/userdoc-pdf.el
@@ -0,0 +1,15 @@
+;;; userdoc-pdf --- builds userdoc (pdf)
+;;; Commentary:
+;;;
+;;; Code:
+
+(load-library "${HOME}/build-doc-config.el")
+
+(with-current-buffer
+    (find-file-noselect (substitute-in-file-name "${PUGS_SOURCE_DIR}/doc/userdoc.org"))
+  (org-latex-export-to-pdf))
+
+;;; Local Variables:
+;;; byte-compile-warnings: (not free-vars)
+;;; End: (provide 'userdoc-pdf)
+;;; userdoc-pdf.el ends here
diff --git a/doc/userdoc.org b/doc/userdoc.org
new file mode 100644
index 0000000000000000000000000000000000000000..15175e2f8180c9c87a35ecf115a2c4153c47b03d
--- /dev/null
+++ b/doc/userdoc.org
@@ -0,0 +1,57 @@
+#+SETUPFILE: ../packages/org-themes/src/bigblow_inline/bigblow_inline.theme
+
+#+STARTUP: org-pretty-entities entitiespretty
+#+PROPERTY: header-args :comments no
+#+OPTIONS: timestamp:nil
+#+OPTIONS: h:3 num:t toc:3
+#+TITLE: Pugs User Manual
+#+OPTIONS: author:nil date:nil
+#+OPTIONS: tex:t
+
+#+LANGUAGE: en
+
+#+HTML_HEAD_EXTRA: <style> pre.src-pugs:before { content: 'Pugs'; } </style>
+
+#+LATEX_CLASS_OPTIONS: [10pt]
+#+LATEX_HEADER: \usepackage[hmargin=2.5cm,vmargin=1.5cm]{geometry}
+#+LATEX_COMPILER: pdflatex --shell-escape
+
+#+LATEX_HEADER_EXTRA: \usepackage{amsmath}
+#+LATEX_HEADER_EXTRA: \usepackage{amsthm}
+#+LATEX_HEADER_EXTRA: \usepackage{amssymb}
+#+LATEX_HEADER_EXTRA: \usepackage{xcolor}
+#+LATEX_HEADER_EXTRA: \usepackage{mdframed}
+#+LATEX_HEADER_EXTRA: \BeforeBeginEnvironment{minted}{\begin{mdframed}[linecolor=blue,backgroundcolor=blue!5]}
+#+LATEX_HEADER_EXTRA: \AfterEndEnvironment{minted}{\end{mdframed}}
+#+LATEX_HEADER_EXTRA: \BeforeBeginEnvironment{verbatim}{\begin{mdframed}[linecolor=gray,backgroundcolor=green!5]}
+#+LATEX_HEADER_EXTRA: \AfterEndEnvironment{verbatim}{\end{mdframed}}
+
+* Introduction
+
+Reading this document one should get how to use ~pugs~.
+
+* Basics
+
+** Hello world!
+
+*** Python
+#+BEGIN_SRC python :exports both :results output
+  print ("hellow orld!")
+#+END_SRC
+
+*** Pugs
+In this simple example, one defines the function $f:\mathbb{R} \to \mathbb{R}, x \mapsto 2\sin(x)$
+#+name: hello-world
+#+BEGIN_SRC pugs :exports both :results output
+  import math;
+  let f: R -> R, x -> 2*sin(x);
+  cout << "Hello world!\n";
+  cout << "f(12) = " <<  f(12) << "\n";
+#+END_SRC
+Then one prints ~Hello world!~ and the evaluation of $f$ at position $12$.
+#+results: hello-world
+* The end
+
+#+BEGIN_SRC python :exports both :results output
+  print ("hello world!")
+#+END_SRC