Compare commits
No commits in common. "723976b37e65e1f6807f5e6496d6d492bacf9133" and "62e16219f71c9441cffc7706c1833e8c2f48986e" have entirely different histories.
723976b37e
...
62e16219f7
@ -1,41 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// context-helpers.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
func cloneContext(sourceCtx ExprContext) (clonedCtx ExprContext) {
|
|
||||||
if sourceCtx != nil {
|
|
||||||
clonedCtx = sourceCtx.Clone()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportVar(ctx ExprContext, name string, value any) {
|
|
||||||
if name[0] == '@' {
|
|
||||||
name = name[1:]
|
|
||||||
}
|
|
||||||
ctx.setVar(name, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportFunc(ctx ExprContext, name string, info ExprFunc) {
|
|
||||||
if name[0] == '@' {
|
|
||||||
name = name[1:]
|
|
||||||
}
|
|
||||||
ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
|
|
||||||
}
|
|
||||||
|
|
||||||
func exportObjects(destCtx, sourceCtx ExprContext) {
|
|
||||||
exportAll := isEnabled(sourceCtx, control_export_all)
|
|
||||||
// Export variables
|
|
||||||
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) {
|
|
||||||
refValue, _ := sourceCtx.GetVar(refName)
|
|
||||||
exportVar(destCtx, refName, refValue)
|
|
||||||
}
|
|
||||||
// Export functions
|
|
||||||
for _, refName := range sourceCtx.EnumFuncs(func(name string) bool { return exportAll || name[0] == '@' }) {
|
|
||||||
if info, _ := sourceCtx.GetFuncInfo(refName); info != nil {
|
|
||||||
exportFunc(destCtx, refName, info)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// data-cursors.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
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 newDataCursor(ctx ExprContext) (dc *dataCursor) {
|
|
||||||
dc = &dataCursor{
|
|
||||||
index: -1,
|
|
||||||
ctx: ctx.Clone(),
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *dataCursor) String() string {
|
|
||||||
return "$(...)"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
|
|
||||||
ctx := cloneContext(dc.ctx)
|
|
||||||
if item, err = dc.currentFunc.Invoke(ctx, currentName, []any{}); err == nil && item == nil {
|
|
||||||
err = io.EOF
|
|
||||||
}
|
|
||||||
exportObjects(dc.ctx, ctx)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item
|
|
||||||
ctx := cloneContext(dc.ctx)
|
|
||||||
if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{}); err == nil {
|
|
||||||
if item == nil {
|
|
||||||
err = io.EOF
|
|
||||||
} else {
|
|
||||||
dc.index++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exportObjects(dc.ctx, ctx)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dc *dataCursor) Index() int {
|
|
||||||
return dc.index
|
|
||||||
}
|
|
19
expr_test.go
19
expr_test.go
@ -27,22 +27,11 @@ func TestExpr(t *testing.T) {
|
|||||||
succeeded := 0
|
succeeded := 0
|
||||||
failed := 0
|
failed := 0
|
||||||
|
|
||||||
inputs1 := []inputType{
|
// inputs1 := []inputType{
|
||||||
/* 1 */ {`
|
// /* 1 */ {`0?{}`, nil, nil},
|
||||||
ds={
|
// }
|
||||||
"init":func(end){@end=end; @current=0 but true},
|
|
||||||
"current":func(){current},
|
|
||||||
"next":func(){
|
|
||||||
((next=current+1) <= end) ? [true] {@current=next but current} :: {nil}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
it=$(ds,3);
|
|
||||||
it++;
|
|
||||||
it++
|
|
||||||
`, int64(1), nil},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, input := range inputs1 {
|
for i, input := range inputs {
|
||||||
var expr Expr
|
var expr Expr
|
||||||
var gotResult any
|
var gotResult any
|
||||||
var gotErr error
|
var gotErr error
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
// -------- dict term
|
// -------- dict term
|
||||||
|
// func newDictTermA(args ...*term) *term {
|
||||||
|
// return newDictTerm(args)
|
||||||
|
// }
|
||||||
|
|
||||||
func newDictTerm(args map[any]*term) *term {
|
func newDictTerm(args map[any]*term) *term {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *NewValueToken(0, 0, SymDict, "{}", args),
|
tk: *NewValueToken(0, 0, SymDict, "{}", args),
|
||||||
|
@ -27,3 +27,8 @@ func evalExpr(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
// func init() {
|
||||||
|
// registerTermConstructor(SymExpression, newExprTerm)
|
||||||
|
// }
|
||||||
|
@ -22,7 +22,7 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
|
|||||||
|
|
||||||
// -------- eval func call
|
// -------- eval func call
|
||||||
func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
|
func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
|
||||||
ctx := cloneContext(parentCtx)
|
ctx := parentCtx.Clone()
|
||||||
name, _ := self.tk.Value.(string)
|
name, _ := self.tk.Value.(string)
|
||||||
params := make([]any, len(self.children))
|
params := make([]any, len(self.children))
|
||||||
for i, tree := range self.children {
|
for i, tree := range self.children {
|
||||||
@ -34,12 +34,37 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if v, err = ctx.Call(name, params); err == nil {
|
if v, err = ctx.Call(name, params); err == nil {
|
||||||
exportObjects(parentCtx, ctx)
|
exportAll := isEnabled(ctx, control_export_all)
|
||||||
|
// Export variables
|
||||||
|
for _, refName := range ctx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) {
|
||||||
|
refValue, _ := ctx.GetVar(refName)
|
||||||
|
exportVar(parentCtx, refName, refValue)
|
||||||
|
}
|
||||||
|
// Export functions
|
||||||
|
for _, refName := range ctx.EnumFuncs(func(name string) bool { return exportAll || name[0] == '@' }) {
|
||||||
|
if info, _ := ctx.GetFuncInfo(refName); info != nil {
|
||||||
|
exportFunc(parentCtx, refName, info)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func exportVar(ctx ExprContext, name string, value any) {
|
||||||
|
if name[0] == '@' {
|
||||||
|
name = name[1:]
|
||||||
|
}
|
||||||
|
ctx.setVar(name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exportFunc(ctx ExprContext, name string, info ExprFunc) {
|
||||||
|
if name[0] == '@' {
|
||||||
|
name = name[1:]
|
||||||
|
}
|
||||||
|
ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
|
||||||
|
}
|
||||||
|
|
||||||
// -------- function definition term
|
// -------- function definition term
|
||||||
func newFuncDefTerm(tk *Token, args []*term) *term {
|
func newFuncDefTerm(tk *Token, args []*term) *term {
|
||||||
return &term{
|
return &term{
|
||||||
@ -82,6 +107,12 @@ func evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
paramList := make([]string, 0, len(self.children))
|
paramList := make([]string, 0, len(self.children))
|
||||||
for _, param := range self.children {
|
for _, param := range self.children {
|
||||||
paramList = append(paramList, param.source())
|
paramList = append(paramList, param.source())
|
||||||
|
// if paramName, ok := param.value().(string); ok {
|
||||||
|
// paramList = append(paramList, paramName)
|
||||||
|
// } else {
|
||||||
|
// err = fmt.Errorf("invalid function definition: formal param nr %d must be an identifier", i+1)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
v = &funcDefFunctor{
|
v = &funcDefFunctor{
|
||||||
params: paramList,
|
params: paramList,
|
||||||
|
@ -6,10 +6,58 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- iterator term
|
// -------- 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 {
|
func newIteratorTerm(tk *Token, dsTerm *term, args []*term) *term {
|
||||||
tk.Sym = SymIterator
|
tk.Sym = SymIterator
|
||||||
|
|
||||||
@ -77,7 +125,10 @@ func evalIterator(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
dc := newDataCursor(ctx)
|
dc := &dataCursor{
|
||||||
|
index: -1,
|
||||||
|
ctx: ctx.Clone(),
|
||||||
|
}
|
||||||
|
|
||||||
if initFunc, exists := ds[initName]; exists && initFunc != nil {
|
if initFunc, exists := ds[initName]; exists && initFunc != nil {
|
||||||
var args []any
|
var args []any
|
||||||
@ -88,12 +139,9 @@ func evalIterator(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
} else {
|
} else {
|
||||||
args = []any{}
|
args = []any{}
|
||||||
}
|
}
|
||||||
|
if dc.resource, err = initFunc.Invoke(dc.ctx, initName, args); err != nil {
|
||||||
initCtx := dc.ctx.Clone()
|
|
||||||
if dc.resource, err = initFunc.Invoke(initCtx, initName, args); err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
exportObjects(dc.ctx, initCtx)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dc.nextFunc, _ = ds[nextName]
|
dc.nextFunc, _ = ds[nextName]
|
||||||
|
@ -1,49 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-context-value.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
//-------- context term
|
|
||||||
|
|
||||||
func newContextTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 1),
|
|
||||||
position: posPrefix,
|
|
||||||
priority: priPrePost,
|
|
||||||
evalFunc: evalContextValue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalContextValue(ctx ExprContext, self *term) (v any, err error) {
|
|
||||||
var childValue any
|
|
||||||
|
|
||||||
var sourceCtx ExprContext
|
|
||||||
if len(self.children) == 0 {
|
|
||||||
sourceCtx = ctx
|
|
||||||
} else if childValue, err = self.evalPrefix(ctx); err == nil {
|
|
||||||
if dc, ok := childValue.(*dataCursor); ok {
|
|
||||||
sourceCtx = dc.ctx
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if sourceCtx != nil {
|
|
||||||
keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
|
|
||||||
d := make(map[string]any)
|
|
||||||
for _, key := range keys {
|
|
||||||
d[key], _ = sourceCtx.GetVar(key)
|
|
||||||
}
|
|
||||||
v = d
|
|
||||||
} else {
|
|
||||||
err = self.errIncompatibleType(childValue)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymDoubleDollar, newContextTerm)
|
|
||||||
}
|
|
@ -29,8 +29,9 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
} else if isString(rightValue) {
|
} else if isString(rightValue) {
|
||||||
s, _ := rightValue.(string)
|
s, _ := rightValue.(string)
|
||||||
v = len(s)
|
v = len(s)
|
||||||
} else if it, ok := rightValue.(Iterator); ok {
|
// } else {
|
||||||
v = it.Index()
|
// v = 1
|
||||||
|
// }
|
||||||
} else {
|
} else {
|
||||||
err = self.errIncompatibleType(rightValue)
|
err = self.errIncompatibleType(rightValue)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,65 @@
|
|||||||
// operator-selector.go
|
// operator-selector.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
//-------- selector term
|
// //-------- export all term
|
||||||
|
|
||||||
|
// func newSelectorTerm(tk *Token) (inst *term) {
|
||||||
|
// return &term{
|
||||||
|
// tk: *tk,
|
||||||
|
// children: make([]*term, 0, 3),
|
||||||
|
// position: posMultifix,
|
||||||
|
// priority: priSelector,
|
||||||
|
// evalFunc: evalSelector,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func isSelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (selectedValue any, err error) {
|
||||||
|
// caseData, _ := caseSel.(*selectorCase)
|
||||||
|
// if caseData.filterList == nil {
|
||||||
|
// selectedValue, err = caseData.caseExpr.eval(ctx, false)
|
||||||
|
// } else {
|
||||||
|
// filterList := caseData.filterList.children
|
||||||
|
// if len(filterList) == 0 && exprValue == int64(caseIndex) {
|
||||||
|
// selectedValue, err = caseData.caseExpr.eval(ctx, false)
|
||||||
|
// } else {
|
||||||
|
// var caseValue any
|
||||||
|
// for _, caseTerm := range filterList {
|
||||||
|
// if caseValue, err = caseTerm.compute(ctx); err != nil || caseValue == exprValue {
|
||||||
|
// selectedValue, err = caseData.caseExpr.eval(ctx, false)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func evalSelector(ctx ExprContext, self *term) (v any, err error) {
|
||||||
|
// var exprValue any
|
||||||
|
// // var caseList []*term
|
||||||
|
|
||||||
|
// if err = self.checkOperands(); err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// exprTerm := self.children[0]
|
||||||
|
// if exprValue, err = exprTerm.compute(ctx); err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// caseList := self.children[1:]
|
||||||
|
// for i, caseTerm := range caseList {
|
||||||
|
// caseSel := caseTerm.value()
|
||||||
|
// if v, err = isSelectorCase(ctx, exprValue, caseSel, i); err != nil || v != nil {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if err == nil && v == nil {
|
||||||
|
// err = exprTerm.tk.Errorf("no case catches the value (%v) of the selection expression", exprValue)
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
//-------- export all term
|
||||||
|
|
||||||
func newSelectorTerm(tk *Token) (inst *term) {
|
func newSelectorTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
@ -16,7 +74,7 @@ func newSelectorTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (match bool, selectedValue any, err error) {
|
func isSelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (match bool, selectedValue any, err error) {
|
||||||
caseData, _ := caseSel.(*selectorCase)
|
caseData, _ := caseSel.(*selectorCase)
|
||||||
if caseData.filterList == nil {
|
if caseData.filterList == nil {
|
||||||
selectedValue, err = caseData.caseExpr.eval(ctx, false)
|
selectedValue, err = caseData.caseExpr.eval(ctx, false)
|
||||||
@ -55,7 +113,7 @@ func evalSelector(ctx ExprContext, self *term) (v any, err error) {
|
|||||||
caseList, _ := caseListTerm.value().([]*term)
|
caseList, _ := caseListTerm.value().([]*term)
|
||||||
for i, caseTerm := range caseList {
|
for i, caseTerm := range caseList {
|
||||||
caseSel := caseTerm.value()
|
caseSel := caseTerm.value()
|
||||||
if match, v, err = trySelectorCase(ctx, exprValue, caseSel, i); err != nil || match {
|
if match, v, err = isSelectorCase(ctx, exprValue, caseSel, i); err != nil || match {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,8 +242,6 @@ func (self *scanner) fetchNextToken() (tk *Token) {
|
|||||||
if next, _ := self.peek(); next == '(' {
|
if next, _ := self.peek(); next == '(' {
|
||||||
tk = self.moveOn(SymDollarRound, ch, next)
|
tk = self.moveOn(SymDollarRound, ch, next)
|
||||||
tk.source += ")"
|
tk.source += ")"
|
||||||
} else if next == '$' {
|
|
||||||
tk = self.moveOn(SymDoubleDollar, ch, next)
|
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeToken(SymDollar, ch)
|
tk = self.makeToken(SymDollar, ch)
|
||||||
}
|
}
|
||||||
@ -251,8 +249,8 @@ func (self *scanner) fetchNextToken() (tk *Token) {
|
|||||||
if next, _ := self.peek(); next == ')' {
|
if next, _ := self.peek(); next == ')' {
|
||||||
tk = self.moveOn(SymOpenClosedRound, ch, next)
|
tk = self.moveOn(SymOpenClosedRound, ch, next)
|
||||||
} else {
|
} 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 '[':
|
||||||
|
@ -63,8 +63,7 @@ const (
|
|||||||
SymAppend // 52: '<<'
|
SymAppend // 52: '<<'
|
||||||
SymCaret // 53: '^'
|
SymCaret // 53: '^'
|
||||||
SymDollarRound // 54: '$('
|
SymDollarRound // 54: '$('
|
||||||
SymOpenClosedRound // 55: '()'
|
SymOpenClosedRound // 55: '()'
|
||||||
SymDoubleDollar // 56: '$$
|
|
||||||
SymChangeSign
|
SymChangeSign
|
||||||
SymUnchangeSign
|
SymUnchangeSign
|
||||||
SymIdentifier
|
SymIdentifier
|
||||||
|
Loading…
Reference in New Issue
Block a user