Compare commits

...

14 Commits

23 changed files with 415 additions and 141 deletions
+1
View File
@@ -128,6 +128,7 @@ func (self *ast) eval(ctx ExprContext, preset bool) (result any, err error) {
} }
if err == nil { if err == nil {
result, err = self.root.compute(ctx) result, err = self.root.compute(ctx)
ctx.setVar(ControlLastResult, result)
} }
// } else { // } else {
// err = errors.New("empty expression") // err = errors.New("empty expression")
+95 -13
View File
@@ -22,17 +22,23 @@ Expressions calculator
toc::[] toc::[]
#TODO: Work in progress (last update on 2024/05/07, 07:15 am)# #TODO: Work in progress (last update on 2024/05/10, 06:52 a.m.)#
== Expr == Expr
_Expr_ is a GO package capable of analysing, interpreting and calculating expressions. _Expr_ is a GO package capable of analysing, interpreting and calculating expressions.
=== Concepts and terminology
#TODO#
image::expression-diagram.png[]
=== `dev-expr` test tool === `dev-expr` test tool
`dev-expr` is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, beyond in additon to the automatic test suite based on the Go test framework, `dev-expr` provides an important aid for quickly testing of new features during their development. `dev-expr` is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, beyond in additon to the automatic test suite based on the Go test framework, `dev-expr` provides an important aid for quickly testing of new features during their development.
It cat work as a REPL, *R*ead-*E*xecute-*P*rint-*L*oop, or it can process expression acquired from files or standard input. It cat work as a _REPL_, _**R**ead-**E**xecute-**P**rint-**L**oop_, or it can process expression acquired from files or standard input.
The program can be downloaded from https://git.portale-stac.it/go-pkg/-/packages/generic/dev-expr/[dev-expr].
The program in located in the _tools_ directory.
Here are some examples of execution. Here are some examples of execution.
.Run `dev-expr` in REPL mode and ask for help .Run `dev-expr` in REPL mode and ask for help
@@ -40,17 +46,19 @@ Here are some examples of execution.
---- ----
# Assume the expr source directory. Type 'exit' or Ctrl+D to quit the program. # Assume the expr source directory. Type 'exit' or Ctrl+D to quit the program.
[user]$ tools/expr -- Expressions calculator v1.6.1,2024/05/06 (celestino.amoroso@portale-stac.it) [user]$ tools/expr -- Expressions calculator v1.7.0,2024/05/08 (celestino.amoroso@portale-stac.it)
Type help to get the list of command. Type help to get the list of command.
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
>>> help >>> help
--- REPL commands: --- REPL commands:
base -- Set the integer output base: 2, 8, 10, or 16
exit -- Exit the program
help -- Show command list help -- Show command list
ml -- Enable/Disable multi-line output ml -- Enable/Disable multi-line output
mods -- List builtin modules mods -- List builtin modules
source -- Load a file as input
tty -- Enable/Disable ansi output <1> tty -- Enable/Disable ansi output <1>
exit -- Exit the program
--- Command line options: --- Command line options:
-b <builtin> Import builtin modules. -b <builtin> Import builtin modules.
@@ -62,6 +70,8 @@ Here are some examples of execution.
-m, --modules List all builtin modules -m, --modules List all builtin modules
-p Print prefix form -p Print prefix form
-t Print tree form <2> -t Print tree form <2>
>>>
---- ----
<1> Only available for single fraction values <1> Only available for single fraction values
@@ -99,11 +109,6 @@ Here are some examples of execution.
<4> But operator, see <<_but_operator>>. <4> But operator, see <<_but_operator>>.
<5> Multi-expression: the same result of the previous single expression but this it is obtained with two separated calculations. <5> Multi-expression: the same result of the previous single expression but this it is obtained with two separated calculations.
=== Concepts and terminology
#TODO#
image::expression-diagram.png[]
== Data types == Data types
_Expr_ supports numerical, string, relational, boolean expressions, and mixed-type lists. _Expr_ supports numerical, string, relational, boolean expressions, and mixed-type lists.
@@ -136,7 +141,44 @@ Numbers can be integers (GO int64) or float (GO float64). In mixed operations in
|=== |===
=== String === Fractions
_Expr_ also supports fractions. Fraction literals are made with two integers separated by a vertical bar `|`.
.Examples
// [source,go]
// ----
`>>>` [blue]`1 | 2` +
[green]`1|2` +
`>>>` [blue]`4|6` +
[green]`2|3` [gray]_Fractions are always reduced to their lowest terms_ +
`>>>` [blue]`1|2 + 2|3` +
[green]`7|6` +
`>>>` [blue]`1|2 * 2|3` +
[green]`1|3` +
`>>>` [blue]`1|2 / 1|3` +
[green]`3|2` +
`>>>` [blue]`1|2 ./ 1|3` [gray]_Force decimal division_ +
[green]`1.5` +
`>>>` [blue]`-1|2` +
[green]`-1|2` +
`>>>` [blue]`1|-2` [gray]_Wrong sign specification_ +
[red]_Eval Error: [1:3] infix operator "|" requires two non-nil operands, got 1_ +
`>>>` [blue]`1|(-2)` +
[green]`-1|2`
// ----
Fractions can be used together with integers and floats in expressions.
`>>>` [blue]`1|2 + 5` +
[green]`11|2` +
`>>>` [blue]`4 - 1|2` +
[green]`7|2` +
`>>>` [blue]`1.0 + 1|2` +
[green]`1.5` +
=== Strings
Strings are character sequences enclosed between two double quote [blue]`"`. Example: [blue]`"I'm a string"`. Strings are character sequences enclosed between two double quote [blue]`"`. Example: [blue]`"I'm a string"`.
Some arithmetic operators can also be used with strings. Some arithmetic operators can also be used with strings.
@@ -204,7 +246,7 @@ Currently, boolean operations are evaluated using _short cut evaluation_. This m
<1> This multi-expression returns _1_ because in the first expression the left value of [blue]`or` is _true_ and as a conseguence its right value is not computed. Therefore the _a_ variable only receives the integer _1_. <1> This multi-expression returns _1_ because in the first expression the left value of [blue]`or` is _true_ and as a conseguence its right value is not computed. Therefore the _a_ variable only receives the integer _1_.
==== ====
=== List === Lists
_Expr_ supports list of mixed-type values, also specified by normal expressions. _Expr_ supports list of mixed-type values, also specified by normal expressions.
.List examples .List examples
@@ -217,7 +259,6 @@ _Expr_ supports list of mixed-type values, also specified by normal expressions.
[ [1,"one"], [2,"two"]] // List of lists [ [1,"one"], [2,"two"]] // List of lists
---- ----
.List operators .List operators
[cols="^2,^2,5,4"] [cols="^2,^2,5,4"]
|=== |===
@@ -228,6 +269,47 @@ _Expr_ supports list of mixed-type values, also specified by normal expressions.
| [blue]`-` | _Difference_ | Left list without elements in the right list | [blue]`[1,2,3] - [2]` _[ [1,3] ]_ | [blue]`-` | _Difference_ | Left list without elements in the right list | [blue]`[1,2,3] - [2]` _[ [1,3] ]_
|=== |===
The items of array can be accessed using the dot `.` operator.
.Item access syntax
[source,bnf]
----
<item> ::= <list-expr>"."<index-expr>
----
.Items of list
[source,go]
----
`>>>` [blue]`[1,2,3].1`
[green]`2`
`>>>` [blue]`list=[1,2,3]; list.1`
[green]`2`
`>>>` [blue]`["one","two","three"].1`
[green]`two`
`>>>` [blue]`list=["one","two","three"]; list.(2-1)`
[green]`two`
`>>>` [blue]`list.(-1)`
[green]`three`
`>>>` [blue]`list.(-1)`
[green]`three`
`>>>` [blue]`list.(10)`
----
== Dictionaries
The _dictionary_ data-type is set of pairs _key/value_. It is also known as _map_ or _associative array_. Dictionary literals are sequences of pairs separated by comma `,`; sequences are enclosed between brace brackets.
.List examples
[source,go]
----
{1:"one", 2:"two"}
{"one":1, "two": 2}
{"sum":1+2+3, "prod":1*2*3}
----
WARNING: Support for dictionaries is still ongoing.
== Variables == Variables
A variable is an identifier with an assigned value. Variables are stored in the object that implements the _ExprContext_ interface. A variable is an identifier with an assigned value. Variables are stored in the object that implements the _ExprContext_ interface.
+155 -54
View File
@@ -535,38 +535,40 @@ pre.rouge .ss {
<ul class="sectlevel1"> <ul class="sectlevel1">
<li><a href="#_expr">1. Expr</a> <li><a href="#_expr">1. Expr</a>
<ul class="sectlevel2"> <ul class="sectlevel2">
<li><a href="#_dev_expr_test_tool">1.1. <code>dev-expr</code> test tool</a></li> <li><a href="#_concepts_and_terminology">1.1. Concepts and terminology</a></li>
<li><a href="#_concepts_and_terminology">1.2. Concepts and terminology</a></li> <li><a href="#_dev_expr_test_tool">1.2. <code>dev-expr</code> test tool</a></li>
</ul> </ul>
</li> </li>
<li><a href="#_data_types">2. Data types</a> <li><a href="#_data_types">2. Data types</a>
<ul class="sectlevel2"> <ul class="sectlevel2">
<li><a href="#_numbers">2.1. Numbers</a></li> <li><a href="#_numbers">2.1. Numbers</a></li>
<li><a href="#_string">2.2. String</a></li> <li><a href="#_fractions">2.2. Fractions</a></li>
<li><a href="#_boolean">2.3. Boolean</a></li> <li><a href="#_strings">2.3. Strings</a></li>
<li><a href="#_list">2.4. List</a></li> <li><a href="#_boolean">2.4. Boolean</a></li>
<li><a href="#_lists">2.5. Lists</a></li>
</ul> </ul>
</li> </li>
<li><a href="#_variables">3. Variables</a></li> <li><a href="#_dictionaries">3. Dictionaries</a></li>
<li><a href="#_other_operations">4. Other operations</a> <li><a href="#_variables">4. Variables</a></li>
<li><a href="#_other_operations">5. Other operations</a>
<ul class="sectlevel2"> <ul class="sectlevel2">
<li><a href="#_operator">4.1. <code class="blue">;</code> operator</a></li> <li><a href="#_operator">5.1. <code class="blue">;</code> operator</a></li>
<li><a href="#_but_operator">4.2. <code class="blue">but</code> operator</a></li> <li><a href="#_but_operator">5.2. <code class="blue">but</code> operator</a></li>
<li><a href="#_assignment_operator">4.3. Assignment operator <code class="blue">=</code></a></li> <li><a href="#_assignment_operator">5.3. Assignment operator <code class="blue">=</code></a></li>
<li><a href="#_selector_operator">4.4. Selector operator <code class="blue">? : ::</code></a></li> <li><a href="#_selector_operator">5.4. Selector operator <code class="blue">? : ::</code></a></li>
</ul> </ul>
</li> </li>
<li><a href="#_priorities_of_operators">5. Priorities of operators</a></li> <li><a href="#_priorities_of_operators">6. Priorities of operators</a></li>
<li><a href="#_functions">6. Functions</a> <li><a href="#_functions">7. Functions</a>
<ul class="sectlevel2"> <ul class="sectlevel2">
<li><a href="#_function_calls">6.1. Function calls</a></li> <li><a href="#_function_calls">7.1. Function calls</a></li>
<li><a href="#_function_definitions">6.2. Function definitions</a></li> <li><a href="#_function_definitions">7.2. Function definitions</a></li>
</ul> </ul>
</li> </li>
<li><a href="#_builtins">7. Builtins</a> <li><a href="#_builtins">8. Builtins</a>
<ul class="sectlevel2"> <ul class="sectlevel2">
<li><a href="#_builtin_functions">7.1. Builtin functions</a></li> <li><a href="#_builtin_functions">8.1. Builtin functions</a></li>
<li><a href="#_import">7.2. <em class="blue">import()</em></a></li> <li><a href="#_import">8.2. <em class="blue">import()</em></a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
@@ -577,7 +579,7 @@ pre.rouge .ss {
<div class="sectionbody"> <div class="sectionbody">
<!-- toc disabled --> <!-- toc disabled -->
<div class="paragraph"> <div class="paragraph">
<p><mark>TODO: Work in progress (last update on 2024/05/07, 07:15 am)</mark></p> <p><mark>TODO: Work in progress (last update on 2024/05/10, 06:52 a.m.)</mark></p>
</div> </div>
</div> </div>
</div> </div>
@@ -588,33 +590,48 @@ pre.rouge .ss {
<p><em>Expr</em> is a GO package capable of analysing, interpreting and calculating expressions.</p> <p><em>Expr</em> is a GO package capable of analysing, interpreting and calculating expressions.</p>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_dev_expr_test_tool"><a class="anchor" href="#_dev_expr_test_tool"></a><a class="link" href="#_dev_expr_test_tool">1.1. <code>dev-expr</code> test tool</a></h3> <h3 id="_concepts_and_terminology"><a class="anchor" href="#_concepts_and_terminology"></a><a class="link" href="#_concepts_and_terminology">1.1. Concepts and terminology</a></h3>
<div class="paragraph">
<p><mark>TODO</mark></p>
</div>
<div class="imageblock">
<div class="content">
<img src="expression-diagram.png" alt="expression diagram">
</div>
</div>
</div>
<div class="sect2">
<h3 id="_dev_expr_test_tool"><a class="anchor" href="#_dev_expr_test_tool"></a><a class="link" href="#_dev_expr_test_tool">1.2. <code>dev-expr</code> test tool</a></h3>
<div class="paragraph"> <div class="paragraph">
<p><code>dev-expr</code> is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, beyond in additon to the automatic test suite based on the Go test framework, <code>dev-expr</code> provides an important aid for quickly testing of new features during their development.</p> <p><code>dev-expr</code> is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, beyond in additon to the automatic test suite based on the Go test framework, <code>dev-expr</code> provides an important aid for quickly testing of new features during their development.</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p>It cat work as a REPL, *R*ead-*E*xecute-*P*rint-*L*oop, or it can process expression acquired from files or standard input.</p> <p>It cat work as a <em>REPL</em>, <em><strong>R</strong>ead-<strong>E</strong>xecute-<strong>P</strong>rint-<strong>L</strong>oop</em>, or it can process expression acquired from files or standard input.</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p>The program in located in the <em>tools</em> directory. <p>The program can be downloaded from <a href="https://git.portale-stac.it/go-pkg/-/packages/generic/dev-expr/">dev-expr</a>.</p>
Here are some examples of execution.</p> </div>
<div class="paragraph">
<p>Here are some examples of execution.</p>
</div> </div>
<div class="listingblock"> <div class="listingblock">
<div class="title">Run <code>dev-expr</code> in REPL mode and ask for help</div> <div class="title">Run <code>dev-expr</code> in REPL mode and ask for help</div>
<div class="content"> <div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="c"># Assume the expr source directory. Type 'exit' or Ctrl+D to quit the program.</span> <pre class="rouge highlight"><code data-lang="shell"><span class="c"># Assume the expr source directory. Type 'exit' or Ctrl+D to quit the program.</span>
<span class="o">[</span>user]<span class="nv">$ </span>tools/expr <span class="nt">--</span> Expressions calculator v1.6.1,2024/05/06 <span class="o">(</span>celestino.amoroso@portale-stac.it<span class="o">)</span> <span class="o">[</span>user]<span class="nv">$ </span>tools/expr <span class="nt">--</span> Expressions calculator v1.7.0,2024/05/08 <span class="o">(</span>celestino.amoroso@portale-stac.it<span class="o">)</span>
Type <span class="nb">help </span>to get the list of command. Type <span class="nb">help </span>to get the list of command.
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
<span class="o">&gt;&gt;&gt;</span> <span class="nb">help</span> <span class="o">&gt;&gt;&gt;</span> <span class="nb">help</span>
<span class="nt">---</span> REPL commands: <span class="nt">---</span> REPL commands:
base <span class="nt">--</span> Set the integer output base: 2, 8, 10, or 16
<span class="nb">exit</span> <span class="nt">--</span> Exit the program
<span class="nb">help</span> <span class="nt">--</span> Show <span class="nb">command </span>list <span class="nb">help</span> <span class="nt">--</span> Show <span class="nb">command </span>list
ml <span class="nt">--</span> Enable/Disable multi-line output ml <span class="nt">--</span> Enable/Disable multi-line output
mods <span class="nt">--</span> List <span class="nb">builtin </span>modules mods <span class="nt">--</span> List <span class="nb">builtin </span>modules
<span class="nb">source</span> <span class="nt">--</span> Load a file as input
<span class="nb">tty</span> <span class="nt">--</span> Enable/Disable ansi output <i class="conum" data-value="1"></i><b>(1)</b> <span class="nb">tty</span> <span class="nt">--</span> Enable/Disable ansi output <i class="conum" data-value="1"></i><b>(1)</b>
<span class="nb">exit</span> <span class="nt">--</span> Exit the program
<span class="nt">---</span> Command line options: <span class="nt">---</span> Command line options:
<span class="nt">-b</span> &lt;<span class="nb">builtin</span><span class="o">&gt;</span> Import <span class="nb">builtin </span>modules. <span class="nt">-b</span> &lt;<span class="nb">builtin</span><span class="o">&gt;</span> Import <span class="nb">builtin </span>modules.
@@ -625,7 +642,9 @@ Here are some examples of execution.</p>
<span class="nt">-h</span>, <span class="nt">--help</span>, <span class="nb">help </span>Show this <span class="nb">help </span>menu <span class="nt">-h</span>, <span class="nt">--help</span>, <span class="nb">help </span>Show this <span class="nb">help </span>menu
<span class="nt">-m</span>, <span class="nt">--modules</span> List all <span class="nb">builtin </span>modules <span class="nt">-m</span>, <span class="nt">--modules</span> List all <span class="nb">builtin </span>modules
<span class="nt">-p</span> Print prefix form <span class="nt">-p</span> Print prefix form
<span class="nt">-t</span> Print tree form <i class="conum" data-value="2"></i><b>(2)</b></code></pre> <span class="nt">-t</span> Print tree form <i class="conum" data-value="2"></i><b>(2)</b>
<span class="o">&gt;&gt;&gt;</span></code></pre>
</div> </div>
</div> </div>
<div class="colist arabic"> <div class="colist arabic">
@@ -691,17 +710,6 @@ Here are some examples of execution.</p>
</table> </table>
</div> </div>
</div> </div>
<div class="sect2">
<h3 id="_concepts_and_terminology"><a class="anchor" href="#_concepts_and_terminology"></a><a class="link" href="#_concepts_and_terminology">1.2. Concepts and terminology</a></h3>
<div class="paragraph">
<p><mark>TODO</mark></p>
</div>
<div class="imageblock">
<div class="content">
<img src="expression-diagram.png" alt="expression diagram">
</div>
</div>
</div>
</div> </div>
</div> </div>
<div class="sect1"> <div class="sect1">
@@ -783,7 +791,45 @@ Here are some examples of execution.</p>
</table> </table>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_string"><a class="anchor" href="#_string"></a><a class="link" href="#_string">2.2. String</a></h3> <h3 id="_fractions"><a class="anchor" href="#_fractions"></a><a class="link" href="#_fractions">2.2. Fractions</a></h3>
<div class="paragraph">
<p><em>Expr</em> also supports fractions. Fraction literals are made with two integers separated by a vertical bar <code>|</code>.</p>
</div>
<div class="paragraph">
<div class="title">Examples</div>
<p><code>&gt;&gt;&gt;</code> <code class="blue">1 | 2</code><br>
<code class="green">1|2</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">4|6</code><br>
<code class="green">2|3</code> <em class="gray">Fractions are always reduced to their lowest terms</em><br>
<code>&gt;&gt;&gt;</code> <code class="blue">1|2 + 2|3</code><br>
<code class="green">7|6</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">1|2 * 2|3</code><br>
<code class="green">1|3</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">1|2 / 1|3</code><br>
<code class="green">3|2</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">1|2 ./ 1|3</code> <em class="gray">Force decimal division</em><br>
<code class="green">1.5</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">-1|2</code><br>
<code class="green">-1|2</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">1|-2</code> <em class="gray">Wrong sign specification</em><br>
<em class="red">Eval Error: [1:3] infix operator "|" requires two non-nil operands, got 1</em><br>
<code>&gt;&gt;&gt;</code> <code class="blue">1|(-2)</code><br>
<code class="green">-1|2</code></p>
</div>
<div class="paragraph">
<p>Fractions can be used together with integers and floats in expressions.</p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">1|2 + 5</code><br>
<code class="green">11|2</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">4 - 1|2</code><br>
<code class="green">7|2</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">1.0 + 1|2</code><br>
<code class="green">1.5</code><br></p>
</div>
</div>
<div class="sect2">
<h3 id="_strings"><a class="anchor" href="#_strings"></a><a class="link" href="#_strings">2.3. Strings</a></h3>
<div class="paragraph"> <div class="paragraph">
<p>Strings are character sequences enclosed between two double quote <code class="blue">"</code>. Example: <code class="blue">"I&#8217;m a string"</code>.</p> <p>Strings are character sequences enclosed between two double quote <code class="blue">"</code>. Example: <code class="blue">"I&#8217;m a string"</code>.</p>
</div> </div>
@@ -824,7 +870,7 @@ Here are some examples of execution.</p>
</table> </table>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_boolean"><a class="anchor" href="#_boolean"></a><a class="link" href="#_boolean">2.3. Boolean</a></h3> <h3 id="_boolean"><a class="anchor" href="#_boolean"></a><a class="link" href="#_boolean">2.4. Boolean</a></h3>
<div class="paragraph"> <div class="paragraph">
<p>Boolean data type has two values only: <em>true</em> and <em>false</em>. Relational and Boolean expressions produce Boolean values.</p> <p>Boolean data type has two values only: <em>true</em> and <em>false</em>. Relational and Boolean expressions produce Boolean values.</p>
</div> </div>
@@ -959,7 +1005,7 @@ Here are some examples of execution.</p>
</div> </div>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_list"><a class="anchor" href="#_list"></a><a class="link" href="#_list">2.4. List</a></h3> <h3 id="_lists"><a class="anchor" href="#_lists"></a><a class="link" href="#_lists">2.5. Lists</a></h3>
<div class="paragraph"> <div class="paragraph">
<p><em>Expr</em> supports list of mixed-type values, also specified by normal expressions.</p> <p><em>Expr</em> supports list of mixed-type values, also specified by normal expressions.</p>
</div> </div>
@@ -1004,11 +1050,66 @@ Here are some examples of execution.</p>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div class="paragraph">
<p>The items of array can be accessed using the dot <code>.</code> operator.</p>
</div>
<div class="listingblock">
<div class="title">Item access syntax</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="bnf">&lt;item&gt; ::= &lt;list-expr&gt;"."&lt;index-expr&gt;</code></pre>
</div>
</div>
<div class="listingblock">
<div class="title">Items of list</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="go"><span class="s">`&gt;&gt;&gt;`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`[1,2,3].1`</span>
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`2`</span>
<span class="s">`&gt;&gt;&gt;`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`list=[1,2,3]; list.1`</span>
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`2`</span>
<span class="s">`&gt;&gt;&gt;`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`["one","two","three"].1`</span>
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`two`</span>
<span class="s">`&gt;&gt;&gt;`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`list=["one","two","three"]; list.(2-1)`</span>
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`two`</span>
<span class="s">`&gt;&gt;&gt;`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`list.(-1)`</span>
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`three`</span>
<span class="s">`&gt;&gt;&gt;`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`list.(-1)`</span>
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`three`</span>
<span class="s">`&gt;&gt;&gt;`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`list.(10)`</span></code></pre>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="sect1"> <div class="sect1">
<h2 id="_variables"><a class="anchor" href="#_variables"></a><a class="link" href="#_variables">3. Variables</a></h2> <h2 id="_dictionaries"><a class="anchor" href="#_dictionaries"></a><a class="link" href="#_dictionaries">3. Dictionaries</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>The <em>dictionary</em> data-type is set of pairs <em>key/value</em>. It is also known as <em>map</em> or <em>associative array</em>. Dictionary literals are sequences of pairs separated by comma <code>,</code>; sequences are enclosed between brace brackets.</p>
</div>
<div class="listingblock">
<div class="title">List examples</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="go"><span class="p">{</span><span class="m">1</span><span class="o">:</span><span class="s">"one"</span><span class="p">,</span> <span class="m">2</span><span class="o">:</span><span class="s">"two"</span><span class="p">}</span>
<span class="p">{</span><span class="s">"one"</span><span class="o">:</span><span class="m">1</span><span class="p">,</span> <span class="s">"two"</span><span class="o">:</span> <span class="m">2</span><span class="p">}</span>
<span class="p">{</span><span class="s">"sum"</span><span class="o">:</span><span class="m">1</span><span class="o">+</span><span class="m">2</span><span class="o">+</span><span class="m">3</span><span class="p">,</span> <span class="s">"prod"</span><span class="o">:</span><span class="m">1</span><span class="o">*</span><span class="m">2</span><span class="o">*</span><span class="m">3</span><span class="p">}</span></code></pre>
</div>
</div>
<div class="admonitionblock warning">
<table>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
Support for dictionaries is still ongoing.
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_variables"><a class="anchor" href="#_variables"></a><a class="link" href="#_variables">4. Variables</a></h2>
<div class="sectionbody"> <div class="sectionbody">
<div class="paragraph"> <div class="paragraph">
<p>A variable is an identifier with an assigned value. Variables are stored in the object that implements the <em>ExprContext</em> interface.</p> <p>A variable is an identifier with an assigned value. Variables are stored in the object that implements the <em>ExprContext</em> interface.</p>
@@ -1024,10 +1125,10 @@ Here are some examples of execution.</p>
</div> </div>
</div> </div>
<div class="sect1"> <div class="sect1">
<h2 id="_other_operations"><a class="anchor" href="#_other_operations"></a><a class="link" href="#_other_operations">4. Other operations</a></h2> <h2 id="_other_operations"><a class="anchor" href="#_other_operations"></a><a class="link" href="#_other_operations">5. Other operations</a></h2>
<div class="sectionbody"> <div class="sectionbody">
<div class="sect2"> <div class="sect2">
<h3 id="_operator"><a class="anchor" href="#_operator"></a><a class="link" href="#_operator">4.1. <code class="blue">;</code> operator</a></h3> <h3 id="_operator"><a class="anchor" href="#_operator"></a><a class="link" href="#_operator">5.1. <code class="blue">;</code> operator</a></h3>
<div class="paragraph"> <div class="paragraph">
<p>The semicolon operator <code class="blue">;</code> is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The latter is the final result.</p> <p>The semicolon operator <code class="blue">;</code> is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The latter is the final result.</p>
</div> </div>
@@ -1063,7 +1164,7 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
</div> </div>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_but_operator"><a class="anchor" href="#_but_operator"></a><a class="link" href="#_but_operator">4.2. <code class="blue">but</code> operator</a></h3> <h3 id="_but_operator"><a class="anchor" href="#_but_operator"></a><a class="link" href="#_but_operator">5.2. <code class="blue">but</code> operator</a></h3>
<div class="paragraph"> <div class="paragraph">
<p><code class="blue">but</code> is an infixed operator. Its operands can be expressions of any type. It evaluates the left expression first, then the right expression. The value of the right expression is the final result. Examples: <code class="blue">5 but 2</code> returns 2, <code class="blue">x=2*3 but x-1</code> returns 5.</p> <p><code class="blue">but</code> is an infixed operator. Its operands can be expressions of any type. It evaluates the left expression first, then the right expression. The value of the right expression is the final result. Examples: <code class="blue">5 but 2</code> returns 2, <code class="blue">x=2*3 but x-1</code> returns 5.</p>
</div> </div>
@@ -1072,7 +1173,7 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
</div> </div>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_assignment_operator"><a class="anchor" href="#_assignment_operator"></a><a class="link" href="#_assignment_operator">4.3. Assignment operator <code class="blue">=</code></a></h3> <h3 id="_assignment_operator"><a class="anchor" href="#_assignment_operator"></a><a class="link" href="#_assignment_operator">5.3. Assignment operator <code class="blue">=</code></a></h3>
<div class="paragraph"> <div class="paragraph">
<p>The assignment operator <code class="blue">=</code> is used to define variables in the evaluation context or to change their value (see <em>ExprContext</em>). <p>The assignment operator <code class="blue">=</code> is used to define variables in the evaluation context or to change their value (see <em>ExprContext</em>).
The value on the left side of <code class="blue">=</code> must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation.</p> The value on the left side of <code class="blue">=</code> must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation.</p>
@@ -1085,7 +1186,7 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
</div> </div>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_selector_operator"><a class="anchor" href="#_selector_operator"></a><a class="link" href="#_selector_operator">4.4. Selector operator <code class="blue">? : ::</code></a></h3> <h3 id="_selector_operator"><a class="anchor" href="#_selector_operator"></a><a class="link" href="#_selector_operator">5.4. Selector operator <code class="blue">? : ::</code></a></h3>
<div class="paragraph"> <div class="paragraph">
<p>The <em>selector operator</em> is very similar to the <em>switch/case/default</em> statement available in many programming languages.</p> <p>The <em>selector operator</em> is very similar to the <em>switch/case/default</em> statement available in many programming languages.</p>
</div> </div>
@@ -1125,7 +1226,7 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
</div> </div>
</div> </div>
<div class="sect1"> <div class="sect1">
<h2 id="_priorities_of_operators"><a class="anchor" href="#_priorities_of_operators"></a><a class="link" href="#_priorities_of_operators">5. Priorities of operators</a></h2> <h2 id="_priorities_of_operators"><a class="anchor" href="#_priorities_of_operators"></a><a class="link" href="#_priorities_of_operators">6. Priorities of operators</a></h2>
<div class="sectionbody"> <div class="sectionbody">
<div class="paragraph"> <div class="paragraph">
<p>The table below shows all supported operators by decreasing priorities.</p> <p>The table below shows all supported operators by decreasing priorities.</p>
@@ -1346,7 +1447,7 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
</div> </div>
</div> </div>
<div class="sect1"> <div class="sect1">
<h2 id="_functions"><a class="anchor" href="#_functions"></a><a class="link" href="#_functions">6. Functions</a></h2> <h2 id="_functions"><a class="anchor" href="#_functions"></a><a class="link" href="#_functions">7. Functions</a></h2>
<div class="sectionbody"> <div class="sectionbody">
<div class="paragraph"> <div class="paragraph">
<p>Functions in <em>Expr</em> are very similar to functions in many programming languages.</p> <p>Functions in <em>Expr</em> are very similar to functions in many programming languages.</p>
@@ -1355,13 +1456,13 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
<p>In <em>Expr</em> functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the reference operator <code class="blue">@</code> it is possibile to export local definition to the calling context.</p> <p>In <em>Expr</em> functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the reference operator <code class="blue">@</code> it is possibile to export local definition to the calling context.</p>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_function_calls"><a class="anchor" href="#_function_calls"></a><a class="link" href="#_function_calls">6.1. Function calls</a></h3> <h3 id="_function_calls"><a class="anchor" href="#_function_calls"></a><a class="link" href="#_function_calls">7.1. Function calls</a></h3>
<div class="paragraph"> <div class="paragraph">
<p><mark>TODO: function calls operations</mark></p> <p><mark>TODO: function calls operations</mark></p>
</div> </div>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_function_definitions"><a class="anchor" href="#_function_definitions"></a><a class="link" href="#_function_definitions">6.2. Function definitions</a></h3> <h3 id="_function_definitions"><a class="anchor" href="#_function_definitions"></a><a class="link" href="#_function_definitions">7.2. Function definitions</a></h3>
<div class="paragraph"> <div class="paragraph">
<p><mark>TODO: function definitions operations</mark></p> <p><mark>TODO: function definitions operations</mark></p>
</div> </div>
@@ -1369,17 +1470,17 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
</div> </div>
</div> </div>
<div class="sect1"> <div class="sect1">
<h2 id="_builtins"><a class="anchor" href="#_builtins"></a><a class="link" href="#_builtins">7. Builtins</a></h2> <h2 id="_builtins"><a class="anchor" href="#_builtins"></a><a class="link" href="#_builtins">8. Builtins</a></h2>
<div class="sectionbody"> <div class="sectionbody">
<div class="paragraph"> <div class="paragraph">
<p><mark>TODO: builtins</mark></p> <p><mark>TODO: builtins</mark></p>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_builtin_functions"><a class="anchor" href="#_builtin_functions"></a><a class="link" href="#_builtin_functions">7.1. Builtin functions</a></h3> <h3 id="_builtin_functions"><a class="anchor" href="#_builtin_functions"></a><a class="link" href="#_builtin_functions">8.1. Builtin functions</a></h3>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_import"><a class="anchor" href="#_import"></a><a class="link" href="#_import">7.2. <em class="blue">import()</em></a></h3> <h3 id="_import"><a class="anchor" href="#_import"></a><a class="link" href="#_import">8.2. <em class="blue">import()</em></a></h3>
<div class="paragraph"> <div class="paragraph">
<p><em class="blue">import(<span class="grey">&lt;source-file&gt;</span>)</em> loads the multi-expression contained in the specified source and returns its value.</p> <p><em class="blue">import(<span class="grey">&lt;source-file&gt;</span>)</em> loads the multi-expression contained in the specified source and returns its value.</p>
</div> </div>
@@ -1389,7 +1490,7 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
</div> </div>
<div id="footer"> <div id="footer">
<div id="footer-text"> <div id="footer-text">
Last updated 2024-05-07 07:20:42 +0200 Last updated 2024-05-10 06:38:03 +0200
</div> </div>
</div> </div>
</body> </body>
+4
View File
@@ -9,6 +9,10 @@ type FmtOpt uint16
const ( const (
TTY FmtOpt = 1 << iota TTY FmtOpt = 1 << iota
MultiLine MultiLine
Base2
Base8
Base10
Base16
) )
type Formatter interface { type Formatter interface {
+47 -2
View File
@@ -18,6 +18,42 @@ func isNilFunc(ctx ExprContext, name string, args []any) (result any, err error)
return return
} }
func isIntFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsInteger(args[0])
return
}
func isFloatFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsFloat(args[0])
return
}
func isBoolFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsBool(args[0])
return
}
func isStringFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsString(args[0])
return
}
func isFractionFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsFract(args[0])
return
}
func isListFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsList(args[0])
return
}
func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = IsDict(args[0])
return
}
func intFunc(ctx ExprContext, name string, args []any) (result any, err error) { func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
if len(args) == 1 { if len(args) == 1 {
switch v := args[0].(type) { switch v := args[0].(type) {
@@ -50,8 +86,17 @@ func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err err
} }
func ImportBuiltinsFuncs(ctx ExprContext) { func ImportBuiltinsFuncs(ctx ExprContext) {
ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 1, -1) ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 1, 1)
ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 1, -1) ctx.RegisterFunc("isInt", &simpleFunctor{f: isIntFunc}, 1, 1)
ctx.RegisterFunc("isFloat", &simpleFunctor{f: isFloatFunc}, 1, 1)
ctx.RegisterFunc("isBool", &simpleFunctor{f: isBoolFunc}, 1, 1)
ctx.RegisterFunc("isString", &simpleFunctor{f: isStringFunc}, 1, 1)
ctx.RegisterFunc("isFraction", &simpleFunctor{f: isFractionFunc}, 1, 1)
ctx.RegisterFunc("isFract", &simpleFunctor{f: isFractionFunc}, 1, 1)
ctx.RegisterFunc("isList", &simpleFunctor{f: isListFunc}, 1, 1)
ctx.RegisterFunc("isDictionary", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
ctx.RegisterFunc("isDict", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 1, 1)
} }
func init() { func init() {
+1 -1
View File
@@ -35,7 +35,7 @@ func importGeneral(ctx ExprContext, name string, args []any) (result any, err er
} }
func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) { func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) {
if !(isString(paramValue) /*|| isList(paramValue)*/) { if !(IsString(paramValue) /*|| isList(paramValue)*/) {
err = fmt.Errorf("%s(): param nr %d has wrong type %T, string expected", funcName, paramPos+1, paramValue) err = fmt.Errorf("%s(): param nr %d has wrong type %T, string expected", funcName, paramPos+1, paramValue)
} }
return return
+3 -3
View File
@@ -10,7 +10,7 @@ import (
) )
func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) { func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) {
if !(isNumber(paramValue) || isFraction(paramValue)) /*|| isList(paramValue)*/ { if !(IsNumber(paramValue) || isFraction(paramValue)) /*|| isList(paramValue)*/ {
err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected", err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected",
funcName, paramPos+1, subPos+1, level, paramValue) funcName, paramPos+1, subPos+1, level, paramValue)
} }
@@ -45,7 +45,7 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
count++ count++
if !sumAsFloat { if !sumAsFloat {
if isFloat(v) { if IsFloat(v) {
sumAsFloat = true sumAsFloat = true
if sumAsFract { if sumAsFract {
floatSum = fractSum.toFloat() floatSum = fractSum.toFloat()
@@ -120,7 +120,7 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
count++ count++
if !mulAsFloat { if !mulAsFloat {
if isFloat(v) { if IsFloat(v) {
mulAsFloat = true mulAsFloat = true
if mulAsFract { if mulAsFract {
floatProd = fractProd.toFloat() floatProd = fractProd.toFloat()
+10 -4
View File
@@ -20,7 +20,7 @@ func TestFuncs(t *testing.T) {
/* 7 */ {`int(3.9)`, int64(3), nil}, /* 7 */ {`int(3.9)`, int64(3), nil},
/* 8 */ {`int("432")`, int64(432), nil}, /* 8 */ {`int("432")`, int64(432), nil},
/* 9 */ {`int("1.5")`, nil, errors.New(`strconv.Atoi: parsing "1.5": invalid syntax`)}, /* 9 */ {`int("1.5")`, nil, errors.New(`strconv.Atoi: parsing "1.5": invalid syntax`)},
/* 10 */ {`int("432", 4)`, nil, errors.New(`int() requires exactly one param`)}, /* 10 */ {`int("432", 4)`, nil, errors.New(`too much params -- expected 1, got 2`)},
/* 11 */ {`int(nil)`, nil, errors.New(`int() can't convert <nil> to int`)}, /* 11 */ {`int(nil)`, nil, errors.New(`int() can't convert <nil> to int`)},
/* 12 */ {`two=func(){2}; two()`, int64(2), nil}, /* 12 */ {`two=func(){2}; two()`, int64(2), nil},
/* 13 */ {`double=func(x) {2*x}; (double(3))`, int64(6), nil}, /* 13 */ {`double=func(x) {2*x}; (double(3))`, int64(6), nil},
@@ -54,9 +54,15 @@ func TestFuncs(t *testing.T) {
/* 41 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "789")`, true, nil}, /* 41 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "789")`, true, nil},
/* 42 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "0125")`, false, nil}, /* 42 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "0125")`, false, nil},
/* 43 */ {`builtin "string"; endsWithStr("0123456789")`, nil, errors.New(`too few params -- expected 2 or more, got 1`)}, /* 43 */ {`builtin "string"; endsWithStr("0123456789")`, nil, errors.New(`too few params -- expected 2 or more, got 1`)},
/* 44 */ {`builtin "string"; splitStr("one-two-three", "-", )`, newListA("one","two","three"), nil}, /* 44 */ {`builtin "string"; splitStr("one-two-three", "-", )`, newListA("one", "two", "three"), nil},
/* 45 */ {`["a", "b", "c"]`, newListA("a","b","c"), nil}, /* 45 */ {`isInt(2+1)`, true, nil},
/* 46 */ {`["a", "b", "c"]`, newList([]any{"a","b","c"}), nil}, /* 46 */ {`isInt(3.1)`, false, nil},
/* 46 */ {`isFloat(3.1)`, true, nil},
/* 47 */ {`isString("3.1")`, true, nil},
/* 48 */ {`isString("3" + 1)`, true, nil},
/* 49 */ {`isList(["3", 1])`, true, nil},
/* 50 */ {`isDict({"a":"3", "b":1})`, true, nil},
/* 51 */ {`isFract(3|1)`, true, nil},
} }
t.Setenv("EXPR_PATH", ".") t.Setenv("EXPR_PATH", ".")
+19 -16
View File
@@ -20,22 +20,25 @@ func TestListParser(t *testing.T) {
} }
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`[]`, []any{}, nil}, /* 1 */ {`[]`, []any{}, nil},
/* 2 */ {`[1,2,3]`, []any{int64(1), int64(2), int64(3)}, nil}, /* 2 */ {`[1,2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
/* 3 */ {`[1,2,"hello"]`, []any{int64(1), int64(2), "hello"}, nil}, /* 3 */ {`[1,2,"hello"]`, []any{int64(1), int64(2), "hello"}, nil},
/* 4 */ {`[1+2, not true, "hello"]`, []any{int64(3), false, "hello"}, nil}, /* 4 */ {`[1+2, not true, "hello"]`, []any{int64(3), false, "hello"}, nil},
/* 5 */ {`[1,2]+[3]`, []any{int64(1), int64(2), int64(3)}, nil}, /* 5 */ {`[1,2]+[3]`, []any{int64(1), int64(2), int64(3)}, nil},
/* 6 */ {`[1,4,3,2]-[3]`, []any{int64(1), int64(4), int64(2)}, nil}, /* 6 */ {`[1,4,3,2]-[3]`, []any{int64(1), int64(4), int64(2)}, nil},
/* 7 */ {`add([1,4,3,2])`, int64(10), nil}, /* 7 */ {`add([1,4,3,2])`, int64(10), nil},
/* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil}, /* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil},
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil}, /* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
/* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 (2 in 1) has wrong type string, number expected`)}, /* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 (2 in 1) has wrong type string, number expected`)},
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil}, /* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
/* 12 */ {`[1,2,3] << 2+2`, []any{int64(1), int64(2), int64(3), int64(4)}, nil}, /* 12 */ {`[1,2,3] << 2+2`, []any{int64(1), int64(2), int64(3), int64(4)}, nil},
/* 13 */ {`2-1 >> [2,3]`, []any{int64(1), int64(2), int64(3)}, nil}, /* 13 */ {`2-1 >> [2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
/* 14 */ {`[1,2,3].1`, int64(2), nil}, /* 14 */ {`[1,2,3].1`, int64(2), nil},
/* 15 */ {`ls=[1,2,3] but ls.1`, int64(2), nil}, /* 15 */ {`ls=[1,2,3] but ls.1`, int64(2), nil},
/* 16 */ {`ls=[1,2,3] but ls.(-1)`, int64(3), nil}, /* 16 */ {`ls=[1,2,3] but ls.(-1)`, int64(3), nil},
/* 17 */ {`list=["one","two","three"]; list.10`, nil, errors.New(`[1:36] index 10 out of bounds`)},
/* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil},
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
// /* 8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil}, // /* 8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil},
// /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil}, // /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
+1 -1
View File
@@ -26,7 +26,7 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
} }
count := 0 count := 0
if isString(childValue) { if IsString(childValue) {
module, _ := childValue.(string) module, _ := childValue.(string)
count, err = ImportInContextByGlobPattern(ctx, module) count, err = ImportInContextByGlobPattern(ctx, module)
} else { } else {
+4 -3
View File
@@ -22,10 +22,11 @@ func verifyIndex(ctx ExprContext, indexTerm *term, maxValue int) (index int, err
var indexValue any var indexValue any
if indexValue, err = indexTerm.compute(ctx); err == nil { if indexValue, err = indexTerm.compute(ctx); err == nil {
if v, err = indexTerm.toInt(indexValue, "index expression value must be integer"); err == nil { if v, err = indexTerm.toInt(indexValue, "index expression value must be integer"); err == nil {
if v < 0 && v >= -maxValue {
v = maxValue + v
}
if v >= 0 && v < maxValue { if v >= 0 && v < maxValue {
index = v index = v
} else if index >= -maxValue {
index = maxValue + v
} else { } else {
err = indexTerm.Errorf("index %d out of bounds", v) err = indexTerm.Errorf("index %d out of bounds", v)
} }
@@ -56,7 +57,7 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
case string: case string:
var index int var index int
if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil { if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil {
v = unboxedValue[index] v = string(unboxedValue[index])
} }
case map[any]any: case map[any]any:
var ok bool var ok bool
+1 -1
View File
@@ -25,7 +25,7 @@ func evalFact(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if isInteger(leftValue) { if IsInteger(leftValue) {
if i, _ := leftValue.(int64); i >= 0 { if i, _ := leftValue.(int64); i >= 0 {
f := int64(1) f := int64(1)
for k := int64(1); k <= i; k++ { for k := int64(1); k <= i; k++ {
+2 -2
View File
@@ -24,7 +24,7 @@ func evalInclude(ctx ExprContext, self *term) (v any, err error) {
} }
count := 0 count := 0
if isList(childValue) { if IsList(childValue) {
list, _ := childValue.([]any) list, _ := childValue.([]any)
for i, filePathSpec := range list { for i, filePathSpec := range list {
if filePath, ok := filePathSpec.(string); ok { if filePath, ok := filePathSpec.(string); ok {
@@ -39,7 +39,7 @@ func evalInclude(ctx ExprContext, self *term) (v any, err error) {
break break
} }
} }
} else if isString(childValue) { } else if IsString(childValue) {
filePath, _ := childValue.(string) filePath, _ := childValue.(string)
v, err = EvalFile(ctx, filePath) v, err = EvalFile(ctx, filePath)
} else { } else {
+2 -2
View File
@@ -33,7 +33,7 @@ func evalInsert(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if isList(rightValue) { if IsList(rightValue) {
list, _ := rightValue.(*ListType) list, _ := rightValue.(*ListType)
newList := append(ListType{leftValue}, *list...) newList := append(ListType{leftValue}, *list...)
v = &newList v = &newList
@@ -50,7 +50,7 @@ func evalAppend(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if isList(leftValue) { if IsList(leftValue) {
list, _ := leftValue.(*ListType) list, _ := leftValue.(*ListType)
newList := append(*list, rightValue) newList := append(*list, rightValue)
v = &newList v = &newList
+2 -2
View File
@@ -23,10 +23,10 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if isList(childValue) { if IsList(childValue) {
list, _ := childValue.([]any) list, _ := childValue.([]any)
v = int64(len(list)) v = int64(len(list))
} else if isString(childValue) { } else if IsString(childValue) {
s, _ := childValue.(string) s, _ := childValue.(string)
v = int64(len(s)) v = int64(len(s))
} else if it, ok := childValue.(Iterator); ok { } else if it, ok := childValue.(Iterator); ok {
+1 -1
View File
@@ -25,7 +25,7 @@ func evalPostInc(ctx ExprContext, self *term) (v any, err error) {
if it, ok := childValue.(Iterator); ok { if it, ok := childValue.(Iterator); ok {
v, err = it.Next() v, err = it.Next()
} else if isInteger(childValue) && self.children[0].symbol() == SymIdentifier { } else if IsInteger(childValue) && self.children[0].symbol() == SymIdentifier {
v = childValue v = childValue
i, _ := childValue.(int64) i, _ := childValue.(int64)
ctx.SetVar(self.children[0].source(), i+1) ctx.SetVar(self.children[0].source(), i+1)
+4 -4
View File
@@ -30,12 +30,12 @@ func evalMultiply(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if isString(leftValue) && isInteger(rightValue) { if IsString(leftValue) && IsInteger(rightValue) {
s, _ := leftValue.(string) s, _ := leftValue.(string)
n, _ := rightValue.(int64) n, _ := rightValue.(int64)
v = strings.Repeat(s, int(n)) v = strings.Repeat(s, int(n))
} else if isNumOrFract(leftValue) && isNumOrFract(rightValue) { } else if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
if isFloat(leftValue) || isFloat(rightValue) { if IsFloat(leftValue) || IsFloat(rightValue) {
v = numAsFloat(leftValue) * numAsFloat(rightValue) v = numAsFloat(leftValue) * numAsFloat(rightValue)
} else if isFraction(leftValue) || isFraction(rightValue) { } else if isFraction(leftValue) || isFraction(rightValue) {
v, err = mulAnyFract(leftValue, rightValue) v, err = mulAnyFract(leftValue, rightValue)
@@ -72,7 +72,7 @@ func evalDivide(ctx ExprContext, self *term) (v any, err error) {
} }
if isNumOrFract(leftValue) && isNumOrFract(rightValue) { if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
if isFloat(leftValue) || isFloat(rightValue) { if IsFloat(leftValue) || IsFloat(rightValue) {
d := numAsFloat(rightValue) d := numAsFloat(rightValue)
if d == 0.0 { if d == 0.0 {
err = errors.New("division by zero") err = errors.New("division by zero")
@@ -148,7 +148,7 @@ func evalReminder(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if isInteger(leftValue) && isInteger(rightValue) { if IsInteger(leftValue) && IsInteger(rightValue) {
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
if rightInt == 0 { if rightInt == 0 {
err = errors.New("division by zero") err = errors.New("division by zero")
+9 -9
View File
@@ -25,15 +25,15 @@ func evalEqual(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if isNumber(leftValue) && isNumber(rightValue) { if IsNumber(leftValue) && IsNumber(rightValue) {
if isInteger(leftValue) && isInteger(rightValue) { if IsInteger(leftValue) && IsInteger(rightValue) {
li, _ := leftValue.(int64) li, _ := leftValue.(int64)
ri, _ := rightValue.(int64) ri, _ := rightValue.(int64)
v = li == ri v = li == ri
} else { } else {
v = numAsFloat(leftValue) == numAsFloat(rightValue) v = numAsFloat(leftValue) == numAsFloat(rightValue)
} }
} else if isString(leftValue) && isString(rightValue) { } else if IsString(leftValue) && IsString(rightValue) {
ls, _ := leftValue.(string) ls, _ := leftValue.(string)
rs, _ := rightValue.(string) rs, _ := rightValue.(string)
v = ls == rs v = ls == rs
@@ -111,15 +111,15 @@ func evalLess(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if isNumber(leftValue) && isNumber(rightValue) { if IsNumber(leftValue) && IsNumber(rightValue) {
if isInteger(leftValue) && isInteger(rightValue) { if IsInteger(leftValue) && IsInteger(rightValue) {
li, _ := leftValue.(int64) li, _ := leftValue.(int64)
ri, _ := rightValue.(int64) ri, _ := rightValue.(int64)
v = li < ri v = li < ri
} else { } else {
v = numAsFloat(leftValue) < numAsFloat(rightValue) v = numAsFloat(leftValue) < numAsFloat(rightValue)
} }
} else if isString(leftValue) && isString(rightValue) { } else if IsString(leftValue) && IsString(rightValue) {
ls, _ := leftValue.(string) ls, _ := leftValue.(string)
rs, _ := rightValue.(string) rs, _ := rightValue.(string)
v = ls < rs v = ls < rs
@@ -148,15 +148,15 @@ func evalLessEqual(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if isNumber(leftValue) && isNumber(rightValue) { if IsNumber(leftValue) && IsNumber(rightValue) {
if isInteger(leftValue) && isInteger(rightValue) { if IsInteger(leftValue) && IsInteger(rightValue) {
li, _ := leftValue.(int64) li, _ := leftValue.(int64)
ri, _ := rightValue.(int64) ri, _ := rightValue.(int64)
v = li <= ri v = li <= ri
} else { } else {
v = numAsFloat(leftValue) <= numAsFloat(rightValue) v = numAsFloat(leftValue) <= numAsFloat(rightValue)
} }
} else if isString(leftValue) && isString(rightValue) { } else if IsString(leftValue) && IsString(rightValue) {
ls, _ := leftValue.(string) ls, _ := leftValue.(string)
rs, _ := rightValue.(string) rs, _ := rightValue.(string)
v = ls <= rs v = ls <= rs
+2 -2
View File
@@ -35,14 +35,14 @@ func evalSign(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if isFloat(rightValue) { if IsFloat(rightValue) {
if self.tk.Sym == SymChangeSign { if self.tk.Sym == SymChangeSign {
f, _ := rightValue.(float64) f, _ := rightValue.(float64)
v = -f v = -f
} else { } else {
v = rightValue v = rightValue
} }
} else if isInteger(rightValue) { } else if IsInteger(rightValue) {
if self.tk.Sym == SymChangeSign { if self.tk.Sym == SymChangeSign {
i, _ := rightValue.(int64) i, _ := rightValue.(int64)
v = -i v = -i
+7 -7
View File
@@ -28,17 +28,17 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if (isString(leftValue) && isNumberString(rightValue)) || (isString(rightValue) && isNumberString(leftValue)) { if (IsString(leftValue) && isNumberString(rightValue)) || (IsString(rightValue) && isNumberString(leftValue)) {
v = fmt.Sprintf("%v%v", leftValue, rightValue) v = fmt.Sprintf("%v%v", leftValue, rightValue)
} else if isNumber(leftValue) && isNumber(rightValue) { } else if IsNumber(leftValue) && IsNumber(rightValue) {
if isFloat(leftValue) || isFloat(rightValue) { if IsFloat(leftValue) || IsFloat(rightValue) {
v = numAsFloat(leftValue) + numAsFloat(rightValue) v = numAsFloat(leftValue) + numAsFloat(rightValue)
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
v = leftInt + rightInt v = leftInt + rightInt
} }
} else if isList(leftValue) || isList(rightValue) { } else if IsList(leftValue) || IsList(rightValue) {
var leftList, rightList *ListType var leftList, rightList *ListType
var ok bool var ok bool
if leftList, ok = leftValue.(*ListType); !ok { if leftList, ok = leftValue.(*ListType); !ok {
@@ -56,7 +56,7 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
} }
v = &sumList v = &sumList
} else if isFraction(leftValue) || isFraction(rightValue) { } else if isFraction(leftValue) || isFraction(rightValue) {
if isFloat(leftValue) || isFloat(rightValue) { if IsFloat(leftValue) || IsFloat(rightValue) {
v = numAsFloat(leftValue) + numAsFloat(rightValue) v = numAsFloat(leftValue) + numAsFloat(rightValue)
} else { } else {
v, err = sumAnyFract(leftValue, rightValue) v, err = sumAnyFract(leftValue, rightValue)
@@ -87,7 +87,7 @@ func evalMinus(ctx ExprContext, self *term) (v any, err error) {
} }
if isNumOrFract(leftValue) && isNumOrFract(rightValue) { if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
if isFloat(leftValue) || isFloat(rightValue) { if IsFloat(leftValue) || IsFloat(rightValue) {
v = numAsFloat(leftValue) - numAsFloat(rightValue) v = numAsFloat(leftValue) - numAsFloat(rightValue)
} else if isFraction(leftValue) || isFraction(rightValue) { } else if isFraction(leftValue) || isFraction(rightValue) {
v, err = subAnyFract(leftValue, rightValue) v, err = subAnyFract(leftValue, rightValue)
@@ -96,7 +96,7 @@ func evalMinus(ctx ExprContext, self *term) (v any, err error) {
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
v = leftInt - rightInt v = leftInt - rightInt
} }
} else if isList(leftValue) && isList(rightValue) { } else if IsList(leftValue) && IsList(rightValue) {
leftList, _ := leftValue.(*ListType) leftList, _ := leftValue.(*ListType)
rightList, _ := rightValue.(*ListType) rightList, _ := rightValue.(*ListType)
diffList := make(ListType, 0, len(*leftList)-len(*rightList)) diffList := make(ListType, 0, len(*leftList)-len(*rightList))
+5 -5
View File
@@ -35,10 +35,10 @@ func TestGeneralParser(t *testing.T) {
/* 14 */ {`not true`, false, nil}, /* 14 */ {`not true`, false, nil},
/* 15 */ {`true and false`, false, nil}, /* 15 */ {`true and false`, false, nil},
/* 16 */ {`true or false`, true, nil}, /* 16 */ {`true or false`, true, nil},
/* 17 */ {`"uno" + "due"`, `unodue`, nil}, /* *17 */ {`"uno" + "due"`, `unodue`, nil},
/* 18 */ {`"uno" + 2`, `uno2`, nil}, /* *18 */ {`"uno" + 2`, `uno2`, nil},
/* 19 */ {`"uno" + (2+1)`, `uno3`, nil}, /* *19 */ {`"uno" + (2+1)`, `uno3`, nil},
/* 20 */ {`"uno" * (2+1)`, `unounouno`, nil}, /* *20 */ {`"uno" * (2+1)`, `unounouno`, nil},
/* 21 */ {"1", int64(1), nil}, /* 21 */ {"1", int64(1), nil},
/* 22 */ {"1.5", float64(1.5), nil}, /* 22 */ {"1.5", float64(1.5), nil},
/* 23 */ {"1.5*2", float64(3.0), nil}, /* 23 */ {"1.5*2", float64(3.0), nil},
@@ -177,7 +177,7 @@ func parserTest(t *testing.T, section string, inputs []inputType) {
ctx := NewSimpleFuncStore() ctx := NewSimpleFuncStore()
parser := NewParser(ctx) parser := NewParser(ctx)
logTest(t, i+1, "Iterator", input.source, input.wantResult, input.wantErr) logTest(t, i+1, section, input.source, input.wantResult, input.wantErr)
r := strings.NewReader(input.source) r := strings.NewReader(input.source)
scanner := NewScanner(r, DefaultTranslations()) scanner := NewScanner(r, DefaultTranslations())
+21
View File
@@ -0,0 +1,21 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// strings_test.go
package expr
import (
"testing"
)
func TestStringsParser(t *testing.T) {
inputs := []inputType{
/* 1 */ {`"uno" + "due"`, `unodue`, nil},
/* 2 */ {`"uno" + 2`, `uno2`, nil},
/* 3 */ {`"uno" + (2+1)`, `uno3`, nil},
/* 4 */ {`"uno" * (2+1)`, `unounouno`, nil},
/* 5 */ {`"abc".1`, `b`, nil},
/* 5 */ {`#"abc"`, int64(3), nil},
}
parserTest(t, "String", inputs)
}
+19 -9
View File
@@ -9,41 +9,51 @@ import (
"reflect" "reflect"
) )
func isString(v any) (ok bool) { func IsString(v any) (ok bool) {
_, ok = v.(string) _, ok = v.(string)
return ok return ok
} }
func isInteger(v any) (ok bool) { func IsInteger(v any) (ok bool) {
_, ok = v.(int64) _, ok = v.(int64)
return ok return ok
} }
func isFloat(v any) (ok bool) { func IsFloat(v any) (ok bool) {
_, ok = v.(float64) _, ok = v.(float64)
return ok return ok
} }
func isList(v any) (ok bool) { func IsBool(v any) (ok bool) {
_, ok = v.(bool)
return ok
}
func IsList(v any) (ok bool) {
_, ok = v.(*ListType) _, ok = v.(*ListType)
return ok return ok
} }
func isDict(v any) (ok bool) { func IsDict(v any) (ok bool) {
_, ok = v.(map[any]any) _, ok = v.(map[any]any)
return ok return ok
} }
func isNumber(v any) (ok bool) { func IsFract(v any) (ok bool) {
return isFloat(v) || isInteger(v) _, ok = v.(*fraction)
return ok
}
func IsNumber(v any) (ok bool) {
return IsFloat(v) || IsInteger(v)
} }
func isNumOrFract(v any) (ok bool) { func isNumOrFract(v any) (ok bool) {
return isFloat(v) || isInteger(v) || isFraction(v) return IsFloat(v) || IsInteger(v) || isFraction(v)
} }
func isNumberString(v any) (ok bool) { func isNumberString(v any) (ok bool) {
return isString(v) || isNumber(v) return IsString(v) || IsNumber(v)
} }
func isFunctor(v any) (ok bool) { func isFunctor(v any) (ok bool) {