Compare commits

...

9 Commits

8 changed files with 168 additions and 85 deletions
+109 -47
View File
@@ -1,6 +1,7 @@
= Expr
Expressions calculator
:authors: Celestino Amoroso
:email: celestino.amoroso@gmail.com
:docinfo: shared
:encoding: utf-8
:toc: right
@@ -16,10 +17,11 @@ Expressions calculator
:sectlinks:
:sectanchors:
:source-highlighter: rouge
// :rouge-style: ThankfulEyes
:rouge-style: gruvbox
// :rouge-style: colorful
//:rouge-style: monokay
// :rouge-style: gruvbox
:rouge-style: manni
:stylesdir: /home/share/s3-howto/styles
:stylesheet: adoc-colony.css
// Workaround to manage double-column in back-tick quotes
:2c: ::
// Workaround to manage double-plus in back-tick quotes
@@ -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:
* *_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.
image::expression-diagram.png[]
==== 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
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.
@@ -74,7 +76,7 @@ Imported functions are registered in the _global context_. When an expression fi
===== 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.
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
@@ -103,7 +105,7 @@ An interactive tool could like `dev-expr` (see <<_dev-expr_test_tool>>) can be u
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 function in the builtin module *string* have names starting with "str".__ +
`>>>` [blue]`builtin "string` [gray]__// most functions in the builtin module *string* have names starting with "str".__ +
[green]`1`
:dollar: $
@@ -123,24 +125,25 @@ In order to inspect the global context issue the [blue]`$$ global` operation.
[green]`]`
////
=== `dev-expr` test tool
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.
[[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.
`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.
`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.
`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.
`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/dev-expr/[dev-expr].
The program can be downloaded from https://git.portale-stac.it/go-pkg/-/packages/generic/ecli/[ecli].
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]
----
# Type 'exit' or Ctrl+D to quit the program.
[user]$ ./dev-expr
dev-expr -- Expressions calculator v1.12.0(build 1),2024/09/14 (celestino.amoroso@portale-stac.it)
[user]$ ./ecli
ecli -- Expressions calculator v1.12.0(build 1),2024/09/14 (celestino.amoroso@portale-stac.it)
Based on the Expr package v0.26.0
Type help to get the list of available commands
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
@@ -176,8 +179,8 @@ dev-expr -- Expressions calculator v1.12.0(build 1),2024/09/14 (celestino.amoros
.REPL examples
[source,shell]
----
[user]$ ./dev-expr
dev-expr -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoroso@portale-stac.it)
[user]$ ./ecli
ecli -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoroso@portale-stac.it)
Based on the Expr package v0.19.0
Type help to get the list of available commands
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
@@ -254,7 +257,7 @@ Value range: *-9223372036854775808* to *9223372036854775807*
^(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"`.
[[note_string_repl]]
^(2)^ The product operator also supports multiplying a string by an integer. In this case, the number represents homw may times the string has to be repeated in the result, e.g. `"foo" * 3` returnsn `"foofoofoo"`.
^(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.
@@ -393,7 +396,7 @@ Some arithmetic operators also apply to strings.
| [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
====
@@ -416,7 +419,7 @@ The charanters in a string can be accessed using the square `[]` operator.
[green]`"d"`
`>>>` [blue]`#s` [gray]_// number of chars_ +
[gren]`4`
[green]`4`
`>>>` [blue]`#"abc"` [gray]_// number of chars_ +
[green]`3`
@@ -473,14 +476,14 @@ Currently, boolean operations are evaluated using _short cut evaluation_. This m
----
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
_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
====
@@ -585,7 +588,21 @@ Array's items can be accessed using the index `[]` operator.
`>>>` [blue]`[1,2,3,2,4] - [2,4]` +
[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
The _dictionary_, or _dict_, data-type represents sets of pairs _key/value_. It is also known as _map_ or _associative array_.
@@ -707,7 +724,7 @@ The value of each sub-expression is stored in the automatic variable _last_.
[green]`2`
`>>>` [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]`)`.
@@ -787,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]`?!`
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.
@@ -795,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 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.
@@ -897,7 +914,7 @@ The table below shows all supported operators by decreasing priorities.
//^1^ Experimental
.Special assignment perators
.Special assignment operators
[cols="^2,^2,^4,^6"]
|===
| Priority | Operator | Operation |Equivalent operation
@@ -980,7 +997,7 @@ _param-name_ = _identifier_
=== _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
To call a function, either Expr or Golang type, it is necessary to specify its name and, at least, its required parameters.
@@ -1246,7 +1263,7 @@ Returns _true_ if the value type of _<expr>_ is string, false otherwise.
===== 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
`>>>` [blue]`bool(1)` +
@@ -1709,6 +1726,8 @@ Returns a string obtained by converting all characters of the specified string t
`>>>` [blue]`strLower("Hello, world!")` +
[green]`"hello, world!"`
== 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.
@@ -1723,16 +1742,16 @@ _data-source_ = _explicit_ | _list-spec_ | _dict-spec_ | _custom-data-source_
_explicit_ = _any-expr_ { "**,**" _any-expr_ }
_list-spec_ = _list_ _range-options_ +
_list-spec_ = _list_ ["**,**" _range-options_] +
_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_
_dict-spec_ = _dict_ _dict-options_ +
_dict-spec_ = _dict_ ["**,**" _dict-options_] +
_dict_ = "**{**" _key-value-pair_ { "**,**" _key-value-pair_ } "**}**" +
_key-value-pair_ = _scalar-value_ "**:**" _any-expr_ +
_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_ +
_asc-order_ = "**asc**" | "**a**" +
_desc-order_ = "**desc**" | "**d**" +
@@ -1810,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_.
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
`>>>` [blue]`it = $(["one", "two", "three"])` +
@@ -1822,12 +1841,17 @@ After the first use of the [blue]`{plusplus}` operator, the prefixed operato [bl
`>>>` [blue]`{star}it` +
[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 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
* *_.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.
* *_.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_.
@@ -1854,13 +1878,21 @@ TIP: Iterators built on custom data-sources can provide additional named operato
There are also some infixed operators that can be used with iterators. They are defined as follows.
* <<_cat,cat operator>>
* <<_diget,digest 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
@@ -1870,33 +1902,63 @@ Syntax: +
[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 list of the elements for which the expression evaluates to true.
[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}<dict> groupby <key> -> <dict>{4sp}`
`{4sp}<list-of-dicts> groupby <key> -> <dict>{4sp}`
[blue]`groupby` 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.
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> -> <list>{4sp}`
`{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 list of the computed values of the that expression. The current element of the iterator is available in the expression as the variable `$_`.
[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]`it map $_ + "!"` +
[green]`["one!", "two!", "three!"]`
`>>>` [blue]`excl_it = it map $_ + "!"` +
[green]`$($([#3]))` +
`>>>` [blue]`$$(excl_it)` +
[green]`[<"one!", "two!", "three!">]`
=== 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.
+9
View File
@@ -11,6 +11,15 @@ import (
"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) {
if value == nil {
return NewArrayIterator([]any{}), nil
+2 -2
View File
@@ -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() {
ctx.SetVar("_", item)
ctx.SetVar("__", it.Index())
ctx.SetVar("_#", it.Count())
ctx.SetVar("#", it.Count())
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
if rightValue == nil {
break
@@ -53,7 +53,7 @@ func evalDigest(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
lastValue = rightValue
}
}
ctx.DeleteVar("_#")
ctx.DeleteVar("#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
if err != nil {
+2 -2
View File
@@ -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() {
ctx.SetVar("_", attempt)
ctx.SetVar("__", it.Index())
ctx.SetVar("_#", it.Count())
ctx.SetVar("#", it.Count())
result, err = it.expr.Compute(ctx)
ctx.DeleteVar("_#")
ctx.DeleteVar("#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
+7 -3
View File
@@ -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() {
ctx.SetVar("_", item)
ctx.SetVar("__", it.Index())
ctx.SetVar("_#", it.Count())
ctx.SetVar("#", it.Count())
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()))
} else if d.HasKey(sKey) {
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 {
sItemKey = "_"
}
@@ -92,7 +96,7 @@ func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
ls.AppendItem(item)
values.SetItem(sItemKey, ls)
ctx.DeleteVar("_#")
ctx.DeleteVar("#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
}
+18 -20
View File
@@ -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 {
var it kern.Iterator
if it, ok = leftValue.(kern.Iterator); !ok {
if it, err = NewIterator(ctx, leftValue, nil); err != nil {
return
}
it = NewFormalIterator(leftValue)
}
ls := kern.NewLinkedList()
@@ -54,12 +52,13 @@ func prependToList(ctx kern.ExprContext, opTerm *scan.Term, leftValue, rightValu
newList = append(newList, item)
}
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
// ***** CON UN OPERATORE SPECIFICO
// if opTerm.Children[1].Symbol() == scan.SymVariable {
// ctx.UnsafeSetVar(opTerm.Children[1].Source(), result)
// }
result = list.SeqPushBack(it)
} else {
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
}
@@ -73,17 +72,15 @@ func evalPrepend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return
}
v, err = prependToList(ctx, opTerm, leftValue, rightValue)
v, err = prependToList(opTerm, leftValue, rightValue)
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 {
var it kern.Iterator
if it, ok = rightValue.(kern.Iterator); !ok {
if it, err = NewIterator(ctx, rightValue, nil); err != nil {
return
}
it = NewFormalIterator(rightValue)
}
ls := kern.NewLinkedList()
@@ -100,12 +97,13 @@ func appendToList(ctx kern.ExprContext, opTerm *scan.Term, leftValue, rightValue
newList = append(newList, node.Data())
}
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
// ***** CON UN OPERATORE SPECIFICO
// if opTerm.Children[0].Symbol() == scan.SymVariable {
// ctx.UnsafeSetVar(opTerm.Children[0].Source(), result)
// }
result = list.SeqPushBack(it)
} else {
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
}
@@ -119,7 +117,7 @@ func evalAppend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
return
}
v, err = appendToList(ctx, opTerm, leftValue, rightValue)
v, err = appendToList(opTerm, leftValue, rightValue)
return
}
+2 -2
View File
@@ -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 {
v, err = prependToList(ctx, opTerm, leftValue, rightValue)
v, err = prependToList(opTerm, leftValue, rightValue)
}
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 {
v, err = appendToList(ctx, opTerm, leftValue, rightValue)
v, err = appendToList(opTerm, leftValue, rightValue)
}
return
}
+19 -9
View File
@@ -53,15 +53,25 @@ func TestOperatorInsert(t *testing.T) {
section := "Operator-Insert"
inputs := []inputType{
/* 1 */ {`["a", "b"] << nil`, kern.NewListA("a", "b"), nil},
/* 2 */ {`["a", "b"] << []`, kern.NewListA("a", "b"), nil},
/* 3 */ {`["a", "b"] << 3`, kern.NewListA("a", "b", int64(3)), nil},
/* 4 */ {`3 << ["a", "b"]`, nil, `[1:5] left operand '3' [integer] and right operand '["a", "b"]' [list] are not compatible with operator "<<"`},
/* 5 */ {`nil >> ["a", "b"]`, kern.NewListA("a", "b"), nil},
/* 6 */ {`[] >> ["a", "b"]`, kern.NewListA("a", "b"), nil},
/* 7 */ {`["a", "b"] << $([1,2,3])`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil},
/* 8 */ {`L=["a", "b"]; L << $([1,2,3])`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil},
/* 9 */ {`L=["a", "b"]; L << $([1,2,3]); L`, kern.NewListA("a", "b"), nil},
/* 10 */ {`L << $([1,2,3])`, nil, `undefined variable or function "L"`},
/* 2 */ {`["a", "b"] << []`, kern.NewListA("a", "b", kern.NewListA()), nil},
/* 3 */ {`["a", "b"] << $([])`, kern.NewListA("a", "b"), nil},
/* 4 */ {`["a", "b"] << 3`, kern.NewListA("a", "b", int64(3)), nil},
/* 5 */ {`3 << ["a", "b"]`, nil, `[1:5] left operand '3' [integer] and right operand '["a", "b"]' [list] are not compatible with operator "<<"`},
/* 6 */ {`nil >> ["a", "b"]`, kern.NewListA("a", "b"), nil},
/* 7 */ {`[] >> ["a", "b"]`, kern.NewListA(kern.NewListA(), "a", "b"), nil},
/* 8 */ {`$([]) >> ["a", "b"]`, kern.NewListA("a", "b"), nil},
/* 9 */ {`["a", "b"] << $([1,2,3])`, kern.NewListA("a", "b", int64(1), int64(2), int64(3)), nil},
/* 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)