Merge branch 'main' into all-in-one
This commit is contained in:
+92
-32
@@ -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
|
||||
@@ -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 could like `ecli` (see <<_ecli_test_tool>>) can be used to inspect contexts interactively.
|
||||
|
||||
|
||||
.Example: inspecting contexts
|
||||
@@ -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 additon 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
|
||||
@@ -476,11 +479,11 @@ Currently, boolean operations are evaluated using _short cut evaluation_. This m
|
||||
<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_.
|
||||
|
||||
|
||||
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_.
|
||||
@@ -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,6 +1841,11 @@ 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 that can be used with 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`.
|
||||
|
||||
@@ -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,61 @@ 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.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`[{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}, {"name": "Charlie", "age": 30}] groupby "age"` +
|
||||
[source,json]
|
||||
----
|
||||
{
|
||||
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.
|
||||
|
||||
+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() {
|
||||
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
@@ -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
@@ -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("_")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user