Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0e55f83d56 | |||
| 4725145d1c | |||
| edf8818f51 | |||
| 6211be8a8f | |||
| f50ddf48db | |||
| 76e01f12d2 | |||
| 406bced450 | |||
| 409dc86a92 | |||
| 4184221428 | |||
| 8cf8b36a26 | |||
| de87050188 | |||
| a1ec0cc611 | |||
| 8e5550bfa7 | |||
| 6ee21e10af | |||
| 5c44532790 | |||
| cb66c1ab19 | |||
| a28d24ba68 | |||
| 523349a204 | |||
| b185f1df3a | |||
| 5da5a61a42 | |||
| 6e9205abc4 | |||
| f61004fb5d | |||
| 321030c8d3 | |||
| 98fc89e84f | |||
| 778d00677d | |||
| ba3dbb7f02 | |||
| 7285109115 |
+1
-1
@@ -44,7 +44,7 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
|
||||
var expr *ast
|
||||
scanner := NewScanner(file, DefaultTranslations())
|
||||
parser := NewParser()
|
||||
if expr, err = parser.parseGeneral(scanner, true, true); err == nil {
|
||||
if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymEos); err == nil {
|
||||
result, err = expr.Eval(ctx)
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
+237
-68
@@ -58,7 +58,7 @@ The expression context is analogous to the stack-frame of other programming lang
|
||||
|
||||
Function contexts are created by cloning the calling context. More details on this topic are given later in this document.
|
||||
|
||||
_Expr_ creates and keeps a inner _global context_ where it stores imported functions, either from builtin or plugin modules. To perform calculations, the calling program must provide its own context; this is the _main context_. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The created context can be called _function context_.
|
||||
_Expr_ creates and keeps a inner _global context_ where it stores imported functions, either from builtin or plugin modules. To perform calculations, the user program must provide its own context; this is the _main context_. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The created context can be called _function context_.
|
||||
|
||||
Imported functions are registerd in the _global context_. When an expression first calls an imported function, that function is linked to the current context; this can be the _main context_ or a _function context_.
|
||||
|
||||
@@ -79,20 +79,20 @@ Here are some examples of execution.
|
||||
# Type 'exit' or Ctrl+D to quit the program.
|
||||
|
||||
[user]$ ./dev-expr
|
||||
dev-expr -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoroso@portale-stac.it)
|
||||
Based on the Expr package v0.19.0
|
||||
dev-expr -- Expressions calculator v1.12.0(build 1),2024/09/14 (celestino.amoroso@portale-stac.it)
|
||||
Based on the Expr package v0.26.0
|
||||
Type help to get the list of available commands
|
||||
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||
>>> help
|
||||
--- REPL commands:
|
||||
source -- Load a file as input
|
||||
tty -- Enable/Disable ansi output <1>
|
||||
base -- Set the integer output base: 2, 8, 10, or 16
|
||||
exit -- Exit the program
|
||||
help -- Show command list
|
||||
ml -- Enable/Disable multi-line output
|
||||
mods -- List builtin modules
|
||||
output -- Enable/Disable printing expression results. Options 'on', 'off', 'status'
|
||||
source -- Load a file as input
|
||||
tty -- Enable/Disable ansi output <1>
|
||||
|
||||
--- Command line options:
|
||||
-b <builtin> Import builtin modules.
|
||||
@@ -127,22 +127,22 @@ dev-expr -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoro
|
||||
9.5
|
||||
>>> 0xFD + 0b1 + 0o1 <1>
|
||||
255
|
||||
>>> 1|2 + 2|3 <2>
|
||||
7|6
|
||||
>>> 1:2 + 2:3 <2>
|
||||
7:6
|
||||
>>> ml <3>
|
||||
>>> 1|2 + 2|3
|
||||
>>> 1:2 + 2:3
|
||||
7
|
||||
-
|
||||
6
|
||||
>>> 4+2 but 5|2+0.5 <4>
|
||||
>>> 4+2 but 5:2+0.5 <4>
|
||||
3
|
||||
>>> 4+2; 5|2+0.5 <5>
|
||||
>>> 4+2; 5:2+0.5 <5>
|
||||
3
|
||||
>>>
|
||||
----
|
||||
|
||||
<1> Number bases: 0x = _hexadecimal_, 0o = _octal_, 0b = _binary_.
|
||||
<2> Fractions: _numerator_ | _denominator_.
|
||||
<2> Fractions: _numerator_ : _denominator_.
|
||||
<3> Activate multi-line output of fractions.
|
||||
<4> But operator, see <<_but_operator>>.
|
||||
<5> Multi-expression: the same result of the previous single expression but this it is obtained with two separated calculations.
|
||||
@@ -155,9 +155,9 @@ _Expr_ supports three type of numbers:
|
||||
|
||||
. [blue]#Integers#
|
||||
. [blue]#Floats#
|
||||
. [blue]#Factions#
|
||||
. [blue]#Fractions#
|
||||
|
||||
In mixed operations involving integers, fractions and floats, automatic type promotion to the largest type take place.
|
||||
In mixed operations involving integers, fractions and floats, automatic type promotion to the largest type is performed.
|
||||
|
||||
==== Integers
|
||||
__Expr__'s integers are a subset of the integer set. Internally they are stored as Golang _int64_ values.
|
||||
@@ -183,11 +183,11 @@ Value range: *-9223372036854775808* to *9223372036854775807*
|
||||
[cols="^1,^2,6,4"]
|
||||
|===
|
||||
| Symbol | Operation | Description | Examples
|
||||
| [blue]`+` | _sum_ | Add two values | [blue]`-1 + 2` -> 1
|
||||
| [blue]`-` | _subtraction_ | Subtract the right value from the left one | [blue]`3 - 1` -> 2
|
||||
| [blue]`*` | _product_ | Multiply two values | [blue]`-1 * 2` -> -2
|
||||
| [blue]`/` | _Division_ | Divide the left value by the right one^(*)^ | [blue]`-10 / 2` -> 5
|
||||
| [blue]`%` | _Modulo_ | Remainder of the integer division | [blue]`5 % 2` -> 1
|
||||
| [blue]`+` | _Sum_ | Add two values | [blue]`-1 + 2` -> _1_
|
||||
| [blue]`-` | _Subtraction_ | Subtract the right value from the left one | [blue]`3 - 1` -> _2_
|
||||
| [blue]`*` | _Product_ | Multiply two values | [blue]`-1 * 2` -> _-2_
|
||||
| [blue]`/` | _Integer division_ | Divide the left value by the right one^(*)^ | [blue]`-11 / 2` -> _-5_
|
||||
| [blue]`%` | _Modulo_ | Remainder of the integer division | [blue]`5 % 2` -> _1_
|
||||
|===
|
||||
|
||||
^(*)^ See also the _float division_ [blue]`./` below.
|
||||
@@ -228,19 +228,19 @@ _dec-seq_ = _see-integer-literal-syntax_
|
||||
[cols="^1,^2,6,4"]
|
||||
|===
|
||||
| Symbol | Operation | Description | Examples
|
||||
| [blue]`+` | _sum_ | Add two values | [blue]`4 + 0.5` -> 4.5
|
||||
| [blue]`-` | _subtraction_ | Subtract the right value from the left one | [blue]`4 - 0.5` -> 3.5
|
||||
| [blue]`*` | _product_ | Multiply two values | [blue]`4 * 0.5` -> 2.0
|
||||
| [blue]`/` | _Division_ | Divide the left value by the right one | [blue]`1.0 / 2` -> 0.5
|
||||
| [blue]`./`| _Float division_ | Force float division | [blue]`-1 ./ 2` -> -0.5
|
||||
| [blue]`+` | _Sum_ | Add two values | [blue]`4 + 0.5` -> 4.5
|
||||
| [blue]`-` | _Subtraction_ | Subtract the right value from the left one | [blue]`4 - 0.5` -> 3.5
|
||||
| [blue]`*` | _Product_ | Multiply two values | [blue]`4 * 0.5` -> 2.0
|
||||
| [blue]`/` | _Float division_ | Divide the left value by the right one | [blue]`1.0 / 2` -> 0.5
|
||||
| [blue]`./`| _Forced float division_ | Force float division | [blue]`-1 ./ 2` -> -0.5
|
||||
|===
|
||||
|
||||
==== Fractions
|
||||
_Expr_ also supports fractions. Fraction literals are made with two integers separated by a vertical bar `|`.
|
||||
_Expr_ also supports fractions. Fraction literals are made with two integers separated by a colon character `:`.
|
||||
|
||||
.Fraction literal syntax
|
||||
====
|
||||
*_fraction_* = [__sign__] (_num-den-spec_ | _float-spec_) +
|
||||
*_fraction_* = [__sign__] (_num-den-spec_ "**:**" _float-spec_) +
|
||||
_sign_ = "**+**" | "**-**" +
|
||||
_num-den-spec_ = _digit-seq_ "**|**" _digit-seq_ +
|
||||
_float-spec_ = _dec-seq_ "**.**" [_dec-seq_] "**(**" _dec-seq_ "**)**" +
|
||||
@@ -249,44 +249,44 @@ _digit-seq_ = _see-integer-literal-syntax_
|
||||
====
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`1 | 2` +
|
||||
[green]`1|2`
|
||||
`>>>` [blue]`1 : 2` +
|
||||
[green]`1:2`
|
||||
|
||||
`>>>` [blue]`4|6` [gray]_// Fractions are always reduced to their lowest terms_ +
|
||||
[green]`2|3`
|
||||
`>>>` [blue]`4:6` [gray]_// Fractions are always reduced to their lowest terms_ +
|
||||
[green]`2:3`
|
||||
|
||||
`>>>` [blue]`1|2 + 2|3` +
|
||||
[green]`7|6`
|
||||
`>>>` [blue]`1:2 + 2:3` +
|
||||
[green]`7:6`
|
||||
|
||||
`>>>` [blue]`1|2 * 2|3` +
|
||||
[green]`1|3`
|
||||
`>>>` [blue]`1:2 * 2:3` +
|
||||
[green]`1:3`
|
||||
|
||||
`>>>` [blue]`1|2 / 1|3` +
|
||||
[green]`3|2`
|
||||
`>>>` [blue]`1:2 / 1:3` +
|
||||
[green]`3:2`
|
||||
|
||||
`>>>` [blue]`1|2 ./ 1|3` [gray]_// Force decimal division_ +
|
||||
`>>>` [blue]`1:2 ./ 1:3` [gray]_// Force decimal division_ +
|
||||
[green]`1.5`
|
||||
|
||||
`>>>` [blue]`-1|2` +
|
||||
[green]`-1|2`
|
||||
`>>>` [blue]`-1:2` +
|
||||
[green]`-1:2`
|
||||
|
||||
`>>>` [blue]`1|-2` [gray]_// Invalid sign specification_ +
|
||||
[red]_Eval Error: [1:3] infix operator "|" requires two non-nil operands, got 1_
|
||||
`>>>` [blue]`1:-2` [gray]_// Invalid sign specification_ +
|
||||
[red]_Eval Error: [1:3] infix operator ":" requires two non-nil operands, got 1_
|
||||
|
||||
`>>>` [blue]`1|(-2)` +
|
||||
[green]`-1|2`
|
||||
`>>>` [blue]`1:(-2)` +
|
||||
[green]`-1:2`
|
||||
|
||||
|
||||
Fractions can be used together with integers and floats in expressions.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`1|2 + 5` +
|
||||
[green]`11|2`
|
||||
`>>>` [blue]`1:2 + 5` +
|
||||
[green]`11:2`
|
||||
|
||||
`>>>` [blue]`4 - 1|2` +
|
||||
[green]`7|2`
|
||||
`>>>` [blue]`4 - 1:2` +
|
||||
[green]`7:2`
|
||||
|
||||
`>>>` [blue]`1.0 + 1|2` +
|
||||
`>>>` [blue]`1.0 + 1:2` +
|
||||
[green]`1.5`
|
||||
|
||||
|
||||
@@ -308,7 +308,7 @@ Strings are character sequences enclosed between two double quote [blue]`"`.
|
||||
`>>>` [blue]`"123\tabc"` +
|
||||
[green]`123{nbsp}{nbsp}{nbsp}{nbsp}abc`
|
||||
|
||||
Some arithmetic operators can also be used with strings.
|
||||
Some arithmetic operators also apply to strings.
|
||||
|
||||
.String operators
|
||||
[cols="^1,^2,6,4"]
|
||||
@@ -444,6 +444,7 @@ _non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value_} "**]**" +
|
||||
| [blue]`[]` | _Item at index_ | 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_
|
||||
| [blue]`#` | _Size_ | Number of items in a list | [blue]`#[1,2,3]` -> _3_
|
||||
|===
|
||||
|
||||
Array's items can be accessed using the index `[]` operator.
|
||||
@@ -458,10 +459,16 @@ Array's items can be accessed using the index `[]` operator.
|
||||
*_slice_* = _string-expr_ "**[**" _integer-expr_ "**:**" _integer-expr_ "**]**"
|
||||
====
|
||||
|
||||
.Items of list
|
||||
.Examples: Getting items from lists
|
||||
`>>>` [blue]`[1,2,3][1]` +
|
||||
[green]`2`
|
||||
|
||||
`>>>` [blue]`index=2; ["a", "b", "c", "d"][index]` +
|
||||
[green]`c`
|
||||
|
||||
`>>>` [blue]`["a", "b", "c", "d"][2:]` +
|
||||
[green]`["c", "d"]`
|
||||
|
||||
`>>>` [blue]`list=[1,2,3]; list[1]` +
|
||||
[green]`2`
|
||||
|
||||
@@ -471,25 +478,44 @@ Array's items can be accessed using the index `[]` operator.
|
||||
`>>>` [blue]`list=["one","two","three"]; list[2-1]` +
|
||||
[green]`two`
|
||||
|
||||
`>>>` [blue]`list[1]="six"; list` +
|
||||
[green]`["one", "six", "three"]`
|
||||
|
||||
`>>>` [blue]`list[-1]` +
|
||||
[green]`three`
|
||||
|
||||
`>>>` [blue]`list[10]` +
|
||||
[red]`Eval Error: [1:9] index 10 out of bounds`
|
||||
|
||||
.Example: Number of elements in a list
|
||||
`>>>` [blue]`#list` +
|
||||
[green]`3`
|
||||
|
||||
`>>>` [blue]`index=2; ["a", "b", "c", "d"][index]` +
|
||||
[green]`c`
|
||||
.Examples: Element insertion
|
||||
`>>>` [blue]`"first" >> list` +
|
||||
[green]`["first", "one", "six", "three"]`
|
||||
|
||||
`>>>` [blue]`list << "last"` +
|
||||
[green]`["first", "one", "six", "three", "last"]`
|
||||
|
||||
.Examples: Element in list
|
||||
`>>>` [blue]`"six" in list` +
|
||||
[green]`true`
|
||||
|
||||
`>>>` [blue]`"ten" in list` +
|
||||
[green]`false`
|
||||
|
||||
.Examples: Concatenation and filtering
|
||||
`>>>` [blue]`[1,2,3] + ["one", "two", "three"]` +
|
||||
[green]`[1, 2, 3, "one", "two", "three"]`
|
||||
|
||||
`>>>` [blue]`[1,2,3,4] - [2,4]` +
|
||||
[green]`[1, 3]`
|
||||
|
||||
`>>>` [blue]`["a", "b", "c", "d"][2:]` +
|
||||
[green]`["c", "d"]`
|
||||
|
||||
|
||||
=== Dictionaries
|
||||
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 represents sets of pairs _key/value_. It is also known as _map_ or _associative array_.
|
||||
|
||||
Dictionary literals are sequences of pairs separated by comma [blue]`,` enclosed between brace brackets.
|
||||
|
||||
@@ -515,6 +541,7 @@ _non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar
|
||||
| [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_
|
||||
| [blue]`#` | _Size_ | Number of items in a dict | [blue]`#{1:"a",2:"b",3:"c"}` -> _3_
|
||||
|===
|
||||
|
||||
.Examples
|
||||
@@ -533,6 +560,9 @@ _non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar
|
||||
`>>>` [blue]`d={"one":1, "two":2}; d["six"]=6; d` +
|
||||
[green]`{"two": 2, "one": 1, "six": 6}`
|
||||
|
||||
`>>>` [blue]`#d` +
|
||||
[green]`3`
|
||||
|
||||
|
||||
== Variables
|
||||
_Expr_, like most programming languages, supports variables. A variable is an identifier with an assigned value. Variables are stored in _contexts_.
|
||||
@@ -556,7 +586,7 @@ NOTE: The assign operator [blue]`=` returns the value assigned to the variable.
|
||||
`>>>` [blue]`a_b` +
|
||||
[green]`3`
|
||||
|
||||
`>>>` [blue]`x = 5.2 * (9-3)` [gray]_// The assigned value has the typical approximation error of the float data-type_ +
|
||||
`>>>` [blue]`x = 5.2 * (9-3)` [gray]_// The assigned value here has the typical approximation error of the float data-type_ +
|
||||
[green]`31.200000000000003`
|
||||
|
||||
`>>>` [blue]`x = 1; y = 2*x` +
|
||||
@@ -664,8 +694,8 @@ The [blue]`:` symbol (colon) is the separator of the selector-cases. Note that i
|
||||
[red]`Eval Error: [1:3] no case catches the value (10) of the selection expression`
|
||||
|
||||
|
||||
=== Variable default value [blue]`??` and [blue]`?=`
|
||||
The left operand of these two operators must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.
|
||||
=== Variable default value [blue]`??`, [blue]`?=`, and [blue]`?!`
|
||||
The left operand of first two operators, [blue]`??` and [blue]`?=`, must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.
|
||||
|
||||
IMPORTANT: If the left variable is defined, the right expression is not evaluated at all.
|
||||
|
||||
@@ -673,6 +703,10 @@ The [blue]`??` operator do not change the status of the left variable.
|
||||
|
||||
The [blue]`?=` assigns the calculated value of the right expression to the left variable.
|
||||
|
||||
The third one, [blue]`?!`, is the alternate operator. If the variable on the left size is not defined, it returns [blue]_nil_. Otherwise it returns the result of the expressione on the right side.
|
||||
|
||||
IMPORTANT: If the left variable is NOT defined, the right expression is not evaluated at all.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`var ?? (1+2)` +
|
||||
[green]`3`
|
||||
@@ -686,6 +720,15 @@ The [blue]`?=` assigns the calculated value of the right expression to the left
|
||||
`>>>` [blue]`var` +
|
||||
[green]`3`
|
||||
|
||||
`>>>` [blue]`x ?! 5` +
|
||||
[green]`nil`
|
||||
|
||||
`>>>` [blue]`x=1; x ?! 5` +
|
||||
[green]`5`
|
||||
|
||||
`>>>` [blue]`y ?! (c=5); c` +
|
||||
[red]`Eval Error: undefined variable or function "c"`
|
||||
|
||||
NOTE: These operators have a high priority, in particular higher than the operator [blue]`=`.
|
||||
|
||||
== Priorities of operators
|
||||
@@ -700,16 +743,17 @@ The table below shows all supported operators by decreasing priorities.
|
||||
| [blue]`[`...`]` | _Postfix_ | _Dict item_ | _dict_ `[` _any_ `]` -> _any_
|
||||
.2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `++` -> _integer_
|
||||
| [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `++` -> _any_
|
||||
.2+|*DEFAULT*| [blue]`??` | _Infix_ | _Default value_| _variable_ `??` _any-expr_ -> _any_
|
||||
.3+|*DEFAULT*| [blue]`??` | _Infix_ | _Default value_| _variable_ `??` _any-expr_ -> _any_
|
||||
| [blue]`?=` | _Infix_ | _Default/assign value_| _variable_ `?=` _any-expr_ -> _any_
|
||||
.1+| *ITER*^1^| [blue]`()` | _Prefix_ | _Iterator value_ | `()` _iterator_ -> _any_
|
||||
| [blue]`?!` | _Infix_ | _Alternate value_| _variable_ `?!` _any-expr_ -> _any_
|
||||
//.1+| *ITER*^1^| [blue]`()` | _Prefix_ | _Iterator value_ | `()` _iterator_ -> _any_
|
||||
.1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ `!` -> _integer_
|
||||
.3+|*SIGN*| [blue]`+`, [blue]`-` | _Prefix_ | _Change-sign_| (`+`\|`-`) _number_ -> _number_
|
||||
| [blue]`#` | _Prefix_ | _Lenght-of_ | `#` _collection_ -> _integer_
|
||||
| [blue]`#` | _Prefix_ | _Size-of_ | `#` _iterator_ -> _integer_
|
||||
.2+|*SELECT*| [blue]`? : ::` | _Multi-Infix_ | _Case-Selector_ | _any-expr_ `?` _case-list_ _case-expr_ `:` _case-list_ _case-expr_ ... `::` _default-expr_ -> _any_
|
||||
| [blue]`? : ::` | _Multi-Infix_ | _Index-Selector_ | _int-expr_ `?` _case-expr_ `:` _case-expr_ ... `::` _default-expr_ -> _any_
|
||||
.1+|*FRACT*| [blue]`\|` | _Infix_ | _Fraction_ | _integer_ `\|` _integer_ -> _fraction_
|
||||
.1+|*FRACT*| [blue]`:` | _Infix_ | _Fraction_ | _integer_ `:` _integer_ -> _fraction_
|
||||
.5+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ `*` _number_ -> _number_
|
||||
| [blue]`*` | _Infix_ | _String-repeat_ | _string_ `*` _integer_ -> _string_
|
||||
| [blue]`/` | _Infix_ | _Division_ | _number_ `/` _number_ -> _number_
|
||||
@@ -721,6 +765,9 @@ The table below shows all supported operators by decreasing priorities.
|
||||
| [blue]`+` | _Infix_ | _Dict-join_ | _dict_ `+` _dict_ -> _dict_
|
||||
| [blue]`-` | _Infix_ | _Subtraction_ | _number_ `-` _number_ -> _number_
|
||||
| [blue]`-` | _Infix_ | _List-difference_ | _list_ `-` _list_ -> _list_
|
||||
.3+|*BINARY*| [blue]`&` | _Infix_ | _Binary And_ | _number_ `&` _number_ -> _number_
|
||||
| [blue]`\|` | _Infix_ | _Binary Or_ | _number_ `\|` _number_ -> _number_
|
||||
| [blue]`~` | _Prefix_ | _Binary Not_ | `~` _number_ -> _number_
|
||||
.8+|*RELATION*| [blue]`<` | _Infix_ | _Less_ | _comparable_ `<` _comparable_ -> _boolean_
|
||||
| [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ `\<=` _comparable_ -> _boolean_
|
||||
| [blue]`>` | _Infix_ | _Greater_ | _comparable_ `>` _comparable_ -> _boolean_
|
||||
@@ -741,7 +788,7 @@ The table below shows all supported operators by decreasing priorities.
|
||||
.1+|*RANGE*| [blue]`:` | _Infix_ | _Index-range_ | _integer_ `:` _integer_ -> _integer-pair_
|
||||
|===
|
||||
|
||||
^1^ Experimental
|
||||
//^1^ Experimental
|
||||
|
||||
|
||||
== Functions
|
||||
@@ -752,28 +799,150 @@ Functions in _Expr_ are very similar to functions available in many programming
|
||||
|
||||
|
||||
=== _Expr_ function definition
|
||||
A function is identified and referenced by its name. It can have zero or more parameter. _Expr_ functions also support optional parameters.
|
||||
A function is identified and referenced by its name. It can have zero or more parameter. _Expr_ functions also support optional parameters and passing paramters by name.
|
||||
|
||||
.Expr's function definition syntax
|
||||
====
|
||||
*_function-definition_* = _identifier_ "**=**" "**func(**" [_param-list_] "**)**" "**{**" _multi-expression_ "**}**" +
|
||||
_param_list_ = _required-param-list_ [ "**,**" _optional-param-list_ ] +
|
||||
*_function-definition_* = _identifier_ "**=**" "**func(**" [_formal-param-list_] "**)**" "**{**" _multi-expression_ "**}**" +
|
||||
_formal-param_list_ = _required-param-list_ [ "**,**" _optional-param-list_ ] +
|
||||
_required-param-list_ = _identifier_ { "**,**" _identifier_ } +
|
||||
_optional-param-list_ = _optional-parm_ { "**,**" _optional-param_ } +
|
||||
_optional-param_ = _identifier_ "**=**" _any-expr_
|
||||
_optional-param_ = _param-name_ "**=**" _any-expr_ +
|
||||
_param-name_ = _identifier_
|
||||
====
|
||||
|
||||
.Examples
|
||||
#TODO#
|
||||
`>>>` [gray]_// A simple function: it takes two parameters and returns their "sum"_**^(*)^** +
|
||||
`>>>` [blue]`sum = func(a, b){ a + b }` +
|
||||
[green]`sum(a, b):any{}`
|
||||
|
||||
^(\*)^ Since the plus, *+*, operator is defined for multiple data-types, the _sum()_ function can be used for any pair of that types.
|
||||
|
||||
`>>>` [gray]_// A more complex example: recursive calculation of the n-th value of Fibonacci's sequence_ +
|
||||
`>>>` [blue]`fib = func(n){ n ? [0] {0}: [1] {1} :: {fib(n-1)+fib(n-2)} }` +
|
||||
[green]`fib(n):any{}`
|
||||
|
||||
|
||||
`>>>` [gray]_// Same function fib() but entered by splitting it over mulple text lines_ +
|
||||
`>>>` [blue]`fib = func(n){ \` +
|
||||
`\...` [blue]`{nbsp}{nbsp}n ? \` +
|
||||
`\...` [blue]`{nbsp}{nbsp}{nbsp}{nbsp}[0] {0} : \` +
|
||||
`\...` [blue]`{nbsp}{nbsp}{nbsp}{nbsp}[1] {1} :: \` +
|
||||
`\...` [blue]`{nbsp}{nbsp}{nbsp}{nbsp}{ \` +
|
||||
`\...` [blue]`{nbsp}{nbsp}{nbsp}{nbsp}{nbsp}{nbsp}fib(n-1) + fib(n-2) \` +
|
||||
`\...` [blue]`{nbsp}{nbsp}{nbsp}{nbsp}} \` +
|
||||
`\...` [blue]`}` +
|
||||
[green]`fib(n):any{}`
|
||||
|
||||
`>>>` [gray]_// Required and optional parameters_ +
|
||||
`>>>` [blue]`measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? [true] {"s"} :: {""}}` +
|
||||
[green]`measure(value, unit="meter"):any{}`
|
||||
|
||||
|
||||
=== _Golang_ function definition
|
||||
Description of how to define Golan functions and how to bind them to _Expr_ are topics treated in another document that I'll write, one day, maybe.
|
||||
|
||||
=== Function calls
|
||||
#TODO: function calls operations#
|
||||
To call a function, either Expr or Golang type, it is necessary to specify its name and, at least, its required parameters.
|
||||
|
||||
Functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the reference operator [blue]`@` it is possibile to export local definition to the calling context.
|
||||
.Function invocation syntax
|
||||
====
|
||||
*_function-call_* = _identifier_ "**(**" _actual-param-list_ "**)**" +
|
||||
_actual-param-list_ = [_positional-params_] [_named-parameters_] +
|
||||
_positional-params_ = _any-value_ { "*,*" _any-value_ } +
|
||||
_named-params_ = _param-name_ "**=**" _any-value_ { "*,*" _param-name_ "**=**" _any-value_ } +
|
||||
_param-name_ = _identifier_
|
||||
====
|
||||
|
||||
.Examples of calling the `sum()` functions defined above
|
||||
`>>>` [gray]_// sum of two integers_ +
|
||||
`>>>` [blue]`sum(-6, 2)` +
|
||||
[green]`-4` +
|
||||
`>>>` [gray]_// same as above but passing the parameters by name_ +
|
||||
`>>>` [blue]`sum(a=-6, b=2)` +
|
||||
[green]`-4` +
|
||||
`>>>` [gray]_// again, but swapping parameter positions (see the diff() examples below)_ +
|
||||
`>>>` [blue]`sum(b=2, a=-6)` +
|
||||
[green]`-4` +
|
||||
`>>>` [gray]_// sum of a fraction and an integer_ +
|
||||
`>>>` [blue]`sum(3|2, 2)` +
|
||||
[green]`7|2` +
|
||||
`>>>` [gray]_// sum of two strings_ +
|
||||
`>>>` [blue]`sum("bye", "-bye")` +
|
||||
[green]`"bye-bye"` +
|
||||
`>>>` [gray]_// sum of two lists_ +
|
||||
`>>>` [blue]`sum(["one", 1], ["two", 2])` +
|
||||
[green]`["one", 1, "two", 2]`
|
||||
|
||||
.Examples of calling a function with parameters passed by name
|
||||
`>>>` [gray]_// diff(a,b) calculates a-b_ +
|
||||
`>>>` [blue]`diff = func(a,b){a-b}` +
|
||||
[green]`diff(a, b):any{}` +
|
||||
`>>>` [gray]_// simple invocation_ +
|
||||
`>>>` [blue]`diff(10,8)` +
|
||||
[green]`2` +
|
||||
`>>>` [gray]_// swapped parameters passed by name_ +
|
||||
`>>>` [blue]`diff(b=8,a=10)` +
|
||||
[green]`2`
|
||||
|
||||
.Examples of calling the `fib()` function defined above
|
||||
`>>>` [gray]_// Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ..._ +
|
||||
`>>>` [blue]`fib(6)` +
|
||||
[green]`8` +
|
||||
`>>>` [blue]`fib(9)` +
|
||||
[green]`34`
|
||||
|
||||
.Examples of calling the `measure()` functions defined above
|
||||
`>>>` [gray]_// simple call_ +
|
||||
`>>>` [blue]`measure(10,"litre")` +
|
||||
[green]`"10 litres"` +
|
||||
`>>>` [gray]_// accept the default unit_ +
|
||||
`>>>` [blue]`measure(8)` +
|
||||
[green]`"8 meters"` +
|
||||
`>>>` [gray]_// without the required parameter 'value'_ +
|
||||
`>>>` [blue]`measure(unit="degrees"))` +
|
||||
[red]`Eval Error: measure(): missing params -- value`
|
||||
|
||||
.Examples of context binding (closures)
|
||||
`>>>` [blue]`factory = func(n=2){ func(x){x*n} }` +
|
||||
[green]`factory(n=2):any{}` +
|
||||
`>>>` [blue]`double = factory()` +
|
||||
[green]`double(x):any{}` +
|
||||
`>>>` [blue]`triple = factory(3)` +
|
||||
[green]`triple(x):any{}` +
|
||||
`>>>` [blue]`double(5)` +
|
||||
[green]`10` +
|
||||
`>>>` [blue]`triple(5)` +
|
||||
[green]`15`
|
||||
|
||||
|
||||
=== Function context
|
||||
Functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the _clone_ modifier [blue]`@` it is possibile to export local definition to the calling context. The clone modifier must be used as prefix to variable names and it is part of the name. E.g. [blue]`@x` is not the same as [blue]`x`; they are two different and un related variables.
|
||||
|
||||
Clone variables are normal local variables. The only diffence will appear when the defining function terminate, just before the destruction of its local context. At that point, all local clone variables are cloned in the calling context with the same names but the [blue]`@` symbol.
|
||||
|
||||
.Example
|
||||
`>>>` [blue]`f = func() { @x = 3; x = 5 }` [gray]_// f() declares two *different* local variables: ``@x`` and ``x``_ +
|
||||
[green]`f():any{}` +
|
||||
`>>>` [blue]`f()` [gray]_// The multi-expression (two) in f() is calculated and the last result is returned_ +
|
||||
[green]`5` +
|
||||
`>>>` [blue]`x` [gray]_// The `x` variable was not defined in the main context before the f() invocation. It appears in the main context by cloning the `@x` variable, local to f() after its termnation._ +
|
||||
[green]`3`
|
||||
|
||||
NOTE: The clone modifier [blue]`@` does not make a variable a reference variable, as the ampersand character `&` does in languages such as C and C++.
|
||||
|
||||
[IMPORTANT]
|
||||
====
|
||||
The clone modifier can also be used to declare the formal parameters of functions, because they are local variables too.
|
||||
|
||||
.Example
|
||||
`>>>` [blue]`g = func(@p) {2+@p}`
|
||||
g(@p):any{}`
|
||||
`>>>` [blue]`g(9)`
|
||||
11`
|
||||
`>>>` [blue]`p
|
||||
9
|
||||
====
|
||||
|
||||
== Iterators
|
||||
#TODO: function calls operations#
|
||||
|
||||
+338
-81
@@ -474,6 +474,16 @@ pre.rouge .gh {
|
||||
color: #b8bb26;
|
||||
font-weight: bold;
|
||||
}
|
||||
pre.rouge .ge {
|
||||
font-style: italic;
|
||||
}
|
||||
pre.rouge .ges {
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
}
|
||||
pre.rouge .gs {
|
||||
font-weight: bold;
|
||||
}
|
||||
pre.rouge .k, pre.rouge .kn, pre.rouge .kp, pre.rouge .kr, pre.rouge .kv {
|
||||
color: #fb4934;
|
||||
}
|
||||
@@ -567,7 +577,7 @@ pre.rouge .ss {
|
||||
<li><a href="#_but_operator">4.2. <code class="blue">but</code> operator</a></li>
|
||||
<li><a href="#_assignment_operator">4.3. Assignment operator <code class="blue">=</code></a></li>
|
||||
<li><a href="#_selector_operator">4.4. Selector operator <code class="blue">? : ::</code></a></li>
|
||||
<li><a href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code> and <code class="blue">?=</code></a></li>
|
||||
<li><a href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code>, <code class="blue">?=</code>, and <code class="blue">?!</code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_priorities_of_operators">5. Priorities of operators</a></li>
|
||||
@@ -576,6 +586,7 @@ pre.rouge .ss {
|
||||
<li><a href="#_expr_function_definition">6.1. <em>Expr</em> function definition</a></li>
|
||||
<li><a href="#_golang_function_definition">6.2. <em>Golang</em> function definition</a></li>
|
||||
<li><a href="#_function_calls">6.3. Function calls</a></li>
|
||||
<li><a href="#_function_context">6.4. Function context</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_iterators">7. Iterators</a></li>
|
||||
@@ -657,7 +668,7 @@ pre.rouge .ss {
|
||||
<p>Function contexts are created by cloning the calling context. More details on this topic are given later in this document.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><em>Expr</em> creates and keeps a inner <em>global context</em> where it stores imported functions, either from builtin or plugin modules. To perform calculations, the calling program must provide its own context; this is the <em>main context</em>. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The created context can be called <em>function context</em>.</p>
|
||||
<p><em>Expr</em> creates and keeps a inner <em>global context</em> where it stores imported functions, either from builtin or plugin modules. To perform calculations, the user program must provide its own context; this is the <em>main context</em>. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The created context can be called <em>function context</em>.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Imported functions are registerd in the <em>global context</em>. When an expression first calls an imported function, that function is linked to the current context; this can be the <em>main context</em> or a <em>function context</em>.</p>
|
||||
@@ -687,20 +698,20 @@ pre.rouge .ss {
|
||||
<pre class="rouge highlight"><code data-lang="shell"><span class="c"># Type 'exit' or Ctrl+D to quit the program.</span>
|
||||
|
||||
<span class="o">[</span>user]<span class="nv">$ </span>./dev-expr
|
||||
dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o">(</span>build 14<span class="o">)</span>,2024/06/17 <span class="o">(</span>celestino.amoroso@portale-stac.it<span class="o">)</span>
|
||||
Based on the Expr package v0.19.0
|
||||
dev-expr <span class="nt">--</span> Expressions calculator v1.12.0<span class="o">(</span>build 1<span class="o">)</span>,2024/09/14 <span class="o">(</span>celestino.amoroso@portale-stac.it<span class="o">)</span>
|
||||
Based on the Expr package v0.26.0
|
||||
Type <span class="nb">help </span>to get the list of available commands
|
||||
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||
<span class="o">>>></span> <span class="nb">help</span>
|
||||
<span class="nt">---</span> REPL commands:
|
||||
<span class="nb">source</span> <span class="nt">--</span> Load a file as input
|
||||
<span class="nb">tty</span> <span class="nt">--</span> Enable/Disable ansi output <i class="conum" data-value="1"></i><b>(1)</b>
|
||||
base <span class="nt">--</span> Set the integer output base: 2, 8, 10, or 16
|
||||
<span class="nb">exit</span> <span class="nt">--</span> Exit the program
|
||||
<span class="nb">help</span> <span class="nt">--</span> Show <span class="nb">command </span>list
|
||||
ml <span class="nt">--</span> Enable/Disable multi-line output
|
||||
mods <span class="nt">--</span> List <span class="nb">builtin </span>modules
|
||||
output <span class="nt">--</span> Enable/Disable printing expression results. Options <span class="s1">'on'</span>, <span class="s1">'off'</span>, <span class="s1">'status'</span>
|
||||
<span class="nb">source</span> <span class="nt">--</span> Load a file as input
|
||||
<span class="nb">tty</span> <span class="nt">--</span> Enable/Disable ansi output <i class="conum" data-value="1"></i><b>(1)</b>
|
||||
|
||||
<span class="nt">---</span> Command line options:
|
||||
<span class="nt">-b</span> <<span class="nb">builtin</span><span class="o">></span> Import <span class="nb">builtin </span>modules.
|
||||
@@ -744,16 +755,16 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
9.5
|
||||
<span class="o">>>></span> 0xFD + 0b1 + 0o1 <i class="conum" data-value="1"></i><b>(1)</b>
|
||||
255
|
||||
<span class="o">>>></span> 1|2 + 2|3 <i class="conum" data-value="2"></i><b>(2)</b>
|
||||
7|6
|
||||
<span class="o">>>></span> 1:2 + 2:3 <i class="conum" data-value="2"></i><b>(2)</b>
|
||||
7:6
|
||||
<span class="o">>>></span> ml <i class="conum" data-value="3"></i><b>(3)</b>
|
||||
<span class="o">>>></span> 1|2 + 2|3
|
||||
<span class="o">>>></span> 1:2 + 2:3
|
||||
7
|
||||
-
|
||||
6
|
||||
<span class="o">>>></span> 4+2 but 5|2+0.5 <i class="conum" data-value="4"></i><b>(4)</b>
|
||||
<span class="o">>>></span> 4+2 but 5:2+0.5 <i class="conum" data-value="4"></i><b>(4)</b>
|
||||
3
|
||||
<span class="o">>>></span> 4+2<span class="p">;</span> 5|2+0.5 <i class="conum" data-value="5"></i><b>(5)</b>
|
||||
<span class="o">>>></span> 4+2<span class="p">;</span> 5:2+0.5 <i class="conum" data-value="5"></i><b>(5)</b>
|
||||
3
|
||||
<span class="o">>>></span></code></pre>
|
||||
</div>
|
||||
@@ -766,7 +777,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i class="conum" data-value="2"></i><b>2</b></td>
|
||||
<td>Fractions: <em>numerator</em> | <em>denominator</em>.</td>
|
||||
<td>Fractions: <em>numerator</em> : <em>denominator</em>.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i class="conum" data-value="3"></i><b>3</b></td>
|
||||
@@ -805,12 +816,12 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<p><span class="blue">Floats</span></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><span class="blue">Factions</span></p>
|
||||
<p><span class="blue">Fractions</span></p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>In mixed operations involving integers, fractions and floats, automatic type promotion to the largest type take place.</p>
|
||||
<p>In mixed operations involving integers, fractions and floats, automatic type promotion to the largest type is performed.</p>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_integers"><a class="anchor" href="#_integers"></a><a class="link" href="#_integers">2.1.1. Integers</a></h4>
|
||||
@@ -855,33 +866,33 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
</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>sum</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Sum</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Add two values</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1 + 2</code> → 1</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1 + 2</code> → <em>1</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>subtraction</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Subtraction</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Subtract the right value from the left one</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">3 - 1</code> → 2</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">3 - 1</code> → <em>2</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>product</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Product</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Multiply two values</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1 * 2</code> → -2</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1 * 2</code> → <em>-2</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>Division</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Integer division</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Divide the left value by the right one<sup>(*)</sup></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-10 / 2</code> → 5</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-11 / 2</code> → <em>-5</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>Modulo</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Remainder of the integer division</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 % 2</code> → 1</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 % 2</code> → <em>1</em></p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -946,31 +957,31 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
</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>sum</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Sum</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Add two values</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">4 + 0.5</code> → 4.5</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>subtraction</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Subtraction</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Subtract the right value from the left one</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">4 - 0.5</code> → 3.5</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>product</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Product</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Multiply two values</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">4 * 0.5</code> → 2.0</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>Division</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Float division</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Divide the left value by the right one</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">1.0 / 2</code> → 0.5</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>Float division</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Forced float division</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Force float division</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1 ./ 2</code> → -0.5</p></td>
|
||||
</tr>
|
||||
@@ -980,13 +991,13 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<div class="sect3">
|
||||
<h4 id="_fractions"><a class="anchor" href="#_fractions"></a><a class="link" href="#_fractions">2.1.3. Fractions</a></h4>
|
||||
<div class="paragraph">
|
||||
<p><em>Expr</em> also supports fractions. Fraction literals are made with two integers separated by a vertical bar <code>|</code>.</p>
|
||||
<p><em>Expr</em> also supports fractions. Fraction literals are made with two integers separated by a colon character <code>:</code>.</p>
|
||||
</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 3. Fraction literal syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><strong><em>fraction</em></strong> = [<em>sign</em>] (<em>num-den-spec</em> | <em>float-spec</em>)<br>
|
||||
<p><strong><em>fraction</em></strong> = [<em>sign</em>] (<em>num-den-spec</em> "<strong>:</strong>" <em>float-spec</em>)<br>
|
||||
<em>sign</em> = "<strong>+</strong>" | "<strong>-</strong>"<br>
|
||||
<em>num-den-spec</em> = <em>digit-seq</em> "<strong>|</strong>" <em>digit-seq</em><br>
|
||||
<em>float-spec</em> = <em>dec-seq</em> "<strong>.</strong>" [<em>dec-seq</em>] "<strong>(</strong>" <em>dec-seq</em> "<strong>)</strong>"<br>
|
||||
@@ -997,55 +1008,55 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">1 | 2</code><br>
|
||||
<code class="green">1|2</code></p>
|
||||
<p><code>>>></code> <code class="blue">1 : 2</code><br>
|
||||
<code class="green">1:2</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">4|6</code> <em class="gray">// Fractions are always reduced to their lowest terms</em><br>
|
||||
<code class="green">2|3</code></p>
|
||||
<p><code>>>></code> <code class="blue">4:6</code> <em class="gray">// Fractions are always reduced to their lowest terms</em><br>
|
||||
<code class="green">2:3</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">1|2 + 2|3</code><br>
|
||||
<code class="green">7|6</code></p>
|
||||
<p><code>>>></code> <code class="blue">1:2 + 2:3</code><br>
|
||||
<code class="green">7:6</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">1|2 * 2|3</code><br>
|
||||
<code class="green">1|3</code></p>
|
||||
<p><code>>>></code> <code class="blue">1:2 * 2:3</code><br>
|
||||
<code class="green">1:3</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">1|2 / 1|3</code><br>
|
||||
<code class="green">3|2</code></p>
|
||||
<p><code>>>></code> <code class="blue">1:2 / 1:3</code><br>
|
||||
<code class="green">3:2</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">1|2 ./ 1|3</code> <em class="gray">// Force decimal division</em><br>
|
||||
<p><code>>>></code> <code class="blue">1:2 ./ 1:3</code> <em class="gray">// Force decimal division</em><br>
|
||||
<code class="green">1.5</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">-1|2</code><br>
|
||||
<code class="green">-1|2</code></p>
|
||||
<p><code>>>></code> <code class="blue">-1:2</code><br>
|
||||
<code class="green">-1:2</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">1|-2</code> <em class="gray">// Invalid sign specification</em><br>
|
||||
<em class="red">Eval Error: [1:3] infix operator "|" requires two non-nil operands, got 1</em></p>
|
||||
<p><code>>>></code> <code class="blue">1:-2</code> <em class="gray">// Invalid sign specification</em><br>
|
||||
<em class="red">Eval Error: [1:3] infix operator ":" requires two non-nil operands, got 1</em></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">1|(-2)</code><br>
|
||||
<code class="green">-1|2</code></p>
|
||||
<p><code>>>></code> <code class="blue">1:(-2)</code><br>
|
||||
<code class="green">-1:2</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Fractions can be used together with integers and floats in expressions.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">1|2 + 5</code><br>
|
||||
<code class="green">11|2</code></p>
|
||||
<p><code>>>></code> <code class="blue">1:2 + 5</code><br>
|
||||
<code class="green">11:2</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">4 - 1|2</code><br>
|
||||
<code class="green">7|2</code></p>
|
||||
<p><code>>>></code> <code class="blue">4 - 1:2</code><br>
|
||||
<code class="green">7:2</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">1.0 + 1|2</code><br>
|
||||
<p><code>>>></code> <code class="blue">1.0 + 1:2</code><br>
|
||||
<code class="green">1.5</code></p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1074,7 +1085,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<code class="green">123    abc</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Some arithmetic operators can also be used with strings.</p>
|
||||
<p>Some arithmetic operators also apply to strings.</p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all stretch">
|
||||
<caption class="title">Table 3. String operators</caption>
|
||||
@@ -1393,6 +1404,12 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">2 in [1,2,3]</code> → <em>true</em><br>
|
||||
<code class="blue">6 in [1,2,3]</code> → <em>false</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>Size</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of items in a list</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">#[1,2,3]</code> → <em>3</em></p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="paragraph">
|
||||
@@ -1415,11 +1432,19 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Items of list</div>
|
||||
<div class="title">Examples: Getting items from lists</div>
|
||||
<p><code>>>></code> <code class="blue">[1,2,3][1]</code><br>
|
||||
<code class="green">2</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">index=2; ["a", "b", "c", "d"][index]</code><br>
|
||||
<code class="green">c</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">["a", "b", "c", "d"][2:]</code><br>
|
||||
<code class="green">["c", "d"]</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">list=[1,2,3]; list[1]</code><br>
|
||||
<code class="green">2</code></p>
|
||||
</div>
|
||||
@@ -1432,6 +1457,10 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<code class="green">two</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">list[1]="six"; list</code><br>
|
||||
<code class="green">["one", "six", "three"]</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">list[-1]</code><br>
|
||||
<code class="green">three</code></p>
|
||||
</div>
|
||||
@@ -1440,22 +1469,42 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<code class="red">Eval Error: [1:9] index 10 out of bounds</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example: Number of elements in a list</div>
|
||||
<p><code>>>></code> <code class="blue">#list</code><br>
|
||||
<code class="green">3</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">index=2; ["a", "b", "c", "d"][index]</code><br>
|
||||
<code class="green">c</code></p>
|
||||
<div class="title">Examples: Element insertion</div>
|
||||
<p><code>>>></code> <code class="blue">"first" >> list</code><br>
|
||||
<code class="green">["first", "one", "six", "three"]</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">["a", "b", "c", "d"][2:]</code><br>
|
||||
<code class="green">["c", "d"]</code></p>
|
||||
<p><code>>>></code> <code class="blue">list << "last"</code><br>
|
||||
<code class="green">["first", "one", "six", "three", "last"]</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples: Element in list</div>
|
||||
<p><code>>>></code> <code class="blue">"six" in list</code><br>
|
||||
<code class="green">true</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">"ten" in list</code><br>
|
||||
<code class="green">false</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples: Concatenation and filtering</div>
|
||||
<p><code>>>></code> <code class="blue">[1,2,3] + ["one", "two", "three"]</code><br>
|
||||
<code class="green">[1, 2, 3, "one", "two", "three"]</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">[1,2,3,4] - [2,4]</code><br>
|
||||
<code class="green">[1, 3]</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_dictionaries"><a class="anchor" href="#_dictionaries"></a><a class="link" href="#_dictionaries">2.5. Dictionaries</a></h3>
|
||||
<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>.</p>
|
||||
<p>The <em>dictionary</em>, or <em>dict</em>, data-type represents sets of pairs <em>key/value</em>. It is also known as <em>map</em> or <em>associative array</em>.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Dictionary literals are sequences of pairs separated by comma <code class="blue">,</code> enclosed between brace brackets.</p>
|
||||
@@ -1514,6 +1563,12 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">"one" in {"one":1, "two":2}</code> → <em>true</em><br>
|
||||
<code class="blue">"six" in {"one":1, "two":2}</code> → <em>false</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>Size</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Number of items in a dict</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">#{1:"a",2:"b",3:"c"}</code> → <em>3</em></p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="paragraph">
|
||||
@@ -1537,6 +1592,10 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<p><code>>>></code> <code class="blue">d={"one":1, "two":2}; d["six"]=6; d</code><br>
|
||||
<code class="green">{"two": 2, "one": 1, "six": 6}</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">#d</code><br>
|
||||
<code class="green">3</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1582,7 +1641,7 @@ The assign operator <code class="blue">=</code> returns the value assigned to th
|
||||
<code class="green">3</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">x = 5.2 * (9-3)</code> <em class="gray">// The assigned value has the typical approximation error of the float data-type</em><br>
|
||||
<p><code>>>></code> <code class="blue">x = 5.2 * (9-3)</code> <em class="gray">// The assigned value here has the typical approximation error of the float data-type</em><br>
|
||||
<code class="green">31.200000000000003</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
@@ -1751,9 +1810,9 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_variable_default_value_and"><a class="anchor" href="#_variable_default_value_and"></a><a class="link" href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code> and <code class="blue">?=</code></a></h3>
|
||||
<h3 id="_variable_default_value_and"><a class="anchor" href="#_variable_default_value_and"></a><a class="link" href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code>, <code class="blue">?=</code>, and <code class="blue">?!</code></a></h3>
|
||||
<div class="paragraph">
|
||||
<p>The left operand of these two operators must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.</p>
|
||||
<p>The left operand of first two operators, <code class="blue">??</code> and <code class="blue">?=</code>, must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.</p>
|
||||
</div>
|
||||
<div class="admonitionblock important">
|
||||
<table>
|
||||
@@ -1774,6 +1833,21 @@ If the left variable is defined, the right expression is not evaluated at all.
|
||||
<p>The <code class="blue">?=</code> assigns the calculated value of the right expression to the left variable.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The third one, <code class="blue">?!</code>, is the alternate operator. If the variable on the left size is not defined, it returns <em class="blue">nil</em>. Otherwise it returns the result of the expressione on the right side.</p>
|
||||
</div>
|
||||
<div class="admonitionblock important">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<i class="fa icon-important" title="Important"></i>
|
||||
</td>
|
||||
<td class="content">
|
||||
If the left variable is NOT defined, the right expression is not evaluated at all.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">var ?? (1+2)</code><br>
|
||||
<code class="green">3</code></p>
|
||||
@@ -1790,6 +1864,18 @@ If the left variable is defined, the right expression is not evaluated at all.
|
||||
<p><code>>>></code> <code class="blue">var</code><br>
|
||||
<code class="green">3</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">x ?! 5</code><br>
|
||||
<code class="green">nil</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">x=1; x ?! 5</code><br>
|
||||
<code class="green">5</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">y ?! (c=5); c</code><br>
|
||||
<code class="red">Eval Error: undefined variable or function "c"</code></p>
|
||||
</div>
|
||||
<div class="admonitionblock note">
|
||||
<table>
|
||||
<tr>
|
||||
@@ -1857,7 +1943,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>iterator</em> <code>++</code> → <em>any</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>DEFAULT</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top" rowspan="3"><p class="tableblock"><strong>DEFAULT</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">??</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Default value</em></p></td>
|
||||
@@ -1870,11 +1956,10 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>variable</em> <code>?=</code> <em>any-expr</em> → <em>any</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>ITER</strong><sup>1</sup></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>Prefix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Iterator value</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code>()</code> <em>iterator</em> → <em>any</em></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>Alternate value</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>variable</em> <code>?!</code> <em>any-expr</em> → <em>any</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>FACT</strong></p></td>
|
||||
@@ -1917,10 +2002,10 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>FRACT</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>Fraction</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>integer</em> <code>|</code> <em>integer</em> → <em>fraction</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>integer</em> <code>:</code> <em>integer</em> → <em>fraction</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="5"><p class="tableblock"><strong>PROD</strong></p></td>
|
||||
@@ -1991,6 +2076,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>list</em> <code>-</code> <em>list</em> → <em>list</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="3"><p class="tableblock"><strong>BINARY</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Binary And</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>number</em> <code>&</code> <em>number</em> → <em>number</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>Binary Or</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>number</em> <code>|</code> <em>number</em> → <em>number</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>Prefix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Binary Not</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code>~</code> <em>number</em> → <em>number</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<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"><</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
@@ -2107,9 +2211,6 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="paragraph">
|
||||
<p><sup>1</sup> Experimental</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect1">
|
||||
@@ -2131,23 +2232,51 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<div class="sect2">
|
||||
<h3 id="_expr_function_definition"><a class="anchor" href="#_expr_function_definition"></a><a class="link" href="#_expr_function_definition">6.1. <em>Expr</em> function definition</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>A function is identified and referenced by its name. It can have zero or more parameter. <em>Expr</em> functions also support optional parameters.</p>
|
||||
<p>A function is identified and referenced by its name. It can have zero or more parameter. <em>Expr</em> functions also support optional parameters and passing paramters by name.</p>
|
||||
</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 14. Expr’s function definition syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><strong><em>function-definition</em></strong> = <em>identifier</em> "<strong>=</strong>" "<strong>func(</strong>" [<em>param-list</em>] "<strong>)</strong>" "<strong>{</strong>" <em>multi-expression</em> "<strong>}</strong>"<br>
|
||||
<em>param_list</em> = <em>required-param-list</em> [ "<strong>,</strong>" <em>optional-param-list</em> ]<br>
|
||||
<p><strong><em>function-definition</em></strong> = <em>identifier</em> "<strong>=</strong>" "<strong>func(</strong>" [<em>formal-param-list</em>] "<strong>)</strong>" "<strong>{</strong>" <em>multi-expression</em> "<strong>}</strong>"<br>
|
||||
<em>formal-param_list</em> = <em>required-param-list</em> [ "<strong>,</strong>" <em>optional-param-list</em> ]<br>
|
||||
<em>required-param-list</em> = <em>identifier</em> { "<strong>,</strong>" <em>identifier</em> }<br>
|
||||
<em>optional-param-list</em> = <em>optional-parm</em> { "<strong>,</strong>" <em>optional-param</em> }<br>
|
||||
<em>optional-param</em> = <em>identifier</em> "<strong>=</strong>" <em>any-expr</em></p>
|
||||
<em>optional-param</em> = <em>param-name</em> "<strong>=</strong>" <em>any-expr</em><br>
|
||||
<em>param-name</em> = <em>identifier</em></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><mark>TODO</mark></p>
|
||||
<p><code>>>></code> <em class="gray">// A simple function: it takes two parameters and returns their "sum"</em><strong><sup>(*)</sup></strong><br>
|
||||
<code>>>></code> <code class="blue">sum = func(a, b){ a + b }</code><br>
|
||||
<code class="green">sum(a, b):any{}</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><sup>(*)</sup> Since the plus, *+*, operator is defined for multiple data-types, the <em>sum()</em> function can be used for any pair of that types.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <em class="gray">// A more complex example: recursive calculation of the n-th value of Fibonacci’s sequence</em><br>
|
||||
<code>>>></code> <code class="blue">fib = func(n){ n ? [0] {0}: [1] {1} :: {fib(n-1)+fib(n-2)} }</code><br>
|
||||
<code class="green">fib(n):any{}</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <em class="gray">// Same function fib() but entered by splitting it over mulple text lines</em><br>
|
||||
<code>>>></code> <code class="blue">fib = func(n){ \</code><br>
|
||||
<code>...</code> <code class="blue">  n ? \</code><br>
|
||||
<code>...</code> <code class="blue">    [0] {0} : \</code><br>
|
||||
<code>...</code> <code class="blue">    [1] {1} :: \</code><br>
|
||||
<code>...</code> <code class="blue">    { \</code><br>
|
||||
<code>...</code> <code class="blue">      fib(n-1) + fib(n-2) \</code><br>
|
||||
<code>...</code> <code class="blue">    } \</code><br>
|
||||
<code>...</code> <code class="blue">}</code><br>
|
||||
<code class="green">fib(n):any{}</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <em class="gray">// Required and optional parameters</em><br>
|
||||
<code>>>></code> <code class="blue">measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? [true] {"s"} :: {""}}</code><br>
|
||||
<code class="green">measure(value, unit="meter"):any{}</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
@@ -2159,10 +2288,138 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<div class="sect2">
|
||||
<h3 id="_function_calls"><a class="anchor" href="#_function_calls"></a><a class="link" href="#_function_calls">6.3. Function calls</a></h3>
|
||||
<div class="paragraph">
|
||||
<p><mark>TODO: function calls operations</mark></p>
|
||||
<p>To call a function, either Expr or Golang type, it is necessary to specify its name and, at least, its required parameters.</p>
|
||||
</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 15. Function invocation syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><strong><em>function-call</em></strong> = <em>identifier</em> "<strong>(</strong>" <em>actual-param-list</em> "<strong>)</strong>"<br>
|
||||
<em>actual-param-list</em> = [<em>positional-params</em>] [<em>named-parameters</em>]<br>
|
||||
<em>positional-params</em> = <em>any-value</em> { "<strong>,</strong>" <em>any-value</em> }<br>
|
||||
<em>named-params</em> = <em>param-name</em> "<strong>=</strong>" <em>any-value</em> { "<strong>,</strong>" <em>param-name</em> "<strong>=</strong>" <em>any-value</em> }<br>
|
||||
<em>param-name</em> = <em>identifier</em></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the reference operator <code class="blue">@</code> it is possibile to export local definition to the calling context.</p>
|
||||
<div class="title">Examples of calling the <code>sum()</code> functions defined above</div>
|
||||
<p><code>>>></code> <em class="gray">// sum of two integers</em><br>
|
||||
<code>>>></code> <code class="blue">sum(-6, 2)</code><br>
|
||||
<code class="green">-4</code><br>
|
||||
<code>>>></code> <em class="gray">// same as above but passing the parameters by name</em><br>
|
||||
<code>>>></code> <code class="blue">sum(a=-6, b=2)</code><br>
|
||||
<code class="green">-4</code><br>
|
||||
<code>>>></code> <em class="gray">// again, but swapping parameter positions (see the diff() examples below)</em><br>
|
||||
<code>>>></code> <code class="blue">sum(b=2, a=-6)</code><br>
|
||||
<code class="green">-4</code><br>
|
||||
<code>>>></code> <em class="gray">// sum of a fraction and an integer</em><br>
|
||||
<code>>>></code> <code class="blue">sum(3|2, 2)</code><br>
|
||||
<code class="green">7|2</code><br>
|
||||
<code>>>></code> <em class="gray">// sum of two strings</em><br>
|
||||
<code>>>></code> <code class="blue">sum("bye", "-bye")</code><br>
|
||||
<code class="green">"bye-bye"</code><br>
|
||||
<code>>>></code> <em class="gray">// sum of two lists</em><br>
|
||||
<code>>>></code> <code class="blue">sum(["one", 1], ["two", 2])</code><br>
|
||||
<code class="green">["one", 1, "two", 2]</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples of calling a function with parameters passed by name</div>
|
||||
<p><code>>>></code> <em class="gray">// diff(a,b) calculates a-b</em><br>
|
||||
<code>>>></code> <code class="blue">diff = func(a,b){a-b}</code><br>
|
||||
<code class="green">diff(a, b):any{}</code><br>
|
||||
<code>>>></code> <em class="gray">// simple invocation</em><br>
|
||||
<code>>>></code> <code class="blue">diff(10,8)</code><br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <em class="gray">// swapped parameters passed by name</em><br>
|
||||
<code>>>></code> <code class="blue">diff(b=8,a=10)</code><br>
|
||||
<code class="green">2</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples of calling the <code>fib()</code> function defined above</div>
|
||||
<p><code>>>></code> <em class="gray">// Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …​</em><br>
|
||||
<code>>>></code> <code class="blue">fib(6)</code><br>
|
||||
<code class="green">8</code><br>
|
||||
<code>>>></code> <code class="blue">fib(9)</code><br>
|
||||
<code class="green">34</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples of calling the <code>measure()</code> functions defined above</div>
|
||||
<p><code>>>></code> <em class="gray">// simple call</em><br>
|
||||
<code>>>></code> <code class="blue">measure(10,"litre")</code><br>
|
||||
<code class="green">"10 litres"</code><br>
|
||||
<code>>>></code> <em class="gray">// accept the default unit</em><br>
|
||||
<code>>>></code> <code class="blue">measure(8)</code><br>
|
||||
<code class="green">"8 meters"</code><br>
|
||||
<code>>>></code> <em class="gray">// without the required parameter 'value'</em><br>
|
||||
<code>>>></code> <code class="blue">measure(unit="degrees"))</code><br>
|
||||
<code class="red">Eval Error: measure(): missing params — value</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples of context binding (closures)</div>
|
||||
<p><code>>>></code> <code class="blue">factory = func(n=2){ func(x){x*n} }</code><br>
|
||||
<code class="green">factory(n=2):any{}</code><br>
|
||||
<code>>>></code> <code class="blue">double = factory()</code><br>
|
||||
<code class="green">double(x):any{}</code><br>
|
||||
<code>>>></code> <code class="blue">triple = factory(3)</code><br>
|
||||
<code class="green">triple(x):any{}</code><br>
|
||||
<code>>>></code> <code class="blue">double(5)</code><br>
|
||||
<code class="green">10</code><br>
|
||||
<code>>>></code> <code class="blue">triple(5)</code><br>
|
||||
<code class="green">15</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_function_context"><a class="anchor" href="#_function_context"></a><a class="link" href="#_function_context">6.4. Function context</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>Functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the <em>clone</em> modifier <code class="blue">@</code> it is possibile to export local definition to the calling context. The clone modifier must be used as prefix to variable names and it is part of the name. E.g. <code class="blue">@x</code> is not the same as <code class="blue">x</code>; they are two different and un related variables.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Clone variables are normal local variables. The only diffence will appear when the defining function terminate, just before the destruction of its local context. At that point, all local clone variables are cloned in the calling context with the same names but the <code class="blue">@</code> symbol.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example</div>
|
||||
<p><code>>>></code> <code class="blue">f = func() { @x = 3; x = 5 }</code> <em class="gray">// f() declares two <strong>different</strong> local variables: <code>@x</code> and <code>x</code></em><br>
|
||||
<code class="green">f():any{}</code><br>
|
||||
<code>>>></code> <code class="blue">f()</code> <em class="gray">// The multi-expression (two) in f() is calculated and the last result is returned</em><br>
|
||||
<code class="green">5</code><br>
|
||||
<code>>>></code> <code class="blue">x</code> <em class="gray">// The <code>x</code> variable was not defined in the main context before the f() invocation. It appears in the main context by cloning the <code>@x</code> variable, local to f() after its termnation.</em><br>
|
||||
<code class="green">3</code></p>
|
||||
</div>
|
||||
<div class="admonitionblock note">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<i class="fa icon-note" title="Note"></i>
|
||||
</td>
|
||||
<td class="content">
|
||||
The clone modifier <code class="blue">@</code> does not make a variable a reference variable, as the ampersand character <code>&</code> does in languages such as C and C++.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="admonitionblock important">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<i class="fa icon-important" title="Important"></i>
|
||||
</td>
|
||||
<td class="content">
|
||||
<div class="paragraph">
|
||||
<p>The clone modifier can also be used to declare the formal parameters of functions, because they are local variables too.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example</div>
|
||||
<p><code>>>></code> <code class="blue">g = func(@p) {2+@p}</code>
|
||||
g(@p):any{}`
|
||||
<code>>>></code> <code class="blue">g(9)</code>
|
||||
11`
|
||||
<code>>>></code> [blue]`p
|
||||
9</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2204,7 +2461,7 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2024-09-12 06:56:30 +0200
|
||||
Last updated 2024-12-23 07:20:48 +0100
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ func (f *FractionType) String() string {
|
||||
func (f *FractionType) ToString(opt FmtOpt) string {
|
||||
var sb strings.Builder
|
||||
if opt&MultiLine == 0 {
|
||||
sb.WriteString(fmt.Sprintf("%d|%d", f.num, f.den))
|
||||
sb.WriteString(fmt.Sprintf("%d:%d", f.num, f.den))
|
||||
} else {
|
||||
var s, num string
|
||||
if f.num < 0 && opt&TTY == 0 {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
module git.portale-stac.it/go-pkg/expr
|
||||
|
||||
go 1.21.6
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.23.3
|
||||
|
||||
require golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe h1:bWYrKmmfv37uNgXTdwkLSKYiYPJ1yfWmjBnvtMyAYzk=
|
||||
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe/go.mod h1:alTKUpAJ/zbp17qvZwcFNwzufrb5DljMDY4mgJlIHao=
|
||||
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
|
||||
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
@@ -99,7 +99,106 @@ func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
//-------- assign term
|
||||
|
||||
func newOpAssignTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priAssign,
|
||||
evalFunc: evalOpAssign,
|
||||
}
|
||||
}
|
||||
|
||||
func getCollectionItemValue(ctx ExprContext, collectionTerm, keyListTerm *term) (value any, err error) {
|
||||
var collectionValue, keyListValue, keyValue any
|
||||
var keyList *ListType
|
||||
var ok bool
|
||||
|
||||
if collectionValue, err = collectionTerm.compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if keyListValue, err = keyListTerm.compute(ctx); err != nil {
|
||||
return
|
||||
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 {
|
||||
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue))
|
||||
return
|
||||
}
|
||||
if keyValue = (*keyList)[0]; keyValue == nil {
|
||||
err = keyListTerm.Errorf("index/key is nil")
|
||||
return
|
||||
}
|
||||
|
||||
switch collection := collectionValue.(type) {
|
||||
case *ListType:
|
||||
if index, ok := keyValue.(int64); ok {
|
||||
value = (*collection)[index]
|
||||
} else {
|
||||
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue))
|
||||
}
|
||||
case *DictType:
|
||||
value = (*collection)[keyValue]
|
||||
default:
|
||||
err = collectionTerm.Errorf("collection expected")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getAssignValue(ctx ExprContext, leftTerm *term) (value any, err error) {
|
||||
if leftTerm.symbol() == SymIndex {
|
||||
value, err = getCollectionItemValue(ctx, leftTerm.children[0], leftTerm.children[1])
|
||||
} else {
|
||||
value, _ = ctx.GetVar(leftTerm.source())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalOpAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var rightValue, leftValue any
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftTerm := opTerm.children[0]
|
||||
leftSym := leftTerm.symbol()
|
||||
if leftSym != SymVariable && leftSym != SymIndex {
|
||||
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
|
||||
return
|
||||
}
|
||||
|
||||
rightChild := opTerm.children[1]
|
||||
|
||||
if rightValue, err = rightChild.compute(ctx); err == nil {
|
||||
if leftValue, err = getAssignValue(ctx, leftTerm); err == nil {
|
||||
switch opTerm.symbol() {
|
||||
case SymPlusEqual:
|
||||
v, err = sumValues(opTerm, leftValue, rightValue)
|
||||
case SymMinusEqual:
|
||||
v, err = diffValues(opTerm, leftValue, rightValue)
|
||||
case SymStarEqual:
|
||||
v, err = mulValues(opTerm, leftValue, rightValue)
|
||||
case SymSlashEqual:
|
||||
v, err = divValues(opTerm, leftValue, rightValue)
|
||||
case SymPercEqual:
|
||||
v, err = remainderValues(opTerm, leftValue, rightValue)
|
||||
default:
|
||||
err = opTerm.Errorf("unsupported assign operator %q", opTerm.source())
|
||||
}
|
||||
if err == nil {
|
||||
err = assignValue(ctx, leftTerm, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymEqual, newAssignTerm)
|
||||
registerTermConstructor(SymPlusEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymMinusEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymStarEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymSlashEqual, newOpAssignTerm)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-binary.go
|
||||
package expr
|
||||
|
||||
//-------- NOT term
|
||||
|
||||
func newBinNotTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priBinary,
|
||||
evalFunc: evalBinaryNot,
|
||||
}
|
||||
}
|
||||
|
||||
func evalBinaryNot(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var value any
|
||||
|
||||
if value, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger(value) {
|
||||
i, _ := value.(int64)
|
||||
v = ^i
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(value)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//-------- Binary AND term
|
||||
|
||||
func newBinAndTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBinary,
|
||||
evalFunc: evalBinaryAnd,
|
||||
}
|
||||
}
|
||||
|
||||
func evalBinaryAnd(ctx ExprContext, self *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
var leftInt, rightInt int64
|
||||
var lok, rok bool
|
||||
|
||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftInt, lok = leftValue.(int64)
|
||||
rightInt, rok = rightValue.(int64)
|
||||
|
||||
if lok && rok {
|
||||
v = leftInt & rightInt
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//-------- Binary OR term
|
||||
|
||||
func newBinOrTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBinary,
|
||||
evalFunc: evalBinaryOr,
|
||||
}
|
||||
}
|
||||
|
||||
func evalBinaryOr(ctx ExprContext, self *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
var leftInt, rightInt int64
|
||||
var lok, rok bool
|
||||
|
||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftInt, lok = leftValue.(int64)
|
||||
rightInt, rok = rightValue.(int64)
|
||||
|
||||
if lok && rok {
|
||||
v = leftInt | rightInt
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymTilde, newBinNotTerm)
|
||||
registerTermConstructor(SymAmpersand, newBinAndTerm)
|
||||
registerTermConstructor(SymVertBar, newBinOrTerm)
|
||||
}
|
||||
@@ -49,6 +49,7 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
den = -den
|
||||
num = -num
|
||||
}
|
||||
if num != 0 {
|
||||
g := gcd(num, den)
|
||||
num = num / g
|
||||
den = den / g
|
||||
@@ -57,10 +58,14 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
} else {
|
||||
v = &FractionType{num, den}
|
||||
}
|
||||
} else {
|
||||
v = &FractionType{0, den}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymVertBar, newFractionTerm)
|
||||
// registerTermConstructor(SymVertBar, newFractionTerm)
|
||||
registerTermConstructor(SymColon, newFractionTerm)
|
||||
}
|
||||
|
||||
@@ -113,6 +113,9 @@ func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
} else if IsDict(leftValue) {
|
||||
d := leftValue.(*DictType)
|
||||
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
||||
} else {
|
||||
rightChild := opTerm.children[1]
|
||||
err = rightChild.Errorf("invalid index type: %v", (*indexList)[0])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -40,6 +40,10 @@ func evalInsert(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
if opTerm.children[1].symbol() == SymVariable {
|
||||
ctx.UnsafeSetVar(opTerm.children[1].source(), v)
|
||||
}
|
||||
} else if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
leftInt := leftValue.(int64)
|
||||
rightInt := rightValue.(int64)
|
||||
v = leftInt >> rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
@@ -60,6 +64,10 @@ func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
if opTerm.children[0].symbol() == SymVariable {
|
||||
ctx.UnsafeSetVar(opTerm.children[0].source(), v)
|
||||
}
|
||||
} else if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
leftInt := leftValue.(int64)
|
||||
rightInt := rightValue.(int64)
|
||||
v = leftInt << rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
|
||||
+36
-25
@@ -21,13 +21,7 @@ func newMultiplyTerm(tk *Token) (inst *term) {
|
||||
}
|
||||
}
|
||||
|
||||
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func mulValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if IsString(leftValue) && IsInteger(rightValue) {
|
||||
s, _ := leftValue.(string)
|
||||
n, _ := rightValue.(int64)
|
||||
@@ -43,11 +37,21 @@ func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
||||
v = leftInt * rightInt
|
||||
}
|
||||
} else {
|
||||
err = prodTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return mulValues(prodTerm, leftValue, rightValue)
|
||||
}
|
||||
|
||||
//-------- divide term
|
||||
|
||||
func newDivideTerm(tk *Token) (inst *term) {
|
||||
@@ -60,13 +64,7 @@ func newDivideTerm(tk *Token) (inst *term) {
|
||||
}
|
||||
}
|
||||
|
||||
func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||
d := numAsFloat(rightValue)
|
||||
@@ -91,6 +89,16 @@ func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return divValues(opTerm, leftValue, rightValue)
|
||||
}
|
||||
|
||||
//-------- divide as float term
|
||||
|
||||
func newDivideAsFloatTerm(tk *Token) (inst *term) {
|
||||
@@ -131,17 +139,10 @@ func newRemainderTerm(tk *Token) (inst *term) {
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priProduct,
|
||||
evalFunc: evalReminder,
|
||||
evalFunc: evalRemainder,
|
||||
}
|
||||
}
|
||||
|
||||
func evalReminder(ctx ExprContext, ramainderTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = ramainderTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func remainderValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
rightInt, _ := rightValue.(int64)
|
||||
if rightInt == 0 {
|
||||
@@ -151,11 +152,21 @@ func evalReminder(ctx ExprContext, ramainderTerm *term) (v any, err error) {
|
||||
v = leftInt % rightInt
|
||||
}
|
||||
} else {
|
||||
err = ramainderTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalRemainder(ctx ExprContext, remainderTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = remainderTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return remainderValues(remainderTerm, leftValue, rightValue)
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymStar, newMultiplyTerm)
|
||||
|
||||
+18
-5
@@ -34,12 +34,16 @@ func newRangeTerm(tk *Token) (inst *term) {
|
||||
}
|
||||
}
|
||||
|
||||
func changeColonToRange(t *term) {
|
||||
if t.tk.IsSymbol(SymColon) {
|
||||
t.tk.Sym = SymRange
|
||||
t.evalFunc = evalRange
|
||||
}
|
||||
}
|
||||
|
||||
func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
// if err = self.checkOperands(); err != nil {
|
||||
// return
|
||||
// }
|
||||
if len(opTerm.children) == 0 {
|
||||
leftValue = int64(0)
|
||||
rightValue = int64(-1)
|
||||
@@ -52,7 +56,8 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
if !(IsInteger(leftValue) && IsInteger(rightValue)) {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
// err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = errRangeInvalidSpecification(opTerm)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -63,7 +68,15 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func errRangeInvalidSpecification(t *term) error {
|
||||
return t.Errorf("invalid range specification")
|
||||
}
|
||||
|
||||
func errRangeUnexpectedExpression(t *term) error {
|
||||
return t.Errorf("unexpected range expression")
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymColon, newRangeTerm)
|
||||
registerTermConstructor(SymRange, newRangeTerm)
|
||||
}
|
||||
|
||||
+11
-1
@@ -22,9 +22,19 @@ func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (ma
|
||||
selectedValue, err = caseData.caseExpr.Eval(ctx)
|
||||
match = true
|
||||
} else if filterList, ok := caseData.filterList.value().([]*term); ok {
|
||||
if len(filterList) == 0 && exprValue == int64(caseIndex) {
|
||||
if len(filterList) == 0 {
|
||||
var valueAsInt = int64(0)
|
||||
if b, ok := exprValue.(bool); ok {
|
||||
if !b {
|
||||
valueAsInt = 1
|
||||
}
|
||||
} else if valueAsInt, ok = exprValue.(int64); !ok {
|
||||
return
|
||||
}
|
||||
if valueAsInt == int64(caseIndex) {
|
||||
selectedValue, err = caseData.caseExpr.Eval(ctx)
|
||||
match = true
|
||||
}
|
||||
} else {
|
||||
var caseValue any
|
||||
for _, caseTerm := range filterList {
|
||||
|
||||
+24
-14
@@ -21,13 +21,7 @@ func newPlusTerm(tk *Token) (inst *term) {
|
||||
}
|
||||
}
|
||||
|
||||
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func sumValues(plusTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if (IsString(leftValue) && isNumberString(rightValue)) || (IsString(rightValue) && isNumberString(leftValue)) {
|
||||
v = fmt.Sprintf("%v%v", leftValue, rightValue)
|
||||
} else if IsNumber(leftValue) && IsNumber(rightValue) {
|
||||
@@ -59,12 +53,24 @@ func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
||||
c := leftDict.clone()
|
||||
c.merge(rightDict)
|
||||
v = c
|
||||
} else if isFraction(leftValue) && isFraction(rightValue) {
|
||||
v, err = sumAnyFract(leftValue, rightValue)
|
||||
} else {
|
||||
err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return v, err
|
||||
}
|
||||
|
||||
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return sumValues(plusTerm, leftValue, rightValue)
|
||||
}
|
||||
|
||||
//-------- minus term
|
||||
|
||||
func newMinusTerm(tk *Token) (inst *term) {
|
||||
@@ -77,13 +83,7 @@ func newMinusTerm(tk *Token) (inst *term) {
|
||||
}
|
||||
}
|
||||
|
||||
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func diffValues(minusTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||
v = numAsFloat(leftValue) - numAsFloat(rightValue)
|
||||
@@ -110,6 +110,16 @@ func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return diffValues(minusTerm, leftValue, rightValue)
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymPlus, newPlusTerm)
|
||||
|
||||
@@ -6,10 +6,46 @@ package expr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
//-------- parser
|
||||
|
||||
type parserContext uint16
|
||||
|
||||
const (
|
||||
parserNoFlags = 0
|
||||
allowMultiExpr parserContext = 1 << iota
|
||||
allowVarRef
|
||||
selectorContext
|
||||
listContext // squareContext for list
|
||||
indexContext // squareContext for index
|
||||
allowIndex // allow index in squareContext
|
||||
squareContext = listContext | indexContext // Square parenthesis for list or index
|
||||
)
|
||||
|
||||
func hasFlag[T constraints.Unsigned](set T, singleFlag T) bool {
|
||||
return (set & singleFlag) != 0
|
||||
}
|
||||
|
||||
func addFlags[T constraints.Unsigned](set T, flags T) T {
|
||||
return set | flags
|
||||
}
|
||||
|
||||
func addFlagsCond[T constraints.Unsigned](set T, flags T, cond bool) (newSet T) {
|
||||
if cond {
|
||||
newSet = set | flags
|
||||
} else {
|
||||
newSet = set
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func remFlags[T constraints.Unsigned](set T, flags T) T {
|
||||
return set & (^flags)
|
||||
}
|
||||
|
||||
type parser struct {
|
||||
}
|
||||
|
||||
@@ -24,13 +60,13 @@ func (parser *parser) Next(scanner *scanner) (tk *Token) {
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
|
||||
func (parser *parser) parseFuncCall(scanner *scanner, ctx parserContext, tk *Token) (tree *term, err error) {
|
||||
args := make([]*term, 0, 10)
|
||||
itemExpected := false
|
||||
lastSym := SymUnknown
|
||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||
var subTree *ast
|
||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil {
|
||||
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err != nil {
|
||||
break
|
||||
}
|
||||
prev := scanner.Previous()
|
||||
@@ -66,12 +102,18 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||
tk = parser.Next(scanner)
|
||||
if tk.IsSymbol(SymIdentifier) {
|
||||
param := newTerm(tk)
|
||||
if len(args) > 0 {
|
||||
if pos := paramAlreadyDefined(args, param); pos > 0 {
|
||||
err = tk.Errorf("parameter %q at position %d already defined at position %d", param.source(), len(args)+1, pos)
|
||||
break
|
||||
}
|
||||
}
|
||||
args = append(args, param)
|
||||
tk = parser.Next(scanner)
|
||||
if tk.Sym == SymEqual {
|
||||
var paramExpr *ast
|
||||
defaultParamsStarted = true
|
||||
if paramExpr, err = parser.parseItem(scanner, false, SymComma, SymClosedRound); err != nil {
|
||||
if paramExpr, err = parser.parseItem(scanner, parserNoFlags, SymComma, SymClosedRound); err != nil {
|
||||
break
|
||||
}
|
||||
param.forceChild(paramExpr.root)
|
||||
@@ -94,7 +136,7 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||
if err == nil {
|
||||
tk = parser.Next(scanner)
|
||||
if tk.IsSymbol(SymOpenBrace) {
|
||||
body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace)
|
||||
body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymClosedBrace)
|
||||
} else {
|
||||
err = tk.ErrorExpectedGot("{")
|
||||
}
|
||||
@@ -110,27 +152,43 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) {
|
||||
func paramAlreadyDefined(args []*term, param *term) (position int) {
|
||||
position = 0
|
||||
for i, arg := range args {
|
||||
if arg.source() == param.source() {
|
||||
position = i + 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseList(scanner *scanner, ctx parserContext) (listTerm *term, err error) {
|
||||
r, c := scanner.lastPos()
|
||||
args := make([]*term, 0)
|
||||
lastSym := SymUnknown
|
||||
itemExpected := false
|
||||
itemCtx := remFlags(ctx, allowIndex)
|
||||
for lastSym != SymClosedSquare && lastSym != SymEos {
|
||||
var subTree *ast
|
||||
zeroRequired := scanner.current.Sym == SymColon
|
||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
||||
root := subTree.root
|
||||
var itemTree *ast
|
||||
if itemTree, err = parser.parseItem(scanner, itemCtx, SymComma, SymClosedSquare); err == nil {
|
||||
root := itemTree.root
|
||||
if root != nil {
|
||||
if !parsingIndeces && root.symbol() == SymColon {
|
||||
err = root.Errorf("unexpected range expression")
|
||||
if hasFlag(ctx, allowIndex) && root.symbol() == SymColon {
|
||||
changeColonToRange(root)
|
||||
}
|
||||
if !hasFlag(ctx, allowIndex) && root.symbol() == SymRange {
|
||||
// err = root.Errorf("unexpected range expression")
|
||||
err = errRangeUnexpectedExpression(root)
|
||||
break
|
||||
}
|
||||
args = append(args, root)
|
||||
if parsingIndeces && root.symbol() == SymColon && zeroRequired { //len(root.children) == 0 {
|
||||
if hasFlag(ctx, allowIndex) && root.symbol() == SymRange && zeroRequired { //len(root.children) == 0 {
|
||||
if len(root.children) == 1 {
|
||||
root.children = append(root.children, root.children[0])
|
||||
} else if len(root.children) > 1 {
|
||||
err = root.Errorf("invalid range specification")
|
||||
// err = root.Errorf("invalid range specification")
|
||||
err = errRangeInvalidSpecification(root)
|
||||
break
|
||||
}
|
||||
zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0))
|
||||
@@ -147,26 +205,28 @@ func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarR
|
||||
break
|
||||
}
|
||||
lastSym = scanner.Previous().Sym
|
||||
itemExpected = lastSym == SymComma
|
||||
if itemExpected = lastSym == SymComma; itemExpected {
|
||||
remFlags(ctx, allowIndex)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if lastSym != SymClosedSquare {
|
||||
err = scanner.Previous().ErrorExpectedGot("]")
|
||||
} else {
|
||||
subtree = newListTerm(r, c, args)
|
||||
listTerm = newListTerm(r, c, args)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
||||
func (parser *parser) parseIterDef(scanner *scanner, ctx parserContext) (subtree *term, err error) {
|
||||
tk := scanner.Previous()
|
||||
args := make([]*term, 0)
|
||||
lastSym := SymUnknown
|
||||
itemExpected := false
|
||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||
var subTree *ast
|
||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
|
||||
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err == nil {
|
||||
if subTree.root != nil {
|
||||
args = append(args, subTree.root)
|
||||
} else if itemExpected {
|
||||
@@ -212,7 +272,7 @@ func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
||||
func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subtree *term, err error) {
|
||||
args := make(map[any]*term, 0)
|
||||
lastSym := SymUnknown
|
||||
itemExpected := false
|
||||
@@ -229,7 +289,7 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
|
||||
}
|
||||
break
|
||||
}
|
||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
|
||||
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedBrace); err == nil {
|
||||
if subTree.root != nil {
|
||||
args[key] = subTree.root
|
||||
} else /*if key != nil*/ {
|
||||
@@ -248,15 +308,15 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
|
||||
err = scanner.Previous().ErrorExpectedGot("}")
|
||||
} else {
|
||||
subtree = newDictTerm(args)
|
||||
// subtree = newMapTerm(args)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
|
||||
func (parser *parser) parseSelectorCase(scanner *scanner, ctx parserContext, defaultCase bool) (caseTerm *term, err error) {
|
||||
var filterList *term
|
||||
var caseExpr *ast
|
||||
ctx = remFlags(ctx, allowIndex)
|
||||
tk := parser.Next(scanner)
|
||||
startRow := tk.row
|
||||
startCol := tk.col
|
||||
@@ -265,7 +325,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
|
||||
err = tk.Errorf("case list in default clause")
|
||||
return
|
||||
}
|
||||
if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil {
|
||||
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex)); err != nil {
|
||||
return
|
||||
}
|
||||
tk = parser.Next(scanner)
|
||||
@@ -276,7 +336,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
|
||||
}
|
||||
|
||||
if tk.Sym == SymOpenBrace {
|
||||
if caseExpr, err = parser.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil {
|
||||
if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, SymClosedBrace); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@@ -302,25 +362,28 @@ func addSelectorCase(selectorTerm, caseTerm *term) {
|
||||
caseTerm.parent = selectorTerm
|
||||
}
|
||||
|
||||
func (parser *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
|
||||
func (parser *parser) parseSelector(scanner *scanner, tree *ast, ctx parserContext) (selectorTerm *term, err error) {
|
||||
var caseTerm *term
|
||||
|
||||
ctx = remFlags(ctx, allowIndex)
|
||||
tk := scanner.makeToken(SymSelector, '?')
|
||||
if selectorTerm, err = tree.addToken(tk); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, false); err == nil {
|
||||
if caseTerm, err = parser.parseSelectorCase(scanner, ctx|allowVarRef, false); err == nil {
|
||||
addSelectorCase(selectorTerm, caseTerm)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
||||
return parser.parseGeneral(scanner, false, allowVarRef, termSymbols...)
|
||||
func (parser *parser) parseItem(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
|
||||
return parser.parseGeneral(scanner, ctx|allowVarRef, termSymbols...)
|
||||
}
|
||||
|
||||
func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
||||
return parser.parseGeneral(scanner, true, false, termSymbols...)
|
||||
termSymbols = append(termSymbols, SymEos)
|
||||
return parser.parseGeneral(scanner, allowMultiExpr, termSymbols...)
|
||||
}
|
||||
|
||||
func couldBeACollection(t *term) bool {
|
||||
@@ -331,15 +394,22 @@ func couldBeACollection(t *term) bool {
|
||||
return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable
|
||||
}
|
||||
|
||||
// func areSymbolsOutOfCtx(tk *Token, ctxTerm *term, syms ...Symbol) bool {
|
||||
// var areOut = false
|
||||
// if ctxTerm != nil {
|
||||
// areOut = tk.IsOneOf(syms)
|
||||
// }
|
||||
// return areOut
|
||||
// }
|
||||
func listSubTree(tree *ast, listTerm *term, allowIndeces bool) (root *term, err error) {
|
||||
var tk *Token
|
||||
if allowIndeces {
|
||||
tk = NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
||||
root = newTerm(tk)
|
||||
if err = tree.addTerm(root); err == nil {
|
||||
err = tree.addTerm(listTerm)
|
||||
}
|
||||
} else {
|
||||
root = listTerm
|
||||
err = tree.addTerm(listTerm)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
||||
func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
|
||||
var selectorTerm *term = nil
|
||||
var currentTerm *term = nil
|
||||
var tk *Token
|
||||
@@ -353,7 +423,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
||||
// }
|
||||
|
||||
if tk.Sym == SymSemiColon {
|
||||
if allowForest {
|
||||
if hasFlag(ctx, allowMultiExpr) {
|
||||
tree.ToForest()
|
||||
firstToken = true
|
||||
currentTerm = nil
|
||||
@@ -371,6 +441,9 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
||||
tk.Sym = SymChangeSign
|
||||
} else if tk.Sym == SymPlus {
|
||||
tk.Sym = SymUnchangeSign
|
||||
} else if tk.IsSymbol(SymExclamation) {
|
||||
err = tk.Errorf("postfix opertor %q requires an operand on its left", tk)
|
||||
break
|
||||
}
|
||||
firstToken = false
|
||||
}
|
||||
@@ -378,46 +451,36 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
||||
switch tk.Sym {
|
||||
case SymOpenRound:
|
||||
var subTree *ast
|
||||
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil {
|
||||
if subTree, err = parser.parseGeneral(scanner, ctx, SymClosedRound); err == nil {
|
||||
subTree.root.priority = priValue
|
||||
err = tree.addTerm(newExprTerm(subTree.root))
|
||||
currentTerm = subTree.root
|
||||
}
|
||||
case SymFuncCall:
|
||||
var funcCallTerm *term
|
||||
if funcCallTerm, err = parser.parseFuncCall(scanner, allowVarRef, tk); err == nil {
|
||||
if funcCallTerm, err = parser.parseFuncCall(scanner, ctx, tk); err == nil {
|
||||
err = tree.addTerm(funcCallTerm)
|
||||
currentTerm = funcCallTerm
|
||||
}
|
||||
case SymOpenSquare:
|
||||
var listTerm *term
|
||||
parsingIndeces := couldBeACollection(currentTerm)
|
||||
if listTerm, err = parser.parseList(scanner, parsingIndeces, allowVarRef); err == nil {
|
||||
if parsingIndeces {
|
||||
indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
||||
indexTerm := newTerm(indexTk)
|
||||
if err = tree.addTerm(indexTerm); err == nil {
|
||||
err = tree.addTerm(listTerm)
|
||||
}
|
||||
} else {
|
||||
err = tree.addTerm(listTerm)
|
||||
}
|
||||
currentTerm = listTerm
|
||||
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
|
||||
if listTerm, err = parser.parseList(scanner, newCtx); err == nil {
|
||||
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||
}
|
||||
case SymOpenBrace:
|
||||
if currentTerm != nil && currentTerm.symbol() == SymColon {
|
||||
err = currentTerm.Errorf(`selector-case outside of a selector context`)
|
||||
} else {
|
||||
var mapTerm *term
|
||||
if mapTerm, err = parser.parseDictionary(scanner, allowVarRef); err == nil {
|
||||
if mapTerm, err = parser.parseDictionary(scanner, ctx); err == nil {
|
||||
err = tree.addTerm(mapTerm)
|
||||
currentTerm = mapTerm
|
||||
}
|
||||
}
|
||||
case SymEqual:
|
||||
// if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
||||
case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual:
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
// }
|
||||
firstToken = true
|
||||
case SymFuncDef:
|
||||
var funcDefTerm *term
|
||||
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
|
||||
@@ -426,24 +489,25 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
||||
}
|
||||
case SymDollarRound:
|
||||
var iterDefTerm *term
|
||||
if iterDefTerm, err = parser.parseIterDef(scanner, allowVarRef); err == nil {
|
||||
if iterDefTerm, err = parser.parseIterDef(scanner, ctx); err == nil {
|
||||
err = tree.addTerm(iterDefTerm)
|
||||
currentTerm = iterDefTerm
|
||||
}
|
||||
case SymIdentifier:
|
||||
if tk.source[0] == '@' && !allowVarRef {
|
||||
if tk.source[0] == '@' && !hasFlag(ctx, allowVarRef) {
|
||||
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
||||
} else {
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
}
|
||||
case SymQuestion:
|
||||
if selectorTerm, err = parser.parseSelector(scanner, tree, allowVarRef); err == nil {
|
||||
if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil {
|
||||
currentTerm = selectorTerm
|
||||
addFlags(ctx, selectorContext)
|
||||
}
|
||||
case SymColon, SymDoubleColon:
|
||||
var caseTerm *term
|
||||
if selectorTerm != nil {
|
||||
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
|
||||
if caseTerm, err = parser.parseSelectorCase(scanner, ctx, tk.Sym == SymDoubleColon); err == nil {
|
||||
addSelectorCase(selectorTerm, caseTerm)
|
||||
currentTerm = caseTerm
|
||||
if tk.Sym == SymDoubleColon {
|
||||
@@ -451,80 +515,38 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if hasFlag(ctx, allowIndex) {
|
||||
// tk.Sym = SymRange
|
||||
// }
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
}
|
||||
if tk.IsSymbol(SymColon) {
|
||||
if tk.IsOneOfA(SymColon, SymRange) {
|
||||
// Colon outside a selector term acts like a separator
|
||||
firstToken = true
|
||||
}
|
||||
case SymPlusEqual, SymMinusEqual, SymStarEqual:
|
||||
currentTerm, err = parser.expandOpAssign(scanner, tree, tk, allowVarRef)
|
||||
default:
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
}
|
||||
|
||||
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
|
||||
selectorTerm = nil
|
||||
|
||||
remFlags(ctx, selectorContext)
|
||||
}
|
||||
// lastSym = tk.Sym
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if !tk.IsOneOf(termSymbols) {
|
||||
var symDesc string
|
||||
if tk.IsSymbol(SymError) {
|
||||
symDesc = tk.ErrorText()
|
||||
} else {
|
||||
symDesc = SymToString(tk.Sym)
|
||||
}
|
||||
err = tk.ErrorExpectedGotStringWithPrefix("expected one of", SymListToString(termSymbols, true), symDesc)
|
||||
} else {
|
||||
err = tk.Error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// func checkPrevSymbol(lastSym, wantedSym Symbol, tk *Token) (err error) {
|
||||
// if lastSym != wantedSym {
|
||||
// err = fmt.Errorf(`assign operator (%q) must be preceded by a variable`, tk.source)
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (parser *parser) expandOpAssign(scanner * scanner, tree *ast, tk *Token, allowVarRef bool) (t *term, err error) {
|
||||
var opSym Symbol
|
||||
var opString string
|
||||
|
||||
if tree.root != nil {
|
||||
switch tk.Sym {
|
||||
case SymPlusEqual:
|
||||
opString = "+"
|
||||
opSym = SymPlus
|
||||
case SymMinusEqual:
|
||||
opString = "-"
|
||||
opSym = SymMinus
|
||||
case SymStarEqual:
|
||||
opString = "*"
|
||||
opSym = SymStar
|
||||
default:
|
||||
err = tk.Errorf("unsopported operator %q", tk.source)
|
||||
return
|
||||
}
|
||||
leftExpr := tree.root.Clone()
|
||||
leftExpr.setParent(nil)
|
||||
if t, err = tree.addToken(NewToken(tk.row, tk.col, SymEqual, "=")); err == nil {
|
||||
t = leftExpr
|
||||
if err = tree.addTerm(leftExpr); err == nil {
|
||||
if t, err = tree.addToken(NewToken(tk.row, tk.col, opSym, opString)); err == nil {
|
||||
|
||||
var subTree *ast
|
||||
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare); err == nil {
|
||||
if scanner.Previous().IsOneOfA(SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare) {
|
||||
if err = scanner.UnreadToken(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
subTree.root.priority = priValue
|
||||
err = tree.addTerm(newExprTerm(subTree.root))
|
||||
t = subTree.root
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = tk.Errorf("left operand of %q must be a variable or a variable expression", tk)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+4
-2
@@ -40,7 +40,7 @@ func DefaultTranslations() map[Symbol]Symbol {
|
||||
SymKwAnd: SymAnd,
|
||||
SymDoubleVertBar: SymOr,
|
||||
SymKwOr: SymOr,
|
||||
SymTilde: SymNot,
|
||||
// SymTilde: SymNot,
|
||||
SymKwNot: SymNot,
|
||||
SymLessGreater: SymNotEqual,
|
||||
}
|
||||
@@ -149,6 +149,8 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
if next, _ := scanner.peek(); next == '*' {
|
||||
scanner.readChar()
|
||||
tk = scanner.fetchBlockComment()
|
||||
} else if next, _ = scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymSlashEqual, ch, next)
|
||||
} else if next == '/' {
|
||||
scanner.readChar()
|
||||
tk = scanner.fetchOnLineComment()
|
||||
@@ -590,7 +592,7 @@ func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
tk = scanner.makeErrorToken(errors.New("missing string termination \""))
|
||||
tk = scanner.makeErrorToken(errors.New(string(termCh)))
|
||||
} else {
|
||||
tk = scanner.makeErrorToken(err)
|
||||
}
|
||||
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// Symbol.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var symbolMap map[Symbol]symbolSpec
|
||||
|
||||
type symbolClass int16
|
||||
|
||||
const (
|
||||
symClassOperator symbolClass = iota
|
||||
symClassPostOp
|
||||
symClassIdentifier
|
||||
symClassDelimiter
|
||||
symClassParenthesis
|
||||
symClassDeclaration
|
||||
symClassValue
|
||||
symClassOther
|
||||
)
|
||||
|
||||
type symbolSpec struct {
|
||||
repr string
|
||||
kind symbolClass
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
symbolMap = map[Symbol]symbolSpec{
|
||||
SymUnknown: {"<unknown>", symClassOther}, // -1: Unknown symbol
|
||||
SymNone: {"<null>", symClassOther}, // 0: Null value for variable of type symbol
|
||||
SymError: {"<error>", symClassOther}, // 1: Error reading from stream
|
||||
SymEos: {"<eos>", symClassOther}, // 2: End of stream
|
||||
SymMinus: {"-", symClassOperator}, // 3: '-'
|
||||
SymMinusEqual: {"-=", symClassOperator}, // 4: '-='
|
||||
SymDoubleMinus: {"--", symClassOperator}, // 5: '--'
|
||||
SymPlus: {"+", symClassOperator}, // 6: '+'
|
||||
SymPlusEqual: {"+=", symClassOperator}, // 7: '+='
|
||||
SymDoublePlus: {"++", symClassOperator}, // 8: '++'
|
||||
SymStar: {"*", symClassOperator}, // 9: '*'
|
||||
SymDoubleStar: {"**", symClassOperator}, // 10: '**'
|
||||
SymSlash: {"/", symClassOperator}, // 11: '/'
|
||||
SymBackSlash: {"\\", symClassOperator}, // 12: '\'
|
||||
SymVertBar: {"|", symClassOperator}, // 13: '|'
|
||||
SymDoubleVertBar: {"||", symClassOperator}, // 14: '||'
|
||||
SymComma: {",", symClassOperator}, // 15: ','
|
||||
SymColon: {":", symClassOperator}, // 16: ':'
|
||||
SymSemiColon: {";", symClassOperator}, // 17: ';'
|
||||
SymDot: {".", symClassOperator}, // 18: '.'
|
||||
SymDotSlash: {"./", symClassOperator}, // 19: './'
|
||||
SymQuote: {"'", symClassDelimiter}, // 20: '\''
|
||||
SymDoubleQuote: {"\"", symClassDelimiter}, // 21: '"'
|
||||
SymBackTick: {"`", symClassOperator}, // 22: '`'
|
||||
SymExclamation: {"!", symClassPostOp}, // 23: '!'
|
||||
SymQuestion: {"?", symClassOperator}, // 24: '?'
|
||||
SymAmpersand: {"&", symClassOperator}, // 25: '&'
|
||||
SymDoubleAmpersand: {"&&", symClassOperator}, // 26: '&&'
|
||||
SymPercent: {"%", symClassOperator}, // 27: '%'
|
||||
SymAt: {"@", symClassOperator}, // 28: '@'
|
||||
SymUndescore: {"_", symClassOperator}, // 29: '_'
|
||||
SymEqual: {"=", symClassOperator}, // 30: '='
|
||||
SymDoubleEqual: {"==", symClassOperator}, // 31: '=='
|
||||
SymLess: {"<", symClassOperator}, // 32: '<'
|
||||
SymLessOrEqual: {"<=", symClassOperator}, // 33: '<='
|
||||
SymGreater: {">", symClassOperator}, // 34: '>'
|
||||
SymGreaterOrEqual: {">=", symClassOperator}, // 35: '>='
|
||||
SymLessGreater: {"<>", symClassOperator}, // 36: '<>'
|
||||
SymNotEqual: {"!=", symClassOperator}, // 37: '!='
|
||||
SymDollar: {"$", symClassOperator}, // 38: '$'
|
||||
SymHash: {"#", symClassOperator}, // 39: '#'
|
||||
SymOpenRound: {"(", symClassParenthesis}, // 40: '('
|
||||
SymClosedRound: {")", symClassParenthesis}, // 41: ')'
|
||||
SymOpenSquare: {"[", symClassParenthesis}, // 42: '['
|
||||
SymClosedSquare: {"]", symClassParenthesis}, // 43: ']'
|
||||
SymOpenBrace: {"{", symClassParenthesis}, // 44: '{'
|
||||
SymClosedBrace: {"}", symClassParenthesis}, // 45: '}'
|
||||
SymTilde: {"~", symClassOperator}, // 46: '~'
|
||||
SymDoubleQuestion: {"??", symClassOperator}, // 47: '??'
|
||||
SymQuestionEqual: {"?=", symClassOperator}, // 48: '?='
|
||||
SymQuestionExclam: {"?!", symClassOperator}, // 49: '?!'
|
||||
SymDoubleAt: {"@@", symClassOperator}, // 50: '@@'
|
||||
SymDoubleColon: {"::", symClassOperator}, // 51: '::'
|
||||
SymInsert: {">>", symClassOperator}, // 52: '>>'
|
||||
SymAppend: {"<<", symClassOperator}, // 53: '<<'
|
||||
SymCaret: {"^", symClassOperator}, // 54: '^'
|
||||
SymDollarRound: {"$(", symClassOperator}, // 55: '$('
|
||||
SymOpenClosedRound: {"()", symClassPostOp}, // 56: '()'
|
||||
SymDoubleDollar: {"$$", symClassOperator}, // 57: '$$'
|
||||
SymDoubleDot: {"..", symClassOperator}, // 58: '..'
|
||||
SymTripleDot: {"...", symClassOperator}, // 59: '...'
|
||||
SymStarEqual: {"*=", symClassOperator}, // 60: '*='
|
||||
SymSlashEqual: {"/=", symClassOperator}, // 61: '/='
|
||||
SymPercEqual: {"%=", symClassOperator}, // 62: '%='
|
||||
// SymChangeSign
|
||||
// SymUnchangeSign
|
||||
// SymIdentifier
|
||||
// SymBool
|
||||
// SymInteger
|
||||
// SymVariable
|
||||
// SymFloat
|
||||
// SymFraction
|
||||
// SymString
|
||||
// SymIterator
|
||||
// SymOr: "or",
|
||||
// SymAnd: "and",
|
||||
// SymNot: "not",
|
||||
// SymComment
|
||||
// SymFuncCall
|
||||
// SymFuncDef
|
||||
// SymList
|
||||
// SymDict
|
||||
// SymIndex
|
||||
// SymExpression
|
||||
// SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||
// SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||
// // SymOpenComment // 0: '/*'
|
||||
// // SymClosedComment // 0: '*/'
|
||||
// // SymOneLineComment // 0: '//'
|
||||
// keywordBase
|
||||
SymKwAnd: {"and", symClassOperator},
|
||||
SymKwNot: {"not", symClassOperator},
|
||||
SymKwOr: {"or", symClassOperator},
|
||||
SymKwBut: {"but", symClassOperator},
|
||||
SymKwFunc: {"func(", symClassDeclaration},
|
||||
SymKwBuiltin: {"builtin", symClassOperator},
|
||||
SymKwPlugin: {"plugin", symClassOperator},
|
||||
SymKwIn: {"in", symClassOperator},
|
||||
SymKwInclude: {"include", symClassOperator},
|
||||
SymKwNil: {"nil", symClassValue},
|
||||
SymKwUnset: {"unset", symClassOperator},
|
||||
}
|
||||
}
|
||||
|
||||
func SymToString(sym Symbol) string {
|
||||
if s, ok := symbolMap[sym]; ok {
|
||||
return s.repr
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func SymListToString(symList []Symbol, quote bool) string {
|
||||
var sb strings.Builder
|
||||
if len(symList) == 0 {
|
||||
sb.WriteString("<nothing>")
|
||||
} else {
|
||||
for _, sym := range symList {
|
||||
if sb.Len() > 0 {
|
||||
sb.WriteByte(',')
|
||||
sb.WriteByte(' ')
|
||||
}
|
||||
if quote {
|
||||
sb.WriteByte('`')
|
||||
}
|
||||
sb.WriteString(SymToString(sym))
|
||||
if quote {
|
||||
sb.WriteByte('`')
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func StringEndsWithOperator(s string) bool {
|
||||
return endingOperator(s) != SymNone
|
||||
}
|
||||
|
||||
func endingOperator(s string) (sym Symbol) {
|
||||
sym = SymNone
|
||||
lower := strings.ToLower(s)
|
||||
for symbol, spec := range symbolMap {
|
||||
if spec.kind == symClassOperator && strings.HasSuffix(lower, spec.repr) {
|
||||
sym = symbol
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -69,6 +69,8 @@ const (
|
||||
SymDoubleDot // 58: '..'
|
||||
SymTripleDot // 59: '...'
|
||||
SymStarEqual // 60: '*='
|
||||
SymSlashEqual // 61: '/='
|
||||
SymPercEqual // 62: '%='
|
||||
SymChangeSign
|
||||
SymUnchangeSign
|
||||
SymIdentifier
|
||||
@@ -88,6 +90,7 @@ const (
|
||||
SymList
|
||||
SymDict
|
||||
SymIndex
|
||||
SymRange // [index : index]
|
||||
SymExpression
|
||||
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||
|
||||
@@ -30,9 +30,9 @@ func TestFuncBase(t *testing.T) {
|
||||
/* 16 */ {`isString("3" + 1)`, true, nil},
|
||||
/* 17 */ {`isList(["3", 1])`, true, nil},
|
||||
/* 18 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
||||
/* 19 */ {`isFract(1|3)`, true, nil},
|
||||
/* 20 */ {`isFract(3|1)`, false, nil},
|
||||
/* 21 */ {`isRational(3|1)`, true, nil},
|
||||
/* 19 */ {`isFract(1:3)`, true, nil},
|
||||
/* 20 */ {`isFract(3:1)`, false, nil},
|
||||
/* 21 */ {`isRational(3:1)`, true, nil},
|
||||
/* 22 */ {`fract("2.2(3)")`, newFraction(67, 30), nil},
|
||||
/* 23 */ {`fract("1.21(3)")`, newFraction(91, 75), nil},
|
||||
/* 24 */ {`fract(1.21(3))`, newFraction(91, 75), nil},
|
||||
@@ -41,26 +41,26 @@ func TestFuncBase(t *testing.T) {
|
||||
/* 27 */ {`dec(2.0)`, float64(2), nil},
|
||||
/* 28 */ {`dec("2.0")`, float64(2), nil},
|
||||
/* 29 */ {`dec(true)`, float64(1), nil},
|
||||
/* 30 */ {`dec(true")`, nil, `[1:11] missing string termination "`},
|
||||
/* 30 */ {`dec(true")`, nil, "[1:11] expected one of `,`, `)`, got `\"`"},
|
||||
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`},
|
||||
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`},
|
||||
/* 33 */ {`isBool(false)`, true, nil},
|
||||
/* 34 */ {`fract(1|2)`, newFraction(1, 2), nil},
|
||||
/* 34 */ {`fract(1:2)`, newFraction(1, 2), nil},
|
||||
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
|
||||
/* 36 */ {`bool(2)`, true, nil},
|
||||
/* 37 */ {`bool(1|2)`, true, nil},
|
||||
/* 37 */ {`bool(1:2)`, true, nil},
|
||||
/* 38 */ {`bool(1.0)`, true, nil},
|
||||
/* 39 */ {`bool("1")`, true, nil},
|
||||
/* 40 */ {`bool(false)`, false, nil},
|
||||
/* 41 */ {`bool([1])`, nil, `bool(): can't convert list to bool`},
|
||||
/* 42 */ {`dec(false)`, float64(0), nil},
|
||||
/* 43 */ {`dec(1|2)`, float64(0.5), nil},
|
||||
/* 43 */ {`dec(1:2)`, float64(0.5), nil},
|
||||
/* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
|
||||
// /* 45 */ {`string([1])`, nil, `string(): can't convert list to string`},
|
||||
}
|
||||
|
||||
t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
//runTestSuiteSpec(t, section, inputs, 10)
|
||||
// runTestSuiteSpec(t, section, inputs, 30)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
+8
-7
@@ -17,18 +17,19 @@ func TestExpr(t *testing.T) {
|
||||
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
|
||||
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
||||
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
||||
/* 6 */ {`a=3; a+=1`, int64(4), nil},
|
||||
/* 7 */ {`a=3; a-=1`, int64(2), nil},
|
||||
/* 8 */ {`a=3; a*=2`, int64(6), nil},
|
||||
/* 6 */ {`a=3; a+=1; a`, int64(4), nil},
|
||||
/* 7 */ {`a=3; a-=1; a`, int64(2), nil},
|
||||
/* 8 */ {`a=3; a*=2; a`, int64(6), nil},
|
||||
/* 9 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil},
|
||||
/* 10 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil},
|
||||
/* 11 */ {`a=3; a/=2`, nil, `[1:8] left operand of "=" must be a variable or a collection's item`},
|
||||
/* 12 */ {`*=2`, nil, `[1:2] left operand of "*=" must be a variable or a variable expression`},
|
||||
/* 11 */ {`a=3; a/=2`, int64(1), nil},
|
||||
/* 12 */ {`*=2`, nil, `[1:2] infix operator "*=" requires two non-nil operands, got 1`},
|
||||
/* 13 */ {`a=3; a*=2+1; a`, int64(9), nil},
|
||||
/* 14 */ {`a=3; (a*=2)+1; a`, int64(6), nil},
|
||||
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
|
||||
/* 16 */ {`v=[2]; a=1; v[a-=1]=5; v[0]`, int64(5), nil},
|
||||
/* 17 */ {`
|
||||
/* 17 */ {`true ? {"a"} :: {"b"}`, "a", nil},
|
||||
/* 18 */ {`
|
||||
ds={
|
||||
"init":func(@end){@current=0 but true},
|
||||
//"current":func(){current},
|
||||
@@ -43,6 +44,6 @@ func TestExpr(t *testing.T) {
|
||||
}
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 17)
|
||||
// runTestSuiteSpec(t, section, inputs, 6, 7, 8, 9)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
+21
-17
@@ -11,35 +11,39 @@ import (
|
||||
func TestFractionsParser(t *testing.T) {
|
||||
section := "Fraction"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`1|2`, newFraction(1, 2), nil},
|
||||
/* 2 */ {`1|2 + 1`, newFraction(3, 2), nil},
|
||||
/* 3 */ {`1|2 - 1`, newFraction(-1, 2), nil},
|
||||
/* 4 */ {`1|2 * 1`, newFraction(1, 2), nil},
|
||||
/* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil},
|
||||
/* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil},
|
||||
/* 7 */ {`1|"5"`, nil, `denominator must be integer, got string (5)`},
|
||||
/* 8 */ {`"1"|5`, nil, `numerator must be integer, got string (1)`},
|
||||
/* 9 */ {`1|+5`, nil, `[1:3] infix operator "|" requires two non-nil operands, got 1`},
|
||||
/* 10 */ {`1|(-2)`, newFraction(-1, 2), nil},
|
||||
/* 11 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil},
|
||||
/* 12 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil},
|
||||
/* 13 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil},
|
||||
/* 14 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil},
|
||||
/* 15 */ {`1|0`, nil, `division by zero`},
|
||||
/* 1 */ {`1:2`, newFraction(1, 2), nil},
|
||||
/* 2 */ {`1:2 + 1`, newFraction(3, 2), nil},
|
||||
/* 3 */ {`1:2 - 1`, newFraction(-1, 2), nil},
|
||||
/* 4 */ {`1:2 * 1`, newFraction(1, 2), nil},
|
||||
/* 5 */ {`1:2 * 2:3`, newFraction(2, 6), nil},
|
||||
/* 6 */ {`1:2 / 2:3`, newFraction(3, 4), nil},
|
||||
/* 7 */ {`1:"5"`, nil, `denominator must be integer, got string (5)`},
|
||||
/* 8 */ {`"1":5`, nil, `numerator must be integer, got string (1)`},
|
||||
/* 9 */ {`1:+5`, newFraction(1, 5), nil},
|
||||
/* 10 */ {`1:(-2)`, newFraction(-1, 2), nil},
|
||||
/* 11 */ {`builtin "math.arith"; add(1:2, 2:3)`, newFraction(7, 6), nil},
|
||||
/* 12 */ {`builtin "math.arith"; add(1:2, 1.0, 2)`, float64(3.5), nil},
|
||||
/* 13 */ {`builtin "math.arith"; mul(1:2, 2:3)`, newFraction(2, 6), nil},
|
||||
/* 14 */ {`builtin "math.arith"; mul(1:2, 1.0, 2)`, float64(1.0), nil},
|
||||
/* 15 */ {`1:0`, nil, `division by zero`},
|
||||
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
|
||||
/* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
|
||||
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
|
||||
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
|
||||
/* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
|
||||
/* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`},
|
||||
/* 22 */ {`string(1|2)`, "1|2", nil},
|
||||
/* 22 */ {`string(1:2)`, "1:2", nil},
|
||||
/* 23 */ {`1+1:2+0.5`, float64(2), nil},
|
||||
/* 24 */ {`1:(2-2)`, nil, `division by zero`},
|
||||
/* 25 */ {`[0,1][1-1]:1`, newFraction(0, 1), nil},
|
||||
}
|
||||
// runTestSuiteSpec(t, section, inputs, 25)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
func TestFractionToStringSimple(t *testing.T) {
|
||||
source := newFraction(1, 2)
|
||||
want := "1|2"
|
||||
want := "1:2"
|
||||
got := source.ToString(0)
|
||||
if got != want {
|
||||
t.Errorf(`(1,2) -> result = %v [%T], want = %v [%T]`, got, got, want, want)
|
||||
|
||||
@@ -35,6 +35,7 @@ func TestFuncs(t *testing.T) {
|
||||
/* 22 */ {`f=func(a,b){a*2+b}; f(b=2,a=1)`, int64(4), nil},
|
||||
/* 23 */ {`f=func(a=10,b=10){a*2+b}; f(b=1)`, int64(21), nil},
|
||||
/* 24 */ {`f=func(a,b){a*2+b}; f(a=1,2)`, nil, `f(): positional param nr 2 not allowed after named params`},
|
||||
/* 25 */ {`f=func(x,x){x}`, nil, `[1:11] parameter "x" at position 2 already defined at position 1`},
|
||||
// /* 20 */ {`a=[func(){3}]; a[0]()`, int64(3), nil},
|
||||
// /* 20 */ {`m={}; m["f"]=func(){3}; m["f"]()`, int64(3), nil},
|
||||
// /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
|
||||
|
||||
+4
-2
@@ -16,11 +16,13 @@ func TestCollections(t *testing.T) {
|
||||
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
||||
/* 4 */ {`"abcdef"[:]`, "abcdef", nil},
|
||||
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||
/* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`},
|
||||
/* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] invalid range specification`},
|
||||
/* 6 */ {`"abcdef"[((1>0)?{1}:{0}):3]`, "bc", nil},
|
||||
/* 7 */ {`"abcdef"[[0,1][0]:1]`, "a", nil},
|
||||
}
|
||||
|
||||
t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
// parserTestSpec(t, section, inputs, 5)
|
||||
// runTestSuiteSpec(t, section, inputs, 5)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
+2
-2
@@ -39,9 +39,9 @@ func TestListParser(t *testing.T) {
|
||||
/* 25 */ {`["a","b","c","d"][1,1]`, nil, `[1:19] one index only is allowed`},
|
||||
/* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||
/* 27 */ {`["a", "b", "c"] << ;`, nil, `[1:18] infix operator "<<" requires two non-nil operands, got 1`},
|
||||
/* 28 */ {`2 << 3;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator "<<"`},
|
||||
/* 28 */ {`2 << 3;`, int64(16), nil},
|
||||
/* 29 */ {`but >> ["a", "b", "c"]`, nil, `[1:6] infix operator ">>" requires two non-nil operands, got 0`},
|
||||
/* 30 */ {`2 >> 3;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator ">>"`},
|
||||
/* 30 */ {`2 >> 3;`, int64(0), nil},
|
||||
/* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||
/* 33 */ {`a=[1,2]; 5>>a`, newListA(int64(5), int64(1), int64(2)), nil},
|
||||
/* 34 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
|
||||
|
||||
+12
-14
@@ -77,7 +77,7 @@ func TestGeneralParser(t *testing.T) {
|
||||
/* 63 */ {`"1.5" == `, nil, `[1:8] infix operator "==" requires two non-nil operands, got 1`},
|
||||
/* 64 */ {`"1.5" != `, nil, `[1:8] infix operator "!=" requires two non-nil operands, got 1`},
|
||||
/* 65 */ {"+1.5", float64(1.5), nil},
|
||||
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one not nil operand`},
|
||||
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one non-nil operand`},
|
||||
/* 67 */ {"4 / 0", nil, `division by zero`},
|
||||
/* 68 */ {"4.0 / 0", nil, `division by zero`},
|
||||
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
|
||||
@@ -94,10 +94,10 @@ func TestGeneralParser(t *testing.T) {
|
||||
/* 80 */ {`5 % 2.0`, nil, `[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},
|
||||
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
||||
/* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
|
||||
/* 84 */ {`~ 2 > 1`, false, nil},
|
||||
/* 85 */ {`~ true && true`, false, nil},
|
||||
/* 86 */ {`~ false || true`, true, nil},
|
||||
/* 83 */ {`"a" < "b" AND NOT 2 == 1`, true, nil},
|
||||
/* 84 */ {`NOT 2 > 1`, false, nil},
|
||||
/* 85 */ {`nOT true && true`, false, nil},
|
||||
/* 86 */ {`NOT false || true`, true, nil},
|
||||
/* 87 */ {`false but true`, true, nil},
|
||||
/* 88 */ {`2+3 but 5*2`, int64(10), nil},
|
||||
/* 89 */ {`x=2`, int64(2), nil},
|
||||
@@ -132,15 +132,13 @@ func TestGeneralParser(t *testing.T) {
|
||||
/* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
|
||||
/* 119 */ {`{}`, &DictType{}, nil},
|
||||
/* 120 */ {`v=10; v++; v`, int64(11), nil},
|
||||
/* 121 */ {`1+1|2+0.5`, float64(2), nil},
|
||||
/* 122 */ {`1.2()`, newFraction(6, 5), nil},
|
||||
/* 123 */ {`1|(2-2)`, nil, `division by zero`},
|
||||
/* 124 */ {`x="abc"; x ?! #x`, int64(3), nil},
|
||||
/* 125 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
|
||||
/* 126 */ {`x ?! (x+1)`, nil, nil},
|
||||
/* 127 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
|
||||
/* 128 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
|
||||
/* 129 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
|
||||
/* 121 */ {`1.2()`, newFraction(6, 5), nil},
|
||||
/* 122 */ {`x="abc"; x ?! #x`, int64(3), nil},
|
||||
/* 123 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
|
||||
/* 124 */ {`x ?! (x+1)`, nil, nil},
|
||||
/* 125 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
|
||||
/* 126 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
|
||||
/* 127 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
@@ -21,9 +21,9 @@ func TestRelational(t *testing.T) {
|
||||
/* 8 */ {`true != false`, true, nil},
|
||||
/* 9 */ {`1.0 != 3.0-2`, false, nil},
|
||||
/* 10 */ {`[1,2] != [2,1]`, true, nil},
|
||||
/* 11 */ {`1|2 == 1|3`, false, nil},
|
||||
/* 12 */ {`1|2 != 1|3`, true, nil},
|
||||
/* 13 */ {`1|2 == 4|8`, true, nil},
|
||||
/* 11 */ {`1:2 == 1:3`, false, nil},
|
||||
/* 12 */ {`1:2 != 1:3`, true, nil},
|
||||
/* 13 */ {`1:2 == 4:8`, true, nil},
|
||||
/* 14 */ {`1 < 8`, true, nil},
|
||||
/* 15 */ {`1 <= 8`, true, nil},
|
||||
/* 16 */ {`"a" < "b"`, true, nil},
|
||||
@@ -32,10 +32,10 @@ func TestRelational(t *testing.T) {
|
||||
/* 19 */ {`1.0 <= 8`, true, nil},
|
||||
/* 20 */ {`1.0 <= 1.0`, true, nil},
|
||||
/* 21 */ {`1.0 == 1`, true, nil},
|
||||
/* 22 */ {`1|2 < 1|3`, false, nil},
|
||||
/* 23 */ {`1|2 <= 1|3`, false, nil},
|
||||
/* 24 */ {`1|2 > 1|3`, true, nil},
|
||||
/* 25 */ {`1|2 >= 1|3`, true, nil},
|
||||
/* 22 */ {`1:2 < 1:3`, false, nil},
|
||||
/* 23 */ {`1:2 <= 1:3`, false, nil},
|
||||
/* 24 */ {`1:2 > 1:3`, true, nil},
|
||||
/* 25 */ {`1:2 >= 1:3`, true, nil},
|
||||
/* 26 */ {`[1,2,3] > [2]`, true, nil},
|
||||
/* 27 */ {`[1,2,3] > [9]`, false, nil},
|
||||
/* 28 */ {`[1,2,3] >= [6]`, false, nil},
|
||||
|
||||
@@ -19,6 +19,7 @@ const (
|
||||
priAnd
|
||||
priNot
|
||||
priRelational
|
||||
priBinary
|
||||
priSum
|
||||
priProduct
|
||||
priFraction
|
||||
@@ -211,11 +212,11 @@ func (term *term) checkOperands() (err error) {
|
||||
}
|
||||
case posPrefix:
|
||||
if term.children == nil || len(term.children) != 1 || term.children[0] == nil {
|
||||
err = term.tk.Errorf("prefix operator %q requires one not nil operand", term.tk.String())
|
||||
err = term.tk.Errorf("prefix operator %q requires one non-nil operand", term.tk.String())
|
||||
}
|
||||
case posPostfix:
|
||||
if term.children == nil || len(term.children) != 1 || term.anyChildrenNil() {
|
||||
err = term.tk.Errorf("postfix operator %q requires one not nil operand", term.tk.String())
|
||||
err = term.tk.Errorf("postfix operator %q requires one non-nil operand", term.tk.String())
|
||||
}
|
||||
case posMultifix:
|
||||
if term.children == nil || len(term.children) < 3 || term.anyChildrenNil() {
|
||||
|
||||
@@ -93,6 +93,15 @@ func (tk *Token) Error() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (tk *Token) ErrorText() (err string) {
|
||||
if tk.Sym == SymError {
|
||||
if msg, ok := tk.Value.(error); ok {
|
||||
err = msg.Error()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tk *Token) Errors(msg string) (err error) {
|
||||
err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
|
||||
return
|
||||
@@ -104,6 +113,10 @@ func (tk *Token) ErrorExpectedGot(symbol string) (err error) {
|
||||
}
|
||||
|
||||
func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
|
||||
err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, got)
|
||||
return tk.ErrorExpectedGotStringWithPrefix("expected", symbol, got)
|
||||
}
|
||||
|
||||
func (tk *Token) ErrorExpectedGotStringWithPrefix(prefix, symbol, got string) (err error) {
|
||||
err = fmt.Errorf("[%d:%d] %s %s, got `%s`", tk.row, tk.col, prefix, symbol, got)
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user