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) {
|
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)
|
err = self.addTerm(t)
|
||||||
} else {
|
} else {
|
||||||
err = tk.Errorf("unexpected token %q", tk.String())
|
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)
|
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,22 +23,9 @@ 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},
|
||||||
// /* 3 */ {`[1,2,"hello"]`, []any{int64(1), int64(2), "hello"}, nil},
|
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), 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,6 +194,25 @@ 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.
|
||||||
@ -278,29 +297,27 @@ The items of array can be accessed using the dot `.` operator.
|
|||||||
----
|
----
|
||||||
|
|
||||||
.Items of list
|
.Items of list
|
||||||
[source,go]
|
`>>>` [blue]`[1,2,3].1` +
|
||||||
----
|
[green]`2` +
|
||||||
`>>>` [blue]`[1,2,3].1`
|
`>>>` [blue]`list=[1,2,3]; list.1` +
|
||||||
[green]`2`
|
[green]`2` +
|
||||||
`>>>` [blue]`list=[1,2,3]; list.1`
|
`>>>` [blue]`["one","two","three"].1` +
|
||||||
[green]`2`
|
[green]`two` +
|
||||||
`>>>` [blue]`["one","two","three"].1`
|
`>>>` [blue]`list=["one","two","three"]; list.(2-1)` +
|
||||||
[green]`two`
|
[green]`two` +
|
||||||
`>>>` [blue]`list=["one","two","three"]; list.(2-1)`
|
`>>>` [blue]`list.(-1)` +
|
||||||
[green]`two`
|
[green]`three` +
|
||||||
`>>>` [blue]`list.(-1)`
|
`>>>` [blue]`list.(10)` +
|
||||||
[green]`three`
|
[red]`Eval Error: [1:9] index 10 out of bounds` +
|
||||||
`>>>` [blue]`list.(-1)`
|
`>>>` [blue]`#list` +
|
||||||
[green]`three`
|
[green]`3`
|
||||||
`>>>` [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.
|
||||||
|
|
||||||
.List examples
|
.Dictionary examples
|
||||||
[source,go]
|
[source,go]
|
||||||
----
|
----
|
||||||
{1:"one", 2:"two"}
|
{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.
|
WARNING: Support for dictionaries is still ongoing.
|
||||||
|
|
||||||
== Variables
|
== Variables
|
||||||
A variable is an identifier with an assigned value. Variables are stored in the object that implements the _ExprContext_ interface.
|
A variable is an identifier with an assigned value. Variables are stored in the object that implements the Go _ExprContext_ interface, e.g. _SimpleVarStore_ or _SimpleFuncStore_.
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
[source,go]
|
[source,go]
|
||||||
@ -365,23 +382,30 @@ 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 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 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]
|
||||||
----
|
----
|
||||||
1 ? {"a"} : {"b"} // returns "b"
|
`>>>` [blue]`1 ? {"a"} : {"b"}`
|
||||||
10 ? {"a"} : {"b"} :: {"c"} // returns "c"
|
[green]`b`
|
||||||
10 ? {"a"} :[true, 2+8] {"b"} :: {"c"} // returns "b"
|
`>>>` [blue]`10 ? {"a"} : {"b"} :: {"c"}`
|
||||||
10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"} // error: "... case list in default clause"
|
[green]`c'
|
||||||
10 ? {"a"} :[10] {x="b" but x} :: {"c"} // returns "b"
|
[green]`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`
|
||||||
10 ? {"a"} :[10] {x="b"; x} :: {"c"} // returns "b"
|
[green]`b`
|
||||||
10 ? {"a"} : {"b"} // error: "... no case catches the value (10) of the selection expression
|
`>>>` [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
|
== Priorities of operators
|
||||||
|
@ -868,6 +868,28 @@ 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>
|
||||||
@ -1059,23 +1081,22 @@ 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="listingblock">
|
<div class="paragraph">
|
||||||
<div class="title">Items of list</div>
|
<div class="title">Items of list</div>
|
||||||
<div class="content">
|
<p><code>>>></code> <code class="blue">[1,2,3].1</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 class="green">2</code><br>
|
||||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`2`</span>
|
<code>>>></code> <code class="blue">list=[1,2,3]; list.1</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 class="green">2</code><br>
|
||||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`2`</span>
|
<code>>>></code> <code class="blue">["one","two","three"].1</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 class="green">two</code><br>
|
||||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`two`</span>
|
<code>>>></code> <code class="blue">list=["one","two","three"]; list.(2-1)</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 class="green">two</code><br>
|
||||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`two`</span>
|
<code>>>></code> <code class="blue">list.(-1)</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 class="green">three</code><br>
|
||||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`three`</span>
|
<code>>>></code> <code class="blue">list.(10)</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 class="red">Eval Error: [1:9] index 10 out of bounds</code><br>
|
||||||
<span class="p">[</span><span class="n">green</span><span class="p">]</span><span class="s">`three`</span>
|
<code>>>></code> <code class="blue">#list</code><br>
|
||||||
<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>
|
<code class="green">3</code></p>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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>
|
<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">List examples</div>
|
<div class="title">Dictionary 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>
|
||||||
@ -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>
|
<h2 id="_variables"><a class="anchor" href="#_variables"></a><a class="link" href="#_variables">4. Variables</a></h2>
|
||||||
<div class="sectionbody">
|
<div class="sectionbody">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>A variable is an identifier with an assigned value. Variables are stored in the object that implements the <em>ExprContext</em> interface.</p>
|
<p>A variable is an identifier with an assigned value. Variables are stored in the object that implements the Go <em>ExprContext</em> interface, e.g. <em>SimpleVarStore</em> or <em>SimpleFuncStore</em>.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="listingblock">
|
<div class="listingblock">
|
||||||
<div class="title">Examples</div>
|
<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>
|
</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 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>
|
||||||
<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>
|
||||||
@ -1213,13 +1234,21 @@ 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="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>
|
<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="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="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="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="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="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="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="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="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="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>
|
[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="s">"b"</span><span class="p">}</span> <span class="c">// error: "... no case catches the value (10) of the selection expression</span></code></pre>
|
`</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1490,7 +1519,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-10 06:38:03 +0200
|
Last updated 2024-05-11 20:13:17 +0200
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</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
|
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
|
||||||
@ -53,30 +58,97 @@ 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) {
|
||||||
if len(args) == 1 {
|
switch v := args[0].(type) {
|
||||||
switch v := args[0].(type) {
|
case int64:
|
||||||
case int64:
|
result = v
|
||||||
result = v
|
case float64:
|
||||||
case float64:
|
result = int64(math.Trunc(v))
|
||||||
result = int64(math.Trunc(v))
|
case bool:
|
||||||
case bool:
|
if v {
|
||||||
if v {
|
result = int64(1)
|
||||||
result = int64(1)
|
} else {
|
||||||
} else {
|
result = int64(0)
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
} else {
|
case string:
|
||||||
err = errOneParam(name)
|
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
|
return
|
||||||
}
|
}
|
||||||
@ -93,10 +165,13 @@ 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,16 +57,21 @@ 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},
|
||||||
/* 46 */ {`isFloat(3.1)`, true, nil},
|
/* 47 */ {`isFloat(3.1)`, true, nil},
|
||||||
/* 47 */ {`isString("3.1")`, true, nil},
|
/* 48 */ {`isString("3.1")`, true, nil},
|
||||||
/* 48 */ {`isString("3" + 1)`, true, nil},
|
/* 49 */ {`isString("3" + 1)`, true, nil},
|
||||||
/* 49 */ {`isList(["3", 1])`, true, nil},
|
/* 50 */ {`isList(["3", 1])`, true, nil},
|
||||||
/* 50 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
/* 51 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
||||||
/* 51 */ {`isFract(3|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", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTest(t, "Func", inputs[25:26])
|
//parserTest(t, "Func", inputs[54:55])
|
||||||
parserTest(t, "Func", inputs)
|
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`)},
|
/* 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,7 +6,6 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type module struct {
|
type module struct {
|
||||||
@ -31,30 +30,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 := ctx.GetFuncInfo(name); exists {
|
if info, exists, owner := GetFuncInfo(ctx, 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,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))
|
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))
|
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 = ctx.GetVar(name); !exists {
|
if v, exists = GetVar(ctx, name); !exists {
|
||||||
if info, exists := ctx.GetFuncInfo(name); exists {
|
if info, exists, _ := GetFuncInfo(ctx, 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(ctx, module)
|
count, err = ImportInContextByGlobPattern(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(ctx, module) {
|
if ImportInContext(module) {
|
||||||
count++
|
count++
|
||||||
} else {
|
} else {
|
||||||
err = self.Errorf("unknown module %q", module)
|
err = self.Errorf("unknown module %q", module)
|
||||||
|
@ -7,6 +7,7 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -24,6 +25,134 @@ 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)
|
||||||
}
|
}
|
||||||
@ -146,12 +275,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,11 +24,14 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if IsList(childValue) {
|
if IsList(childValue) {
|
||||||
list, _ := childValue.([]any)
|
ls, _ := childValue.(*ListType)
|
||||||
v = int64(len(list))
|
v = int64(len(*ls))
|
||||||
} 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, nil)
|
param := newTerm(tk)
|
||||||
args = append(args, param)
|
args = append(args, param)
|
||||||
tk = scanner.Next()
|
tk = scanner.Next()
|
||||||
} else if itemExpected {
|
} else if itemExpected {
|
||||||
|
@ -17,11 +17,10 @@ func registerTermConstructor(sym Symbol, constructor termContructor) {
|
|||||||
constructorRegistry[sym] = constructor
|
constructorRegistry[sym] = constructor
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTerm(tk *Token, parent *term) (inst *term) {
|
func newTerm(tk *Token) (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,6 +44,13 @@ 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