Compare commits
No commits in common. "f028485caae5e92a9fe30c63b700c8ce17efb8fa" and "4aa0113c6a47f54ade44013682bcaf6e0a9f6c32" have entirely different histories.
f028485caa
...
4aa0113c6a
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) {
|
func (self *ast) addToken2(tk *Token) (t *term, err error) {
|
||||||
if t = newTerm(tk); t != nil {
|
if t = newTerm(tk, nil); t != nil {
|
||||||
err = self.addTerm(t)
|
err = self.addTerm(t)
|
||||||
} else {
|
} else {
|
||||||
err = tk.Errorf("unexpected token %q", tk.String())
|
err = tk.Errorf("unexpected token %q", tk.String())
|
||||||
|
@ -18,10 +18,6 @@ func errExpectedGot(funcName string, kind string, value any) error {
|
|||||||
return fmt.Errorf("%s() expected %s, got %T (%v)", funcName, kind, value, value)
|
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
|
// --- Parameter errors
|
||||||
|
|
||||||
func errOneParam(funcName string) error {
|
func errOneParam(funcName string) error {
|
||||||
|
17
dict_test.go
17
dict_test.go
@ -23,9 +23,22 @@ func TestDictParser(t *testing.T) {
|
|||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`{}`, map[any]any{}, nil},
|
/* 1 */ {`{}`, map[any]any{}, nil},
|
||||||
/* 2 */ {`{123}`, nil, errors.New(`[1:6] expected ":", got "}"`)},
|
/* 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},
|
/* 4 */ {`{1:"one",2:"two",3:"three"}.2`, "three", nil},
|
||||||
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), 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},
|
||||||
}
|
}
|
||||||
|
|
||||||
succeeded := 0
|
succeeded := 0
|
||||||
|
@ -194,25 +194,6 @@ Some arithmetic operators can also be used with strings.
|
|||||||
| [blue]`*` | _repeat_ | Make _n_ copy of a string | [blue]`"one" * 2` _["oneone"]_
|
| [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
|
||||||
Boolean data type has two values only: _true_ and _false_. Relational and Boolean expressions produce Boolean values.
|
Boolean data type has two values only: _true_ and _false_. Relational and Boolean expressions produce Boolean values.
|
||||||
@ -297,27 +278,29 @@ The items of array can be accessed using the dot `.` operator.
|
|||||||
----
|
----
|
||||||
|
|
||||||
.Items of list
|
.Items of list
|
||||||
`>>>` [blue]`[1,2,3].1` +
|
[source,go]
|
||||||
[green]`2` +
|
----
|
||||||
`>>>` [blue]`list=[1,2,3]; list.1` +
|
`>>>` [blue]`[1,2,3].1`
|
||||||
[green]`2` +
|
[green]`2`
|
||||||
`>>>` [blue]`["one","two","three"].1` +
|
`>>>` [blue]`list=[1,2,3]; list.1`
|
||||||
[green]`two` +
|
[green]`2`
|
||||||
`>>>` [blue]`list=["one","two","three"]; list.(2-1)` +
|
`>>>` [blue]`["one","two","three"].1`
|
||||||
[green]`two` +
|
[green]`two`
|
||||||
`>>>` [blue]`list.(-1)` +
|
`>>>` [blue]`list=["one","two","three"]; list.(2-1)`
|
||||||
[green]`three` +
|
[green]`two`
|
||||||
`>>>` [blue]`list.(10)` +
|
`>>>` [blue]`list.(-1)`
|
||||||
[red]`Eval Error: [1:9] index 10 out of bounds` +
|
[green]`three`
|
||||||
`>>>` [blue]`#list` +
|
`>>>` [blue]`list.(-1)`
|
||||||
[green]`3`
|
[green]`three`
|
||||||
|
`>>>` [blue]`list.(10)`
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
== Dictionaries
|
== 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.
|
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.
|
||||||
|
|
||||||
.Dictionary examples
|
.List examples
|
||||||
[source,go]
|
[source,go]
|
||||||
----
|
----
|
||||||
{1:"one", 2:"two"}
|
{1:"one", 2:"two"}
|
||||||
@ -328,7 +311,7 @@ The _dictionary_ data-type is set of pairs _key/value_. It is also known as _map
|
|||||||
WARNING: Support for dictionaries is still ongoing.
|
WARNING: Support for dictionaries is still ongoing.
|
||||||
|
|
||||||
== Variables
|
== Variables
|
||||||
A variable is an identifier with an assigned value. Variables are stored in the object that implements the Go _ExprContext_ interface, e.g. _SimpleVarStore_ or _SimpleFuncStore_.
|
A variable is an identifier with an assigned value. Variables are stored in the object that implements the _ExprContext_ interface.
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
[source,go]
|
[source,go]
|
||||||
@ -382,30 +365,23 @@ The _selector operator_ is very similar to the _switch/case/default_ statement a
|
|||||||
<multi-expression> ::= <expression> {";" <expression>}
|
<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 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.
|
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.
|
||||||
|
|
||||||
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 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.
|
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
|
.Examples
|
||||||
[source,go]
|
[source,go]
|
||||||
----
|
----
|
||||||
`>>>` [blue]`1 ? {"a"} : {"b"}`
|
1 ? {"a"} : {"b"} // returns "b"
|
||||||
[green]`b`
|
10 ? {"a"} : {"b"} :: {"c"} // returns "c"
|
||||||
`>>>` [blue]`10 ? {"a"} : {"b"} :: {"c"}`
|
10 ? {"a"} :[true, 2+8] {"b"} :: {"c"} // returns "b"
|
||||||
[green]`c'
|
10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"} // error: "... case list in default clause"
|
||||||
[green]`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`
|
10 ? {"a"} :[10] {x="b" but x} :: {"c"} // returns "b"
|
||||||
[green]`b`
|
10 ? {"a"} :[10] {x="b"; x} :: {"c"} // returns "b"
|
||||||
`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}`
|
10 ? {"a"} : {"b"} // error: "... no case catches the value (10) of the selection expression
|
||||||
[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
|
== Priorities of operators
|
||||||
|
@ -868,28 +868,6 @@ pre.rouge .ss {
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</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>
|
||||||
<div class="sect2">
|
<div class="sect2">
|
||||||
<h3 id="_boolean"><a class="anchor" href="#_boolean"></a><a class="link" href="#_boolean">2.4. Boolean</a></h3>
|
<h3 id="_boolean"><a class="anchor" href="#_boolean"></a><a class="link" href="#_boolean">2.4. Boolean</a></h3>
|
||||||
@ -1081,22 +1059,23 @@ pre.rouge .ss {
|
|||||||
<pre class="rouge highlight"><code data-lang="bnf"><item> ::= <list-expr>"."<index-expr></code></pre>
|
<pre class="rouge highlight"><code data-lang="bnf"><item> ::= <list-expr>"."<index-expr></code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="listingblock">
|
||||||
<div class="title">Items of list</div>
|
<div class="title">Items of list</div>
|
||||||
<p><code>>>></code> <code class="blue">[1,2,3].1</code><br>
|
<div class="content">
|
||||||
<code class="green">2</code><br>
|
<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>
|
||||||
<code>>>></code> <code class="blue">list=[1,2,3]; list.1</code><br>
|
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`2`</span>
|
||||||
<code class="green">2</code><br>
|
<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>
|
||||||
<code>>>></code> <code class="blue">["one","two","three"].1</code><br>
|
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`2`</span>
|
||||||
<code class="green">two</code><br>
|
<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>
|
||||||
<code>>>></code> <code class="blue">list=["one","two","three"]; list.(2-1)</code><br>
|
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`two`</span>
|
||||||
<code class="green">two</code><br>
|
<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>
|
||||||
<code>>>></code> <code class="blue">list.(-1)</code><br>
|
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`two`</span>
|
||||||
<code class="green">three</code><br>
|
<span class="s">`>>>`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`list.(-1)`</span>
|
||||||
<code>>>></code> <code class="blue">list.(10)</code><br>
|
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`three`</span>
|
||||||
<code class="red">Eval Error: [1:9] index 10 out of bounds</code><br>
|
<span class="s">`>>>`</span> <span class="p">[</span><span class="n">blue</span><span class="p">]</span><span class="s">`list.(-1)`</span>
|
||||||
<code>>>></code> <code class="blue">#list</code><br>
|
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`three`</span>
|
||||||
<code class="green">3</code></p>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1108,7 +1087,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>
|
<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>
|
||||||
<div class="listingblock">
|
<div class="listingblock">
|
||||||
<div class="title">Dictionary examples</div>
|
<div class="title">List examples</div>
|
||||||
<div class="content">
|
<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>
|
<pre class="rouge highlight"><code data-lang="go"><span class="p">{</span><span class="m">1</span><span class="o">:</span><span class="s">"one"</span><span class="p">,</span> <span class="m">2</span><span class="o">:</span><span class="s">"two"</span><span class="p">}</span>
|
||||||
<span class="p">{</span><span class="s">"one"</span><span class="o">:</span><span class="m">1</span><span class="p">,</span> <span class="s">"two"</span><span class="o">:</span> <span class="m">2</span><span class="p">}</span>
|
<span class="p">{</span><span class="s">"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>
|
||||||
@ -1133,7 +1112,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>
|
<h2 id="_variables"><a class="anchor" href="#_variables"></a><a class="link" href="#_variables">4. Variables</a></h2>
|
||||||
<div class="sectionbody">
|
<div class="sectionbody">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>A variable is an identifier with an assigned value. Variables are stored in the object that implements the Go <em>ExprContext</em> interface, e.g. <em>SimpleVarStore</em> or <em>SimpleFuncStore</em>.</p>
|
<p>A variable is an identifier with an assigned value. Variables are stored in the object that implements the <em>ExprContext</em> interface.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="listingblock">
|
<div class="listingblock">
|
||||||
<div class="title">Examples</div>
|
<div class="title">Examples</div>
|
||||||
@ -1223,7 +1202,7 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<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 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>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<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>
|
<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>
|
||||||
@ -1234,21 +1213,13 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
|
|||||||
<div class="listingblock">
|
<div class="listingblock">
|
||||||
<div class="title">Examples</div>
|
<div class="title">Examples</div>
|
||||||
<div class="content">
|
<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 ? {"a"} : {"b"}`</span>
|
<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="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`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="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="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="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`c'
|
<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>
|
||||||
[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">`
|
<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>
|
||||||
[green]`</span><span class="n">b</span><span class="s">`
|
<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><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">`
|
<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>
|
||||||
[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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1519,7 +1490,7 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
|
|||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<div id="footer-text">
|
<div id="footer-text">
|
||||||
Last updated 2024-05-11 20:13:17 +0200
|
Last updated 2024-05-10 06:38:03 +0200
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
117
func-base.go
117
func-base.go
@ -43,11 +43,6 @@ func isFractionFunc(ctx ExprContext, name string, args []any) (result any, err e
|
|||||||
return
|
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) {
|
func isListFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
result = IsList(args[0])
|
result = IsList(args[0])
|
||||||
return
|
return
|
||||||
@ -58,97 +53,30 @@ func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
switch v := args[0].(type) {
|
if len(args) == 1 {
|
||||||
case int64:
|
switch v := args[0].(type) {
|
||||||
result = v
|
case int64:
|
||||||
case float64:
|
result = v
|
||||||
result = int64(math.Trunc(v))
|
case float64:
|
||||||
case bool:
|
result = int64(math.Trunc(v))
|
||||||
if v {
|
case bool:
|
||||||
result = int64(1)
|
if v {
|
||||||
} else {
|
result = int64(1)
|
||||||
result = int64(0)
|
} 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")
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
case string:
|
||||||
|
var i int
|
||||||
|
if i, err = strconv.Atoi(v); err == nil {
|
||||||
|
result = int64(i)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err = errCantConvert(name, v, "int")
|
||||||
}
|
}
|
||||||
if err == nil {
|
} else {
|
||||||
result = newFraction(v, den)
|
err = errOneParam(name)
|
||||||
}
|
|
||||||
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
|
return
|
||||||
}
|
}
|
||||||
@ -165,13 +93,10 @@ func ImportBuiltinsFuncs(ctx ExprContext) {
|
|||||||
ctx.RegisterFunc("isString", &simpleFunctor{f: isStringFunc}, 1, 1)
|
ctx.RegisterFunc("isString", &simpleFunctor{f: isStringFunc}, 1, 1)
|
||||||
ctx.RegisterFunc("isFraction", &simpleFunctor{f: isFractionFunc}, 1, 1)
|
ctx.RegisterFunc("isFraction", &simpleFunctor{f: isFractionFunc}, 1, 1)
|
||||||
ctx.RegisterFunc("isFract", &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("isList", &simpleFunctor{f: isListFunc}, 1, 1)
|
||||||
ctx.RegisterFunc("isDictionary", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
|
ctx.RegisterFunc("isDictionary", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
|
||||||
ctx.RegisterFunc("isDict", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
|
ctx.RegisterFunc("isDict", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
|
||||||
ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 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() {
|
func init() {
|
||||||
|
@ -57,21 +57,16 @@ func TestFuncs(t *testing.T) {
|
|||||||
/* 44 */ {`builtin "string"; splitStr("one-two-three", "-", )`, newListA("one", "two", "three"), nil},
|
/* 44 */ {`builtin "string"; splitStr("one-two-three", "-", )`, newListA("one", "two", "three"), nil},
|
||||||
/* 45 */ {`isInt(2+1)`, true, nil},
|
/* 45 */ {`isInt(2+1)`, true, nil},
|
||||||
/* 46 */ {`isInt(3.1)`, false, nil},
|
/* 46 */ {`isInt(3.1)`, false, nil},
|
||||||
/* 47 */ {`isFloat(3.1)`, true, nil},
|
/* 46 */ {`isFloat(3.1)`, true, nil},
|
||||||
/* 48 */ {`isString("3.1")`, true, nil},
|
/* 47 */ {`isString("3.1")`, true, nil},
|
||||||
/* 49 */ {`isString("3" + 1)`, true, nil},
|
/* 48 */ {`isString("3" + 1)`, true, nil},
|
||||||
/* 50 */ {`isList(["3", 1])`, true, nil},
|
/* 49 */ {`isList(["3", 1])`, true, nil},
|
||||||
/* 51 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
/* 50 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
||||||
/* 52 */ {`isFract(1|3)`, true, nil},
|
/* 51 */ {`isFract(3|1)`, 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", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
//parserTest(t, "Func", inputs[54:55])
|
// parserTest(t, "Func", inputs[25:26])
|
||||||
parserTest(t, "Func", inputs)
|
parserTest(t, "Func", inputs)
|
||||||
}
|
}
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
// 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,7 +39,6 @@ func TestListParser(t *testing.T) {
|
|||||||
/* 17 */ {`list=["one","two","three"]; list.10`, nil, errors.New(`[1:36] index 10 out of bounds`)},
|
/* 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},
|
/* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil},
|
||||||
/* 19 */ {`["a", "b", "c"]`, newList([]any{"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},
|
// /* 8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil},
|
||||||
// /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
|
// /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
|
||||||
|
@ -6,6 +6,7 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
type module struct {
|
type module struct {
|
||||||
@ -30,30 +31,30 @@ func registerImport(name string, importFunc func(ExprContext), description strin
|
|||||||
moduleRegister[name] = newModule(importFunc, description)
|
moduleRegister[name] = newModule(importFunc, description)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func ImportInContext(ctx ExprContext, name string) (exists bool) {
|
func ImportInContext(ctx ExprContext, name string) (exists bool) {
|
||||||
// var mod *module
|
var mod *module
|
||||||
// if mod, exists = moduleRegister[name]; exists {
|
if mod, exists = moduleRegister[name]; exists {
|
||||||
// mod.importFunc(ctx)
|
mod.importFunc(ctx)
|
||||||
// mod.imported = true
|
mod.imported = true
|
||||||
// }
|
}
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
|
|
||||||
// func ImportInContextByGlobPattern(ctx ExprContext, pattern string) (count int, err error) {
|
func ImportInContextByGlobPattern(ctx ExprContext, pattern string) (count int, err error) {
|
||||||
// var matched bool
|
var matched bool
|
||||||
// for name, mod := range moduleRegister {
|
for name, mod := range moduleRegister {
|
||||||
// if matched, err = filepath.Match(pattern, name); err == nil {
|
if matched, err = filepath.Match(pattern, name); err == nil {
|
||||||
// if matched {
|
if matched {
|
||||||
// count++
|
count++
|
||||||
// mod.importFunc(ctx)
|
mod.importFunc(ctx)
|
||||||
// mod.imported = true
|
mod.imported = true
|
||||||
// }
|
}
|
||||||
// } else {
|
} else {
|
||||||
// break
|
break
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// return
|
return
|
||||||
// }
|
}
|
||||||
|
|
||||||
func IterateModules(op func(name, description string, imported bool) bool) {
|
func IterateModules(op func(name, description string, imported bool) bool) {
|
||||||
if op != nil {
|
if op != nil {
|
||||||
|
@ -23,7 +23,7 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
|
|||||||
|
|
||||||
// -------- eval func call
|
// -------- eval func call
|
||||||
func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) {
|
func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) {
|
||||||
if info, exists, owner := GetFuncInfo(ctx, name); exists {
|
if info, exists := ctx.GetFuncInfo(name); exists {
|
||||||
if info.MinArgs() > len(params) {
|
if info.MinArgs() > len(params) {
|
||||||
if info.MaxArgs() < 0 {
|
if info.MaxArgs() < 0 {
|
||||||
err = fmt.Errorf("too few params -- expected %d or more, got %d", info.MinArgs(), len(params))
|
err = fmt.Errorf("too few params -- expected %d or more, got %d", info.MinArgs(), len(params))
|
||||||
@ -31,12 +31,9 @@ func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) {
|
|||||||
err = fmt.Errorf("too few params -- expected %d, got %d", info.MinArgs(), len(params))
|
err = fmt.Errorf("too few params -- expected %d, got %d", info.MinArgs(), len(params))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(params) {
|
if info.MaxArgs() >= 0 && info.MaxArgs() < len(params) {
|
||||||
err = fmt.Errorf("too much params -- expected %d, got %d", 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 {
|
} else {
|
||||||
err = fmt.Errorf("unknown function %s()", name)
|
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) {
|
func evalVar(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var exists bool
|
var exists bool
|
||||||
name := self.source()
|
name := self.source()
|
||||||
if v, exists = GetVar(ctx, name); !exists {
|
if v, exists = ctx.GetVar(name); !exists {
|
||||||
if info, exists, _ := GetFuncInfo(ctx, name); exists {
|
if info, exists := ctx.GetFuncInfo(name); exists {
|
||||||
v = info.Functor()
|
v = info.Functor()
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("undefined variable or function %q", name)
|
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
|
count := 0
|
||||||
if IsString(childValue) {
|
if IsString(childValue) {
|
||||||
module, _ := childValue.(string)
|
module, _ := childValue.(string)
|
||||||
count, err = ImportInContextByGlobPattern(module)
|
count, err = ImportInContextByGlobPattern(ctx, module)
|
||||||
} else {
|
} else {
|
||||||
var moduleSpec any
|
var moduleSpec any
|
||||||
it := NewAnyIterator(childValue)
|
it := NewAnyIterator(childValue)
|
||||||
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
||||||
if module, ok := moduleSpec.(string); ok {
|
if module, ok := moduleSpec.(string); ok {
|
||||||
if ImportInContext(module) {
|
if ImportInContext(ctx, module) {
|
||||||
count++
|
count++
|
||||||
} else {
|
} else {
|
||||||
err = self.Errorf("unknown module %q", module)
|
err = self.Errorf("unknown module %q", module)
|
||||||
|
@ -7,7 +7,6 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -25,134 +24,6 @@ func newFraction(num, den int64) *fraction {
|
|||||||
return &fraction{num, den}
|
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 {
|
func (f *fraction) toFloat() float64 {
|
||||||
return float64(f.num) / float64(f.den)
|
return float64(f.num) / float64(f.den)
|
||||||
}
|
}
|
||||||
@ -275,12 +146,12 @@ func lcm(a, b int64) (l int64) {
|
|||||||
|
|
||||||
func sumFract(f1, f2 *fraction) (sum *fraction) {
|
func sumFract(f1, f2 *fraction) (sum *fraction) {
|
||||||
m := lcm(f1.den, f2.den)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func mulFract(f1, f2 *fraction) (prod *fraction) {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,14 +24,11 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if IsList(childValue) {
|
if IsList(childValue) {
|
||||||
ls, _ := childValue.(*ListType)
|
list, _ := childValue.([]any)
|
||||||
v = int64(len(*ls))
|
v = int64(len(list))
|
||||||
} else if IsString(childValue) {
|
} else if IsString(childValue) {
|
||||||
s, _ := childValue.(string)
|
s, _ := childValue.(string)
|
||||||
v = int64(len(s))
|
v = int64(len(s))
|
||||||
} else if IsDict(childValue) {
|
|
||||||
m, _ := childValue.(map[any]any)
|
|
||||||
v = int64(len(m))
|
|
||||||
} else if it, ok := childValue.(Iterator); ok {
|
} else if it, ok := childValue.(Iterator); ok {
|
||||||
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) {
|
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) {
|
||||||
count, _ := extIt.CallOperation(countName, nil)
|
count, _ := extIt.CallOperation(countName, nil)
|
||||||
|
@ -119,7 +119,7 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
tk = scanner.Next()
|
tk = scanner.Next()
|
||||||
if tk.IsSymbol(SymIdentifier) {
|
if tk.IsSymbol(SymIdentifier) {
|
||||||
param := newTerm(tk)
|
param := newTerm(tk, nil)
|
||||||
args = append(args, param)
|
args = append(args, param)
|
||||||
tk = scanner.Next()
|
tk = scanner.Next()
|
||||||
} else if itemExpected {
|
} else if itemExpected {
|
||||||
|
@ -17,10 +17,11 @@ func registerTermConstructor(sym Symbol, constructor termContructor) {
|
|||||||
constructorRegistry[sym] = constructor
|
constructorRegistry[sym] = constructor
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTerm(tk *Token) (inst *term) {
|
func newTerm(tk *Token, parent *term) (inst *term) {
|
||||||
if constructorRegistry != nil {
|
if constructorRegistry != nil {
|
||||||
if construct, exists := constructorRegistry[tk.Sym]; exists {
|
if construct, exists := constructorRegistry[tk.Sym]; exists {
|
||||||
inst = construct(tk)
|
inst = construct(tk)
|
||||||
|
inst.setParent(parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
7
utils.go
7
utils.go
@ -44,13 +44,6 @@ func IsFract(v any) (ok bool) {
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsRational(v any) (ok bool) {
|
|
||||||
if _, ok = v.(*fraction); !ok {
|
|
||||||
_, ok = v.(int64)
|
|
||||||
}
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsNumber(v any) (ok bool) {
|
func IsNumber(v any) (ok bool) {
|
||||||
return IsFloat(v) || IsInteger(v)
|
return IsFloat(v) || IsInteger(v)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user