diff --git a/doc/userdoc.org b/doc/userdoc.org index f3e7ce3171053c72aad5474b3bec902dd230cc6c..12ce427db3bdd792588d6e32a12c9b0af980c02e 100644 --- a/doc/userdoc.org +++ b/doc/userdoc.org @@ -28,6 +28,8 @@ #+LATEX_HEADER_EXTRA: \usepackage{amssymb} #+LATEX_HEADER_EXTRA: \usepackage{xcolor} #+LATEX_HEADER_EXTRA: \usepackage{mdframed} +#+LATEX_HEADER_EXTRA: \usepackage{mathpazo} +#+LATEX_HEADER_EXTRA: \usepackage{inconsolata} #+LATEX_HEADER_EXTRA: \BeforeBeginEnvironment{tabular}{\rowcolors[]{2}{orange!5}{orange!10}} #+LATEX_HEADER_EXTRA: \BeforeBeginEnvironment{minted}{\begin{mdframed}[linecolor=blue,backgroundcolor=blue!10]} @@ -41,7 +43,10 @@ #+LATEX_HEADER_EXTRA: \BeforeBeginEnvironment{warning}{\begin{mdframed}[linecolor=red,backgroundcolor=red!10]} #+LATEX_HEADER_EXTRA: \AfterEndEnvironment{warning}{\end{mdframed}} -#+LATEX_HEADER_EXTRA: \usepackage{mathpazo} +#+begin_src latex :exports results + \let\OldTexttt\texttt + \renewcommand{\texttt}[1]{\fcolorbox{gray!50}{gray!5}{\OldTexttt{#1}}} +#+end_src * Introduction @@ -56,7 +61,7 @@ of some [[https:github.com/kokkos/kokkos][Kokkos]] mechanisms. The philosophy of ~pugs~ is to provide "simple" numerical tools that are assembled together through a high-level language (a DSL[fn:DSL-def] close to the mathematics) to build more complex solvers. This approach -is inspired by the success of [[http://freefem.org][FreeFEM]], which use a similar approach. +is inspired by the success of [[http://freefem.org][FreeFEM]], which uses a similar approach. Before detailing the leading concepts and choices that we have made to develop ~pugs~, we give a simple example. @@ -118,7 +123,7 @@ The example is quite easy to read. allow* the modifications of values of variables of *non-basic* types. This is discussed in the section [[high-level-types]]. - Finally, the last block consists in saving the obtained mesh in a - ~gnuplot~ file. The result is shown on Figure [[fig:intro-example]]. + ~gnuplot~ file. The result is shown in Figure [[fig:intro-example]]. #+NAME: intro-transform-mesh-img #+BEGIN_SRC gnuplot :exports results :file (substitute-in-file-name "${PUGS_SOURCE_DIR}/doc/intro-transform-mesh.png") @@ -144,10 +149,10 @@ already be discussed. - There is no predefined constant in ~pugs~. Here a value is provided for ~pi~. - There are two kinds of variable in ~pugs~: variables of basic types - and variable of high-level types. This two kinds of variable behave + and variables of high-level types. This two kinds of variable behave almost the same but one must know their differences to understand better the underlying mechanisms and choices that we made. See - section [[basic-types]] and [[high-level-types]] for details. + sections [[basic-types]] and [[high-level-types]] for details. - Also, there are two types of functions: *user-defined* functions and *builtin* functions. In this example, ~theta~, ~M~ and ~T~ are user-defined functions. All other functions (~cos~, ~cartesianMesh~,...) are @@ -168,9 +173,9 @@ calculations,... *** A C++ toolbox driven by a user friendly language -Numerical simulation packages are software of a particular +Numerical simulation packages are softwares of a particular kind. Generally, in order to run a calculation, one has to define a -set of data and parameters. This can simply be definition of a +set of data and parameters. This can simply be the definition of a discretization parameter such as the mesh size. One can also specify boundary conditions, equations of state, source terms for a specific model. Choosing a numerical method or even more, setting the model @@ -180,9 +185,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? @@ -195,11 +200,11 @@ There are lots of reasons not to use data files. By data file, we refer to a set of options that describe physical models, numerical methods or their settings. -- Data files are not flexible. This implies in then one hand that +- Data files are not flexible. This implies in the one hand that application scenarios must be known somehow precisely to reflect possible option combinations and in the other hand even defining a specific initial data may require the creation of a new option and - the associated code (in ~C++~ for instance). \\ + its associated code (in ~C++~ for instance). \\ Usually, the last point is addressed by adding a local interpreter to evaluate user functions. - Data files may contain irrelevant information. Actually, it is quite @@ -224,9 +229,9 @@ 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 to much freedom. While +- The first one is probably that it allows too much freedom. While defining the model and numerical options, the user has generally access to the whole code and can change almost everything, even things that should not be changed. @@ -238,8 +243,8 @@ files or a script), but it presents several drawbacks. coupled physics. - 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 setting may become rapidly obsolete. In other - words keeping up to date embedded "data file" might be difficult. + constructions or settings may become rapidly obsolete. In other + 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. @@ -294,7 +299,7 @@ writing of new methods. *new functions in the language*. Moreover, if a new method is close to an existing one, it is generally *better* to use completely new underlying ~C++~ code than to patch existing methods. Starting from a - *copy* of the existing code ~C++~ is *encouraged* for developments. This + *copy* of the existing ~C++~ code is *encouraged* for developments. This may sound weird since classical development guidelines encourage inheritance or early redesign. Actually, this policy is the result of the following discussion. @@ -306,7 +311,7 @@ writing of new methods. corrections. - It is much more difficult to introduce bugs in existing methods, since previously validated methods are unchanged! - - For the same reason, existing methods performances are naturally + - For the same reason, existing method performances are naturally unchanged by new developments. - Also, when comparing methods, it is better to compare to the original existing code. @@ -338,7 +343,7 @@ writing of new methods. ***** Why not python or any other scripting language? As it was already pointed out above, general purpose languages offer -to much freedom: it is not easy to protect data. For instance in the +too much freedom: it is not easy to protect data. For instance in the ~pugs~ DSL, non basic variables are constant (see paragraph [[high-level-types]]). It is important since it prevents the user from modifying data in inconsistent ways. Also, one must keep in mind that @@ -391,7 +396,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 @@ -433,7 +438,7 @@ change of type in its lifetime. **** Declaration of simple variables -To declare a variable ~v~ of a given type ~V~, one writes +To declare a variable ~v~ of a given type ~V~, one simply writes #+BEGIN_SRC pugs :exports source let v:V; #+END_SRC @@ -475,7 +480,7 @@ 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 +There is not too much to comment, reading is quite natural - ~v~ is the variable name, - ~=~ is the affectation operator, - ~expression~ is some code that provides a value of the same type as ~v~ @@ -584,31 +589,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 +1603,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 +1621,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 +2764,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 @@ -2775,7 +2777,7 @@ gmsh -2 hybrid-2d.geo -format msh2 write_mesh(gnuplot_writer("hybrid-2d"),m); #+END_SRC -The ~mesh~ is represented on Figure [[fig:gmsh-hybrid-2d]]. +The ~mesh~ is represented in Figure [[fig:gmsh-hybrid-2d]]. #+NAME: gmsh-hybrid-2d-img #+BEGIN_SRC gnuplot :exports results :file (substitute-in-file-name "${PUGS_SOURCE_DIR}/doc/gmsh-hybrid-2d.png") @@ -2810,7 +2812,7 @@ This function creates the diamond dual ~mesh~ of a primal ~mesh~. The diamond dual mesh is defined by joining the nodes of the faces to the center of the adjacent cells of the primal mesh. -The ~mesh~ is represented on Figure [[fig:gmsh-hybrid-2d]]. +The ~mesh~ is represented in Figure [[fig:gmsh-hybrid-2d]]. #+NAME: diamond-dual-img #+BEGIN_SRC gnuplot :exports results :file (substitute-in-file-name "${PUGS_SOURCE_DIR}/doc/diamond-dual.png") @@ -2863,7 +2865,7 @@ This function creates the median dual ~mesh~ of a primal ~mesh~. The median dual mesh is defined by joining the centers of the faces to the centers of the adjacent cells of the primal mesh. -The ~mesh~ is represented on Figure [[fig:gmsh-hybrid-2d]]. +The ~mesh~ is represented in Figure [[fig:gmsh-hybrid-2d]]. #+NAME: median-dual-img #+BEGIN_SRC gnuplot :exports results :file (substitute-in-file-name "${PUGS_SOURCE_DIR}/doc/median-dual.png") @@ -2924,7 +2926,7 @@ that in ~pugs~ internals, there is only one connectivity object for these two meshes. #+END_note -The result of the previous script is given on Figure +The result of the previous script is given in Figure [[fig:transformed]]. They all share the same connectivity in memory. #+NAME: transformed-img @@ -2949,7 +2951,7 @@ The result of the previous script is given on 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}. @@ -2990,7 +2992,7 @@ write_mesh(gnuplot_writer("relax_example_m2"), m2); #+END_SRC In this example, the relaxation parameter is set to $\theta=0.3$. The -different meshes produced in this example are displayed on Figure +different meshes produced in this example are displayed in Figure [[fig:relax]]. #+NAME: relax-img @@ -3118,7 +3120,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 +3478,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 @@ -3538,7 +3558,7 @@ In this example, the ~mesh~ provided in the file ~zones-1d.msh~ is a 1d ~mesh~ of $]-1,1[$ made of $200$ cells that is partitioned into 3 connected subdomains. The zones corresponding to these 3 subdomains are named ~LEFT~ for $]-1,-0.3[$, ~MIDDLE~ for $]-0.3, 0.3[$ and ~RIGHT~ for -$]0.3,1[$. The result is displayed on Figure [[fig:zone-integrate-1d]]. In +$]0.3,1[$. The result is displayed in Figure [[fig:zone-integrate-1d]]. In the ~MIDDLE~ region, cell values are set to 0. #+NAME: zone-integrate-1d-img @@ -3632,7 +3652,7 @@ Let us consider a simple example write_mesh(gnuplot_writer("random-mesh"), m); #+END_SRC -Running this script one gets the ~mesh~ displayed on Figure +Running this script one gets the ~mesh~ displayed in Figure [[fig:random-mesh]]. To reduce the vertices displacement, one can use the ~relax~ function, see section [[relax-function]]. @@ -3692,7 +3712,7 @@ displacement is allowed for $x<2y$. write_mesh(gnuplot_writer("random-mesh-chi"), m); #+END_SRC -Running this script one gets the ~mesh~ displayed on Figure +Running this script one gets the ~mesh~ displayed in Figure [[fig:random-mesh-chi]]. #+NAME: random-mesh-chi-img @@ -3731,7 +3751,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 +3862,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 +3895,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 +4100,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 +4162,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 @@ -4228,10 +4248,10 @@ Let us illustrate it by an important second example. write(gnuplot_writer("writer-example2-2"), output_list); #+END_SRC -Running this code produces the gnuplot file displayed on Figure +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 on 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 +4268,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 +4295,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~). @@ -4491,7 +4511,7 @@ Figure [[fig:writer-gp-sin]]. plot '<(sed "" $PUGS_SOURCE_DIR/doc/gp_sin.gnu)' lw 2 w lp #+END_SRC -#+CAPTION: Example of produced gnuplot results from the ~gnuplot_writer~. One can compare ths produced result to the one of the ~gnuplot_1d_writer~ given on Figure [[fig:writer-gp-1d-sin]] +#+CAPTION: Example of produced gnuplot results from the ~gnuplot_writer~. One can compare ths produced result to the one of the ~gnuplot_1d_writer~ given in Figure [[fig:writer-gp-1d-sin]] #+NAME: fig:writer-gp-sin #+ATTR_LATEX: :width 0.38\textwidth #+ATTR_HTML: :width 300px; 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"(