diff --git a/doc/Expr.adoc b/doc/Expr.adoc index 7c952a5..8e36207 100644 --- a/doc/Expr.adoc +++ b/doc/Expr.adoc @@ -23,7 +23,7 @@ Expressions calculator :stylesheet: adoc-colony.css // Workaround to manage double-column in back-tick quotes -:2c: :: +:2colon: :: // Workaround to manage double-plus in back-tick quotes :plusplus: ++ // Workaround to manage asterisk in back-tick quotes @@ -33,6 +33,7 @@ Expressions calculator :2sp:    :4sp:      +:show_todo: toc::[] @@ -63,7 +64,7 @@ _Expr_ parses and computes each expression of a multi-expression, from the left 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. ==== Calculation context -All objects, such as variables and functions, created during the calculation of an expression are stored in a memory called _context_. +All objects, such as variables and functions, created during the calculation of an expression are stored in a memory block called _context_. The expression context is analogous to the stack-frame of other programming languages. When a function is called, a new context is allocated to store local definitions. @@ -76,21 +77,21 @@ 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 like `ecli` (see <<_ecli_test_tool>>) can be used to inspect contexts interactively. +An interactive tool like `ecli` (see <>) can be used to inspect contexts interactively. .Example: inspecting contexts `>>>` [blue]`$$` + -[green]`{"variables": {"ls": "[10, 20, 30]", "it": "$(#3)", "last": "-1"}, "functions": {"about": "about():string{}", "bin": "bin(value, digits=8):integer{}", "ctrl": "ctrl(prop, value):any{}", "ctrlList": "ctrlList():list-of-strings{}", "envGet": "envGet(name):string{}", "envSet": "envSet(name, value):string{}"}}` + +[green]`{"variables": {"ls": "[10, 20, 30]", "it": "$([#3])", "last": "-1"}, "functions": {"about": "about():string{}", "bin": "bin(value, digits=8):integer{}", "ctrl": "ctrl(prop, value):any{}", "ctrlList": "ctrlList():list-of-strings{}", "envGet": "envGet(name):string{}", "envSet": "envSet(name, value):string{}"}}` + `>>>` [gray]_// Let use the *ml* command to activate multi-line output of contexts, which is more readable._ + `>>>` [blue]`ml` + `>>>` [blue]`$$` + [green]`{` + [green]`{2sp}"variables": {` + - [green]`{4sp}"last": {"variables": {"last": "-1", "ls": "[10, 20, 30]", "it": "$(#3)"}, "functions": {"ctrlList": "ctrlList():list-of-strings{}", "envGet": "envGet(name):string{}", "envSet": "envSet(name, value):string{}", "about": "about():string{}", "bin": "bin(value, digits=8):integer{}", "ctrl": "ctrl(prop, value):any{}"}},` + + [green]`{4sp}"last": {"variables": {"last": "-1", "ls": "[10, 20, 30]", "it": "$([#3])"}, "functions": {"ctrlList": "ctrlList():list-of-strings{}", "envGet": "envGet(name):string{}", "envSet": "envSet(name, value):string{}", "about": "about():string{}", "bin": "bin(value, digits=8):integer{}", "ctrl": "ctrl(prop, value):any{}"}},` + [green]`{4sp}"ls": [10, 20, 30],` + - [green]`{4sp}"it": $(#3)` + + [green]`{4sp}"it": $([#3])` + [green]`{2sp}},` + [green]`{2sp}"functions": {` + [green]`{4sp}"ctrlList": ctrlList():list-of-strings{},` + @@ -102,7 +103,8 @@ An interactive tool like `ecli` (see <<_ecli_test_tool>>) can be used to inspect [green]`{2sp}}` + [green]`}` -In order to inspect the global context issue the [blue]`$$ global` operation. +To inspect the global context perform 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".__ + @@ -129,7 +131,7 @@ In order to inspect the global context issue the [blue]`$$ global` operation. === `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. -`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` is a simple program that can be used to evaluate expressions interactively. As its original name _dev-expr_ suggests, it was created for testing purpose. In fact, in addition to the automatic verification test suite based on the Go test framework, `ecli` provides 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. @@ -212,7 +214,7 @@ ecli -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoroso@p <5> Multi-expression: the same result as the previous single expression, but this time it is obtained with two separate calculations. == Data types -_Expr_ has its type system which is a subset of Golang's type system. It supports numerical, string, relational, boolean expressions, and mixed-type lists and maps. +_Expr_ has its type system which is approximately a subset of Golang's type system. It supports numerical, string, relational, boolean expressions, and mixed-type lists and maps. === Numbers _Expr_ supports three type of numbers: @@ -221,7 +223,7 @@ _Expr_ supports three type of numbers: . [blue]#Floats# . [blue]#Fractions# -In mixed operations involving integers, fractions and floats, automatic type promotion to the largest type is performed. +In mixed operations involving integers, fractions and floats, automatic type promotion to the larger type is performed. ==== Integers __Expr__'s integers are a subset of the integer set. Internally they are stored as Golang _int64_ values. @@ -247,21 +249,23 @@ 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"`. +^*(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 how many times the string has to be repeated in the result, e.g. `"foo" * 3` returns `"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. - +^*(3)*^ See also the _float division_ [blue]`./` below. +==== ==== Floats __Expr__'s floats are a subset of the rational number set. Note that they can't hold the exact value of an unlimited number; floats can only approximate them. Internally floats are stored as Golang's _float64_ values. @@ -298,14 +302,17 @@ _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"`. +^*(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 `:`. @@ -315,8 +322,8 @@ _Expr_ also supports fractions. Fraction literals are made with two integers sep *_fraction_* = [__sign__] (_num-den-spec_ | _float-spec_) + _sign_ = "**+**" | "**-**" + _num-den-spec_ = _digit-seq_ "**:**" _digit-seq_ + -_float-spec_ = _dec-seq_ "**.**" [_dec-seq_] "**(**" _repetend_ "**)**" + -_repetend_ = _dec-seq_ + +_float-spec_ = _dec-seq_ "**.**" [_dec-seq_] "**(**" _repeated-digits_ "**)**" + +_repeated-digits_ = _dec-seq_ + _dec-seq_ = _see-integer-literal-syntax_ + _digit-seq_ = _see-integer-literal-syntax_ ==== @@ -343,16 +350,33 @@ _digit-seq_ = _see-integer-literal-syntax_ `>>>` [blue]`-1:2` + [green]`-1:2` -`>>>` [blue]`1:-2` [gray]_// Invalid sign specification_ + -[red]_Eval Error: [1:3] infix operator ":" requires two non-nil operands, got 1_ +// `>>>` [blue]`1:-2` [gray]_// Invalid sign specification_ + +// [red]_Eval Error: [1:3] infix operator ":" requires two non-nil operands, got 1_ + +`>>>` [blue]`1:-2` + +[green]`-1:2` `>>>` [blue]`1:(-2)` + [green]`-1:2` -`>>>` [blue]`1.(3)` // 1.33333... + +`>>>` [blue]`1.(3)` [gray]_// 1.33333..._ + [green]`4:3` -Fractions can be used together with integers and floats in expressions. +`>>>` [blue]`1.2()` [gray]_// parenthesis force float to fraction conversion_ + +[green]`6:5` + +`>>>` [blue]`float(6:5)` [gray]_// convert fraction to float_<> + +[green]`1.2` + +`>>>` [blue]`float(1.2())` [gray]_// convert fraction to float_<> + +[green]`1.2` + +==== +[[note_fraction_to_float]] +^*(1)*^ The _float()_ function belongs to the builtin-in module "base". Before to use it, you needs to load the module with `BUILTIN "base"`. +==== + +Fractions can be used in conjunction with integers and floats in expressions. .Examples `>>>` [blue]`1:2 + 5` + @@ -367,7 +391,7 @@ Fractions can be used together with integers and floats in expressions. === Strings -Strings are character sequences enclosed between two double quote [blue]`"`. +Strings are character sequences enclosed in single [blue]`'` or double [blue]`"` quotes. .Examples `>>>` [blue]`"I'm a string"` + @@ -396,7 +420,7 @@ Some arithmetic operators also apply to strings. | [blue]`*` | _repeat_ | Make _n_ copy of a string | [blue]`"one" * 2` -> _"oneone"_ |=== -The characters in a string can be accessed using the square `[]` operator. +The characters in a string can be accessed by specifying the index using the square `[]` operator. Positive indexes start from the left, with the first character at index 0. Negative indexes start from the right, with the last character at index -1. .Item access syntax ==== @@ -449,7 +473,7 @@ Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relationa [blue]`"b" >= "b"` -> _true_ |=== -^(*)^ See also the [blue]`in` operator in the _list_ and _dictionary_ sections. +^(*)^ See also the [blue]`in` operator in the _array_, _list_ and _dictionary_ sections. .Boolean operators @@ -482,60 +506,63 @@ Currently, boolean operations are evaluated using _short cut evaluation_. This m 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 slices. +=== Arrays +_Expr_ supports arrays of mixed-type values, also specified by normal expressions. Internally, _Expr_'s arrays are Go slices. -.List literal syntax +.Array literal syntax ==== -*_list_* = _empty-list_ | _non-empty-list_ + -_empty-list_ = "**[]**" + -_non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value_} "**]**" + +*_array_* = _empty-array_ | _non-empty-array_ + +_empty-array_ = "**[]**" + +_non-empty-array_ = "**[**" _any-value_ {"**,**" _any-value_} "**]**" + ==== .Examples -`>>>` [blue]`[1,2,3]` [gray]_// List of integers_ + +`>>>` [blue]`[1,2,3]` [gray]_// Array of integers_ + [green]`[1, 2, 3]` -`>>>` [blue]`["one", "two", "three"]` [gray]_// List of strings_ + +`>>>` [blue]`["one", "two", "three"]` [gray]_// Array of strings_ + [green]`["one", "two", "three"]` -`>>>` [blue]`["one", 2, false, 4.1]` [gray]_// List of mixed-types_ + +`>>>` [blue]`["one", 2, false, 4.1]` [gray]_// Array of mixed-types_ + [green]`["one", 2, false, 4.1]` -`>>>` [blue]`["one"+1, 2.0*(9-2)]` [gray]_// List of expressions_ + +`>>>` [blue]`["one"+1, 2.0*(9-2)]` [gray]_// Items as results of expressions_ + [green]`["one1", 14]` -`>>>` [blue]`[ [1,"one"], [2,"two"]]` [gray]_// List of lists_ + +`>>>` [blue]`[ [1,"one"], [2,"two"]]` [gray]_// Array of arrays_ + [green]`[[1, "one"], [2, "two"]]` -.List operators +.Array operators [cols="^2,^2,5,4"] |=== | Symbol | Operation | Description | Examples -| [blue]`+` | _Join_ | Joins two lists | [blue]`[1,2] + [3]` -> _[1,2,3]_ -| [blue]`-` | _Difference_ | Left list without elements in the right list | [blue]`[1,2,3] - [2]` -> _[1,3]_ -| [blue]`+>` | _Front insertion_ | Insert an item in front | [blue]`0 +> [1,2]` -> _[0,1,2]_ -| [blue]`<+` | _Back insertion_ | Insert an item at end | [blue]`[1,2] <+ 3` -> _[1,2,3]_ +| [blue]`+` | _Join_ | Joins two arrays | [blue]`[1,2] + [3]` -> _[1,2,3]_ +| [blue]`-` | _Difference_ | Left array without elements in the right array | [blue]`[1,2,3] - [2]` -> _[1,3]_ +| [blue]`>>` + +[blue]`+>` | _Front insertion_ | Insert an item in front | [blue]`0 >> [1,2]` -> _[0,1,2]_ +| [blue]`<<` + +[blue]`<+` | _Back insertion_ | Insert an item at end | [blue]`[1,2] << 3` -> _[1,2,3]_ | [blue]`[]` | _Item at index_ | Item at given position | [blue]`[1,2,3][1]` -> _2_ -| [blue]`in` | _Item in list_ | True if item is in list | [blue]`2 in [1,2,3]` -> _true_ + -[blue]`6 in [1,2,3]` -> _false_ -| [blue]`#` | _Size_ | Number of items in a list | [blue]`#[1,2,3]` -> _3_ +| [blue]`in` | _Item in array_ | True if item is in array | [blue]`2 in [1,2,3]` -> _true_ + +| [blue]`at` | _Index of an item_ | Returns the index of an item in array | [blue]`2 at in [1,2,3]` -> _1_ + +[blue]`6 at [1,2,3]` -> _-1_ +| [blue]`#` | _Size_ | Number of items in a array | [blue]`#[1,2,3]` -> _3_ |=== Array's items can be accessed using the index `[]` operator. .Item access syntax ==== -*_item_* = _list-expr_ "**[**" _integer-expr_ "**]**" +*_item_* = _array-expr_ "**[**" _integer-expr_ "**]**" ==== .Sub-array (or slice of array) syntax ==== -*_slice_* = _string-expr_ "**[**" _integer-expr_ "**:**" _integer-expr_ "**]**" +*_slice_* = _array-expr_ "**[**" _integer-expr_ "**:**" _integer-expr_ "**]**" ==== -.Examples: Getting items from lists +.Examples: Getting items from arrays `>>>` [blue]`[1,2,3][1]` + [green]`2` @@ -545,55 +572,69 @@ Array's items can be accessed using the index `[]` operator. `>>>` [blue]`["a", "b", "c", "d"][2:]` + [green]`["c", "d"]` -`>>>` [blue]`list=[1,2,3]; list[1]` + +`>>>` [blue]`array=[1,2,3]; array[1]` + [green]`2` `>>>` [blue]`["one","two","three"][1]` + [green]`two` -`>>>` [blue]`list=["one","two","three"]; list[2-1]` + +`>>>` [blue]`array=["one","two","three"]; array[2-1]` + [green]`two` -`>>>` [blue]`list[1]="six"; list` + +`>>>` [blue]`array[1]="six"; array` + [green]`["one", "six", "three"]` -`>>>` [blue]`list[-1]` + +`>>>` [blue]`array[-1]` + [green]`three` -`>>>` [blue]`list[10]` + +`>>>` [blue]`array[10]` + [red]`Eval Error: [1:9] index 10 out of bounds` -.Example: Number of elements in a list -`>>>` [blue]`#list` + +.Example: Number of elements in an array +`>>>` [blue]`#array` + [green]`3` .Examples: Element insertion -`>>>` [blue]`"first" +> list` + +`>>>` [blue]`"first" >> array` + [green]`["first", "one", "six", "three"]` -`>>>` [blue]`list <+ "last"` + +`>>>` [blue]`array << "last"` + [green]`["first", "one", "six", "three", "last"]` -.Examples: Element in list -`>>>` [blue]`"six" in list` + +.Examples: Element in array +`>>>` [blue]`"six" in array` + [green]`true` -`>>>` [blue]`"ten" in list` + +`>>>` [blue]`"ten" in array` + [green]`false` +.Examples: Index of an element in array +`>>>` [blue]`"six" at array` + +[green]`2` + +`>>>` [blue]`"ten" at array` + +[green]`-1` + .Examples: Concatenation and filtering `>>>` [blue]`[1,2,3] + ["one", "two", "three"]` + [green]`[1, 2, 3, "one", "two", "three"]` -`>>>` [blue]`[1,2,3,2,4] - [2,4]` + +`>>>` [blue]`[1,2,3,2,4] - [2,4]` [gray]_Note that *all* occurrences are removed_ + [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_. +A linked list is a fundamental linear data structure in computer science. Unlike an array, where elements are stored in contiguous memory locations, the elements in a linked list are scattered in memory. -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. +These elements are called _nodes_. Each node consists of two main parts: -.Linked List literal syntax +_Data_: The actual value or information stored in the node. + +_Pointer_ (or _Reference_): A link that points to the memory address of the next node in the sequence. + +The first node in the list is called the _head_. The last node in the list is called the _tail_. + +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_ @@ -602,36 +643,53 @@ _item-expr_ = _any-value_ `>>>` [blue]`ls=[<1,2,3,4>]` + [green]`[<1, 2, 3, 4>]` +In general, arrays and linked-lists support similar operations. However, there are some differences between them. For example, arrays support efficient random access to elements, while linked-lists support efficient insertion and deletion of elements at both ends of the list. + + #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_. -Dictionary literals are sequences of pairs separated by comma [blue]`,` enclosed between brace brackets. +Dictionary literals are sequences of pairs separated by comma [blue]`,` enclosed in curly brackets [blue]`{}`. .Dict literal syntax ==== *_dict_* = _empty-dict_ | _non-empty-dict_ + _empty-dict_ = "**{}**" + -_non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar_ "**:**" _any-value_} "**}**" + +_non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar_ "**:**" _any-value_} "**}**" <> + ==== +==== +[[note_scalar_key]] +^*(1)*^ Currently, _key-scalar_ type can only be _integer_ or _string_. +==== -.Item access syntax +To get the value of a dictionary's item, the key must be specified. The key is used to access the corresponding value in the dictionary. +Two access methods are available: the _key in square brackets_ and the _dot notation_ method. + +.Item access syntax by key in square brackets ==== *_item_* = _dict-expr_ "**[**" _key-expr_ "**]**" ==== +.Item access syntax by dot notation +==== +*_item_* = _dict-expr_ "**.**" _key-expr_ +==== + .Dict operators [cols="^2,^2,4,5"] |=== | Symbol | Operation | Description | Examples -| [blue]`+` | _Join_ | Joins two dicts | [blue]`{1:"one"}+{6:"six"}` -> _{1: "one", 6: "six"}_ +| [blue]`+` | _Join_ | Joins two dicts | [blue]`{1:"one"} + {6:"six"}` -> _{1: "one", 6: "six"}_ | [blue]`[]` | _Dict item value_ | Item value of given key | [blue]`{"one":1, "two":2}["two"]` -> _2_ +| [blue]`.` | _Dict item value_ | Item value of given key | [blue]`{"one":1, "two":2}."two"` -> _2_ | [blue]`in` | _Key in dict_ | True if key is in dict | [blue]`"one" in {"one":1, "two":2}` -> _true_ + -[blue]`"six" in {"one":1, "two":2}` -> _false_ -| [blue]`#` | _Size_ | Number of items in a dict | [blue]`#{1:"a",2:"b",3:"c"}` -> _3_ +| [blue]`at` | _Key of given value_ | Returns the key of given value | [blue]`1 at {"one":1, "two":2}` -> _"one"_ + +[blue]`6 at {"one":1, "two":2}` -> _nil_ +| [blue]`#` | _Size_ | Number of items in a dict | [blue]`#{1:"a", 2:"b", 3:"c"}` -> _3_ |=== .Examples @@ -647,6 +705,9 @@ _non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar `>>>` [blue]`{"one":1, "two":2}["two"]` + [green]`2` +`>>>` [blue]`{"one":1, "two":2}."two"` <> + +[green]`2` + `>>>` [blue]`d={"one":1, "two":2}; d["six"]=6; d` + [green]`{"two": 2, "one": 1, "six": 6}` @@ -654,6 +715,21 @@ _non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar [green]`3` +==== +[[note_dict_dot_notation]] +^*(1)*^ When the key type is string, the key value can be expressed without quotes in the dot notation method. + +Example: `d={"one":1, "two":2}; d.two` returns _2_. + +To force the evaluation of a key expression, enclose it in parenthesis. Example: + +`a="b"; d={"a":"one", "b":"two"}; d.(a)` returns _"two"_. + +whereas + +`a="b"; d={"a":"one", "b":"two"}; d.a` returns _"one"_. +==== + == Variables _Expr_, like most programming languages, supports variables. A variable is an identifier with an assigned value. Variables are stored in _contexts_. @@ -664,7 +740,7 @@ _identifier_ = _alpha_ {_alpha_|_dec-digit_|"*_*"} + __alpha__ = "*a*"|"*b*"|..."*z*"|"*A*"|"*B*"|..."*Z*" ==== -NOTE: The assign operator [blue]`=` returns the value assigned to the variable. +NOTE: The assign operator <> returns the value assigned to the variable. .Examples `>>>` [blue]`a=1` + @@ -686,7 +762,7 @@ NOTE: The assign operator [blue]`=` returns the value assigned to the variable. [red]`Parse Error: [1:2] unexpected token "_"` `>>>` [blue]`1=2` + -[red]`Parse Error: assign operator ("=") must be preceded by a variable` +[red]`Eval Error: [1:2] left operand of "=" must be a variable or a collection's item` == Other operations @@ -726,12 +802,13 @@ The value of each sub-expression is stored in the automatic variable _last_. `>>>` [blue]`x=2*3 but x-1` + [green]`5` -[blue]`but` behavior is very similar to [blue]`;`. The only difference is that [blue]`;` is not a true operator and can't be used inside parenthesis [blue]`(` and [blue]`)`. +[blue]`but` behavior is very similar to [blue]`;`. The only difference is that [blue]`;` is a pseudo-operator and can't be used inside parenthesis [blue]`(` and [blue]`)`. Furthermore, [blue]`but`, being a true operator, does not set the _last_ variable. -=== Assignment operator [blue]`=` +[[section_assignment_operators]] +=== Assignment operators [blue]`=` and [blue]`:=` The assignment operator [blue]`=` is used to define variables or to change their value in the evaluation context (see _ExprContext_). -The value on the left side of [blue]`=` must be a variable identifier or an expression that evalutes to a variable. The value on the right side can be any expression and it becomes the result of the assignment operation. +The value on the left side of [blue]`=` must be a variable identifier or an expression that evalutes to a variable. The value on the right side can be any expression and its value becomes the result of the assignment operation. .Examples `>>>` [blue]`a=15+1` + @@ -740,14 +817,29 @@ The value on the left side of [blue]`=` must be a variable identifier or an expr `>>>` [blue]`L=[1,2,3]; L[1]=5; L` + [green]`[1, 5, 3]` +The [blue]`:=` operator has same effect as [blue]`=` for scalar values. However their behavior is different for array, linked-list and dict variables. In such cases [blue]`=` assignes a reference to the variable, whereas [blue]`:=` assignes a deep copy of the value. + +.Examples +[gray]_// Assign reference with '='_ + +`>>>` [blue]`L=[1,2,3]; M=L; M[0]=42; L` + +[green]`[42, 2, 3]` + +[gray]_// In this case, both L and M point to the same array. Therefore, changing M also changes L._ + + +[gray]_// Assign deep copy with ':='_ + +`>>>` [blue]`L=[1,2,3]; M:=L; M[0]=42; L` + +[green]`[1, 2, 3]` + +[gray]_// In this case, L and M are separate arrays. Therefore, changing M does not affect L._ + === Selector operator [blue]`? : ::` The _selector operator_ is very similar to the _switch/case/default_ statement available in many programming languages. .Selector literal syntax ==== -*_selector-operator_* = _select-expression_ "*?*" _selector-case_ { "*:*" _selector-case_ } ["*::*" _default-multi-expression_] + -_selector-case_ = [_match-list_] _case-value_ + +*_selector-operator_* = _select-expression_ "*?*" _selector-case_ { "*:*" _selector-case_ } ["*::*" _default-multi-expression_] + +_selector-case_ = [_match-list_] _case-multi-expression_ + _match-list_ = "*[*" _item_ {"*,*" _items_} "*]*" + _item_ = _expression_ + _case-multi-expression_ = "*{*" _multi-expression_ "*}*" + @@ -755,7 +847,7 @@ _multi-expression_ = _expression_ { "*;*" _expression_ } + _default-multi-expression_ = _multi-expression_ ==== -In other words, the selector operator evaluates the _select-expression_ on the left-hand side of the [blue]`?` symbol; it then compares the result obtained with the values listed in the __match-list__'s, from left to right. If the comparision finds a match with a value in a _match-list_, the associated _case-multi-expression_ is evaluted, and its result will be the final result of the selection operation. +In other words, the selector operator evaluates the _select-expression_ on the left-hand side of the [blue]`?` symbol; it then compares the result obtained with the values listed in the __match-list__, from left to right. If the comparision finds a match with a value in a _match-list_, the associated _case-multi-expression_ is evaluted, and its result will be the final result of the selection operation. The match lists are optional. In that case, the position, from left to right, of the _selector-case_ is used as _match-list_. Of course, that only works if the _select-expression_ results in an integer. @@ -765,21 +857,24 @@ The [blue]`:` symbol (colon) is the separator of the selector-cases. Note that i `>>>` [blue]`1 ? {"a"} : {"b"}` + [green]`b` -`>>>` [blue]`10 ? {"a"} : {"b"} {2c} {"c"}` + +`>>>` [blue]`10 ? {"a"} : {"b"} {2colon} {"c"}` + [green]`c` -`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} {2c} {"c"}` + +`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} {2colon} {"c"}` + [green]`b` -`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} {2c} [10] {"c"}` + +`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} {2colon} [10] {"c"}` + [red]`Parse Error: [1:34] case list in default clause` -`>>>` [blue]`10 ? {"a"} :[10] {x="b" but x} {2c} {"c"}` + +`>>>` [blue]`10 ? {"a"} :[10] {x="b" but x} {2colon} {"c"}` + [green]`b` -`>>>` [blue]`10 ? {"a"} :[10] {x="b"; x} {2c} {"c"}` + +`>>>` [blue]`10 ? {"a"} :[10] {x="b"; x} {2colon} {"c"}` + [green]`b` +`>>>` [blue]`11 ? {"a"} :[10] {"b"} {2colon} {"c"}` + +[green]`c` + `>>>` [blue]`10 ? {"a"} : {"b"}` + [red]`Eval Error: [1:3] no case catches the value (10) of the selection expression` @@ -816,19 +911,27 @@ The third one, [blue]`?!`, is the alternate operator. If the variable on the lef IMPORTANT: If the variable [blue]`?!` is NOT defined, the expression is not evaluated at all. -.Examples +.Examples: effect of the [blue]`??` operator `>>>` [blue]`var ?? (1+2)` + [green]`3` `>>>` [blue]`var` + [red]`Eval Error: undefined variable or function "var"` +.Examples: effect of the [blue]`?=` operator `>>>` [blue]`var ?= (1+2)` + [green]`3` `>>>` [blue]`var` + [green]`3` +`>>>` [blue]`var ?= 5` + +[green]`3` + +`>>>` [blue]`var` + +[green]`3` + +.Examples: effect of the [blue]`?!` operator `>>>` [blue]`x ?! 5` + [green]`nil` @@ -843,7 +946,7 @@ NOTE: These operators have a high priority, in particular higher than the operat == Priorities of operators The table below shows all supported operators by decreasing priorities. -.Operators priorities +.Operators priorities (in this table "list" means _array_ or _linked-list_) [cols="^3,^2,^2,^5,^6"] |=== | Priority | Operator | Position | Operation | Operands and results @@ -863,10 +966,16 @@ The table below shows all supported operators by decreasing priorities. .3+|*SIGN*| [blue]`+`, [blue]`-` | _Prefix_ | _Change-sign_| (`+`\|`-`) _number_ -> _number_ | [blue]`#` | _Prefix_ | _Lenght-of_ | `#` _collection_ -> _integer_ | [blue]`#` | _Prefix_ | _Size-of_ | `#` _iterator_ -> _integer_ -.2+|*BIT SHIFT*| [blue]`<<` | _Infix_ | _Left-Shift_ | _integer_ `<<` _integer_ -> _integer_ +.4+|*BIT SHIFT* + +and +*INSERT*| [blue]`<<` | _Infix_ | _Left-Shift_ | _integer_ `<<` _integer_ -> _integer_ | [blue]`>>` | _Infix_ | _Right-Shift_ | _integer_ `>>` _iterator_ -> _integer_ -.2+|*SELECT*| [blue]`? : ::` | _Multi-Infix_ | _Case-Selector_ | _any-expr_ `?` _case-list_ _case-expr_ `:` _case-list_ _case-expr_ ... `::` _default-expr_ -> _any_ +| [blue]`>>` | _Infix_ | _Head-Insert_ | _any_ `>>` _list_ -> _list_ +| [blue]`<<` | _Infix_ | _Tail-Insert_ | _list_ `<<` _any_ -> _list_ +.3+|*SELECT*| [blue]`? : ::` | _Multi-Infix_ | _Case-Selector_ | _any-expr_ `?` _case-list_ _case-expr_ `:` _case-list_ _case-expr_ ... `::` _default-expr_ -> _any_ | [blue]`? : ::` | _Multi-Infix_ | _Index-Selector_ | _int-expr_ `?` _case-expr_ `:` _case-expr_ ... `::` _default-expr_ -> _any_ +| [blue]`? :` | _Multi-Infix_ | _Boolean-Selector_ + +(aka _triple_)| _bool-expr_ `?` _true-case-expr_ `:` _false_case-expr_ -> _any_ .1+|*FRACT*| [blue]`:` | _Infix_ | _Fraction_ | _integer_ `:` _integer_ -> _fraction_ .7+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ `*` _number_ -> _number_ | [blue]`*` | _Infix_ | _String-repeat_ | _string_ `*` _integer_ -> _string_ @@ -874,7 +983,7 @@ The table below shows all supported operators by decreasing priorities. | [blue]`./` | _Infix_ | _Float-division_ | __number__ `./` _number_ -> _float_ | [blue]`/` | _Infix_ | _Split_ | _string_ `/` _string_ -> _list_ | [blue]`/` | _Infix_ | _Split_ | _string_ `/` integer -> _list_ -| [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ `%` _integer_ -> _integer_ +| [blue]`%` | _Infix_ | _Division-remainder_ | _integer_ `%` _integer_ -> _integer_ .6+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ `+` _number_ -> _number_ | [blue]`+` | _Infix_ | _String-concat_ | (_string_\|_number_) `+` (_string_\|_number_) -> _string_ | [blue]`+` | _Infix_ | _List-join_ | _list_ `+` _list_ -> _list_ @@ -885,7 +994,7 @@ The table below shows all supported operators by decreasing priorities. .1+|*BITWISE AND*| [blue]`&` | _Infix_ | _Binary And_ | _integer_ `&` _integer_ -> _integer_ .2+|*BITWISE OR*| [blue]`\|` | _Infix_ | _Binary Or_ | _integer_ `\|` _integer_ -> _integer_ | [blue]`^` | _Infix_ | _Binary Xor_ | _integer_ `^` _integer_ -> _integer_ -.8+|*RELATION*| [blue]`<` | _Infix_ | _Less_ | _comparable_ `<` _comparable_ -> _boolean_ +.10+|*RELATION*| [blue]`<` | _Infix_ | _Less_ | _comparable_ `<` _comparable_ -> _boolean_ | [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ `\<=` _comparable_ -> _boolean_ | [blue]`>` | _Infix_ | _Greater_ | _comparable_ `>` _comparable_ -> _boolean_ | [blue]`>=` | _Infix_ | _Greater-equal_ | _comparable_ `>=` _comparable_ -> _boolean_ @@ -893,6 +1002,8 @@ The table below shows all supported operators by decreasing priorities. | [blue]`!=` | _Infix_ | _Not-equal_ | _comparable_ `!=` _comparable_ -> _boolean_ | [blue]`in` | _Infix_ | _Member-of-list_ | _any_ `in` _list_ -> _boolean_ | [blue]`in` | _Infix_ | _Key-of-dict_ | _any_ `in` _dict_ -> _boolean_ +| [blue]`at` | _Infix_ | _Index-of-value-in-list_ | _any_ `at` _dict_ -> _intger_ +| [blue]`at` | _Infix_ | _Key-of-value-in-dict_ | _any_ `at` _dict_ -> _key_ .1+|*LOGIC NOT*| [blue]`not` | _Prefix_ | _Not_ | `not` _boolean_ -> _boolean_ .2+|*LOGIC AND*| [blue]`and` | _Infix_ | _And_ | _boolean_ `and` _boolean_ -> _boolean_ | [blue]`&&` | _Infix_ | _And_ | _boolean_ `&&` _boolean_ -> _boolean_ @@ -900,14 +1011,15 @@ The table below shows all supported operators by decreasing priorities. | [blue]`\|\|` | _Infix_ | _Or_ | _boolean_ `\|\|` _boolean_ -> _boolean_ .2+|*INSERT*| [blue]`+>` | _Infix_ | _Prepend_ | _any_ `+>` _list_ -> _list_ | [blue]`<+` | _Infix_ | _Append_ | _list_ `<+` _any_ -> _list_ -.2+|*ASSIGN*| [blue]`=` | _Infix_ | _Assignment_ | _identifier_ `=` _any_ -> _any_ +.3+|*ASSIGN*| [blue]`=` | _Infix_ | _Ref-Assignment_ | _identifier_ `=` _any_ -> _any_ +| [blue]`:=` | _Infix_ | _Copy-Assignment_ | _identifier_ `=` _any_ -> _any_ 4+| _See also the table of special assignment operators below_ .1+|*BUT*| [blue]`but` | _Infix_ | _But_ | _any_ `but` _any_ -> _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_ -> _iterable_ | [blue]`groupby` | _Infix_ | _Dict-grouping_ | _iterable_ `groupby` _key-expr_ -> _dict_ -| [blue]`cat` | _Infix_ | _Item-concatenation_ | _iterable_ `cat ` _iterable_ -> _list_ -| [blue]`map` | _Infix_ | _Item-mapping_ | _iterable_ `map` _-expr_ -> _list_ +| [blue]`cat` | _Infix_ | _Item-concatenation_ | _iterable_ `cat` _iterable_ -> _iterable_ +| [blue]`map` | _Infix_ | _Item-mapping_ | _iterable_ `map` _expr_ -> _iterable_ 4+| _See iterators section for examples_ .1+|*RANGE*| [blue]`:` | _Infix_ | _Index-range_ | _integer_ `:` _integer_ -> _integer-pair_ |=== @@ -960,7 +1072,8 @@ An expr-function is identified and referenced by its name. It can have zero or m .Expr's function definition syntax ==== -*_function-definition_* = _identifier_ "**=**" "**func(**" [_formal-param-list_] "**)**" "**{**" _multi-expression_ "**}**" + +*_function-definition_* = _identifier_ "**=**" "**func(**" [_formal-param-list_] "**)**" "**{**" _multi-expression_ "**}**" + _formal-param-list_ = _required-param-list_ [ "**,**" _optional-param-list_ ] + _required-param-list_ = _identifier_ { "**,**" _identifier_ } + _optional-param-list_ = _optional-parm_ { "**,**" _optional-param_ } + @@ -968,6 +1081,7 @@ _optional-param_ = _param-name_ "**=**" _any-expr_ + _param-name_ = _identifier_ ==== +[[ex_sum_function]] .Examples `>>>` [gray]_// A simple function: it takes two parameters and returns their "sum"_**^(*)^** + `>>>` [blue]`sum = func(a, b){ a + b }` + @@ -975,6 +1089,7 @@ _param-name_ = _identifier_ ^(*)^ Since the plus, **+**, operator is defined for multiple data-types, the _sum()_ function can be used for any pair of that types. +[[ex_fib_function]] `>>>` [gray]_// A more complex example: recursive calculation of the n-th value of Fibonacci's sequence_ + `>>>` [blue]`fib = func(n){ n ? [0] {0}: [1] {1} :: {fib(n-1)+fib(n-2)} }` + [green]`fib(n):any{}` @@ -991,6 +1106,7 @@ _param-name_ = _identifier_ `\...` [blue]`}` + [green]`fib(n):any{}` +[[ex_measure_function]] `>>>` [gray]_// Required and optional parameters_ + `>>>` [blue]`measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? {"s"} :: {""}}` + [green]`measure(value, unit="meter"):any{}` @@ -1004,14 +1120,15 @@ To call a function, either Expr or Golang type, it is necessary to specify its n .Function invocation syntax ==== -*_function-call_* = _identifier_ "**(**" _actual-param-list_ "**)**" + +*_function-call_* = _identifier_ "**(**" _actual-param-list_ "**)**" + _actual-param-list_ = [_positional-params_] [_named-parameters_] + _positional-params_ = _any-value_ { "*,*" _any-value_ } + _named-params_ = _param-name_ "**=**" _any-value_ { "*,*" _param-name_ "**=**" _any-value_ } + _param-name_ = _identifier_ ==== -.Examples of calling the `sum()` functions defined above +.Examples of calling the <> functions defined above `>>>` [gray]_// sum of two integers_ + `>>>` [blue]`sum(-6, 2)` + [green]`-4` + @@ -1027,10 +1144,13 @@ _param-name_ = _identifier_ `>>>` [gray]_// sum of two strings_ + `>>>` [blue]`sum("bye", "-bye")` + [green]`"bye-bye"` + -`>>>` [gray]_// sum of two lists_ + +`>>>` [gray]_// sum of two arrays_ + `>>>` [blue]`sum(["one", 1], ["two", 2])` + [green]`["one", 1, "two", 2]` +=== Parameters passed by name +When calling a function, it is possible to pass parameters by name. In that case, the order of the parameters does not matter. The only requirement is that all required parameters must be specified. + .Examples of calling a function with parameters passed by name `>>>` [gray]_// diff(a,b) calculates a-b_ + `>>>` [blue]`diff = func(a,b){a-b}` + @@ -1042,14 +1162,19 @@ _param-name_ = _identifier_ `>>>` [blue]`diff(b=8,a=10)` + [green]`2` -.Examples of calling the `fib()` function defined above +[IMPORTANT] +==== +In function calls, parameters passed by name must be specified after all positional parameters. Otherwise, a parse error will be issued. +==== + +.Examples of calling the <> function defined above `>>>` [gray]_// Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ..._ + `>>>` [blue]`fib(6)` + [green]`8` + `>>>` [blue]`fib(9)` + [green]`34` -.Examples of calling the `measure()` functions defined above +.Examples of calling the <> functions defined above `>>>` [gray]_// simple call_ + `>>>` [blue]`measure(10,"litre")` + [green]`"10 litres"` + @@ -1083,7 +1208,7 @@ Clone variables are normal local variables. The only diffence will appear when t [green]`f():any{}` + `>>>` [blue]`f()` [gray]_// The multi-expression (two expressions) in f() is calculated and the last result is returned_ + [green]`5` + -`>>>` [blue]`x` [gray]_// 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._ + +`>>>` [blue]`x` [gray]_// 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._ + [green]`3` NOTE: The clone modifier [blue]`@` does not make a variable a reference variable, as the ampersand character `&` does in languages such as C and C++. @@ -1144,6 +1269,7 @@ The "base" builtin module provides functions for type checking and type conversi * <<_isdict,isDict()>> * <<_isfloat,isFloat()>> * <<_isfract,isFract()>> +* <<_isarray,isArray()>> * <<_islist,isList()>> * <<_isnil,isNil()>> * <<_isrational,isRational()>> @@ -1152,7 +1278,7 @@ The "base" builtin module provides functions for type checking and type conversi .Conversion functions * <<_bool,bool()>> * <<_int,int()>> -* <<_dec,dec()>> +* <<_float,float()>> * <<_string,string()>> * <<_fract,fract()>> @@ -1213,14 +1339,26 @@ Returns _true_ if the value type of __ is fraction, false otherwise. `>>>` [blue]`isFract(1.(3))` + [green]`true` +===== isArray() +Syntax: `isArray() -> bool` + +Returns _true_ if the value type of __ is array, false otherwise. + +.Examples +`>>>` [blue]`isArray([])` + +[green]`true` + +`>>>` [blue]`isArray([1, "2"])` + +[green]`true` + +`>>>` [blue]`isArray(1,2)` + +[red]`Eval Error: isList(): too many params -- expected 1, got 2` + ===== isList() Syntax: `isList() -> bool` + Returns _true_ if the value type of __ is list, false otherwise. .Examples -`>>>` [blue]`isList([])` + +`>>>` [blue]`isList([<>])` + [green]`true` + -`>>>` [blue]`isList([1, "2"])` + +`>>>` [blue]`isList([<1, "2">])` + [green]`true` + `>>>` [blue]`isList(1,2)` + [red]`Eval Error: isList(): too many params -- expected 1, got 2` @@ -1310,24 +1448,24 @@ Returns an _integer_ value consistent with the value of the expression. [green]`0` -===== dec() -Syntax: `dec() -> float` + +===== float() +Syntax: `float() -> float` + Returns a _float_ value consistent with the value of the expression. .Examples -`>>>` [blue]`dec(2)` + +`>>>` [blue]`float(2)` + [green]`2` + -`>>>` [blue]`dec(2.1)` + +`>>>` [blue]`float(2.1)` + [green]`2.1` + -`>>>` [blue]`dec(2.3(1))` + +`>>>` [blue]`float(2.3(1))` + [green]`2.311111111111111` + -`>>>` [blue]`dec("3.14")` + +`>>>` [blue]`float("3.14")` + [green]`3.14` + -`>>>` [blue]`dec(3:4)` + +`>>>` [blue]`float(3:4)` + [green]`0.75` + -`>>>` [blue]`dec(true)` + +`>>>` [blue]`float(true)` + [green]`1` + -`>>>` [blue]`dec(false)` + +`>>>` [blue]`float(false)` + [green]`0` ===== string() @@ -1362,7 +1500,7 @@ Returns a _fraction_ value consistent with the value of the expression. `>>>` [blue]`fract(1.(3))` + [green]`4:3` + `>>>` [blue]`fract([2])` + -[red]`Eval Error: fract(): can't convert list to float` + +[red]`Eval Error: fract(): can't convert array to float` + `>>>` [blue]`fract(false)` + [green]`0:1` + `>>>` [blue]`fract(true)` + @@ -1451,7 +1589,7 @@ Iterates over the specified iterator and applies the specified operator to the c Module activation: + `{4sp}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. +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. * <<_add,add()>> * <<_mul,mul()>> @@ -1538,7 +1676,7 @@ Like <<_fileCreate,fileCreate()>> but write operations happen at the end of the Syntax: + `{4sp}fileCreate() -> file-handle` -Creates or truncates the named __. 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. +Creates or truncates the named __. 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. ===== fileClose() #to-do# @@ -1559,10 +1697,12 @@ Syntax: + 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" + +`>>>` [blue]`builtin "os.file"` + [green]`1` + -`>>>` [blue]`fileByteIterator("test-file.txt") map $_` + -[green]`[ +`>>>` [blue]`it=fileByteIterator("test-file.txt") map $_` + +[green]`$($(fileByteIterator@"test-file.txt"))` + +`>>>` [blue]`$$( it )` + +[green]`[< 117, 110, 111, @@ -1571,12 +1711,14 @@ Returns an iterator that produces the bytes of the specified file. The parameter 117, 101, 10 -]` +>]` ->>> builtin ["os.file", "string"] + +`>>>` [blue]`builtin ["os.file", "string"]` + [green]`2` + -`>>>` [blue]`fileByteIterator("test-file.txt") map char($_)` + -[green]`[ +`>>>` [blue]`it = fileByteIterator("test-file.txt") map char($_)` + +[green]`$($(fileByteIterator@"test-file.txt"))` + +`>>>` [blue]`$$(it))` + +[green]`[< "u", "n", "o", @@ -1587,7 +1729,7 @@ Returns an iterator that produces the bytes of the specified file. The parameter "e", " ", -]` +>]` ===== fileLineIterator() Syntax: + @@ -1598,7 +1740,9 @@ Returns an iterator that produces the lines of the specified file. The parameter .Examples `>>>` [blue]`builtin "os.file"` + [green]`1` + -`>>>` [blue]`fileLineIterator("test-file.txt") map $_` + +`>>>` [blue]`it = fileLineIterator("test-file.txt") map $_` + +[green]`$($(fileLineIterator@"test-file.txt"))` + +`>>>` [blue]`$$(it)` + [green]`[ "uno", "due" @@ -1628,7 +1772,7 @@ Currently available functions: Syntax: + `{4sp}strJoin(, ...) -> string` -Returns a string obtained by concatenating the string items (sarting from the second argument), separated by the string value of the separator. +Returns a string obtained by concatenating the string items (starting from the second argument), separated by the string value of the separator. .Examples `>>>` [blue]`strJoin(", ", "one", "two", "three")` + @@ -1731,19 +1875,19 @@ Returns a string obtained by converting all characters of the specified string t == 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 `$()` expression. Once an iterator is created, it can be used to access the elements of the collection one by one. -In general, data-sources are objects that can be iterated over. They are defined as dictionaries having the key `next` whose value is a function that returns the next element of the collection and updates the state of the iterator. The _next_ function must return a special value, [blue]_nil_, when there are no more elements to iterate over. +In general, data-sources are objects that can be iterated over. They are defined as dictionaries having the key `next` whose value is a function that returns the next element of the collection and updates the state of the iterator. The _next_ function must return the special value [blue]_nil_ when there are no more elements to iterate over. Lists and dictionaries are implicit data-sources. The syntax for creating an iterator is as follows. .Iterator creation syntax ==== *_iterator_* = "**$(**" _data-source_ "**)**" + -_data-source_ = _explicit_ | _list-spec_ | _dict-spec_ | _custom-data-source_ +_data-source_ = _explicit_ | _array_spec_ | _linked-list-spec_ | _dict-spec_ | _custom-data-source_ _explicit_ = _any-expr_ { "**,**" _any-expr_ } -_list-spec_ = _list_ ["**,**" _range-options_] + -_list_ = "**[**" _any-expr_ { "**,**" _any-expr_ } "**]**" + +_array-spec_ = _array_ ["**,**" _range-options_] + +_array_ = "**[**" _any-expr_ { "**,**" _any-expr_ } "**]**" + _range-options_ = _start-index_ [ "**,**" _end-index_ [ "**,**" _step_ ]] + _start-index_, _end-index_, _step_ = _integer-expr_ @@ -1782,9 +1926,9 @@ NOTE: Currently, _default-iter_ is the same as _keys-iter_. In the future, it wi `>>>` [blue]`it{plusplus}` + [red]`Eval Error: EOF` -.Example: iterator over a list +.Example: iterator over an array `>>>` [blue]`it = $(["one", "two", "three"])` + -[green]`$(#3)` + +[green]`$([#3])` + `>>>` [blue]`it{plusplus}` + [green]`"one"` + `>>>` [blue]`it{plusplus}` + @@ -1794,17 +1938,21 @@ NOTE: Currently, _default-iter_ is the same as _keys-iter_. In the future, it wi `>>>` [blue]`it{plusplus}` + [red]`Eval Error: EOF` -When creating a list-type iterator expression, you can specify a range of indices and a step to iterate over a subset of the list. +When creating an array-type iterator expression, you can specify a range of indices and a step to iterate over a subset of the array. -Indexing starts at 0. If the start index is not specified, it defaults to 0. If the end index is not specified, it defaults to the index of the last element of the list. If the step is not specified, it defaults to 1. +ifdef::show_todo[] +#TODO: implement same functionality for linked lists and dictionaries# +endif::[] -Negative indexes are allowed. They are interpreted as offsets from the end of the list. For example, an end index of -1 means the index of the last element of the list, an end index of -2 means the index of the second to last element of the list, and so on. +Indexing starts at 0. If the start index is not specified, it defaults to 0. If the end index is not specified, it defaults to the index of the last element of the array. If the step is not specified, it defaults to 1. -Negative steps are also allowed. They are interpreted as reverse iteration. For example, a step of -1 means to iterate over the list in reverse order. +Negative indexes are allowed. They are interpreted as offsets from the end of the array. For example, an end index of -1 means the index of the last element of the array, an end index of -2 means the index of the second to last element of the array, and so on. -.Example: iterator over a list with index range and step +Negative steps are also allowed. They are interpreted as reverse iteration. For example, a step of -1 means to iterate over the array in reverse order. + +.Example: iterator over an array with index, range and step `>>>` [blue]`it = $([1, 2, 3, 4, 5], 1, 4, 2)` + -[green]`$(#5)` + +[green]`$([#5])` + `>>>` [blue]`it{plusplus}` + [green]`2` + `>>>` [blue]`it{plusplus}` + @@ -1812,9 +1960,9 @@ Negative steps are also allowed. They are interpreted as reverse iteration. For `>>>` [blue]`it{plusplus}` + [red]`Eval Error: EOF` -.Example: iterator over a list in reverse order +.Example: iterator over an array in reverse order `>>>` [blue]`it = $([1, 2, 3], -1, 0, -1)` + -[green]`$(#3)` + +[green]`$([#3])` + `>>>` [blue]`it{plusplus}` + [green]`3` + `>>>` [blue]`it{plusplus}` + @@ -1833,7 +1981,7 @@ After the first use of the [blue]`{plusplus}` operator, the prefixed operator [b .Example: using the [blue]`{star}` operator `>>>` [blue]`it = $(["one", "two", "three"])` + -[green]`$(#3)` + +[green]`$([#3])` + `>>>` [blue]`{star}it` + [red]`Eval Error: EOF` + `>>>` [blue]`it{plusplus}` + @@ -1842,7 +1990,7 @@ After the first use of the [blue]`{plusplus}` operator, the prefixed operator [b [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. +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 visits all remaining elements of the iterator and returns them as a _linked list_. 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# @@ -1854,13 +2002,13 @@ Named operators are operators that are identified by a name instead of a symbol. * *_.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_. +* *_.index_*: returns the index of the current element in the iterator. Before the first use of the [blue]`{plusplus}` operator, it returns the invalid value [red]_-1_. TIP: Iterators built on custom data-sources can provide additional named operators, depending on the functionality they want to expose. For example, an iterator over a list could provide a named operator called [blue]`.reverse` that returns the next element of the collection in reverse order. .Example: using the named operators `>>>` [blue]`it = $(["one", "two", "three"])` + -[green]`$(#3)` + +[green]`$([#3])` + `>>>` [blue]`it.next` + [green]`"one"` + `>>>` [blue]`it.current` + @@ -1902,7 +2050,36 @@ 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]`it = [1,2] cat [3] cat [4,5]` + +[green]`$($($([#2]) cat $([#1])) cat $([#2]))` + +`>>>` [blue]`$$(it)` + +[green]`[<1, 2, 3, 4, 5>]` + + +==== [blue]`digest` operator +Syntax: + +`{4sp} digest -> {4sp}` + +[blue]`digest` operator takes one iterator or iterable and an expression. It evaluates the expression for each element of the iterator and returns the last computed value. + +.Examples +`>>>` [blue]`it = $(2,5)` + +[green]`$(2..5..1)` + +`>>>` [blue]`sum=0; it digest (sum+=$_)` + +[green]`9` + +.Some notes on the expression used with the [blue]`digest` operator +[NOTE] +==== +* All assign operators have a lower precedence than the [blue]`digest` operator. Therefore, parentheses are required around the expression to ensure that the assignment is evaluated before the [blue]`digest` operator. +* Since the [blue]`digest` operator evaluates the expression for each element of the iterator, it can be used to perform side effects, such as updating a variable. +* At each iteration, the automatic variables `$_`, `$__`, `$#`, and `last` are available for use in the expression. In the above example, the _sum_ variable can be replaced by the automatic variable _last_ to get the same result. The _last_ variable is initialized to 0 before the first iteration and is updated to the last computed value after each iteration. Therefore, the above example can be rewritten as follows. + +`>>>` [blue]`it = $(2,5)` + +[green]`$(2..5..1)` + +`>>>` [blue]`last=0; it digest last+$_` + +[green]`9` +==== ==== [blue]`filter` operator Syntax: + @@ -1911,14 +2088,18 @@ Syntax: + [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]`it = $(["one", "two", "three"])` + +[green]`$([#3])` + +`>>>` [blue]`filtered_it = it filter $_ == "two"` + +[green]`$($([#1]))` + +`>>>` [blue]`$$(filtered_it)` + +[green]`[<"two">]` ==== [blue]`groupby` operator Syntax: + `{4sp} groupby -> {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. +The left side of [blue]`groupby` operator is a list of dictionaries 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. @@ -1954,7 +2135,7 @@ Syntax: + .Example: using the [blue]`map` operator `>>>` [blue]`it = $(["one", "two", "three"])` + -[green]`$(#3)` + +[green]`$([#3])` + `>>>` [blue]`excl_it = it map $_ + "!"` + [green]`$($([#3]))` + `>>>` [blue]`$$(excl_it)` +