Compare commits
No commits in common. "62e16219f71c9441cffc7706c1833e8c2f48986e" and "730b59e6d3a5cf3e47e26ee6b4836de1a10a35dd" have entirely different histories.
62e16219f7
...
730b59e6d3
5
ast.go
5
ast.go
@ -83,9 +83,8 @@ 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()
|
||||||
if subRoot, err = self.insert(last, node); err == nil {
|
subRoot, err = self.insert(last, node)
|
||||||
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) (item ExprFunc, exists bool)
|
GetFuncInfo(name string) ExprFunc
|
||||||
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,7 +21,6 @@ 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,10 +45,6 @@ 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,7 +140,3 @@ 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)
|
|
||||||
}
|
|
||||||
|
17
func-math.go
17
func-math.go
@ -23,19 +23,14 @@ 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 err = checkNumberParamExpected(name, v, it.Index()); err != nil {
|
||||||
if v, err = doAdd(ctx, name, subIter); err != nil {
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if array, ok := v.([]any); ok {
|
||||||
|
if v, err = doAdd(ctx, name, NewFlatArrayIterator(array)); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if err = checkNumberParamExpected(name, v, it.Index()); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if array, ok := v.([]any); ok {
|
|
||||||
if v, err = doAdd(ctx, name, NewFlatArrayIterator(array)); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sumAsFloat && isFloat(v) {
|
if !sumAsFloat && isFloat(v) {
|
||||||
|
@ -162,7 +162,3 @@ 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
42
iter-list.go
@ -1,42 +0,0 @@
|
|||||||
// 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,8 +4,37 @@
|
|||||||
// 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
|
||||||
|
}
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
// 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
|
|
||||||
}
|
|
@ -11,7 +11,9 @@ import (
|
|||||||
// -------- function call term
|
// -------- function call term
|
||||||
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,
|
||||||
@ -42,7 +44,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,152 +0,0 @@
|
|||||||
// 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,13 +23,8 @@ 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
|
||||||
name := self.source()
|
if v, exists = ctx.GetVar(self.tk.source); !exists {
|
||||||
if v, exists = ctx.GetVar(name); !exists {
|
err = fmt.Errorf("undefined variable %q", self.tk.source)
|
||||||
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.source(), v)
|
ctx.setVar(leftTerm.tk.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 functor, ok := rightValue.(Functor); ok {
|
if _, ok := rightValue.(Functor); ok {
|
||||||
ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
err = errCoalesceNoFunc(self.children[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,8 +4,6 @@
|
|||||||
// 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{
|
||||||
@ -25,13 +23,15 @@ 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)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
index64, _ := rightValue.(int64)
|
||||||
|
index := int(index64)
|
||||||
|
|
||||||
if isList(leftValue) {
|
if isList(leftValue) {
|
||||||
var index int
|
|
||||||
if index, err = indexTerm.toInt(rightValue, "index expression value must be integer"); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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,11 +41,6 @@ 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])
|
||||||
@ -54,12 +49,6 @@ 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)
|
||||||
}
|
}
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
// 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)
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
// 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,72 +58,23 @@ 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)
|
||||||
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.Errorf("exptected identifier, got %q", subTree.root)
|
|
||||||
}
|
|
||||||
} else if itemExpected {
|
|
||||||
prev := scanner.Previous()
|
|
||||||
err = prev.Errorf("expected function parameter, got %q", prev)
|
|
||||||
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.Errorf("unterminated function parameters list")
|
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tk = scanner.Next()
|
tk = scanner.Next()
|
||||||
@ -146,22 +97,16 @@ 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
|
||||||
@ -174,115 +119,6 @@ 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
|
||||||
@ -405,12 +241,6 @@ 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)
|
||||||
@ -421,12 +251,6 @@ 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,7 +7,6 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@ -118,9 +117,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 or function "x"`)},
|
/* 99 */ {`false and (x=2 but x==2) or x==2`, nil, errors.New(`undefined variable "x"`)},
|
||||||
/* 100 */ {`false or true`, true, nil},
|
/* 100 */ {`false or true`, true, nil},
|
||||||
/* 101 */ {`false or (x==2)`, nil, errors.New(`undefined variable or function "x"`)},
|
/* 101 */ {`false or (x==2)`, nil, errors.New(`undefined variable "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`)},
|
||||||
@ -135,13 +134,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 */ {`y=x ?? func(){4}; y()`, int64(4), nil},
|
/* 116 */ {`x ?? func(){}`, nil, errors.New(`[1:14] the right operand of a coalescing operation cannot be a function definition`)},
|
||||||
/* 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 or function "x"`)},
|
/* 122 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, errors.New(`undefined variable "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},
|
||||||
@ -158,11 +157,7 @@ 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 or function "null"`)},
|
/* 139 */ {`null`, nil, errors.New(`undefined variable "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
|
||||||
|
|
||||||
@ -170,7 +165,7 @@ func TestParser(t *testing.T) {
|
|||||||
failed := 0
|
failed := 0
|
||||||
|
|
||||||
// inputs1 := []inputType{
|
// inputs1 := []inputType{
|
||||||
// /* 140 */ {`ds={}; $(ds)`, nil, nil},
|
// /* 140 */ {`isNil(nil)`, true, nil},
|
||||||
// }
|
// }
|
||||||
|
|
||||||
for i, input := range inputs {
|
for i, input := range inputs {
|
||||||
@ -195,9 +190,7 @@ func TestParser(t *testing.T) {
|
|||||||
gotResult, gotErr = expr.Eval(ctx)
|
gotResult, gotErr = expr.Eval(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
eq := reflect.DeepEqual(gotResult, input.wantResult)
|
if 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
|
||||||
}
|
}
|
||||||
@ -228,6 +221,10 @@ 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},
|
||||||
@ -247,10 +244,6 @@ 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
|
||||||
|
19
scanner.go
19
scanner.go
@ -86,14 +86,13 @@ 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 == '+' {
|
||||||
@ -144,8 +143,6 @@ 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)
|
||||||
@ -239,18 +236,9 @@ 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.makeToken(SymDollar, ch)
|
||||||
tk = self.moveOn(SymDollarRound, ch, next)
|
|
||||||
tk.source += ")"
|
|
||||||
} else {
|
|
||||||
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 '[':
|
||||||
@ -283,9 +271,6 @@ 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, exists bool) {
|
func (ctx *SimpleFuncStore) GetFuncInfo(name string) (info ExprFunc) {
|
||||||
info, exists = ctx.funcStore[name]
|
info, _ = 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, exists bool) {
|
func (ctx *SimpleVarStore) GetFuncInfo(name string) (f ExprFunc) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,9 +61,6 @@ 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
|
||||||
@ -71,7 +68,6 @@ const (
|
|||||||
SymInteger
|
SymInteger
|
||||||
SymFloat
|
SymFloat
|
||||||
SymString
|
SymString
|
||||||
SymIterator
|
|
||||||
SymOr
|
SymOr
|
||||||
SymAnd
|
SymAnd
|
||||||
SymNot
|
SymNot
|
||||||
@ -79,7 +75,6 @@ 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,9 +23,7 @@ const (
|
|||||||
priSelector
|
priSelector
|
||||||
priSign
|
priSign
|
||||||
priFact
|
priFact
|
||||||
priIterValue
|
|
||||||
priCoalesce
|
priCoalesce
|
||||||
priPrePost
|
|
||||||
priDot
|
priDot
|
||||||
priValue
|
priValue
|
||||||
)
|
)
|
||||||
@ -129,10 +127,6 @@ 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
|
||||||
}
|
}
|
||||||
@ -150,15 +144,6 @@ 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