Compare commits
25 Commits
0bca3333aa
...
c977e82d9e
Author | SHA1 | Date | |
---|---|---|---|
c977e82d9e | |||
903f1ae1ce | |||
9c66056c18 | |||
f55a48aa26 | |||
434ddee733 | |||
fcced6149f | |||
1f0f9cae22 | |||
1d8569d3a9 | |||
0fdd51049d | |||
f9ed5776cd | |||
a2c0a24494 | |||
2c5f02cc69 | |||
d9fbe6f36d | |||
e6174aca82 | |||
a736bba2c7 | |||
7724cabdcc | |||
16557d70de | |||
04e71a1b3f | |||
e463bd61d8 | |||
419af7bfea | |||
6c604812ee | |||
5cf0bfbad4 | |||
a838361ea8 | |||
0dbb0ba515 | |||
7a0ba26aa3 |
2
ast.go
2
ast.go
@ -63,7 +63,7 @@ func (self *ast) addToken2(tk *Token) (t *term, err error) {
|
|||||||
if t = newTerm(tk, nil); t != nil {
|
if t = newTerm(tk, nil); t != nil {
|
||||||
err = self.addTerm(t)
|
err = self.addTerm(t)
|
||||||
} else {
|
} else {
|
||||||
err = tk.Errorf("No term constructor for token %q", tk.String())
|
err = tk.Errorf("unexpected token %q", tk.String())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ func TestAddTokensBad(t *testing.T) {
|
|||||||
func TestAddUnknownTokens(t *testing.T) {
|
func TestAddUnknownTokens(t *testing.T) {
|
||||||
tk0 := NewToken(0, 0, SymPercent, "%")
|
tk0 := NewToken(0, 0, SymPercent, "%")
|
||||||
|
|
||||||
wantErr := errors.New(`No term constructor for token "%"`)
|
wantErr := errors.New(`unexpected token "%"`)
|
||||||
|
|
||||||
tree := NewAst()
|
tree := NewAst()
|
||||||
if gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() {
|
if gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() {
|
||||||
|
@ -6,17 +6,7 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
initName = "init"
|
|
||||||
cleanName = "clean"
|
|
||||||
resetName = "reset"
|
|
||||||
nextName = "next"
|
|
||||||
currentName = "current"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type dataCursor struct {
|
type dataCursor struct {
|
||||||
@ -39,23 +29,24 @@ func newDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapToString(m map[string]Functor) string {
|
// func mapToString(m map[string]Functor) string {
|
||||||
var sb strings.Builder
|
// var sb strings.Builder
|
||||||
sb.WriteByte('{')
|
// sb.WriteByte('{')
|
||||||
for key, _ := range m {
|
// for key, _ := range m {
|
||||||
if sb.Len() > 1 {
|
// if sb.Len() > 1 {
|
||||||
sb.WriteString(fmt.Sprintf(", %q: func(){}", key))
|
// sb.WriteString(fmt.Sprintf(", %q: func(){}", key))
|
||||||
} else {
|
// } else {
|
||||||
sb.WriteString(fmt.Sprintf("%q: func(){}", key))
|
// sb.WriteString(fmt.Sprintf("%q: func(){}", key))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
sb.WriteByte('}')
|
// sb.WriteByte('}')
|
||||||
return sb.String()
|
// return sb.String()
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (dc *dataCursor) String() string {
|
func (dc *dataCursor) String() string {
|
||||||
return "$()"
|
return "$()"
|
||||||
/* var sb strings.Builder
|
/*
|
||||||
|
var sb strings.Builder
|
||||||
sb.WriteString(fmt.Sprintf(`$(
|
sb.WriteString(fmt.Sprintf(`$(
|
||||||
index: %d,
|
index: %d,
|
||||||
ds: %s,
|
ds: %s,
|
||||||
@ -67,30 +58,34 @@ func (dc *dataCursor) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dc *dataCursor) HasOperation(name string) (exists bool) {
|
func (dc *dataCursor) HasOperation(name string) (exists bool) {
|
||||||
|
exists = name == indexName
|
||||||
|
if !exists {
|
||||||
f, ok := dc.ds[name]
|
f, ok := dc.ds[name]
|
||||||
exists = ok && isFunctor(f)
|
exists = ok && isFunctor(f)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *dataCursor) CallOperation(name string, args []any) (value any, err error) {
|
func (dc *dataCursor) CallOperation(name string, args []any) (value any, err error) {
|
||||||
if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
|
if name == indexName {
|
||||||
|
value = int64(dc.Index())
|
||||||
|
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
|
||||||
if functor == dc.cleanFunc {
|
if functor == dc.cleanFunc {
|
||||||
return nil, dc.Clean()
|
value, err = dc.Clean()
|
||||||
} else if functor == dc.resetFunc {
|
} else if functor == dc.resetFunc {
|
||||||
return nil, dc.Reset()
|
value, err = dc.Reset()
|
||||||
} else {
|
} else {
|
||||||
ctx := cloneContext(dc.ctx)
|
ctx := cloneContext(dc.ctx)
|
||||||
value, err = functor.Invoke(ctx, name, []any{})
|
value, err = functor.Invoke(ctx, name, []any{})
|
||||||
exportObjects(dc.ctx, ctx)
|
exportObjects(dc.ctx, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
err = errNoOperation(name)
|
err = errNoOperation(name)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *dataCursor) Reset() (err error) {
|
func (dc *dataCursor) Reset() (success bool, err error) {
|
||||||
if dc.resetFunc != nil {
|
if dc.resetFunc != nil {
|
||||||
if dc.resource != nil {
|
if dc.resource != nil {
|
||||||
ctx := cloneContext(dc.ctx)
|
ctx := cloneContext(dc.ctx)
|
||||||
@ -104,10 +99,11 @@ func (dc *dataCursor) Reset() (err error) {
|
|||||||
} else {
|
} else {
|
||||||
err = errNoOperation(resetName)
|
err = errNoOperation(resetName)
|
||||||
}
|
}
|
||||||
|
success = err == nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *dataCursor) Clean() (err error) {
|
func (dc *dataCursor) Clean() (success bool, err error) {
|
||||||
if dc.cleanFunc != nil {
|
if dc.cleanFunc != nil {
|
||||||
if dc.resource != nil {
|
if dc.resource != nil {
|
||||||
ctx := cloneContext(dc.ctx)
|
ctx := cloneContext(dc.ctx)
|
||||||
@ -120,6 +116,7 @@ func (dc *dataCursor) Clean() (err error) {
|
|||||||
} else {
|
} else {
|
||||||
err = errors.New("no 'clean' function defined in the data-source")
|
err = errors.New("no 'clean' function defined in the data-source")
|
||||||
}
|
}
|
||||||
|
success = err == nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ x = 1; y = 2*x
|
|||||||
== Other operations
|
== Other operations
|
||||||
|
|
||||||
=== [blue]`;` operator
|
=== [blue]`;` operator
|
||||||
The semicolon operator [blue]`;` is an infixed operator. It evaluates the left expression first and then the right expression. The latter is the final result.
|
The semicolon operator [blue]`;` is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The latter is the final result.
|
||||||
|
|
||||||
IMPORTANT: Technically [blue]`;` is not treated as a real operator. It acts as a separator in lists of expressions.
|
IMPORTANT: Technically [blue]`;` is not treated as a real operator. It acts as a separator in lists of expressions.
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ a=1; b=2; c=3; a+b+c // returns 6
|
|||||||
[blue]`but` is very similar to [blue]`;`. The only difference is that [blue]`;` can't be used inside parenthesis [blue]`(` and [blue]`)`.
|
[blue]`but` is very similar to [blue]`;`. The only difference is that [blue]`;` can't be used inside parenthesis [blue]`(` and [blue]`)`.
|
||||||
|
|
||||||
=== Assignment operator [blue]`=`
|
=== Assignment operator [blue]`=`
|
||||||
The assignment operator [blue]`=` is used to define variable in the evaluation context or to change their value (see _ExprContext_).
|
The assignment operator [blue]`=` is used to define variables in the evaluation context or to change their value (see _ExprContext_).
|
||||||
The value on the left side of [blue]`=` must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation.
|
The value on the left side of [blue]`=` must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation.
|
||||||
|
|
||||||
.Example
|
.Example
|
||||||
@ -202,13 +202,22 @@ The _selector operator_ is very similar to the _switch/case/default_ statement a
|
|||||||
.Syntax
|
.Syntax
|
||||||
[source,bnf]
|
[source,bnf]
|
||||||
----
|
----
|
||||||
<selector-operator> ::= <expression> "?" <selector-case> { ":" <selector-case> } ["::" <case-value>]
|
<selector-operator> ::= <select-expression> "?" <selector-case> { ":" <selector-case> } ["::" <default-multi-expression>]
|
||||||
<selector-case> ::= [<list>] <case-value>
|
<selector-case> ::= [<match-list>] <case-value>
|
||||||
<case-value> ::= "{" <multi-expr> "}"
|
<match-list> ::= "["<item>{","<items>}"]"
|
||||||
<multi-expr> ::= <expr> {";" <expr>}
|
<item> ::= <expression
|
||||||
|
<case-multi-expression> ::= "{" <multi-expression> "}"
|
||||||
|
<multi-expression> ::= <expression> {";" <expression>}
|
||||||
----
|
----
|
||||||
|
|
||||||
.Example
|
In other words, the selector operator evaluates the expression (`<select-expression>`) on the left-hand side of the `?` symbol; it then compares the result obtained with the values listed in the `<match-list>`'s. If the comparision find a match with a value in a match-list, the associated `<case-multi-expression>` is evaluted, and its result will be the final result of the selection operation.
|
||||||
|
|
||||||
|
The match lists are optional. In that case, the position, from left to right, of the `<selector-case>` is used as match-list. Of course, that only works if the select-expression results in an integer.
|
||||||
|
|
||||||
|
The `:` symbol (colon) is the separator of the selector-cases. Note that if the value of the select-expression does not match any match-list, an error will be issued. Therefore, it is strongly recommended to provide a default (multi-)expression introduced by the `::` symbol (double-colon). Also note that the default expression has no match-list.
|
||||||
|
|
||||||
|
|
||||||
|
.Examples
|
||||||
[source,go]
|
[source,go]
|
||||||
----
|
----
|
||||||
1 ? {"a"} : {"b"} // returns "b"
|
1 ? {"a"} : {"b"} // returns "b"
|
||||||
@ -224,33 +233,38 @@ The _selector operator_ is very similar to the _switch/case/default_ statement a
|
|||||||
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,<5"]
|
[cols="^2,^2,^2,^5,^5"]
|
||||||
|===
|
|===
|
||||||
| Priority | Operators | Position | Operation | Operands and results
|
| Priority | Operators | Position | Operation | Operands and results
|
||||||
|
|
||||||
1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ "!" -> _integer_
|
.1+|*ITEM*| [blue]`.` | _Infix_ | _Item_| _collection_ `"."` _key_ -> _any_
|
||||||
1+|*SIGN*| [blue]`+`, [blue]`-` | _Prefix_ | _Change-sign_| ("+"\|"-") _number_ -> _number_
|
.2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `"++"` -> _integer_
|
||||||
.5+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ "*" _number_ -> _number_
|
| [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `"++"` -> _any_
|
||||||
| [blue]`*` | _Infix_ | _String-repeat_ | _string_ "*" _integer_ -> _string_
|
.1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ `"!"` -> _integer_
|
||||||
| [blue]`/` | _Infix_ | _Division_ | _number_ "/" _number_ -> _number_
|
.3+|*SIGN*| [blue]`+`, [blue]`-` | _Prefix_ | _Change-sign_| (`"+"`\|`"-"`) _number_ -> _number_
|
||||||
| [blue]`./` | _Infix_ | _Float-division_ | __number__ "./" _number_ -> _float_
|
| [blue]`#` | _Prefix_ | _Lenght-of_ | `"#"` _collection_ -> _integer_
|
||||||
| [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ "%" _integer_ -> _integer_
|
| [blue]`#` | _Prefix_ | _Size-of_ | `"#"` _iterator_ -> _integer_
|
||||||
.5+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ "+" _number_ -> _number_
|
.5+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ `"*"` _number_ -> _number_
|
||||||
| [blue]`+` | _Infix_ | _String-concat_ | (_string_\|_number_) "+" (_string_\|_number_) -> _string_
|
| [blue]`*` | _Infix_ | _String-repeat_ | _string_ `"*"` _integer_ -> _string_
|
||||||
| [blue]`+` | _Infix_ | _List-join_ | _list_ "+" _list_ -> _list_
|
| [blue]`/` | _Infix_ | _Division_ | _number_ `"/"` _number_ -> _number_
|
||||||
| [blue]`-` | _Infix_ | _Subtraction_ | _number_ "-" _number_ -> _number_
|
| [blue]`./` | _Infix_ | _Float-division_ | __number__ `"./"` _number_ -> _float_
|
||||||
| [blue]`-` | _Infix_ | _List-difference_ | _list_ "-" _list_ -> _list_
|
| [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ `"%"` _integer_ -> _integer_
|
||||||
.6+|*RELATION*| [blue]`<` | _Infix_ | _less_ | _comparable_ "<" _comparable_ -> _boolean_
|
.5+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ `"+"` _number_ -> _number_
|
||||||
| [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ "\<=" _comparable_ -> _boolean_
|
| [blue]`+` | _Infix_ | _String-concat_ | (_string_\|_number_) `"+"` (_string_\|_number_) -> _string_
|
||||||
| [blue]`>` | _Infix_ | _greater_ | _comparable_ ">" _comparable_ -> _boolean_
|
| [blue]`+` | _Infix_ | _List-join_ | _list_ `"+"` _list_ -> _list_
|
||||||
| [blue]`>=` | _Infix_ | _greater-equal_ | _comparable_ ">=" _comparable_ -> _boolean_
|
| [blue]`-` | _Infix_ | _Subtraction_ | _number_ `"-"` _number_ -> _number_
|
||||||
| [blue]`==` | _Infix_ | _equal_ | _comparable_ "==" _comparable_ -> _boolean_
|
| [blue]`-` | _Infix_ | _List-difference_ | _list_ `"-"` _list_ -> _list_
|
||||||
| [blue]`!=` | _Infix_ | _not-equal_ | _comparable_ "!=" _comparable_ -> _boolean_
|
.6+|*RELATION*| [blue]`<` | _Infix_ | _less_ | _comparable_ `"<"` _comparable_ -> _boolean_
|
||||||
.1+|*NOT*| [blue]`not` | _Prefix_ | _not_ | "not" _boolean_ -> _boolean_
|
| [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ `"\<="` _comparable_ -> _boolean_
|
||||||
.2+|*AND*| [blue]`and` | _Infix_ | _and_ | _boolean_ "and" _boolean_ -> _boolean_
|
| [blue]`>` | _Infix_ | _greater_ | _comparable_ `">"` _comparable_ -> _boolean_
|
||||||
| [blue]`&&` | _Infix_ | _and_ | _boolean_ "&&" _boolean_ -> _boolean_
|
| [blue]`>=` | _Infix_ | _greater-equal_ | _comparable_ `">="` _comparable_ -> _boolean_
|
||||||
.2+|*OR*| [blue]`or` | _Infix_ | _or_ | _boolean_ "or" _boolean_ -> _boolean_
|
| [blue]`==` | _Infix_ | _equal_ | _comparable_ `"=="` _comparable_ -> _boolean_
|
||||||
| [blue]`\|\|` | _Infix_ | _or_ | _boolean_ "\|\|" _boolean_ -> _boolean_
|
| [blue]`!=` | _Infix_ | _not-equal_ | _comparable_ `"!="` _comparable_ -> _boolean_
|
||||||
|
.1+|*NOT*| [blue]`not` | _Prefix_ | _not_ | `"not"` _boolean_ -> _boolean_
|
||||||
|
.2+|*AND*| [blue]`and` | _Infix_ | _and_ | _boolean_ `"and"` _boolean_ -> _boolean_
|
||||||
|
| [blue]`&&` | _Infix_ | _and_ | _boolean_ `"&&"` _boolean_ -> _boolean_
|
||||||
|
.2+|*OR*| [blue]`or` | _Infix_ | _or_ | _boolean_ `"or"` _boolean_ -> _boolean_
|
||||||
|
| [blue]`\|\|` | _Infix_ | _or_ | _boolean_ `"\|\|"` _boolean_ -> _boolean_
|
||||||
.1+|*ASSIGN*| [blue]`=` | _Infix_ | _assignment_ | _identifier_ "=" _any_ -> _any_
|
.1+|*ASSIGN*| [blue]`=` | _Infix_ | _assignment_ | _identifier_ "=" _any_ -> _any_
|
||||||
.1+|*BUT*| [blue]`but` | _Infix_ | _but_ | _any_ "but" _any_ -> _any_
|
.1+|*BUT*| [blue]`but` | _Infix_ | _but_ | _any_ "but" _any_ -> _any_
|
||||||
|===
|
|===
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -53,7 +52,7 @@ func TestExpr(t *testing.T) {
|
|||||||
ImportOsFuncs(ctx)
|
ImportOsFuncs(ctx)
|
||||||
parser := NewParser(ctx)
|
parser := NewParser(ctx)
|
||||||
|
|
||||||
logTest(t, i+1, input.source, input.wantResult, input.wantErr)
|
logTest(t, i+1, "Expr", input.source, input.wantResult, input.wantErr)
|
||||||
|
|
||||||
r := strings.NewReader(input.source)
|
r := strings.NewReader(input.source)
|
||||||
scanner := NewScanner(r, DefaultTranslations())
|
scanner := NewScanner(r, DefaultTranslations())
|
||||||
@ -81,5 +80,5 @@ func TestExpr(t *testing.T) {
|
|||||||
failed++
|
failed++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed))
|
t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed)
|
||||||
}
|
}
|
||||||
|
33
file-reader.expr
Normal file
33
file-reader.expr
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
builtin ["os.file", "base"];
|
||||||
|
|
||||||
|
readInt=func(fh){
|
||||||
|
line=readFile(fh);
|
||||||
|
line ? [nil] {nil} :: {int(line)}
|
||||||
|
};
|
||||||
|
|
||||||
|
ds={
|
||||||
|
"init":func(filename){
|
||||||
|
fh=openFile(filename);
|
||||||
|
fh ? [nil] {nil} :: { @current=readInt(fh); @prev=@current };
|
||||||
|
fh
|
||||||
|
},
|
||||||
|
"current":func(){
|
||||||
|
prev
|
||||||
|
},
|
||||||
|
"next":func(fh){
|
||||||
|
current ?
|
||||||
|
[nil] {current}
|
||||||
|
:: {@prev=current; @current=readInt(fh) but current}
|
||||||
|
},
|
||||||
|
"clean":func(fh){
|
||||||
|
closeFile(fh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//;f=$(ds, "int.list")
|
||||||
|
/*
|
||||||
|
;f++
|
||||||
|
;f++
|
||||||
|
;f++
|
||||||
|
*/
|
||||||
|
//;add(f)
|
57
func-math.go
57
func-math.go
@ -9,40 +9,40 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkNumberParamExpected(funcName string, paramValue any, paramPos int) (err error) {
|
func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) {
|
||||||
if !(isNumber(paramValue) || isList(paramValue) || isFraction(paramValue)) {
|
if !(isNumber(paramValue) || isFraction(paramValue)) /*|| isList(paramValue)*/ {
|
||||||
err = fmt.Errorf("%s(): param nr %d has wrong type %T, number expected", funcName, paramPos+1, paramValue)
|
err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected",
|
||||||
|
funcName, paramPos+1, subPos+1, level, paramValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func doAdd(ctx ExprContext, name string, it Iterator) (result any, err error) {
|
func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result any, err error) {
|
||||||
var sumAsFloat, sumAsFract bool
|
var sumAsFloat, sumAsFract bool
|
||||||
var floatSum float64 = 0.0
|
var floatSum float64 = 0.0
|
||||||
var intSum int64 = 0
|
var intSum int64 = 0
|
||||||
var fractSum *fraction
|
var fractSum *fraction
|
||||||
var v any
|
var v any
|
||||||
|
|
||||||
|
level++
|
||||||
|
|
||||||
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
||||||
|
if list, ok := v.(*ListType); ok {
|
||||||
|
v = NewListIterator(list, nil)
|
||||||
|
}
|
||||||
if subIter, ok := v.(Iterator); ok {
|
if subIter, ok := v.(Iterator); ok {
|
||||||
if v, err = doAdd(ctx, name, subIter); err != nil {
|
if v, err = doAdd(ctx, name, subIter, count, level); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if subIter.HasOperation(cleanName) {
|
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) {
|
||||||
if _, err = subIter.CallOperation(cleanName, nil); err != nil {
|
if _, err = extIter.CallOperation(cleanName, nil); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if err = checkNumberParamExpected(name, v, count, level, it.Index()); err != nil {
|
||||||
if err = checkNumberParamExpected(name, v, it.Index()); err != nil {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if list, ok := v.(*ListType); ok {
|
count++
|
||||||
if v, err = doAdd(ctx, name, NewListIterator(list)); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sumAsFloat {
|
if !sumAsFloat {
|
||||||
if isFloat(v) {
|
if isFloat(v) {
|
||||||
@ -87,38 +87,37 @@ func doAdd(ctx ExprContext, name string, it Iterator) (result any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func addFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func addFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
result, err = doAdd(ctx, name, NewArrayIterator(args))
|
result, err = doAdd(ctx, name, NewArrayIterator(args), 0, -1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func doMul(ctx ExprContext, name string, it Iterator) (result any, err error) {
|
func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result any, err error) {
|
||||||
var mulAsFloat, mulAsFract bool
|
var mulAsFloat, mulAsFract bool
|
||||||
var floatProd float64 = 1.0
|
var floatProd float64 = 1.0
|
||||||
var intProd int64 = 1
|
var intProd int64 = 1
|
||||||
var fractProd *fraction
|
var fractProd *fraction
|
||||||
var v any
|
var v any
|
||||||
|
|
||||||
|
level++
|
||||||
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
||||||
|
if list, ok := v.(*ListType); ok {
|
||||||
|
v = NewListIterator(list, nil)
|
||||||
|
}
|
||||||
if subIter, ok := v.(Iterator); ok {
|
if subIter, ok := v.(Iterator); ok {
|
||||||
if v, err = doMul(ctx, name, subIter); err != nil {
|
if v, err = doMul(ctx, name, subIter, count, level); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if subIter.HasOperation(cleanName) {
|
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) {
|
||||||
if _, err = subIter.CallOperation(cleanName, nil); err != nil {
|
if _, err = extIter.CallOperation(cleanName, nil); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err = checkNumberParamExpected(name, v, it.Index()); err != nil {
|
if err = checkNumberParamExpected(name, v, count, level, it.Index()); err != nil {
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if list, ok := v.(*ListType); ok {
|
|
||||||
if v, err = doMul(ctx, name, NewListIterator(list)); err != nil {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
count++
|
||||||
|
|
||||||
if !mulAsFloat {
|
if !mulAsFloat {
|
||||||
if isFloat(v) {
|
if isFloat(v) {
|
||||||
@ -163,7 +162,7 @@ func doMul(ctx ExprContext, name string, it Iterator) (result any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
result, err = doMul(ctx, name, NewArrayIterator(args))
|
result, err = doMul(ctx, name, NewArrayIterator(args), 0, -1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,5 +172,5 @@ func ImportMathFuncs(ctx ExprContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
registerImport("math.arith", ImportMathFuncs, "Function add() and mul()")
|
registerImport("math.arith", ImportMathFuncs, "Functions add() and mul()")
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err erro
|
|||||||
result = ""
|
result = ""
|
||||||
} else if len(args) == 2 {
|
} else if len(args) == 2 {
|
||||||
if ls, ok := args[1].(*ListType); ok {
|
if ls, ok := args[1].(*ListType); ok {
|
||||||
result, err = doJoinStr(name, sep, NewListIterator(ls))
|
result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
|
||||||
} else if it, ok := args[1].(Iterator); ok {
|
} else if it, ok := args[1].(Iterator); ok {
|
||||||
result, err = doJoinStr(name, sep, it)
|
result, err = doJoinStr(name, sep, it)
|
||||||
} else {
|
} else {
|
||||||
|
@ -6,7 +6,6 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -50,7 +49,7 @@ func TestFuncs(t *testing.T) {
|
|||||||
// ImportOsFuncs(ctx)
|
// ImportOsFuncs(ctx)
|
||||||
parser := NewParser(ctx)
|
parser := NewParser(ctx)
|
||||||
|
|
||||||
logTest(t, i+1, input.source, input.wantResult, input.wantErr)
|
logTest(t, i+1, "Funcs", input.source, input.wantResult, input.wantErr)
|
||||||
|
|
||||||
r := strings.NewReader(input.source)
|
r := strings.NewReader(input.source)
|
||||||
scanner := NewScanner(r, DefaultTranslations())
|
scanner := NewScanner(r, DefaultTranslations())
|
||||||
@ -78,5 +77,5 @@ func TestFuncs(t *testing.T) {
|
|||||||
failed++
|
failed++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed))
|
t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed)
|
||||||
}
|
}
|
||||||
|
100
iter-list.go
100
iter-list.go
@ -4,26 +4,69 @@
|
|||||||
// iter-list.go
|
// iter-list.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import "io"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
type ListIterator struct {
|
type ListIterator struct {
|
||||||
a *ListType
|
a *ListType
|
||||||
|
count int
|
||||||
index int
|
index int
|
||||||
|
start int
|
||||||
|
stop int
|
||||||
|
step int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewListIterator(list *ListType) *ListIterator {
|
func NewListIterator(list *ListType, args []any) (it *ListIterator) {
|
||||||
return &ListIterator{a: list, index: 0}
|
var argc int = 0
|
||||||
|
listLen := len(([]any)(*list))
|
||||||
|
if args != nil {
|
||||||
|
argc = len(args)
|
||||||
|
}
|
||||||
|
it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1}
|
||||||
|
if argc >= 1 {
|
||||||
|
if i, err := toInt(args[0], "start index"); err == nil {
|
||||||
|
if i < 0 {
|
||||||
|
i = listLen + i
|
||||||
|
}
|
||||||
|
it.start = i
|
||||||
|
}
|
||||||
|
if argc >= 2 {
|
||||||
|
if i, err := toInt(args[1], "stop index"); err == nil {
|
||||||
|
if i < 0 {
|
||||||
|
i = listLen + i
|
||||||
|
}
|
||||||
|
it.stop = i
|
||||||
|
}
|
||||||
|
if argc >= 3 {
|
||||||
|
if i, err := toInt(args[2], "step"); err == nil {
|
||||||
|
if i < 0 {
|
||||||
|
i = -i
|
||||||
|
}
|
||||||
|
if it.start > it.stop {
|
||||||
|
it.step = -i
|
||||||
|
} else {
|
||||||
|
it.step = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.index = it.start - it.step
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewArrayIterator(array []any) *ListIterator {
|
func NewArrayIterator(array []any) (it *ListIterator) {
|
||||||
return &ListIterator{a: (*ListType)(&array), index: 0}
|
it = &ListIterator{a: (*ListType)(&array), count: 0, index: -1, start: 0, stop: len(array) - 1, step: 1}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAnyIterator(value any) (it *ListIterator) {
|
func NewAnyIterator(value any) (it *ListIterator) {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
it = NewArrayIterator([]any{})
|
it = NewArrayIterator([]any{})
|
||||||
} else if list, ok := value.(*ListType); ok {
|
} else if list, ok := value.(*ListType); ok {
|
||||||
it = NewListIterator(list)
|
it = NewListIterator(list, nil)
|
||||||
} else if array, ok := value.([]any); ok {
|
} else if array, ok := value.([]any); ok {
|
||||||
it = NewArrayIterator(array)
|
it = NewArrayIterator(array)
|
||||||
} else if it1, ok := value.(*ListIterator); ok {
|
} else if it1, ok := value.(*ListIterator); ok {
|
||||||
@ -34,17 +77,36 @@ func NewAnyIterator(value any) (it *ListIterator) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *ListIterator) HasOperation(name string) bool {
|
func (it *ListIterator) String() string {
|
||||||
return false
|
var l = 0
|
||||||
|
if it.a != nil {
|
||||||
|
l = len(*it.a)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("$(#%d)", l)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *ListIterator) CallOperation(name string, args []any) (any, error) {
|
func (it *ListIterator) HasOperation(name string) bool {
|
||||||
return nil, errNoOperation(name)
|
yes := name == resetName || name == indexName || name == countName
|
||||||
|
return yes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *ListIterator) CallOperation(name string, args []any) (v any, err error) {
|
||||||
|
switch name {
|
||||||
|
case resetName:
|
||||||
|
v, err = it.Reset()
|
||||||
|
case indexName:
|
||||||
|
v = int64(it.Index())
|
||||||
|
case countName:
|
||||||
|
v = it.count
|
||||||
|
default:
|
||||||
|
err = errNoOperation(name)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *ListIterator) Current() (item any, err error) {
|
func (it *ListIterator) Current() (item any, err error) {
|
||||||
a := *(it.a)
|
a := *(it.a)
|
||||||
if it.index >= 0 && it.index < len(a) {
|
if it.index >= 0 && it.index <= it.stop {
|
||||||
item = a[it.index]
|
item = a[it.index]
|
||||||
} else {
|
} else {
|
||||||
err = io.EOF
|
err = io.EOF
|
||||||
@ -53,18 +115,18 @@ func (it *ListIterator) Current() (item any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (it *ListIterator) Next() (item any, err error) {
|
func (it *ListIterator) Next() (item any, err error) {
|
||||||
|
it.index += it.step
|
||||||
if item, err = it.Current(); err != io.EOF {
|
if item, err = it.Current(); err != io.EOF {
|
||||||
it.index++
|
it.count++
|
||||||
}
|
}
|
||||||
// if it.index < len(it.a) {
|
|
||||||
// item = it.a[it.index]
|
|
||||||
// it.index++
|
|
||||||
// } else {
|
|
||||||
// err = io.EOF
|
|
||||||
// }
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *ListIterator) Index() int {
|
func (it *ListIterator) Index() int {
|
||||||
return it.index - 1
|
return it.index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *ListIterator) Reset() (bool, error) {
|
||||||
|
it.index = it.start
|
||||||
|
return true, nil
|
||||||
}
|
}
|
||||||
|
18
iterator.expr
Normal file
18
iterator.expr
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
ds={
|
||||||
|
"init":func(end){@end=end; @current=0; @prev=@current},
|
||||||
|
"current":func(){prev},
|
||||||
|
"next":func(){
|
||||||
|
(current <= end) ? [true] {@current=current+1; @prev=current} :: {nil}
|
||||||
|
},
|
||||||
|
"reset":func(){@current=0; @prev=@current}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example
|
||||||
|
//;
|
||||||
|
//it=$(ds,3);
|
||||||
|
//it++;
|
||||||
|
//it."reset"
|
||||||
|
//it++;
|
||||||
|
//it++;
|
||||||
|
//add(it)
|
||||||
|
|
16
iterator.go
16
iterator.go
@ -9,10 +9,26 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Operator names
|
||||||
|
|
||||||
|
const (
|
||||||
|
initName = "init"
|
||||||
|
cleanName = "clean"
|
||||||
|
resetName = "reset"
|
||||||
|
nextName = "next"
|
||||||
|
currentName = "current"
|
||||||
|
indexName = "index"
|
||||||
|
countName = "count"
|
||||||
|
)
|
||||||
|
|
||||||
type Iterator interface {
|
type Iterator interface {
|
||||||
Next() (item any, err error) // must return io.EOF after the last item
|
Next() (item any, err error) // must return io.EOF after the last item
|
||||||
Current() (item any, err error)
|
Current() (item any, err error)
|
||||||
Index() int
|
Index() int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExtIterator interface {
|
||||||
|
Iterator
|
||||||
HasOperation(name string) bool
|
HasOperation(name string) bool
|
||||||
CallOperation(name string, args []any) (value any, err error)
|
CallOperation(name string, args []any) (value any, err error)
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- function call term
|
// -------- function call term
|
||||||
@ -21,6 +22,20 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval func call
|
// -------- eval func call
|
||||||
|
func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) {
|
||||||
|
if info, exists := ctx.GetFuncInfo(name); exists {
|
||||||
|
if info.MinArgs() > len(params) {
|
||||||
|
err = fmt.Errorf("too few params -- expected %d, got %d", info.MinArgs(), len(params))
|
||||||
|
}
|
||||||
|
if info.MaxArgs() >= 0 && info.MaxArgs() < len(params) {
|
||||||
|
err = fmt.Errorf("too much params -- expected %d, got %d", info.MaxArgs(), len(params))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("unknown function %s()", name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
|
func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
|
||||||
ctx := cloneContext(parentCtx)
|
ctx := cloneContext(parentCtx)
|
||||||
name, _ := self.tk.Value.(string)
|
name, _ := self.tk.Value.(string)
|
||||||
@ -34,19 +49,21 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
|
|||||||
params[i] = param
|
params[i] = param
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
if err = checkFunctionCall(ctx, name, params); err == nil {
|
||||||
if v, err = ctx.Call(name, params); err == nil {
|
if v, err = ctx.Call(name, params); err == nil {
|
||||||
exportObjects(parentCtx, ctx)
|
exportObjects(parentCtx, ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------- function definition term
|
// -------- function definition term
|
||||||
func newFuncDefTerm(tk *Token, args []*term) *term {
|
func newFuncDefTerm(tk *Token, args []*term) *term {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk, // value is the expression body
|
||||||
parent: nil,
|
parent: nil,
|
||||||
children: args, // arg[0]=formal-param-list, arg[1]=*ast
|
children: args, // function params
|
||||||
position: posLeaf,
|
position: posLeaf,
|
||||||
priority: priValue,
|
priority: priValue,
|
||||||
evalFunc: evalFuncDef,
|
evalFunc: evalFuncDef,
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
// -------- iterator term
|
// -------- iterator term
|
||||||
|
|
||||||
func newIteratorTerm(tk *Token, dsTerm *term, args []*term) *term {
|
func newDsIteratorTerm(tk *Token, dsTerm *term, args []*term) *term {
|
||||||
tk.Sym = SymIterator
|
tk.Sym = SymIterator
|
||||||
|
|
||||||
children := make([]*term, 0, 1+len(args))
|
children := make([]*term, 0, 1+len(args))
|
||||||
@ -28,6 +28,18 @@ func newIteratorTerm(tk *Token, dsTerm *term, args []*term) *term {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newIteratorTerm(tk *Token, args []*term) *term {
|
||||||
|
tk.Sym = SymIterator
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
parent: nil,
|
||||||
|
children: args,
|
||||||
|
position: posLeaf,
|
||||||
|
priority: priValue,
|
||||||
|
evalFunc: evalIterator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -------- eval iterator
|
// -------- eval iterator
|
||||||
|
|
||||||
func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) {
|
func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) {
|
||||||
@ -43,59 +55,21 @@ func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// func getDataSourceDict(ctx ExprContext, self *term) (ds map[string]Functor, err error) {
|
func evalFirstChild(ctx ExprContext, self *term) (value any, err error) {
|
||||||
// var value any
|
|
||||||
// if len(self.children) < 1 || self.children[0] == nil {
|
|
||||||
// err = self.Errorf("missing the data-source parameter")
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if value, err = self.children[0].compute(ctx); err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if dictAny, ok := value.(map[any]any); ok {
|
|
||||||
// ds = make(map[string]Functor)
|
|
||||||
// // required functions
|
|
||||||
// for _, k := range []string{currentName, nextName} {
|
|
||||||
// if item, exists := dictAny[k]; exists && item != nil {
|
|
||||||
// if functor, ok := item.(*funcDefFunctor); ok {
|
|
||||||
// ds[k] = functor
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// err = fmt.Errorf("the data-source must provide a non-nil %q operator", k)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // Optional functions
|
|
||||||
// for _, k := range []string{initName, resetName, cleanName} {
|
|
||||||
// if item, exists := dictAny[k]; exists && item != nil {
|
|
||||||
// if functor, ok := item.(*funcDefFunctor); ok {
|
|
||||||
// ds[k] = functor
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// err = self.Errorf("the first param (data-source) of an iterator must be a dict, not a %T", value)
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
func getDataSourceDict(ctx ExprContext, self *term) (ds map[string]Functor, err error) {
|
|
||||||
var value any
|
|
||||||
if len(self.children) < 1 || self.children[0] == nil {
|
if len(self.children) < 1 || self.children[0] == nil {
|
||||||
err = self.Errorf("missing the data-source parameter")
|
err = self.Errorf("missing the data-source parameter")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if value, err = self.children[0].compute(ctx); err != nil {
|
value, err = self.children[0].compute(ctx)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map[string]Functor, err error) {
|
||||||
|
if dictAny, ok := firstChildValue.(map[any]any); ok {
|
||||||
requiredFields := []string{currentName, nextName}
|
requiredFields := []string{currentName, nextName}
|
||||||
fieldsMask := 0b11
|
fieldsMask := 0b11
|
||||||
foundFields := 0
|
foundFields := 0
|
||||||
if dictAny, ok := value.(map[any]any); ok {
|
|
||||||
ds = make(map[string]Functor)
|
ds = make(map[string]Functor)
|
||||||
for keyAny, item := range dictAny {
|
for keyAny, item := range dictAny {
|
||||||
if key, ok := keyAny.(string); ok {
|
if key, ok := keyAny.(string); ok {
|
||||||
@ -117,22 +91,24 @@ func getDataSourceDict(ctx ExprContext, self *term) (ds map[string]Functor, err
|
|||||||
}
|
}
|
||||||
err = fmt.Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
|
err = fmt.Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
err = self.Errorf("the first param (data-source) of an iterator must be a dict, not a %T", value)
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalIterator(ctx ExprContext, self *term) (v any, err error) {
|
func evalIterator(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
var firstChildValue any
|
||||||
var ds map[string]Functor
|
var ds map[string]Functor
|
||||||
|
|
||||||
if ds, err = getDataSourceDict(ctx, self); err != nil {
|
if firstChildValue, err = evalFirstChild(ctx, self); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ds, err = getDataSourceDict(ctx, self, firstChildValue); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ds != nil {
|
||||||
dc := newDataCursor(ctx, ds)
|
dc := newDataCursor(ctx, ds)
|
||||||
// var it Iterator = dc
|
|
||||||
// fmt.Println(it)
|
|
||||||
if initFunc, exists := ds[initName]; exists && initFunc != nil {
|
if initFunc, exists := ds[initName]; exists && initFunc != nil {
|
||||||
var args []any
|
var args []any
|
||||||
if len(self.children) > 1 {
|
if len(self.children) > 1 {
|
||||||
@ -156,6 +132,53 @@ func evalIterator(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
dc.resetFunc, _ = ds[resetName]
|
dc.resetFunc, _ = ds[resetName]
|
||||||
|
|
||||||
v = dc
|
v = dc
|
||||||
|
} else if list, ok := firstChildValue.(*ListType); ok {
|
||||||
|
var args []any
|
||||||
|
if args, err = evalSibling(ctx, self.children, nil); err == nil {
|
||||||
|
v = NewListIterator(list, args)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var list []any
|
||||||
|
if list, err = evalSibling(ctx, self.children, firstChildValue); err == nil {
|
||||||
|
v = NewArrayIterator(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// func evalChildren(ctx ExprContext, terms []*term, firstChildValue any) (list *ListType, err error) {
|
||||||
|
// items := make(ListType, len(terms))
|
||||||
|
// for i, tree := range terms {
|
||||||
|
// var param any
|
||||||
|
// if i == 0 && firstChildValue != nil {
|
||||||
|
// param = firstChildValue
|
||||||
|
// } else if param, err = tree.compute(ctx); err != nil {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// items[i] = param
|
||||||
|
// }
|
||||||
|
// if err == nil {
|
||||||
|
// list = &items
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
func evalSibling(ctx ExprContext, terms []*term, firstChildValue any) (list []any, err error) {
|
||||||
|
items := make([]any, 0, len(terms))
|
||||||
|
for i, tree := range terms {
|
||||||
|
var param any
|
||||||
|
if i == 0 {
|
||||||
|
if firstChildValue == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
param = firstChildValue
|
||||||
|
} else if param, err = tree.compute(ctx); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
items = append(items, param)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
list = items
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,14 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
|
|
||||||
if v, err = self.children[1].compute(ctx); err == nil {
|
if v, err = self.children[1].compute(ctx); err == nil {
|
||||||
if functor, ok := v.(Functor); ok {
|
if functor, ok := v.(Functor); ok {
|
||||||
ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
var minArgs, maxArgs int = 0, 0
|
||||||
|
|
||||||
|
if funcDef, ok := functor.(*funcDefFunctor); ok {
|
||||||
|
l := len(funcDef.params)
|
||||||
|
minArgs = l
|
||||||
|
maxArgs = l
|
||||||
|
}
|
||||||
|
ctx.RegisterFunc(leftTerm.source(), functor, minArgs, maxArgs)
|
||||||
} else {
|
} else {
|
||||||
ctx.setVar(leftTerm.source(), v)
|
ctx.setVar(leftTerm.source(), v)
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,7 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
v = count
|
v = int64(count)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ func newContextTerm(tk *Token) (inst *term) {
|
|||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 1),
|
children: make([]*term, 0, 1),
|
||||||
position: posPrefix,
|
position: posPrefix,
|
||||||
priority: priPrePost,
|
priority: priIncDec,
|
||||||
evalFunc: evalContextValue,
|
evalFunc: evalContextValue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,10 +47,11 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
indexTerm := self.children[1]
|
indexTerm := self.children[1]
|
||||||
|
|
||||||
switch unboxedValue := leftValue.(type) {
|
switch unboxedValue := leftValue.(type) {
|
||||||
case []any:
|
case *ListType:
|
||||||
var index int
|
var index int
|
||||||
if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil {
|
array := ([]any)(*unboxedValue)
|
||||||
v = unboxedValue[index]
|
if index, err = verifyIndex(ctx, indexTerm, len(array)); err == nil {
|
||||||
|
v = array[index]
|
||||||
}
|
}
|
||||||
case string:
|
case string:
|
||||||
var index int
|
var index int
|
||||||
@ -65,17 +66,27 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
err = fmt.Errorf("key %v does not belong to the dictionary", rightValue)
|
err = fmt.Errorf("key %v does not belong to the dictionary", rightValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *dataCursor:
|
// case *dataCursor:
|
||||||
|
// if indexTerm.symbol() == SymIdentifier {
|
||||||
|
// opName := indexTerm.source()
|
||||||
|
// if opName == resetName {
|
||||||
|
// _, err = unboxedValue.Reset()
|
||||||
|
// } else if opName == cleanName {
|
||||||
|
// _, err = unboxedValue.Clean()
|
||||||
|
// } else {
|
||||||
|
// err = indexTerm.Errorf("iterators do not support command %q", opName)
|
||||||
|
// }
|
||||||
|
// v = err == nil
|
||||||
|
// }
|
||||||
|
case ExtIterator:
|
||||||
if indexTerm.symbol() == SymIdentifier {
|
if indexTerm.symbol() == SymIdentifier {
|
||||||
opName := indexTerm.source()
|
opName := indexTerm.source()
|
||||||
if opName == resetName {
|
if unboxedValue.HasOperation(opName) {
|
||||||
err = unboxedValue.Reset()
|
v, err = unboxedValue.CallOperation(opName, []any{})
|
||||||
} else if opName == cleanName {
|
|
||||||
err = unboxedValue.Clean()
|
|
||||||
} else {
|
} else {
|
||||||
err = indexTerm.Errorf("iterators do not support command %q", opName)
|
err = indexTerm.Errorf("this iterator do not support the %q command", opName)
|
||||||
|
v = false
|
||||||
}
|
}
|
||||||
v = err == nil
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
// operator-iter-value.go
|
// operator-iter-value.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
|
|
||||||
//-------- iter value term
|
//-------- iter value term
|
||||||
|
|
||||||
func newIterValueTerm(tk *Token) (inst *term) {
|
func newIterValueTerm(tk *Token) (inst *term) {
|
||||||
@ -18,16 +17,16 @@ func newIterValueTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func evalIterValue(ctx ExprContext, self *term) (v any, err error) {
|
func evalIterValue(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue any
|
var childValue any
|
||||||
|
|
||||||
if leftValue, err = self.evalPrefix(ctx); err != nil {
|
if childValue, err = self.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if dc, ok := leftValue.(*dataCursor); ok {
|
if it, ok := childValue.(Iterator); ok {
|
||||||
v, err = dc.Current()
|
v, err = it.Current()
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleType(leftValue)
|
err = self.errIncompatibleType(childValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -17,22 +17,27 @@ func newLengthTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func evalLength(ctx ExprContext, self *term) (v any, err error) {
|
func evalLength(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var rightValue any
|
var childValue any
|
||||||
|
|
||||||
if rightValue, err = self.evalPrefix(ctx); err != nil {
|
if childValue, err = self.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isList(rightValue) {
|
if isList(childValue) {
|
||||||
list, _ := rightValue.([]any)
|
list, _ := childValue.([]any)
|
||||||
v = int64(len(list))
|
v = int64(len(list))
|
||||||
} else if isString(rightValue) {
|
} else if isString(childValue) {
|
||||||
s, _ := rightValue.(string)
|
s, _ := childValue.(string)
|
||||||
v = int64(len(s))
|
v = int64(len(s))
|
||||||
} else if it, ok := rightValue.(Iterator); ok {
|
} else if it, ok := childValue.(Iterator); ok {
|
||||||
v = int64(it.Index())
|
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) {
|
||||||
|
count, _ := extIt.CallOperation(countName, nil)
|
||||||
|
v, _ = toInt(count, "")
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleType(rightValue)
|
v = int64(it.Index() + 1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = self.errIncompatibleType(childValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -12,25 +12,25 @@ func newPostIncTerm(tk *Token) *term {
|
|||||||
parent: nil,
|
parent: nil,
|
||||||
children: make([]*term, 0, 1),
|
children: make([]*term, 0, 1),
|
||||||
position: posPostfix,
|
position: posPostfix,
|
||||||
priority: priPrePost,
|
priority: priIncDec,
|
||||||
evalFunc: evalPostInc,
|
evalFunc: evalPostInc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalPostInc(ctx ExprContext, self *term) (v any, err error) {
|
func evalPostInc(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var leftValue any
|
var childValue any
|
||||||
if leftValue, err = self.evalPrefix(ctx); err != nil {
|
if childValue, err = self.evalPrefix(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if dc, ok := leftValue.(*dataCursor); ok {
|
if it, ok := childValue.(Iterator); ok {
|
||||||
v, err = dc.Next()
|
v, err = it.Next()
|
||||||
} else if isInteger(leftValue) && self.children[0].symbol() == SymIdentifier {
|
} else if isInteger(childValue) && self.children[0].symbol() == SymIdentifier {
|
||||||
v = leftValue
|
v = childValue
|
||||||
i, _ := leftValue.(int64)
|
i, _ := childValue.(int64)
|
||||||
ctx.SetVar(self.children[0].source(), i+1)
|
ctx.SetVar(self.children[0].source(), i+1)
|
||||||
} else {
|
} else {
|
||||||
self.errIncompatibleType(leftValue)
|
err = self.errIncompatibleType(childValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
97
parser.go
97
parser.go
@ -62,30 +62,45 @@ func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token)
|
|||||||
// // Example: "add = func(x,y) {x+y}
|
// // Example: "add = func(x,y) {x+y}
|
||||||
// var body *ast
|
// var body *ast
|
||||||
// args := make([]*term, 0)
|
// args := make([]*term, 0)
|
||||||
// tk := scanner.Next()
|
// lastSym := SymUnknown
|
||||||
// for tk.Sym != SymClosedRound && tk.Sym != SymEos {
|
// itemExpected := false
|
||||||
// if tk.Sym == SymIdentifier {
|
// tk := scanner.Previous()
|
||||||
// t := newTerm(tk, nil)
|
// for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
// args = append(args, t)
|
// var subTree *ast
|
||||||
|
// if subTree, err = self.parseItem(scanner, true, SymComma, SymClosedRound); err == nil {
|
||||||
|
// if subTree.root != nil {
|
||||||
|
// if subTree.root.symbol() == SymIdentifier {
|
||||||
|
// args = append(args, subTree.root)
|
||||||
// } else {
|
// } else {
|
||||||
// err = tk.Errorf("invalid param %q, variable identifier expected", tk.source)
|
// err = tk.ErrorExpectedGotString("param-name", subTree.root.String())
|
||||||
|
// }
|
||||||
|
// } else if itemExpected {
|
||||||
|
// prev := scanner.Previous()
|
||||||
|
// err = prev.ErrorExpectedGot("function-param")
|
||||||
// break
|
// break
|
||||||
// }
|
// }
|
||||||
// tk = scanner.Next()
|
// } else {
|
||||||
|
// break
|
||||||
// }
|
// }
|
||||||
// if err == nil && tk.Sym != SymClosedRound {
|
// lastSym = scanner.Previous().Sym
|
||||||
// err = tk.Errorf("unterminate function params list")
|
// itemExpected = lastSym == SymComma
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if err == nil && lastSym != SymClosedRound {
|
||||||
|
// err = tk.ErrorExpectedGot(")")
|
||||||
// }
|
// }
|
||||||
// if err == nil {
|
// if err == nil {
|
||||||
// tk = scanner.Next()
|
// tk = scanner.Next()
|
||||||
// if tk.Sym == SymOpenBrace {
|
// if tk.Sym == SymOpenBrace {
|
||||||
// body, err = self.parseGeneral(scanner, true, true, SymClosedBrace)
|
// body, err = self.parseGeneral(scanner, true, true, SymClosedBrace)
|
||||||
|
// } else {
|
||||||
|
// err = tk.ErrorExpectedGot("{")
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
// if err == nil {
|
// if err == nil {
|
||||||
// // TODO Check arguments
|
// // TODO Check arguments
|
||||||
// if scanner.Previous().Sym != SymClosedBrace {
|
// if scanner.Previous().Sym != SymClosedBrace {
|
||||||
// err = scanner.Previous().Errorf("not properly terminated function body")
|
// err = scanner.Previous().ErrorExpectedGot("}")
|
||||||
// } else {
|
// } else {
|
||||||
// tk = scanner.makeValueToken(SymExpression, "", body)
|
// tk = scanner.makeValueToken(SymExpression, "", body)
|
||||||
// tree = newFuncDefTerm(tk, args)
|
// tree = newFuncDefTerm(tk, args)
|
||||||
@ -102,20 +117,14 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
itemExpected := false
|
itemExpected := false
|
||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
var subTree *ast
|
tk = scanner.Next()
|
||||||
if subTree, err = self.parseItem(scanner, true, SymComma, SymClosedRound); err == nil {
|
if tk.IsSymbol(SymIdentifier) {
|
||||||
if subTree.root != nil {
|
param := newTerm(tk, nil)
|
||||||
if subTree.root.symbol() == SymIdentifier {
|
args = append(args, param)
|
||||||
args = append(args, subTree.root)
|
tk = scanner.Next()
|
||||||
} else {
|
|
||||||
err = tk.Errorf("exptected identifier, got %q", subTree.root)
|
|
||||||
}
|
|
||||||
} else if itemExpected {
|
} else if itemExpected {
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
err = prev.Errorf("expected function parameter, got %q", prev)
|
err = prev.ErrorExpectedGot("function-param")
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
lastSym = scanner.Previous().Sym
|
lastSym = scanner.Previous().Sym
|
||||||
@ -123,18 +132,20 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && lastSym != SymClosedRound {
|
if err == nil && lastSym != SymClosedRound {
|
||||||
err = tk.Errorf("unterminated function parameters list")
|
err = tk.ErrorExpectedGot(")")
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tk = scanner.Next()
|
tk = scanner.Next()
|
||||||
if tk.Sym == SymOpenBrace {
|
if tk.IsSymbol(SymOpenBrace) {
|
||||||
body, err = self.parseGeneral(scanner, true, true, SymClosedBrace)
|
body, err = self.parseGeneral(scanner, true, true, SymClosedBrace)
|
||||||
|
} else {
|
||||||
|
err = tk.ErrorExpectedGot("{")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO Check arguments
|
// TODO Check arguments
|
||||||
if scanner.Previous().Sym != SymClosedBrace {
|
if scanner.Previous().Sym != SymClosedBrace {
|
||||||
err = scanner.Previous().Errorf("not properly terminated function body")
|
err = scanner.Previous().ErrorExpectedGot("}")
|
||||||
} else {
|
} else {
|
||||||
tk = scanner.makeValueToken(SymExpression, "", body)
|
tk = scanner.makeValueToken(SymExpression, "", body)
|
||||||
tree = newFuncDefTerm(tk, args)
|
tree = newFuncDefTerm(tk, args)
|
||||||
@ -154,7 +165,7 @@ func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term
|
|||||||
args = append(args, subTree.root)
|
args = append(args, subTree.root)
|
||||||
} else if itemExpected {
|
} else if itemExpected {
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
err = prev.Errorf("expected list item, got %q", prev)
|
err = prev.ErrorExpectedGot("list-item")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -166,7 +177,7 @@ func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO Check arguments
|
// TODO Check arguments
|
||||||
if lastSym != SymClosedSquare {
|
if lastSym != SymClosedSquare {
|
||||||
err = scanner.Previous().Errorf("unterminate items list")
|
err = scanner.Previous().ErrorExpectedGot("]")
|
||||||
} else {
|
} else {
|
||||||
subtree = newListTerm(args)
|
subtree = newListTerm(args)
|
||||||
}
|
}
|
||||||
@ -175,29 +186,18 @@ func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
||||||
var ds *term
|
|
||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
dsExpected := true
|
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
|
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
|
||||||
if subTree.root != nil {
|
if subTree.root != nil {
|
||||||
if dsExpected {
|
|
||||||
if sym := subTree.root.symbol(); sym == SymDict || sym == SymIdentifier {
|
|
||||||
ds = subTree.root
|
|
||||||
} else {
|
|
||||||
err = subTree.root.Errorf("data-source dictionary expected, got %q", subTree.root.source())
|
|
||||||
}
|
|
||||||
dsExpected = false
|
|
||||||
} else {
|
|
||||||
args = append(args, subTree.root)
|
args = append(args, subTree.root)
|
||||||
}
|
|
||||||
} else if itemExpected {
|
} else if itemExpected {
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
err = prev.Errorf("expected iterator argument, got %q", prev)
|
err = prev.ErrorExpectedGot("iterator-param")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -209,11 +209,9 @@ func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *t
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO Check arguments
|
// TODO Check arguments
|
||||||
if lastSym != SymClosedRound {
|
if lastSym != SymClosedRound {
|
||||||
err = scanner.Previous().Errorf("unterminate iterator param list")
|
err = scanner.Previous().ErrorExpectedGot(")")
|
||||||
} else if ds != nil {
|
|
||||||
subtree = newIteratorTerm(tk, ds, args)
|
|
||||||
} else {
|
} else {
|
||||||
tk.Errorf("missing data-source param")
|
subtree = newIteratorTerm(tk, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -231,12 +229,13 @@ func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, e
|
|||||||
if tk.Sym == SymInteger || tk.Sym == SymString {
|
if tk.Sym == SymInteger || tk.Sym == SymString {
|
||||||
tkSep := scanner.Next()
|
tkSep := scanner.Next()
|
||||||
if tkSep.Sym != SymColon {
|
if tkSep.Sym != SymColon {
|
||||||
err = tkSep.Errorf("expected \":\", got %q", tkSep)
|
err = tkSep.ErrorExpectedGot(":")
|
||||||
} else {
|
} else {
|
||||||
key = tk.Value
|
key = tk.Value
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = tk.Errorf("expected dictionary key or closed brace, got %q", tk)
|
// err = tk.Errorf("expected dictionary key or closed brace, got %q", tk)
|
||||||
|
err = tk.ErrorExpectedGot("dictionary-key or }")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -254,7 +253,7 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree
|
|||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
lastSym = tk.Sym
|
lastSym = tk.Sym
|
||||||
if itemExpected {
|
if itemExpected {
|
||||||
err = tk.Errorf("expected dictionary key, got %q", tk)
|
err = tk.ErrorExpectedGot("dictionary-key")
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -263,7 +262,7 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree
|
|||||||
args[key] = subTree.root
|
args[key] = subTree.root
|
||||||
} else if key != nil {
|
} else if key != nil {
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
err = prev.Errorf("expected dictionary value, got %q", prev)
|
err = prev.ErrorExpectedGot("dictionary-value")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -275,7 +274,7 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO Check arguments
|
// TODO Check arguments
|
||||||
if lastSym != SymClosedBrace {
|
if lastSym != SymClosedBrace {
|
||||||
err = scanner.Previous().Errorf("unterminated dictionary")
|
err = scanner.Previous().ErrorExpectedGot("}")
|
||||||
} else {
|
} else {
|
||||||
subtree = newDictTerm(args)
|
subtree = newDictTerm(args)
|
||||||
}
|
}
|
||||||
@ -309,7 +308,7 @@ func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaul
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = tk.Errorf("selector-case expected, got %q", tk.source)
|
err = tk.ErrorExpectedGot("{")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -6,7 +6,6 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -160,7 +159,7 @@ func TestParser(t *testing.T) {
|
|||||||
/* 138 */ {`nil`, nil, nil},
|
/* 138 */ {`nil`, nil, nil},
|
||||||
/* 139 */ {`null`, nil, errors.New(`undefined variable or function "null"`)},
|
/* 139 */ {`null`, nil, errors.New(`undefined variable or function "null"`)},
|
||||||
/* 140 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)},
|
/* 140 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)},
|
||||||
/* 141 */ {`{"key":}`, nil, errors.New(`[1:9] expected dictionary value, got "}"`)},
|
/* 141 */ {`{"key":}`, nil, errors.New(`[1:9] expected "dictionary-value", got "}"`)},
|
||||||
/* 142 */ {`{}`, map[any]any{}, nil},
|
/* 142 */ {`{}`, map[any]any{}, nil},
|
||||||
/* 143 */ {`1|2`, newFraction(1, 2), nil},
|
/* 143 */ {`1|2`, newFraction(1, 2), nil},
|
||||||
/* 144 */ {`1|2 + 1`, newFraction(3, 2), nil},
|
/* 144 */ {`1|2 + 1`, newFraction(3, 2), nil},
|
||||||
@ -174,11 +173,15 @@ func TestParser(t *testing.T) {
|
|||||||
/* 152 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil},
|
/* 152 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil},
|
||||||
/* 153 */ {`include "iterator.expr"; it=$(ds,3); ()it`, int64(0), nil},
|
/* 153 */ {`include "iterator.expr"; it=$(ds,3); ()it`, int64(0), nil},
|
||||||
/* 154 */ {`include "iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
|
/* 154 */ {`include "iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
|
||||||
/* 155 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(1), nil},
|
/* 155 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil},
|
||||||
/* 156 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil},
|
/* 156 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil},
|
||||||
/* 157 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
|
/* 157 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
|
||||||
/* 158 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
|
/* 158 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
|
||||||
/* 159 */ {`include "file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil},
|
/* 159 */ {`include "file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil},
|
||||||
|
/* 160 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it++; it.index`, int64(0), nil},
|
||||||
|
/* 161 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it.clean`, true, nil},
|
||||||
|
/* 162 */ {`builtin "os.file"`, int64(1), nil},
|
||||||
|
/* 163 */ {`it=$(1,2,3); it++`, int64(1), nil},
|
||||||
}
|
}
|
||||||
check_env_expr_path := 113
|
check_env_expr_path := 113
|
||||||
|
|
||||||
@ -186,7 +189,7 @@ func TestParser(t *testing.T) {
|
|||||||
failed := 0
|
failed := 0
|
||||||
|
|
||||||
// inputs1 := []inputType{
|
// inputs1 := []inputType{
|
||||||
// /* 140 */ {`ds={}; $(ds)`, nil, nil},
|
// /* 159 */ {`include "file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil},
|
||||||
// }
|
// }
|
||||||
|
|
||||||
for i, input := range inputs {
|
for i, input := range inputs {
|
||||||
@ -201,7 +204,7 @@ func TestParser(t *testing.T) {
|
|||||||
ImportImportFuncs(ctx)
|
ImportImportFuncs(ctx)
|
||||||
parser := NewParser(ctx)
|
parser := NewParser(ctx)
|
||||||
|
|
||||||
logTest(t, i+1, input.source, input.wantResult, input.wantErr)
|
logTest(t, i+1, "General", input.source, input.wantResult, input.wantErr)
|
||||||
|
|
||||||
r := strings.NewReader(input.source)
|
r := strings.NewReader(input.source)
|
||||||
scanner := NewScanner(r, DefaultTranslations())
|
scanner := NewScanner(r, DefaultTranslations())
|
||||||
@ -234,7 +237,7 @@ func TestParser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed))
|
t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestListParser(t *testing.T) {
|
func TestListParser(t *testing.T) {
|
||||||
@ -254,8 +257,9 @@ func TestListParser(t *testing.T) {
|
|||||||
/* 7 */ {`add([1,4,3,2])`, int64(10), nil},
|
/* 7 */ {`add([1,4,3,2])`, int64(10), nil},
|
||||||
/* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil},
|
/* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil},
|
||||||
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
||||||
/* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 has wrong type string, number expected`)},
|
/* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 (2 in 1) has wrong type string, number expected`)},
|
||||||
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
||||||
|
|
||||||
// /* 8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil},
|
// /* 8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil},
|
||||||
// /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
|
// /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
|
||||||
}
|
}
|
||||||
@ -278,7 +282,7 @@ func TestListParser(t *testing.T) {
|
|||||||
ImportMathFuncs(ctx)
|
ImportMathFuncs(ctx)
|
||||||
parser := NewParser(ctx)
|
parser := NewParser(ctx)
|
||||||
|
|
||||||
logTest(t, i+1, input.source, input.wantResult, input.wantErr)
|
logTest(t, i+1, "List", input.source, input.wantResult, input.wantErr)
|
||||||
|
|
||||||
r := strings.NewReader(input.source)
|
r := strings.NewReader(input.source)
|
||||||
scanner := NewScanner(r, DefaultTranslations())
|
scanner := NewScanner(r, DefaultTranslations())
|
||||||
@ -330,13 +334,13 @@ func TestListParser(t *testing.T) {
|
|||||||
failed++
|
failed++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed))
|
t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed)
|
||||||
}
|
}
|
||||||
|
|
||||||
func logTest(t *testing.T, n int, source string, wantResult any, wantErr error) {
|
func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) {
|
||||||
if wantErr == nil {
|
if wantErr == nil {
|
||||||
t.Log(fmt.Sprintf("[+]Test nr %3d -- %q --> %v", n, source, wantResult))
|
t.Logf("[+]%s nr %3d -- %q --> %v", section, n, source, wantResult)
|
||||||
} else {
|
} else {
|
||||||
t.Log(fmt.Sprintf("[-]Test nr %3d -- %q --> %v", n, source, wantErr))
|
t.Logf("[-]%s nr %3d -- %q --> %v", section, n, source, wantErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,6 +160,12 @@ func (self *scanner) fetchNextToken() (tk *Token) {
|
|||||||
//} else if next == '/' {
|
//} else if next == '/' {
|
||||||
if next, _ := self.peek(); next == '/' {
|
if next, _ := self.peek(); next == '/' {
|
||||||
tk = self.moveOn(SymDotSlash, ch, next)
|
tk = self.moveOn(SymDotSlash, ch, next)
|
||||||
|
} else if next == '.' {
|
||||||
|
if next1, _ := self.peek(); next1 == '.' {
|
||||||
|
tk = self.moveOn(SymTripleDot, ch, next, next1)
|
||||||
|
} else {
|
||||||
|
tk = self.moveOn(SymDoubleDot, ch, next)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymDot, ch)
|
tk = self.makeToken(SymDot, ch)
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,8 @@ const (
|
|||||||
SymDollarRound // 54: '$('
|
SymDollarRound // 54: '$('
|
||||||
SymOpenClosedRound // 55: '()'
|
SymOpenClosedRound // 55: '()'
|
||||||
SymDoubleDollar // 56: '$$'
|
SymDoubleDollar // 56: '$$'
|
||||||
|
SymDoubleDot // 57: '..'
|
||||||
|
SymTripleDot // 58: '...'
|
||||||
SymChangeSign
|
SymChangeSign
|
||||||
SymUnchangeSign
|
SymUnchangeSign
|
||||||
SymIdentifier
|
SymIdentifier
|
||||||
|
2
term.go
2
term.go
@ -26,7 +26,7 @@ const (
|
|||||||
priFact
|
priFact
|
||||||
priIterValue
|
priIterValue
|
||||||
priCoalesce
|
priCoalesce
|
||||||
priPrePost
|
priIncDec
|
||||||
priDot
|
priDot
|
||||||
priValue
|
priValue
|
||||||
)
|
)
|
||||||
|
16
token.go
16
token.go
@ -46,7 +46,7 @@ func NewValueToken(row, col int, sym Symbol, source string, value any) *Token {
|
|||||||
|
|
||||||
func NewErrorToken(row, col int, err error) *Token {
|
func NewErrorToken(row, col int, err error) *Token {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return NewToken(row, col, SymEos, "")
|
return NewToken(row, col, SymEos, "<EOF>")
|
||||||
}
|
}
|
||||||
return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err)
|
return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err)
|
||||||
}
|
}
|
||||||
@ -63,6 +63,10 @@ func (tk *Token) IsTerm(termSymbols []Symbol) bool {
|
|||||||
return tk.IsEos() || tk.IsError() || (termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0)
|
return tk.IsEos() || tk.IsError() || (termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tk *Token) IsSymbol(sym Symbol) bool {
|
||||||
|
return tk.Sym == sym
|
||||||
|
}
|
||||||
|
|
||||||
func (self *Token) Errorf(template string, args ...any) (err error) {
|
func (self *Token) Errorf(template string, args ...any) (err error) {
|
||||||
err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", self.row, self.col)+template, args...)
|
err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", self.row, self.col)+template, args...)
|
||||||
return
|
return
|
||||||
@ -81,3 +85,13 @@ func (self *Token) Errors(msg string) (err error) {
|
|||||||
err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg)
|
err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Token) ErrorExpectedGot(symbol string) (err error) {
|
||||||
|
err = fmt.Errorf("[%d:%d] expected %q, got %q", self.row, self.col, symbol, self)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Token) ErrorExpectedGotString(symbol, got string) (err error) {
|
||||||
|
err = fmt.Errorf("[%d:%d] expected %q, got %q", self.row, self.col, symbol, got)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user