diff --git a/doc/Expr.adoc b/doc/Expr.adoc
index 3907183..7d13f94 100644
--- a/doc/Expr.adoc
+++ b/doc/Expr.adoc
@@ -34,7 +34,7 @@ Expressions calculator
toc::[]
-#TODO: Work in progress (last update on 2026/05/08)#
+#TODO: Work in progress#
== Expr
_Expr_ is a GO package that can analyze, interpret and calculate expressions.
@@ -100,7 +100,28 @@ An interactive tool could like `dev-expr` (see <<_dev-expr_test_tool>>) can be u
[green]`{2sp}}` +
[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 function in the builtin module *string* have names starting with "str".__ +
+[green]`1`
+
+:dollar: $
+:2dollars: $$
+
+`>>>` [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` 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.
@@ -223,14 +244,20 @@ Value range: *-9223372036854775808* to *9223372036854775807*
[cols="^1,^2,6,4"]
|===
| Symbol | Operation | Description | Examples
-| [blue]`+` | _Sum_ | Add two values | [blue]`-1 + 2` -> _1_
+| [blue]`+` | _Sum_ | Add two values^(<>)^ | [blue]`-1 + 2` -> _1_
| [blue]`-` | _Subtraction_ | Subtract the right value from the left one | [blue]`3 - 1` -> _2_
-| [blue]`*` | _Product_ | Multiply two values | [blue]`-1 * 2` -> _-2_
-| [blue]`/` | _Integer division_ | Divide the left value by the right one^(*)^ | [blue]`-11 / 2` -> _-5_
+| [blue]`*` | _Product_ | Multiply two values^(<>)^ | [blue]`-1 * 2` -> _-2_
+| [blue]`/` | _Integer division_ | Divide the left value by the right one^(<>)^ | [blue]`-11 / 2` -> _-5_
| [blue]`%` | _Modulo_ | Remainder of the integer division | [blue]`5 % 2` -> _1_
|===
+[[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 homw may times the string has to be repeated in the result, e.g. `"foo" * 3` returnsn `"foofoofoo"`.
+
+[[note_float_division]]
+^(3)^ See also the _float division_ [blue]`./` below.
==== Floats
@@ -268,12 +295,14 @@ _dec-seq_ = _see-integer-literal-syntax_
[cols="^1,^2,6,4"]
|===
| Symbol | Operation | Description | Examples
-| [blue]`+` | _Sum_ | Add two values | [blue]`4 + 0.5` -> 4.5
+| [blue]`+` | _Sum_ | Add two values^(<>)^ | [blue]`4 + 0.5` -> 4.5
| [blue]`-` | _Subtraction_ | Subtract the right value from the left one | [blue]`4 - 0.5` -> 3.5
| [blue]`*` | _Product_ | Multiply two values | [blue]`4 * 0.5` -> 2.0
| [blue]`/` | _Float division_ | Divide the left value by the right one | [blue]`1.0 / 2` -> 0.5
| [blue]`./`| _Forced float division_ | Force float division | [blue]`-1 ./ 2` -> -0.5
|===
+[[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
_Expr_ also supports fractions. Fraction literals are made with two integers separated by a colon character `:`.
@@ -860,7 +889,7 @@ The table below shows all supported operators by decreasing priorities.
.6+|*ITER-OP*| [blue]`digest` | _Infix_ | _Item-digesting_ | _iterable_ `digest` _expr_ -> _any_
| [blue]`filter` | _Infix_ | _Item-filtering_ | _iterable_ `filter` _expr_ -> _list_
| [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_
4+| _See iterators section for examples_
.1+|*RANGE*| [blue]`:` | _Infix_ | _Index-range_ | _integer_ `:` _integer_ -> _integer-pair_
@@ -1507,10 +1536,56 @@ Creates or truncates the named __. If the file already exists, it is
#to-do#
===== 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()
-#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"
@@ -1775,6 +1850,54 @@ TIP: Iterators built on custom data-sources can provide additional named operato
`>>>` [blue]`it.next` +
[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>>
+* <<_diget,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.
+
+---
+
+==== [blue]`cat` operator
+Syntax: +
+`{4sp} cat -> {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
+
+
+==== [blue]`filter` operator
+Syntax: +
+`{4sp} filter -> {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]`groupby` operator
+Syntax: +
+`{4sp} groupby -> {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.
+
+==== [blue]`map` operator
+Syntax: +
+`{4sp} map -> {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 `$_`.
+
+.Example: using the [blue]`map` operator
+`>>>` [blue]`it = $(["one", "two", "three"])` +
+[green]`$(#3)` +
+`>>>` [blue]`it map $_ + "!"` +
+[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.
diff --git a/doc/Expr.html b/doc/Expr.html
index 4343b4b..387194b 100644
--- a/doc/Expr.html
+++ b/doc/Expr.html
@@ -581,7 +581,11 @@ pre.rouge .ss {
4.1. ; operator
4.2. but operator
4.3. Assignment operator =
-4.4. Selector operator ? : ::
+4.4. Selector operator ? : ::
+
+
4.5. Variable default value ??, ?=, and ?!
@@ -613,8 +617,10 @@ pre.rouge .ss {
dec()
string()
fract()
+char()
eval()
var()
+set
7.1.2. Module "fmt"
@@ -649,6 +655,8 @@ pre.rouge .ss {
fileWriteText()
fileReadText()
fileReadTextAll()
+fileByteIterator()
+fileLineIterator()
7.1.7. Module "string"
@@ -674,7 +682,15 @@ pre.rouge .ss {
8.1.1. Named operators
-8.2. Iterator over custom data-sources
+8.2. Infixed operators on iterators
+
+
+8.3. Iterator over custom data-sources
9. Plugins
@@ -686,7 +702,7 @@ pre.rouge .ss {
-
TODO: Work in progress (last update on 2026/04/15, 6:02 p.m.)
+
TODO: Work in progress (last update on 2026/05/08)
@@ -788,7 +804,26 @@ pre.rouge .ss {
}
-
In order to inspect the global context issue the $$global operator.
+
In order to inspect the global context issue the $$ global operation.
+
+
+
Example: list all functions whose name starts with "str"
+
>>> builtin "string // most function in the builtin module string have names starting with "str".
+1
+
+
+
>>> ($$$global).functions) filter strStartsWith($_, "str"
+[
+ "strEndsWith",
+ "strJoin",
+ "strLower",
+ "strSplit",
+ "strStartsWith",
+ "strSub",
+ "strTrim",
+ "strUpper",
+ "string"
+]
@@ -1880,10 +1915,10 @@ Technically ; is not treated as a real operator. It ac
The selector operator is very similar to the switch/case/default statement available in many programming languages.
-
Example 13. Selector literal Syntax
+
Example 13. Selector literal syntax
-
selector-operator = select-expression "?" selector-case { ":" selector-case } ["::" default-multi-expression]
+
selector-operator = select-expression "?" selector-case { ":" selector-case } ["::" default-multi-expression]
selector-case = [match-list] case-value
match-list = "[" item {"," items} "]"
item = expression
@@ -1931,11 +1966,45 @@ Technically ; is not treated as a real operator. It ac
>>> 10 ? {"a"} : {"b"}
Eval Error: [1:3] no case catches the value (10) of the selection expression
+
+
+
+
If the select-expression is a boolean expression, the selector operator can be used as a sort of if-then-else statement. In this case, the first case is evaluated if the select-expression is true, and the second case is evaluated if the select-expression is false. In this special case, the match-list of both cases must be empty.
+
+
+
Example
+
>>> (true) ? {"T"}: {"F"}
+T
+>>> (2 > 1) ? {"a"} : {"b"}
+a
+>>> (2 < 1) ? {"a"} : {"b"}
+b
+
+
+
+
+|
+
+ |
+
+
+ The triple special case of the selector operator is very useful, but it only works with boolean expressions.
+
+
+ Example of confusion
+ >>> int(true) ? {"T"}: {"F"}
+F
+
+ |
+
+
+
+
-
The left operand of the first two operators, ?? and ?=, 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.
+
The left operand of the first two operators, ?? and ?=, 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.
@@ -1953,7 +2022,7 @@ If the left variable is defined, the right expression is not evaluated at all.
The ?? operator do not change the status of the left variable.
-
The ?= assigns the calculated value of the right expression to the left variable.
+
The ?= assigns the calculated value of the right expression to the variable on the left side.
The third one, ?!, is the alternate operator. If the variable on the left size is not defined, it returns nil. Otherwise it returns the result of the expressione on the right side.
@@ -1965,7 +2034,7 @@ If the left variable is defined, the right expression is not evaluated at all.
-If the left variable is NOT defined, the right expression is not evaluated at all.
+If the variable ?! is NOT defined, the expression is not evaluated at all.
|
@@ -2371,7 +2440,7 @@ These operators have a high priority, in particular higher than the operator
identifier = any → any
-See also the table of special allocation operators below |
+See also the table of special assignment operators below |
BUT |
@@ -2381,6 +2450,40 @@ These operators have a high priority, in particular higher than the operator any but any → any
+ITER-OP |
+digest
|
+Infix |
+Item-digesting |
+iterable digest expr → any |
+
+
+filter
|
+Infix |
+Item-filtering |
+iterable filter expr → list |
+
+
+groupby
|
+Infix |
+Dict-grouping |
+iterable groupby key-expr → dict |
+
+
+cat
|
+Infix |
+Item-concatenation |
+iterable `cat ` iterable → list |
+
+
+map
|
+Infix |
+Item-mapping |
+iterable map -expr → list |
+
+
+See iterators section for examples |
+
+
RANGE |
:
|
Infix |
@@ -2536,14 +2639,14 @@ short for
>>> // Required and optional parameters
->>> measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? [true] {"s"} :: {""}}
+>>> measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? {"s"} :: {""}}
measure(value, unit="meter"):any{}
-
Description of how to define Golang functions and how to bind them to Expr are topics covered in another document 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 another documents that I’ll write, one day, maybe.
@@ -2642,7 +2745,7 @@ short for
Example
>>> f = func() { @x = 3; x = 5 } // f() declares two different local variables: @x and x
f():any{}
->>> f() // The multi-expression (two) in f() is calculated and the last result is returned
+>>> f() // The multi-expression (two expressions) in f() is calculated and the last result is returned
5
>>> x // The x variable was not defined in the main context before the f() invocation. It appears in the main context by cloning the @x variable, local to f() after its termnation.
3
@@ -2727,7 +2830,7 @@ The clone modifier
@ does not make a variable a refere
Example 16. Builtin activation syntax
-
builtin-activation = BUILTIN (builtin-name | list-of-builtin-names)
+
builtin-activation = BUILTIN (builtin-name | list-of-builtin-names | "*")
builtin-name = string
list-of-builtin-names = [ string { "," string } ]
@@ -2815,9 +2918,15 @@ To avoid the need to activate builtin modules one by one, it is possible to acti
Other functions
@@ -2833,7 +2942,9 @@ Returns
true if the value type of
<expr> is boolean, fal
>>> isBool(true)
true
>>> isBool(3==2)
-true
+
true
+
>>> isBool(3 + 2)
+
false
@@ -2938,7 +3049,7 @@ Returns
true if the value type of
<expr> is fraction or
Syntax: isString(<expr>) → bool
-Returns a boolean value , false otherwise.
+Returns
true if the value type of
<expr> is string, false otherwise.
Examples
@@ -2954,7 +3065,7 @@ Returns a boolean value , false otherwise.
Syntax: bool(<expr>) → bool
-Returns a boolean value consisent to the value of the expression.
+Returns a
boolean value consisent with the value of the expression.
Examples
@@ -3077,6 +3188,20 @@ Returns a
fraction value consistent with the value of the expression.
+
+
+
Syntax: char(<intexpr>) → string
+Returns the character whose ASCII (soon Unicode too) code point is specified by the integer expression.
+
+
+
Examples
+
>>> char(65)
+"A"
+>>> char(97)
+"a"
+
+
+
Syntax: eval(<string-expr>) → any
@@ -3096,13 +3221,13 @@ Computes and returns the value of the string expr
var(<string-expr>) → any
-
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.
+
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.
Examples
>>> var("$x", 3+9)
12
->>> var("$x")
+>>> var("$"+"x")
12
>>> var("gain%", var("$x"))
12
@@ -3110,9 +3235,32 @@ Computes and returns the value of the string expr
13
+
+
+
+
Syntax:
+ set(<string-expr>, <expr>) → any
+
+
+
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.
+
+
+
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.
+
+
+
Examples
+
>>> set("$x", 100)
+100
+>>> var("$x")
+100
+
+
+
@@ -3124,10 +3272,18 @@ Computes and returns the value of the
string expr
+
+
Module activation:
+ BUILTIN "import"
+
-
import(<source-file>) — loads the multi-expression contained in the specified source and returns its value.
+
Syntax:
+ import(<source-file>)
+
+
+
Loads the multi-expression contained in the specified source and returns its value.
@@ -3137,16 +3293,40 @@ Computes and returns the value of the string expr
+
+
Module activation:
+ BUILTIN "iterator"
+
-
+
+
Syntax:
+ run(<iterator>, <operator>, <vars>) → any
+
+
+
Iterates over the specified iterator and applies the specified operator to the current value of the iterator.
+
+
Module activation:
+ BUILTIN "math.arith"
+
+
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.
+
@@ -3202,37 +3382,213 @@ Computes and returns the value of the string expr
+
+
Module activation:
+ BUILTIN "os.file"
+
+
+
The "os.file" module provides functions for working with files.
+
+
+
File related functions
+
+
+
+
Iterator functions for files
+
+
+
+
More functions will be added in the future.
+
+
-
+
+
Syntax:
+ fileOpen(<file-path>) → file-handle
+
+
+
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.
+
-
+
+
Syntax:
+ fileAppend(<file-path>) → any
+
+
+
Like fileCreate() but write operations happen at the end of the file.
+
-
+
+
Syntax:
+ fileCreate(<file-path>) → file-handle
+
+
+
Creates or truncates the named <file-path>. 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.
+
+
+
+
+
Syntax:
+ 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"
+1
+>>> fileByteIterator("test-file.txt") map $_
+[
+ 117,
+ 110,
+ 111,
+ 10,
+ 100,
+ 117,
+ 101,
+ 10
+]
+
+
+
>>> builtin ["os.file", "string"]
+2
+>>> fileByteIterator("test-file.txt") map char($_)
+[
+ "u",
+ "n",
+ "o",
+ "
+",
+ "d",
+ "u",
+ "e",
+ "
+",
+]
+
+
+
+
+
+
Syntax:
+ 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
+
>>> builtin "os.file"
+1
+>>> fileLineIterator("test-file.txt") map $_
+[
+ "uno",
+ "due"
+]
+
+
+
Module activation:
+ BUILTIN "string"
+
+
+
This module provides functions for working with strings.
+
+
+
Currently available functions:
+
+
+
@@ -3589,7 +3945,81 @@ Iterators built on custom data-sources can provide additional named operators, d
-
+
+
+
There are also some infixed operators that can be used with iterators. They are defined as follows.
+
+
+
+
+
+
+
Syntax:
+ <iterable> cat <iterable> → <iterator>
+
+
+
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
+
Syntax:
+ <iterable> filter <expr> → <iterator>
+
+
+
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.
+
+
+
+
+
+
Syntax:
+ <dict> groupby <key> → <dict>
+
+
+
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.
+
+
+
+
+
+
Syntax:
+ <iterable> map <expr> → <list>
+
+
+
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 $_.
+
+
+
Example: using the map operator
+
>>> it = $(["one", "two", "three"])
+$(#3)
+>>> it map $_ + "!"
+["one!", "two!", "three!"]
+
+
+
+
+
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.
@@ -3610,7 +4040,7 @@ Iterators built on custom data-sources can provide additional named operators, d