Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c39970fa7e | |||
| 14bb9e942b | |||
| 9451958218 | |||
| 91fdc1926e | |||
| 9a3abdf1b6 | |||
| ac3e690f87 | |||
| f0a152a17a | |||
| ca89931ca9 | |||
| d2e8aed4f7 | |||
| 8138cd2a80 | |||
| 6786666cf4 | |||
| 35e794701a | |||
| 52ef134be6 | |||
| 624318d84e | |||
| aa1338cd51 | |||
| c9db4b84e3 | |||
| e7e9330b71 | |||
| 8eb25bbc86 | |||
| efc92d434b | |||
| 4151f3f5e2 | |||
| f028485caa | |||
| ab07405cda | |||
| 47be0c66cf | |||
| 50e7168214 | |||
| 0a9543543d | |||
| 775751c67b | |||
| 4aaffd6c44 | |||
| 924051fbcd | |||
| 5a9b6525a2 | |||
| 8c66d90532 | |||
| 4aa0113c6a | |||
| d035fa0d5e | |||
| cf73b5c98d | |||
| 8346e28340 | |||
| 9c2eca40d7 | |||
| c3198e4c79 | |||
| 99e0190b9c | |||
| d4f63a3837 | |||
| 389d48b646 | |||
| 1f7b9131fc | |||
| dce49fd2b7 |
@@ -60,7 +60,7 @@ func (self *ast) addToken(tk *Token) (err error) {
|
||||
}
|
||||
|
||||
func (self *ast) addToken2(tk *Token) (t *term, err error) {
|
||||
if t = newTerm(tk, nil); t != nil {
|
||||
if t = newTerm(tk); t != nil {
|
||||
err = self.addTerm(t)
|
||||
} else {
|
||||
err = tk.Errorf("unexpected token %q", tk.String())
|
||||
@@ -128,6 +128,7 @@ func (self *ast) eval(ctx ExprContext, preset bool) (result any, err error) {
|
||||
}
|
||||
if err == nil {
|
||||
result, err = self.root.compute(ctx)
|
||||
ctx.setVar(ControlLastResult, result)
|
||||
}
|
||||
// } else {
|
||||
// err = errors.New("empty expression")
|
||||
|
||||
+21
-3
@@ -8,6 +8,20 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func errTooFewParams(minArgs, maxArgs, argCount int) (err error) {
|
||||
if maxArgs < 0 {
|
||||
err = fmt.Errorf("too few params -- expected %d or more, got %d", minArgs, argCount)
|
||||
} else {
|
||||
err = fmt.Errorf("too few params -- expected %d, got %d", minArgs, argCount)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func errTooMuchParams(maxArgs, argCount int) (err error) {
|
||||
err = fmt.Errorf("too much params -- expected %d, got %d", maxArgs, argCount)
|
||||
return
|
||||
}
|
||||
|
||||
// --- General errors
|
||||
|
||||
func errCantConvert(funcName string, value any, kind string) error {
|
||||
@@ -18,11 +32,15 @@ func errExpectedGot(funcName string, kind string, value any) error {
|
||||
return fmt.Errorf("%s() expected %s, got %T (%v)", funcName, kind, value, value)
|
||||
}
|
||||
|
||||
func errDivisionByZero(funcName string) error {
|
||||
return fmt.Errorf("%s() division by zero", funcName)
|
||||
}
|
||||
|
||||
// --- Parameter errors
|
||||
|
||||
func errOneParam(funcName string) error {
|
||||
return fmt.Errorf("%s() requires exactly one param", funcName)
|
||||
}
|
||||
// func errOneParam(funcName string) error {
|
||||
// return fmt.Errorf("%s() requires exactly one param", funcName)
|
||||
// }
|
||||
|
||||
func errMissingRequiredParameter(funcName, paramName string) error {
|
||||
return fmt.Errorf("%s() missing required parameter %q", funcName, paramName)
|
||||
|
||||
+4
-15
@@ -23,22 +23,11 @@ func TestDictParser(t *testing.T) {
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`{}`, map[any]any{}, nil},
|
||||
/* 2 */ {`{123}`, nil, errors.New(`[1:6] expected ":", got "}"`)},
|
||||
/* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1):"one", int64(2):"two", int64(3):"three"}, nil},
|
||||
/* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
|
||||
/* 4 */ {`{1:"one",2:"two",3:"three"}.2`, "three", nil},
|
||||
// /* 3 */ {`[1,2,"hello"]`, []any{int64(1), int64(2), "hello"}, nil},
|
||||
// /* 4 */ {`[1+2, not true, "hello"]`, []any{int64(3), false, "hello"}, nil},
|
||||
// /* 5 */ {`[1,2]+[3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
||||
// /* 6 */ {`[1,4,3,2]-[3]`, []any{int64(1), int64(4), int64(2)}, nil},
|
||||
// /* 7 */ {`add([1,4,3,2])`, int64(10), nil},
|
||||
// /* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil},
|
||||
// /* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
||||
// /* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 (2 in 1) has wrong type string, number expected`)},
|
||||
// /* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
||||
// /* 12 */ {`[1,2,3] << 2+2`, []any{int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||
// /* 13 */ {`2-1 >> [2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
||||
// /* 14 */ {`[1,2,3].1`, int64(2), nil},
|
||||
// /* 15 */ {`ls=[1,2,3] but ls.1`, int64(2), nil},
|
||||
// /* 16 */ {`ls=[1,2,3] but ls.(-1)`, int64(3), nil},
|
||||
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
|
||||
/* 6 */ {`{1:"one"} + {2:"two"}`, map[any]any{1: "one", 2: "two"}, nil},
|
||||
/* 7 */ {`2 in {1:"one", 2:"two"}`, true, nil},
|
||||
}
|
||||
|
||||
succeeded := 0
|
||||
|
||||
+346
-116
@@ -22,37 +22,45 @@ Expressions calculator
|
||||
|
||||
toc::[]
|
||||
|
||||
#TODO: Work in progress (last update on 2024/05/07, 07:15 am)#
|
||||
#TODO: Work in progress (last update on 2024/05/17, 15:47 p.m.)#
|
||||
|
||||
== Expr
|
||||
_Expr_ is a GO package capable of analysing, interpreting and calculating expressions.
|
||||
|
||||
=== Concepts and terminology
|
||||
#TODO#
|
||||
|
||||
image::expression-diagram.png[]
|
||||
|
||||
=== `dev-expr` test tool
|
||||
`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, beyond in additon to the automatic test suite based on the Go test framework, `dev-expr` provides an important aid for quickly testing of new features during their development.
|
||||
`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` provides an important aid for quickly testing of new features during their development.
|
||||
|
||||
It cat work as a REPL, *R*ead-*E*xecute-*P*rint-*L*oop, or it can process expression acquired from files or standard input.
|
||||
`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.
|
||||
|
||||
The program can be downloaded from https://git.portale-stac.it/go-pkg/-/packages/generic/dev-expr/[dev-expr].
|
||||
|
||||
The program in located in the _tools_ directory.
|
||||
Here are some examples of execution.
|
||||
|
||||
.Run `dev-expr` in REPL mode and ask for help
|
||||
[source,shell]
|
||||
----
|
||||
# Assume the expr source directory. Type 'exit' or Ctrl+D to quit the program.
|
||||
# Type 'exit' or Ctrl+D to quit the program.
|
||||
|
||||
[user]$ tools/expr -- Expressions calculator v1.7.0,2024/05/08 (celestino.amoroso@portale-stac.it)
|
||||
Type help to get the list of command.
|
||||
[user]$ ./dev-expr
|
||||
expr -- Expressions calculator v1.7.1(build 2),2024/05/16 (celestino.amoroso@portale-stac.it)
|
||||
Based on the Expr package v0.10.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
|
||||
|
||||
>>> help
|
||||
--- REPL commands:
|
||||
source -- Load a file as input
|
||||
tty -- Enable/Disable ansi output <1>
|
||||
base -- Set the integer output base: 2, 8, 10, or 16
|
||||
exit -- Exit the program
|
||||
help -- Show command list
|
||||
ml -- Enable/Disable multi-line output
|
||||
mods -- List builtin modules
|
||||
source -- Load a file as input
|
||||
tty -- Enable/Disable ansi output <1>
|
||||
|
||||
--- Command line options:
|
||||
-b <builtin> Import builtin modules.
|
||||
@@ -74,9 +82,12 @@ Here are some examples of execution.
|
||||
.REPL examples
|
||||
[source,shell]
|
||||
----
|
||||
[user]$ tools/expr -- Expressions calculator v1.6.1,2024/05/06 (celestino.amoroso@portale-stac.it)
|
||||
Type help to get the list of command.
|
||||
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||
[user]$ ./dev-expr
|
||||
expr -- Expressions calculator v1.7.1(build 2),2024/05/16 (celestino.amoroso@portale-stac.it)
|
||||
Based on the Expr package v0.10.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
|
||||
|
||||
>>> 2+3
|
||||
5
|
||||
>>> 2+3*(4-1.5)
|
||||
@@ -103,45 +114,150 @@ Here are some examples of execution.
|
||||
<4> But operator, see <<_but_operator>>.
|
||||
<5> Multi-expression: the same result of the previous single expression but this it is obtained with two separated calculations.
|
||||
|
||||
=== Concepts and terminology
|
||||
#TODO#
|
||||
|
||||
image::expression-diagram.png[]
|
||||
|
||||
== Data types
|
||||
_Expr_ supports numerical, string, relational, boolean expressions, and mixed-type lists.
|
||||
|
||||
=== Numbers
|
||||
Numbers can be integers (GO int64) or float (GO float64). In mixed operations involving integers and floats, integers are automatically promoted to floats.
|
||||
_Expr_ supports three type of numbers:
|
||||
|
||||
. [blue]#Integers#
|
||||
. [blue]#Floats#
|
||||
. [blue]#Factions#
|
||||
|
||||
In mixed operations involving integers, fractions and floats, automatic type promotion to the largest type take place.
|
||||
|
||||
==== Integers
|
||||
__Expr__'s integers are a subset of the integer set. Internally they are stored as Golang _int64_ values.
|
||||
|
||||
.Integer literal syntax
|
||||
====
|
||||
*_integer_* = [_sign_] _digit-seq_ +
|
||||
_sign_ = "**+**" | "**-**" +
|
||||
_digit-seq_ = _dec-seq_ | _bin-seq_ | _oct-seq_ | _hex-seq_ +
|
||||
_dec-seq_ = {__dec-digit__} +
|
||||
_dec-digit_ = "**0**"|"**1**"|...|"**9**" +
|
||||
_bin-seq_ = "**0b**"{__bin-digit__} +
|
||||
_bin-digit_ = "**0**"|"**1**" +
|
||||
_oct-seq_ = "**0o**"{__oct-digit__} +
|
||||
_oct-digit_ = "**0**"|"**1**"|...|"**7**" +
|
||||
_hex-seq_ = "**0x**"{__hex-digit__} +
|
||||
_hex-digit_ = "**0**"|"**1**"|...|"**9**"|"**a**"|...|"**z**"|"**A**"|...|"**Z**"
|
||||
====
|
||||
|
||||
Value range: *-9223372036854775808* to *9223372036854775807*
|
||||
|
||||
.Arithmetic operators
|
||||
[cols="^1,^2,6,4"]
|
||||
|===
|
||||
| Symbol | Operation | Description | Examples
|
||||
|
||||
| [blue]`+` / [blue]`-` | _change sign_ | Change the sign of values | [blue]`-1` _[-1]_ +
|
||||
[blue]`-(+2)` _[-2]_
|
||||
|
||||
| [blue]`+` | _sum_ | Add two values | [blue]`-1 + 2` _[1]_ +
|
||||
[blue]`4 + 0.5` _[4.5]_
|
||||
|
||||
| [blue]`-` | _subtraction_ | Subtract the right value from the left one | [blue]`3 - 1` _[2]_ +
|
||||
[blue]`4 - 0.5` _[3.5]_
|
||||
|
||||
| [blue]`*` | _product_ | Multiply two values | `-1 * 2` _[-2]_ +
|
||||
[blue]`4 * 0.5` _[2.0]_
|
||||
|
||||
| [blue]`/` | _Division_ | Divide the left value by the right one | [blue]`-1 / 2` _[0]_ +
|
||||
[blue]`1.0 / 2` _[0.5]_
|
||||
|
||||
| [blue]`./` | _Float division_ | Force float division | [blue]`-1 ./ 2` _[-0.5]_
|
||||
|
||||
| [blue]`%` | _Modulo_ | Remainder of the integer division | [blue]`5 % 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]`/` | _Division_ | Divide the left value by the right one^(*)^ | [blue]`-10 / 2` -> 5
|
||||
| [blue]`%` | _Modulo_ | Remainder of the integer division | [blue]`5 % 2` -> 1
|
||||
|===
|
||||
|
||||
=== String
|
||||
Strings are character sequences enclosed between two double quote [blue]`"`. Example: [blue]`"I'm a string"`.
|
||||
^(*)^ 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.
|
||||
|
||||
|
||||
.Float literal syntax
|
||||
====
|
||||
*_float_* = [_sign_] _dec-seq_ "**.**" [_dec-seq_] [("**e**"|"**E**") [_sign_] _dec-seq_] +
|
||||
_sign_ = "**+**" | "**-**" +
|
||||
_dec-seq_ = _see-integer-literal-syntax_
|
||||
====
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`1.0` +
|
||||
[green]`1` +
|
||||
`>>>` [blue]`0.123` +
|
||||
[green]`0.123` +
|
||||
`>>>` [blue]`4.5e+3` +
|
||||
[green]`4500` +
|
||||
`>>>` [blue]`4.5E-33` +
|
||||
[green]`4.5e-33` +
|
||||
`>>>` [blue]`4.5E-3` +
|
||||
[green]`0.0045` +
|
||||
`>>>` [blue]`4.5E10` +
|
||||
[green]`4.5e+10`
|
||||
|
||||
|
||||
.Arithmetic operators
|
||||
[cols="^1,^2,6,4"]
|
||||
|===
|
||||
| Symbol | Operation | Description | Examples
|
||||
| [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]`/` | _Division_ | Divide the left value by the right one | [blue]`1.0 / 2` -> 0.5
|
||||
| [blue]`./`| _Float division_ | Force float division | [blue]`-1 ./ 2` -> -0.5
|
||||
|===
|
||||
|
||||
==== Fractions
|
||||
_Expr_ also supports fractions. Fraction literals are made with two integers separated by a vertical bar `|`.
|
||||
|
||||
.Fraction literal syntax
|
||||
====
|
||||
*_fraction_* = [__sign__] (_num-den-spec_ | _float-spec_) +
|
||||
_sign_ = "**+**" | "**-**" +
|
||||
_num-den-spec_ = _digit-seq_ "**|**" _digit-seq_ +
|
||||
_float-spec_ = _dec-seq_ "**.**" [_dec-seq_] "**(**" _dec-seq_ "**)**" +
|
||||
_dec-seq_ = _see-integer-literal-syntax_ +
|
||||
_digit-seq_ = _see-integer-literal-syntax_
|
||||
====
|
||||
|
||||
.Examples
|
||||
// [source,go]
|
||||
// ----
|
||||
`>>>` [blue]`1 | 2` +
|
||||
[green]`1|2` +
|
||||
`>>>` [blue]`4|6` [gray]_// Fractions are always reduced to their lowest terms_ +
|
||||
[green]`2|3` +
|
||||
`>>>` [blue]`1|2 + 2|3` +
|
||||
[green]`7|6` +
|
||||
`>>>` [blue]`1|2 * 2|3` +
|
||||
[green]`1|3` +
|
||||
`>>>` [blue]`1|2 / 1|3` +
|
||||
[green]`3|2` +
|
||||
`>>>` [blue]`1|2 ./ 1|3` [gray]_// Force decimal division_ +
|
||||
[green]`1.5` +
|
||||
`>>>` [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)` +
|
||||
[green]`-1|2`
|
||||
// ----
|
||||
|
||||
Fractions can be used together with integers and floats in expressions.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`1|2 + 5` +
|
||||
[green]`11|2` +
|
||||
`>>>` [blue]`4 - 1|2` +
|
||||
[green]`7|2` +
|
||||
`>>>` [blue]`1.0 + 1|2` +
|
||||
[green]`1.5`
|
||||
|
||||
|
||||
|
||||
=== Strings
|
||||
Strings are character sequences enclosed between two double quote [blue]`"`.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`"I'm a string"` +
|
||||
[green]`I'm a string` +
|
||||
`>>>` [blue]`"123abc?!"` +
|
||||
[green]`123abc?!` +
|
||||
`>>>` [blue]`"123\nabc"` +
|
||||
[green]`123` +
|
||||
[green]`abc` +
|
||||
`>>>` [blue]`"123\tabc"` +
|
||||
[green]`123{nbsp}{nbsp}{nbsp}{nbsp}abc`
|
||||
|
||||
Some arithmetic operators can also be used with strings.
|
||||
|
||||
@@ -156,28 +272,46 @@ Some arithmetic operators can also be used with strings.
|
||||
| [blue]`*` | _repeat_ | Make _n_ copy of a string | [blue]`"one" * 2` _["oneone"]_
|
||||
|===
|
||||
|
||||
The items of strings can be accessed using the dot `.` operator.
|
||||
|
||||
=== Boolean
|
||||
Boolean data type has two values only: _true_ and _false_. Relational and Boolean expressions produce Boolean values.
|
||||
.Item access syntax
|
||||
====
|
||||
_item_ = _string-expr_ "**.**" _integer-expr_
|
||||
====
|
||||
|
||||
.String examples
|
||||
`>>>` [blue]`s="abc"` [gray]_assign the string to variable s_ +
|
||||
[green]`abc` +
|
||||
`>>>` [blue]`s.1` [gray]_char at position 1 (starting from 0)_ +
|
||||
[green]`b` +
|
||||
`>>>` [blue]`s.(-1)` [gray]_char at position -1, the rightmost one_ +
|
||||
[green]`c` +
|
||||
`>>>` [blue]`\#s` [gray]_number of chars_ +
|
||||
[gren]`3` +
|
||||
`>>>` [blue]`#"abc"` [gray]_number of chars_ +
|
||||
[green]`3`
|
||||
|
||||
|
||||
=== Booleans
|
||||
Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relational and boolean expressions result in boolean values.
|
||||
|
||||
.Relational operators
|
||||
[cols="^1,^2,6,4"]
|
||||
|===
|
||||
| Symbol | Operation | Description | Examples
|
||||
|
||||
| [blue]`==` | _Equal_ | True if the left value is equal to the right one | [blue]`5 == 2` _[false]_ +
|
||||
[blue]`"a" == "a"` _[true]_
|
||||
| [blue]`!=` | _Not Equal_ | True if the left value is NOT equal to the right one | [blue]`5 != 2` _[true]_ +
|
||||
[blue]`"a" != "a"` _[false]_
|
||||
| [blue]`<` | _Less_ | True if the left value is less than the right one | [blue]`5 < 2` _[false]_ +
|
||||
[blue]`"a" < "b"` _[true]_
|
||||
| [blue]`\<=` | _Less or Equal_ | True if the left value is less than or equal to the right one | [blue]`5 \<= 2` _[false]_ +
|
||||
[blue]`"b" \<= "b"` _[true]_
|
||||
| [blue]`>` | _Greater_ | True if the left value is greater than the right one | [blue]`5 > 2` _[true]_ +
|
||||
[blue]`"a" < "b"` _[false]_
|
||||
| [blue]`>=` | _Greater or Equal_ | True if the left value is greater than or equal to the right one | [blue]`5 >= 2` _[true]_ +
|
||||
[blue]`"b" \<= "b"` _[true]_
|
||||
| [blue]`==` | _Equal_ | True if the left value is equal to the right one | [blue]`5 == 2` -> _false_ +
|
||||
[blue]`"a" == "a"` -> _true_
|
||||
| [blue]`!=` | _Not Equal_ | True if the left value is NOT equal to the right one | [blue]`5 != 2` -> _true_ +
|
||||
[blue]`"a" != "a"` -> _false_
|
||||
| [blue]`<` | _Less_ | True if the left value is less than the right one | [blue]`5 < 2` -> _false_ +
|
||||
[blue]`"a" < "b"` -> _true_
|
||||
| [blue]`\<=` | _Less or Equal_ | True if the left value is less than or equal to the right one | [blue]`5 \<= 2` -> _false_ +
|
||||
[blue]`"b" \<= "b"` -> _true_
|
||||
| [blue]`>` | _Greater_ | True if the left value is greater than the right one | [blue]`5 > 2` -> _true_ +
|
||||
[blue]`"a" < "b"` -> _false_
|
||||
| [blue]`>=` | _Greater or Equal_ | True if the left value is greater than or equal to the right one | [blue]`5 >= 2` -> _true_ +
|
||||
[blue]`"b" \<= "b"` -> _true_
|
||||
|===
|
||||
|
||||
|
||||
@@ -186,19 +320,19 @@ Boolean data type has two values only: _true_ and _false_. Relational and Boolea
|
||||
|===
|
||||
| Symbol | Operation | Description | Examples
|
||||
|
||||
| [blue]`NOT` | _Not_ | True if the right value is false | [blue]`NOT true` _[false]_ +
|
||||
[blue]`NOT (2 < 1)` _[true]_
|
||||
| [blue]`NOT` | _Not_ | True if the right value is false | [blue]`NOT true` -> _false_ +
|
||||
[blue]`NOT (2 < 1)` -> _true_
|
||||
|
||||
| [blue]`AND` / [blue]`&&` | _And_ | True if both left and right values are true | [blue]`false && true` _[false]_ +
|
||||
[blue]`"a" < "b" AND NOT (2 < 1)` _[true]_
|
||||
| [blue]`AND` / [blue]`&&` | _And_ | True if both left and right values are true | [blue]`false && true` -> _false_ +
|
||||
[blue]`"a" < "b" AND NOT (2 < 1)` -> _true_
|
||||
|
||||
| [blue]`OR` / [blue]`\|\|` | _Or_ | True if at least one of the left and right values integers true| [blue]`false or true` _[true]_ +
|
||||
[blue]`"a" == "b" OR (2 == 1)` _[false]_
|
||||
| [blue]`OR` / [blue]`\|\|` | _Or_ | True if at least one of the left and right values integers true| [blue]`false or true` -> _true_ +
|
||||
[blue]`"a" == "b" OR (2 == 1)` -> _false_
|
||||
|===
|
||||
|
||||
[CAUTION]
|
||||
====
|
||||
Currently, boolean operations are evaluated using _short cut evaluation_. This means that, if the left expression of operators [blue]`and` and [blue]`or` is sufficient to establish the result of the whole operation, the right expression would not evaluated at all.
|
||||
Currently, boolean operations are evaluated using _short cut evaluation_. This means that, if the left expression of the [blue]`and` and [blue]`or` operators is sufficient to establish the result of the whole operation, the right expression would not evaluated at all.
|
||||
|
||||
.Example
|
||||
[source,go]
|
||||
@@ -208,55 +342,129 @@ 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_.
|
||||
====
|
||||
|
||||
=== List
|
||||
_Expr_ supports list of mixed-type values, also specified by normal expressions.
|
||||
=== Lists
|
||||
_Expr_ supports list of mixed-type values, also specified by normal expressions. Internally, _Expr_'s lists are Go arrays.
|
||||
|
||||
.List examples
|
||||
[source,go]
|
||||
----
|
||||
[1, 2, 3] // List of integers
|
||||
["one", "two", "three"] // List of strings
|
||||
["one", 2, false, 4.1] // List of mixed-types
|
||||
["one"+1, 2.0*(9-2)] // List of expressions
|
||||
[ [1,"one"], [2,"two"]] // List of lists
|
||||
----
|
||||
.List literal syntax
|
||||
====
|
||||
*_list_* = _empty-list_ | _non-empty-list_ +
|
||||
_empty-list_ = "**[]**" +
|
||||
_non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value} "**]**" +
|
||||
====
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`[1,2,3]` [gray]_// List of integers_ +
|
||||
[green]`[1, 2, 3]` +
|
||||
`>>>` [blue]`["one", "two", "three"]` [gray]_// List of strings_ +
|
||||
[green]`["one", "two", "three"]` +
|
||||
`>>>` [blue]`["one", 2, false, 4.1]` [gray]_// List of mixed-types_ +
|
||||
[green]`["one", 2, false, 4.1]` +
|
||||
`>>>` [blue]`["one"+1, 2.0*(9-2)]` [gray]_// List of expressions_ +
|
||||
[green]`["one1", 14]` +
|
||||
`>>>` [blue]`[ [1,"one"], [2,"two"]]` [gray]_// List of lists_ +
|
||||
[green]`[[1, "one"], [2, "two"]]`
|
||||
|
||||
.List 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]`+` | _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]_
|
||||
|===
|
||||
|
||||
== Variables
|
||||
A variable is an identifier with an assigned value. Variables are stored in the object that implements the _ExprContext_ interface.
|
||||
The items of array can be accessed using the dot `.` operator.
|
||||
|
||||
.Item access syntax
|
||||
====
|
||||
_item_ = _list-expr_ "**.**" _list-expr_
|
||||
====
|
||||
|
||||
.Items of list
|
||||
`>>>` [blue]`[1,2,3].1` +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`list=[1,2,3]; list.1` +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`["one","two","three"].1` +
|
||||
[green]`two` +
|
||||
`>>>` [blue]`list=["one","two","three"]; list.(2-1)` +
|
||||
[green]`two` +
|
||||
`>>>` [blue]`list.(-1)` +
|
||||
[green]`three` +
|
||||
`>>>` [blue]`list.(10)` +
|
||||
[red]`Eval Error: [1:9] index 10 out of bounds` +
|
||||
`>>>` [blue]`#list` +
|
||||
[green]`3`
|
||||
|
||||
|
||||
|
||||
=== Dictionaries
|
||||
WARNING: Support for dictionaries is still ongoing.
|
||||
|
||||
The _dictionary_, or _dict_, data-type is set of pairs _key/value_. It is also known as _map_ or _associative array_. Dictionary literals are sequences of pairs separated by comma `,`; sequences are enclosed between brace brackets.
|
||||
|
||||
.Dict literal syntax
|
||||
====
|
||||
*_dict_* = _empty-dict_ | _non-empty-dict_ +
|
||||
_empty-dict_ = "**{}**" +
|
||||
_non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar_ "**:**" _any-value} "**}**" +
|
||||
====
|
||||
|
||||
.Examples
|
||||
[source,go]
|
||||
----
|
||||
a=1
|
||||
x = 5.2 * (9-3)
|
||||
x = 1; y = 2*x
|
||||
----
|
||||
`>>>` [blue]`{1:"one", 2:"two"}` +
|
||||
`>>>` [blue]`{"one":1, "two": 2}` +
|
||||
`>>>` [blue]`{"sum":1+2+3, "prod":1*2*3}`
|
||||
|
||||
== Variables
|
||||
_Expr_ supports variables like most programming languages. A variable is an identifier with an assigned value. Variables are stored in _contexts_.
|
||||
|
||||
.Variable literal syntax
|
||||
====
|
||||
*_variable_* = _identifier_ "*=*" _any-value_ +
|
||||
_identifier_ = _alpha_ {(_alpha_)|_dec-digit_|"*_*"} +
|
||||
__alpha__ = "*a*"|"*b*"|..."*z*"|"*A*"|"*B*"|..."*Z*"
|
||||
====
|
||||
|
||||
NOTE: The assign operator [blue]`=` returns the value assigned to the variable.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`a=1` +
|
||||
[green]`1` +
|
||||
`>>>` [blue]`a_b=1+2` +
|
||||
[green]`1+2` +
|
||||
`>>>` [blue]`a_b` +
|
||||
[green]`3` +
|
||||
`>>>` [blue]`x = 5.2 * (9-3)` [gray]_// The assigned value has the approximation error typical of the float data-type_ +
|
||||
[green]`31.200000000000003` +
|
||||
`>>>` [blue]`x = 1; y = 2*x` +
|
||||
[green]`2` +
|
||||
`>>>` [blue]`_a=2` +
|
||||
[red]`Parse Error: [1:2] unexpected token "_"` +
|
||||
`>>>` [blue]`1=2` +
|
||||
[red]`Parse Error: assign operator ("=") must be preceded by a variable`
|
||||
|
||||
|
||||
== Other operations
|
||||
|
||||
=== [blue]`;` operator
|
||||
The semicolon operator [blue]`;` is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The latter is the final result.
|
||||
The semicolon operator [blue]`;` is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The value of the latter is the final result.
|
||||
|
||||
An expression that contains [blue]`;` is called a _multi-expression_ and each component expressione is called a _sub-expression_.
|
||||
|
||||
IMPORTANT: Technically [blue]`;` is not treated as a real operator. It acts as a separator in lists of expressions.
|
||||
|
||||
TIP: [blue]`;` can be used to set some variables before the final calculation.
|
||||
|
||||
.Example
|
||||
[source,go]
|
||||
----
|
||||
a=1; b=2; c=3; a+b+c // returns 6
|
||||
----
|
||||
`>>>` [blue]`a=1; b=2; c=3; a+b+c` +
|
||||
[green]`6`
|
||||
|
||||
The value of each sub-expression is stored in the automatica variable _last_.
|
||||
|
||||
.Example
|
||||
`>>>` [blue]`2+3; b=last+10; last` +
|
||||
[green]`15`
|
||||
|
||||
|
||||
=== [blue]`but` operator
|
||||
[blue]`but` is an infixed operator. Its operands can be expressions of any type. It evaluates the left expression first, then the right expression. The value of the right expression is the final result. Examples: [blue]`5 but 2` returns 2, [blue]`x=2*3 but x-1` returns 5.
|
||||
@@ -268,43 +476,65 @@ The assignment operator [blue]`=` is used to define variables in the evaluation
|
||||
The value on the left side of [blue]`=` must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation.
|
||||
|
||||
.Example
|
||||
[source,go]
|
||||
----
|
||||
a=15+1 // returns 16
|
||||
----
|
||||
`>>>` [blue]`a=15+1`
|
||||
[green]`16`
|
||||
|
||||
|
||||
=== Selector operator [blue]`? : ::`
|
||||
The _selector operator_ is very similar to the _switch/case/default_ statement available in many programming languages.
|
||||
|
||||
.Syntax
|
||||
[source,bnf]
|
||||
----
|
||||
<selector-operator> ::= <select-expression> "?" <selector-case> { ":" <selector-case> } ["::" <default-multi-expression>]
|
||||
<selector-case> ::= [<match-list>] <case-value>
|
||||
<match-list> ::= "["<item>{","<items>}"]"
|
||||
<item> ::= <expression
|
||||
<case-multi-expression> ::= "{" <multi-expression> "}"
|
||||
<multi-expression> ::= <expression> {";" <expression>}
|
||||
----
|
||||
.Selector literal Syntax
|
||||
_selector-operator_ = _select-expression_ "*?*" _selector-case_ { "*:*" _selector-case_ } ["*::*" _default-multi-expression_] +
|
||||
_selector-case_ = [_match-list_] _case-value_ +
|
||||
_match-list_ = "*[*" _item_ {"*,*" _items_} "*]*" +
|
||||
_item_ = _expression_ +
|
||||
_case-multi-expression_ = "*{*" _multi-expression_ "*}*" +
|
||||
_multi-expression_ = _expression_ { "*;*" _expression_ } +
|
||||
_default-multi-expression_ = _multi-expression_
|
||||
|
||||
In other words, the selector operator evaluates the expression (`<select-expression>`) on the left-hand side of the `?` symbol; it then compares the result obtained with the values listed in the `<match-list>`'s. If the comparision find 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__'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.
|
||||
|
||||
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.
|
||||
|
||||
The `:` symbol (colon) is the separator of the selector-cases. Note that if the value of the select-expression does not match any match-list, an error will be issued. Therefore, it is strongly recommended to provide a default (multi-)expression introduced by the `::` symbol (double-colon). Also note that the default expression has no match-list.
|
||||
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.
|
||||
|
||||
The [blue]`:` symbol (colon) is the separator of the selector-cases. Note that if the value of the _select-expression_ does not match any _match-list_, an error will be issued. Therefore, it is strongly recommended to provide a default (multi-)expression introduced by the [blue]`::` symbol (double-colon). Also note that the default expression has no _match-list_.
|
||||
|
||||
.Examples
|
||||
[source,go]
|
||||
----
|
||||
1 ? {"a"} : {"b"} // returns "b"
|
||||
10 ? {"a"} : {"b"} :: {"c"} // returns "c"
|
||||
10 ? {"a"} :[true, 2+8] {"b"} :: {"c"} // returns "b"
|
||||
10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"} // error: "... case list in default clause"
|
||||
10 ? {"a"} :[10] {x="b" but x} :: {"c"} // returns "b"
|
||||
10 ? {"a"} :[10] {x="b"; x} :: {"c"} // returns "b"
|
||||
10 ? {"a"} : {"b"} // error: "... no case catches the value (10) of the selection expression
|
||||
----
|
||||
`>>>` [blue]`1 ? {"a"} : {"b"}` +
|
||||
[green]`b` +
|
||||
`>>>` [blue]`10 ? {"a"} : {"b"} :: {"c"}` +
|
||||
[green]`c' +
|
||||
[green]`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}` +
|
||||
[green]`b` +
|
||||
`>>>` [blue]`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}` +
|
||||
[red]`Parse Error: [1:34] case list in default clause` +
|
||||
[green]`>>>` [blue]`10 ? {"a"} :[10] {x="b" but x} :: {"c"}` +
|
||||
[green]`b` +
|
||||
`>>>` [blue]`10 ? {"a"} :[10] {x="b"; x} :: {"c"}` +
|
||||
[green]`b` +
|
||||
`>>>` [blue]`10 ? {"a"} : {"b"}` +
|
||||
[red]`Eval Error: [1:3] no case catches the value (10) of the selection expression`
|
||||
|
||||
|
||||
=== Variable default value [blue]`??` and [blue]`?=`
|
||||
The left operand of these two operators must be a variable. The right operator can be any expression. They return the value of the variable if this is define; otherwise they return the value of the right expression.
|
||||
|
||||
IMPORTANT: If the left variable is defined, the right expression is not evuated at all.
|
||||
|
||||
The [blue]`??` do not change the status of the left variable.
|
||||
|
||||
The [blue]`?=` assigns the calculated value of the right expression to the left variable.
|
||||
|
||||
.Examples
|
||||
`>>>` [blue]`var ?? (1+2)`'
|
||||
[green]`3` +
|
||||
`>>>` [blue]`var` +
|
||||
[red]`Eval Error: undefined variable or function "var"` +
|
||||
`>>>` [blue]`var ?= (1+2)` +
|
||||
[green]`3` +
|
||||
`>>>` [blue]`var` +
|
||||
[green]`3`
|
||||
|
||||
NOTE: These operators have a high priority, in particular higher than the operator [blue]`=`.
|
||||
|
||||
== Priorities of operators
|
||||
The table below shows all supported operators by decreasing priorities.
|
||||
|
||||
+512
-138
@@ -535,16 +535,23 @@ pre.rouge .ss {
|
||||
<ul class="sectlevel1">
|
||||
<li><a href="#_expr">1. Expr</a>
|
||||
<ul class="sectlevel2">
|
||||
<li><a href="#_dev_expr_test_tool">1.1. <code>dev-expr</code> test tool</a></li>
|
||||
<li><a href="#_concepts_and_terminology">1.2. Concepts and terminology</a></li>
|
||||
<li><a href="#_concepts_and_terminology">1.1. Concepts and terminology</a></li>
|
||||
<li><a href="#_dev_expr_test_tool">1.2. <code>dev-expr</code> test tool</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_data_types">2. Data types</a>
|
||||
<ul class="sectlevel2">
|
||||
<li><a href="#_numbers">2.1. Numbers</a></li>
|
||||
<li><a href="#_string">2.2. String</a></li>
|
||||
<li><a href="#_boolean">2.3. Boolean</a></li>
|
||||
<li><a href="#_list">2.4. List</a></li>
|
||||
<li><a href="#_numbers">2.1. Numbers</a>
|
||||
<ul class="sectlevel3">
|
||||
<li><a href="#_integers">2.1.1. Integers</a></li>
|
||||
<li><a href="#_floats">2.1.2. Floats</a></li>
|
||||
<li><a href="#_fractions">2.1.3. Fractions</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_strings">2.2. Strings</a></li>
|
||||
<li><a href="#_booleans">2.3. Booleans</a></li>
|
||||
<li><a href="#_lists">2.4. Lists</a></li>
|
||||
<li><a href="#_dictionaries">2.5. Dictionaries</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_variables">3. Variables</a></li>
|
||||
@@ -554,6 +561,7 @@ pre.rouge .ss {
|
||||
<li><a href="#_but_operator">4.2. <code class="blue">but</code> operator</a></li>
|
||||
<li><a href="#_assignment_operator">4.3. Assignment operator <code class="blue">=</code></a></li>
|
||||
<li><a href="#_selector_operator">4.4. Selector operator <code class="blue">? : ::</code></a></li>
|
||||
<li><a href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code> and <code class="blue">?=</code></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#_priorities_of_operators">5. Priorities of operators</a></li>
|
||||
@@ -577,7 +585,7 @@ pre.rouge .ss {
|
||||
<div class="sectionbody">
|
||||
<!-- toc disabled -->
|
||||
<div class="paragraph">
|
||||
<p><mark>TODO: Work in progress (last update on 2024/05/07, 07:15 am)</mark></p>
|
||||
<p><mark>TODO: Work in progress (last update on 2024/05/17, 15:47 p.m.)</mark></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -588,35 +596,50 @@ pre.rouge .ss {
|
||||
<p><em>Expr</em> is a GO package capable of analysing, interpreting and calculating expressions.</p>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_dev_expr_test_tool"><a class="anchor" href="#_dev_expr_test_tool"></a><a class="link" href="#_dev_expr_test_tool">1.1. <code>dev-expr</code> test tool</a></h3>
|
||||
<h3 id="_concepts_and_terminology"><a class="anchor" href="#_concepts_and_terminology"></a><a class="link" href="#_concepts_and_terminology">1.1. Concepts and terminology</a></h3>
|
||||
<div class="paragraph">
|
||||
<p><code>dev-expr</code> is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, beyond in additon to the automatic test suite based on the Go test framework, <code>dev-expr</code> provides an important aid for quickly testing of new features during their development.</p>
|
||||
<p><mark>TODO</mark></p>
|
||||
</div>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="expression-diagram.png" alt="expression diagram">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_dev_expr_test_tool"><a class="anchor" href="#_dev_expr_test_tool"></a><a class="link" href="#_dev_expr_test_tool">1.2. <code>dev-expr</code> test tool</a></h3>
|
||||
<div class="paragraph">
|
||||
<p><code>dev-expr</code> 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, <code>dev-expr</code> provides an important aid for quickly testing of new features during their development.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>It cat work as a REPL, *R*ead-*E*xecute-*P*rint-*L*oop, or it can process expression acquired from files or standard input.</p>
|
||||
<p><code>dev-expr</code> can work as a <em>REPL</em>, <em><strong>R</strong>ead-<strong>E</strong>xecute-<strong>P</strong>rint-<strong>L</strong>oop</em>, or it can process expression acquired from files or standard input.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The program in located in the <em>tools</em> directory.
|
||||
Here are some examples of execution.</p>
|
||||
<p>The program can be downloaded from <a href="https://git.portale-stac.it/go-pkg/-/packages/generic/dev-expr/">dev-expr</a>.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Here are some examples of execution.</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Run <code>dev-expr</code> in REPL mode and ask for help</div>
|
||||
<div class="content">
|
||||
<pre class="rouge highlight"><code data-lang="shell"><span class="c"># Assume the expr source directory. Type 'exit' or Ctrl+D to quit the program.</span>
|
||||
<pre class="rouge highlight"><code data-lang="shell"><span class="c"># Type 'exit' or Ctrl+D to quit the program.</span>
|
||||
|
||||
<span class="o">[</span>user]<span class="nv">$ </span>tools/expr <span class="nt">--</span> Expressions calculator v1.7.0,2024/05/08 <span class="o">(</span>celestino.amoroso@portale-stac.it<span class="o">)</span>
|
||||
Type <span class="nb">help </span>to get the list of command.
|
||||
<span class="o">[</span>user]<span class="nv">$ </span>./dev-expr
|
||||
<span class="nb">expr</span> <span class="nt">--</span> Expressions calculator v1.7.1<span class="o">(</span>build 2<span class="o">)</span>,2024/05/16 <span class="o">(</span>celestino.amoroso@portale-stac.it<span class="o">)</span>
|
||||
Based on the Expr package v0.10.0
|
||||
Type <span class="nb">help </span>to get the list of available commands
|
||||
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||
|
||||
<span class="o">>>></span> <span class="nb">help</span>
|
||||
<span class="nt">---</span> REPL commands:
|
||||
<span class="nb">source</span> <span class="nt">--</span> Load a file as input
|
||||
<span class="nb">tty</span> <span class="nt">--</span> Enable/Disable ansi output <i class="conum" data-value="1"></i><b>(1)</b>
|
||||
base <span class="nt">--</span> Set the integer output base: 2, 8, 10, or 16
|
||||
<span class="nb">exit</span> <span class="nt">--</span> Exit the program
|
||||
<span class="nb">help</span> <span class="nt">--</span> Show <span class="nb">command </span>list
|
||||
ml <span class="nt">--</span> Enable/Disable multi-line output
|
||||
mods <span class="nt">--</span> List <span class="nb">builtin </span>modules
|
||||
<span class="nb">source</span> <span class="nt">--</span> Load a file as input
|
||||
<span class="nb">tty</span> <span class="nt">--</span> Enable/Disable ansi output <i class="conum" data-value="1"></i><b>(1)</b>
|
||||
|
||||
<span class="nt">---</span> Command line options:
|
||||
<span class="nt">-b</span> <<span class="nb">builtin</span><span class="o">></span> Import <span class="nb">builtin </span>modules.
|
||||
@@ -647,9 +670,12 @@ Here are some examples of execution.</p>
|
||||
<div class="listingblock">
|
||||
<div class="title">REPL examples</div>
|
||||
<div class="content">
|
||||
<pre class="rouge highlight"><code data-lang="shell"><span class="o">[</span>user]<span class="nv">$ </span>tools/expr <span class="nt">--</span> Expressions calculator v1.6.1,2024/05/06 <span class="o">(</span>celestino.amoroso@portale-stac.it<span class="o">)</span>
|
||||
Type <span class="nb">help </span>to get the list of command.
|
||||
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||
<pre class="rouge highlight"><code data-lang="shell"><span class="o">[</span>user]<span class="nv">$ </span>./dev-expr
|
||||
<span class="nb">expr</span> <span class="nt">--</span> Expressions calculator v1.7.1<span class="o">(</span>build 2<span class="o">)</span>,2024/05/16 <span class="o">(</span>celestino.amoroso@portale-stac.it<span class="o">)</span>
|
||||
Based on the Expr package v0.10.0
|
||||
Type <span class="nb">help </span>to get the list of available commands
|
||||
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||
|
||||
<span class="o">>>></span> 2+3
|
||||
5
|
||||
<span class="o">>>></span> 2+3<span class="k">*</span><span class="o">(</span>4-1.5<span class="o">)</span>
|
||||
@@ -695,17 +721,6 @@ Here are some examples of execution.</p>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_concepts_and_terminology"><a class="anchor" href="#_concepts_and_terminology"></a><a class="link" href="#_concepts_and_terminology">1.2. Concepts and terminology</a></h3>
|
||||
<div class="paragraph">
|
||||
<p><mark>TODO</mark></p>
|
||||
</div>
|
||||
<div class="imageblock">
|
||||
<div class="content">
|
||||
<img src="expression-diagram.png" alt="expression diagram">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect1">
|
||||
@@ -717,7 +732,49 @@ Here are some examples of execution.</p>
|
||||
<div class="sect2">
|
||||
<h3 id="_numbers"><a class="anchor" href="#_numbers"></a><a class="link" href="#_numbers">2.1. Numbers</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>Numbers can be integers (GO int64) or float (GO float64). In mixed operations involving integers and floats, integers are automatically promoted to floats.</p>
|
||||
<p><em>Expr</em> supports three type of numbers:</p>
|
||||
</div>
|
||||
<div class="olist arabic">
|
||||
<ol class="arabic">
|
||||
<li>
|
||||
<p><span class="blue">Integers</span></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><span class="blue">Floats</span></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><span class="blue">Factions</span></p>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>In mixed operations involving integers, fractions and floats, automatic type promotion to the largest type take place.</p>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_integers"><a class="anchor" href="#_integers"></a><a class="link" href="#_integers">2.1.1. Integers</a></h4>
|
||||
<div class="paragraph">
|
||||
<p><em>Expr</em>'s integers are a subset of the integer set. Internally they are stored as Golang <em>int64</em> values.</p>
|
||||
</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 1. Integer literal syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><strong><em>integer</em></strong> = [<em>sign</em>] <em>digit-seq</em><br>
|
||||
<em>sign</em> = "<strong>+</strong>" | "<strong>-</strong>"<br>
|
||||
<em>digit-seq</em> = <em>dec-seq</em> | <em>bin-seq</em> | <em>oct-seq</em> | <em>hex-seq</em><br>
|
||||
<em>dec-seq</em> = {<em>dec-digit</em>}<br>
|
||||
<em>dec-digit</em> = "<strong>0</strong>"|"<strong>1</strong>"|…​|"<strong>9</strong>"<br>
|
||||
<em>bin-seq</em> = "<strong>0b</strong>"{<em>bin-digit</em>}<br>
|
||||
<em>bin-digit</em> = "<strong>0</strong>"|"<strong>1</strong>"<br>
|
||||
<em>oct-seq</em> = "<strong>0o</strong>"{<em>oct-digit</em>}<br>
|
||||
<em>oct-digit</em> = "<strong>0</strong>"|"<strong>1</strong>"|…​|"<strong>7</strong>"<br>
|
||||
<em>hex-seq</em> = "<strong>0x</strong>"{<em>hex-digit</em>}<br>
|
||||
<em>hex-digit</em> = "<strong>0</strong>"|"<strong>1</strong>"|…​|"<strong>9</strong>"|"<strong>a</strong>"|…​|"<strong>z</strong>"|"<strong>A</strong>"|…​|"<strong>Z</strong>"</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Value range: <strong>-9223372036854775808</strong> to <strong>9223372036854775807</strong></p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all stretch">
|
||||
<caption class="title">Table 1. Arithmetic operators</caption>
|
||||
@@ -727,75 +784,202 @@ Here are some examples of execution.</p>
|
||||
<col style="width: 46.1538%;">
|
||||
<col style="width: 30.7693%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="tableblock halign-center valign-top">Symbol</th>
|
||||
<th class="tableblock halign-center valign-top">Operation</th>
|
||||
<th class="tableblock halign-left valign-top">Description</th>
|
||||
<th class="tableblock halign-left valign-top">Examples</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">+</code> / <code class="blue">-</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>change sign</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Change the sign of values</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1</code> <em>[-1]</em><br>
|
||||
<code class="blue">-(+2)</code> <em>[-2]</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock">Symbol</p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock">Operation</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Examples</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">+</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>sum</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Add two values</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1 + 2</code> <em>[1]</em><br>
|
||||
<code class="blue">4 + 0.5</code> <em>[4.5]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1 + 2</code> → 1</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">-</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>subtraction</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Subtract the right value from the left one</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">3 - 1</code> <em>[2]</em><br>
|
||||
<code class="blue">4 - 0.5</code> <em>[3.5]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">3 - 1</code> → 2</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">*</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>product</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Multiply two values</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>-1 * 2</code> <em>[-2]</em><br>
|
||||
<code class="blue">4 * 0.5</code> <em>[2.0]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1 * 2</code> → -2</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">/</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Division</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Divide the left value by the right one</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1 / 2</code> <em>[0]</em><br>
|
||||
<code class="blue">1.0 / 2</code> <em>[0.5]</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">./</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Float division</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Force float division</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1 ./ 2</code> <em>[-0.5]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Divide the left value by the right one<sup>(*)</sup></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-10 / 2</code> → 5</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">%</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Modulo</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Remainder of the integer division</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 % 2</code> <em>[1]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 % 2</code> → 1</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="paragraph">
|
||||
<p><sup>(*)</sup> See also the <em>float division</em> <code class="blue">./</code> below.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
<h4 id="_floats"><a class="anchor" href="#_floats"></a><a class="link" href="#_floats">2.1.2. Floats</a></h4>
|
||||
<div class="paragraph">
|
||||
<p><em>Expr</em>'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 <em>float64</em> values.</p>
|
||||
</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 2. Float literal syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><strong><em>float</em></strong> = [<em>sign</em>] <em>dec-seq</em> "<strong>.</strong>" [<em>dec-seq</em>] [("<strong>e</strong>"|"<strong>E</strong>") [<em>sign</em>] <em>dec-seq</em>]<br>
|
||||
<em>sign</em> = "<strong>+</strong>" | "<strong>-</strong>"<br>
|
||||
<em>dec-seq</em> = <em>see-integer-literal-syntax</em></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">1.0</code><br>
|
||||
<code class="green">1</code><br>
|
||||
<code>>>></code> <code class="blue">0.123</code><br>
|
||||
<code class="green">0.123</code><br>
|
||||
<code>>>></code> <code class="blue">4.5e+3</code><br>
|
||||
<code class="green">4500</code><br>
|
||||
<code>>>></code> <code class="blue">4.5E-33</code><br>
|
||||
<code class="green">4.5e-33</code><br>
|
||||
<code>>>></code> <code class="blue">4.5E-3</code><br>
|
||||
<code class="green">0.0045</code><br>
|
||||
<code>>>></code> <code class="blue">4.5E10</code><br>
|
||||
<code class="green">4.5e+10</code></p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all stretch">
|
||||
<caption class="title">Table 2. Arithmetic operators</caption>
|
||||
<colgroup>
|
||||
<col style="width: 7.6923%;">
|
||||
<col style="width: 15.3846%;">
|
||||
<col style="width: 46.1538%;">
|
||||
<col style="width: 30.7693%;">
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock">Symbol</p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock">Operation</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Description</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Examples</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">+</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>sum</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Add two values</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">4 + 0.5</code> → 4.5</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">-</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>subtraction</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Subtract the right value from the left one</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">4 - 0.5</code> → 3.5</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">*</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>product</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Multiply two values</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">4 * 0.5</code> → 2.0</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">/</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Division</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Divide the left value by the right one</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">1.0 / 2</code> → 0.5</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">./</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Float division</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Force float division</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">-1 ./ 2</code> → -0.5</p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_string"><a class="anchor" href="#_string"></a><a class="link" href="#_string">2.2. String</a></h3>
|
||||
<div class="sect3">
|
||||
<h4 id="_fractions"><a class="anchor" href="#_fractions"></a><a class="link" href="#_fractions">2.1.3. Fractions</a></h4>
|
||||
<div class="paragraph">
|
||||
<p>Strings are character sequences enclosed between two double quote <code class="blue">"</code>. Example: <code class="blue">"I’m a string"</code>.</p>
|
||||
<p><em>Expr</em> also supports fractions. Fraction literals are made with two integers separated by a vertical bar <code>|</code>.</p>
|
||||
</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 3. Fraction literal syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><strong><em>fraction</em></strong> = [<em>sign</em>] (<em>num-den-spec</em> | <em>float-spec</em>)<br>
|
||||
<em>sign</em> = "<strong>+</strong>" | "<strong>-</strong>"<br>
|
||||
<em>num-den-spec</em> = <em>digit-seq</em> "<strong>|</strong>" <em>digit-seq</em><br>
|
||||
<em>float-spec</em> = <em>dec-seq</em> "<strong>.</strong>" [<em>dec-seq</em>] "<strong>(</strong>" <em>dec-seq</em> "<strong>)</strong>"<br>
|
||||
<em>dec-seq</em> = <em>see-integer-literal-syntax</em><br>
|
||||
<em>digit-seq</em> = <em>see-integer-literal-syntax</em></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">1 | 2</code><br>
|
||||
<code class="green">1|2</code><br>
|
||||
<code>>>></code> <code class="blue">4|6</code> <em class="gray">// Fractions are always reduced to their lowest terms</em><br>
|
||||
<code class="green">2|3</code><br>
|
||||
<code>>>></code> <code class="blue">1|2 + 2|3</code><br>
|
||||
<code class="green">7|6</code><br>
|
||||
<code>>>></code> <code class="blue">1|2 * 2|3</code><br>
|
||||
<code class="green">1|3</code><br>
|
||||
<code>>>></code> <code class="blue">1|2 / 1|3</code><br>
|
||||
<code class="green">3|2</code><br>
|
||||
<code>>>></code> <code class="blue">1|2 ./ 1|3</code> <em class="gray">// Force decimal division</em><br>
|
||||
<code class="green">1.5</code><br>
|
||||
<code>>>></code> <code class="blue">-1|2</code><br>
|
||||
<code class="green">-1|2</code><br>
|
||||
<code>>>></code> <code class="blue">1|-2</code> <em class="gray">// Invalid sign specification</em><br>
|
||||
<em class="red">Eval Error: [1:3] infix operator "|" requires two non-nil operands, got 1</em><br>
|
||||
<code>>>></code> <code class="blue">1|(-2)</code><br>
|
||||
<code class="green">-1|2</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Fractions can be used together with integers and floats in expressions.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">1|2 + 5</code><br>
|
||||
<code class="green">11|2</code><br>
|
||||
<code>>>></code> <code class="blue">4 - 1|2</code><br>
|
||||
<code class="green">7|2</code><br>
|
||||
<code>>>></code> <code class="blue">1.0 + 1|2</code><br>
|
||||
<code class="green">1.5</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_strings"><a class="anchor" href="#_strings"></a><a class="link" href="#_strings">2.2. Strings</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>Strings are character sequences enclosed between two double quote <code class="blue">"</code>.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">"I’m a string"</code><br>
|
||||
<code class="green">I’m a string</code><br>
|
||||
<code>>>></code> <code class="blue">"123abc?!"</code><br>
|
||||
<code class="green">123abc?!</code><br>
|
||||
<code>>>></code> <code class="blue">"123\nabc"</code><br>
|
||||
<code class="green">123</code><br>
|
||||
<code class="green">abc</code><br>
|
||||
<code>>>></code> <code class="blue">"123\tabc"</code><br>
|
||||
<code class="green">123    abc</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Some arithmetic operators can also be used with strings.</p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all stretch">
|
||||
<caption class="title">Table 2. String operators</caption>
|
||||
<caption class="title">Table 3. String operators</caption>
|
||||
<colgroup>
|
||||
<col style="width: 7.6923%;">
|
||||
<col style="width: 15.3846%;">
|
||||
@@ -826,14 +1010,38 @@ Here are some examples of execution.</p>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="paragraph">
|
||||
<p>The items of strings can be accessed using the dot <code>.</code> operator.</p>
|
||||
</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 4. Item access syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><em>item</em> = <em>string-expr</em> "<strong>.</strong>" <em>integer-expr</em></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">String examples</div>
|
||||
<p><code>>>></code> <code class="blue">s="abc"</code> <em class="gray">assign the string to variable s</em><br>
|
||||
<code class="green">abc</code><br>
|
||||
<code>>>></code> <code class="blue">s.1</code> <em class="gray">char at position 1 (starting from 0)</em><br>
|
||||
<code class="green">b</code><br>
|
||||
<code>>>></code> <code class="blue">s.(-1)</code> <em class="gray">char at position -1, the rightmost one</em><br>
|
||||
<code class="green">c</code><br>
|
||||
<code>>>></code> <code class="blue">#s</code> <em class="gray">number of chars</em><br>
|
||||
<code class="gren">3</code><br>
|
||||
<code>>>></code> <code class="blue">#"abc"</code> <em class="gray">number of chars</em><br>
|
||||
<code class="green">3</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_boolean"><a class="anchor" href="#_boolean"></a><a class="link" href="#_boolean">2.3. Boolean</a></h3>
|
||||
<h3 id="_booleans"><a class="anchor" href="#_booleans"></a><a class="link" href="#_booleans">2.3. Booleans</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>Boolean data type has two values only: <em>true</em> and <em>false</em>. Relational and Boolean expressions produce Boolean values.</p>
|
||||
<p>Boolean data type has two values only: <em class="blue">true</em> and <em class="blue">false</em>. Relational and boolean expressions result in boolean values.</p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all stretch">
|
||||
<caption class="title">Table 3. Relational operators</caption>
|
||||
<caption class="title">Table 4. Relational operators</caption>
|
||||
<colgroup>
|
||||
<col style="width: 7.6923%;">
|
||||
<col style="width: 15.3846%;">
|
||||
@@ -853,48 +1061,48 @@ Here are some examples of execution.</p>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">==</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Equal</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">True if the left value is equal to the right one</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 == 2</code> <em>[false]</em><br>
|
||||
<code class="blue">"a" == "a"</code> <em>[true]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 == 2</code> → <em>false</em><br>
|
||||
<code class="blue">"a" == "a"</code> → <em>true</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">!=</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Not Equal</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">True if the left value is NOT equal to the right one</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 != 2</code> <em>[true]</em><br>
|
||||
<code class="blue">"a" != "a"</code> <em>[false]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 != 2</code> → <em>true</em><br>
|
||||
<code class="blue">"a" != "a"</code> → <em>false</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue"><</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Less</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">True if the left value is less than the right one</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 < 2</code> <em>[false]</em><br>
|
||||
<code class="blue">"a" < "b"</code> <em>[true]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 < 2</code> → <em>false</em><br>
|
||||
<code class="blue">"a" < "b"</code> → <em>true</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue"><=</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Less or Equal</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">True if the left value is less than or equal to the right one</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 <= 2</code> <em>[false]</em><br>
|
||||
<code class="blue">"b" <= "b"</code> <em>[true]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 <= 2</code> → <em>false</em><br>
|
||||
<code class="blue">"b" <= "b"</code> → <em>true</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">></code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Greater</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">True if the left value is greater than the right one</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 > 2</code> <em>[true]</em><br>
|
||||
<code class="blue">"a" < "b"</code> <em>[false]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 > 2</code> → <em>true</em><br>
|
||||
<code class="blue">"a" < "b"</code> → <em>false</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">>=</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Greater or Equal</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">True if the left value is greater than or equal to the right one</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 >= 2</code> <em>[true]</em><br>
|
||||
<code class="blue">"b" <= "b"</code> <em>[true]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">5 >= 2</code> → <em>true</em><br>
|
||||
<code class="blue">"b" <= "b"</code> → <em>true</em></p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="tableblock frame-all grid-all stretch">
|
||||
<caption class="title">Table 4. Boolean operators</caption>
|
||||
<caption class="title">Table 5. Boolean operators</caption>
|
||||
<colgroup>
|
||||
<col style="width: 15.3846%;">
|
||||
<col style="width: 15.3846%;">
|
||||
@@ -914,22 +1122,22 @@ Here are some examples of execution.</p>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">NOT</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Not</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">True if the right value is false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">NOT true</code> <em>[false]</em><br>
|
||||
<code class="blue">NOT (2 < 1)</code> <em>[true]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">NOT true</code> → <em>false</em><br>
|
||||
<code class="blue">NOT (2 < 1)</code> → <em>true</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">AND</code> / <code class="blue">&&</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>And</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">True if both left and right values are true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">false && true</code> <em>[false]</em><br>
|
||||
<code class="blue">"a" < "b" AND NOT (2 < 1)</code> <em>[true]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">false && true</code> → <em>false</em><br>
|
||||
<code class="blue">"a" < "b" AND NOT (2 < 1)</code> → <em>true</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">OR</code> / <code class="blue">||</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Or</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">True if at least one of the left and right values integers true</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">false or true</code> <em>[true]</em><br>
|
||||
<code class="blue">"a" == "b" OR (2 == 1)</code> <em>[false]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">false or true</code> → <em>true</em><br>
|
||||
<code class="blue">"a" == "b" OR (2 == 1)</code> → <em>false</em></p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -941,7 +1149,7 @@ Here are some examples of execution.</p>
|
||||
</td>
|
||||
<td class="content">
|
||||
<div class="paragraph">
|
||||
<p>Currently, boolean operations are evaluated using <em>short cut evaluation</em>. This means that, if the left expression of operators <code class="blue">and</code> and <code class="blue">or</code> is sufficient to establish the result of the whole operation, the right expression would not evaluated at all.</p>
|
||||
<p>Currently, boolean operations are evaluated using <em>short cut evaluation</em>. This means that, if the left expression of the <code class="blue">and</code> and <code class="blue">or</code> operators is sufficient to establish the result of the whole operation, the right expression would not evaluated at all.</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Example</div>
|
||||
@@ -963,22 +1171,35 @@ Here are some examples of execution.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_list"><a class="anchor" href="#_list"></a><a class="link" href="#_list">2.4. List</a></h3>
|
||||
<h3 id="_lists"><a class="anchor" href="#_lists"></a><a class="link" href="#_lists">2.4. Lists</a></h3>
|
||||
<div class="paragraph">
|
||||
<p><em>Expr</em> supports list of mixed-type values, also specified by normal expressions.</p>
|
||||
<p><em>Expr</em> supports list of mixed-type values, also specified by normal expressions. Internally, <em>Expr</em>'s lists are Go arrays.</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="title">List examples</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 5. List literal syntax</div>
|
||||
<div class="content">
|
||||
<pre class="rouge highlight"><code data-lang="go"><span class="p">[</span><span class="m">1</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="m">3</span><span class="p">]</span> <span class="c">// List of integers</span>
|
||||
<span class="p">[</span><span class="s">"one"</span><span class="p">,</span> <span class="s">"two"</span><span class="p">,</span> <span class="s">"three"</span><span class="p">]</span> <span class="c">// List of strings</span>
|
||||
<span class="p">[</span><span class="s">"one"</span><span class="p">,</span> <span class="m">2</span><span class="p">,</span> <span class="no">false</span><span class="p">,</span> <span class="m">4.1</span><span class="p">]</span> <span class="c">// List of mixed-types</span>
|
||||
<span class="p">[</span><span class="s">"one"</span><span class="o">+</span><span class="m">1</span><span class="p">,</span> <span class="m">2.0</span><span class="o">*</span><span class="p">(</span><span class="m">9</span><span class="o">-</span><span class="m">2</span><span class="p">)]</span> <span class="c">// List of expressions</span>
|
||||
<span class="p">[</span> <span class="p">[</span><span class="m">1</span><span class="p">,</span><span class="s">"one"</span><span class="p">],</span> <span class="p">[</span><span class="m">2</span><span class="p">,</span><span class="s">"two"</span><span class="p">]]</span> <span class="c">// List of lists</span></code></pre>
|
||||
<div class="paragraph">
|
||||
<p><strong><em>list</em></strong> = <em>empty-list</em> | <em>non-empty-list</em><br>
|
||||
<em>empty-list</em> = "<strong>[]</strong>"<br>
|
||||
<em>non-empty-list</em> = "<strong>[</strong>" <em>any-value</em> {"<strong>,</strong>" _any-value} "<strong>]</strong>"<br></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">[1,2,3]</code> <em class="gray">// List of integers</em><br>
|
||||
<code class="green">[1, 2, 3]</code><br>
|
||||
<code>>>></code> <code class="blue">["one", "two", "three"]</code> <em class="gray">// List of strings</em><br>
|
||||
<code class="green">["one", "two", "three"]</code><br>
|
||||
<code>>>></code> <code class="blue">["one", 2, false, 4.1]</code> <em class="gray">// List of mixed-types</em><br>
|
||||
<code class="green">["one", 2, false, 4.1]</code><br>
|
||||
<code>>>></code> <code class="blue">["one"+1, 2.0*(9-2)]</code> <em class="gray">// List of expressions</em><br>
|
||||
<code class="green">["one1", 14]</code><br>
|
||||
<code>>>></code> <code class="blue">[ [1,"one"], [2,"two"]]</code> <em class="gray">// List of lists</em><br>
|
||||
<code class="green">[[1, "one"], [2, "two"]]</code></p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all stretch">
|
||||
<caption class="title">Table 5. List operators</caption>
|
||||
<caption class="title">Table 6. List operators</caption>
|
||||
<colgroup>
|
||||
<col style="width: 15.3846%;">
|
||||
<col style="width: 15.3846%;">
|
||||
@@ -998,16 +1219,78 @@ Here are some examples of execution.</p>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">+</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Join</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Joins two lists</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">[1,2] + [3]</code> <em>[ [1,2,3] ]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">[1,2] + [3]</code> → <em>[1,2,3]</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">-</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Difference</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Left list without elements in the right list</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">[1,2,3] - [2]</code> <em>[ [1,3] ]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">[1,2,3] - [2]</code> → <em>[1,3]</em></p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="paragraph">
|
||||
<p>The items of array can be accessed using the dot <code>.</code> operator.</p>
|
||||
</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 6. Item access syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><em>item</em> = <em>list-expr</em> "<strong>.</strong>" <em>list-expr</em></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Items of list</div>
|
||||
<p><code>>>></code> <code class="blue">[1,2,3].1</code><br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue">list=[1,2,3]; list.1</code><br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue">["one","two","three"].1</code><br>
|
||||
<code class="green">two</code><br>
|
||||
<code>>>></code> <code class="blue">list=["one","two","three"]; list.(2-1)</code><br>
|
||||
<code class="green">two</code><br>
|
||||
<code>>>></code> <code class="blue">list.(-1)</code><br>
|
||||
<code class="green">three</code><br>
|
||||
<code>>>></code> <code class="blue">list.(10)</code><br>
|
||||
<code class="red">Eval Error: [1:9] index 10 out of bounds</code><br>
|
||||
<code>>>></code> <code class="blue">#list</code><br>
|
||||
<code class="green">3</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_dictionaries"><a class="anchor" href="#_dictionaries"></a><a class="link" href="#_dictionaries">2.5. Dictionaries</a></h3>
|
||||
<div class="admonitionblock warning">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<i class="fa icon-warning" title="Warning"></i>
|
||||
</td>
|
||||
<td class="content">
|
||||
Support for dictionaries is still ongoing.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The <em>dictionary</em>, or <em>dict</em>, data-type is set of pairs <em>key/value</em>. It is also known as <em>map</em> or <em>associative array</em>. Dictionary literals are sequences of pairs separated by comma <code>,</code>; sequences are enclosed between brace brackets.</p>
|
||||
</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 7. Dict literal syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><strong><em>dict</em></strong> = <em>empty-dict</em> | <em>non-empty-dict</em><br>
|
||||
<em>empty-dict</em> = "<strong>{}</strong>"<br>
|
||||
<em>non-empty-dict</em> = "<strong>{</strong>" <em>key-scalar</em> "<strong>:</strong>" <em>any-value</em> {"<strong>,</strong>" <em>key-scalar</em> "<strong>:</strong>" _any-value} "<strong>}</strong>"<br></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">{1:"one", 2:"two"}</code><br>
|
||||
<code>>>></code> <code class="blue">{"one":1, "two": 2}</code><br>
|
||||
<code>>>></code> <code class="blue">{"sum":1+2+3, "prod":1*2*3}</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1015,17 +1298,48 @@ Here are some examples of execution.</p>
|
||||
<h2 id="_variables"><a class="anchor" href="#_variables"></a><a class="link" href="#_variables">3. Variables</a></h2>
|
||||
<div class="sectionbody">
|
||||
<div class="paragraph">
|
||||
<p>A variable is an identifier with an assigned value. Variables are stored in the object that implements the <em>ExprContext</em> interface.</p>
|
||||
<p><em>Expr</em> supports variables like most programming languages. A variable is an identifier with an assigned value. Variables are stored in <em>contexts</em>.</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Examples</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 8. Variable literal syntax</div>
|
||||
<div class="content">
|
||||
<pre class="rouge highlight"><code data-lang="go"><span class="n">a</span><span class="o">=</span><span class="m">1</span>
|
||||
<span class="n">x</span> <span class="o">=</span> <span class="m">5.2</span> <span class="o">*</span> <span class="p">(</span><span class="m">9</span><span class="o">-</span><span class="m">3</span><span class="p">)</span>
|
||||
<span class="n">x</span> <span class="o">=</span> <span class="m">1</span><span class="p">;</span> <span class="n">y</span> <span class="o">=</span> <span class="m">2</span><span class="o">*</span><span class="n">x</span></code></pre>
|
||||
<div class="paragraph">
|
||||
<p><strong><em>variable</em></strong> = <em>identifier</em> "<strong>=</strong>" <em>any-value</em><br>
|
||||
<em>identifier</em> = <em>alpha</em> {(<em>alpha</em>)|<em>dec-digit</em>|"<strong>_</strong>"}<br>
|
||||
<em>alpha</em> = "<strong>a</strong>"|"<strong>b</strong>"|…​"<strong>z</strong>"|"<strong>A</strong>"|"<strong>B</strong>"|…​"<strong>Z</strong>"</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="admonitionblock note">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<i class="fa icon-note" title="Note"></i>
|
||||
</td>
|
||||
<td class="content">
|
||||
The assign operator <code class="blue">=</code> returns the value assigned to the variable.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">a=1</code><br>
|
||||
<code class="green">1</code><br>
|
||||
<code>>>></code> <code class="blue">a_b=1+2</code><br>
|
||||
<code class="green">1+2</code><br>
|
||||
<code>>>></code> <code class="blue">a_b</code><br>
|
||||
<code class="green">3</code><br>
|
||||
<code>>>></code> <code class="blue">x = 5.2 * (9-3)</code> <em class="gray">// The assigned value has the approximation error typical of the float data-type</em><br>
|
||||
<code class="green">31.200000000000003</code><br>
|
||||
<code>>>></code> <code class="blue">x = 1; y = 2*x</code><br>
|
||||
<code class="green">2</code><br>
|
||||
<code>>>></code> <code class="blue"><em>a=2</code><br>
|
||||
<code class="red">Parse Error: [1:2] unexpected token "</em>"</code><br>
|
||||
<code>>>></code> <code class="blue">1=2</code><br>
|
||||
<code class="red">Parse Error: assign operator ("=") must be preceded by a variable</code></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect1">
|
||||
<h2 id="_other_operations"><a class="anchor" href="#_other_operations"></a><a class="link" href="#_other_operations">4. Other operations</a></h2>
|
||||
@@ -1033,7 +1347,10 @@ Here are some examples of execution.</p>
|
||||
<div class="sect2">
|
||||
<h3 id="_operator"><a class="anchor" href="#_operator"></a><a class="link" href="#_operator">4.1. <code class="blue">;</code> operator</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>The semicolon operator <code class="blue">;</code> is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The latter is the final result.</p>
|
||||
<p>The semicolon operator <code class="blue">;</code> is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The value of the latter is the final result.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>An expression that contains <code class="blue">;</code> is called a <em>multi-expression</em> and each component expressione is called a <em>sub-expression</em>.</p>
|
||||
</div>
|
||||
<div class="admonitionblock important">
|
||||
<table>
|
||||
@@ -1059,11 +1376,18 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="paragraph">
|
||||
<div class="title">Example</div>
|
||||
<div class="content">
|
||||
<pre class="rouge highlight"><code data-lang="go"><span class="n">a</span><span class="o">=</span><span class="m">1</span><span class="p">;</span> <span class="n">b</span><span class="o">=</span><span class="m">2</span><span class="p">;</span> <span class="n">c</span><span class="o">=</span><span class="m">3</span><span class="p">;</span> <span class="n">a</span><span class="o">+</span><span class="n">b</span><span class="o">+</span><span class="n">c</span> <span class="c">// returns 6</span></code></pre>
|
||||
<p><code>>>></code> <code class="blue">a=1; b=2; c=3; a+b+c</code><br>
|
||||
<code class="green">6</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The value of each sub-expression is stored in the automatica variable <em>last</em>.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example</div>
|
||||
<p><code>>>></code> <code class="blue">2+3; b=last+10; last</code><br>
|
||||
<code class="green">15</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
@@ -1081,11 +1405,10 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
|
||||
<p>The assignment operator <code class="blue">=</code> is used to define variables in the evaluation context or to change their value (see <em>ExprContext</em>).
|
||||
The value on the left side of <code class="blue">=</code> must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation.</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="paragraph">
|
||||
<div class="title">Example</div>
|
||||
<div class="content">
|
||||
<pre class="rouge highlight"><code data-lang="go"><span class="n">a</span><span class="o">=</span><span class="m">15</span><span class="o">+</span><span class="m">1</span> <span class="c">// returns 16</span></code></pre>
|
||||
</div>
|
||||
<p><code>>>></code> <code class="blue">a=15+1</code>
|
||||
<code class="green">16</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
@@ -1093,38 +1416,89 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
|
||||
<div class="paragraph">
|
||||
<p>The <em>selector operator</em> is very similar to the <em>switch/case/default</em> statement available in many programming languages.</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Syntax</div>
|
||||
<div class="content">
|
||||
<pre class="rouge highlight"><code data-lang="bnf"><selector-operator> ::= <select-expression> "?" <selector-case> { ":" <selector-case> } ["::" <default-multi-expression>]
|
||||
<selector-case> ::= [<match-list>] <case-value>
|
||||
<match-list> ::= "["<item>{","<items>}"]"
|
||||
<item> ::= <expression
|
||||
<case-multi-expression> ::= "{" <multi-expression> "}"
|
||||
<multi-expression> ::= <expression> {";" <expression>}</code></pre>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Selector literal Syntax</div>
|
||||
<p><em>selector-operator</em> = <em>select-expression</em> "<strong>?</strong>" <em>selector-case</em> { "<strong>:</strong>" <em>selector-case</em> } ["<strong>::</strong>" <em>default-multi-expression</em>]<br>
|
||||
<em>selector-case</em> = [<em>match-list</em>] <em>case-value</em><br>
|
||||
<em>match-list</em> = "<strong>[</strong>" <em>item</em> {"<strong>,</strong>" <em>items</em>} "<strong>]</strong>"<br>
|
||||
<em>item</em> = <em>expression</em><br>
|
||||
<em>case-multi-expression</em> = "<strong>{</strong>" <em>multi-expression</em> "<strong>}</strong>"<br>
|
||||
<em>multi-expression</em> = <em>expression</em> { "<strong>;</strong>" <em>expression</em> }<br>
|
||||
<em>default-multi-expression</em> = <em>multi-expression</em></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>In other words, the selector operator evaluates the expression (<code><select-expression></code>) on the left-hand side of the <code>?</code> symbol; it then compares the result obtained with the values listed in the <code><match-list>’s. If the comparision find a match with a value in a match-list, the associated `<case-multi-expression></code> is evaluted, and its result will be the final result of the selection operation.</p>
|
||||
<p>In other words, the selector operator evaluates the <em>select-expression</em> on the left-hand side of the <code class="blue">?</code> symbol; it then compares the result obtained with the values listed in the <em>match-list</em>'s, from left to right. If the comparision finds a match with a value in a <em>match-list</em>, the associated <em>case-multi-expression</em> is evaluted, and its result will be the final result of the selection operation.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The match lists are optional. In that case, the position, from left to right, of the <code><selector-case></code> is used as match-list. Of course, that only works if the select-expression results in an integer.</p>
|
||||
<p>The match lists are optional. In that case, the position, from left to right, of the <em>selector-case</em> is used as <em>match-list</em>. Of course, that only works if the <em>select-expression</em> results in an integer.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The <code>:</code> symbol (colon) is the separator of the selector-cases. Note that if the value of the select-expression does not match any match-list, an error will be issued. Therefore, it is strongly recommended to provide a default (multi-)expression introduced by the <code>::</code> symbol (double-colon). Also note that the default expression has no match-list.</p>
|
||||
<p>The <code class="blue">:</code> symbol (colon) is the separator of the selector-cases. Note that if the value of the <em>select-expression</em> does not match any <em>match-list</em>, an error will be issued. Therefore, it is strongly recommended to provide a default (multi-)expression introduced by the <code class="blue">::</code> symbol (double-colon). Also note that the default expression has no <em>match-list</em>.</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<div class="content">
|
||||
<pre class="rouge highlight"><code data-lang="go"><span class="m">1</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span> <span class="p">{</span><span class="s">"b"</span><span class="p">}</span> <span class="c">// returns "b"</span>
|
||||
<span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span> <span class="p">{</span><span class="s">"b"</span><span class="p">}</span> <span class="o">::</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span> <span class="c">// returns "c"</span>
|
||||
<span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span><span class="p">[</span><span class="no">true</span><span class="p">,</span> <span class="m">2</span><span class="o">+</span><span class="m">8</span><span class="p">]</span> <span class="p">{</span><span class="s">"b"</span><span class="p">}</span> <span class="o">::</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span> <span class="c">// returns "b"</span>
|
||||
<span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span><span class="p">[</span><span class="no">true</span><span class="p">,</span> <span class="m">2</span><span class="o">+</span><span class="m">8</span><span class="p">]</span> <span class="p">{</span><span class="s">"b"</span><span class="p">}</span> <span class="o">::</span><span class="p">[</span><span class="m">10</span><span class="p">]</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span> <span class="c">// error: "... case list in default clause"</span>
|
||||
<span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span><span class="p">[</span><span class="m">10</span><span class="p">]</span> <span class="p">{</span><span class="n">x</span><span class="o">=</span><span class="s">"b"</span> <span class="n">but</span> <span class="n">x</span><span class="p">}</span> <span class="o">::</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span> <span class="c">// returns "b"</span>
|
||||
<span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span><span class="p">[</span><span class="m">10</span><span class="p">]</span> <span class="p">{</span><span class="n">x</span><span class="o">=</span><span class="s">"b"</span><span class="p">;</span> <span class="n">x</span><span class="p">}</span> <span class="o">::</span> <span class="p">{</span><span class="s">"c"</span><span class="p">}</span> <span class="c">// returns "b"</span>
|
||||
<span class="m">10</span> <span class="err">?</span> <span class="p">{</span><span class="s">"a"</span><span class="p">}</span> <span class="o">:</span> <span class="p">{</span><span class="s">"b"</span><span class="p">}</span> <span class="c">// error: "... no case catches the value (10) of the selection expression</span></code></pre>
|
||||
<p><code>>>></code> <code class="blue">1 ? {"a"} : {"b"}</code><br>
|
||||
<code class="green">b</code><br>
|
||||
<code>>>></code> <code class="blue">10 ? {"a"} : {"b"} :: {"c"}</code><br>
|
||||
<code class="green">c'<br>
|
||||
[green]</code>>>>` <code class="blue">10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}</code><br>
|
||||
<code class="green">b</code><br>
|
||||
<code>>>></code> <code class="blue">10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}</code><br>
|
||||
<code class="red">Parse Error: [1:34] case list in default clause</code><br>
|
||||
<code class="green">>>></code> <code class="blue">10 ? {"a"} :[10] {x="b" but x} :: {"c"}</code><br>
|
||||
<code class="green">b</code><br>
|
||||
<code>>>></code> <code class="blue">10 ? {"a"} :[10] {x="b"; x} :: {"c"}</code><br>
|
||||
<code class="green">b</code><br>
|
||||
<code>>>></code> <code class="blue">10 ? {"a"} : {"b"}</code><br>
|
||||
<code class="red">Eval Error: [1:3] no case catches the value (10) of the selection expression</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
<h3 id="_variable_default_value_and"><a class="anchor" href="#_variable_default_value_and"></a><a class="link" href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code> and <code class="blue">?=</code></a></h3>
|
||||
<div class="paragraph">
|
||||
<p>The left operand of these two operators must be a variable. The right operator can be any expression. They return the value of the variable if this is define; otherwise they return the value of the right expression.</p>
|
||||
</div>
|
||||
<div class="admonitionblock important">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<i class="fa icon-important" title="Important"></i>
|
||||
</td>
|
||||
<td class="content">
|
||||
If the left variable is defined, the right expression is not evuated at all.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The <code class="blue">??</code> do not change the status of the left variable.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>The <code class="blue">?=</code> assigns the calculated value of the right expression to the left variable.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples</div>
|
||||
<p><code>>>></code> <code class="blue">var ?? (1+2)’
|
||||
[green]`3</code><br>
|
||||
<code>>>></code> <code class="blue">var</code><br>
|
||||
<code class="red">Eval Error: undefined variable or function "var"</code><br>
|
||||
<code>>>></code> <code class="blue">var ?= (1+2)</code><br>
|
||||
<code class="green">3</code><br>
|
||||
<code>>>></code> <code class="blue">var</code><br>
|
||||
<code class="green">3</code></p>
|
||||
</div>
|
||||
<div class="admonitionblock note">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="icon">
|
||||
<i class="fa icon-note" title="Note"></i>
|
||||
</td>
|
||||
<td class="content">
|
||||
These operators have a high priority, in particular higher than the operator <code class="blue">=</code>.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1135,7 +1509,7 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
|
||||
<p>The table below shows all supported operators by decreasing priorities.</p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all stretch">
|
||||
<caption class="title">Table 6. Operators priorities</caption>
|
||||
<caption class="title">Table 7. Operators priorities</caption>
|
||||
<colgroup>
|
||||
<col style="width: 12.5%;">
|
||||
<col style="width: 12.5%;">
|
||||
@@ -1393,7 +1767,7 @@ The value on the left side of <code class="blue">=</code> must be an identifier.
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2024-05-08 07:50:05 +0200
|
||||
Last updated 2024-05-17 15:47:29 +0200
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
+144
-28
@@ -10,37 +10,141 @@ import (
|
||||
)
|
||||
|
||||
func isNilFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
if len(args) == 1 {
|
||||
result = args[0] == nil
|
||||
} else {
|
||||
err = errOneParam(name)
|
||||
}
|
||||
result = args[0] == nil
|
||||
return
|
||||
}
|
||||
|
||||
func isIntFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
result = IsInteger(args[0])
|
||||
return
|
||||
}
|
||||
|
||||
func isFloatFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
result = IsFloat(args[0])
|
||||
return
|
||||
}
|
||||
|
||||
func isBoolFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
result = IsBool(args[0])
|
||||
return
|
||||
}
|
||||
|
||||
func isStringFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
result = IsString(args[0])
|
||||
return
|
||||
}
|
||||
|
||||
func isFractionFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
result = IsFract(args[0])
|
||||
return
|
||||
}
|
||||
|
||||
func isRationalFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
result = IsRational(args[0])
|
||||
return
|
||||
}
|
||||
|
||||
func isListFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
result = IsList(args[0])
|
||||
return
|
||||
}
|
||||
|
||||
func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
result = IsDict(args[0])
|
||||
return
|
||||
}
|
||||
|
||||
func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
if len(args) == 1 {
|
||||
switch v := args[0].(type) {
|
||||
case int64:
|
||||
result = v
|
||||
case float64:
|
||||
result = int64(math.Trunc(v))
|
||||
case bool:
|
||||
if v {
|
||||
result = int64(1)
|
||||
} else {
|
||||
result = int64(0)
|
||||
}
|
||||
case string:
|
||||
var i int
|
||||
if i, err = strconv.Atoi(v); err == nil {
|
||||
result = int64(i)
|
||||
}
|
||||
default:
|
||||
err = errCantConvert(name, v, "int")
|
||||
switch v := args[0].(type) {
|
||||
case int64:
|
||||
result = v
|
||||
case float64:
|
||||
result = int64(math.Trunc(v))
|
||||
case bool:
|
||||
if v {
|
||||
result = int64(1)
|
||||
} else {
|
||||
result = int64(0)
|
||||
}
|
||||
} else {
|
||||
err = errOneParam(name)
|
||||
case string:
|
||||
var i int
|
||||
if i, err = strconv.Atoi(v); err == nil {
|
||||
result = int64(i)
|
||||
}
|
||||
default:
|
||||
err = errCantConvert(name, v, "int")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
switch v := args[0].(type) {
|
||||
case int64:
|
||||
result = float64(v)
|
||||
case float64:
|
||||
result = v
|
||||
case bool:
|
||||
if v {
|
||||
result = float64(1)
|
||||
} else {
|
||||
result = float64(0)
|
||||
}
|
||||
case string:
|
||||
var f float64
|
||||
if f, err = strconv.ParseFloat(v, 64); err == nil {
|
||||
result = f
|
||||
}
|
||||
case *fraction:
|
||||
result = v.toFloat()
|
||||
default:
|
||||
err = errCantConvert(name, v, "float")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func fractFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
switch v := args[0].(type) {
|
||||
case int64:
|
||||
var den int64 = 1
|
||||
if len(args) > 1 {
|
||||
var ok bool
|
||||
if den, ok = args[1].(int64); !ok {
|
||||
err = errExpectedGot(name, "integer", args[1])
|
||||
} else if den == 0 {
|
||||
err = errDivisionByZero(name)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
result = newFraction(v, den)
|
||||
}
|
||||
case float64:
|
||||
result, err = float64ToFraction(v)
|
||||
// var n, d int64
|
||||
// if n, d, err = float64ToFraction(v); err == nil {
|
||||
// result = newFraction(n, d)
|
||||
// }
|
||||
case bool:
|
||||
if v {
|
||||
result = newFraction(1, 1)
|
||||
} else {
|
||||
result = newFraction(0, 1)
|
||||
}
|
||||
case string:
|
||||
result, err = makeGeneratingFraction(v)
|
||||
// var f float64
|
||||
// // TODO temporary implementation
|
||||
// if f, err = strconv.ParseFloat(v, 64); err == nil {
|
||||
// var n, d int64
|
||||
// if n, d, err = float64ToFraction(f); err == nil {
|
||||
// result = newFraction(n, d)
|
||||
// }
|
||||
// } else {
|
||||
// errors.New("convertion from string to float is ongoing")
|
||||
// }
|
||||
case *fraction:
|
||||
result = v
|
||||
default:
|
||||
err = errCantConvert(name, v, "float")
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -50,8 +154,20 @@ func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err err
|
||||
}
|
||||
|
||||
func ImportBuiltinsFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 1, -1)
|
||||
ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 1, -1)
|
||||
ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isInt", &simpleFunctor{f: isIntFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isFloat", &simpleFunctor{f: isFloatFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isBool", &simpleFunctor{f: isBoolFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isString", &simpleFunctor{f: isStringFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isFraction", &simpleFunctor{f: isFractionFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isFract", &simpleFunctor{f: isFractionFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isRational", &simpleFunctor{f: isRationalFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isList", &simpleFunctor{f: isListFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isDictionary", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
|
||||
ctx.RegisterFunc("isDict", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
|
||||
ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 1, 1)
|
||||
ctx.RegisterFunc("dec", &simpleFunctor{f: decFunc}, 1, 1)
|
||||
ctx.RegisterFunc("fract", &simpleFunctor{f: fractFunc}, 1, 2)
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
+18
-18
@@ -33,9 +33,9 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
|
||||
}
|
||||
|
||||
func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||
if len(args) < 1 {
|
||||
return nil, errMissingRequiredParameter(name, paramSeparator)
|
||||
}
|
||||
// if len(args) < 1 {
|
||||
// return nil, errMissingRequiredParameter(name, paramSeparator)
|
||||
// }
|
||||
if sep, ok := args[0].(string); ok {
|
||||
if len(args) == 1 {
|
||||
result = ""
|
||||
@@ -62,9 +62,9 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error
|
||||
var source string
|
||||
var ok bool
|
||||
|
||||
if len(args) < 1 {
|
||||
return nil, errMissingRequiredParameter(name, paramSource)
|
||||
}
|
||||
// if len(args) < 1 {
|
||||
// return nil, errMissingRequiredParameter(name, paramSource)
|
||||
// }
|
||||
if source, ok = args[0].(string); !ok {
|
||||
return nil, errWrongParamType(name, paramSource, typeString, args[0])
|
||||
}
|
||||
@@ -93,9 +93,9 @@ func trimStrFunc(ctx ExprContext, name string, args []any) (result any, err erro
|
||||
var source string
|
||||
var ok bool
|
||||
|
||||
if len(args) < 1 {
|
||||
return nil, errMissingRequiredParameter(name, paramSource)
|
||||
}
|
||||
// if len(args) < 1 {
|
||||
// return nil, errMissingRequiredParameter(name, paramSource)
|
||||
// }
|
||||
if source, ok = args[0].(string); !ok {
|
||||
return nil, errWrongParamType(name, paramSource, typeString, args[0])
|
||||
}
|
||||
@@ -108,9 +108,9 @@ func startsWithStrFunc(ctx ExprContext, name string, args []any) (result any, er
|
||||
var ok bool
|
||||
|
||||
result = false
|
||||
if len(args) < 1 {
|
||||
return result, errMissingRequiredParameter(name, paramSource)
|
||||
}
|
||||
// if len(args) < 1 {
|
||||
// return result, errMissingRequiredParameter(name, paramSource)
|
||||
// }
|
||||
if source, ok = args[0].(string); !ok {
|
||||
return result, errWrongParamType(name, paramSource, typeString, args[0])
|
||||
}
|
||||
@@ -133,9 +133,9 @@ func endsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err
|
||||
var ok bool
|
||||
|
||||
result = false
|
||||
if len(args) < 1 {
|
||||
return result, errMissingRequiredParameter(name, paramSource)
|
||||
}
|
||||
// if len(args) < 1 {
|
||||
// return result, errMissingRequiredParameter(name, paramSource)
|
||||
// }
|
||||
if source, ok = args[0].(string); !ok {
|
||||
return result, errWrongParamType(name, paramSource, typeString, args[0])
|
||||
}
|
||||
@@ -159,9 +159,9 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
|
||||
var parts []string
|
||||
var ok bool
|
||||
|
||||
if len(args) < 1 {
|
||||
return result, errMissingRequiredParameter(name, paramSource)
|
||||
}
|
||||
// if len(args) < 1 {
|
||||
// return result, errMissingRequiredParameter(name, paramSource)
|
||||
// }
|
||||
if source, ok = args[0].(string); !ok {
|
||||
return result, errWrongParamType(name, paramSource, typeString, args[0])
|
||||
}
|
||||
|
||||
+28
-5
@@ -20,7 +20,7 @@ func TestFuncs(t *testing.T) {
|
||||
/* 7 */ {`int(3.9)`, int64(3), nil},
|
||||
/* 8 */ {`int("432")`, int64(432), nil},
|
||||
/* 9 */ {`int("1.5")`, nil, errors.New(`strconv.Atoi: parsing "1.5": invalid syntax`)},
|
||||
/* 10 */ {`int("432", 4)`, nil, errors.New(`int() requires exactly one param`)},
|
||||
/* 10 */ {`int("432", 4)`, nil, errors.New(`too much params -- expected 1, got 2`)},
|
||||
/* 11 */ {`int(nil)`, nil, errors.New(`int() can't convert <nil> to int`)},
|
||||
/* 12 */ {`two=func(){2}; two()`, int64(2), nil},
|
||||
/* 13 */ {`double=func(x) {2*x}; (double(3))`, int64(6), nil},
|
||||
@@ -54,13 +54,36 @@ func TestFuncs(t *testing.T) {
|
||||
/* 41 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "789")`, true, nil},
|
||||
/* 42 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "0125")`, false, nil},
|
||||
/* 43 */ {`builtin "string"; endsWithStr("0123456789")`, nil, errors.New(`too few params -- expected 2 or more, got 1`)},
|
||||
/* 44 */ {`builtin "string"; splitStr("one-two-three", "-", )`, newListA("one","two","three"), nil},
|
||||
/* 45 */ {`["a", "b", "c"]`, newListA("a","b","c"), nil},
|
||||
/* 46 */ {`["a", "b", "c"]`, newList([]any{"a","b","c"}), nil},
|
||||
/* 44 */ {`builtin "string"; splitStr("one-two-three", "-", )`, newListA("one", "two", "three"), nil},
|
||||
/* 45 */ {`isInt(2+1)`, true, nil},
|
||||
/* 46 */ {`isInt(3.1)`, false, nil},
|
||||
/* 47 */ {`isFloat(3.1)`, true, nil},
|
||||
/* 48 */ {`isString("3.1")`, true, nil},
|
||||
/* 49 */ {`isString("3" + 1)`, true, nil},
|
||||
/* 50 */ {`isList(["3", 1])`, true, nil},
|
||||
/* 51 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
||||
/* 52 */ {`isFract(1|3)`, true, nil},
|
||||
/* 53 */ {`isFract(3|1)`, false, nil},
|
||||
/* 54 */ {`isRational(3|1)`, true, nil},
|
||||
/* 55 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil},
|
||||
/* 56 */ {`fract("2.2(3)")`, newFraction(67, 30), nil},
|
||||
/* 57 */ {`fract("1.21(3)")`, newFraction(91, 75), nil},
|
||||
/* 58 */ {`fract(1.21(3))`, newFraction(91, 75), nil},
|
||||
/* 59 */ {`fract(1.21)`, newFraction(121, 100), nil},
|
||||
/* 60 */ {`dec(2)`, float64(2), nil},
|
||||
/* 61 */ {`dec(2.0)`, float64(2), nil},
|
||||
/* 62 */ {`dec("2.0")`, float64(2), nil},
|
||||
/* 63 */ {`dec(true)`, float64(1), nil},
|
||||
/* 64 */ {`dec(true")`, nil, errors.New("[1:11] missing string termination \"")},
|
||||
/* 65 */ {`builtin "string"; joinStr("-", [1, "two", "three"])`, nil, errors.New(`joinStr() expected string, got int64 (1)`)},
|
||||
/* 65 */ {`dec()`, nil, errors.New(`too few params -- expected 1, got 0`)},
|
||||
/* 66 */ {`dec(1,2,3)`, nil, errors.New(`too much params -- expected 1, got 3`)},
|
||||
/* 67 */ {`builtin "string"; joinStr()`, nil, errors.New(`too few params -- expected 1 or more, got 0`)},
|
||||
// /* 64 */ {`string(true)`, "true", nil},
|
||||
}
|
||||
|
||||
t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
// parserTest(t, "Func", inputs[25:26])
|
||||
//parserTest(t, "Func", inputs[54:55])
|
||||
parserTest(t, "Func", inputs)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// global-context.go
|
||||
package expr
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
var globalCtx *SimpleFuncStore
|
||||
|
||||
func ImportInContext(name string) (exists bool) {
|
||||
var mod *module
|
||||
if mod, exists = moduleRegister[name]; exists {
|
||||
mod.importFunc(globalCtx)
|
||||
mod.imported = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ImportInContextByGlobPattern(pattern string) (count int, err error) {
|
||||
var matched bool
|
||||
for name, mod := range moduleRegister {
|
||||
if matched, err = filepath.Match(pattern, name); err == nil {
|
||||
if matched {
|
||||
count++
|
||||
mod.importFunc(globalCtx)
|
||||
mod.imported = true
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetVar(ctx ExprContext, name string) (value any, exists bool) {
|
||||
if value, exists = ctx.GetVar(name); !exists {
|
||||
value, exists = globalCtx.GetVar(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool, ownerCtx ExprContext) {
|
||||
if item, exists = ctx.GetFuncInfo(name); exists {
|
||||
ownerCtx = ctx
|
||||
} else if item, exists = globalCtx.GetFuncInfo(name); exists {
|
||||
ownerCtx = globalCtx
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
globalCtx = NewSimpleFuncStore()
|
||||
}
|
||||
+23
-18
@@ -20,22 +20,27 @@ func TestListParser(t *testing.T) {
|
||||
}
|
||||
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`[]`, []any{}, nil},
|
||||
/* 2 */ {`[1,2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
||||
/* 3 */ {`[1,2,"hello"]`, []any{int64(1), int64(2), "hello"}, nil},
|
||||
/* 4 */ {`[1+2, not true, "hello"]`, []any{int64(3), false, "hello"}, nil},
|
||||
/* 5 */ {`[1,2]+[3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
||||
/* 6 */ {`[1,4,3,2]-[3]`, []any{int64(1), int64(4), int64(2)}, nil},
|
||||
/* 7 */ {`add([1,4,3,2])`, int64(10), nil},
|
||||
/* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil},
|
||||
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
||||
/* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 (2 in 1) has wrong type string, number expected`)},
|
||||
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
||||
/* 12 */ {`[1,2,3] << 2+2`, []any{int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||
/* 13 */ {`2-1 >> [2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
||||
/* 14 */ {`[1,2,3].1`, int64(2), nil},
|
||||
/* 15 */ {`ls=[1,2,3] but ls.1`, int64(2), nil},
|
||||
/* 16 */ {`ls=[1,2,3] but ls.(-1)`, int64(3), nil},
|
||||
/* 1 */ {`[]`, []any{}, nil},
|
||||
/* 2 */ {`[1,2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
||||
/* 3 */ {`[1,2,"hello"]`, []any{int64(1), int64(2), "hello"}, nil},
|
||||
/* 4 */ {`[1+2, not true, "hello"]`, []any{int64(3), false, "hello"}, nil},
|
||||
/* 5 */ {`[1,2]+[3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
||||
/* 6 */ {`[1,4,3,2]-[3]`, []any{int64(1), int64(4), int64(2)}, nil},
|
||||
/* 7 */ {`add([1,4,3,2])`, int64(10), nil},
|
||||
/* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil},
|
||||
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
||||
/* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 (2 in 1) has wrong type string, number expected`)},
|
||||
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
||||
/* 12 */ {`[1,2,3] << 2+2`, []any{int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||
/* 13 */ {`2-1 >> [2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
||||
/* 14 */ {`[1,2,3].1`, int64(2), nil},
|
||||
/* 15 */ {`ls=[1,2,3] but ls.1`, int64(2), nil},
|
||||
/* 16 */ {`ls=[1,2,3] but ls.(-1)`, int64(3), nil},
|
||||
/* 17 */ {`list=["one","two","three"]; list.10`, nil, errors.New(`[1:36] index 10 out of bounds`)},
|
||||
/* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil},
|
||||
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
|
||||
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
|
||||
/* 21 */ {`"b" in ["a", "b", "c"]`, true, nil},
|
||||
|
||||
// /* 8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil},
|
||||
// /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
|
||||
@@ -54,8 +59,8 @@ func TestListParser(t *testing.T) {
|
||||
var gotErr error
|
||||
|
||||
ctx := NewSimpleFuncStore()
|
||||
ctx.SetVar("var1", int64(123))
|
||||
ctx.SetVar("var2", "abc")
|
||||
// ctx.SetVar("var1", int64(123))
|
||||
// ctx.SetVar("var2", "abc")
|
||||
ImportMathFuncs(ctx)
|
||||
parser := NewParser(ctx)
|
||||
|
||||
|
||||
+23
-24
@@ -6,7 +6,6 @@ package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type module struct {
|
||||
@@ -31,30 +30,30 @@ func registerImport(name string, importFunc func(ExprContext), description strin
|
||||
moduleRegister[name] = newModule(importFunc, description)
|
||||
}
|
||||
|
||||
func ImportInContext(ctx ExprContext, name string) (exists bool) {
|
||||
var mod *module
|
||||
if mod, exists = moduleRegister[name]; exists {
|
||||
mod.importFunc(ctx)
|
||||
mod.imported = true
|
||||
}
|
||||
return
|
||||
}
|
||||
// func ImportInContext(ctx ExprContext, name string) (exists bool) {
|
||||
// var mod *module
|
||||
// if mod, exists = moduleRegister[name]; exists {
|
||||
// mod.importFunc(ctx)
|
||||
// mod.imported = true
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func ImportInContextByGlobPattern(ctx ExprContext, pattern string) (count int, err error) {
|
||||
var matched bool
|
||||
for name, mod := range moduleRegister {
|
||||
if matched, err = filepath.Match(pattern, name); err == nil {
|
||||
if matched {
|
||||
count++
|
||||
mod.importFunc(ctx)
|
||||
mod.imported = true
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// func ImportInContextByGlobPattern(ctx ExprContext, pattern string) (count int, err error) {
|
||||
// var matched bool
|
||||
// for name, mod := range moduleRegister {
|
||||
// if matched, err = filepath.Match(pattern, name); err == nil {
|
||||
// if matched {
|
||||
// count++
|
||||
// mod.importFunc(ctx)
|
||||
// mod.imported = true
|
||||
// }
|
||||
// } else {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func IterateModules(op func(name, description string, imported bool) bool) {
|
||||
if op != nil {
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operand-const.go
|
||||
package expr
|
||||
|
||||
// -------- const term
|
||||
func newConstTerm(tk *Token) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalConst,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- eval func
|
||||
func evalConst(ctx ExprContext, self *term) (v any, err error) {
|
||||
v = self.tk.Value
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymString, newConstTerm)
|
||||
registerTermConstructor(SymInteger, newConstTerm)
|
||||
registerTermConstructor(SymFloat, newConstTerm)
|
||||
registerTermConstructor(SymBool, newConstTerm)
|
||||
registerTermConstructor(SymKwNil, newConstTerm)
|
||||
}
|
||||
+7
-8
@@ -23,16 +23,15 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
|
||||
|
||||
// -------- eval func call
|
||||
func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) {
|
||||
if info, exists := ctx.GetFuncInfo(name); exists {
|
||||
if info, exists, owner := GetFuncInfo(ctx, name); exists {
|
||||
if info.MinArgs() > len(params) {
|
||||
if info.MaxArgs() < 0 {
|
||||
err = fmt.Errorf("too few params -- expected %d or more, got %d", info.MinArgs(), len(params))
|
||||
} else {
|
||||
err = fmt.Errorf("too few params -- expected %d, got %d", info.MinArgs(), len(params))
|
||||
}
|
||||
err = errTooFewParams(info.MinArgs(), info.MaxArgs(), len(params))
|
||||
}
|
||||
if info.MaxArgs() >= 0 && info.MaxArgs() < len(params) {
|
||||
err = fmt.Errorf("too much params -- expected %d, got %d", info.MaxArgs(), len(params))
|
||||
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(params) {
|
||||
err = errTooMuchParams(info.MaxArgs(), len(params))
|
||||
}
|
||||
if err == nil && owner != ctx {
|
||||
ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("unknown function %s()", name)
|
||||
|
||||
@@ -6,6 +6,7 @@ package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -61,6 +62,16 @@ func newList(listAny []any) (list *ListType) {
|
||||
return
|
||||
}
|
||||
|
||||
func (list *ListType) indexDeepCmp(target any) (index int) {
|
||||
index = -1
|
||||
for i, item := range *list {
|
||||
if reflect.DeepEqual(item, target) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// -------- list term
|
||||
func newListTermA(args ...*term) *term {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operand-literal.go
|
||||
package expr
|
||||
|
||||
// -------- literal term
|
||||
func newLiteralTerm(tk *Token) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: nil,
|
||||
position: posLeaf,
|
||||
priority: priValue,
|
||||
evalFunc: evalLiteral,
|
||||
}
|
||||
}
|
||||
|
||||
// -------- eval func
|
||||
func evalLiteral(ctx ExprContext, self *term) (v any, err error) {
|
||||
v = self.tk.Value
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymString, newLiteralTerm)
|
||||
registerTermConstructor(SymInteger, newLiteralTerm)
|
||||
registerTermConstructor(SymFloat, newLiteralTerm)
|
||||
registerTermConstructor(SymFraction, newLiteralTerm)
|
||||
registerTermConstructor(SymBool, newLiteralTerm)
|
||||
registerTermConstructor(SymKwNil, newLiteralTerm)
|
||||
}
|
||||
+2
-2
@@ -24,8 +24,8 @@ func newVarTerm(tk *Token) *term {
|
||||
func evalVar(ctx ExprContext, self *term) (v any, err error) {
|
||||
var exists bool
|
||||
name := self.source()
|
||||
if v, exists = ctx.GetVar(name); !exists {
|
||||
if info, exists := ctx.GetFuncInfo(name); exists {
|
||||
if v, exists = GetVar(ctx, name); !exists {
|
||||
if info, exists, _ := GetFuncInfo(ctx, name); exists {
|
||||
v = info.Functor()
|
||||
} else {
|
||||
err = fmt.Errorf("undefined variable or function %q", name)
|
||||
|
||||
+2
-2
@@ -28,13 +28,13 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
|
||||
count := 0
|
||||
if IsString(childValue) {
|
||||
module, _ := childValue.(string)
|
||||
count, err = ImportInContextByGlobPattern(ctx, module)
|
||||
count, err = ImportInContextByGlobPattern(module)
|
||||
} else {
|
||||
var moduleSpec any
|
||||
it := NewAnyIterator(childValue)
|
||||
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
||||
if module, ok := moduleSpec.(string); ok {
|
||||
if ImportInContext(ctx, module) {
|
||||
if ImportInContext(module) {
|
||||
count++
|
||||
} else {
|
||||
err = self.Errorf("unknown module %q", module)
|
||||
|
||||
+4
-3
@@ -22,10 +22,11 @@ func verifyIndex(ctx ExprContext, indexTerm *term, maxValue int) (index int, err
|
||||
var indexValue any
|
||||
if indexValue, err = indexTerm.compute(ctx); err == nil {
|
||||
if v, err = indexTerm.toInt(indexValue, "index expression value must be integer"); err == nil {
|
||||
if v < 0 && v >= -maxValue {
|
||||
v = maxValue + v
|
||||
}
|
||||
if v >= 0 && v < maxValue {
|
||||
index = v
|
||||
} else if index >= -maxValue {
|
||||
index = maxValue + v
|
||||
} else {
|
||||
err = indexTerm.Errorf("index %d out of bounds", v)
|
||||
}
|
||||
@@ -56,7 +57,7 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
||||
case string:
|
||||
var index int
|
||||
if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil {
|
||||
v = unboxedValue[index]
|
||||
v = string(unboxedValue[index])
|
||||
}
|
||||
case map[any]any:
|
||||
var ok bool
|
||||
|
||||
+136
-2
@@ -4,9 +4,12 @@
|
||||
// operand-fraction.go
|
||||
package expr
|
||||
|
||||
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -24,6 +27,137 @@ func newFraction(num, den int64) *fraction {
|
||||
return &fraction{num, den}
|
||||
}
|
||||
|
||||
func float64ToFraction(f float64) (fract *fraction, err error) {
|
||||
var sign string
|
||||
intPart, decPart := math.Modf(f)
|
||||
if decPart < 0.0 {
|
||||
sign = "-"
|
||||
intPart = -intPart
|
||||
decPart = -decPart
|
||||
}
|
||||
dec := fmt.Sprintf("%.12f", decPart)
|
||||
s := fmt.Sprintf("%s%.f%s", sign, intPart, dec[1:])
|
||||
// fmt.Printf("S: '%s'\n",s)
|
||||
return makeGeneratingFraction(s)
|
||||
}
|
||||
|
||||
// Based on https://cs.opensource.google/go/go/+/refs/tags/go1.22.3:src/math/big/rat.go;l=39
|
||||
/*
|
||||
func _float64ToFraction(f float64) (num, den int64, err error) {
|
||||
const expMask = 1<<11 - 1
|
||||
bits := math.Float64bits(f)
|
||||
mantissa := bits & (1<<52 - 1)
|
||||
exp := int((bits >> 52) & expMask)
|
||||
switch exp {
|
||||
case expMask: // non-finite
|
||||
err = errors.New("infite")
|
||||
return
|
||||
case 0: // denormal
|
||||
exp -= 1022
|
||||
default: // normal
|
||||
mantissa |= 1 << 52
|
||||
exp -= 1023
|
||||
}
|
||||
|
||||
shift := 52 - exp
|
||||
|
||||
// Optimization (?): partially pre-normalise.
|
||||
for mantissa&1 == 0 && shift > 0 {
|
||||
mantissa >>= 1
|
||||
shift--
|
||||
}
|
||||
|
||||
if f < 0 {
|
||||
num = -int64(mantissa)
|
||||
} else {
|
||||
num = int64(mantissa)
|
||||
}
|
||||
den = int64(1)
|
||||
|
||||
if shift > 0 {
|
||||
den = den << shift
|
||||
} else {
|
||||
num = num << (-shift)
|
||||
}
|
||||
return
|
||||
}
|
||||
*/
|
||||
|
||||
func makeGeneratingFraction(s string) (f *fraction, err error) {
|
||||
var num, den int64
|
||||
var sign int64 = 1
|
||||
var parts []string
|
||||
if len(s) == 0 {
|
||||
goto exit
|
||||
}
|
||||
if s[0] == '-' {
|
||||
sign = int64(-1)
|
||||
s = s[1:]
|
||||
} else if s[0] == '+' {
|
||||
s = s[1:]
|
||||
}
|
||||
if strings.HasSuffix(s, "()") {
|
||||
s = s[0 : len(s)-2]
|
||||
}
|
||||
parts = strings.SplitN(s, ".", 2)
|
||||
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
||||
return
|
||||
}
|
||||
if len(parts) == 1 {
|
||||
f = newFraction(sign*num, 1)
|
||||
} else if len(parts) == 2 {
|
||||
subParts := strings.SplitN(parts[1], "(", 2)
|
||||
if len(subParts) == 1 {
|
||||
den = 1
|
||||
dec := parts[1]
|
||||
lsd := len(dec)
|
||||
for i := lsd - 1; i >= 0 && dec[i] == '0'; i-- {
|
||||
lsd--
|
||||
}
|
||||
for _, c := range dec[0:lsd] {
|
||||
if c < '0' || c > '9' {
|
||||
return nil, errExpectedGot("fract", "digit", c)
|
||||
}
|
||||
num = num*10 + int64(c-'0')
|
||||
den = den * 10
|
||||
}
|
||||
f = newFraction(sign*num, den)
|
||||
} else if len(subParts) == 2 {
|
||||
sub := num
|
||||
mul := int64(1)
|
||||
for _, c := range subParts[0] {
|
||||
if c < '0' || c > '9' {
|
||||
return nil, errExpectedGot("fract", "digit", c)
|
||||
}
|
||||
num = num*10 + int64(c-'0')
|
||||
sub = sub*10 + int64(c-'0')
|
||||
mul *= 10
|
||||
}
|
||||
if len(subParts) == 2 {
|
||||
if s[len(s)-1] != ')' {
|
||||
goto exit
|
||||
}
|
||||
p := subParts[1][0 : len(subParts[1])-1]
|
||||
for _, c := range p {
|
||||
if c < '0' || c > '9' {
|
||||
return nil, errExpectedGot("fract", "digit", c)
|
||||
}
|
||||
num = num*10 + int64(c-'0')
|
||||
den = den*10 + 9
|
||||
}
|
||||
den *= mul
|
||||
}
|
||||
num -= sub
|
||||
f = newFraction(sign*num, den)
|
||||
}
|
||||
}
|
||||
exit:
|
||||
if f == nil {
|
||||
err = errors.New("bad syntax")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *fraction) toFloat() float64 {
|
||||
return float64(f.num) / float64(f.den)
|
||||
}
|
||||
@@ -146,12 +280,12 @@ func lcm(a, b int64) (l int64) {
|
||||
|
||||
func sumFract(f1, f2 *fraction) (sum *fraction) {
|
||||
m := lcm(f1.den, f2.den)
|
||||
sum = newFraction(f1.num*(m/f1.den) + f2.num*(m/f2.den), m)
|
||||
sum = newFraction(f1.num*(m/f1.den)+f2.num*(m/f2.den), m)
|
||||
return
|
||||
}
|
||||
|
||||
func mulFract(f1, f2 *fraction) (prod *fraction) {
|
||||
prod = newFraction(f1.num * f2.num, f1.den * f2.den)
|
||||
prod = newFraction(f1.num*f2.num, f1.den*f2.den)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-in.go
|
||||
package expr
|
||||
|
||||
//-------- in term
|
||||
|
||||
func newInTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priRelational,
|
||||
evalFunc: evalIn,
|
||||
}
|
||||
}
|
||||
|
||||
func hasKey(d map[any]any, target any) (ok bool) {
|
||||
_, ok = d[target]
|
||||
return
|
||||
}
|
||||
|
||||
func evalIn(ctx ExprContext, self *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsList(rightValue) {
|
||||
list, _ := rightValue.(*ListType)
|
||||
v = list.indexDeepCmp(leftValue) >= 0
|
||||
} else if IsDict(rightValue) {
|
||||
d, _ := rightValue.(map[any]any)
|
||||
v = hasKey(d, leftValue)
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymKwIn, newInTerm)
|
||||
}
|
||||
+5
-2
@@ -24,11 +24,14 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) {
|
||||
}
|
||||
|
||||
if IsList(childValue) {
|
||||
list, _ := childValue.([]any)
|
||||
v = int64(len(list))
|
||||
ls, _ := childValue.(*ListType)
|
||||
v = int64(len(*ls))
|
||||
} else if IsString(childValue) {
|
||||
s, _ := childValue.(string)
|
||||
v = int64(len(s))
|
||||
} else if IsDict(childValue) {
|
||||
m, _ := childValue.(map[any]any)
|
||||
v = int64(len(m))
|
||||
} else if it, ok := childValue.(Iterator); ok {
|
||||
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) {
|
||||
count, _ := extIt.CallOperation(countName, nil)
|
||||
|
||||
@@ -61,6 +61,14 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
|
||||
} else {
|
||||
v, err = sumAnyFract(leftValue, rightValue)
|
||||
}
|
||||
} else if IsDict(leftValue) && IsDict(rightValue) {
|
||||
leftDict, _ := leftValue.(map[any]any)
|
||||
rightDict, _ := rightValue.(map[any]any)
|
||||
c := CloneMap(leftDict)
|
||||
for key, value := range rightDict {
|
||||
c[key] = value
|
||||
}
|
||||
v = c
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||
tk = scanner.Next()
|
||||
if tk.IsSymbol(SymIdentifier) {
|
||||
param := newTerm(tk, nil)
|
||||
param := newTerm(tk)
|
||||
args = append(args, param)
|
||||
tk = scanner.Next()
|
||||
} else if itemExpected {
|
||||
|
||||
+7
-5
@@ -35,10 +35,10 @@ func TestGeneralParser(t *testing.T) {
|
||||
/* 14 */ {`not true`, false, nil},
|
||||
/* 15 */ {`true and false`, false, nil},
|
||||
/* 16 */ {`true or false`, true, nil},
|
||||
/* 17 */ {`"uno" + "due"`, `unodue`, nil},
|
||||
/* 18 */ {`"uno" + 2`, `uno2`, nil},
|
||||
/* 19 */ {`"uno" + (2+1)`, `uno3`, nil},
|
||||
/* 20 */ {`"uno" * (2+1)`, `unounouno`, nil},
|
||||
/* *17 */ {`"uno" + "due"`, `unodue`, nil},
|
||||
/* *18 */ {`"uno" + 2`, `uno2`, nil},
|
||||
/* *19 */ {`"uno" + (2+1)`, `uno3`, nil},
|
||||
/* *20 */ {`"uno" * (2+1)`, `unounouno`, nil},
|
||||
/* 21 */ {"1", int64(1), nil},
|
||||
/* 22 */ {"1.5", float64(1.5), nil},
|
||||
/* 23 */ {"1.5*2", float64(3.0), nil},
|
||||
@@ -158,6 +158,8 @@ func TestGeneralParser(t *testing.T) {
|
||||
/* 137 */ {`builtin "os.file"`, int64(1), nil},
|
||||
/* 138 */ {`v=10; v++; v`, int64(11), nil},
|
||||
/* 139 */ {`1+1|2+0.5`, float64(2), nil},
|
||||
/* 140 */ {`1.2()`, newFraction(6, 5), nil},
|
||||
/* 141 */ {`1|(2-2)`, nil, errors.New(`division by zero`)},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
@@ -177,7 +179,7 @@ func parserTest(t *testing.T, section string, inputs []inputType) {
|
||||
ctx := NewSimpleFuncStore()
|
||||
parser := NewParser(ctx)
|
||||
|
||||
logTest(t, i+1, "Iterator", input.source, input.wantResult, input.wantErr)
|
||||
logTest(t, i+1, section, input.source, input.wantResult, input.wantErr)
|
||||
|
||||
r := strings.NewReader(input.source)
|
||||
scanner := NewScanner(r, DefaultTranslations())
|
||||
|
||||
+31
-12
@@ -385,20 +385,37 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil && (ch == 'e' || ch == 'E') {
|
||||
sym = SymFloat
|
||||
sb.WriteByte(ch)
|
||||
if ch, err = self.readChar(); err == nil {
|
||||
if ch == '+' || ch == '-' {
|
||||
sb.WriteByte(ch)
|
||||
ch, err = self.readChar()
|
||||
}
|
||||
if ch >= '0' && ch <= '9' {
|
||||
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() {
|
||||
if err == nil {
|
||||
if ch == 'e' || ch == 'E' {
|
||||
sym = SymFloat
|
||||
sb.WriteByte(ch)
|
||||
if ch, err = self.readChar(); err == nil {
|
||||
if ch == '+' || ch == '-' {
|
||||
sb.WriteByte(ch)
|
||||
ch, err = self.readChar()
|
||||
}
|
||||
if ch >= '0' && ch <= '9' {
|
||||
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() {
|
||||
sb.WriteByte(ch)
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("[%d:%d] expected integer exponent, got %c", self.row, self.column, ch)
|
||||
}
|
||||
}
|
||||
} else if ch == '(' {
|
||||
sym = SymFraction
|
||||
sb.WriteByte(ch)
|
||||
ch, err = self.readChar()
|
||||
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() {
|
||||
sb.WriteByte(ch)
|
||||
}
|
||||
if err == nil {
|
||||
if ch != ')' {
|
||||
err = fmt.Errorf("[%d:%d] expected ')', got '%c'", self.row, self.column, ch)
|
||||
} else {
|
||||
sb.WriteByte(ch)
|
||||
_, err = self.readChar()
|
||||
}
|
||||
} else {
|
||||
err = errors.New("expected integer exponent")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -412,6 +429,8 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
|
||||
txt := sb.String()
|
||||
if sym == SymFloat {
|
||||
value, err = strconv.ParseFloat(txt, 64)
|
||||
} else if sym == SymFraction {
|
||||
value, err = makeGeneratingFraction(txt)
|
||||
} else {
|
||||
value, err = strconv.ParseInt(txt, numBase, 64)
|
||||
}
|
||||
|
||||
+10
-5
@@ -7,6 +7,7 @@ package expr
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -55,15 +56,18 @@ func TestScanner(t *testing.T) {
|
||||
/* 33 */ {`(`, SymOpenRound, nil, nil},
|
||||
/* 34 */ {`)`, SymClosedRound, nil, nil},
|
||||
/* 35 */ {`1E+2`, SymFloat, float64(100), nil},
|
||||
/* 36 */ {`1E+x`, SymError, errors.New("expected integer exponent"), nil},
|
||||
/* 36 */ {`1E+x`, SymError, errors.New("[1:5] expected integer exponent, got x"), nil},
|
||||
/* 37 */ {`$`, SymDollar, nil, nil},
|
||||
/* 38 */ {`\`, SymError, errors.New("incomplete escape sequence"), nil},
|
||||
/* 39 */ {`"string"`, SymString, "string", nil},
|
||||
/* 39 */ {`identifier`, SymIdentifier, "identifier", nil},
|
||||
/* 40 */ {`identifier`, SymIdentifier, "identifier", nil},
|
||||
/* 41 */ {`1.2(3)`, SymFraction, newFraction(37, 30), nil},
|
||||
}
|
||||
|
||||
for i, input := range inputs {
|
||||
|
||||
// if i != 40 {
|
||||
// continue
|
||||
// }
|
||||
if input.wantErr == nil {
|
||||
t.Log(fmt.Sprintf("[+]Test nr %2d -- %q", i+1, input.source))
|
||||
} else {
|
||||
@@ -75,7 +79,8 @@ func TestScanner(t *testing.T) {
|
||||
|
||||
if tk := scanner.Next(); tk == nil {
|
||||
t.Errorf("%d: %q -> got = (nil), want %v (value %v [%T])", i+1, input.source, input.wantSym, input.wantValue, input.wantValue)
|
||||
} else if tk.Sym != input.wantSym || tk.Value != input.wantValue {
|
||||
// } else if tk.Sym != input.wantSym || tk.Value != input.wantValue {
|
||||
} else if tk.Sym != input.wantSym || !reflect.DeepEqual(tk.Value, input.wantValue) {
|
||||
if tk.Sym == SymError && input.wantSym == tk.Sym {
|
||||
if tkErr, tkOk := tk.Value.(error); tkOk {
|
||||
if inputErr, inputOk := input.wantValue.(error); inputOk {
|
||||
@@ -86,7 +91,7 @@ func TestScanner(t *testing.T) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
t.Errorf("%d: %q -> got = %v (value=%v [%T), want %v (value=%v [%T)", i+1,
|
||||
t.Errorf("%d: %q -> got = %v (value=%v [%T]), want %v (value=%v [%T])", i+1,
|
||||
input.source, tk.Sym, tk.Value, tk.Value, input.wantSym, input.wantValue, input.wantValue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// strings_test.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStringsParser(t *testing.T) {
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`"uno" + "due"`, `unodue`, nil},
|
||||
/* 2 */ {`"uno" + 2`, `uno2`, nil},
|
||||
/* 3 */ {`"uno" + (2+1)`, `uno3`, nil},
|
||||
/* 4 */ {`"uno" * (2+1)`, `unounouno`, nil},
|
||||
/* 5 */ {`"abc".1`, `b`, nil},
|
||||
/* 5 */ {`#"abc"`, int64(3), nil},
|
||||
}
|
||||
parserTest(t, "String", inputs)
|
||||
}
|
||||
@@ -73,6 +73,7 @@ const (
|
||||
SymBool
|
||||
SymInteger
|
||||
SymFloat
|
||||
SymFraction
|
||||
SymString
|
||||
SymIterator
|
||||
SymOr
|
||||
@@ -98,6 +99,7 @@ const (
|
||||
SymKwBut
|
||||
SymKwFunc
|
||||
SymKwBuiltin
|
||||
SymKwIn
|
||||
SymKwInclude
|
||||
SymKwNil
|
||||
)
|
||||
@@ -111,6 +113,7 @@ func init() {
|
||||
"BUILTIN": SymKwBuiltin,
|
||||
"BUT": SymKwBut,
|
||||
"FUNC": SymKwFunc,
|
||||
"IN": SymKwIn,
|
||||
"INCLUDE": SymKwInclude,
|
||||
"NOT": SymKwNot,
|
||||
"OR": SymKwOr,
|
||||
|
||||
@@ -17,11 +17,10 @@ func registerTermConstructor(sym Symbol, constructor termContructor) {
|
||||
constructorRegistry[sym] = constructor
|
||||
}
|
||||
|
||||
func newTerm(tk *Token, parent *term) (inst *term) {
|
||||
func newTerm(tk *Token) (inst *term) {
|
||||
if constructorRegistry != nil {
|
||||
if construct, exists := constructorRegistry[tk.Sym]; exists {
|
||||
inst = construct(tk)
|
||||
inst.setParent(parent)
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@@ -31,6 +31,7 @@ func TestGetRoom(t *testing.T) {
|
||||
t.Errorf("err: got <%v>, want <nil>", gotErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetChildrenCount(t *testing.T) {
|
||||
tk1 := NewValueToken(0, 0, SymInteger, "100", 100)
|
||||
|
||||
|
||||
@@ -24,6 +24,11 @@ func IsFloat(v any) (ok bool) {
|
||||
return ok
|
||||
}
|
||||
|
||||
func IsBool(v any) (ok bool) {
|
||||
_, ok = v.(bool)
|
||||
return ok
|
||||
}
|
||||
|
||||
func IsList(v any) (ok bool) {
|
||||
_, ok = v.(*ListType)
|
||||
return ok
|
||||
@@ -34,6 +39,18 @@ func IsDict(v any) (ok bool) {
|
||||
return ok
|
||||
}
|
||||
|
||||
func IsFract(v any) (ok bool) {
|
||||
_, ok = v.(*fraction)
|
||||
return ok
|
||||
}
|
||||
|
||||
func IsRational(v any) (ok bool) {
|
||||
if _, ok = v.(*fraction); !ok {
|
||||
_, ok = v.(int64)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
func IsNumber(v any) (ok bool) {
|
||||
return IsFloat(v) || IsInteger(v)
|
||||
}
|
||||
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// utils_test.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsString(t *testing.T) {
|
||||
count := 0
|
||||
succeeded := 0
|
||||
failed := 0
|
||||
|
||||
count++
|
||||
if !IsBool(true) {
|
||||
t.Errorf("%d: IsBool(true) -> result = false, want true", count)
|
||||
failed++
|
||||
} else {
|
||||
succeeded++
|
||||
}
|
||||
|
||||
count++
|
||||
if !IsString("abc") {
|
||||
t.Errorf("%d: IsString(\"abc\") -> result = false, want true", count)
|
||||
failed++
|
||||
} else {
|
||||
succeeded++
|
||||
}
|
||||
|
||||
count++
|
||||
if !IsInteger(int64(123)) {
|
||||
t.Errorf("%d: IsInteger(123) -> result = false, want true", count)
|
||||
failed++
|
||||
} else {
|
||||
succeeded++
|
||||
}
|
||||
|
||||
count++
|
||||
if !IsFloat(1.23) {
|
||||
t.Errorf("%d: IsFloat(1.23) -> result = false, want true", count)
|
||||
failed++
|
||||
} else {
|
||||
succeeded++
|
||||
}
|
||||
|
||||
count++
|
||||
if !IsFloat(numAsFloat(123)) {
|
||||
t.Errorf("%d: IsFloat(numAsFloat(123)) -> result = false, want true", count)
|
||||
failed++
|
||||
} else {
|
||||
succeeded++
|
||||
}
|
||||
|
||||
count++
|
||||
b, ok := toBool(true)
|
||||
if !(ok && b) {
|
||||
t.Errorf("%d: toBool(true) b=%v, ok=%v -> result = false, want true", count, b, ok)
|
||||
failed++
|
||||
} else {
|
||||
succeeded++
|
||||
}
|
||||
|
||||
count++
|
||||
b, ok = toBool("abc")
|
||||
if !(ok && b) {
|
||||
t.Errorf("%d: toBool(\"abc\") b=%v, ok=%v -> result = false, want true", count, b, ok)
|
||||
failed++
|
||||
} else {
|
||||
succeeded++
|
||||
}
|
||||
|
||||
t.Logf("test count: %d, succeeded count: %d, failed count: %d", count, succeeded, failed)
|
||||
}
|
||||
|
||||
func TestToIntOk(t *testing.T) {
|
||||
source := int64(64)
|
||||
wantValue := int(64)
|
||||
wantErr := error(nil)
|
||||
|
||||
gotValue, gotErr := toInt(source, "test")
|
||||
|
||||
if gotErr != nil || gotValue != wantValue {
|
||||
t.Errorf("toInt(%v, \"test\") gotValue=%v, gotErr=%v -> wantValue=%v, wantErr=%v",
|
||||
source, gotValue, gotErr, wantValue, wantErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestToIntErr(t *testing.T) {
|
||||
source := uint64(64)
|
||||
wantValue := 0
|
||||
wantErr := errors.New(`test expected integer, got uint64 (64)`)
|
||||
|
||||
gotValue, gotErr := toInt(source, "test")
|
||||
|
||||
if gotErr.Error() != wantErr.Error() || gotValue != wantValue {
|
||||
t.Errorf("toInt(%v, \"test\") gotValue=%v, gotErr=%v -> wantValue=%v, wantErr=%v",
|
||||
source, gotValue, gotErr, wantValue, wantErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyInteger(t *testing.T) {
|
||||
type inputType struct {
|
||||
source any
|
||||
wantValue int64
|
||||
wantOk bool
|
||||
}
|
||||
section := "utils.anyInteger"
|
||||
|
||||
inputs := []inputType{
|
||||
/* 1 */ {int8(-8), int64(-8), true},
|
||||
/* 2 */ {int16(-16), int64(-16), true},
|
||||
/* 3 */ {int32(-32), int64(-32), true},
|
||||
/* 4 */ {int64(-64), int64(-64), true},
|
||||
/* 5 */ {uint8(8), int64(8), true},
|
||||
/* 6 */ {uint16(16), int64(16), true},
|
||||
/* 7 */ {uint32(32), int64(32), true},
|
||||
/* 8 */ {uint64(64), int64(64), true},
|
||||
/* 9 */ {int(-1), int64(-1), true},
|
||||
/* 10 */ {true, 0, false},
|
||||
}
|
||||
|
||||
succeeded := 0
|
||||
failed := 0
|
||||
|
||||
for i, input := range inputs {
|
||||
gotValue, gotOk := anyInteger(input.source)
|
||||
if gotOk != input.wantOk || gotValue != input.wantValue {
|
||||
t.Errorf("%d: anyInteger(%v) -> gotOk = %t, wantOk = %t; gotValue = %v, wantValue = %v",
|
||||
i+1, input.source, gotOk, input.wantOk, gotValue, input.wantValue)
|
||||
failed++
|
||||
} else {
|
||||
succeeded++
|
||||
}
|
||||
}
|
||||
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
|
||||
}
|
||||
Reference in New Issue
Block a user