Compare commits

..

No commits in common. "c977e82d9ec2424680b14749737a0dfde4720f62" and "0bca3333aa699a493c58b7689a3f68b8f2077ba2" have entirely different histories.

28 changed files with 307 additions and 535 deletions

2
ast.go
View File

@ -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("unexpected token %q", tk.String()) err = tk.Errorf("No term constructor for token %q", tk.String())
} }
return return
} }

View File

@ -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(`unexpected token "%"`) wantErr := errors.New(`No term constructor for 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() {

View File

@ -6,7 +6,17 @@ 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 {
@ -29,63 +39,58 @@ 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, ctx: `, dc.index, mapToString(dc.ds)))
ctx: `, dc.index, mapToString(dc.ds))) CtxToBuilder(&sb, dc.ctx, 1)
CtxToBuilder(&sb, dc.ctx, 1) sb.WriteByte(')')
sb.WriteByte(')') return sb.String()
return sb.String() */
*/
} }
func (dc *dataCursor) HasOperation(name string) (exists bool) { func (dc *dataCursor) HasOperation(name string) (exists bool) {
exists = name == indexName f, ok := dc.ds[name]
if !exists { exists = ok && isFunctor(f)
f, ok := dc.ds[name]
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 name == indexName { if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
value = int64(dc.Index())
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
if functor == dc.cleanFunc { if functor == dc.cleanFunc {
value, err = dc.Clean() return nil, dc.Clean()
} else if functor == dc.resetFunc { } else if functor == dc.resetFunc {
value, err = dc.Reset() return nil, 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() (success bool, err error) { func (dc *dataCursor) Reset() (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)
@ -99,11 +104,10 @@ func (dc *dataCursor) Reset() (success bool, err error) {
} else { } else {
err = errNoOperation(resetName) err = errNoOperation(resetName)
} }
success = err == nil
return return
} }
func (dc *dataCursor) Clean() (success bool, err error) { func (dc *dataCursor) Clean() (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)
@ -116,7 +120,6 @@ func (dc *dataCursor) Clean() (success bool, 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
} }

View File

@ -169,7 +169,7 @@ x = 1; y = 2*x
== Other operations == Other operations
=== [blue]`;` operator === [blue]`;` operator
The semicolon operator [blue]`;` is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The latter is the final result. The semicolon operator [blue]`;` is an infixed 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 variables in the evaluation context or to change their value (see _ExprContext_). The assignment operator [blue]`=` is used to define variable 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,22 +202,13 @@ The _selector operator_ is very similar to the _switch/case/default_ statement a
.Syntax .Syntax
[source,bnf] [source,bnf]
---- ----
<selector-operator> ::= <select-expression> "?" <selector-case> { ":" <selector-case> } ["::" <default-multi-expression>] <selector-operator> ::= <expression> "?" <selector-case> { ":" <selector-case> } ["::" <case-value>]
<selector-case> ::= [<match-list>] <case-value> <selector-case> ::= [<list>] <case-value>
<match-list> ::= "["<item>{","<items>}"]" <case-value> ::= "{" <multi-expr> "}"
<item> ::= <expression <multi-expr> ::= <expr> {";" <expr>}
<case-multi-expression> ::= "{" <multi-expression> "}"
<multi-expression> ::= <expression> {";" <expression>}
---- ----
In other words, the selector operator evaluates the expression (`<select-expression>`) on the left-hand side of the `?` symbol; it then compares the result obtained with the values listed in the `<match-list>`'s. If the comparision find a match with a value in a match-list, the associated `<case-multi-expression>` is evaluted, and its result will be the final result of the selection operation. .Example
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"
@ -233,38 +224,33 @@ The `:` symbol (colon) is the separator of the selector-cases. Note that if the
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+|*ITEM*| [blue]`.` | _Infix_ | _Item_| _collection_ `"."` _key_ -> _any_ 1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ "!" -> _integer_
.2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `"++"` -> _integer_ 1+|*SIGN*| [blue]`+`, [blue]`-` | _Prefix_ | _Change-sign_| ("+"\|"-") _number_ -> _number_
| [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `"++"` -> _any_ .5+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ "*" _number_ -> _number_
.1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ `"!"` -> _integer_ | [blue]`*` | _Infix_ | _String-repeat_ | _string_ "*" _integer_ -> _string_
.3+|*SIGN*| [blue]`+`, [blue]`-` | _Prefix_ | _Change-sign_| (`"+"`\|`"-"`) _number_ -> _number_ | [blue]`/` | _Infix_ | _Division_ | _number_ "/" _number_ -> _number_
| [blue]`#` | _Prefix_ | _Lenght-of_ | `"#"` _collection_ -> _integer_ | [blue]`./` | _Infix_ | _Float-division_ | __number__ "./" _number_ -> _float_
| [blue]`#` | _Prefix_ | _Size-of_ | `"#"` _iterator_ -> _integer_ | [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ "%" _integer_ -> _integer_
.5+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ `"*"` _number_ -> _number_ .5+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ "+" _number_ -> _number_
| [blue]`*` | _Infix_ | _String-repeat_ | _string_ `"*"` _integer_ -> _string_ | [blue]`+` | _Infix_ | _String-concat_ | (_string_\|_number_) "+" (_string_\|_number_) -> _string_
| [blue]`/` | _Infix_ | _Division_ | _number_ `"/"` _number_ -> _number_ | [blue]`+` | _Infix_ | _List-join_ | _list_ "+" _list_ -> _list_
| [blue]`./` | _Infix_ | _Float-division_ | __number__ `"./"` _number_ -> _float_ | [blue]`-` | _Infix_ | _Subtraction_ | _number_ "-" _number_ -> _number_
| [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ `"%"` _integer_ -> _integer_ | [blue]`-` | _Infix_ | _List-difference_ | _list_ "-" _list_ -> _list_
.5+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ `"+"` _number_ -> _number_ .6+|*RELATION*| [blue]`<` | _Infix_ | _less_ | _comparable_ "<" _comparable_ -> _boolean_
| [blue]`+` | _Infix_ | _String-concat_ | (_string_\|_number_) `"+"` (_string_\|_number_) -> _string_ | [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ "\<=" _comparable_ -> _boolean_
| [blue]`+` | _Infix_ | _List-join_ | _list_ `"+"` _list_ -> _list_ | [blue]`>` | _Infix_ | _greater_ | _comparable_ ">" _comparable_ -> _boolean_
| [blue]`-` | _Infix_ | _Subtraction_ | _number_ `"-"` _number_ -> _number_ | [blue]`>=` | _Infix_ | _greater-equal_ | _comparable_ ">=" _comparable_ -> _boolean_
| [blue]`-` | _Infix_ | _List-difference_ | _list_ `"-"` _list_ -> _list_ | [blue]`==` | _Infix_ | _equal_ | _comparable_ "==" _comparable_ -> _boolean_
.6+|*RELATION*| [blue]`<` | _Infix_ | _less_ | _comparable_ `"<"` _comparable_ -> _boolean_ | [blue]`!=` | _Infix_ | _not-equal_ | _comparable_ "!=" _comparable_ -> _boolean_
| [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ `"\<="` _comparable_ -> _boolean_ .1+|*NOT*| [blue]`not` | _Prefix_ | _not_ | "not" _boolean_ -> _boolean_
| [blue]`>` | _Infix_ | _greater_ | _comparable_ `">"` _comparable_ -> _boolean_ .2+|*AND*| [blue]`and` | _Infix_ | _and_ | _boolean_ "and" _boolean_ -> _boolean_
| [blue]`>=` | _Infix_ | _greater-equal_ | _comparable_ `">="` _comparable_ -> _boolean_ | [blue]`&&` | _Infix_ | _and_ | _boolean_ "&&" _boolean_ -> _boolean_
| [blue]`==` | _Infix_ | _equal_ | _comparable_ `"=="` _comparable_ -> _boolean_ .2+|*OR*| [blue]`or` | _Infix_ | _or_ | _boolean_ "or" _boolean_ -> _boolean_
| [blue]`!=` | _Infix_ | _not-equal_ | _comparable_ `"!="` _comparable_ -> _boolean_ | [blue]`\|\|` | _Infix_ | _or_ | _boolean_ "\|\|" _boolean_ -> _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_
|=== |===

View File

@ -5,6 +5,7 @@
package expr package expr
import ( import (
"fmt"
"strings" "strings"
"testing" "testing"
) )
@ -52,7 +53,7 @@ func TestExpr(t *testing.T) {
ImportOsFuncs(ctx) ImportOsFuncs(ctx)
parser := NewParser(ctx) parser := NewParser(ctx)
logTest(t, i+1, "Expr", input.source, input.wantResult, input.wantErr) logTest(t, i+1, input.source, input.wantResult, input.wantErr)
r := strings.NewReader(input.source) r := strings.NewReader(input.source)
scanner := NewScanner(r, DefaultTranslations()) scanner := NewScanner(r, DefaultTranslations())
@ -80,5 +81,5 @@ func TestExpr(t *testing.T) {
failed++ failed++
} }
} }
t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed) t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed))
} }

View File

@ -1,33 +0,0 @@
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)

View File

@ -9,40 +9,40 @@ import (
"io" "io"
) )
func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) { func checkNumberParamExpected(funcName string, paramValue any, paramPos int) (err error) {
if !(isNumber(paramValue) || isFraction(paramValue)) /*|| isList(paramValue)*/ { if !(isNumber(paramValue) || isList(paramValue) || isFraction(paramValue)) {
err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected", err = fmt.Errorf("%s(): param nr %d has wrong type %T, number expected", funcName, paramPos+1, paramValue)
funcName, paramPos+1, subPos+1, level, paramValue)
} }
return return
} }
func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result any, err error) { func doAdd(ctx ExprContext, name string, it Iterator) (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, count, level); err != nil { if v, err = doAdd(ctx, name, subIter); err != nil {
break break
} }
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) { if subIter.HasOperation(cleanName) {
if _, err = extIter.CallOperation(cleanName, nil); err != nil { if _, err = subIter.CallOperation(cleanName, nil); err != nil {
return return
} }
} }
} else if err = checkNumberParamExpected(name, v, count, level, it.Index()); err != nil { } else {
break if err = checkNumberParamExpected(name, v, it.Index()); err != nil {
break
}
if list, ok := v.(*ListType); ok {
if v, err = doAdd(ctx, name, NewListIterator(list)); err != nil {
break
}
}
} }
count++
if !sumAsFloat { if !sumAsFloat {
if isFloat(v) { if isFloat(v) {
@ -87,37 +87,38 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
} }
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), 0, -1) result, err = doAdd(ctx, name, NewArrayIterator(args))
return return
} }
func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result any, err error) { func doMul(ctx ExprContext, name string, it Iterator) (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, count, level); err != nil { if v, err = doMul(ctx, name, subIter); err != nil {
break break
} }
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) { if subIter.HasOperation(cleanName) {
if _, err = extIter.CallOperation(cleanName, nil); err != nil { if _, err = subIter.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 {
if v, err = doMul(ctx, name, NewListIterator(list)); err != nil {
break
}
}
} }
count++
if !mulAsFloat { if !mulAsFloat {
if isFloat(v) { if isFloat(v) {
@ -162,7 +163,7 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
} }
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), 0, -1) result, err = doMul(ctx, name, NewArrayIterator(args))
return return
} }
@ -172,5 +173,5 @@ func ImportMathFuncs(ctx ExprContext) {
} }
func init() { func init() {
registerImport("math.arith", ImportMathFuncs, "Functions add() and mul()") registerImport("math.arith", ImportMathFuncs, "Function add() and mul()")
} }

View File

@ -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, nil)) result, err = doJoinStr(name, sep, NewListIterator(ls))
} 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 {

View File

@ -6,6 +6,7 @@ package expr
import ( import (
"errors" "errors"
"fmt"
"strings" "strings"
"testing" "testing"
) )
@ -49,7 +50,7 @@ func TestFuncs(t *testing.T) {
// ImportOsFuncs(ctx) // ImportOsFuncs(ctx)
parser := NewParser(ctx) parser := NewParser(ctx)
logTest(t, i+1, "Funcs", input.source, input.wantResult, input.wantErr) logTest(t, i+1, input.source, input.wantResult, input.wantErr)
r := strings.NewReader(input.source) r := strings.NewReader(input.source)
scanner := NewScanner(r, DefaultTranslations()) scanner := NewScanner(r, DefaultTranslations())
@ -77,5 +78,5 @@ func TestFuncs(t *testing.T) {
failed++ failed++
} }
} }
t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed) t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed))
} }

View File

@ -1,4 +0,0 @@
10
20
5
12

View File

@ -4,69 +4,26 @@
// iter-list.go // iter-list.go
package expr package expr
import ( import "io"
"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, args []any) (it *ListIterator) { func NewListIterator(list *ListType) *ListIterator {
var argc int = 0 return &ListIterator{a: list, index: 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) (it *ListIterator) { func NewArrayIterator(array []any) *ListIterator {
it = &ListIterator{a: (*ListType)(&array), count: 0, index: -1, start: 0, stop: len(array) - 1, step: 1} return &ListIterator{a: (*ListType)(&array), index: 0}
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, nil) it = NewListIterator(list)
} 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 {
@ -77,36 +34,17 @@ func NewAnyIterator(value any) (it *ListIterator) {
return return
} }
func (it *ListIterator) String() string {
var l = 0
if it.a != nil {
l = len(*it.a)
}
return fmt.Sprintf("$(#%d)", l)
}
func (it *ListIterator) HasOperation(name string) bool { func (it *ListIterator) HasOperation(name string) bool {
yes := name == resetName || name == indexName || name == countName return false
return yes
} }
func (it *ListIterator) CallOperation(name string, args []any) (v any, err error) { func (it *ListIterator) CallOperation(name string, args []any) (any, error) {
switch name { return nil, errNoOperation(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 <= it.stop { if it.index >= 0 && it.index < len(a) {
item = a[it.index] item = a[it.index]
} else { } else {
err = io.EOF err = io.EOF
@ -115,18 +53,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.count++ it.index++
} }
// 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 return it.index - 1
}
func (it *ListIterator) Reset() (bool, error) {
it.index = it.start
return true, nil
} }

View File

@ -1,18 +0,0 @@
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)

View File

@ -9,26 +9,10 @@ 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)
} }

View File

@ -6,7 +6,6 @@ package expr
import ( import (
"errors" "errors"
"fmt"
) )
// -------- function call term // -------- function call term
@ -22,20 +21,6 @@ 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)
@ -49,10 +34,8 @@ 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
@ -61,9 +44,9 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
// -------- 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, // value is the expression body tk: *tk,
parent: nil, parent: nil,
children: args, // function params children: args, // arg[0]=formal-param-list, arg[1]=*ast
position: posLeaf, position: posLeaf,
priority: priValue, priority: priValue,
evalFunc: evalFuncDef, evalFunc: evalFuncDef,

View File

@ -12,7 +12,7 @@ import (
// -------- iterator term // -------- iterator term
func newDsIteratorTerm(tk *Token, dsTerm *term, args []*term) *term { func newIteratorTerm(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,18 +28,6 @@ func newDsIteratorTerm(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) {
@ -55,21 +43,59 @@ func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) {
return return
} }
func evalFirstChild(ctx ExprContext, self *term) (value any, err error) { // func getDataSourceDict(ctx ExprContext, self *term) (ds map[string]Functor, 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
} }
value, err = self.children[0].compute(ctx) if value, err = self.children[0].compute(ctx); err != nil {
return return
} }
func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map[string]Functor, err error) { requiredFields := []string{currentName, nextName}
if dictAny, ok := firstChildValue.(map[any]any); ok { fieldsMask := 0b11
requiredFields := []string{currentName, nextName} foundFields := 0
fieldsMask := 0b11 if dictAny, ok := value.(map[any]any); ok {
foundFields := 0
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 {
@ -91,94 +117,45 @@ func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map
} }
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 firstChildValue, err = evalFirstChild(ctx, self); err != nil { if ds, err = getDataSourceDict(ctx, self); err != nil {
return return
} }
if ds, err = getDataSourceDict(ctx, self, firstChildValue); err != nil { dc := newDataCursor(ctx, ds)
return // var it Iterator = dc
} // fmt.Println(it)
if initFunc, exists := ds[initName]; exists && initFunc != nil {
if ds != nil { var args []any
dc := newDataCursor(ctx, ds) if len(self.children) > 1 {
if initFunc, exists := ds[initName]; exists && initFunc != nil { if args, err = evalTermArray(ctx, self.children[1:]); err != nil {
var args []any
if len(self.children) > 1 {
if args, err = evalTermArray(ctx, self.children[1:]); err != nil {
return
}
} else {
args = []any{}
}
initCtx := dc.ctx.Clone()
if dc.resource, err = initFunc.Invoke(initCtx, initName, args); err != nil {
return return
} }
exportObjects(dc.ctx, initCtx) } else {
args = []any{}
} }
dc.nextFunc, _ = ds[nextName] initCtx := dc.ctx.Clone()
dc.currentFunc, _ = ds[currentName] if dc.resource, err = initFunc.Invoke(initCtx, initName, args); err != nil {
dc.cleanFunc, _ = ds[cleanName] return
dc.resetFunc, _ = ds[resetName]
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)
} }
exportObjects(dc.ctx, initCtx)
} }
return
} dc.nextFunc, _ = ds[nextName]
dc.currentFunc, _ = ds[currentName]
// func evalChildren(ctx ExprContext, terms []*term, firstChildValue any) (list *ListType, err error) { dc.cleanFunc, _ = ds[cleanName]
// items := make(ListType, len(terms)) dc.resetFunc, _ = ds[resetName]
// for i, tree := range terms {
// var param any v = dc
// 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
} }

View File

@ -29,14 +29,7 @@ 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 {
var minArgs, maxArgs int = 0, 0 ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
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)
} }

View File

@ -50,7 +50,7 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
} }
} }
if err == nil { if err == nil {
v = int64(count) v = count
} }
return return
} }

View File

@ -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: priIncDec, priority: priPrePost,
evalFunc: evalContextValue, evalFunc: evalContextValue,
} }
} }

View File

@ -47,11 +47,10 @@ 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 *ListType: case []any:
var index int var index int
array := ([]any)(*unboxedValue) if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil {
if index, err = verifyIndex(ctx, indexTerm, len(array)); err == nil { v = unboxedValue[index]
v = array[index]
} }
case string: case string:
var index int var index int
@ -66,27 +65,17 @@ 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 unboxedValue.HasOperation(opName) { if opName == resetName {
v, err = unboxedValue.CallOperation(opName, []any{}) err = unboxedValue.Reset()
} else if opName == cleanName {
err = unboxedValue.Clean()
} else { } else {
err = indexTerm.Errorf("this iterator do not support the %q command", opName) err = indexTerm.Errorf("iterators do not support command %q", opName)
v = false
} }
v = err == nil
} }
default: default:
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.errIncompatibleTypes(leftValue, rightValue)

View File

@ -4,6 +4,7 @@
// 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) {
@ -17,16 +18,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 childValue any var leftValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if leftValue, err = self.evalPrefix(ctx); err != nil {
return return
} }
if it, ok := childValue.(Iterator); ok { if dc, ok := leftValue.(*dataCursor); ok {
v, err = it.Current() v, err = dc.Current()
} else { } else {
err = self.errIncompatibleType(childValue) err = self.errIncompatibleType(leftValue)
} }
return return
} }

View File

@ -17,27 +17,22 @@ 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 childValue any var rightValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if rightValue, err = self.evalPrefix(ctx); err != nil {
return return
} }
if isList(childValue) { if isList(rightValue) {
list, _ := childValue.([]any) list, _ := rightValue.([]any)
v = int64(len(list)) v = int64(len(list))
} else if isString(childValue) { } else if isString(rightValue) {
s, _ := childValue.(string) s, _ := rightValue.(string)
v = int64(len(s)) v = int64(len(s))
} else if it, ok := childValue.(Iterator); ok { } else if it, ok := rightValue.(Iterator); ok {
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) { v = int64(it.Index())
count, _ := extIt.CallOperation(countName, nil)
v, _ = toInt(count, "")
} else {
v = int64(it.Index() + 1)
}
} else { } else {
err = self.errIncompatibleType(childValue) err = self.errIncompatibleType(rightValue)
} }
return return
} }

View File

@ -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: priIncDec, priority: priPrePost,
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 childValue any var leftValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if leftValue, err = self.evalPrefix(ctx); err != nil {
return return
} }
if it, ok := childValue.(Iterator); ok { if dc, ok := leftValue.(*dataCursor); ok {
v, err = it.Next() v, err = dc.Next()
} else if isInteger(childValue) && self.children[0].symbol() == SymIdentifier { } else if isInteger(leftValue) && self.children[0].symbol() == SymIdentifier {
v = childValue v = leftValue
i, _ := childValue.(int64) i, _ := leftValue.(int64)
ctx.SetVar(self.children[0].source(), i+1) ctx.SetVar(self.children[0].source(), i+1)
} else { } else {
err = self.errIncompatibleType(childValue) self.errIncompatibleType(leftValue)
} }
return return
} }

103
parser.go
View File

@ -62,45 +62,30 @@ 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)
// lastSym := SymUnknown // tk := scanner.Next()
// itemExpected := false // for tk.Sym != SymClosedRound && tk.Sym != SymEos {
// tk := scanner.Previous() // if tk.Sym == SymIdentifier {
// for lastSym != SymClosedRound && lastSym != SymEos { // t := newTerm(tk, nil)
// var subTree *ast // args = append(args, t)
// 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 {
// err = tk.ErrorExpectedGotString("param-name", subTree.root.String())
// }
// } else if itemExpected {
// prev := scanner.Previous()
// err = prev.ErrorExpectedGot("function-param")
// break
// }
// } else { // } else {
// err = tk.Errorf("invalid param %q, variable identifier expected", tk.source)
// break // break
// } // }
// lastSym = scanner.Previous().Sym // tk = scanner.Next()
// itemExpected = lastSym == SymComma
// } // }
// if err == nil && tk.Sym != SymClosedRound {
// if err == nil && lastSym != SymClosedRound { // err = tk.Errorf("unterminate function params list")
// 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().ErrorExpectedGot("}") // err = scanner.Previous().Errorf("not properly terminated function body")
// } else { // } else {
// tk = scanner.makeValueToken(SymExpression, "", body) // tk = scanner.makeValueToken(SymExpression, "", body)
// tree = newFuncDefTerm(tk, args) // tree = newFuncDefTerm(tk, args)
@ -117,14 +102,20 @@ 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 {
tk = scanner.Next() var subTree *ast
if tk.IsSymbol(SymIdentifier) { if subTree, err = self.parseItem(scanner, true, SymComma, SymClosedRound); err == nil {
param := newTerm(tk, nil) if subTree.root != nil {
args = append(args, param) if subTree.root.symbol() == SymIdentifier {
tk = scanner.Next() args = append(args, subTree.root)
} else if itemExpected { } else {
prev := scanner.Previous() err = tk.Errorf("exptected identifier, got %q", subTree.root)
err = prev.ErrorExpectedGot("function-param") }
} else if itemExpected {
prev := scanner.Previous()
err = prev.Errorf("expected function parameter, got %q", prev)
break
}
} else {
break break
} }
lastSym = scanner.Previous().Sym lastSym = scanner.Previous().Sym
@ -132,20 +123,18 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
} }
if err == nil && lastSym != SymClosedRound { if err == nil && lastSym != SymClosedRound {
err = tk.ErrorExpectedGot(")") err = tk.Errorf("unterminated function parameters list")
} }
if err == nil { if err == nil {
tk = scanner.Next() tk = scanner.Next()
if tk.IsSymbol(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().ErrorExpectedGot("}") err = scanner.Previous().Errorf("not properly terminated function body")
} else { } else {
tk = scanner.makeValueToken(SymExpression, "", body) tk = scanner.makeValueToken(SymExpression, "", body)
tree = newFuncDefTerm(tk, args) tree = newFuncDefTerm(tk, args)
@ -165,7 +154,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.ErrorExpectedGot("list-item") err = prev.Errorf("expected list item, got %q", prev)
break break
} }
} else { } else {
@ -177,7 +166,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().ErrorExpectedGot("]") err = scanner.Previous().Errorf("unterminate items list")
} else { } else {
subtree = newListTerm(args) subtree = newListTerm(args)
} }
@ -186,18 +175,29 @@ 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 {
args = append(args, subTree.root) 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)
}
} else if itemExpected { } else if itemExpected {
prev := scanner.Previous() prev := scanner.Previous()
err = prev.ErrorExpectedGot("iterator-param") err = prev.Errorf("expected iterator argument, got %q", prev)
break break
} }
} else { } else {
@ -209,9 +209,11 @@ 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().ErrorExpectedGot(")") err = scanner.Previous().Errorf("unterminate iterator param list")
} else if ds != nil {
subtree = newIteratorTerm(tk, ds, args)
} else { } else {
subtree = newIteratorTerm(tk, args) tk.Errorf("missing data-source param")
} }
} }
return return
@ -229,13 +231,12 @@ 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.ErrorExpectedGot(":") err = tkSep.Errorf("expected \":\", got %q", tkSep)
} 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
} }
@ -253,7 +254,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.ErrorExpectedGot("dictionary-key") err = tk.Errorf("expected dictionary key, got %q", tk)
} }
break break
} }
@ -262,7 +263,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.ErrorExpectedGot("dictionary-value") err = prev.Errorf("expected dictionary value, got %q", prev)
break break
} }
} else { } else {
@ -274,7 +275,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().ErrorExpectedGot("}") err = scanner.Previous().Errorf("unterminated dictionary")
} else { } else {
subtree = newDictTerm(args) subtree = newDictTerm(args)
} }
@ -308,7 +309,7 @@ func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaul
return return
} }
} else { } else {
err = tk.ErrorExpectedGot("{") err = tk.Errorf("selector-case expected, got %q", tk.source)
} }
if err == nil { if err == nil {

View File

@ -6,6 +6,7 @@ package expr
import ( import (
"errors" "errors"
"fmt"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
@ -159,7 +160,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},
@ -173,15 +174,11 @@ 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(2), nil}, /* 155 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(1), 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
@ -189,7 +186,7 @@ func TestParser(t *testing.T) {
failed := 0 failed := 0
// inputs1 := []inputType{ // inputs1 := []inputType{
// /* 159 */ {`include "file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil}, // /* 140 */ {`ds={}; $(ds)`, nil, nil},
// } // }
for i, input := range inputs { for i, input := range inputs {
@ -204,7 +201,7 @@ func TestParser(t *testing.T) {
ImportImportFuncs(ctx) ImportImportFuncs(ctx)
parser := NewParser(ctx) parser := NewParser(ctx)
logTest(t, i+1, "General", input.source, input.wantResult, input.wantErr) logTest(t, i+1, input.source, input.wantResult, input.wantErr)
r := strings.NewReader(input.source) r := strings.NewReader(input.source)
scanner := NewScanner(r, DefaultTranslations()) scanner := NewScanner(r, DefaultTranslations())
@ -237,7 +234,7 @@ func TestParser(t *testing.T) {
} }
} }
} }
t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed) t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed))
} }
func TestListParser(t *testing.T) { func TestListParser(t *testing.T) {
@ -257,9 +254,8 @@ 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 (2 in 1) has wrong type string, number expected`)}, /* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 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},
} }
@ -282,7 +278,7 @@ func TestListParser(t *testing.T) {
ImportMathFuncs(ctx) ImportMathFuncs(ctx)
parser := NewParser(ctx) parser := NewParser(ctx)
logTest(t, i+1, "List", input.source, input.wantResult, input.wantErr) logTest(t, i+1, input.source, input.wantResult, input.wantErr)
r := strings.NewReader(input.source) r := strings.NewReader(input.source)
scanner := NewScanner(r, DefaultTranslations()) scanner := NewScanner(r, DefaultTranslations())
@ -334,13 +330,13 @@ func TestListParser(t *testing.T) {
failed++ failed++
} }
} }
t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed) t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed))
} }
func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) { func logTest(t *testing.T, n int, source string, wantResult any, wantErr error) {
if wantErr == nil { if wantErr == nil {
t.Logf("[+]%s nr %3d -- %q --> %v", section, n, source, wantResult) t.Log(fmt.Sprintf("[+]Test nr %3d -- %q --> %v", n, source, wantResult))
} else { } else {
t.Logf("[-]%s nr %3d -- %q --> %v", section, n, source, wantErr) t.Log(fmt.Sprintf("[-]Test nr %3d -- %q --> %v", n, source, wantErr))
} }
} }

View File

@ -160,12 +160,6 @@ 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)
} }

View File

@ -65,8 +65,6 @@ const (
SymDollarRound // 54: '$(' SymDollarRound // 54: '$('
SymOpenClosedRound // 55: '()' SymOpenClosedRound // 55: '()'
SymDoubleDollar // 56: '$$' SymDoubleDollar // 56: '$$'
SymDoubleDot // 57: '..'
SymTripleDot // 58: '...'
SymChangeSign SymChangeSign
SymUnchangeSign SymUnchangeSign
SymIdentifier SymIdentifier

View File

@ -26,7 +26,7 @@ const (
priFact priFact
priIterValue priIterValue
priCoalesce priCoalesce
priIncDec priPrePost
priDot priDot
priValue priValue
) )

View File

@ -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, "<EOF>") return NewToken(row, col, SymEos, "")
} }
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,10 +63,6 @@ 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
@ -85,13 +81,3 @@ 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
}