Compare commits
24 Commits
5da5a61a42
...
v0.29.0
| Author | SHA1 | Date | |
|---|---|---|---|
| cca3b76baa | |||
| 24e31997fc | |||
| 646710e180 | |||
| b38327b841 | |||
| fd912b2eb1 | |||
| 0e55f83d56 | |||
| 4725145d1c | |||
| edf8818f51 | |||
| 6211be8a8f | |||
| f50ddf48db | |||
| 76e01f12d2 | |||
| 406bced450 | |||
| 409dc86a92 | |||
| 4184221428 | |||
| 8cf8b36a26 | |||
| de87050188 | |||
| a1ec0cc611 | |||
| 8e5550bfa7 | |||
| 6ee21e10af | |||
| 5c44532790 | |||
| cb66c1ab19 | |||
| a28d24ba68 | |||
| 523349a204 | |||
| b185f1df3a |
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -188,6 +189,30 @@ func fractFunc(ctx ExprContext, name string, args map[string]any) (result any, e
|
|||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
func evalFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
if source, ok := args[ParamSource].(string); ok {
|
||||||
|
var expr Expr
|
||||||
|
|
||||||
|
parser := NewParser()
|
||||||
|
if ctx == nil {
|
||||||
|
ctx = NewSimpleStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
r := strings.NewReader(source)
|
||||||
|
scanner := NewScanner(r, DefaultTranslations())
|
||||||
|
|
||||||
|
if expr, err = parser.Parse(scanner); err == nil {
|
||||||
|
CtrlEnable(ctx, control_export_all)
|
||||||
|
result, err = expr.Eval(ctx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//// import
|
||||||
|
|
||||||
func ImportBuiltinsFuncs(ctx ExprContext) {
|
func ImportBuiltinsFuncs(ctx ExprContext) {
|
||||||
anyParams := []ExprFuncParam{
|
anyParams := []ExprFuncParam{
|
||||||
NewFuncParam(ParamValue),
|
NewFuncParam(ParamValue),
|
||||||
@@ -211,6 +236,10 @@ func ImportBuiltinsFuncs(ctx ExprContext) {
|
|||||||
NewFuncParam(ParamValue),
|
NewFuncParam(ParamValue),
|
||||||
NewFuncParamFlagDef(ParamDenominator, PfDefault, int64(1)),
|
NewFuncParamFlagDef(ParamDenominator, PfDefault, int64(1)),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ctx.RegisterFunc("eval", NewGolangFunctor(evalFunc), TypeAny, []ExprFuncParam{
|
||||||
|
NewFuncParam(ParamSource),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
|
|||||||
var expr *ast
|
var expr *ast
|
||||||
scanner := NewScanner(file, DefaultTranslations())
|
scanner := NewScanner(file, DefaultTranslations())
|
||||||
parser := NewParser()
|
parser := NewParser()
|
||||||
if expr, err = parser.parseGeneral(scanner, true, true); err == nil {
|
if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymEos); err == nil {
|
||||||
result, err = expr.Eval(ctx)
|
result, err = expr.Eval(ctx)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+18
-1
@@ -55,8 +55,25 @@ func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error
|
|||||||
return fmt.Errorf("%s(): invalid value %s (%v) for parameter %q", funcName, TypeName(paramValue), paramValue, paramName)
|
return fmt.Errorf("%s(): invalid value %s (%v) for parameter %q", funcName, TypeName(paramValue), paramValue, paramName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func undefArticle(s string) (article string) {
|
||||||
|
if len(s) > 0 && strings.Contains("aeiou", s[0:1]) {
|
||||||
|
article = "an"
|
||||||
|
} else {
|
||||||
|
article = "a"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func prependUndefArticle(s string) (result string) {
|
||||||
|
return undefArticle(s) + " " + s
|
||||||
|
}
|
||||||
|
|
||||||
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
|
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
|
||||||
return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue)
|
var artWantType, artGotType string
|
||||||
|
gotType := TypeName(paramValue)
|
||||||
|
artGotType = prependUndefArticle(gotType)
|
||||||
|
artWantType = prependUndefArticle(paramType)
|
||||||
|
return fmt.Errorf("%s(): the %q parameter must be %s, got %s (%v)", funcName, paramName, artWantType, artGotType, paramValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ErrUnknownParam(funcName, paramName string) error {
|
func ErrUnknownParam(funcName, paramName string) error {
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ func exportObjects(destCtx, sourceCtx ExprContext) {
|
|||||||
exportAll := CtrlIsEnabled(sourceCtx, control_export_all)
|
exportAll := CtrlIsEnabled(sourceCtx, control_export_all)
|
||||||
// fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
|
// fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
|
||||||
// Export variables
|
// Export variables
|
||||||
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) {
|
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return (exportAll || name[0] == '@') && !(name[0] == '_') }) {
|
||||||
// fmt.Printf("\tExporting %q\n", refName)
|
// fmt.Printf("\tExporting %q\n", refName)
|
||||||
refValue, _ := sourceCtx.GetVar(refName)
|
refValue, _ := sourceCtx.GetVar(refName)
|
||||||
exportVar(destCtx, refName, refValue)
|
exportVar(destCtx, refName, refValue)
|
||||||
|
|||||||
+34
-31
@@ -127,22 +127,22 @@ dev-expr -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoro
|
|||||||
9.5
|
9.5
|
||||||
>>> 0xFD + 0b1 + 0o1 <1>
|
>>> 0xFD + 0b1 + 0o1 <1>
|
||||||
255
|
255
|
||||||
>>> 1|2 + 2|3 <2>
|
>>> 1:2 + 2:3 <2>
|
||||||
7|6
|
7:6
|
||||||
>>> ml <3>
|
>>> ml <3>
|
||||||
>>> 1|2 + 2|3
|
>>> 1:2 + 2:3
|
||||||
7
|
7
|
||||||
-
|
-
|
||||||
6
|
6
|
||||||
>>> 4+2 but 5|2+0.5 <4>
|
>>> 4+2 but 5:2+0.5 <4>
|
||||||
3
|
3
|
||||||
>>> 4+2; 5|2+0.5 <5>
|
>>> 4+2; 5:2+0.5 <5>
|
||||||
3
|
3
|
||||||
>>>
|
>>>
|
||||||
----
|
----
|
||||||
|
|
||||||
<1> Number bases: 0x = _hexadecimal_, 0o = _octal_, 0b = _binary_.
|
<1> Number bases: 0x = _hexadecimal_, 0o = _octal_, 0b = _binary_.
|
||||||
<2> Fractions: _numerator_ | _denominator_.
|
<2> Fractions: _numerator_ : _denominator_.
|
||||||
<3> Activate multi-line output of fractions.
|
<3> Activate multi-line output of fractions.
|
||||||
<4> But operator, see <<_but_operator>>.
|
<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 of the previous single expression but this it is obtained with two separated calculations.
|
||||||
@@ -236,11 +236,11 @@ _dec-seq_ = _see-integer-literal-syntax_
|
|||||||
|===
|
|===
|
||||||
|
|
||||||
==== Fractions
|
==== Fractions
|
||||||
_Expr_ also supports fractions. Fraction literals are made with two integers separated by a vertical bar `|`.
|
_Expr_ also supports fractions. Fraction literals are made with two integers separated by a colon character `:`.
|
||||||
|
|
||||||
.Fraction literal syntax
|
.Fraction literal syntax
|
||||||
====
|
====
|
||||||
*_fraction_* = [__sign__] (_num-den-spec_ | _float-spec_) +
|
*_fraction_* = [__sign__] (_num-den-spec_ "**:**" _float-spec_) +
|
||||||
_sign_ = "**+**" | "**-**" +
|
_sign_ = "**+**" | "**-**" +
|
||||||
_num-den-spec_ = _digit-seq_ "**|**" _digit-seq_ +
|
_num-den-spec_ = _digit-seq_ "**|**" _digit-seq_ +
|
||||||
_float-spec_ = _dec-seq_ "**.**" [_dec-seq_] "**(**" _dec-seq_ "**)**" +
|
_float-spec_ = _dec-seq_ "**.**" [_dec-seq_] "**(**" _dec-seq_ "**)**" +
|
||||||
@@ -249,44 +249,44 @@ _digit-seq_ = _see-integer-literal-syntax_
|
|||||||
====
|
====
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
`>>>` [blue]`1 | 2` +
|
`>>>` [blue]`1 : 2` +
|
||||||
[green]`1|2`
|
[green]`1:2`
|
||||||
|
|
||||||
`>>>` [blue]`4|6` [gray]_// Fractions are always reduced to their lowest terms_ +
|
`>>>` [blue]`4:6` [gray]_// Fractions are always reduced to their lowest terms_ +
|
||||||
[green]`2|3`
|
[green]`2:3`
|
||||||
|
|
||||||
`>>>` [blue]`1|2 + 2|3` +
|
`>>>` [blue]`1:2 + 2:3` +
|
||||||
[green]`7|6`
|
[green]`7:6`
|
||||||
|
|
||||||
`>>>` [blue]`1|2 * 2|3` +
|
`>>>` [blue]`1:2 * 2:3` +
|
||||||
[green]`1|3`
|
[green]`1:3`
|
||||||
|
|
||||||
`>>>` [blue]`1|2 / 1|3` +
|
`>>>` [blue]`1:2 / 1:3` +
|
||||||
[green]`3|2`
|
[green]`3:2`
|
||||||
|
|
||||||
`>>>` [blue]`1|2 ./ 1|3` [gray]_// Force decimal division_ +
|
`>>>` [blue]`1:2 ./ 1:3` [gray]_// Force decimal division_ +
|
||||||
[green]`1.5`
|
[green]`1.5`
|
||||||
|
|
||||||
`>>>` [blue]`-1|2` +
|
`>>>` [blue]`-1:2` +
|
||||||
[green]`-1|2`
|
[green]`-1:2`
|
||||||
|
|
||||||
`>>>` [blue]`1|-2` [gray]_// Invalid sign specification_ +
|
`>>>` [blue]`1:-2` [gray]_// Invalid sign specification_ +
|
||||||
[red]_Eval Error: [1:3] infix operator "|" requires two non-nil operands, got 1_
|
[red]_Eval Error: [1:3] infix operator ":" requires two non-nil operands, got 1_
|
||||||
|
|
||||||
`>>>` [blue]`1|(-2)` +
|
`>>>` [blue]`1:(-2)` +
|
||||||
[green]`-1|2`
|
[green]`-1:2`
|
||||||
|
|
||||||
|
|
||||||
Fractions can be used together with integers and floats in expressions.
|
Fractions can be used together with integers and floats in expressions.
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
`>>>` [blue]`1|2 + 5` +
|
`>>>` [blue]`1:2 + 5` +
|
||||||
[green]`11|2`
|
[green]`11:2`
|
||||||
|
|
||||||
`>>>` [blue]`4 - 1|2` +
|
`>>>` [blue]`4 - 1:2` +
|
||||||
[green]`7|2`
|
[green]`7:2`
|
||||||
|
|
||||||
`>>>` [blue]`1.0 + 1|2` +
|
`>>>` [blue]`1.0 + 1:2` +
|
||||||
[green]`1.5`
|
[green]`1.5`
|
||||||
|
|
||||||
|
|
||||||
@@ -753,7 +753,7 @@ The table below shows all supported operators by decreasing priorities.
|
|||||||
| [blue]`#` | _Prefix_ | _Size-of_ | `#` _iterator_ -> _integer_
|
| [blue]`#` | _Prefix_ | _Size-of_ | `#` _iterator_ -> _integer_
|
||||||
.2+|*SELECT*| [blue]`? : ::` | _Multi-Infix_ | _Case-Selector_ | _any-expr_ `?` _case-list_ _case-expr_ `:` _case-list_ _case-expr_ ... `::` _default-expr_ -> _any_
|
.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_
|
| [blue]`? : ::` | _Multi-Infix_ | _Index-Selector_ | _int-expr_ `?` _case-expr_ `:` _case-expr_ ... `::` _default-expr_ -> _any_
|
||||||
.1+|*FRACT*| [blue]`\|` | _Infix_ | _Fraction_ | _integer_ `\|` _integer_ -> _fraction_
|
.1+|*FRACT*| [blue]`:` | _Infix_ | _Fraction_ | _integer_ `:` _integer_ -> _fraction_
|
||||||
.5+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ `*` _number_ -> _number_
|
.5+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ `*` _number_ -> _number_
|
||||||
| [blue]`*` | _Infix_ | _String-repeat_ | _string_ `*` _integer_ -> _string_
|
| [blue]`*` | _Infix_ | _String-repeat_ | _string_ `*` _integer_ -> _string_
|
||||||
| [blue]`/` | _Infix_ | _Division_ | _number_ `/` _number_ -> _number_
|
| [blue]`/` | _Infix_ | _Division_ | _number_ `/` _number_ -> _number_
|
||||||
@@ -765,6 +765,9 @@ The table below shows all supported operators by decreasing priorities.
|
|||||||
| [blue]`+` | _Infix_ | _Dict-join_ | _dict_ `+` _dict_ -> _dict_
|
| [blue]`+` | _Infix_ | _Dict-join_ | _dict_ `+` _dict_ -> _dict_
|
||||||
| [blue]`-` | _Infix_ | _Subtraction_ | _number_ `-` _number_ -> _number_
|
| [blue]`-` | _Infix_ | _Subtraction_ | _number_ `-` _number_ -> _number_
|
||||||
| [blue]`-` | _Infix_ | _List-difference_ | _list_ `-` _list_ -> _list_
|
| [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_
|
||||||
.8+|*RELATION*| [blue]`<` | _Infix_ | _Less_ | _comparable_ `<` _comparable_ -> _boolean_
|
.8+|*RELATION*| [blue]`<` | _Infix_ | _Less_ | _comparable_ `<` _comparable_ -> _boolean_
|
||||||
| [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ `\<=` _comparable_ -> _boolean_
|
| [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ `\<=` _comparable_ -> _boolean_
|
||||||
| [blue]`>` | _Infix_ | _Greater_ | _comparable_ `>` _comparable_ -> _boolean_
|
| [blue]`>` | _Infix_ | _Greater_ | _comparable_ `>` _comparable_ -> _boolean_
|
||||||
|
|||||||
+62
-33
@@ -474,6 +474,16 @@ pre.rouge .gh {
|
|||||||
color: #b8bb26;
|
color: #b8bb26;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
pre.rouge .ge {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
pre.rouge .ges {
|
||||||
|
font-weight: bold;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
pre.rouge .gs {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
pre.rouge .k, pre.rouge .kn, pre.rouge .kp, pre.rouge .kr, pre.rouge .kv {
|
pre.rouge .k, pre.rouge .kn, pre.rouge .kp, pre.rouge .kr, pre.rouge .kv {
|
||||||
color: #fb4934;
|
color: #fb4934;
|
||||||
}
|
}
|
||||||
@@ -745,16 +755,16 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
|||||||
9.5
|
9.5
|
||||||
<span class="o">>>></span> 0xFD + 0b1 + 0o1 <i class="conum" data-value="1"></i><b>(1)</b>
|
<span class="o">>>></span> 0xFD + 0b1 + 0o1 <i class="conum" data-value="1"></i><b>(1)</b>
|
||||||
255
|
255
|
||||||
<span class="o">>>></span> 1|2 + 2|3 <i class="conum" data-value="2"></i><b>(2)</b>
|
<span class="o">>>></span> 1:2 + 2:3 <i class="conum" data-value="2"></i><b>(2)</b>
|
||||||
7|6
|
7:6
|
||||||
<span class="o">>>></span> ml <i class="conum" data-value="3"></i><b>(3)</b>
|
<span class="o">>>></span> ml <i class="conum" data-value="3"></i><b>(3)</b>
|
||||||
<span class="o">>>></span> 1|2 + 2|3
|
<span class="o">>>></span> 1:2 + 2:3
|
||||||
7
|
7
|
||||||
-
|
-
|
||||||
6
|
6
|
||||||
<span class="o">>>></span> 4+2 but 5|2+0.5 <i class="conum" data-value="4"></i><b>(4)</b>
|
<span class="o">>>></span> 4+2 but 5:2+0.5 <i class="conum" data-value="4"></i><b>(4)</b>
|
||||||
3
|
3
|
||||||
<span class="o">>>></span> 4+2<span class="p">;</span> 5|2+0.5 <i class="conum" data-value="5"></i><b>(5)</b>
|
<span class="o">>>></span> 4+2<span class="p">;</span> 5:2+0.5 <i class="conum" data-value="5"></i><b>(5)</b>
|
||||||
3
|
3
|
||||||
<span class="o">>>></span></code></pre>
|
<span class="o">>>></span></code></pre>
|
||||||
</div>
|
</div>
|
||||||
@@ -767,7 +777,7 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><i class="conum" data-value="2"></i><b>2</b></td>
|
<td><i class="conum" data-value="2"></i><b>2</b></td>
|
||||||
<td>Fractions: <em>numerator</em> | <em>denominator</em>.</td>
|
<td>Fractions: <em>numerator</em> : <em>denominator</em>.</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><i class="conum" data-value="3"></i><b>3</b></td>
|
<td><i class="conum" data-value="3"></i><b>3</b></td>
|
||||||
@@ -981,13 +991,13 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
|||||||
<div class="sect3">
|
<div class="sect3">
|
||||||
<h4 id="_fractions"><a class="anchor" href="#_fractions"></a><a class="link" href="#_fractions">2.1.3. Fractions</a></h4>
|
<h4 id="_fractions"><a class="anchor" href="#_fractions"></a><a class="link" href="#_fractions">2.1.3. Fractions</a></h4>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><em>Expr</em> also supports fractions. Fraction literals are made with two integers separated by a vertical bar <code>|</code>.</p>
|
<p><em>Expr</em> also supports fractions. Fraction literals are made with two integers separated by a colon character <code>:</code>.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="exampleblock">
|
<div class="exampleblock">
|
||||||
<div class="title">Example 3. Fraction literal syntax</div>
|
<div class="title">Example 3. Fraction literal syntax</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><strong><em>fraction</em></strong> = [<em>sign</em>] (<em>num-den-spec</em> | <em>float-spec</em>)<br>
|
<p><strong><em>fraction</em></strong> = [<em>sign</em>] (<em>num-den-spec</em> "<strong>:</strong>" <em>float-spec</em>)<br>
|
||||||
<em>sign</em> = "<strong>+</strong>" | "<strong>-</strong>"<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>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>float-spec</em> = <em>dec-seq</em> "<strong>.</strong>" [<em>dec-seq</em>] "<strong>(</strong>" <em>dec-seq</em> "<strong>)</strong>"<br>
|
||||||
@@ -998,55 +1008,55 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
|
|||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<div class="title">Examples</div>
|
<div class="title">Examples</div>
|
||||||
<p><code>>>></code> <code class="blue">1 | 2</code><br>
|
<p><code>>>></code> <code class="blue">1 : 2</code><br>
|
||||||
<code class="green">1|2</code></p>
|
<code class="green">1:2</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><code>>>></code> <code class="blue">4|6</code> <em class="gray">// Fractions are always reduced to their lowest terms</em><br>
|
<p><code>>>></code> <code class="blue">4:6</code> <em class="gray">// Fractions are always reduced to their lowest terms</em><br>
|
||||||
<code class="green">2|3</code></p>
|
<code class="green">2:3</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><code>>>></code> <code class="blue">1|2 + 2|3</code><br>
|
<p><code>>>></code> <code class="blue">1:2 + 2:3</code><br>
|
||||||
<code class="green">7|6</code></p>
|
<code class="green">7:6</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><code>>>></code> <code class="blue">1|2 * 2|3</code><br>
|
<p><code>>>></code> <code class="blue">1:2 * 2:3</code><br>
|
||||||
<code class="green">1|3</code></p>
|
<code class="green">1:3</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><code>>>></code> <code class="blue">1|2 / 1|3</code><br>
|
<p><code>>>></code> <code class="blue">1:2 / 1:3</code><br>
|
||||||
<code class="green">3|2</code></p>
|
<code class="green">3:2</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><code>>>></code> <code class="blue">1|2 ./ 1|3</code> <em class="gray">// Force decimal division</em><br>
|
<p><code>>>></code> <code class="blue">1:2 ./ 1:3</code> <em class="gray">// Force decimal division</em><br>
|
||||||
<code class="green">1.5</code></p>
|
<code class="green">1.5</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><code>>>></code> <code class="blue">-1|2</code><br>
|
<p><code>>>></code> <code class="blue">-1:2</code><br>
|
||||||
<code class="green">-1|2</code></p>
|
<code class="green">-1:2</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><code>>>></code> <code class="blue">1|-2</code> <em class="gray">// Invalid sign specification</em><br>
|
<p><code>>>></code> <code class="blue">1:-2</code> <em class="gray">// Invalid sign specification</em><br>
|
||||||
<em class="red">Eval Error: [1:3] infix operator "|" requires two non-nil operands, got 1</em></p>
|
<em class="red">Eval Error: [1:3] infix operator ":" requires two non-nil operands, got 1</em></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><code>>>></code> <code class="blue">1|(-2)</code><br>
|
<p><code>>>></code> <code class="blue">1:(-2)</code><br>
|
||||||
<code class="green">-1|2</code></p>
|
<code class="green">-1:2</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>Fractions can be used together with integers and floats in expressions.</p>
|
<p>Fractions can be used together with integers and floats in expressions.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<div class="title">Examples</div>
|
<div class="title">Examples</div>
|
||||||
<p><code>>>></code> <code class="blue">1|2 + 5</code><br>
|
<p><code>>>></code> <code class="blue">1:2 + 5</code><br>
|
||||||
<code class="green">11|2</code></p>
|
<code class="green">11:2</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><code>>>></code> <code class="blue">4 - 1|2</code><br>
|
<p><code>>>></code> <code class="blue">4 - 1:2</code><br>
|
||||||
<code class="green">7|2</code></p>
|
<code class="green">7:2</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><code>>>></code> <code class="blue">1.0 + 1|2</code><br>
|
<p><code>>>></code> <code class="blue">1.0 + 1:2</code><br>
|
||||||
<code class="green">1.5</code></p>
|
<code class="green">1.5</code></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1992,10 +2002,10 @@ These operators have a high priority, in particular higher than the operator <co
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>FRACT</strong></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>FRACT</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"><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>Infix</em></p></td>
|
||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Fraction</em></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Fraction</em></p></td>
|
||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>integer</em> <code>|</code> <em>integer</em> → <em>fraction</em></p></td>
|
<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>
|
||||||
<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="5"><p class="tableblock"><strong>PROD</strong></p></td>
|
||||||
@@ -2066,6 +2076,25 @@ 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>
|
<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>
|
||||||
<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"><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>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td class="tableblock halign-center valign-top" rowspan="8"><p class="tableblock"><strong>RELATION</strong></p></td>
|
<td class="tableblock halign-center valign-top" rowspan="8"><p class="tableblock"><strong>RELATION</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"><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>Infix</em></p></td>
|
||||||
@@ -2432,7 +2461,7 @@ g(@p):any{}`
|
|||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<div id="footer-text">
|
<div id="footer-text">
|
||||||
Last updated 2024-09-30 07:29:03 +0200
|
Last updated 2024-12-23 07:20:48 +0100
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
+14
-12
@@ -50,9 +50,9 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
|
|||||||
} else if s[0] == '+' {
|
} else if s[0] == '+' {
|
||||||
s = s[1:]
|
s = s[1:]
|
||||||
}
|
}
|
||||||
// if strings.HasSuffix(s, "()") {
|
// if strings.HasSuffix(s, "()") {
|
||||||
// s = s[0 : len(s)-2]
|
// s = s[0 : len(s)-2]
|
||||||
// }
|
// }
|
||||||
s = strings.TrimSuffix(s, "()")
|
s = strings.TrimSuffix(s, "()")
|
||||||
parts = strings.SplitN(s, ".", 2)
|
parts = strings.SplitN(s, ".", 2)
|
||||||
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
||||||
@@ -124,36 +124,38 @@ func (f *FractionType) String() string {
|
|||||||
func (f *FractionType) ToString(opt FmtOpt) string {
|
func (f *FractionType) ToString(opt FmtOpt) string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
if opt&MultiLine == 0 {
|
if opt&MultiLine == 0 {
|
||||||
sb.WriteString(fmt.Sprintf("%d|%d", f.num, f.den))
|
sb.WriteString(fmt.Sprintf("%d:%d", f.num, f.den))
|
||||||
} else {
|
} else {
|
||||||
var s, num string
|
var sign, num string
|
||||||
if f.num < 0 && opt&TTY == 0 {
|
if f.num < 0 && opt&TTY == 0 {
|
||||||
num = strconv.FormatInt(-f.num, 10)
|
num = strconv.FormatInt(-f.num, 10)
|
||||||
s = "-"
|
sign = "-"
|
||||||
} else {
|
} else {
|
||||||
num = strconv.FormatInt(f.num, 10)
|
num = strconv.FormatInt(f.num, 10)
|
||||||
}
|
}
|
||||||
den := strconv.FormatInt(f.den, 10)
|
den := strconv.FormatInt(f.den, 10)
|
||||||
size := max(len(num), len(den))
|
size := max(len(num), len(den))
|
||||||
if opt&TTY != 0 {
|
if opt&TTY != 0 {
|
||||||
sb.WriteString(fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, s+num)))
|
sNum := fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, sign+num))
|
||||||
|
sb.WriteString(sNum)
|
||||||
} else {
|
} else {
|
||||||
if len(s) > 0 {
|
if len(sign) > 0 {
|
||||||
sb.WriteString(" ")
|
sb.WriteString(" ")
|
||||||
}
|
}
|
||||||
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, num)))
|
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, num)))
|
||||||
sb.WriteByte('\n')
|
sb.WriteByte('\n')
|
||||||
if len(s) > 0 {
|
if len(sign) > 0 {
|
||||||
sb.WriteString(s)
|
sb.WriteString(sign)
|
||||||
sb.WriteByte(' ')
|
sb.WriteByte(' ')
|
||||||
}
|
}
|
||||||
sb.WriteString(strings.Repeat("-", size))
|
sb.WriteString(strings.Repeat("-", size))
|
||||||
sb.WriteByte('\n')
|
sb.WriteByte('\n')
|
||||||
if len(s) > 0 {
|
if len(sign) > 0 {
|
||||||
sb.WriteString(" ")
|
sb.WriteString(" ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den)))
|
sDen := fmt.Sprintf("%[1]*s", size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den))
|
||||||
|
sb.WriteString(sDen)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.String()
|
return sb.String()
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
module git.portale-stac.it/go-pkg/expr
|
module git.portale-stac.it/go-pkg/expr
|
||||||
|
|
||||||
go 1.21.6
|
go 1.22.0
|
||||||
|
|
||||||
|
toolchain go1.23.3
|
||||||
|
|
||||||
|
require golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe h1:bWYrKmmfv37uNgXTdwkLSKYiYPJ1yfWmjBnvtMyAYzk=
|
||||||
|
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe/go.mod h1:alTKUpAJ/zbp17qvZwcFNwzufrb5DljMDY4mgJlIHao=
|
||||||
|
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
|
||||||
|
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||||
+4
-4
@@ -5,7 +5,7 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
// "errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -43,6 +43,6 @@ func errNoOperation(name string) error {
|
|||||||
return fmt.Errorf("no %s() function defined in the data-source", name)
|
return fmt.Errorf("no %s() function defined in the data-source", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func errInvalidDataSource() error {
|
// func errInvalidDataSource() error {
|
||||||
return errors.New("invalid data-source")
|
// return errors.New("invalid data-source")
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -99,7 +99,106 @@ func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-------- assign term
|
||||||
|
|
||||||
|
func newOpAssignTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priAssign,
|
||||||
|
evalFunc: evalOpAssign,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCollectionItemValue(ctx ExprContext, collectionTerm, keyListTerm *term) (value any, err error) {
|
||||||
|
var collectionValue, keyListValue, keyValue any
|
||||||
|
var keyList *ListType
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if collectionValue, err = collectionTerm.compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyListValue, err = keyListTerm.compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 {
|
||||||
|
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if keyValue = (*keyList)[0]; keyValue == nil {
|
||||||
|
err = keyListTerm.Errorf("index/key is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch collection := collectionValue.(type) {
|
||||||
|
case *ListType:
|
||||||
|
if index, ok := keyValue.(int64); ok {
|
||||||
|
value = (*collection)[index]
|
||||||
|
} else {
|
||||||
|
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue))
|
||||||
|
}
|
||||||
|
case *DictType:
|
||||||
|
value = (*collection)[keyValue]
|
||||||
|
default:
|
||||||
|
err = collectionTerm.Errorf("collection expected")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAssignValue(ctx ExprContext, leftTerm *term) (value any, err error) {
|
||||||
|
if leftTerm.symbol() == SymIndex {
|
||||||
|
value, err = getCollectionItemValue(ctx, leftTerm.children[0], leftTerm.children[1])
|
||||||
|
} else {
|
||||||
|
value, _ = ctx.GetVar(leftTerm.source())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalOpAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var rightValue, leftValue any
|
||||||
|
if err = opTerm.checkOperands(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
leftTerm := opTerm.children[0]
|
||||||
|
leftSym := leftTerm.symbol()
|
||||||
|
if leftSym != SymVariable && leftSym != SymIndex {
|
||||||
|
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rightChild := opTerm.children[1]
|
||||||
|
|
||||||
|
if rightValue, err = rightChild.compute(ctx); err == nil {
|
||||||
|
if leftValue, err = getAssignValue(ctx, leftTerm); err == nil {
|
||||||
|
switch opTerm.symbol() {
|
||||||
|
case SymPlusEqual:
|
||||||
|
v, err = sumValues(opTerm, leftValue, rightValue)
|
||||||
|
case SymMinusEqual:
|
||||||
|
v, err = diffValues(opTerm, leftValue, rightValue)
|
||||||
|
case SymStarEqual:
|
||||||
|
v, err = mulValues(opTerm, leftValue, rightValue)
|
||||||
|
case SymSlashEqual:
|
||||||
|
v, err = divValues(opTerm, leftValue, rightValue)
|
||||||
|
case SymPercEqual:
|
||||||
|
v, err = remainderValues(opTerm, leftValue, rightValue)
|
||||||
|
default:
|
||||||
|
err = opTerm.Errorf("unsupported assign operator %q", opTerm.source())
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = assignValue(ctx, leftTerm, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymEqual, newAssignTerm)
|
registerTermConstructor(SymEqual, newAssignTerm)
|
||||||
|
registerTermConstructor(SymPlusEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymMinusEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymStarEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymSlashEqual, newOpAssignTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
// 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)
|
||||||
|
}
|
||||||
+1
-1
@@ -20,7 +20,7 @@ func evalContextValue(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
var childValue any
|
var childValue any
|
||||||
|
|
||||||
var sourceCtx ExprContext
|
var sourceCtx ExprContext
|
||||||
if opTerm.children == nil || len(opTerm.children) == 0 {
|
if len(opTerm.children) == 0 {
|
||||||
sourceCtx = ctx
|
sourceCtx = ctx
|
||||||
} else if opTerm.children[0].symbol() == SymVariable && opTerm.children[0].source() == "global" {
|
} else if opTerm.children[0].symbol() == SymVariable && opTerm.children[0].source() == "global" {
|
||||||
sourceCtx = globalCtx
|
sourceCtx = globalCtx
|
||||||
|
|||||||
+12
-7
@@ -49,18 +49,23 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
den = -den
|
den = -den
|
||||||
num = -num
|
num = -num
|
||||||
}
|
}
|
||||||
g := gcd(num, den)
|
if num != 0 {
|
||||||
num = num / g
|
g := gcd(num, den)
|
||||||
den = den / g
|
num = num / g
|
||||||
if den == 1 {
|
den = den / g
|
||||||
v = num
|
if den == 1 {
|
||||||
|
v = num
|
||||||
|
} else {
|
||||||
|
v = &FractionType{num, den}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
v = &FractionType{num, den}
|
v = &FractionType{0, den}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymVertBar, newFractionTerm)
|
// registerTermConstructor(SymVertBar, newFractionTerm)
|
||||||
|
registerTermConstructor(SymColon, newFractionTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
} else if IsDict(leftValue) {
|
} else if IsDict(leftValue) {
|
||||||
d := leftValue.(*DictType)
|
d := leftValue.(*DictType)
|
||||||
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
||||||
|
} else {
|
||||||
|
rightChild := opTerm.children[1]
|
||||||
|
err = rightChild.Errorf("invalid index type: %v", (*indexList)[0])
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ func evalInsert(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
if opTerm.children[1].symbol() == SymVariable {
|
if opTerm.children[1].symbol() == SymVariable {
|
||||||
ctx.UnsafeSetVar(opTerm.children[1].source(), v)
|
ctx.UnsafeSetVar(opTerm.children[1].source(), v)
|
||||||
}
|
}
|
||||||
|
} else if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||||
|
leftInt := leftValue.(int64)
|
||||||
|
rightInt := rightValue.(int64)
|
||||||
|
v = leftInt >> rightInt
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
@@ -60,6 +64,10 @@ func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
if opTerm.children[0].symbol() == SymVariable {
|
if opTerm.children[0].symbol() == SymVariable {
|
||||||
ctx.UnsafeSetVar(opTerm.children[0].source(), v)
|
ctx.UnsafeSetVar(opTerm.children[0].source(), v)
|
||||||
}
|
}
|
||||||
|
} else if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||||
|
leftInt := leftValue.(int64)
|
||||||
|
rightInt := rightValue.(int64)
|
||||||
|
v = leftInt << rightInt
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-28
@@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
func newMultiplyTerm(tk *Token) (inst *term) {
|
func newMultiplyTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priProduct,
|
priority: priProduct,
|
||||||
@@ -21,13 +21,7 @@ func newMultiplyTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
func mulValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsString(leftValue) && IsInteger(rightValue) {
|
if IsString(leftValue) && IsInteger(rightValue) {
|
||||||
s, _ := leftValue.(string)
|
s, _ := leftValue.(string)
|
||||||
n, _ := rightValue.(int64)
|
n, _ := rightValue.(int64)
|
||||||
@@ -43,16 +37,26 @@ func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
|||||||
v = leftInt * rightInt
|
v = leftInt * rightInt
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = prodTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return mulValues(prodTerm, leftValue, rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
//-------- divide term
|
//-------- divide term
|
||||||
|
|
||||||
func newDivideTerm(tk *Token) (inst *term) {
|
func newDivideTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priProduct,
|
priority: priProduct,
|
||||||
@@ -60,13 +64,7 @@ func newDivideTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||||
d := numAsFloat(rightValue)
|
d := numAsFloat(rightValue)
|
||||||
@@ -91,6 +89,16 @@ func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return divValues(opTerm, leftValue, rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
//-------- divide as float term
|
//-------- divide as float term
|
||||||
|
|
||||||
func newDivideAsFloatTerm(tk *Token) (inst *term) {
|
func newDivideAsFloatTerm(tk *Token) (inst *term) {
|
||||||
@@ -127,21 +135,14 @@ func evalDivideAsFloat(ctx ExprContext, floatDivTerm *term) (v any, err error) {
|
|||||||
|
|
||||||
func newRemainderTerm(tk *Token) (inst *term) {
|
func newRemainderTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priProduct,
|
priority: priProduct,
|
||||||
evalFunc: evalReminder,
|
evalFunc: evalRemainder,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func remainderValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
func evalReminder(ctx ExprContext, ramainderTerm *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = ramainderTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||||
rightInt, _ := rightValue.(int64)
|
rightInt, _ := rightValue.(int64)
|
||||||
if rightInt == 0 {
|
if rightInt == 0 {
|
||||||
@@ -151,11 +152,21 @@ func evalReminder(ctx ExprContext, ramainderTerm *term) (v any, err error) {
|
|||||||
v = leftInt % rightInt
|
v = leftInt % rightInt
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = ramainderTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalRemainder(ctx ExprContext, remainderTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = remainderTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return remainderValues(remainderTerm, leftValue, rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymStar, newMultiplyTerm)
|
registerTermConstructor(SymStar, newMultiplyTerm)
|
||||||
|
|||||||
+18
-5
@@ -34,12 +34,16 @@ func newRangeTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func changeColonToRange(t *term) {
|
||||||
|
if t.tk.IsSymbol(SymColon) {
|
||||||
|
t.tk.Sym = SymRange
|
||||||
|
t.evalFunc = evalRange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
// if err = self.checkOperands(); err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
if len(opTerm.children) == 0 {
|
if len(opTerm.children) == 0 {
|
||||||
leftValue = int64(0)
|
leftValue = int64(0)
|
||||||
rightValue = int64(-1)
|
rightValue = int64(-1)
|
||||||
@@ -52,7 +56,8 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !(IsInteger(leftValue) && IsInteger(rightValue)) {
|
if !(IsInteger(leftValue) && IsInteger(rightValue)) {
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
// err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
err = errRangeInvalidSpecification(opTerm)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +68,15 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func errRangeInvalidSpecification(t *term) error {
|
||||||
|
return t.Errorf("invalid range specification")
|
||||||
|
}
|
||||||
|
|
||||||
|
func errRangeUnexpectedExpression(t *term) error {
|
||||||
|
return t.Errorf("unexpected range expression")
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymColon, newRangeTerm)
|
registerTermConstructor(SymRange, newRangeTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-15
@@ -21,13 +21,7 @@ func newPlusTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
func sumValues(plusTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsString(leftValue) && isNumberString(rightValue)) || (IsString(rightValue) && isNumberString(leftValue)) {
|
if (IsString(leftValue) && isNumberString(rightValue)) || (IsString(rightValue) && isNumberString(leftValue)) {
|
||||||
v = fmt.Sprintf("%v%v", leftValue, rightValue)
|
v = fmt.Sprintf("%v%v", leftValue, rightValue)
|
||||||
} else if IsNumber(leftValue) && IsNumber(rightValue) {
|
} else if IsNumber(leftValue) && IsNumber(rightValue) {
|
||||||
@@ -59,10 +53,22 @@ func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
|||||||
c := leftDict.clone()
|
c := leftDict.clone()
|
||||||
c.merge(rightDict)
|
c.merge(rightDict)
|
||||||
v = c
|
v = c
|
||||||
|
} else if isFraction(leftValue) && isFraction(rightValue) {
|
||||||
|
v, err = sumAnyFract(leftValue, rightValue)
|
||||||
} else {
|
} else {
|
||||||
err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return sumValues(plusTerm, leftValue, rightValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------- minus term
|
//-------- minus term
|
||||||
@@ -77,13 +83,7 @@ func newMinusTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
func diffValues(minusTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||||
v = numAsFloat(leftValue) - numAsFloat(rightValue)
|
v = numAsFloat(leftValue) - numAsFloat(rightValue)
|
||||||
@@ -110,6 +110,16 @@ func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return diffValues(minusTerm, leftValue, rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymPlus, newPlusTerm)
|
registerTermConstructor(SymPlus, newPlusTerm)
|
||||||
|
|||||||
@@ -6,10 +6,46 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- parser
|
//-------- parser
|
||||||
|
|
||||||
|
type parserContext uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
parserNoFlags = 0
|
||||||
|
allowMultiExpr parserContext = 1 << iota
|
||||||
|
allowVarRef
|
||||||
|
selectorContext
|
||||||
|
listContext // squareContext for list
|
||||||
|
indexContext // squareContext for index
|
||||||
|
allowIndex // allow index in squareContext
|
||||||
|
squareContext = listContext | indexContext // Square parenthesis for list or index
|
||||||
|
)
|
||||||
|
|
||||||
|
func hasFlag[T constraints.Unsigned](set T, singleFlag T) bool {
|
||||||
|
return (set & singleFlag) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFlags[T constraints.Unsigned](set T, flags T) T {
|
||||||
|
return set | flags
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFlagsCond[T constraints.Unsigned](set T, flags T, cond bool) (newSet T) {
|
||||||
|
if cond {
|
||||||
|
newSet = set | flags
|
||||||
|
} else {
|
||||||
|
newSet = set
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func remFlags[T constraints.Unsigned](set T, flags T) T {
|
||||||
|
return set & (^flags)
|
||||||
|
}
|
||||||
|
|
||||||
type parser struct {
|
type parser struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,13 +60,13 @@ func (parser *parser) Next(scanner *scanner) (tk *Token) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
|
func (parser *parser) parseFuncCall(scanner *scanner, ctx parserContext, tk *Token) (tree *term, err error) {
|
||||||
args := make([]*term, 0, 10)
|
args := make([]*term, 0, 10)
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil {
|
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
@@ -77,7 +113,7 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
if tk.Sym == SymEqual {
|
if tk.Sym == SymEqual {
|
||||||
var paramExpr *ast
|
var paramExpr *ast
|
||||||
defaultParamsStarted = true
|
defaultParamsStarted = true
|
||||||
if paramExpr, err = parser.parseItem(scanner, false, SymComma, SymClosedRound); err != nil {
|
if paramExpr, err = parser.parseItem(scanner, parserNoFlags, SymComma, SymClosedRound); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
param.forceChild(paramExpr.root)
|
param.forceChild(paramExpr.root)
|
||||||
@@ -100,7 +136,7 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
tk = parser.Next(scanner)
|
tk = parser.Next(scanner)
|
||||||
if tk.IsSymbol(SymOpenBrace) {
|
if tk.IsSymbol(SymOpenBrace) {
|
||||||
body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace)
|
body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymClosedBrace)
|
||||||
} else {
|
} else {
|
||||||
err = tk.ErrorExpectedGot("{")
|
err = tk.ErrorExpectedGot("{")
|
||||||
}
|
}
|
||||||
@@ -126,27 +162,33 @@ func paramAlreadyDefined(args []*term, param *term) (position int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) {
|
func (parser *parser) parseList(scanner *scanner, ctx parserContext) (listTerm *term, err error) {
|
||||||
r, c := scanner.lastPos()
|
r, c := scanner.lastPos()
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
|
itemCtx := remFlags(ctx, allowIndex)
|
||||||
for lastSym != SymClosedSquare && lastSym != SymEos {
|
for lastSym != SymClosedSquare && lastSym != SymEos {
|
||||||
var subTree *ast
|
|
||||||
zeroRequired := scanner.current.Sym == SymColon
|
zeroRequired := scanner.current.Sym == SymColon
|
||||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
var itemTree *ast
|
||||||
root := subTree.root
|
if itemTree, err = parser.parseItem(scanner, itemCtx, SymComma, SymClosedSquare); err == nil {
|
||||||
|
root := itemTree.root
|
||||||
if root != nil {
|
if root != nil {
|
||||||
if !parsingIndeces && root.symbol() == SymColon {
|
if hasFlag(ctx, allowIndex) && root.symbol() == SymColon {
|
||||||
err = root.Errorf("unexpected range expression")
|
changeColonToRange(root)
|
||||||
|
}
|
||||||
|
if !hasFlag(ctx, allowIndex) && root.symbol() == SymRange {
|
||||||
|
// err = root.Errorf("unexpected range expression")
|
||||||
|
err = errRangeUnexpectedExpression(root)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
args = append(args, root)
|
args = append(args, root)
|
||||||
if parsingIndeces && root.symbol() == SymColon && zeroRequired { //len(root.children) == 0 {
|
if hasFlag(ctx, allowIndex) && root.symbol() == SymRange && zeroRequired { //len(root.children) == 0 {
|
||||||
if len(root.children) == 1 {
|
if len(root.children) == 1 {
|
||||||
root.children = append(root.children, root.children[0])
|
root.children = append(root.children, root.children[0])
|
||||||
} else if len(root.children) > 1 {
|
} else if len(root.children) > 1 {
|
||||||
err = root.Errorf("invalid range specification")
|
// err = root.Errorf("invalid range specification")
|
||||||
|
err = errRangeInvalidSpecification(root)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0))
|
zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0))
|
||||||
@@ -163,26 +205,28 @@ func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarR
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
lastSym = scanner.Previous().Sym
|
lastSym = scanner.Previous().Sym
|
||||||
itemExpected = lastSym == SymComma
|
if itemExpected = lastSym == SymComma; itemExpected {
|
||||||
|
remFlags(ctx, allowIndex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if lastSym != SymClosedSquare {
|
if lastSym != SymClosedSquare {
|
||||||
err = scanner.Previous().ErrorExpectedGot("]")
|
err = scanner.Previous().ErrorExpectedGot("]")
|
||||||
} else {
|
} else {
|
||||||
subtree = newListTerm(r, c, args)
|
listTerm = newListTerm(r, c, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
func (parser *parser) parseIterDef(scanner *scanner, ctx parserContext) (subtree *term, err error) {
|
||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
|
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err == nil {
|
||||||
if subTree.root != nil {
|
if subTree.root != nil {
|
||||||
args = append(args, subTree.root)
|
args = append(args, subTree.root)
|
||||||
} else if itemExpected {
|
} else if itemExpected {
|
||||||
@@ -228,7 +272,7 @@ func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subtree *term, err error) {
|
||||||
args := make(map[any]*term, 0)
|
args := make(map[any]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
@@ -245,7 +289,7 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
|
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedBrace); err == nil {
|
||||||
if subTree.root != nil {
|
if subTree.root != nil {
|
||||||
args[key] = subTree.root
|
args[key] = subTree.root
|
||||||
} else /*if key != nil*/ {
|
} else /*if key != nil*/ {
|
||||||
@@ -264,15 +308,15 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
|
|||||||
err = scanner.Previous().ErrorExpectedGot("}")
|
err = scanner.Previous().ErrorExpectedGot("}")
|
||||||
} else {
|
} else {
|
||||||
subtree = newDictTerm(args)
|
subtree = newDictTerm(args)
|
||||||
// subtree = newMapTerm(args)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
|
func (parser *parser) parseSelectorCase(scanner *scanner, ctx parserContext, defaultCase bool) (caseTerm *term, err error) {
|
||||||
var filterList *term
|
var filterList *term
|
||||||
var caseExpr *ast
|
var caseExpr *ast
|
||||||
|
ctx = remFlags(ctx, allowIndex)
|
||||||
tk := parser.Next(scanner)
|
tk := parser.Next(scanner)
|
||||||
startRow := tk.row
|
startRow := tk.row
|
||||||
startCol := tk.col
|
startCol := tk.col
|
||||||
@@ -281,7 +325,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
|
|||||||
err = tk.Errorf("case list in default clause")
|
err = tk.Errorf("case list in default clause")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil {
|
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex)); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tk = parser.Next(scanner)
|
tk = parser.Next(scanner)
|
||||||
@@ -292,7 +336,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tk.Sym == SymOpenBrace {
|
if tk.Sym == SymOpenBrace {
|
||||||
if caseExpr, err = parser.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil {
|
if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, SymClosedBrace); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -318,25 +362,28 @@ func addSelectorCase(selectorTerm, caseTerm *term) {
|
|||||||
caseTerm.parent = selectorTerm
|
caseTerm.parent = selectorTerm
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
|
func (parser *parser) parseSelector(scanner *scanner, tree *ast, ctx parserContext) (selectorTerm *term, err error) {
|
||||||
var caseTerm *term
|
var caseTerm *term
|
||||||
|
|
||||||
|
ctx = remFlags(ctx, allowIndex)
|
||||||
tk := scanner.makeToken(SymSelector, '?')
|
tk := scanner.makeToken(SymSelector, '?')
|
||||||
if selectorTerm, err = tree.addToken(tk); err != nil {
|
if selectorTerm, err = tree.addToken(tk); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, false); err == nil {
|
if caseTerm, err = parser.parseSelectorCase(scanner, ctx|allowVarRef, false); err == nil {
|
||||||
addSelectorCase(selectorTerm, caseTerm)
|
addSelectorCase(selectorTerm, caseTerm)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
func (parser *parser) parseItem(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
return parser.parseGeneral(scanner, false, allowVarRef, termSymbols...)
|
return parser.parseGeneral(scanner, ctx|allowVarRef, termSymbols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
return parser.parseGeneral(scanner, true, false, termSymbols...)
|
termSymbols = append(termSymbols, SymEos)
|
||||||
|
return parser.parseGeneral(scanner, allowMultiExpr, termSymbols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func couldBeACollection(t *term) bool {
|
func couldBeACollection(t *term) bool {
|
||||||
@@ -347,15 +394,22 @@ func couldBeACollection(t *term) bool {
|
|||||||
return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable
|
return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable
|
||||||
}
|
}
|
||||||
|
|
||||||
// func areSymbolsOutOfCtx(tk *Token, ctxTerm *term, syms ...Symbol) bool {
|
func listSubTree(tree *ast, listTerm *term, allowIndeces bool) (root *term, err error) {
|
||||||
// var areOut = false
|
var tk *Token
|
||||||
// if ctxTerm != nil {
|
if allowIndeces {
|
||||||
// areOut = tk.IsOneOf(syms)
|
tk = NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
||||||
// }
|
root = newTerm(tk)
|
||||||
// return areOut
|
if err = tree.addTerm(root); err == nil {
|
||||||
// }
|
err = tree.addTerm(listTerm)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root = listTerm
|
||||||
|
err = tree.addTerm(listTerm)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
var selectorTerm *term = nil
|
var selectorTerm *term = nil
|
||||||
var currentTerm *term = nil
|
var currentTerm *term = nil
|
||||||
var tk *Token
|
var tk *Token
|
||||||
@@ -369,7 +423,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
if tk.Sym == SymSemiColon {
|
if tk.Sym == SymSemiColon {
|
||||||
if allowForest {
|
if hasFlag(ctx, allowMultiExpr) {
|
||||||
tree.ToForest()
|
tree.ToForest()
|
||||||
firstToken = true
|
firstToken = true
|
||||||
currentTerm = nil
|
currentTerm = nil
|
||||||
@@ -387,6 +441,9 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
tk.Sym = SymChangeSign
|
tk.Sym = SymChangeSign
|
||||||
} else if tk.Sym == SymPlus {
|
} else if tk.Sym == SymPlus {
|
||||||
tk.Sym = SymUnchangeSign
|
tk.Sym = SymUnchangeSign
|
||||||
|
} else if tk.IsSymbol(SymExclamation) {
|
||||||
|
err = tk.Errorf("postfix opertor %q requires an operand on its left", tk)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
firstToken = false
|
firstToken = false
|
||||||
}
|
}
|
||||||
@@ -394,47 +451,36 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
switch tk.Sym {
|
switch tk.Sym {
|
||||||
case SymOpenRound:
|
case SymOpenRound:
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil {
|
if subTree, err = parser.parseGeneral(scanner, ctx, SymClosedRound); err == nil {
|
||||||
subTree.root.priority = priValue
|
subTree.root.priority = priValue
|
||||||
err = tree.addTerm(newExprTerm(subTree.root))
|
err = tree.addTerm(newExprTerm(subTree.root))
|
||||||
currentTerm = subTree.root
|
currentTerm = subTree.root
|
||||||
}
|
}
|
||||||
case SymFuncCall:
|
case SymFuncCall:
|
||||||
var funcCallTerm *term
|
var funcCallTerm *term
|
||||||
if funcCallTerm, err = parser.parseFuncCall(scanner, allowVarRef, tk); err == nil {
|
if funcCallTerm, err = parser.parseFuncCall(scanner, ctx, tk); err == nil {
|
||||||
err = tree.addTerm(funcCallTerm)
|
err = tree.addTerm(funcCallTerm)
|
||||||
currentTerm = funcCallTerm
|
currentTerm = funcCallTerm
|
||||||
}
|
}
|
||||||
case SymOpenSquare:
|
case SymOpenSquare:
|
||||||
var listTerm *term
|
var listTerm *term
|
||||||
parsingIndeces := couldBeACollection(currentTerm)
|
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
|
||||||
if listTerm, err = parser.parseList(scanner, parsingIndeces, allowVarRef); err == nil {
|
if listTerm, err = parser.parseList(scanner, newCtx); err == nil {
|
||||||
if parsingIndeces {
|
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||||
indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
|
||||||
indexTerm := newTerm(indexTk)
|
|
||||||
if err = tree.addTerm(indexTerm); err == nil {
|
|
||||||
err = tree.addTerm(listTerm)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = tree.addTerm(listTerm)
|
|
||||||
}
|
|
||||||
currentTerm = listTerm
|
|
||||||
}
|
}
|
||||||
case SymOpenBrace:
|
case SymOpenBrace:
|
||||||
if currentTerm != nil && currentTerm.symbol() == SymColon {
|
if currentTerm != nil && currentTerm.symbol() == SymColon {
|
||||||
err = currentTerm.Errorf(`selector-case outside of a selector context`)
|
err = currentTerm.Errorf(`selector-case outside of a selector context`)
|
||||||
} else {
|
} else {
|
||||||
var mapTerm *term
|
var mapTerm *term
|
||||||
if mapTerm, err = parser.parseDictionary(scanner, allowVarRef); err == nil {
|
if mapTerm, err = parser.parseDictionary(scanner, ctx); err == nil {
|
||||||
err = tree.addTerm(mapTerm)
|
err = tree.addTerm(mapTerm)
|
||||||
currentTerm = mapTerm
|
currentTerm = mapTerm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case SymEqual:
|
case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual:
|
||||||
// if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
firstToken = true
|
firstToken = true
|
||||||
// }
|
|
||||||
case SymFuncDef:
|
case SymFuncDef:
|
||||||
var funcDefTerm *term
|
var funcDefTerm *term
|
||||||
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
|
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
|
||||||
@@ -443,24 +489,25 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
}
|
}
|
||||||
case SymDollarRound:
|
case SymDollarRound:
|
||||||
var iterDefTerm *term
|
var iterDefTerm *term
|
||||||
if iterDefTerm, err = parser.parseIterDef(scanner, allowVarRef); err == nil {
|
if iterDefTerm, err = parser.parseIterDef(scanner, ctx); err == nil {
|
||||||
err = tree.addTerm(iterDefTerm)
|
err = tree.addTerm(iterDefTerm)
|
||||||
currentTerm = iterDefTerm
|
currentTerm = iterDefTerm
|
||||||
}
|
}
|
||||||
case SymIdentifier:
|
case SymIdentifier:
|
||||||
if tk.source[0] == '@' && !allowVarRef {
|
if tk.source[0] == '@' && !hasFlag(ctx, allowVarRef) {
|
||||||
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
||||||
} else {
|
} else {
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
}
|
}
|
||||||
case SymQuestion:
|
case SymQuestion:
|
||||||
if selectorTerm, err = parser.parseSelector(scanner, tree, allowVarRef); err == nil {
|
if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil {
|
||||||
currentTerm = selectorTerm
|
currentTerm = selectorTerm
|
||||||
|
addFlags(ctx, selectorContext)
|
||||||
}
|
}
|
||||||
case SymColon, SymDoubleColon:
|
case SymColon, SymDoubleColon:
|
||||||
var caseTerm *term
|
var caseTerm *term
|
||||||
if selectorTerm != nil {
|
if selectorTerm != nil {
|
||||||
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
|
if caseTerm, err = parser.parseSelectorCase(scanner, ctx, tk.Sym == SymDoubleColon); err == nil {
|
||||||
addSelectorCase(selectorTerm, caseTerm)
|
addSelectorCase(selectorTerm, caseTerm)
|
||||||
currentTerm = caseTerm
|
currentTerm = caseTerm
|
||||||
if tk.Sym == SymDoubleColon {
|
if tk.Sym == SymDoubleColon {
|
||||||
@@ -468,80 +515,38 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// if hasFlag(ctx, allowIndex) {
|
||||||
|
// tk.Sym = SymRange
|
||||||
|
// }
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
}
|
}
|
||||||
if tk.IsSymbol(SymColon) {
|
if tk.IsOneOfA(SymColon, SymRange) {
|
||||||
// Colon outside a selector term acts like a separator
|
// Colon outside a selector term acts like a separator
|
||||||
firstToken = true
|
firstToken = true
|
||||||
}
|
}
|
||||||
case SymPlusEqual, SymMinusEqual, SymStarEqual:
|
|
||||||
currentTerm, err = parser.expandOpAssign(scanner, tree, tk, allowVarRef)
|
|
||||||
default:
|
default:
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
|
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
|
||||||
selectorTerm = nil
|
selectorTerm = nil
|
||||||
|
remFlags(ctx, selectorContext)
|
||||||
}
|
}
|
||||||
// lastSym = tk.Sym
|
// lastSym = tk.Sym
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = tk.Error()
|
if !tk.IsOneOf(termSymbols) {
|
||||||
}
|
var symDesc string
|
||||||
return
|
if tk.IsSymbol(SymError) {
|
||||||
}
|
symDesc = tk.ErrorText()
|
||||||
|
} else {
|
||||||
// func checkPrevSymbol(lastSym, wantedSym Symbol, tk *Token) (err error) {
|
symDesc = SymToString(tk.Sym)
|
||||||
// if lastSym != wantedSym {
|
|
||||||
// err = fmt.Errorf(`assign operator (%q) must be preceded by a variable`, tk.source)
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (parser *parser) expandOpAssign(scanner *scanner, tree *ast, tk *Token, allowVarRef bool) (t *term, err error) {
|
|
||||||
var opSym Symbol
|
|
||||||
var opString string
|
|
||||||
|
|
||||||
if tree.root != nil {
|
|
||||||
switch tk.Sym {
|
|
||||||
case SymPlusEqual:
|
|
||||||
opString = "+"
|
|
||||||
opSym = SymPlus
|
|
||||||
case SymMinusEqual:
|
|
||||||
opString = "-"
|
|
||||||
opSym = SymMinus
|
|
||||||
case SymStarEqual:
|
|
||||||
opString = "*"
|
|
||||||
opSym = SymStar
|
|
||||||
default:
|
|
||||||
err = tk.Errorf("unsopported operator %q", tk.source)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
leftExpr := tree.root.Clone()
|
|
||||||
leftExpr.setParent(nil)
|
|
||||||
if t, err = tree.addToken(NewToken(tk.row, tk.col, SymEqual, "=")); err == nil {
|
|
||||||
t = leftExpr
|
|
||||||
if err = tree.addTerm(leftExpr); err == nil {
|
|
||||||
if t, err = tree.addToken(NewToken(tk.row, tk.col, opSym, opString)); err == nil {
|
|
||||||
|
|
||||||
var subTree *ast
|
|
||||||
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare); err == nil {
|
|
||||||
if scanner.Previous().IsOneOfA(SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare) {
|
|
||||||
if err = scanner.UnreadToken(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subTree.root.priority = priValue
|
|
||||||
err = tree.addTerm(newExprTerm(subTree.root))
|
|
||||||
t = subTree.root
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
err = tk.ErrorExpectedGotStringWithPrefix("expected one of", SymListToString(termSymbols, true), symDesc)
|
||||||
|
} else {
|
||||||
|
err = tk.Error()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
err = tk.Errorf("left operand of %q must be a variable or a variable expression", tk)
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-5
@@ -40,9 +40,9 @@ func DefaultTranslations() map[Symbol]Symbol {
|
|||||||
SymKwAnd: SymAnd,
|
SymKwAnd: SymAnd,
|
||||||
SymDoubleVertBar: SymOr,
|
SymDoubleVertBar: SymOr,
|
||||||
SymKwOr: SymOr,
|
SymKwOr: SymOr,
|
||||||
SymTilde: SymNot,
|
// SymTilde: SymNot,
|
||||||
SymKwNot: SymNot,
|
SymKwNot: SymNot,
|
||||||
SymLessGreater: SymNotEqual,
|
SymLessGreater: SymNotEqual,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +149,8 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
|||||||
if next, _ := scanner.peek(); next == '*' {
|
if next, _ := scanner.peek(); next == '*' {
|
||||||
scanner.readChar()
|
scanner.readChar()
|
||||||
tk = scanner.fetchBlockComment()
|
tk = scanner.fetchBlockComment()
|
||||||
|
} else if next, _ = scanner.peek(); next == '=' {
|
||||||
|
tk = scanner.moveOn(SymSlashEqual, ch, next)
|
||||||
} else if next == '/' {
|
} else if next == '/' {
|
||||||
scanner.readChar()
|
scanner.readChar()
|
||||||
tk = scanner.fetchOnLineComment()
|
tk = scanner.fetchOnLineComment()
|
||||||
@@ -458,7 +460,7 @@ func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) {
|
|||||||
tk = scanner.makeErrorToken(err)
|
tk = scanner.makeErrorToken(err)
|
||||||
} else {
|
} else {
|
||||||
var value any
|
var value any
|
||||||
err = scanner.sync(err) // TODO: Check this function
|
_ = scanner.sync(err) // TODO: Check this function
|
||||||
txt := sb.String()
|
txt := sb.String()
|
||||||
if sym == SymFloat {
|
if sym == SymFloat {
|
||||||
value, err = strconv.ParseFloat(txt, 64)
|
value, err = strconv.ParseFloat(txt, 64)
|
||||||
@@ -590,7 +592,7 @@ func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
|
|||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
tk = scanner.makeErrorToken(errors.New("missing string termination \""))
|
tk = scanner.makeErrorToken(errors.New(string(termCh)))
|
||||||
} else {
|
} else {
|
||||||
tk = scanner.makeErrorToken(err)
|
tk = scanner.makeErrorToken(err)
|
||||||
}
|
}
|
||||||
|
|||||||
+181
@@ -0,0 +1,181 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// Symbol.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var symbolMap map[Symbol]symbolSpec
|
||||||
|
|
||||||
|
type symbolClass int16
|
||||||
|
|
||||||
|
const (
|
||||||
|
symClassOperator symbolClass = iota
|
||||||
|
symClassPostOp
|
||||||
|
symClassIdentifier
|
||||||
|
symClassDelimiter
|
||||||
|
symClassParenthesis
|
||||||
|
symClassDeclaration
|
||||||
|
symClassValue
|
||||||
|
symClassOther
|
||||||
|
)
|
||||||
|
|
||||||
|
type symbolSpec struct {
|
||||||
|
repr string
|
||||||
|
kind symbolClass
|
||||||
|
}
|
||||||
|
|
||||||
|
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: '%='
|
||||||
|
// SymChangeSign
|
||||||
|
// SymUnchangeSign
|
||||||
|
// SymIdentifier
|
||||||
|
// SymBool
|
||||||
|
// SymInteger
|
||||||
|
// SymVariable
|
||||||
|
// SymFloat
|
||||||
|
// SymFraction
|
||||||
|
// SymString
|
||||||
|
// SymIterator
|
||||||
|
// SymOr: "or",
|
||||||
|
// SymAnd: "and",
|
||||||
|
// SymNot: "not",
|
||||||
|
// SymComment
|
||||||
|
// SymFuncCall
|
||||||
|
// SymFuncDef
|
||||||
|
// SymList
|
||||||
|
// SymDict
|
||||||
|
// SymIndex
|
||||||
|
// SymExpression
|
||||||
|
// SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||||
|
// SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||||
|
// // SymOpenComment // 0: '/*'
|
||||||
|
// // 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},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SymToString(sym Symbol) string {
|
||||||
|
if s, ok := symbolMap[sym]; ok {
|
||||||
|
return s.repr
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func SymListToString(symList []Symbol, quote bool) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
if len(symList) == 0 {
|
||||||
|
sb.WriteString("<nothing>")
|
||||||
|
} else {
|
||||||
|
for _, sym := range symList {
|
||||||
|
if sb.Len() > 0 {
|
||||||
|
sb.WriteByte(',')
|
||||||
|
sb.WriteByte(' ')
|
||||||
|
}
|
||||||
|
if quote {
|
||||||
|
sb.WriteByte('`')
|
||||||
|
}
|
||||||
|
sb.WriteString(SymToString(sym))
|
||||||
|
if quote {
|
||||||
|
sb.WriteByte('`')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func StringEndsWithOperator(s string) bool {
|
||||||
|
return endingOperator(s) != SymNone
|
||||||
|
}
|
||||||
|
|
||||||
|
func endingOperator(s string) (sym Symbol) {
|
||||||
|
sym = SymNone
|
||||||
|
lower := strings.ToLower(s)
|
||||||
|
for symbol, spec := range symbolMap {
|
||||||
|
if spec.kind == symClassOperator && strings.HasSuffix(lower, spec.repr) {
|
||||||
|
sym = symbol
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -69,6 +69,8 @@ const (
|
|||||||
SymDoubleDot // 58: '..'
|
SymDoubleDot // 58: '..'
|
||||||
SymTripleDot // 59: '...'
|
SymTripleDot // 59: '...'
|
||||||
SymStarEqual // 60: '*='
|
SymStarEqual // 60: '*='
|
||||||
|
SymSlashEqual // 61: '/='
|
||||||
|
SymPercEqual // 62: '%='
|
||||||
SymChangeSign
|
SymChangeSign
|
||||||
SymUnchangeSign
|
SymUnchangeSign
|
||||||
SymIdentifier
|
SymIdentifier
|
||||||
@@ -88,6 +90,7 @@ const (
|
|||||||
SymList
|
SymList
|
||||||
SymDict
|
SymDict
|
||||||
SymIndex
|
SymIndex
|
||||||
|
SymRange // [index : index]
|
||||||
SymExpression
|
SymExpression
|
||||||
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||||
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ func TestFuncBase(t *testing.T) {
|
|||||||
/* 16 */ {`isString("3" + 1)`, true, nil},
|
/* 16 */ {`isString("3" + 1)`, true, nil},
|
||||||
/* 17 */ {`isList(["3", 1])`, true, nil},
|
/* 17 */ {`isList(["3", 1])`, true, nil},
|
||||||
/* 18 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
/* 18 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
||||||
/* 19 */ {`isFract(1|3)`, true, nil},
|
/* 19 */ {`isFract(1:3)`, true, nil},
|
||||||
/* 20 */ {`isFract(3|1)`, false, nil},
|
/* 20 */ {`isFract(3:1)`, false, nil},
|
||||||
/* 21 */ {`isRational(3|1)`, true, nil},
|
/* 21 */ {`isRational(3:1)`, true, nil},
|
||||||
/* 22 */ {`fract("2.2(3)")`, newFraction(67, 30), nil},
|
/* 22 */ {`fract("2.2(3)")`, newFraction(67, 30), nil},
|
||||||
/* 23 */ {`fract("1.21(3)")`, newFraction(91, 75), nil},
|
/* 23 */ {`fract("1.21(3)")`, newFraction(91, 75), nil},
|
||||||
/* 24 */ {`fract(1.21(3))`, newFraction(91, 75), nil},
|
/* 24 */ {`fract(1.21(3))`, newFraction(91, 75), nil},
|
||||||
@@ -41,26 +41,27 @@ func TestFuncBase(t *testing.T) {
|
|||||||
/* 27 */ {`dec(2.0)`, float64(2), nil},
|
/* 27 */ {`dec(2.0)`, float64(2), nil},
|
||||||
/* 28 */ {`dec("2.0")`, float64(2), nil},
|
/* 28 */ {`dec("2.0")`, float64(2), nil},
|
||||||
/* 29 */ {`dec(true)`, float64(1), nil},
|
/* 29 */ {`dec(true)`, float64(1), nil},
|
||||||
/* 30 */ {`dec(true")`, nil, `[1:11] missing string termination "`},
|
/* 30 */ {`dec(true")`, nil, "[1:11] expected one of `,`, `)`, got `\"`"},
|
||||||
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`},
|
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`},
|
||||||
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`},
|
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`},
|
||||||
/* 33 */ {`isBool(false)`, true, nil},
|
/* 33 */ {`isBool(false)`, true, nil},
|
||||||
/* 34 */ {`fract(1|2)`, newFraction(1, 2), nil},
|
/* 34 */ {`fract(1:2)`, newFraction(1, 2), nil},
|
||||||
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
|
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
|
||||||
/* 36 */ {`bool(2)`, true, nil},
|
/* 36 */ {`bool(2)`, true, nil},
|
||||||
/* 37 */ {`bool(1|2)`, true, nil},
|
/* 37 */ {`bool(1:2)`, true, nil},
|
||||||
/* 38 */ {`bool(1.0)`, true, nil},
|
/* 38 */ {`bool(1.0)`, true, nil},
|
||||||
/* 39 */ {`bool("1")`, true, nil},
|
/* 39 */ {`bool("1")`, true, nil},
|
||||||
/* 40 */ {`bool(false)`, false, nil},
|
/* 40 */ {`bool(false)`, false, nil},
|
||||||
/* 41 */ {`bool([1])`, nil, `bool(): can't convert list to bool`},
|
/* 41 */ {`bool([1])`, nil, `bool(): can't convert list to bool`},
|
||||||
/* 42 */ {`dec(false)`, float64(0), nil},
|
/* 42 */ {`dec(false)`, float64(0), nil},
|
||||||
/* 43 */ {`dec(1|2)`, float64(0.5), nil},
|
/* 43 */ {`dec(1:2)`, float64(0.5), nil},
|
||||||
/* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
|
/* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
|
||||||
|
/* 45 */ {`eval("a=3"); a`, int64(3), nil},
|
||||||
// /* 45 */ {`string([1])`, nil, `string(): can't convert list to string`},
|
// /* 45 */ {`string([1])`, nil, `string(): can't convert list to string`},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("EXPR_PATH", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
//runTestSuiteSpec(t, section, inputs, 10)
|
// runTestSuiteSpec(t, section, inputs, 45)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func TestFuncString(t *testing.T) {
|
|||||||
/* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil},
|
/* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil},
|
||||||
/* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil},
|
/* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil},
|
||||||
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil},
|
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil},
|
||||||
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, `strJoin(): the "separator" parameter must be a string, got a integer (1)`},
|
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, `strJoin(): the "separator" parameter must be a string, got an integer (1)`},
|
||||||
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, `strJoin(): expected string, got integer (2)`},
|
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, `strJoin(): expected string, got integer (2)`},
|
||||||
/* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil},
|
/* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil},
|
||||||
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
|
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
|
||||||
|
|||||||
+6
-6
@@ -17,13 +17,13 @@ func TestExpr(t *testing.T) {
|
|||||||
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
|
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
|
||||||
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
||||||
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
||||||
/* 6 */ {`a=3; a+=1`, int64(4), nil},
|
/* 6 */ {`a=3; a+=1; a`, int64(4), nil},
|
||||||
/* 7 */ {`a=3; a-=1`, int64(2), nil},
|
/* 7 */ {`a=3; a-=1; a`, int64(2), nil},
|
||||||
/* 8 */ {`a=3; a*=2`, int64(6), nil},
|
/* 8 */ {`a=3; a*=2; a`, int64(6), nil},
|
||||||
/* 9 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil},
|
/* 9 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil},
|
||||||
/* 10 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil},
|
/* 10 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil},
|
||||||
/* 11 */ {`a=3; a/=2`, nil, `[1:8] left operand of "=" must be a variable or a collection's item`},
|
/* 11 */ {`a=3; a/=2`, int64(1), nil},
|
||||||
/* 12 */ {`*=2`, nil, `[1:2] left operand of "*=" must be a variable or a variable expression`},
|
/* 12 */ {`*=2`, nil, `[1:2] infix operator "*=" requires two non-nil operands, got 1`},
|
||||||
/* 13 */ {`a=3; a*=2+1; a`, int64(9), nil},
|
/* 13 */ {`a=3; a*=2+1; a`, int64(9), nil},
|
||||||
/* 14 */ {`a=3; (a*=2)+1; a`, int64(6), nil},
|
/* 14 */ {`a=3; (a*=2)+1; a`, int64(6), nil},
|
||||||
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
|
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
|
||||||
@@ -44,6 +44,6 @@ func TestExpr(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 17)
|
// runTestSuiteSpec(t, section, inputs, 6, 7, 8, 9)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-20
@@ -11,35 +11,39 @@ import (
|
|||||||
func TestFractionsParser(t *testing.T) {
|
func TestFractionsParser(t *testing.T) {
|
||||||
section := "Fraction"
|
section := "Fraction"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`1|2`, newFraction(1, 2), nil},
|
/* 1 */ {`1:2`, newFraction(1, 2), nil},
|
||||||
/* 2 */ {`1|2 + 1`, newFraction(3, 2), nil},
|
/* 2 */ {`1:2 + 1`, newFraction(3, 2), nil},
|
||||||
/* 3 */ {`1|2 - 1`, newFraction(-1, 2), nil},
|
/* 3 */ {`1:2 - 1`, newFraction(-1, 2), nil},
|
||||||
/* 4 */ {`1|2 * 1`, newFraction(1, 2), nil},
|
/* 4 */ {`1:2 * 1`, newFraction(1, 2), nil},
|
||||||
/* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil},
|
/* 5 */ {`1:2 * 2:3`, newFraction(2, 6), nil},
|
||||||
/* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil},
|
/* 6 */ {`1:2 / 2:3`, newFraction(3, 4), nil},
|
||||||
/* 7 */ {`1|"5"`, nil, `denominator must be integer, got string (5)`},
|
/* 7 */ {`1:"5"`, nil, `denominator must be integer, got string (5)`},
|
||||||
/* 8 */ {`"1"|5`, nil, `numerator must be integer, got string (1)`},
|
/* 8 */ {`"1":5`, nil, `numerator must be integer, got string (1)`},
|
||||||
/* 9 */ {`1|+5`, nil, `[1:3] infix operator "|" requires two non-nil operands, got 1`},
|
/* 9 */ {`1:+5`, newFraction(1, 5), nil},
|
||||||
/* 10 */ {`1|(-2)`, newFraction(-1, 2), nil},
|
/* 10 */ {`1:(-2)`, newFraction(-1, 2), nil},
|
||||||
/* 11 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil},
|
/* 11 */ {`builtin "math.arith"; add(1:2, 2:3)`, newFraction(7, 6), nil},
|
||||||
/* 12 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil},
|
/* 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},
|
/* 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},
|
/* 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, `division by zero`},
|
||||||
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
|
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
|
||||||
/* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
|
/* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
|
||||||
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
|
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
|
||||||
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
|
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
|
||||||
/* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
|
/* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
|
||||||
/* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`},
|
/* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`},
|
||||||
/* 22 */ {`string(1|2)`, "1|2", nil},
|
/* 22 */ {`string(1:2)`, "1:2", nil},
|
||||||
|
/* 23 */ {`1+1:2+0.5`, float64(2), nil},
|
||||||
|
/* 24 */ {`1:(2-2)`, nil, `division by zero`},
|
||||||
|
/* 25 */ {`[0,1][1-1]:1`, newFraction(0, 1), nil},
|
||||||
}
|
}
|
||||||
|
// runTestSuiteSpec(t, section, inputs, 25)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFractionToStringSimple(t *testing.T) {
|
func TestFractionToStringSimple(t *testing.T) {
|
||||||
source := newFraction(1, 2)
|
source := newFraction(1, 2)
|
||||||
want := "1|2"
|
want := "1:2"
|
||||||
got := source.ToString(0)
|
got := source.ToString(0)
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf(`(1,2) -> result = %v [%T], want = %v [%T]`, got, got, want, want)
|
t.Errorf(`(1,2) -> result = %v [%T], want = %v [%T]`, got, got, want, want)
|
||||||
@@ -55,10 +59,9 @@ func TestFractionToStringMultiline(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Check this test: the output string ends with a space
|
func TestToStringMultilineTty(t *testing.T) {
|
||||||
func _TestToStringMultilineTty(t *testing.T) {
|
|
||||||
source := newFraction(-1, 2)
|
source := newFraction(-1, 2)
|
||||||
want := "\x1b[4m-1\x1b[0m\n2"
|
want := "\x1b[4m-1\x1b[0m\n 2"
|
||||||
got := source.ToString(MultiLine | TTY)
|
got := source.ToString(MultiLine | TTY)
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf(`(1,2) -> result = %#v [%T], want = %#v [%T]`, got, got, want, want)
|
t.Errorf(`(1,2) -> result = %#v [%T], want = %#v [%T]`, got, got, want, want)
|
||||||
|
|||||||
+4
-2
@@ -16,11 +16,13 @@ func TestCollections(t *testing.T) {
|
|||||||
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
||||||
/* 4 */ {`"abcdef"[:]`, "abcdef", nil},
|
/* 4 */ {`"abcdef"[:]`, "abcdef", nil},
|
||||||
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
|
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||||
/* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`},
|
/* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] invalid range specification`},
|
||||||
|
/* 6 */ {`"abcdef"[((1>0)?{1}:{0}):3]`, "bc", nil},
|
||||||
|
/* 7 */ {`"abcdef"[[0,1][0]:1]`, "a", nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("EXPR_PATH", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 5)
|
// runTestSuiteSpec(t, section, inputs, 5)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -39,9 +39,9 @@ func TestListParser(t *testing.T) {
|
|||||||
/* 25 */ {`["a","b","c","d"][1,1]`, nil, `[1:19] one index only is allowed`},
|
/* 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},
|
/* 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;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator "<<"`},
|
/* 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;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator ">>"`},
|
/* 30 */ {`2 >> 3;`, int64(0), nil},
|
||||||
/* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), 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},
|
/* 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},
|
/* 34 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
|
||||||
|
|||||||
+12
-14
@@ -77,7 +77,7 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 63 */ {`"1.5" == `, nil, `[1:8] infix operator "==" requires two non-nil operands, got 1`},
|
/* 63 */ {`"1.5" == `, nil, `[1:8] infix operator "==" requires two non-nil operands, got 1`},
|
||||||
/* 64 */ {`"1.5" != `, nil, `[1:8] infix operator "!=" requires two non-nil operands, got 1`},
|
/* 64 */ {`"1.5" != `, nil, `[1:8] infix operator "!=" requires two non-nil operands, got 1`},
|
||||||
/* 65 */ {"+1.5", float64(1.5), nil},
|
/* 65 */ {"+1.5", float64(1.5), nil},
|
||||||
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one not nil operand`},
|
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one non-nil operand`},
|
||||||
/* 67 */ {"4 / 0", nil, `division by zero`},
|
/* 67 */ {"4 / 0", nil, `division by zero`},
|
||||||
/* 68 */ {"4.0 / 0", nil, `division by zero`},
|
/* 68 */ {"4.0 / 0", nil, `division by zero`},
|
||||||
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
|
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
|
||||||
@@ -94,10 +94,10 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 80 */ {`5 % 2.0`, nil, `[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`},
|
/* 80 */ {`5 % 2.0`, nil, `[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`},
|
||||||
/* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
|
/* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
|
||||||
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
||||||
/* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
|
/* 83 */ {`"a" < "b" AND NOT 2 == 1`, true, nil},
|
||||||
/* 84 */ {`~ 2 > 1`, false, nil},
|
/* 84 */ {`NOT 2 > 1`, false, nil},
|
||||||
/* 85 */ {`~ true && true`, false, nil},
|
/* 85 */ {`nOT true && true`, false, nil},
|
||||||
/* 86 */ {`~ false || true`, true, nil},
|
/* 86 */ {`NOT false || true`, true, nil},
|
||||||
/* 87 */ {`false but true`, true, nil},
|
/* 87 */ {`false but true`, true, nil},
|
||||||
/* 88 */ {`2+3 but 5*2`, int64(10), nil},
|
/* 88 */ {`2+3 but 5*2`, int64(10), nil},
|
||||||
/* 89 */ {`x=2`, int64(2), nil},
|
/* 89 */ {`x=2`, int64(2), nil},
|
||||||
@@ -132,15 +132,13 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
|
/* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
|
||||||
/* 119 */ {`{}`, &DictType{}, nil},
|
/* 119 */ {`{}`, &DictType{}, nil},
|
||||||
/* 120 */ {`v=10; v++; v`, int64(11), nil},
|
/* 120 */ {`v=10; v++; v`, int64(11), nil},
|
||||||
/* 121 */ {`1+1|2+0.5`, float64(2), nil},
|
/* 121 */ {`1.2()`, newFraction(6, 5), nil},
|
||||||
/* 122 */ {`1.2()`, newFraction(6, 5), nil},
|
/* 122 */ {`x="abc"; x ?! #x`, int64(3), nil},
|
||||||
/* 123 */ {`1|(2-2)`, nil, `division by zero`},
|
/* 123 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
|
||||||
/* 124 */ {`x="abc"; x ?! #x`, int64(3), nil},
|
/* 124 */ {`x ?! (x+1)`, nil, nil},
|
||||||
/* 125 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
|
/* 125 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
|
||||||
/* 126 */ {`x ?! (x+1)`, nil, nil},
|
/* 126 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
|
||||||
/* 127 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
|
/* 127 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
|
||||||
/* 128 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
|
|
||||||
/* 129 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|||||||
+11
-11
@@ -8,18 +8,18 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func _TestImportPlugin(t *testing.T) {
|
// func TestImportPlugin(t *testing.T) {
|
||||||
t.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
|
// t.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
|
||||||
t.Setenv("EXPR_PLUGIN_PATH","${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
|
// t.Setenv("EXPR_PLUGIN_PATH","${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
|
||||||
|
|
||||||
gotCount, gotErr := importPluginFromSearchPath("json")
|
// gotCount, gotErr := importPluginFromSearchPath("json")
|
||||||
if gotCount != 1 {
|
// if gotCount != 1 {
|
||||||
t.Errorf("Import count: got=%d, want=1", gotCount)
|
// t.Errorf("Import count: got=%d, want=1", gotCount)
|
||||||
}
|
// }
|
||||||
if gotErr != nil {
|
// if gotErr != nil {
|
||||||
t.Errorf("importPlugin() failed: %v", gotErr)
|
// t.Errorf("importPlugin() failed: %v", gotErr)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func TestPluginExists(t *testing.T) {
|
func TestPluginExists(t *testing.T) {
|
||||||
name := "json"
|
name := "json"
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ func TestRelational(t *testing.T) {
|
|||||||
/* 8 */ {`true != false`, true, nil},
|
/* 8 */ {`true != false`, true, nil},
|
||||||
/* 9 */ {`1.0 != 3.0-2`, false, nil},
|
/* 9 */ {`1.0 != 3.0-2`, false, nil},
|
||||||
/* 10 */ {`[1,2] != [2,1]`, true, nil},
|
/* 10 */ {`[1,2] != [2,1]`, true, nil},
|
||||||
/* 11 */ {`1|2 == 1|3`, false, nil},
|
/* 11 */ {`1:2 == 1:3`, false, nil},
|
||||||
/* 12 */ {`1|2 != 1|3`, true, nil},
|
/* 12 */ {`1:2 != 1:3`, true, nil},
|
||||||
/* 13 */ {`1|2 == 4|8`, true, nil},
|
/* 13 */ {`1:2 == 4:8`, true, nil},
|
||||||
/* 14 */ {`1 < 8`, true, nil},
|
/* 14 */ {`1 < 8`, true, nil},
|
||||||
/* 15 */ {`1 <= 8`, true, nil},
|
/* 15 */ {`1 <= 8`, true, nil},
|
||||||
/* 16 */ {`"a" < "b"`, true, nil},
|
/* 16 */ {`"a" < "b"`, true, nil},
|
||||||
@@ -32,10 +32,10 @@ func TestRelational(t *testing.T) {
|
|||||||
/* 19 */ {`1.0 <= 8`, true, nil},
|
/* 19 */ {`1.0 <= 8`, true, nil},
|
||||||
/* 20 */ {`1.0 <= 1.0`, true, nil},
|
/* 20 */ {`1.0 <= 1.0`, true, nil},
|
||||||
/* 21 */ {`1.0 == 1`, true, nil},
|
/* 21 */ {`1.0 == 1`, true, nil},
|
||||||
/* 22 */ {`1|2 < 1|3`, false, nil},
|
/* 22 */ {`1:2 < 1:3`, false, nil},
|
||||||
/* 23 */ {`1|2 <= 1|3`, false, nil},
|
/* 23 */ {`1:2 <= 1:3`, false, nil},
|
||||||
/* 24 */ {`1|2 > 1|3`, true, nil},
|
/* 24 */ {`1:2 > 1:3`, true, nil},
|
||||||
/* 25 */ {`1|2 >= 1|3`, true, nil},
|
/* 25 */ {`1:2 >= 1:3`, true, nil},
|
||||||
/* 26 */ {`[1,2,3] > [2]`, true, nil},
|
/* 26 */ {`[1,2,3] > [2]`, true, nil},
|
||||||
/* 27 */ {`[1,2,3] > [9]`, false, nil},
|
/* 27 */ {`[1,2,3] > [9]`, false, nil},
|
||||||
/* 28 */ {`[1,2,3] >= [6]`, false, nil},
|
/* 28 */ {`[1,2,3] >= [6]`, false, nil},
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ const (
|
|||||||
priAnd
|
priAnd
|
||||||
priNot
|
priNot
|
||||||
priRelational
|
priRelational
|
||||||
|
priBinary
|
||||||
priSum
|
priSum
|
||||||
priProduct
|
priProduct
|
||||||
priFraction
|
priFraction
|
||||||
@@ -62,8 +63,8 @@ func (s *term) Clone() (d *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
d = &term{
|
d = &term{
|
||||||
tk: *s.tk.Clone(),
|
tk: *s.tk.Clone(),
|
||||||
parent: s.parent,
|
parent: s.parent,
|
||||||
children: children,
|
children: children,
|
||||||
position: s.position,
|
position: s.position,
|
||||||
priority: s.priority,
|
priority: s.priority,
|
||||||
@@ -211,11 +212,11 @@ func (term *term) checkOperands() (err error) {
|
|||||||
}
|
}
|
||||||
case posPrefix:
|
case posPrefix:
|
||||||
if term.children == nil || len(term.children) != 1 || term.children[0] == nil {
|
if term.children == nil || len(term.children) != 1 || term.children[0] == nil {
|
||||||
err = term.tk.Errorf("prefix operator %q requires one not nil operand", term.tk.String())
|
err = term.tk.Errorf("prefix operator %q requires one non-nil operand", term.tk.String())
|
||||||
}
|
}
|
||||||
case posPostfix:
|
case posPostfix:
|
||||||
if term.children == nil || len(term.children) != 1 || term.anyChildrenNil() {
|
if term.children == nil || len(term.children) != 1 || term.anyChildrenNil() {
|
||||||
err = term.tk.Errorf("postfix operator %q requires one not nil operand", term.tk.String())
|
err = term.tk.Errorf("postfix operator %q requires one non-nil operand", term.tk.String())
|
||||||
}
|
}
|
||||||
case posMultifix:
|
case posMultifix:
|
||||||
if term.children == nil || len(term.children) < 3 || term.anyChildrenNil() {
|
if term.children == nil || len(term.children) < 3 || term.anyChildrenNil() {
|
||||||
|
|||||||
@@ -93,6 +93,15 @@ func (tk *Token) Error() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tk *Token) ErrorText() (err string) {
|
||||||
|
if tk.Sym == SymError {
|
||||||
|
if msg, ok := tk.Value.(error); ok {
|
||||||
|
err = msg.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (tk *Token) Errors(msg string) (err error) {
|
func (tk *Token) Errors(msg string) (err error) {
|
||||||
err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
|
err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
|
||||||
return
|
return
|
||||||
@@ -104,6 +113,10 @@ func (tk *Token) ErrorExpectedGot(symbol string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
|
func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
|
||||||
err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, got)
|
return tk.ErrorExpectedGotStringWithPrefix("expected", symbol, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *Token) ErrorExpectedGotStringWithPrefix(prefix, symbol, got string) (err error) {
|
||||||
|
err = fmt.Errorf("[%d:%d] %s %s, got `%s`", tk.row, tk.col, prefix, symbol, got)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user