diff --git a/doc/userdoc.org b/doc/userdoc.org index 58d78e37b7d36b594bc756fa06ffa67c06d24cf5..5ff07d98b6d7294dac115a74b6b719883c5b7b0f 100644 --- a/doc/userdoc.org +++ b/doc/userdoc.org @@ -180,9 +180,9 @@ In ~pugs~, all these "parameters" are set through a DSL[fn:DSL-def]. Thus, when ~pugs~ is launched, it actually executes a provided script. A ~C++~ function is associated to each instruction of the script. The ~C++~ components of ~pugs~ are completely unaware one of -the others. ~pugs~ interpreter is responsible of data flow between the -components: it manages the data transfer between those ~C++~ components -and ensures that the workflow is properly defined. +the others. ~pugs~ interpreter is responsible for the data flow between +the components: it manages the data transfer between those ~C++~ +components and ensures that the workflow is properly defined. **** Why? @@ -224,7 +224,7 @@ methods or their settings. Using directly the general purpose language (~C~, ~C++~, ~Fortran~,...) used to write the code can be tempting. It has the advantage that no particular treatment is necessary to build a parser (to read data -files or a script), but it presents several drawbacks. +files or scripts), but it presents several drawbacks. - The first one is probably that it allows too much freedom. While defining the model and numerical options, the user has generally @@ -239,7 +239,7 @@ files or a script), but it presents several drawbacks. - Another difficulty is related to the fact that code's internal API is likely to change permanently in a research code. Thus valid constructions or settings may become rapidly obsolete. In other - words keeping up to date embedded "data file" might be difficult. + words keeping up to date embedded "data files" might be difficult. - Finally it requires recompilation of pieces of code (which can be large in some cases) even if one is just changing a simple parameter. @@ -391,7 +391,7 @@ it is generally to decide not to offer them.\\ If the grammar of ~pugs~ is still likely to be extended, it should *never* integrate low-level instructions. Low-level instructions give too much freedom and thus are a source of errors. Several things are already -done to forbid this kind of evolution. The constness of high-level +done to forbid this kind of evolution. The immutability of high-level data is a good illustration. For instance, meshes or discrete functions *cannot* be modified. This is not only a security to protect the user from doing "dangerous" manipulations, but it also permits to @@ -584,31 +584,27 @@ we give a few examples. #+END_SRC #+results: out-of-scope-variable-use -**** Variable name can be reused in an enclosed scope +**** Variable name *cannot* be reused in an enclosed scope #+NAME: nested-scope-variable-example -#+BEGIN_SRC pugs :exports both :results output - let n:N, n = 0; // global variable +#+BEGIN_SRC pugs-error :exports both :results output { - cout << "global scope n = " << n << "\n"; - let n:N, n = 1; // scope level 1 variable - { - cout << "scope level 1 n = " << n << "\n"; - let n:N, n = 2; // scope level 2 variable - cout << "scope level 2 n = " << n << "\n"; - } - { - cout << "scope level 1 n = " << n << "\n"; - let n:N, n = 4; // scope level 2.2 variable - cout << "scope level 2.2 n = " << n << "\n"; - } - cout << "scope level 1 n = " << n << "\n"; + let n:N, n = 1; + } + { + let n:N, n = 2; + } + let n:N, n = 3; + { + let n:N, n = 4; } - cout << "global scope n = " << n << "\n"; #+END_SRC #+results: nested-scope-variable-example -This example is self explanatory. Obviously such constructions are -generally *bad ideas*. This kind of constructions can appear in loops -where the variables defined in blocks follow the same lifetime rules. +This example is self explanatory. The same variable name can be used +in unrelated blocks (this is useful in loops for instance). However, +it cannot be reused if a variable has already been declared with the +same one in an embedding scope. This is a difference with ~C++~, the +reason is that this kind of construction is dangerous and difficult to +read. *** Basic types<<basic-types>> @@ -1602,7 +1598,7 @@ types. These can be meshes (the ~mesh~ type), output streams (the One can already see that the complexity of these types may vary a lot. The main difference between these types and basic types is that, -high-level types are not available in directly the language but they +high-level types are not available directly in the language but they are loaded on demand (using the ~import~ keyword in the preamble of the script). @@ -1620,8 +1616,8 @@ operators can never be applied to variables of this kind | ~/=~ | assignment by quotient | We conclude by stating that if access operator ~[]~ can eventually be -overloaded for high-level types, it should be done with care. It is -not recommended. +defined for high-level types, it should be done with care. It is not +recommended. Finally, the last difference lies in the fact that high-level types use shallow copies and not value copies as it is the case for basic @@ -2763,7 +2759,8 @@ Physical Point("XMAXYMIN") = {5}; Physical Point("XMAXYMAX") = {6}; #+END_SRC -#+BEGIN_SRC shell :exports results :results none +Here is an example of ~gmsh~ use that produces a mesh at format ~msh2~. +#+BEGIN_SRC shell :exports both :results none gmsh -2 hybrid-2d.geo -format msh2 #+END_SRC @@ -2949,7 +2946,7 @@ The result of the previous script is given in Figure ***** ~relax: mesh*mesh*R -> mesh~ <<relax-function>> This function is a simple utility that computes a ~mesh~ as the /mean/ of -two other mesh that share the same connectivity. The coordinates of +two other meshes that share the same connectivity. The coordinates of the vertices of the relaxed mesh $\mathcal{M}_2$, are given by \begin{equation*} \forall r\in\mathcal{R},\quad\mathbf{x}_r^{\mathcal{M}_2} = (1-\theta) \mathbf{x}_r^{\mathcal{M}_0} + \theta \mathbf{x}_r^{\mathcal{M}_1}. @@ -3118,7 +3115,7 @@ Here is the list of the functions These functions are defined for $\mathbb{P}_0(\mathbb{R})$ data and the return value is also a $\mathbb{P}_0(\mathbb{R})$ function. These -functions require that the two arguments are defined one the *same +functions require that the two arguments are defined on the *same mesh*. The result is obtained by applying the function cell by cell. Here is the function list @@ -3476,6 +3473,24 @@ $\vec{\mathbb{P}}_0(\mathbb{R})$ of dimension 1 (since passing a single function as a list of user function, the previous function [[integrate-classic]], would be used). +#+BEGIN_SRC pugs :exports both :results none + import mesh; + import scheme; + import math; + + let m:mesh, m = readGmsh("hybrid-2d.msh"); + let u:R^2 -> R, x -> cos(x[0]*x[1]); + let v:R^2 -> R, x -> x[0]*x[1]; + let w:R^2 -> R, x -> x[0]+x[1]; + + let u1h:Vh, u1h = integrate(m, Gauss(5), P0Vector(), u); + let u2h:Vh, u2h = integrate(m, Gauss(5), u); + let u3h:Vh, u3h = integrate(m, Gauss(5), P0Vector(), (u,v,w)); +#+END_SRC +In this example one observes the difference between ~u1h~ and ~u2h~. The +first one is a $\vec{\mathbb{P}}_0(\mathbb{R})$ where the vector size +is 1, and ~u2h~ is a $\mathbb{P}_0(\mathbb{R})$. + ****** ~integrate: mesh*(zone)*quadrature*function -> Vh~ This function is an enhancement of the function defined in @@ -3731,7 +3746,7 @@ descriptors that are provided by the ~scheme~ module. #+BEGIN_note These functions provide *descriptors*, these are not related to a particular implementation. The way they are used in different -functions dependents of the context or the numerical method itself. +functions may vary. #+END_note #+BEGIN_note @@ -3842,9 +3857,9 @@ to supported elements. This function is a special function whose purpose is to transport lagrangian quantities from one mesh to the other. Obviously, this -function make lots of sense in the case of lagrangian calculations. +function makes a lot of sense in the case of lagrangian calculations. -This is a zero cost function, since discrete functions are *constants* +This is a zero cost function, since discrete functions are *constant* in ~pugs~, it consists in associating the data of the given discrete function to the provided ~mesh~. In other words, the underlying array of values is shared by the two discrete functions, which are associated @@ -3875,13 +3890,13 @@ this order: - the sound speed $c$ of type $\mathbb{P}_0(\mathbb{R})$, - the pressure $p$ of type $\mathbb{P}_0(\mathbb{R})$, - a list of boundary conditions, -- and a time step of type ~R~. +- and a time step of type $\mathbb{R}$. Observe that ~pugs~ checks the types of the parameters and that all discrete functions are defined on the same mesh. The functions return a compound type made of - the new ~mesh~ $\mathcal{M}$, -- the new mass density ~\rho~ of type $\mathbb{P}_0(\mathbb{R})$ defined +- the new mass density $\rho$ of type $\mathbb{P}_0(\mathbb{R})$ defined on $\mathcal{M}$, - the new velocity $\mathbf{u}$ of type $\mathbb{P}_0(\mathbb{R}^d)$ in dimension $d$, defined on the mesh $\mathcal{M}$, @@ -4080,7 +4095,7 @@ depends on the compilation options of the code. ****** ~getLSOptions: void -> string~ -This function show the current tuning of the global linear solver +This function shows the current tuning of the global linear solver #+NAME: get-ls-options-example #+BEGIN_SRC pugs :exports both :results output import linear_solver; @@ -4142,7 +4157,7 @@ different. Variables of this type manage outputs: which format is used and eventually the writing policy. This policy sets for instance the time -period of for time dependent post processing. +period for time-dependent post processing. **** ~writer~ provided functions @@ -4231,7 +4246,7 @@ Let us illustrate it by an important second example. Running this code produces the gnuplot file displayed in Figure [[fig:writer-example2]]. One sees that ~f~ is the $\mathbb{P}_0(\mathbb{R})$ function corresponding to the function $x \to x$ and not to the function -$x -> |\cos(\pi x)|$. This later function is plotted in Figure +$x \to |\cos(\pi x)|$. This later function is plotted in Figure [[fig:writer-example2-2]] since ~output_list~ is set with the updated value of ~fh~. @@ -4248,7 +4263,7 @@ of ~fh~. plot '<(sed "" $PUGS_SOURCE_DIR/doc/writer-example2.gnu)' u 1:2 lw 2 t "f" w l, '<(sed "" $PUGS_SOURCE_DIR/doc/writer-example2.gnu)' u 1:3 lw 2 t "g" w l, '<(sed "" $PUGS_SOURCE_DIR/doc/writer-example2.gnu)' u 1:4 lw 2 t "sin(pi*f)" w l #+END_SRC -#+CAPTION: Illustration of the life time of variables. The output for ~fh~ corresponds to its value when ~output_list~ is created: the interpolation of $x -> x$. +#+CAPTION: Illustration of the life time of variables. The output for ~fh~ corresponds to its value when ~output_list~ is created: the interpolation of $x \to x$. #+NAME: fig:writer-example2 #+ATTR_LATEX: :width 0.38\textwidth #+ATTR_HTML: :width 300px; @@ -4275,7 +4290,7 @@ of ~fh~. ***** ~gnuplot~ writers <<gnuplot-writers>> -There is two ~gnuplot~ writers. One is dedicated to output of dimension +There are two ~gnuplot~ writers. One is dedicated to output of dimension 1 results (~gnuplot_1d_writer~) and the other allows post processing in dimension 1 and 2 (~gnuplot_writer~). diff --git a/src/language/ast/ASTSymbolTableBuilder.cpp b/src/language/ast/ASTSymbolTableBuilder.cpp index ac7d03155f60fc4a54751018f9d6cf45b5c9ec58..87baa5716cd36bd19db71e81e8e4398bfcb2e1fe 100644 --- a/src/language/ast/ASTSymbolTableBuilder.cpp +++ b/src/language/ast/ASTSymbolTableBuilder.cpp @@ -32,12 +32,14 @@ ASTSymbolTableBuilder::buildSymbolTable(ASTNode& n, std::shared_ptr<SymbolTable> throw ParseError(error_message.str(), std::vector{n.children[0]->begin()}); } - auto [i_symbol, success] = symbol_table->add(symbol, n.children[0]->begin()); - if (not success) { + if (auto [i_symbol, found] = symbol_table->find(symbol, n.children[0]->begin()); found) { std::ostringstream error_message; - error_message << "symbol '" << rang::fg::red << symbol << rang::fg::reset << "' was already defined!"; + error_message << "symbol '" << rang::fg::red << symbol << rang::fg::reset << "' was already defined at line " + << i_symbol->attributes().position().line; throw ParseError(error_message.str(), std::vector{n.children[0]->begin()}); } + auto [i_symbol, success] = symbol_table->add(symbol, n.children[0]->begin()); + Assert(success); for (auto& child : n.children) { this->buildSymbolTable(*child, local_symbol_table); @@ -59,13 +61,13 @@ ASTSymbolTableBuilder::buildSymbolTable(ASTNode& n, std::shared_ptr<SymbolTable> throw ParseError(error_message.str(), std::vector{argument_node.begin()}); } - auto [i_symbol, success] = symbol_table->add(argument_node.string(), argument_node.begin()); - if (not success) { + if (auto [i_symbol, found] = symbol_table->find(argument_node.string(), argument_node.begin()); found) { std::ostringstream error_message; error_message << "symbol '" << rang::fg::red << argument_node.string() << rang::fg::reset - << "' was already defined!"; + << "' was already defined at line " << i_symbol->attributes().position().line; throw ParseError(error_message.str(), std::vector{argument_node.begin()}); } + symbol_table->add(argument_node.string(), argument_node.begin()); }; if (n.children[0]->is_type<language::name>()) { @@ -91,7 +93,7 @@ ASTSymbolTableBuilder::buildSymbolTable(ASTNode& n, std::shared_ptr<SymbolTable> if (not success) { std::ostringstream error_message; error_message << "symbol '" << rang::fg::red << argument_node.string() << rang::fg::reset - << "' was already defined!"; + << "' was already defined at line " << i_symbol->attributes().position().line; throw ParseError(error_message.str(), std::vector{argument_node.begin()}); } // Symbols will be initialized at call diff --git a/tests/test_ASTSymbolTableBuilder.cpp b/tests/test_ASTSymbolTableBuilder.cpp index e336d7841a3fbc54903a16584d0eb224252b464d..5f5eae42fe968a730ae82b5ae347a94fa3a9a4fb 100644 --- a/tests/test_ASTSymbolTableBuilder.cpp +++ b/tests/test_ASTSymbolTableBuilder.cpp @@ -17,11 +17,11 @@ TEST_CASE("ASTSymbolTableBuilder", "[language]") SECTION("Build symbols") { std::string_view data = R"( -let n:N, n = 2; { - let m:N, m = n; + let m:N, m = 2; let n:R, n = m/3.; } +let n:N, n = 2; )"; string_input input{data, "test.pgs"}; @@ -92,7 +92,22 @@ let n:N, n = 1; string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); - REQUIRE_THROWS_WITH(ASTSymbolTableBuilder{*ast}, "symbol 'n' was already defined!"); + REQUIRE_THROWS_WITH(ASTSymbolTableBuilder{*ast}, "symbol 'n' was already defined at line 2"); + } + + SECTION("Re-declared symbol (nested scope)") + { + std::string_view data = R"( +let n:N, n = 0; +{ + let n:N, n = 1; +} +)"; + + string_input input{data, "test.pgs"}; + auto ast = ASTBuilder::build(input); + + REQUIRE_THROWS_WITH(ASTSymbolTableBuilder{*ast}, "symbol 'n' was already defined at line 2"); } SECTION("Re-declared symbol (function)") @@ -105,7 +120,7 @@ let f : R -> R, x -> 1; string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); - REQUIRE_THROWS_WITH(ASTSymbolTableBuilder{*ast}, "symbol 'f' was already defined!"); + REQUIRE_THROWS_WITH(ASTSymbolTableBuilder{*ast}, "symbol 'f' was already defined at line 2"); } SECTION("Re-declared symbol (builtin function)") @@ -140,13 +155,14 @@ let cos: R -> R, x->2*x; SECTION("Re-declared parameter (function)") { std::string_view data = R"( -let f : R*R*N -> R, (x,y,x) -> 1; +let f : R*R*N -> R, + (x,y,x) -> 1; )"; string_input input{data, "test.pgs"}; auto ast = ASTBuilder::build(input); - REQUIRE_THROWS_WITH(ASTSymbolTableBuilder{*ast}, "symbol 'x' was already defined!"); + REQUIRE_THROWS_WITH(ASTSymbolTableBuilder{*ast}, "symbol 'x' was already defined at line 3"); } } } diff --git a/tests/test_DoWhileProcessor.cpp b/tests/test_DoWhileProcessor.cpp index 9ef421988b09c1d5639bf6d3a117d73bb02214d0..13942906c78cc81abde2ba40b64c1adb91756434 100644 --- a/tests/test_DoWhileProcessor.cpp +++ b/tests/test_DoWhileProcessor.cpp @@ -105,21 +105,6 @@ do { CHECK_WHILE_PROCESSOR_RESULT(data, "i", 12ul); } - SECTION("do-while lifetime variable") - { - std::string_view data = R"( -let i:N, i = 3; -let j:N, j = 0; -do { - j = 5; - let j:N, j = 2; - i = j; -} while(false); -)"; - CHECK_WHILE_PROCESSOR_RESULT(data, "i", 2ul); - CHECK_WHILE_PROCESSOR_RESULT(data, "j", 5ul); - } - SECTION("empty do-while symbol table untouched") { std::string_view data = R"( diff --git a/tests/test_WhileProcessor.cpp b/tests/test_WhileProcessor.cpp index 028d8eae035f8743ecd01776901a493c8d743eea..1de4c6c5c4a7aa8b356a2fb77d4c211b521eedf5 100644 --- a/tests/test_WhileProcessor.cpp +++ b/tests/test_WhileProcessor.cpp @@ -101,21 +101,6 @@ while(i<10) { CHECK_WHILE_PROCESSOR_RESULT(data, "i", 12ul); } - SECTION("while lifetime variable") - { - std::string_view data = R"( -let i:N, i = 3; -let j:N, j = 0; -while(i != 2) { - j = 5; - let j:N, j = 2; - i = j; -} -)"; - CHECK_WHILE_PROCESSOR_RESULT(data, "i", 2ul); - CHECK_WHILE_PROCESSOR_RESULT(data, "j", 5ul); - } - SECTION("while symbol table untouched") { std::string_view data = R"(