Compare commits
10 Commits
4aa0113c6a
...
f028485caa
Author | SHA1 | Date | |
---|---|---|---|
f028485caa | |||
ab07405cda | |||
47be0c66cf | |||
50e7168214 | |||
0a9543543d | |||
775751c67b | |||
4aaffd6c44 | |||
924051fbcd | |||
5a9b6525a2 | |||
8c66d90532 |
2
ast.go
2
ast.go
@ -60,7 +60,7 @@ func (self *ast) addToken(tk *Token) (err error) {
|
||||
}
|
||||
|
||||
func (self *ast) addToken2(tk *Token) (t *term, err error) {
|
||||
if t = newTerm(tk, nil); t != nil {
|
||||
if t = newTerm(tk); t != nil {
|
||||
err = self.addTerm(t)
|
||||
} else {
|
||||
err = tk.Errorf("unexpected token %q", tk.String())
|
||||
|
@ -18,6 +18,10 @@ func errExpectedGot(funcName string, kind string, value any) error {
|
||||
return fmt.Errorf("%s() expected %s, got %T (%v)", funcName, kind, value, value)
|
||||
}
|
||||
|
||||
func errDivisionByZero(funcName string) error {
|
||||
return fmt.Errorf("%s() division by zero", funcName)
|
||||
}
|
||||
|
||||
// --- Parameter errors
|
||||
|
||||
func errOneParam(funcName string) error {
|
||||
|
17
dict_test.go
17
dict_test.go
@ -23,22 +23,9 @@ func TestDictParser(t *testing.T) {
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`{}`, map[any]any{}, 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"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
|
||||
/* 4 */ {`{1:"one",2:"two",3:"three"}.2`, "three", nil},
|
||||
// /* 3 */ {`[1,2,"hello"]`, []any{int64(1), int64(2), "hello"}, nil},
|
||||
// /* 4 */ {`[1+2, not true, "hello"]`, []any{int64(3), false, "hello"}, nil},
|
||||
// /* 5 */ {`[1,2]+[3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
||||
// /* 6 */ {`[1,4,3,2]-[3]`, []any{int64(1), int64(4), int64(2)}, nil},
|
||||
// /* 7 */ {`add([1,4,3,2])`, int64(10), nil},
|
||||
// /* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil},
|
||||
// /* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
||||
// /* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 (2 in 1) has wrong type string, number expected`)},
|
||||
// /* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
||||
// /* 12 */ {`[1,2,3] << 2+2`, []any{int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||
// /* 13 */ {`2-1 >> [2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
||||
// /* 14 */ {`[1,2,3].1`, int64(2), nil},
|
||||
// /* 15 */ {`ls=[1,2,3] but ls.1`, int64(2), nil},
|
||||
// /* 16 */ {`ls=[1,2,3] but ls.(-1)`, int64(3), nil},
|
||||
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
|
||||
}
|
||||
|
||||
succeeded := 0
|
||||
|
@ -194,6 +194,25 @@ Some arithmetic operators can also be used with strings.
|
||||
| [blue]`*` | _repeat_ | Make _n_ copy of a string | [blue]`"one" * 2` _["oneone"]_
|
||||
|===
|
||||
|
||||
The items of strings can be accessed using the dot `.` operator.
|
||||
|
||||
.Item access syntax
|
||||
[source,bnf]
|
||||
----
|
||||
<item> ::= <string-expr>"."<index-expr>
|
||||
----
|
||||
|
||||
.String examples
|
||||
`>>>` [blue]`s="abc"` [gray]_assign the string to variable s_ +
|
||||
[green]`abc` +
|
||||
`>>>` [blue]`s.1` [gray]_char at position 1 (starting from 0)_ +
|
||||
[green]`b` +
|
||||
`>>>` [blue]`s.(-1)` [gray]_char at position -1, the rightmost one_ +
|
||||
[green]`c` +
|
||||
`>>>` [blue]`\#s` [gray]_number of chars_ +
|
||||
[gren]`3` +
|
||||
`>>>` [blue]`#"abc"` [gray]_number of chars_ +
|
||||
[green]`3` +
|
||||
|
||||
=== Boolean
|
||||
Boolean data type has two values only: _true_ and _false_. Relational and Boolean expressions produce Boolean values.
|
||||
@ -278,29 +297,27 @@ The items of array can be accessed using the dot `.` operator.
|
||||
----
|
||||
|
||||
.Items of list
|
||||
[source,go]
|
||||
----
|
||||
`>>>` [blue]`[1,2,3].1`
|
||||
[green]`2`
|
||||
`>>>` [blue]`list=[1,2,3]; list.1`
|
||||
[green]`2`
|
||||
`>>>` [blue]`["one","two","three"].1`
|
||||
[green]`two`
|
||||
`>>>` [blue]`list=["one","two","three"]; list.(2-1)`
|
||||
[green]`two`
|
||||
`>>>` [blue]`list.(-1)`
|
||||
[green]`three`
|
||||
`>>>` [blue]`list.(-1)`
|
||||
[green]`three`
|
||||
`>>>` [blue]`list.(10)`
|
||||
----
|
||||
`>>>` [blue]`[1,2,3].1` +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`list=[1,2,3]; list.1` +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`["one","two","three"].1` +
|
||||
[green]`two` +
|
||||
`>>>` [blue]`list=["one","two","three"]; list.(2-1)` +
|
||||
[green]`two` +
|
||||
`>>>` [blue]`list.(-1)` +
|
||||
[green]`three` +
|
||||
`>>>` [blue]`list.(10)` +
|
||||
[red]`Eval Error: [1:9] index 10 out of bounds` +
|
||||
`>>>` [blue]`#list` +
|
||||
[green]`3`
|
||||
|
||||
|
||||
|
||||
== Dictionaries
|
||||
The _dictionary_ data-type is set of pairs _key/value_. It is also known as _map_ or _associative array_. Dictionary literals are sequences of pairs separated by comma `,`; sequences are enclosed between brace brackets.
|
||||
|
||||
.List examples
|
||||
.Dictionary examples
|
||||
[source,go]
|
||||
----
|
||||
{1:"one", 2:"two"}
|
||||
@ -311,7 +328,7 @@ The _dictionary_ data-type is set of pairs _key/value_. It is also known as _map
|
||||
WARNING: Support for dictionaries is still ongoing.
|
||||
|
||||
== Variables
|
||||
A variable is an identifier with an assigned value. Variables are stored in the object that implements the _ExprContext_ interface.
|
||||
A variable is an identifier with an assigned value. Variables are stored in the object that implements the Go _ExprContext_ interface, e.g. _SimpleVarStore_ or _SimpleFuncStore_.
|
||||
|
||||
.Examples
|
||||
[source,go]
|
||||
@ -365,23 +382,30 @@ The _selector operator_ is very similar to the _switch/case/default_ statement a
|
||||
<multi-expression> ::= <expression> {";" <expression>}
|
||||
----
|
||||
|
||||
In other words, the selector operator evaluates the expression (`<select-expression>`) on the left-hand side of the `?` symbol; it then compares the result obtained with the values listed in the `<match-list>`'s. If the comparision find a match with a value in a match-list, the associated `<case-multi-expression>` is evaluted, and its result will be the final result of the selection operation.
|
||||
In other words, the selector operator evaluates the expression (`<select-expression>`) on the left-hand side of the `?` symbol; it then compares the result obtained with the values listed in the `<match-list>`'s. If the comparision finds a match with a value in a match-list, the associated `<case-multi-expression>` is evaluted, and its result will be the final result of the selection operation.
|
||||
|
||||
The match lists are optional. In that case, the position, from left to right, of the `<selector-case>` is used as match-list. Of course, that only works if the select-expression results in an integer.
|
||||
|
||||
The `:` symbol (colon) is the separator of the selector-cases. Note that if the value of the select-expression does not match any match-list, an error will be issued. Therefore, it is strongly recommended to provide a default (multi-)expression introduced by the `::` symbol (double-colon). Also note that the default expression has no match-list.
|
||||
|
||||
|
||||
.Examples
|
||||
[source,go]
|
||||
----
|
||||
1 ? {"a"} : {"b"} // returns "b"
|
||||
10 ? {"a"} : {"b"} :: {"c"} // returns "c"
|
||||
10 ? {"a"} :[true, 2+8] {"b"} :: {"c"} // returns "b"
|
||||
10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"} // error: "... case list in default clause"
|
||||
10 ? {"a"} :[10] {x="b" but x} :: {"c"} // returns "b"
|
||||
10 ? {"a"} :[10] {x="b"; x} :: {"c"} // returns "b"
|
||||
10 ? {"a"} : {"b"} // error: "... no case catches the value (10) of the selection expression
|
||||
`>>>` [blue]`1 ? {"a"} : {"b"}`
|
||||
[green]`b`
|
||||
`>>>` [blue]`10 ? {"a"} : {"b"} :: {"c"}`
|
||||
[green]`c'
|
||||
[green]`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`
|
||||
[green]`b`
|
||||
`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}`
|
||||
[red]`Parse Error: [1:34] case list in default clause`
|
||||
[green]`>>>` [blue]`10 ? {"a"} :[10] {x="b" but x} :: {"c"}`
|
||||
[green]`b`
|
||||
`>>>` [blue]`10 ? {"a"} :[10] {x="b"; x} :: {"c"}`
|
||||
[green]`b`
|
||||
`>>>` [blue]`10 ? {"a"} : {"b"}`
|
||||
[red]`Eval Error: [1:3] no case catches the value (10) of the selection expression`
|
||||
|
||||
----
|
||||
|
||||
== Priorities of operators
|
||||
|
@ -868,6 +868,28 @@ pre.rouge .ss {
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="paragraph">
|
||||
<p>The items of strings can be accessed using the dot <code>.</code> operator.</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Item access syntax</div>
|
||||
<div class="content">
|
||||
<pre class="rouge highlight"><code data-lang="bnf"><item> ::= <string-expr>"."<index-expr></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">String examples</div>
|
||||
<p><code>>>></code> <code class="blue">s="abc"</code> <em class="gray">assign the string to variable s</em><br>
|
||||
<code class="green">abc</code><br>
|
||||
<code>>>></code> <code class="blue">s.1</code> <em class="gray">char at position 1 (starting from 0)</em><br>
|
||||
<code class="green">b</code><br>
|
||||
<code>>>></code> <code class="blue">s.(-1)</code> <em class="gray">char at position -1, the rightmost one</em><br>
|
||||
<code class="green">c</code><br>
|
||||
<code>>>></code> <code class="blue">#s</code> <em class="gray">number of chars</em><br>
|
||||
<code class="gren">3</code><br>
|
||||
<code>>>></code> <code class="blue">#"abc"</code> <em class="gray">number of chars</em><br>
|
||||
<code class="green">3</code><br></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_boolean"><a class="anchor" href="#_boolean"></a><a class="link" href="#_boolean">2.4. Boolean</a></h3>
|
||||
@ -1059,23 +1081,22 @@ pre.rouge .ss {
|
||||
<pre class="rouge highlight"><code data-lang="bnf"><item> ::= <list-expr>"."<index-expr></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="paragraph">
|
||||
<div class="title">Items of list</div>
|
||||
<div class="content">
|
||||
<pre class="rouge highlight"><code data-lang="go"><span class="s">`>>>`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`[1,2,3].1`</span>
|
||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`2`</span>
|
||||
<span class="s">`>>>`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`list=[1,2,3]; list.1`</span>
|
||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`2`</span>
|
||||
<span class="s">`>>>`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`["one","two","three"].1`</span>
|
||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`two`</span>
|
||||
<span class="s">`>>>`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`list=["one","two","three"]; list.(2-1)`</span>
|
||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`two`</span>
|
||||
<span class="s">`>>>`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`list.(-1)`</span>
|
||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`three`</span>
|
||||
<span class="s">`>>>`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`list.(-1)`</span>
|
||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`three`</span>
|
||||
<span class="s">`>>>`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`list.(10)`</span></code></pre>
|
||||
</div>
|
||||
<p><code>>>></code> <code class="blue">[1,2,3].1</code><br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue">list=[1,2,3]; list.1</code><br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue">["one","two","three"].1</code><br>
|
||||
<code class="green">two</code><br>
|
||||
<code>>>></code> <code class="blue">list=["one","two","three"]; list.(2-1)</code><br>
|
||||
<code class="green">two</code><br>
|
||||
<code>>>></code> <code class="blue">list.(-1)</code><br>
|
||||
<code class="green">three</code><br>
|
||||
<code>>>></code> <code class="blue">list.(10)</code><br>
|
||||
<code class="red">Eval Error: [1:9] index 10 out of bounds</code><br>
|
||||
<code>>>></code> <code class="blue">#list</code><br>
|
||||
<code class="green">3</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1087,7 +1108,7 @@ pre.rouge .ss {
|
||||
<p>The <em>dictionary</em> data-type is set of pairs <em>key/value</em>. It is also known as <em>map</em> or <em>associative array</em>. Dictionary literals are sequences of pairs separated by comma <code>,</code>; sequences are enclosed between brace brackets.</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="title">List examples</div>
|
||||
<div class="title">Dictionary examples</div>
|
||||
<div class="content">
|
||||
<pre class="rouge highlight"><code data-lang="go"><span class="p">{</span><span class="m">1</span><span class="o">:</span><span class="s">"one"</span><span class="p">,</span> <span class="m">2</span><span class="o">:</span><span class="s">"two"</span><span class="p">}</span>
|
||||
<span class="p">{</span><span class="s">"one"</span><span class="o">:</span><span class="m">1</span><span class="p">,</span> <span class="s">"two"</span><span class="o">:</span> <span class="m">2</span><span class="p">}</span>
|
||||
@ -1112,7 +1133,7 @@ Support for dictionaries is still ongoing.
|
||||
<h2 id="_variables"><a class="anchor" href="#_variables"></a><a class="link" href="#_variables">4. Variables</a></h2>
|
||||
<div class="sectionbody">
|
||||
<div class="paragraph">
|
||||
<p>A variable is an identifier with an assigned value. Variables are stored in the object that implements the <em>ExprContext</em> interface.</p>
|
||||
<p>A variable is an identifier with an assigned value. Variables are stored in the object that implements the Go <em>ExprContext</em> interface, e.g. <em>SimpleVarStore</em> or <em>SimpleFuncStore</em>.</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Examples</div>
|
||||
@ -1202,7 +1223,7 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>In other words, the selector operator evaluates the expression (<code><select-expression></code>) on the left-hand side of the <code>?</code> symbol; it then compares the result obtained with the values listed in the <code><match-list>’s. If the comparision find a match with a value in a match-list, the associated `<case-multi-expression></code> is evaluted, and its result will be the final result of the selection operation.</p>
|
||||
<p>In other words, the selector operator evaluates the expression (<code><select-expression></code>) on the left-hand side of the <code>?</code> symbol; it then compares the result obtained with the values listed in the <code><match-list>’s. If the comparision finds a match with a value in a match-list, the associated `<case-multi-expression></code> is evaluted, and its result will be the final result of the selection operation.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The match lists are optional. In that case, the position, from left to right, of the <code><selector-case></code> is used as match-list. Of course, that only works if the select-expression results in an integer.</p>
|
||||
@ -1213,13 +1234,21 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
|
||||
<div class="listingblock">
|
||||
<div class="title">Examples</div>
|
||||
<div class="content">
|
||||
<pre class="rouge highlight"><code data-lang="go"><span class="m">1</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span> <span class="p">{</span><span class="s">"b"</span><span class="p">}</span> <span class="c">// returns "b"</span>
|
||||
<span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span> <span class="p">{</span><span class="s">"b"</span><span class="p">}</span> <span class="o">::</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span> <span class="c">// returns "c"</span>
|
||||
<span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span><span class="p">[</span><span class="no">true</span><span class="p">,</span> <span class="m">2</span><span class="o">+</span><span class="m">8</span><span class="p">]</span> <span class="p">{</span><span class="s">"b"</span><span class="p">}</span> <span class="o">::</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span> <span class="c">// returns "b"</span>
|
||||
<span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span><span class="p">[</span><span class="no">true</span><span class="p">,</span> <span class="m">2</span><span class="o">+</span><span class="m">8</span><span class="p">]</span> <span class="p">{</span><span class="s">"b"</span><span class="p">}</span> <span class="o">::</span><span class="p">[</span><span class="m">10</span><span class="p">]</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span> <span class="c">// error: "... case list in default clause"</span>
|
||||
<span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span><span class="p">[</span><span class="m">10</span><span class="p">]</span> <span class="p">{</span><span class="n">x</span><span class="o">=</span><span class="s">"b"</span> <span class="n">but</span> <span class="n">x</span><span class="p">}</span> <span class="o">::</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span> <span class="c">// returns "b"</span>
|
||||
<span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span><span class="p">[</span><span class="m">10</span><span class="p">]</span> <span class="p">{</span><span class="n">x</span><span class="o">=</span><span class="s">"b"</span><span class="p">;</span> <span class="n">x</span><span class="p">}</span> <span class="o">::</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span> <span class="c">// returns "b"</span>
|
||||
<span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span> <span class="p">{</span><span class="s">"b"</span><span class="p">}</span> <span class="c">// error: "... no case catches the value (10) of the selection expression</span></code></pre>
|
||||
<pre class="rouge highlight"><code data-lang="go"><span class="s">`>>>`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`1 ? {"a"} : {"b"}`</span>
|
||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`b`</span>
|
||||
<span class="s">`>>>`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`10 ? {"a"} : {"b"} :: {"c"}`</span>
|
||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`c'
|
||||
[green]`</span><span class="o">>>></span><span class="s">` [blue]`</span><span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span><span class="p">[</span><span class="no">true</span><span class="p">,</span> <span class="m">2</span><span class="o">+</span><span class="m">8</span><span class="p">]</span> <span class="p">{</span><span class="s">"b"</span><span class="p">}</span> <span class="o">::</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span><span class="s">`
|
||||
[green]`</span><span class="n">b</span><span class="s">`
|
||||
`</span><span class="o">>>></span><span class="s">` [blue]`</span><span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span><span class="p">[</span><span class="no">true</span><span class="p">,</span> <span class="m">2</span><span class="o">+</span><span class="m">8</span><span class="p">]</span> <span class="p">{</span><span class="s">"b"</span><span class="p">}</span> <span class="o">::</span><span class="p">[</span><span class="m">10</span><span class="p">]</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span><span class="s">`
|
||||
[red]`</span><span class="n">Parse</span> <span class="n">Error</span><span class="o">:</span> <span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="m">34</span><span class="p">]</span> <span class="k">case</span> <span class="n">list</span> <span class="n">in</span> <span class="k">default</span> <span class="n">clause</span><span class="s">`
|
||||
[green]`</span><span class="o">>>></span><span class="s">` [blue]`</span><span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span><span class="p">[</span><span class="m">10</span><span class="p">]</span> <span class="p">{</span><span class="n">x</span><span class="o">=</span><span class="s">"b"</span> <span class="n">but</span> <span class="n">x</span><span class="p">}</span> <span class="o">::</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span><span class="s">`
|
||||
[green]`</span><span class="n">b</span><span class="s">`
|
||||
`</span><span class="o">>>></span><span class="s">` [blue]`</span><span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span><span class="p">[</span><span class="m">10</span><span class="p">]</span> <span class="p">{</span><span class="n">x</span><span class="o">=</span><span class="s">"b"</span><span class="p">;</span> <span class="n">x</span><span class="p">}</span> <span class="o">::</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span><span class="s">`
|
||||
[green]`</span><span class="n">b</span><span class="s">`
|
||||
`</span><span class="o">>>></span><span class="s">` [blue]`</span><span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span> <span class="p">{</span><span class="s">"b"</span><span class="p">}</span><span class="s">`
|
||||
[red]`</span><span class="n">Eval</span> <span class="n">Error</span><span class="o">:</span> <span class="p">[</span><span class="m">1</span><span class="o">:</span><span class="m">3</span><span class="p">]</span> <span class="n">no</span> <span class="k">case</span> <span class="n">catches</span> <span class="n">the</span> <span class="n">value</span> <span class="p">(</span><span class="m">10</span><span class="p">)</span> <span class="n">of</span> <span class="n">the</span> <span class="n">selection</span> <span class="n">expression</span><span class="s">`
|
||||
</span></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -1490,7 +1519,7 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2024-05-10 06:38:03 +0200
|
||||
Last updated 2024-05-11 20:13:17 +0200
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
119
func-base.go
119
func-base.go
@ -43,6 +43,11 @@ func isFractionFunc(ctx ExprContext, name string, args []any) (result any, err e
|
||||
return
|
||||
}
|
||||
|
||||
func isRationalFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
result = IsRational(args[0])
|
||||
return
|
||||
}
|
||||
|
||||
func isListFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
result = IsList(args[0])
|
||||
return
|
||||
@ -53,30 +58,97 @@ func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
if len(args) == 1 {
|
||||
switch v := args[0].(type) {
|
||||
case int64:
|
||||
result = v
|
||||
case float64:
|
||||
result = int64(math.Trunc(v))
|
||||
case bool:
|
||||
if v {
|
||||
result = int64(1)
|
||||
} else {
|
||||
result = int64(0)
|
||||
}
|
||||
case string:
|
||||
var i int
|
||||
if i, err = strconv.Atoi(v); err == nil {
|
||||
result = int64(i)
|
||||
}
|
||||
default:
|
||||
err = errCantConvert(name, v, "int")
|
||||
switch v := args[0].(type) {
|
||||
case int64:
|
||||
result = v
|
||||
case float64:
|
||||
result = int64(math.Trunc(v))
|
||||
case bool:
|
||||
if v {
|
||||
result = int64(1)
|
||||
} else {
|
||||
result = int64(0)
|
||||
}
|
||||
} else {
|
||||
err = errOneParam(name)
|
||||
case string:
|
||||
var i int
|
||||
if i, err = strconv.Atoi(v); err == nil {
|
||||
result = int64(i)
|
||||
}
|
||||
default:
|
||||
err = errCantConvert(name, v, "int")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
switch v := args[0].(type) {
|
||||
case int64:
|
||||
result = float64(v)
|
||||
case float64:
|
||||
result = v
|
||||
case bool:
|
||||
if v {
|
||||
result = float64(1)
|
||||
} else {
|
||||
result = float64(0)
|
||||
}
|
||||
case string:
|
||||
var f float64
|
||||
if f, err = strconv.ParseFloat(v, 64); err == nil {
|
||||
result = f
|
||||
}
|
||||
case *fraction:
|
||||
result = v.toFloat()
|
||||
default:
|
||||
err = errCantConvert(name, v, "float")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func fractFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
switch v := args[0].(type) {
|
||||
case int64:
|
||||
var den int64 = 1
|
||||
if len(args) > 1 {
|
||||
var ok bool
|
||||
if den, ok = args[1].(int64); !ok {
|
||||
err = errExpectedGot(name, "integer", args[1])
|
||||
} else if den == 0 {
|
||||
err = errDivisionByZero(name)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
result = newFraction(v, den)
|
||||
}
|
||||
case float64:
|
||||
result, err = float64ToFraction(v)
|
||||
// var n, d int64
|
||||
// if n, d, err = float64ToFraction(v); err == nil {
|
||||
// result = newFraction(n, d)
|
||||
// }
|
||||
case bool:
|
||||
if v {
|
||||
result = newFraction(1, 1)
|
||||
} else {
|
||||
result = newFraction(0, 1)
|
||||
}
|
||||
case string:
|
||||
result, err = makeGeneratingFraction(v)
|
||||
// var f float64
|
||||
// // TODO temporary implementation
|
||||
// if f, err = strconv.ParseFloat(v, 64); err == nil {
|
||||
// var n, d int64
|
||||
// if n, d, err = float64ToFraction(f); err == nil {
|
||||
// result = newFraction(n, d)
|
||||
// }
|
||||
// } else {
|
||||
// errors.New("convertion from string to float is ongoing")
|
||||
// }
|
||||
case *fraction:
|
||||
result = v
|
||||
default:
|
||||
err = errCantConvert(name, v, "float")
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -93,10 +165,13 @@ func ImportBuiltinsFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("isString", &simpleFunctor{f: isStringFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isFraction", &simpleFunctor{f: isFractionFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isFract", &simpleFunctor{f: isFractionFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isRational", &simpleFunctor{f: isRationalFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isList", &simpleFunctor{f: isListFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isDictionary", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isDict", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
|
||||
ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 1, 1)
|
||||
ctx.RegisterFunc("dec", &simpleFunctor{f: decFunc}, 1, 1)
|
||||
ctx.RegisterFunc("fract", &simpleFunctor{f: fractFunc}, 1, 2)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@ -57,16 +57,21 @@ func TestFuncs(t *testing.T) {
|
||||
/* 44 */ {`builtin "string"; splitStr("one-two-three", "-", )`, newListA("one", "two", "three"), nil},
|
||||
/* 45 */ {`isInt(2+1)`, true, nil},
|
||||
/* 46 */ {`isInt(3.1)`, false, nil},
|
||||
/* 46 */ {`isFloat(3.1)`, true, nil},
|
||||
/* 47 */ {`isString("3.1")`, true, nil},
|
||||
/* 48 */ {`isString("3" + 1)`, true, nil},
|
||||
/* 49 */ {`isList(["3", 1])`, true, nil},
|
||||
/* 50 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
||||
/* 51 */ {`isFract(3|1)`, true, nil},
|
||||
/* 47 */ {`isFloat(3.1)`, true, nil},
|
||||
/* 48 */ {`isString("3.1")`, true, nil},
|
||||
/* 49 */ {`isString("3" + 1)`, true, nil},
|
||||
/* 50 */ {`isList(["3", 1])`, true, nil},
|
||||
/* 51 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
||||
/* 52 */ {`isFract(1|3)`, true, nil},
|
||||
/* 53 */ {`isFract(3|1)`, false, nil},
|
||||
/* 54 */ {`isRational(3|1)`, true, nil},
|
||||
/* 55 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil},
|
||||
/* 56 */ {`fract("2.2(3)")`, newFraction(67, 30), nil},
|
||||
/* 57 */ {`fract("1.21(3)")`, newFraction(91, 75), nil},
|
||||
}
|
||||
|
||||
t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
// parserTest(t, "Func", inputs[25:26])
|
||||
//parserTest(t, "Func", inputs[54:55])
|
||||
parserTest(t, "Func", inputs)
|
||||
}
|
||||
|
54
global-context.go
Normal file
54
global-context.go
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// global-context.go
|
||||
package expr
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
var globalCtx *SimpleFuncStore
|
||||
|
||||
func ImportInContext(name string) (exists bool) {
|
||||
var mod *module
|
||||
if mod, exists = moduleRegister[name]; exists {
|
||||
mod.importFunc(globalCtx)
|
||||
mod.imported = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ImportInContextByGlobPattern(pattern string) (count int, err error) {
|
||||
var matched bool
|
||||
for name, mod := range moduleRegister {
|
||||
if matched, err = filepath.Match(pattern, name); err == nil {
|
||||
if matched {
|
||||
count++
|
||||
mod.importFunc(globalCtx)
|
||||
mod.imported = true
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetVar(ctx ExprContext, name string) (value any, exists bool) {
|
||||
if value, exists = ctx.GetVar(name); !exists {
|
||||
value, exists = globalCtx.GetVar(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool, ownerCtx ExprContext) {
|
||||
if item, exists = ctx.GetFuncInfo(name); exists {
|
||||
ownerCtx = ctx
|
||||
} else if item, exists = globalCtx.GetFuncInfo(name); exists {
|
||||
ownerCtx = globalCtx
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
globalCtx = NewSimpleFuncStore()
|
||||
}
|
@ -39,6 +39,7 @@ func TestListParser(t *testing.T) {
|
||||
/* 17 */ {`list=["one","two","three"]; list.10`, nil, errors.New(`[1:36] index 10 out of bounds`)},
|
||||
/* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil},
|
||||
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
|
||||
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
|
||||
|
||||
// /* 8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil},
|
||||
// /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
|
||||
|
@ -6,7 +6,6 @@ package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type module struct {
|
||||
@ -31,30 +30,30 @@ func registerImport(name string, importFunc func(ExprContext), description strin
|
||||
moduleRegister[name] = newModule(importFunc, description)
|
||||
}
|
||||
|
||||
func ImportInContext(ctx ExprContext, name string) (exists bool) {
|
||||
var mod *module
|
||||
if mod, exists = moduleRegister[name]; exists {
|
||||
mod.importFunc(ctx)
|
||||
mod.imported = true
|
||||
}
|
||||
return
|
||||
}
|
||||
// func ImportInContext(ctx ExprContext, name string) (exists bool) {
|
||||
// var mod *module
|
||||
// if mod, exists = moduleRegister[name]; exists {
|
||||
// mod.importFunc(ctx)
|
||||
// mod.imported = true
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func ImportInContextByGlobPattern(ctx ExprContext, pattern string) (count int, err error) {
|
||||
var matched bool
|
||||
for name, mod := range moduleRegister {
|
||||
if matched, err = filepath.Match(pattern, name); err == nil {
|
||||
if matched {
|
||||
count++
|
||||
mod.importFunc(ctx)
|
||||
mod.imported = true
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// func ImportInContextByGlobPattern(ctx ExprContext, pattern string) (count int, err error) {
|
||||
// var matched bool
|
||||
// for name, mod := range moduleRegister {
|
||||
// if matched, err = filepath.Match(pattern, name); err == nil {
|
||||
// if matched {
|
||||
// count++
|
||||
// mod.importFunc(ctx)
|
||||
// mod.imported = true
|
||||
// }
|
||||
// } else {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func IterateModules(op func(name, description string, imported bool) bool) {
|
||||
if op != nil {
|
||||
|
@ -23,7 +23,7 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
|
||||
|
||||
// -------- eval func call
|
||||
func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) {
|
||||
if info, exists := ctx.GetFuncInfo(name); exists {
|
||||
if info, exists, owner := GetFuncInfo(ctx, name); exists {
|
||||
if info.MinArgs() > len(params) {
|
||||
if info.MaxArgs() < 0 {
|
||||
err = fmt.Errorf("too few params -- expected %d or more, got %d", info.MinArgs(), len(params))
|
||||
@ -31,9 +31,12 @@ func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) {
|
||||
err = fmt.Errorf("too few params -- expected %d, got %d", info.MinArgs(), len(params))
|
||||
}
|
||||
}
|
||||
if info.MaxArgs() >= 0 && info.MaxArgs() < len(params) {
|
||||
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(params) {
|
||||
err = fmt.Errorf("too much params -- expected %d, got %d", info.MaxArgs(), len(params))
|
||||
}
|
||||
if err == nil && owner != ctx {
|
||||
ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("unknown function %s()", name)
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ func newVarTerm(tk *Token) *term {
|
||||
func evalVar(ctx ExprContext, self *term) (v any, err error) {
|
||||
var exists bool
|
||||
name := self.source()
|
||||
if v, exists = ctx.GetVar(name); !exists {
|
||||
if info, exists := ctx.GetFuncInfo(name); exists {
|
||||
if v, exists = GetVar(ctx, name); !exists {
|
||||
if info, exists, _ := GetFuncInfo(ctx, name); exists {
|
||||
v = info.Functor()
|
||||
} else {
|
||||
err = fmt.Errorf("undefined variable or function %q", name)
|
||||
|
@ -28,13 +28,13 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
|
||||
count := 0
|
||||
if IsString(childValue) {
|
||||
module, _ := childValue.(string)
|
||||
count, err = ImportInContextByGlobPattern(ctx, module)
|
||||
count, err = ImportInContextByGlobPattern(module)
|
||||
} else {
|
||||
var moduleSpec any
|
||||
it := NewAnyIterator(childValue)
|
||||
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
||||
if module, ok := moduleSpec.(string); ok {
|
||||
if ImportInContext(ctx, module) {
|
||||
if ImportInContext(module) {
|
||||
count++
|
||||
} else {
|
||||
err = self.Errorf("unknown module %q", module)
|
||||
|
@ -7,6 +7,7 @@ package expr
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@ -24,6 +25,134 @@ func newFraction(num, den int64) *fraction {
|
||||
return &fraction{num, den}
|
||||
}
|
||||
|
||||
func float64ToFraction(f float64) (fract *fraction, err error) {
|
||||
var sign string
|
||||
intPart, decPart := math.Modf(f)
|
||||
if decPart < 0.0 {
|
||||
sign="-"
|
||||
intPart = -intPart
|
||||
decPart = -decPart
|
||||
}
|
||||
dec := fmt.Sprintf("%.12f", decPart)
|
||||
s := fmt.Sprintf("%s%.f%s", sign, intPart, dec[1:])
|
||||
// fmt.Printf("S: '%s'\n",s)
|
||||
return makeGeneratingFraction(s)
|
||||
}
|
||||
|
||||
// Based on https://cs.opensource.google/go/go/+/refs/tags/go1.22.3:src/math/big/rat.go;l=39
|
||||
/*
|
||||
func _float64ToFraction(f float64) (num, den int64, err error) {
|
||||
const expMask = 1<<11 - 1
|
||||
bits := math.Float64bits(f)
|
||||
mantissa := bits & (1<<52 - 1)
|
||||
exp := int((bits >> 52) & expMask)
|
||||
switch exp {
|
||||
case expMask: // non-finite
|
||||
err = errors.New("infite")
|
||||
return
|
||||
case 0: // denormal
|
||||
exp -= 1022
|
||||
default: // normal
|
||||
mantissa |= 1 << 52
|
||||
exp -= 1023
|
||||
}
|
||||
|
||||
shift := 52 - exp
|
||||
|
||||
// Optimization (?): partially pre-normalise.
|
||||
for mantissa&1 == 0 && shift > 0 {
|
||||
mantissa >>= 1
|
||||
shift--
|
||||
}
|
||||
|
||||
if f < 0 {
|
||||
num = -int64(mantissa)
|
||||
} else {
|
||||
num = int64(mantissa)
|
||||
}
|
||||
den = int64(1)
|
||||
|
||||
if shift > 0 {
|
||||
den = den << shift
|
||||
} else {
|
||||
num = num << (-shift)
|
||||
}
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
func makeGeneratingFraction(s string) (f *fraction, err error) {
|
||||
var num, den int64
|
||||
var sign int64 = 1
|
||||
var parts []string
|
||||
if len(s) == 0 {
|
||||
goto exit
|
||||
}
|
||||
if s[0] == '-' {
|
||||
sign=int64(-1)
|
||||
s = s[1:]
|
||||
} else if s[0] == '+' {
|
||||
s = s[1:]
|
||||
}
|
||||
parts = strings.SplitN(s, ".", 2)
|
||||
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
||||
return
|
||||
}
|
||||
if len(parts) == 1 {
|
||||
f = newFraction(sign*num, 1)
|
||||
} else if len(parts) == 2 {
|
||||
subParts := strings.SplitN(parts[1], "(", 2)
|
||||
if len(subParts) == 1 {
|
||||
den = 1
|
||||
dec := parts[1]
|
||||
lsd := len(dec)
|
||||
for i:=lsd-1; i>= 0 && dec[i]=='0'; i-- {
|
||||
lsd--
|
||||
}
|
||||
for _, c := range dec[0:lsd] {
|
||||
if c < '0' || c > '9' {
|
||||
return nil, errExpectedGot("fract", "digit", c)
|
||||
}
|
||||
num = num*10 + int64(c-'0')
|
||||
den = den * 10
|
||||
}
|
||||
f = newFraction(sign*num, den)
|
||||
} else if len(subParts) == 2 {
|
||||
sub := num
|
||||
mul := int64(1)
|
||||
for _, c := range subParts[0] {
|
||||
if c < '0' || c > '9' {
|
||||
return nil, errExpectedGot("fract", "digit", c)
|
||||
}
|
||||
num = num*10 + int64(c-'0')
|
||||
sub = sub*10 + int64(c-'0')
|
||||
mul *= 10
|
||||
}
|
||||
if len(subParts) == 2 {
|
||||
if s[len(s)-1] != ')' {
|
||||
goto exit
|
||||
}
|
||||
p := subParts[1][0 : len(subParts[1])-1]
|
||||
for _, c := range p {
|
||||
if c < '0' || c > '9' {
|
||||
return nil, errExpectedGot("fract", "digit", c)
|
||||
}
|
||||
num = num*10 + int64(c-'0')
|
||||
den = den*10 + 9
|
||||
}
|
||||
den *= mul
|
||||
}
|
||||
num -= sub
|
||||
f = newFraction(sign*num, den)
|
||||
}
|
||||
}
|
||||
exit:
|
||||
if f == nil {
|
||||
err = errors.New("bad syntax")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *fraction) toFloat() float64 {
|
||||
return float64(f.num) / float64(f.den)
|
||||
}
|
||||
@ -146,12 +275,12 @@ func lcm(a, b int64) (l int64) {
|
||||
|
||||
func sumFract(f1, f2 *fraction) (sum *fraction) {
|
||||
m := lcm(f1.den, f2.den)
|
||||
sum = newFraction(f1.num*(m/f1.den) + f2.num*(m/f2.den), m)
|
||||
sum = newFraction(f1.num*(m/f1.den)+f2.num*(m/f2.den), m)
|
||||
return
|
||||
}
|
||||
|
||||
func mulFract(f1, f2 *fraction) (prod *fraction) {
|
||||
prod = newFraction(f1.num * f2.num, f1.den * f2.den)
|
||||
prod = newFraction(f1.num*f2.num, f1.den*f2.den)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -24,11 +24,14 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) {
|
||||
}
|
||||
|
||||
if IsList(childValue) {
|
||||
list, _ := childValue.([]any)
|
||||
v = int64(len(list))
|
||||
ls, _ := childValue.(*ListType)
|
||||
v = int64(len(*ls))
|
||||
} else if IsString(childValue) {
|
||||
s, _ := childValue.(string)
|
||||
v = int64(len(s))
|
||||
} else if IsDict(childValue) {
|
||||
m, _ := childValue.(map[any]any)
|
||||
v = int64(len(m))
|
||||
} else if it, ok := childValue.(Iterator); ok {
|
||||
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) {
|
||||
count, _ := extIt.CallOperation(countName, nil)
|
||||
|
@ -119,7 +119,7 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||
tk = scanner.Next()
|
||||
if tk.IsSymbol(SymIdentifier) {
|
||||
param := newTerm(tk, nil)
|
||||
param := newTerm(tk)
|
||||
args = append(args, param)
|
||||
tk = scanner.Next()
|
||||
} else if itemExpected {
|
||||
|
@ -17,11 +17,10 @@ func registerTermConstructor(sym Symbol, constructor termContructor) {
|
||||
constructorRegistry[sym] = constructor
|
||||
}
|
||||
|
||||
func newTerm(tk *Token, parent *term) (inst *term) {
|
||||
func newTerm(tk *Token) (inst *term) {
|
||||
if constructorRegistry != nil {
|
||||
if construct, exists := constructorRegistry[tk.Sym]; exists {
|
||||
inst = construct(tk)
|
||||
inst.setParent(parent)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
Loading…
Reference in New Issue
Block a user