diff --git a/cmake/PugsDoc.cmake b/cmake/PugsDoc.cmake index 0cd8a4c5829189d7d1b97173eb57e48035275f93..c4af5758153ab8c75e0ad4bb82a51af8d89090eb 100644 --- a/cmake/PugsDoc.cmake +++ b/cmake/PugsDoc.cmake @@ -22,6 +22,7 @@ if (EMACS) "${PUGS_SOURCE_DIR}/doc/lisp/build-doc-config.el" "${PUGS_SOURCE_DIR}/doc/lisp/share/pugs.el" "${PUGS_SOURCE_DIR}/doc/lisp/share/ob-pugs.el" + "${PUGS_SOURCE_DIR}/doc/lisp/share/ob-pugs-error.el" ) add_custom_command( diff --git a/doc/lisp/build-doc-config.el b/doc/lisp/build-doc-config.el index fdde4a3e33a3b1a863bbc1b13dde4cfbf3648afc..4cf83f69251b96aaa0f9473a4c6b0ba8fa8a1e51 100644 --- a/doc/lisp/build-doc-config.el +++ b/doc/lisp/build-doc-config.el @@ -44,7 +44,7 @@ (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-latex-minted-langs '((pugs "Text") (pugs-error "Text"))) '(org-confirm-babel-evaluate nil) '(org-html-validation-link nil) '(org-src-fontify-natively t) @@ -77,9 +77,16 @@ (load-library "${HOME}/share/pugs.el") (require 'pugs-mode) +;; allow to use pugs mode when dealing with invalid examples +(define-derived-mode pugs-error-mode pugs-mode "pugs-error" + "pugs-error mode " + ) + (add-to-list 'auto-mode-alist '("\\.pgs?\\'" . pugs-mode)) (load-library "${HOME}/share/ob-pugs.el") (require 'ob-pugs) +(load-library "${HOME}/share/ob-pugs-error.el") +(require 'ob-pugs-error) (org-babel-do-load-languages 'org-babel-load-languages @@ -87,6 +94,7 @@ (emacs-lisp . t) (shell . t) (pugs . t) + (pugs-error . t) (C . t) (gnuplot . t) (js . t) diff --git a/doc/lisp/share/ob-pugs-error.el b/doc/lisp/share/ob-pugs-error.el new file mode 100644 index 0000000000000000000000000000000000000000..1e4680cbcc71729be805622081b37dfad6176d9c --- /dev/null +++ b/doc/lisp/share/ob-pugs-error.el @@ -0,0 +1,196 @@ +;;; ob-pugs-error.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-error + +;; test + +;; #+end_src + +;; Finally you can use `edebug' to instrumentalize +;; `org-babel-expand-body:pugs-error' 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-error" . "pgs")) + +;; optionally declare default header arguments for this language +(defvar org-babel-default-header-args:pugs-error '()) + +;; 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-error' 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-error (body params &optional processed-params) + "Expand BODY according to PARAMS, return the expanded body." + (require 'inf-pugs-error 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-error-var-to-pugs-error (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-error (body params) + "Execute a block of pugs-error code with org-babel." + (setq org-babel-eval-verbose t) + (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 2>&1 | sed 's@/.*\.pgs:@test.pgs:@'" + (org-babel-process-file-name in-file)) + ""))) + +;; SDP: original `org-babel-execute:pugs-error' example. +;; (defun org-babel-execute:pugs-error (body params) +;; "Execute a block of Pugs-error code with org-babel. +;; This function is called by `org-babel-execute-src-block'" +;; (message "executing Pugs-error 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-error-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-error' +;; (full-body (org-babel-expand-body:pugs-error +;; 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-error (session params) + "Prepare SESSION according to the header arguments specified in PARAMS." + ) + +(defun org-babel-pugs-error-var-to-pugs-error (var) + "Convert an elisp var into a string of pugs-error source code +specifying a var of the same value." + (format "%S" var)) + +(defun org-babel-pugs-error-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-error-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-error) +;;; ob-pugs-error.el ends here diff --git a/doc/lisp/share/pugs-error.el b/doc/lisp/share/pugs-error.el new file mode 100644 index 0000000000000000000000000000000000000000..01043c61d5fda36c09a40a0f28905b073854c1c5 --- /dev/null +++ b/doc/lisp/share/pugs-error.el @@ -0,0 +1,14 @@ +;;; pugs-error -- simple major mode for pgs files used to document errors +;;; Commentary: +;;; inherits pugs mode +;;; Code: + +(define-derived-mode pugs-error-mode pugs-mode "pugs-error" + "pugs mode is a major mode for editing pugs files" + ;; define pugs keywords + ) + +(provide 'pugs-error-mode) + +;;; (provide 'pugs-error-mode) +;;; pugs-error.el ends here diff --git a/doc/userdoc.org b/doc/userdoc.org index 3ef4c8f52b997d5b5d4dfe957c3b6f34f3a6c315..e53ebdf13b1b8d20812539db787f7f1bb0dad8ad 100644 --- a/doc/userdoc.org +++ b/doc/userdoc.org @@ -11,7 +11,8 @@ #+LANGUAGE: en #+ATTR_LATEX: :width 4cm #+HTML_HEAD_EXTRA: <style> pre.src-pugs:before { content: 'pugs'; } </style> - +#+HTML_HEAD_EXTRA: <style> pre.src-pugs-error:before { content: 'invalid pugs'; } </style> +#+HTML_HEAD_EXTRA: <style> .remark{ @extend .todo !optional;} </style> #+LATEX_CLASS_OPTIONS: [10pt] #+LATEX_HEADER: \usepackage[hmargin=2.5cm,vmargin=1.5cm]{geometry} #+LATEX_COMPILER: pdflatex --shell-escape @@ -25,6 +26,12 @@ #+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}} +#+LATEX_HEADER_EXTRA: \newtheorem{note}{Note} +#+LATEX_HEADER_EXTRA: \BeforeBeginEnvironment{note}{\begin{mdframed}[linecolor=orange,backgroundcolor=orange!5]} +#+LATEX_HEADER_EXTRA: \AfterEndEnvironment{note}{\end{mdframed}} +#+LATEX_HEADER_EXTRA: \newtheorem{warning}{Warning} +#+LATEX_HEADER_EXTRA: \BeforeBeginEnvironment{warning}{\begin{mdframed}[linecolor=red,backgroundcolor=red!5]} +#+LATEX_HEADER_EXTRA: \AfterEndEnvironment{warning}{\end{mdframed}} * Introduction @@ -359,7 +366,7 @@ modifications themselves look easy. Obviously, any experienced programmer knows that writing the code is only the first step of a much longer process which requires rigorous tests and validations, the enrichment of a non-regression database. Generally one also desires to -*ensure that the development is used within the correct bounds which +ensure that the development is used within the correct bounds which requires to implement some checking. In a perfect world, an up-to-date documentation of the functionality and its domain of validity. @@ -415,15 +422,311 @@ Actually, ~cout~ is itself a variable, we will come to this later. ~pugs~ is a strongly typed language. It means that a variable *cannot* change of type in its lifetime. + +*** Declaration and affectation syntax + +**** Declaration of simple variables + +To declare a variable ~v~ of a given type ~V~, one writes +#+BEGIN_SRC pugs :exports source + let v:V; +#+END_SRC + +This instruction is read as +#+begin_verse +Let $v\in V$. +#+end_verse + +Actually, +- ~let~ is the declaration keyword, +- ~v~ is the variable name, +- ~:~ is a separation token (can be read as "/in/" in this context), +- ~V~ is the identifier of the type, and +- ~;~ marks the end of the instruction. + +For instance to declare a real variable ~x~, one writes +#+NAME: declare-R +#+BEGIN_SRC pugs :exports both :results none + let x:R; +#+END_SRC +The instructions that follow the declaration of a variable can use it +while defined in the same scope, but it cannot be used before. Also, +after its declaration, one cannot declare another variable with the +same name in the same scope. +#+NAME: redeclare-variable +#+BEGIN_SRC pugs-error :exports both :results output + let x:R; // first declaration + let x:R; // second declaration +#+END_SRC +produces the following error +#+results: redeclare-variable + +**** Affectation of simple variables + +To affect the value of an expression ~expression~ to variable ~v~ one +simply uses the ~=~ operator. Thus assuming that a variable ~v~ has +already been /declared/, one writes simply +#+BEGIN_SRC pugs :exports source + v = expression; +#+END_SRC +There is not to much to comment, reading is quite natural +- ~v~ is the variable name, +- ~=~ is the affectation operator, +- ~expression~ is some code that provide a value (it can be another + variable, an arithmetic expression, the result of a function,...), + and +- ~;~ marks the end of the instruction. + +One for instance can write +#+NAME: simple-affectations-example +#+BEGIN_SRC pugs :exports both :results output + import math; // to load sin function + + let a:N; + let b:N; + let x:R; + a=3; + b=2+a; + x=sin(b)+a; + + cout << "a = " << a << " b = " << b << " x = " << x << "\n"; +#+END_SRC +In this example, we imported the `math` module which provides the +`sin` function. and we used the ~N~ data type of natural integers +($\mathbb{N}\equiv\mathbb{Z}_{\ge0}$). + +Running the example gives the following result. +#+results: simple-affectations-example + +#+BEGIN_warning +Actually, *separating* the *declaration* from the *initialization* of a +variable is quite *dangerous*. This is prone to errors and can lead to +the use of undefined values. During the compilation of scripts, ~pugs~ +detects uninitialized values. For instance, +#+NAME: uninitialized-variable +#+BEGIN_SRC pugs-error :exports both :results output + let x:R; + let y:R; + y=x+1; +#+END_SRC +produces the following compilation error +#+results: uninitialized-variable + +For more complex constructions, it can be very difficult to detect at +compile time, this is why it is *encourage* to use variable definition +(see [[definition-simple-variables]]). + +Observe nonetheless, ~pugs~ checks at runtime that used variables are +correctly defined. If not an error is produced. +#+END_warning + +**** Definition of simple variables<<definition-simple-variables>> + +The best way to define variables is the following. To define a +variable ~v~ of a given type ~V~, from an expression one writes +#+BEGIN_SRC pugs :exports source + let v:V, v = expression; +#+END_SRC +- ~let~ is the declaration keyword, +- ~v~ is the variable name, +- ~:~ is a separation token (can be read as "/in/" in this context), +- ~V~ is the identifier of the type, +- ~,~ is the separator that indicates the beginning of the affectation, +- ~expression~ is some code that provide a value (it can be another + variable, an expression, the result of a function,...), + and +- ~;~ marks the end of the instruction. + +A practical example is +#+NAME: simple-definition-example +#+BEGIN_SRC pugs :exports both :results output + import math; // to load sin function + + let a:N, a=3; + let b:N, b=2+a; + let x:R, x=sin(b)+a; + + cout << "a = " << a << " b = " << b << " x = " << x << "\n"; +#+END_SRC +which produces the result +#+results: simple-definition-example + +**** Compound declarations and affectations +We shall now see some weird constructions that are not useful by +themselves but which are important when dealing with builtin functions. + +We just give an example which should be enough to explain the syntax. + +#+NAME: compound-definition-example +#+BEGIN_SRC pugs :exports both :results output + let (a, b, x):N*N*R; + (x,b) = (2.3, 12); + a = 3; + + cout << "a = " << a << " b = " << b << " x = " << x << "\n"; + + let (c,y,d) : N*R*N, (c, y, d) = (8, 1e-3, 2); + + cout << "c = " << c << " d = " << d << " y = " << y << "\n"; +#+END_SRC +which produces the result +#+results: compound-definition-example + +The only potential mistake with this construction, is that variables +must appear in the same order in the declaration part and in the +affectation part of a definition. For instance, the following code is +invalid. + +#+NAME: invalid-compound-definition +#+BEGIN_SRC pugs-error :exports both :results output + let (x,y):R*R, (y,x) = (0,1); +#+END_SRC +It produces the following error +#+results: invalid-compound-definition +which is easy to fix. + *** TODO Basic types<<basic-types>> Basic types in ~pugs~ are boolean ~B~, natural integers ~N~, integers ~Z~, real ~R~, small vectors ~R^1~, ~R^2~ and ~R^3~, small matrices ~R^1x1~, ~R^2x2~ and ~R^3x3~ and strings ~string~. +#+BEGIN_note +Observe that while mathematically, obviously $\mathbb{R} = \mathbb{R}^1 += \mathbb{R}^{1\times1}$, the data types ~R~, ~R^1~ and ~R^1x1~ are different +in ~pugs~ and are *not implicitly* convertible from one to the other! + +This may sound strange but there are few reasons for that. +- First, these are the reflect of internal ~pugs~ ~C++~-data types that + are used to write algorithms. In its core design pugs aim at writing + numerical methods generically with regard to the dimension. One of + the ingredients to achieve this purpose is to use dimension $1$ + vectors and matrices when some algorithms reduce to dimension $1$ + instead of ~double~ values. To avoid ambiguity that may arise in some + situations (this can lead to very tricky code), we decided to forbid + automatic conversions of these types with ~double~. When designing the + language we adopt the same rule to avoid ambiguity. +- A second reason is connected to the first one. Since ~pugs~ aims at + providing numerical methods for problems in dimension $1$, $2$ or + $3$, this allow to distinguish the nature of the underlying objects. + - It is natural to consider that the coordinates of the vertices + defining a mesh in dimension $d$ are elements of $\mathbb{R}^d$, + - or that a velocity or a displacement are also defined as + $\mathbb{R}^d$ values. + Thus using ~R^1~ in dimension $1$ for this kind data precise their + nature in some sense . +#+END_note + +**** Variables of basic types are stored by value + +This means that each variable of basic type allocates its own memory +to store its data. This is the natural behavior for variables thus one +understands easily that in the following example: + +#+NAME: basic-type-value-storage +#+BEGIN_SRC pugs :exports both :results output + let a:N, a = 3; + let b:N, b = a; + a = 1; + cout << "a = " << a << " b = " << b << "\n"; +#+END_SRC +which produces the expected result +#+results: basic-type-value-storage +When defining ~b~, the *value* contained is ~a~ is copied to set the value +of ~b~. Thus changing ~a~'s value does not impact the variable ~b~. -*** TODO Declaration and affectation syntax +**** Variables of basic types are mutable + +In ~pugs~ the only variables that are mutable (their value can be +*modified*) are of basic types. Executing the following code +#+NAME: basic-type-mutable-value +#+BEGIN_SRC pugs :exports both :results output + let a:N, a = 2; + a += 3; + cout << "a = " << a << "\n"; +#+END_SRC +gives +#+results: basic-type-mutable-value +which is not a surprise. However, the use of the ~+=~ operator results +in the modification of the stored value. There is no copy. + +Actually, this is not really important from the user point of +view. One just have to keep in mind that, as it will be depicted +after, high-level variables *are not mutable*: their values can be +*replaced* by a new ones but *cannot be modified*. + +*** Implicit type conversions + +In order to avoid ambiguities, in ~pugs~, there is *no implicit* +conversion in general. + +#+BEGIN_note +Actually, there are only two situations for which implicit type +conversion can occur +- when the value is given as a parameter of a function, or +- when the value is used as the returned value of a function. +This will be detailed in section [[functions]]. +#+END_note + +This means that all affectation, unary and binary operators are +defined for all types. + +*** Operators + +**** Affectation operators + +In the ~pugs~ language the affectation operators are the following. +| operator | description | +|----------+----------------------| +| ~=~ | affectation operator | +|----------+----------------------| +| ~+=~ | increment operator | +| ~-=~ | decrement operator | +| ~*=~ | multiply operator | +| ~/=~ | divide operator | + +#+BEGIN_note +It is important to note that in ~pugs~ language, affectation operators +have *no* return value. This is a notable difference with ~C~ or +~C++~. Again, this is done to avoid common mistakes that can be +difficult to address. For instance, the following ~C++~ code is valid +but does not produce the expected result. +#+BEGIN_SRC C++ :exports source + bool b = true; + // do things ... + if (b=false) { + // do other things ... + } +#+END_SRC +Obviously the mistake is that the test should have been ~(b==false)~, +since otherwise, the following block is never executed (in ~C++~, the +result of an affectation is the value affected to the variable, which +is always false in that case. + +This cannot happen with ~pugs~. A similar example +#+NAME: no-affectation-result +#+BEGIN_SRC pugs-error :exports both :results output + let b:B, b=true; + // do things + if (b=false) { + // do other things + } +#+END_SRC +produces the following error +#+results: no-affectation-result + +#+END_note + +Here is the complete list of supported affectation operators according +to left hand side variable type (/lvalue/ type) and right hand side +expression type (/rvalue/ type). + + + +**** Unary operators + +**** Binary operators *** TODO High-level types<<high-level-types>> @@ -449,6 +752,10 @@ and ~R^3x3~ and strings ~string~. ** TODO Functions<<functions>> +*** Pure functions + +*** Implicit type conversion for parameters and returned values + *** User-defined functions *** Builtin functions