Compare commits
26 Commits
e6844ad1e8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d8dc2939b4 | |||
| 6e868b568d | |||
| 73e4ad7b87 | |||
| b86ce6fe62 | |||
| 38316cec0b | |||
| ee7f488ebb | |||
| 02ea20cdb5 | |||
| 20cffbddfa | |||
| 4b2b573420 | |||
| ac5c97bfd3 | |||
| e1c24daac4 | |||
| a62f27b104 | |||
| 1055569dd6 | |||
| 1aea1c14d2 | |||
| 081395be5f | |||
| 35a599b284 | |||
| eda3037855 | |||
| 47c181546a | |||
| a8a5d6aaa6 | |||
| 84b255a51b | |||
| 9efdeffcac | |||
| d34b9d8a48 | |||
| 1bf8015da1 | |||
| 3ccbeb3978 | |||
| 0c719025cd | |||
| 08617378e0 |
@@ -275,6 +275,16 @@ func charFunc(ctx kern.ExprContext, name string, args map[string]any) (result an
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func seqFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
list := kern.NewLinkedList()
|
||||||
|
items := args[kern.ParamValue].([]any)
|
||||||
|
for _, arg := range items {
|
||||||
|
list.PushBack(arg)
|
||||||
|
}
|
||||||
|
result = list
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//// import
|
//// import
|
||||||
|
|
||||||
func ImportBuiltinsFuncs(ctx kern.ExprContext) {
|
func ImportBuiltinsFuncs(ctx kern.ExprContext) {
|
||||||
@@ -318,6 +328,10 @@ func ImportBuiltinsFuncs(ctx kern.ExprContext) {
|
|||||||
ctx.RegisterFunc("char", kern.NewGolangFunctor(charFunc), kern.TypeString, []kern.ExprFuncParam{
|
ctx.RegisterFunc("char", kern.NewGolangFunctor(charFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||||
kern.NewFuncParam(kern.ParamValue),
|
kern.NewFuncParam(kern.ParamValue),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ctx.RegisterFunc("seq", kern.NewGolangFunctor(seqFunc), kern.TypeLinkedList, []kern.ExprFuncParam{
|
||||||
|
kern.NewFuncParamFlag(kern.ParamValue, kern.PfRepeat),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
+230
-45
@@ -1,6 +1,7 @@
|
|||||||
= Expr
|
= Expr
|
||||||
Expressions calculator
|
Expressions calculator
|
||||||
:authors: Celestino Amoroso
|
:authors: Celestino Amoroso
|
||||||
|
:email: celestino.amoroso@gmail.com
|
||||||
:docinfo: shared
|
:docinfo: shared
|
||||||
:encoding: utf-8
|
:encoding: utf-8
|
||||||
:toc: right
|
:toc: right
|
||||||
@@ -16,10 +17,11 @@ Expressions calculator
|
|||||||
:sectlinks:
|
:sectlinks:
|
||||||
:sectanchors:
|
:sectanchors:
|
||||||
:source-highlighter: rouge
|
:source-highlighter: rouge
|
||||||
// :rouge-style: ThankfulEyes
|
// :rouge-style: gruvbox
|
||||||
:rouge-style: gruvbox
|
:rouge-style: manni
|
||||||
// :rouge-style: colorful
|
:stylesdir: /home/share/s3-howto/styles
|
||||||
//:rouge-style: monokay
|
:stylesheet: adoc-colony.css
|
||||||
|
|
||||||
// Workaround to manage double-column in back-tick quotes
|
// Workaround to manage double-column in back-tick quotes
|
||||||
:2c: ::
|
:2c: ::
|
||||||
// Workaround to manage double-plus in back-tick quotes
|
// Workaround to manage double-plus in back-tick quotes
|
||||||
@@ -34,7 +36,7 @@ Expressions calculator
|
|||||||
|
|
||||||
toc::[]
|
toc::[]
|
||||||
|
|
||||||
#TODO: Work in progress (last update on 2026/05/08)#
|
#TODO: Work in progress#
|
||||||
|
|
||||||
== Expr
|
== Expr
|
||||||
_Expr_ is a GO package that can analyze, interpret and calculate expressions.
|
_Expr_ is a GO package that can analyze, interpret and calculate expressions.
|
||||||
@@ -45,18 +47,18 @@ _Expr_ is a GO package that can analyze, interpret and calculate expressions.
|
|||||||
Expressions are texts containing sequences of operations represented by a syntax very similar to that of most programming languages. _Expr_ package provides these macro functions:
|
Expressions are texts containing sequences of operations represented by a syntax very similar to that of most programming languages. _Expr_ package provides these macro functions:
|
||||||
|
|
||||||
* *_Scanner_* -- Its input is a text. It scans expression text characters to produce a flow of logical symbol and related attributes, aka tokens.
|
* *_Scanner_* -- Its input is a text. It scans expression text characters to produce a flow of logical symbol and related attributes, aka tokens.
|
||||||
* *_Parser_* -- Parser input is the token flow coming from the scanner. It analyses the token flow verifyng if it complies with the _Expr_ syntax. If that is the case, the Parser generates the Abstract Syntax Tree (AST). This is tree data structure that represents the components of an expressions and how they are related one each other.
|
* *_Parser_* -- Parser input is the token flow coming from the scanner. It analyses the token flow verifying if it complies with the _Expr_ syntax. If that is the case, the Parser generates the Abstract Syntax Tree (AST). This is tree data structure that represents the components of an expression and how they are related to each other.
|
||||||
* *_Calculator_*. Its input is the AST. It computes the parsed expression contained in the AST and returns the result or an error.
|
* *_Calculator_*. Its input is the AST. It computes the parsed expression contained in the AST and returns the result or an error.
|
||||||
|
|
||||||
image::expression-diagram.png[]
|
image::expression-diagram.png[]
|
||||||
|
|
||||||
==== Variables
|
==== Variables
|
||||||
_Expr_ supports variables. The result of an expression can be stored in a variable and reused in other espressions by simply specifying the name of the variable as an operand.
|
_Expr_ supports variables. The result of an expression can be stored in a variable and reused in other expressions by simply specifying the name of the variable as an operand.
|
||||||
|
|
||||||
==== Multi-expression
|
==== Multi-expression
|
||||||
An input text valid for _Expr_ can contain more than an expression. Expressions are separated by [blue]`;` (semicolon). When an input contains two or more expressions it is called _multi-expression_.
|
An input text valid for _Expr_ can contain more than an expression. Expressions are separated by [blue]`;` (semicolon). When an input contains two or more expressions it is called _multi-expression_.
|
||||||
|
|
||||||
_Expr_ parses and computes each expression of a multi-espression, from the left to the right. If all expressions are computed without errors, it only returns the value of the last, the right most.
|
_Expr_ parses and computes each expression of a multi-expression, from the left to the right. If all expressions are computed without errors, it only returns the value of the last, the right most.
|
||||||
|
|
||||||
The result of each expression of a multi-expression is stored in an automatic variable named _last_. In this way, each expression can refer to the result of the previous one without the need to assign that value to a new dedicated variable.
|
The result of each expression of a multi-expression is stored in an automatic variable named _last_. In this way, each expression can refer to the result of the previous one without the need to assign that value to a new dedicated variable.
|
||||||
|
|
||||||
@@ -74,7 +76,7 @@ Imported functions are registered in the _global context_. When an expression fi
|
|||||||
===== Inspecting contexts
|
===== Inspecting contexts
|
||||||
_Expr_ provides the operator [blue]_$$_ that returns the current context. This can be used to inspect the content of the context, for example to check the value of a variable or to see which functions are currently linked to the context. This operator is primarily intended for debugging purposes.
|
_Expr_ provides the operator [blue]_$$_ that returns the current context. This can be used to inspect the content of the context, for example to check the value of a variable or to see which functions are currently linked to the context. This operator is primarily intended for debugging purposes.
|
||||||
|
|
||||||
An interactive tool could like `dev-expr` (see <<_dev-expr_test_tool>>) can be used to inspect contexts interactively.
|
An interactive tool like `ecli` (see <<_ecli_test_tool>>) can be used to inspect contexts interactively.
|
||||||
|
|
||||||
|
|
||||||
.Example: inspecting contexts
|
.Example: inspecting contexts
|
||||||
@@ -100,26 +102,48 @@ An interactive tool could like `dev-expr` (see <<_dev-expr_test_tool>>) can be u
|
|||||||
[green]`{2sp}}` +
|
[green]`{2sp}}` +
|
||||||
[green]`}`
|
[green]`}`
|
||||||
|
|
||||||
In order to inspect the global context issue the [blue]`$$global` operator.
|
In order to inspect the global context issue the [blue]`$$ global` operation.
|
||||||
|
////
|
||||||
|
.Example: list all functions whose name starts with "str"
|
||||||
|
`>>>` [blue]`builtin "string` [gray]__// most functions in the builtin module *string* have names starting with "str".__ +
|
||||||
|
[green]`1`
|
||||||
|
|
||||||
=== `dev-expr` test tool
|
:dollar: $
|
||||||
Before we begin to describe the syntax of _Expr_, it is worth introducing _dev-expr_ because it will be used to show many examples of expressions.
|
:2dollars: $$
|
||||||
|
|
||||||
`dev-expr` is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, in additon to the automatic verification test suite based on the Go test framework, `dev-expr` provided an important aid for quickly testing of new features during their development.
|
`>>>` [blue]`($(($$global).functions) filter strStartsWith($_, "str"))` +
|
||||||
|
[green]`[` +
|
||||||
|
[green]`{2sp}"strEndsWith",` +
|
||||||
|
[green]`{2sp}"strJoin",` +
|
||||||
|
[green]`{2sp}"strLower",` +
|
||||||
|
[green]`{2sp}"strSplit",` +
|
||||||
|
[green]`{2sp}"strStartsWith",` +
|
||||||
|
[green]`{2sp}"strSub",` +
|
||||||
|
[green]`{2sp}"strTrim",` +
|
||||||
|
[green]`{2sp}"strUpper",` +
|
||||||
|
[green]`{2sp}"string"` +
|
||||||
|
[green]`]`
|
||||||
|
////
|
||||||
|
|
||||||
`dev-expr` can work as a _REPL_, _**R**ead-**E**xecute-**P**rint-**L**oop_, or it can process expression acquired from files or standard input.
|
[[sec_ecli]]
|
||||||
|
=== `ecli` Expression Calculator Interactive Tool
|
||||||
|
Before we begin to describe the syntax of _Expr_, it is worth introducing _ecli_, former _dev-expr_, because it will be used to show many examples of expressions.
|
||||||
|
|
||||||
The program can be downloaded from https://git.portale-stac.it/go-pkg/-/packages/generic/dev-expr/[dev-expr].
|
`ecli` is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, in addition to the automatic verification test suite based on the Go test framework, `ecli` provided an important aid for quickly testing of new features during their development.
|
||||||
|
|
||||||
|
`ecli` can work as a _REPL_, _**R**ead-**E**xecute-**P**rint-**L**oop_, or it can process expression acquired from files or standard input.
|
||||||
|
|
||||||
|
The program can be downloaded from https://git.portale-stac.it/go-pkg/-/packages/generic/ecli/[ecli].
|
||||||
|
|
||||||
Here are some examples of execution.
|
Here are some examples of execution.
|
||||||
|
|
||||||
.Run `dev-expr` in REPL mode and ask for help
|
.Run `ecli` in REPL mode and ask for help
|
||||||
[source,shell]
|
[source,shell]
|
||||||
----
|
----
|
||||||
# Type 'exit' or Ctrl+D to quit the program.
|
# Type 'exit' or Ctrl+D to quit the program.
|
||||||
|
|
||||||
[user]$ ./dev-expr
|
[user]$ ./ecli
|
||||||
dev-expr -- Expressions calculator v1.12.0(build 1),2024/09/14 (celestino.amoroso@portale-stac.it)
|
ecli -- Expressions calculator v1.12.0(build 1),2024/09/14 (celestino.amoroso@portale-stac.it)
|
||||||
Based on the Expr package v0.26.0
|
Based on the Expr package v0.26.0
|
||||||
Type help to get the list of available commands
|
Type help to get the list of available commands
|
||||||
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||||
@@ -155,8 +179,8 @@ dev-expr -- Expressions calculator v1.12.0(build 1),2024/09/14 (celestino.amoros
|
|||||||
.REPL examples
|
.REPL examples
|
||||||
[source,shell]
|
[source,shell]
|
||||||
----
|
----
|
||||||
[user]$ ./dev-expr
|
[user]$ ./ecli
|
||||||
dev-expr -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoroso@portale-stac.it)
|
ecli -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoroso@portale-stac.it)
|
||||||
Based on the Expr package v0.19.0
|
Based on the Expr package v0.19.0
|
||||||
Type help to get the list of available commands
|
Type help to get the list of available commands
|
||||||
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||||
@@ -223,14 +247,20 @@ Value range: *-9223372036854775808* to *9223372036854775807*
|
|||||||
[cols="^1,^2,6,4"]
|
[cols="^1,^2,6,4"]
|
||||||
|===
|
|===
|
||||||
| Symbol | Operation | Description | Examples
|
| Symbol | Operation | Description | Examples
|
||||||
| [blue]`+` | _Sum_ | Add two values | [blue]`-1 + 2` -> _1_
|
| [blue]`+` | _Sum_ | Add two values^(<<note_int_plus_string,1>>)^ | [blue]`-1 + 2` -> _1_
|
||||||
| [blue]`-` | _Subtraction_ | Subtract the right value from the left one | [blue]`3 - 1` -> _2_
|
| [blue]`-` | _Subtraction_ | Subtract the right value from the left one | [blue]`3 - 1` -> _2_
|
||||||
| [blue]`*` | _Product_ | Multiply two values | [blue]`-1 * 2` -> _-2_
|
| [blue]`*` | _Product_ | Multiply two values^(<<note_string_repl,2>>)^ | [blue]`-1 * 2` -> _-2_
|
||||||
| [blue]`/` | _Integer division_ | Divide the left value by the right one^(*)^ | [blue]`-11 / 2` -> _-5_
|
| [blue]`/` | _Integer division_ | Divide the left value by the right one^(<<note_float_division,3>>)^ | [blue]`-11 / 2` -> _-5_
|
||||||
| [blue]`%` | _Modulo_ | Remainder of the integer division | [blue]`5 % 2` -> _1_
|
| [blue]`%` | _Modulo_ | Remainder of the integer division | [blue]`5 % 2` -> _1_
|
||||||
|===
|
|===
|
||||||
|
[[note_int_plus_string]]
|
||||||
|
^(1)^ The sum operator [blue]`+` also supports adding an integer number to a string. In this case, the number is converted to a string and prependend or appended to the string, e.g. `"x" + 48` results in `"x48"`.
|
||||||
|
|
||||||
^(*)^ See also the _float division_ [blue]`./` below.
|
[[note_string_repl]]
|
||||||
|
^(2)^ The product operator also supports multiplying a string by an integer. In this case, the number represents how many times the string has to be repeated in the result, e.g. `"foo" * 3` returns `"foofoofoo"`.
|
||||||
|
|
||||||
|
[[note_float_division]]
|
||||||
|
^(3)^ See also the _float division_ [blue]`./` below.
|
||||||
|
|
||||||
|
|
||||||
==== Floats
|
==== Floats
|
||||||
@@ -268,12 +298,14 @@ _dec-seq_ = _see-integer-literal-syntax_
|
|||||||
[cols="^1,^2,6,4"]
|
[cols="^1,^2,6,4"]
|
||||||
|===
|
|===
|
||||||
| Symbol | Operation | Description | Examples
|
| Symbol | Operation | Description | Examples
|
||||||
| [blue]`+` | _Sum_ | Add two values | [blue]`4 + 0.5` -> 4.5
|
| [blue]`+` | _Sum_ | Add two values^(<<note_float_plus_string,1>>)^ | [blue]`4 + 0.5` -> 4.5
|
||||||
| [blue]`-` | _Subtraction_ | Subtract the right value from the left one | [blue]`4 - 0.5` -> 3.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]`*` | _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]`/` | _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
|
| [blue]`./`| _Forced float division_ | Force float division | [blue]`-1 ./ 2` -> -0.5
|
||||||
|===
|
|===
|
||||||
|
[[note_float_plus_string]]
|
||||||
|
^(1)^ The sum operator [blue]`+` also supports adding a float number to a string. In this case, the number is converted to a string and prependend or appended to the string, e.g. `"x" + 1.2` results in `"x1.2"`.
|
||||||
|
|
||||||
==== Fractions
|
==== Fractions
|
||||||
_Expr_ also supports fractions. Fraction literals are made with two integers separated by a colon character `:`.
|
_Expr_ also supports fractions. Fraction literals are made with two integers separated by a colon character `:`.
|
||||||
@@ -364,7 +396,7 @@ Some arithmetic operators also apply to strings.
|
|||||||
| [blue]`*` | _repeat_ | Make _n_ copy of a string | [blue]`"one" * 2` -> _"oneone"_
|
| [blue]`*` | _repeat_ | Make _n_ copy of a string | [blue]`"one" * 2` -> _"oneone"_
|
||||||
|===
|
|===
|
||||||
|
|
||||||
The charanters in a string can be accessed using the square `[]` operator.
|
The characters in a string can be accessed using the square `[]` operator.
|
||||||
|
|
||||||
.Item access syntax
|
.Item access syntax
|
||||||
====
|
====
|
||||||
@@ -387,7 +419,7 @@ The charanters in a string can be accessed using the square `[]` operator.
|
|||||||
[green]`"d"`
|
[green]`"d"`
|
||||||
|
|
||||||
`>>>` [blue]`#s` [gray]_// number of chars_ +
|
`>>>` [blue]`#s` [gray]_// number of chars_ +
|
||||||
[gren]`4`
|
[green]`4`
|
||||||
|
|
||||||
`>>>` [blue]`#"abc"` [gray]_// number of chars_ +
|
`>>>` [blue]`#"abc"` [gray]_// number of chars_ +
|
||||||
[green]`3`
|
[green]`3`
|
||||||
@@ -444,14 +476,14 @@ Currently, boolean operations are evaluated using _short cut evaluation_. This m
|
|||||||
----
|
----
|
||||||
2 > (a=1) or (a=8) > 0; a // <1>
|
2 > (a=1) or (a=8) > 0; a // <1>
|
||||||
----
|
----
|
||||||
<1> This multi-expression returns _1_ because in the first expression the left value of [blue]`or` is _true_ and, as a conseguence, its right value is not computed. Therefore the _a_ variable only receives the integer _1_.
|
<1> This multi-expression returns _1_ because in the first expression the left value of [blue]`or` is _true_ and, as a consequence, its right value is not computed. Therefore the _a_ variable only receives the integer _1_.
|
||||||
|
|
||||||
|
|
||||||
TIP: `dev-expr` provides the _ctrl()_ function that allows to change this behaviour.
|
TIP: `ecli` provides the _ctrl()_ function that allows to change this behaviour.
|
||||||
====
|
====
|
||||||
|
|
||||||
=== Lists
|
=== Lists
|
||||||
_Expr_ supports list of mixed-type values, also specified by normal expressions. Internally, _Expr_'s lists are Go arrays.
|
_Expr_ supports list of mixed-type values, also specified by normal expressions. Internally, _Expr_'s lists are Go slices.
|
||||||
|
|
||||||
.List literal syntax
|
.List literal syntax
|
||||||
====
|
====
|
||||||
@@ -556,7 +588,21 @@ Array's items can be accessed using the index `[]` operator.
|
|||||||
`>>>` [blue]`[1,2,3,2,4] - [2,4]` +
|
`>>>` [blue]`[1,2,3,2,4] - [2,4]` +
|
||||||
[green]`[1, 3]`
|
[green]`[1, 3]`
|
||||||
|
|
||||||
|
=== Linked Lists
|
||||||
|
Linked lists are a special kind of lists that are used to represent sequences of items that can be easily modified. They are represented as lists of two items: the first item is the value of the current node, and the second item is the next node in the list. The last node in the list has a next node that is _nil_.
|
||||||
|
|
||||||
|
Internally,Expr's linked lists hold two pointers: one to the head of the list and one to the tail of the list. This allows for efficient insertion and deletion of items at both ends of the list. Also, linked lists keep track of their size, so the number of items in a linked list can be obtained in constant time.
|
||||||
|
|
||||||
|
.Linked List literal syntax
|
||||||
|
====
|
||||||
|
*_linked-list_* = "**[<**" [_item-expr_ {"**,**" _item-expr_}] "**>]**" +
|
||||||
|
_item-expr_ = _any-value_
|
||||||
|
====
|
||||||
|
|
||||||
|
`>>>` [blue]`ls=[<1,2,3,4>]` +
|
||||||
|
[green]`[<1, 2, 3, 4>]`
|
||||||
|
|
||||||
|
#todo: to be completed#
|
||||||
|
|
||||||
=== Dictionaries
|
=== Dictionaries
|
||||||
The _dictionary_, or _dict_, data-type represents sets 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_.
|
||||||
@@ -678,7 +724,7 @@ The value of each sub-expression is stored in the automatic variable _last_.
|
|||||||
[green]`2`
|
[green]`2`
|
||||||
|
|
||||||
`>>>` [blue]`x=2*3 but x-1` +
|
`>>>` [blue]`x=2*3 but x-1` +
|
||||||
[green]`5`.
|
[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]`)`.
|
[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]`)`.
|
||||||
|
|
||||||
@@ -758,7 +804,7 @@ The triple special case of the selector operator is very useful, but it only wor
|
|||||||
====
|
====
|
||||||
|
|
||||||
=== Variable default value [blue]`??`, [blue]`?=`, and [blue]`?!`
|
=== Variable default value [blue]`??`, [blue]`?=`, and [blue]`?!`
|
||||||
The left operand of the first two operators, [blue]`??` and [blue]`?=`, must be a variable. The right operatand can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.
|
The left operand of the first two operators, [blue]`??` and [blue]`?=`, must be a variable. The right operand 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.
|
IMPORTANT: If the left variable is defined, the right expression is not evaluated at all.
|
||||||
|
|
||||||
@@ -766,7 +812,7 @@ The [blue]`??` operator do not change the status of the left variable.
|
|||||||
|
|
||||||
The [blue]`?=` assigns the calculated value of the right expression to the variable on the left side.
|
The [blue]`?=` assigns the calculated value of the right expression to the variable on the left side.
|
||||||
|
|
||||||
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.
|
The third one, [blue]`?!`, is the alternate operator. If the variable on the left side is not defined, it returns [blue]_nil_. Otherwise it returns the result of the expression on the right side.
|
||||||
|
|
||||||
IMPORTANT: If the variable [blue]`?!` is NOT defined, the expression is not evaluated at all.
|
IMPORTANT: If the variable [blue]`?!` is NOT defined, the expression is not evaluated at all.
|
||||||
|
|
||||||
@@ -860,7 +906,7 @@ The table below shows all supported operators by decreasing priorities.
|
|||||||
.6+|*ITER-OP*| [blue]`digest` | _Infix_ | _Item-digesting_ | _iterable_ `digest` _expr_ -> _any_
|
.6+|*ITER-OP*| [blue]`digest` | _Infix_ | _Item-digesting_ | _iterable_ `digest` _expr_ -> _any_
|
||||||
| [blue]`filter` | _Infix_ | _Item-filtering_ | _iterable_ `filter` _expr_ -> _list_
|
| [blue]`filter` | _Infix_ | _Item-filtering_ | _iterable_ `filter` _expr_ -> _list_
|
||||||
| [blue]`groupby` | _Infix_ | _Dict-grouping_ | _iterable_ `groupby` _key-expr_ -> _dict_
|
| [blue]`groupby` | _Infix_ | _Dict-grouping_ | _iterable_ `groupby` _key-expr_ -> _dict_
|
||||||
| [blue]`join` | _Infix_ | _Item-joining_ | _iterable_ `join` _iterable_ -> _list_
|
| [blue]`cat` | _Infix_ | _Item-concatenation_ | _iterable_ `cat ` _iterable_ -> _list_
|
||||||
| [blue]`map` | _Infix_ | _Item-mapping_ | _iterable_ `map` _-expr_ -> _list_
|
| [blue]`map` | _Infix_ | _Item-mapping_ | _iterable_ `map` _-expr_ -> _list_
|
||||||
4+| _See iterators section for examples_
|
4+| _See iterators section for examples_
|
||||||
.1+|*RANGE*| [blue]`:` | _Infix_ | _Index-range_ | _integer_ `:` _integer_ -> _integer-pair_
|
.1+|*RANGE*| [blue]`:` | _Infix_ | _Index-range_ | _integer_ `:` _integer_ -> _integer-pair_
|
||||||
@@ -868,7 +914,7 @@ The table below shows all supported operators by decreasing priorities.
|
|||||||
|
|
||||||
//^1^ Experimental
|
//^1^ Experimental
|
||||||
|
|
||||||
.Special assignment perators
|
.Special assignment operators
|
||||||
[cols="^2,^2,^4,^6"]
|
[cols="^2,^2,^4,^6"]
|
||||||
|===
|
|===
|
||||||
| Priority | Operator | Operation |Equivalent operation
|
| Priority | Operator | Operation |Equivalent operation
|
||||||
@@ -951,7 +997,7 @@ _param-name_ = _identifier_
|
|||||||
|
|
||||||
|
|
||||||
=== _Golang_ function definition
|
=== _Golang_ function definition
|
||||||
Description of how to define Golang functions and how to bind them to _Expr_ are topics covered in another documents that I'll write, one day, maybe.
|
Description of how to define Golang functions and how to bind them to _Expr_ are topics covered in other documents.
|
||||||
|
|
||||||
=== Function calls
|
=== Function calls
|
||||||
To call a function, either Expr or Golang type, it is necessary to specify its name and, at least, its required parameters.
|
To call a function, either Expr or Golang type, it is necessary to specify its name and, at least, its required parameters.
|
||||||
@@ -1217,7 +1263,7 @@ Returns _true_ if the value type of _<expr>_ is string, false otherwise.
|
|||||||
|
|
||||||
===== bool()
|
===== bool()
|
||||||
Syntax: `bool(<expr>) -> bool` +
|
Syntax: `bool(<expr>) -> bool` +
|
||||||
Returns a _boolean_ value consisent with the value of the expression.
|
Returns a _boolean_ value consistent with the value of the expression.
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
`>>>` [blue]`bool(1)` +
|
`>>>` [blue]`bool(1)` +
|
||||||
@@ -1507,10 +1553,56 @@ Creates or truncates the named _<file-path>_. If the file already exists, it is
|
|||||||
#to-do#
|
#to-do#
|
||||||
|
|
||||||
===== fileByteIterator()
|
===== fileByteIterator()
|
||||||
#to-do#
|
Syntax: +
|
||||||
|
`{4sp}fileByteIterator(handle-or-path) -> iterator`
|
||||||
|
|
||||||
|
Returns an iterator that produces the bytes of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.
|
||||||
|
|
||||||
|
.Examples
|
||||||
|
>>> builtin "os.file" +
|
||||||
|
[green]`1` +
|
||||||
|
`>>>` [blue]`fileByteIterator("test-file.txt") map $_` +
|
||||||
|
[green]`[
|
||||||
|
117,
|
||||||
|
110,
|
||||||
|
111,
|
||||||
|
10,
|
||||||
|
100,
|
||||||
|
117,
|
||||||
|
101,
|
||||||
|
10
|
||||||
|
]`
|
||||||
|
|
||||||
|
>>> builtin ["os.file", "string"] +
|
||||||
|
[green]`2` +
|
||||||
|
`>>>` [blue]`fileByteIterator("test-file.txt") map char($_)` +
|
||||||
|
[green]`[
|
||||||
|
"u",
|
||||||
|
"n",
|
||||||
|
"o",
|
||||||
|
"
|
||||||
|
",
|
||||||
|
"d",
|
||||||
|
"u",
|
||||||
|
"e",
|
||||||
|
"
|
||||||
|
",
|
||||||
|
]`
|
||||||
|
|
||||||
===== fileLineIterator()
|
===== fileLineIterator()
|
||||||
#to-do#
|
Syntax: +
|
||||||
|
`{4sp}fileLineIterator(handle-or-path) -> iterator`
|
||||||
|
|
||||||
|
Returns an iterator that produces the lines of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.
|
||||||
|
|
||||||
|
.Examples
|
||||||
|
`>>>` [blue]`builtin "os.file"` +
|
||||||
|
[green]`1` +
|
||||||
|
`>>>` [blue]`fileLineIterator("test-file.txt") map $_` +
|
||||||
|
[green]`[
|
||||||
|
"uno",
|
||||||
|
"due"
|
||||||
|
]`
|
||||||
|
|
||||||
|
|
||||||
==== Module "string"
|
==== Module "string"
|
||||||
@@ -1634,6 +1726,8 @@ Returns a string obtained by converting all characters of the specified string t
|
|||||||
`>>>` [blue]`strLower("Hello, world!")` +
|
`>>>` [blue]`strLower("Hello, world!")` +
|
||||||
[green]`"hello, world!"`
|
[green]`"hello, world!"`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
== Iterators
|
== Iterators
|
||||||
Iterators are objects that can be used to traverse collections, such as lists and dictionaries. They are created by providing a _data-source_ object, the collection, in a `$(<data-source>)` expression. Once an iterator is created, it can be used to access the elements of the collection one by one.
|
Iterators are objects that can be used to traverse collections, such as lists and dictionaries. They are created by providing a _data-source_ object, the collection, in a `$(<data-source>)` expression. Once an iterator is created, it can be used to access the elements of the collection one by one.
|
||||||
|
|
||||||
@@ -1648,16 +1742,16 @@ _data-source_ = _explicit_ | _list-spec_ | _dict-spec_ | _custom-data-source_
|
|||||||
|
|
||||||
_explicit_ = _any-expr_ { "**,**" _any-expr_ }
|
_explicit_ = _any-expr_ { "**,**" _any-expr_ }
|
||||||
|
|
||||||
_list-spec_ = _list_ _range-options_ +
|
_list-spec_ = _list_ ["**,**" _range-options_] +
|
||||||
_list_ = "**[**" _any-expr_ { "**,**" _any-expr_ } "**]**" +
|
_list_ = "**[**" _any-expr_ { "**,**" _any-expr_ } "**]**" +
|
||||||
_range-options_ = [ "**,**" _start-index_ [ "**,**" _end-index_ [ "**,**" _step_ ]]] +
|
_range-options_ = _start-index_ [ "**,**" _end-index_ [ "**,**" _step_ ]] +
|
||||||
_start-index_, _end-index_, _step_ = _integer-expr_
|
_start-index_, _end-index_, _step_ = _integer-expr_
|
||||||
|
|
||||||
_dict-spec_ = _dict_ _dict-options_ +
|
_dict-spec_ = _dict_ ["**,**" _dict-options_] +
|
||||||
_dict_ = "**{**" _key-value-pair_ { "**,**" _key-value-pair_ } "**}**" +
|
_dict_ = "**{**" _key-value-pair_ { "**,**" _key-value-pair_ } "**}**" +
|
||||||
_key-value-pair_ = _scalar-value_ "**:**" _any-expr_ +
|
_key-value-pair_ = _scalar-value_ "**:**" _any-expr_ +
|
||||||
_scalar-value_ = _string_ | _number_ | _boolean_ +
|
_scalar-value_ = _string_ | _number_ | _boolean_ +
|
||||||
_dict-options_ = [ "**,**" _sort-order_ [ "**,**" _iter-mode_ ] ] +
|
_dict-options_ = _sort-order_ [ "**,**" _iter-mode_ ] +
|
||||||
_sort-order_ = _asc-order_ | _desc-order_ | _no-sort_ | _default-order_ +
|
_sort-order_ = _asc-order_ | _desc-order_ | _no-sort_ | _default-order_ +
|
||||||
_asc-order_ = "**asc**" | "**a**" +
|
_asc-order_ = "**asc**" | "**a**" +
|
||||||
_desc-order_ = "**desc**" | "**d**" +
|
_desc-order_ = "**desc**" | "**d**" +
|
||||||
@@ -1735,7 +1829,7 @@ Negative steps are also allowed. They are interpreted as reverse iteration. For
|
|||||||
The above example shows the use of the [blue]`{plusplus}` operator to get the next element of an iterator. The [blue]`{plusplus}` operator is a postfix operator that can be used with iterators. It returns the next element of the collection and updates the state of the iterator. When there are no more elements to iterate over, it returns the error [red]_Eval Error: EOF_.
|
The above example shows the use of the [blue]`{plusplus}` operator to get the next element of an iterator. The [blue]`{plusplus}` operator is a postfix operator that can be used with iterators. It returns the next element of the collection and updates the state of the iterator. When there are no more elements to iterate over, it returns the error [red]_Eval Error: EOF_.
|
||||||
|
|
||||||
|
|
||||||
After the first use of the [blue]`{plusplus}` operator, the prefixed operato [blue]`{star}` operator can be used to get the current element of the collection without updating the state of the iterator. When there are no more elements to iterate over, it returns the error [red]_Eval Error: EOF_. Same error is returned if the [blue]`{star}` operator is used before the first use of the [blue]`{plusplus}` operator.
|
After the first use of the [blue]`{plusplus}` operator, the prefixed operator [blue]`{star}` operator can be used to get the current element of the collection without updating the state of the iterator. When there are no more elements to iterate over, it returns the error [red]_Eval Error: EOF_. Same error is returned if the [blue]`{star}` operator is used before the first use of the [blue]`{plusplus}` operator.
|
||||||
|
|
||||||
.Example: using the [blue]`{star}` operator
|
.Example: using the [blue]`{star}` operator
|
||||||
`>>>` [blue]`it = $(["one", "two", "three"])` +
|
`>>>` [blue]`it = $(["one", "two", "three"])` +
|
||||||
@@ -1747,12 +1841,17 @@ After the first use of the [blue]`{plusplus}` operator, the prefixed operato [bl
|
|||||||
`>>>` [blue]`{star}it` +
|
`>>>` [blue]`{star}it` +
|
||||||
[green]`"one"`
|
[green]`"one"`
|
||||||
|
|
||||||
|
==== [blue]*`$$()`* -- Expansion special function for iterators
|
||||||
|
The [blue]`$$()` operator is a special function already seen applied to contexts. It can also be applied to iterators. When applied to an iterator, it returns a _linked list_ of all the remaining elements of the collection. The state of the iterator is updated to the end of the collection. If there are no more elements to iterate over, it returns an empty list.
|
||||||
|
|
||||||
|
#todo: examples#
|
||||||
|
|
||||||
==== Named operators
|
==== Named operators
|
||||||
Named operators are operators that are identified by a name instead of a symbol. They are implicitly defined and can be called using a special syntax. For example, the [blue]`{plusplus}` has the equivalent named operator [blue]`.next`.
|
Named operators are operators that are identified by a name instead of a symbol. They are implicitly defined and can be called using a special syntax. For example, the [blue]`{plusplus}` has the equivalent named operator [blue]`.next`.
|
||||||
|
|
||||||
.Available named operators
|
.Available named operators
|
||||||
* *_.next_*: same as [blue]`{plusplus}`.
|
* *_.next_*: same as [blue]`{plusplus}`.
|
||||||
* *_.current_*: same as [blue]`{star}`.
|
* *_.current_*: same as [blue]`{star}.`
|
||||||
* *_.reset_*: resets the state of the iterator to the initial state.
|
* *_.reset_*: resets the state of the iterator to the initial state.
|
||||||
* *_.count_*: returns the number of elements in the iterator already visited.
|
* *_.count_*: returns the number of elements in the iterator already visited.
|
||||||
* *_.index_*: returns the index of the current element in the iterator. Before the first use of the [blue]`{plusplus}` operator, it returns the error [red]_-1_.
|
* *_.index_*: returns the index of the current element in the iterator. Before the first use of the [blue]`{plusplus}` operator, it returns the error [red]_-1_.
|
||||||
@@ -1775,6 +1874,92 @@ TIP: Iterators built on custom data-sources can provide additional named operato
|
|||||||
`>>>` [blue]`it.next` +
|
`>>>` [blue]`it.next` +
|
||||||
[green]`"one"`
|
[green]`"one"`
|
||||||
|
|
||||||
|
=== Infixed operators on iterators
|
||||||
|
There are also some infixed operators that can be used with iterators. They are defined as follows.
|
||||||
|
|
||||||
|
* <<_cat,cat operator>>
|
||||||
|
* <<_digest,digest operator>>
|
||||||
|
* <<_filter,filter operator>>
|
||||||
|
* <<_groupby,groupby operator>>
|
||||||
|
* <<_map,map operator>>
|
||||||
|
//* <<_reduce,reduce operator>>: applies a binary expression cumulatively to the elements of the iterator, from left to right, to reduce the iterator to a single value.
|
||||||
|
//* <<_zip,zip operator>>: takes two or more iterators and returns a list of tuples, where the i-th tuple contains the i-th element from each of the input iterators.
|
||||||
|
|
||||||
|
|
||||||
|
==== Automatic variables in operators
|
||||||
|
At each iteration, the following automatic variables are available for use in the expression of the [blue]`digest`, [blue]`filter`, [blue]`groupby`, and [blue]`map` operators.
|
||||||
|
|
||||||
|
* `$_`: the current element of the iterator.
|
||||||
|
* `$__`: the index of the current element in the iterator, starting from 0.
|
||||||
|
* `$#`: the number of elements already visited in the iterator, starting from 0.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
==== [blue]`cat` operator
|
||||||
|
Syntax: +
|
||||||
|
`{4sp}<iterable> cat <iterable> -> <iterator>{4sp}`
|
||||||
|
|
||||||
|
[blue]`cat` operator takes two iterators or iterables and returns a new iterator that produces the elements of the first iterator followed by the elements of the second iterator.
|
||||||
|
|
||||||
|
.Examples
|
||||||
|
#todo: examples#
|
||||||
|
|
||||||
|
==== [blue]`filter` operator
|
||||||
|
Syntax: +
|
||||||
|
`{4sp}<iterable> filter <expr> -> <iterator>{4sp}`
|
||||||
|
|
||||||
|
[blue]`filter` applies a boolean expression to each element of the iterator and returns a new iterator that only produces the elements of the first iterator for which the expression evaluates to true.
|
||||||
|
|
||||||
|
.Examples
|
||||||
|
#todo: examples#
|
||||||
|
|
||||||
|
|
||||||
|
==== [blue]`groupby` operator
|
||||||
|
Syntax: +
|
||||||
|
`{4sp}<list-of-dicts> groupby <key> -> <dict>{4sp}`
|
||||||
|
|
||||||
|
The left side of [blue]`groupby` operator is a list ofdictionaries or an iterator over a list of dictionaries. It takes a key and returns a dictionary where the keys are the unique values of the specified key in the dictionaries and the values are lists of dictionaries that have that key value. In other words, it groups the dictionaries by the specified key value.
|
||||||
|
|
||||||
|
NOTE: Currently, keys of group are always strings. In the future, it will be possible to specify a key function to compute the keys of the groups.
|
||||||
|
|
||||||
|
.Examples
|
||||||
|
`>>>` [blue]`[{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}, {"name": "Charlie", "age": 30}] groupby "age"` +
|
||||||
|
[source,yaml]
|
||||||
|
----
|
||||||
|
{
|
||||||
|
"25": [
|
||||||
|
{
|
||||||
|
"name": "Bob",
|
||||||
|
"age": 25
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"30": [
|
||||||
|
{
|
||||||
|
"name": "Alice",
|
||||||
|
"age": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Charlie",
|
||||||
|
"age": 30
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
==== [blue]`map` operator
|
||||||
|
Syntax: +
|
||||||
|
`{4sp}<iterable> map <expr> -> <iterator>{4sp}`
|
||||||
|
|
||||||
|
[blue]`map` operator iterates over the elements of the iterator and evaluates the expressions provided on the right side for each element. Its result is a new iterator over the computed values of the that expression. The current element of the iterator is available in the expression as the variable `$_`.
|
||||||
|
|
||||||
|
.Example: using the [blue]`map` operator
|
||||||
|
`>>>` [blue]`it = $(["one", "two", "three"])` +
|
||||||
|
[green]`$(#3)` +
|
||||||
|
`>>>` [blue]`excl_it = it map $_ + "!"` +
|
||||||
|
[green]`$($([#3]))` +
|
||||||
|
`>>>` [blue]`$$(excl_it)` +
|
||||||
|
[green]`[<"one!", "two!", "three!">]`
|
||||||
|
|
||||||
=== Iterator over custom data-sources
|
=== Iterator over custom data-sources
|
||||||
It is possible to create iterators over custom data-sources by defining a dictionary that has the key `next` whose value is a function that returns the next element of the collection and updates the state of the iterator. The syntax for creating an iterator over a custom data-source is as follows.
|
It is possible to create iterators over custom data-sources by defining a dictionary that has the key `next` whose value is a function that returns the next element of the collection and updates the state of the iterator. The syntax for creating an iterator over a custom data-source is as follows.
|
||||||
|
|
||||||
|
|||||||
+460
-30
@@ -581,7 +581,11 @@ pre.rouge .ss {
|
|||||||
<li><a href="#_operator">4.1. <code class="blue">;</code> operator</a></li>
|
<li><a href="#_operator">4.1. <code class="blue">;</code> operator</a></li>
|
||||||
<li><a href="#_but_operator">4.2. <code class="blue">but</code> operator</a></li>
|
<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="#_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="#_selector_operator">4.4. Selector operator <code class="blue">? : ::</code></a>
|
||||||
|
<ul class="sectlevel3">
|
||||||
|
<li><a href="#_triple_special_case_of_the_selector_operator">4.4.1. Triple special case of the selector operator</a></li>
|
||||||
|
</ul>
|
||||||
|
</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>
|
<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>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@@ -613,8 +617,10 @@ pre.rouge .ss {
|
|||||||
<li><a href="#_dec">dec()</a></li>
|
<li><a href="#_dec">dec()</a></li>
|
||||||
<li><a href="#_string">string()</a></li>
|
<li><a href="#_string">string()</a></li>
|
||||||
<li><a href="#_fract">fract()</a></li>
|
<li><a href="#_fract">fract()</a></li>
|
||||||
|
<li><a href="#_char">char()</a></li>
|
||||||
<li><a href="#_eval">eval()</a></li>
|
<li><a href="#_eval">eval()</a></li>
|
||||||
<li><a href="#_var">var()</a></li>
|
<li><a href="#_var">var()</a></li>
|
||||||
|
<li><a href="#_set">set</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#_module_fmt">7.1.2. Module "fmt"</a>
|
<li><a href="#_module_fmt">7.1.2. Module "fmt"</a>
|
||||||
@@ -649,6 +655,8 @@ pre.rouge .ss {
|
|||||||
<li><a href="#_filewritetext">fileWriteText()</a></li>
|
<li><a href="#_filewritetext">fileWriteText()</a></li>
|
||||||
<li><a href="#_filereadtext">fileReadText()</a></li>
|
<li><a href="#_filereadtext">fileReadText()</a></li>
|
||||||
<li><a href="#_filereadtextall">fileReadTextAll()</a></li>
|
<li><a href="#_filereadtextall">fileReadTextAll()</a></li>
|
||||||
|
<li><a href="#_filebyteiterator">fileByteIterator()</a></li>
|
||||||
|
<li><a href="#_filelineiterator">fileLineIterator()</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#_module_string">7.1.7. Module "string"</a>
|
<li><a href="#_module_string">7.1.7. Module "string"</a>
|
||||||
@@ -674,7 +682,15 @@ pre.rouge .ss {
|
|||||||
<li><a href="#_named_operators">8.1.1. Named operators</a></li>
|
<li><a href="#_named_operators">8.1.1. Named operators</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#_iterator_over_custom_data_sources">8.2. Iterator over custom data-sources</a></li>
|
<li><a href="#_infixed_operators_on_iterators">8.2. Infixed operators on iterators</a>
|
||||||
|
<ul class="sectlevel3">
|
||||||
|
<li><a href="#_cat_operator">8.2.1. <code class="blue">cat</code> operator</a></li>
|
||||||
|
<li><a href="#_filter_operator">8.2.2. <code class="blue">filter</code> operator</a></li>
|
||||||
|
<li><a href="#_groupby_operator">8.2.3. <code class="blue">groupby</code> operator</a></li>
|
||||||
|
<li><a href="#_map_operator">8.2.4. <code class="blue">map</code> operator</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><a href="#_iterator_over_custom_data_sources">8.3. Iterator over custom data-sources</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#_plugins">9. Plugins</a></li>
|
<li><a href="#_plugins">9. Plugins</a></li>
|
||||||
@@ -686,7 +702,7 @@ pre.rouge .ss {
|
|||||||
<div class="sectionbody">
|
<div class="sectionbody">
|
||||||
<!-- toc disabled -->
|
<!-- toc disabled -->
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><mark>TODO: Work in progress (last update on 2026/04/15, 6:02 p.m.)</mark></p>
|
<p><mark>TODO: Work in progress (last update on 2026/05/08)</mark></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -788,7 +804,26 @@ pre.rouge .ss {
|
|||||||
<code class="green">}</code></p>
|
<code class="green">}</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>In order to inspect the global context issue the <code class="blue">$$global</code> operator.</p>
|
<p>In order to inspect the global context issue the <code class="blue">$$ global</code> operation.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Example: list all functions whose name starts with "str"</div>
|
||||||
|
<p><code>>>></code> <code class="blue">builtin "string</code> <em class="gray">// most function in the builtin module <strong>string</strong> have names starting with "str".</em><br>
|
||||||
|
<code class="green">1</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><code>>>></code> <code class="blue">($$$global).functions) filter strStartsWith($_, "str"</code><br>
|
||||||
|
<code class="green">[</code><br>
|
||||||
|
<code class="green">  "strEndsWith",</code><br>
|
||||||
|
<code class="green">  "strJoin",</code><br>
|
||||||
|
<code class="green">  "strLower",</code><br>
|
||||||
|
<code class="green">  "strSplit",</code><br>
|
||||||
|
<code class="green">  "strStartsWith",</code><br>
|
||||||
|
<code class="green">  "strSub",</code><br>
|
||||||
|
<code class="green">  "strTrim",</code><br>
|
||||||
|
<code class="green">  "strUpper",</code><br>
|
||||||
|
<code class="green">  "string"</code><br>
|
||||||
|
<code class="green">]</code></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1880,10 +1915,10 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
|
|||||||
<p>The <em>selector operator</em> is very similar to the <em>switch/case/default</em> statement available in many programming languages.</p>
|
<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>
|
||||||
<div class="exampleblock">
|
<div class="exampleblock">
|
||||||
<div class="title">Example 13. Selector literal Syntax</div>
|
<div class="title">Example 13. Selector literal syntax</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<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>
|
<p><strong><em>selector-operator</em></strong> = <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>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>
|
<em>match-list</em> = "<strong>[</strong>" <em>item</em> {"<strong>,</strong>" <em>items</em>} "<strong>]</strong>"<br>
|
||||||
<em>item</em> = <em>expression</em><br>
|
<em>item</em> = <em>expression</em><br>
|
||||||
@@ -1931,11 +1966,45 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
|
|||||||
<p><code>>>></code> <code class="blue">10 ? {"a"} : {"b"}</code><br>
|
<p><code>>>></code> <code class="blue">10 ? {"a"} : {"b"}</code><br>
|
||||||
<code class="red">Eval Error: [1:3] no case catches the value (10) of the selection expression</code></p>
|
<code class="red">Eval Error: [1:3] no case catches the value (10) of the selection expression</code></p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="sect3">
|
||||||
|
<h4 id="_triple_special_case_of_the_selector_operator"><a class="anchor" href="#_triple_special_case_of_the_selector_operator"></a><a class="link" href="#_triple_special_case_of_the_selector_operator">4.4.1. Triple special case of the selector operator</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>If the <em>select-expression</em> is a boolean expression, the selector operator can be used as a sort of <em>if-then-else</em> statement. In this case, the first case is evaluated if the <em>select-expression</em> is true, and the second case is evaluated if the <em>select-expression</em> is false. In this special case, the <em>match-list</em> of both cases must be empty.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Example</div>
|
||||||
|
<p><code>>>></code> <code class="blue">(true) ? {"T"}: {"F"}</code><br>
|
||||||
|
<code class="green">T</code><br>
|
||||||
|
<code>>>></code> <code class="blue">(2 > 1) ? {"a"} : {"b"}</code><br>
|
||||||
|
<code class="green">a</code><br>
|
||||||
|
<code>>>></code> <code class="blue">(2 < 1) ? {"a"} : {"b"}</code><br>
|
||||||
|
<code class="green">b</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="admonitionblock warning">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td class="icon">
|
||||||
|
<i class="fa icon-warning" title="Warning"></i>
|
||||||
|
</td>
|
||||||
|
<td class="content">
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>The triple special case of the selector operator is very useful, but it only works with boolean expressions.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Example of confusion</div>
|
||||||
|
<p><code>>>></code> <code class="blue">int(true) ? {"T"}: {"F"}</code><br>
|
||||||
|
<code class="green">F</code></p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect2">
|
<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>, <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">
|
<div class="paragraph">
|
||||||
<p>The left operand of the 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>
|
<p>The left operand of the first two operators, <code class="blue">??</code> and <code class="blue">?=</code>, must be a variable. The right operatand 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>
|
||||||
<div class="admonitionblock important">
|
<div class="admonitionblock important">
|
||||||
<table>
|
<table>
|
||||||
@@ -1953,7 +2022,7 @@ If the left variable is defined, the right expression is not evaluated at all.
|
|||||||
<p>The <code class="blue">??</code> operator do not change the status of the left variable.</p>
|
<p>The <code class="blue">??</code> operator do not change the status of the left variable.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>The <code class="blue">?=</code> assigns the calculated value of the right expression to the left variable.</p>
|
<p>The <code class="blue">?=</code> assigns the calculated value of the right expression to the variable on the left side.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<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>
|
<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>
|
||||||
@@ -1965,7 +2034,7 @@ If the left variable is defined, the right expression is not evaluated at all.
|
|||||||
<i class="fa icon-important" title="Important"></i>
|
<i class="fa icon-important" title="Important"></i>
|
||||||
</td>
|
</td>
|
||||||
<td class="content">
|
<td class="content">
|
||||||
If the left variable is NOT defined, the right expression is not evaluated at all.
|
If the variable <code class="blue">?!</code> is NOT defined, the expression is not evaluated at all.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@@ -2371,7 +2440,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>identifier</em> <code>=</code> <em>any</em> → <em>any</em></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>identifier</em> <code>=</code> <em>any</em> → <em>any</em></p></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="tableblock halign-center valign-top" colspan="4"><p class="tableblock"><em>See also the table of special allocation operators below</em></p></td>
|
<td class="tableblock halign-center valign-top" colspan="4"><p class="tableblock"><em>See also the table of special assignment operators below</em></p></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>BUT</strong></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>BUT</strong></p></td>
|
||||||
@@ -2381,6 +2450,40 @@ These operators have a high priority, in particular higher than the operator <co
|
|||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>but</code> <em>any</em> → <em>any</em></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>but</code> <em>any</em> → <em>any</em></p></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td class="tableblock halign-center valign-top" rowspan="6"><p class="tableblock"><strong>ITER-OP</strong></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">digest</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>Item-digesting</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>digest</code> <em>expr</em> → <em>any</em></p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">filter</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>Item-filtering</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>filter</code> <em>expr</em> → <em>list</em></p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">groupby</code></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Dict-grouping</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>groupby</code> <em>key-expr</em> → <em>dict</em></p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">cat</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>Item-concatenation</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> `cat ` <em>iterable</em> → <em>list</em></p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">map</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>Item-mapping</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>map</code> <em>-expr</em> → <em>list</em></p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tableblock halign-center valign-top" colspan="4"><p class="tableblock"><em>See iterators section for examples</em></p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>RANGE</strong></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>RANGE</strong></p></td>
|
||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">:</code></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">:</code></p></td>
|
||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||||
@@ -2536,14 +2639,14 @@ short for<br>
|
|||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><code>>>></code> <em class="gray">// Required and optional parameters</em><br>
|
<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>>>></code> <code class="blue">measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? {"s"} :: {""}}</code><br>
|
||||||
<code class="green">measure(value, unit="meter"):any{}</code></p>
|
<code class="green">measure(value, unit="meter"):any{}</code></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect2">
|
<div class="sect2">
|
||||||
<h3 id="_golang_function_definition"><a class="anchor" href="#_golang_function_definition"></a><a class="link" href="#_golang_function_definition">6.2. <em>Golang</em> function definition</a></h3>
|
<h3 id="_golang_function_definition"><a class="anchor" href="#_golang_function_definition"></a><a class="link" href="#_golang_function_definition">6.2. <em>Golang</em> function definition</a></h3>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>Description of how to define Golang functions and how to bind them to <em>Expr</em> are topics covered in another document that I’ll write, one day, maybe.</p>
|
<p>Description of how to define Golang functions and how to bind them to <em>Expr</em> are topics covered in another documents that I’ll write, one day, maybe.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect2">
|
<div class="sect2">
|
||||||
@@ -2642,7 +2745,7 @@ short for<br>
|
|||||||
<div class="title">Example</div>
|
<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>
|
<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 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>>>></code> <code class="blue">f()</code> <em class="gray">// The multi-expression (two expressions) in f() is calculated and the last result is returned</em><br>
|
||||||
<code class="green">5</code><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>>>></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>
|
<code class="green">3</code></p>
|
||||||
@@ -2727,7 +2830,7 @@ The clone modifier <code class="blue">@</code> does not make a variable a refere
|
|||||||
<div class="title">Example 16. Builtin activation syntax</div>
|
<div class="title">Example 16. Builtin activation syntax</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><strong><em>builtin-activation</em></strong> = <code class="blue">BUILTIN</code> (<em>builtin-name</em> | <em>list-of-builtin-names</em>)<br>
|
<p><strong><em>builtin-activation</em></strong> = <code class="blue">BUILTIN</code> (<em>builtin-name</em> | <em>list-of-builtin-names</em> | <strong>"*"</strong>)<br>
|
||||||
<em>builtin-name</em> = <em>string</em><br>
|
<em>builtin-name</em> = <em>string</em><br>
|
||||||
<em>list-of-builtin-names</em> = <strong>[</strong> <em>string</em> { "<strong>,</strong>" <em>string</em> } <strong>]</strong></p>
|
<em>list-of-builtin-names</em> = <strong>[</strong> <em>string</em> { "<strong>,</strong>" <em>string</em> } <strong>]</strong></p>
|
||||||
</div>
|
</div>
|
||||||
@@ -2815,9 +2918,15 @@ To avoid the need to activate builtin modules one by one, it is possible to acti
|
|||||||
<div class="title">Other functions</div>
|
<div class="title">Other functions</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
|
<p><a href="#_char">char()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
<p><a href="#_eval">eval()</a></p>
|
<p><a href="#_eval">eval()</a></p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
<p><a href="#_set">set()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
<p><a href="#_var">var()</a></p>
|
<p><a href="#_var">var()</a></p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -2833,7 +2942,9 @@ Returns <em>true</em> if the value type of <em><expr></em> is boolean, fal
|
|||||||
<p><code>>>></code> <code class="blue">isBool(true)</code><br>
|
<p><code>>>></code> <code class="blue">isBool(true)</code><br>
|
||||||
<code class="green">true</code><br>
|
<code class="green">true</code><br>
|
||||||
<code>>>></code> <code class="blue">isBool(3==2)</code><br>
|
<code>>>></code> <code class="blue">isBool(3==2)</code><br>
|
||||||
<code class="green">true</code></p>
|
<code class="green">true</code><br>
|
||||||
|
<code>>>></code> <code class="blue">isBool(3 + 2)</code><br>
|
||||||
|
<code class="green">false</code></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
@@ -2938,7 +3049,7 @@ Returns <em>true</em> if the value type of <em><expr></em> is fraction or
|
|||||||
<h5 id="_isstring"><a class="anchor" href="#_isstring"></a><a class="link" href="#_isstring">isString()</a></h5>
|
<h5 id="_isstring"><a class="anchor" href="#_isstring"></a><a class="link" href="#_isstring">isString()</a></h5>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>Syntax: <code>isString(<expr>) → bool</code><br>
|
<p>Syntax: <code>isString(<expr>) → bool</code><br>
|
||||||
Returns a boolean value , false otherwise.</p>
|
Returns <em>true</em> if the value type of <em><expr></em> is string, false otherwise.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<div class="title">Examples</div>
|
<div class="title">Examples</div>
|
||||||
@@ -2954,7 +3065,7 @@ Returns a boolean value , false otherwise.</p>
|
|||||||
<h5 id="_bool"><a class="anchor" href="#_bool"></a><a class="link" href="#_bool">bool()</a></h5>
|
<h5 id="_bool"><a class="anchor" href="#_bool"></a><a class="link" href="#_bool">bool()</a></h5>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>Syntax: <code>bool(<expr>) → bool</code><br>
|
<p>Syntax: <code>bool(<expr>) → bool</code><br>
|
||||||
Returns a <em>boolean</em> value consisent to the value of the expression.</p>
|
Returns a <em>boolean</em> value consisent with the value of the expression.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<div class="title">Examples</div>
|
<div class="title">Examples</div>
|
||||||
@@ -3077,6 +3188,20 @@ Returns a <em>fraction</em> value consistent with the value of the expression.</
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
|
<h5 id="_char"><a class="anchor" href="#_char"></a><a class="link" href="#_char">char()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax: <code>char(<intexpr>) → string</code><br>
|
||||||
|
Returns the character whose ASCII (soon Unicode too) code point is specified by the integer expression.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Examples</div>
|
||||||
|
<p><code>>>></code> <code class="blue">char(65)</code><br>
|
||||||
|
<code class="green">"A"</code><br>
|
||||||
|
<code>>>></code> <code class="blue">char(97)</code><br>
|
||||||
|
<code class="green">"a"</code></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect4">
|
||||||
<h5 id="_eval"><a class="anchor" href="#_eval"></a><a class="link" href="#_eval">eval()</a></h5>
|
<h5 id="_eval"><a class="anchor" href="#_eval"></a><a class="link" href="#_eval">eval()</a></h5>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>Syntax: <code>eval(<string-expr>) → any</code><br>
|
<p>Syntax: <code>eval(<string-expr>) → any</code><br>
|
||||||
@@ -3096,13 +3221,13 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
|||||||
<code>    var(<string-expr>) → any</code></p>
|
<code>    var(<string-expr>) → any</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>This function allows you to define variables whose names must include special characters. The first form of the function allows you to define a variable with a name specified by the first parameter and assign it the value of the second parameter. The second form only returns the value of the variable with the specified name.</p>
|
<p>This function allows you to define variables whose names can include special characters. The first form of the function allows you to define a variable with a name specified by the first parameter and assign it the value of the second parameter. The second form only returns the value of the variable with the specified name.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<div class="title">Examples</div>
|
<div class="title">Examples</div>
|
||||||
<p><code>>>></code> <code class="blue">var("$x", 3+9)</code><br>
|
<p><code>>>></code> <code class="blue">var("$x", 3+9)</code><br>
|
||||||
<code class="green">12</code><br>
|
<code class="green">12</code><br>
|
||||||
<code>>>></code> <code class="blue">var("$x")</code><br>
|
<code>>>></code> <code class="blue">var("$"+"x")</code><br>
|
||||||
<code class="green">12</code><br>
|
<code class="green">12</code><br>
|
||||||
<code>>>></code> <code class="blue">var("gain%", var("$x"))</code><br>
|
<code>>>></code> <code class="blue">var("gain%", var("$x"))</code><br>
|
||||||
<code class="green">12</code><br>
|
<code class="green">12</code><br>
|
||||||
@@ -3110,9 +3235,32 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
|||||||
<code class="green">13</code></p>
|
<code class="green">13</code></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="sect4">
|
||||||
|
<h5 id="_set"><a class="anchor" href="#_set"></a><a class="link" href="#_set">set</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    set(<string-expr>, <expr>) → any</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>This function allows you to set the value of a variable whose name can include special characters. The first parameter is the name of the variable and the second parameter is the new value to assign to that variable.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>It is equivalent to the first form of the var() function, but it is more explicit about the intent of changing the value of an existing variable.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Examples</div>
|
||||||
|
<p><code>>>></code> <code class="blue">set("$x", 100)</code><br>
|
||||||
|
<code class="green">100</code><br>
|
||||||
|
<code>>>></code> <code class="blue">var("$x")</code><br>
|
||||||
|
<code class="green">100</code><br></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect3">
|
<div class="sect3">
|
||||||
<h4 id="_module_fmt"><a class="anchor" href="#_module_fmt"></a><a class="link" href="#_module_fmt">7.1.2. Module "fmt"</a></h4>
|
<h4 id="_module_fmt"><a class="anchor" href="#_module_fmt"></a><a class="link" href="#_module_fmt">7.1.2. Module "fmt"</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><mark>to-do</mark></p>
|
||||||
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_print"><a class="anchor" href="#_print"></a><a class="link" href="#_print">print()</a></h5>
|
<h5 id="_print"><a class="anchor" href="#_print"></a><a class="link" href="#_print">print()</a></h5>
|
||||||
|
|
||||||
@@ -3124,10 +3272,18 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
|||||||
</div>
|
</div>
|
||||||
<div class="sect3">
|
<div class="sect3">
|
||||||
<h4 id="_module_import"><a class="anchor" href="#_module_import"></a><a class="link" href="#_module_import">7.1.3. Module "import"</a></h4>
|
<h4 id="_module_import"><a class="anchor" href="#_module_import"></a><a class="link" href="#_module_import">7.1.3. Module "import"</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Module activation:<br>
|
||||||
|
<code>    BUILTIN "import"</code></p>
|
||||||
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_import"><a class="anchor" href="#_import"></a><a class="link" href="#_import"><em>import()</em></a></h5>
|
<h5 id="_import"><a class="anchor" href="#_import"></a><a class="link" href="#_import"><em>import()</em></a></h5>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><em class="blue">import(<span class="grey"><source-file></span>)</em> — loads the multi-expression contained in the specified source and returns its value.</p>
|
<p>Syntax:<br>
|
||||||
|
<code>    import(<source-file>)</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Loads the multi-expression contained in the specified source and returns its value.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
@@ -3137,16 +3293,40 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
|||||||
</div>
|
</div>
|
||||||
<div class="sect3">
|
<div class="sect3">
|
||||||
<h4 id="_module_iterator"><a class="anchor" href="#_module_iterator"></a><a class="link" href="#_module_iterator">7.1.4. Module "iterator"</a></h4>
|
<h4 id="_module_iterator"><a class="anchor" href="#_module_iterator"></a><a class="link" href="#_module_iterator">7.1.4. Module "iterator"</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Module activation:<br>
|
||||||
|
<code>    BUILTIN "iterator"</code></p>
|
||||||
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_run"><a class="anchor" href="#_run"></a><a class="link" href="#_run">run()</a></h5>
|
<h5 id="_run"><a class="anchor" href="#_run"></a><a class="link" href="#_run">run()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    run(<iterator>, <operator>, <vars>) → any</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Iterates over the specified iterator and applies the specified operator to the current value of the iterator.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect3">
|
<div class="sect3">
|
||||||
<h4 id="_module_math_arith"><a class="anchor" href="#_module_math_arith"></a><a class="link" href="#_module_math_arith">7.1.5. Module "math.arith"</a></h4>
|
<h4 id="_module_math_arith"><a class="anchor" href="#_module_math_arith"></a><a class="link" href="#_module_math_arith">7.1.5. Module "math.arith"</a></h4>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
|
<p>Module activation:<br>
|
||||||
|
<code>    BUILTIN "math.arith"</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
<p>Currently, the "math.arith" module provides two functions, add() and mul(), that perform addition and multiplication of an arbitrary number of parameters. More functions will be added in the future.</p>
|
<p>Currently, the "math.arith" module provides two functions, add() and mul(), that perform addition and multiplication of an arbitrary number of parameters. More functions will be added in the future.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ulist">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_add">add()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_mul">mul()</a></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_add"><a class="anchor" href="#_add"></a><a class="link" href="#_add">add()</a></h5>
|
<h5 id="_add"><a class="anchor" href="#_add"></a><a class="link" href="#_add">add()</a></h5>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -3202,37 +3382,213 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
|||||||
</div>
|
</div>
|
||||||
<div class="sect3">
|
<div class="sect3">
|
||||||
<h4 id="_module_os_file"><a class="anchor" href="#_module_os_file"></a><a class="link" href="#_module_os_file">7.1.6. Module "os.file"</a></h4>
|
<h4 id="_module_os_file"><a class="anchor" href="#_module_os_file"></a><a class="link" href="#_module_os_file">7.1.6. Module "os.file"</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Module activation:<br>
|
||||||
|
<code>    BUILTIN "os.file"</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>The "os.file" module provides functions for working with files.</p>
|
||||||
|
</div>
|
||||||
|
<div class="ulist">
|
||||||
|
<div class="title">File related functions</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileOpen">fileOpen()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileAppend">fileAppend()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileCreate">fileCreate()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileClose">fileClose()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileWriteText">fileWriteText()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileReadText">fileReadText()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileReadTextAll">fileReadTextAll()</a></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="ulist">
|
||||||
|
<div class="title">Iterator functions for files</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileByteIterator">fileByteIterator()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileLineIterator">fileLineIterator()</a></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>More functions will be added in the future.</p>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_fileopen"><a class="anchor" href="#_fileopen"></a><a class="link" href="#_fileopen">fileOpen()</a></h5>
|
<h5 id="_fileopen"><a class="anchor" href="#_fileopen"></a><a class="link" href="#_fileopen">fileOpen()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    fileOpen(<file-path>) → file-handle</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Returns a file handle for the specified file path. The file is opened in read-write mode. If the file does not exist, it is created.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_fileappend"><a class="anchor" href="#_fileappend"></a><a class="link" href="#_fileappend">fileAppend()</a></h5>
|
<h5 id="_fileappend"><a class="anchor" href="#_fileappend"></a><a class="link" href="#_fileappend">fileAppend()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    fileAppend(<file-path>) → any</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Like <a href="#_fileCreate">fileCreate()</a> but write operations happen at the end of the file.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_filecreate"><a class="anchor" href="#_filecreate"></a><a class="link" href="#_filecreate">fileCreate()</a></h5>
|
<h5 id="_filecreate"><a class="anchor" href="#_filecreate"></a><a class="link" href="#_filecreate">fileCreate()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    fileCreate(<file-path>) → file-handle</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Creates or truncates the named <em><file-path></em>. If the file already exists, it is truncated. If the file does not exist, it is created with mode 0o666 (before umask). The associated file descriptor has mode [O_RDWR]. The directory containing the file must already exist.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_fileclose"><a class="anchor" href="#_fileclose"></a><a class="link" href="#_fileclose">fileClose()</a></h5>
|
<h5 id="_fileclose"><a class="anchor" href="#_fileclose"></a><a class="link" href="#_fileclose">fileClose()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><mark>to-do</mark></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_filewritetext"><a class="anchor" href="#_filewritetext"></a><a class="link" href="#_filewritetext">fileWriteText()</a></h5>
|
<h5 id="_filewritetext"><a class="anchor" href="#_filewritetext"></a><a class="link" href="#_filewritetext">fileWriteText()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><mark>to-do</mark></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_filereadtext"><a class="anchor" href="#_filereadtext"></a><a class="link" href="#_filereadtext">fileReadText()</a></h5>
|
<h5 id="_filereadtext"><a class="anchor" href="#_filereadtext"></a><a class="link" href="#_filereadtext">fileReadText()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><mark>to-do</mark></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_filereadtextall"><a class="anchor" href="#_filereadtextall"></a><a class="link" href="#_filereadtextall">fileReadTextAll()</a></h5>
|
<h5 id="_filereadtextall"><a class="anchor" href="#_filereadtextall"></a><a class="link" href="#_filereadtextall">fileReadTextAll()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><mark>to-do</mark></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect4">
|
||||||
|
<h5 id="_filebyteiterator"><a class="anchor" href="#_filebyteiterator"></a><a class="link" href="#_filebyteiterator">fileByteIterator()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    fileByteIterator(handle-or-path) → iterator</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Returns an iterator that produces the bytes of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Examples</div>
|
||||||
|
<p>>>> builtin "os.file"<br>
|
||||||
|
<code class="green">1</code><br>
|
||||||
|
<code>>>></code> <code class="blue">fileByteIterator("test-file.txt") map $_</code><br>
|
||||||
|
<code class="green">[
|
||||||
|
117,
|
||||||
|
110,
|
||||||
|
111,
|
||||||
|
10,
|
||||||
|
100,
|
||||||
|
117,
|
||||||
|
101,
|
||||||
|
10
|
||||||
|
]</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>>>> builtin ["os.file", "string"]<br>
|
||||||
|
<code class="green">2</code><br>
|
||||||
|
<code>>>></code> <code class="blue">fileByteIterator("test-file.txt") map char($_)</code><br>
|
||||||
|
<code class="green">[
|
||||||
|
"u",
|
||||||
|
"n",
|
||||||
|
"o",
|
||||||
|
"
|
||||||
|
",
|
||||||
|
"d",
|
||||||
|
"u",
|
||||||
|
"e",
|
||||||
|
"
|
||||||
|
",
|
||||||
|
]</code></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect4">
|
||||||
|
<h5 id="_filelineiterator"><a class="anchor" href="#_filelineiterator"></a><a class="link" href="#_filelineiterator">fileLineIterator()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    fileLineIterator(handle-or-path) → iterator</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Returns an iterator that produces the lines of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Examples</div>
|
||||||
|
<p><code>>>></code> <code class="blue">builtin "os.file"</code><br>
|
||||||
|
<code class="green">1</code><br>
|
||||||
|
<code>>>></code> <code class="blue">fileLineIterator("test-file.txt") map $_</code><br>
|
||||||
|
<code class="green">[
|
||||||
|
"uno",
|
||||||
|
"due"
|
||||||
|
]</code></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect3">
|
<div class="sect3">
|
||||||
<h4 id="_module_string"><a class="anchor" href="#_module_string"></a><a class="link" href="#_module_string">7.1.7. Module "string"</a></h4>
|
<h4 id="_module_string"><a class="anchor" href="#_module_string"></a><a class="link" href="#_module_string">7.1.7. Module "string"</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Module activation:<br>
|
||||||
|
<code>    BUILTIN "string"</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>This module provides functions for working with strings.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Currently available functions:</p>
|
||||||
|
</div>
|
||||||
|
<div class="ulist">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strJoin">strJoin()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strSub">strSub()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strSplit">strSplit()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strTrim">strTrim()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strStartsWith">strStartsWith()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strEndsWith">strEndsWith()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strUpper">strUpper()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strLower">strLower()</a></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_strjoin"><a class="anchor" href="#_strjoin"></a><a class="link" href="#_strjoin">strJoin()</a></h5>
|
<h5 id="_strjoin"><a class="anchor" href="#_strjoin"></a><a class="link" href="#_strjoin">strJoin()</a></h5>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -3589,7 +3945,81 @@ Iterators built on custom data-sources can provide additional named operators, d
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect2">
|
<div class="sect2">
|
||||||
<h3 id="_iterator_over_custom_data_sources"><a class="anchor" href="#_iterator_over_custom_data_sources"></a><a class="link" href="#_iterator_over_custom_data_sources">8.2. Iterator over custom data-sources</a></h3>
|
<h3 id="_infixed_operators_on_iterators"><a class="anchor" href="#_infixed_operators_on_iterators"></a><a class="link" href="#_infixed_operators_on_iterators">8.2. Infixed operators on iterators</a></h3>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>There are also some infixed operators that can be used with iterators. They are defined as follows.</p>
|
||||||
|
</div>
|
||||||
|
<div class="ulist">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_cat">cat operator</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_diget">digest operator</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_filter">filter operator</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_groupby">groupby operator</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_map">map operator</a></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="sect3">
|
||||||
|
<h4 id="_cat_operator"><a class="anchor" href="#_cat_operator"></a><a class="link" href="#_cat_operator">8.2.1. <code class="blue">cat</code> operator</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    <iterable> cat <iterable> → <iterator>    </code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><code class="blue">cat</code> operator takes two iterators or iterables and returns a new iterator that produces the elements of the first iterator followed by the elements of the second iterator.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect3">
|
||||||
|
<h4 id="_filter_operator"><a class="anchor" href="#_filter_operator"></a><a class="link" href="#_filter_operator">8.2.2. <code class="blue">filter</code> operator</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Examples</div>
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    <iterable> filter <expr> → <iterator>    </code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><code class="blue">filter</code> applies a boolean expression to each element of the iterator and returns a list of the elements for which the expression evaluates to true.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect3">
|
||||||
|
<h4 id="_groupby_operator"><a class="anchor" href="#_groupby_operator"></a><a class="link" href="#_groupby_operator">8.2.3. <code class="blue">groupby</code> operator</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    <dict> groupby <key> → <dict>    </code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><code class="blue">groupby</code> operator groups the elements of the iterator based on the value of a specified expression and returns a dictionary where the keys are the group values and the values are lists of the elements in each group.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect3">
|
||||||
|
<h4 id="_map_operator"><a class="anchor" href="#_map_operator"></a><a class="link" href="#_map_operator">8.2.4. <code class="blue">map</code> operator</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    <iterable> map <expr> → <list>    </code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><code class="blue">map</code> operator iterates over the elements of the iterator and evaluates the expressions provided on the right side for each element. Its result is a list of the computed values of the that expression. The current element of the iterator is available in the expression as the variable <code>$_</code>.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Example: using the <code class="blue">map</code> operator</div>
|
||||||
|
<p><code>>>></code> <code class="blue">it = $(["one", "two", "three"])</code><br>
|
||||||
|
<code class="green">$(#3)</code><br>
|
||||||
|
<code>>>></code> <code class="blue">it map $_ + "!"</code><br>
|
||||||
|
<code class="green">["one!", "two!", "three!"]</code></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect2">
|
||||||
|
<h3 id="_iterator_over_custom_data_sources"><a class="anchor" href="#_iterator_over_custom_data_sources"></a><a class="link" href="#_iterator_over_custom_data_sources">8.3. Iterator over custom data-sources</a></h3>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>It is possible to create iterators over custom data-sources by defining a dictionary that has the key <code>next</code> whose value is a function that returns the next element of the collection and updates the state of the iterator. The syntax for creating an iterator over a custom data-source is as follows.</p>
|
<p>It is possible to create iterators over custom data-sources by defining a dictionary that has the key <code>next</code> whose value is a function that returns the next element of the collection and updates the state of the iterator. The syntax for creating an iterator over a custom data-source is as follows.</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -3610,7 +4040,7 @@ Iterators built on custom data-sources can provide additional named operators, d
|
|||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<div id="footer-text">
|
<div id="footer-text">
|
||||||
Last updated 2026-04-21 06:35:14 +0200
|
Last updated 2026-05-12 16:25:27 +0200
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
+11
-13
@@ -42,12 +42,16 @@ func ImportInContextByGlobPattern(ctx kern.ExprContext, pattern string) (count i
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fixCtrlVar(name string) string {
|
||||||
|
if !strings.HasPrefix(name, "_") {
|
||||||
|
name = "_" + name
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValue any) {
|
func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValue any) {
|
||||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||||
if !strings.HasPrefix(name, "_") {
|
name = fixCtrlVar(name)
|
||||||
name = "_" + name
|
|
||||||
}
|
|
||||||
|
|
||||||
currentValue, _ = globalCtx.GetVar(name)
|
currentValue, _ = globalCtx.GetVar(name)
|
||||||
globalCtx.SetVar(name, newValue)
|
globalCtx.SetVar(name, newValue)
|
||||||
}
|
}
|
||||||
@@ -56,18 +60,14 @@ func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValu
|
|||||||
|
|
||||||
func GlobalCtrlGet(ctx kern.ExprContext, name string) (currentValue any) {
|
func GlobalCtrlGet(ctx kern.ExprContext, name string) (currentValue any) {
|
||||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||||
if !strings.HasPrefix(name, "_") {
|
name = fixCtrlVar(name)
|
||||||
name = "_" + name
|
|
||||||
}
|
|
||||||
currentValue, _ = globalCtx.GetVar(name)
|
currentValue, _ = globalCtx.GetVar(name)
|
||||||
}
|
}
|
||||||
return currentValue
|
return currentValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
||||||
if !strings.HasPrefix(name, "_") {
|
name = fixCtrlVar(name)
|
||||||
name = "_" + name
|
|
||||||
}
|
|
||||||
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
|
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
|
||||||
currentStatus, _ = v.(bool)
|
currentStatus, _ = v.(bool)
|
||||||
}
|
}
|
||||||
@@ -77,9 +77,7 @@ func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CtrlDisable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
func CtrlDisable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
||||||
if !strings.HasPrefix(name, "_") {
|
name = fixCtrlVar(name)
|
||||||
name = "_" + name
|
|
||||||
}
|
|
||||||
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
|
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
|
||||||
currentStatus, _ = v.(bool)
|
currentStatus, _ = v.(bool)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ type IntIterator struct {
|
|||||||
step int64
|
step int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewIntIteratorA(args ...any) (it *IntIterator, err error) {
|
||||||
|
return NewIntIterator(args)
|
||||||
|
}
|
||||||
|
|
||||||
func NewIntIterator(args []any) (it *IntIterator, err error) {
|
func NewIntIterator(args []any) (it *IntIterator, err error) {
|
||||||
var argc int = 0
|
var argc int = 0
|
||||||
if args != nil {
|
if args != nil {
|
||||||
|
|||||||
+17
-6
@@ -11,6 +11,15 @@ import (
|
|||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func NewFormalIterator(value any) (it kern.Iterator) {
|
||||||
|
if value == nil {
|
||||||
|
it = NewArrayIterator([]any{})
|
||||||
|
} else {
|
||||||
|
it = NewArrayIterator([]any{value})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func NewIterator(ctx kern.ExprContext, value any, ops []*scan.Term) (it kern.Iterator, err error) {
|
func NewIterator(ctx kern.ExprContext, value any, ops []*scan.Term) (it kern.Iterator, err error) {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return NewArrayIterator([]any{}), nil
|
return NewArrayIterator([]any{}), nil
|
||||||
@@ -19,6 +28,8 @@ func NewIterator(ctx kern.ExprContext, value any, ops []*scan.Term) (it kern.Ite
|
|||||||
switch v := value.(type) {
|
switch v := value.(type) {
|
||||||
case *kern.ListType:
|
case *kern.ListType:
|
||||||
it = NewListIterator(v, nil)
|
it = NewListIterator(v, nil)
|
||||||
|
case *kern.LinkedList:
|
||||||
|
it = NewLinkedListIterator(v, nil)
|
||||||
case *kern.DictType:
|
case *kern.DictType:
|
||||||
it, err = NewDictIterator(v, nil)
|
it, err = NewDictIterator(v, nil)
|
||||||
case []any:
|
case []any:
|
||||||
@@ -36,9 +47,9 @@ func HasIterStandardOperations(name string) bool {
|
|||||||
return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
|
return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func HasIterOperations(name string, ops ...string) bool {
|
// func HasIterOperations(name string, ops ...string) bool {
|
||||||
return slices.Contains([]string{
|
// return slices.Contains([]string{
|
||||||
kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName,
|
// kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName,
|
||||||
}, name) ||
|
// }, name) ||
|
||||||
slices.Contains(ops, name)
|
// slices.Contains(ops, name)
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// clone-value.go
|
||||||
|
package kern
|
||||||
|
|
||||||
|
func Clone(v any) (c any) {
|
||||||
|
if v == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch unboxed := v.(type) {
|
||||||
|
case int64:
|
||||||
|
c = unboxed
|
||||||
|
case float64:
|
||||||
|
c = unboxed
|
||||||
|
case string:
|
||||||
|
c = unboxed
|
||||||
|
case bool:
|
||||||
|
c = unboxed
|
||||||
|
case *ListType:
|
||||||
|
c = unboxed.Clone()
|
||||||
|
case *DictType:
|
||||||
|
c = unboxed.Clone()
|
||||||
|
case *LinkedList:
|
||||||
|
c = unboxed.Clone()
|
||||||
|
default:
|
||||||
|
c = v
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -20,4 +20,5 @@ const (
|
|||||||
TypeDict = "dict"
|
TypeDict = "dict"
|
||||||
TypeListOf = "list-of-"
|
TypeListOf = "list-of-"
|
||||||
TypeListOfStrings = "list-of-strings"
|
TypeListOfStrings = "list-of-strings"
|
||||||
|
TypeLinkedList = "linked-list"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ func Equal(value1, value2 any) (equal bool) {
|
|||||||
d1 := value1.(*DictType)
|
d1 := value1.(*DictType)
|
||||||
d2 := value2.(*DictType)
|
d2 := value2.(*DictType)
|
||||||
equal = d1.Equals(*d2)
|
equal = d1.Equals(*d2)
|
||||||
|
} else if IsLinkedList(value1) && IsLinkedList(value2) {
|
||||||
|
ll1 := value1.(*LinkedList)
|
||||||
|
ll2 := value2.(*LinkedList)
|
||||||
|
equal = ll1.Equals(ll2)
|
||||||
} else if IsInteger(value1) && IsInteger(value2) {
|
} else if IsInteger(value1) && IsInteger(value2) {
|
||||||
equal = value1.(int64) == value2.(int64)
|
equal = value1.(int64) == value2.(int64)
|
||||||
} else if IsString(value1) && IsString(value2) {
|
} else if IsString(value1) && IsString(value2) {
|
||||||
|
|||||||
+14
-8
@@ -10,6 +10,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const DictTypeName = "dict"
|
||||||
|
|
||||||
type DictType map[any]any
|
type DictType map[any]any
|
||||||
|
|
||||||
func IsDict(v any) (ok bool) {
|
func IsDict(v any) (ok bool) {
|
||||||
@@ -74,9 +76,11 @@ func (dict *DictType) toMultiLine(sb *strings.Builder, opt FmtOpt) {
|
|||||||
|
|
||||||
sb.WriteString(nest)
|
sb.WriteString(nest)
|
||||||
if key, ok := name.(string); ok {
|
if key, ok := name.(string); ok {
|
||||||
sb.WriteString(string('"') + key + string('"'))
|
sb.WriteByte('"')
|
||||||
|
sb.WriteString(key)
|
||||||
|
sb.WriteByte('"')
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString(fmt.Sprintf("%v", name))
|
fmt.Fprintf(sb, "%v", name)
|
||||||
}
|
}
|
||||||
sb.WriteString(": ")
|
sb.WriteString(": ")
|
||||||
if f, ok := value.(Formatter); ok {
|
if f, ok := value.(Formatter); ok {
|
||||||
@@ -84,7 +88,7 @@ func (dict *DictType) toMultiLine(sb *strings.Builder, opt FmtOpt) {
|
|||||||
} else if _, ok = value.(Functor); ok {
|
} else if _, ok = value.(Functor); ok {
|
||||||
sb.WriteString("func(){}")
|
sb.WriteString("func(){}")
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString(fmt.Sprintf("%v", value))
|
fmt.Fprintf(sb, "%v", value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.WriteByte('\n')
|
sb.WriteByte('\n')
|
||||||
@@ -108,9 +112,11 @@ func (dict *DictType) ToString(opt FmtOpt) string {
|
|||||||
sb.WriteString(", ")
|
sb.WriteString(", ")
|
||||||
}
|
}
|
||||||
if s, ok := key.(string); ok {
|
if s, ok := key.(string); ok {
|
||||||
sb.WriteString(string('"') + s + string('"'))
|
sb.WriteByte('"')
|
||||||
|
sb.WriteString(s)
|
||||||
|
sb.WriteByte('"')
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString(fmt.Sprintf("%v", key))
|
fmt.Fprintf(&sb, "%v", key)
|
||||||
}
|
}
|
||||||
sb.WriteString(": ")
|
sb.WriteString(": ")
|
||||||
if formatter, ok := value.(Formatter); ok {
|
if formatter, ok := value.(Formatter); ok {
|
||||||
@@ -118,7 +124,7 @@ func (dict *DictType) ToString(opt FmtOpt) string {
|
|||||||
} else if t, ok := value.(Term); ok {
|
} else if t, ok := value.(Term); ok {
|
||||||
sb.WriteString(t.String())
|
sb.WriteString(t.String())
|
||||||
} else {
|
} else {
|
||||||
sb.WriteString(fmt.Sprintf("%#v", value))
|
fmt.Fprintf(&sb, "%#v", value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.WriteByte('}')
|
sb.WriteByte('}')
|
||||||
@@ -131,7 +137,7 @@ func (dict *DictType) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dict *DictType) TypeName() string {
|
func (dict *DictType) TypeName() string {
|
||||||
return "dict"
|
return DictTypeName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dict *DictType) HasKey(target any) (ok bool) {
|
func (dict *DictType) HasKey(target any) (ok bool) {
|
||||||
@@ -155,7 +161,7 @@ func (dict *DictType) GetItem(key any) (value any, exists bool) {
|
|||||||
func (dict *DictType) Clone() (c *DictType) {
|
func (dict *DictType) Clone() (c *DictType) {
|
||||||
c = newDict(nil)
|
c = newDict(nil)
|
||||||
for k, v := range *dict {
|
for k, v := range *dict {
|
||||||
(*c)[k] = v
|
(*c)[k] = Clone(v)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,4 +28,36 @@ type ExprContext interface {
|
|||||||
Call(name string, args map[string]any) (result any, err error)
|
Call(name string, args map[string]any) (result any, err error)
|
||||||
RegisterFuncInfo(info ExprFunc)
|
RegisterFuncInfo(info ExprFunc)
|
||||||
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error)
|
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error)
|
||||||
|
|
||||||
|
ToDict() (dict *DictType)
|
||||||
|
ToString(opt FmtOpt) string
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextToDict(ctx ExprContext) (dict *DictType) {
|
||||||
|
var keys []string
|
||||||
|
// Variables
|
||||||
|
keys = ctx.EnumVars(nil)
|
||||||
|
vars := MakeDict()
|
||||||
|
for _, key := range keys {
|
||||||
|
value, _ := ctx.GetVar(key)
|
||||||
|
vars.SetItem(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions
|
||||||
|
keys = ctx.EnumFuncs(func(name string) bool { return true })
|
||||||
|
funcs := MakeDict()
|
||||||
|
for _, key := range keys {
|
||||||
|
funcInfo, _ := ctx.GetFuncInfo(key)
|
||||||
|
funcs.SetItem(key, funcInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
dict = MakeDict()
|
||||||
|
dict.SetItem("vars", vars)
|
||||||
|
dict.SetItem("funcs", funcs)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextToString(ctx ExprContext, opt FmtOpt) string {
|
||||||
|
dict := ctx.ToDict()
|
||||||
|
return dict.ToString(opt)
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-1
@@ -4,7 +4,10 @@
|
|||||||
// formatter.go
|
// formatter.go
|
||||||
package kern
|
package kern
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
type FmtOpt uint32 // lower 16 bits hold a bit-mask, higher 16 bits hold an indentation number
|
type FmtOpt uint32 // lower 16 bits hold a bit-mask, higher 16 bits hold an indentation number
|
||||||
|
|
||||||
@@ -46,6 +49,18 @@ type Formatter interface {
|
|||||||
ToString(options FmtOpt) string
|
ToString(options FmtOpt) string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Format(sb *strings.Builder, item any, opt FmtOpt) {
|
||||||
|
if s, ok := item.(string); ok {
|
||||||
|
sb.WriteByte('"')
|
||||||
|
sb.WriteString(s)
|
||||||
|
sb.WriteByte('"')
|
||||||
|
} else if formatter, ok := item.(Formatter); ok {
|
||||||
|
sb.WriteString(formatter.ToString(opt))
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(sb, "%v", item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func GetFormatted(v any, opt FmtOpt) (text string) {
|
func GetFormatted(v any, opt FmtOpt) (text string) {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
text = "(nil)"
|
text = "(nil)"
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// linked-list-type.go
|
||||||
|
package kern
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const LinkedListTypeName = "lisked-list"
|
||||||
|
const MaxUint64Allowed = uint64(9_223_372_036_854_775_807)
|
||||||
|
|
||||||
|
func IsLinkedList(v any) (ok bool) {
|
||||||
|
_, ok = v.(*LinkedList)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLinkedListA(listAny ...any) (list *LinkedList) {
|
||||||
|
if listAny == nil {
|
||||||
|
listAny = []any{}
|
||||||
|
}
|
||||||
|
list = NewLinkedList()
|
||||||
|
for _, item := range listAny {
|
||||||
|
list.PushBack(FixAnyNumber(item))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func LinkedListFromStrings(stringList []string) (list *LinkedList) {
|
||||||
|
list = NewLinkedList()
|
||||||
|
for _, s := range stringList {
|
||||||
|
list.PushBack(s)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *LinkedList) ToString(opt FmtOpt) (s string) {
|
||||||
|
indent := GetFormatIndent(opt)
|
||||||
|
flags := GetFormatFlags(opt)
|
||||||
|
|
||||||
|
var sb strings.Builder
|
||||||
|
sb.WriteString("[<")
|
||||||
|
if ls.Len() > 0 {
|
||||||
|
innerOpt := MakeFormatOptions(flags, indent+1)
|
||||||
|
nest := strings.Repeat(" ", indent+1)
|
||||||
|
|
||||||
|
if flags&MultiLine != 0 {
|
||||||
|
sb.WriteByte('\n')
|
||||||
|
sb.WriteString(nest)
|
||||||
|
}
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for item := ls.FirstNode(); item != nil; item = item.Next() {
|
||||||
|
if i > 0 {
|
||||||
|
if flags&MultiLine != 0 {
|
||||||
|
sb.WriteString(",\n")
|
||||||
|
sb.WriteString(nest)
|
||||||
|
} else {
|
||||||
|
sb.WriteString(", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// data := item.Data()
|
||||||
|
// if s, ok := data.(string); ok {
|
||||||
|
// sb.WriteByte('"')
|
||||||
|
// sb.WriteString(s)
|
||||||
|
// sb.WriteByte('"')
|
||||||
|
// } else if formatter, ok := data.(Formatter); ok {
|
||||||
|
// sb.WriteString(formatter.ToString(innerOpt))
|
||||||
|
// } else {
|
||||||
|
// fmt.Fprintf(&sb, "%v", data)
|
||||||
|
// }
|
||||||
|
Format(&sb, item.Data(), innerOpt)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if flags&MultiLine != 0 {
|
||||||
|
sb.WriteByte('\n')
|
||||||
|
sb.WriteString(strings.Repeat(" ", indent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.WriteString(">]")
|
||||||
|
s = sb.String()
|
||||||
|
if flags&Truncate != 0 && len(s) > TruncateSize {
|
||||||
|
s = TruncateString(s)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *LinkedList) String() string {
|
||||||
|
return ls.ToString(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *LinkedList) TypeName() string {
|
||||||
|
return LinkedListTypeName
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (ls *LinkedList) Contains(t *ListType) (answer bool) {
|
||||||
|
// if len(*ls) >= len(*t) {
|
||||||
|
// answer = true
|
||||||
|
// for _, item := range *t {
|
||||||
|
// if answer = ls.IndexDeepSameCmp(item) >= 0; !answer {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (ls1 *LinkedList) Equals(ls2 *LinkedList) (answer bool) {
|
||||||
|
if ls2 != nil && ls1.Len() == ls2.Len() {
|
||||||
|
answer = true
|
||||||
|
i2 := ls2.FirstNode()
|
||||||
|
for i1 := ls1.FirstNode(); i1 != nil; i1 = i1.Next() {
|
||||||
|
if !Equal(i1.Data(), i2.Data()) {
|
||||||
|
answer = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i2 = i2.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls1 *LinkedList) Clone() (ls2 *LinkedList) {
|
||||||
|
ls2 = NewLinkedListA()
|
||||||
|
for i1 := ls1.FirstNode(); i1 != nil; i1 = i1.Next() {
|
||||||
|
ls2.PushBack(Clone(i1.Data()))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
+24
-13
@@ -19,7 +19,7 @@ func (self *ListNode) Data() any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LinkedList struct {
|
type LinkedList struct {
|
||||||
count uint32
|
count int
|
||||||
first *ListNode
|
first *ListNode
|
||||||
last *ListNode
|
last *ListNode
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ func NewLinkedList() (list *LinkedList) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *LinkedList) Len() uint32 {
|
func (ls *LinkedList) Len() int {
|
||||||
return ls.count
|
return ls.count
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ func (ls *LinkedList) Last() (data interface{}, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *LinkedList) NodeAt(index uint32) (node *ListNode, err error) {
|
func (ls *LinkedList) NodeAt(index int) (node *ListNode, err error) {
|
||||||
if ls.count == 0 {
|
if ls.count == 0 {
|
||||||
err = errorListEmpty()
|
err = errorListEmpty()
|
||||||
} else if index > ls.count {
|
} else if index > ls.count {
|
||||||
@@ -130,7 +130,7 @@ func (ls *LinkedList) NodeAt(index uint32) (node *ListNode, err error) {
|
|||||||
node = ls.last
|
node = ls.last
|
||||||
} else {
|
} else {
|
||||||
current := ls.first
|
current := ls.first
|
||||||
for pos := uint32(0); pos < index; pos++ {
|
for range index {
|
||||||
current = current.next
|
current = current.next
|
||||||
}
|
}
|
||||||
node = current
|
node = current
|
||||||
@@ -138,7 +138,7 @@ func (ls *LinkedList) NodeAt(index uint32) (node *ListNode, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *LinkedList) At(index uint32) (data interface{}, err error) {
|
func (ls *LinkedList) At(index int) (data interface{}, err error) {
|
||||||
node, err := ls.NodeAt(index)
|
node, err := ls.NodeAt(index)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
data = node.data
|
data = node.data
|
||||||
@@ -146,12 +146,12 @@ func (ls *LinkedList) At(index uint32) (data interface{}, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls *LinkedList) Insert(index uint32, data interface{}) *ListNode {
|
func (ls *LinkedList) Insert(index int, data any) *ListNode {
|
||||||
var prev *ListNode
|
var prev *ListNode
|
||||||
|
|
||||||
prev = nil
|
prev = nil
|
||||||
current := ls.first
|
current := ls.first
|
||||||
for pos := uint32(0); current != nil && pos < index; pos++ {
|
for pos := 0; current != nil && pos < index; pos++ {
|
||||||
prev = current
|
prev = current
|
||||||
current = current.next
|
current = current.next
|
||||||
}
|
}
|
||||||
@@ -178,10 +178,21 @@ func (ls *LinkedList) PushBackStringArray(items []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// type TraverseOperator func(index uint32, elem interface{}, userData interface{}) (err error)
|
func (ls *LinkedList) Sub(start, end int) (subList *LinkedList) {
|
||||||
|
subList = NewLinkedList()
|
||||||
|
if node, err := ls.NodeAt(start); err == nil {
|
||||||
|
for i := start; i < end && node != nil; i++ {
|
||||||
|
subList.PushBack(node.data)
|
||||||
|
node = node.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// type TraverseOperator func(index int, elem interface{}, userData interface{}) (err error)
|
||||||
|
|
||||||
// func (self *LinkedList) Traverse(op TraverseOperator, user_data interface{}) (err error) {
|
// func (self *LinkedList) Traverse(op TraverseOperator, user_data interface{}) (err error) {
|
||||||
// index := uint32(0)
|
// index := int(0)
|
||||||
// for current := self.first; current != nil; current = current.next {
|
// for current := self.first; current != nil; current = current.next {
|
||||||
// err = op(index, current.data, user_data)
|
// err = op(index, current.data, user_data)
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
@@ -193,7 +204,7 @@ func (ls *LinkedList) PushBackStringArray(items []string) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// func (self *LinkedList) Traverse2(observer Observer, abortOnError bool) (err error) {
|
// func (self *LinkedList) Traverse2(observer Observer, abortOnError bool) (err error) {
|
||||||
// index := uint32(0)
|
// index := int(0)
|
||||||
// for current := self.first; current != nil; current = current.next {
|
// for current := self.first; current != nil; current = current.next {
|
||||||
// err = observer.Observe(current, index)
|
// err = observer.Observe(current, index)
|
||||||
// if err != nil && abortOnError {
|
// if err != nil && abortOnError {
|
||||||
@@ -227,9 +238,9 @@ func (ls *LinkedList) FindNext(eqFunc EqualFunc, startNode *ListNode) (targetNod
|
|||||||
}
|
}
|
||||||
|
|
||||||
// type DataFeeder func(user_data interface{}) interface{}
|
// type DataFeeder func(user_data interface{}) interface{}
|
||||||
// type NodeObserver func(node *ListNode, index uint32, userData interface{})
|
// type NodeObserver func(node *ListNode, index int, userData interface{})
|
||||||
|
|
||||||
// func (self *LinkedList) FeedTail(feeder DataFeeder, feederUserData interface{}, observer NodeObserver, observerUserData interface{}) (count uint32) {
|
// func (self *LinkedList) FeedTail(feeder DataFeeder, feederUserData interface{}, observer NodeObserver, observerUserData interface{}) (count int) {
|
||||||
// count = 0
|
// count = 0
|
||||||
// for item := feeder(feederUserData); item != nil; item = feeder(feederUserData) {
|
// for item := feeder(feederUserData); item != nil; item = feeder(feederUserData) {
|
||||||
// // fmt.Println("Item", count, item)
|
// // fmt.Println("Item", count, item)
|
||||||
@@ -242,7 +253,7 @@ func (ls *LinkedList) FindNext(eqFunc EqualFunc, startNode *ListNode) (targetNod
|
|||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// func (self *LinkedList) FeedTail2(feeder Feeder, observer Observer, abortOnError bool) (count uint32, err error) {
|
// func (self *LinkedList) FeedTail2(feeder Feeder, observer Observer, abortOnError bool) (count int, err error) {
|
||||||
// count = 0
|
// count = 0
|
||||||
// // item := feeder.Next()
|
// // item := feeder.Next()
|
||||||
// for item, err1 := feeder.Next(); item != nil; item, err1 = feeder.Next() {
|
// for item, err1 := feeder.Next(); item != nil; item, err1 = feeder.Next() {
|
||||||
|
|||||||
+24
-23
@@ -73,15 +73,7 @@ func (ls *ListType) ToString(opt FmtOpt) (s string) {
|
|||||||
sb.WriteString(", ")
|
sb.WriteString(", ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if s, ok := item.(string); ok {
|
Format(&sb, item, innerOpt)
|
||||||
sb.WriteByte('"')
|
|
||||||
sb.WriteString(s)
|
|
||||||
sb.WriteByte('"')
|
|
||||||
} else if formatter, ok := item.(Formatter); ok {
|
|
||||||
sb.WriteString(formatter.ToString(innerOpt))
|
|
||||||
} else {
|
|
||||||
sb.WriteString(fmt.Sprintf("%v", item))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if flags&MultiLine != 0 {
|
if flags&MultiLine != 0 {
|
||||||
sb.WriteByte('\n')
|
sb.WriteByte('\n')
|
||||||
@@ -104,11 +96,11 @@ func (ls *ListType) TypeName() string {
|
|||||||
return "list"
|
return "list"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dict *ListType) Contains(t *ListType) (answer bool) {
|
func (ls *ListType) Contains(t *ListType) (answer bool) {
|
||||||
if len(*dict) >= len(*t) {
|
if len(*ls) >= len(*t) {
|
||||||
answer = true
|
answer = true
|
||||||
for _, item := range *t {
|
for _, item := range *t {
|
||||||
if answer = dict.IndexDeepSameCmp(item) >= 0; !answer {
|
if answer = ls.IndexDeepSameCmp(item) >= 0; !answer {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -116,10 +108,10 @@ func (dict *ListType) Contains(t *ListType) (answer bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
|
func (ls *ListType) Equals(ls2 ListType) (answer bool) {
|
||||||
if ls2 != nil && len(*ls1) == len(ls2) {
|
if ls2 != nil && len(*ls) == len(ls2) {
|
||||||
answer = true
|
answer = true
|
||||||
for index, i1 := range *ls1 {
|
for index, i1 := range *ls {
|
||||||
// if !reflect.DeepEqual(i1, ls2[index]) {
|
// if !reflect.DeepEqual(i1, ls2[index]) {
|
||||||
// answer = false
|
// answer = false
|
||||||
// break
|
// break
|
||||||
@@ -133,11 +125,20 @@ func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dict *ListType) IndexDeepSameCmp(target any) (index int) {
|
func (ls1 *ListType) Clone() (ls2 *ListType) {
|
||||||
|
ls := make(ListType, len(*ls1))
|
||||||
|
for i, item := range *ls1 {
|
||||||
|
ls[i] = Clone(item)
|
||||||
|
}
|
||||||
|
ls2 = &ls
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ls *ListType) IndexDeepSameCmp(target any) (index int) {
|
||||||
var eq bool
|
var eq bool
|
||||||
var err error
|
var err error
|
||||||
index = -1
|
index = -1
|
||||||
for i, item := range *dict {
|
for i, item := range *ls {
|
||||||
if eq, err = deepSame(item, target, SameContent); err != nil {
|
if eq, err = deepSame(item, target, SameContent); err != nil {
|
||||||
break
|
break
|
||||||
} else if eq {
|
} else if eq {
|
||||||
@@ -188,15 +189,15 @@ func deepSame(a, b any, deepCmp DeepFuncTemplate) (eq bool, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dict *ListType) SetItem(index int64, value any) (err error) {
|
func (ls *ListType) SetItem(index int64, value any) (err error) {
|
||||||
if index >= 0 && index < int64(len(*dict)) {
|
if index >= 0 && index < int64(len(*ls)) {
|
||||||
(*dict)[index] = value
|
(*ls)[index] = value
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*dict)-1)
|
err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*ls)-1)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dict *ListType) AppendItem(value any) {
|
func (ls *ListType) AppendItem(value any) {
|
||||||
*dict = append(*dict, value)
|
*ls = append(*ls, value)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,38 @@ func AnyInteger(v any) (i int64, ok bool) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FixAnyNumber(v any) (fixed any) {
|
||||||
|
switch unboxed := v.(type) {
|
||||||
|
case int:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case int8:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case int16:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case int32:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case uint:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case uint8:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case uint16:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case uint32:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case uint64:
|
||||||
|
if unboxed <= MaxUint64Allowed {
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
} else {
|
||||||
|
fixed = float64(unboxed)
|
||||||
|
}
|
||||||
|
case float32:
|
||||||
|
fixed = float64(unboxed)
|
||||||
|
default:
|
||||||
|
fixed = v
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func ToGoInt(value any, description string) (i int, err error) {
|
func ToGoInt(value any, description string) (i int, err error) {
|
||||||
if valueInt64, ok := value.(int64); ok {
|
if valueInt64, ok := value.(int64); ok {
|
||||||
i = int(valueInt64)
|
i = int(valueInt64)
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// list-iterator.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LinkedListIterator struct {
|
||||||
|
a *kern.LinkedList
|
||||||
|
count int64
|
||||||
|
index int64
|
||||||
|
current *kern.ListNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLinkedListIterator(list *kern.LinkedList, args []any) (it *LinkedListIterator) {
|
||||||
|
it = &LinkedListIterator{a: list, count: 0, index: -1, current: list.FirstNode()}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) String() string {
|
||||||
|
var l = int64(0)
|
||||||
|
if it.a != nil {
|
||||||
|
l = int64(it.a.Len())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("$([<#%d>])", l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) TypeName() string {
|
||||||
|
return "LinkedListIterator"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) HasOperation(name string) bool {
|
||||||
|
yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName}, name)
|
||||||
|
return yes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
||||||
|
switch name {
|
||||||
|
case kern.NextName:
|
||||||
|
v, err = it.Next()
|
||||||
|
case kern.ResetName:
|
||||||
|
err = it.Reset()
|
||||||
|
case kern.CleanName:
|
||||||
|
err = it.Clean()
|
||||||
|
case kern.IndexName:
|
||||||
|
v = int64(it.Index())
|
||||||
|
case kern.CurrentName:
|
||||||
|
v, err = it.Current()
|
||||||
|
case kern.CountName:
|
||||||
|
v = it.count
|
||||||
|
default:
|
||||||
|
err = kern.ErrNoOperation(name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) Current() (item any, err error) {
|
||||||
|
if it.current != nil {
|
||||||
|
item = it.current.Data()
|
||||||
|
} else {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) Next() (item any, err error) {
|
||||||
|
if it.current != nil {
|
||||||
|
item = it.current.Data()
|
||||||
|
it.current = it.current.Next()
|
||||||
|
it.index++
|
||||||
|
it.count++
|
||||||
|
} else {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) Index() int64 {
|
||||||
|
return it.index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) Count() int64 {
|
||||||
|
return it.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) Reset() error {
|
||||||
|
it.current = it.a.FirstNode()
|
||||||
|
it.index = -1
|
||||||
|
it.count = 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) Clean() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+8
-1
@@ -30,7 +30,14 @@ func evalDict(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
if param, err = tree.Compute(ctx); err != nil {
|
if param, err = tree.Compute(ctx); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
items[key] = param
|
var keyValue any
|
||||||
|
if keyValue, err = (key.(*scan.Term)).Compute(ctx); err == nil {
|
||||||
|
if kern.IsInteger(keyValue) || kern.IsString(keyValue) {
|
||||||
|
items[keyValue] = param
|
||||||
|
} else {
|
||||||
|
err = key.(*scan.Term).Errorf("dict key can be integer or string, got %s", kern.TypeName(keyValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
v = &items
|
v = &items
|
||||||
|
|||||||
@@ -137,6 +137,11 @@ func evalIterator(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
||||||
v = NewListIterator(list, args)
|
v = NewListIterator(list, args)
|
||||||
}
|
}
|
||||||
|
} else if list, ok := firstChildValue.(*kern.LinkedList); ok {
|
||||||
|
var args []any
|
||||||
|
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
||||||
|
v = NewLinkedListIterator(list, args)
|
||||||
|
}
|
||||||
} else if intVal, ok := firstChildValue.(int64); ok {
|
} else if intVal, ok := firstChildValue.(int64); ok {
|
||||||
var args []any
|
var args []any
|
||||||
if args, err = evalSiblings(ctx, opTerm.Children, intVal); err == nil {
|
if args, err = evalSiblings(ctx, opTerm.Children, intVal); err == nil {
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operand-list.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -------- list term
|
||||||
|
// func newLinkedListTermA(args ...*scan.Term) *scan.Term {
|
||||||
|
// return newLinkedListTerm(0, 0, args)
|
||||||
|
// }
|
||||||
|
|
||||||
|
func newLinkedListTerm(row, col int, args []*scan.Term) *scan.Term {
|
||||||
|
return &scan.Term{
|
||||||
|
Tk: *scan.NewValueToken(row, col, scan.SymLinkedList, "[<>]", args),
|
||||||
|
Parent: nil,
|
||||||
|
Children: nil,
|
||||||
|
Position: scan.PosLeaf,
|
||||||
|
Priority: scan.PriValue,
|
||||||
|
EvalFunc: evalLinkedList,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- list func
|
||||||
|
func evalLinkedList(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
|
list, _ := opTerm.Value().([]*scan.Term)
|
||||||
|
items := kern.NewLinkedList()
|
||||||
|
for _, tree := range list {
|
||||||
|
var param any
|
||||||
|
if param, err = tree.Compute(ctx); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
items.PushBack(param)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
v = items
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
+66
-6
@@ -22,6 +22,26 @@ func newAssignTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
|
v, err = generalEvalAssign(ctx, opTerm, false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDeepCopyAssignTerm(tk *scan.Token) (inst *scan.Term) {
|
||||||
|
return &scan.Term{
|
||||||
|
Tk: *tk,
|
||||||
|
Children: make([]*scan.Term, 0, 2),
|
||||||
|
Position: scan.PosInfix,
|
||||||
|
Priority: scan.PriAssign,
|
||||||
|
EvalFunc: evalDeepCopyAssign,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalDeepCopyAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
|
v, err = generalEvalAssign(ctx, opTerm, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term, value any) (err error) {
|
func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term, value any) (err error) {
|
||||||
var collectionValue, keyListValue, keyValue any
|
var collectionValue, keyListValue, keyValue any
|
||||||
var keyList *kern.ListType
|
var keyList *kern.ListType
|
||||||
@@ -57,23 +77,62 @@ func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *sca
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any) (err error) {
|
func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any, deepCopy bool) (err error) {
|
||||||
if leftTerm.Symbol() == scan.SymIndex {
|
if leftTerm.Symbol() == scan.SymIndex {
|
||||||
err = assignCollectionItem(ctx, leftTerm.Children[0], leftTerm.Children[1], v)
|
err = assignCollectionItem(ctx, leftTerm.Children[0], leftTerm.Children[1], v)
|
||||||
} else {
|
} else {
|
||||||
|
if deepCopy {
|
||||||
|
v = kern.Clone(v)
|
||||||
|
}
|
||||||
ctx.UnsafeSetVar(leftTerm.Source(), v)
|
ctx.UnsafeSetVar(leftTerm.Source(), v)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
func evalAssignDictItem(ctx kern.ExprContext, dotTerm *scan.Term, valueTerm kern.Term) (v any, err error) {
|
||||||
|
var ok bool
|
||||||
|
var dictAny, dotKey any
|
||||||
|
var dict *kern.DictType
|
||||||
|
|
||||||
|
if err = dotTerm.CheckOperands(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dotLeftTerm := dotTerm.GetChild(0)
|
||||||
|
if dictAny, err = dotLeftTerm.Compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if dict, ok = dictAny.(*kern.DictType); !ok {
|
||||||
|
err = dotTerm.Tk.ErrorExpectedGot(kern.DictTypeName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dotRightTerm := dotTerm.Children[1]
|
||||||
|
dotRightSym := dotRightTerm.Symbol()
|
||||||
|
if dotRightSym == scan.SymVariable || dotRightSym == scan.SymString {
|
||||||
|
dotKey = util.UnquoteString(dotRightTerm.Source())
|
||||||
|
} else if dotKey, err = dotRightTerm.Compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err = valueTerm.Compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dict.SetItem(dotKey, v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func generalEvalAssign(ctx kern.ExprContext, opTerm *scan.Term, deepCopy bool) (v any, err error) {
|
||||||
if err = opTerm.CheckOperands(); err != nil {
|
if err = opTerm.CheckOperands(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
leftTerm := opTerm.Children[0]
|
leftTerm := opTerm.Children[0]
|
||||||
leftSym := leftTerm.Symbol()
|
leftSym := leftTerm.Symbol()
|
||||||
if leftSym != scan.SymVariable && leftSym != scan.SymIndex {
|
if leftSym == scan.SymDot {
|
||||||
|
return evalAssignDictItem(ctx, opTerm.Children[0], opTerm.GetChild(1))
|
||||||
|
} else if leftSym != scan.SymVariable && leftSym != scan.SymIndex {
|
||||||
err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source())
|
err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -93,10 +152,10 @@ func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
err = opTerm.Errorf("unknown function %s()", rightChild.Source())
|
err = opTerm.Errorf("unknown function %s()", rightChild.Source())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = assignValue(ctx, leftTerm, v)
|
err = assignValue(ctx, leftTerm, v, deepCopy)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = assignValue(ctx, leftTerm, v)
|
err = assignValue(ctx, leftTerm, v, deepCopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -203,7 +262,7 @@ func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
err = opTerm.Errorf("unsupported assign operator %q", opTerm.Source())
|
err = opTerm.Errorf("unsupported assign operator %q", opTerm.Source())
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = assignValue(ctx, leftTerm, v)
|
err = assignValue(ctx, leftTerm, v, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,6 +272,7 @@ func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymEqual, newAssignTerm)
|
scan.RegisterTermConstructor(scan.SymEqual, newAssignTerm)
|
||||||
|
scan.RegisterTermConstructor(scan.SymColonEqual, newDeepCopyAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymPlusEqual, newOpAssignTerm)
|
scan.RegisterTermConstructor(scan.SymPlusEqual, newOpAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymMinusEqual, newOpAssignTerm)
|
scan.RegisterTermConstructor(scan.SymMinusEqual, newOpAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymStarEqual, newOpAssignTerm)
|
scan.RegisterTermConstructor(scan.SymStarEqual, newOpAssignTerm)
|
||||||
|
|||||||
+8
-22
@@ -40,36 +40,22 @@ func evalContextValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sourceCtx != nil {
|
if sourceCtx != nil {
|
||||||
if formatter, ok := sourceCtx.(kern.DictFormat); ok {
|
v = sourceCtx.ToDict()
|
||||||
v = formatter.ToDict()
|
|
||||||
} else if formatter, ok := sourceCtx.(kern.Formatter); ok {
|
|
||||||
v = formatter.ToString(0)
|
|
||||||
} else {
|
|
||||||
// keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
|
|
||||||
keys := sourceCtx.EnumVars(nil)
|
|
||||||
d := make(map[string]any)
|
|
||||||
for _, key := range keys {
|
|
||||||
d[key], _ = sourceCtx.GetVar(key)
|
|
||||||
}
|
|
||||||
keys = sourceCtx.EnumFuncs(func(name string) bool { return true })
|
|
||||||
for _, key := range keys {
|
|
||||||
d[key], _ = sourceCtx.GetFuncInfo(key)
|
|
||||||
}
|
|
||||||
v = d
|
|
||||||
}
|
|
||||||
} else if childValue != nil {
|
} else if childValue != nil {
|
||||||
if it, ok := childValue.(kern.Iterator); ok {
|
it, ok := childValue.(kern.Iterator)
|
||||||
|
if !ok {
|
||||||
|
it, err = NewIterator(ctx, childValue, nil)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
var item any
|
var item any
|
||||||
values := kern.NewListA()
|
values := kern.NewLinkedListA()
|
||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||||
values.AppendItem(item)
|
values.PushBack(item)
|
||||||
}
|
}
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
err = nil
|
err = nil
|
||||||
v = values
|
v = values
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||||
|
|||||||
+2
-2
@@ -45,7 +45,7 @@ func evalDigest(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||||
ctx.SetVar("_", item)
|
ctx.SetVar("_", item)
|
||||||
ctx.SetVar("__", it.Index())
|
ctx.SetVar("__", it.Index())
|
||||||
ctx.SetVar("_#", it.Count())
|
ctx.SetVar("#", it.Count())
|
||||||
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||||
if rightValue == nil {
|
if rightValue == nil {
|
||||||
break
|
break
|
||||||
@@ -53,7 +53,7 @@ func evalDigest(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
lastValue = rightValue
|
lastValue = rightValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.DeleteVar("_#")
|
ctx.DeleteVar("#")
|
||||||
ctx.DeleteVar("__")
|
ctx.DeleteVar("__")
|
||||||
ctx.DeleteVar("_")
|
ctx.DeleteVar("_")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+46
-16
@@ -7,6 +7,7 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- dot term
|
// -------- dot term
|
||||||
@@ -34,7 +35,7 @@ func evalDot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
switch unboxedValue := leftValue.(type) {
|
switch unboxedValue := leftValue.(type) {
|
||||||
case kern.ExtIterator:
|
case kern.ExtIterator:
|
||||||
if indexTerm.Tk.Sym == scan.SymVariable /*|| indexTerm.Tk.Sym == scan.SymString */ {
|
if indexTerm.Symbol() == scan.SymVariable /*|| indexTerm.Tk.Sym == scan.SymString */ {
|
||||||
opName := indexTerm.Source()
|
opName := indexTerm.Source()
|
||||||
if unboxedValue.HasOperation(opName) {
|
if unboxedValue.HasOperation(opName) {
|
||||||
v, err = unboxedValue.CallOperation(opName, map[string]any{})
|
v, err = unboxedValue.CallOperation(opName, map[string]any{})
|
||||||
@@ -46,21 +47,22 @@ func evalDot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
err = indexTerm.Tk.ErrorExpectedGot("identifier")
|
err = indexTerm.Tk.ErrorExpectedGot("identifier")
|
||||||
}
|
}
|
||||||
case *kern.DictType:
|
case *kern.DictType:
|
||||||
var ok bool
|
// var ok bool
|
||||||
s := opTerm.Children[1].Tk.Sym
|
// s := opTerm.Children[1].Symbol()
|
||||||
if s == scan.SymVariable || s == scan.SymString {
|
// if s == scan.SymVariable || s == scan.SymString {
|
||||||
src := opTerm.Children[1].Source()
|
// src := opTerm.Children[1].Source()
|
||||||
if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
|
// if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
|
||||||
src = src[1 : len(src)-1]
|
// src = src[1 : len(src)-1]
|
||||||
}
|
// }
|
||||||
if v, ok = unboxedValue.GetItem(src); !ok {
|
// if v, ok = unboxedValue.GetItem(src); !ok {
|
||||||
err = opTerm.Errorf("key %q not found", src)
|
// err = opTerm.Errorf("key %q not found", src)
|
||||||
}
|
// }
|
||||||
} else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
// } else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||||
if v, ok = unboxedValue.GetItem(rightValue); !ok {
|
// if v, ok = unboxedValue.GetItem(rightValue); !ok {
|
||||||
err = opTerm.Errorf("key %q not found", rightValue)
|
// err = opTerm.Errorf("key %q not found", rightValue)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
v, err = dotGetDictItemValue(ctx, unboxedValue, opTerm.Children[1])
|
||||||
default:
|
default:
|
||||||
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||||
err = opTerm.Errorf("incompatible types: %s and %s", kern.TypeName(leftValue), kern.TypeName(rightValue))
|
err = opTerm.Errorf("incompatible types: %s and %s", kern.TypeName(leftValue), kern.TypeName(rightValue))
|
||||||
@@ -69,6 +71,34 @@ func evalDot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dotGetDictItemValue(ctx kern.ExprContext, d *kern.DictType, rightTerm *scan.Term) (v any, err error) {
|
||||||
|
var ok bool
|
||||||
|
var rightValue any
|
||||||
|
s := rightTerm.Symbol()
|
||||||
|
if s == scan.SymVariable || s == scan.SymString {
|
||||||
|
// src := rightTerm.Source()
|
||||||
|
// if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
|
||||||
|
// src = src[1 : len(src)-1]
|
||||||
|
// }
|
||||||
|
src := util.UnquoteString(rightTerm.Source())
|
||||||
|
if v, ok = d.GetItem(src); !ok {
|
||||||
|
err = errDictKeyNotFound(rightTerm, src)
|
||||||
|
}
|
||||||
|
} else if rightValue, err = rightTerm.Compute(ctx); err == nil {
|
||||||
|
if v, ok = d.GetItem(rightValue); !ok {
|
||||||
|
err = errDictKeyNotFound(rightTerm, rightValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func errDictKeyNotFound(term *scan.Term, key any) error {
|
||||||
|
if s, ok := key.(string); ok {
|
||||||
|
return term.Errorf("key %q not found", s)
|
||||||
|
}
|
||||||
|
return term.Errorf("key %v not found", key)
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymDot, newDotTerm)
|
scan.RegisterTermConstructor(scan.SymDot, newDotTerm)
|
||||||
|
|||||||
+2
-2
@@ -110,9 +110,9 @@ func (it *filterIterator) Next() (item any, err error) {
|
|||||||
for attempt, err = it.itSrc.Next(); err == nil; attempt, err = it.itSrc.Next() {
|
for attempt, err = it.itSrc.Next(); err == nil; attempt, err = it.itSrc.Next() {
|
||||||
ctx.SetVar("_", attempt)
|
ctx.SetVar("_", attempt)
|
||||||
ctx.SetVar("__", it.Index())
|
ctx.SetVar("__", it.Index())
|
||||||
ctx.SetVar("_#", it.Count())
|
ctx.SetVar("#", it.Count())
|
||||||
result, err = it.expr.Compute(ctx)
|
result, err = it.expr.Compute(ctx)
|
||||||
ctx.DeleteVar("_#")
|
ctx.DeleteVar("#")
|
||||||
ctx.DeleteVar("__")
|
ctx.DeleteVar("__")
|
||||||
ctx.DeleteVar("_")
|
ctx.DeleteVar("_")
|
||||||
|
|
||||||
|
|||||||
+7
-3
@@ -62,7 +62,7 @@ func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||||
ctx.SetVar("_", item)
|
ctx.SetVar("_", item)
|
||||||
ctx.SetVar("__", it.Index())
|
ctx.SetVar("__", it.Index())
|
||||||
ctx.SetVar("_#", it.Count())
|
ctx.SetVar("#", it.Count())
|
||||||
|
|
||||||
var sItemKey string
|
var sItemKey string
|
||||||
|
|
||||||
@@ -71,7 +71,11 @@ func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
sItemKey = strconv.Itoa(int(it.Index()))
|
sItemKey = strconv.Itoa(int(it.Index()))
|
||||||
} else if d.HasKey(sKey) {
|
} else if d.HasKey(sKey) {
|
||||||
if keyValue, exists := d.GetItem(sKey); exists {
|
if keyValue, exists := d.GetItem(sKey); exists {
|
||||||
sItemKey = fmt.Sprintf("%v", keyValue)
|
if s, ok := keyValue.(string); ok {
|
||||||
|
sItemKey = s
|
||||||
|
} else {
|
||||||
|
sItemKey = fmt.Sprintf("%v", keyValue)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sItemKey = "_"
|
sItemKey = "_"
|
||||||
}
|
}
|
||||||
@@ -92,7 +96,7 @@ func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
ls.AppendItem(item)
|
ls.AppendItem(item)
|
||||||
values.SetItem(sItemKey, ls)
|
values.SetItem(sItemKey, ls)
|
||||||
|
|
||||||
ctx.DeleteVar("_#")
|
ctx.DeleteVar("#")
|
||||||
ctx.DeleteVar("__")
|
ctx.DeleteVar("__")
|
||||||
ctx.DeleteVar("_")
|
ctx.DeleteVar("_")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,11 @@ func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
||||||
v = (*unboxedValue)[index]
|
v = (*unboxedValue)[index]
|
||||||
}
|
}
|
||||||
|
case *kern.LinkedList:
|
||||||
|
var index int
|
||||||
|
if index, err = verifyIndex(indexTerm, indexList, unboxedValue.Len()); err == nil {
|
||||||
|
v, err = unboxedValue.At(index)
|
||||||
|
}
|
||||||
case string:
|
case string:
|
||||||
var index int
|
var index int
|
||||||
if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil {
|
if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil {
|
||||||
@@ -107,6 +112,11 @@ func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
sublist := kern.ListType((*unboxedValue)[start:end])
|
sublist := kern.ListType((*unboxedValue)[start:end])
|
||||||
v = &sublist
|
v = &sublist
|
||||||
}
|
}
|
||||||
|
case *kern.LinkedList:
|
||||||
|
var start, end int
|
||||||
|
if start, end, err = verifyRange(indexTerm, indexList, unboxedValue.Len()); err == nil {
|
||||||
|
v = unboxedValue.Sub(start, end)
|
||||||
|
}
|
||||||
case string:
|
case string:
|
||||||
var start, end int
|
var start, end int
|
||||||
if start, end, err = verifyRange(indexTerm, indexList, len(unboxedValue)); err == nil {
|
if start, end, err = verifyRange(indexTerm, indexList, len(unboxedValue)); err == nil {
|
||||||
|
|||||||
+18
-20
@@ -31,13 +31,11 @@ func newAppendTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func prependToList(ctx kern.ExprContext, opTerm *scan.Term, leftValue, rightValue any) (result any, err error) {
|
func prependToList(opTerm *scan.Term, leftValue, rightValue any) (result any, err error) {
|
||||||
if list, ok := rightValue.(*kern.ListType); ok {
|
if list, ok := rightValue.(*kern.ListType); ok {
|
||||||
var it kern.Iterator
|
var it kern.Iterator
|
||||||
if it, ok = leftValue.(kern.Iterator); !ok {
|
if it, ok = leftValue.(kern.Iterator); !ok {
|
||||||
if it, err = NewIterator(ctx, leftValue, nil); err != nil {
|
it = NewFormalIterator(leftValue)
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ls := kern.NewLinkedList()
|
ls := kern.NewLinkedList()
|
||||||
@@ -54,12 +52,13 @@ func prependToList(ctx kern.ExprContext, opTerm *scan.Term, leftValue, rightValu
|
|||||||
newList = append(newList, item)
|
newList = append(newList, item)
|
||||||
}
|
}
|
||||||
result = &newList
|
result = &newList
|
||||||
|
} else if list, ok := rightValue.(*kern.LinkedList); ok {
|
||||||
|
var it kern.Iterator
|
||||||
|
if it, ok = leftValue.(kern.Iterator); !ok {
|
||||||
|
it = NewFormalIterator(leftValue)
|
||||||
|
}
|
||||||
|
|
||||||
// ***** EVENTUALMENTE ABILITARE LA MODIFICA DELLA VARIABILE
|
result = list.SeqPushBack(it)
|
||||||
// ***** CON UN OPERATORE SPECIFICO
|
|
||||||
// if opTerm.Children[1].Symbol() == scan.SymVariable {
|
|
||||||
// ctx.UnsafeSetVar(opTerm.Children[1].Source(), result)
|
|
||||||
// }
|
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
@@ -73,17 +72,15 @@ func evalPrepend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err = prependToList(ctx, opTerm, leftValue, rightValue)
|
v, err = prependToList(opTerm, leftValue, rightValue)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendToList(ctx kern.ExprContext, opTerm *scan.Term, leftValue, rightValue any) (result any, err error) {
|
func appendToList(opTerm *scan.Term, leftValue, rightValue any) (result any, err error) {
|
||||||
if list, ok := leftValue.(*kern.ListType); ok {
|
if list, ok := leftValue.(*kern.ListType); ok {
|
||||||
var it kern.Iterator
|
var it kern.Iterator
|
||||||
if it, ok = rightValue.(kern.Iterator); !ok {
|
if it, ok = rightValue.(kern.Iterator); !ok {
|
||||||
if it, err = NewIterator(ctx, rightValue, nil); err != nil {
|
it = NewFormalIterator(rightValue)
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ls := kern.NewLinkedList()
|
ls := kern.NewLinkedList()
|
||||||
@@ -100,12 +97,13 @@ func appendToList(ctx kern.ExprContext, opTerm *scan.Term, leftValue, rightValue
|
|||||||
newList = append(newList, node.Data())
|
newList = append(newList, node.Data())
|
||||||
}
|
}
|
||||||
result = &newList
|
result = &newList
|
||||||
|
} else if list, ok := leftValue.(*kern.LinkedList); ok {
|
||||||
|
var it kern.Iterator
|
||||||
|
if it, ok = rightValue.(kern.Iterator); !ok {
|
||||||
|
it = NewFormalIterator(rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
// ***** EVENTUALMENTE ABILITARE LA MODIFICA DELLA VARIABILE
|
result = list.SeqPushBack(it)
|
||||||
// ***** CON UN OPERATORE SPECIFICO
|
|
||||||
// if opTerm.Children[0].Symbol() == scan.SymVariable {
|
|
||||||
// ctx.UnsafeSetVar(opTerm.Children[0].Source(), result)
|
|
||||||
// }
|
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
@@ -119,7 +117,7 @@ func evalAppend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err = appendToList(ctx, opTerm, leftValue, rightValue)
|
v, err = appendToList(opTerm, leftValue, rightValue)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ func evalLength(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
} else if kern.IsDict(childValue) {
|
} else if kern.IsDict(childValue) {
|
||||||
m, _ := childValue.(*kern.DictType)
|
m, _ := childValue.(*kern.DictType)
|
||||||
v = int64(len(*m))
|
v = int64(len(*m))
|
||||||
|
} else if lls, ok := childValue.(*kern.LinkedList); ok {
|
||||||
|
v = int64(lls.Len())
|
||||||
} else if it, ok := childValue.(kern.Iterator); ok {
|
} else if it, ok := childValue.(kern.Iterator); ok {
|
||||||
v = int64(it.Count())
|
v = int64(it.Count())
|
||||||
// if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
|
// if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
|
||||||
|
|||||||
+2
-2
@@ -40,7 +40,7 @@ func evalRightShift(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if v, err = bitRightShift(opTerm, leftValue, rightValue); err != nil {
|
if v, err = bitRightShift(opTerm, leftValue, rightValue); err != nil {
|
||||||
v, err = prependToList(ctx, opTerm, leftValue, rightValue)
|
v, err = prependToList(opTerm, leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,7 @@ func evalLeftShift(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if v, err = bitLeftShift(opTerm, leftValue, rightValue); err != nil {
|
if v, err = bitLeftShift(opTerm, leftValue, rightValue); err != nil {
|
||||||
v, err = appendToList(ctx, opTerm, leftValue, rightValue)
|
v, err = appendToList(opTerm, leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
@@ -163,16 +165,16 @@ func paramAlreadyDefined(args []*scan.Term, param *scan.Term) (position int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext) (listTerm *scan.Term, err error) {
|
func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext, termSym scan.Symbol) (listTerm *scan.Term, err error) {
|
||||||
r, c := scanner.LastPos()
|
r, c := scanner.LastPos()
|
||||||
args := make([]*scan.Term, 0)
|
args := make([]*scan.Term, 0)
|
||||||
lastSym := scan.SymUnknown
|
lastSym := scan.SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
itemCtx := remFlags(ctx, allowIndex)
|
itemCtx := remFlags(ctx, allowIndex)
|
||||||
for lastSym != scan.SymClosedSquare && lastSym != scan.SymEos {
|
for lastSym != termSym && lastSym != scan.SymEos {
|
||||||
zeroRequired := scanner.Current().Sym == scan.SymColon
|
zeroRequired := scanner.Current().Sym == scan.SymColon
|
||||||
var itemTree *scan.Ast
|
var itemTree *scan.Ast
|
||||||
if itemTree, err = parser.parseItem(scanner, itemCtx, scan.SymComma, scan.SymClosedSquare); err == nil {
|
if itemTree, err = parser.parseItem(scanner, itemCtx, scan.SymComma, termSym); err == nil {
|
||||||
root := itemTree.Root()
|
root := itemTree.Root()
|
||||||
if root != nil {
|
if root != nil {
|
||||||
if hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymColon {
|
if hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymColon {
|
||||||
@@ -211,10 +213,14 @@ func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext) (listT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if lastSym != scan.SymClosedSquare {
|
if lastSym != termSym {
|
||||||
err = scanner.Previous().ErrorExpectedGot("]")
|
err = scanner.Previous().ErrorExpectedGot("]")
|
||||||
} else {
|
} else if termSym == scan.SymClosedSquare {
|
||||||
listTerm = newListTerm(r, c, args)
|
listTerm = newListTerm(r, c, args)
|
||||||
|
} else if termSym == scan.SymGreaterClosedSquare {
|
||||||
|
listTerm = newLinkedListTerm(r, c, args)
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("[%d:%d] unknown list type", r, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -251,24 +257,41 @@ func (parser *parser) parseIterDef(scanner *scan.Scanner, ctx parserContext) (su
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseDictKey(scanner *scan.Scanner) (key any, err error) {
|
// func (parser *parser) parseDictKey(scanner *scan.Scanner) (key any, err error) {
|
||||||
tk := parser.Next(scanner)
|
// tk := parser.Next(scanner)
|
||||||
if tk.Sym == scan.SymError {
|
// if tk.Sym == scan.SymError {
|
||||||
err = tk.Error()
|
// err = tk.Error()
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if tk.Sym == scan.SymClosedBrace || tk.Sym == scan.SymEos {
|
// if tk.Sym == scan.SymClosedBrace || tk.Sym == scan.SymEos {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if tk.Sym == scan.SymInteger || tk.Sym == scan.SymString {
|
// if tk.Sym == scan.SymInteger || tk.Sym == scan.SymString || tk.Sym == scan.SymIdentifier {
|
||||||
tkSep := parser.Next(scanner)
|
// tkSep := parser.Next(scanner)
|
||||||
if tkSep.Sym != scan.SymColon {
|
// if tkSep.Sym != scan.SymColon {
|
||||||
|
// err = tkSep.ErrorExpectedGot(":")
|
||||||
|
// } else {
|
||||||
|
// key = tk.Value
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// err = tk.ErrorExpectedGot("dictionary-key or }")
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (parser *parser) parseDictKey(scanner *scan.Scanner) (key *scan.Term, err error) {
|
||||||
|
var keyTree *scan.Ast
|
||||||
|
if keyTree, err = parser.parseItem(scanner, parserNoFlags, scan.SymColon, scan.SymClosedBrace); err == nil {
|
||||||
|
key = keyTree.Root()
|
||||||
|
tkSep := scanner.Previous()
|
||||||
|
sym := tkSep.Sym
|
||||||
|
if sym == scan.SymClosedBrace || sym == scan.SymEos {
|
||||||
|
if key != nil {
|
||||||
|
err = tkSep.ErrorExpectedGot(":")
|
||||||
|
}
|
||||||
|
} else if sym != scan.SymColon {
|
||||||
err = tkSep.ErrorExpectedGot(":")
|
err = tkSep.ErrorExpectedGot(":")
|
||||||
} else {
|
|
||||||
key = tk.Value
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
err = tk.ErrorExpectedGot("dictionary-key or }")
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -278,11 +301,11 @@ func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext)
|
|||||||
lastSym := scan.SymUnknown
|
lastSym := scan.SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
for lastSym != scan.SymClosedBrace && lastSym != scan.SymEos {
|
for lastSym != scan.SymClosedBrace && lastSym != scan.SymEos {
|
||||||
var subTree *scan.Ast
|
var valueTree *scan.Ast
|
||||||
var key any
|
var key any
|
||||||
if key, err = parser.parseDictKey(scanner); err != nil {
|
if key, err = parser.parseDictKey(scanner); err != nil {
|
||||||
break
|
break
|
||||||
} else if key == nil {
|
} else if key.(*scan.Term) == nil {
|
||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
lastSym = tk.Sym
|
lastSym = tk.Sym
|
||||||
if itemExpected {
|
if itemExpected {
|
||||||
@@ -290,9 +313,9 @@ func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext)
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedBrace); err == nil {
|
if valueTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedBrace); err == nil {
|
||||||
if subTree.Root() != nil {
|
if valueTree.Root() != nil {
|
||||||
args[key] = subTree.Root()
|
args[key] = valueTree.Root()
|
||||||
} else /*if key != nil*/ {
|
} else /*if key != nil*/ {
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
err = prev.ErrorExpectedGot("dictionary-value")
|
err = prev.ErrorExpectedGot("dictionary-value")
|
||||||
@@ -326,7 +349,7 @@ func (parser *parser) parseSelectorCase(scanner *scan.Scanner, ctx parserContext
|
|||||||
err = tk.Errorf("case list in default clause")
|
err = tk.Errorf("case list in default clause")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex)); err != nil {
|
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex), scan.SymClosedSquare); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tk = parser.Next(scanner)
|
tk = parser.Next(scanner)
|
||||||
@@ -401,7 +424,8 @@ func couldBeACollection(t *scan.Term) bool {
|
|||||||
if t != nil {
|
if t != nil {
|
||||||
sym = t.Symbol()
|
sym = t.Symbol()
|
||||||
}
|
}
|
||||||
return sym == scan.SymList || sym == scan.SymString || sym == scan.SymDict || sym == scan.SymExpression || sym == scan.SymVariable
|
// return sym == scan.SymList || sym == scan.SymString || sym == scan.SymDict || sym == scan.SymExpression || sym == scan.SymVariable
|
||||||
|
return slices.Contains([]scan.Symbol{scan.SymList, scan.SymLinkedList, scan.SymString, scan.SymDict, scan.SymExpression, scan.SymVariable, scan.SymIndex}, sym)
|
||||||
}
|
}
|
||||||
|
|
||||||
func listSubTree(tree *scan.Ast, listTerm *scan.Term, allowIndeces bool) (root *scan.Term, err error) {
|
func listSubTree(tree *scan.Ast, listTerm *scan.Term, allowIndeces bool) (root *scan.Term, err error) {
|
||||||
@@ -444,7 +468,7 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
|||||||
tree = scan.NewAst()
|
tree = scan.NewAst()
|
||||||
firstToken := true
|
firstToken := true
|
||||||
// lastSym := SymUnknown
|
// lastSym := SymUnknown
|
||||||
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) {
|
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); tk = parser.Next(scanner) {
|
||||||
// if tk.Sym == SymComment {
|
// if tk.Sym == SymComment {
|
||||||
// continue
|
// continue
|
||||||
// }
|
// }
|
||||||
@@ -457,7 +481,7 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
|||||||
selectorTerm = nil
|
selectorTerm = nil
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.Source())
|
err = tk.ErrorExpectedOneOfGot(termSymbols...)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -489,7 +513,13 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
|||||||
case scan.SymOpenSquare:
|
case scan.SymOpenSquare:
|
||||||
var listTerm *scan.Term
|
var listTerm *scan.Term
|
||||||
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
|
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
|
||||||
if listTerm, err = parser.parseList(scanner, newCtx); err == nil {
|
if listTerm, err = parser.parseList(scanner, newCtx, scan.SymClosedSquare); err == nil {
|
||||||
|
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||||
|
}
|
||||||
|
case scan.SymOpenSquareLess:
|
||||||
|
var listTerm *scan.Term
|
||||||
|
newCtx := addFlagsCond(addFlags(ctx, listContext), allowIndex, false)
|
||||||
|
if listTerm, err = parser.parseList(scanner, newCtx, scan.SymGreaterClosedSquare); err == nil {
|
||||||
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||||
}
|
}
|
||||||
case scan.SymOpenBrace:
|
case scan.SymOpenBrace:
|
||||||
|
|||||||
+1
-1
@@ -98,7 +98,7 @@ func (ast *Ast) insert(tree, node *Term) (root *Term, err error) {
|
|||||||
root = node
|
root = node
|
||||||
tree.SetParent(node)
|
tree.SetParent(node)
|
||||||
} else {
|
} else {
|
||||||
err = node.Errorf("two adjacent operators: %q and %q", tree, node)
|
err = node.Errorf("two adjacent operators: %q and %q", tree, node.Source())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-1
@@ -192,6 +192,8 @@ func (scanner *Scanner) fetchNextToken() (tk *Token) {
|
|||||||
case ':':
|
case ':':
|
||||||
if next, _ := scanner.peek(); next == ':' {
|
if next, _ := scanner.peek(); next == ':' {
|
||||||
tk = scanner.moveOn(SymDoubleColon, ch, next)
|
tk = scanner.moveOn(SymDoubleColon, ch, next)
|
||||||
|
} else if next == '=' {
|
||||||
|
tk = scanner.moveOn(SymColonEqual, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = scanner.MakeToken(SymColon, ch)
|
tk = scanner.MakeToken(SymColon, ch)
|
||||||
}
|
}
|
||||||
@@ -313,6 +315,8 @@ func (scanner *Scanner) fetchNextToken() (tk *Token) {
|
|||||||
} else {
|
} else {
|
||||||
tk = scanner.accept(SymDoubleGreater, ch, next)
|
tk = scanner.accept(SymDoubleGreater, ch, next)
|
||||||
}
|
}
|
||||||
|
} else if next == ']' {
|
||||||
|
tk = scanner.moveOn(SymGreaterClosedSquare, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = scanner.MakeToken(SymGreater, ch)
|
tk = scanner.MakeToken(SymGreater, ch)
|
||||||
}
|
}
|
||||||
@@ -342,7 +346,11 @@ func (scanner *Scanner) fetchNextToken() (tk *Token) {
|
|||||||
case ')':
|
case ')':
|
||||||
tk = scanner.MakeToken(SymClosedRound, ch)
|
tk = scanner.MakeToken(SymClosedRound, ch)
|
||||||
case '[':
|
case '[':
|
||||||
tk = scanner.MakeToken(SymOpenSquare, ch)
|
if next, _ := scanner.peek(); next == '<' {
|
||||||
|
tk = scanner.moveOn(SymOpenSquareLess, ch, next)
|
||||||
|
} else {
|
||||||
|
tk = scanner.MakeToken(SymOpenSquare, ch)
|
||||||
|
}
|
||||||
case ']':
|
case ']':
|
||||||
tk = scanner.MakeToken(SymClosedSquare, ch)
|
tk = scanner.MakeToken(SymClosedSquare, ch)
|
||||||
case '{':
|
case '{':
|
||||||
|
|||||||
+77
-92
@@ -32,79 +32,82 @@ type symbolSpec struct {
|
|||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
symbolMap = map[Symbol]symbolSpec{
|
symbolMap = map[Symbol]symbolSpec{
|
||||||
SymUnknown: {"<unknown>", SymClassOther, PosLeaf}, // -1: Unknown symbol
|
SymUnknown: {"<unknown>", SymClassOther, PosLeaf}, // -1: Unknown symbol
|
||||||
SymNone: {"<null>", SymClassOther, PosLeaf}, // 0: Null value for variable of type symbol
|
SymNone: {"<null>", SymClassOther, PosLeaf}, // 0: Null value for variable of type symbol
|
||||||
SymError: {"<error>", SymClassOther, PosLeaf}, // 1: Error reading from stream
|
SymError: {"<error>", SymClassOther, PosLeaf}, // 1: Error reading from stream
|
||||||
SymEos: {"<eos>", SymClassOther, PosLeaf}, // 2: End of stream
|
SymEos: {"<eos>", SymClassOther, PosLeaf}, // 2: End of stream
|
||||||
SymMinus: {"-", SymClassOperator, PosInfix}, // 3: '-'
|
SymMinus: {"-", SymClassOperator, PosInfix}, // 3: '-'
|
||||||
SymMinusEqual: {"-=", SymClassOperator, PosInfix}, // 4: '-='
|
SymMinusEqual: {"-=", SymClassOperator, PosInfix}, // 4: '-='
|
||||||
SymDoubleMinus: {"--", SymClassOperator, PosPostfix}, // 5: '--'
|
SymDoubleMinus: {"--", SymClassOperator, PosPostfix}, // 5: '--'
|
||||||
SymPlus: {"+", SymClassOperator, PosInfix}, // 6: '+'
|
SymPlus: {"+", SymClassOperator, PosInfix}, // 6: '+'
|
||||||
SymPlusEqual: {"+=", SymClassOperator, PosInfix}, // 7: '+='
|
SymPlusEqual: {"+=", SymClassOperator, PosInfix}, // 7: '+='
|
||||||
SymDoublePlus: {"++", SymClassOperator, PosPostfix}, // 8: '++'
|
SymDoublePlus: {"++", SymClassOperator, PosPostfix}, // 8: '++'
|
||||||
SymStar: {"*", SymClassOperator, PosInfix}, // 9: '*'
|
SymStar: {"*", SymClassOperator, PosInfix}, // 9: '*'
|
||||||
SymDoubleStar: {"**", SymClassOperator, PosInfix}, // 10: '**'
|
SymDoubleStar: {"**", SymClassOperator, PosInfix}, // 10: '**'
|
||||||
SymSlash: {"/", SymClassOperator, PosInfix}, // 11: '/'
|
SymSlash: {"/", SymClassOperator, PosInfix}, // 11: '/'
|
||||||
SymBackSlash: {"\\", SymClassOperator, PosLeaf}, // 12: '\'
|
SymBackSlash: {"\\", SymClassOperator, PosLeaf}, // 12: '\'
|
||||||
SymVertBar: {"|", SymClassOperator, PosInfix}, // 13: '|'
|
SymVertBar: {"|", SymClassOperator, PosInfix}, // 13: '|'
|
||||||
SymDoubleVertBar: {"||", SymClassOperator, PosInfix}, // 14: '||'
|
SymDoubleVertBar: {"||", SymClassOperator, PosInfix}, // 14: '||'
|
||||||
SymComma: {",", SymClassOperator, PosInfix}, // 15: ','
|
SymComma: {",", SymClassOperator, PosInfix}, // 15: ','
|
||||||
SymColon: {":", SymClassOperator, PosInfix}, // 16: ':'
|
SymColon: {":", SymClassOperator, PosInfix}, // 16: ':'
|
||||||
SymSemiColon: {";", SymClassOperator, PosInfix}, // 17: ';'
|
SymSemiColon: {";", SymClassOperator, PosInfix}, // 17: ';'
|
||||||
SymDot: {".", SymClassOperator, PosInfix}, // 18: '.'
|
SymDot: {".", SymClassOperator, PosInfix}, // 18: '.'
|
||||||
SymDotSlash: {"./", SymClassOperator, PosInfix}, // 19: './'
|
SymDotSlash: {"./", SymClassOperator, PosInfix}, // 19: './'
|
||||||
SymQuote: {"'", SymClassDelimiter, PosLeaf}, // 20: '\''
|
SymQuote: {"'", SymClassDelimiter, PosLeaf}, // 20: '\''
|
||||||
SymDoubleQuote: {"\"", SymClassDelimiter, PosLeaf}, // 21: '"'
|
SymDoubleQuote: {"\"", SymClassDelimiter, PosLeaf}, // 21: '"'
|
||||||
SymBackTick: {"`", SymClassDelimiter, PosLeaf}, // 22: '`'
|
SymBackTick: {"`", SymClassDelimiter, PosLeaf}, // 22: '`'
|
||||||
SymExclamation: {"!", SymClassOperator, PosPostfix}, // 23: '!'
|
SymExclamation: {"!", SymClassOperator, PosPostfix}, // 23: '!'
|
||||||
SymQuestion: {"?", SymClassOperator, PosInfix}, // 24: '?'
|
SymQuestion: {"?", SymClassOperator, PosInfix}, // 24: '?'
|
||||||
SymAmpersand: {"&", SymClassOperator, PosInfix}, // 25: '&'
|
SymAmpersand: {"&", SymClassOperator, PosInfix}, // 25: '&'
|
||||||
SymDoubleAmpersand: {"&&", SymClassOperator, PosInfix}, // 26: '&&'
|
SymDoubleAmpersand: {"&&", SymClassOperator, PosInfix}, // 26: '&&'
|
||||||
SymPercent: {"%", SymClassOperator, PosInfix}, // 27: '%'
|
SymPercent: {"%", SymClassOperator, PosInfix}, // 27: '%'
|
||||||
SymAt: {"@", SymClassOperator, PosPrefix}, // 28: '@'
|
SymAt: {"@", SymClassOperator, PosPrefix}, // 28: '@'
|
||||||
SymUndescore: {"_", SymClassIdentifier, PosLeaf}, // 29: '_'
|
SymUndescore: {"_", SymClassIdentifier, PosLeaf}, // 29: '_'
|
||||||
SymEqual: {"=", SymClassOperator, PosInfix}, // 30: '='
|
SymEqual: {"=", SymClassOperator, PosInfix}, // 30: '='
|
||||||
SymDoubleEqual: {"==", SymClassOperator, PosInfix}, // 31: '=='
|
SymColonEqual: {":=", SymClassOperator, PosInfix}, // 31: ':='
|
||||||
SymLess: {"<", SymClassOperator, PosInfix}, // 32: '<'
|
SymDoubleEqual: {"==", SymClassOperator, PosInfix}, // 32: '=='
|
||||||
SymLessOrEqual: {"<=", SymClassOperator, PosInfix}, // 33: '<='
|
SymLess: {"<", SymClassOperator, PosInfix}, // 33: '<'
|
||||||
SymGreater: {">", SymClassOperator, PosInfix}, // 34: '>'
|
SymLessOrEqual: {"<=", SymClassOperator, PosInfix}, // 34: '<='
|
||||||
SymGreaterOrEqual: {">=", SymClassOperator, PosInfix}, // 35: '>='
|
SymGreater: {">", SymClassOperator, PosInfix}, // 35: '>'
|
||||||
SymLessGreater: {"<>", SymClassOperator, PosInfix}, // 36: '<>'
|
SymGreaterOrEqual: {">=", SymClassOperator, PosInfix}, // 36: '>='
|
||||||
SymNotEqual: {"!=", SymClassOperator, PosInfix}, // 37: '!='
|
SymLessGreater: {"<>", SymClassOperator, PosInfix}, // 37: '<>'
|
||||||
SymDollar: {"$", SymClassOperator, PosPrefix}, // 38: '$'
|
SymNotEqual: {"!=", SymClassOperator, PosInfix}, // 38: '!='
|
||||||
SymHash: {"#", SymClassOperator, PosPrefix}, // 39: '#'
|
SymDollar: {"$", SymClassOperator, PosPrefix}, // 39: '$'
|
||||||
SymOpenRound: {"(", SymClassParenthesis, PosPrefix}, // 40: '('
|
SymHash: {"#", SymClassOperator, PosPrefix}, // 40: '#'
|
||||||
SymClosedRound: {")", SymClassParenthesis, PosPostfix}, // 41: ')'
|
SymOpenRound: {"(", SymClassParenthesis, PosPrefix}, // 41: '('
|
||||||
SymOpenSquare: {"[", SymClassParenthesis, PosPrefix}, // 42: '['
|
SymClosedRound: {")", SymClassParenthesis, PosPostfix}, // 42: ')'
|
||||||
SymClosedSquare: {"]", SymClassParenthesis, PosPostfix}, // 43: ']'
|
SymOpenSquare: {"[", SymClassParenthesis, PosPrefix}, // 43: '['
|
||||||
SymOpenBrace: {"{", SymClassParenthesis, PosPrefix}, // 44: '{'
|
SymClosedSquare: {"]", SymClassParenthesis, PosPostfix}, // 44: ']'
|
||||||
SymClosedBrace: {"}", SymClassParenthesis, PosPostfix}, // 45: '}'
|
SymOpenBrace: {"{", SymClassParenthesis, PosPrefix}, // 45: '{'
|
||||||
SymTilde: {"~", SymClassOperator, PosPrefix}, // 46: '~'
|
SymClosedBrace: {"}", SymClassParenthesis, PosPostfix}, // 46: '}'
|
||||||
SymDoubleQuestion: {"??", SymClassOperator, PosInfix}, // 47: '??'
|
SymTilde: {"~", SymClassOperator, PosPrefix}, // 47: '~'
|
||||||
SymQuestionEqual: {"?=", SymClassOperator, PosInfix}, // 48: '?='
|
SymDoubleQuestion: {"??", SymClassOperator, PosInfix}, // 48: '??'
|
||||||
SymQuestionExclam: {"?!", SymClassOperator, PosInfix}, // 49: '?!'
|
SymQuestionEqual: {"?=", SymClassOperator, PosInfix}, // 49: '?='
|
||||||
SymDoubleAt: {"@@", SymClassCommand, PosLeaf}, // 50: '@@'
|
SymQuestionExclam: {"?!", SymClassOperator, PosInfix}, // 50: '?!'
|
||||||
SymDoubleColon: {"::", SymClassOperator, PosInfix}, // 51: '::'
|
SymDoubleAt: {"@@", SymClassCommand, PosLeaf}, // 51: '@@'
|
||||||
SymDoubleGreater: {">>", SymClassOperator, PosInfix}, // 52: '>>'
|
SymDoubleColon: {"::", SymClassOperator, PosInfix}, // 52: '::'
|
||||||
SymDoubleLess: {"<<", SymClassOperator, PosInfix}, // 53: '<<'
|
SymDoubleGreater: {">>", SymClassOperator, PosInfix}, // 53: '>>'
|
||||||
SymCaret: {"^", SymClassOperator, PosInfix}, // 54: '^'
|
SymDoubleLess: {"<<", SymClassOperator, PosInfix}, // 54: '<<'
|
||||||
SymDollarRound: {"$(", SymClassOperator, PosPrefix}, // 55: '$('
|
SymCaret: {"^", SymClassOperator, PosInfix}, // 55: '^'
|
||||||
SymOpenClosedRound: {"()", SymClassOperator, PosPostfix}, // 56: '()'
|
SymDollarRound: {"$(", SymClassOperator, PosPrefix}, // 56: '$('
|
||||||
SymDoubleDollar: {"$$", SymClassCommand, PosLeaf}, // 57: '$$'
|
SymOpenClosedRound: {"()", SymClassOperator, PosPostfix}, // 57: '()'
|
||||||
SymDoubleDot: {"..", SymClassOperator, PosInfix}, // 58: '..'
|
SymDoubleDollar: {"$$", SymClassCommand, PosLeaf}, // 58: '$$'
|
||||||
SymTripleDot: {"...", SymClassOperator, PosPostfix}, // 59: '...'
|
SymDoubleDot: {"..", SymClassOperator, PosInfix}, // 59: '..'
|
||||||
SymStarEqual: {"*=", SymClassOperator, PosInfix}, // 60: '*='
|
SymTripleDot: {"...", SymClassOperator, PosPostfix}, // 60: '...'
|
||||||
SymSlashEqual: {"/=", SymClassOperator, PosInfix}, // 61: '/='
|
SymStarEqual: {"*=", SymClassOperator, PosInfix}, // 61: '*='
|
||||||
SymPercEqual: {"%=", SymClassOperator, PosInfix}, // 62: '%='
|
SymSlashEqual: {"/=", SymClassOperator, PosInfix}, // 62: '/='
|
||||||
SymDoubleLessEqual: {"<<=", SymClassOperator, PosInfix}, // 63: '<<='
|
SymPercEqual: {"%=", SymClassOperator, PosInfix}, // 63: '%='
|
||||||
SymDoubleGreaterEqual: {">>=", SymClassOperator, PosInfix}, // 64: '>>='
|
SymDoubleLessEqual: {"<<=", SymClassOperator, PosInfix}, // 64: '<<='
|
||||||
SymAmpersandEqual: {"&=", SymClassOperator, PosInfix}, // 65: '&='
|
SymDoubleGreaterEqual: {">>=", SymClassOperator, PosInfix}, // 65: '>>='
|
||||||
SymVertBarEqual: {"|=", SymClassOperator, PosInfix}, // 65: '|='
|
SymAmpersandEqual: {"&=", SymClassOperator, PosInfix}, // 66: '&='
|
||||||
SymCaretEqual: {"^=", SymClassOperator, PosInfix}, // 66: '^='
|
SymVertBarEqual: {"|=", SymClassOperator, PosInfix}, // 67: '|='
|
||||||
SymPlusGreater: {"+>", SymClassOperator, PosInfix}, // 67: '+>'
|
SymCaretEqual: {"^=", SymClassOperator, PosInfix}, // 68: '^='
|
||||||
SymLessPlus: {"<+", SymClassOperator, PosInfix}, // 68: '<+'
|
SymPlusGreater: {"+>", SymClassOperator, PosInfix}, // 69: '+>'
|
||||||
SymPreInc: {"++", SymClassOperator, PosPrefix}, // : '++'
|
SymLessPlus: {"<+", SymClassOperator, PosInfix}, // 70: '<+'
|
||||||
SymPreDec: {"--", SymClassOperator, PosPrefix}, // : '--'
|
SymPreInc: {"++", SymClassOperator, PosPrefix}, // 71: '++'
|
||||||
|
SymPreDec: {"--", SymClassOperator, PosPrefix}, // 72: '--'
|
||||||
|
SymOpenSquareLess: {"[<", SymClassOperator, PosPrefix}, // 97: '[<'
|
||||||
|
SymGreaterClosedSquare: {">]", SymClassOperator, PosPostfix}, // 98: '>]'
|
||||||
// SymChangeSign
|
// SymChangeSign
|
||||||
// SymUnchangeSign
|
// SymUnchangeSign
|
||||||
// SymIdentifier
|
// SymIdentifier
|
||||||
@@ -122,6 +125,7 @@ func init() {
|
|||||||
// SymFuncCall
|
// SymFuncCall
|
||||||
// SymFuncDef
|
// SymFuncDef
|
||||||
// SymList
|
// SymList
|
||||||
|
// SymLinkedList
|
||||||
// SymDict
|
// SymDict
|
||||||
// SymIndex
|
// SymIndex
|
||||||
// SymExpression
|
// SymExpression
|
||||||
@@ -184,25 +188,6 @@ func StringEndsWithOperator(s string) bool {
|
|||||||
return endingOperator(s) != SymNone
|
return endingOperator(s) != SymNone
|
||||||
}
|
}
|
||||||
|
|
||||||
// func endingOperator(s string) (sym Symbol) {
|
|
||||||
// var matchLength = 0
|
|
||||||
// sym = SymNone
|
|
||||||
// lower := strings.TrimRight(strings.ToLower(s), " \t")
|
|
||||||
// for symbol, spec := range symbolMap {
|
|
||||||
// if strings.HasSuffix(lower, spec.repr) {
|
|
||||||
// if len(spec.repr) > matchLength {
|
|
||||||
// matchLength = len(spec.repr)
|
|
||||||
// if spec.kind == symClassOperator && (spec.opType == PosInfix || spec.opType == PosPrefix) {
|
|
||||||
// sym = symbol
|
|
||||||
// } else {
|
|
||||||
// sym = SymNone
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
func endingOperator(s string) (sym Symbol) {
|
func endingOperator(s string) (sym Symbol) {
|
||||||
var matchLength = 0
|
var matchLength = 0
|
||||||
var repr string
|
var repr string
|
||||||
|
|||||||
+102
-98
@@ -1,109 +1,113 @@
|
|||||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// Symbol.go
|
// symbol.go
|
||||||
package scan
|
package scan
|
||||||
|
|
||||||
type Symbol int16
|
type Symbol int16
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SymUnknown Symbol = iota - 1 // -1: Unknown symbol
|
SymUnknown Symbol = iota - 1 // -1: Unknown symbol
|
||||||
SymNone // 0: Null value for variable of type symbol
|
SymNone // 0: Null value for variable of type symbol
|
||||||
SymError // 1: Error reading from stream
|
SymError // 1: Error reading from stream
|
||||||
SymEos // 2: End of stream
|
SymEos // 2: End of stream
|
||||||
SymMinus // 3: '-'
|
SymMinus // 3: '-'
|
||||||
SymMinusEqual // 4: '-='
|
SymMinusEqual // 4: '-='
|
||||||
SymDoubleMinus // 5: '--'
|
SymDoubleMinus // 5: '--'
|
||||||
SymPlus // 6: '+'
|
SymPlus // 6: '+'
|
||||||
SymPlusEqual // 7: '+='
|
SymPlusEqual // 7: '+='
|
||||||
SymDoublePlus // 8: '++'
|
SymDoublePlus // 8: '++'
|
||||||
SymStar // 9: '*'
|
SymStar // 9: '*'
|
||||||
SymDoubleStar // 10: '**'
|
SymDoubleStar // 10: '**'
|
||||||
SymSlash // 11: '/'
|
SymSlash // 11: '/'
|
||||||
SymBackSlash // 12: '\'
|
SymBackSlash // 12: '\'
|
||||||
SymVertBar // 13: '|'
|
SymVertBar // 13: '|'
|
||||||
SymDoubleVertBar // 14: '||'
|
SymDoubleVertBar // 14: '||'
|
||||||
SymComma // 15: ','
|
SymComma // 15: ','
|
||||||
SymColon // 16: ':'
|
SymColon // 16: ':'
|
||||||
SymSemiColon // 17: ';'
|
SymSemiColon // 17: ';'
|
||||||
SymDot // 18: '.'
|
SymDot // 18: '.'
|
||||||
SymDotSlash // 19: './'
|
SymDotSlash // 19: './'
|
||||||
SymQuote // 20: '\''
|
SymQuote // 20: '\''
|
||||||
SymDoubleQuote // 21: '"'
|
SymDoubleQuote // 21: '"'
|
||||||
SymBackTick // 22: '`'
|
SymBackTick // 22: '`'
|
||||||
SymExclamation // 23: '!'
|
SymExclamation // 23: '!'
|
||||||
SymQuestion // 24: '?'
|
SymQuestion // 24: '?'
|
||||||
SymAmpersand // 25: '&'
|
SymAmpersand // 25: '&'
|
||||||
SymDoubleAmpersand // 26: '&&'
|
SymDoubleAmpersand // 26: '&&'
|
||||||
SymPercent // 27: '%'
|
SymPercent // 27: '%'
|
||||||
SymAt // 28: '@'
|
SymAt // 28: '@'
|
||||||
SymUndescore // 29: '_'
|
SymUndescore // 29: '_'
|
||||||
SymEqual // 30: '='
|
SymEqual // 30: '='
|
||||||
SymDoubleEqual // 31: '=='
|
SymColonEqual // 31: ':='
|
||||||
SymLess // 32: '<'
|
SymDoubleEqual // 32: '=='
|
||||||
SymLessOrEqual // 33: '<='
|
SymLess // 33: '<'
|
||||||
SymGreater // 34: '>'
|
SymLessOrEqual // 34: '<='
|
||||||
SymGreaterOrEqual // 35: '>='
|
SymGreater // 35: '>'
|
||||||
SymLessGreater // 36: '<>'
|
SymGreaterOrEqual // 36: '>='
|
||||||
SymNotEqual // 37: '!='
|
SymLessGreater // 37: '<>'
|
||||||
SymDollar // 38: '$'
|
SymNotEqual // 38: '!='
|
||||||
SymHash // 39: '#'
|
SymDollar // 39: '$'
|
||||||
SymOpenRound // 40: '('
|
SymHash // 40: '#'
|
||||||
SymClosedRound // 41: ')'
|
SymOpenRound // 41: '('
|
||||||
SymOpenSquare // 42: '['
|
SymClosedRound // 42: ')'
|
||||||
SymClosedSquare // 43: ']'
|
SymOpenSquare // 43: '['
|
||||||
SymOpenBrace // 44: '{'
|
SymClosedSquare // 44: ']'
|
||||||
SymClosedBrace // 45: '}'
|
SymOpenBrace // 45: '{'
|
||||||
SymTilde // 46: '~'
|
SymClosedBrace // 46: '}'
|
||||||
SymDoubleQuestion // 47: '??'
|
SymTilde // 47: '~'
|
||||||
SymQuestionEqual // 48: '?='
|
SymDoubleQuestion // 48: '??'
|
||||||
SymQuestionExclam // 49: '?!'
|
SymQuestionEqual // 49: '?='
|
||||||
SymDoubleAt // 50: '@@'
|
SymQuestionExclam // 50: '?!'
|
||||||
SymDoubleColon // 51: '::'
|
SymDoubleAt // 51: '@@'
|
||||||
SymDoubleGreater // 52: '>>'
|
SymDoubleColon // 52: '::'
|
||||||
SymDoubleLess // 53: '<<'
|
SymDoubleGreater // 53: '>>'
|
||||||
SymCaret // 54: '^'
|
SymDoubleLess // 54: '<<'
|
||||||
SymDollarRound // 55: '$('
|
SymCaret // 55: '^'
|
||||||
SymOpenClosedRound // 56: '()'
|
SymDollarRound // 56: '$('
|
||||||
SymDoubleDollar // 57: '$$'
|
SymOpenClosedRound // 57: '()'
|
||||||
SymDoubleDot // 58: '..'
|
SymDoubleDollar // 58: '$$'
|
||||||
SymTripleDot // 59: '...'
|
SymDoubleDot // 59: '..'
|
||||||
SymStarEqual // 60: '*='
|
SymTripleDot // 60: '...'
|
||||||
SymSlashEqual // 61: '/='
|
SymStarEqual // 61: '*='
|
||||||
SymPercEqual // 62: '%='
|
SymSlashEqual // 62: '/='
|
||||||
SymDoubleLessEqual // 63: '<<='
|
SymPercEqual // 63: '%='
|
||||||
SymDoubleGreaterEqual // 64: '>>='
|
SymDoubleLessEqual // 64: '<<='
|
||||||
SymAmpersandEqual // 65: '&='
|
SymDoubleGreaterEqual // 65: '>>='
|
||||||
SymVertBarEqual // 65: '|='
|
SymAmpersandEqual // 66: '&='
|
||||||
SymCaretEqual // 66: '^='
|
SymVertBarEqual // 67: '|='
|
||||||
SymPlusGreater // 67: '+>'
|
SymCaretEqual // 68: '^='
|
||||||
SymLessPlus // 68: '<+'
|
SymPlusGreater // 69: '+>'
|
||||||
SymChangeSign
|
SymLessPlus // 70: '<+'
|
||||||
SymUnchangeSign
|
SymChangeSign // 71: '-'
|
||||||
SymDereference
|
SymUnchangeSign // 72: '+''
|
||||||
SymPreInc
|
SymDereference // 73: '*'
|
||||||
SymPreDec
|
SymPreInc // 74: '++'
|
||||||
SymIdentifier
|
SymPreDec // 75: '--'
|
||||||
SymBool
|
SymIdentifier // 76: identifier
|
||||||
SymInteger
|
SymBool // 77: boolean
|
||||||
SymVariable
|
SymInteger // 78: integer
|
||||||
SymFloat
|
SymVariable // 79: variable
|
||||||
SymFraction
|
SymFloat // 80: float
|
||||||
SymString
|
SymFraction // 81: fraction
|
||||||
SymIterator
|
SymString // 82: string
|
||||||
SymOr
|
SymIterator // 83: iterator
|
||||||
SymAnd
|
SymOr // 84: 'or'
|
||||||
SymNot
|
SymAnd // 85: 'and'
|
||||||
SymComment
|
SymNot // 86: 'not'
|
||||||
SymFuncCall
|
SymComment // 87: comment
|
||||||
SymFuncDef
|
SymFuncCall // 88: function call
|
||||||
SymList
|
SymFuncDef // 89: function definition
|
||||||
SymDict
|
SymList // 90: list
|
||||||
SymIndex
|
SymDict // 91: dict
|
||||||
SymRange // [index : index]
|
SymIndex // 92: index
|
||||||
SymExpression
|
SymRange // 93: range [index : index]
|
||||||
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
SymExpression // 94: expression
|
||||||
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
SymSelector // 95: selector <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||||
|
SymSelectorCase // 96: <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||||
|
SymOpenSquareLess // 97: '[<'
|
||||||
|
SymGreaterClosedSquare // 98: '>]'
|
||||||
|
SymLinkedList // 99: linked-list
|
||||||
// SymOpenComment // 0: '/*'
|
// SymOpenComment // 0: '/*'
|
||||||
// SymClosedComment // 0: '*/'
|
// SymClosedComment // 0: '*/'
|
||||||
// SymOneLineComment // 0: '//'
|
// SymOneLineComment // 0: '//'
|
||||||
|
|||||||
@@ -136,3 +136,7 @@ func (tk *Token) ErrorExpectedGotStringWithPrefix(prefix, symbol, got string) (e
|
|||||||
err = fmt.Errorf("[%d:%d] %s %s, got `%s`", tk.row, tk.col, prefix, symbol, got)
|
err = fmt.Errorf("[%d:%d] %s %s, got `%s`", tk.row, tk.col, prefix, symbol, got)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tk *Token) ErrorExpectedOneOfGot(expected ...Symbol) (err error) {
|
||||||
|
return tk.ErrorExpectedGotStringWithPrefix("expected one of ", SymListToString(expected, true), SymToString(tk.Sym))
|
||||||
|
}
|
||||||
|
|||||||
+47
-40
@@ -6,7 +6,6 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/util"
|
"git.portale-stac.it/go-pkg/expr/util"
|
||||||
@@ -77,48 +76,56 @@ func (ctx *SimpleStore) Clone() kern.ExprContext {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
func (ctx *SimpleStore) ToString(opt kern.FmtOpt) string {
|
|
||||||
dict := ctx.ToDict()
|
|
||||||
return dict.ToString(opt)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *SimpleStore) varsToDict(dict *kern.DictType) *kern.DictType {
|
|
||||||
names := ctx.EnumVars(nil)
|
|
||||||
slices.Sort(names)
|
|
||||||
for _, name := range ctx.EnumVars(nil) {
|
|
||||||
value, _ := ctx.GetVar(name)
|
|
||||||
if f, ok := value.(kern.Formatter); ok {
|
|
||||||
(*dict)[name] = f.ToString(0)
|
|
||||||
} else if _, ok = value.(kern.Functor); ok {
|
|
||||||
(*dict)[name] = "func(){}"
|
|
||||||
} else {
|
|
||||||
(*dict)[name] = fmt.Sprintf("%v", value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dict
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *SimpleStore) funcsToDict(dict *kern.DictType) *kern.DictType {
|
|
||||||
names := ctx.EnumFuncs(func(name string) bool { return true })
|
|
||||||
slices.Sort(names)
|
|
||||||
for _, name := range names {
|
|
||||||
value, _ := ctx.GetFuncInfo(name)
|
|
||||||
if formatter, ok := value.(kern.Formatter); ok {
|
|
||||||
(*dict)[name] = formatter.ToString(0)
|
|
||||||
} else {
|
|
||||||
(*dict)[name] = fmt.Sprintf("%v", value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dict
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctx *SimpleStore) ToDict() (dict *kern.DictType) {
|
func (ctx *SimpleStore) ToDict() (dict *kern.DictType) {
|
||||||
dict = kern.MakeDict()
|
return kern.ContextToDict(ctx)
|
||||||
(*dict)["variables"] = ctx.varsToDict(kern.MakeDict())
|
|
||||||
(*dict)["functions"] = ctx.funcsToDict(kern.MakeDict())
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleStore) ToString(opt kern.FmtOpt) string {
|
||||||
|
return kern.ContextToString(ctx, opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (ctx *SimpleStore) ToString(opt kern.FmtOpt) string {
|
||||||
|
// dict := ctx.ToDict()
|
||||||
|
// return dict.ToString(opt)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (ctx *SimpleStore) varsToDict(dict *kern.DictType) *kern.DictType {
|
||||||
|
// names := ctx.EnumVars(nil)
|
||||||
|
// slices.Sort(names)
|
||||||
|
// for _, name := range ctx.EnumVars(nil) {
|
||||||
|
// value, _ := ctx.GetVar(name)
|
||||||
|
// if f, ok := value.(kern.Formatter); ok {
|
||||||
|
// (*dict)[name] = f.ToString(0)
|
||||||
|
// } else if _, ok = value.(kern.Functor); ok {
|
||||||
|
// (*dict)[name] = "func(){}"
|
||||||
|
// } else {
|
||||||
|
// (*dict)[name] = fmt.Sprintf("%v", value)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return dict
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (ctx *SimpleStore) funcsToDict(dict *kern.DictType) *kern.DictType {
|
||||||
|
// names := ctx.EnumFuncs(func(name string) bool { return true })
|
||||||
|
// slices.Sort(names)
|
||||||
|
// for _, name := range names {
|
||||||
|
// value, _ := ctx.GetFuncInfo(name)
|
||||||
|
// if formatter, ok := value.(kern.Formatter); ok {
|
||||||
|
// (*dict)[name] = formatter.ToString(0)
|
||||||
|
// } else {
|
||||||
|
// (*dict)[name] = fmt.Sprintf("%v", value)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return dict
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (ctx *SimpleStore) ToDict() (dict *kern.DictType) {
|
||||||
|
// dict = kern.MakeDict()
|
||||||
|
// (*dict)["variables"] = ctx.varsToDict(kern.MakeDict())
|
||||||
|
// (*dict)["functions"] = ctx.funcsToDict(kern.MakeDict())
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
func (ctx *SimpleStore) GetVar(varName string) (value any, exists bool) {
|
func (ctx *SimpleStore) GetVar(varName string) (value any, exists bool) {
|
||||||
if value, exists = ctx.varStore[varName]; !exists && ctx.global != nil {
|
if value, exists = ctx.varStore[varName]; !exists && ctx.global != nil {
|
||||||
value, exists = ctx.global.GetVar(varName)
|
value, exists = ctx.global.GetVar(varName)
|
||||||
|
|||||||
@@ -118,8 +118,8 @@ func TestFuncBaseOthers(t *testing.T) {
|
|||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`set("a", 3); a`, int64(3), nil},
|
/* 1 */ {`set("a", 3); a`, int64(3), nil},
|
||||||
/* 2 */ {`set(true, 3)`, nil, `set(): the "name" parameter must be a string, got a bool (true)`},
|
/* 2 */ {`set(true, 3)`, nil, `set(): the "name" parameter must be a string, got a bool (true)`},
|
||||||
// /* 3 */ {`a=3; unset("a"); a`, nil, `undefined variable or function "a"`},
|
/* 3 */ {`seq(1,2,3)`, kern.NewLinkedListA(int64(1), int64(2), int64(3)), nil},
|
||||||
// /* 4 */ {`unset("a")`, nil, `undefined variable or function "a"`},
|
// /* 4 */ {`seq(1,2,4)`, kern.NewLinkedListA(int64(1), int64(2), int64(3)), nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 4)
|
// runTestSuiteSpec(t, section, inputs, 4)
|
||||||
|
|||||||
@@ -121,3 +121,27 @@ func logTest(t *testing.T, n int, section, source string, wantResult any, wantEr
|
|||||||
t.Logf("[-]%s nr %3d -- `%s` --> %v", section, n, source, wantErr)
|
t.Logf("[-]%s nr %3d -- `%s` --> %v", section, n, source, wantErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testIteratorCallOp(t *testing.T, section string, it kern.Iterator) {
|
||||||
|
if _, err := it.CallOperation("next", map[string]any{}); err != nil {
|
||||||
|
t.Errorf(`%s -- CallOperation("next") failed: %v`, section, err)
|
||||||
|
}
|
||||||
|
if _, err := it.CallOperation("current", map[string]any{}); err != nil {
|
||||||
|
t.Errorf(`%s -- CallOperation("current") failed: %v`, section, err)
|
||||||
|
}
|
||||||
|
if _, err := it.CallOperation("clean", map[string]any{}); err != nil {
|
||||||
|
t.Errorf(`%s -- CallOperation("clean") failed: %v`, section, err)
|
||||||
|
}
|
||||||
|
if _, err := it.CallOperation("index", map[string]any{}); err != nil {
|
||||||
|
t.Errorf(`%s -- CallOperation("index") failed: %v`, section, err)
|
||||||
|
}
|
||||||
|
if _, err := it.CallOperation("count", map[string]any{}); err != nil {
|
||||||
|
t.Errorf(`%s -- CallOperation("count") failed: %v`, section, err)
|
||||||
|
}
|
||||||
|
if _, err := it.CallOperation("reset", map[string]any{}); err != nil {
|
||||||
|
t.Errorf(`%s -- CallOperation("reset") failed: %v`, section, err)
|
||||||
|
}
|
||||||
|
if _, err := it.CallOperation("fake", map[string]any{}); err == nil {
|
||||||
|
t.Errorf(`%s -- CallOperation("fake") should return error`, section)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCtrlSet(t *testing.T) {
|
func TestCtrlSet(t *testing.T) {
|
||||||
@@ -58,3 +60,34 @@ func TestCtrlGetNotDefined(t *testing.T) {
|
|||||||
t.Errorf(`%s -- CtrlGet(%q) should have returned nil, got %v`, section, varName, v)
|
t.Errorf(`%s -- CtrlGet(%q) should have returned nil, got %v`, section, varName, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCtrlEnable(t *testing.T) {
|
||||||
|
section := "Context"
|
||||||
|
varName := "test_var"
|
||||||
|
varValue := true
|
||||||
|
|
||||||
|
ctx := NewSimpleStore()
|
||||||
|
GlobalCtrlSet(ctx, varName, varValue)
|
||||||
|
if !CtrlEnable(ctx, varName) {
|
||||||
|
t.Errorf(`%s -- CtrlEnable(ctx, %q) should have returned 'true', got 'false'`, section, varName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !CtrlDisable(ctx, varName) {
|
||||||
|
t.Errorf(`%s -- CtrlEnable(ctx, %q) should have returned 'true', got 'false'`, section, varName)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestList(t *testing.T) {
|
||||||
|
section := "Context"
|
||||||
|
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`$$(5)`, kern.NewLinkedListA(5), nil},
|
||||||
|
/* 2 */ {`$$($(2))`, kern.NewLinkedListA(0, 1), nil},
|
||||||
|
/* 3 */ {`string(($$global).funcs.bool)`, `bool(value):boolean{}`, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
// runTestSuiteSpec(t, section, inputs, 3)
|
||||||
|
runTestSuite(t, section, inputs)
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
+35
-81
@@ -7,7 +7,6 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
@@ -17,105 +16,60 @@ import (
|
|||||||
func TestDictParser(t *testing.T) {
|
func TestDictParser(t *testing.T) {
|
||||||
section := "Dict"
|
section := "Dict"
|
||||||
|
|
||||||
type inputType struct {
|
|
||||||
source string
|
|
||||||
wantResult any
|
|
||||||
wantErr error
|
|
||||||
}
|
|
||||||
|
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`{}`, map[any]any{}, nil},
|
/* 1 */ {`{}`, kern.NewDict(nil), nil},
|
||||||
/* 2 */ {`{123}`, nil, errors.New("[1:6] expected `:`, got `}`")},
|
/* 2 */ {`{123}`, nil, errors.New("[1:6] expected `:`, got `}`")},
|
||||||
/* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
|
/* 3 */ {`{1:"one",2:"two",3:"three"}`, kern.NewDict(map[any]any{int64(1): "one", int64(2): "two", int64(3): "three"}), nil},
|
||||||
/* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
|
/* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
|
||||||
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
|
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
|
||||||
/* 6 */ {`{1:"one"} + {2:"two"}`, map[any]any{1: "one", 2: "two"}, nil},
|
/* 6 */ {`{1:"one"} + {2:"two"}`, kern.NewDict(map[any]any{int64(1): "one", int64(2): "two"}), nil},
|
||||||
/* 7 */ {`2 in {1:"one", 2:"two"}`, true, nil},
|
/* 7 */ {`2 in {1:"one", 2:"two"}`, true, nil},
|
||||||
/* 8 */ {`D={"a":1, "b":2}; D["a"]=9; D`, map[any]any{"a": 9, "b": 2}, nil},
|
/* 8 */ {`D={"a":1, "b":2}; D["a"]=9; D`, kern.NewDict(map[any]any{"a": int64(9), "b": int64(2)}), nil},
|
||||||
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil},
|
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, kern.NewDict(map[any]any{"z": int64(9), "a": int64(1), "b": int64(2)}), nil},
|
||||||
/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
|
/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
|
||||||
/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
|
/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
|
||||||
/* 12 */ {`m={
|
/* 12 */ {`m={
|
||||||
"a":1,
|
"a":1,
|
||||||
//"b":2,
|
//"b":2,
|
||||||
"c":3
|
"c":3
|
||||||
}`, map[any]any{"a": 1, "c": 3}, nil},
|
}`, kern.NewDict(map[any]any{"a": int64(1), "c": int64(3)}), nil},
|
||||||
/* 13 */ {`D={"a":1, "b":2}; D."a"`, int64(1), nil},
|
/* 13 */ {`D={"a":1, "b":2}; D."a"`, int64(1), nil},
|
||||||
/* 14 */ {`D={"a":1, "b":2}; D.a`, int64(1), nil},
|
/* 14 */ {`D={"a":1, "b":2}; D.a`, int64(1), nil},
|
||||||
/* 15 */ {`D={1:"a", 2:"b", 3:"c"}; D.(1+2)`, "c", nil},
|
/* 15 */ {`D={1:"a", 2:"b", 3:"c"}; D.(1+2)`, "c", nil},
|
||||||
|
/* 16 */ {`D={1:"a"}; D.2`, nil, `[1:15] key 2 not found`},
|
||||||
|
/* 17 */ {`D={1:"a"}; D."2"`, nil, `[1:17] key "2" not found`},
|
||||||
|
/* 18 */ {`D={"a":1, "b":2}; D.a = 10; D.a`, int64(10), nil},
|
||||||
|
/* 19 */ {`k="a"; D={k: 10}`, kern.NewDict(map[any]any{"a": int64(10)}), nil},
|
||||||
|
/* 20 */ {`k=[<1,2>]; D={k: 10}`, nil, `[1:16] dict key can be integer or string, got lisked-list`},
|
||||||
|
/* 21 */ {`f=func(n){"x"+n}; d{f(5):10}`, nil, `[0:0] two adjacent operators: "d" and "{}"`},
|
||||||
|
/* 22 */ {`f=func(n){"x"+n}; d={f(5):10}`, kern.NewDict(map[any]any{"x5": int64(10)}), nil},
|
||||||
|
/* 23 */ {`f=func(n){"x"+n}; d={f(5); "z":10}`, nil, "[1:27] expected one of `:`, `}`, got `;`"},
|
||||||
|
/* 24 */ {`f=func(n){"x"+n}; d={f(5) but "z":10}`, kern.NewDict(map[any]any{"z": int64(10)}), nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
succeeded := 0
|
// runTestSuiteSpec(t, section, inputs, 23, 24)
|
||||||
failed := 0
|
runTestSuite(t, section, inputs)
|
||||||
|
}
|
||||||
|
|
||||||
// inputs1 := []inputType{
|
func TestAccessSubFields(t *testing.T) {
|
||||||
// /* 7 */ {`add([1,4,3,2])`, int64(10), nil},
|
section := "Dict-Assign-Item"
|
||||||
// }
|
ctx := NewSimpleStore()
|
||||||
|
ctx.UnsafeSetVar(
|
||||||
|
"D",
|
||||||
|
kern.NewDict(map[any]any{
|
||||||
|
"a": kern.NewDict(map[any]any{"uno": int64(10)}),
|
||||||
|
"b": kern.NewListA(1, 2, 3),
|
||||||
|
}),
|
||||||
|
) // D={"a": {"uno":1}}
|
||||||
|
|
||||||
for i, input := range inputs {
|
inputs := []inputType{
|
||||||
var expr *scan.Ast
|
/* 1 */ {`D.a.uno`, int64(10), nil},
|
||||||
var gotResult any
|
/* 2 */ {`D.a.uno = 111; D.a."uno"`, int64(111), nil},
|
||||||
var gotErr error
|
/* 3 */ {`D.a.uno = 111; D["a"]["uno"]`, int64(111), nil},
|
||||||
|
/* 4 */ {`D.b[1] = 22; D["b"][1]`, int64(22), nil},
|
||||||
ctx := NewSimpleStoreWithoutGlobalContext()
|
|
||||||
ctx.SetVar("var1", int64(123))
|
|
||||||
ctx.SetVar("var2", "abc")
|
|
||||||
ImportMathFuncs(ctx)
|
|
||||||
parser := NewParser()
|
|
||||||
|
|
||||||
logTest(t, i+1, "Dict", input.source, input.wantResult, input.wantErr)
|
|
||||||
|
|
||||||
r := strings.NewReader(input.source)
|
|
||||||
scanner := scan.NewScanner(r, scan.DefaultTranslations())
|
|
||||||
|
|
||||||
good := true
|
|
||||||
if expr, gotErr = parser.Parse(scanner); gotErr == nil {
|
|
||||||
gotResult, gotErr = expr.Eval(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gotResult == nil && input.wantResult != nil) || (gotResult != nil && input.wantResult == nil) {
|
|
||||||
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
|
|
||||||
good = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if gotList, okGot := gotResult.([]any); okGot {
|
|
||||||
if wantList, okWant := input.wantResult.([]any); okWant {
|
|
||||||
if (gotList == nil && wantList != nil) || (gotList != nil && wantList == nil) {
|
|
||||||
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
|
|
||||||
good = false
|
|
||||||
} else {
|
|
||||||
equal := len(gotList) == len(wantList)
|
|
||||||
if equal {
|
|
||||||
for i, gotItem := range gotList {
|
|
||||||
wantItem := wantList[i]
|
|
||||||
equal = gotItem == wantItem
|
|
||||||
if !equal {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !equal {
|
|
||||||
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
|
|
||||||
good = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if gotErr != input.wantErr {
|
|
||||||
if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) {
|
|
||||||
t.Errorf("%d: %q -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr)
|
|
||||||
good = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if good {
|
|
||||||
succeeded++
|
|
||||||
} else {
|
|
||||||
failed++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
|
// runCtxTestSuiteSpec(t, ctx, section, inputs, 4)
|
||||||
|
runCtxTestSuite(t, ctx, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDictToStringMultiLine(t *testing.T) {
|
func TestDictToStringMultiLine(t *testing.T) {
|
||||||
|
|||||||
+1
-2
@@ -31,8 +31,7 @@ func TestExpr(t *testing.T) {
|
|||||||
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
|
/* 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},
|
/* 16 */ {`v=[2]; a=1; v[a-=1]=5; v[0]`, int64(5), nil},
|
||||||
/* 17 */ {`true ? {"a"} :: {"b"}`, "a", nil},
|
/* 17 */ {`true ? {"a"} :: {"b"}`, "a", nil},
|
||||||
/* 18 */ {`$$`, kern.NewDict(map[any]any{"variables": kern.NewDict(nil), "functions": kern.NewDict(nil)}), nil},
|
/* 18 */ {`$$`, kern.NewDict(map[any]any{"vars": kern.NewDict(nil), "funcs": kern.NewDict(nil)}), nil},
|
||||||
///* 19 */ {`$$global`, NewDict(map[any]any{"variables": NewDict(nil), "functions": NewDict(nil)}), nil},
|
|
||||||
/* 19 */ {`
|
/* 19 */ {`
|
||||||
ds={
|
ds={
|
||||||
"init":func(@end){@current=0 but true},
|
"init":func(@end){@current=0 but true},
|
||||||
|
|||||||
+43
-39
@@ -5,51 +5,55 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO The new function param model does not allow this kind of test
|
func subtract(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
// ------------------------------------------------------------------
|
if len(args) != 2 {
|
||||||
// func subtract(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
err = fmt.Errorf("%s(): requires exactly two arguments", name)
|
||||||
// if len(args) != 2 {
|
return
|
||||||
// err = fmt.Errorf("%s(): requires exactly two arguments", name)
|
}
|
||||||
// return
|
x, xok := args["a"].(int64)
|
||||||
// }
|
y, yok := args["b"].(int64)
|
||||||
// x, xok := args[0].(int64)
|
if xok && yok {
|
||||||
// y, yok := args[1].(int64)
|
result = x - y
|
||||||
// if xok && yok {
|
} else {
|
||||||
// result = x - y
|
err = fmt.Errorf("expected integer (int64) arguments, got %T and %T values", x, y)
|
||||||
// } else {
|
}
|
||||||
// err = fmt.Errorf("expected integer (int64) arguments, got %T and %T values", x, y)
|
return
|
||||||
// }
|
}
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func TestEvalStringA(t *testing.T) {
|
func TestEvalStringA(t *testing.T) {
|
||||||
|
|
||||||
// source := `a + b * subtract(4,2)`
|
source := `a + b * subtract(4,2)`
|
||||||
// args := []Arg{
|
args := []Arg{
|
||||||
// {"a", uint8(1)},
|
{"a", uint8(1)},
|
||||||
// {"b", int8(2)},
|
{"b", int8(2)},
|
||||||
// {"subtract", FuncTemplate2(subtract)},
|
{"subtract", kern.FuncTemplate(subtract)},
|
||||||
// // force coverage
|
// force coverage
|
||||||
// {"a16", uint16(1)},
|
{"a16", uint16(1)},
|
||||||
// {"b16", int16(2)},
|
{"b16", int16(2)},
|
||||||
// {"a32", uint32(1)},
|
{"a32", uint32(1)},
|
||||||
// {"b32", int32(2)},
|
{"b32", int32(2)},
|
||||||
// {"a64", uint64(1)},
|
{"a64", uint64(1)},
|
||||||
// {"b64", int64(2)},
|
{"b64", int64(2)},
|
||||||
// {"f32", float32(1.0)},
|
{"f32", float32(1.0)},
|
||||||
// {"f64", float64(1.0)},
|
{"f64", float64(1.0)},
|
||||||
// }
|
{"string", "text"},
|
||||||
|
{"bool", true},
|
||||||
|
}
|
||||||
|
|
||||||
// wantResult := int64(5)
|
wantResult := int64(5)
|
||||||
// gotResult, gotErr := EvalStringA(source, args...)
|
gotResult, gotErr := EvalStringA(source, args...)
|
||||||
// if value, ok := gotResult.(int64); ok && value != wantResult {
|
if value, ok := gotResult.(int64); ok && value != wantResult {
|
||||||
// t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
|
t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
|
||||||
// t.Errorf("Error: %v", gotErr)
|
t.Errorf("Error: %v", gotErr)
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
func TestEvalString(t *testing.T) {
|
func TestEvalString(t *testing.T) {
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -6,19 +6,21 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCollections(t *testing.T) {
|
func TestCollections(t *testing.T) {
|
||||||
section := "Collection"
|
section := "Index"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`"abcdef"[1:3]`, "bc", nil},
|
/* 1 */ {`"abcdef"[1:3]`, "bc", nil},
|
||||||
/* 2 */ {`"abcdef"[:3]`, "abc", nil},
|
/* 2 */ {`"abcdef"[:3]`, "abc", nil},
|
||||||
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
||||||
/* 4 */ {`"abcdef"[:]`, "abcdef", 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] invalid range specification`},
|
/* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] invalid range specification`},
|
||||||
/* 6 */ {`"abcdef"[((1>0)?{1}:{0}):3]`, "bc", nil},
|
/* 6 */ {`"abcdef"[((1>0)?{1}:{0}):3]`, "bc", nil},
|
||||||
/* 7 */ {`"abcdef"[[0,1][0]:1]`, "a", nil},
|
/* 7 */ {`"abcdef"[[0,1][0]:1]`, "a", nil},
|
||||||
|
/* 8 */ {`[0,1,2,3,4][:]`, kern.NewListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("EXPR_PATH", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|||||||
+2
-2
@@ -13,8 +13,8 @@ import (
|
|||||||
func TestIterIterator(t *testing.T) {
|
func TestIterIterator(t *testing.T) {
|
||||||
section := "Iter-Iter"
|
section := "Iter-Iter"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`it=$(4); $$($(it) filter ${_}==100)`, kern.NewListA(), nil},
|
/* 1 */ {`it=$(4); $$($(it) filter ${_}==100)`, kern.NewLinkedListA(), nil},
|
||||||
/* 2 */ {`it=$(4); $$($(it, $_) filter ${_}==100)`, kern.NewListA(), nil},
|
/* 2 */ {`it=$(4); $$($(it, $_) filter ${_}==100)`, kern.NewLinkedListA(), nil},
|
||||||
/* 3 */ {`it=$(4); $(it, 10+$_, last-1) digest ${_}`, int64(12), nil},
|
/* 3 */ {`it=$(4); $(it, 10+$_, last-1) digest ${_}`, int64(12), nil},
|
||||||
/* 4 */ {`f=func(n){last-n}; it=$(4); $(it, 10+$_, f(-1)) digest ${_}`, int64(14), nil},
|
/* 4 */ {`f=func(n){last-n}; it=$(4); $(it, 10+$_, f(-1)) digest ${_}`, int64(14), nil},
|
||||||
}
|
}
|
||||||
|
|||||||
+88
-10
@@ -8,6 +8,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIteratorParser(t *testing.T) {
|
func TestIteratorParser(t *testing.T) {
|
||||||
@@ -35,17 +36,94 @@ func TestIteratorParser(t *testing.T) {
|
|||||||
/* 20 */ {`it=$({1:"one",2:"two",3:"three"}, "default", "value"); it++`, "one", nil},
|
/* 20 */ {`it=$({1:"one",2:"two",3:"three"}, "default", "value"); it++`, "one", nil},
|
||||||
/* 21 */ {`it=$({1:"one",2:"two",3:"three"}, "desc", "key"); it++`, int64(3), nil},
|
/* 21 */ {`it=$({1:"one",2:"two",3:"three"}, "desc", "key"); it++`, int64(3), nil},
|
||||||
/* 22 */ {`it=$({1:"one",2:"two",3:"three"}, "asc", "item"); it++`, kern.NewList([]any{int64(1), "one"}), nil},
|
/* 22 */ {`it=$({1:"one",2:"two",3:"three"}, "asc", "item"); it++`, kern.NewList([]any{int64(1), "one"}), nil},
|
||||||
|
/* 23 */ {`$$($(1,4,0))`, nil, `step cannot be zero`},
|
||||||
|
/* 24 */ {`$$($(1,4,-1))`, nil, `step cannot be negative when start < stop`},
|
||||||
|
/* 25 */ {`$$($(4,1,1))`, nil, `step cannot be positive when start > stop`},
|
||||||
}
|
}
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 1)
|
// runTestSuiteSpec(t, section, inputs, 25)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCallOpIntIter(t *testing.T) {
|
||||||
|
section := "IntIterator-CallOp"
|
||||||
|
|
||||||
|
if it, err := NewIntIteratorA(int64(1), int64(4), int64(1)); err != nil {
|
||||||
|
t.Errorf(`%s -- NewIntIteratorA() failed: %v`, section, err)
|
||||||
|
} else {
|
||||||
|
testIteratorCallOp(t, section, it)
|
||||||
|
testIterAttrs(t, section, it, "IntIterator", "$(1..4..1)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCallOpIterIter(t *testing.T) {
|
||||||
|
section := "IterIterator-CallOp"
|
||||||
|
|
||||||
|
ctx := NewSimpleStore()
|
||||||
|
if inner, err := NewIntIteratorA(int64(1), int64(4), int64(1)); err == nil {
|
||||||
|
if it, err := NewIterIter(inner, ctx, []*scan.Term{}); err != nil {
|
||||||
|
t.Errorf(`%s -- NewIterIter() failed: %v`, section, err)
|
||||||
|
} else {
|
||||||
|
testIteratorCallOp(t, section, it)
|
||||||
|
testIterAttrs(t, section, it, "IterIter", "$($(1..4..1))")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf(`%s -- can't create inner iterator: %v`, section, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCallOpDictIter(t *testing.T) {
|
||||||
|
section := "DictIterator-CallOp"
|
||||||
|
|
||||||
|
inner := kern.NewDict(map[any]any{"a": 1})
|
||||||
|
if it, err := NewDictIterator(inner, nil); err != nil {
|
||||||
|
t.Errorf(`%s -- NewIterIter() failed: %v`, section, err)
|
||||||
|
} else {
|
||||||
|
testIteratorCallOp(t, section, it)
|
||||||
|
testIterAttrs(t, section, it, "DictIterator", "$({#1})")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCallOpLinkedListIter(t *testing.T) {
|
||||||
|
section := "LinkedListIterator-CallOp"
|
||||||
|
|
||||||
|
inner := kern.NewLinkedListA(1, 2, 3)
|
||||||
|
it := NewLinkedListIterator(inner, nil)
|
||||||
|
testIteratorCallOp(t, section, it)
|
||||||
|
|
||||||
|
// wanted := "$([<#3>])"
|
||||||
|
// got := it.String()
|
||||||
|
// if wanted != got {
|
||||||
|
// t.Errorf(`%s -- LinkedListIterator.String() failed: expected %q, got %q`, section, wanted, got)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// wanted = "LinkedListIterator"
|
||||||
|
// got = it.TypeName()
|
||||||
|
// if wanted != got {
|
||||||
|
// t.Errorf(`%s -- LinkedListIterator.TypeName() failed: expected %q, got %q`, section, wanted, got)
|
||||||
|
// }
|
||||||
|
testIterAttrs(t, section, it, "LinkedListIterator", "$([<#3>])")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testIterAttrs(t *testing.T, section string, it kern.Iterator, name, repr string) {
|
||||||
|
wanted := repr
|
||||||
|
got := it.String()
|
||||||
|
if wanted != got {
|
||||||
|
t.Errorf(`%s -- %s.String() failed: expected %q, got %q`, section, name, wanted, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
wanted = name
|
||||||
|
got = it.TypeName()
|
||||||
|
if wanted != got {
|
||||||
|
t.Errorf(`%s -- %s.TypeName() failed: expected %q, got %q`, section, name, wanted, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFilterIterator(t *testing.T) {
|
func TestFilterIterator(t *testing.T) {
|
||||||
section := "Iterator-Filter"
|
section := "Iterator-Filter"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`$$([1,2,3] filter $_%2==0)`, kern.NewList([]any{int64(2)}), nil},
|
/* 1 */ {`$$([1,2,3] filter $_%2==0)`, kern.NewLinkedListA(2), nil},
|
||||||
/* 2 */ {`it = [1,2,3] filter $_%2!=1; $$(it)`, kern.NewList([]any{int64(2)}), nil},
|
/* 2 */ {`it = [1,2,3] filter $_%2!=1; $$(it)`, kern.NewLinkedListA(2), nil},
|
||||||
/* 3 */ {`builtin "os.file"; #$$(fileLineIterator("test-file.txt") filter (#${_} == 2))`, int64(0), nil},
|
/* 3 */ {`builtin "os.file"; #$$(fileLineIterator("test-file.txt") filter (#${_} == 2))`, int64(0), nil},
|
||||||
/* 4 */ {`builtin "os.file"; #$$(fileLineIterator("test-file.txt") filter (#${_} == 3))`, int64(2), nil},
|
/* 4 */ {`builtin "os.file"; #$$(fileLineIterator("test-file.txt") filter (#${_} == 3))`, int64(2), nil},
|
||||||
}
|
}
|
||||||
@@ -69,11 +147,11 @@ func TestDigestIterator(t *testing.T) {
|
|||||||
func TestCatIterator(t *testing.T) {
|
func TestCatIterator(t *testing.T) {
|
||||||
section := "Iterator-Cat"
|
section := "Iterator-Cat"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`$$([1] cat [])`, kern.NewList([]any{int64(1)}), nil},
|
/* 1 */ {`$$([1] cat [])`, kern.NewLinkedListA(1), nil},
|
||||||
/* 2 */ {`$$([1] cat [2])`, kern.NewList([]any{int64(1), int64(2)}), nil},
|
/* 2 */ {`$$([1] cat [2])`, kern.NewLinkedListA(1, 2), nil},
|
||||||
/* 3 */ {`$$([1] cat nil)`, kern.NewList([]any{int64(1)}), nil},
|
/* 3 */ {`$$([1] cat nil)`, kern.NewLinkedListA(1), nil},
|
||||||
/* 4 */ {`$$(nil cat [2])`, kern.NewList([]any{int64(2)}), nil},
|
/* 4 */ {`$$(nil cat [2])`, kern.NewLinkedListA(2), nil},
|
||||||
/* 5 */ {`$$(nil cat nil)`, kern.NewList([]any{}), nil},
|
/* 5 */ {`$$(nil cat nil)`, kern.NewLinkedListA(), nil},
|
||||||
/* 6 */ {`$$(["a","b"] cat ["x"-true])`, nil, `[1:23] left operand 'x' [string] and right operand 'true' [bool] are not compatible with operator "-"`},
|
/* 6 */ {`$$(["a","b"] cat ["x"-true])`, nil, `[1:23] left operand 'x' [string] and right operand 'true' [bool] are not compatible with operator "-"`},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,10 +162,10 @@ func TestCatIterator(t *testing.T) {
|
|||||||
func TestMapIterator(t *testing.T) {
|
func TestMapIterator(t *testing.T) {
|
||||||
section := "Iterator-Map"
|
section := "Iterator-Map"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`$$([3,4,5] map ${_#})`, kern.NewList([]any{int64(1), int64(2), int64(3)}), nil},
|
/* 1 */ {`$$([3,4,5] map ${_#})`, kern.NewLinkedListA(1, 2, 3), nil},
|
||||||
/* 2 */ {`#$$($(10) map ${_})`, int64(10), nil},
|
/* 2 */ {`#$$($(10) map ${_})`, int64(10), nil},
|
||||||
/* 3 */ {`#$$($(10,0) map ${_})`, int64(10), nil},
|
/* 3 */ {`#$$($(10,0) map ${_})`, int64(10), nil},
|
||||||
/* 4 */ {`builtin "os.file"; $$(fileLineIterator("test-file.txt") map ${__})`, kern.NewList([]any{int64(0), int64(1)}), nil},
|
/* 4 */ {`builtin "os.file"; $$(fileLineIterator("test-file.txt") map ${__})`, kern.NewLinkedListA(0, 1), nil},
|
||||||
/* 5 */ {`$$(["1", "2", "3"] map int())`, nil, `int(): too few params -- expected 1, got 0`},
|
/* 5 */ {`$$(["1", "2", "3"] map int())`, nil, `int(): too few params -- expected 1, got 0`},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-1
@@ -57,10 +57,28 @@ func TestListParser(t *testing.T) {
|
|||||||
/* 41 */ {`[0] << $([1,2,3,4])`, kern.NewListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
/* 41 */ {`[0] << $([1,2,3,4])`, kern.NewListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||||
/* 42 */ {`L=[]; [1] >> L; L`, kern.NewListA(), nil},
|
/* 42 */ {`L=[]; [1] >> L; L`, kern.NewListA(), nil},
|
||||||
/* 43 */ {`L=[]; L << [1]; L`, kern.NewListA(), nil},
|
/* 43 */ {`L=[]; L << [1]; L`, kern.NewListA(), nil},
|
||||||
|
// /* 44 */ {`[0,1,2,3,4][2:3]`, kern.NewListA(int64(20)), nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 42)
|
// runTestSuiteSpec(t, section, inputs, 44)
|
||||||
|
runTestSuite(t, section, inputs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLinkedListParser(t *testing.T) {
|
||||||
|
section := "Linked-List"
|
||||||
|
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`[<1,2>]`, kern.NewLinkedListA(1, 2), nil},
|
||||||
|
/* 2 */ {`it=$([<1,2,3>]); it++`, int64(1), nil},
|
||||||
|
/* 3 */ {`it=$([<1,2,3>]); #($$(it))`, int64(3), nil},
|
||||||
|
/* 4 */ {`[<1,2,3>][0]`, int64(1), nil},
|
||||||
|
/* 5 */ {`[<1,2,3>][0:2]`, kern.NewLinkedListA(1, 2), nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
|
// runTestSuiteSpec(t, section, inputs, 4)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-9
@@ -35,6 +35,12 @@ func TestOperator(t *testing.T) {
|
|||||||
/* 20 */ {`a=1; a^=2`, int64(3), nil},
|
/* 20 */ {`a=1; a^=2`, int64(3), nil},
|
||||||
/* 21 */ {`a=1; ++a`, int64(2), nil},
|
/* 21 */ {`a=1; ++a`, int64(2), nil},
|
||||||
/* 22 */ {`a=1; --a`, int64(0), nil},
|
/* 22 */ {`a=1; --a`, int64(0), nil},
|
||||||
|
/* 23 */ {`a=1; a++`, int64(1), nil},
|
||||||
|
/* 24 */ {`a=1; a--`, int64(1), nil},
|
||||||
|
/* 25 */ {`a="1"; ++a`, nil, `[1:9] prefix/postfix operator "++" does not support operand '1' [string]`},
|
||||||
|
/* 26 */ {`a="1"; --a`, nil, `[1:9] prefix/postfix operator "--" does not support operand '1' [string]`},
|
||||||
|
/* 27 */ {`a="1"; a++`, nil, `[1:10] prefix/postfix operator "++" does not support operand '1' [string]`},
|
||||||
|
/* 28 */ {`a="1"; a--`, nil, `[1:10] prefix/postfix operator "--" does not support operand '1' [string]`},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
@@ -47,21 +53,44 @@ func TestOperatorInsert(t *testing.T) {
|
|||||||
section := "Operator-Insert"
|
section := "Operator-Insert"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`["a", "b"] << nil`, kern.NewListA("a", "b"), nil},
|
/* 1 */ {`["a", "b"] << nil`, kern.NewListA("a", "b"), nil},
|
||||||
/* 2 */ {`["a", "b"] << []`, kern.NewListA("a", "b"), nil},
|
/* 2 */ {`["a", "b"] << []`, kern.NewListA("a", "b", kern.NewListA()), nil},
|
||||||
/* 3 */ {`["a", "b"] << 3`, kern.NewListA("a", "b", int64(3)), nil},
|
/* 3 */ {`["a", "b"] << $([])`, kern.NewListA("a", "b"), nil},
|
||||||
/* 4 */ {`3 << ["a", "b"]`, nil, `[1:5] left operand '3' [integer] and right operand '["a", "b"]' [list] are not compatible with operator "<<"`},
|
/* 4 */ {`["a", "b"] << 3`, kern.NewListA("a", "b", int64(3)), nil},
|
||||||
/* 5 */ {`nil >> ["a", "b"]`, kern.NewListA("a", "b"), nil},
|
/* 5 */ {`3 << ["a", "b"]`, nil, `[1:5] left operand '3' [integer] and right operand '["a", "b"]' [list] are not compatible with operator "<<"`},
|
||||||
/* 6 */ {`[] >> ["a", "b"]`, kern.NewListA("a", "b"), nil},
|
/* 6 */ {`nil >> ["a", "b"]`, kern.NewListA("a", "b"), nil},
|
||||||
/* 7 */ {`["a", "b"] << $([1,2,3])`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil},
|
/* 7 */ {`[] >> ["a", "b"]`, kern.NewListA(kern.NewListA(), "a", "b"), nil},
|
||||||
/* 8 */ {`L=["a", "b"]; L << $([1,2,3])`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil},
|
/* 8 */ {`$([]) >> ["a", "b"]`, kern.NewListA("a", "b"), nil},
|
||||||
/* 9 */ {`L=["a", "b"]; L << $([1,2,3]); L`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil},
|
/* 9 */ {`["a", "b"] << $([1,2,3])`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil},
|
||||||
/* 10 */ {`L << $([1,2,3])`, nil, `undefined variable or function "L"`},
|
/* 10 */ {`L=["a", "b"]; L << $([1,2,3])`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil},
|
||||||
|
/* 11 */ {`L=["a", "b"]; L << $([1,2,3]); L`, kern.NewListA("a", "b"), nil},
|
||||||
|
/* 12 */ {`L << $([1,2,3])`, nil, `undefined variable or function "L"`},
|
||||||
|
/* 13 */ {`[<>] << 1`, kern.NewLinkedListA(1), nil},
|
||||||
|
/* 14 */ {`[<>] << [1,2]`, kern.NewLinkedListA(kern.NewListA(int64(1), int64(2))), nil},
|
||||||
|
/* 15 */ {`[<>] << [<1,2>]`, kern.NewLinkedListA(kern.NewLinkedListA(1, 2)), nil},
|
||||||
|
/* 16 */ {`1 >> [<>]`, kern.NewLinkedListA(1), nil},
|
||||||
|
/* 17 */ {`[1,2] >> [<>]`, kern.NewLinkedListA(kern.NewListA(int64(1), int64(2))), nil},
|
||||||
|
/* 18 */ {`[<1,2>] >> [<>]`, kern.NewLinkedListA(kern.NewLinkedListA(1, 2)), nil},
|
||||||
|
/* 19 */ {`$([1,2]) >> [<>]`, kern.NewLinkedListA(1, 2), nil},
|
||||||
|
/* 20 */ {`$([<1,2>]) >> [<>]`, kern.NewLinkedListA(1, 2), nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 9)
|
// runTestSuiteSpec(t, section, inputs, 9)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOperatorDeepAssign(t *testing.T) {
|
||||||
|
section := "Operator-DeepAssign"
|
||||||
|
inputs := []inputType{
|
||||||
|
/* 1 */ {`DD={"uno": 1, "due": 2}; D=DD; D["uno"]=11; DD`, kern.NewDict(map[any]any{"uno": int64(11), "due": int64(2)}), nil},
|
||||||
|
/* 2 */ {`DD={"uno": 1, "due": 2}; D:=DD; D["uno"]=11; DD`, kern.NewDict(map[any]any{"uno": int64(1), "due": int64(2)}), nil},
|
||||||
|
/* 3 */ {`LL=["a", "b"]; L=LL; L[0]="x"; LL`, kern.NewListA("x", "b"), nil},
|
||||||
|
/* 4 */ {`LL=["a", "b"]; L:=LL; L[0]="x"; LL`, kern.NewListA("a", "b"), nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
// runTestSuiteSpec(t, section, inputs, 4)
|
||||||
|
runTestSuite(t, section, inputs)
|
||||||
|
}
|
||||||
|
|
||||||
func TestOperatorDigest(t *testing.T) {
|
func TestOperatorDigest(t *testing.T) {
|
||||||
section := "Operator-Digest"
|
section := "Operator-Digest"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
|
|||||||
@@ -77,3 +77,10 @@ func ForAll[T, V any](ts []T, fn func(T) V) []V {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UnquoteString(src string) string {
|
||||||
|
if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
|
||||||
|
src = src[1 : len(src)-1]
|
||||||
|
}
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user