Compare commits
23 Commits
v0.29.0
...
3ba8194ddb
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ba8194ddb | |||
| 037565c41e | |||
| f8d12b1a93 | |||
| 518d4d8d65 | |||
| d64602cb00 | |||
| 4709248828 | |||
| 5ecf81412e | |||
| ff4db34f7b | |||
| 0f848071c2 | |||
| 6b3351b324 | |||
| 760c1ee6da | |||
| 5ab6876ea1 | |||
| 6268abda8c | |||
| d25bd325b7 | |||
| 01c04feea5 | |||
| 6fc689c46c | |||
| eccb0c4dc9 | |||
| e43823740f | |||
| 526982a564 | |||
| 252514943e | |||
| 32686fac62 | |||
| 52a627c747 | |||
| d91e7eb979 |
@@ -211,6 +211,25 @@ func evalFunc(ctx ExprContext, name string, args map[string]any) (result any, er
|
||||
return
|
||||
}
|
||||
|
||||
func varFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var varName string
|
||||
var ok bool
|
||||
|
||||
if varName, ok = args[ParamName].(string); !ok {
|
||||
return nil, ErrWrongParamType(name, ParamName, TypeString, args[ParamName])
|
||||
}
|
||||
|
||||
if result, ok = args[ParamValue]; ok && result != nil {
|
||||
ctx.GetParent().UnsafeSetVar(varName, result)
|
||||
// } else {
|
||||
// err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
// }
|
||||
} else if result, ok = ctx.GetVar(varName); !ok {
|
||||
err = ErrUnknownVar(name, varName)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//// import
|
||||
|
||||
func ImportBuiltinsFuncs(ctx ExprContext) {
|
||||
@@ -240,6 +259,11 @@ func ImportBuiltinsFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("eval", NewGolangFunctor(evalFunc), TypeAny, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("var", NewGolangFunctor(varFunc), TypeAny, []ExprFuncParam{
|
||||
NewFuncParam(ParamName),
|
||||
NewFuncParamFlagDef(ParamValue, PfDefault, nil),
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -200,6 +200,24 @@ func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any
|
||||
return
|
||||
}
|
||||
|
||||
func upperStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[ParamSource].(string); ok {
|
||||
result = strings.ToUpper(source)
|
||||
} else {
|
||||
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lowerStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[ParamSource].(string); ok {
|
||||
result = strings.ToLower(source)
|
||||
} else {
|
||||
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// --- End of function definitions
|
||||
|
||||
// Import above functions in the context
|
||||
@@ -236,6 +254,14 @@ func ImportStringFuncs(ctx ExprContext) {
|
||||
NewFuncParam(ParamSuffix),
|
||||
NewFuncParamFlag(strParamOther, PfRepeat),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strUpper", NewGolangFunctor(upperStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strLower", NewGolangFunctor(lowerStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
})
|
||||
}
|
||||
|
||||
// Register the import function in the import-register.
|
||||
|
||||
@@ -80,6 +80,10 @@ func ErrUnknownParam(funcName, paramName string) error {
|
||||
return fmt.Errorf("%s(): unknown parameter %q", funcName, paramName)
|
||||
}
|
||||
|
||||
func ErrUnknownVar(funcName, varName string) error {
|
||||
return fmt.Errorf("%s(): unknown variable %q", funcName, varName)
|
||||
}
|
||||
|
||||
// --- Operator errors
|
||||
|
||||
func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error {
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ func NewDict(dictAny map[any]any) (dict *DictType) {
|
||||
}
|
||||
|
||||
func newDict(dictAny map[any]*term) (dict *DictType) {
|
||||
// TODO Change wi a call to NewDict()
|
||||
// TODO Change with a call to NewDict()
|
||||
var d DictType
|
||||
if dictAny != nil {
|
||||
d = make(DictType, len(dictAny))
|
||||
|
||||
+90
-44
@@ -20,15 +20,15 @@ Expressions calculator
|
||||
:rouge-style: gruvbox
|
||||
// :rouge-style: colorful
|
||||
//:rouge-style: monokay
|
||||
// Work around to manage double-column in back-tick quotes
|
||||
// Workaround to manage double-column in back-tick quotes
|
||||
:2c: ::
|
||||
|
||||
toc::[]
|
||||
|
||||
#TODO: Work in progress (last update on 2024/06/21, 05:40 a.m.)#
|
||||
#TODO: Work in progress (last update on 2026/04/15, 6:02 p.m.)#
|
||||
|
||||
== Expr
|
||||
_Expr_ is a GO package capable of analysing, interpreting and calculating expressions.
|
||||
_Expr_ is a GO package that can analyze, interpret and calculate expressions.
|
||||
|
||||
|
||||
=== Concepts and terminology
|
||||
@@ -42,7 +42,7 @@ Expressions are texts containing sequences of operations represented by a syntax
|
||||
image::expression-diagram.png[]
|
||||
|
||||
==== Variables
|
||||
_Expr_ supports variables. The result of an expression can be stored in a variable and reused in other espressions simply specifying the name of the variable as an operand.
|
||||
_Expr_ supports variables. The result of an expression can be stored in a variable and reused in other espressions by simply specifying the name of the variable as an operand.
|
||||
|
||||
==== Multi-expression
|
||||
An input text valid for _Expr_ can contain more than an expression. Expressions are separated by [blue]`;` (semicolon). When an input contains two or more expressions it is called _multi-expression_.
|
||||
@@ -58,9 +58,9 @@ The expression context is analogous to the stack-frame of other programming lang
|
||||
|
||||
Function contexts are created by cloning the calling context. More details on this topic are given later in this document.
|
||||
|
||||
_Expr_ creates and keeps a inner _global context_ where it stores imported functions, either from builtin or plugin modules. To perform calculations, the user program must provide its own context; this is the _main context_. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The created context can be called _function context_.
|
||||
_Expr_ creates and keeps a inner _global context_ where it stores imported functions, either from builtin or plugin modules. To perform calculations, the user program must provide its own context; this is the _main context_. All calculations take place in this context. As mentioned earlier, when a function is called, a new context is created by cloning the calling context. The created context can be called _function context_.
|
||||
|
||||
Imported functions are registerd in the _global context_. When an expression first calls an imported function, that function is linked to the current context; this can be the _main context_ or a _function context_.
|
||||
Imported functions are registered in the _global context_. When an expression first calls an imported function, that function is linked to the current context; this can be the _main context_ or a _function context_.
|
||||
|
||||
=== `dev-expr` test tool
|
||||
Before we begin to describe the syntax of _Expr_, it is worth introducing _dev-expr_ because it will be used to show many examples of expressions.
|
||||
@@ -145,7 +145,7 @@ dev-expr -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoro
|
||||
<2> Fractions: _numerator_ : _denominator_.
|
||||
<3> Activate multi-line output of fractions.
|
||||
<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.
|
||||
<5> Multi-expression: the same result as the previous single expression, but this time it is obtained with two separate calculations.
|
||||
|
||||
== Data types
|
||||
_Expr_ has its type system which is a subset of Golang's type system. It supports numerical, string, relational, boolean expressions, and mixed-type lists and maps.
|
||||
@@ -240,10 +240,11 @@ _Expr_ also supports fractions. Fraction literals are made with two integers sep
|
||||
|
||||
.Fraction literal syntax
|
||||
====
|
||||
*_fraction_* = [__sign__] (_num-den-spec_ "**:**" _float-spec_) +
|
||||
*_fraction_* = [__sign__] (_num-den-spec_ | _float-spec_) +
|
||||
_sign_ = "**+**" | "**-**" +
|
||||
_num-den-spec_ = _digit-seq_ "**|**" _digit-seq_ +
|
||||
_float-spec_ = _dec-seq_ "**.**" [_dec-seq_] "**(**" _dec-seq_ "**)**" +
|
||||
_num-den-spec_ = _digit-seq_ "**:**" _digit-seq_ +
|
||||
_float-spec_ = _dec-seq_ "**.**" [_dec-seq_] "**(**" _repetend_ "**)**" +
|
||||
_repetend_ = _dec-seq_ +
|
||||
_dec-seq_ = _see-integer-literal-syntax_ +
|
||||
_digit-seq_ = _see-integer-literal-syntax_
|
||||
====
|
||||
@@ -276,6 +277,8 @@ _digit-seq_ = _see-integer-literal-syntax_
|
||||
`>>>` [blue]`1:(-2)` +
|
||||
[green]`-1:2`
|
||||
|
||||
`>>>` [blue]`1.(3)` // 1.33333... +
|
||||
[green]`4:3`
|
||||
|
||||
Fractions can be used together with integers and floats in expressions.
|
||||
|
||||
@@ -395,12 +398,13 @@ Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relationa
|
||||
[CAUTION]
|
||||
====
|
||||
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 be evaluated at all.
|
||||
|
||||
.Example
|
||||
[source,go]
|
||||
----
|
||||
2 > (a=1) or (a=8) > 0; a // <1>
|
||||
----
|
||||
<1> This multi-expression returns _1_ because in the first expression the left value of [blue]`or` is _true_ and as a conseguence its right value is not computed. Therefore the _a_ variable only receives the integer _1_.
|
||||
<1> This multi-expression returns _1_ because in the first expression the left value of [blue]`or` is _true_ and, as a conseguence, its right value is not computed. Therefore the _a_ variable only receives the integer _1_.
|
||||
|
||||
|
||||
TIP: `dev-expr` provides the _ctrl()_ function that allows to change this behaviour.
|
||||
@@ -439,8 +443,8 @@ _non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value_} "**]**" +
|
||||
|
||||
| [blue]`+` | _Join_ | Joins two lists | [blue]`[1,2] + [3]` -> _[1,2,3]_
|
||||
| [blue]`-` | _Difference_ | Left list without elements in the right list | [blue]`[1,2,3] - [2]` -> _[1,3]_
|
||||
| [blue]`>>` | _Front insertion_ | Insert an item in front | [blue]`0 >> [1,2]` -> _[0,1,2]_
|
||||
| [blue]`<<` | _Back insertion_ | Insert an item at end | [blue]`[1,2] << 3` -> _[1,2,3]_
|
||||
| [blue]`+>` | _Front insertion_ | Insert an item in front | [blue]`0 +> [1,2]` -> _[0,1,2]_
|
||||
| [blue]`<+` | _Back insertion_ | Insert an item at end | [blue]`[1,2] <+ 3` -> _[1,2,3]_
|
||||
| [blue]`[]` | _Item at index_ | Item at given position | [blue]`[1,2,3][1]` -> _2_
|
||||
| [blue]`in` | _Item in list_ | True if item is in list | [blue]`2 in [1,2,3]` -> _true_ +
|
||||
[blue]`6 in [1,2,3]` -> _false_
|
||||
@@ -492,10 +496,10 @@ Array's items can be accessed using the index `[]` operator.
|
||||
[green]`3`
|
||||
|
||||
.Examples: Element insertion
|
||||
`>>>` [blue]`"first" >> list` +
|
||||
`>>>` [blue]`"first" +> list` +
|
||||
[green]`["first", "one", "six", "three"]`
|
||||
|
||||
`>>>` [blue]`list << "last"` +
|
||||
`>>>` [blue]`list <+ "last"` +
|
||||
[green]`["first", "one", "six", "three", "last"]`
|
||||
|
||||
.Examples: Element in list
|
||||
@@ -509,7 +513,7 @@ Array's items can be accessed using the index `[]` operator.
|
||||
`>>>` [blue]`[1,2,3] + ["one", "two", "three"]` +
|
||||
[green]`[1, 2, 3, "one", "two", "three"]`
|
||||
|
||||
`>>>` [blue]`[1,2,3,4] - [2,4]` +
|
||||
`>>>` [blue]`[1,2,3,2,4] - [2,4]` +
|
||||
[green]`[1, 3]`
|
||||
|
||||
|
||||
@@ -570,7 +574,7 @@ _Expr_, like most programming languages, supports variables. A variable is an id
|
||||
.Variable literal syntax
|
||||
====
|
||||
*_variable_* = _identifier_ "*=*" _any-value_ +
|
||||
_identifier_ = _alpha_ {(_alpha_)|_dec-digit_|"*_*"} +
|
||||
_identifier_ = _alpha_ {_alpha_|_dec-digit_|"*_*"} +
|
||||
__alpha__ = "*a*"|"*b*"|..."*z*"|"*A*"|"*B*"|..."*Z*"
|
||||
====
|
||||
|
||||
@@ -695,7 +699,7 @@ The [blue]`:` symbol (colon) is the separator of the selector-cases. Note that i
|
||||
|
||||
|
||||
=== Variable default value [blue]`??`, [blue]`?=`, and [blue]`?!`
|
||||
The left operand of first two operators, [blue]`??` and [blue]`?=`, must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.
|
||||
The left operand of the first two operators, [blue]`??` and [blue]`?=`, must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.
|
||||
|
||||
IMPORTANT: If the left variable is defined, the right expression is not evaluated at all.
|
||||
|
||||
@@ -735,14 +739,17 @@ NOTE: These operators have a high priority, in particular higher than the operat
|
||||
The table below shows all supported operators by decreasing priorities.
|
||||
|
||||
.Operators priorities
|
||||
[cols="^2,^2,^2,^5,^6"]
|
||||
[cols="^3,^2,^2,^5,^6"]
|
||||
|===
|
||||
| Priority | Operators | Position | Operation | Operands and results
|
||||
| Priority | Operator | Position | Operation | Operands and results
|
||||
|
||||
.2+|*ITEM*| [blue]`[`...`]` | _Postfix_ | _List item_| _list_ `[` _integer_ `]` -> _any_
|
||||
| [blue]`[`...`]` | _Postfix_ | _Dict item_ | _dict_ `[` _any_ `]` -> _any_
|
||||
.2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `++` -> _integer_
|
||||
| [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `++` -> _any_
|
||||
.5+|*INC/DEC*| [blue]`++` | _Postfix_ | _Iterator next item_ | _iterator_ `++` -> _any_
|
||||
| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `++` -> _integer_
|
||||
| [blue]`++` | _Prefix_ | _Pre increment_ | `++` _integer-variable_ -> _integer_
|
||||
| [blue]`--` | _Postfix_ | _Post decrement_ | _integer-variable_ `--` -> _integer_
|
||||
| [blue]`--` | _Prefix_ | _Pre decrement_ | `--` _integer-variable_ -> _integer_
|
||||
.3+|*DEFAULT*| [blue]`??` | _Infix_ | _Default value_| _variable_ `??` _any-expr_ -> _any_
|
||||
| [blue]`?=` | _Infix_ | _Default/assign value_| _variable_ `?=` _any-expr_ -> _any_
|
||||
| [blue]`?!` | _Infix_ | _Alternate value_| _variable_ `?!` _any-expr_ -> _any_
|
||||
@@ -751,13 +758,17 @@ The table below shows all supported operators by decreasing priorities.
|
||||
.3+|*SIGN*| [blue]`+`, [blue]`-` | _Prefix_ | _Change-sign_| (`+`\|`-`) _number_ -> _number_
|
||||
| [blue]`#` | _Prefix_ | _Lenght-of_ | `#` _collection_ -> _integer_
|
||||
| [blue]`#` | _Prefix_ | _Size-of_ | `#` _iterator_ -> _integer_
|
||||
.2+|*BIT SHIFT*| [blue]`<<` | _Infix_ | _Left-Shift_ | _integer_ `<<` _integer_ -> _integer_
|
||||
| [blue]`>>` | _Infix_ | _Right-Shift_ | _integer_ `>>` _iterator_ -> _integer_
|
||||
.2+|*SELECT*| [blue]`? : ::` | _Multi-Infix_ | _Case-Selector_ | _any-expr_ `?` _case-list_ _case-expr_ `:` _case-list_ _case-expr_ ... `::` _default-expr_ -> _any_
|
||||
| [blue]`? : ::` | _Multi-Infix_ | _Index-Selector_ | _int-expr_ `?` _case-expr_ `:` _case-expr_ ... `::` _default-expr_ -> _any_
|
||||
.1+|*FRACT*| [blue]`:` | _Infix_ | _Fraction_ | _integer_ `:` _integer_ -> _fraction_
|
||||
.5+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ `*` _number_ -> _number_
|
||||
.7+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ `*` _number_ -> _number_
|
||||
| [blue]`*` | _Infix_ | _String-repeat_ | _string_ `*` _integer_ -> _string_
|
||||
| [blue]`/` | _Infix_ | _Division_ | _number_ `/` _number_ -> _number_
|
||||
| [blue]`./` | _Infix_ | _Float-division_ | __number__ `./` _number_ -> _float_
|
||||
| [blue]`/` | _Infix_ | _Split_ | _string_ `/` _string_ -> _list_
|
||||
| [blue]`/` | _Infix_ | _Split_ | _string_ `/` integer -> _list_
|
||||
| [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ `%` _integer_ -> _integer_
|
||||
.6+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ `+` _number_ -> _number_
|
||||
| [blue]`+` | _Infix_ | _String-concat_ | (_string_\|_number_) `+` (_string_\|_number_) -> _string_
|
||||
@@ -765,9 +776,10 @@ The table below shows all supported operators by decreasing priorities.
|
||||
| [blue]`+` | _Infix_ | _Dict-join_ | _dict_ `+` _dict_ -> _dict_
|
||||
| [blue]`-` | _Infix_ | _Subtraction_ | _number_ `-` _number_ -> _number_
|
||||
| [blue]`-` | _Infix_ | _List-difference_ | _list_ `-` _list_ -> _list_
|
||||
.3+|*BINARY*| [blue]`&` | _Infix_ | _Binary And_ | _number_ `&` _number_ -> _number_
|
||||
| [blue]`\|` | _Infix_ | _Binary Or_ | _number_ `\|` _number_ -> _number_
|
||||
| [blue]`~` | _Prefix_ | _Binary Not_ | `~` _number_ -> _number_
|
||||
.1+|*BITWISE NOT*| [blue]`~` | _Prefix_ | _Binary Not_ | `~` _integer_ -> _integer_
|
||||
.1+|*BITWISE AND*| [blue]`&` | _Infix_ | _Binary And_ | _integer_ `&` _integer_ -> _integer_
|
||||
.2+|*BITWISE OR*| [blue]`\|` | _Infix_ | _Binary Or_ | _integer_ `\|` _integer_ -> _integer_
|
||||
| [blue]`^` | _Infix_ | _Binary Xor_ | _integer_ `^` _integer_ -> _integer_
|
||||
.8+|*RELATION*| [blue]`<` | _Infix_ | _Less_ | _comparable_ `<` _comparable_ -> _boolean_
|
||||
| [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ `\<=` _comparable_ -> _boolean_
|
||||
| [blue]`>` | _Infix_ | _Greater_ | _comparable_ `>` _comparable_ -> _boolean_
|
||||
@@ -776,20 +788,54 @@ The table below shows all supported operators by decreasing priorities.
|
||||
| [blue]`!=` | _Infix_ | _Not-equal_ | _comparable_ `!=` _comparable_ -> _boolean_
|
||||
| [blue]`in` | _Infix_ | _Member-of-list_ | _any_ `in` _list_ -> _boolean_
|
||||
| [blue]`in` | _Infix_ | _Key-of-dict_ | _any_ `in` _dict_ -> _boolean_
|
||||
.1+|*NOT*| [blue]`not` | _Prefix_ | _Not_ | `not` _boolean_ -> _boolean_
|
||||
.2+|*AND*| [blue]`and` | _Infix_ | _And_ | _boolean_ `and` _boolean_ -> _boolean_
|
||||
.1+|*LOGIC NOT*| [blue]`not` | _Prefix_ | _Not_ | `not` _boolean_ -> _boolean_
|
||||
.2+|*LOGIC AND*| [blue]`and` | _Infix_ | _And_ | _boolean_ `and` _boolean_ -> _boolean_
|
||||
| [blue]`&&` | _Infix_ | _And_ | _boolean_ `&&` _boolean_ -> _boolean_
|
||||
.2+|*OR*| [blue]`or` | _Infix_ | _Or_ | _boolean_ `or` _boolean_ -> _boolean_
|
||||
.2+|*LOGIC OR*| [blue]`or` | _Infix_ | _Or_ | _boolean_ `or` _boolean_ -> _boolean_
|
||||
| [blue]`\|\|` | _Infix_ | _Or_ | _boolean_ `\|\|` _boolean_ -> _boolean_
|
||||
.3+|*ASSIGN*| [blue]`=` | _Infix_ | _Assignment_ | _identifier_ `=` _any_ -> _any_
|
||||
| [blue]`>>` | _Infix_ | _Front-insert_ | _any_ `>>` _list_ -> _list_
|
||||
| [blue]`<<` | _Infix_ | _Back-insert_ | _list_ `<<` _any_ -> _list_
|
||||
.2+|*INSERT*| [blue]`+>` | _Infix_ | _Prepend_ | _any_ `+>` _list_ -> _list_
|
||||
| [blue]`<+` | _Infix_ | _Append_ | _list_ `<+` _any_ -> _list_
|
||||
.2+|*ASSIGN*| [blue]`=` | _Infix_ | _Assignment_ | _identifier_ `=` _any_ -> _any_
|
||||
4+| _See also the table of special allocation operators below_
|
||||
.1+|*BUT*| [blue]`but` | _Infix_ | _But_ | _any_ `but` _any_ -> _any_
|
||||
.1+|*RANGE*| [blue]`:` | _Infix_ | _Index-range_ | _integer_ `:` _integer_ -> _integer-pair_
|
||||
|===
|
||||
|
||||
//^1^ Experimental
|
||||
|
||||
.Special assignment perators
|
||||
[cols="^2,^2,^4,^6"]
|
||||
|===
|
||||
| Priority | Operator | Operation |Equivalent operation
|
||||
|
||||
.9+|*ASSIGN*| [blue]`+=` | _Sum & Assign_ | _var_ `\+=` _expr_ +
|
||||
short for +
|
||||
_var_ `=` _value-of-var_ `+` _expr_
|
||||
| [blue]`-=` | _Subtract & Assign_ | _var_ `-=` _expr_ +
|
||||
short for +
|
||||
_var_ `=` _value-of-var_ `-` _expr_
|
||||
| [blue]`*=` | _Multiply & Assign_ | _var_ `\*=` _expr_ +
|
||||
short for +
|
||||
_var_ `=` _value-of-var_ `*` _expr_
|
||||
| [blue]`/=` | _Divide & Assign_ | _var_ `/=` _expr_ +
|
||||
short for +
|
||||
_var_ `=` _value-of-var_ `/` _expr_
|
||||
| [blue]`%=` | _Remainder & Assign_ | _var_ `%=` _expr_ +
|
||||
short for +
|
||||
_var_ `=` _value-of-var_ `%` _expr_
|
||||
| [blue]`&=` | _Bitwise and & Assign_ | _var_ `&=` _expr_ +
|
||||
short for +
|
||||
_var_ `=` _value-of-var_ `&` _expr_
|
||||
| [blue]`\|=` | _Bitwise or & Assign_ | _var_ `\|=` _expr_ +
|
||||
short for +
|
||||
_var_ `=` _value-of-var_ `\|` _expr_
|
||||
| [blue]`<\<=` | _Left shift & Assign_ | _var_ `<\<=` _expr_ +
|
||||
short for +
|
||||
_var_ `=` _value-of-var_ `<<` _expr_
|
||||
| [blue]`>>=` | _Right shift & Assign_ | _var_ `>>=` _expr_ +
|
||||
short for +
|
||||
_var_ `=` _value-of-var_ `>>` _expr_
|
||||
|===
|
||||
|
||||
== Functions
|
||||
Functions in _Expr_ are very similar to functions available in many programming languages. Currently, _Expr_ supports two types of function, _expr-functions_ and _go-functions_.
|
||||
@@ -799,12 +845,12 @@ Functions in _Expr_ are very similar to functions available in many programming
|
||||
|
||||
|
||||
=== _Expr_ function definition
|
||||
A function is identified and referenced by its name. It can have zero or more parameter. _Expr_ functions also support optional parameters and passing paramters by name.
|
||||
An expr-function is identified and referenced by its name. It can have zero or more parameter. expr-functions also support optional parameters and passing paramters by name.
|
||||
|
||||
.Expr's function definition syntax
|
||||
====
|
||||
*_function-definition_* = _identifier_ "**=**" "**func(**" [_formal-param-list_] "**)**" "**{**" _multi-expression_ "**}**" +
|
||||
_formal-param_list_ = _required-param-list_ [ "**,**" _optional-param-list_ ] +
|
||||
_formal-param-list_ = _required-param-list_ [ "**,**" _optional-param-list_ ] +
|
||||
_required-param-list_ = _identifier_ { "**,**" _identifier_ } +
|
||||
_optional-param-list_ = _optional-parm_ { "**,**" _optional-param_ } +
|
||||
_optional-param_ = _param-name_ "**=**" _any-expr_ +
|
||||
@@ -816,7 +862,7 @@ _param-name_ = _identifier_
|
||||
`>>>` [blue]`sum = func(a, b){ a + b }` +
|
||||
[green]`sum(a, b):any{}`
|
||||
|
||||
^(\*)^ Since the plus, *+*, operator is defined for multiple data-types, the _sum()_ function can be used for any pair of that types.
|
||||
^(*)^ Since the plus, **+**, operator is defined for multiple data-types, the _sum()_ function can be used for any pair of that types.
|
||||
|
||||
`>>>` [gray]_// A more complex example: recursive calculation of the n-th value of Fibonacci's sequence_ +
|
||||
`>>>` [blue]`fib = func(n){ n ? [0] {0}: [1] {1} :: {fib(n-1)+fib(n-2)} }` +
|
||||
@@ -840,7 +886,7 @@ _param-name_ = _identifier_
|
||||
|
||||
|
||||
=== _Golang_ function definition
|
||||
Description of how to define Golan functions and how to bind them to _Expr_ are topics treated in another document that I'll write, one day, maybe.
|
||||
Description of how to define Golang functions and how to bind them to _Expr_ are topics covered in another document that I'll write, one day, maybe.
|
||||
|
||||
=== Function calls
|
||||
To call a function, either Expr or Golang type, it is necessary to specify its name and, at least, its required parameters.
|
||||
@@ -917,9 +963,9 @@ _param-name_ = _identifier_
|
||||
|
||||
|
||||
=== Function context
|
||||
Functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the _clone_ modifier [blue]`@` it is possibile to export local definition to the calling context. The clone modifier must be used as prefix to variable names and it is part of the name. E.g. [blue]`@x` is not the same as [blue]`x`; they are two different and un related variables.
|
||||
Functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the _clone_ modifier [blue]`@` it is possibile to export local definition to the calling context. The clone modifier must be used as prefix to variable names and it is part of the name. E.g. [blue]`@x` is not the same as [blue]`x`; they are two different and unrelated variables.
|
||||
|
||||
Clone variables are normal local variables. The only diffence will appear when the defining function terminate, just before the destruction of its local context. At that point, all local clone variables are cloned in the calling context with the same names but the [blue]`@` symbol.
|
||||
Clone variables are normal local variables. The only diffence will appear when the defining function ends, just before the destruction of its local context. At that point, all local clone variables are cloned in the calling context with the same names but the [blue]`@` symbol.
|
||||
|
||||
.Example
|
||||
`>>>` [blue]`f = func() { @x = 3; x = 5 }` [gray]_// f() declares two *different* local variables: ``@x`` and ``x``_ +
|
||||
@@ -936,12 +982,12 @@ NOTE: The clone modifier [blue]`@` does not make a variable a reference variable
|
||||
The clone modifier can also be used to declare the formal parameters of functions, because they are local variables too.
|
||||
|
||||
.Example
|
||||
`>>>` [blue]`g = func(@p) {2+@p}`
|
||||
g(@p):any{}`
|
||||
`>>>` [blue]`g(9)`
|
||||
11`
|
||||
`>>>` [blue]`p
|
||||
9
|
||||
`>>>` [blue]`g = func(@p) {2+@p}` +
|
||||
[green]`g(@p):any{}` +
|
||||
`>>>` [blue]`g(9)` +
|
||||
[green]`11` +
|
||||
`>>>` [blue]`p` +
|
||||
[green]`9`
|
||||
====
|
||||
|
||||
== Iterators
|
||||
|
||||
+220
-76
@@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="generator" content="Asciidoctor 2.0.20">
|
||||
<meta name="generator" content="Asciidoctor 2.0.26">
|
||||
<meta name="author" content="Celestino Amoroso">
|
||||
<title>Expr</title>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
|
||||
@@ -117,7 +117,6 @@ h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title str
|
||||
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
|
||||
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
|
||||
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
|
||||
pre>code{display:block}
|
||||
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
|
||||
em em{font-style:normal}
|
||||
strong strong{font-weight:400}
|
||||
@@ -141,7 +140,7 @@ p a>code:hover{color:rgba(0,0,0,.9)}
|
||||
#content::before{content:none}
|
||||
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
|
||||
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
|
||||
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
|
||||
#header>h1:only-child{border-bottom:1px solid #dddddf;padding-bottom:8px}
|
||||
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap}
|
||||
#header .details span:first-child{margin-left:-.125em}
|
||||
#header .details span.email a{color:rgba(0,0,0,.85)}
|
||||
@@ -163,6 +162,7 @@ p a>code:hover{color:rgba(0,0,0,.9)}
|
||||
#toctitle{color:#7a2518;font-size:1.2em}
|
||||
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
|
||||
body.toc2{padding-left:15em;padding-right:0}
|
||||
body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
|
||||
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
|
||||
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
|
||||
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
|
||||
@@ -219,6 +219,7 @@ table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
|
||||
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
|
||||
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
|
||||
.listingblock>.content{position:relative}
|
||||
.listingblock pre>code{display:block}
|
||||
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
|
||||
.listingblock:hover code[data-lang]::before{display:block}
|
||||
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
|
||||
@@ -328,7 +329,7 @@ a.image{text-decoration:none;display:inline-block}
|
||||
a.image object{pointer-events:none}
|
||||
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
|
||||
sup.footnote a,sup.footnoteref a{text-decoration:none}
|
||||
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
|
||||
sup.footnote a:active,sup.footnoteref a:active,#footnotes .footnote a:first-of-type:active{text-decoration:underline}
|
||||
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
|
||||
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
|
||||
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
|
||||
@@ -605,7 +606,7 @@ pre.rouge .ss {
|
||||
<div class="sectionbody">
|
||||
<!-- toc disabled -->
|
||||
<div class="paragraph">
|
||||
<p><mark>TODO: Work in progress (last update on 2024/06/21, 05:40 a.m.)</mark></p>
|
||||
<p><mark>TODO: Work in progress (last update on 2026/04/15, 6:02 p.m.)</mark></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -613,7 +614,7 @@ pre.rouge .ss {
|
||||
<h2 id="_expr"><a class="anchor" href="#_expr"></a><a class="link" href="#_expr">1. Expr</a></h2>
|
||||
<div class="sectionbody">
|
||||
<div class="paragraph">
|
||||
<p><em>Expr</em> is a GO package capable of analysing, interpreting and calculating expressions.</p>
|
||||
<p><em>Expr</em> is a GO package that can analyze, interpret and calculate expressions.</p>
|
||||
</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.1. Concepts and terminology</a></h3>
|
||||
@@ -641,7 +642,7 @@ pre.rouge .ss {
|
||||
<div class="sect3">
|
||||
<h4 id="_variables"><a class="anchor" href="#_variables"></a><a class="link" href="#_variables">1.1.1. Variables</a></h4>
|
||||
<div class="paragraph">
|
||||
<p><em>Expr</em> supports variables. The result of an expression can be stored in a variable and reused in other espressions simply specifying the name of the variable as an operand.</p>
|
||||
<p><em>Expr</em> supports variables. The result of an expression can be stored in a variable and reused in other espressions by simply specifying the name of the variable as an operand.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect3">
|
||||
@@ -668,10 +669,10 @@ pre.rouge .ss {
|
||||
<p>Function contexts are created by cloning the calling context. More details on this topic are given later in this document.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><em>Expr</em> creates and keeps a inner <em>global context</em> where it stores imported functions, either from builtin or plugin modules. To perform calculations, the user program must provide its own context; this is the <em>main context</em>. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The created context can be called <em>function context</em>.</p>
|
||||
<p><em>Expr</em> creates and keeps a inner <em>global context</em> where it stores imported functions, either from builtin or plugin modules. To perform calculations, the user program must provide its own context; this is the <em>main context</em>. All calculations take place in this context. As mentioned earlier, when a function is called, a new context is created by cloning the calling context. The created context can be called <em>function context</em>.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Imported functions are registerd in the <em>global context</em>. When an expression first calls an imported function, that function is linked to the current context; this can be the <em>main context</em> or a <em>function context</em>.</p>
|
||||
<p>Imported functions are registered in the <em>global context</em>. When an expression first calls an imported function, that function is linked to the current context; this can be the <em>main context</em> or a <em>function context</em>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -789,7 +790,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i class="conum" data-value="5"></i><b>5</b></td>
|
||||
<td>Multi-expression: the same result of the previous single expression but this it is obtained with two separated calculations.</td>
|
||||
<td>Multi-expression: the same result as the previous single expression, but this time it is obtained with two separate calculations.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -997,10 +998,11 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<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> "<strong>:</strong>" <em>float-spec</em>)<br>
|
||||
<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>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>repetend</em> "<strong>)</strong>"<br>
|
||||
<em>repetend</em> = <em>dec-seq</em><br>
|
||||
<em>dec-seq</em> = <em>see-integer-literal-syntax</em><br>
|
||||
<em>digit-seq</em> = <em>see-integer-literal-syntax</em></p>
|
||||
</div>
|
||||
@@ -1044,6 +1046,10 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<code class="green">-1:2</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">1.(3)</code> // 1.33333…​<br>
|
||||
<code class="green">4:3</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Fractions can be used together with integers and floats in expressions.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
@@ -1281,10 +1287,10 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
</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 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 be evaluated at all.
|
||||
.Example</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 be evaluated at all.</p>
|
||||
</div>
|
||||
<div class="listingblock">
|
||||
<div class="title">Example</div>
|
||||
<div class="content">
|
||||
<pre class="rouge highlight"><code data-lang="go"><span class="m">2</span> <span class="o">></span> <span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="m">1</span><span class="p">)</span> <span class="n">or</span> <span class="p">(</span><span class="n">a</span><span class="o">=</span><span class="m">8</span><span class="p">)</span> <span class="o">></span> <span class="m">0</span><span class="p">;</span> <span class="n">a</span> <i class="conum" data-value="1"></i><b>(1)</b></code></pre>
|
||||
</div>
|
||||
@@ -1293,7 +1299,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<table>
|
||||
<tr>
|
||||
<td><i class="conum" data-value="1"></i><b>1</b></td>
|
||||
<td>This multi-expression returns <em>1</em> because in the first expression the left value of <code class="blue">or</code> is <em>true</em> and as a conseguence its right value is not computed. Therefore the <em>a</em> variable only receives the integer <em>1</em>.</td>
|
||||
<td>This multi-expression returns <em>1</em> because in the first expression the left value of <code class="blue">or</code> is <em>true</em> and, as a conseguence, its right value is not computed. Therefore the <em>a</em> variable only receives the integer <em>1</em>.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
@@ -1380,16 +1386,16 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<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>
|
||||
<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"><code class="blue">+></code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Front insertion</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Insert an item in front</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">0 >> [1,2]</code> → <em>[0,1,2]</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">0 +> [1,2]</code> → <em>[0,1,2]</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"><code class="blue"><+</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Back insertion</em></p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">Insert an item at end</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>
|
||||
@@ -1475,11 +1481,11 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Examples: Element insertion</div>
|
||||
<p><code>>>></code> <code class="blue">"first" >> list</code><br>
|
||||
<p><code>>>></code> <code class="blue">"first" +> list</code><br>
|
||||
<code class="green">["first", "one", "six", "three"]</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">list << "last"</code><br>
|
||||
<p><code>>>></code> <code class="blue">list <+ "last"</code><br>
|
||||
<code class="green">["first", "one", "six", "three", "last"]</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
@@ -1497,7 +1503,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<code class="green">[1, 2, 3, "one", "two", "three"]</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <code class="blue">[1,2,3,4] - [2,4]</code><br>
|
||||
<p><code>>>></code> <code class="blue">[1,2,3,2,4] - [2,4]</code><br>
|
||||
<code class="green">[1, 3]</code></p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1610,7 +1616,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
||||
<div class="content">
|
||||
<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>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>
|
||||
@@ -1812,7 +1818,7 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
|
||||
<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>, <code class="blue">?=</code>, and <code class="blue">?!</code></a></h3>
|
||||
<div class="paragraph">
|
||||
<p>The left operand of first two operators, <code class="blue">??</code> and <code class="blue">?=</code>, must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.</p>
|
||||
<p>The left operand of the first two operators, <code class="blue">??</code> and <code class="blue">?=</code>, must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.</p>
|
||||
</div>
|
||||
<div class="admonitionblock important">
|
||||
<table>
|
||||
@@ -1900,16 +1906,16 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<table class="tableblock frame-all grid-all stretch">
|
||||
<caption class="title">Table 8. Operators priorities</caption>
|
||||
<colgroup>
|
||||
<col style="width: 11.7647%;">
|
||||
<col style="width: 11.7647%;">
|
||||
<col style="width: 11.7647%;">
|
||||
<col style="width: 29.4117%;">
|
||||
<col style="width: 35.2942%;">
|
||||
<col style="width: 16.6666%;">
|
||||
<col style="width: 11.1111%;">
|
||||
<col style="width: 11.1111%;">
|
||||
<col style="width: 27.7777%;">
|
||||
<col style="width: 33.3335%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="tableblock halign-center valign-top">Priority</th>
|
||||
<th class="tableblock halign-center valign-top">Operators</th>
|
||||
<th class="tableblock halign-center valign-top">Operator</th>
|
||||
<th class="tableblock halign-center valign-top">Position</th>
|
||||
<th class="tableblock halign-center valign-top">Operation</th>
|
||||
<th class="tableblock halign-center valign-top">Operands and results</th>
|
||||
@@ -1930,7 +1936,13 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>dict</em> <code>[</code> <em>any</em> <code>]</code> → <em>any</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>INC</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top" rowspan="5"><p class="tableblock"><strong>INC/DEC</strong></p></td>
|
||||
<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>Postfix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Iterator next item</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterator</em> <code>++</code> → <em>any</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>Postfix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Post increment</em></p></td>
|
||||
@@ -1938,9 +1950,21 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
</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>Prefix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Pre increment</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code>++</code> <em>integer-variable</em> → <em>integer</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>Postfix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Next item</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterator</em> <code>++</code> → <em>any</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Post decrement</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>integer-variable</em> <code>--</code> → <em>integer</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>Prefix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Pre decrement</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code>--</code> <em>integer-variable</em> → <em>integer</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="3"><p class="tableblock"><strong>DEFAULT</strong></p></td>
|
||||
@@ -1988,6 +2012,19 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code>#</code> <em>iterator</em> → <em>integer</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>BIT SHIFT</strong></p></td>
|
||||
<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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Left-Shift</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>integer</em> <code><<</code> <em>integer</em> → <em>integer</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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Right-Shift</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>integer</em> <code>>></code> <em>iterator</em> → <em>integer</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>SELECT</strong></p></td>
|
||||
<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>Multi-Infix</em></p></td>
|
||||
@@ -2008,7 +2045,7 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>integer</em> <code>:</code> <em>integer</em> → <em>fraction</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="5"><p class="tableblock"><strong>PROD</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top" rowspan="7"><p class="tableblock"><strong>PROD</strong></p></td>
|
||||
<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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Product</em></p></td>
|
||||
@@ -2033,6 +2070,18 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>number</em> <code>./</code> <em>number</em> → <em>float</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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Split</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>string</em> <code>/</code> <em>string</em> → <em>list</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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Split</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>string</em> <code>/</code> integer → <em>list</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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Integer-remainder</em></p></td>
|
||||
@@ -2076,23 +2125,31 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>list</em> <code>-</code> <em>list</em> → <em>list</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="3"><p class="tableblock"><strong>BINARY</strong></p></td>
|
||||
<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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Binary And</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>number</em> <code>&</code> <em>number</em> → <em>number</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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Binary Or</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>number</em> <code>|</code> <em>number</em> → <em>number</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>BITWISE NOT</strong></p></td>
|
||||
<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>Prefix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Binary Not</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code>~</code> <em>number</em> → <em>number</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code>~</code> <em>integer</em> → <em>integer</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>BITWISE AND</strong></p></td>
|
||||
<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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Binary And</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>integer</em> <code>&</code> <em>integer</em> → <em>integer</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>BITWISE OR</strong></p></td>
|
||||
<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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Binary Or</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>integer</em> <code>|</code> <em>integer</em> → <em>integer</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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Binary Xor</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>integer</em> <code>^</code> <em>integer</em> → <em>integer</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="8"><p class="tableblock"><strong>RELATION</strong></p></td>
|
||||
@@ -2144,14 +2201,14 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>in</code> <em>dict</em> → <em>boolean</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>NOT</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>LOGIC NOT</strong></p></td>
|
||||
<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>Prefix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Not</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code>not</code> <em>boolean</em> → <em>boolean</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>AND</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>LOGIC AND</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">and</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>And</em></p></td>
|
||||
@@ -2164,7 +2221,7 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>boolean</em> <code>&&</code> <em>boolean</em> → <em>boolean</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>OR</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>LOGIC OR</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">or</code></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Or</em></p></td>
|
||||
@@ -2177,23 +2234,27 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>boolean</em> <code>||</code> <em>boolean</em> → <em>boolean</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="3"><p class="tableblock"><strong>ASSIGN</strong></p></td>
|
||||
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>INSERT</strong></p></td>
|
||||
<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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Prepend</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>+></code> <em>list</em> → <em>list</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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Append</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>list</em> <code><+</code> <em>any</em> → <em>list</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>ASSIGN</strong></p></td>
|
||||
<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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Assignment</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>identifier</em> <code>=</code> <em>any</em> → <em>any</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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Front-insert</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>>></code> <em>list</em> → <em>list</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>Infix</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Back-insert</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>list</em> <code><<</code> <em>any</em> → <em>list</em></p></td>
|
||||
<td class="tableblock halign-center valign-top" colspan="4"><p class="tableblock"><em>See also the table of special allocation operators below</em></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>BUT</strong></p></td>
|
||||
@@ -2211,6 +2272,89 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="tableblock frame-all grid-all stretch">
|
||||
<caption class="title">Table 9. Special assignment perators</caption>
|
||||
<colgroup>
|
||||
<col style="width: 14.2857%;">
|
||||
<col style="width: 14.2857%;">
|
||||
<col style="width: 28.5714%;">
|
||||
<col style="width: 42.8572%;">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="tableblock halign-center valign-top">Priority</th>
|
||||
<th class="tableblock halign-center valign-top">Operator</th>
|
||||
<th class="tableblock halign-center valign-top">Operation</th>
|
||||
<th class="tableblock halign-center valign-top">Equivalent operation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tableblock halign-center valign-top" rowspan="9"><p class="tableblock"><strong>ASSIGN</strong></p></td>
|
||||
<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 & Assign</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>var</em> <code>+=</code> <em>expr</em> <br>
|
||||
short for<br>
|
||||
<em>var</em> <code>=</code> <em>value-of-var</em> <code>+</code> <em>expr</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>Subtract & Assign</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>var</em> <code>-=</code> <em>expr</em> <br>
|
||||
short for<br>
|
||||
<em>var</em> <code>=</code> <em>value-of-var</em> <code>-</code> <em>expr</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>Multiply & Assign</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>var</em> <code>*=</code> <em>expr</em> <br>
|
||||
short for<br>
|
||||
<em>var</em> <code>=</code> <em>value-of-var</em> <code>*</code> <em>expr</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>Divide & Assign</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>var</em> <code>/=</code> <em>expr</em> <br>
|
||||
short for<br>
|
||||
<em>var</em> <code>=</code> <em>value-of-var</em> <code>/</code> <em>expr</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>Remainder & Assign</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>var</em> <code>%=</code> <em>expr</em> <br>
|
||||
short for<br>
|
||||
<em>var</em> <code>=</code> <em>value-of-var</em> <code>%</code> <em>expr</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>Bitwise and & Assign</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>var</em> <code>&=</code> <em>expr</em> <br>
|
||||
short for<br>
|
||||
<em>var</em> <code>=</code> <em>value-of-var</em> <code>&</code> <em>expr</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>Bitwise or & Assign</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>var</em> <code>|=</code> <em>expr</em> <br>
|
||||
short for<br>
|
||||
<em>var</em> <code>=</code> <em>value-of-var</em> <code>|</code> <em>expr</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>Left shift & Assign</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>var</em> <code><<=</code> <em>expr</em> <br>
|
||||
short for<br>
|
||||
<em>var</em> <code>=</code> <em>value-of-var</em> <code><<</code> <em>expr</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>Right shift & Assign</em></p></td>
|
||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>var</em> <code>>>=</code> <em>expr</em> <br>
|
||||
short for<br>
|
||||
<em>var</em> <code>=</code> <em>value-of-var</em> <code>>></code> <em>expr</em></p></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect1">
|
||||
@@ -2232,14 +2376,14 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<div class="sect2">
|
||||
<h3 id="_expr_function_definition"><a class="anchor" href="#_expr_function_definition"></a><a class="link" href="#_expr_function_definition">6.1. <em>Expr</em> function definition</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>A function is identified and referenced by its name. It can have zero or more parameter. <em>Expr</em> functions also support optional parameters and passing paramters by name.</p>
|
||||
<p>An expr-function is identified and referenced by its name. It can have zero or more parameter. expr-functions also support optional parameters and passing paramters by name.</p>
|
||||
</div>
|
||||
<div class="exampleblock">
|
||||
<div class="title">Example 14. Expr’s function definition syntax</div>
|
||||
<div class="content">
|
||||
<div class="paragraph">
|
||||
<p><strong><em>function-definition</em></strong> = <em>identifier</em> "<strong>=</strong>" "<strong>func(</strong>" [<em>formal-param-list</em>] "<strong>)</strong>" "<strong>{</strong>" <em>multi-expression</em> "<strong>}</strong>"<br>
|
||||
<em>formal-param_list</em> = <em>required-param-list</em> [ "<strong>,</strong>" <em>optional-param-list</em> ]<br>
|
||||
<em>formal-param-list</em> = <em>required-param-list</em> [ "<strong>,</strong>" <em>optional-param-list</em> ]<br>
|
||||
<em>required-param-list</em> = <em>identifier</em> { "<strong>,</strong>" <em>identifier</em> }<br>
|
||||
<em>optional-param-list</em> = <em>optional-parm</em> { "<strong>,</strong>" <em>optional-param</em> }<br>
|
||||
<em>optional-param</em> = <em>param-name</em> "<strong>=</strong>" <em>any-expr</em><br>
|
||||
@@ -2254,7 +2398,7 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<code class="green">sum(a, b):any{}</code></p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><sup>(*)</sup> Since the plus, *+*, operator is defined for multiple data-types, the <em>sum()</em> function can be used for any pair of that types.</p>
|
||||
<p><sup>(*)</sup> Since the plus, <strong>+</strong>, operator is defined for multiple data-types, the <em>sum()</em> function can be used for any pair of that types.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p><code>>>></code> <em class="gray">// A more complex example: recursive calculation of the n-th value of Fibonacci’s sequence</em><br>
|
||||
@@ -2282,7 +2426,7 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<div class="sect2">
|
||||
<h3 id="_golang_function_definition"><a class="anchor" href="#_golang_function_definition"></a><a class="link" href="#_golang_function_definition">6.2. <em>Golang</em> function definition</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>Description of how to define Golan functions and how to bind them to <em>Expr</em> are topics treated in another document that I’ll write, one day, maybe.</p>
|
||||
<p>Description of how to define Golang functions and how to bind them to <em>Expr</em> are topics covered in another document that I’ll write, one day, maybe.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sect2">
|
||||
@@ -2372,10 +2516,10 @@ These operators have a high priority, in particular higher than the operator <co
|
||||
<div class="sect2">
|
||||
<h3 id="_function_context"><a class="anchor" href="#_function_context"></a><a class="link" href="#_function_context">6.4. Function context</a></h3>
|
||||
<div class="paragraph">
|
||||
<p>Functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the <em>clone</em> modifier <code class="blue">@</code> it is possibile to export local definition to the calling context. The clone modifier must be used as prefix to variable names and it is part of the name. E.g. <code class="blue">@x</code> is not the same as <code class="blue">x</code>; they are two different and un related variables.</p>
|
||||
<p>Functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the <em>clone</em> modifier <code class="blue">@</code> it is possibile to export local definition to the calling context. The clone modifier must be used as prefix to variable names and it is part of the name. E.g. <code class="blue">@x</code> is not the same as <code class="blue">x</code>; they are two different and unrelated variables.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<p>Clone variables are normal local variables. The only diffence will appear when the defining function terminate, just before the destruction of its local context. At that point, all local clone variables are cloned in the calling context with the same names but the <code class="blue">@</code> symbol.</p>
|
||||
<p>Clone variables are normal local variables. The only diffence will appear when the defining function ends, just before the destruction of its local context. At that point, all local clone variables are cloned in the calling context with the same names but the <code class="blue">@</code> symbol.</p>
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example</div>
|
||||
@@ -2410,12 +2554,12 @@ The clone modifier <code class="blue">@</code> does not make a variable a refere
|
||||
</div>
|
||||
<div class="paragraph">
|
||||
<div class="title">Example</div>
|
||||
<p><code>>>></code> <code class="blue">g = func(@p) {2+@p}</code>
|
||||
g(@p):any{}`
|
||||
<code>>>></code> <code class="blue">g(9)</code>
|
||||
11`
|
||||
<code>>>></code> [blue]`p
|
||||
9</p>
|
||||
<p><code>>>></code> <code class="blue">g = func(@p) {2+@p}</code><br>
|
||||
<code class="green">g(@p):any{}</code><br>
|
||||
<code>>>></code> <code class="blue">g(9)</code><br>
|
||||
<code class="green">11</code><br>
|
||||
<code>>>></code> <code class="blue">p</code><br>
|
||||
<code class="green">9</code></p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -2461,7 +2605,7 @@ g(@p):any{}`
|
||||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2024-12-23 07:20:48 +0100
|
||||
Last updated 2026-04-15 18:14:50 +0200
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -183,6 +183,16 @@ func evalOpAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
v, err = divValues(opTerm, leftValue, rightValue)
|
||||
case SymPercEqual:
|
||||
v, err = remainderValues(opTerm, leftValue, rightValue)
|
||||
case SymAmpersandEqual:
|
||||
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
||||
case SymVertBarEqual:
|
||||
v, err = bitwiseOr(opTerm, leftValue, rightValue)
|
||||
case SymCaretEqual:
|
||||
v, err = bitwiseXor(opTerm, leftValue, rightValue)
|
||||
case SymDoubleLessEqual:
|
||||
v, err = bitLeftShift(opTerm, leftValue, rightValue)
|
||||
case SymDoubleGreaterEqual:
|
||||
v, err = bitRightShift(opTerm, leftValue, rightValue)
|
||||
default:
|
||||
err = opTerm.Errorf("unsupported assign operator %q", opTerm.source())
|
||||
}
|
||||
@@ -201,4 +211,10 @@ func init() {
|
||||
registerTermConstructor(SymMinusEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymStarEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymSlashEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymPercEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymDoubleLessEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymDoubleGreaterEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymAmpersandEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymVertBarEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymCaretEqual, newOpAssignTerm)
|
||||
}
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-binary.go
|
||||
package expr
|
||||
|
||||
//-------- NOT term
|
||||
|
||||
func newBinNotTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priBinary,
|
||||
evalFunc: evalBinaryNot,
|
||||
}
|
||||
}
|
||||
|
||||
func evalBinaryNot(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var value any
|
||||
|
||||
if value, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger(value) {
|
||||
i, _ := value.(int64)
|
||||
v = ^i
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(value)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//-------- Binary AND term
|
||||
|
||||
func newBinAndTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBinary,
|
||||
evalFunc: evalBinaryAnd,
|
||||
}
|
||||
}
|
||||
|
||||
func evalBinaryAnd(ctx ExprContext, self *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
var leftInt, rightInt int64
|
||||
var lok, rok bool
|
||||
|
||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftInt, lok = leftValue.(int64)
|
||||
rightInt, rok = rightValue.(int64)
|
||||
|
||||
if lok && rok {
|
||||
v = leftInt & rightInt
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//-------- Binary OR term
|
||||
|
||||
func newBinOrTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBinary,
|
||||
evalFunc: evalBinaryOr,
|
||||
}
|
||||
}
|
||||
|
||||
func evalBinaryOr(ctx ExprContext, self *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
var leftInt, rightInt int64
|
||||
var lok, rok bool
|
||||
|
||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftInt, lok = leftValue.(int64)
|
||||
rightInt, rok = rightValue.(int64)
|
||||
|
||||
if lok && rok {
|
||||
v = leftInt | rightInt
|
||||
} else {
|
||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymTilde, newBinNotTerm)
|
||||
registerTermConstructor(SymAmpersand, newBinAndTerm)
|
||||
registerTermConstructor(SymVertBar, newBinOrTerm)
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-bitwise.go
|
||||
package expr
|
||||
|
||||
//-------- Bitwise NOT term
|
||||
|
||||
func newBitwiseNotTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priBitwiseNot,
|
||||
evalFunc: evalBitwiseNot,
|
||||
}
|
||||
}
|
||||
|
||||
func evalBitwiseNot(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var value any
|
||||
|
||||
if value, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger(value) {
|
||||
i, _ := value.(int64)
|
||||
v = ^i
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(value)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//-------- Bitwise AND term
|
||||
|
||||
func newBitwiseAndTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBitwiseAnd,
|
||||
evalFunc: evalBitwiseAnd,
|
||||
}
|
||||
}
|
||||
|
||||
func bitwiseAnd(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
var leftInt, rightInt int64
|
||||
var lok, rok bool
|
||||
|
||||
leftInt, lok = leftValue.(int64)
|
||||
rightInt, rok = rightValue.(int64)
|
||||
|
||||
if lok && rok {
|
||||
v = leftInt & rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalBitwiseAnd(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
||||
return
|
||||
}
|
||||
|
||||
//-------- Bitwise OR term
|
||||
|
||||
func newBitwiseOrTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBitwiseOr,
|
||||
evalFunc: evalBitwiseOr,
|
||||
}
|
||||
}
|
||||
|
||||
func bitwiseOr(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
var leftInt, rightInt int64
|
||||
var lok, rok bool
|
||||
|
||||
leftInt, lok = leftValue.(int64)
|
||||
rightInt, rok = rightValue.(int64)
|
||||
|
||||
if lok && rok {
|
||||
v = leftInt | rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalBitwiseOr(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v, err = bitwiseOr(opTerm, leftValue, rightValue)
|
||||
return
|
||||
}
|
||||
|
||||
//-------- Bitwise XOR term
|
||||
|
||||
func newBitwiseXorTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBitwiseOr,
|
||||
evalFunc: evalBitwiseXor,
|
||||
}
|
||||
}
|
||||
|
||||
func bitwiseXor(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
var leftInt, rightInt int64
|
||||
var lok, rok bool
|
||||
|
||||
leftInt, lok = leftValue.(int64)
|
||||
rightInt, rok = rightValue.(int64)
|
||||
|
||||
if lok && rok {
|
||||
v = leftInt ^ rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalBitwiseXor(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v, err = bitwiseXor(opTerm, leftValue, rightValue)
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymTilde, newBitwiseNotTerm)
|
||||
registerTermConstructor(SymAmpersand, newBitwiseAndTerm)
|
||||
registerTermConstructor(SymVertBar, newBitwiseOrTerm)
|
||||
registerTermConstructor(SymCaret, newBitwiseXorTerm)
|
||||
}
|
||||
@@ -7,7 +7,6 @@ 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"
|
||||
)
|
||||
|
||||
@@ -41,7 +40,7 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
if den == 0 {
|
||||
err = errors.New("division by zero")
|
||||
err = opTerm.errDivisionByZero()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+8
-16
@@ -4,15 +4,15 @@
|
||||
// operator-insert.go
|
||||
package expr
|
||||
|
||||
//-------- insert term
|
||||
//-------- prepend term
|
||||
|
||||
func newInsertTerm(tk *Token) (inst *term) {
|
||||
func newPrependTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priAssign,
|
||||
evalFunc: evalInsert,
|
||||
priority: priInsert,
|
||||
evalFunc: evalPrepend,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@ func newAppendTerm(tk *Token) (inst *term) {
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priAssign,
|
||||
priority: priInsert,
|
||||
evalFunc: evalAppend,
|
||||
}
|
||||
}
|
||||
|
||||
func evalInsert(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalPrepend(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
@@ -40,10 +40,6 @@ func evalInsert(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
if opTerm.children[1].symbol() == SymVariable {
|
||||
ctx.UnsafeSetVar(opTerm.children[1].source(), v)
|
||||
}
|
||||
} else if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
leftInt := leftValue.(int64)
|
||||
rightInt := rightValue.(int64)
|
||||
v = leftInt >> rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
@@ -64,10 +60,6 @@ func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
if opTerm.children[0].symbol() == SymVariable {
|
||||
ctx.UnsafeSetVar(opTerm.children[0].source(), v)
|
||||
}
|
||||
} else if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
leftInt := leftValue.(int64)
|
||||
rightInt := rightValue.(int64)
|
||||
v = leftInt << rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
@@ -94,6 +86,6 @@ func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymInsert, newInsertTerm)
|
||||
registerTermConstructor(SymAppend, newAppendTerm)
|
||||
registerTermConstructor(SymPlusGreater, newPrependTerm)
|
||||
registerTermConstructor(SymLessPlus, newAppendTerm)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ func newIterValueTerm(tk *Token) (inst *term) {
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priIterValue,
|
||||
priority: priDereference,
|
||||
evalFunc: evalIterValue,
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,6 @@ func evalIterValue(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
||||
registerTermConstructor(SymCaret, newIterValueTerm)
|
||||
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
||||
registerTermConstructor(SymDereference, newIterValueTerm)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-post-inc.go
|
||||
// operator-post-inc-dec.go
|
||||
package expr
|
||||
|
||||
// -------- post increment term
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-pre-inc-dec.go
|
||||
package expr
|
||||
|
||||
// -------- pre increment term
|
||||
|
||||
func newPreIncTerm(tk *Token) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priIncDec,
|
||||
evalFunc: evalPreInc,
|
||||
}
|
||||
}
|
||||
|
||||
func evalPreInc(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var childValue any
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
|
||||
i := childValue.(int64) + 1
|
||||
ctx.SetVar(opTerm.children[0].source(), i)
|
||||
v = i
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(childValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// -------- pre decrement term
|
||||
|
||||
func newPreDecTerm(tk *Token) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priIncDec,
|
||||
evalFunc: evalPreDec,
|
||||
}
|
||||
}
|
||||
|
||||
func evalPreDec(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var childValue any
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
|
||||
i := childValue.(int64) - 1
|
||||
ctx.SetVar(opTerm.children[0].source(), i)
|
||||
v = i
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(childValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymPreInc, newPreIncTerm)
|
||||
registerTermConstructor(SymPreDec, newPreDecTerm)
|
||||
}
|
||||
+29
-5
@@ -5,7 +5,6 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -69,7 +68,7 @@ func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||
d := numAsFloat(rightValue)
|
||||
if d == 0.0 {
|
||||
err = errors.New("division by zero")
|
||||
err = opTerm.errDivisionByZero()
|
||||
} else {
|
||||
v = numAsFloat(leftValue) / d
|
||||
}
|
||||
@@ -78,11 +77,36 @@ func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
} else {
|
||||
leftInt, _ := leftValue.(int64)
|
||||
if rightInt, _ := rightValue.(int64); rightInt == 0 {
|
||||
err = errors.New("division by zero")
|
||||
err = opTerm.errDivisionByZero()
|
||||
} else {
|
||||
v = leftInt / rightInt
|
||||
}
|
||||
}
|
||||
} else if IsString(leftValue) && IsString(rightValue) {
|
||||
source := leftValue.(string)
|
||||
sep := rightValue.(string)
|
||||
v = ListFromStrings(strings.Split(source, sep))
|
||||
} else if IsString(leftValue) && IsInteger(rightValue) {
|
||||
source := leftValue.(string)
|
||||
partSize := int(rightValue.(int64))
|
||||
if partSize == 0 {
|
||||
err = opTerm.errDivisionByZero()
|
||||
} else {
|
||||
partCount := len(source) / partSize
|
||||
remainder := len(source) % partSize
|
||||
listSize := partCount
|
||||
if remainder > 0 {
|
||||
listSize++
|
||||
}
|
||||
parts := make([]any, 0, listSize)
|
||||
for i := 0; i < partCount; i++ {
|
||||
parts = append(parts, source[i*partSize:(i+1)*partSize])
|
||||
}
|
||||
if remainder > 0 {
|
||||
parts = append(parts, source[len(source)-remainder:])
|
||||
}
|
||||
v = newList(parts)
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
@@ -121,7 +145,7 @@ func evalDivideAsFloat(ctx ExprContext, floatDivTerm *term) (v any, err error) {
|
||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||
d := numAsFloat(rightValue)
|
||||
if d == 0.0 {
|
||||
err = errors.New("division by zero")
|
||||
err = floatDivTerm.errDivisionByZero()
|
||||
} else {
|
||||
v = numAsFloat(leftValue) / d
|
||||
}
|
||||
@@ -146,7 +170,7 @@ func remainderValues(opTerm *term, leftValue, rightValue any) (v any, err error)
|
||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
rightInt, _ := rightValue.(int64)
|
||||
if rightInt == 0 {
|
||||
err = errors.New("division by zero")
|
||||
err = opTerm.errDivisionByZero()
|
||||
} else {
|
||||
leftInt, _ := leftValue.(int64)
|
||||
v = leftInt % rightInt
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-shift.go
|
||||
package expr
|
||||
|
||||
//-------- bit right shift term
|
||||
|
||||
func newRightShiftTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBinShift,
|
||||
evalFunc: evalRightShift,
|
||||
}
|
||||
}
|
||||
|
||||
func bitRightShift(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
leftInt := leftValue.(int64)
|
||||
rightInt := rightValue.(int64)
|
||||
v = leftInt >> rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalRightShift(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v, err = bitRightShift(opTerm, leftValue, rightValue)
|
||||
return
|
||||
}
|
||||
|
||||
func newLeftShiftTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBinShift,
|
||||
evalFunc: evalLeftShift,
|
||||
}
|
||||
}
|
||||
|
||||
func bitLeftShift(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
leftInt := leftValue.(int64)
|
||||
rightInt := rightValue.(int64)
|
||||
v = leftInt << rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalLeftShift(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v, err = bitLeftShift(opTerm, leftValue, rightValue)
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymDoubleGreater, newRightShiftTerm)
|
||||
registerTermConstructor(SymDoubleLess, newLeftShiftTerm)
|
||||
}
|
||||
@@ -409,6 +409,23 @@ func listSubTree(tree *ast, listTerm *term, allowIndeces bool) (root *term, err
|
||||
return
|
||||
}
|
||||
|
||||
func changePrefix(tk *Token) {
|
||||
switch tk.Sym {
|
||||
case SymMinus:
|
||||
tk.SetSymbol(SymChangeSign)
|
||||
case SymPlus:
|
||||
tk.SetSymbol(SymUnchangeSign)
|
||||
case SymStar:
|
||||
tk.SetSymbol(SymDereference)
|
||||
case SymExclamation:
|
||||
tk.SetSymbol(SymNot)
|
||||
case SymDoublePlus:
|
||||
tk.SetSymbol(SymPreInc)
|
||||
case SymDoubleMinus:
|
||||
tk.SetSymbol(SymPreDec)
|
||||
}
|
||||
}
|
||||
|
||||
func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
|
||||
var selectorTerm *term = nil
|
||||
var currentTerm *term = nil
|
||||
@@ -437,14 +454,16 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
|
||||
|
||||
//fmt.Println("Token:", tk)
|
||||
if firstToken {
|
||||
if tk.Sym == SymMinus {
|
||||
tk.Sym = SymChangeSign
|
||||
} else if tk.Sym == SymPlus {
|
||||
tk.Sym = SymUnchangeSign
|
||||
} else if tk.IsSymbol(SymExclamation) {
|
||||
err = tk.Errorf("postfix opertor %q requires an operand on its left", tk)
|
||||
break
|
||||
}
|
||||
changePrefix(tk)
|
||||
// if tk.Sym == SymMinus {
|
||||
// tk.Sym = SymChangeSign
|
||||
// } else if tk.Sym == SymPlus {
|
||||
// tk.Sym = SymUnchangeSign
|
||||
// } else if tk.IsSymbol(SymStar) {
|
||||
// tk.SetSymbol(SymDereference)
|
||||
// } else if tk.IsSymbol(SymExclamation) {
|
||||
// tk.SetSymbol(SymNot)
|
||||
// }
|
||||
firstToken = false
|
||||
}
|
||||
|
||||
@@ -452,9 +471,12 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
|
||||
case SymOpenRound:
|
||||
var subTree *ast
|
||||
if subTree, err = parser.parseGeneral(scanner, ctx, SymClosedRound); err == nil {
|
||||
subTree.root.priority = priValue
|
||||
err = tree.addTerm(newExprTerm(subTree.root))
|
||||
currentTerm = subTree.root
|
||||
exprTerm := newExprTerm(subTree.root)
|
||||
err = tree.addTerm(exprTerm)
|
||||
currentTerm = exprTerm
|
||||
// subTree.root.priority = priValue
|
||||
// err = tree.addTerm(newExprTerm(subTree.root))
|
||||
// currentTerm = subTree.root
|
||||
}
|
||||
case SymFuncCall:
|
||||
var funcCallTerm *term
|
||||
@@ -478,7 +500,7 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
|
||||
currentTerm = mapTerm
|
||||
}
|
||||
}
|
||||
case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual:
|
||||
case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual, SymAmpersandEqual, SymVertBarEqual, SymDoubleLessEqual, SymDoubleGreaterEqual, SymCaretEqual:
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
firstToken = true
|
||||
case SymFuncDef:
|
||||
@@ -515,15 +537,12 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if hasFlag(ctx, allowIndex) {
|
||||
// tk.Sym = SymRange
|
||||
// }
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
}
|
||||
if tk.IsOneOfA(SymColon, SymRange) {
|
||||
// Colon outside a selector term acts like a separator
|
||||
firstToken = true
|
||||
}
|
||||
}
|
||||
default:
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
}
|
||||
|
||||
+40
-3
@@ -124,6 +124,8 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
tk = scanner.moveOn(SymDoublePlus, ch, next)
|
||||
} else if next == '=' {
|
||||
tk = scanner.moveOn(SymPlusEqual, ch, next)
|
||||
} else if next == '>' {
|
||||
tk = scanner.moveOn(SymPlusGreater, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymPlus, ch)
|
||||
}
|
||||
@@ -167,13 +169,19 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
case '|':
|
||||
if next, _ := scanner.peek(); next == '|' {
|
||||
tk = scanner.moveOn(SymDoubleVertBar, ch, next)
|
||||
} else if next, _ = scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymVertBarEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymVertBar, ch)
|
||||
}
|
||||
case ',':
|
||||
tk = scanner.makeToken(SymComma, ch)
|
||||
case '^':
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymCaretEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymCaret, ch)
|
||||
}
|
||||
case ':':
|
||||
if next, _ := scanner.peek(); next == ':' {
|
||||
tk = scanner.moveOn(SymDoubleColon, ch, next)
|
||||
@@ -232,11 +240,17 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
case '&':
|
||||
if next, _ := scanner.peek(); next == '&' {
|
||||
tk = scanner.moveOn(SymDoubleAmpersand, ch, next)
|
||||
} else if next, _ = scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymAmpersandEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymAmpersand, ch)
|
||||
}
|
||||
case '%':
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymPercEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymPercent, ch)
|
||||
}
|
||||
case '#':
|
||||
tk = scanner.makeToken(SymHash, ch)
|
||||
case '@':
|
||||
@@ -265,9 +279,18 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymLessOrEqual, ch, next)
|
||||
} else if next == '<' {
|
||||
tk = scanner.moveOn(SymAppend, ch, next)
|
||||
scanner.readChar()
|
||||
next2, _ := scanner.readChar()
|
||||
scanner.unreadChar()
|
||||
if next2 == '=' {
|
||||
tk = scanner.moveOn(SymDoubleLessEqual, ch, next, next2)
|
||||
} else {
|
||||
tk = scanner.accept(SymDoubleLess, ch, next)
|
||||
}
|
||||
} else if next == '>' {
|
||||
tk = scanner.moveOn(SymLessGreater, ch, next)
|
||||
} else if next == '+' {
|
||||
tk = scanner.moveOn(SymLessPlus, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymLess, ch)
|
||||
}
|
||||
@@ -275,7 +298,14 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymGreaterOrEqual, ch, next)
|
||||
} else if next == '>' {
|
||||
tk = scanner.moveOn(SymInsert, ch, next)
|
||||
scanner.readChar()
|
||||
next2, _ := scanner.readChar()
|
||||
scanner.unreadChar()
|
||||
if next2 == '=' {
|
||||
tk = scanner.moveOn(SymDoubleGreaterEqual, ch, next, next2)
|
||||
} else {
|
||||
tk = scanner.accept(SymDoubleGreater, ch, next)
|
||||
}
|
||||
} else {
|
||||
tk = scanner.makeToken(SymGreater, ch)
|
||||
}
|
||||
@@ -630,9 +660,16 @@ func (scanner *scanner) translate(sym Symbol) Symbol {
|
||||
|
||||
func (scanner *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
|
||||
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
|
||||
for i := 1; i < len(chars); i++ {
|
||||
// for i := 1; i < len(chars); i++ {
|
||||
if len(chars) > 1 {
|
||||
scanner.readChar()
|
||||
}
|
||||
// }
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) accept(sym Symbol, chars ...byte) (tk *Token) {
|
||||
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,11 @@ func NewSimpleStore() *SimpleStore {
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (ss *SimpleStore) Init() {
|
||||
ss.varStore = make(map[string]any)
|
||||
ss.funcStore = make(map[string]ExprFunc)
|
||||
}
|
||||
|
||||
func filterRefName(name string) bool { return name[0] != '@' }
|
||||
//func filterPrivName(name string) bool { return name[0] != '_' }
|
||||
|
||||
|
||||
+96
-79
@@ -14,7 +14,7 @@ type symbolClass int16
|
||||
|
||||
const (
|
||||
symClassOperator symbolClass = iota
|
||||
symClassPostOp
|
||||
symClassCommand
|
||||
symClassIdentifier
|
||||
symClassDelimiter
|
||||
symClassParenthesis
|
||||
@@ -26,75 +26,85 @@ const (
|
||||
type symbolSpec struct {
|
||||
repr string
|
||||
kind symbolClass
|
||||
opType termPosition
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
symbolMap = map[Symbol]symbolSpec{
|
||||
SymUnknown: {"<unknown>", symClassOther}, // -1: Unknown symbol
|
||||
SymNone: {"<null>", symClassOther}, // 0: Null value for variable of type symbol
|
||||
SymError: {"<error>", symClassOther}, // 1: Error reading from stream
|
||||
SymEos: {"<eos>", symClassOther}, // 2: End of stream
|
||||
SymMinus: {"-", symClassOperator}, // 3: '-'
|
||||
SymMinusEqual: {"-=", symClassOperator}, // 4: '-='
|
||||
SymDoubleMinus: {"--", symClassOperator}, // 5: '--'
|
||||
SymPlus: {"+", symClassOperator}, // 6: '+'
|
||||
SymPlusEqual: {"+=", symClassOperator}, // 7: '+='
|
||||
SymDoublePlus: {"++", symClassOperator}, // 8: '++'
|
||||
SymStar: {"*", symClassOperator}, // 9: '*'
|
||||
SymDoubleStar: {"**", symClassOperator}, // 10: '**'
|
||||
SymSlash: {"/", symClassOperator}, // 11: '/'
|
||||
SymBackSlash: {"\\", symClassOperator}, // 12: '\'
|
||||
SymVertBar: {"|", symClassOperator}, // 13: '|'
|
||||
SymDoubleVertBar: {"||", symClassOperator}, // 14: '||'
|
||||
SymComma: {",", symClassOperator}, // 15: ','
|
||||
SymColon: {":", symClassOperator}, // 16: ':'
|
||||
SymSemiColon: {";", symClassOperator}, // 17: ';'
|
||||
SymDot: {".", symClassOperator}, // 18: '.'
|
||||
SymDotSlash: {"./", symClassOperator}, // 19: './'
|
||||
SymQuote: {"'", symClassDelimiter}, // 20: '\''
|
||||
SymDoubleQuote: {"\"", symClassDelimiter}, // 21: '"'
|
||||
SymBackTick: {"`", symClassOperator}, // 22: '`'
|
||||
SymExclamation: {"!", symClassPostOp}, // 23: '!'
|
||||
SymQuestion: {"?", symClassOperator}, // 24: '?'
|
||||
SymAmpersand: {"&", symClassOperator}, // 25: '&'
|
||||
SymDoubleAmpersand: {"&&", symClassOperator}, // 26: '&&'
|
||||
SymPercent: {"%", symClassOperator}, // 27: '%'
|
||||
SymAt: {"@", symClassOperator}, // 28: '@'
|
||||
SymUndescore: {"_", symClassOperator}, // 29: '_'
|
||||
SymEqual: {"=", symClassOperator}, // 30: '='
|
||||
SymDoubleEqual: {"==", symClassOperator}, // 31: '=='
|
||||
SymLess: {"<", symClassOperator}, // 32: '<'
|
||||
SymLessOrEqual: {"<=", symClassOperator}, // 33: '<='
|
||||
SymGreater: {">", symClassOperator}, // 34: '>'
|
||||
SymGreaterOrEqual: {">=", symClassOperator}, // 35: '>='
|
||||
SymLessGreater: {"<>", symClassOperator}, // 36: '<>'
|
||||
SymNotEqual: {"!=", symClassOperator}, // 37: '!='
|
||||
SymDollar: {"$", symClassOperator}, // 38: '$'
|
||||
SymHash: {"#", symClassOperator}, // 39: '#'
|
||||
SymOpenRound: {"(", symClassParenthesis}, // 40: '('
|
||||
SymClosedRound: {")", symClassParenthesis}, // 41: ')'
|
||||
SymOpenSquare: {"[", symClassParenthesis}, // 42: '['
|
||||
SymClosedSquare: {"]", symClassParenthesis}, // 43: ']'
|
||||
SymOpenBrace: {"{", symClassParenthesis}, // 44: '{'
|
||||
SymClosedBrace: {"}", symClassParenthesis}, // 45: '}'
|
||||
SymTilde: {"~", symClassOperator}, // 46: '~'
|
||||
SymDoubleQuestion: {"??", symClassOperator}, // 47: '??'
|
||||
SymQuestionEqual: {"?=", symClassOperator}, // 48: '?='
|
||||
SymQuestionExclam: {"?!", symClassOperator}, // 49: '?!'
|
||||
SymDoubleAt: {"@@", symClassOperator}, // 50: '@@'
|
||||
SymDoubleColon: {"::", symClassOperator}, // 51: '::'
|
||||
SymInsert: {">>", symClassOperator}, // 52: '>>'
|
||||
SymAppend: {"<<", symClassOperator}, // 53: '<<'
|
||||
SymCaret: {"^", symClassOperator}, // 54: '^'
|
||||
SymDollarRound: {"$(", symClassOperator}, // 55: '$('
|
||||
SymOpenClosedRound: {"()", symClassPostOp}, // 56: '()'
|
||||
SymDoubleDollar: {"$$", symClassOperator}, // 57: '$$'
|
||||
SymDoubleDot: {"..", symClassOperator}, // 58: '..'
|
||||
SymTripleDot: {"...", symClassOperator}, // 59: '...'
|
||||
SymStarEqual: {"*=", symClassOperator}, // 60: '*='
|
||||
SymSlashEqual: {"/=", symClassOperator}, // 61: '/='
|
||||
SymPercEqual: {"%=", symClassOperator}, // 62: '%='
|
||||
SymUnknown: {"<unknown>", symClassOther, posLeaf}, // -1: Unknown symbol
|
||||
SymNone: {"<null>", symClassOther, posLeaf}, // 0: Null value for variable of type symbol
|
||||
SymError: {"<error>", symClassOther, posLeaf}, // 1: Error reading from stream
|
||||
SymEos: {"<eos>", symClassOther, posLeaf}, // 2: End of stream
|
||||
SymMinus: {"-", symClassOperator, posInfix}, // 3: '-'
|
||||
SymMinusEqual: {"-=", symClassOperator, posInfix}, // 4: '-='
|
||||
SymDoubleMinus: {"--", symClassOperator, posPostfix}, // 5: '--'
|
||||
SymPlus: {"+", symClassOperator, posInfix}, // 6: '+'
|
||||
SymPlusEqual: {"+=", symClassOperator, posInfix}, // 7: '+='
|
||||
SymDoublePlus: {"++", symClassOperator, posPostfix}, // 8: '++'
|
||||
SymStar: {"*", symClassOperator, posInfix}, // 9: '*'
|
||||
SymDoubleStar: {"**", symClassOperator, posInfix}, // 10: '**'
|
||||
SymSlash: {"/", symClassOperator, posInfix}, // 11: '/'
|
||||
SymBackSlash: {"\\", symClassOperator, posLeaf}, // 12: '\'
|
||||
SymVertBar: {"|", symClassOperator, posInfix}, // 13: '|'
|
||||
SymDoubleVertBar: {"||", symClassOperator, posInfix}, // 14: '||'
|
||||
SymComma: {",", symClassOperator, posInfix}, // 15: ','
|
||||
SymColon: {":", symClassOperator, posInfix}, // 16: ':'
|
||||
SymSemiColon: {";", symClassOperator, posInfix}, // 17: ';'
|
||||
SymDot: {".", symClassOperator, posInfix}, // 18: '.'
|
||||
SymDotSlash: {"./", symClassOperator, posInfix}, // 19: './'
|
||||
SymQuote: {"'", symClassDelimiter, posLeaf}, // 20: '\''
|
||||
SymDoubleQuote: {"\"", symClassDelimiter, posLeaf}, // 21: '"'
|
||||
SymBackTick: {"`", symClassDelimiter, posLeaf}, // 22: '`'
|
||||
SymExclamation: {"!", symClassOperator, posPostfix}, // 23: '!'
|
||||
SymQuestion: {"?", symClassOperator, posInfix}, // 24: '?'
|
||||
SymAmpersand: {"&", symClassOperator, posInfix}, // 25: '&'
|
||||
SymDoubleAmpersand: {"&&", symClassOperator, posInfix}, // 26: '&&'
|
||||
SymPercent: {"%", symClassOperator, posInfix}, // 27: '%'
|
||||
SymAt: {"@", symClassOperator, posPrefix}, // 28: '@'
|
||||
SymUndescore: {"_", symClassIdentifier, posLeaf}, // 29: '_'
|
||||
SymEqual: {"=", symClassOperator, posInfix}, // 30: '='
|
||||
SymDoubleEqual: {"==", symClassOperator, posInfix}, // 31: '=='
|
||||
SymLess: {"<", symClassOperator, posInfix}, // 32: '<'
|
||||
SymLessOrEqual: {"<=", symClassOperator, posInfix}, // 33: '<='
|
||||
SymGreater: {">", symClassOperator, posInfix}, // 34: '>'
|
||||
SymGreaterOrEqual: {">=", symClassOperator, posInfix}, // 35: '>='
|
||||
SymLessGreater: {"<>", symClassOperator, posInfix}, // 36: '<>'
|
||||
SymNotEqual: {"!=", symClassOperator, posInfix}, // 37: '!='
|
||||
SymDollar: {"$", symClassOperator, posPrefix}, // 38: '$'
|
||||
SymHash: {"#", symClassOperator, posPrefix}, // 39: '#'
|
||||
SymOpenRound: {"(", symClassParenthesis, posPrefix}, // 40: '('
|
||||
SymClosedRound: {")", symClassParenthesis, posPostfix}, // 41: ')'
|
||||
SymOpenSquare: {"[", symClassParenthesis, posPrefix}, // 42: '['
|
||||
SymClosedSquare: {"]", symClassParenthesis, posPostfix}, // 43: ']'
|
||||
SymOpenBrace: {"{", symClassParenthesis, posPrefix}, // 44: '{'
|
||||
SymClosedBrace: {"}", symClassParenthesis, posPostfix}, // 45: '}'
|
||||
SymTilde: {"~", symClassOperator, posPrefix}, // 46: '~'
|
||||
SymDoubleQuestion: {"??", symClassOperator, posInfix}, // 47: '??'
|
||||
SymQuestionEqual: {"?=", symClassOperator, posInfix}, // 48: '?='
|
||||
SymQuestionExclam: {"?!", symClassOperator, posInfix}, // 49: '?!'
|
||||
SymDoubleAt: {"@@", symClassCommand, posLeaf}, // 50: '@@'
|
||||
SymDoubleColon: {"::", symClassOperator, posInfix}, // 51: '::'
|
||||
SymDoubleGreater: {">>", symClassOperator, posInfix}, // 52: '>>'
|
||||
SymDoubleLess: {"<<", symClassOperator, posInfix}, // 53: '<<'
|
||||
SymCaret: {"^", symClassOperator, posInfix}, // 54: '^'
|
||||
SymDollarRound: {"$(", symClassOperator, posPrefix}, // 55: '$('
|
||||
SymOpenClosedRound: {"()", symClassOperator, posPostfix}, // 56: '()'
|
||||
SymDoubleDollar: {"$$", symClassCommand, posLeaf}, // 57: '$$'
|
||||
SymDoubleDot: {"..", symClassOperator, posInfix}, // 58: '..'
|
||||
SymTripleDot: {"...", symClassOperator, posPostfix}, // 59: '...'
|
||||
SymStarEqual: {"*=", symClassOperator, posInfix}, // 60: '*='
|
||||
SymSlashEqual: {"/=", symClassOperator, posInfix}, // 61: '/='
|
||||
SymPercEqual: {"%=", symClassOperator, posInfix}, // 62: '%='
|
||||
SymDoubleLessEqual: {"<<=", symClassOperator, posInfix}, // 63: '<<='
|
||||
SymDoubleGreaterEqual: {">>=", symClassOperator, posInfix}, // 64: '>>='
|
||||
SymAmpersandEqual: {"&=", symClassOperator, posInfix}, // 65: '&='
|
||||
SymVertBarEqual: {"|=", symClassOperator, posInfix}, // 65: '|='
|
||||
SymCaretEqual: {"^=", symClassOperator, posInfix}, // 66: '^='
|
||||
SymPlusGreater: {"+>", symClassOperator, posInfix}, // 67: '+>'
|
||||
SymLessPlus: {"<+", symClassOperator, posInfix}, // 68: '<+'
|
||||
SymPreInc: {"++", symClassOperator, posPrefix}, // : '++'
|
||||
SymPreDec: {"--", symClassOperator, posPrefix}, // : '--'
|
||||
// SymChangeSign
|
||||
// SymUnchangeSign
|
||||
// SymIdentifier
|
||||
@@ -121,17 +131,17 @@ func init() {
|
||||
// // SymClosedComment // 0: '*/'
|
||||
// // SymOneLineComment // 0: '//'
|
||||
// keywordBase
|
||||
SymKwAnd: {"and", symClassOperator},
|
||||
SymKwNot: {"not", symClassOperator},
|
||||
SymKwOr: {"or", symClassOperator},
|
||||
SymKwBut: {"but", symClassOperator},
|
||||
SymKwFunc: {"func(", symClassDeclaration},
|
||||
SymKwBuiltin: {"builtin", symClassOperator},
|
||||
SymKwPlugin: {"plugin", symClassOperator},
|
||||
SymKwIn: {"in", symClassOperator},
|
||||
SymKwInclude: {"include", symClassOperator},
|
||||
SymKwNil: {"nil", symClassValue},
|
||||
SymKwUnset: {"unset", symClassOperator},
|
||||
SymKwAnd: {"and", symClassOperator, posInfix},
|
||||
SymKwNot: {"not", symClassOperator, posInfix},
|
||||
SymKwOr: {"or", symClassOperator, posInfix},
|
||||
SymKwBut: {"but", symClassOperator, posInfix},
|
||||
SymKwFunc: {"func(", symClassDeclaration, posPrefix},
|
||||
SymKwBuiltin: {"builtin", symClassOperator, posPrefix},
|
||||
SymKwPlugin: {"plugin", symClassOperator, posPrefix},
|
||||
SymKwIn: {"in", symClassOperator, posInfix},
|
||||
SymKwInclude: {"include", symClassOperator, posPrefix},
|
||||
SymKwNil: {"nil", symClassValue, posLeaf},
|
||||
SymKwUnset: {"unset", symClassOperator, posPrefix},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,12 +179,19 @@ func StringEndsWithOperator(s string) bool {
|
||||
}
|
||||
|
||||
func endingOperator(s string) (sym Symbol) {
|
||||
var matchLength = 0
|
||||
sym = SymNone
|
||||
lower := strings.ToLower(s)
|
||||
lower := strings.TrimRight(strings.ToLower(s), " \t")
|
||||
for symbol, spec := range symbolMap {
|
||||
if spec.kind == symClassOperator && strings.HasSuffix(lower, spec.repr) {
|
||||
if strings.HasSuffix(lower, spec.repr) {
|
||||
if len(spec.repr) > matchLength {
|
||||
matchLength = len(spec.repr)
|
||||
if spec.kind == symClassOperator && (spec.opType == posInfix || spec.opType == posPrefix) {
|
||||
sym = symbol
|
||||
break
|
||||
} else {
|
||||
sym = SymNone
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@@ -60,8 +60,8 @@ const (
|
||||
SymQuestionExclam // 49: '?!'
|
||||
SymDoubleAt // 50: '@@'
|
||||
SymDoubleColon // 51: '::'
|
||||
SymInsert // 52: '>>'
|
||||
SymAppend // 53: '<<'
|
||||
SymDoubleGreater // 52: '>>'
|
||||
SymDoubleLess // 53: '<<'
|
||||
SymCaret // 54: '^'
|
||||
SymDollarRound // 55: '$('
|
||||
SymOpenClosedRound // 56: '()'
|
||||
@@ -71,8 +71,18 @@ const (
|
||||
SymStarEqual // 60: '*='
|
||||
SymSlashEqual // 61: '/='
|
||||
SymPercEqual // 62: '%='
|
||||
SymDoubleLessEqual // 63: '<<='
|
||||
SymDoubleGreaterEqual // 64: '>>='
|
||||
SymAmpersandEqual // 65: '&='
|
||||
SymVertBarEqual // 65: '|='
|
||||
SymCaretEqual // 66: '^='
|
||||
SymPlusGreater // 67: '+>'
|
||||
SymLessPlus // 68: '<+'
|
||||
SymChangeSign
|
||||
SymUnchangeSign
|
||||
SymDereference
|
||||
SymPreInc
|
||||
SymPreDec
|
||||
SymIdentifier
|
||||
SymBool
|
||||
SymInteger
|
||||
|
||||
+2
-1
@@ -26,12 +26,13 @@ func TestBool(t *testing.T) {
|
||||
/* 12 */ {`true or false`, true, nil},
|
||||
/* 13 */ {`true or []`, true, nil},
|
||||
/* 14 */ {`[] or false`, nil, errors.New(`got list as left operand type of 'OR' operator, it must be bool`)},
|
||||
/* 15 */ {`!true`, false, nil},
|
||||
/* 13 */ //{`true or []`, nil, errors.New(`[1:8] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "OR"`)},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 1)
|
||||
// runTestSuiteSpec(t, section, inputs, 15)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ func TestFuncString(t *testing.T) {
|
||||
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil},
|
||||
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, `strJoin(): expected string, got integer (1)`},
|
||||
/* 19 */ {`builtin "string"; strJoin()`, nil, `strJoin(): too few params -- expected 1 or more, got 0`},
|
||||
/* 20 */ {`builtin "string"; strUpper("StOp")`, "STOP", nil},
|
||||
/* 21 */ {`builtin "string"; strLower("StOp")`, "stop", nil},
|
||||
|
||||
/* 69 */ /*{`builtin "string"; $$global`, `vars: {
|
||||
}
|
||||
|
||||
+4
-2
@@ -29,7 +29,9 @@ func TestExpr(t *testing.T) {
|
||||
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
|
||||
/* 16 */ {`v=[2]; a=1; v[a-=1]=5; v[0]`, int64(5), nil},
|
||||
/* 17 */ {`true ? {"a"} :: {"b"}`, "a", nil},
|
||||
/* 18 */ {`
|
||||
/* 18 */ {`$$`, NewDict(map[any]any{"variables": NewDict(nil), "functions": NewDict(nil)}), nil},
|
||||
///* 19 */ {`$$global`, NewDict(map[any]any{"variables": NewDict(nil), "functions": NewDict(nil)}), nil},
|
||||
/* 19 */ {`
|
||||
ds={
|
||||
"init":func(@end){@current=0 but true},
|
||||
//"current":func(){current},
|
||||
@@ -44,6 +46,6 @@ func TestExpr(t *testing.T) {
|
||||
}
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 6, 7, 8, 9)
|
||||
// runTestSuiteSpec(t, section, inputs, 18)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ func TestFractionsParser(t *testing.T) {
|
||||
/* 12 */ {`builtin "math.arith"; add(1:2, 1.0, 2)`, float64(3.5), nil},
|
||||
/* 13 */ {`builtin "math.arith"; mul(1:2, 2:3)`, newFraction(2, 6), nil},
|
||||
/* 14 */ {`builtin "math.arith"; mul(1:2, 1.0, 2)`, float64(1.0), nil},
|
||||
/* 15 */ {`1:0`, nil, `division by zero`},
|
||||
/* 15 */ {`1:0`, nil, `[1:3] division by zero`},
|
||||
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
|
||||
/* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
|
||||
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
|
||||
@@ -34,7 +34,7 @@ func TestFractionsParser(t *testing.T) {
|
||||
/* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`},
|
||||
/* 22 */ {`string(1:2)`, "1:2", nil},
|
||||
/* 23 */ {`1+1:2+0.5`, float64(2), nil},
|
||||
/* 24 */ {`1:(2-2)`, nil, `division by zero`},
|
||||
/* 24 */ {`1:(2-2)`, nil, `[1:3] division by zero`},
|
||||
/* 25 */ {`[0,1][1-1]:1`, newFraction(0, 1), nil},
|
||||
}
|
||||
// runTestSuiteSpec(t, section, inputs, 25)
|
||||
|
||||
@@ -68,3 +68,27 @@ func TestFunctionGetFunc(t *testing.T) {
|
||||
t.Errorf(`(func() -> result = %v [%T], want = %v [%T]`, got, got, want, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoFunction(t *testing.T) {
|
||||
section := "Funcs"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`myName()`, "Celestino Amoroso", nil},
|
||||
/* 2 */ {`myName("Peppino")`, "Peppino", nil},
|
||||
}
|
||||
|
||||
myName := func(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var ok bool
|
||||
if result, ok = args["name"].(string); !ok {
|
||||
err = ErrWrongParamType(name, "name", TypeString, args["name"])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx := NewSimpleStore()
|
||||
ctx.RegisterFunc("myName", NewGolangFunctor(myName), TypeString, []ExprFuncParam{
|
||||
NewFuncParamFlagDef("name", PfOptional|PfDefault, "Celestino Amoroso"),
|
||||
})
|
||||
|
||||
runCtxTestSuite(t, ctx, section, inputs)
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -9,10 +9,10 @@ import "testing"
|
||||
func TestIteratorParser(t *testing.T) {
|
||||
section := "Iterator"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ^it`, int64(0), nil},
|
||||
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); *it`, int64(0), nil},
|
||||
/* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
|
||||
/* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(3), nil},
|
||||
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ^it`, int64(0), nil},
|
||||
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; *it`, int64(0), nil},
|
||||
/* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
|
||||
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
|
||||
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
|
||||
|
||||
+16
-16
@@ -23,8 +23,8 @@ func TestListParser(t *testing.T) {
|
||||
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
||||
/* 10 */ {`add([1,"hello"])`, nil, `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`, newListA(int64(1), int64(2), int64(3), int64(4)), nil},
|
||||
/* 13 */ {`2-1 >> [2,3]`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||
/* 12 */ {`[1,2,3] <+ 2+2`, newListA(int64(1), int64(2), int64(3), int64(4)), nil},
|
||||
/* 13 */ {`2-1 +> [2,3]`, newListA(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},
|
||||
@@ -33,25 +33,25 @@ func TestListParser(t *testing.T) {
|
||||
/* 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},
|
||||
/* 22 */ {`a=[1,2]; (a)<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||
/* 23 */ {`a=[1,2]; (a)<<3; a`, newListA(int64(1), int64(2)), nil},
|
||||
/* 22 */ {`a=[1,2]; (a)<+3`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||
/* 23 */ {`a=[1,2]; (a)<+3; a`, newListA(int64(1), int64(2)), nil},
|
||||
/* 24 */ {`["a","b","c","d"][1]`, "b", nil},
|
||||
/* 25 */ {`["a","b","c","d"][1,1]`, nil, `[1:19] one index only is allowed`},
|
||||
/* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||
/* 27 */ {`["a", "b", "c"] << ;`, nil, `[1:18] infix operator "<<" requires two non-nil operands, got 1`},
|
||||
/* 27 */ {`["a", "b", "c"] <+ ;`, nil, `[1:18] infix operator "<+" requires two non-nil operands, got 1`},
|
||||
/* 28 */ {`2 << 3;`, int64(16), nil},
|
||||
/* 29 */ {`but >> ["a", "b", "c"]`, nil, `[1:6] infix operator ">>" requires two non-nil operands, got 0`},
|
||||
/* 29 */ {`but +> ["a", "b", "c"]`, nil, `[1:6] infix operator "+>" requires two non-nil operands, got 0`},
|
||||
/* 30 */ {`2 >> 3;`, int64(0), nil},
|
||||
/* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||
/* 33 */ {`a=[1,2]; 5>>a`, newListA(int64(5), int64(1), int64(2)), nil},
|
||||
/* 34 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
|
||||
/* 35 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`},
|
||||
/* 36 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [list]`},
|
||||
/* 37 */ {`L=[1,2]; L[nil]=9;`, nil, `[1:12] index/key is nil`},
|
||||
/* 38 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil},
|
||||
/* 39 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil},
|
||||
/* 40 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil},
|
||||
/* 41 */ {`[0,1,2,3,4][0:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||
/* 31 */ {`a=[1,2]; a<+3`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||
/* 32 */ {`a=[1,2]; 5+>a`, newListA(int64(5), int64(1), int64(2)), nil},
|
||||
/* 33 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
|
||||
/* 34 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`},
|
||||
/* 35 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [list]`},
|
||||
/* 36 */ {`L=[1,2]; L[nil]=9;`, nil, `[1:12] index/key is nil`},
|
||||
/* 37 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil},
|
||||
/* 38 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil},
|
||||
/* 30 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil},
|
||||
/* 40 */ {`[0,1,2,3,4][0:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
+20
-1
@@ -14,10 +14,29 @@ func TestOperator(t *testing.T) {
|
||||
/* 1 */ {`a=1; unset "a"; a`, nil, `undefined variable or function "a"`},
|
||||
/* 2 */ {`a=1; unset ["a", "b"]`, int64(1), nil},
|
||||
/* 3 */ {`f=func(){3}; unset "f()"`, int64(1), nil},
|
||||
/* 4 */ {`a=1; a<<=1+0`, int64(2), nil},
|
||||
/* 5 */ {`a=2; a>>=1+0`, int64(1), nil},
|
||||
/* 6 */ {`1<<1`, int64(2), nil},
|
||||
/* 7 */ {`1>>1`, int64(0), nil},
|
||||
/* 8 */ {`1|2`, int64(3), nil},
|
||||
/* 9 */ {`a=1; a|=2`, int64(3), nil},
|
||||
/* 10 */ {`3&1`, int64(1), nil},
|
||||
/* 11 */ {`a=3; a&=1`, int64(1), nil},
|
||||
/* 12 */ {`~1`, int64(-2), nil},
|
||||
/* 13 */ {`0x10`, int64(16), nil},
|
||||
/* 14 */ {`0x1X`, nil, `[1:5] two adjacent operators: "1" and "X"`},
|
||||
/* 15 */ {`0o10`, int64(8), nil},
|
||||
/* 16 */ {`0b10`, int64(2), nil},
|
||||
/* 17 */ {`~true`, nil, `[1:2] prefix/postfix operator "~" do not support operand 'true' [bool]`},
|
||||
/* 18 */ {`1^2`, int64(3), nil},
|
||||
/* 19 */ {`3^2`, int64(1), nil},
|
||||
/* 19 */ {`a=1; a^=2`, int64(3), nil},
|
||||
/* 20 */ {`a=1; ++a`, int64(2), nil},
|
||||
/* 21 */ {`a=1; --a`, int64(0), nil},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
//runTestSuiteSpec(t, section, inputs, 3)
|
||||
// runTestSuiteSpec(t, section, inputs, 4)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
+3
-3
@@ -78,8 +78,8 @@ func TestGeneralParser(t *testing.T) {
|
||||
/* 64 */ {`"1.5" != `, nil, `[1:8] infix operator "!=" requires two non-nil operands, got 1`},
|
||||
/* 65 */ {"+1.5", float64(1.5), nil},
|
||||
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one non-nil operand`},
|
||||
/* 67 */ {"4 / 0", nil, `division by zero`},
|
||||
/* 68 */ {"4.0 / 0", nil, `division by zero`},
|
||||
/* 67 */ {"4 / 0", nil, `[1:4] division by zero`},
|
||||
/* 68 */ {"4.0 / 0", nil, `[1:6] division by zero`},
|
||||
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
|
||||
/* 70 */ {`123`, int64(123), nil},
|
||||
/* 71 */ {`1.`, float64(1.0), nil},
|
||||
@@ -142,6 +142,6 @@ func TestGeneralParser(t *testing.T) {
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
//runTestSuiteSpec(t, section, inputs, 130)
|
||||
// runTestSuiteSpec(t, section, inputs, 114)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
+8
-1
@@ -9,6 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func TestStringsParser(t *testing.T) {
|
||||
section := "String"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`"uno" + "due"`, `unodue`, nil},
|
||||
/* 2 */ {`"uno" + 2`, `uno2`, nil},
|
||||
@@ -16,6 +17,12 @@ func TestStringsParser(t *testing.T) {
|
||||
/* 4 */ {`"uno" * (2+1)`, `unounouno`, nil},
|
||||
/* 5 */ {`"abc"[1]`, `b`, nil},
|
||||
/* 6 */ {`#"abc"`, int64(3), nil},
|
||||
/* 7 */ {`"192.168.0.240" / "."`, newListA("192", "168", "0", "240"), nil},
|
||||
/* 8 */ {`("192.168.0.240" / ".")[1]`, "168", nil},
|
||||
/* 9 */ {`"AF3B0Dz" / 2`, newListA("AF", "3B", "0D", "z"), nil},
|
||||
/* 10 */ {`"AF3B0Dz" / 0`, nil, "[1:12] division by zero"},
|
||||
}
|
||||
runTestSuite(t, "String", inputs)
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 8)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
@@ -15,21 +15,26 @@ const (
|
||||
priRange
|
||||
priBut
|
||||
priAssign
|
||||
priInsert
|
||||
priOr
|
||||
priAnd
|
||||
priNot
|
||||
priRelational
|
||||
priBinary
|
||||
priBitwiseOr
|
||||
priBitwiseAnd
|
||||
priBitwiseNot
|
||||
priSum
|
||||
priProduct
|
||||
priFraction
|
||||
priSelector
|
||||
priBinShift
|
||||
priSign
|
||||
priFact
|
||||
priIterValue
|
||||
priDefault
|
||||
priIncDec
|
||||
priDot
|
||||
priDereference
|
||||
priValue
|
||||
)
|
||||
|
||||
@@ -199,6 +204,10 @@ func (term *term) errIncompatibleType(value any) error {
|
||||
term.source(), value, TypeName(value))
|
||||
}
|
||||
|
||||
func (term *term) errDivisionByZero() error {
|
||||
return term.tk.Errorf("division by zero")
|
||||
}
|
||||
|
||||
func (term *term) Errorf(template string, args ...any) (err error) {
|
||||
err = term.tk.Errorf(template, args...)
|
||||
return
|
||||
|
||||
@@ -79,6 +79,10 @@ func (tk *Token) IsSymbol(sym Symbol) bool {
|
||||
return tk.Sym == sym
|
||||
}
|
||||
|
||||
func (tk *Token) SetSymbol(sym Symbol) {
|
||||
tk.Sym = sym
|
||||
}
|
||||
|
||||
func (tk *Token) Errorf(template string, args ...any) (err error) {
|
||||
err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", tk.row, tk.col)+template, args...)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user