Compare commits
17 Commits
e6844ad1e8
...
ac5c97bfd3
| Author | SHA1 | Date | |
|---|---|---|---|
| ac5c97bfd3 | |||
| e1c24daac4 | |||
| a62f27b104 | |||
| 1055569dd6 | |||
| 1aea1c14d2 | |||
| 081395be5f | |||
| 35a599b284 | |||
| eda3037855 | |||
| 47c181546a | |||
| a8a5d6aaa6 | |||
| 84b255a51b | |||
| 9efdeffcac | |||
| d34b9d8a48 | |||
| 1bf8015da1 | |||
| 3ccbeb3978 | |||
| 0c719025cd | |||
| 08617378e0 |
@@ -275,6 +275,16 @@ func charFunc(ctx kern.ExprContext, name string, args map[string]any) (result an
|
||||
return
|
||||
}
|
||||
|
||||
func seqFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
list := kern.NewLinkedList()
|
||||
items := args[kern.ParamValue].([]any)
|
||||
for _, arg := range items {
|
||||
list.PushBack(arg)
|
||||
}
|
||||
result = list
|
||||
return
|
||||
}
|
||||
|
||||
//// import
|
||||
|
||||
func ImportBuiltinsFuncs(ctx kern.ExprContext) {
|
||||
@@ -318,6 +328,10 @@ func ImportBuiltinsFuncs(ctx kern.ExprContext) {
|
||||
ctx.RegisterFunc("char", kern.NewGolangFunctor(charFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||
kern.NewFuncParam(kern.ParamValue),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("seq", kern.NewGolangFunctor(seqFunc), kern.TypeLinkedList, []kern.ExprFuncParam{
|
||||
kern.NewFuncParamFlag(kern.ParamValue, kern.PfRepeat),
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
+133
-10
@@ -34,7 +34,7 @@ Expressions calculator
|
||||
|
||||
toc::[]
|
||||
|
||||
#TODO: Work in progress (last update on 2026/05/08)#
|
||||
#TODO: Work in progress#
|
||||
|
||||
== Expr
|
||||
_Expr_ is a GO package that can analyze, interpret and calculate expressions.
|
||||
@@ -100,7 +100,28 @@ An interactive tool could like `dev-expr` (see <<_dev-expr_test_tool>>) can be u
|
||||
[green]`{2sp}}` +
|
||||
[green]`}`
|
||||
|
||||
In order to inspect the global context issue the [blue]`$$global` operator.
|
||||
In order to inspect the global context issue the [blue]`$$ global` operation.
|
||||
////
|
||||
.Example: list all functions whose name starts with "str"
|
||||
`>>>` [blue]`builtin "string` [gray]__// most function in the builtin module *string* have names starting with "str".__ +
|
||||
[green]`1`
|
||||
|
||||
:dollar: $
|
||||
:2dollars: $$
|
||||
|
||||
`>>>` [blue]`($(($$global).functions) filter strStartsWith($_, "str"))` +
|
||||
[green]`[` +
|
||||
[green]`{2sp}"strEndsWith",` +
|
||||
[green]`{2sp}"strJoin",` +
|
||||
[green]`{2sp}"strLower",` +
|
||||
[green]`{2sp}"strSplit",` +
|
||||
[green]`{2sp}"strStartsWith",` +
|
||||
[green]`{2sp}"strSub",` +
|
||||
[green]`{2sp}"strTrim",` +
|
||||
[green]`{2sp}"strUpper",` +
|
||||
[green]`{2sp}"string"` +
|
||||
[green]`]`
|
||||
////
|
||||
|
||||
=== `dev-expr` test tool
|
||||
Before we begin to describe the syntax of _Expr_, it is worth introducing _dev-expr_ because it will be used to show many examples of expressions.
|
||||
@@ -223,14 +244,20 @@ Value range: *-9223372036854775808* to *9223372036854775807*
|
||||
[cols="^1,^2,6,4"]
|
||||
|===
|
||||
| Symbol | Operation | Description | Examples
|
||||
| [blue]`+` | _Sum_ | Add two values | [blue]`-1 + 2` -> _1_
|
||||
| [blue]`+` | _Sum_ | Add two values^(<<note_int_plus_string,1>>)^ | [blue]`-1 + 2` -> _1_
|
||||
| [blue]`-` | _Subtraction_ | Subtract the right value from the left one | [blue]`3 - 1` -> _2_
|
||||
| [blue]`*` | _Product_ | Multiply two values | [blue]`-1 * 2` -> _-2_
|
||||
| [blue]`/` | _Integer division_ | Divide the left value by the right one^(*)^ | [blue]`-11 / 2` -> _-5_
|
||||
| [blue]`*` | _Product_ | Multiply two values^(<<note_string_repl,2>>)^ | [blue]`-1 * 2` -> _-2_
|
||||
| [blue]`/` | _Integer division_ | Divide the left value by the right one^(<<note_float_division,3>>)^ | [blue]`-11 / 2` -> _-5_
|
||||
| [blue]`%` | _Modulo_ | Remainder of the integer division | [blue]`5 % 2` -> _1_
|
||||
|===
|
||||
[[note_int_plus_string]]
|
||||
^(1)^ The sum operator [blue]`+` also supports adding an integer number to a string. In this case, the number is converted to a string and prependend or appended to the string, e.g. `"x" + 48` results in `"x48"`.
|
||||
|
||||
^(*)^ See also the _float division_ [blue]`./` below.
|
||||
[[note_string_repl]]
|
||||
^(2)^ The product operator also supports multiplying a string by an integer. In this case, the number represents homw may times the string has to be repeated in the result, e.g. `"foo" * 3` returnsn `"foofoofoo"`.
|
||||
|
||||
[[note_float_division]]
|
||||
^(3)^ See also the _float division_ [blue]`./` below.
|
||||
|
||||
|
||||
==== Floats
|
||||
@@ -268,12 +295,14 @@ _dec-seq_ = _see-integer-literal-syntax_
|
||||
[cols="^1,^2,6,4"]
|
||||
|===
|
||||
| Symbol | Operation | Description | Examples
|
||||
| [blue]`+` | _Sum_ | Add two values | [blue]`4 + 0.5` -> 4.5
|
||||
| [blue]`+` | _Sum_ | Add two values^(<<note_float_plus_string,1>>)^ | [blue]`4 + 0.5` -> 4.5
|
||||
| [blue]`-` | _Subtraction_ | Subtract the right value from the left one | [blue]`4 - 0.5` -> 3.5
|
||||
| [blue]`*` | _Product_ | Multiply two values | [blue]`4 * 0.5` -> 2.0
|
||||
| [blue]`/` | _Float division_ | Divide the left value by the right one | [blue]`1.0 / 2` -> 0.5
|
||||
| [blue]`./`| _Forced float division_ | Force float division | [blue]`-1 ./ 2` -> -0.5
|
||||
|===
|
||||
[[note_float_plus_string]]
|
||||
^(1)^ The sum operator [blue]`+` also supports adding a float number to a string. In this case, the number is converted to a string and prependend or appended to the string, e.g. `"x" + 1.2` results in `"x1.2"`.
|
||||
|
||||
==== Fractions
|
||||
_Expr_ also supports fractions. Fraction literals are made with two integers separated by a colon character `:`.
|
||||
@@ -860,7 +889,7 @@ The table below shows all supported operators by decreasing priorities.
|
||||
.6+|*ITER-OP*| [blue]`digest` | _Infix_ | _Item-digesting_ | _iterable_ `digest` _expr_ -> _any_
|
||||
| [blue]`filter` | _Infix_ | _Item-filtering_ | _iterable_ `filter` _expr_ -> _list_
|
||||
| [blue]`groupby` | _Infix_ | _Dict-grouping_ | _iterable_ `groupby` _key-expr_ -> _dict_
|
||||
| [blue]`join` | _Infix_ | _Item-joining_ | _iterable_ `join` _iterable_ -> _list_
|
||||
| [blue]`cat` | _Infix_ | _Item-concatenation_ | _iterable_ `cat ` _iterable_ -> _list_
|
||||
| [blue]`map` | _Infix_ | _Item-mapping_ | _iterable_ `map` _-expr_ -> _list_
|
||||
4+| _See iterators section for examples_
|
||||
.1+|*RANGE*| [blue]`:` | _Infix_ | _Index-range_ | _integer_ `:` _integer_ -> _integer-pair_
|
||||
@@ -1507,10 +1536,56 @@ Creates or truncates the named _<file-path>_. If the file already exists, it is
|
||||
#to-do#
|
||||
|
||||
===== fileByteIterator()
|
||||
#to-do#
|
||||
Syntax: +
|
||||
`{4sp}fileByteIterator(handle-or-path) -> iterator`
|
||||
|
||||
Returns an iterator that produces the bytes of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.
|
||||
|
||||
.Examples
|
||||
>>> builtin "os.file" +
|
||||
[green]`1` +
|
||||
`>>>` [blue]`fileByteIterator("test-file.txt") map $_` +
|
||||
[green]`[
|
||||
117,
|
||||
110,
|
||||
111,
|
||||
10,
|
||||
100,
|
||||
117,
|
||||
101,
|
||||
10
|
||||
]`
|
||||
|
||||
>>> builtin ["os.file", "string"] +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`fileByteIterator("test-file.txt") map char($_)` +
|
||||
[green]`[
|
||||
"u",
|
||||
"n",
|
||||
"o",
|
||||
"
|
||||
",
|
||||
"d",
|
||||
"u",
|
||||
"e",
|
||||
"
|
||||
",
|
||||
]`
|
||||
|
||||
===== fileLineIterator()
|
||||
#to-do#
|
||||
Syntax: +
|
||||
`{4sp}fileLineIterator(handle-or-path) -> iterator`
|
||||
|
||||
Returns an iterator that produces the lines of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`builtin "os.file"` +
|
||||
[green]`1` +
|
||||
`>>>` [blue]`fileLineIterator("test-file.txt") map $_` +
|
||||
[green]`[
|
||||
"uno",
|
||||
"due"
|
||||
]`
|
||||
|
||||
|
||||
==== Module "string"
|
||||
@@ -1775,6 +1850,54 @@ TIP: Iterators built on custom data-sources can provide additional named operato
|
||||
`>>>` [blue]`it.next` +
|
||||
[green]`"one"`
|
||||
|
||||
=== Infixed operators on iterators
|
||||
There are also some infixed operators that can be used with iterators. They are defined as follows.
|
||||
|
||||
* <<_cat,cat operator>>
|
||||
* <<_diget,digest operator>>
|
||||
* <<_filter,filter operator>>
|
||||
* <<_groupby,groupby operator>>
|
||||
* <<_map,map operator>>
|
||||
//* <<_reduce,reduce operator>>: applies a binary expression cumulatively to the elements of the iterator, from left to right, to reduce the iterator to a single value.
|
||||
//* <<_zip,zip operator>>: takes two or more iterators and returns a list of tuples, where the i-th tuple contains the i-th element from each of the input iterators.
|
||||
|
||||
---
|
||||
|
||||
==== [blue]`cat` operator
|
||||
Syntax: +
|
||||
`{4sp}<iterable> cat <iterable> -> <iterator>{4sp}`
|
||||
|
||||
[blue]`cat` operator takes two iterators or iterables and returns a new iterator that produces the elements of the first iterator followed by the elements of the second iterator.
|
||||
|
||||
.Examples
|
||||
|
||||
|
||||
==== [blue]`filter` operator
|
||||
Syntax: +
|
||||
`{4sp}<iterable> filter <expr> -> <iterator>{4sp}`
|
||||
|
||||
[blue]`filter` applies a boolean expression to each element of the iterator and returns a list of the elements for which the expression evaluates to true.
|
||||
|
||||
|
||||
==== [blue]`groupby` operator
|
||||
Syntax: +
|
||||
`{4sp}<dict> groupby <key> -> <dict>{4sp}`
|
||||
|
||||
[blue]`groupby` operator groups the elements of the iterator based on the value of a specified expression and returns a dictionary where the keys are the group values and the values are lists of the elements in each group.
|
||||
|
||||
==== [blue]`map` operator
|
||||
Syntax: +
|
||||
`{4sp}<iterable> map <expr> -> <list>{4sp}`
|
||||
|
||||
|
||||
[blue]`map` operator iterates over the elements of the iterator and evaluates the expressions provided on the right side for each element. Its result is a list of the computed values of the that expression. The current element of the iterator is available in the expression as the variable `$_`.
|
||||
|
||||
.Example: using the [blue]`map` operator
|
||||
`>>>` [blue]`it = $(["one", "two", "three"])` +
|
||||
[green]`$(#3)` +
|
||||
`>>>` [blue]`it map $_ + "!"` +
|
||||
[green]`["one!", "two!", "three!"]`
|
||||
|
||||
=== Iterator over custom data-sources
|
||||
It is possible to create iterators over custom data-sources by defining a dictionary that has the key `next` whose value is a function that returns the next element of the collection and updates the state of the iterator. The syntax for creating an iterator over a custom data-source is as follows.
|
||||
|
||||
|
||||
+460
-30
@@ -581,7 +581,11 @@ pre.rouge .ss {
|
||||
<li><a href="#_operator">4.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="#_assignment_operator">4.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">4.4. Selector operator <code class="blue">? : ::</code></a>
|
||||
<ul class="sectlevel3">
|
||||
<li><a href="#_triple_special_case_of_the_selector_operator">4.4.1. Triple special case of the selector operator</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code>, <code class="blue">?=</code>, and <code class="blue">?!</code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -613,8 +617,10 @@ pre.rouge .ss {
|
||||
<li><a href="#_dec">dec()</a></li>
|
||||
<li><a href="#_string">string()</a></li>
|
||||
<li><a href="#_fract">fract()</a></li>
|
||||
<li><a href="#_char">char()</a></li>
|
||||
<li><a href="#_eval">eval()</a></li>
|
||||
<li><a href="#_var">var()</a></li>
|
||||
<li><a href="#_set">set</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_module_fmt">7.1.2. Module "fmt"</a>
|
||||
@@ -649,6 +655,8 @@ pre.rouge .ss {
|
||||
<li><a href="#_filewritetext">fileWriteText()</a></li>
|
||||
<li><a href="#_filereadtext">fileReadText()</a></li>
|
||||
<li><a href="#_filereadtextall">fileReadTextAll()</a></li>
|
||||
<li><a href="#_filebyteiterator">fileByteIterator()</a></li>
|
||||
<li><a href="#_filelineiterator">fileLineIterator()</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_module_string">7.1.7. Module "string"</a>
|
||||
@@ -674,7 +682,15 @@ pre.rouge .ss {
|
||||
<li><a href="#_named_operators">8.1.1. Named operators</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_iterator_over_custom_data_sources">8.2. Iterator over custom data-sources</a></li>
|
||||
<li><a href="#_infixed_operators_on_iterators">8.2. Infixed operators on iterators</a>
|
||||
<ul class="sectlevel3">
|
||||
<li><a href="#_cat_operator">8.2.1. <code class="blue">cat</code> operator</a></li>
|
||||
<li><a href="#_filter_operator">8.2.2. <code class="blue">filter</code> operator</a></li>
|
||||
<li><a href="#_groupby_operator">8.2.3. <code class="blue">groupby</code> operator</a></li>
|
||||
<li><a href="#_map_operator">8.2.4. <code class="blue">map</code> operator</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_iterator_over_custom_data_sources">8.3. Iterator over custom data-sources</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_plugins">9. Plugins</a></li>
|
||||
@@ -686,7 +702,7 @@ pre.rouge .ss {
|
||||
<div class="sectionbody">
|
||||
<!-- toc disabled -->
|
||||
<div class="paragraph">
|
||||
<p><mark>TODO: Work in progress (last update on 2026/04/15, 6:02 p.m.)</mark></p>
|
||||
<p><mark>TODO: Work in progress (last update on 2026/05/08)</mark></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -788,7 +804,26 @@ pre.rouge .ss {
|
||||
<code class="green">}</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>In order to inspect the global context issue the <code class="blue">$$global</code> operator.</p>
|
||||
<p>In order to inspect the global context issue the <code class="blue">$$ global</code> operation.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example: list all functions whose name starts with "str"</div>
|
||||
<p><code>>>></code> <code class="blue">builtin "string</code> <em class="gray">// most function in the builtin module <strong>string</strong> have names starting with "str".</em><br>
|
||||
<code class="green">1</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">($$$global).functions) filter strStartsWith($_, "str"</code><br>
|
||||
<code class="green">[</code><br>
|
||||
<code class="green">  "strEndsWith",</code><br>
|
||||
<code class="green">  "strJoin",</code><br>
|
||||
<code class="green">  "strLower",</code><br>
|
||||
<code class="green">  "strSplit",</code><br>
|
||||
<code class="green">  "strStartsWith",</code><br>
|
||||
<code class="green">  "strSub",</code><br>
|
||||
<code class="green">  "strTrim",</code><br>
|
||||
<code class="green">  "strUpper",</code><br>
|
||||
<code class="green">  "string"</code><br>
|
||||
<code class="green">]</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1880,10 +1915,10 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
|
||||
<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 class="exampleblock">
|
||||
<div class="title">Example 13. Selector literal Syntax</div>
|
||||
<div class="title">Example 13. Selector literal syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><em>selector-operator</em> = <em>select-expression</em> "<strong>?</strong>" <em>selector-case</em> { "<strong>:</strong>" <em>selector-case</em> } ["<strong>::</strong>" <em>default-multi-expression</em>]<br>
|
||||
<p><strong><em>selector-operator</em></strong> = <em>select-expression</em> "<strong>?</strong>" <em>selector-case</em> { "<strong>:</strong>" <em>selector-case</em> } ["<strong>::</strong>" <em>default-multi-expression</em>]<br>
|
||||
<em>selector-case</em> = [<em>match-list</em>] <em>case-value</em><br>
|
||||
<em>match-list</em> = "<strong>[</strong>" <em>item</em> {"<strong>,</strong>" <em>items</em>} "<strong>]</strong>"<br>
|
||||
<em>item</em> = <em>expression</em><br>
|
||||
@@ -1931,11 +1966,45 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
|
||||
<p><code>>>></code> <code class="blue">10 ? {"a"} : {"b"}</code><br>
|
||||
<code class="red">Eval Error: [1:3] no case catches the value (10) of the selection expression</code></p>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_triple_special_case_of_the_selector_operator"><a class="anchor" href="#_triple_special_case_of_the_selector_operator"></a><a class="link" href="#_triple_special_case_of_the_selector_operator">4.4.1. Triple special case of the selector operator</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>If the <em>select-expression</em> is a boolean expression, the selector operator can be used as a sort of <em>if-then-else</em> statement. In this case, the first case is evaluated if the <em>select-expression</em> is true, and the second case is evaluated if the <em>select-expression</em> is false. In this special case, the <em>match-list</em> of both cases must be empty.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example</div>
|
||||
<p><code>>>></code> <code class="blue">(true) ? {"T"}: {"F"}</code><br>
|
||||
<code class="green">T</code><br>
|
||||
<code>>>></code> <code class="blue">(2 > 1) ? {"a"} : {"b"}</code><br>
|
||||
<code class="green">a</code><br>
|
||||
<code>>>></code> <code class="blue">(2 < 1) ? {"a"} : {"b"}</code><br>
|
||||
<code class="green">b</code></p>
|
||||
</div>
|
||||
<div class="admonitionblock warning">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<i class="fa icon-warning" title="Warning"></i>
|
||||
</td>
|
||||
<td class="content">
|
||||
<div class="paragraph">
|
||||
<p>The triple special case of the selector operator is very useful, but it only works with boolean expressions.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example of confusion</div>
|
||||
<p><code>>>></code> <code class="blue">int(true) ? {"T"}: {"F"}</code><br>
|
||||
<code class="green">F</code></p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_variable_default_value_and"><a class="anchor" href="#_variable_default_value_and"></a><a class="link" href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code>, <code class="blue">?=</code>, and <code class="blue">?!</code></a></h3>
|
||||
<div class="paragraph">
|
||||
<p>The left operand of the first two operators, <code class="blue">??</code> and <code class="blue">?=</code>, must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.</p>
|
||||
<p>The left operand of the first two operators, <code class="blue">??</code> and <code class="blue">?=</code>, must be a variable. The right operatand can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.</p>
|
||||
</div>
|
||||
<div class="admonitionblock important">
|
||||
<table>
|
||||
@@ -1953,7 +2022,7 @@ If the left variable is defined, the right expression is not evaluated at all.
|
||||
<p>The <code class="blue">??</code> operator do not change the status of the left variable.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The <code class="blue">?=</code> assigns the calculated value of the right expression to the left variable.</p>
|
||||
<p>The <code class="blue">?=</code> assigns the calculated value of the right expression to the variable on the left side.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The third one, <code class="blue">?!</code>, is the alternate operator. If the variable on the left size is not defined, it returns <em class="blue">nil</em>. Otherwise it returns the result of the expressione on the right side.</p>
|
||||
@@ -1965,7 +2034,7 @@ If the left variable is defined, the right expression is not evaluated at all.
|
||||
<i class="fa icon-important" title="Important"></i>
|
||||
</td>
|
||||
<td class="content">
|
||||
If the left variable is NOT defined, the right expression is not evaluated at all.
|
||||
If the variable <code class="blue">?!</code> is NOT defined, the expression is not evaluated at all.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
@@ -2371,7 +2440,7 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>identifier</em> <code>=</code> <em>any</em> → <em>any</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" colspan="4"><p class="tableblock"><em>See also the table of special allocation operators below</em></p></td>
|
||||
<td class="tableblock halign-center valign-top" colspan="4"><p class="tableblock"><em>See also the table of special assignment operators below</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>BUT</strong></p></td>
|
||||
@@ -2381,6 +2450,40 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>but</code> <em>any</em> → <em>any</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="6"><p class="tableblock"><strong>ITER-OP</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">digest</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item-digesting</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>digest</code> <em>expr</em> → <em>any</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">filter</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item-filtering</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>filter</code> <em>expr</em> → <em>list</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">groupby</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Dict-grouping</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>groupby</code> <em>key-expr</em> → <em>dict</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">cat</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item-concatenation</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> `cat ` <em>iterable</em> → <em>list</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">map</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item-mapping</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>map</code> <em>-expr</em> → <em>list</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" colspan="4"><p class="tableblock"><em>See iterators section for examples</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>RANGE</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">:</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
@@ -2536,14 +2639,14 @@ short for<br>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <em class="gray">// Required and optional parameters</em><br>
|
||||
<code>>>></code> <code class="blue">measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? [true] {"s"} :: {""}}</code><br>
|
||||
<code>>>></code> <code class="blue">measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? {"s"} :: {""}}</code><br>
|
||||
<code class="green">measure(value, unit="meter"):any{}</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_golang_function_definition"><a class="anchor" href="#_golang_function_definition"></a><a class="link" href="#_golang_function_definition">6.2. <em>Golang</em> function definition</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>Description of how to define Golang functions and how to bind them to <em>Expr</em> are topics covered in another document that I’ll write, one day, maybe.</p>
|
||||
<p>Description of how to define Golang functions and how to bind them to <em>Expr</em> are topics covered in another documents that I’ll write, one day, maybe.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
@@ -2642,7 +2745,7 @@ short for<br>
|
||||
<div class="title">Example</div>
|
||||
<p><code>>>></code> <code class="blue">f = func() { @x = 3; x = 5 }</code> <em class="gray">// f() declares two <strong>different</strong> local variables: <code>@x</code> and <code>x</code></em><br>
|
||||
<code class="green">f():any{}</code><br>
|
||||
<code>>>></code> <code class="blue">f()</code> <em class="gray">// The multi-expression (two) in f() is calculated and the last result is returned</em><br>
|
||||
<code>>>></code> <code class="blue">f()</code> <em class="gray">// The multi-expression (two expressions) in f() is calculated and the last result is returned</em><br>
|
||||
<code class="green">5</code><br>
|
||||
<code>>>></code> <code class="blue">x</code> <em class="gray">// The <code>x</code> variable was not defined in the main context before the f() invocation. It appears in the main context by cloning the <code>@x</code> variable, local to f() after its termnation.</em><br>
|
||||
<code class="green">3</code></p>
|
||||
@@ -2727,7 +2830,7 @@ The clone modifier <code class="blue">@</code> does not make a variable a refere
|
||||
<div class="title">Example 16. Builtin activation syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><strong><em>builtin-activation</em></strong> = <code class="blue">BUILTIN</code> (<em>builtin-name</em> | <em>list-of-builtin-names</em>)<br>
|
||||
<p><strong><em>builtin-activation</em></strong> = <code class="blue">BUILTIN</code> (<em>builtin-name</em> | <em>list-of-builtin-names</em> | <strong>"*"</strong>)<br>
|
||||
<em>builtin-name</em> = <em>string</em><br>
|
||||
<em>list-of-builtin-names</em> = <strong>[</strong> <em>string</em> { "<strong>,</strong>" <em>string</em> } <strong>]</strong></p>
|
||||
</div>
|
||||
@@ -2815,9 +2918,15 @@ To avoid the need to activate builtin modules one by one, it is possible to acti
|
||||
<div class="title">Other functions</div>
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="#_char">char()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_eval">eval()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_set">set()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_var">var()</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -2833,7 +2942,9 @@ Returns <em>true</em> if the value type of <em><expr></em> is boolean, fal
|
||||
<p><code>>>></code> <code class="blue">isBool(true)</code><br>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isBool(3==2)</code><br>
|
||||
<code class="green">true</code></p>
|
||||
<code class="green">true</code><br>
|
||||
<code>>>></code> <code class="blue">isBool(3 + 2)</code><br>
|
||||
<code class="green">false</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -2938,7 +3049,7 @@ Returns <em>true</em> if the value type of <em><expr></em> is fraction or
|
||||
<h5 id="_isstring"><a class="anchor" href="#_isstring"></a><a class="link" href="#_isstring">isString()</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax: <code>isString(<expr>) → bool</code><br>
|
||||
Returns a boolean value , false otherwise.</p>
|
||||
Returns <em>true</em> if the value type of <em><expr></em> is string, false otherwise.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
@@ -2954,7 +3065,7 @@ Returns a boolean value , false otherwise.</p>
|
||||
<h5 id="_bool"><a class="anchor" href="#_bool"></a><a class="link" href="#_bool">bool()</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax: <code>bool(<expr>) → bool</code><br>
|
||||
Returns a <em>boolean</em> value consisent to the value of the expression.</p>
|
||||
Returns a <em>boolean</em> value consisent with the value of the expression.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
@@ -3077,6 +3188,20 @@ Returns a <em>fraction</em> value consistent with the value of the expression.</
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_char"><a class="anchor" href="#_char"></a><a class="link" href="#_char">char()</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax: <code>char(<intexpr>) → string</code><br>
|
||||
Returns the character whose ASCII (soon Unicode too) code point is specified by the integer expression.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">char(65)</code><br>
|
||||
<code class="green">"A"</code><br>
|
||||
<code>>>></code> <code class="blue">char(97)</code><br>
|
||||
<code class="green">"a"</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_eval"><a class="anchor" href="#_eval"></a><a class="link" href="#_eval">eval()</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax: <code>eval(<string-expr>) → any</code><br>
|
||||
@@ -3096,13 +3221,13 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
||||
<code>    var(<string-expr>) → any</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>This function allows you to define variables whose names must include special characters. The first form of the function allows you to define a variable with a name specified by the first parameter and assign it the value of the second parameter. The second form only returns the value of the variable with the specified name.</p>
|
||||
<p>This function allows you to define variables whose names can include special characters. The first form of the function allows you to define a variable with a name specified by the first parameter and assign it the value of the second parameter. The second form only returns the value of the variable with the specified name.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">var("$x", 3+9)</code><br>
|
||||
<code class="green">12</code><br>
|
||||
<code>>>></code> <code class="blue">var("$x")</code><br>
|
||||
<code>>>></code> <code class="blue">var("$"+"x")</code><br>
|
||||
<code class="green">12</code><br>
|
||||
<code>>>></code> <code class="blue">var("gain%", var("$x"))</code><br>
|
||||
<code class="green">12</code><br>
|
||||
@@ -3110,9 +3235,32 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
||||
<code class="green">13</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_set"><a class="anchor" href="#_set"></a><a class="link" href="#_set">set</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    set(<string-expr>, <expr>) → any</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>This function allows you to set the value of a variable whose name can include special characters. The first parameter is the name of the variable and the second parameter is the new value to assign to that variable.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>It is equivalent to the first form of the var() function, but it is more explicit about the intent of changing the value of an existing variable.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">set("$x", 100)</code><br>
|
||||
<code class="green">100</code><br>
|
||||
<code>>>></code> <code class="blue">var("$x")</code><br>
|
||||
<code class="green">100</code><br></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_module_fmt"><a class="anchor" href="#_module_fmt"></a><a class="link" href="#_module_fmt">7.1.2. Module "fmt"</a></h4>
|
||||
<div class="paragraph">
|
||||
<p><mark>to-do</mark></p>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_print"><a class="anchor" href="#_print"></a><a class="link" href="#_print">print()</a></h5>
|
||||
|
||||
@@ -3124,10 +3272,18 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_module_import"><a class="anchor" href="#_module_import"></a><a class="link" href="#_module_import">7.1.3. Module "import"</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Module activation:<br>
|
||||
<code>    BUILTIN "import"</code></p>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_import"><a class="anchor" href="#_import"></a><a class="link" href="#_import"><em>import()</em></a></h5>
|
||||
<div class="paragraph">
|
||||
<p><em class="blue">import(<span class="grey"><source-file></span>)</em> — loads the multi-expression contained in the specified source and returns its value.</p>
|
||||
<p>Syntax:<br>
|
||||
<code>    import(<source-file>)</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Loads the multi-expression contained in the specified source and returns its value.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
@@ -3137,16 +3293,40 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_module_iterator"><a class="anchor" href="#_module_iterator"></a><a class="link" href="#_module_iterator">7.1.4. Module "iterator"</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Module activation:<br>
|
||||
<code>    BUILTIN "iterator"</code></p>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_run"><a class="anchor" href="#_run"></a><a class="link" href="#_run">run()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    run(<iterator>, <operator>, <vars>) → any</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Iterates over the specified iterator and applies the specified operator to the current value of the iterator.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_module_math_arith"><a class="anchor" href="#_module_math_arith"></a><a class="link" href="#_module_math_arith">7.1.5. Module "math.arith"</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Module activation:<br>
|
||||
<code>    BUILTIN "math.arith"</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Currently, the "math.arith" module provides two functions, add() and mul(), that perform addition and multiplication of an arbitrary number of parameters. More functions will be added in the future.</p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="#_add">add()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_mul">mul()</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_add"><a class="anchor" href="#_add"></a><a class="link" href="#_add">add()</a></h5>
|
||||
<div class="paragraph">
|
||||
@@ -3202,37 +3382,213 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_module_os_file"><a class="anchor" href="#_module_os_file"></a><a class="link" href="#_module_os_file">7.1.6. Module "os.file"</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Module activation:<br>
|
||||
<code>    BUILTIN "os.file"</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The "os.file" module provides functions for working with files.</p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<div class="title">File related functions</div>
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="#_fileOpen">fileOpen()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileAppend">fileAppend()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileCreate">fileCreate()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileClose">fileClose()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileWriteText">fileWriteText()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileReadText">fileReadText()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileReadTextAll">fileReadTextAll()</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<div class="title">Iterator functions for files</div>
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="#_fileByteIterator">fileByteIterator()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_fileLineIterator">fileLineIterator()</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>More functions will be added in the future.</p>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="sect4">
|
||||
<h5 id="_fileopen"><a class="anchor" href="#_fileopen"></a><a class="link" href="#_fileopen">fileOpen()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    fileOpen(<file-path>) → file-handle</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Returns a file handle for the specified file path. The file is opened in read-write mode. If the file does not exist, it is created.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_fileappend"><a class="anchor" href="#_fileappend"></a><a class="link" href="#_fileappend">fileAppend()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    fileAppend(<file-path>) → any</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Like <a href="#_fileCreate">fileCreate()</a> but write operations happen at the end of the file.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_filecreate"><a class="anchor" href="#_filecreate"></a><a class="link" href="#_filecreate">fileCreate()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    fileCreate(<file-path>) → file-handle</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Creates or truncates the named <em><file-path></em>. If the file already exists, it is truncated. If the file does not exist, it is created with mode 0o666 (before umask). The associated file descriptor has mode [O_RDWR]. The directory containing the file must already exist.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_fileclose"><a class="anchor" href="#_fileclose"></a><a class="link" href="#_fileclose">fileClose()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p><mark>to-do</mark></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_filewritetext"><a class="anchor" href="#_filewritetext"></a><a class="link" href="#_filewritetext">fileWriteText()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p><mark>to-do</mark></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_filereadtext"><a class="anchor" href="#_filereadtext"></a><a class="link" href="#_filereadtext">fileReadText()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p><mark>to-do</mark></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_filereadtextall"><a class="anchor" href="#_filereadtextall"></a><a class="link" href="#_filereadtextall">fileReadTextAll()</a></h5>
|
||||
|
||||
<div class="paragraph">
|
||||
<p><mark>to-do</mark></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_filebyteiterator"><a class="anchor" href="#_filebyteiterator"></a><a class="link" href="#_filebyteiterator">fileByteIterator()</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    fileByteIterator(handle-or-path) → iterator</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Returns an iterator that produces the bytes of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>>>> builtin "os.file"<br>
|
||||
<code class="green">1</code><br>
|
||||
<code>>>></code> <code class="blue">fileByteIterator("test-file.txt") map $_</code><br>
|
||||
<code class="green">[
|
||||
117,
|
||||
110,
|
||||
111,
|
||||
10,
|
||||
100,
|
||||
117,
|
||||
101,
|
||||
10
|
||||
]</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>>>> builtin ["os.file", "string"]<br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue">fileByteIterator("test-file.txt") map char($_)</code><br>
|
||||
<code class="green">[
|
||||
"u",
|
||||
"n",
|
||||
"o",
|
||||
"
|
||||
",
|
||||
"d",
|
||||
"u",
|
||||
"e",
|
||||
"
|
||||
",
|
||||
]</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect4">
|
||||
<h5 id="_filelineiterator"><a class="anchor" href="#_filelineiterator"></a><a class="link" href="#_filelineiterator">fileLineIterator()</a></h5>
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    fileLineIterator(handle-or-path) → iterator</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Returns an iterator that produces the lines of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">builtin "os.file"</code><br>
|
||||
<code class="green">1</code><br>
|
||||
<code>>>></code> <code class="blue">fileLineIterator("test-file.txt") map $_</code><br>
|
||||
<code class="green">[
|
||||
"uno",
|
||||
"due"
|
||||
]</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_module_string"><a class="anchor" href="#_module_string"></a><a class="link" href="#_module_string">7.1.7. Module "string"</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Module activation:<br>
|
||||
<code>    BUILTIN "string"</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>This module provides functions for working with strings.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Currently available functions:</p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="#_strJoin">strJoin()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strSub">strSub()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strSplit">strSplit()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strTrim">strTrim()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strStartsWith">strStartsWith()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strEndsWith">strEndsWith()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strUpper">strUpper()</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_strLower">strLower()</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="sect4">
|
||||
<h5 id="_strjoin"><a class="anchor" href="#_strjoin"></a><a class="link" href="#_strjoin">strJoin()</a></h5>
|
||||
<div class="paragraph">
|
||||
@@ -3589,7 +3945,81 @@ Iterators built on custom data-sources can provide additional named operators, d
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_iterator_over_custom_data_sources"><a class="anchor" href="#_iterator_over_custom_data_sources"></a><a class="link" href="#_iterator_over_custom_data_sources">8.2. Iterator over custom data-sources</a></h3>
|
||||
<h3 id="_infixed_operators_on_iterators"><a class="anchor" href="#_infixed_operators_on_iterators"></a><a class="link" href="#_infixed_operators_on_iterators">8.2. Infixed operators on iterators</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>There are also some infixed operators that can be used with iterators. They are defined as follows.</p>
|
||||
</div>
|
||||
<div class="ulist">
|
||||
<ul>
|
||||
<li>
|
||||
<p><a href="#_cat">cat operator</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_diget">digest operator</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_filter">filter operator</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_groupby">groupby operator</a></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><a href="#_map">map operator</a></p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="sect3">
|
||||
<h4 id="_cat_operator"><a class="anchor" href="#_cat_operator"></a><a class="link" href="#_cat_operator">8.2.1. <code class="blue">cat</code> operator</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    <iterable> cat <iterable> → <iterator>    </code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code class="blue">cat</code> operator takes two iterators or iterables and returns a new iterator that produces the elements of the first iterator followed by the elements of the second iterator.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_filter_operator"><a class="anchor" href="#_filter_operator"></a><a class="link" href="#_filter_operator">8.2.2. <code class="blue">filter</code> operator</a></h4>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p>Syntax:<br>
|
||||
<code>    <iterable> filter <expr> → <iterator>    </code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code class="blue">filter</code> applies a boolean expression to each element of the iterator and returns a list of the elements for which the expression evaluates to true.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_groupby_operator"><a class="anchor" href="#_groupby_operator"></a><a class="link" href="#_groupby_operator">8.2.3. <code class="blue">groupby</code> operator</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    <dict> groupby <key> → <dict>    </code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code class="blue">groupby</code> operator groups the elements of the iterator based on the value of a specified expression and returns a dictionary where the keys are the group values and the values are lists of the elements in each group.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_map_operator"><a class="anchor" href="#_map_operator"></a><a class="link" href="#_map_operator">8.2.4. <code class="blue">map</code> operator</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Syntax:<br>
|
||||
<code>    <iterable> map <expr> → <list>    </code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code class="blue">map</code> operator iterates over the elements of the iterator and evaluates the expressions provided on the right side for each element. Its result is a list of the computed values of the that expression. The current element of the iterator is available in the expression as the variable <code>$_</code>.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example: using the <code class="blue">map</code> operator</div>
|
||||
<p><code>>>></code> <code class="blue">it = $(["one", "two", "three"])</code><br>
|
||||
<code class="green">$(#3)</code><br>
|
||||
<code>>>></code> <code class="blue">it map $_ + "!"</code><br>
|
||||
<code class="green">["one!", "two!", "three!"]</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_iterator_over_custom_data_sources"><a class="anchor" href="#_iterator_over_custom_data_sources"></a><a class="link" href="#_iterator_over_custom_data_sources">8.3. Iterator over custom data-sources</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>It is possible to create iterators over custom data-sources by defining a dictionary that has the key <code>next</code> whose value is a function that returns the next element of the collection and updates the state of the iterator. The syntax for creating an iterator over a custom data-source is as follows.</p>
|
||||
</div>
|
||||
@@ -3610,7 +4040,7 @@ Iterators built on custom data-sources can provide additional named operators, d
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2026-04-21 06:35:14 +0200
|
||||
Last updated 2026-05-12 16:25:27 +0200
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
+9
-11
@@ -42,12 +42,16 @@ func ImportInContextByGlobPattern(ctx kern.ExprContext, pattern string) (count i
|
||||
return
|
||||
}
|
||||
|
||||
func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValue any) {
|
||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||
func fixCtrlVar(name string) string {
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValue any) {
|
||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||
name = fixCtrlVar(name)
|
||||
currentValue, _ = globalCtx.GetVar(name)
|
||||
globalCtx.SetVar(name, newValue)
|
||||
}
|
||||
@@ -56,18 +60,14 @@ func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValu
|
||||
|
||||
func GlobalCtrlGet(ctx kern.ExprContext, name string) (currentValue any) {
|
||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
}
|
||||
name = fixCtrlVar(name)
|
||||
currentValue, _ = globalCtx.GetVar(name)
|
||||
}
|
||||
return currentValue
|
||||
}
|
||||
|
||||
func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
}
|
||||
name = fixCtrlVar(name)
|
||||
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
|
||||
currentStatus, _ = v.(bool)
|
||||
}
|
||||
@@ -77,9 +77,7 @@ func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
||||
}
|
||||
|
||||
func CtrlDisable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
||||
if !strings.HasPrefix(name, "_") {
|
||||
name = "_" + name
|
||||
}
|
||||
name = fixCtrlVar(name)
|
||||
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
|
||||
currentStatus, _ = v.(bool)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@ type IntIterator struct {
|
||||
step int64
|
||||
}
|
||||
|
||||
func NewIntIteratorA(args ...any) (it *IntIterator, err error) {
|
||||
return NewIntIterator(args)
|
||||
}
|
||||
|
||||
func NewIntIterator(args []any) (it *IntIterator, err error) {
|
||||
var argc int = 0
|
||||
if args != nil {
|
||||
|
||||
+8
-6
@@ -19,6 +19,8 @@ func NewIterator(ctx kern.ExprContext, value any, ops []*scan.Term) (it kern.Ite
|
||||
switch v := value.(type) {
|
||||
case *kern.ListType:
|
||||
it = NewListIterator(v, nil)
|
||||
case *kern.LinkedList:
|
||||
it = NewLinkedListIterator(v, nil)
|
||||
case *kern.DictType:
|
||||
it, err = NewDictIterator(v, nil)
|
||||
case []any:
|
||||
@@ -36,9 +38,9 @@ func HasIterStandardOperations(name string) bool {
|
||||
return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
|
||||
}
|
||||
|
||||
func HasIterOperations(name string, ops ...string) bool {
|
||||
return slices.Contains([]string{
|
||||
kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName,
|
||||
}, name) ||
|
||||
slices.Contains(ops, name)
|
||||
}
|
||||
// func HasIterOperations(name string, ops ...string) bool {
|
||||
// return slices.Contains([]string{
|
||||
// kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName,
|
||||
// }, name) ||
|
||||
// slices.Contains(ops, name)
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// clone-value.go
|
||||
package kern
|
||||
|
||||
func Clone(v any) (c any) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
switch unboxed := v.(type) {
|
||||
case int64:
|
||||
c = unboxed
|
||||
case float64:
|
||||
c = unboxed
|
||||
case string:
|
||||
c = unboxed
|
||||
case bool:
|
||||
c = unboxed
|
||||
case *ListType:
|
||||
c = unboxed.Clone()
|
||||
case *DictType:
|
||||
c = unboxed.Clone()
|
||||
case *LinkedList:
|
||||
c = unboxed.Clone()
|
||||
default:
|
||||
c = v
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -20,4 +20,5 @@ const (
|
||||
TypeDict = "dict"
|
||||
TypeListOf = "list-of-"
|
||||
TypeListOfStrings = "list-of-strings"
|
||||
TypeLinkedList = "linked-list"
|
||||
)
|
||||
|
||||
@@ -20,6 +20,10 @@ func Equal(value1, value2 any) (equal bool) {
|
||||
d1 := value1.(*DictType)
|
||||
d2 := value2.(*DictType)
|
||||
equal = d1.Equals(*d2)
|
||||
} else if IsLinkedList(value1) && IsLinkedList(value2) {
|
||||
ll1 := value1.(*LinkedList)
|
||||
ll2 := value2.(*LinkedList)
|
||||
equal = ll1.Equals(ll2)
|
||||
} else if IsInteger(value1) && IsInteger(value2) {
|
||||
equal = value1.(int64) == value2.(int64)
|
||||
} else if IsString(value1) && IsString(value2) {
|
||||
|
||||
+14
-8
@@ -10,6 +10,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const DictTypeName = "dict"
|
||||
|
||||
type DictType map[any]any
|
||||
|
||||
func IsDict(v any) (ok bool) {
|
||||
@@ -74,9 +76,11 @@ func (dict *DictType) toMultiLine(sb *strings.Builder, opt FmtOpt) {
|
||||
|
||||
sb.WriteString(nest)
|
||||
if key, ok := name.(string); ok {
|
||||
sb.WriteString(string('"') + key + string('"'))
|
||||
sb.WriteByte('"')
|
||||
sb.WriteString(key)
|
||||
sb.WriteByte('"')
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%v", name))
|
||||
fmt.Fprintf(sb, "%v", name)
|
||||
}
|
||||
sb.WriteString(": ")
|
||||
if f, ok := value.(Formatter); ok {
|
||||
@@ -84,7 +88,7 @@ func (dict *DictType) toMultiLine(sb *strings.Builder, opt FmtOpt) {
|
||||
} else if _, ok = value.(Functor); ok {
|
||||
sb.WriteString("func(){}")
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%v", value))
|
||||
fmt.Fprintf(sb, "%v", value)
|
||||
}
|
||||
}
|
||||
sb.WriteByte('\n')
|
||||
@@ -108,9 +112,11 @@ func (dict *DictType) ToString(opt FmtOpt) string {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
if s, ok := key.(string); ok {
|
||||
sb.WriteString(string('"') + s + string('"'))
|
||||
sb.WriteByte('"')
|
||||
sb.WriteString(s)
|
||||
sb.WriteByte('"')
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%v", key))
|
||||
fmt.Fprintf(&sb, "%v", key)
|
||||
}
|
||||
sb.WriteString(": ")
|
||||
if formatter, ok := value.(Formatter); ok {
|
||||
@@ -118,7 +124,7 @@ func (dict *DictType) ToString(opt FmtOpt) string {
|
||||
} else if t, ok := value.(Term); ok {
|
||||
sb.WriteString(t.String())
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%#v", value))
|
||||
fmt.Fprintf(&sb, "%#v", value)
|
||||
}
|
||||
}
|
||||
sb.WriteByte('}')
|
||||
@@ -131,7 +137,7 @@ func (dict *DictType) String() string {
|
||||
}
|
||||
|
||||
func (dict *DictType) TypeName() string {
|
||||
return "dict"
|
||||
return DictTypeName
|
||||
}
|
||||
|
||||
func (dict *DictType) HasKey(target any) (ok bool) {
|
||||
@@ -155,7 +161,7 @@ func (dict *DictType) GetItem(key any) (value any, exists bool) {
|
||||
func (dict *DictType) Clone() (c *DictType) {
|
||||
c = newDict(nil)
|
||||
for k, v := range *dict {
|
||||
(*c)[k] = v
|
||||
(*c)[k] = Clone(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -28,4 +28,36 @@ type ExprContext interface {
|
||||
Call(name string, args map[string]any) (result any, err error)
|
||||
RegisterFuncInfo(info ExprFunc)
|
||||
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error)
|
||||
|
||||
ToDict() (dict *DictType)
|
||||
ToString(opt FmtOpt) string
|
||||
}
|
||||
|
||||
func ContextToDict(ctx ExprContext) (dict *DictType) {
|
||||
var keys []string
|
||||
// Variables
|
||||
keys = ctx.EnumVars(nil)
|
||||
vars := MakeDict()
|
||||
for _, key := range keys {
|
||||
value, _ := ctx.GetVar(key)
|
||||
vars.SetItem(key, value)
|
||||
}
|
||||
|
||||
// Functions
|
||||
keys = ctx.EnumFuncs(func(name string) bool { return true })
|
||||
funcs := MakeDict()
|
||||
for _, key := range keys {
|
||||
funcInfo, _ := ctx.GetFuncInfo(key)
|
||||
funcs.SetItem(key, funcInfo)
|
||||
}
|
||||
|
||||
dict = MakeDict()
|
||||
dict.SetItem("vars", vars)
|
||||
dict.SetItem("funcs", funcs)
|
||||
return
|
||||
}
|
||||
|
||||
func ContextToString(ctx ExprContext, opt FmtOpt) string {
|
||||
dict := ctx.ToDict()
|
||||
return dict.ToString(opt)
|
||||
}
|
||||
|
||||
+16
-1
@@ -4,7 +4,10 @@
|
||||
// formatter.go
|
||||
package kern
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type FmtOpt uint32 // lower 16 bits hold a bit-mask, higher 16 bits hold an indentation number
|
||||
|
||||
@@ -46,6 +49,18 @@ type Formatter interface {
|
||||
ToString(options FmtOpt) string
|
||||
}
|
||||
|
||||
func Format(sb *strings.Builder, item any, opt FmtOpt) {
|
||||
if s, ok := item.(string); ok {
|
||||
sb.WriteByte('"')
|
||||
sb.WriteString(s)
|
||||
sb.WriteByte('"')
|
||||
} else if formatter, ok := item.(Formatter); ok {
|
||||
sb.WriteString(formatter.ToString(opt))
|
||||
} else {
|
||||
fmt.Fprintf(sb, "%v", item)
|
||||
}
|
||||
}
|
||||
|
||||
func GetFormatted(v any, opt FmtOpt) (text string) {
|
||||
if v == nil {
|
||||
text = "(nil)"
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// linked-list-type.go
|
||||
package kern
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const LinkedListTypeName = "lisked-list"
|
||||
const MaxUint64Allowed = uint64(9_223_372_036_854_775_807)
|
||||
|
||||
func IsLinkedList(v any) (ok bool) {
|
||||
_, ok = v.(*LinkedList)
|
||||
return ok
|
||||
}
|
||||
|
||||
func NewLinkedListA(listAny ...any) (list *LinkedList) {
|
||||
if listAny == nil {
|
||||
listAny = []any{}
|
||||
}
|
||||
list = NewLinkedList()
|
||||
for _, item := range listAny {
|
||||
list.PushBack(FixAnyNumber(item))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func LinkedListFromStrings(stringList []string) (list *LinkedList) {
|
||||
list = NewLinkedList()
|
||||
for _, s := range stringList {
|
||||
list.PushBack(s)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *LinkedList) ToString(opt FmtOpt) (s string) {
|
||||
indent := GetFormatIndent(opt)
|
||||
flags := GetFormatFlags(opt)
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString("[<")
|
||||
if ls.Len() > 0 {
|
||||
innerOpt := MakeFormatOptions(flags, indent+1)
|
||||
nest := strings.Repeat(" ", indent+1)
|
||||
|
||||
if flags&MultiLine != 0 {
|
||||
sb.WriteByte('\n')
|
||||
sb.WriteString(nest)
|
||||
}
|
||||
|
||||
i := 0
|
||||
for item := ls.FirstNode(); item != nil; item = item.Next() {
|
||||
if i > 0 {
|
||||
if flags&MultiLine != 0 {
|
||||
sb.WriteString(",\n")
|
||||
sb.WriteString(nest)
|
||||
} else {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
}
|
||||
// data := item.Data()
|
||||
// if s, ok := data.(string); ok {
|
||||
// sb.WriteByte('"')
|
||||
// sb.WriteString(s)
|
||||
// sb.WriteByte('"')
|
||||
// } else if formatter, ok := data.(Formatter); ok {
|
||||
// sb.WriteString(formatter.ToString(innerOpt))
|
||||
// } else {
|
||||
// fmt.Fprintf(&sb, "%v", data)
|
||||
// }
|
||||
Format(&sb, item.Data(), innerOpt)
|
||||
i++
|
||||
}
|
||||
if flags&MultiLine != 0 {
|
||||
sb.WriteByte('\n')
|
||||
sb.WriteString(strings.Repeat(" ", indent))
|
||||
}
|
||||
}
|
||||
sb.WriteString(">]")
|
||||
s = sb.String()
|
||||
if flags&Truncate != 0 && len(s) > TruncateSize {
|
||||
s = TruncateString(s)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *LinkedList) String() string {
|
||||
return ls.ToString(0)
|
||||
}
|
||||
|
||||
func (ls *LinkedList) TypeName() string {
|
||||
return LinkedListTypeName
|
||||
}
|
||||
|
||||
// func (ls *LinkedList) Contains(t *ListType) (answer bool) {
|
||||
// if len(*ls) >= len(*t) {
|
||||
// answer = true
|
||||
// for _, item := range *t {
|
||||
// if answer = ls.IndexDeepSameCmp(item) >= 0; !answer {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (ls1 *LinkedList) Equals(ls2 *LinkedList) (answer bool) {
|
||||
if ls2 != nil && ls1.Len() == ls2.Len() {
|
||||
answer = true
|
||||
i2 := ls2.FirstNode()
|
||||
for i1 := ls1.FirstNode(); i1 != nil; i1 = i1.Next() {
|
||||
if !Equal(i1.Data(), i2.Data()) {
|
||||
answer = false
|
||||
break
|
||||
}
|
||||
i2 = i2.Next()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ls1 *LinkedList) Clone() (ls2 *LinkedList) {
|
||||
ls2 = NewLinkedListA()
|
||||
for i1 := ls1.FirstNode(); i1 != nil; i1 = i1.Next() {
|
||||
ls2.PushBack(Clone(i1.Data()))
|
||||
}
|
||||
return
|
||||
}
|
||||
+24
-13
@@ -19,7 +19,7 @@ func (self *ListNode) Data() any {
|
||||
}
|
||||
|
||||
type LinkedList struct {
|
||||
count uint32
|
||||
count int
|
||||
first *ListNode
|
||||
last *ListNode
|
||||
}
|
||||
@@ -29,7 +29,7 @@ func NewLinkedList() (list *LinkedList) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *LinkedList) Len() uint32 {
|
||||
func (ls *LinkedList) Len() int {
|
||||
return ls.count
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ func (ls *LinkedList) Last() (data interface{}, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *LinkedList) NodeAt(index uint32) (node *ListNode, err error) {
|
||||
func (ls *LinkedList) NodeAt(index int) (node *ListNode, err error) {
|
||||
if ls.count == 0 {
|
||||
err = errorListEmpty()
|
||||
} else if index > ls.count {
|
||||
@@ -130,7 +130,7 @@ func (ls *LinkedList) NodeAt(index uint32) (node *ListNode, err error) {
|
||||
node = ls.last
|
||||
} else {
|
||||
current := ls.first
|
||||
for pos := uint32(0); pos < index; pos++ {
|
||||
for range index {
|
||||
current = current.next
|
||||
}
|
||||
node = current
|
||||
@@ -138,7 +138,7 @@ func (ls *LinkedList) NodeAt(index uint32) (node *ListNode, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *LinkedList) At(index uint32) (data interface{}, err error) {
|
||||
func (ls *LinkedList) At(index int) (data interface{}, err error) {
|
||||
node, err := ls.NodeAt(index)
|
||||
if err == nil {
|
||||
data = node.data
|
||||
@@ -146,12 +146,12 @@ func (ls *LinkedList) At(index uint32) (data interface{}, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *LinkedList) Insert(index uint32, data interface{}) *ListNode {
|
||||
func (ls *LinkedList) Insert(index int, data any) *ListNode {
|
||||
var prev *ListNode
|
||||
|
||||
prev = nil
|
||||
current := ls.first
|
||||
for pos := uint32(0); current != nil && pos < index; pos++ {
|
||||
for pos := 0; current != nil && pos < index; pos++ {
|
||||
prev = current
|
||||
current = current.next
|
||||
}
|
||||
@@ -178,10 +178,21 @@ func (ls *LinkedList) PushBackStringArray(items []string) {
|
||||
}
|
||||
}
|
||||
|
||||
// type TraverseOperator func(index uint32, elem interface{}, userData interface{}) (err error)
|
||||
func (ls *LinkedList) Sub(start, end int) (subList *LinkedList) {
|
||||
subList = NewLinkedList()
|
||||
if node, err := ls.NodeAt(start); err == nil {
|
||||
for i := start; i < end && node != nil; i++ {
|
||||
subList.PushBack(node.data)
|
||||
node = node.next
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// type TraverseOperator func(index int, elem interface{}, userData interface{}) (err error)
|
||||
|
||||
// func (self *LinkedList) Traverse(op TraverseOperator, user_data interface{}) (err error) {
|
||||
// index := uint32(0)
|
||||
// index := int(0)
|
||||
// for current := self.first; current != nil; current = current.next {
|
||||
// err = op(index, current.data, user_data)
|
||||
// if err != nil {
|
||||
@@ -193,7 +204,7 @@ func (ls *LinkedList) PushBackStringArray(items []string) {
|
||||
// }
|
||||
|
||||
// func (self *LinkedList) Traverse2(observer Observer, abortOnError bool) (err error) {
|
||||
// index := uint32(0)
|
||||
// index := int(0)
|
||||
// for current := self.first; current != nil; current = current.next {
|
||||
// err = observer.Observe(current, index)
|
||||
// if err != nil && abortOnError {
|
||||
@@ -227,9 +238,9 @@ func (ls *LinkedList) FindNext(eqFunc EqualFunc, startNode *ListNode) (targetNod
|
||||
}
|
||||
|
||||
// type DataFeeder func(user_data interface{}) interface{}
|
||||
// type NodeObserver func(node *ListNode, index uint32, userData interface{})
|
||||
// type NodeObserver func(node *ListNode, index int, userData interface{})
|
||||
|
||||
// func (self *LinkedList) FeedTail(feeder DataFeeder, feederUserData interface{}, observer NodeObserver, observerUserData interface{}) (count uint32) {
|
||||
// func (self *LinkedList) FeedTail(feeder DataFeeder, feederUserData interface{}, observer NodeObserver, observerUserData interface{}) (count int) {
|
||||
// count = 0
|
||||
// for item := feeder(feederUserData); item != nil; item = feeder(feederUserData) {
|
||||
// // fmt.Println("Item", count, item)
|
||||
@@ -242,7 +253,7 @@ func (ls *LinkedList) FindNext(eqFunc EqualFunc, startNode *ListNode) (targetNod
|
||||
// return
|
||||
// }
|
||||
|
||||
// func (self *LinkedList) FeedTail2(feeder Feeder, observer Observer, abortOnError bool) (count uint32, err error) {
|
||||
// func (self *LinkedList) FeedTail2(feeder Feeder, observer Observer, abortOnError bool) (count int, err error) {
|
||||
// count = 0
|
||||
// // item := feeder.Next()
|
||||
// for item, err1 := feeder.Next(); item != nil; item, err1 = feeder.Next() {
|
||||
|
||||
+24
-23
@@ -73,15 +73,7 @@ func (ls *ListType) ToString(opt FmtOpt) (s string) {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
}
|
||||
if s, ok := item.(string); ok {
|
||||
sb.WriteByte('"')
|
||||
sb.WriteString(s)
|
||||
sb.WriteByte('"')
|
||||
} else if formatter, ok := item.(Formatter); ok {
|
||||
sb.WriteString(formatter.ToString(innerOpt))
|
||||
} else {
|
||||
sb.WriteString(fmt.Sprintf("%v", item))
|
||||
}
|
||||
Format(&sb, item, innerOpt)
|
||||
}
|
||||
if flags&MultiLine != 0 {
|
||||
sb.WriteByte('\n')
|
||||
@@ -104,11 +96,11 @@ func (ls *ListType) TypeName() string {
|
||||
return "list"
|
||||
}
|
||||
|
||||
func (dict *ListType) Contains(t *ListType) (answer bool) {
|
||||
if len(*dict) >= len(*t) {
|
||||
func (ls *ListType) Contains(t *ListType) (answer bool) {
|
||||
if len(*ls) >= len(*t) {
|
||||
answer = true
|
||||
for _, item := range *t {
|
||||
if answer = dict.IndexDeepSameCmp(item) >= 0; !answer {
|
||||
if answer = ls.IndexDeepSameCmp(item) >= 0; !answer {
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -116,10 +108,10 @@ func (dict *ListType) Contains(t *ListType) (answer bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
|
||||
if ls2 != nil && len(*ls1) == len(ls2) {
|
||||
func (ls *ListType) Equals(ls2 ListType) (answer bool) {
|
||||
if ls2 != nil && len(*ls) == len(ls2) {
|
||||
answer = true
|
||||
for index, i1 := range *ls1 {
|
||||
for index, i1 := range *ls {
|
||||
// if !reflect.DeepEqual(i1, ls2[index]) {
|
||||
// answer = false
|
||||
// break
|
||||
@@ -133,11 +125,20 @@ func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (dict *ListType) IndexDeepSameCmp(target any) (index int) {
|
||||
func (ls1 *ListType) Clone() (ls2 *ListType) {
|
||||
ls := make(ListType, len(*ls1))
|
||||
for i, item := range *ls1 {
|
||||
ls[i] = Clone(item)
|
||||
}
|
||||
ls2 = &ls
|
||||
return
|
||||
}
|
||||
|
||||
func (ls *ListType) IndexDeepSameCmp(target any) (index int) {
|
||||
var eq bool
|
||||
var err error
|
||||
index = -1
|
||||
for i, item := range *dict {
|
||||
for i, item := range *ls {
|
||||
if eq, err = deepSame(item, target, SameContent); err != nil {
|
||||
break
|
||||
} else if eq {
|
||||
@@ -188,15 +189,15 @@ func deepSame(a, b any, deepCmp DeepFuncTemplate) (eq bool, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (dict *ListType) SetItem(index int64, value any) (err error) {
|
||||
if index >= 0 && index < int64(len(*dict)) {
|
||||
(*dict)[index] = value
|
||||
func (ls *ListType) SetItem(index int64, value any) (err error) {
|
||||
if index >= 0 && index < int64(len(*ls)) {
|
||||
(*ls)[index] = value
|
||||
} else {
|
||||
err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*dict)-1)
|
||||
err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*ls)-1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (dict *ListType) AppendItem(value any) {
|
||||
*dict = append(*dict, value)
|
||||
func (ls *ListType) AppendItem(value any) {
|
||||
*ls = append(*ls, value)
|
||||
}
|
||||
|
||||
@@ -65,6 +65,38 @@ func AnyInteger(v any) (i int64, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func FixAnyNumber(v any) (fixed any) {
|
||||
switch unboxed := v.(type) {
|
||||
case int:
|
||||
fixed = int64(unboxed)
|
||||
case int8:
|
||||
fixed = int64(unboxed)
|
||||
case int16:
|
||||
fixed = int64(unboxed)
|
||||
case int32:
|
||||
fixed = int64(unboxed)
|
||||
case uint:
|
||||
fixed = int64(unboxed)
|
||||
case uint8:
|
||||
fixed = int64(unboxed)
|
||||
case uint16:
|
||||
fixed = int64(unboxed)
|
||||
case uint32:
|
||||
fixed = int64(unboxed)
|
||||
case uint64:
|
||||
if unboxed <= MaxUint64Allowed {
|
||||
fixed = int64(unboxed)
|
||||
} else {
|
||||
fixed = float64(unboxed)
|
||||
}
|
||||
case float32:
|
||||
fixed = float64(unboxed)
|
||||
default:
|
||||
fixed = v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ToGoInt(value any, description string) (i int, err error) {
|
||||
if valueInt64, ok := value.(int64); ok {
|
||||
i = int(valueInt64)
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// list-iterator.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
type LinkedListIterator struct {
|
||||
a *kern.LinkedList
|
||||
count int64
|
||||
index int64
|
||||
current *kern.ListNode
|
||||
}
|
||||
|
||||
func NewLinkedListIterator(list *kern.LinkedList, args []any) (it *LinkedListIterator) {
|
||||
it = &LinkedListIterator{a: list, count: 0, index: -1, current: list.FirstNode()}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) String() string {
|
||||
var l = int64(0)
|
||||
if it.a != nil {
|
||||
l = int64(it.a.Len())
|
||||
}
|
||||
return fmt.Sprintf("$([<#%d>])", l)
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) TypeName() string {
|
||||
return "LinkedListIterator"
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) HasOperation(name string) bool {
|
||||
yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName}, name)
|
||||
return yes
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
||||
switch name {
|
||||
case kern.NextName:
|
||||
v, err = it.Next()
|
||||
case kern.ResetName:
|
||||
err = it.Reset()
|
||||
case kern.CleanName:
|
||||
err = it.Clean()
|
||||
case kern.IndexName:
|
||||
v = int64(it.Index())
|
||||
case kern.CurrentName:
|
||||
v, err = it.Current()
|
||||
case kern.CountName:
|
||||
v = it.count
|
||||
default:
|
||||
err = kern.ErrNoOperation(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) Current() (item any, err error) {
|
||||
if it.current != nil {
|
||||
item = it.current.Data()
|
||||
} else {
|
||||
err = io.EOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) Next() (item any, err error) {
|
||||
if it.current != nil {
|
||||
item = it.current.Data()
|
||||
it.current = it.current.Next()
|
||||
it.index++
|
||||
it.count++
|
||||
} else {
|
||||
err = io.EOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) Index() int64 {
|
||||
return it.index
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) Count() int64 {
|
||||
return it.count
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) Reset() error {
|
||||
it.current = it.a.FirstNode()
|
||||
it.index = -1
|
||||
it.count = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (it *LinkedListIterator) Clean() error {
|
||||
return nil
|
||||
}
|
||||
+8
-1
@@ -30,7 +30,14 @@ func evalDict(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
if param, err = tree.Compute(ctx); err != nil {
|
||||
break
|
||||
}
|
||||
items[key] = param
|
||||
var keyValue any
|
||||
if keyValue, err = (key.(*scan.Term)).Compute(ctx); err == nil {
|
||||
if kern.IsInteger(keyValue) || kern.IsString(keyValue) {
|
||||
items[keyValue] = param
|
||||
} else {
|
||||
err = key.(*scan.Term).Errorf("dict key can be integer or string, got %s", kern.TypeName(keyValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
v = &items
|
||||
|
||||
@@ -137,6 +137,11 @@ func evalIterator(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
||||
v = NewListIterator(list, args)
|
||||
}
|
||||
} else if list, ok := firstChildValue.(*kern.LinkedList); ok {
|
||||
var args []any
|
||||
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
||||
v = NewLinkedListIterator(list, args)
|
||||
}
|
||||
} else if intVal, ok := firstChildValue.(int64); ok {
|
||||
var args []any
|
||||
if args, err = evalSiblings(ctx, opTerm.Children, intVal); err == nil {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operand-list.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
// -------- list term
|
||||
// func newLinkedListTermA(args ...*scan.Term) *scan.Term {
|
||||
// return newLinkedListTerm(0, 0, args)
|
||||
// }
|
||||
|
||||
func newLinkedListTerm(row, col int, args []*scan.Term) *scan.Term {
|
||||
return &scan.Term{
|
||||
Tk: *scan.NewValueToken(row, col, scan.SymLinkedList, "[<>]", args),
|
||||
Parent: nil,
|
||||
Children: nil,
|
||||
Position: scan.PosLeaf,
|
||||
Priority: scan.PriValue,
|
||||
EvalFunc: evalLinkedList,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- list func
|
||||
func evalLinkedList(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
list, _ := opTerm.Value().([]*scan.Term)
|
||||
items := kern.NewLinkedList()
|
||||
for _, tree := range list {
|
||||
var param any
|
||||
if param, err = tree.Compute(ctx); err != nil {
|
||||
break
|
||||
}
|
||||
items.PushBack(param)
|
||||
}
|
||||
if err == nil {
|
||||
v = items
|
||||
}
|
||||
return
|
||||
}
|
||||
+66
-6
@@ -22,6 +22,26 @@ func newAssignTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
}
|
||||
}
|
||||
|
||||
func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
v, err = generalEvalAssign(ctx, opTerm, false)
|
||||
return
|
||||
}
|
||||
|
||||
func newDeepCopyAssignTerm(tk *scan.Token) (inst *scan.Term) {
|
||||
return &scan.Term{
|
||||
Tk: *tk,
|
||||
Children: make([]*scan.Term, 0, 2),
|
||||
Position: scan.PosInfix,
|
||||
Priority: scan.PriAssign,
|
||||
EvalFunc: evalDeepCopyAssign,
|
||||
}
|
||||
}
|
||||
|
||||
func evalDeepCopyAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
v, err = generalEvalAssign(ctx, opTerm, true)
|
||||
return
|
||||
}
|
||||
|
||||
func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term, value any) (err error) {
|
||||
var collectionValue, keyListValue, keyValue any
|
||||
var keyList *kern.ListType
|
||||
@@ -57,23 +77,62 @@ func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *sca
|
||||
return
|
||||
}
|
||||
|
||||
func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any) (err error) {
|
||||
func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any, deepCopy bool) (err error) {
|
||||
if leftTerm.Symbol() == scan.SymIndex {
|
||||
err = assignCollectionItem(ctx, leftTerm.Children[0], leftTerm.Children[1], v)
|
||||
} else {
|
||||
if deepCopy {
|
||||
v = kern.Clone(v)
|
||||
}
|
||||
ctx.UnsafeSetVar(leftTerm.Source(), v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
func evalAssignDictItem(ctx kern.ExprContext, dotTerm *scan.Term, valueTerm kern.Term) (v any, err error) {
|
||||
var ok bool
|
||||
var dictAny, dotKey any
|
||||
var dict *kern.DictType
|
||||
|
||||
if err = dotTerm.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dotLeftTerm := dotTerm.GetChild(0)
|
||||
if dictAny, err = dotLeftTerm.Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
if dict, ok = dictAny.(*kern.DictType); !ok {
|
||||
err = dotTerm.Tk.ErrorExpectedGot(kern.DictTypeName)
|
||||
return
|
||||
}
|
||||
|
||||
dotRightTerm := dotTerm.Children[1]
|
||||
dotRightSym := dotRightTerm.Symbol()
|
||||
if dotRightSym == scan.SymVariable || dotRightSym == scan.SymString {
|
||||
dotKey = util.UnquoteString(dotRightTerm.Source())
|
||||
} else if dotKey, err = dotRightTerm.Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if v, err = valueTerm.Compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dict.SetItem(dotKey, v)
|
||||
return
|
||||
}
|
||||
|
||||
func generalEvalAssign(ctx kern.ExprContext, opTerm *scan.Term, deepCopy bool) (v any, err error) {
|
||||
if err = opTerm.CheckOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftTerm := opTerm.Children[0]
|
||||
leftSym := leftTerm.Symbol()
|
||||
if leftSym != scan.SymVariable && leftSym != scan.SymIndex {
|
||||
if leftSym == scan.SymDot {
|
||||
return evalAssignDictItem(ctx, opTerm.Children[0], opTerm.GetChild(1))
|
||||
} else if leftSym != scan.SymVariable && leftSym != scan.SymIndex {
|
||||
err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source())
|
||||
return
|
||||
}
|
||||
@@ -93,10 +152,10 @@ func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
err = opTerm.Errorf("unknown function %s()", rightChild.Source())
|
||||
}
|
||||
} else {
|
||||
err = assignValue(ctx, leftTerm, v)
|
||||
err = assignValue(ctx, leftTerm, v, deepCopy)
|
||||
}
|
||||
} else {
|
||||
err = assignValue(ctx, leftTerm, v)
|
||||
err = assignValue(ctx, leftTerm, v, deepCopy)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
@@ -203,7 +262,7 @@ func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
err = opTerm.Errorf("unsupported assign operator %q", opTerm.Source())
|
||||
}
|
||||
if err == nil {
|
||||
err = assignValue(ctx, leftTerm, v)
|
||||
err = assignValue(ctx, leftTerm, v, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -213,6 +272,7 @@ func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
// init
|
||||
func init() {
|
||||
scan.RegisterTermConstructor(scan.SymEqual, newAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymColonEqual, newDeepCopyAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymPlusEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymMinusEqual, newOpAssignTerm)
|
||||
scan.RegisterTermConstructor(scan.SymStarEqual, newOpAssignTerm)
|
||||
|
||||
+8
-22
@@ -40,36 +40,22 @@ func evalContextValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error
|
||||
}
|
||||
|
||||
if sourceCtx != nil {
|
||||
if formatter, ok := sourceCtx.(kern.DictFormat); ok {
|
||||
v = formatter.ToDict()
|
||||
} else if formatter, ok := sourceCtx.(kern.Formatter); ok {
|
||||
v = formatter.ToString(0)
|
||||
} else {
|
||||
// keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
|
||||
keys := sourceCtx.EnumVars(nil)
|
||||
d := make(map[string]any)
|
||||
for _, key := range keys {
|
||||
d[key], _ = sourceCtx.GetVar(key)
|
||||
}
|
||||
keys = sourceCtx.EnumFuncs(func(name string) bool { return true })
|
||||
for _, key := range keys {
|
||||
d[key], _ = sourceCtx.GetFuncInfo(key)
|
||||
}
|
||||
v = d
|
||||
}
|
||||
v = sourceCtx.ToDict()
|
||||
} else if childValue != nil {
|
||||
if it, ok := childValue.(kern.Iterator); ok {
|
||||
it, ok := childValue.(kern.Iterator)
|
||||
if !ok {
|
||||
it, err = NewIterator(ctx, childValue, nil)
|
||||
}
|
||||
if err == nil {
|
||||
var item any
|
||||
values := kern.NewListA()
|
||||
values := kern.NewLinkedListA()
|
||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||
values.AppendItem(item)
|
||||
values.PushBack(item)
|
||||
}
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
v = values
|
||||
}
|
||||
} else {
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||
}
|
||||
} else {
|
||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||
|
||||
+46
-16
@@ -7,6 +7,7 @@ package expr
|
||||
import (
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
"git.portale-stac.it/go-pkg/expr/util"
|
||||
)
|
||||
|
||||
// -------- dot term
|
||||
@@ -34,7 +35,7 @@ func evalDot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
|
||||
switch unboxedValue := leftValue.(type) {
|
||||
case kern.ExtIterator:
|
||||
if indexTerm.Tk.Sym == scan.SymVariable /*|| indexTerm.Tk.Sym == scan.SymString */ {
|
||||
if indexTerm.Symbol() == scan.SymVariable /*|| indexTerm.Tk.Sym == scan.SymString */ {
|
||||
opName := indexTerm.Source()
|
||||
if unboxedValue.HasOperation(opName) {
|
||||
v, err = unboxedValue.CallOperation(opName, map[string]any{})
|
||||
@@ -46,21 +47,22 @@ func evalDot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
err = indexTerm.Tk.ErrorExpectedGot("identifier")
|
||||
}
|
||||
case *kern.DictType:
|
||||
var ok bool
|
||||
s := opTerm.Children[1].Tk.Sym
|
||||
if s == scan.SymVariable || s == scan.SymString {
|
||||
src := opTerm.Children[1].Source()
|
||||
if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
|
||||
src = src[1 : len(src)-1]
|
||||
}
|
||||
if v, ok = unboxedValue.GetItem(src); !ok {
|
||||
err = opTerm.Errorf("key %q not found", src)
|
||||
}
|
||||
} else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||
if v, ok = unboxedValue.GetItem(rightValue); !ok {
|
||||
err = opTerm.Errorf("key %q not found", rightValue)
|
||||
}
|
||||
}
|
||||
// var ok bool
|
||||
// s := opTerm.Children[1].Symbol()
|
||||
// if s == scan.SymVariable || s == scan.SymString {
|
||||
// src := opTerm.Children[1].Source()
|
||||
// if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
|
||||
// src = src[1 : len(src)-1]
|
||||
// }
|
||||
// if v, ok = unboxedValue.GetItem(src); !ok {
|
||||
// err = opTerm.Errorf("key %q not found", src)
|
||||
// }
|
||||
// } else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||
// if v, ok = unboxedValue.GetItem(rightValue); !ok {
|
||||
// err = opTerm.Errorf("key %q not found", rightValue)
|
||||
// }
|
||||
// }
|
||||
v, err = dotGetDictItemValue(ctx, unboxedValue, opTerm.Children[1])
|
||||
default:
|
||||
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||
err = opTerm.Errorf("incompatible types: %s and %s", kern.TypeName(leftValue), kern.TypeName(rightValue))
|
||||
@@ -69,6 +71,34 @@ func evalDot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func dotGetDictItemValue(ctx kern.ExprContext, d *kern.DictType, rightTerm *scan.Term) (v any, err error) {
|
||||
var ok bool
|
||||
var rightValue any
|
||||
s := rightTerm.Symbol()
|
||||
if s == scan.SymVariable || s == scan.SymString {
|
||||
// src := rightTerm.Source()
|
||||
// if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
|
||||
// src = src[1 : len(src)-1]
|
||||
// }
|
||||
src := util.UnquoteString(rightTerm.Source())
|
||||
if v, ok = d.GetItem(src); !ok {
|
||||
err = errDictKeyNotFound(rightTerm, src)
|
||||
}
|
||||
} else if rightValue, err = rightTerm.Compute(ctx); err == nil {
|
||||
if v, ok = d.GetItem(rightValue); !ok {
|
||||
err = errDictKeyNotFound(rightTerm, rightValue)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func errDictKeyNotFound(term *scan.Term, key any) error {
|
||||
if s, ok := key.(string); ok {
|
||||
return term.Errorf("key %q not found", s)
|
||||
}
|
||||
return term.Errorf("key %v not found", key)
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
scan.RegisterTermConstructor(scan.SymDot, newDotTerm)
|
||||
|
||||
@@ -89,6 +89,11 @@ func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
||||
v = (*unboxedValue)[index]
|
||||
}
|
||||
case *kern.LinkedList:
|
||||
var index int
|
||||
if index, err = verifyIndex(indexTerm, indexList, unboxedValue.Len()); err == nil {
|
||||
v, err = unboxedValue.At(index)
|
||||
}
|
||||
case string:
|
||||
var index int
|
||||
if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil {
|
||||
@@ -107,6 +112,11 @@ func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
sublist := kern.ListType((*unboxedValue)[start:end])
|
||||
v = &sublist
|
||||
}
|
||||
case *kern.LinkedList:
|
||||
var start, end int
|
||||
if start, end, err = verifyRange(indexTerm, indexList, unboxedValue.Len()); err == nil {
|
||||
v = unboxedValue.Sub(start, end)
|
||||
}
|
||||
case string:
|
||||
var start, end int
|
||||
if start, end, err = verifyRange(indexTerm, indexList, len(unboxedValue)); err == nil {
|
||||
|
||||
@@ -37,6 +37,8 @@ func evalLength(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||
} else if kern.IsDict(childValue) {
|
||||
m, _ := childValue.(*kern.DictType)
|
||||
v = int64(len(*m))
|
||||
} else if lls, ok := childValue.(*kern.LinkedList); ok {
|
||||
v = int64(lls.Len())
|
||||
} else if it, ok := childValue.(kern.Iterator); ok {
|
||||
v = int64(it.Count())
|
||||
// if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
|
||||
|
||||
@@ -6,6 +6,8 @@ package expr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
"golang.org/x/exp/constraints"
|
||||
@@ -163,16 +165,16 @@ func paramAlreadyDefined(args []*scan.Term, param *scan.Term) (position int) {
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext) (listTerm *scan.Term, err error) {
|
||||
func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext, termSym scan.Symbol) (listTerm *scan.Term, err error) {
|
||||
r, c := scanner.LastPos()
|
||||
args := make([]*scan.Term, 0)
|
||||
lastSym := scan.SymUnknown
|
||||
itemExpected := false
|
||||
itemCtx := remFlags(ctx, allowIndex)
|
||||
for lastSym != scan.SymClosedSquare && lastSym != scan.SymEos {
|
||||
for lastSym != termSym && lastSym != scan.SymEos {
|
||||
zeroRequired := scanner.Current().Sym == scan.SymColon
|
||||
var itemTree *scan.Ast
|
||||
if itemTree, err = parser.parseItem(scanner, itemCtx, scan.SymComma, scan.SymClosedSquare); err == nil {
|
||||
if itemTree, err = parser.parseItem(scanner, itemCtx, scan.SymComma, termSym); err == nil {
|
||||
root := itemTree.Root()
|
||||
if root != nil {
|
||||
if hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymColon {
|
||||
@@ -211,10 +213,14 @@ func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext) (listT
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if lastSym != scan.SymClosedSquare {
|
||||
if lastSym != termSym {
|
||||
err = scanner.Previous().ErrorExpectedGot("]")
|
||||
} else {
|
||||
} else if termSym == scan.SymClosedSquare {
|
||||
listTerm = newListTerm(r, c, args)
|
||||
} else if termSym == scan.SymGreaterClosedSquare {
|
||||
listTerm = newLinkedListTerm(r, c, args)
|
||||
} else {
|
||||
err = fmt.Errorf("[%d:%d] unknown list type", r, c)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -251,24 +257,41 @@ func (parser *parser) parseIterDef(scanner *scan.Scanner, ctx parserContext) (su
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseDictKey(scanner *scan.Scanner) (key any, err error) {
|
||||
tk := parser.Next(scanner)
|
||||
if tk.Sym == scan.SymError {
|
||||
err = tk.Error()
|
||||
return
|
||||
// func (parser *parser) parseDictKey(scanner *scan.Scanner) (key any, err error) {
|
||||
// tk := parser.Next(scanner)
|
||||
// if tk.Sym == scan.SymError {
|
||||
// err = tk.Error()
|
||||
// return
|
||||
// }
|
||||
// if tk.Sym == scan.SymClosedBrace || tk.Sym == scan.SymEos {
|
||||
// return
|
||||
// }
|
||||
// if tk.Sym == scan.SymInteger || tk.Sym == scan.SymString || tk.Sym == scan.SymIdentifier {
|
||||
// tkSep := parser.Next(scanner)
|
||||
// if tkSep.Sym != scan.SymColon {
|
||||
// err = tkSep.ErrorExpectedGot(":")
|
||||
// } else {
|
||||
// key = tk.Value
|
||||
// }
|
||||
// } else {
|
||||
// err = tk.ErrorExpectedGot("dictionary-key or }")
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (parser *parser) parseDictKey(scanner *scan.Scanner) (key *scan.Term, err error) {
|
||||
var keyTree *scan.Ast
|
||||
if keyTree, err = parser.parseItem(scanner, parserNoFlags, scan.SymColon, scan.SymClosedBrace); err == nil {
|
||||
key = keyTree.Root()
|
||||
tkSep := scanner.Previous()
|
||||
sym := tkSep.Sym
|
||||
if sym == scan.SymClosedBrace || sym == scan.SymEos {
|
||||
if key != nil {
|
||||
err = tkSep.ErrorExpectedGot(":")
|
||||
}
|
||||
if tk.Sym == scan.SymClosedBrace || tk.Sym == scan.SymEos {
|
||||
return
|
||||
}
|
||||
if tk.Sym == scan.SymInteger || tk.Sym == scan.SymString {
|
||||
tkSep := parser.Next(scanner)
|
||||
if tkSep.Sym != scan.SymColon {
|
||||
} else if sym != scan.SymColon {
|
||||
err = tkSep.ErrorExpectedGot(":")
|
||||
} else {
|
||||
key = tk.Value
|
||||
}
|
||||
} else {
|
||||
err = tk.ErrorExpectedGot("dictionary-key or }")
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -278,11 +301,11 @@ func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext)
|
||||
lastSym := scan.SymUnknown
|
||||
itemExpected := false
|
||||
for lastSym != scan.SymClosedBrace && lastSym != scan.SymEos {
|
||||
var subTree *scan.Ast
|
||||
var valueTree *scan.Ast
|
||||
var key any
|
||||
if key, err = parser.parseDictKey(scanner); err != nil {
|
||||
break
|
||||
} else if key == nil {
|
||||
} else if key.(*scan.Term) == nil {
|
||||
tk := scanner.Previous()
|
||||
lastSym = tk.Sym
|
||||
if itemExpected {
|
||||
@@ -290,9 +313,9 @@ func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext)
|
||||
}
|
||||
break
|
||||
}
|
||||
if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedBrace); err == nil {
|
||||
if subTree.Root() != nil {
|
||||
args[key] = subTree.Root()
|
||||
if valueTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedBrace); err == nil {
|
||||
if valueTree.Root() != nil {
|
||||
args[key] = valueTree.Root()
|
||||
} else /*if key != nil*/ {
|
||||
prev := scanner.Previous()
|
||||
err = prev.ErrorExpectedGot("dictionary-value")
|
||||
@@ -326,7 +349,7 @@ func (parser *parser) parseSelectorCase(scanner *scan.Scanner, ctx parserContext
|
||||
err = tk.Errorf("case list in default clause")
|
||||
return
|
||||
}
|
||||
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex)); err != nil {
|
||||
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex), scan.SymClosedSquare); err != nil {
|
||||
return
|
||||
}
|
||||
tk = parser.Next(scanner)
|
||||
@@ -401,7 +424,8 @@ func couldBeACollection(t *scan.Term) bool {
|
||||
if t != nil {
|
||||
sym = t.Symbol()
|
||||
}
|
||||
return sym == scan.SymList || sym == scan.SymString || sym == scan.SymDict || sym == scan.SymExpression || sym == scan.SymVariable
|
||||
// return sym == scan.SymList || sym == scan.SymString || sym == scan.SymDict || sym == scan.SymExpression || sym == scan.SymVariable
|
||||
return slices.Contains([]scan.Symbol{scan.SymList, scan.SymLinkedList, scan.SymString, scan.SymDict, scan.SymExpression, scan.SymVariable, scan.SymIndex}, sym)
|
||||
}
|
||||
|
||||
func listSubTree(tree *scan.Ast, listTerm *scan.Term, allowIndeces bool) (root *scan.Term, err error) {
|
||||
@@ -444,7 +468,7 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
||||
tree = scan.NewAst()
|
||||
firstToken := true
|
||||
// lastSym := SymUnknown
|
||||
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) {
|
||||
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); tk = parser.Next(scanner) {
|
||||
// if tk.Sym == SymComment {
|
||||
// continue
|
||||
// }
|
||||
@@ -457,7 +481,7 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
||||
selectorTerm = nil
|
||||
continue
|
||||
} else {
|
||||
err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.Source())
|
||||
err = tk.ErrorExpectedOneOfGot(termSymbols...)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -489,7 +513,13 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
||||
case scan.SymOpenSquare:
|
||||
var listTerm *scan.Term
|
||||
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
|
||||
if listTerm, err = parser.parseList(scanner, newCtx); err == nil {
|
||||
if listTerm, err = parser.parseList(scanner, newCtx, scan.SymClosedSquare); err == nil {
|
||||
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||
}
|
||||
case scan.SymOpenSquareLess:
|
||||
var listTerm *scan.Term
|
||||
newCtx := addFlagsCond(addFlags(ctx, listContext), allowIndex, false)
|
||||
if listTerm, err = parser.parseList(scanner, newCtx, scan.SymGreaterClosedSquare); err == nil {
|
||||
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||
}
|
||||
case scan.SymOpenBrace:
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ func (ast *Ast) insert(tree, node *Term) (root *Term, err error) {
|
||||
root = node
|
||||
tree.SetParent(node)
|
||||
} else {
|
||||
err = node.Errorf("two adjacent operators: %q and %q", tree, node)
|
||||
err = node.Errorf("two adjacent operators: %q and %q", tree, node.Source())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -192,6 +192,8 @@ func (scanner *Scanner) fetchNextToken() (tk *Token) {
|
||||
case ':':
|
||||
if next, _ := scanner.peek(); next == ':' {
|
||||
tk = scanner.moveOn(SymDoubleColon, ch, next)
|
||||
} else if next == '=' {
|
||||
tk = scanner.moveOn(SymColonEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.MakeToken(SymColon, ch)
|
||||
}
|
||||
@@ -313,6 +315,8 @@ func (scanner *Scanner) fetchNextToken() (tk *Token) {
|
||||
} else {
|
||||
tk = scanner.accept(SymDoubleGreater, ch, next)
|
||||
}
|
||||
} else if next == ']' {
|
||||
tk = scanner.moveOn(SymGreaterClosedSquare, ch, next)
|
||||
} else {
|
||||
tk = scanner.MakeToken(SymGreater, ch)
|
||||
}
|
||||
@@ -342,7 +346,11 @@ func (scanner *Scanner) fetchNextToken() (tk *Token) {
|
||||
case ')':
|
||||
tk = scanner.MakeToken(SymClosedRound, ch)
|
||||
case '[':
|
||||
if next, _ := scanner.peek(); next == '<' {
|
||||
tk = scanner.moveOn(SymOpenSquareLess, ch, next)
|
||||
} else {
|
||||
tk = scanner.MakeToken(SymOpenSquare, ch)
|
||||
}
|
||||
case ']':
|
||||
tk = scanner.MakeToken(SymClosedSquare, ch)
|
||||
case '{':
|
||||
|
||||
+45
-60
@@ -64,47 +64,50 @@ func init() {
|
||||
SymAt: {"@", SymClassOperator, PosPrefix}, // 28: '@'
|
||||
SymUndescore: {"_", SymClassIdentifier, PosLeaf}, // 29: '_'
|
||||
SymEqual: {"=", SymClassOperator, PosInfix}, // 30: '='
|
||||
SymDoubleEqual: {"==", SymClassOperator, PosInfix}, // 31: '=='
|
||||
SymLess: {"<", SymClassOperator, PosInfix}, // 32: '<'
|
||||
SymLessOrEqual: {"<=", SymClassOperator, PosInfix}, // 33: '<='
|
||||
SymGreater: {">", SymClassOperator, PosInfix}, // 34: '>'
|
||||
SymGreaterOrEqual: {">=", SymClassOperator, PosInfix}, // 35: '>='
|
||||
SymLessGreater: {"<>", SymClassOperator, PosInfix}, // 36: '<>'
|
||||
SymNotEqual: {"!=", SymClassOperator, PosInfix}, // 37: '!='
|
||||
SymDollar: {"$", SymClassOperator, PosPrefix}, // 38: '$'
|
||||
SymHash: {"#", SymClassOperator, PosPrefix}, // 39: '#'
|
||||
SymOpenRound: {"(", SymClassParenthesis, PosPrefix}, // 40: '('
|
||||
SymClosedRound: {")", SymClassParenthesis, PosPostfix}, // 41: ')'
|
||||
SymOpenSquare: {"[", SymClassParenthesis, PosPrefix}, // 42: '['
|
||||
SymClosedSquare: {"]", SymClassParenthesis, PosPostfix}, // 43: ']'
|
||||
SymOpenBrace: {"{", SymClassParenthesis, PosPrefix}, // 44: '{'
|
||||
SymClosedBrace: {"}", SymClassParenthesis, PosPostfix}, // 45: '}'
|
||||
SymTilde: {"~", SymClassOperator, PosPrefix}, // 46: '~'
|
||||
SymDoubleQuestion: {"??", SymClassOperator, PosInfix}, // 47: '??'
|
||||
SymQuestionEqual: {"?=", SymClassOperator, PosInfix}, // 48: '?='
|
||||
SymQuestionExclam: {"?!", SymClassOperator, PosInfix}, // 49: '?!'
|
||||
SymDoubleAt: {"@@", SymClassCommand, PosLeaf}, // 50: '@@'
|
||||
SymDoubleColon: {"::", SymClassOperator, PosInfix}, // 51: '::'
|
||||
SymDoubleGreater: {">>", SymClassOperator, PosInfix}, // 52: '>>'
|
||||
SymDoubleLess: {"<<", SymClassOperator, PosInfix}, // 53: '<<'
|
||||
SymCaret: {"^", SymClassOperator, PosInfix}, // 54: '^'
|
||||
SymDollarRound: {"$(", SymClassOperator, PosPrefix}, // 55: '$('
|
||||
SymOpenClosedRound: {"()", SymClassOperator, PosPostfix}, // 56: '()'
|
||||
SymDoubleDollar: {"$$", SymClassCommand, PosLeaf}, // 57: '$$'
|
||||
SymDoubleDot: {"..", SymClassOperator, PosInfix}, // 58: '..'
|
||||
SymTripleDot: {"...", SymClassOperator, PosPostfix}, // 59: '...'
|
||||
SymStarEqual: {"*=", SymClassOperator, PosInfix}, // 60: '*='
|
||||
SymSlashEqual: {"/=", SymClassOperator, PosInfix}, // 61: '/='
|
||||
SymPercEqual: {"%=", SymClassOperator, PosInfix}, // 62: '%='
|
||||
SymDoubleLessEqual: {"<<=", SymClassOperator, PosInfix}, // 63: '<<='
|
||||
SymDoubleGreaterEqual: {">>=", SymClassOperator, PosInfix}, // 64: '>>='
|
||||
SymAmpersandEqual: {"&=", SymClassOperator, PosInfix}, // 65: '&='
|
||||
SymVertBarEqual: {"|=", SymClassOperator, PosInfix}, // 65: '|='
|
||||
SymCaretEqual: {"^=", SymClassOperator, PosInfix}, // 66: '^='
|
||||
SymPlusGreater: {"+>", SymClassOperator, PosInfix}, // 67: '+>'
|
||||
SymLessPlus: {"<+", SymClassOperator, PosInfix}, // 68: '<+'
|
||||
SymPreInc: {"++", SymClassOperator, PosPrefix}, // : '++'
|
||||
SymPreDec: {"--", SymClassOperator, PosPrefix}, // : '--'
|
||||
SymColonEqual: {":=", SymClassOperator, PosInfix}, // 31: ':='
|
||||
SymDoubleEqual: {"==", SymClassOperator, PosInfix}, // 32: '=='
|
||||
SymLess: {"<", SymClassOperator, PosInfix}, // 33: '<'
|
||||
SymLessOrEqual: {"<=", SymClassOperator, PosInfix}, // 34: '<='
|
||||
SymGreater: {">", SymClassOperator, PosInfix}, // 35: '>'
|
||||
SymGreaterOrEqual: {">=", SymClassOperator, PosInfix}, // 36: '>='
|
||||
SymLessGreater: {"<>", SymClassOperator, PosInfix}, // 37: '<>'
|
||||
SymNotEqual: {"!=", SymClassOperator, PosInfix}, // 38: '!='
|
||||
SymDollar: {"$", SymClassOperator, PosPrefix}, // 39: '$'
|
||||
SymHash: {"#", SymClassOperator, PosPrefix}, // 40: '#'
|
||||
SymOpenRound: {"(", SymClassParenthesis, PosPrefix}, // 41: '('
|
||||
SymClosedRound: {")", SymClassParenthesis, PosPostfix}, // 42: ')'
|
||||
SymOpenSquare: {"[", SymClassParenthesis, PosPrefix}, // 43: '['
|
||||
SymClosedSquare: {"]", SymClassParenthesis, PosPostfix}, // 44: ']'
|
||||
SymOpenBrace: {"{", SymClassParenthesis, PosPrefix}, // 45: '{'
|
||||
SymClosedBrace: {"}", SymClassParenthesis, PosPostfix}, // 46: '}'
|
||||
SymTilde: {"~", SymClassOperator, PosPrefix}, // 47: '~'
|
||||
SymDoubleQuestion: {"??", SymClassOperator, PosInfix}, // 48: '??'
|
||||
SymQuestionEqual: {"?=", SymClassOperator, PosInfix}, // 49: '?='
|
||||
SymQuestionExclam: {"?!", SymClassOperator, PosInfix}, // 50: '?!'
|
||||
SymDoubleAt: {"@@", SymClassCommand, PosLeaf}, // 51: '@@'
|
||||
SymDoubleColon: {"::", SymClassOperator, PosInfix}, // 52: '::'
|
||||
SymDoubleGreater: {">>", SymClassOperator, PosInfix}, // 53: '>>'
|
||||
SymDoubleLess: {"<<", SymClassOperator, PosInfix}, // 54: '<<'
|
||||
SymCaret: {"^", SymClassOperator, PosInfix}, // 55: '^'
|
||||
SymDollarRound: {"$(", SymClassOperator, PosPrefix}, // 56: '$('
|
||||
SymOpenClosedRound: {"()", SymClassOperator, PosPostfix}, // 57: '()'
|
||||
SymDoubleDollar: {"$$", SymClassCommand, PosLeaf}, // 58: '$$'
|
||||
SymDoubleDot: {"..", SymClassOperator, PosInfix}, // 59: '..'
|
||||
SymTripleDot: {"...", SymClassOperator, PosPostfix}, // 60: '...'
|
||||
SymStarEqual: {"*=", SymClassOperator, PosInfix}, // 61: '*='
|
||||
SymSlashEqual: {"/=", SymClassOperator, PosInfix}, // 62: '/='
|
||||
SymPercEqual: {"%=", SymClassOperator, PosInfix}, // 63: '%='
|
||||
SymDoubleLessEqual: {"<<=", SymClassOperator, PosInfix}, // 64: '<<='
|
||||
SymDoubleGreaterEqual: {">>=", SymClassOperator, PosInfix}, // 65: '>>='
|
||||
SymAmpersandEqual: {"&=", SymClassOperator, PosInfix}, // 66: '&='
|
||||
SymVertBarEqual: {"|=", SymClassOperator, PosInfix}, // 67: '|='
|
||||
SymCaretEqual: {"^=", SymClassOperator, PosInfix}, // 68: '^='
|
||||
SymPlusGreater: {"+>", SymClassOperator, PosInfix}, // 69: '+>'
|
||||
SymLessPlus: {"<+", SymClassOperator, PosInfix}, // 70: '<+'
|
||||
SymPreInc: {"++", SymClassOperator, PosPrefix}, // 71: '++'
|
||||
SymPreDec: {"--", SymClassOperator, PosPrefix}, // 72: '--'
|
||||
SymOpenSquareLess: {"[<", SymClassOperator, PosPrefix}, // 97: '[<'
|
||||
SymGreaterClosedSquare: {">]", SymClassOperator, PosPostfix}, // 98: '>]'
|
||||
// SymChangeSign
|
||||
// SymUnchangeSign
|
||||
// SymIdentifier
|
||||
@@ -122,6 +125,7 @@ func init() {
|
||||
// SymFuncCall
|
||||
// SymFuncDef
|
||||
// SymList
|
||||
// SymLinkedList
|
||||
// SymDict
|
||||
// SymIndex
|
||||
// SymExpression
|
||||
@@ -184,25 +188,6 @@ func StringEndsWithOperator(s string) bool {
|
||||
return endingOperator(s) != SymNone
|
||||
}
|
||||
|
||||
// func endingOperator(s string) (sym Symbol) {
|
||||
// var matchLength = 0
|
||||
// sym = SymNone
|
||||
// lower := strings.TrimRight(strings.ToLower(s), " \t")
|
||||
// for symbol, spec := range symbolMap {
|
||||
// if strings.HasSuffix(lower, spec.repr) {
|
||||
// if len(spec.repr) > matchLength {
|
||||
// matchLength = len(spec.repr)
|
||||
// if spec.kind == symClassOperator && (spec.opType == PosInfix || spec.opType == PosPrefix) {
|
||||
// sym = symbol
|
||||
// } else {
|
||||
// sym = SymNone
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func endingOperator(s string) (sym Symbol) {
|
||||
var matchLength = 0
|
||||
var repr string
|
||||
|
||||
+70
-66
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// Symbol.go
|
||||
// symbol.go
|
||||
package scan
|
||||
|
||||
type Symbol int16
|
||||
@@ -39,71 +39,75 @@ const (
|
||||
SymAt // 28: '@'
|
||||
SymUndescore // 29: '_'
|
||||
SymEqual // 30: '='
|
||||
SymDoubleEqual // 31: '=='
|
||||
SymLess // 32: '<'
|
||||
SymLessOrEqual // 33: '<='
|
||||
SymGreater // 34: '>'
|
||||
SymGreaterOrEqual // 35: '>='
|
||||
SymLessGreater // 36: '<>'
|
||||
SymNotEqual // 37: '!='
|
||||
SymDollar // 38: '$'
|
||||
SymHash // 39: '#'
|
||||
SymOpenRound // 40: '('
|
||||
SymClosedRound // 41: ')'
|
||||
SymOpenSquare // 42: '['
|
||||
SymClosedSquare // 43: ']'
|
||||
SymOpenBrace // 44: '{'
|
||||
SymClosedBrace // 45: '}'
|
||||
SymTilde // 46: '~'
|
||||
SymDoubleQuestion // 47: '??'
|
||||
SymQuestionEqual // 48: '?='
|
||||
SymQuestionExclam // 49: '?!'
|
||||
SymDoubleAt // 50: '@@'
|
||||
SymDoubleColon // 51: '::'
|
||||
SymDoubleGreater // 52: '>>'
|
||||
SymDoubleLess // 53: '<<'
|
||||
SymCaret // 54: '^'
|
||||
SymDollarRound // 55: '$('
|
||||
SymOpenClosedRound // 56: '()'
|
||||
SymDoubleDollar // 57: '$$'
|
||||
SymDoubleDot // 58: '..'
|
||||
SymTripleDot // 59: '...'
|
||||
SymStarEqual // 60: '*='
|
||||
SymSlashEqual // 61: '/='
|
||||
SymPercEqual // 62: '%='
|
||||
SymDoubleLessEqual // 63: '<<='
|
||||
SymDoubleGreaterEqual // 64: '>>='
|
||||
SymAmpersandEqual // 65: '&='
|
||||
SymVertBarEqual // 65: '|='
|
||||
SymCaretEqual // 66: '^='
|
||||
SymPlusGreater // 67: '+>'
|
||||
SymLessPlus // 68: '<+'
|
||||
SymChangeSign
|
||||
SymUnchangeSign
|
||||
SymDereference
|
||||
SymPreInc
|
||||
SymPreDec
|
||||
SymIdentifier
|
||||
SymBool
|
||||
SymInteger
|
||||
SymVariable
|
||||
SymFloat
|
||||
SymFraction
|
||||
SymString
|
||||
SymIterator
|
||||
SymOr
|
||||
SymAnd
|
||||
SymNot
|
||||
SymComment
|
||||
SymFuncCall
|
||||
SymFuncDef
|
||||
SymList
|
||||
SymDict
|
||||
SymIndex
|
||||
SymRange // [index : index]
|
||||
SymExpression
|
||||
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||
SymColonEqual // 31: ':='
|
||||
SymDoubleEqual // 32: '=='
|
||||
SymLess // 33: '<'
|
||||
SymLessOrEqual // 34: '<='
|
||||
SymGreater // 35: '>'
|
||||
SymGreaterOrEqual // 36: '>='
|
||||
SymLessGreater // 37: '<>'
|
||||
SymNotEqual // 38: '!='
|
||||
SymDollar // 39: '$'
|
||||
SymHash // 40: '#'
|
||||
SymOpenRound // 41: '('
|
||||
SymClosedRound // 42: ')'
|
||||
SymOpenSquare // 43: '['
|
||||
SymClosedSquare // 44: ']'
|
||||
SymOpenBrace // 45: '{'
|
||||
SymClosedBrace // 46: '}'
|
||||
SymTilde // 47: '~'
|
||||
SymDoubleQuestion // 48: '??'
|
||||
SymQuestionEqual // 49: '?='
|
||||
SymQuestionExclam // 50: '?!'
|
||||
SymDoubleAt // 51: '@@'
|
||||
SymDoubleColon // 52: '::'
|
||||
SymDoubleGreater // 53: '>>'
|
||||
SymDoubleLess // 54: '<<'
|
||||
SymCaret // 55: '^'
|
||||
SymDollarRound // 56: '$('
|
||||
SymOpenClosedRound // 57: '()'
|
||||
SymDoubleDollar // 58: '$$'
|
||||
SymDoubleDot // 59: '..'
|
||||
SymTripleDot // 60: '...'
|
||||
SymStarEqual // 61: '*='
|
||||
SymSlashEqual // 62: '/='
|
||||
SymPercEqual // 63: '%='
|
||||
SymDoubleLessEqual // 64: '<<='
|
||||
SymDoubleGreaterEqual // 65: '>>='
|
||||
SymAmpersandEqual // 66: '&='
|
||||
SymVertBarEqual // 67: '|='
|
||||
SymCaretEqual // 68: '^='
|
||||
SymPlusGreater // 69: '+>'
|
||||
SymLessPlus // 70: '<+'
|
||||
SymChangeSign // 71: '-'
|
||||
SymUnchangeSign // 72: '+''
|
||||
SymDereference // 73: '*'
|
||||
SymPreInc // 74: '++'
|
||||
SymPreDec // 75: '--'
|
||||
SymIdentifier // 76: identifier
|
||||
SymBool // 77: boolean
|
||||
SymInteger // 78: integer
|
||||
SymVariable // 79: variable
|
||||
SymFloat // 80: float
|
||||
SymFraction // 81: fraction
|
||||
SymString // 82: string
|
||||
SymIterator // 83: iterator
|
||||
SymOr // 84: 'or'
|
||||
SymAnd // 85: 'and'
|
||||
SymNot // 86: 'not'
|
||||
SymComment // 87: comment
|
||||
SymFuncCall // 88: function call
|
||||
SymFuncDef // 89: function definition
|
||||
SymList // 90: list
|
||||
SymDict // 91: dict
|
||||
SymIndex // 92: index
|
||||
SymRange // 93: range [index : index]
|
||||
SymExpression // 94: expression
|
||||
SymSelector // 95: selector <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||
SymSelectorCase // 96: <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||
SymOpenSquareLess // 97: '[<'
|
||||
SymGreaterClosedSquare // 98: '>]'
|
||||
SymLinkedList // 99: linked-list
|
||||
// SymOpenComment // 0: '/*'
|
||||
// SymClosedComment // 0: '*/'
|
||||
// SymOneLineComment // 0: '//'
|
||||
|
||||
@@ -136,3 +136,7 @@ func (tk *Token) ErrorExpectedGotStringWithPrefix(prefix, symbol, got string) (e
|
||||
err = fmt.Errorf("[%d:%d] %s %s, got `%s`", tk.row, tk.col, prefix, symbol, got)
|
||||
return
|
||||
}
|
||||
|
||||
func (tk *Token) ErrorExpectedOneOfGot(expected ...Symbol) (err error) {
|
||||
return tk.ErrorExpectedGotStringWithPrefix("expected one of ", SymListToString(expected, true), SymToString(tk.Sym))
|
||||
}
|
||||
|
||||
+47
-40
@@ -6,7 +6,6 @@ package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/util"
|
||||
@@ -77,48 +76,56 @@ func (ctx *SimpleStore) Clone() kern.ExprContext {
|
||||
// }
|
||||
// }
|
||||
|
||||
func (ctx *SimpleStore) ToString(opt kern.FmtOpt) string {
|
||||
dict := ctx.ToDict()
|
||||
return dict.ToString(opt)
|
||||
}
|
||||
|
||||
func (ctx *SimpleStore) varsToDict(dict *kern.DictType) *kern.DictType {
|
||||
names := ctx.EnumVars(nil)
|
||||
slices.Sort(names)
|
||||
for _, name := range ctx.EnumVars(nil) {
|
||||
value, _ := ctx.GetVar(name)
|
||||
if f, ok := value.(kern.Formatter); ok {
|
||||
(*dict)[name] = f.ToString(0)
|
||||
} else if _, ok = value.(kern.Functor); ok {
|
||||
(*dict)[name] = "func(){}"
|
||||
} else {
|
||||
(*dict)[name] = fmt.Sprintf("%v", value)
|
||||
}
|
||||
}
|
||||
return dict
|
||||
}
|
||||
|
||||
func (ctx *SimpleStore) funcsToDict(dict *kern.DictType) *kern.DictType {
|
||||
names := ctx.EnumFuncs(func(name string) bool { return true })
|
||||
slices.Sort(names)
|
||||
for _, name := range names {
|
||||
value, _ := ctx.GetFuncInfo(name)
|
||||
if formatter, ok := value.(kern.Formatter); ok {
|
||||
(*dict)[name] = formatter.ToString(0)
|
||||
} else {
|
||||
(*dict)[name] = fmt.Sprintf("%v", value)
|
||||
}
|
||||
}
|
||||
return dict
|
||||
}
|
||||
|
||||
func (ctx *SimpleStore) ToDict() (dict *kern.DictType) {
|
||||
dict = kern.MakeDict()
|
||||
(*dict)["variables"] = ctx.varsToDict(kern.MakeDict())
|
||||
(*dict)["functions"] = ctx.funcsToDict(kern.MakeDict())
|
||||
return
|
||||
return kern.ContextToDict(ctx)
|
||||
}
|
||||
|
||||
func (ctx *SimpleStore) ToString(opt kern.FmtOpt) string {
|
||||
return kern.ContextToString(ctx, opt)
|
||||
}
|
||||
|
||||
// func (ctx *SimpleStore) ToString(opt kern.FmtOpt) string {
|
||||
// dict := ctx.ToDict()
|
||||
// return dict.ToString(opt)
|
||||
// }
|
||||
|
||||
// func (ctx *SimpleStore) varsToDict(dict *kern.DictType) *kern.DictType {
|
||||
// names := ctx.EnumVars(nil)
|
||||
// slices.Sort(names)
|
||||
// for _, name := range ctx.EnumVars(nil) {
|
||||
// value, _ := ctx.GetVar(name)
|
||||
// if f, ok := value.(kern.Formatter); ok {
|
||||
// (*dict)[name] = f.ToString(0)
|
||||
// } else if _, ok = value.(kern.Functor); ok {
|
||||
// (*dict)[name] = "func(){}"
|
||||
// } else {
|
||||
// (*dict)[name] = fmt.Sprintf("%v", value)
|
||||
// }
|
||||
// }
|
||||
// return dict
|
||||
// }
|
||||
|
||||
// func (ctx *SimpleStore) funcsToDict(dict *kern.DictType) *kern.DictType {
|
||||
// names := ctx.EnumFuncs(func(name string) bool { return true })
|
||||
// slices.Sort(names)
|
||||
// for _, name := range names {
|
||||
// value, _ := ctx.GetFuncInfo(name)
|
||||
// if formatter, ok := value.(kern.Formatter); ok {
|
||||
// (*dict)[name] = formatter.ToString(0)
|
||||
// } else {
|
||||
// (*dict)[name] = fmt.Sprintf("%v", value)
|
||||
// }
|
||||
// }
|
||||
// return dict
|
||||
// }
|
||||
|
||||
// func (ctx *SimpleStore) ToDict() (dict *kern.DictType) {
|
||||
// dict = kern.MakeDict()
|
||||
// (*dict)["variables"] = ctx.varsToDict(kern.MakeDict())
|
||||
// (*dict)["functions"] = ctx.funcsToDict(kern.MakeDict())
|
||||
// return
|
||||
// }
|
||||
|
||||
func (ctx *SimpleStore) GetVar(varName string) (value any, exists bool) {
|
||||
if value, exists = ctx.varStore[varName]; !exists && ctx.global != nil {
|
||||
value, exists = ctx.global.GetVar(varName)
|
||||
|
||||
@@ -118,8 +118,8 @@ func TestFuncBaseOthers(t *testing.T) {
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`set("a", 3); a`, int64(3), nil},
|
||||
/* 2 */ {`set(true, 3)`, nil, `set(): the "name" parameter must be a string, got a bool (true)`},
|
||||
// /* 3 */ {`a=3; unset("a"); a`, nil, `undefined variable or function "a"`},
|
||||
// /* 4 */ {`unset("a")`, nil, `undefined variable or function "a"`},
|
||||
/* 3 */ {`seq(1,2,3)`, kern.NewLinkedListA(int64(1), int64(2), int64(3)), nil},
|
||||
// /* 4 */ {`seq(1,2,4)`, kern.NewLinkedListA(int64(1), int64(2), int64(3)), nil},
|
||||
}
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 4)
|
||||
|
||||
@@ -121,3 +121,27 @@ func logTest(t *testing.T, n int, section, source string, wantResult any, wantEr
|
||||
t.Logf("[-]%s nr %3d -- `%s` --> %v", section, n, source, wantErr)
|
||||
}
|
||||
}
|
||||
|
||||
func testIteratorCallOp(t *testing.T, section string, it kern.Iterator) {
|
||||
if _, err := it.CallOperation("next", map[string]any{}); err != nil {
|
||||
t.Errorf(`%s -- CallOperation("next") failed: %v`, section, err)
|
||||
}
|
||||
if _, err := it.CallOperation("current", map[string]any{}); err != nil {
|
||||
t.Errorf(`%s -- CallOperation("current") failed: %v`, section, err)
|
||||
}
|
||||
if _, err := it.CallOperation("clean", map[string]any{}); err != nil {
|
||||
t.Errorf(`%s -- CallOperation("clean") failed: %v`, section, err)
|
||||
}
|
||||
if _, err := it.CallOperation("index", map[string]any{}); err != nil {
|
||||
t.Errorf(`%s -- CallOperation("index") failed: %v`, section, err)
|
||||
}
|
||||
if _, err := it.CallOperation("count", map[string]any{}); err != nil {
|
||||
t.Errorf(`%s -- CallOperation("count") failed: %v`, section, err)
|
||||
}
|
||||
if _, err := it.CallOperation("reset", map[string]any{}); err != nil {
|
||||
t.Errorf(`%s -- CallOperation("reset") failed: %v`, section, err)
|
||||
}
|
||||
if _, err := it.CallOperation("fake", map[string]any{}); err == nil {
|
||||
t.Errorf(`%s -- CallOperation("fake") should return error`, section)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ package expr
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
func TestCtrlSet(t *testing.T) {
|
||||
@@ -58,3 +60,34 @@ func TestCtrlGetNotDefined(t *testing.T) {
|
||||
t.Errorf(`%s -- CtrlGet(%q) should have returned nil, got %v`, section, varName, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCtrlEnable(t *testing.T) {
|
||||
section := "Context"
|
||||
varName := "test_var"
|
||||
varValue := true
|
||||
|
||||
ctx := NewSimpleStore()
|
||||
GlobalCtrlSet(ctx, varName, varValue)
|
||||
if !CtrlEnable(ctx, varName) {
|
||||
t.Errorf(`%s -- CtrlEnable(ctx, %q) should have returned 'true', got 'false'`, section, varName)
|
||||
}
|
||||
|
||||
if !CtrlDisable(ctx, varName) {
|
||||
t.Errorf(`%s -- CtrlEnable(ctx, %q) should have returned 'true', got 'false'`, section, varName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
section := "Context"
|
||||
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`$$(5)`, kern.NewLinkedListA(5), nil},
|
||||
/* 2 */ {`$$($(2))`, kern.NewLinkedListA(0, 1), nil},
|
||||
/* 3 */ {`string(($$global).funcs.bool)`, `bool(value):boolean{}`, nil},
|
||||
}
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 3)
|
||||
runTestSuite(t, section, inputs)
|
||||
|
||||
}
|
||||
|
||||
+34
-80
@@ -7,7 +7,6 @@ package expr
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
@@ -17,105 +16,60 @@ import (
|
||||
func TestDictParser(t *testing.T) {
|
||||
section := "Dict"
|
||||
|
||||
type inputType struct {
|
||||
source string
|
||||
wantResult any
|
||||
wantErr error
|
||||
}
|
||||
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`{}`, map[any]any{}, nil},
|
||||
/* 1 */ {`{}`, kern.NewDict(nil), nil},
|
||||
/* 2 */ {`{123}`, nil, errors.New("[1:6] expected `:`, got `}`")},
|
||||
/* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
|
||||
/* 3 */ {`{1:"one",2:"two",3:"three"}`, kern.NewDict(map[any]any{int64(1): "one", int64(2): "two", int64(3): "three"}), nil},
|
||||
/* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
|
||||
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
|
||||
/* 6 */ {`{1:"one"} + {2:"two"}`, map[any]any{1: "one", 2: "two"}, nil},
|
||||
/* 6 */ {`{1:"one"} + {2:"two"}`, kern.NewDict(map[any]any{int64(1): "one", int64(2): "two"}), nil},
|
||||
/* 7 */ {`2 in {1:"one", 2:"two"}`, true, nil},
|
||||
/* 8 */ {`D={"a":1, "b":2}; D["a"]=9; D`, map[any]any{"a": 9, "b": 2}, nil},
|
||||
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil},
|
||||
/* 8 */ {`D={"a":1, "b":2}; D["a"]=9; D`, kern.NewDict(map[any]any{"a": int64(9), "b": int64(2)}), nil},
|
||||
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, kern.NewDict(map[any]any{"z": int64(9), "a": int64(1), "b": int64(2)}), nil},
|
||||
/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
|
||||
/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
|
||||
/* 12 */ {`m={
|
||||
"a":1,
|
||||
//"b":2,
|
||||
"c":3
|
||||
}`, map[any]any{"a": 1, "c": 3}, nil},
|
||||
}`, kern.NewDict(map[any]any{"a": int64(1), "c": int64(3)}), nil},
|
||||
/* 13 */ {`D={"a":1, "b":2}; D."a"`, int64(1), nil},
|
||||
/* 14 */ {`D={"a":1, "b":2}; D.a`, int64(1), nil},
|
||||
/* 15 */ {`D={1:"a", 2:"b", 3:"c"}; D.(1+2)`, "c", nil},
|
||||
/* 16 */ {`D={1:"a"}; D.2`, nil, `[1:15] key 2 not found`},
|
||||
/* 17 */ {`D={1:"a"}; D."2"`, nil, `[1:17] key "2" not found`},
|
||||
/* 18 */ {`D={"a":1, "b":2}; D.a = 10; D.a`, int64(10), nil},
|
||||
/* 19 */ {`k="a"; D={k: 10}`, kern.NewDict(map[any]any{"a": int64(10)}), nil},
|
||||
/* 20 */ {`k=[<1,2>]; D={k: 10}`, nil, `[1:16] dict key can be integer or string, got lisked-list`},
|
||||
/* 21 */ {`f=func(n){"x"+n}; d{f(5):10}`, nil, `[0:0] two adjacent operators: "d" and "{}"`},
|
||||
/* 22 */ {`f=func(n){"x"+n}; d={f(5):10}`, kern.NewDict(map[any]any{"x5": int64(10)}), nil},
|
||||
/* 23 */ {`f=func(n){"x"+n}; d={f(5); "z":10}`, nil, "[1:27] expected one of `:`, `}`, got `;`"},
|
||||
/* 24 */ {`f=func(n){"x"+n}; d={f(5) but "z":10}`, kern.NewDict(map[any]any{"z": int64(10)}), nil},
|
||||
}
|
||||
|
||||
succeeded := 0
|
||||
failed := 0
|
||||
|
||||
// inputs1 := []inputType{
|
||||
// /* 7 */ {`add([1,4,3,2])`, int64(10), nil},
|
||||
// }
|
||||
|
||||
for i, input := range inputs {
|
||||
var expr *scan.Ast
|
||||
var gotResult any
|
||||
var gotErr error
|
||||
|
||||
ctx := NewSimpleStoreWithoutGlobalContext()
|
||||
ctx.SetVar("var1", int64(123))
|
||||
ctx.SetVar("var2", "abc")
|
||||
ImportMathFuncs(ctx)
|
||||
parser := NewParser()
|
||||
|
||||
logTest(t, i+1, "Dict", input.source, input.wantResult, input.wantErr)
|
||||
|
||||
r := strings.NewReader(input.source)
|
||||
scanner := scan.NewScanner(r, scan.DefaultTranslations())
|
||||
|
||||
good := true
|
||||
if expr, gotErr = parser.Parse(scanner); gotErr == nil {
|
||||
gotResult, gotErr = expr.Eval(ctx)
|
||||
// runTestSuiteSpec(t, section, inputs, 23, 24)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
if (gotResult == nil && input.wantResult != nil) || (gotResult != nil && input.wantResult == nil) {
|
||||
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
|
||||
good = false
|
||||
}
|
||||
func TestAccessSubFields(t *testing.T) {
|
||||
section := "Dict-Assign-Item"
|
||||
ctx := NewSimpleStore()
|
||||
ctx.UnsafeSetVar(
|
||||
"D",
|
||||
kern.NewDict(map[any]any{
|
||||
"a": kern.NewDict(map[any]any{"uno": int64(10)}),
|
||||
"b": kern.NewListA(1, 2, 3),
|
||||
}),
|
||||
) // D={"a": {"uno":1}}
|
||||
|
||||
if gotList, okGot := gotResult.([]any); okGot {
|
||||
if wantList, okWant := input.wantResult.([]any); okWant {
|
||||
if (gotList == nil && wantList != nil) || (gotList != nil && wantList == nil) {
|
||||
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
|
||||
good = false
|
||||
} else {
|
||||
equal := len(gotList) == len(wantList)
|
||||
if equal {
|
||||
for i, gotItem := range gotList {
|
||||
wantItem := wantList[i]
|
||||
equal = gotItem == wantItem
|
||||
if !equal {
|
||||
break
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`D.a.uno`, int64(10), nil},
|
||||
/* 2 */ {`D.a.uno = 111; D.a."uno"`, int64(111), nil},
|
||||
/* 3 */ {`D.a.uno = 111; D["a"]["uno"]`, int64(111), nil},
|
||||
/* 4 */ {`D.b[1] = 22; D["b"][1]`, int64(22), nil},
|
||||
}
|
||||
}
|
||||
}
|
||||
if !equal {
|
||||
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
|
||||
good = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if gotErr != input.wantErr {
|
||||
if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) {
|
||||
t.Errorf("%d: %q -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr)
|
||||
good = false
|
||||
}
|
||||
}
|
||||
|
||||
if good {
|
||||
succeeded++
|
||||
} else {
|
||||
failed++
|
||||
}
|
||||
}
|
||||
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
|
||||
// runCtxTestSuiteSpec(t, ctx, section, inputs, 4)
|
||||
runCtxTestSuite(t, ctx, section, inputs)
|
||||
}
|
||||
|
||||
func TestDictToStringMultiLine(t *testing.T) {
|
||||
|
||||
+1
-2
@@ -31,8 +31,7 @@ func TestExpr(t *testing.T) {
|
||||
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
|
||||
/* 16 */ {`v=[2]; a=1; v[a-=1]=5; v[0]`, int64(5), nil},
|
||||
/* 17 */ {`true ? {"a"} :: {"b"}`, "a", nil},
|
||||
/* 18 */ {`$$`, kern.NewDict(map[any]any{"variables": kern.NewDict(nil), "functions": kern.NewDict(nil)}), nil},
|
||||
///* 19 */ {`$$global`, NewDict(map[any]any{"variables": NewDict(nil), "functions": NewDict(nil)}), nil},
|
||||
/* 18 */ {`$$`, kern.NewDict(map[any]any{"vars": kern.NewDict(nil), "funcs": kern.NewDict(nil)}), nil},
|
||||
/* 19 */ {`
|
||||
ds={
|
||||
"init":func(@end){@current=0 but true},
|
||||
|
||||
+43
-39
@@ -5,51 +5,55 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"testing"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
// TODO The new function param model does not allow this kind of test
|
||||
// ------------------------------------------------------------------
|
||||
// func subtract(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
// if len(args) != 2 {
|
||||
// err = fmt.Errorf("%s(): requires exactly two arguments", name)
|
||||
// return
|
||||
// }
|
||||
// x, xok := args[0].(int64)
|
||||
// y, yok := args[1].(int64)
|
||||
// if xok && yok {
|
||||
// result = x - y
|
||||
// } else {
|
||||
// err = fmt.Errorf("expected integer (int64) arguments, got %T and %T values", x, y)
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
func subtract(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if len(args) != 2 {
|
||||
err = fmt.Errorf("%s(): requires exactly two arguments", name)
|
||||
return
|
||||
}
|
||||
x, xok := args["a"].(int64)
|
||||
y, yok := args["b"].(int64)
|
||||
if xok && yok {
|
||||
result = x - y
|
||||
} else {
|
||||
err = fmt.Errorf("expected integer (int64) arguments, got %T and %T values", x, y)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// func TestEvalStringA(t *testing.T) {
|
||||
func TestEvalStringA(t *testing.T) {
|
||||
|
||||
// source := `a + b * subtract(4,2)`
|
||||
// args := []Arg{
|
||||
// {"a", uint8(1)},
|
||||
// {"b", int8(2)},
|
||||
// {"subtract", FuncTemplate2(subtract)},
|
||||
// // force coverage
|
||||
// {"a16", uint16(1)},
|
||||
// {"b16", int16(2)},
|
||||
// {"a32", uint32(1)},
|
||||
// {"b32", int32(2)},
|
||||
// {"a64", uint64(1)},
|
||||
// {"b64", int64(2)},
|
||||
// {"f32", float32(1.0)},
|
||||
// {"f64", float64(1.0)},
|
||||
// }
|
||||
source := `a + b * subtract(4,2)`
|
||||
args := []Arg{
|
||||
{"a", uint8(1)},
|
||||
{"b", int8(2)},
|
||||
{"subtract", kern.FuncTemplate(subtract)},
|
||||
// force coverage
|
||||
{"a16", uint16(1)},
|
||||
{"b16", int16(2)},
|
||||
{"a32", uint32(1)},
|
||||
{"b32", int32(2)},
|
||||
{"a64", uint64(1)},
|
||||
{"b64", int64(2)},
|
||||
{"f32", float32(1.0)},
|
||||
{"f64", float64(1.0)},
|
||||
{"string", "text"},
|
||||
{"bool", true},
|
||||
}
|
||||
|
||||
// wantResult := int64(5)
|
||||
// gotResult, gotErr := EvalStringA(source, args...)
|
||||
// if value, ok := gotResult.(int64); ok && value != wantResult {
|
||||
// t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
|
||||
// t.Errorf("Error: %v", gotErr)
|
||||
// }
|
||||
// }
|
||||
wantResult := int64(5)
|
||||
gotResult, gotErr := EvalStringA(source, args...)
|
||||
if value, ok := gotResult.(int64); ok && value != wantResult {
|
||||
t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
|
||||
t.Errorf("Error: %v", gotErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvalString(t *testing.T) {
|
||||
|
||||
|
||||
+4
-2
@@ -6,19 +6,21 @@ package expr
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
)
|
||||
|
||||
func TestCollections(t *testing.T) {
|
||||
section := "Collection"
|
||||
section := "Index"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`"abcdef"[1:3]`, "bc", nil},
|
||||
/* 2 */ {`"abcdef"[:3]`, "abc", nil},
|
||||
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
||||
/* 4 */ {`"abcdef"[:]`, "abcdef", nil},
|
||||
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||
/* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] invalid range specification`},
|
||||
/* 6 */ {`"abcdef"[((1>0)?{1}:{0}):3]`, "bc", nil},
|
||||
/* 7 */ {`"abcdef"[[0,1][0]:1]`, "a", nil},
|
||||
/* 8 */ {`[0,1,2,3,4][:]`, kern.NewListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||
}
|
||||
|
||||
t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@ import (
|
||||
func TestIterIterator(t *testing.T) {
|
||||
section := "Iter-Iter"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`it=$(4); $$($(it) filter ${_}==100)`, kern.NewListA(), nil},
|
||||
/* 2 */ {`it=$(4); $$($(it, $_) filter ${_}==100)`, kern.NewListA(), nil},
|
||||
/* 1 */ {`it=$(4); $$($(it) filter ${_}==100)`, kern.NewLinkedListA(), nil},
|
||||
/* 2 */ {`it=$(4); $$($(it, $_) filter ${_}==100)`, kern.NewLinkedListA(), nil},
|
||||
/* 3 */ {`it=$(4); $(it, 10+$_, last-1) digest ${_}`, int64(12), nil},
|
||||
/* 4 */ {`f=func(n){last-n}; it=$(4); $(it, 10+$_, f(-1)) digest ${_}`, int64(14), nil},
|
||||
}
|
||||
|
||||
+88
-10
@@ -8,6 +8,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"git.portale-stac.it/go-pkg/expr/kern"
|
||||
"git.portale-stac.it/go-pkg/expr/scan"
|
||||
)
|
||||
|
||||
func TestIteratorParser(t *testing.T) {
|
||||
@@ -35,17 +36,94 @@ func TestIteratorParser(t *testing.T) {
|
||||
/* 20 */ {`it=$({1:"one",2:"two",3:"three"}, "default", "value"); it++`, "one", nil},
|
||||
/* 21 */ {`it=$({1:"one",2:"two",3:"three"}, "desc", "key"); it++`, int64(3), nil},
|
||||
/* 22 */ {`it=$({1:"one",2:"two",3:"three"}, "asc", "item"); it++`, kern.NewList([]any{int64(1), "one"}), nil},
|
||||
/* 23 */ {`$$($(1,4,0))`, nil, `step cannot be zero`},
|
||||
/* 24 */ {`$$($(1,4,-1))`, nil, `step cannot be negative when start < stop`},
|
||||
/* 25 */ {`$$($(4,1,1))`, nil, `step cannot be positive when start > stop`},
|
||||
}
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 1)
|
||||
// runTestSuiteSpec(t, section, inputs, 25)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
func TestCallOpIntIter(t *testing.T) {
|
||||
section := "IntIterator-CallOp"
|
||||
|
||||
if it, err := NewIntIteratorA(int64(1), int64(4), int64(1)); err != nil {
|
||||
t.Errorf(`%s -- NewIntIteratorA() failed: %v`, section, err)
|
||||
} else {
|
||||
testIteratorCallOp(t, section, it)
|
||||
testIterAttrs(t, section, it, "IntIterator", "$(1..4..1)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallOpIterIter(t *testing.T) {
|
||||
section := "IterIterator-CallOp"
|
||||
|
||||
ctx := NewSimpleStore()
|
||||
if inner, err := NewIntIteratorA(int64(1), int64(4), int64(1)); err == nil {
|
||||
if it, err := NewIterIter(inner, ctx, []*scan.Term{}); err != nil {
|
||||
t.Errorf(`%s -- NewIterIter() failed: %v`, section, err)
|
||||
} else {
|
||||
testIteratorCallOp(t, section, it)
|
||||
testIterAttrs(t, section, it, "IterIter", "$($(1..4..1))")
|
||||
}
|
||||
} else {
|
||||
t.Errorf(`%s -- can't create inner iterator: %v`, section, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallOpDictIter(t *testing.T) {
|
||||
section := "DictIterator-CallOp"
|
||||
|
||||
inner := kern.NewDict(map[any]any{"a": 1})
|
||||
if it, err := NewDictIterator(inner, nil); err != nil {
|
||||
t.Errorf(`%s -- NewIterIter() failed: %v`, section, err)
|
||||
} else {
|
||||
testIteratorCallOp(t, section, it)
|
||||
testIterAttrs(t, section, it, "DictIterator", "$({#1})")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCallOpLinkedListIter(t *testing.T) {
|
||||
section := "LinkedListIterator-CallOp"
|
||||
|
||||
inner := kern.NewLinkedListA(1, 2, 3)
|
||||
it := NewLinkedListIterator(inner, nil)
|
||||
testIteratorCallOp(t, section, it)
|
||||
|
||||
// wanted := "$([<#3>])"
|
||||
// got := it.String()
|
||||
// if wanted != got {
|
||||
// t.Errorf(`%s -- LinkedListIterator.String() failed: expected %q, got %q`, section, wanted, got)
|
||||
// }
|
||||
|
||||
// wanted = "LinkedListIterator"
|
||||
// got = it.TypeName()
|
||||
// if wanted != got {
|
||||
// t.Errorf(`%s -- LinkedListIterator.TypeName() failed: expected %q, got %q`, section, wanted, got)
|
||||
// }
|
||||
testIterAttrs(t, section, it, "LinkedListIterator", "$([<#3>])")
|
||||
}
|
||||
|
||||
func testIterAttrs(t *testing.T, section string, it kern.Iterator, name, repr string) {
|
||||
wanted := repr
|
||||
got := it.String()
|
||||
if wanted != got {
|
||||
t.Errorf(`%s -- %s.String() failed: expected %q, got %q`, section, name, wanted, got)
|
||||
}
|
||||
|
||||
wanted = name
|
||||
got = it.TypeName()
|
||||
if wanted != got {
|
||||
t.Errorf(`%s -- %s.TypeName() failed: expected %q, got %q`, section, name, wanted, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilterIterator(t *testing.T) {
|
||||
section := "Iterator-Filter"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`$$([1,2,3] filter $_%2==0)`, kern.NewList([]any{int64(2)}), nil},
|
||||
/* 2 */ {`it = [1,2,3] filter $_%2!=1; $$(it)`, kern.NewList([]any{int64(2)}), nil},
|
||||
/* 1 */ {`$$([1,2,3] filter $_%2==0)`, kern.NewLinkedListA(2), nil},
|
||||
/* 2 */ {`it = [1,2,3] filter $_%2!=1; $$(it)`, kern.NewLinkedListA(2), nil},
|
||||
/* 3 */ {`builtin "os.file"; #$$(fileLineIterator("test-file.txt") filter (#${_} == 2))`, int64(0), nil},
|
||||
/* 4 */ {`builtin "os.file"; #$$(fileLineIterator("test-file.txt") filter (#${_} == 3))`, int64(2), nil},
|
||||
}
|
||||
@@ -69,11 +147,11 @@ func TestDigestIterator(t *testing.T) {
|
||||
func TestCatIterator(t *testing.T) {
|
||||
section := "Iterator-Cat"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`$$([1] cat [])`, kern.NewList([]any{int64(1)}), nil},
|
||||
/* 2 */ {`$$([1] cat [2])`, kern.NewList([]any{int64(1), int64(2)}), nil},
|
||||
/* 3 */ {`$$([1] cat nil)`, kern.NewList([]any{int64(1)}), nil},
|
||||
/* 4 */ {`$$(nil cat [2])`, kern.NewList([]any{int64(2)}), nil},
|
||||
/* 5 */ {`$$(nil cat nil)`, kern.NewList([]any{}), nil},
|
||||
/* 1 */ {`$$([1] cat [])`, kern.NewLinkedListA(1), nil},
|
||||
/* 2 */ {`$$([1] cat [2])`, kern.NewLinkedListA(1, 2), nil},
|
||||
/* 3 */ {`$$([1] cat nil)`, kern.NewLinkedListA(1), nil},
|
||||
/* 4 */ {`$$(nil cat [2])`, kern.NewLinkedListA(2), nil},
|
||||
/* 5 */ {`$$(nil cat nil)`, kern.NewLinkedListA(), nil},
|
||||
/* 6 */ {`$$(["a","b"] cat ["x"-true])`, nil, `[1:23] left operand 'x' [string] and right operand 'true' [bool] are not compatible with operator "-"`},
|
||||
}
|
||||
|
||||
@@ -84,10 +162,10 @@ func TestCatIterator(t *testing.T) {
|
||||
func TestMapIterator(t *testing.T) {
|
||||
section := "Iterator-Map"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`$$([3,4,5] map ${_#})`, kern.NewList([]any{int64(1), int64(2), int64(3)}), nil},
|
||||
/* 1 */ {`$$([3,4,5] map ${_#})`, kern.NewLinkedListA(1, 2, 3), nil},
|
||||
/* 2 */ {`#$$($(10) map ${_})`, int64(10), nil},
|
||||
/* 3 */ {`#$$($(10,0) map ${_})`, int64(10), nil},
|
||||
/* 4 */ {`builtin "os.file"; $$(fileLineIterator("test-file.txt") map ${__})`, kern.NewList([]any{int64(0), int64(1)}), nil},
|
||||
/* 4 */ {`builtin "os.file"; $$(fileLineIterator("test-file.txt") map ${__})`, kern.NewLinkedListA(0, 1), nil},
|
||||
/* 5 */ {`$$(["1", "2", "3"] map int())`, nil, `int(): too few params -- expected 1, got 0`},
|
||||
}
|
||||
|
||||
|
||||
+19
-1
@@ -57,10 +57,28 @@ func TestListParser(t *testing.T) {
|
||||
/* 41 */ {`[0] << $([1,2,3,4])`, kern.NewListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||
/* 42 */ {`L=[]; [1] >> L; L`, kern.NewListA(), nil},
|
||||
/* 43 */ {`L=[]; L << [1]; L`, kern.NewListA(), nil},
|
||||
// /* 44 */ {`[0,1,2,3,4][2:3]`, kern.NewListA(int64(20)), nil},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 42)
|
||||
// runTestSuiteSpec(t, section, inputs, 44)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
func TestLinkedListParser(t *testing.T) {
|
||||
section := "Linked-List"
|
||||
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`[<1,2>]`, kern.NewLinkedListA(1, 2), nil},
|
||||
/* 2 */ {`it=$([<1,2,3>]); it++`, int64(1), nil},
|
||||
/* 3 */ {`it=$([<1,2,3>]); #($$(it))`, int64(3), nil},
|
||||
/* 4 */ {`[<1,2,3>][0]`, int64(1), nil},
|
||||
/* 5 */ {`[<1,2,3>][0:2]`, kern.NewLinkedListA(1, 2), nil},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 4)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
+20
-1
@@ -35,6 +35,12 @@ func TestOperator(t *testing.T) {
|
||||
/* 20 */ {`a=1; a^=2`, int64(3), nil},
|
||||
/* 21 */ {`a=1; ++a`, int64(2), nil},
|
||||
/* 22 */ {`a=1; --a`, int64(0), nil},
|
||||
/* 23 */ {`a=1; a++`, int64(1), nil},
|
||||
/* 24 */ {`a=1; a--`, int64(1), nil},
|
||||
/* 25 */ {`a="1"; ++a`, nil, `[1:9] prefix/postfix operator "++" does not support operand '1' [string]`},
|
||||
/* 26 */ {`a="1"; --a`, nil, `[1:9] prefix/postfix operator "--" does not support operand '1' [string]`},
|
||||
/* 27 */ {`a="1"; a++`, nil, `[1:10] prefix/postfix operator "++" does not support operand '1' [string]`},
|
||||
/* 28 */ {`a="1"; a--`, nil, `[1:10] prefix/postfix operator "--" does not support operand '1' [string]`},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
@@ -54,7 +60,7 @@ func TestOperatorInsert(t *testing.T) {
|
||||
/* 6 */ {`[] >> ["a", "b"]`, kern.NewListA("a", "b"), nil},
|
||||
/* 7 */ {`["a", "b"] << $([1,2,3])`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil},
|
||||
/* 8 */ {`L=["a", "b"]; L << $([1,2,3])`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil},
|
||||
/* 9 */ {`L=["a", "b"]; L << $([1,2,3]); L`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil},
|
||||
/* 9 */ {`L=["a", "b"]; L << $([1,2,3]); L`, kern.NewListA("a", "b"), nil},
|
||||
/* 10 */ {`L << $([1,2,3])`, nil, `undefined variable or function "L"`},
|
||||
}
|
||||
|
||||
@@ -62,6 +68,19 @@ func TestOperatorInsert(t *testing.T) {
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
func TestOperatorDeepAssign(t *testing.T) {
|
||||
section := "Operator-DeepAssign"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`DD={"uno": 1, "due": 2}; D=DD; D["uno"]=11; DD`, kern.NewDict(map[any]any{"uno": int64(11), "due": int64(2)}), nil},
|
||||
/* 2 */ {`DD={"uno": 1, "due": 2}; D:=DD; D["uno"]=11; DD`, kern.NewDict(map[any]any{"uno": int64(1), "due": int64(2)}), nil},
|
||||
/* 3 */ {`LL=["a", "b"]; L=LL; L[0]="x"; LL`, kern.NewListA("x", "b"), nil},
|
||||
/* 4 */ {`LL=["a", "b"]; L:=LL; L[0]="x"; LL`, kern.NewListA("a", "b"), nil},
|
||||
}
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 4)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
func TestOperatorDigest(t *testing.T) {
|
||||
section := "Operator-Digest"
|
||||
inputs := []inputType{
|
||||
|
||||
@@ -77,3 +77,10 @@ func ForAll[T, V any](ts []T, fn func(T) V) []V {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func UnquoteString(src string) string {
|
||||
if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
|
||||
src = src[1 : len(src)-1]
|
||||
}
|
||||
return src
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user