Compare commits

...

10 Commits

Author SHA1 Message Date
camoroso 526982a564 new string operator '/' 2024-12-28 19:16:45 +01:00
camoroso 252514943e parser.go: fix currentItem after parse a subexpr 2024-12-28 19:16:03 +01:00
camoroso 32686fac62 term.go: new member function errDivisionByZero() 2024-12-28 19:14:34 +01:00
camoroso 52a627c747 Edited according to preious commit 2024-12-28 09:28:12 +01:00
camoroso d91e7eb979 The list operator '<<' (append) and '>>' (prepend) have been replaced
with the new operators '<+' and '+>' respectively.
'<<' and '>>' are now used only for left and right binary shit
respectively. Further, their priority has been increased moving them to
a higher priority than that of the assignment operator.
2024-12-28 09:17:27 +01:00
camoroso cca3b76baa solved a number of problems highlighted by the syntax analyzer 2024-12-27 07:46:11 +01:00
camoroso 24e31997fc context-helpers.go: export functions no longer export control flags 2024-12-27 07:22:28 +01:00
camoroso 646710e180 builtin-base.go: new eval() function 2024-12-27 07:14:26 +01:00
camoroso b38327b841 t_builtin-string_test.go: corrected an undefinite article 2024-12-27 07:14:01 +01:00
camoroso fd912b2eb1 common-errors.go: undefinte article selection in error messages 2024-12-27 07:13:03 +01:00
25 changed files with 305 additions and 115 deletions
+29
View File
@@ -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() {
+18 -1
View File
@@ -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
View File
@@ -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)
+11 -9
View File
@@ -439,8 +439,8 @@ _non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value_} "**]**" +
| [blue]`+` | _Join_ | Joins two lists | [blue]`[1,2] + [3]` -> _[1,2,3]_ | [blue]`+` | _Join_ | Joins two lists | [blue]`[1,2] + [3]` -> _[1,2,3]_
| [blue]`-` | _Difference_ | Left list without elements in the right list | [blue]`[1,2,3] - [2]` -> _[1,3]_ | [blue]`-` | _Difference_ | Left list without elements in the right list | [blue]`[1,2,3] - [2]` -> _[1,3]_
| [blue]`>>` | _Front insertion_ | Insert an item in front | [blue]`0 >> [1,2]` -> _[0,1,2]_ | [blue]`+>` | _Front insertion_ | Insert an item in front | [blue]`0 +> [1,2]` -> _[0,1,2]_
| [blue]`<<` | _Back insertion_ | Insert an item at end | [blue]`[1,2] << 3` -> _[1,2,3]_ | [blue]`<+` | _Back insertion_ | Insert an item at end | [blue]`[1,2] <+ 3` -> _[1,2,3]_
| [blue]`[]` | _Item at index_ | Item at given position | [blue]`[1,2,3][1]` -> _2_ | [blue]`[]` | _Item at index_ | Item at given position | [blue]`[1,2,3][1]` -> _2_
| [blue]`in` | _Item in list_ | True if item is in list | [blue]`2 in [1,2,3]` -> _true_ + | [blue]`in` | _Item in list_ | True if item is in list | [blue]`2 in [1,2,3]` -> _true_ +
[blue]`6 in [1,2,3]` -> _false_ [blue]`6 in [1,2,3]` -> _false_
@@ -735,7 +735,7 @@ NOTE: These operators have a high priority, in particular higher than the operat
The table below shows all supported operators by decreasing priorities. The table below shows all supported operators by decreasing priorities.
.Operators priorities .Operators priorities
[cols="^2,^2,^2,^5,^6"] [cols="^3,^2,^2,^5,^6"]
|=== |===
| Priority | Operators | Position | Operation | Operands and results | Priority | Operators | Position | Operation | Operands and results
@@ -765,9 +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_ .1+|*BINARY NOT*| [blue]`~` | _Prefix_ | _Binary Not_ | `~` _number_ -> _number_
| [blue]`\|` | _Infix_ | _Binary Or_ | _number_ `\|` _number_ -> _number_ .1+|*BINARY AND*| [blue]`&` | _Infix_ | _Binary And_ | _number_ `&` _number_ -> _number_
| [blue]`~` | _Prefix_ | _Binary Not_ | `~` _number_ -> _number_ .1+|*BINARY OR*| [blue]`\|` | _Infix_ | _Binary Or_ | _number_ `\|` _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_
@@ -776,11 +776,13 @@ The table below shows all supported operators by decreasing priorities.
| [blue]`!=` | _Infix_ | _Not-equal_ | _comparable_ `!=` _comparable_ -> _boolean_ | [blue]`!=` | _Infix_ | _Not-equal_ | _comparable_ `!=` _comparable_ -> _boolean_
| [blue]`in` | _Infix_ | _Member-of-list_ | _any_ `in` _list_ -> _boolean_ | [blue]`in` | _Infix_ | _Member-of-list_ | _any_ `in` _list_ -> _boolean_
| [blue]`in` | _Infix_ | _Key-of-dict_ | _any_ `in` _dict_ -> _boolean_ | [blue]`in` | _Infix_ | _Key-of-dict_ | _any_ `in` _dict_ -> _boolean_
.1+|*NOT*| [blue]`not` | _Prefix_ | _Not_ | `not` _boolean_ -> _boolean_ .1+|*LOGIC NOT*| [blue]`not` | _Prefix_ | _Not_ | `not` _boolean_ -> _boolean_
.2+|*AND*| [blue]`and` | _Infix_ | _And_ | _boolean_ `and` _boolean_ -> _boolean_ .2+|*LOGIC AND*| [blue]`and` | _Infix_ | _And_ | _boolean_ `and` _boolean_ -> _boolean_
| [blue]`&&` | _Infix_ | _And_ | _boolean_ `&&` _boolean_ -> _boolean_ | [blue]`&&` | _Infix_ | _And_ | _boolean_ `&&` _boolean_ -> _boolean_
.2+|*OR*| [blue]`or` | _Infix_ | _Or_ | _boolean_ `or` _boolean_ -> _boolean_ .2+|*LOGIC OR*| [blue]`or` | _Infix_ | _Or_ | _boolean_ `or` _boolean_ -> _boolean_
| [blue]`\|\|` | _Infix_ | _Or_ | _boolean_ `\|\|` _boolean_ -> _boolean_ | [blue]`\|\|` | _Infix_ | _Or_ | _boolean_ `\|\|` _boolean_ -> _boolean_
.2+|*INSERT*| [blue]`+>` | _Infix_ | _Prepend_ | _any_ `+>` _list_ -> _list_
| [blue]`<+` | _Infix_ | _Append_ | _list_ `<+` _any_ -> _list_
.3+|*ASSIGN*| [blue]`=` | _Infix_ | _Assignment_ | _identifier_ `=` _any_ -> _any_ .3+|*ASSIGN*| [blue]`=` | _Infix_ | _Assignment_ | _identifier_ `=` _any_ -> _any_
| [blue]`>>` | _Infix_ | _Front-insert_ | _any_ `>>` _list_ -> _list_ | [blue]`>>` | _Infix_ | _Front-insert_ | _any_ `>>` _list_ -> _list_
| [blue]`<<` | _Infix_ | _Back-insert_ | _list_ `<<` _any_ -> _list_ | [blue]`<<` | _Infix_ | _Back-insert_ | _list_ `<<` _any_ -> _list_
+35 -20
View File
@@ -1380,16 +1380,16 @@ dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">[1,2,3] - [2]</code> &#8594; <em>[1,3]</em></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">[1,2,3] - [2]</code> &#8594; <em>[1,3]</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&gt;&gt;</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">+&gt;</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Front insertion</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Front insertion</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Insert an item in front</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Insert an item in front</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">0 &gt;&gt; [1,2]</code> &#8594; <em>[0,1,2]</em></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">0 +&gt; [1,2]</code> &#8594; <em>[0,1,2]</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&lt;&lt;</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&lt;+</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Back insertion</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Back insertion</em></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Insert an item at end</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock">Insert an item at end</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">[1,2] &lt;&lt; 3</code> &#8594; <em>[1,2,3]</em></p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><code class="blue">[1,2] &lt;+ 3</code> &#8594; <em>[1,2,3]</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">[]</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">[]</code></p></td>
@@ -1900,11 +1900,11 @@ These operators have a high priority, in particular higher than the operator <co
<table class="tableblock frame-all grid-all stretch"> <table class="tableblock frame-all grid-all stretch">
<caption class="title">Table 8. Operators priorities</caption> <caption class="title">Table 8. Operators priorities</caption>
<colgroup> <colgroup>
<col style="width: 11.7647%;"> <col style="width: 16.6666%;">
<col style="width: 11.7647%;"> <col style="width: 11.1111%;">
<col style="width: 11.7647%;"> <col style="width: 11.1111%;">
<col style="width: 29.4117%;"> <col style="width: 27.7777%;">
<col style="width: 35.2942%;"> <col style="width: 33.3335%;">
</colgroup> </colgroup>
<thead> <thead>
<tr> <tr>
@@ -2076,25 +2076,27 @@ These operators have a high priority, in particular higher than the operator <co
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>list</em> <code>-</code> <em>list</em> &#8594; <em>list</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>list</em> <code>-</code> <em>list</em> &#8594; <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"><strong>BINARY NOT</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">~</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Prefix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Binary Not</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code>~</code> <em>number</em> &#8594; <em>number</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>BINARY AND</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&amp;</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&amp;</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>Binary And</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>&amp;</code> <em>number</em> &#8594; <em>number</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>number</em> <code>&amp;</code> <em>number</em> &#8594; <em>number</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>BINARY OR</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">|</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><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>Binary Or</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> &#8594; <em>number</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>number</em> <code>|</code> <em>number</em> &#8594; <em>number</em></p></td>
</tr> </tr>
<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> &#8594; <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">&lt;</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&lt;</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>
@@ -2144,14 +2146,14 @@ These operators have a high priority, in particular higher than the operator <co
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>in</code> <em>dict</em> &#8594; <em>boolean</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>in</code> <em>dict</em> &#8594; <em>boolean</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>NOT</strong></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><strong>LOGIC NOT</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">not</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">not</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Prefix</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Prefix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Not</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Not</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code>not</code> <em>boolean</em> &#8594; <em>boolean</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code>not</code> <em>boolean</em> &#8594; <em>boolean</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>AND</strong></p></td> <td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>LOGIC AND</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">and</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">and</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>And</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>And</em></p></td>
@@ -2164,7 +2166,7 @@ These operators have a high priority, in particular higher than the operator <co
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>boolean</em> <code>&amp;&amp;</code> <em>boolean</em> &#8594; <em>boolean</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>boolean</em> <code>&amp;&amp;</code> <em>boolean</em> &#8594; <em>boolean</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>OR</strong></p></td> <td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>LOGIC OR</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">or</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">or</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Or</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>Or</em></p></td>
@@ -2177,6 +2179,19 @@ These operators have a high priority, in particular higher than the operator <co
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>boolean</em> <code>||</code> <em>boolean</em> &#8594; <em>boolean</em></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><em>boolean</em> <code>||</code> <em>boolean</em> &#8594; <em>boolean</em></p></td>
</tr> </tr>
<tr> <tr>
<td class="tableblock halign-center valign-top" rowspan="2"><p class="tableblock"><strong>INSERT</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">+&gt;</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Prepend</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>+&gt;</code> <em>list</em> &#8594; <em>list</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">&lt;+</code></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Append</em></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>list</em> <code>&lt;+</code> <em>any</em> &#8594; <em>list</em></p></td>
</tr>
<tr>
<td class="tableblock halign-center valign-top" rowspan="3"><p class="tableblock"><strong>ASSIGN</strong></p></td> <td class="tableblock halign-center valign-top" rowspan="3"><p class="tableblock"><strong>ASSIGN</strong></p></td>
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">=</code></p></td> <td class="tableblock halign-center valign-top"><p class="tableblock"><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>
@@ -2461,7 +2476,7 @@ g(@p):any{}`
</div> </div>
<div id="footer"> <div id="footer">
<div id="footer-text"> <div id="footer-text">
Last updated 2024-12-23 07:20:48 +0100 Last updated 2024-12-28 09:13:00 +0100
</div> </div>
</div> </div>
</body> </body>
+10 -8
View File
@@ -126,34 +126,36 @@ func (f *FractionType) ToString(opt FmtOpt) string {
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()
+4 -4
View File
@@ -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")
} // }
+3 -3
View File
@@ -11,7 +11,7 @@ func newBinNotTerm(tk *Token) (inst *term) {
tk: *tk, tk: *tk,
children: make([]*term, 0, 1), children: make([]*term, 0, 1),
position: posPrefix, position: posPrefix,
priority: priBinary, priority: priBinNot,
evalFunc: evalBinaryNot, evalFunc: evalBinaryNot,
} }
} }
@@ -39,7 +39,7 @@ func newBinAndTerm(tk *Token) (inst *term) {
tk: *tk, tk: *tk,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priBinary, priority: priBinAnd,
evalFunc: evalBinaryAnd, evalFunc: evalBinaryAnd,
} }
} }
@@ -71,7 +71,7 @@ func newBinOrTerm(tk *Token) (inst *term) {
tk: *tk, tk: *tk,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priBinary, priority: priBinOr,
evalFunc: evalBinaryOr, evalFunc: evalBinaryOr,
} }
} }
+1 -1
View File
@@ -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
+1 -2
View File
@@ -7,7 +7,6 @@ package expr
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html //https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
import ( import (
"errors"
"fmt" "fmt"
) )
@@ -41,7 +40,7 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
return return
} }
if den == 0 { if den == 0 {
err = errors.New("division by zero") err = opTerm.errDivisionByZero()
return return
} }
+8 -16
View File
@@ -4,15 +4,15 @@
// operator-insert.go // operator-insert.go
package expr package expr
//-------- insert term //-------- prepend term
func newInsertTerm(tk *Token) (inst *term) { func newPrependTerm(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: priAssign, priority: priInsert,
evalFunc: evalInsert, evalFunc: evalPrepend,
} }
} }
@@ -21,12 +21,12 @@ func newAppendTerm(tk *Token) (inst *term) {
tk: *tk, tk: *tk,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priAssign, priority: priInsert,
evalFunc: evalAppend, evalFunc: evalAppend,
} }
} }
func evalInsert(ctx ExprContext, opTerm *term) (v any, err error) { func evalPrepend(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
@@ -40,10 +40,6 @@ func evalInsert(ctx ExprContext, opTerm *term) (v any, err error) {
if opTerm.children[1].symbol() == SymVariable { 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)
} }
@@ -64,10 +60,6 @@ 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)
} }
@@ -94,6 +86,6 @@ func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymInsert, newInsertTerm) registerTermConstructor(SymPlusGreater, newPrependTerm)
registerTermConstructor(SymAppend, newAppendTerm) registerTermConstructor(SymLessPlus, newAppendTerm)
} }
+29 -5
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"strings" "strings"
) )
@@ -69,7 +68,7 @@ func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
if IsFloat(leftValue) || IsFloat(rightValue) { if IsFloat(leftValue) || IsFloat(rightValue) {
d := numAsFloat(rightValue) d := numAsFloat(rightValue)
if d == 0.0 { if d == 0.0 {
err = errors.New("division by zero") err = opTerm.errDivisionByZero()
} else { } else {
v = numAsFloat(leftValue) / d v = numAsFloat(leftValue) / d
} }
@@ -78,11 +77,36 @@ func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
if rightInt, _ := rightValue.(int64); rightInt == 0 { if rightInt, _ := rightValue.(int64); rightInt == 0 {
err = errors.New("division by zero") err = opTerm.errDivisionByZero()
} else { } else {
v = leftInt / rightInt v = leftInt / rightInt
} }
} }
} else if IsString(leftValue) && IsString(rightValue) {
source := leftValue.(string)
sep := rightValue.(string)
v = ListFromStrings(strings.Split(source, sep))
} else if IsString(leftValue) && IsInteger(rightValue) {
source := leftValue.(string)
partSize := int(rightValue.(int64))
if partSize == 0 {
err = opTerm.errDivisionByZero()
} else {
partCount := len(source) / partSize
remainder := len(source) % partSize
listSize := partCount
if remainder > 0 {
listSize++
}
parts := make([]any, 0, listSize)
for i := 0; i < partCount; i++ {
parts = append(parts, source[i*partSize:(i+1)*partSize])
}
if remainder > 0 {
parts = append(parts, source[len(source)-remainder:])
}
v = newList(parts)
}
} else { } else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
@@ -121,7 +145,7 @@ func evalDivideAsFloat(ctx ExprContext, floatDivTerm *term) (v any, err error) {
if isNumOrFract(leftValue) && isNumOrFract(rightValue) { if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
d := numAsFloat(rightValue) d := numAsFloat(rightValue)
if d == 0.0 { if d == 0.0 {
err = errors.New("division by zero") err = floatDivTerm.errDivisionByZero()
} else { } else {
v = numAsFloat(leftValue) / d v = numAsFloat(leftValue) / d
} }
@@ -146,7 +170,7 @@ func remainderValues(opTerm *term, leftValue, rightValue any) (v any, err error)
if IsInteger(leftValue) && IsInteger(rightValue) { if IsInteger(leftValue) && IsInteger(rightValue) {
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
if rightInt == 0 { if rightInt == 0 {
err = errors.New("division by zero") err = opTerm.errDivisionByZero()
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
v = leftInt % rightInt v = leftInt % rightInt
+85
View File
@@ -0,0 +1,85 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-shift.go
package expr
//-------- shift term
func newRightShiftTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priBinShift,
evalFunc: evalRightShift,
}
}
func evalRightShift(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
if IsInteger(leftValue) && IsInteger(rightValue) {
leftInt := leftValue.(int64)
rightInt := rightValue.(int64)
v = leftInt >> rightInt
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func newLeftShiftTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priBinShift,
evalFunc: evalLeftShift,
}
}
func evalLeftShift(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
if IsInteger(leftValue) && IsInteger(rightValue) {
leftInt := leftValue.(int64)
rightInt := rightValue.(int64)
v = leftInt << rightInt
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
// func evalAssignAppend(ctx ExprContext, self *term) (v any, err error) {
// var leftValue, rightValue any
// if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
// return
// }
// if IsList(leftValue) {
// list, _ := leftValue.(*ListType)
// newList := append(*list, rightValue)
// v = &newList
// if
// } else {
// err = self.errIncompatibleTypes(leftValue, rightValue)
// }
// return
// }
// init
func init() {
registerTermConstructor(SymDoubleGreater, newRightShiftTerm)
registerTermConstructor(SymDoubleLess, newLeftShiftTerm)
}
+6 -3
View File
@@ -452,9 +452,12 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
case SymOpenRound: case SymOpenRound:
var subTree *ast var subTree *ast
if subTree, err = parser.parseGeneral(scanner, ctx, SymClosedRound); err == nil { if subTree, err = parser.parseGeneral(scanner, ctx, SymClosedRound); err == nil {
subTree.root.priority = priValue exprTerm := newExprTerm(subTree.root)
err = tree.addTerm(newExprTerm(subTree.root)) err = tree.addTerm(exprTerm)
currentTerm = subTree.root currentTerm = exprTerm
// subTree.root.priority = priValue
// err = tree.addTerm(newExprTerm(subTree.root))
// currentTerm = subTree.root
} }
case SymFuncCall: case SymFuncCall:
var funcCallTerm *term var funcCallTerm *term
+7 -3
View File
@@ -124,6 +124,8 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
tk = scanner.moveOn(SymDoublePlus, ch, next) tk = scanner.moveOn(SymDoublePlus, ch, next)
} else if next == '=' { } else if next == '=' {
tk = scanner.moveOn(SymPlusEqual, ch, next) tk = scanner.moveOn(SymPlusEqual, ch, next)
} else if next == '>' {
tk = scanner.moveOn(SymPlusGreater, ch, next)
} else { } else {
tk = scanner.makeToken(SymPlus, ch) tk = scanner.makeToken(SymPlus, ch)
} }
@@ -265,9 +267,11 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymLessOrEqual, ch, next) tk = scanner.moveOn(SymLessOrEqual, ch, next)
} else if next == '<' { } else if next == '<' {
tk = scanner.moveOn(SymAppend, ch, next) tk = scanner.moveOn(SymDoubleLess, ch, next)
} else if next == '>' { } else if next == '>' {
tk = scanner.moveOn(SymLessGreater, ch, next) tk = scanner.moveOn(SymLessGreater, ch, next)
} else if next == '+' {
tk = scanner.moveOn(SymLessPlus, ch, next)
} else { } else {
tk = scanner.makeToken(SymLess, ch) tk = scanner.makeToken(SymLess, ch)
} }
@@ -275,7 +279,7 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
if next, _ := scanner.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = scanner.moveOn(SymGreaterOrEqual, ch, next) tk = scanner.moveOn(SymGreaterOrEqual, ch, next)
} else if next == '>' { } else if next == '>' {
tk = scanner.moveOn(SymInsert, ch, next) tk = scanner.moveOn(SymDoubleGreater, ch, next)
} else { } else {
tk = scanner.makeToken(SymGreater, ch) tk = scanner.makeToken(SymGreater, ch)
} }
@@ -460,7 +464,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)
+4 -2
View File
@@ -84,8 +84,8 @@ func init() {
SymQuestionExclam: {"?!", symClassOperator}, // 49: '?!' SymQuestionExclam: {"?!", symClassOperator}, // 49: '?!'
SymDoubleAt: {"@@", symClassOperator}, // 50: '@@' SymDoubleAt: {"@@", symClassOperator}, // 50: '@@'
SymDoubleColon: {"::", symClassOperator}, // 51: '::' SymDoubleColon: {"::", symClassOperator}, // 51: '::'
SymInsert: {">>", symClassOperator}, // 52: '>>' SymDoubleGreater: {">>", symClassOperator}, // 52: '>>'
SymAppend: {"<<", symClassOperator}, // 53: '<<' SymDoubleLess: {"<<", symClassOperator}, // 53: '<<'
SymCaret: {"^", symClassOperator}, // 54: '^' SymCaret: {"^", symClassOperator}, // 54: '^'
SymDollarRound: {"$(", symClassOperator}, // 55: '$(' SymDollarRound: {"$(", symClassOperator}, // 55: '$('
SymOpenClosedRound: {"()", symClassPostOp}, // 56: '()' SymOpenClosedRound: {"()", symClassPostOp}, // 56: '()'
@@ -95,6 +95,8 @@ func init() {
SymStarEqual: {"*=", symClassOperator}, // 60: '*=' SymStarEqual: {"*=", symClassOperator}, // 60: '*='
SymSlashEqual: {"/=", symClassOperator}, // 61: '/=' SymSlashEqual: {"/=", symClassOperator}, // 61: '/='
SymPercEqual: {"%=", symClassOperator}, // 62: '%=' SymPercEqual: {"%=", symClassOperator}, // 62: '%='
SymPlusGreater: {"+>", symClassOperator}, // 63: '+>'
SymLessPlus: {"<+", symClassOperator}, // 64: '<+'
// SymChangeSign // SymChangeSign
// SymUnchangeSign // SymUnchangeSign
// SymIdentifier // SymIdentifier
+4 -2
View File
@@ -60,8 +60,8 @@ const (
SymQuestionExclam // 49: '?!' SymQuestionExclam // 49: '?!'
SymDoubleAt // 50: '@@' SymDoubleAt // 50: '@@'
SymDoubleColon // 51: '::' SymDoubleColon // 51: '::'
SymInsert // 52: '>>' SymDoubleGreater // 52: '>>'
SymAppend // 53: '<<' SymDoubleLess // 53: '<<'
SymCaret // 54: '^' SymCaret // 54: '^'
SymDollarRound // 55: '$(' SymDollarRound // 55: '$('
SymOpenClosedRound // 56: '()' SymOpenClosedRound // 56: '()'
@@ -71,6 +71,8 @@ const (
SymStarEqual // 60: '*=' SymStarEqual // 60: '*='
SymSlashEqual // 61: '/=' SymSlashEqual // 61: '/='
SymPercEqual // 62: '%=' SymPercEqual // 62: '%='
SymPlusGreater // 63: '+>'
SymLessPlus // 64: '<+'
SymChangeSign SymChangeSign
SymUnchangeSign SymUnchangeSign
SymIdentifier SymIdentifier
+2 -1
View File
@@ -56,11 +56,12 @@ func TestFuncBase(t *testing.T) {
/* 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, 30) // runTestSuiteSpec(t, section, inputs, 45)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+1 -1
View File
@@ -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},
+4 -5
View File
@@ -25,7 +25,7 @@ func TestFractionsParser(t *testing.T) {
/* 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, `[1:3] 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},
@@ -34,7 +34,7 @@ func TestFractionsParser(t *testing.T) {
/* 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}, /* 23 */ {`1+1:2+0.5`, float64(2), nil},
/* 24 */ {`1:(2-2)`, nil, `division by zero`}, /* 24 */ {`1:(2-2)`, nil, `[1:3] division by zero`},
/* 25 */ {`[0,1][1-1]:1`, newFraction(0, 1), nil}, /* 25 */ {`[0,1][1-1]:1`, newFraction(0, 1), nil},
} }
// runTestSuiteSpec(t, section, inputs, 25) // runTestSuiteSpec(t, section, inputs, 25)
@@ -59,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)
+14 -14
View File
@@ -23,8 +23,8 @@ func TestListParser(t *testing.T) {
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil}, /* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
/* 10 */ {`add([1,"hello"])`, nil, `add(): param nr 2 (2 in 1) has wrong type string, number expected`}, /* 10 */ {`add([1,"hello"])`, nil, `add(): param nr 2 (2 in 1) has wrong type string, number expected`},
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil}, /* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
/* 12 */ {`[1,2,3] << 2+2`, newListA(int64(1), int64(2), int64(3), int64(4)), nil}, /* 12 */ {`[1,2,3] <+ 2+2`, newListA(int64(1), int64(2), int64(3), int64(4)), nil},
/* 13 */ {`2-1 >> [2,3]`, newListA(int64(1), int64(2), int64(3)), nil}, /* 13 */ {`2-1 +> [2,3]`, newListA(int64(1), int64(2), int64(3)), nil},
/* 14 */ {`[1,2,3][1]`, int64(2), nil}, /* 14 */ {`[1,2,3][1]`, int64(2), nil},
/* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil}, /* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil},
/* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil}, /* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil},
@@ -33,8 +33,8 @@ func TestListParser(t *testing.T) {
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil}, /* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil}, /* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
/* 21 */ {`"b" in ["a", "b", "c"]`, true, nil}, /* 21 */ {`"b" in ["a", "b", "c"]`, true, nil},
/* 22 */ {`a=[1,2]; (a)<<3`, newListA(int64(1), int64(2), int64(3)), nil}, /* 22 */ {`a=[1,2]; (a)<+3`, newListA(int64(1), int64(2), int64(3)), nil},
/* 23 */ {`a=[1,2]; (a)<<3; a`, newListA(int64(1), int64(2)), nil}, /* 23 */ {`a=[1,2]; (a)<+3; a`, newListA(int64(1), int64(2)), nil},
/* 24 */ {`["a","b","c","d"][1]`, "b", nil}, /* 24 */ {`["a","b","c","d"][1]`, "b", nil},
/* 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},
@@ -42,16 +42,16 @@ func TestListParser(t *testing.T) {
/* 28 */ {`2 << 3;`, int64(16), nil}, /* 28 */ {`2 << 3;`, int64(16), nil},
/* 29 */ {`but >> ["a", "b", "c"]`, nil, `[1:6] infix operator ">>" requires two non-nil operands, got 0`}, /* 29 */ {`but >> ["a", "b", "c"]`, nil, `[1:6] infix operator ">>" requires two non-nil operands, got 0`},
/* 30 */ {`2 >> 3;`, int64(0), nil}, /* 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}, /* 32 */ {`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}, /* 33 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
/* 35 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`}, /* 34 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`},
/* 36 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [list]`}, /* 35 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [list]`},
/* 37 */ {`L=[1,2]; L[nil]=9;`, nil, `[1:12] index/key is nil`}, /* 36 */ {`L=[1,2]; L[nil]=9;`, nil, `[1:12] index/key is nil`},
/* 38 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil}, /* 37 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil},
/* 39 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil}, /* 38 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil},
/* 40 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil}, /* 30 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil},
/* 41 */ {`[0,1,2,3,4][0:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil}, /* 40 */ {`[0,1,2,3,4][0:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
+2 -2
View File
@@ -78,8 +78,8 @@ func TestGeneralParser(t *testing.T) {
/* 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 non-nil operand`}, /* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one non-nil operand`},
/* 67 */ {"4 / 0", nil, `division by zero`}, /* 67 */ {"4 / 0", nil, `[1:4] division by zero`},
/* 68 */ {"4.0 / 0", nil, `division by zero`}, /* 68 */ {"4.0 / 0", nil, `[1:6] division by zero`},
/* 69 */ {"4.0 / \n2", float64(2.0), nil}, /* 69 */ {"4.0 / \n2", float64(2.0), nil},
/* 70 */ {`123`, int64(123), nil}, /* 70 */ {`123`, int64(123), nil},
/* 71 */ {`1.`, float64(1.0), nil}, /* 71 */ {`1.`, float64(1.0), nil},
+11 -11
View File
@@ -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"
+6
View File
@@ -16,6 +16,12 @@ func TestStringsParser(t *testing.T) {
/* 4 */ {`"uno" * (2+1)`, `unounouno`, nil}, /* 4 */ {`"uno" * (2+1)`, `unounouno`, nil},
/* 5 */ {`"abc"[1]`, `b`, nil}, /* 5 */ {`"abc"[1]`, `b`, nil},
/* 6 */ {`#"abc"`, int64(3), nil}, /* 6 */ {`#"abc"`, int64(3), nil},
/* 7 */ {`"192.168.0.240" / "."`, newListA("192", "168", "0", "240"), nil},
/* 8 */ {`("192.168.0.240" / ".")[1]`, "168", nil},
/* 9 */ {`"AF3B0Dz" / 2`, newListA("AF", "3B", "0D", "z"), nil},
/* 10 */ {`"AF3B0Dz" / 0`, nil, "[1:12] division by zero"},
} }
// runTestSuiteSpec(t, "String", inputs, 8)
runTestSuite(t, "String", inputs) runTestSuite(t, "String", inputs)
} }
+9 -1
View File
@@ -15,15 +15,19 @@ const (
priRange priRange
priBut priBut
priAssign priAssign
priInsert
priOr priOr
priAnd priAnd
priNot priNot
priRelational priRelational
priBinary priBinOr
priBinAnd
priBinNot
priSum priSum
priProduct priProduct
priFraction priFraction
priSelector priSelector
priBinShift
priSign priSign
priFact priFact
priIterValue priIterValue
@@ -199,6 +203,10 @@ func (term *term) errIncompatibleType(value any) error {
term.source(), value, TypeName(value)) term.source(), value, TypeName(value))
} }
func (term *term) errDivisionByZero() error {
return term.tk.Errorf("division by zero")
}
func (term *term) Errorf(template string, args ...any) (err error) { func (term *term) Errorf(template string, args ...any) (err error) {
err = term.tk.Errorf(template, args...) err = term.tk.Errorf(template, args...)
return return