Compare commits

..

7 Commits

14 changed files with 146 additions and 121 deletions
+67 -55
View File
@@ -5,27 +5,38 @@
package expr
import (
"errors"
"io"
)
type dataCursor struct {
ds map[string]Functor
ctx ExprContext
initState bool // true if no item has prodiced yet (this replace di initial Next() call in the contructor)
index int
count int
current any
lastErr error
resource any
nextFunc Functor
cleanFunc Functor
resetFunc Functor
currentFunc Functor
}
func NewDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) {
func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *dataCursor) {
dc = &dataCursor{
ds: ds,
initState: true,
index: -1,
count: 0,
current: nil,
lastErr: nil,
resource: resource,
ctx: ctx.Clone(),
nextFunc: ds[NextName],
cleanFunc: ds[CleanName],
resetFunc: ds[ResetName],
}
//dc.Next()
return
}
@@ -97,10 +108,14 @@ func (dc *dataCursor) Reset() (success bool, err error) {
if dc.resetFunc != nil {
if dc.resource != nil {
ctx := cloneContext(dc.ctx)
if _, err = dc.resetFunc.Invoke(ctx, ResetName, []any{dc.resource}); err == nil {
dc.index = -1
}
_, err = dc.resetFunc.Invoke(ctx, ResetName, []any{dc.resource})
exportObjects(dc.ctx, ctx)
dc.index = -1
dc.count = 0
dc.initState = true
dc.current = nil
dc.lastErr = nil
//dc.Next()
} else {
err = errInvalidDataSource()
}
@@ -116,57 +131,29 @@ func (dc *dataCursor) Clean() (success bool, err error) {
if dc.resource != nil {
ctx := cloneContext(dc.ctx)
_, err = dc.cleanFunc.Invoke(ctx, CleanName, []any{dc.resource})
dc.resource = nil
// dc.resource = nil
exportObjects(dc.ctx, ctx)
} else {
err = errInvalidDataSource()
}
} else {
err = errors.New("no 'clean' function defined in the data-source")
err = errNoOperation(CleanName)
}
success = err == nil
return
}
func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
ctx := cloneContext(dc.ctx)
if item, err = dc.currentFunc.Invoke(ctx, CurrentName, []any{}); err == nil && item == nil {
dc.init()
if dc.current != nil {
item = dc.current
} else {
err = io.EOF
}
exportObjects(dc.ctx, ctx)
return
}
// func (dc *dataCursor) _Next() (item any, err error) { // must return io.EOF after the last item
// if dc.resource != nil {
// ctx := cloneContext(dc.ctx)
// // fmt.Printf("Entering Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
// if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil {
// if item == nil {
// err = io.EOF
// } else {
// dc.index++
// }
// }
// // fmt.Printf("Exiting Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
// exportObjects(dc.ctx, ctx)
// // fmt.Printf("Outer-Ctx [%p]: %s\n", dc.ctx, CtxToString(dc.ctx, 0))
// } else {
// err = errInvalidDataSource()
// }
// return
// }
// func (dc *dataCursor) _filter(item any) (filterdItem any, err error) {
// if filter, ok := dc.ds[filterName]; ok {
// ctx := cloneContext(dc.ctx)
// filterdItem, err = filter.Invoke(ctx, filterName, []any{item, dc.index})
// } else {
// filterdItem = item
// }
// return
// }
func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err error) {
var v any
var ok bool
@@ -176,46 +163,71 @@ func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err
accepted = true // NOTE: A non-boolean value that is not nil means the item has been accepted
}
}
return
}
func (dc *dataCursor) mapItem(mapper Functor, item any) (mappedItem any, err error) {
ctx := cloneContext(dc.ctx)
mappedItem, err = mapper.Invoke(ctx, MapName, []any{item, dc.index});
mappedItem, err = mapper.Invoke(ctx, MapName, []any{item, dc.index})
return
}
func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item
var accepted bool
func (dc *dataCursor) init() {
if dc.initState {
dc.initState = false
dc.Next()
}
}
func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF after the last item
if dc.initState {
dc.init()
} else if err = dc.lastErr; err != nil {
return
}
current = dc.current
if dc.resource != nil {
filter := dc.ds[FilterName]
mapper := dc.ds[MapName]
for item == nil && err == nil {
var item any
for item == nil && dc.lastErr == nil {
ctx := cloneContext(dc.ctx)
if item, err = dc.nextFunc.Invoke(ctx, NextName, []any{dc.resource}); err == nil {
if item == nil {
err = io.EOF
} else {
dc.index++
if item, dc.lastErr = dc.nextFunc.Invoke(ctx, NextName, []any{dc.resource, dc.index}); dc.lastErr == nil {
if item == nil {
dc.lastErr = io.EOF
} else {
accepted := true
if filter != nil {
if accepted, err = dc.checkFilter(filter, item); err != nil || !accepted {
if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
item = nil
}
}
if accepted {
dc.count++
}
if item != nil && mapper != nil {
item, err = dc.mapItem(mapper, item)
item, dc.lastErr = dc.mapItem(mapper, item)
}
}
}
exportObjects(dc.ctx, ctx)
}
dc.current = item
if dc.lastErr != nil {
dc.index--
dc.Clean()
}
} else {
err = errInvalidDataSource()
dc.lastErr = errInvalidDataSource()
}
return
}
func (dc *dataCursor) Index() int {
return dc.index
return dc.index - 1
}
func (dc *dataCursor) Count() int {
return dc.count
}
+1
View File
@@ -28,6 +28,7 @@ type Iterator interface {
Next() (item any, err error) // must return io.EOF after the last item
Current() (item any, err error)
Index() int
Count() int
}
type ExtIterator interface {
+5
View File
@@ -143,7 +143,12 @@ func (it *ListIterator) Index() int {
return it.index
}
func (it *ListIterator) Count() int {
return it.count
}
func (it *ListIterator) Reset() (bool, error) {
it.index = it.start - it.step
it.count = 0
return true, nil
}
+9 -12
View File
@@ -66,8 +66,8 @@ func evalFirstChild(ctx ExprContext, iteratorTerm *term) (value any, err error)
func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]Functor, err error) {
if dictAny, ok := firstChildValue.(*DictType); ok {
requiredFields := []string{CurrentName, NextName}
fieldsMask := 0b11
requiredFields := []string{NextName}
fieldsMask := 0b1
foundFields := 0
ds = make(map[string]Functor)
for keyAny, item := range *dictAny {
@@ -88,7 +88,6 @@ func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]F
missingFields = append(missingFields, field)
}
}
// err = fmt.Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
err = iteratorTerm.children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
}
}
@@ -108,9 +107,10 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
}
if ds != nil {
dc := NewDataCursor(ctx, ds)
var dc *dataCursor
if initFunc, exists := ds[InitName]; exists && initFunc != nil {
var args []any
var resource any
if len(opTerm.children) > 1 {
if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil {
return
@@ -119,18 +119,15 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
args = []any{}
}
initCtx := dc.ctx.Clone()
if dc.resource, err = initFunc.Invoke(initCtx, InitName, args); err != nil {
initCtx := ctx.Clone()
if resource, err = initFunc.Invoke(initCtx, InitName, args); err != nil {
return
}
exportObjects(dc.ctx, initCtx)
dcCtx := ctx.Clone()
exportObjects(dcCtx, initCtx)
dc = NewDataCursor(dcCtx, ds, resource)
}
dc.nextFunc = ds[NextName]
dc.currentFunc = ds[CurrentName]
dc.cleanFunc = ds[CleanName]
dc.resetFunc = ds[ResetName]
v = dc
} else if list, ok := firstChildValue.(*ListType); ok {
var args []any
+7 -7
View File
@@ -30,16 +30,16 @@ func evalLength(ctx ExprContext, opTerm *term) (v any, err error) {
s, _ := childValue.(string)
v = int64(len(s))
} else if IsDict(childValue) {
// m, _ := childValue.(map[any]any)
m, _ := childValue.(*DictType)
v = int64(len(*m))
} else if it, ok := childValue.(Iterator); ok {
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
count, _ := extIt.CallOperation(CountName, nil)
v, _ = ToGoInt(count, "")
} else {
v = int64(it.Index() + 1)
}
v = int64(it.Count())
// if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
// count, _ := extIt.CallOperation(CountName, nil)
// v, _ = ToGoInt(count, "")
// } else {
// v = int64(it.Index() + 1)
// }
} else {
err = opTerm.errIncompatibleType(childValue)
}
+15 -10
View File
@@ -18,6 +18,11 @@ func NewParser() (p *parser) {
return p
}
func (parser *parser) Next(scanner *scanner) (tk *Token) {
for tk=scanner.Next(); tk.IsSymbol(SymComment); tk=scanner.Next() {}
return
}
func (parser *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
args := make([]*term, 0, 10)
itemExpected := false
@@ -57,11 +62,11 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
itemExpected := false
tk := scanner.Previous()
for lastSym != SymClosedRound && lastSym != SymEos {
tk = scanner.Next()
tk = parser.Next(scanner)
if tk.IsSymbol(SymIdentifier) {
param := newTerm(tk)
args = append(args, param)
tk = scanner.Next()
tk = parser.Next(scanner)
if tk.Sym == SymEqual {
var paramExpr *ast
defaultParamsStarted = true
@@ -86,7 +91,7 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
err = tk.ErrorExpectedGot(")")
}
if err == nil {
tk = scanner.Next()
tk = parser.Next(scanner)
if tk.IsSymbol(SymOpenBrace) {
body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace)
} else {
@@ -184,8 +189,8 @@ func (parser *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree
return
}
func (parser *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, err error) {
tk := scanner.Next()
func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
tk := parser.Next(scanner)
if tk.Sym == SymError {
err = tk.Error()
return
@@ -194,7 +199,7 @@ func (parser *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any,
return
}
if tk.Sym == SymInteger || tk.Sym == SymString {
tkSep := scanner.Next()
tkSep := parser.Next(scanner)
if tkSep.Sym != SymColon {
err = tkSep.ErrorExpectedGot(":")
} else {
@@ -213,7 +218,7 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
for lastSym != SymClosedBrace && lastSym != SymEos {
var subTree *ast
var key any
if key, err = parser.parseDictKey(scanner, allowVarRef); err != nil {
if key, err = parser.parseDictKey(scanner); err != nil {
break
} else if key == nil {
tk := scanner.Previous()
@@ -251,7 +256,7 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
var filterList *term
var caseExpr *ast
tk := scanner.Next()
tk := parser.Next(scanner)
startRow := tk.row
startCol := tk.col
if tk.Sym == SymOpenSquare {
@@ -262,7 +267,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil {
return
}
tk = scanner.Next()
tk = parser.Next(scanner)
startRow = tk.row
startCol = tk.col
} else if !defaultCase {
@@ -340,7 +345,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
tree = NewAst()
firstToken := true
// lastSym := SymUnknown
for tk = scanner.Next(); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = scanner.Next() {
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) {
if tk.Sym == SymComment {
continue
}
+1 -1
View File
@@ -97,7 +97,7 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
if gotErr != wantErr {
if wantErr == nil || gotErr == nil || (gotErr.Error() != wantErr.Error()) {
t.Errorf("%d: %q -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, wantErr)
t.Errorf("%d: %s -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, wantErr)
good = false
}
}
+6 -1
View File
@@ -22,7 +22,7 @@ func TestDictParser(t *testing.T) {
inputs := []inputType{
/* 1 */ {`{}`, map[any]any{}, nil},
/* 2 */ {`{123}`, nil, errors.New(`[1:6] expected ":", got "}"`)},
/* 2 */ {`{123}`, nil, errors.New("[1:6] expected `:`, got `}`")},
/* 3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
/* 4 */ {`{1:"one",2:"two",3:"three"}[3]`, "three", nil},
/* 5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
@@ -32,6 +32,11 @@ func TestDictParser(t *testing.T) {
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil},
/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
/* 12 */ {`m={
"a":1,
//"b":2,
"c":3
}`, map[any]any{"a": 1, "c": 3}, nil},
}
succeeded := 0
+3 -3
View File
@@ -19,8 +19,8 @@ func TestExpr(t *testing.T) {
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
/* 6 */ {`
ds={
"init":func(end){@end=end; @current=0 but true},
"current":func(){current},
"init":func(@end){@current=0 but true},
//"current":func(){current},
"next":func(){
((next=current+1) <= end) ? [true] {@current=next but current} :: {nil}
}
@@ -32,6 +32,6 @@ func TestExpr(t *testing.T) {
}
// t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 3)
//runTestSuiteSpec(t, section, inputs, 6)
runTestSuite(t, section, inputs)
}
+1 -1
View File
@@ -27,7 +27,7 @@ func TestFuncs(t *testing.T) {
/* 14 */ {`two=func(){2}; two(123)`, nil, `two(): too much params -- expected 0, got 1`},
/* 15 */ {`f=func(x,n=2){x+n}; f(3)`, int64(5), nil},
/* 16 */ {`f=func(x,n=2,y){x+n}`, nil, `[1:16] can't mix default and non-default parameters`},
/* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, `[1:24] expected "function-param-value", got ")"`},
/* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, "[1:24] expected `function-param-value`, got `)`"},
/* 18 */ {`factory=func(base){func(){@base=base+1}}; inc10=factory(10); inc5=factory(5); inc10(); inc5(); inc10()`, int64(12), nil},
/* 19 */ {`f=func(a,y=1,z="sos"){}; string(f)`, `f(a, y=1, z="sos"):any{}`, nil},
// /* 20 */ {`a=[func(){3}]; a[0]()`, int64(3), nil},
+7 -7
View File
@@ -15,7 +15,7 @@ func TestNewListIterator(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "b" {
t.Errorf("expcted %q, got %q", "b", item)
t.Errorf("expected %q, got %q", "b", item)
} else {
t.Logf("Next: %v", item)
}
@@ -27,7 +27,7 @@ func TestNewListIterator2(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "d" {
t.Errorf("expcted %q, got %q", "d", item)
t.Errorf("expected %q, got %q", "d", item)
} else {
t.Logf("Next: %v", item)
}
@@ -39,7 +39,7 @@ func TestNewListIterator3(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "b" {
t.Errorf("expcted %q, got %q", "b", item)
t.Errorf("expected %q, got %q", "b", item)
} else {
t.Logf("Next: %v", item)
}
@@ -51,7 +51,7 @@ func TestNewIterList2(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item)
t.Errorf("expected %q, got %q", "a", item)
} else {
t.Logf("Next: %v", item)
}
@@ -63,7 +63,7 @@ func TestNewIterList3(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item)
t.Errorf("expected %q, got %q", "a", item)
} else {
t.Logf("Next: %v", item)
}
@@ -83,7 +83,7 @@ func TestNewIterList5(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "123" {
t.Errorf("expcted %q, got %q", "123", item)
t.Errorf("expected %q, got %q", "123", item)
} else {
t.Logf("Next: %v", item)
}
@@ -96,7 +96,7 @@ func TestNewIterList6(t *testing.T) {
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "a" {
t.Errorf("expcted %q, got %q", "a", item)
t.Errorf("expected %q, got %q", "a", item)
} else {
t.Logf("Next: %v", item)
}
+2 -2
View File
@@ -11,7 +11,7 @@ func TestIteratorParser(t *testing.T) {
inputs := []inputType{
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ^it`, int64(0), nil},
/* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
/* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil},
/* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(3), nil},
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ^it`, int64(0), nil},
/* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
@@ -27,6 +27,6 @@ func TestIteratorParser(t *testing.T) {
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
}
//runTestSuiteSpec(t, section, inputs, 4)
//runTestSuiteSpec(t, section, inputs, 3)
runTestSuite(t, section, inputs)
}
+3 -3
View File
@@ -128,8 +128,8 @@ func TestGeneralParser(t *testing.T) {
/* 114 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil},
/* 115 */ {`nil`, nil, nil},
/* 116 */ {`null`, nil, `undefined variable or function "null"`},
/* 117 */ {`{"key"}`, nil, `[1:8] expected ":", got "}"`},
/* 118 */ {`{"key":}`, nil, `[1:9] expected "dictionary-value", got "}"`},
/* 117 */ {`{"key"}`, nil, "[1:8] expected `:`, got `}`"},
/* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
/* 119 */ {`{}`, &DictType{}, nil},
/* 120 */ {`v=10; v++; v`, int64(11), nil},
/* 121 */ {`1+1|2+0.5`, float64(2), nil},
@@ -144,6 +144,6 @@ func TestGeneralParser(t *testing.T) {
}
// t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 102)
//runTestSuiteSpec(t, section, inputs, 130)
runTestSuite(t, section, inputs)
}
+3 -3
View File
@@ -28,7 +28,7 @@ func (tk *Token) DevString() string {
func (tk *Token) String() string {
if tk.Value != nil {
if s, ok := tk.Value.(string); ok {
return fmt.Sprintf("%q", s)
return s //fmt.Sprintf("%q", s)
} else {
return fmt.Sprintf("%v", tk.Value)
}
@@ -91,11 +91,11 @@ func (tk *Token) Errors(msg string) (err error) {
}
func (tk *Token) ErrorExpectedGot(symbol string) (err error) {
err = fmt.Errorf("[%d:%d] expected %q, got %q", tk.row, tk.col, symbol, tk)
err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, tk)
return
}
func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
err = fmt.Errorf("[%d:%d] expected %q, got %q", tk.row, tk.col, symbol, got)
err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, got)
return
}