Compare commits

..

7 Commits

10 changed files with 432 additions and 245 deletions
+2
View File
@@ -6,6 +6,7 @@ package expr
const (
TypeAny = "any"
TypeNil = "nil"
TypeBoolean = "boolean"
TypeFloat = "float"
TypeFraction = "fraction"
@@ -15,6 +16,7 @@ const (
TypeNumber = "number"
TypePair = "pair"
TypeString = "string"
TypeDict = "dict"
TypeListOf = "list-of-"
TypeListOfStrings = "list-of-strings"
)
+139 -74
View File
@@ -6,12 +6,14 @@ package expr
import (
"io"
"slices"
)
type dataCursor struct {
ds map[string]Functor
ctx ExprContext
initState bool // true if no item has prodiced yet (this replace di initial Next() call in the contructor)
initState bool // true if no item has produced yet (this replace di initial Next() call in the contructor)
// cursorValid bool // true if resource is nil or if clean has not yet been called
index int
count int
current any
@@ -26,6 +28,7 @@ func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *da
dc = &dataCursor{
ds: ds,
initState: true,
// cursorValid: true,
index: -1,
count: 0,
current: nil,
@@ -36,7 +39,6 @@ func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *da
cleanFunc: ds[CleanName],
resetFunc: ds[ResetName],
}
//dc.Next()
return
}
@@ -77,7 +79,7 @@ func (dc *dataCursor) String() string {
}
func (dc *dataCursor) HasOperation(name string) (exists bool) {
exists = name == IndexName
exists = slices.Contains([]string{CleanName, ResetName, CurrentName, IndexName}, name)
if !exists {
f, ok := dc.ds[name]
exists = ok && isFunctor(f)
@@ -88,63 +90,83 @@ func (dc *dataCursor) HasOperation(name string) (exists bool) {
func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any, err error) {
if name == IndexName {
value = int64(dc.Index())
} else if name == CleanName {
err = dc.Clean()
} else if name == ResetName {
err = dc.Reset()
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
if functor == dc.cleanFunc {
value, err = dc.Clean()
} else if functor == dc.resetFunc {
value, err = dc.Reset()
} else {
ctx := cloneContext(dc.ctx)
value, err = functor.InvokeNamed(ctx, name, args)
exportObjects(dc.ctx, ctx)
}
ctx := cloneContext(dc.ctx)
value, err = functor.InvokeNamed(ctx, name, args)
exportObjects(dc.ctx, ctx)
} else {
err = errNoOperation(name)
}
return
}
func (dc *dataCursor) Reset() (success bool, err error) {
// func (dc *dataCursor) Reset() (err error) {
// if dc.resetFunc != nil {
// if dc.resource != nil {
// ctx := cloneContext(dc.ctx)
// actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
// _, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
// exportObjects(dc.ctx, ctx)
// dc.index = -1
// dc.count = 0
// dc.initState = true
// dc.current = nil
// dc.lastErr = nil
// } else {
// err = errInvalidDataSource()
// }
// } else {
// err = errNoOperation(ResetName)
// }
// return
// }
func (dc *dataCursor) Reset() (err error) {
if dc.resetFunc != nil {
if dc.resource != nil {
ctx := cloneContext(dc.ctx)
actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
_, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
exportObjects(dc.ctx, ctx)
dc.index = -1
dc.count = 0
dc.initState = true
dc.current = nil
dc.lastErr = nil
//dc.Next()
} else {
err = errInvalidDataSource()
}
} else {
err = errNoOperation(ResetName)
ctx := cloneContext(dc.ctx)
actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
_, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
exportObjects(dc.ctx, ctx)
}
success = err == nil
dc.index = -1
dc.count = 0
dc.initState = true
dc.current = nil
dc.lastErr = nil
return
}
func (dc *dataCursor) Clean() (success bool, err error) {
func (dc *dataCursor) Clean() (err error) {
if dc.cleanFunc != nil {
if dc.resource != nil {
ctx := cloneContext(dc.ctx)
actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
_, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
// dc.resource = nil
exportObjects(dc.ctx, ctx)
} else {
err = errInvalidDataSource()
}
} else {
err = errNoOperation(CleanName)
ctx := cloneContext(dc.ctx)
actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
_, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
exportObjects(dc.ctx, ctx)
}
success = err == nil
dc.lastErr = io.EOF
return
}
// func (dc *dataCursor) Clean() (err error) {
// if dc.cleanFunc != nil {
// if dc.resource != nil {
// ctx := cloneContext(dc.ctx)
// actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
// _, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
// exportObjects(dc.ctx, ctx)
// } else {
// err = errInvalidDataSource()
// }
// } else {
// err = errNoOperation(CleanName)
// }
// return
// }
func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
dc.init()
@@ -191,46 +213,89 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
return
}
current = dc.current
if dc.resource != nil {
filter := dc.ds[FilterName]
mapper := dc.ds[MapName]
var item any
for item == nil && dc.lastErr == nil {
ctx := cloneContext(dc.ctx)
dc.index++
filter := dc.ds[FilterName]
mapper := dc.ds[MapName]
var item any
for item == nil && dc.lastErr == nil {
ctx := cloneContext(dc.ctx)
dc.index++
actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil {
if item == nil {
dc.lastErr = io.EOF
} else {
accepted := true
if filter != nil {
if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
item = nil
}
}
if accepted {
dc.count++
}
if item != nil && mapper != nil {
item, dc.lastErr = dc.mapItem(mapper, item)
actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil {
if item == nil {
dc.lastErr = io.EOF
} else {
accepted := true
if filter != nil {
if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
item = nil
}
}
if accepted {
dc.count++
}
if item != nil && mapper != nil {
item, dc.lastErr = dc.mapItem(mapper, item)
}
}
exportObjects(dc.ctx, ctx)
}
dc.current = item
if dc.lastErr != nil {
dc.index--
dc.Clean()
}
} else {
dc.lastErr = errInvalidDataSource()
exportObjects(dc.ctx, ctx)
}
dc.current = item
if dc.lastErr != nil {
dc.index--
dc.Clean()
}
return
}
// func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF after the last item
// if dc.initState {
// dc.init()
// } else if err = dc.lastErr; err != nil {
// return
// }
// current = dc.current
// if dc.resource != nil {
// filter := dc.ds[FilterName]
// mapper := dc.ds[MapName]
// var item any
// for item == nil && dc.lastErr == nil {
// ctx := cloneContext(dc.ctx)
// dc.index++
// actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
// if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil {
// if item == nil {
// dc.lastErr = io.EOF
// } else {
// accepted := true
// if filter != nil {
// if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
// item = nil
// }
// }
// if accepted {
// dc.count++
// }
// if item != nil && mapper != nil {
// item, dc.lastErr = dc.mapItem(mapper, item)
// }
// }
// }
// exportObjects(dc.ctx, ctx)
// }
// dc.current = item
// if dc.lastErr != nil {
// dc.index--
// dc.Clean()
// }
// } else {
// dc.lastErr = errInvalidDataSource()
// }
// return
// }
func (dc *dataCursor) Index() int {
return dc.index - 1
}
+111 -71
View File
@@ -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 createt 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 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_.
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.
@@ -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,11 +228,11 @@ _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
@@ -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"]
@@ -321,7 +321,7 @@ Some arithmetic operators can also be used with strings.
| [blue]`*` | _repeat_ | Make _n_ copy of a string | [blue]`"one" * 2` -> _"oneone"_
|===
The items of strings can be accessed using the square `[]` operator.
The charanters in a string can be accessed using the square `[]` operator.
.Item access syntax
====
@@ -340,10 +340,10 @@ The items of strings can be accessed using the square `[]` operator.
`>>>` [blue]`s[1]` [gray]_// char at position 1 (starting from 0)_ +
[green]`"b"`
`>>>` [blue]`s.[-1]` [gray]_// char at position -1, the rightmost one_ +
`>>>` [blue]`s[-1]` [gray]_// char at position -1, the rightmost one_ +
[green]`"d"`
`>>>` [blue]`\#s` [gray]_// number of chars_ +
`>>>` [blue]`#s` [gray]_// number of chars_ +
[gren]`4`
`>>>` [blue]`#"abc"` [gray]_// number of chars_ +
@@ -369,9 +369,9 @@ Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relationa
| [blue]`\<=` | _Less or Equal_ | True if the left value is less than or equal to the right one | [blue]`5 \<= 2` -> _false_ +
[blue]`"b" \<= "b"` -> _true_
| [blue]`>` | _Greater_ | True if the left value is greater than the right one | [blue]`5 > 2` -> _true_ +
[blue]`"a" < "b"` -> _false_
[blue]`"a" > "b"` -> _false_
| [blue]`>=` | _Greater or Equal_ | True if the left value is greater than or equal to the right one | [blue]`5 >= 2` -> _true_ +
[blue]`"b" \<= "b"` -> _true_
[blue]`"b" >= "b"` -> _true_
|===
^(*)^ See also the [blue]`in` operator in the _list_ and _dictionary_ sections.
@@ -388,7 +388,7 @@ Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relationa
| [blue]`AND` / [blue]`&&` | _And_ | True if both left and right values are true | [blue]`false && true` -> _false_ +
[blue]`"a" < "b" AND NOT (2 < 1)` -> _true_
| [blue]`OR` / [blue]`\|\|` | _Or_ | True if at least one of the left and right values integers true| [blue]`false or true` -> _true_ +
| [blue]`OR` / [blue]`\|\|` | _Or_ | True if at least one of the left and right values integers is true| [blue]`false or true` -> _true_ +
[blue]`"a" == "b" OR (2 == 1)` -> _false_
|===
@@ -413,7 +413,7 @@ _Expr_ supports list of mixed-type values, also specified by normal expressions.
====
*_list_* = _empty-list_ | _non-empty-list_ +
_empty-list_ = "**[]**" +
_non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value} "**]**" +
_non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value_} "**]**" +
====
.Examples
@@ -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,38 +459,63 @@ Array's items can be accessed using the index `[]` operator.
*_slice_* = _string-expr_ "**[**" _integer-expr_ "**:**" _integer-expr_ "**]**"
====
.Items of list
`>>>` [blue]`[1,2,3].1` +
.Examples: Getting items from lists
`>>>` [blue]`[1,2,3][1]` +
[green]`2`
`>>>` [blue]`list=[1,2,3]; list.1` +
[green]`2`
`>>>` [blue]`["one","two","three"].1` +
[green]`two`
`>>>` [blue]`list=["one","two","three"]; list.(2-1)` +
[green]`two`
`>>>` [blue]`list.(-1)` +
[green]`three`
`>>>` [blue]`list.(10)` +
[red]`Eval Error: [1:9] index 10 out of bounds`
`>>>` [blue]`#list` +
[green]`3`
`>>>` [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`
`>>>` [blue]`["one","two","three"][1]` +
[green]`two`
`>>>` [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`
.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]`
=== 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.
@@ -497,7 +523,7 @@ Dictionary literals are sequences of pairs separated by comma [blue]`,` enclosed
====
*_dict_* = _empty-dict_ | _non-empty-dict_ +
_empty-dict_ = "**{}**" +
_non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar_ "**:**" _any-value} "**}**" +
_non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar_ "**:**" _any-value_} "**}**" +
====
@@ -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_.
@@ -551,18 +581,18 @@ NOTE: The assign operator [blue]`=` returns the value assigned to the variable.
[green]`1`
`>>>` [blue]`a_b=1+2` +
[green]`1+2`
[green]`3`
`>>>` [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` +
[green]`2`
`>>>` [blue]`_a=2` +
`>>>` [blue]`\_a=2` +
[red]`Parse Error: [1:2] unexpected token "_"`
`>>>` [blue]`1=2` +
@@ -574,12 +604,12 @@ NOTE: The assign operator [blue]`=` returns the value assigned to the variable.
=== [blue]`;` operator
The semicolon operator [blue]`;` is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The value of the latter is the final result.
.Mult-expression syntax
.Multi-expression syntax
====
*_multi-expression_* = _expression_ {"**;**" _expression_ }
====
An expression that contains [blue]`;` is called a _multi-expression_ and each component expressione is called a _sub-expression_.
An expression that contains [blue]`;` is called a _multi-expression_ and each component expression is called a _sub-expression_.
IMPORTANT: Technically [blue]`;` is not treated as a real operator. It acts as a separator in lists of expressions.
@@ -589,7 +619,7 @@ TIP: [blue]`;` can be used to set some variables before the final calculation.
`>>>` [blue]`a=1; b=2; c=3; a+b+c` +
[green]`6`
The value of each sub-expression is stored in the automatica variable _last_.
The value of each sub-expression is stored in the automatic variable _last_.
.Example
`>>>` [blue]`2+3; b=last+10; last` +
@@ -600,9 +630,10 @@ The value of each sub-expression is stored in the automatica variable _last_.
[blue]`but` is an infixed operator. Its operands can be expressions of any type. It evaluates the left expression first, then the right expression. The value of the right expression is the final result.
.Examples
[blue]`5 but 2` +
[green]`2` +
[blue]`x=2*3 but x-1` +
`>>>` [blue]`5 but 2` +
[green]`2`
`>>>` [blue]`x=2*3 but x-1` +
[green]`5`.
[blue]`but` behavior is very similar to [blue]`;`. The only difference is that [blue]`;` is not a true operator and can't be used inside parenthesis [blue]`(` and [blue]`)`.
@@ -610,17 +641,21 @@ The value of each sub-expression is stored in the automatica variable _last_.
=== Assignment operator [blue]`=`
The assignment operator [blue]`=` is used to define variables or to change their value in the evaluation context (see _ExprContext_).
The value on the left side of [blue]`=` must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation.
The value on the left side of [blue]`=` must be a variable identifier or an expression that evalutes to a variable. The value on the right side can be any expression and it becomes the result of the assignment operation.
.Example
`>>>` [blue]`a=15+1`
.Examples
`>>>` [blue]`a=15+1` +
[green]`16`
`>>>` [blue]`L=[1,2,3]; L[1]=5; L` +
[green]`[1, 5, 3]`
=== Selector operator [blue]`? : ::`
The _selector operator_ is very similar to the _switch/case/default_ statement available in many programming languages.
.Selector literal Syntax
====
_selector-operator_ = _select-expression_ "*?*" _selector-case_ { "*:*" _selector-case_ } ["*::*" _default-multi-expression_] +
_selector-case_ = [_match-list_] _case-value_ +
_match-list_ = "*[*" _item_ {"*,*" _items_} "*]*" +
@@ -628,6 +663,7 @@ _item_ = _expression_ +
_case-multi-expression_ = "*{*" _multi-expression_ "*}*" +
_multi-expression_ = _expression_ { "*;*" _expression_ } +
_default-multi-expression_ = _multi-expression_
====
In other words, the selector operator evaluates the _select-expression_ on the left-hand side of the [blue]`?` symbol; it then compares the result obtained with the values listed in the __match-list__'s, from left to right. If the comparision finds a match with a value in a _match-list_, the associated _case-multi-expression_ is evaluted, and its result will be the final result of the selection operation.
@@ -658,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.
@@ -667,8 +703,12 @@ 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)`' +
`>>>` [blue]`var ?? (1+2)` +
[green]`3`
`>>>` [blue]`var` +
@@ -677,7 +717,7 @@ The [blue]`?=` assigns the calculated value of the right expression to the left
`>>>` [blue]`var ?= (1+2)` +
[green]`3`
`>>>` [blue]`var`
`>>>` [blue]`var` +
[green]`3`
NOTE: These operators have a high priority, in particular higher than the operator [blue]`=`.
@@ -739,21 +779,21 @@ The table below shows all supported operators by decreasing priorities.
== Functions
Functions in _Expr_ are very similar to functions available in many programming languages. Actually, _Expr_ supports two types of function, _expr-functions_ and _go-functions_.
Functions in _Expr_ are very similar to functions available in many programming languages. Currently, _Expr_ supports two types of function, _expr-functions_ and _go-functions_.
* _expr-functions_ are defined using _Expr_'s syntax. They can be passed as arguments to other functions and can be returned from functions. Moreover, they bind themselves to the defining context, thus becoming closures.
* _go-functions_ are regular Golang functions callable from _Expr_ expressions. They are defined in Golang source files called _modules_ and compiled within the _Expr_ package. To make Golang functions available in _Expr_ contextes, it is required to _import_ the module in which they are defined.
* _go-functions_ are regular Golang functions callable from _Expr_ expressions. They are defined in Golang source files called _modules_ and compiled within the _Expr_ package. To make Golang functions available in _Expr_ contextes, it is required to activate the builtin module or to load the plugin module in which they are defined.
=== _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.
. Expr's function definition syntax
.Expr's function definition syntax
====
*_function-definition_* = _identifier_ "**=**" "**func(**" [_param-list_] "**)**" "**{**" _multi-expression_ "**}**"
_param_list_ = _required-param-list_ [ "**,**" _optional-param-list_ ]
_required-param-list_ = _identifier_ { "**,**" _identifier_ }
_optional-param-list_ = _optional-parm_ { "**,**" _optional-param_ }
*_function-definition_* = _identifier_ "**=**" "**func(**" [_param-list_] "**)**" "**{**" _multi-expression_ "**}**" +
_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_
====
+158 -91
View File
@@ -567,7 +567,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>
@@ -657,7 +657,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 createt 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 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>
</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 +687,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">&gt;&gt;&gt;</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> &lt;<span class="nb">builtin</span><span class="o">&gt;</span> Import <span class="nb">builtin </span>modules.
@@ -805,12 +805,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 +855,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> &#8594; 1</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1 + 2</code> &#8594; <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> &#8594; 2</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">3 - 1</code> &#8594; <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> &#8594; -2</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1 * 2</code> &#8594; <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> &#8594; 5</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-11 / 2</code> &#8594; <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> &#8594; 1</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 % 2</code> &#8594; <em>1</em></p></td>
</tr>
</tbody>
</table>
@@ -946,31 +946,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> &#8594; 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> &#8594; 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> &#8594; 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> &#8594; 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> &#8594; -0.5</p></td>
</tr>
@@ -1074,7 +1074,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<code class="green">123&#160;&#160;&#160;&#160;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>
@@ -1109,7 +1109,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
</tbody>
</table>
<div class="paragraph">
<p>The items of strings can be accessed using the square <code>[]</code> operator.</p>
<p>The charanters in a string can be accessed using the square <code>[]</code> operator.</p>
</div>
<div class="exampleblock">
<div class="title">Example 4. Item access syntax</div>
@@ -1137,11 +1137,11 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<code class="green">"b"</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">s.[-1]</code> <em class="gray">// char at position -1, the rightmost one</em><br>
<p><code>&gt;&gt;&gt;</code> <code class="blue">s[-1]</code> <em class="gray">// char at position -1, the rightmost one</em><br>
<code class="green">"d"</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">\#s</code> <em class="gray">// number of chars</em><br>
<p><code>&gt;&gt;&gt;</code> <code class="blue">#s</code> <em class="gray">// number of chars</em><br>
<code class="gren">4</code></p>
</div>
<div class="paragraph">
@@ -1208,14 +1208,14 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Greater</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">True if the left value is greater than the right one</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 &gt; 2</code> &#8594; <em>true</em><br>
<code class="blue">"a" &lt; "b"</code> &#8594; <em>false</em></p></td>
<code class="blue">"a" &gt; "b"</code> &#8594; <em>false</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&gt;=</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Greater or Equal</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">True if the left value is greater than or equal to the right one</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 &gt;= 2</code> &#8594; <em>true</em><br>
<code class="blue">"b" &lt;= "b"</code> &#8594; <em>true</em></p></td>
<code class="blue">"b" &gt;= "b"</code> &#8594; <em>true</em></p></td>
</tr>
</tbody>
</table>
@@ -1256,7 +1256,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">OR</code> / <code class="blue">||</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Or</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">True if at least one of the left and right values integers true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">True if at least one of the left and right values integers is true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">false or true</code> &#8594; <em>true</em><br>
<code class="blue">"a" == "b" OR (2 == 1)</code> &#8594; <em>false</em></p></td>
</tr>
@@ -1314,7 +1314,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<div class="paragraph">
<p><strong><em>list</em></strong> = <em>empty-list</em> | <em>non-empty-list</em><br>
<em>empty-list</em> = "<strong>[]</strong>"<br>
<em>non-empty-list</em> = "<strong>[</strong>" <em>any-value</em> {"<strong>,</strong>" _any-value} "<strong>]</strong>"<br></p>
<em>non-empty-list</em> = "<strong>[</strong>" <em>any-value</em> {"<strong>,</strong>" <em>any-value</em>} "<strong>]</strong>"<br></p>
</div>
</div>
</div>
@@ -1393,6 +1393,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> &#8594; <em>true</em><br>
<code class="blue">6 in [1,2,3]</code> &#8594; <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> &#8594; <em>3</em></p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
@@ -1415,35 +1421,11 @@ 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>
<p><code>&gt;&gt;&gt;</code> <code class="blue">[1,2,3].1</code><br>
<div class="title">Examples: Getting items from lists</div>
<p><code>&gt;&gt;&gt;</code> <code class="blue">[1,2,3][1]</code><br>
<code class="green">2</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list=[1,2,3]; list.1</code><br>
<code class="green">2</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">["one","two","three"].1</code><br>
<code class="green">two</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list=["one","two","three"]; list.(2-1)</code><br>
<code class="green">two</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list.(-1)</code><br>
<code class="green">three</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list.(10)</code><br>
<code class="red">Eval Error: [1:9] index 10 out of bounds</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">#list</code><br>
<code class="green">3</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">index=2; ["a", "b", "c", "d"][index]</code><br>
<code class="green">c</code></p>
</div>
@@ -1451,11 +1433,67 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<p><code>&gt;&gt;&gt;</code> <code class="blue">["a", "b", "c", "d"][2:]</code><br>
<code class="green">["c", "d"]</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list=[1,2,3]; list[1]</code><br>
<code class="green">2</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">["one","two","three"][1]</code><br>
<code class="green">two</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list=["one","two","three"]; list[2-1]</code><br>
<code class="green">two</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list[1]="six"; list</code><br>
<code class="green">["one", "six", "three"]</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list[-1]</code><br>
<code class="green">three</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list[10]</code><br>
<code class="red">Eval Error: [1:9] index 10 out of bounds</code></p>
</div>
<div class="paragraph">
<div class="title">Example: Number of elements in a list</div>
<p><code>&gt;&gt;&gt;</code> <code class="blue">#list</code><br>
<code class="green">3</code></p>
</div>
<div class="paragraph">
<div class="title">Examples: Element insertion</div>
<p><code>&gt;&gt;&gt;</code> <code class="blue">"first" &gt;&gt; list</code><br>
<code class="green">["first", "one", "six", "three"]</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">list &lt;&lt; "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>&gt;&gt;&gt;</code> <code class="blue">"six" in list</code><br>
<code class="green">true</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</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>&gt;&gt;&gt;</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>&gt;&gt;&gt;</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>
@@ -1466,7 +1504,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<div class="paragraph">
<p><strong><em>dict</em></strong> = <em>empty-dict</em> | <em>non-empty-dict</em><br>
<em>empty-dict</em> = "<strong>{}</strong>"<br>
<em>non-empty-dict</em> = "<strong>{</strong>" <em>key-scalar</em> "<strong>:</strong>" <em>any-value</em> {"<strong>,</strong>" <em>key-scalar</em> "<strong>:</strong>" _any-value} "<strong>}</strong>"<br></p>
<em>non-empty-dict</em> = "<strong>{</strong>" <em>key-scalar</em> "<strong>:</strong>" <em>any-value</em> {"<strong>,</strong>" <em>key-scalar</em> "<strong>:</strong>" <em>any-value</em>} "<strong>}</strong>"<br></p>
</div>
</div>
</div>
@@ -1514,6 +1552,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> &#8594; <em>true</em><br>
<code class="blue">"six" in {"one":1, "two":2}</code> &#8594; <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> &#8594; <em>3</em></p></td>
</tr>
</tbody>
</table>
<div class="paragraph">
@@ -1537,6 +1581,10 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<p><code>&gt;&gt;&gt;</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>&gt;&gt;&gt;</code> <code class="blue">#d</code><br>
<code class="green">3</code></p>
</div>
</div>
</div>
</div>
@@ -1575,14 +1623,14 @@ The assign operator <code class="blue">=</code> returns the value assigned to th
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">a_b=1+2</code><br>
<code class="green">1+2</code></p>
<code class="green">3</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">a_b</code><br>
<code class="green">3</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</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>&gt;&gt;&gt;</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">
@@ -1590,8 +1638,8 @@ The assign operator <code class="blue">=</code> returns the value assigned to th
<code class="green">2</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue"><em>a=2</code><br>
<code class="red">Parse Error: [1:2] unexpected token "</em>"</code></p>
<p><code>&gt;&gt;&gt;</code> <code class="blue">_a=2</code><br>
<code class="red">Parse Error: [1:2] unexpected token "_"</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">1=2</code><br>
@@ -1608,7 +1656,7 @@ The assign operator <code class="blue">=</code> returns the value assigned to th
<p>The semicolon operator <code class="blue">;</code> is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The value of the latter is the final result.</p>
</div>
<div class="exampleblock">
<div class="title">Example 12. Mult-expression syntax</div>
<div class="title">Example 12. Multi-expression syntax</div>
<div class="content">
<div class="paragraph">
<p><strong><em>multi-expression</em></strong> = <em>expression</em> {"<strong>;</strong>" <em>expression</em> }</p>
@@ -1616,7 +1664,7 @@ The assign operator <code class="blue">=</code> returns the value assigned to th
</div>
</div>
<div class="paragraph">
<p>An expression that contains <code class="blue">;</code> is called a <em>multi-expression</em> and each component expressione is called a <em>sub-expression</em>.</p>
<p>An expression that contains <code class="blue">;</code> is called a <em>multi-expression</em> and each component expression is called a <em>sub-expression</em>.</p>
</div>
<div class="admonitionblock important">
<table>
@@ -1648,7 +1696,7 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
<code class="green">6</code></p>
</div>
<div class="paragraph">
<p>The value of each sub-expression is stored in the automatica variable <em>last</em>.</p>
<p>The value of each sub-expression is stored in the automatic variable <em>last</em>.</p>
</div>
<div class="paragraph">
<div class="title">Example</div>
@@ -1663,9 +1711,11 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
</div>
<div class="paragraph">
<div class="title">Examples</div>
<p><code class="blue">5 but 2</code><br>
<code class="green">2</code><br>
<code class="blue">x=2*3 but x-1</code><br>
<p><code>&gt;&gt;&gt;</code> <code class="blue">5 but 2</code><br>
<code class="green">2</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">x=2*3 but x-1</code><br>
<code class="green">5</code>.</p>
</div>
<div class="paragraph">
@@ -1678,21 +1728,27 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
<p>The assignment operator <code class="blue">=</code> is used to define variables or to change their value in the evaluation context (see <em>ExprContext</em>).</p>
</div>
<div class="paragraph">
<p>The value on the left side of <code class="blue">=</code> must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation.</p>
<p>The value on the left side of <code class="blue">=</code> must be a variable identifier or an expression that evalutes to a variable. The value on the right side can be any expression and it becomes the result of the assignment operation.</p>
</div>
<div class="paragraph">
<div class="title">Example</div>
<p><code>&gt;&gt;&gt;</code> <code class="blue">a=15+1</code>
<div class="title">Examples</div>
<p><code>&gt;&gt;&gt;</code> <code class="blue">a=15+1</code><br>
<code class="green">16</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">L=[1,2,3]; L[1]=5; L</code><br>
<code class="green">[1, 5, 3]</code></p>
</div>
</div>
<div class="sect2">
<h3 id="_selector_operator"><a class="anchor" href="#_selector_operator"></a><a class="link" href="#_selector_operator">4.4. Selector operator <code class="blue">? : ::</code></a></h3>
<div class="paragraph">
<p>The <em>selector operator</em> is very similar to the <em>switch/case/default</em> statement available in many programming languages.</p>
</div>
<div class="exampleblock">
<div class="title">Example 13. Selector literal Syntax</div>
<div class="content">
<div class="paragraph">
<div class="title">Selector literal Syntax</div>
<p><em>selector-operator</em> = <em>select-expression</em> "<strong>?</strong>" <em>selector-case</em> { "<strong>:</strong>" <em>selector-case</em> } ["<strong>::</strong>" <em>default-multi-expression</em>]<br>
<em>selector-case</em> = [<em>match-list</em>] <em>case-value</em><br>
<em>match-list</em> = "<strong>[</strong>" <em>item</em> {"<strong>,</strong>" <em>items</em>} "<strong>]</strong>"<br>
@@ -1701,6 +1757,8 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
<em>multi-expression</em> = <em>expression</em> { "<strong>;</strong>" <em>expression</em> }<br>
<em>default-multi-expression</em> = <em>multi-expression</em></p>
</div>
</div>
</div>
<div class="paragraph">
<p>In other words, the selector operator evaluates the <em>select-expression</em> on the left-hand side of the <code class="blue">?</code> symbol; it then compares the result obtained with the values listed in the <em>match-list</em>'s, from left to right. If the comparision finds a match with a value in a <em>match-list</em>, the associated <em>case-multi-expression</em> is evaluted, and its result will be the final result of the selection operation.</p>
</div>
@@ -1741,9 +1799,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>
@@ -1764,9 +1822,24 @@ 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>&gt;&gt;&gt;</code> <code class="blue">var ?? (1+2)&#8217;<br>
[green]`3</code></p>
<p><code>&gt;&gt;&gt;</code> <code class="blue">var ?? (1+2)</code><br>
<code class="green">3</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">var</code><br>
@@ -1777,7 +1850,7 @@ If the left variable is defined, the right expression is not evaluated at all.
<code class="green">3</code></p>
</div>
<div class="paragraph">
<p><code>&gt;&gt;&gt;</code> <code class="blue">var</code>
<p><code>&gt;&gt;&gt;</code> <code class="blue">var</code><br>
<code class="green">3</code></p>
</div>
<div class="admonitionblock note">
@@ -2106,7 +2179,7 @@ These operators have a high priority, in particular higher than the operator <co
<h2 id="_functions"><a class="anchor" href="#_functions"></a><a class="link" href="#_functions">6. Functions</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p>Functions in <em>Expr</em> are very similar to functions available in many programming languages. Actually, <em>Expr</em> supports two types of function, <em>expr-functions</em> and <em>go-functions</em>.</p>
<p>Functions in <em>Expr</em> are very similar to functions available in many programming languages. Currently, <em>Expr</em> supports two types of function, <em>expr-functions</em> and <em>go-functions</em>.</p>
</div>
<div class="ulist">
<ul>
@@ -2114,7 +2187,7 @@ These operators have a high priority, in particular higher than the operator <co
<p><em>expr-functions</em> are defined using <em>Expr</em>'s syntax. They can be passed as arguments to other functions and can be returned from functions. Moreover, they bind themselves to the defining context, thus becoming closures.</p>
</li>
<li>
<p><em>go-functions</em> are regular Golang functions callable from <em>Expr</em> expressions. They are defined in Golang source files called <em>modules</em> and compiled within the <em>Expr</em> package. To make Golang functions available in <em>Expr</em> contextes, it is required to <em>import</em> the module in which they are defined.</p>
<p><em>go-functions</em> are regular Golang functions callable from <em>Expr</em> expressions. They are defined in Golang source files called <em>modules</em> and compiled within the <em>Expr</em> package. To make Golang functions available in <em>Expr</em> contextes, it is required to activate the builtin module or to load the plugin module in which they are defined.</p>
</li>
</ul>
</div>
@@ -2123,20 +2196,14 @@ These operators have a high priority, in particular higher than the operator <co
<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>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>Expr&#8217;s function definition syntax</p>
</li>
</ol>
</div>
<div class="exampleblock">
<div class="title">Example 14. Expr&#8217;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>"
<em>param_list</em> = <em>required-param-list</em> [ "<strong>,</strong>" <em>optional-param-list</em> ]
<em>required-param-list</em> = <em>identifier</em> { "<strong>,</strong>" <em>identifier</em> }
<em>optional-param-list</em> = <em>optional-parm</em> { "<strong>,</strong>" <em>optional-param</em> }
<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>
<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>
</div>
</div>
@@ -2200,7 +2267,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-06-21 09:06:12 +0200
Last updated 2024-09-14 11:58:38 +0200
</div>
</div>
</body>
+2 -2
View File
@@ -348,8 +348,8 @@ func CallFunctionByArgs(parentCtx ExprContext, name string, args []any) (result
return
}
func CallFunctionByParams(parentCtx ExprContext, name string, params map[string]any) (result any, err error) {
var actualParams map[string]any
func CallFunctionByParams(parentCtx ExprContext, name string, actualParams map[string]any) (result any, err error) {
//var actualParams map[string]any
if info, exists := GetFuncInfo(parentCtx, name); exists {
functor := info.Functor()
ctx := info.AllocContext(parentCtx)
+2
View File
@@ -33,6 +33,8 @@ type Iterator interface {
type ExtIterator interface {
Iterator
Reset() error
Clean() error
HasOperation(name string) bool
CallOperation(name string, args map[string]any) (value any, err error)
}
+9 -3
View File
@@ -99,7 +99,9 @@ func (it *ListIterator) CallOperation(name string, args map[string]any) (v any,
case NextName:
v, err = it.Next()
case ResetName:
v, err = it.Reset()
err = it.Reset()
case CleanName:
err = it.Clean()
case IndexName:
v = int64(it.Index())
case CurrentName:
@@ -147,8 +149,12 @@ func (it *ListIterator) Count() int {
return it.count
}
func (it *ListIterator) Reset() (bool, error) {
func (it *ListIterator) Reset() (error) {
it.index = it.start - it.step
it.count = 0
return true, nil
return nil
}
func (it *ListIterator) Clean() (error) {
return nil
}
+3 -1
View File
@@ -92,6 +92,7 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
if ds != nil {
var dc *dataCursor
dcCtx := ctx.Clone()
if initFunc, exists := ds[InitName]; exists && initFunc != nil {
var args []any
var resource any
@@ -109,9 +110,10 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
if resource, err = initFunc.InvokeNamed(initCtx, InitName, actualParams); err != nil {
return
}
dcCtx := ctx.Clone()
exportObjects(dcCtx, initCtx)
dc = NewDataCursor(dcCtx, ds, resource)
} else {
dc = NewDataCursor(dcCtx, ds, nil)
}
v = dc
+2 -1
View File
@@ -417,6 +417,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
case SymEqual:
// if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
currentTerm, err = tree.addToken(tk)
firstToken = true
// }
case SymFuncDef:
var funcDefTerm *term
@@ -483,7 +484,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
// return
// }
func (parser *parser) expandOpAssign(scanner * scanner, tree *ast, tk *Token, allowVarRef bool) (t *term, err error) {
func (parser *parser) expandOpAssign(scanner *scanner, tree *ast, tk *Token, allowVarRef bool) (t *term, err error) {
var opSym Symbol
var opString string
+4 -2
View File
@@ -17,7 +17,7 @@ func TestIteratorParser(t *testing.T) {
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it++; it.index`, int64(0), nil},
/* 9 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, true, nil},
/* 9 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, nil, nil},
/* 10 */ {`it=$(1,2,3); it++`, int64(1), nil},
/* 11 */ {`it=$(1,2,3); it++; it.reset; it++`, int64(1), nil},
/* 12 */ {`it=$([1,2,3,4],1); it++`, int64(2), nil},
@@ -25,8 +25,10 @@ func TestIteratorParser(t *testing.T) {
/* 14 */ {`it=$([1,2,3,4],1,3,2); it++; it++;`, int64(4), nil},
/* 15 */ {`it=$([1,2,3,4],1,2,2); it++; it++;`, nil, `EOF`},
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
/* 17 */ {`it=$({"next":func(){5}}); it++`, int64(5), nil},
/* 18 */ {`it=$({"next":func(){5}}); it.clean`, nil, nil},
}
// runTestSuiteSpec(t, section, inputs, 11)
//runTestSuiteSpec(t, section, inputs, 18)
runTestSuite(t, section, inputs)
}