Compare commits

...

19 Commits

Author SHA1 Message Date
f22b5a6f0b Doc: more details on some operators 2024-05-20 06:59:49 +02:00
7c8dbb0ac7 Added virtual symbol SymVariable translated from general real SymIdentifier symbol 2024-05-20 06:05:24 +02:00
e5c5920db0 parser_test.go: incompatible type error corrected 2024-05-20 05:32:28 +02:00
61efdb4eef operator-fraction.go: Typer interface implementation 2024-05-20 05:31:20 +02:00
82ec78719d operand-list.go: ToString() now can handle the Truncate option 2024-05-20 05:30:26 +02:00
554ff1a9dd operator-sum.go: better type checking when adding fractions 2024-05-20 05:27:44 +02:00
6bb891e09d term.go: Error messagge about incompatible types now truncates long values 2024-05-20 05:26:33 +02:00
1c4ffd7d64 formatter.go: Truncate function and number type names 2024-05-20 05:25:04 +02:00
b92b19e1dd New interface to Typer: the function TypeName() returns a more readable type name 2024-05-19 02:23:28 +02:00
9967918418 operator-sum.go: adding item to a list is no more allowed. The sum operator '+' now ca only join two list. 2024-05-19 02:20:36 +02:00
6c14c07d66 operand-iterator.go: adapted to the new DictType 2024-05-19 01:47:06 +02:00
9ea170e53b new operator 'in': it returns true if a item belongs to a list or if a key belongs to a dict 2024-05-19 01:44:50 +02:00
a543360151 when the list value involved in an insert or append operations (directly) comes from a variable, that variable receives the result list 2024-05-19 01:42:15 +02:00
24a25bbf94 adapted and enhanced the dict operations to make them compatible with the new DictType 2024-05-19 01:38:07 +02:00
d6a1607041 The content of round bracket now returns an expressione term. This way the content is decoupled from external terms. 2024-05-19 01:34:07 +02:00
4d43ab2c2f context.go: setVar() renamed as UnsafeSetVar() 2024-05-19 01:27:44 +02:00
9bd4a0ba23 utils.go:fromGenericAny() now supports also ListType and DictType 2024-05-19 01:21:06 +02:00
2b184cf3f2 operand-map.go replaced by operand-dict.go 2024-05-19 01:20:04 +02:00
263e419d9a operand-map.go: to be removed 2024-05-18 08:54:18 +02:00
30 changed files with 554 additions and 161 deletions

4
ast.go
View File

@ -119,7 +119,7 @@ func (self *ast) eval(ctx ExprContext, preset bool) (result any, err error) {
if self.forest != nil { if self.forest != nil {
for _, root := range self.forest { for _, root := range self.forest {
if result, err = root.compute(ctx); err == nil { if result, err = root.compute(ctx); err == nil {
ctx.setVar(ControlLastResult, result) ctx.UnsafeSetVar(ControlLastResult, result)
} else { } else {
//err = fmt.Errorf("error in expression nr %d: %v", i+1, err) //err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
break break
@ -128,7 +128,7 @@ func (self *ast) eval(ctx ExprContext, preset bool) (result any, err error) {
} }
if err == nil { if err == nil {
result, err = self.root.compute(ctx) result, err = self.root.compute(ctx)
ctx.setVar(ControlLastResult, result) ctx.UnsafeSetVar(ControlLastResult, result)
} }
// } else { // } else {
// err = errors.New("empty expression") // err = errors.New("empty expression")

View File

@ -15,7 +15,7 @@ func exportVar(ctx ExprContext, name string, value any) {
if name[0] == '@' { if name[0] == '@' {
name = name[1:] name = name[1:]
} }
ctx.setVar(name, value) ctx.UnsafeSetVar(name, value)
} }
func exportFunc(ctx ExprContext, name string, info ExprFunc) { func exportFunc(ctx ExprContext, name string, info ExprFunc) {

View File

@ -33,7 +33,7 @@ type ExprContext interface {
Clone() ExprContext Clone() ExprContext
GetVar(varName string) (value any, exists bool) GetVar(varName string) (value any, exists bool)
SetVar(varName string, value any) SetVar(varName string, value any)
setVar(varName string, value any) UnsafeSetVar(varName string, value any)
EnumVars(func(name string) (accept bool)) (varNames []string) EnumVars(func(name string) (accept bool)) (varNames []string)
EnumFuncs(func(name string) (accept bool)) (funcNames []string) EnumFuncs(func(name string) (accept bool)) (funcNames []string)
GetFuncInfo(name string) (item ExprFunc, exists bool) GetFuncInfo(name string) (item ExprFunc, exists bool)

View File

@ -102,3 +102,49 @@ func TestDictParser(t *testing.T) {
} }
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed) t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
} }
func TestDictToStringMultiLine(t *testing.T) {
var good bool
section := "dict-ToString-ML"
want := `{
"first": 1
}`
args := map[any]*term{
"first": newLiteralTerm(NewValueToken(0, 0, SymInteger, "1", 1)),
}
dict := newDict(args)
got := dict.ToString(MultiLine)
// fmt.Printf("got=%q\n", got)
if good = got == want; !good {
t.Errorf("ToString(MultiLine): got = %q, want %q", got, want)
}
if good {
t.Logf("%s -- succeeded", section)
} else {
t.Logf("%s -- failed", section)
}
}
func TestDictToString(t *testing.T) {
var good bool
section := "dict-ToString-SL"
want := `{"first": 1}`
args := map[any]*term{
"first": newLiteralTerm(NewValueToken(0, 0, SymInteger, "1", 1)),
}
dict := newDict(args)
got := dict.ToString(0)
// fmt.Printf("got=%q\n", got)
if good = got == want; !good {
t.Errorf("ToString(0): got = %q, want %q", got, want)
}
if good {
t.Logf("%s -- succeeded", section)
} else {
t.Logf("%s -- failed", section)
}
}

View File

@ -22,7 +22,7 @@ Expressions calculator
toc::[] toc::[]
#TODO: Work in progress (last update on 2024/05/17, 15:47 p.m.)# #TODO: Work in progress (last update on 2024/05/20, 06:58 a.m.)#
== Expr == Expr
_Expr_ is a GO package capable of analysing, interpreting and calculating expressions. _Expr_ is a GO package capable of analysing, interpreting and calculating expressions.
@ -266,10 +266,10 @@ Some arithmetic operators can also be used with strings.
|=== |===
| Symbol | Operation | Description | Examples | Symbol | Operation | Description | Examples
| [blue]`+` | _concatenation_ | Join two strings or two _stringable_ values | [blue]`"one" + "two"` _["onetwo"]_ + | [blue]`+` | _concatenation_ | Join two strings or two _stringable_ values | [blue]`"one" + "two"` -> _"onetwo"_ +
[blue]`"one" + 2` _["one2"]_ [blue]`"one" + 2` -> _"one2"_
| [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. The items of strings can be accessed using the dot `.` operator.
@ -280,15 +280,15 @@ _item_ = _string-expr_ "**.**" _integer-expr_
==== ====
.String examples .String examples
`>>>` [blue]`s="abc"` [gray]_assign the string to variable s_ + `>>>` [blue]`s="abc"` [gray]_// assign the string to variable s_ +
[green]`abc` + [green]`abc` +
`>>>` [blue]`s.1` [gray]_char at position 1 (starting from 0)_ + `>>>` [blue]`s.1` [gray]_// char at position 1 (starting from 0)_ +
[green]`b` + [green]`b` +
`>>>` [blue]`s.(-1)` [gray]_char at position -1, the rightmost one_ + `>>>` [blue]`s.(-1)` [gray]_// char at position -1, the rightmost one_ +
[green]`c` + [green]`c` +
`>>>` [blue]`\#s` [gray]_number of chars_ + `>>>` [blue]`\#s` [gray]_// number of chars_ +
[gren]`3` + [gren]`3` +
`>>>` [blue]`#"abc"` [gray]_number of chars_ + `>>>` [blue]`#"abc"` [gray]_// number of chars_ +
[green]`3` [green]`3`
@ -371,13 +371,18 @@ _non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value} "**]**" +
| [blue]`+` | _Join_ | Joins two lists | [blue]`[1,2] + [3]` -> _[1,2,3]_ | [blue]`+` | _Join_ | Joins two lists | [blue]`[1,2] + [3]` -> _[1,2,3]_
| [blue]`-` | _Difference_ | Left list without elements in the right list | [blue]`[1,2,3] - [2]` -> _[1,3]_ | [blue]`-` | _Difference_ | Left list without elements in the right list | [blue]`[1,2,3] - [2]` -> _[1,3]_
| [blue]`>>` | _Front insertion_ | Insert an item in front | [blue]`0 >> [1,2]` -> _[0,1,2]_
| [blue]`<<` | _Back insertion_ | Insert an item at end | [blue]`[1,2] << 3` -> _[1,2,3]_
| [blue]`.` | _List item_ | Item at given position | [blue]`[1,2.3].1` -> _2_
| [blue]`in` | _Item in list_ | True if item is in list | [blue]`2 in [1,2,3]` -> _true_ +
[blue]`6 in [1,2,3]` -> _false_
|=== |===
The items of array can be accessed using the dot `.` operator. The items of array can be accessed using the dot `.` operator.
.Item access syntax .Item access syntax
==== ====
_item_ = _list-expr_ "**.**" _list-expr_ _item_ = _list-expr_ "**.**" _integer-expr_
==== ====
.Items of list .Items of list
@ -394,14 +399,16 @@ _item_ = _list-expr_ "**.**" _list-expr_
`>>>` [blue]`list.(10)` + `>>>` [blue]`list.(10)` +
[red]`Eval Error: [1:9] index 10 out of bounds` + [red]`Eval Error: [1:9] index 10 out of bounds` +
`>>>` [blue]`#list` + `>>>` [blue]`#list` +
[green]`3` [green]`3` +
`>>>` [blue]`index=2; ["a", "b", "c", "d"].index` +
[green]`c`
=== Dictionaries === Dictionaries
WARNING: Support for dictionaries is still ongoing. The _dictionary_, or _dict_, data-type is set of pairs _key/value_. It is also known as _map_ or _associative array_.
The _dictionary_, or _dict_, 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 literals are sequences of pairs separated by comma [blue]`,` enclosed between brace brackets.
.Dict literal syntax .Dict literal syntax
==== ====
@ -412,11 +419,27 @@ _non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar
.Examples .Examples
`>>>` [blue]`{1:"one", 2:"two"}` + `>>>` [blue]`{1:"one", 2:"two"}` +
[green]`{1: "one", 2: "two"}` +
`>>>` [blue]`{"one":1, "two": 2}` + `>>>` [blue]`{"one":1, "two": 2}` +
`>>>` [blue]`{"sum":1+2+3, "prod":1*2*3}` [green]`{"one": 1, "two": 2}` +
`>>>` [blue]`{"sum":1+2+3, "prod":1*2*3}` +
[green]`{"sum": 6, "prod": 6}`
.Dict operators
[cols="^2,^2,4,5"]
|===
| Symbol | Operation | Description | Examples
| [blue]`+` | _Join_ | Joins two dicts | [blue]`{1:"one"}+{6:"six"}` -> _{1: "one", 6: "six"}_
| [blue]`.` | _Dict item value_ | Item value of given key | [blue]`{"one":1, "two":2}."two"` -> _2_
| [blue]`in` | _Key in dict_ | True if key is in dict | [blue]`"one" in {"one":1, "two":2}` -> _true_ +
[blue]`"six" in {"one":1, "two":2}` -> _false_
|===
== Variables == Variables
_Expr_ supports variables like most programming languages. A variable is an identifier with an assigned value. Variables are stored in _contexts_. _Expr_, like most programming languages, supports variables. A variable is an identifier with an assigned value. Variables are stored in _contexts_.
.Variable literal syntax .Variable literal syntax
==== ====
@ -467,12 +490,19 @@ The value of each sub-expression is stored in the automatica variable _last_.
=== [blue]`but` operator === [blue]`but` operator
[blue]`but` is an infixed operator. Its operands can be expressions of any type. It evaluates the left expression first, then the right expression. The value of the right expression is the final result. Examples: [blue]`5 but 2` returns 2, [blue]`x=2*3 but x-1` returns 5. [blue]`but` is an infixed operator. Its operands can be expressions of any type. It evaluates the left expression first, then the right expression. The value of the right expression is the final result.
[blue]`but` is very similar to [blue]`;`. The only difference is that [blue]`;` can't be used inside parenthesis [blue]`(` and [blue]`)`. .Examples
[blue]`5 but 2` +
[green]`2` +
[blue]`x=2*3 but x-1` +
[green]`5`.
[blue]`but` behavior is very similar to [blue]`;`. The only difference is that [blue]`;` is not a true operator and can't be used inside parenthesis [blue]`(` and [blue]`)`.
=== Assignment operator [blue]`=` === Assignment operator [blue]`=`
The assignment operator [blue]`=` is used to define variables in the evaluation context or to change their value (see _ExprContext_). The assignment operator [blue]`=` is used to define variables or to change their value in the evaluation context (see _ExprContext_).
The value on the left side of [blue]`=` must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation. The value on the left side of [blue]`=` must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation.
.Example .Example
@ -540,11 +570,12 @@ NOTE: These operators have a high priority, in particular higher than the operat
The table below shows all supported operators by decreasing priorities. The table below shows all supported operators by decreasing priorities.
.Operators priorities .Operators priorities
[cols="^2,^2,^2,^5,^5"] [cols="^2,^2,^2,^5,^6"]
|=== |===
| Priority | Operators | Position | Operation | Operands and results | Priority | Operators | Position | Operation | Operands and results
.1+|*ITEM*| [blue]`.` | _Infix_ | _Item_| _collection_ `"."` _key_ -> _any_ .2+|*ITEM*| [blue]`.` | _Infix_ | _List item_| _list_ `"."` _integer_ -> _any_
| [blue]`.` | _Infix_ | _Dict item_ | _dict_ `""` _any_ -> _any_
.2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `"++"` -> _integer_ .2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `"++"` -> _integer_
| [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `"++"` -> _any_ | [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `"++"` -> _any_
.1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ `"!"` -> _integer_ .1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ `"!"` -> _integer_
@ -556,24 +587,29 @@ The table below shows all supported operators by decreasing priorities.
| [blue]`/` | _Infix_ | _Division_ | _number_ `"/"` _number_ -> _number_ | [blue]`/` | _Infix_ | _Division_ | _number_ `"/"` _number_ -> _number_
| [blue]`./` | _Infix_ | _Float-division_ | __number__ `"./"` _number_ -> _float_ | [blue]`./` | _Infix_ | _Float-division_ | __number__ `"./"` _number_ -> _float_
| [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ `"%"` _integer_ -> _integer_ | [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ `"%"` _integer_ -> _integer_
.5+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ `"+"` _number_ -> _number_ .6+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ `"+"` _number_ -> _number_
| [blue]`+` | _Infix_ | _String-concat_ | (_string_\|_number_) `"+"` (_string_\|_number_) -> _string_ | [blue]`+` | _Infix_ | _String-concat_ | (_string_\|_number_) `"+"` (_string_\|_number_) -> _string_
| [blue]`+` | _Infix_ | _List-join_ | _list_ `"+"` _list_ -> _list_ | [blue]`+` | _Infix_ | _List-join_ | _list_ `"+"` _list_ -> _list_
| [blue]`+` | _Infix_ | _Dict-join_ | _dict_ `"+"` _dict_ -> _dict_
| [blue]`-` | _Infix_ | _Subtraction_ | _number_ `"-"` _number_ -> _number_ | [blue]`-` | _Infix_ | _Subtraction_ | _number_ `"-"` _number_ -> _number_
| [blue]`-` | _Infix_ | _List-difference_ | _list_ `"-"` _list_ -> _list_ | [blue]`-` | _Infix_ | _List-difference_ | _list_ `"-"` _list_ -> _list_
.6+|*RELATION*| [blue]`<` | _Infix_ | _less_ | _comparable_ `"<"` _comparable_ -> _boolean_ .8+|*RELATION*| [blue]`<` | _Infix_ | _less_ | _comparable_ `"<"` _comparable_ -> _boolean_
| [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ `"\<="` _comparable_ -> _boolean_ | [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ `"\<="` _comparable_ -> _boolean_
| [blue]`>` | _Infix_ | _greater_ | _comparable_ `">"` _comparable_ -> _boolean_ | [blue]`>` | _Infix_ | _greater_ | _comparable_ `">"` _comparable_ -> _boolean_
| [blue]`>=` | _Infix_ | _greater-equal_ | _comparable_ `">="` _comparable_ -> _boolean_ | [blue]`>=` | _Infix_ | _greater-equal_ | _comparable_ `">="` _comparable_ -> _boolean_
| [blue]`==` | _Infix_ | _equal_ | _comparable_ `"=="` _comparable_ -> _boolean_ | [blue]`==` | _Infix_ | _equal_ | _comparable_ `"=="` _comparable_ -> _boolean_
| [blue]`!=` | _Infix_ | _not-equal_ | _comparable_ `"!="` _comparable_ -> _boolean_ | [blue]`!=` | _Infix_ | _not-equal_ | _comparable_ `"!="` _comparable_ -> _boolean_
| [blue]`in` | _Infix_ | _member-of-list_ | _any_ `"in"` _list_ -> _boolean_
| [blue]`in` | _Infix_ | _key-of-dict_ | _any_ `"in"` _dict_ -> _boolean_
.1+|*NOT*| [blue]`not` | _Prefix_ | _not_ | `"not"` _boolean_ -> _boolean_ .1+|*NOT*| [blue]`not` | _Prefix_ | _not_ | `"not"` _boolean_ -> _boolean_
.2+|*AND*| [blue]`and` | _Infix_ | _and_ | _boolean_ `"and"` _boolean_ -> _boolean_ .2+|*AND*| [blue]`and` | _Infix_ | _and_ | _boolean_ `"and"` _boolean_ -> _boolean_
| [blue]`&&` | _Infix_ | _and_ | _boolean_ `"&&"` _boolean_ -> _boolean_ | [blue]`&&` | _Infix_ | _and_ | _boolean_ `"&&"` _boolean_ -> _boolean_
.2+|*OR*| [blue]`or` | _Infix_ | _or_ | _boolean_ `"or"` _boolean_ -> _boolean_ .2+|*OR*| [blue]`or` | _Infix_ | _or_ | _boolean_ `"or"` _boolean_ -> _boolean_
| [blue]`\|\|` | _Infix_ | _or_ | _boolean_ `"\|\|"` _boolean_ -> _boolean_ | [blue]`\|\|` | _Infix_ | _or_ | _boolean_ `"\|\|"` _boolean_ -> _boolean_
.1+|*ASSIGN*| [blue]`=` | _Infix_ | _assignment_ | _identifier_ "=" _any_ -> _any_ .3+|*ASSIGN*| [blue]`=` | _Infix_ | _assignment_ | _identifier_ "=" _any_ -> _any_
.1+|*BUT*| [blue]`but` | _Infix_ | _but_ | _any_ "but" _any_ -> _any_ | [blue]`>>` | _Infix_ | _front-insert_ | _any_ ">>" _list_ -> _list_
| [blue]`<<` | _Infix_ | _back-insert_ | _list_ "<<" _any_ -> _list_
.1+|*BUT*| [blue]`but` | _Infix_ | _but_ | _any_ "but" _any_ -> _any_
|=== |===
== Functions == Functions

View File

@ -999,14 +999,14 @@ pre.rouge .ss {
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">+</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">+</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>concatenation</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>concatenation</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Join two strings or two <em>stringable</em> values</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Join two strings or two <em>stringable</em> values</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">"one" + "two"</code> <em>["onetwo"]</em><br> <td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">"one" + "two"</code> &#8594; <em>"onetwo"</em><br>
<code class="blue">"one" + 2</code> <em>["one2"]</em></p></td> <code class="blue">"one" + 2</code> &#8594; <em>"one2"</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">*</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">*</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>repeat</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>repeat</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Make <em>n</em> copy of a string</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Make <em>n</em> copy of a string</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">"one" * 2</code> <em>["oneone"]</em></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">"one" * 2</code> &#8594; <em>"oneone"</em></p></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -1023,15 +1023,15 @@ pre.rouge .ss {
</div> </div>
<div class="paragraph"> <div class="paragraph">
<div class="title">String examples</div> <div class="title">String examples</div>
<p><code>&gt;&gt;&gt;</code> <code class="blue">s="abc"</code> <em class="gray">assign the string to variable s</em><br> <p><code>&gt;&gt;&gt;</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 class="green">abc</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">s.1</code> <em class="gray">char at position 1 (starting from 0)</em><br> <code>&gt;&gt;&gt;</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 class="green">b</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">s.(-1)</code> <em class="gray">char at position -1, the rightmost one</em><br> <code>&gt;&gt;&gt;</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 class="green">c</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">#s</code> <em class="gray">number of chars</em><br> <code>&gt;&gt;&gt;</code> <code class="blue">#s</code> <em class="gray">// number of chars</em><br>
<code class="gren">3</code><br> <code class="gren">3</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">#"abc"</code> <em class="gray">number of chars</em><br> <code>&gt;&gt;&gt;</code> <code class="blue">#"abc"</code> <em class="gray">// number of chars</em><br>
<code class="green">3</code></p> <code class="green">3</code></p>
</div> </div>
</div> </div>
@ -1227,6 +1227,31 @@ pre.rouge .ss {
<td class="tableblock halign-left valign-top"><p class="tableblock">Left list without elements in the right list</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Left list without elements in the right list</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">[1,2,3] - [2]</code> &#8594; <em>[1,3]</em></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">[1,2,3] - [2]</code> &#8594; <em>[1,3]</em></p></td>
</tr> </tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&gt;&gt;</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Front insertion</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Insert an item in front</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">0 &gt;&gt; [1,2]</code> &#8594; <em>[0,1,2]</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&lt;&lt;</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Back insertion</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Insert an item at end</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">[1,2] &lt;&lt; 3</code> &#8594; <em>[1,2,3]</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">.</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>List item</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Item at given position</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">[1,2.3].1</code> &#8594; <em>2</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">in</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item in list</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">True if item is in list</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">2 in [1,2,3]</code> &#8594; <em>true</em><br>
<code class="blue">6 in [1,2,3]</code> &#8594; <em>false</em></p></td>
</tr>
</tbody> </tbody>
</table> </table>
<div class="paragraph"> <div class="paragraph">
@ -1236,7 +1261,7 @@ pre.rouge .ss {
<div class="title">Example 6. Item access syntax</div> <div class="title">Example 6. Item access syntax</div>
<div class="content"> <div class="content">
<div class="paragraph"> <div class="paragraph">
<p><em>item</em> = <em>list-expr</em> "<strong>.</strong>" <em>list-expr</em></p> <p><em>item</em> = <em>list-expr</em> "<strong>.</strong>" <em>integer-expr</em></p>
</div> </div>
</div> </div>
</div> </div>
@ -1255,25 +1280,18 @@ pre.rouge .ss {
<code>&gt;&gt;&gt;</code> <code class="blue">list.(10)</code><br> <code>&gt;&gt;&gt;</code> <code class="blue">list.(10)</code><br>
<code class="red">Eval Error: [1:9] index 10 out of bounds</code><br> <code class="red">Eval Error: [1:9] index 10 out of bounds</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">#list</code><br> <code>&gt;&gt;&gt;</code> <code class="blue">#list</code><br>
<code class="green">3</code></p> <code class="green">3</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">index=2; ["a", "b", "c", "d"].index</code><br>
<code class="green">c</code></p>
</div> </div>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_dictionaries"><a class="anchor" href="#_dictionaries"></a><a class="link" href="#_dictionaries">2.5. Dictionaries</a></h3> <h3 id="_dictionaries"><a class="anchor" href="#_dictionaries"></a><a class="link" href="#_dictionaries">2.5. Dictionaries</a></h3>
<div class="admonitionblock warning"> <div class="paragraph">
<table> <p>The <em>dictionary</em>, or <em>dict</em>, data-type is set of pairs <em>key/value</em>. It is also known as <em>map</em> or <em>associative array</em>.</p>
<tr>
<td class="icon">
<i class="fa icon-warning" title="Warning"></i>
</td>
<td class="content">
Support for dictionaries is still ongoing.
</td>
</tr>
</table>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p>The <em>dictionary</em>, or <em>dict</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>Dictionary literals are sequences of pairs separated by comma <code class="blue">,</code> enclosed between brace brackets.</p>
</div> </div>
<div class="exampleblock"> <div class="exampleblock">
<div class="title">Example 7. Dict literal syntax</div> <div class="title">Example 7. Dict literal syntax</div>
@ -1288,9 +1306,50 @@ Support for dictionaries is still ongoing.
<div class="paragraph"> <div class="paragraph">
<div class="title">Examples</div> <div class="title">Examples</div>
<p><code>&gt;&gt;&gt;</code> <code class="blue">{1:"one", 2:"two"}</code><br> <p><code>&gt;&gt;&gt;</code> <code class="blue">{1:"one", 2:"two"}</code><br>
<code class="green">{1: "one", 2: "two"}</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">{"one":1, "two": 2}</code><br> <code>&gt;&gt;&gt;</code> <code class="blue">{"one":1, "two": 2}</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">{"sum":1+2+3, "prod":1*2*3}</code></p> <code class="green">{"one": 1, "two": 2}</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">{"sum":1+2+3, "prod":1*2*3}</code><br>
<code class="green">{"sum": 6, "prod": 6}</code></p>
</div> </div>
<table class="tableblock frame-all grid-all stretch">
<caption class="title">Table 7. Dict operators</caption>
<colgroup>
<col style="width: 15.3846%;">
<col style="width: 15.3846%;">
<col style="width: 30.7692%;">
<col style="width: 38.4616%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-center valign-top">Symbol</th>
<th class="tableblock halign-center valign-top">Operation</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Examples</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">+</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Join</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Joins two dicts</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">{1:"one"}+{6:"six"}</code> &#8594; <em>{1: "one", 6: "six"}</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">.</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Dict item value</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Item value of given key</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">{"one":1, "two":2}."two"</code> &#8594; <em>2</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">in</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Key in dict</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">True if key is in dict</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">"one" in {"one":1, "two":2}</code> &#8594; <em>true</em><br>
<code class="blue">"six" in {"one":1, "two":2}</code> &#8594; <em>false</em></p></td>
</tr>
</tbody>
</table>
</div> </div>
</div> </div>
</div> </div>
@ -1298,7 +1357,7 @@ Support for dictionaries is still ongoing.
<h2 id="_variables"><a class="anchor" href="#_variables"></a><a class="link" href="#_variables">3. Variables</a></h2> <h2 id="_variables"><a class="anchor" href="#_variables"></a><a class="link" href="#_variables">3. Variables</a></h2>
<div class="sectionbody"> <div class="sectionbody">
<div class="paragraph"> <div class="paragraph">
<p><em>Expr</em> supports variables like most programming languages. A variable is an identifier with an assigned value. Variables are stored in <em>contexts</em>.</p> <p><em>Expr</em>, like most programming languages, supports variables. A variable is an identifier with an assigned value. Variables are stored in <em>contexts</em>.</p>
</div> </div>
<div class="exampleblock"> <div class="exampleblock">
<div class="title">Example 8. Variable literal syntax</div> <div class="title">Example 8. Variable literal syntax</div>
@ -1393,17 +1452,26 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
<div class="sect2"> <div class="sect2">
<h3 id="_but_operator"><a class="anchor" href="#_but_operator"></a><a class="link" href="#_but_operator">4.2. <code class="blue">but</code> operator</a></h3> <h3 id="_but_operator"><a class="anchor" href="#_but_operator"></a><a class="link" href="#_but_operator">4.2. <code class="blue">but</code> operator</a></h3>
<div class="paragraph"> <div class="paragraph">
<p><code class="blue">but</code> is an infixed operator. Its operands can be expressions of any type. It evaluates the left expression first, then the right expression. The value of the right expression is the final result. Examples: <code class="blue">5 but 2</code> returns 2, <code class="blue">x=2*3 but x-1</code> returns 5.</p> <p><code class="blue">but</code> is an infixed operator. Its operands can be expressions of any type. It evaluates the left expression first, then the right expression. The value of the right expression is the final result.</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<p><code class="blue">but</code> is very similar to <code class="blue">;</code>. The only difference is that <code class="blue">;</code> can&#8217;t be used inside parenthesis <code class="blue">(</code> and <code class="blue">)</code>.</p> <div class="title">Examples</div>
<p><code class="blue">5 but 2</code><br>
<code class="green">2</code><br>
<code class="blue">x=2*3 but x-1</code><br>
<code class="green">5</code>.</p>
</div>
<div class="paragraph">
<p><code class="blue">but</code> behavior is very similar to <code class="blue">;</code>. The only difference is that <code class="blue">;</code> is not a true operator and can&#8217;t be used inside parenthesis <code class="blue">(</code> and <code class="blue">)</code>.</p>
</div> </div>
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_assignment_operator"><a class="anchor" href="#_assignment_operator"></a><a class="link" href="#_assignment_operator">4.3. Assignment operator <code class="blue">=</code></a></h3> <h3 id="_assignment_operator"><a class="anchor" href="#_assignment_operator"></a><a class="link" href="#_assignment_operator">4.3. Assignment operator <code class="blue">=</code></a></h3>
<div class="paragraph"> <div class="paragraph">
<p>The assignment operator <code class="blue">=</code> is used to define variables in the evaluation context or to change their value (see <em>ExprContext</em>). <p>The assignment operator <code class="blue">=</code> is used to define variables or to change their value in the evaluation context (see <em>ExprContext</em>).</p>
The value on the left side of <code class="blue">=</code> must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation.</p> </div>
<div class="paragraph">
<p>The value on the left side of <code class="blue">=</code> must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation.</p>
</div> </div>
<div class="paragraph"> <div class="paragraph">
<div class="title">Example</div> <div class="title">Example</div>
@ -1509,13 +1577,13 @@ These operators have a high priority, in particular higher than the operator <co
<p>The table below shows all supported operators by decreasing priorities.</p> <p>The table below shows all supported operators by decreasing priorities.</p>
</div> </div>
<table class="tableblock frame-all grid-all stretch"> <table class="tableblock frame-all grid-all stretch">
<caption class="title">Table 7. Operators priorities</caption> <caption class="title">Table 8. Operators priorities</caption>
<colgroup> <colgroup>
<col style="width: 12.5%;"> <col style="width: 11.7647%;">
<col style="width: 12.5%;"> <col style="width: 11.7647%;">
<col style="width: 12.5%;"> <col style="width: 11.7647%;">
<col style="width: 31.25%;"> <col style="width: 29.4117%;">
<col style="width: 31.25%;"> <col style="width: 35.2942%;">
</colgroup> </colgroup>
<thead> <thead>
<tr> <tr>
@ -1528,11 +1596,17 @@ These operators have a high priority, in particular higher than the operator <co
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>ITEM</strong></p></td> <td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>ITEM</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">.</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">.</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>List item</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>collection</em> <code>"."</code> <em>key</em> &#8594; <em>any</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>list</em> <code>"."</code> <em>integer</em> &#8594; <em>any</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">.</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Dict item</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>dict</em> <code>""</code> <em>any</em> &#8594; <em>any</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>INC</strong></p></td> <td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>INC</strong></p></td>
@ -1605,7 +1679,7 @@ These operators have a high priority, in particular higher than the operator <co
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>integer</em> <code>"%"</code> <em>integer</em> &#8594; <em>integer</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>integer</em> <code>"%"</code> <em>integer</em> &#8594; <em>integer</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top" rowspan="5"><p class="tableblock"><strong>SUM</strong></p></td> <td class="tableblock halign-center valign-top" rowspan="6"><p class="tableblock"><strong>SUM</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">+</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">+</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Sum</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Sum</em></p></td>
@ -1624,6 +1698,12 @@ These operators have a high priority, in particular higher than the operator <co
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>list</em> <code>"+"</code> <em>list</em> &#8594; <em>list</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>list</em> <code>"+"</code> <em>list</em> &#8594; <em>list</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">+</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Dict-join</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>dict</em> <code>"+"</code> <em>dict</em> &#8594; <em>dict</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">-</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">-</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Subtraction</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Subtraction</em></p></td>
@ -1636,7 +1716,7 @@ These operators have a high priority, in particular higher than the operator <co
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>list</em> <code>"-"</code> <em>list</em> &#8594; <em>list</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>list</em> <code>"-"</code> <em>list</em> &#8594; <em>list</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top" rowspan="6"><p class="tableblock"><strong>RELATION</strong></p></td> <td class="tableblock halign-center valign-top" rowspan="8"><p class="tableblock"><strong>RELATION</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&lt;</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&lt;</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>less</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>less</em></p></td>
@ -1673,6 +1753,18 @@ These operators have a high priority, in particular higher than the operator <co
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>comparable</em> <code>"!="</code> <em>comparable</em> &#8594; <em>boolean</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>comparable</em> <code>"!="</code> <em>comparable</em> &#8594; <em>boolean</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">in</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>member-of-list</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>"in"</code> <em>list</em> &#8594; <em>boolean</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">in</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>key-of-dict</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>"in"</code> <em>dict</em> &#8594; <em>boolean</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>NOT</strong></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><strong>NOT</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">not</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">not</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Prefix</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Prefix</em></p></td>
@ -1706,13 +1798,25 @@ These operators have a high priority, in particular higher than the operator <co
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>boolean</em> <code>"||"</code> <em>boolean</em> &#8594; <em>boolean</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>boolean</em> <code>"||"</code> <em>boolean</em> &#8594; <em>boolean</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>ASSIGN</strong></p></td> <td class="tableblock halign-center valign-top" rowspan="3"><p class="tableblock"><strong>ASSIGN</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">=</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">=</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>assignment</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>assignment</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>identifier</em> "=" <em>any</em> &#8594; <em>any</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>identifier</em> "=" <em>any</em> &#8594; <em>any</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&gt;&gt;</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>front-insert</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> "&gt;&gt;" <em>list</em> &#8594; <em>list</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&lt;&lt;</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>back-insert</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>list</em> "&lt;&lt;" <em>any</em> &#8594; <em>list</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>BUT</strong></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><strong>BUT</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">but</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">but</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
@ -1767,7 +1871,7 @@ These operators have a high priority, in particular higher than the operator <co
</div> </div>
<div id="footer"> <div id="footer">
<div id="footer-text"> <div id="footer-text">
Last updated 2024-05-17 15:47:29 +0200 Last updated 2024-05-20 06:58:09 +0200
</div> </div>
</div> </div>
</body> </body>

View File

@ -22,7 +22,7 @@ func TestExpr(t *testing.T) {
/* 3 */ {`f=openFile("test-file.txt"); line=readFile(f); closeFile(f); line`, "uno", nil}, /* 3 */ {`f=openFile("test-file.txt"); line=readFile(f); closeFile(f); line`, "uno", nil},
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil}, /* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil}, /* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
/* 10 */ {` /* 6 */ {`
ds={ ds={
"init":func(end){@end=end; @current=0 but true}, "init":func(end){@end=end; @current=0 but true},
"current":func(){current}, "current":func(){current},

View File

@ -4,17 +4,62 @@
// formatter.go // formatter.go
package expr package expr
import "fmt"
type FmtOpt uint16 type FmtOpt uint16
const ( const (
TTY FmtOpt = 1 << iota TTY FmtOpt = 1 << iota
MultiLine MultiLine
Truncate
Base2 Base2
Base8 Base8
Base10 Base10
Base16 Base16
) )
const (
TruncateEllipsis = "(...)"
MinTruncateSize = 10
TruncateSize = MinTruncateSize + 15
)
func TruncateString(s string) (trunc string) {
finalPart := len(s) - (MinTruncateSize - len(TruncateEllipsis))
trunc = s[0:len(s)-MinTruncateSize] + TruncateEllipsis + s[finalPart:]
return
}
type Formatter interface { type Formatter interface {
ToString(options FmtOpt) string ToString(options FmtOpt) string
} }
func getFormatted(v any, opt FmtOpt) (text string) {
if v == nil {
text = "(nil)"
} else if s, ok := v.(string); ok {
text = s
} else if formatter, ok := v.(Formatter); ok {
text = formatter.ToString(opt)
} else {
text = fmt.Sprintf("%v", v)
}
return
}
type Typer interface {
TypeName() string
}
func getTypeName(v any) (name string) {
if typer, ok := v.(Typer); ok {
name = typer.TypeName()
} else if IsInteger(v) {
name = "integer"
} else if IsFloat(v) {
name = "float"
} else {
name = fmt.Sprintf("%T", v)
}
return
}

View File

@ -41,6 +41,8 @@ func TestListParser(t *testing.T) {
/* 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}, /* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
/* 21 */ {`"b" in ["a", "b", "c"]`, true, nil}, /* 21 */ {`"b" in ["a", "b", "c"]`, true, nil},
/* 22 */ {`a=[1,2]; (a)<<3`, []any{1, 2, 3}, nil},
/* 23 */ {`a=[1,2]; (a)<<3; 1`, []any{1, 2}, 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},

View File

@ -4,6 +4,125 @@
// operand-dict.go // operand-dict.go
package expr package expr
import (
"fmt"
"reflect"
"strings"
)
type DictType map[any]any
func newDict(dictAny map[any]*term) (dict *DictType) {
var d DictType
if dictAny != nil {
d = make(DictType, len(dictAny))
for i, item := range dictAny {
d[i] = item
}
} else {
d = make(DictType)
}
dict = &d
return
}
func (dict *DictType) toMultiLine(sb *strings.Builder, indent int) {
sb.WriteString(strings.Repeat("\t", indent))
sb.WriteString("{\n")
first := true
for name, value := range *dict {
if first {
first = false
} else {
sb.WriteByte(',')
sb.WriteByte('\n')
}
sb.WriteString(strings.Repeat("\t", indent+1))
if key, ok := name.(string); ok {
sb.WriteString(string('"') + key + string('"'))
} else {
sb.WriteString(fmt.Sprintf("%v", name))
}
sb.WriteString(": ")
if f, ok := value.(Formatter); ok {
sb.WriteString(f.ToString(MultiLine))
} else if _, ok = value.(Functor); ok {
sb.WriteString("func(){}")
} else {
sb.WriteString(fmt.Sprintf("%v", value))
}
}
sb.WriteString(strings.Repeat("\t", indent))
sb.WriteString("\n}")
}
func (dict *DictType) ToString(opt FmtOpt) string {
var sb strings.Builder
if opt&MultiLine != 0 {
dict.toMultiLine(&sb, 0)
} else {
sb.WriteByte('{')
first := true
for key, value := range *dict {
if first {
first = false
} else {
sb.WriteString(", ")
}
if s, ok := key.(string); ok {
sb.WriteString(string('"') + s + string('"'))
} else {
sb.WriteString(fmt.Sprintf("%v", key))
}
sb.WriteString(": ")
if formatter, ok := value.(Formatter); ok {
sb.WriteString(formatter.ToString(opt))
} else if t, ok := value.(*term); ok {
sb.WriteString(t.String())
} else {
sb.WriteString(fmt.Sprintf("%#v", value))
}
}
sb.WriteByte('}')
}
return sb.String()
}
func (dict *DictType) String() string {
return dict.ToString(0)
}
func (dict *DictType) TypeName() string {
return "dict"
}
func (dict *DictType) hasKey(target any) (ok bool) {
for key := range *dict {
if ok = reflect.DeepEqual(key, target); ok {
break
}
}
return
}
func (dict *DictType) clone() (c *DictType) {
c = newDict(nil)
for k, v := range *dict {
(*c)[k] = v
}
return
}
func (dict *DictType) merge(second *DictType) {
if second != nil {
for k, v := range *second {
(*dict)[k] = v
}
}
}
// -------- dict term // -------- dict term
func newDictTerm(args map[any]*term) *term { func newDictTerm(args map[any]*term) *term {
return &term{ return &term{
@ -19,7 +138,7 @@ func newDictTerm(args map[any]*term) *term {
// -------- dict func // -------- dict func
func evalDict(ctx ExprContext, self *term) (v any, err error) { func evalDict(ctx ExprContext, self *term) (v any, err error) {
dict, _ := self.value().(map[any]*term) dict, _ := self.value().(map[any]*term)
items := make(map[any]any, len(dict)) items := make(DictType, len(dict))
for key, tree := range dict { for key, tree := range dict {
var param any var param any
if param, err = tree.compute(ctx); err != nil { if param, err = tree.compute(ctx); err != nil {
@ -28,7 +147,7 @@ func evalDict(ctx ExprContext, self *term) (v any, err error) {
items[key] = param items[key] = param
} }
if err == nil { if err == nil {
v = items v = &items
} }
return return
} }

View File

@ -7,7 +7,8 @@ package expr
import "fmt" import "fmt"
// -------- expr term // -------- expr term
func newExprTerm(tk *Token) *term { func newExprTerm(root *term) *term {
tk := NewValueToken(root.tk.row, root.tk.col, SymExpression, root.source(), root)
return &term{ return &term{
tk: *tk, tk: *tk,
parent: nil, parent: nil,
@ -20,8 +21,8 @@ func newExprTerm(tk *Token) *term {
// -------- eval expr // -------- eval expr
func evalExpr(ctx ExprContext, self *term) (v any, err error) { func evalExpr(ctx ExprContext, self *term) (v any, err error) {
if expr, ok := self.value().(Expr); ok { if expr, ok := self.value().(*term); ok {
v, err = expr.eval(ctx, false) v, err = expr.compute(ctx)
} else { } else {
err = fmt.Errorf("expression expected, got %T", self.value()) err = fmt.Errorf("expression expected, got %T", self.value())
} }

View File

@ -87,10 +87,10 @@ func (functor *funcDefFunctor) Invoke(ctx ExprContext, name string, args []any)
if functor, ok := arg.(Functor); ok { if functor, ok := arg.(Functor); ok {
ctx.RegisterFunc(p, functor, 0, -1) ctx.RegisterFunc(p, functor, 0, -1)
} else { } else {
ctx.setVar(p, arg) ctx.UnsafeSetVar(p, arg)
} }
} else { } else {
ctx.setVar(p, nil) ctx.UnsafeSetVar(p, nil)
} }
} }
result, err = functor.expr.eval(ctx, false) result, err = functor.expr.eval(ctx, false)

View File

@ -66,12 +66,13 @@ func evalFirstChild(ctx ExprContext, self *term) (value any, err error) {
} }
func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map[string]Functor, err error) { func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map[string]Functor, err error) {
if dictAny, ok := firstChildValue.(map[any]any); ok { // if dictAny, ok := firstChildValue.(map[any]any); ok {
if dictAny, ok := firstChildValue.(*DictType); ok {
requiredFields := []string{currentName, nextName} requiredFields := []string{currentName, nextName}
fieldsMask := 0b11 fieldsMask := 0b11
foundFields := 0 foundFields := 0
ds = make(map[string]Functor) ds = make(map[string]Functor)
for keyAny, item := range dictAny { for keyAny, item := range *dictAny {
if key, ok := keyAny.(string); ok { if key, ok := keyAny.(string); ok {
if functor, ok := item.(*funcDefFunctor); ok { if functor, ok := item.(*funcDefFunctor); ok {
ds[key] = functor ds[key] = functor

View File

@ -12,7 +12,22 @@ import (
type ListType []any type ListType []any
func (ls *ListType) ToString(opt FmtOpt) string { func newListA(listAny ...any) (list *ListType) {
return newList(listAny)
}
func newList(listAny []any) (list *ListType) {
if listAny != nil {
ls := make(ListType, len(listAny))
for i, item := range listAny {
ls[i] = item
}
list = &ls
}
return
}
func (ls *ListType) ToString(opt FmtOpt) (s string) {
var sb strings.Builder var sb strings.Builder
sb.WriteByte('[') sb.WriteByte('[')
if len(*ls) > 0 { if len(*ls) > 0 {
@ -40,26 +55,19 @@ func (ls *ListType) ToString(opt FmtOpt) string {
} }
} }
sb.WriteByte(']') sb.WriteByte(']')
return sb.String() s = sb.String()
if opt&Truncate != 0 && len(s) > TruncateSize {
s = TruncateString(s)
}
return
} }
func (ls *ListType) String() string { func (ls *ListType) String() string {
return ls.ToString(0) return ls.ToString(0)
} }
func newListA(listAny ...any) (list *ListType) { func (ls *ListType) TypeName() string {
return newList(listAny) return "list"
}
func newList(listAny []any) (list *ListType) {
if listAny != nil {
ls := make(ListType, len(listAny))
for i, item := range listAny {
ls[i] = item
}
list = &ls
}
return
} }
func (list *ListType) indexDeepCmp(target any) (index int) { func (list *ListType) indexDeepCmp(target any) (index int) {

View File

@ -8,7 +8,7 @@ import "fmt"
// -------- variable term // -------- variable term
func newVarTerm(tk *Token) *term { func newVarTerm(tk *Token) *term {
return &term{ t := &term{
tk: *tk, tk: *tk,
// class: classVar, // class: classVar,
// kind: kindUnknown, // kind: kindUnknown,
@ -18,6 +18,8 @@ func newVarTerm(tk *Token) *term {
priority: priValue, priority: priValue,
evalFunc: evalVar, evalFunc: evalVar,
} }
t.tk.Sym = SymVariable
return t
} }
// -------- eval func // -------- eval func

View File

@ -22,7 +22,7 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
} }
leftTerm := self.children[0] leftTerm := self.children[0]
if leftTerm.tk.Sym != SymIdentifier { if leftTerm.tk.Sym != SymVariable {
err = leftTerm.tk.Errorf("left operand of %q must be a variable", self.tk.source) err = leftTerm.tk.Errorf("left operand of %q must be a variable", self.tk.source)
return return
} }
@ -44,7 +44,7 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
} }
ctx.RegisterFunc(leftTerm.source(), functor, minArgs, maxArgs) ctx.RegisterFunc(leftTerm.source(), functor, minArgs, maxArgs)
} else { } else {
ctx.setVar(leftTerm.source(), v) ctx.UnsafeSetVar(leftTerm.source(), v)
} }
} }
return return

View File

@ -8,9 +8,7 @@ package expr
func newNullCoalesceTerm(tk *Token) (inst *term) { func newNullCoalesceTerm(tk *Token) (inst *term) {
return &term{ return &term{
tk: *tk, tk: *tk,
// class: classOperator,
// kind: kindUnknown,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priCoalesce, priority: priCoalesce,
@ -26,7 +24,7 @@ func evalNullCoalesce(ctx ExprContext, self *term) (v any, err error) {
} }
leftTerm := self.children[0] leftTerm := self.children[0]
if leftTerm.tk.Sym != SymIdentifier { if leftTerm.tk.Sym != SymVariable {
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source) err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
return return
} }
@ -34,11 +32,7 @@ func evalNullCoalesce(ctx ExprContext, self *term) (v any, err error) {
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists { if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue v = leftValue
} else if rightValue, err = self.children[1].compute(ctx); err == nil { } else if rightValue, err = self.children[1].compute(ctx); err == nil {
// if _, ok := rightValue.(Functor); ok {
// err = errCoalesceNoFunc(self.children[1])
// } else {
v = rightValue v = rightValue
// }
} }
return return
} }
@ -63,7 +57,7 @@ func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
} }
leftTerm := self.children[0] leftTerm := self.children[0]
if leftTerm.tk.Sym != SymIdentifier { if leftTerm.tk.Sym != SymVariable {
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source) err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
return return
} }
@ -75,17 +69,12 @@ func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
ctx.RegisterFunc(leftTerm.source(), functor, 0, -1) ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
} else { } else {
v = rightValue v = rightValue
ctx.setVar(leftTerm.source(), rightValue) ctx.UnsafeSetVar(leftTerm.source(), rightValue)
} }
} }
return return
} }
// utils
// func errCoalesceNoFunc(t *term) error {
// return t.Errorf("the right operand of a coalescing operation cannot be a function definition")
// }
// init // init
func init() { func init() {
registerTermConstructor(SymDoubleQuestion, newNullCoalesceTerm) registerTermConstructor(SymDoubleQuestion, newNullCoalesceTerm)

View File

@ -59,28 +59,16 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil { if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil {
v = string(unboxedValue[index]) v = string(unboxedValue[index])
} }
case map[any]any: case *DictType:
var ok bool var ok bool
var indexValue any var indexValue any
if indexValue, err = indexTerm.compute(ctx); err == nil { if indexValue, err = indexTerm.compute(ctx); err == nil {
if v, ok = unboxedValue[indexValue]; !ok { if v, ok = (*unboxedValue)[indexValue]; !ok {
err = fmt.Errorf("key %v does not belong to the dictionary", rightValue) err = fmt.Errorf("key %v does not belong to the dictionary", rightValue)
} }
} }
// case *dataCursor:
// if indexTerm.symbol() == SymIdentifier {
// opName := indexTerm.source()
// if opName == resetName {
// _, err = unboxedValue.Reset()
// } else if opName == cleanName {
// _, err = unboxedValue.Clean()
// } else {
// err = indexTerm.Errorf("iterators do not support command %q", opName)
// }
// v = err == nil
// }
case ExtIterator: case ExtIterator:
if indexTerm.symbol() == SymIdentifier { if indexTerm.symbol() == SymVariable {
opName := indexTerm.source() opName := indexTerm.source()
if unboxedValue.HasOperation(opName) { if unboxedValue.HasOperation(opName) {
v, err = unboxedValue.CallOperation(opName, []any{}) v, err = unboxedValue.CallOperation(opName, []any{})

View File

@ -204,6 +204,10 @@ func (f *fraction) ToString(opt FmtOpt) string {
return sb.String() return sb.String()
} }
func (f *fraction) TypeName() string {
return "fraction"
}
// -------- fraction term // -------- fraction term
func newFractionTerm(tk *Token) *term { func newFractionTerm(tk *Token) *term {
return &term{ return &term{

View File

@ -16,10 +16,10 @@ func newInTerm(tk *Token) (inst *term) {
} }
} }
func hasKey(d map[any]any, target any) (ok bool) { // func hasKey(d map[any]any, target any) (ok bool) {
_, ok = d[target] // _, ok = d[target]
return // return
} // }
func evalIn(ctx ExprContext, self *term) (v any, err error) { func evalIn(ctx ExprContext, self *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
@ -32,8 +32,8 @@ func evalIn(ctx ExprContext, self *term) (v any, err error) {
list, _ := rightValue.(*ListType) list, _ := rightValue.(*ListType)
v = list.indexDeepCmp(leftValue) >= 0 v = list.indexDeepCmp(leftValue) >= 0
} else if IsDict(rightValue) { } else if IsDict(rightValue) {
d, _ := rightValue.(map[any]any) dict, _ := rightValue.(*DictType)
v = hasKey(d, leftValue) v = dict.hasKey(leftValue)
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.errIncompatibleTypes(leftValue, rightValue)
} }

View File

@ -37,6 +37,9 @@ func evalInsert(ctx ExprContext, self *term) (v any, err error) {
list, _ := rightValue.(*ListType) list, _ := rightValue.(*ListType)
newList := append(ListType{leftValue}, *list...) newList := append(ListType{leftValue}, *list...)
v = &newList v = &newList
if self.children[1].symbol() == SymVariable {
ctx.UnsafeSetVar(self.children[1].source(), v)
}
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.errIncompatibleTypes(leftValue, rightValue)
} }
@ -54,12 +57,33 @@ func evalAppend(ctx ExprContext, self *term) (v any, err error) {
list, _ := leftValue.(*ListType) list, _ := leftValue.(*ListType)
newList := append(*list, rightValue) newList := append(*list, rightValue)
v = &newList v = &newList
if self.children[0].symbol() == SymVariable {
ctx.UnsafeSetVar(self.children[0].source(), v)
}
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
// func evalAssignAppend(ctx ExprContext, self *term) (v any, err error) {
// var leftValue, rightValue any
// if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
// return
// }
// if IsList(leftValue) {
// list, _ := leftValue.(*ListType)
// newList := append(*list, rightValue)
// v = &newList
// if
// } else {
// err = self.errIncompatibleTypes(leftValue, rightValue)
// }
// return
// }
// init // init
func init() { func init() {
registerTermConstructor(SymInsert, newInsertTerm) registerTermConstructor(SymInsert, newInsertTerm)

View File

@ -30,8 +30,9 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) {
s, _ := childValue.(string) s, _ := childValue.(string)
v = int64(len(s)) v = int64(len(s))
} else if IsDict(childValue) { } else if IsDict(childValue) {
m, _ := childValue.(map[any]any) // m, _ := childValue.(map[any]any)
v = int64(len(m)) m, _ := childValue.(*DictType)
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)

View File

@ -25,7 +25,7 @@ func evalPostInc(ctx ExprContext, self *term) (v any, err error) {
if it, ok := childValue.(Iterator); ok { if it, ok := childValue.(Iterator); ok {
v, err = it.Next() v, err = it.Next()
} else if IsInteger(childValue) && self.children[0].symbol() == SymIdentifier { } else if IsInteger(childValue) && self.children[0].symbol() == SymVariable {
v = childValue v = childValue
i, _ := childValue.(int64) i, _ := childValue.(int64)
ctx.SetVar(self.children[0].source(), i+1) ctx.SetVar(self.children[0].source(), i+1)

View File

@ -38,15 +38,28 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
v = leftInt + rightInt v = leftInt + rightInt
} }
} else if IsList(leftValue) || IsList(rightValue) { // } else if IsList(leftValue) || IsList(rightValue) {
// var leftList, rightList *ListType
// var ok bool
// if leftList, ok = leftValue.(*ListType); !ok {
// leftList = &ListType{leftValue}
// }
// if rightList, ok = rightValue.(*ListType); !ok {
// rightList = &ListType{rightValue}
// }
// sumList := make(ListType, 0, len(*leftList)+len(*rightList))
// for _, item := range *leftList {
// sumList = append(sumList, item)
// }
// for _, item := range *rightList {
// sumList = append(sumList, item)
// }
// v = &sumList
} else if IsList(leftValue) && IsList(rightValue) {
var leftList, rightList *ListType var leftList, rightList *ListType
var ok bool leftList, _ = leftValue.(*ListType)
if leftList, ok = leftValue.(*ListType); !ok { rightList, _ = rightValue.(*ListType)
leftList = &ListType{leftValue}
}
if rightList, ok = rightValue.(*ListType); !ok {
rightList = &ListType{rightValue}
}
sumList := make(ListType, 0, len(*leftList)+len(*rightList)) sumList := make(ListType, 0, len(*leftList)+len(*rightList))
for _, item := range *leftList { for _, item := range *leftList {
sumList = append(sumList, item) sumList = append(sumList, item)
@ -55,19 +68,17 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
sumList = append(sumList, item) sumList = append(sumList, item)
} }
v = &sumList v = &sumList
} else if isFraction(leftValue) || isFraction(rightValue) { } else if (isFraction(leftValue) && IsNumber(rightValue)) || (isFraction(rightValue) && IsNumber(leftValue)) {
if IsFloat(leftValue) || IsFloat(rightValue) { if IsFloat(leftValue) || IsFloat(rightValue) {
v = numAsFloat(leftValue) + numAsFloat(rightValue) v = numAsFloat(leftValue) + numAsFloat(rightValue)
} else { } else {
v, err = sumAnyFract(leftValue, rightValue) v, err = sumAnyFract(leftValue, rightValue)
} }
} else if IsDict(leftValue) && IsDict(rightValue) { } else if IsDict(leftValue) && IsDict(rightValue) {
leftDict, _ := leftValue.(map[any]any) leftDict, _ := leftValue.(*DictType)
rightDict, _ := rightValue.(map[any]any) rightDict, _ := rightValue.(*DictType)
c := CloneMap(leftDict) c := leftDict.clone()
for key, value := range rightDict { c.merge(rightDict)
c[key] = value
}
v = c v = c
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.errIncompatibleTypes(leftValue, rightValue)

View File

@ -277,6 +277,7 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree
err = scanner.Previous().ErrorExpectedGot("}") err = scanner.Previous().ErrorExpectedGot("}")
} else { } else {
subtree = newDictTerm(args) subtree = newDictTerm(args)
// subtree = newMapTerm(args)
} }
} }
return return
@ -389,7 +390,7 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
var subTree *ast var subTree *ast
if subTree, err = self.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil { if subTree, err = self.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil {
subTree.root.priority = priValue subTree.root.priority = priValue
err = tree.addTerm(subTree.root) err = tree.addTerm(newExprTerm(subTree.root))
currentTerm = subTree.root currentTerm = subTree.root
} }
case SymFuncCall: case SymFuncCall:

View File

@ -98,7 +98,7 @@ func TestGeneralParser(t *testing.T) {
/* 77 */ {`5 % 2`, int64(1), nil}, /* 77 */ {`5 % 2`, int64(1), nil},
/* 78 */ {`5 % (-2)`, int64(1), nil}, /* 78 */ {`5 % (-2)`, int64(1), nil},
/* 79 */ {`-5 % 2`, int64(-1), nil}, /* 79 */ {`-5 % 2`, int64(-1), nil},
/* 80 */ {`5 % 2.0`, nil, errors.New(`[1:4] left operand '5' [int64] and right operand '2' [float64] are not compatible with operator "%"`)}, /* 80 */ {`5 % 2.0`, nil, errors.New(`[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`)},
/* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil}, /* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil}, /* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
/* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil}, /* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
@ -137,7 +137,7 @@ func TestGeneralParser(t *testing.T) {
/* 116 */ {`null`, nil, errors.New(`undefined variable or function "null"`)}, /* 116 */ {`null`, nil, errors.New(`undefined variable or function "null"`)},
/* 117 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)}, /* 117 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)},
/* 118 */ {`{"key":}`, nil, errors.New(`[1:9] expected "dictionary-value", got "}"`)}, /* 118 */ {`{"key":}`, nil, errors.New(`[1:9] expected "dictionary-value", got "}"`)},
/* 119 */ {`{}`, map[any]any{}, nil}, /* 119 */ {`{}`, &DictType{}, nil},
/* 120 */ {`1|2`, newFraction(1, 2), nil}, /* 120 */ {`1|2`, newFraction(1, 2), nil},
/* 121 */ {`1|2 + 1`, newFraction(3, 2), nil}, /* 121 */ {`1|2 + 1`, newFraction(3, 2), nil},
/* 122 */ {`1|2 - 1`, newFraction(-1, 2), nil}, /* 122 */ {`1|2 - 1`, newFraction(-1, 2), nil},

View File

@ -36,7 +36,7 @@ func (ctx *SimpleVarStore) GetVar(varName string) (v any, exists bool) {
return return
} }
func (ctx *SimpleVarStore) setVar(varName string, value any) { func (ctx *SimpleVarStore) UnsafeSetVar(varName string, value any) {
// fmt.Printf("[%p] setVar(%v, %v)\n", ctx, varName, value) // fmt.Printf("[%p] setVar(%v, %v)\n", ctx, varName, value)
ctx.varStore[varName] = value ctx.varStore[varName] = value
} }
@ -98,8 +98,8 @@ func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
sb.WriteString(f.ToString(0)) sb.WriteString(f.ToString(0))
} else if _, ok = value.(Functor); ok { } else if _, ok = value.(Functor); ok {
sb.WriteString("func(){}") sb.WriteString("func(){}")
} else if _, ok = value.(map[any]any); ok { // } else if _, ok = value.(map[any]any); ok {
sb.WriteString("dict{}") // sb.WriteString("dict{}")
} else { } else {
sb.WriteString(fmt.Sprintf("%v", value)) sb.WriteString(fmt.Sprintf("%v", value))
} }

View File

@ -72,6 +72,7 @@ const (
SymIdentifier SymIdentifier
SymBool SymBool
SymInteger SymInteger
SymVariable
SymFloat SymFloat
SymFraction SymFraction
SymString SymString

10
term.go
View File

@ -161,10 +161,14 @@ func (self *term) toInt(computedValue any, valueDescription string) (i int, err
} }
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error { func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
leftType := getTypeName(leftValue)
leftText := getFormatted(leftValue, Truncate)
rightType := getTypeName(rightValue)
rightText := getFormatted(rightValue, Truncate)
return self.tk.Errorf( return self.tk.Errorf(
"left operand '%v' [%T] and right operand '%v' [%T] are not compatible with operator %q", "left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q",
leftValue, leftValue, leftText, leftType,
rightValue, rightValue, rightText, rightType,
self.source()) self.source())
} }

View File

@ -35,7 +35,7 @@ func IsList(v any) (ok bool) {
} }
func IsDict(v any) (ok bool) { func IsDict(v any) (ok bool) {
_, ok = v.(map[any]any) _, ok = v.(*DictType)
return ok return ok
} }
@ -147,6 +147,12 @@ func fromGenericAny(v any) (exprAny any, ok bool) {
if exprAny, ok = anyFloat(v); ok { if exprAny, ok = anyFloat(v); ok {
return return
} }
if exprAny, ok = v.(*DictType); ok {
return
}
if exprAny, ok = v.(*ListType); ok {
return
}
return return
} }