Compare commits
16 Commits
730b59e6d3
...
62e16219f7
Author | SHA1 | Date | |
---|---|---|---|
62e16219f7 | |||
750c660331 | |||
7a88449cd1 | |||
b14dc2f1ee | |||
d354102c6a | |||
761ec868e6 | |||
7941c2dfec | |||
ebb2811ed3 | |||
75c0c0f681 | |||
268a968548 | |||
323308d86f | |||
b28d6a8f02 | |||
ab82bcf1ef | |||
a628bfac39 | |||
d1122da566 | |||
6ae5ca34ed |
3
ast.go
3
ast.go
@ -83,8 +83,9 @@ func (self *ast) insert(tree, node *term) (root *term, err error) {
|
|||||||
if tree.isComplete() {
|
if tree.isComplete() {
|
||||||
var subRoot *term
|
var subRoot *term
|
||||||
last := tree.removeLastChild()
|
last := tree.removeLastChild()
|
||||||
subRoot, err = self.insert(last, node)
|
if subRoot, err = self.insert(last, node); err == nil {
|
||||||
subRoot.setParent(tree)
|
subRoot.setParent(tree)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
node.setParent(tree)
|
node.setParent(tree)
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ type ExprContext interface {
|
|||||||
setVar(varName string, value any)
|
setVar(varName string, value any)
|
||||||
EnumVars(func(name string) (accept bool)) (varNames []string)
|
EnumVars(func(name string) (accept bool)) (varNames []string)
|
||||||
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
|
||||||
GetFuncInfo(name string) ExprFunc
|
GetFuncInfo(name string) (item ExprFunc, exists bool)
|
||||||
Call(name string, args []any) (result any, err error)
|
Call(name string, args []any) (result any, err error)
|
||||||
RegisterFunc(name string, f Functor, minArgs, maxArgs int)
|
RegisterFunc(name string, f Functor, minArgs, maxArgs int)
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ func TestExpr(t *testing.T) {
|
|||||||
/* 1 */ {`0?{}`, nil, nil},
|
/* 1 */ {`0?{}`, nil, nil},
|
||||||
/* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil},
|
/* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil},
|
||||||
/* 3 */ {`f=openFile("test-file.txt"); line=readFile(f); closeFile(f); line`, "uno", nil},
|
/* 3 */ {`f=openFile("test-file.txt"); line=readFile(f); closeFile(f); line`, "uno", nil},
|
||||||
|
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
succeeded := 0
|
succeeded := 0
|
||||||
|
@ -45,6 +45,10 @@ func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func ImportBuiltinsFuncs(ctx ExprContext) {
|
func ImportBuiltinsFuncs(ctx ExprContext) {
|
||||||
ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 1, -1)
|
ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 1, -1)
|
||||||
ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 1, -1)
|
ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 1, -1)
|
||||||
|
@ -140,3 +140,7 @@ func ImportImportFuncs(ctx ExprContext) {
|
|||||||
ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1)
|
ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1)
|
||||||
ctx.RegisterFunc("include", &simpleFunctor{f: includeFunc}, 1, -1)
|
ctx.RegisterFunc("include", &simpleFunctor{f: includeFunc}, 1, -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registerImport("import", ImportImportFuncs)
|
||||||
|
}
|
||||||
|
@ -23,15 +23,20 @@ func doAdd(ctx ExprContext, name string, it Iterator) (result any, err error) {
|
|||||||
var v any
|
var v any
|
||||||
|
|
||||||
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
||||||
|
if subIter, ok := v.(Iterator); ok {
|
||||||
|
if v, err = doAdd(ctx, name, subIter); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if err = checkNumberParamExpected(name, v, it.Index()); err != nil {
|
if err = checkNumberParamExpected(name, v, it.Index()); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if array, ok := v.([]any); ok {
|
if array, ok := v.([]any); ok {
|
||||||
if v, err = doAdd(ctx, name, NewFlatArrayIterator(array)); err != nil {
|
if v, err = doAdd(ctx, name, NewFlatArrayIterator(array)); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !sumAsFloat && isFloat(v) {
|
if !sumAsFloat && isFloat(v) {
|
||||||
sumAsFloat = true
|
sumAsFloat = true
|
||||||
|
@ -162,3 +162,7 @@ func ImportOsFuncs(ctx ExprContext) {
|
|||||||
ctx.RegisterFunc("readFile", &simpleFunctor{f: readFileFunc}, 1, 2)
|
ctx.RegisterFunc("readFile", &simpleFunctor{f: readFileFunc}, 1, 2)
|
||||||
ctx.RegisterFunc("closeFile", &simpleFunctor{f: closeFileFunc}, 1, 1)
|
ctx.RegisterFunc("closeFile", &simpleFunctor{f: closeFileFunc}, 1, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
registerImport("os", ImportOsFuncs)
|
||||||
|
}
|
||||||
|
42
iter-list.go
Normal file
42
iter-list.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// iter-list.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
type FlatArrayIterator struct {
|
||||||
|
a []any
|
||||||
|
index int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFlatArrayIterator(array []any) *FlatArrayIterator {
|
||||||
|
return &FlatArrayIterator{a: array, index: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *FlatArrayIterator) Current() (item any, err error) {
|
||||||
|
if it.index >= 0 && it.index < len(it.a) {
|
||||||
|
item = it.a[it.index]
|
||||||
|
} else {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *FlatArrayIterator) Next() (item any, err error) {
|
||||||
|
if item, err = it.Current(); err != io.EOF {
|
||||||
|
it.index++
|
||||||
|
}
|
||||||
|
// if it.index < len(it.a) {
|
||||||
|
// item = it.a[it.index]
|
||||||
|
// it.index++
|
||||||
|
// } else {
|
||||||
|
// err = io.EOF
|
||||||
|
// }
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *FlatArrayIterator) Index() int {
|
||||||
|
return it.index - 1
|
||||||
|
}
|
31
iterator.go
31
iterator.go
@ -4,37 +4,8 @@
|
|||||||
// iterator.go
|
// iterator.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import "io"
|
|
||||||
|
|
||||||
type Iterator interface {
|
type Iterator interface {
|
||||||
Reset()
|
|
||||||
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)
|
||||||
Index() int
|
Index() int
|
||||||
}
|
}
|
||||||
|
|
||||||
type FlatArrayIterator struct {
|
|
||||||
a []any
|
|
||||||
index int
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFlatArrayIterator(array []any) *FlatArrayIterator {
|
|
||||||
return &FlatArrayIterator{a: array, index: 0}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *FlatArrayIterator) Reset() {
|
|
||||||
it.index = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *FlatArrayIterator) Next() (item any, err error) {
|
|
||||||
if it.index < len(it.a) {
|
|
||||||
item = it.a[it.index]
|
|
||||||
it.index++
|
|
||||||
} else {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *FlatArrayIterator) Index() int {
|
|
||||||
return it.index - 1
|
|
||||||
}
|
|
||||||
|
38
operand-dict.go
Normal file
38
operand-dict.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operand-dict.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
// -------- dict term
|
||||||
|
// func newDictTermA(args ...*term) *term {
|
||||||
|
// return newDictTerm(args)
|
||||||
|
// }
|
||||||
|
|
||||||
|
func newDictTerm(args map[any]*term) *term {
|
||||||
|
return &term{
|
||||||
|
tk: *NewValueToken(0, 0, SymDict, "{}", args),
|
||||||
|
parent: nil,
|
||||||
|
children: nil,
|
||||||
|
position: posLeaf,
|
||||||
|
priority: priValue,
|
||||||
|
evalFunc: evalDict,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- dict func
|
||||||
|
func evalDict(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
dict, _ := self.value().(map[any]*term)
|
||||||
|
items := make(map[any]any, len(dict))
|
||||||
|
for key, tree := range dict {
|
||||||
|
var param any
|
||||||
|
if param, err = tree.compute(ctx); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
items[key] = param
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
v = items
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
@ -12,8 +12,6 @@ import (
|
|||||||
func newFuncCallTerm(tk *Token, args []*term) *term {
|
func newFuncCallTerm(tk *Token, args []*term) *term {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
// class: classVar,
|
|
||||||
// kind: kindUnknown,
|
|
||||||
parent: nil,
|
parent: nil,
|
||||||
children: args,
|
children: args,
|
||||||
position: posLeaf,
|
position: posLeaf,
|
||||||
@ -44,7 +42,7 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
// Export functions
|
// Export functions
|
||||||
for _, refName := range ctx.EnumFuncs(func(name string) bool { return exportAll || name[0] == '@' }) {
|
for _, refName := range ctx.EnumFuncs(func(name string) bool { return exportAll || name[0] == '@' }) {
|
||||||
if info := ctx.GetFuncInfo(refName); info != nil {
|
if info, _ := ctx.GetFuncInfo(refName); info != nil {
|
||||||
exportFunc(parentCtx, refName, info)
|
exportFunc(parentCtx, refName, info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
152
operand-iterator.go
Normal file
152
operand-iterator.go
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operand-iterator.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -------- iterator term
|
||||||
|
|
||||||
|
const (
|
||||||
|
initName = "init"
|
||||||
|
nextName = "next"
|
||||||
|
currentName = "current"
|
||||||
|
)
|
||||||
|
|
||||||
|
type dataCursor struct {
|
||||||
|
ds map[any]*term
|
||||||
|
ctx ExprContext
|
||||||
|
index int
|
||||||
|
resource any
|
||||||
|
nextFunc Functor
|
||||||
|
currentFunc Functor
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *dataCursor) String() string {
|
||||||
|
var s string
|
||||||
|
if item, err := dc.Current(); err == nil {
|
||||||
|
s = fmt.Sprintf("%v", item)
|
||||||
|
} else {
|
||||||
|
s = "(nil)"
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
|
||||||
|
if item, err = dc.currentFunc.Invoke(dc.ctx, currentName, []any{}); err == nil && item == nil {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item
|
||||||
|
if item, err = dc.nextFunc.Invoke(dc.ctx, nextName, []any{}); err == nil {
|
||||||
|
if item == nil {
|
||||||
|
err = io.EOF
|
||||||
|
} else {
|
||||||
|
dc.index++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *dataCursor) Index() int {
|
||||||
|
return dc.index
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIteratorTerm(tk *Token, dsTerm *term, args []*term) *term {
|
||||||
|
tk.Sym = SymIterator
|
||||||
|
|
||||||
|
children := make([]*term, 0, 1+len(args))
|
||||||
|
children = append(children, dsTerm)
|
||||||
|
children = append(children, args...)
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
parent: nil,
|
||||||
|
children: children,
|
||||||
|
position: posLeaf,
|
||||||
|
priority: priValue,
|
||||||
|
evalFunc: evalIterator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- eval iterator
|
||||||
|
|
||||||
|
func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) {
|
||||||
|
values = make([]any, len(a))
|
||||||
|
for i, t := range a {
|
||||||
|
var value any
|
||||||
|
if value, err = t.compute(ctx); err == nil {
|
||||||
|
values[i] = value
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
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)
|
||||||
|
for _, k := range []string{initName, currentName, nextName} {
|
||||||
|
if item, exists := dictAny[k]; exists && item != nil {
|
||||||
|
if functor, ok := item.(*funcDefFunctor); ok {
|
||||||
|
ds[k] = functor
|
||||||
|
}
|
||||||
|
} else if k != initName {
|
||||||
|
err = fmt.Errorf("the data-source must provide a non-nil %q operator", k)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = self.Errorf("the first param (data-source) of an iterator must be a dict, not a %T", value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalIterator(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
var ds map[string]Functor
|
||||||
|
|
||||||
|
if ds, err = getDataSourceDict(ctx, self); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dc := &dataCursor{
|
||||||
|
index: -1,
|
||||||
|
ctx: ctx.Clone(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if initFunc, exists := ds[initName]; exists && initFunc != nil {
|
||||||
|
var args []any
|
||||||
|
if len(self.children) > 1 {
|
||||||
|
if args, err = evalTermArray(ctx, self.children[1:]); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args = []any{}
|
||||||
|
}
|
||||||
|
if dc.resource, err = initFunc.Invoke(dc.ctx, initName, args); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dc.nextFunc, _ = ds[nextName]
|
||||||
|
dc.currentFunc, _ = ds[currentName]
|
||||||
|
v = dc
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
@ -23,8 +23,13 @@ func newVarTerm(tk *Token) *term {
|
|||||||
// -------- eval func
|
// -------- eval func
|
||||||
func evalVar(ctx ExprContext, self *term) (v any, err error) {
|
func evalVar(ctx ExprContext, self *term) (v any, err error) {
|
||||||
var exists bool
|
var exists bool
|
||||||
if v, exists = ctx.GetVar(self.tk.source); !exists {
|
name := self.source()
|
||||||
err = fmt.Errorf("undefined variable %q", self.tk.source)
|
if v, exists = ctx.GetVar(name); !exists {
|
||||||
|
if info, exists := ctx.GetFuncInfo(name); exists {
|
||||||
|
v = info.Functor()
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("undefined variable or function %q", name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
if functor, ok := v.(Functor); ok {
|
if functor, ok := v.(Functor); ok {
|
||||||
ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
||||||
} else {
|
} else {
|
||||||
ctx.setVar(leftTerm.tk.source, v)
|
ctx.setVar(leftTerm.source(), v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -34,11 +34,11 @@ func evalNullCoalesce(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
||||||
v = leftValue
|
v = leftValue
|
||||||
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||||
if _, ok := rightValue.(Functor); ok {
|
// if _, ok := rightValue.(Functor); ok {
|
||||||
err = errCoalesceNoFunc(self.children[1])
|
// err = errCoalesceNoFunc(self.children[1])
|
||||||
} else {
|
// } else {
|
||||||
v = rightValue
|
v = rightValue
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -71,8 +71,8 @@ func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
|
||||||
v = leftValue
|
v = leftValue
|
||||||
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
|
||||||
if _, ok := rightValue.(Functor); ok {
|
if functor, ok := rightValue.(Functor); ok {
|
||||||
err = errCoalesceNoFunc(self.children[1])
|
ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
||||||
} else {
|
} else {
|
||||||
v = rightValue
|
v = rightValue
|
||||||
ctx.setVar(leftTerm.source(), rightValue)
|
ctx.setVar(leftTerm.source(), rightValue)
|
||||||
@ -82,9 +82,9 @@ func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// utils
|
// utils
|
||||||
func errCoalesceNoFunc(t *term) error {
|
// func errCoalesceNoFunc(t *term) error {
|
||||||
return t.Errorf("the right operand of a coalescing operation cannot be a function definition")
|
// return t.Errorf("the right operand of a coalescing operation cannot be a function definition")
|
||||||
}
|
// }
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
// operator-dot.go
|
// operator-dot.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
// -------- dot term
|
// -------- dot term
|
||||||
func newDotTerm(tk *Token) (inst *term) {
|
func newDotTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
@ -23,15 +25,13 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
indexTerm := self.children[1]
|
indexTerm := self.children[1]
|
||||||
if !isInteger(rightValue) {
|
|
||||||
err = indexTerm.Errorf("index expression must be integer, got %T", rightValue)
|
if isList(leftValue) {
|
||||||
|
var index int
|
||||||
|
if index, err = indexTerm.toInt(rightValue, "index expression value must be integer"); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
index64, _ := rightValue.(int64)
|
|
||||||
index := int(index64)
|
|
||||||
|
|
||||||
if isList(leftValue) {
|
|
||||||
list, _ := leftValue.([]any)
|
list, _ := leftValue.([]any)
|
||||||
if index >= 0 && index < len(list) {
|
if index >= 0 && index < len(list) {
|
||||||
v = list[index]
|
v = list[index]
|
||||||
@ -41,6 +41,11 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
err = indexTerm.Errorf("index %v out of bounds", index)
|
err = indexTerm.Errorf("index %v out of bounds", index)
|
||||||
}
|
}
|
||||||
} else if isString(leftValue) {
|
} else if isString(leftValue) {
|
||||||
|
var index int
|
||||||
|
if index, err = indexTerm.toInt(rightValue, "index expression value must be integer"); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
s, _ := leftValue.(string)
|
s, _ := leftValue.(string)
|
||||||
if index >= 0 && index < len(s) {
|
if index >= 0 && index < len(s) {
|
||||||
v = string(s[index])
|
v = string(s[index])
|
||||||
@ -49,6 +54,12 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
} else {
|
} else {
|
||||||
err = indexTerm.Errorf("index %v out of bounds", index)
|
err = indexTerm.Errorf("index %v out of bounds", index)
|
||||||
}
|
}
|
||||||
|
} else if isDict(leftValue) {
|
||||||
|
var ok bool
|
||||||
|
d, _ := leftValue.(map[any]any)
|
||||||
|
if v, ok = d[rightValue]; !ok {
|
||||||
|
err = fmt.Errorf("key %v does not belong to the dictionary", rightValue)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
err = self.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
|
38
operator-iter-value.go
Normal file
38
operator-iter-value.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-iter-value.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
|
||||||
|
//-------- iter value term
|
||||||
|
|
||||||
|
func newIterValueTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 1),
|
||||||
|
position: posPrefix,
|
||||||
|
priority: priIterValue,
|
||||||
|
evalFunc: evalIterValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalIterValue(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
var leftValue any
|
||||||
|
|
||||||
|
if leftValue, err = self.evalPrefix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if dc, ok := leftValue.(*dataCursor); ok {
|
||||||
|
v, err = dc.Current()
|
||||||
|
} else {
|
||||||
|
err = self.errIncompatibleType(leftValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
||||||
|
}
|
41
operator-post-inc.go
Normal file
41
operator-post-inc.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-post-inc.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
// -------- post increment term
|
||||||
|
|
||||||
|
func newPostIncTerm(tk *Token) *term {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
parent: nil,
|
||||||
|
children: make([]*term, 0, 1),
|
||||||
|
position: posPostfix,
|
||||||
|
priority: priPrePost,
|
||||||
|
evalFunc: evalPostInc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalPostInc(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
var leftValue any
|
||||||
|
if leftValue, err = self.evalPrefix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if dc, ok := leftValue.(*dataCursor); ok {
|
||||||
|
v, err = dc.Next()
|
||||||
|
} else if isInteger(leftValue) && self.children[0].symbol() == SymIdentifier {
|
||||||
|
v = leftValue
|
||||||
|
i, _ := leftValue.(int64)
|
||||||
|
ctx.SetVar(self.children[0].source(), i+1)
|
||||||
|
} else {
|
||||||
|
self.errIncompatibleType(leftValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
registerTermConstructor(SymDoublePlus, newPostIncTerm)
|
||||||
|
}
|
194
parser.go
194
parser.go
@ -58,23 +58,72 @@ func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||||
|
// // Example: "add = func(x,y) {x+y}
|
||||||
|
// var body *ast
|
||||||
|
// args := make([]*term, 0)
|
||||||
|
// tk := scanner.Next()
|
||||||
|
// for tk.Sym != SymClosedRound && tk.Sym != SymEos {
|
||||||
|
// if tk.Sym == SymIdentifier {
|
||||||
|
// t := newTerm(tk, nil)
|
||||||
|
// args = append(args, t)
|
||||||
|
// } else {
|
||||||
|
// err = tk.Errorf("invalid param %q, variable identifier expected", tk.source)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// tk = scanner.Next()
|
||||||
|
// }
|
||||||
|
// if err == nil && tk.Sym != SymClosedRound {
|
||||||
|
// err = tk.Errorf("unterminate function params list")
|
||||||
|
// }
|
||||||
|
// if err == nil {
|
||||||
|
// tk = scanner.Next()
|
||||||
|
// if tk.Sym == SymOpenBrace {
|
||||||
|
// body, err = self.parseGeneral(scanner, true, true, SymClosedBrace)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if err == nil {
|
||||||
|
// // TODO Check arguments
|
||||||
|
// if scanner.Previous().Sym != SymClosedBrace {
|
||||||
|
// err = scanner.Previous().Errorf("not properly terminated function body")
|
||||||
|
// } else {
|
||||||
|
// tk = scanner.makeValueToken(SymExpression, "", body)
|
||||||
|
// tree = newFuncDefTerm(tk, args)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||||
// 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.Errorf("exptected identifier, got %q", subTree.root)
|
||||||
|
}
|
||||||
|
} else if itemExpected {
|
||||||
|
prev := scanner.Previous()
|
||||||
|
err = prev.Errorf("expected function parameter, got %q", prev)
|
||||||
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.Errorf("unterminated function parameters list")
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tk = scanner.Next()
|
tk = scanner.Next()
|
||||||
@ -97,16 +146,22 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
|
itemExpected := false
|
||||||
for lastSym != SymClosedSquare && lastSym != SymEos {
|
for lastSym != SymClosedSquare && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
||||||
if subTree.root != nil {
|
if subTree.root != nil {
|
||||||
args = append(args, subTree.root)
|
args = append(args, subTree.root)
|
||||||
|
} else if itemExpected {
|
||||||
|
prev := scanner.Previous()
|
||||||
|
err = prev.Errorf("expected list item, got %q", prev)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
lastSym = scanner.Previous().Sym
|
lastSym = scanner.Previous().Sym
|
||||||
|
itemExpected = lastSym == SymComma
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO Check arguments
|
// TODO Check arguments
|
||||||
@ -119,6 +174,115 @@ func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
||||||
|
var ds *term
|
||||||
|
tk := scanner.Previous()
|
||||||
|
args := make([]*term, 0)
|
||||||
|
lastSym := SymUnknown
|
||||||
|
dsExpected := true
|
||||||
|
itemExpected := false
|
||||||
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
|
var subTree *ast
|
||||||
|
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == 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)
|
||||||
|
}
|
||||||
|
} else if itemExpected {
|
||||||
|
prev := scanner.Previous()
|
||||||
|
err = prev.Errorf("expected iterator argument, got %q", prev)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
lastSym = scanner.Previous().Sym
|
||||||
|
itemExpected = lastSym == SymComma
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
// TODO Check arguments
|
||||||
|
if lastSym != SymClosedRound {
|
||||||
|
err = scanner.Previous().Errorf("unterminate iterator param list")
|
||||||
|
} else if ds != nil {
|
||||||
|
subtree = newIteratorTerm(tk, ds, args)
|
||||||
|
} else {
|
||||||
|
tk.Errorf("missing data-source param")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, err error) {
|
||||||
|
tk := scanner.Next()
|
||||||
|
if tk.Sym == SymError {
|
||||||
|
err = tk.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tk.Sym == SymClosedBrace || tk.Sym == SymEos {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tk.Sym == SymInteger || tk.Sym == SymString {
|
||||||
|
tkSep := scanner.Next()
|
||||||
|
if tkSep.Sym != SymColon {
|
||||||
|
err = tkSep.Errorf("expected \":\", got %q", tkSep)
|
||||||
|
} else {
|
||||||
|
key = tk.Value
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = tk.Errorf("expected dictionary key or closed brace, got %q", tk)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
||||||
|
args := make(map[any]*term, 0)
|
||||||
|
lastSym := SymUnknown
|
||||||
|
itemExpected := false
|
||||||
|
for lastSym != SymClosedBrace && lastSym != SymEos {
|
||||||
|
var subTree *ast
|
||||||
|
var key any
|
||||||
|
if key, err = self.parseDictKey(scanner, allowVarRef); err != nil {
|
||||||
|
break
|
||||||
|
} else if key == nil {
|
||||||
|
tk := scanner.Previous()
|
||||||
|
lastSym = tk.Sym
|
||||||
|
if itemExpected {
|
||||||
|
err = tk.Errorf("expected dictionary key, got %q", tk)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
|
||||||
|
if subTree.root != nil {
|
||||||
|
args[key] = subTree.root
|
||||||
|
} else if key != nil {
|
||||||
|
prev := scanner.Previous()
|
||||||
|
err = prev.Errorf("expected dictionary value, got %q", prev)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
lastSym = scanner.Previous().Sym
|
||||||
|
itemExpected = lastSym == SymComma
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
// TODO Check arguments
|
||||||
|
if lastSym != SymClosedBrace {
|
||||||
|
err = scanner.Previous().Errorf("unterminated dictionary")
|
||||||
|
} else {
|
||||||
|
subtree = newDictTerm(args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
|
func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
|
||||||
var filterList *term
|
var filterList *term
|
||||||
var caseExpr *ast
|
var caseExpr *ast
|
||||||
@ -241,6 +405,12 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
|||||||
err = tree.addTerm(listTerm)
|
err = tree.addTerm(listTerm)
|
||||||
currentTerm = listTerm
|
currentTerm = listTerm
|
||||||
}
|
}
|
||||||
|
case SymOpenBrace:
|
||||||
|
var mapTerm *term
|
||||||
|
if mapTerm, err = self.parseDictionary(scanner, allowVarRef); err == nil {
|
||||||
|
err = tree.addTerm(mapTerm)
|
||||||
|
currentTerm = mapTerm
|
||||||
|
}
|
||||||
case SymEqual:
|
case SymEqual:
|
||||||
if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
||||||
currentTerm, err = tree.addToken2(tk)
|
currentTerm, err = tree.addToken2(tk)
|
||||||
@ -251,6 +421,12 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
|
|||||||
err = tree.addTerm(funcDefTerm)
|
err = tree.addTerm(funcDefTerm)
|
||||||
currentTerm = funcDefTerm
|
currentTerm = funcDefTerm
|
||||||
}
|
}
|
||||||
|
case SymDollarRound:
|
||||||
|
var iterDefTerm *term
|
||||||
|
if iterDefTerm, err = self.parseIterDef(scanner, allowVarRef); err == nil {
|
||||||
|
err = tree.addTerm(iterDefTerm)
|
||||||
|
currentTerm = iterDefTerm
|
||||||
|
}
|
||||||
case SymIdentifier:
|
case SymIdentifier:
|
||||||
if tk.source[0] == '@' && !allowVarRef {
|
if tk.source[0] == '@' && !allowVarRef {
|
||||||
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
||||||
|
@ -7,6 +7,7 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -117,9 +118,9 @@ func TestParser(t *testing.T) {
|
|||||||
/* 96 */ {`x=2 but x*10`, int64(20), nil},
|
/* 96 */ {`x=2 but x*10`, int64(20), nil},
|
||||||
/* 97 */ {`false and true`, false, nil},
|
/* 97 */ {`false and true`, false, nil},
|
||||||
/* 98 */ {`false and (x==2)`, false, nil},
|
/* 98 */ {`false and (x==2)`, false, nil},
|
||||||
/* 99 */ {`false and (x=2 but x==2) or x==2`, nil, errors.New(`undefined variable "x"`)},
|
/* 99 */ {`false and (x=2 but x==2) or x==2`, nil, errors.New(`undefined variable or function "x"`)},
|
||||||
/* 100 */ {`false or true`, true, nil},
|
/* 100 */ {`false or true`, true, nil},
|
||||||
/* 101 */ {`false or (x==2)`, nil, errors.New(`undefined variable "x"`)},
|
/* 101 */ {`false or (x==2)`, nil, errors.New(`undefined variable or function "x"`)},
|
||||||
/* 102 */ {`a=5; a`, int64(5), nil},
|
/* 102 */ {`a=5; a`, int64(5), nil},
|
||||||
/* 103 */ {`a=5; b=2; add(a, b*3)`, int64(11), nil},
|
/* 103 */ {`a=5; b=2; add(a, b*3)`, int64(11), nil},
|
||||||
/* 104 */ {`2=5`, nil, errors.New(`assign operator ("=") must be preceded by a variable`)},
|
/* 104 */ {`2=5`, nil, errors.New(`assign operator ("=") must be preceded by a variable`)},
|
||||||
@ -134,13 +135,13 @@ func TestParser(t *testing.T) {
|
|||||||
/* 113 */ {`import("test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
|
/* 113 */ {`import("test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
|
||||||
/* 114 */ {`x ?? "default"`, "default", nil},
|
/* 114 */ {`x ?? "default"`, "default", nil},
|
||||||
/* 115 */ {`x="hello"; x ?? "default"`, "hello", nil},
|
/* 115 */ {`x="hello"; x ?? "default"`, "hello", nil},
|
||||||
/* 116 */ {`x ?? func(){}`, nil, errors.New(`[1:14] the right operand of a coalescing operation cannot be a function definition`)},
|
/* 116 */ {`y=x ?? func(){4}; y()`, int64(4), nil},
|
||||||
/* 117 */ {`x ?= "default"; x`, "default", nil},
|
/* 117 */ {`x ?= "default"; x`, "default", nil},
|
||||||
/* 118 */ {`x="hello"; x ?= "default"; x`, "hello", nil},
|
/* 118 */ {`x="hello"; x ?= "default"; x`, "hello", nil},
|
||||||
/* 119 */ {`@x="hello"; @x`, nil, errors.New(`[1:3] variable references are not allowed in top level expressions: "@x"`)},
|
/* 119 */ {`@x="hello"; @x`, nil, errors.New(`[1:3] variable references are not allowed in top level expressions: "@x"`)},
|
||||||
/* 120 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil},
|
/* 120 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil},
|
||||||
/* 121 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil},
|
/* 121 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil},
|
||||||
/* 122 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, errors.New(`undefined variable "x"`)},
|
/* 122 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, errors.New(`undefined variable or function "x"`)},
|
||||||
/* 123 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil},
|
/* 123 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil},
|
||||||
/* 124 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil},
|
/* 124 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil},
|
||||||
/* 125 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
|
/* 125 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
|
||||||
@ -157,7 +158,11 @@ func TestParser(t *testing.T) {
|
|||||||
/* 136 */ {`1 ? {"a"} : {"b"} ? ["a"] {"A"} :["b"] {"B"}`, "B", nil},
|
/* 136 */ {`1 ? {"a"} : {"b"} ? ["a"] {"A"} :["b"] {"B"}`, "B", nil},
|
||||||
/* 137 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil},
|
/* 137 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil},
|
||||||
/* 138 */ {`nil`, nil, nil},
|
/* 138 */ {`nil`, nil, nil},
|
||||||
/* 139 */ {`null`, nil, errors.New(`undefined variable "null"`)},
|
/* 139 */ {`null`, nil, errors.New(`undefined variable or function "null"`)},
|
||||||
|
/* 140 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)},
|
||||||
|
/* 141 */ {`{"key":}`, nil, errors.New(`[1:9] expected dictionary value, got "}"`)},
|
||||||
|
/* 142 */ {`{}`, map[any]any{}, nil},
|
||||||
|
/* 144 */ //{`3^2`, int64(9), nil},
|
||||||
}
|
}
|
||||||
check_env_expr_path := 113
|
check_env_expr_path := 113
|
||||||
|
|
||||||
@ -165,7 +170,7 @@ func TestParser(t *testing.T) {
|
|||||||
failed := 0
|
failed := 0
|
||||||
|
|
||||||
// inputs1 := []inputType{
|
// inputs1 := []inputType{
|
||||||
// /* 140 */ {`isNil(nil)`, true, nil},
|
// /* 140 */ {`ds={}; $(ds)`, nil, nil},
|
||||||
// }
|
// }
|
||||||
|
|
||||||
for i, input := range inputs {
|
for i, input := range inputs {
|
||||||
@ -190,7 +195,9 @@ func TestParser(t *testing.T) {
|
|||||||
gotResult, gotErr = expr.Eval(ctx)
|
gotResult, gotErr = expr.Eval(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
if gotResult != input.wantResult {
|
eq := reflect.DeepEqual(gotResult, input.wantResult)
|
||||||
|
|
||||||
|
if !eq /*gotResult != input.wantResult*/ {
|
||||||
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
|
t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
|
||||||
good = false
|
good = false
|
||||||
}
|
}
|
||||||
@ -221,10 +228,6 @@ func TestListParser(t *testing.T) {
|
|||||||
wantErr error
|
wantErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
// inputs1 := []inputType{
|
|
||||||
// {`add(1,2,3)`, int64(6), nil},
|
|
||||||
// }
|
|
||||||
|
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`[]`, []any{}, nil},
|
/* 1 */ {`[]`, []any{}, nil},
|
||||||
/* 2 */ {`[1,2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
/* 2 */ {`[1,2,3]`, []any{int64(1), int64(2), int64(3)}, nil},
|
||||||
@ -244,6 +247,10 @@ func TestListParser(t *testing.T) {
|
|||||||
succeeded := 0
|
succeeded := 0
|
||||||
failed := 0
|
failed := 0
|
||||||
|
|
||||||
|
// inputs1 := []inputType{
|
||||||
|
// /* 7 */ {`add([1,4,3,2])`, int64(10), nil},
|
||||||
|
// }
|
||||||
|
|
||||||
for i, input := range inputs {
|
for i, input := range inputs {
|
||||||
var expr *ast
|
var expr *ast
|
||||||
var gotResult any
|
var gotResult any
|
||||||
|
17
scanner.go
17
scanner.go
@ -86,13 +86,14 @@ func (self *scanner) Next() (tk *Token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) fetchNextToken() (tk *Token) {
|
func (self *scanner) fetchNextToken() (tk *Token) {
|
||||||
|
var ch byte
|
||||||
if err := self.skipBlanks(); err != nil {
|
if err := self.skipBlanks(); err != nil {
|
||||||
return self.makeErrorToken(err)
|
return self.makeErrorToken(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
escape := false
|
escape := false
|
||||||
for {
|
for {
|
||||||
ch, _ := self.readChar()
|
ch, _ = self.readChar()
|
||||||
switch ch {
|
switch ch {
|
||||||
case '+':
|
case '+':
|
||||||
if next, _ := self.peek(); next == '+' {
|
if next, _ := self.peek(); next == '+' {
|
||||||
@ -143,6 +144,8 @@ func (self *scanner) fetchNextToken() (tk *Token) {
|
|||||||
}
|
}
|
||||||
case ',':
|
case ',':
|
||||||
tk = self.makeToken(SymComma, ch)
|
tk = self.makeToken(SymComma, ch)
|
||||||
|
case '^':
|
||||||
|
tk = self.makeToken(SymCaret, ch)
|
||||||
case ':':
|
case ':':
|
||||||
if next, _ := self.peek(); next == ':' {
|
if next, _ := self.peek(); next == ':' {
|
||||||
tk = self.moveOn(SymDoubleColon, ch, next)
|
tk = self.moveOn(SymDoubleColon, ch, next)
|
||||||
@ -236,9 +239,18 @@ func (self *scanner) fetchNextToken() (tk *Token) {
|
|||||||
tk = self.makeToken(SymGreater, ch)
|
tk = self.makeToken(SymGreater, ch)
|
||||||
}
|
}
|
||||||
case '$':
|
case '$':
|
||||||
|
if next, _ := self.peek(); next == '(' {
|
||||||
|
tk = self.moveOn(SymDollarRound, ch, next)
|
||||||
|
tk.source += ")"
|
||||||
|
} else {
|
||||||
tk = self.makeToken(SymDollar, ch)
|
tk = self.makeToken(SymDollar, ch)
|
||||||
|
}
|
||||||
case '(':
|
case '(':
|
||||||
|
if next, _ := self.peek(); next == ')' {
|
||||||
|
tk = self.moveOn(SymOpenClosedRound, ch, next)
|
||||||
|
} else {
|
||||||
tk = self.makeToken(SymOpenRound, ch)
|
tk = self.makeToken(SymOpenRound, ch)
|
||||||
|
}
|
||||||
case ')':
|
case ')':
|
||||||
tk = self.makeToken(SymClosedRound, ch)
|
tk = self.makeToken(SymClosedRound, ch)
|
||||||
case '[':
|
case '[':
|
||||||
@ -271,6 +283,9 @@ func (self *scanner) fetchNextToken() (tk *Token) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if tk == nil {
|
||||||
|
tk = NewErrorToken(self.row, self.column, fmt.Errorf("unknown symbol '%c'", ch))
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,8 +50,8 @@ func (ctx *SimpleFuncStore) Clone() ExprContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *SimpleFuncStore) GetFuncInfo(name string) (info ExprFunc) {
|
func (ctx *SimpleFuncStore) GetFuncInfo(name string) (info ExprFunc, exists bool) {
|
||||||
info, _ = ctx.funcStore[name]
|
info, exists = ctx.funcStore[name]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ func (ctx *SimpleVarStore) EnumVars(acceptor func(name string) (accept bool)) (v
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *SimpleVarStore) GetFuncInfo(name string) (f ExprFunc) {
|
func (ctx *SimpleVarStore) GetFuncInfo(name string) (f ExprFunc, exists bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +61,9 @@ const (
|
|||||||
SymDoubleColon // 50: '::'
|
SymDoubleColon // 50: '::'
|
||||||
SymInsert // 51: '>>'
|
SymInsert // 51: '>>'
|
||||||
SymAppend // 52: '<<'
|
SymAppend // 52: '<<'
|
||||||
|
SymCaret // 53: '^'
|
||||||
|
SymDollarRound // 54: '$('
|
||||||
|
SymOpenClosedRound // 55: '()'
|
||||||
SymChangeSign
|
SymChangeSign
|
||||||
SymUnchangeSign
|
SymUnchangeSign
|
||||||
SymIdentifier
|
SymIdentifier
|
||||||
@ -68,6 +71,7 @@ const (
|
|||||||
SymInteger
|
SymInteger
|
||||||
SymFloat
|
SymFloat
|
||||||
SymString
|
SymString
|
||||||
|
SymIterator
|
||||||
SymOr
|
SymOr
|
||||||
SymAnd
|
SymAnd
|
||||||
SymNot
|
SymNot
|
||||||
@ -75,6 +79,7 @@ const (
|
|||||||
SymFuncCall
|
SymFuncCall
|
||||||
SymFuncDef
|
SymFuncDef
|
||||||
SymList
|
SymList
|
||||||
|
SymDict
|
||||||
SymExpression
|
SymExpression
|
||||||
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||||
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||||
|
15
term.go
15
term.go
@ -23,7 +23,9 @@ const (
|
|||||||
priSelector
|
priSelector
|
||||||
priSign
|
priSign
|
||||||
priFact
|
priFact
|
||||||
|
priIterValue
|
||||||
priCoalesce
|
priCoalesce
|
||||||
|
priPrePost
|
||||||
priDot
|
priDot
|
||||||
priValue
|
priValue
|
||||||
)
|
)
|
||||||
@ -127,6 +129,10 @@ func (self *term) setParent(parent *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *term) symbol() Symbol {
|
||||||
|
return self.tk.Sym
|
||||||
|
}
|
||||||
|
|
||||||
func (self *term) source() string {
|
func (self *term) source() string {
|
||||||
return self.tk.source
|
return self.tk.source
|
||||||
}
|
}
|
||||||
@ -144,6 +150,15 @@ func (self *term) compute(ctx ExprContext) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *term) toInt(computedValue any, valueDescription string) (i int, err error) {
|
||||||
|
if index64, ok := computedValue.(int64); ok {
|
||||||
|
i = int(index64)
|
||||||
|
} else {
|
||||||
|
err = self.Errorf("%s, got %T", valueDescription, computedValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
||||||
return self.tk.Errorf(
|
return self.tk.Errorf(
|
||||||
"left operand '%v' [%T] and right operand '%v' [%T] are not compatible with operator %q",
|
"left operand '%v' [%T] and right operand '%v' [%T] are not compatible with operator %q",
|
||||||
|
Loading…
Reference in New Issue
Block a user