Compare commits

..

52 Commits

Author SHA1 Message Date
camoroso 79889cd8e1 New builtin module 'iterator' 2024-07-14 16:53:32 +02:00
camoroso 234759158c scanner: disabled prefix operator '()' 2024-07-13 18:10:04 +02:00
camoroso 00fda29606 operator-iter-value.go: prefix operator () used to get the current value of an iterator replaced by ^ 2024-07-13 17:17:16 +02:00
camoroso 2a2840bdf2 ListIterator now implements next and current command (e.g it.next) 2024-07-13 17:15:53 +02:00
camoroso 06373f5126 Impemented Typer interface 2024-07-13 17:14:25 +02:00
camoroso e69dad5fb5 Rename priority label priCoalesce as priDefault 2024-07-13 17:13:16 +02:00
camoroso 7459057df9 Expr embeds Typer and ast implements it 2024-07-13 17:11:39 +02:00
camoroso 176969c956 New iterator tests 2024-07-13 16:19:04 +02:00
camoroso cde2efacf1 Test on iterator filter and map 2024-07-13 15:30:04 +02:00
camoroso d7a7b3218c Merge branch 'main' into enhance_iterators 2024-07-13 09:08:16 +02:00
camoroso be3bb12f28 operator-unset.go: fixed function removal 2024-07-13 09:07:33 +02:00
camoroso 032916d4fa New operator unset to delete variables and functions from current context 2024-07-13 09:01:59 +02:00
camoroso f3cc0cc7ad operator-include.go: Fixed inclusion of a list of files.
Now returns the value of the last evaluated expression.
2024-07-13 09:00:53 +02:00
camoroso 905337f963 ExprContext: new functions VarCount(), DeleteVar(), FuncCount(), DeleteFunc() 2024-07-13 08:59:15 +02:00
camoroso 9a95a837f6 data-cursor.go: add item mapping support 2024-07-13 06:44:00 +02:00
camoroso 8547248ea2 data-cursor.go: fixed filter 2024-07-13 00:12:08 +02:00
camoroso a02f998fc6 t_iterator_test.go: Fixed test numbering and add a commentend test that is not yet fulfilled 2024-07-11 07:23:35 +02:00
camoroso e904003e6e minor changes 2024-07-11 06:53:14 +02:00
camoroso 990e04f957 operator-assign.go -- Fix: Assigning a functor to a collection's item didn't work 2024-07-11 06:49:02 +02:00
camoroso d8aed9dd7a t_iterator_test.go: added a test to verify the reset command of the list iterator 2024-07-11 05:54:22 +02:00
camoroso a73d24b171 iter-list.go -> list-iterator.go
Fix: the reset command set the initial item to the second one in the list
2024-07-11 05:52:47 +02:00
camoroso 3ebba83bce term.go: replaced self receiver 2024-07-09 07:50:50 +02:00
camoroso 6b3bfa2a11 self param replaced as opTerm 2024-07-09 07:50:06 +02:00
camoroso 867806155e scanner.go: replaced self receiver 2024-07-08 07:30:58 +02:00
camoroso a711333a2e simple-store.go: commented out an unused function 2024-07-08 07:30:26 +02:00
camoroso af3e946bd4 operator-sum.go: replaced self receiver 2024-07-08 07:29:42 +02:00
camoroso 22a36fa630 context.go renamed as expr-context.go 2024-07-07 16:20:29 +02:00
camoroso 6d9a379c92 token.go: replaced self receiver 2024-07-07 16:19:58 +02:00
camoroso dd6404c786 t_scanner_test.go: replaced t.Log(fmtStringf()) with t.Logf() 2024-07-07 16:18:39 +02:00
camoroso 34874ef663 plugins.go: replaced stringsIndex()<0 with !strings.Contains() 2024-07-07 16:17:48 +02:00
camoroso 9bc8e8ca05 operator-sum.go: replaced for-loop with append() 2024-07-07 16:15:56 +02:00
camoroso 7f367cfc49 parser.go: renamed self receiver 2024-07-07 16:14:52 +02:00
camoroso fe999acf2c operator-index.go: removed unused parameter from the function verifyKey() 2024-07-07 16:14:04 +02:00
camoroso 2ed1a1842b operand-iterator.go: commented out an unused function and replaced self receiver 2024-07-07 16:10:43 +02:00
camoroso bb9493d0cc list-type.go: commented out an unused fuction 2024-07-07 16:08:45 +02:00
camoroso f279bf163e import-utils.go: commented out unused fuctions 2024-07-07 15:59:23 +02:00
camoroso 6834d9f47b function.go: removed useless param != nil check 2024-07-07 15:58:29 +02:00
camoroso 8051faa2bf fraction-type.go: use of strings.TrimSuffix() in place of check suffix and slice 2024-07-07 15:57:17 +02:00
camoroso f30e687a79 changed the file name comment 2024-07-07 15:55:51 +02:00
camoroso 2b6e46576b byte-slider.go: renamed function receiver from self to slider 2024-07-07 15:54:26 +02:00
camoroso dc06c03112 builtin-string.go: removed useless err == nil check 2024-07-07 15:53:29 +02:00
camoroso e8f5d3e445 builtin-base.go: unused function iteratorFunc() commented out 2024-07-07 15:52:16 +02:00
camoroso 76ce0945f7 context.go splitted in two files: expr-context.go and expr-function-go.
Expr interface moved from ast.go to the new file expr.go
2024-07-07 15:51:29 +02:00
camoroso 340b99bad7 list-type.go: use of copy() for copying lists 2024-07-07 07:34:58 +02:00
camoroso 93dac956fb t_parser_test.go: more tests on ??, ?= and ?! operators 2024-07-06 17:01:23 +02:00
camoroso 307027d23d t_parser_test.go: changed all error values to string values 2024-07-06 16:47:00 +02:00
camoroso 896844e772 the common test framework now supports error, string and nil as value of the wantErr field 2024-07-06 16:43:13 +02:00
camoroso 9fc20077a1 operator-include.go: the include operator did not count the number of files included when its argument was a single string 2024-07-06 16:08:58 +02:00
camoroso d2a4adebdd dataCursor implements Typer interface 2024-07-06 16:05:54 +02:00
camoroso fd8e32e12b new operator "?!" (alternate value) 2024-07-06 05:54:53 +02:00
camoroso 1e62a51c15 t_fractions_test.go: added a test on the string() function 2024-07-06 04:56:00 +02:00
camoroso 0170baa0f5 iter-list.go: implemented the Typer interface 2024-07-06 04:54:42 +02:00
78 changed files with 1290 additions and 872 deletions
+35 -36
View File
@@ -8,11 +8,6 @@ import (
"strings" "strings"
) )
type Expr interface {
Eval(ctx ExprContext) (result any, err error)
String() string
}
//-------- ast //-------- ast
type ast struct { type ast struct {
@@ -24,65 +19,69 @@ func NewAst() *ast {
return &ast{} return &ast{}
} }
func (self *ast) ToForest() { func (expr *ast) TypeName() string {
if self.root != nil { return "Expression"
if self.forest == nil { }
self.forest = make([]*term, 0)
func (expr *ast) ToForest() {
if expr.root != nil {
if expr.forest == nil {
expr.forest = make([]*term, 0)
} }
self.forest = append(self.forest, self.root) expr.forest = append(expr.forest, expr.root)
self.root = nil expr.root = nil
} }
} }
func (self *ast) String() string { func (expr *ast) String() string {
var sb strings.Builder var sb strings.Builder
if self.root == nil { if expr.root == nil {
sb.WriteString("(nil)") sb.WriteString("(nil)")
} else { } else {
self.root.toString(&sb) expr.root.toString(&sb)
} }
return sb.String() return sb.String()
} }
func (self *ast) addTokens(tokens ...*Token) (err error) { func (expr *ast) addTokens(tokens ...*Token) (err error) {
for _, tk := range tokens { for _, tk := range tokens {
if err = self.addToken(tk); err != nil { if err = expr.addToken(tk); err != nil {
break break
} }
} }
return return
} }
func (self *ast) addToken(tk *Token) (err error) { func (expr *ast) addToken(tk *Token) (err error) {
_, err = self.addToken2(tk) _, err = expr.addToken2(tk)
return return
} }
func (self *ast) addToken2(tk *Token) (t *term, err error) { func (expr *ast) addToken2(tk *Token) (t *term, err error) {
if t = newTerm(tk); t != nil { if t = newTerm(tk); t != nil {
err = self.addTerm(t) err = expr.addTerm(t)
} else { } else {
err = tk.Errorf("unexpected token %q", tk.String()) err = tk.Errorf("unexpected token %q", tk.String())
} }
return return
} }
func (self *ast) addTerm(node *term) (err error) { func (expr *ast) addTerm(node *term) (err error) {
if self.root == nil { if expr.root == nil {
self.root = node expr.root = node
} else { } else {
self.root, err = self.insert(self.root, node) expr.root, err = expr.insert(expr.root, node)
} }
return return
} }
func (self *ast) insert(tree, node *term) (root *term, err error) { func (expr *ast) insert(tree, node *term) (root *term, err error) {
if tree.getPriority() < node.getPriority() { if tree.getPriority() < node.getPriority() {
root = tree root = tree
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 { if subRoot, err = expr.insert(last, node); err == nil {
subRoot.setParent(tree) subRoot.setParent(tree)
} }
} else { } else {
@@ -97,20 +96,20 @@ func (self *ast) insert(tree, node *term) (root *term, err error) {
return return
} }
func (self *ast) Finish() { func (expr *ast) Finish() {
if self.root == nil && self.forest != nil && len(self.forest) >= 1 { if expr.root == nil && expr.forest != nil && len(expr.forest) >= 1 {
self.root = self.forest[len(self.forest)-1] expr.root = expr.forest[len(expr.forest)-1]
self.forest = self.forest[0 : len(self.forest)-1] expr.forest = expr.forest[0 : len(expr.forest)-1]
} }
} }
func (self *ast) Eval(ctx ExprContext) (result any, err error) { func (expr *ast) Eval(ctx ExprContext) (result any, err error) {
self.Finish() expr.Finish()
if self.root != nil { if expr.root != nil {
// initDefaultVars(ctx) // initDefaultVars(ctx)
if self.forest != nil { if expr.forest != nil {
for _, root := range self.forest { for _, root := range expr.forest {
if result, err = root.compute(ctx); err == nil { if result, err = root.compute(ctx); err == nil {
ctx.UnsafeSetVar(ControlLastResult, result) ctx.UnsafeSetVar(ControlLastResult, result)
} else { } else {
@@ -120,7 +119,7 @@ func (self *ast) Eval(ctx ExprContext) (result any, err error) {
} }
} }
if err == nil { if err == nil {
if result, err = self.root.compute(ctx); err == nil { if result, err = expr.root.compute(ctx); err == nil {
ctx.UnsafeSetVar(ControlLastResult, result) ctx.UnsafeSetVar(ControlLastResult, result)
} }
} }
+4
View File
@@ -29,6 +29,10 @@ func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFuncto
return &exprFunctor{expr: e, params: params, defCtx: defCtx} return &exprFunctor{expr: e, params: params, defCtx: defCtx}
} }
func (functor *exprFunctor) TypeName() string {
return "ExprFunctor"
}
func (functor *exprFunctor) GetDefinitionContext() ExprContext { func (functor *exprFunctor) GetDefinitionContext() ExprContext {
return functor.defCtx return functor.defCtx
} }
+4
View File
@@ -14,6 +14,10 @@ func NewGolangFunctor(f FuncTemplate) *golangFunctor {
return &golangFunctor{f: f} return &golangFunctor{f: f}
} }
func (functor *golangFunctor) TypeName() string {
return "GoFunctor"
}
func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) { func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
return functor.f(ctx, name, args) return functor.f(ctx, name, args)
} }
+3 -3
View File
@@ -180,9 +180,9 @@ func fractFunc(ctx ExprContext, name string, args []any) (result any, err error)
return return
} }
func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err error) { // func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err error) {
return // return
} // }
func ImportBuiltinsFuncs(ctx ExprContext) { func ImportBuiltinsFuncs(ctx ExprContext) {
anyParams := []ExprFuncParam{ anyParams := []ExprFuncParam{
+102
View File
@@ -0,0 +1,102 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// builtin-iterator.go
package expr
import (
"fmt"
"io"
)
const (
iterParamOperator = "operator"
iterParamVars = "vars"
)
func parseRunArgs(localCtx ExprContext, args []any) (it Iterator, op Functor, err error) {
var ok bool
if it, ok = args[0].(Iterator); !ok {
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", ParamIterator, args[0], TypeName(args[0]))
return
}
if len(args) > 1 {
if op, ok = args[1].(Functor); !ok || op == nil {
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[1], TypeName(args[1]))
return
}
if len(args) > 2 {
var vars *DictType
if vars, ok = args[2].(*DictType); !ok || vars == nil {
err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[2], TypeName(args[2]))
return
}
for key, value := range *vars {
var varName string
if varName, ok = key.(string); ok {
localCtx.UnsafeSetVar(varName, value)
}
}
}
}
return
}
func runFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var it Iterator
var ok bool
var op Functor
var v any
var usingDefaultOp = false
var params []any
var item any
localCtx := ctx.Clone()
localCtx.UnsafeSetVar("it_status", nil)
if it, op, err = parseRunArgs(localCtx, args); err != nil {
return
} else if op == nil {
op = NewGolangFunctor(printLnFunc)
usingDefaultOp = true
}
for item, err = it.Next(); err == nil; item, err = it.Next() {
if usingDefaultOp {
params = []any{item}
} else {
params = []any{it.Index(), item}
}
if v, err = op.Invoke(localCtx, iterParamOperator, params); err != nil {
break
} else {
var success bool
if success, ok = ToBool(v); !success || !ok {
break
}
}
}
if err == io.EOF {
err = nil
}
if err == nil {
result, _ = localCtx.GetVar("it_status")
}
return
}
func ImportIterFuncs(ctx ExprContext) {
ctx.RegisterFunc("run", NewGolangFunctor(runFunc), TypeAny, []ExprFuncParam{
NewFuncParam(ParamIterator),
NewFuncParamFlag(iterParamOperator, PfOptional),
NewFuncParamFlag(iterParamVars, PfOptional),
})
}
func init() {
RegisterBuiltinModule("iterator", ImportIterFuncs, "Iterator helper functions")
}
+1 -1
View File
@@ -25,7 +25,7 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
return return
} }
} }
if err == nil || err == io.EOF { if err == io.EOF {
err = nil err = nil
result = sb.String() result = sb.String()
} }
+9 -9
View File
@@ -18,17 +18,17 @@ func NewByteSlider(size int) *ByteSlider {
} }
} }
func (self *ByteSlider) PushEnd(b byte) { func (slider *ByteSlider) PushEnd(b byte) {
if self.length == cap(self.buf) { if slider.length == cap(slider.buf) {
self.length-- slider.length--
for i := 0; i < self.length; i++ { for i := 0; i < slider.length; i++ {
self.buf[i] = self.buf[i+1] slider.buf[i] = slider.buf[i+1]
} }
} }
self.buf[self.length] = b slider.buf[slider.length] = b
self.length++ slider.length++
} }
func (self *ByteSlider) Equal(target []byte) bool { func (slider *ByteSlider) Equal(target []byte) bool {
return target != nil && bytes.Equal(self.buf, target) return target != nil && bytes.Equal(slider.buf, target)
} }
+6
View File
@@ -53,3 +53,9 @@ func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error { func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue) return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue)
} }
// --- Operator errors
func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error {
return leftTerm.Errorf("left operand of %q must be a variable", opTerm.source())
}
+1
View File
@@ -19,6 +19,7 @@ const (
ParamEllipsis = "..." ParamEllipsis = "..."
ParamFilepath = "filepath" ParamFilepath = "filepath"
ParamDirpath = "dirpath" ParamDirpath = "dirpath"
ParamIterator = "iterator"
) )
// to be moved in its own source file // to be moved in its own source file
+66 -3
View File
@@ -29,6 +29,10 @@ func newDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) {
return return
} }
func (dc *dataCursor) TypeName() string {
return "DataCursor"
}
// func mapToString(m map[string]Functor) string { // func mapToString(m map[string]Functor) string {
// var sb strings.Builder // var sb strings.Builder
// sb.WriteByte('{') // sb.WriteByte('{')
@@ -129,20 +133,79 @@ func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at
return return
} }
// func (dc *dataCursor) _Next() (item any, err error) { // must return io.EOF after the last item
// if dc.resource != nil {
// ctx := cloneContext(dc.ctx)
// // fmt.Printf("Entering Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
// if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil {
// if item == nil {
// err = io.EOF
// } else {
// dc.index++
// }
// }
// // fmt.Printf("Exiting Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
// exportObjects(dc.ctx, ctx)
// // fmt.Printf("Outer-Ctx [%p]: %s\n", dc.ctx, CtxToString(dc.ctx, 0))
// } else {
// err = errInvalidDataSource()
// }
// return
// }
// func (dc *dataCursor) _filter(item any) (filterdItem any, err error) {
// if filter, ok := dc.ds[filterName]; ok {
// ctx := cloneContext(dc.ctx)
// filterdItem, err = filter.Invoke(ctx, filterName, []any{item, dc.index})
// } else {
// filterdItem = item
// }
// return
// }
func (dc *dataCursor) checkFilter(filter Functor, item any) (accepted bool, err error) {
var v any
var ok bool
ctx := cloneContext(dc.ctx)
if v, err = filter.Invoke(ctx, filterName, []any{item, dc.index}); err == nil && v != nil {
if accepted, ok = v.(bool); !ok {
accepted = true // NOTE: A non-boolean value that is not nil means the item has been accepted
}
}
return
}
func (dc *dataCursor) mapItem(mapper Functor, item any) (mappedItem any, err error) {
ctx := cloneContext(dc.ctx)
mappedItem, err = mapper.Invoke(ctx, mapName, []any{item, dc.index});
return
}
func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item
var accepted bool
if dc.resource != nil { if dc.resource != nil {
filter := dc.ds[filterName]
mapper := dc.ds[mapName]
for item == nil && err == nil {
ctx := cloneContext(dc.ctx) ctx := cloneContext(dc.ctx)
// fmt.Printf("Entering Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil { if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil {
if item == nil { if item == nil {
err = io.EOF err = io.EOF
} else { } else {
dc.index++ dc.index++
if filter != nil {
if accepted, err = dc.checkFilter(filter, item); err != nil || !accepted {
item = nil
}
}
if item != nil && mapper != nil {
item, err = dc.mapItem(mapper, item)
}
} }
} }
// fmt.Printf("Exiting Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
exportObjects(dc.ctx, ctx) exportObjects(dc.ctx, ctx)
// fmt.Printf("Outer-Ctx [%p]: %s\n", dc.ctx, CtxToString(dc.ctx, 0)) }
} else { } else {
err = errInvalidDataSource() err = errInvalidDataSource()
} }
+30
View File
@@ -0,0 +1,30 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expr-context.go
package expr
// ----Expression Context
type ExprContext interface {
Clone() ExprContext
// Merge(ctx ExprContext)
SetParent(ctx ExprContext)
GetParent() (ctx ExprContext)
GetVar(varName string) (value any, exists bool)
GetLast() any
SetVar(varName string, value any)
UnsafeSetVar(varName string, value any)
EnumVars(func(name string) (accept bool)) (varNames []string)
VarCount() int
DeleteVar(varName string)
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
FuncCount() int
DeleteFunc(funcName string)
GetFuncInfo(name string) (item ExprFunc, exists bool)
Call(name string, args []any) (result any, err error)
RegisterFuncInfo(info ExprFunc)
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) error
}
+2 -19
View File
@@ -1,11 +1,12 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// context.go // expr-function.go
package expr package expr
// ---- Functor interface // ---- Functor interface
type Functor interface { type Functor interface {
Typer
Invoke(ctx ExprContext, name string, args []any) (result any, err error) Invoke(ctx ExprContext, name string, args []any) (result any, err error)
SetFunc(info ExprFunc) SetFunc(info ExprFunc)
GetFunc() ExprFunc GetFunc() ExprFunc
@@ -35,21 +36,3 @@ type ExprFunc interface {
PrepareCall(parentCtx ExprContext, name string, varParams *[]any) (ctx ExprContext, err error) PrepareCall(parentCtx ExprContext, name string, varParams *[]any) (ctx ExprContext, err error)
AllocContext(parentCtx ExprContext) (ctx ExprContext) AllocContext(parentCtx ExprContext) (ctx ExprContext)
} }
// ----Expression Context
type ExprContext interface {
Clone() ExprContext
// Merge(ctx ExprContext)
SetParent(ctx ExprContext)
GetParent() (ctx ExprContext)
GetVar(varName string) (value any, exists bool)
GetLast() any
SetVar(varName string, value any)
UnsafeSetVar(varName string, value any)
EnumVars(func(name string) (accept bool)) (varNames []string)
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
GetFuncInfo(name string) (item ExprFunc, exists bool)
Call(name string, args []any) (result any, err error)
RegisterFuncInfo(info ExprFunc)
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) error
}
+12
View File
@@ -0,0 +1,12 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expr.go
package expr
// ----Expression interface
type Expr interface {
Typer
Eval(ctx ExprContext) (result any, err error)
String() string
}
+4 -4
View File
@@ -50,9 +50,10 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
} else if s[0] == '+' { } else if s[0] == '+' {
s = s[1:] s = s[1:]
} }
if strings.HasSuffix(s, "()") { // if strings.HasSuffix(s, "()") {
s = s[0 : len(s)-2] // s = s[0 : len(s)-2]
} // }
s = strings.TrimSuffix(s, "()")
parts = strings.SplitN(s, ".", 2) parts = strings.SplitN(s, ".", 2)
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil { if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
return return
@@ -311,7 +312,6 @@ func divAnyFract(af1, af2 any) (quot any, err error) {
if f2.num == 0 { if f2.num == 0 {
err = errors.New("division by zero") err = errors.New("division by zero")
return return
return
} }
if f1.num == 0 || f2.den == 0 { if f1.num == 0 || f2.den == 0 {
quot = 0 quot = 0
+1 -2
View File
@@ -112,7 +112,6 @@ type funcInfo struct {
func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) { func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
var minArgs = 0 var minArgs = 0
var maxArgs = 0 var maxArgs = 0
if params != nil {
for _, p := range params { for _, p := range params {
if maxArgs == -1 { if maxArgs == -1 {
return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name()) return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
@@ -130,7 +129,7 @@ func newFuncInfo(name string, functor Functor, returnType string, params []ExprF
maxArgs = -1 maxArgs = -1
} }
} }
}
info = &funcInfo{ info = &funcInfo{
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params, name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params,
} }
+6 -6
View File
@@ -25,13 +25,13 @@ func checkStringParamExpected(funcName string, paramValue any, paramPos int) (er
return return
} }
func addSourceEnvImportDirs(varName string, dirList []string) []string { // func addSourceEnvImportDirs(varName string, dirList []string) []string {
return addEnvImportDirs(ENV_EXPR_SOURCE_PATH, dirList) // return addEnvImportDirs(ENV_EXPR_SOURCE_PATH, dirList)
} // }
func addPluginEnvImportDirs(varName string, dirList []string) []string { // func addPluginEnvImportDirs(varName string, dirList []string) []string {
return addEnvImportDirs(ENV_EXPR_PLUGIN_PATH, dirList) // return addEnvImportDirs(ENV_EXPR_PLUGIN_PATH, dirList)
} // }
func addEnvImportDirs(envVarName string, dirList []string) []string { func addEnvImportDirs(envVarName string, dirList []string) []string {
if dirSpec, exists := os.LookupEnv(envVarName); exists { if dirSpec, exists := os.LookupEnv(envVarName); exists {
+3
View File
@@ -19,9 +19,12 @@ const (
currentName = "current" currentName = "current"
indexName = "index" indexName = "index"
countName = "count" countName = "count"
filterName = "filter"
mapName = "map"
) )
type Iterator interface { type Iterator interface {
Typer
Next() (item any, err error) // must return io.EOF after the last item Next() (item any, err error) // must return io.EOF after the last item
Current() (item any, err error) Current() (item any, err error)
Index() int Index() int
+11 -3
View File
@@ -1,7 +1,7 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
// iter-list.go // list-iterator.go
package expr package expr
import ( import (
@@ -85,17 +85,25 @@ func (it *ListIterator) String() string {
return fmt.Sprintf("$(#%d)", l) return fmt.Sprintf("$(#%d)", l)
} }
func (it *ListIterator) TypeName() string {
return "ListIterator"
}
func (it *ListIterator) HasOperation(name string) bool { func (it *ListIterator) HasOperation(name string) bool {
yes := name == resetName || name == indexName || name == countName yes := name == nextName || name == resetName || name == indexName || name == countName || name == currentName
return yes return yes
} }
func (it *ListIterator) CallOperation(name string, args []any) (v any, err error) { func (it *ListIterator) CallOperation(name string, args []any) (v any, err error) {
switch name { switch name {
case nextName:
v, err = it.Next()
case resetName: case resetName:
v, err = it.Reset() v, err = it.Reset()
case indexName: case indexName:
v = int64(it.Index()) v = int64(it.Index())
case currentName:
v, err = it.Current()
case countName: case countName:
v = it.count v = it.count
default: default:
@@ -136,6 +144,6 @@ func (it *ListIterator) Index() int {
} }
func (it *ListIterator) Reset() (bool, error) { func (it *ListIterator) Reset() (bool, error) {
it.index = it.start it.index = it.start - it.step
return true, nil return true, nil
} }
+14 -13
View File
@@ -26,9 +26,10 @@ func newList(listAny []any) (list *ListType) {
func NewList(listAny []any) (list *ListType) { func NewList(listAny []any) (list *ListType) {
if listAny != nil { if listAny != nil {
ls := make(ListType, len(listAny)) ls := make(ListType, len(listAny))
for i, item := range listAny { // for i, item := range listAny {
ls[i] = item // ls[i] = item
} // }
copy(ls, listAny)
list = &ls list = &ls
} }
return return
@@ -105,16 +106,16 @@ func (ls *ListType) TypeName() string {
return "list" return "list"
} }
func (list *ListType) indexDeepCmp(target any) (index int) { // func (list *ListType) indexDeepCmp(target any) (index int) {
index = -1 // index = -1
for i, item := range *list { // for i, item := range *list {
if reflect.DeepEqual(item, target) { // if reflect.DeepEqual(item, target) {
index = i // index = i
break // break
} // }
} // }
return // return
} // }
func (ls *ListType) contains(t *ListType) (answer bool) { func (ls *ListType) contains(t *ListType) (answer bool) {
if len(*ls) >= len(*t) { if len(*ls) >= len(*t) {
+2 -2
View File
@@ -18,8 +18,8 @@ func newDictTerm(args map[any]*term) *term {
} }
// -------- dict func // -------- dict func
func evalDict(ctx ExprContext, self *term) (v any, err error) { func evalDict(ctx ExprContext, opTerm *term) (v any, err error) {
dict, _ := self.value().(map[any]*term) dict, _ := opTerm.value().(map[any]*term)
items := make(DictType, len(dict)) items := make(DictType, len(dict))
for key, tree := range dict { for key, tree := range dict {
var param any var param any
+3 -3
View File
@@ -20,11 +20,11 @@ func newExprTerm(root *term) *term {
} }
// -------- eval expr // -------- eval expr
func evalExpr(ctx ExprContext, self *term) (v any, err error) { func evalExpr(ctx ExprContext, opTerm *term) (v any, err error) {
if expr, ok := self.value().(*term); ok { if expr, ok := opTerm.value().(*term); ok {
v, err = expr.compute(ctx) v, err = expr.compute(ctx)
} else { } else {
err = fmt.Errorf("expression expected, got %T", self.value()) err = fmt.Errorf("expression expected, got %T", opTerm.value())
} }
return return
} }
+8 -8
View File
@@ -21,10 +21,10 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
} }
// -------- eval func call // -------- eval func call
func evalFuncCall(ctx ExprContext, self *term) (v any, err error) { func evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
name, _ := self.tk.Value.(string) name, _ := opTerm.tk.Value.(string)
params := make([]any, len(self.children), len(self.children)+5) params := make([]any, len(opTerm.children), len(opTerm.children)+5)
for i, tree := range self.children { for i, tree := range opTerm.children {
var param any var param any
if param, err = tree.compute(ctx); err != nil { if param, err = tree.compute(ctx); err != nil {
break break
@@ -51,11 +51,11 @@ func newFuncDefTerm(tk *Token, args []*term) *term {
} }
// -------- eval func def // -------- eval func def
func evalFuncDef(ctx ExprContext, self *term) (v any, err error) { func evalFuncDef(ctx ExprContext, opTerm *term) (v any, err error) {
bodySpec := self.value() bodySpec := opTerm.value()
if expr, ok := bodySpec.(*ast); ok { if expr, ok := bodySpec.(*ast); ok {
paramList := make([]ExprFuncParam, 0, len(self.children)) paramList := make([]ExprFuncParam, 0, len(opTerm.children))
for _, param := range self.children { for _, param := range opTerm.children {
var defValue any var defValue any
flags := paramFlags(0) flags := paramFlags(0)
if len(param.children) > 0 { if len(param.children) > 0 {
+35 -37
View File
@@ -5,28 +5,27 @@
package expr package expr
import ( import (
"fmt"
"slices" "slices"
"strings" "strings"
) )
// -------- iterator term // -------- iterator term
func newDsIteratorTerm(tk *Token, dsTerm *term, args []*term) *term { // func newDsIteratorTerm(tk *Token, dsTerm *term, args []*term) *term {
tk.Sym = SymIterator // tk.Sym = SymIterator
children := make([]*term, 0, 1+len(args)) // children := make([]*term, 0, 1+len(args))
children = append(children, dsTerm) // children = append(children, dsTerm)
children = append(children, args...) // children = append(children, args...)
return &term{ // return &term{
tk: *tk, // tk: *tk,
parent: nil, // parent: nil,
children: children, // children: children,
position: posLeaf, // position: posLeaf,
priority: priValue, // priority: priValue,
evalFunc: evalIterator, // evalFunc: evalIterator,
} // }
} // }
func newIteratorTerm(tk *Token, args []*term) *term { func newIteratorTerm(tk *Token, args []*term) *term {
tk.Sym = SymIterator tk.Sym = SymIterator
@@ -42,9 +41,9 @@ func newIteratorTerm(tk *Token, args []*term) *term {
// -------- eval iterator // -------- eval iterator
func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) { func evalTermArray(ctx ExprContext, terms []*term) (values []any, err error) {
values = make([]any, len(a)) values = make([]any, len(terms))
for i, t := range a { for i, t := range terms {
var value any var value any
if value, err = t.compute(ctx); err == nil { if value, err = t.compute(ctx); err == nil {
values[i] = value values[i] = value
@@ -55,18 +54,17 @@ func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) {
return return
} }
func evalFirstChild(ctx ExprContext, self *term) (value any, err error) { func evalFirstChild(ctx ExprContext, iteratorTerm *term) (value any, err error) {
if len(self.children) < 1 || self.children[0] == nil { if len(iteratorTerm.children) < 1 || iteratorTerm.children[0] == nil {
err = self.Errorf("missing the data-source parameter") err = iteratorTerm.Errorf("missing the data-source parameter")
return return
} }
value, err = self.children[0].compute(ctx) value, err = iteratorTerm.children[0].compute(ctx)
return return
} }
func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map[string]Functor, err error) { func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]Functor, err error) {
// if dictAny, ok := firstChildValue.(map[any]any); ok {
if dictAny, ok := firstChildValue.(*DictType); ok { if dictAny, ok := firstChildValue.(*DictType); ok {
requiredFields := []string{currentName, nextName} requiredFields := []string{currentName, nextName}
fieldsMask := 0b11 fieldsMask := 0b11
@@ -74,7 +72,6 @@ func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map
ds = make(map[string]Functor) ds = make(map[string]Functor)
for keyAny, item := range *dictAny { for keyAny, item := range *dictAny {
if key, ok := keyAny.(string); ok { if key, ok := keyAny.(string); ok {
//if functor, ok := item.(*funcDefFunctor); ok {
if functor, ok := item.(Functor); ok { if functor, ok := item.(Functor); ok {
ds[key] = functor ds[key] = functor
if index := slices.Index(requiredFields, key); index >= 0 { if index := slices.Index(requiredFields, key); index >= 0 {
@@ -91,21 +88,22 @@ func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map
missingFields = append(missingFields, field) missingFields = append(missingFields, field)
} }
} }
err = fmt.Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", ")) // err = fmt.Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
err = iteratorTerm.children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
} }
} }
return return
} }
func evalIterator(ctx ExprContext, self *term) (v any, err error) { func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
var firstChildValue any var firstChildValue any
var ds map[string]Functor var ds map[string]Functor
if firstChildValue, err = evalFirstChild(ctx, self); err != nil { if firstChildValue, err = evalFirstChild(ctx, opTerm); err != nil {
return return
} }
if ds, err = getDataSourceDict(ctx, self, firstChildValue); err != nil { if ds, err = getDataSourceDict(opTerm, firstChildValue); err != nil {
return return
} }
@@ -113,8 +111,8 @@ func evalIterator(ctx ExprContext, self *term) (v any, err error) {
dc := newDataCursor(ctx, ds) dc := newDataCursor(ctx, ds)
if initFunc, exists := ds[initName]; exists && initFunc != nil { if initFunc, exists := ds[initName]; exists && initFunc != nil {
var args []any var args []any
if len(self.children) > 1 { if len(opTerm.children) > 1 {
if args, err = evalTermArray(ctx, self.children[1:]); err != nil { if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil {
return return
} }
} else { } else {
@@ -128,20 +126,20 @@ func evalIterator(ctx ExprContext, self *term) (v any, err error) {
exportObjects(dc.ctx, initCtx) exportObjects(dc.ctx, initCtx)
} }
dc.nextFunc, _ = ds[nextName] dc.nextFunc = ds[nextName]
dc.currentFunc, _ = ds[currentName] dc.currentFunc = ds[currentName]
dc.cleanFunc, _ = ds[cleanName] dc.cleanFunc = ds[cleanName]
dc.resetFunc, _ = ds[resetName] dc.resetFunc = ds[resetName]
v = dc v = dc
} else if list, ok := firstChildValue.(*ListType); ok { } else if list, ok := firstChildValue.(*ListType); ok {
var args []any var args []any
if args, err = evalSibling(ctx, self.children, nil); err == nil { if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
v = NewListIterator(list, args) v = NewListIterator(list, args)
} }
} else { } else {
var list []any var list []any
if list, err = evalSibling(ctx, self.children, firstChildValue); err == nil { if list, err = evalSibling(ctx, opTerm.children, firstChildValue); err == nil {
v = NewArrayIterator(list) v = NewArrayIterator(list)
} }
} }
+2 -2
View File
@@ -21,8 +21,8 @@ func newListTerm(row, col int, args []*term) *term {
} }
// -------- list func // -------- list func
func evalList(ctx ExprContext, self *term) (v any, err error) { func evalList(ctx ExprContext, opTerm *term) (v any, err error) {
list, _ := self.value().([]*term) list, _ := opTerm.value().([]*term)
items := make(ListType, len(list)) items := make(ListType, len(list))
for i, tree := range list { for i, tree := range list {
var param any var param any
+2 -2
View File
@@ -17,8 +17,8 @@ func newLiteralTerm(tk *Token) *term {
} }
// -------- eval func // -------- eval func
func evalLiteral(ctx ExprContext, self *term) (v any, err error) { func evalLiteral(ctx ExprContext, opTerm *term) (v any, err error) {
v = self.tk.Value v = opTerm.tk.Value
return return
} }
+3 -3
View File
@@ -41,10 +41,10 @@ func newSelectorCaseTerm(row, col int, filterList *term, caseExpr Expr) *term {
} }
// -------- eval selector case // -------- eval selector case
func evalSelectorCase(ctx ExprContext, self *term) (v any, err error) { func evalSelectorCase(ctx ExprContext, opTerm *term) (v any, err error) {
var ok bool var ok bool
if v, ok = self.value().(*selectorCase); !ok { if v, ok = opTerm.value().(*selectorCase); !ok {
err = fmt.Errorf("selector-case expected, got %T", self.value()) err = fmt.Errorf("selector-case expected, got %T", opTerm.value())
} }
return return
} }
+2 -2
View File
@@ -21,9 +21,9 @@ func newVarTerm(tk *Token) *term {
} }
// -------- eval func // -------- eval func
func evalVar(ctx ExprContext, self *term) (v any, err error) { func evalVar(ctx ExprContext, opTerm *term) (v any, err error) {
var exists bool var exists bool
name := self.source() name := opTerm.source()
if v, exists = GetVar(ctx, name); !exists { if v, exists = GetVar(ctx, name); !exists {
if info, exists, _ := GetFuncInfo(ctx, name); exists { if info, exists, _ := GetFuncInfo(ctx, name); exists {
v = info.Functor() v = info.Functor()
+10 -6
View File
@@ -60,22 +60,23 @@ func assignValue(ctx ExprContext, leftTerm *term, v any) (err error) {
return return
} }
func evalAssign(ctx ExprContext, self *term) (v any, err error) { func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
if err = self.checkOperands(); err != nil { if err = opTerm.checkOperands(); err != nil {
return return
} }
leftTerm := self.children[0] leftTerm := opTerm.children[0]
leftSym := leftTerm.symbol() leftSym := leftTerm.symbol()
if leftSym != SymVariable && leftSym != SymIndex { if leftSym != SymVariable && leftSym != SymIndex {
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", self.tk.source) err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
return return
} }
rightChild := self.children[1] rightChild := opTerm.children[1]
if v, err = rightChild.compute(ctx); err == nil { if v, err = rightChild.compute(ctx); err == nil {
if functor, ok := v.(Functor); ok { if functor, ok := v.(Functor); ok {
if leftSym == SymVariable {
if info := functor.GetFunc(); info != nil { if info := functor.GetFunc(); info != nil {
ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params()) ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params())
} else if funcDef, ok := functor.(*exprFunctor); ok { } else if funcDef, ok := functor.(*exprFunctor); ok {
@@ -83,7 +84,10 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, paramSpecs) ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, paramSpecs)
} else { } else {
err = self.Errorf("unknown function %s()", rightChild.source()) err = opTerm.Errorf("unknown function %s()", rightChild.source())
}
} else {
err = assignValue(ctx, leftTerm, v)
} }
} else { } else {
err = assignValue(ctx, leftTerm, v) err = assignValue(ctx, leftTerm, v)
+3 -3
View File
@@ -18,17 +18,17 @@ func newNotTerm(tk *Token) (inst *term) {
} }
} }
func evalNot(ctx ExprContext, self *term) (v any, err error) { func evalNot(ctx ExprContext, opTerm *term) (v any, err error) {
var rightValue any var rightValue any
if rightValue, err = self.evalPrefix(ctx); err != nil { if rightValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if b, ok := ToBool(rightValue); ok { if b, ok := ToBool(rightValue); ok {
v = !b v = !b
} else { } else {
err = self.errIncompatibleType(rightValue) err = opTerm.errIncompatibleType(rightValue)
} }
return return
} }
+4 -4
View File
@@ -18,10 +18,10 @@ func newBuiltinTerm(tk *Token) (inst *term) {
} }
} }
func evalBuiltin(ctx ExprContext, self *term) (v any, err error) { func evalBuiltin(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
@@ -37,11 +37,11 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
if ImportInContext(module) { if ImportInContext(module) {
count++ count++
} else { } else {
err = self.Errorf("unknown builtin module %q", module) err = opTerm.Errorf("unknown builtin module %q", module)
break break
} }
} else { } else {
err = self.Errorf("expected string at item nr %d, got %s", it.Index()+1, TypeName(moduleSpec)) err = opTerm.Errorf("expected string at item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break break
} }
} }
+2 -2
View File
@@ -16,8 +16,8 @@ func newButTerm(tk *Token) (inst *term) {
} }
} }
func evalBut(ctx ExprContext, self *term) (v any, err error) { func evalBut(ctx ExprContext, opTerm *term) (v any, err error) {
_, v, err = self.evalInfix(ctx) _, v, err = opTerm.evalInfix(ctx)
return return
} }
-85
View File
@@ -1,85 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-coalesce.go
package expr
//-------- null coalesce term
func newNullCoalesceTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priCoalesce,
evalFunc: evalNullCoalesce,
}
}
func evalNullCoalesce(ctx ExprContext, self *term) (v any, err error) {
var rightValue any
if err = self.checkOperands(); err != nil {
return
}
leftTerm := self.children[0]
if leftTerm.tk.Sym != SymVariable {
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
v = rightValue
}
return
}
//-------- coalesce assign term
func newCoalesceAssignTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priCoalesce,
evalFunc: evalAssignCoalesce,
}
}
func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
var rightValue any
if err = self.checkOperands(); err != nil {
return
}
leftTerm := self.children[0]
if leftTerm.tk.Sym != SymVariable {
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
if functor, ok := rightValue.(Functor); ok {
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
})
} else {
v = rightValue
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
}
}
return
}
// init
func init() {
registerTermConstructor(SymDoubleQuestion, newNullCoalesceTerm)
registerTermConstructor(SymQuestionEqual, newCoalesceAssignTerm)
}
+5 -5
View File
@@ -16,15 +16,15 @@ func newContextTerm(tk *Token) (inst *term) {
} }
} }
func evalContextValue(ctx ExprContext, self *term) (v any, err error) { func evalContextValue(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
var sourceCtx ExprContext var sourceCtx ExprContext
if self.children == nil || len(self.children) == 0 { if opTerm.children == nil || len(opTerm.children) == 0 {
sourceCtx = ctx sourceCtx = ctx
} else if self.children[0].symbol() == SymVariable && self.children[0].source() == "global" { } else if opTerm.children[0].symbol() == SymVariable && opTerm.children[0].source() == "global" {
sourceCtx = globalCtx sourceCtx = globalCtx
} else if childValue, err = self.evalPrefix(ctx); err == nil { } else if childValue, err = opTerm.evalPrefix(ctx); err == nil {
if dc, ok := childValue.(*dataCursor); ok { if dc, ok := childValue.(*dataCursor); ok {
sourceCtx = dc.ctx sourceCtx = dc.ctx
} }
@@ -49,7 +49,7 @@ func evalContextValue(ctx ExprContext, self *term) (v any, err error) {
v = d v = d
} }
} else { } else {
err = self.errIncompatibleType(childValue) err = opTerm.errIncompatibleType(childValue)
} }
return return
} }
+1 -1
View File
@@ -16,7 +16,7 @@ func newExportAllTerm(tk *Token) (inst *term) {
} }
} }
func evalExportAll(ctx ExprContext, self *term) (v any, err error) { func evalExportAll(ctx ExprContext, opTerm *term) (v any, err error) {
CtrlEnable(ctx, control_export_all) CtrlEnable(ctx, control_export_all)
return return
} }
+124
View File
@@ -0,0 +1,124 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-default.go
package expr
//-------- default term
func newDefaultTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priDefault,
evalFunc: evalDefault,
}
}
func evalDefault(ctx ExprContext, opTerm *term) (v any, err error) {
var rightValue any
if err = opTerm.checkOperands(); err != nil {
return
}
leftTerm := opTerm.children[0]
if leftTerm.tk.Sym != SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
v = rightValue
}
return
}
//-------- alternate term
func newAlternateTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priDefault,
evalFunc: evalAlternate,
}
}
func evalAlternate(ctx ExprContext, opTerm *term) (v any, err error) {
var rightValue any
if err = opTerm.checkOperands(); err != nil {
return
}
leftTerm := opTerm.children[0]
if leftTerm.tk.Sym != SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists && leftValue != nil {
if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
v = rightValue
}
} else {
v = leftValue
}
return
}
//-------- default assign term
func newDefaultAssignTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priDefault,
evalFunc: evalAssignDefault,
}
}
func evalAssignDefault(ctx ExprContext, opTerm *term) (v any, err error) {
var rightValue any
if err = opTerm.checkOperands(); err != nil {
return
}
leftTerm := opTerm.children[0]
if leftTerm.tk.Sym != SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
if functor, ok := rightValue.(Functor); ok {
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
})
} else {
v = rightValue
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
}
}
return
}
// init
func init() {
registerTermConstructor(SymDoubleQuestion, newDefaultTerm)
registerTermConstructor(SymQuestionEqual, newDefaultAssignTerm)
registerTermConstructor(SymQuestionExclam, newAlternateTerm)
}
+6 -6
View File
@@ -15,17 +15,17 @@ func newDotTerm(tk *Token) (inst *term) {
} }
} }
func evalDot(ctx ExprContext, self *term) (v any, err error) { func evalDot(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if err = self.checkOperands(); err != nil { if err = opTerm.checkOperands(); err != nil {
return return
} }
if leftValue, err = self.children[0].compute(ctx); err != nil { if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
return return
} }
indexTerm := self.children[1] indexTerm := opTerm.children[1]
switch unboxedValue := leftValue.(type) { switch unboxedValue := leftValue.(type) {
case ExtIterator: case ExtIterator:
@@ -41,8 +41,8 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
err = indexTerm.tk.ErrorExpectedGot("identifier") err = indexTerm.tk.ErrorExpectedGot("identifier")
} }
default: default:
if rightValue, err = self.children[1].compute(ctx); err == nil { if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
} }
return return
+3 -3
View File
@@ -18,10 +18,10 @@ func newFactTerm(tk *Token) (inst *term) {
} }
} }
func evalFact(ctx ExprContext, self *term) (v any, err error) { func evalFact(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue any var leftValue any
if leftValue, err = self.evalPrefix(ctx); err != nil { if leftValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
@@ -36,7 +36,7 @@ func evalFact(ctx ExprContext, self *term) (v any, err error) {
err = fmt.Errorf("factorial of a negative integer (%d) is not allowed", i) err = fmt.Errorf("factorial of a negative integer (%d) is not allowed", i)
} }
} else { } else {
err = self.errIncompatibleType(leftValue) err = opTerm.errIncompatibleType(leftValue)
} }
return return
} }
+2 -2
View File
@@ -24,12 +24,12 @@ func newFractionTerm(tk *Token) *term {
} }
// -------- eval func // -------- eval func
func evalFraction(ctx ExprContext, self *term) (v any, err error) { func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
var numValue, denValue any var numValue, denValue any
var num, den int64 var num, den int64
var ok bool var ok bool
if numValue, denValue, err = self.evalInfix(ctx); err != nil { if numValue, denValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
if num, ok = numValue.(int64); !ok { if num, ok = numValue.(int64); !ok {
+3 -3
View File
@@ -21,10 +21,10 @@ func newInTerm(tk *Token) (inst *term) {
// return // return
// } // }
func evalIn(ctx ExprContext, self *term) (v any, err error) { func evalIn(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -35,7 +35,7 @@ func evalIn(ctx ExprContext, self *term) (v any, err error) {
dict, _ := rightValue.(*DictType) dict, _ := rightValue.(*DictType)
v = dict.hasKey(leftValue) v = dict.hasKey(leftValue)
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
+14 -11
View File
@@ -16,37 +16,40 @@ func newIncludeTerm(tk *Token) (inst *term) {
} }
} }
func evalInclude(ctx ExprContext, self *term) (v any, err error) { func evalInclude(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
count := 0 count := 0
if IsList(childValue) { if IsList(childValue) {
list, _ := childValue.([]any) list, _ := childValue.(*ListType)
for i, filePathSpec := range list { for i, filePathSpec := range *list {
if filePath, ok := filePathSpec.(string); ok { if filePath, ok := filePathSpec.(string); ok {
if v, err = EvalFile(ctx, filePath); err == nil { if v, err = EvalFile(ctx, filePath); err == nil {
count++ count++
} else { } else {
err = self.Errorf("can't load file %q", filePath) err = opTerm.Errorf("can't load file %q", filePath)
break break
} }
} else { } else {
err = self.Errorf("expected string at item nr %d, got %T", i+1, filePathSpec) err = opTerm.Errorf("expected string at item nr %d, got %T", i+1, filePathSpec)
break break
} }
} }
} else if IsString(childValue) { } else if IsString(childValue) {
filePath, _ := childValue.(string) filePath, _ := childValue.(string)
v, err = EvalFile(ctx, filePath) if v, err = EvalFile(ctx, filePath); err == nil {
} else { count++
err = self.errIncompatibleType(childValue)
} }
if err == nil { } else {
v = count err = opTerm.errIncompatibleType(childValue)
}
if err != nil {
//v = count
v = nil
} }
return return
} }
+8 -23
View File
@@ -15,7 +15,7 @@ func newIndexTerm(tk *Token) (inst *term) {
} }
} }
func verifyKey(indexTerm *term, indexList *ListType) (index any, err error) { func verifyKey(indexList *ListType) (index any, err error) {
index = (*indexList)[0] index = (*indexList)[0]
return return
} }
@@ -59,18 +59,18 @@ func verifyRange(indexTerm *term, indexList *ListType, maxValue int) (startIndex
return return
} }
func evalIndex(ctx ExprContext, self *term) (v any, err error) { func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
var indexList *ListType var indexList *ListType
var ok bool var ok bool
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
indexTerm := self.children[1] indexTerm := opTerm.children[1]
if indexList, ok = rightValue.(*ListType); !ok { if indexList, ok = rightValue.(*ListType); !ok {
err = self.Errorf("invalid index expression") err = opTerm.Errorf("invalid index expression")
return return
} else if len(*indexList) != 1 { } else if len(*indexList) != 1 {
err = indexTerm.Errorf("one index only is allowed") err = indexTerm.Errorf("one index only is allowed")
@@ -90,16 +90,9 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
v = string(unboxedValue[index]) v = string(unboxedValue[index])
} }
case *DictType: case *DictType:
/* var ok bool
var indexValue any
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
if v, ok = (*unboxedValue)[indexValue]; !ok {
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
}
} */
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue) v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
default: default:
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
} else if isIntPair((*indexList)[0]) { } else if isIntPair((*indexList)[0]) {
switch unboxedValue := leftValue.(type) { switch unboxedValue := leftValue.(type) {
@@ -115,18 +108,10 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
v = unboxedValue[start:end] v = unboxedValue[start:end]
} }
default: default:
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
} else if IsDict(leftValue) { } else if IsDict(leftValue) {
d := leftValue.(*DictType) d := leftValue.(*DictType)
/* var ok bool
var indexValue any
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
if v, ok = (*d)[indexValue]; !ok {
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
}
}*/
v, err = getDictItem(d, indexTerm, indexList, rightValue) v, err = getDictItem(d, indexTerm, indexList, rightValue)
} }
return return
@@ -136,7 +121,7 @@ func getDictItem(d *DictType, indexTerm *term, indexList *ListType, rightValue a
var ok bool var ok bool
var indexValue any var indexValue any
if indexValue, err = verifyKey(indexTerm, indexList); err == nil { if indexValue, err = verifyKey(indexList); err == nil {
if v, ok = (*d)[indexValue]; !ok { if v, ok = (*d)[indexValue]; !ok {
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue) err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
} }
+10 -10
View File
@@ -26,10 +26,10 @@ func newAppendTerm(tk *Token) (inst *term) {
} }
} }
func evalInsert(ctx ExprContext, self *term) (v any, err error) { func evalInsert(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -37,19 +37,19 @@ func evalInsert(ctx ExprContext, self *term) (v any, err error) {
list, _ := rightValue.(*ListType) list, _ := rightValue.(*ListType)
newList := append(ListType{leftValue}, *list...) newList := append(ListType{leftValue}, *list...)
v = &newList v = &newList
if self.children[1].symbol() == SymVariable { if opTerm.children[1].symbol() == SymVariable {
ctx.UnsafeSetVar(self.children[1].source(), v) ctx.UnsafeSetVar(opTerm.children[1].source(), v)
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalAppend(ctx ExprContext, self *term) (v any, err error) { func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -57,11 +57,11 @@ func evalAppend(ctx ExprContext, self *term) (v any, err error) {
list, _ := leftValue.(*ListType) list, _ := leftValue.(*ListType)
newList := append(*list, rightValue) newList := append(*list, rightValue)
v = &newList v = &newList
if self.children[0].symbol() == SymVariable { if opTerm.children[0].symbol() == SymVariable {
ctx.UnsafeSetVar(self.children[0].source(), v) ctx.UnsafeSetVar(opTerm.children[0].source(), v)
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
+5 -4
View File
@@ -16,22 +16,23 @@ func newIterValueTerm(tk *Token) (inst *term) {
} }
} }
func evalIterValue(ctx ExprContext, self *term) (v any, err error) { func evalIterValue(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if it, ok := childValue.(Iterator); ok { if it, ok := childValue.(Iterator); ok {
v, err = it.Current() v, err = it.Current()
} else { } else {
err = self.errIncompatibleType(childValue) err = opTerm.errIncompatibleType(childValue)
} }
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymOpenClosedRound, newIterValueTerm) // registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
registerTermConstructor(SymCaret, newIterValueTerm)
} }
+3 -3
View File
@@ -16,10 +16,10 @@ func newLengthTerm(tk *Token) (inst *term) {
} }
} }
func evalLength(ctx ExprContext, self *term) (v any, err error) { func evalLength(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
@@ -41,7 +41,7 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) {
v = int64(it.Index() + 1) v = int64(it.Index() + 1)
} }
} else { } else {
err = self.errIncompatibleType(childValue) err = opTerm.errIncompatibleType(childValue)
} }
return return
} }
+3 -3
View File
@@ -20,11 +20,11 @@ func newPluginTerm(tk *Token) (inst *term) {
} }
} }
func evalPlugin(ctx ExprContext, self *term) (v any, err error) { func evalPlugin(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
var moduleSpec any var moduleSpec any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
@@ -38,7 +38,7 @@ func evalPlugin(ctx ExprContext, self *term) (v any, err error) {
} }
count++ count++
} else { } else {
err = self.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec)) err = opTerm.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break break
} }
} }
+5 -5
View File
@@ -17,20 +17,20 @@ func newPostIncTerm(tk *Token) *term {
} }
} }
func evalPostInc(ctx ExprContext, self *term) (v any, err error) { func evalPostInc(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if it, ok := childValue.(Iterator); ok { if it, ok := childValue.(Iterator); ok {
v, err = it.Next() v, err = it.Next()
} else if IsInteger(childValue) && self.children[0].symbol() == SymVariable { } else if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
v = childValue v = childValue
i, _ := childValue.(int64) i, _ := childValue.(int64)
ctx.SetVar(self.children[0].source(), i+1) ctx.SetVar(opTerm.children[0].source(), i+1)
} else { } else {
err = self.errIncompatibleType(childValue) err = opTerm.errIncompatibleType(childValue)
} }
return return
} }
+14 -20
View File
@@ -14,8 +14,6 @@ import (
func newMultiplyTerm(tk *Token) (inst *term) { func newMultiplyTerm(tk *Token) (inst *term) {
return &term{ return &term{
tk: *tk, tk: *tk,
// class: classOperator,
// kind: kindUnknown,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priProduct, priority: priProduct,
@@ -23,10 +21,10 @@ func newMultiplyTerm(tk *Token) (inst *term) {
} }
} }
func evalMultiply(ctx ExprContext, self *term) (v any, err error) { func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -45,7 +43,7 @@ func evalMultiply(ctx ExprContext, self *term) (v any, err error) {
v = leftInt * rightInt v = leftInt * rightInt
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = prodTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
@@ -55,8 +53,6 @@ func evalMultiply(ctx ExprContext, self *term) (v any, err error) {
func newDivideTerm(tk *Token) (inst *term) { func newDivideTerm(tk *Token) (inst *term) {
return &term{ return &term{
tk: *tk, tk: *tk,
// class: classOperator,
// kind: kindUnknown,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priProduct, priority: priProduct,
@@ -64,10 +60,10 @@ func newDivideTerm(tk *Token) (inst *term) {
} }
} }
func evalDivide(ctx ExprContext, self *term) (v any, err error) { func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -90,7 +86,7 @@ func evalDivide(ctx ExprContext, self *term) (v any, err error) {
} }
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
@@ -107,10 +103,10 @@ func newDivideAsFloatTerm(tk *Token) (inst *term) {
} }
} }
func evalDivideAsFloat(ctx ExprContext, self *term) (v any, err error) { func evalDivideAsFloat(ctx ExprContext, floatDivTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = floatDivTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -122,18 +118,16 @@ func evalDivideAsFloat(ctx ExprContext, self *term) (v any, err error) {
v = numAsFloat(leftValue) / d v = numAsFloat(leftValue) / d
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = floatDivTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
//-------- reminder term //-------- reminder term
func newReminderTerm(tk *Token) (inst *term) { func newRemainderTerm(tk *Token) (inst *term) {
return &term{ return &term{
tk: *tk, tk: *tk,
// class: classOperator,
// kind: kindUnknown,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priProduct, priority: priProduct,
@@ -141,10 +135,10 @@ func newReminderTerm(tk *Token) (inst *term) {
} }
} }
func evalReminder(ctx ExprContext, self *term) (v any, err error) { func evalReminder(ctx ExprContext, ramainderTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = ramainderTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -157,7 +151,7 @@ func evalReminder(ctx ExprContext, self *term) (v any, err error) {
v = leftInt % rightInt v = leftInt % rightInt
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = ramainderTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
@@ -167,5 +161,5 @@ func init() {
registerTermConstructor(SymStar, newMultiplyTerm) registerTermConstructor(SymStar, newMultiplyTerm)
registerTermConstructor(SymSlash, newDivideTerm) registerTermConstructor(SymSlash, newDivideTerm)
registerTermConstructor(SymDotSlash, newDivideAsFloatTerm) registerTermConstructor(SymDotSlash, newDivideAsFloatTerm)
registerTermConstructor(SymPercent, newReminderTerm) registerTermConstructor(SymPercent, newRemainderTerm)
} }
+6 -6
View File
@@ -34,25 +34,25 @@ func newRangeTerm(tk *Token) (inst *term) {
} }
} }
func evalRange(ctx ExprContext, self *term) (v any, err error) { func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
// if err = self.checkOperands(); err != nil { // if err = self.checkOperands(); err != nil {
// return // return
// } // }
if len(self.children) == 0 { if len(opTerm.children) == 0 {
leftValue = int64(0) leftValue = int64(0)
rightValue = int64(-1) rightValue = int64(-1)
} else if len(self.children) == 1 { } else if len(opTerm.children) == 1 {
if leftValue, err = self.children[0].compute(ctx); err != nil { if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
return return
} }
rightValue = int64(ConstLastIndex) rightValue = int64(ConstLastIndex)
} else if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { } else if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
if !(IsInteger(leftValue) && IsInteger(rightValue)) { if !(IsInteger(leftValue) && IsInteger(rightValue)) {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
return return
} }
+16 -16
View File
@@ -45,10 +45,10 @@ func equals(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) {
return return
} }
func evalEqual(ctx ExprContext, self *term) (v any, err error) { func evalEqual(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -68,8 +68,8 @@ func newNotEqualTerm(tk *Token) (inst *term) {
} }
} }
func evalNotEqual(ctx ExprContext, self *term) (v any, err error) { func evalNotEqual(ctx ExprContext, opTerm *term) (v any, err error) {
if v, err = evalEqual(ctx, self); err == nil { if v, err = evalEqual(ctx, opTerm); err == nil {
b, _ := ToBool(v) b, _ := ToBool(v)
v = !b v = !b
} }
@@ -119,13 +119,13 @@ func lessThan(self *term, a, b any) (isLess bool, err error) {
return return
} }
func evalLess(ctx ExprContext, self *term) (v any, err error) { func evalLess(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
v, err = lessThan(self, leftValue, rightValue) v, err = lessThan(opTerm, leftValue, rightValue)
return return
} }
@@ -154,14 +154,14 @@ func lessThanOrEqual(self *term, a, b any) (isLessEq bool, err error) {
return return
} }
func evalLessEqual(ctx ExprContext, self *term) (v any, err error) { func evalLessEqual(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
v, err = lessThanOrEqual(self, leftValue, rightValue) v, err = lessThanOrEqual(opTerm, leftValue, rightValue)
return return
} }
@@ -178,14 +178,14 @@ func newGreaterTerm(tk *Token) (inst *term) {
} }
} }
func evalGreater(ctx ExprContext, self *term) (v any, err error) { func evalGreater(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
v, err = lessThan(self, rightValue, leftValue) v, err = lessThan(opTerm, rightValue, leftValue)
return return
} }
@@ -201,14 +201,14 @@ func newGreaterEqualTerm(tk *Token) (inst *term) {
} }
} }
func evalGreaterEqual(ctx ExprContext, self *term) (v any, err error) { func evalGreaterEqual(ctx ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
v, err = lessThanOrEqual(self, rightValue, leftValue) v, err = lessThanOrEqual(opTerm, rightValue, leftValue)
return return
} }
+4 -4
View File
@@ -39,19 +39,19 @@ func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (ma
return return
} }
func evalSelector(ctx ExprContext, self *term) (v any, err error) { func evalSelector(ctx ExprContext, opTerm *term) (v any, err error) {
var exprValue any var exprValue any
var match bool var match bool
if err = self.checkOperands(); err != nil { if err = opTerm.checkOperands(); err != nil {
return return
} }
exprTerm := self.children[0] exprTerm := opTerm.children[0]
if exprValue, err = exprTerm.compute(ctx); err != nil { if exprValue, err = exprTerm.compute(ctx); err != nil {
return return
} }
caseListTerm := self.children[1] caseListTerm := opTerm.children[1]
caseList, _ := caseListTerm.value().([]*term) caseList, _ := caseListTerm.value().([]*term)
for i, caseTerm := range caseList { for i, caseTerm := range caseList {
caseSel := caseTerm.value() caseSel := caseTerm.value()
+5 -5
View File
@@ -28,29 +28,29 @@ func newMinusSignTerm(tk *Token) (inst *term) {
} }
} }
func evalSign(ctx ExprContext, self *term) (v any, err error) { func evalSign(ctx ExprContext, opTerm *term) (v any, err error) {
var rightValue any var rightValue any
if rightValue, err = self.evalPrefix(ctx); err != nil { if rightValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if IsFloat(rightValue) { if IsFloat(rightValue) {
if self.tk.Sym == SymChangeSign { if opTerm.tk.Sym == SymChangeSign {
f, _ := rightValue.(float64) f, _ := rightValue.(float64)
v = -f v = -f
} else { } else {
v = rightValue v = rightValue
} }
} else if IsInteger(rightValue) { } else if IsInteger(rightValue) {
if self.tk.Sym == SymChangeSign { if opTerm.tk.Sym == SymChangeSign {
i, _ := rightValue.(int64) i, _ := rightValue.(int64)
v = -i v = -i
} else { } else {
v = rightValue v = rightValue
} }
} else { } else {
err = self.errIncompatibleType(rightValue) err = opTerm.errIncompatibleType(rightValue)
} }
return return
} }
+8 -12
View File
@@ -21,10 +21,10 @@ func newPlusTerm(tk *Token) (inst *term) {
} }
} }
func evalPlus(ctx ExprContext, self *term) (v any, err error) { func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -44,12 +44,8 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
rightList, _ = rightValue.(*ListType) rightList, _ = rightValue.(*ListType)
sumList := make(ListType, 0, len(*leftList)+len(*rightList)) sumList := make(ListType, 0, len(*leftList)+len(*rightList))
for _, item := range *leftList { sumList = append(sumList, *leftList...)
sumList = append(sumList, item) sumList = append(sumList, *rightList...)
}
for _, item := range *rightList {
sumList = append(sumList, item)
}
v = &sumList v = &sumList
} else if (isFraction(leftValue) && IsNumber(rightValue)) || (isFraction(rightValue) && IsNumber(leftValue)) { } else if (isFraction(leftValue) && IsNumber(rightValue)) || (isFraction(rightValue) && IsNumber(leftValue)) {
if IsFloat(leftValue) || IsFloat(rightValue) { if IsFloat(leftValue) || IsFloat(rightValue) {
@@ -64,7 +60,7 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
c.merge(rightDict) c.merge(rightDict)
v = c v = c
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
@@ -81,10 +77,10 @@ func newMinusTerm(tk *Token) (inst *term) {
} }
} }
func evalMinus(ctx ExprContext, self *term) (v any, err error) { func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -109,7 +105,7 @@ func evalMinus(ctx ExprContext, self *term) (v any, err error) {
} }
v = &diffList v = &diffList
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = minusTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
+69
View File
@@ -0,0 +1,69 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-unset.go
package expr
import "strings"
//-------- unset term
func newUnsetTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 1),
position: posPrefix,
priority: priSign,
evalFunc: evalUnset,
}
}
func deleteContextItem(ctx ExprContext, opTerm *term, item any) (deleted bool, err error) {
if name, ok := item.(string); ok {
var size int
if strings.HasSuffix(name, "()") {
size = ctx.FuncCount()
ctx.DeleteFunc(strings.TrimRight(name, "()"))
deleted = ctx.FuncCount() < size
} else {
size = ctx.VarCount()
ctx.DeleteVar(name)
deleted = ctx.VarCount() < size
}
} else {
err = opTerm.errIncompatibleType(item)
}
return
}
func evalUnset(ctx ExprContext, opTerm *term) (v any, err error) {
var childValue any
var deleted bool
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return
}
count := 0
if IsList(childValue) {
list, _ := childValue.(*ListType)
for _, item := range *list {
if deleted, err = deleteContextItem(ctx, opTerm, item); err != nil {
break
} else if deleted {
count++
}
}
} else if deleted, err = deleteContextItem(ctx, opTerm, childValue); err == nil && deleted {
count++
}
if err == nil {
v = int64(count)
}
return
}
// init
func init() {
registerTermConstructor(SymKwUnset, newUnsetTerm)
}
+32 -32
View File
@@ -18,13 +18,13 @@ func NewParser() (p *parser) {
return p return p
} }
func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) { func (parser *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
args := make([]*term, 0, 10) args := make([]*term, 0, 10)
itemExpected := false itemExpected := false
lastSym := SymUnknown lastSym := SymUnknown
for lastSym != SymClosedRound && lastSym != SymEos { for lastSym != SymClosedRound && lastSym != SymEos {
var subTree *ast var subTree *ast
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil { if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil {
break break
} }
prev := scanner.Previous() prev := scanner.Previous()
@@ -48,7 +48,7 @@ func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token)
return return
} }
func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) { func (parser *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)
@@ -65,7 +65,7 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
if tk.Sym == SymEqual { if tk.Sym == SymEqual {
var paramExpr *ast var paramExpr *ast
defaultParamsStarted = true defaultParamsStarted = true
if paramExpr, err = self.parseItem(scanner, false, SymComma, SymClosedRound); err != nil { if paramExpr, err = parser.parseItem(scanner, false, SymComma, SymClosedRound); err != nil {
break break
} }
param.forceChild(paramExpr.root) param.forceChild(paramExpr.root)
@@ -88,7 +88,7 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
if err == nil { if err == nil {
tk = scanner.Next() tk = scanner.Next()
if tk.IsSymbol(SymOpenBrace) { if tk.IsSymbol(SymOpenBrace) {
body, err = self.parseGeneral(scanner, true, true, SymClosedBrace) body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace)
} else { } else {
err = tk.ErrorExpectedGot("{") err = tk.ErrorExpectedGot("{")
} }
@@ -104,7 +104,7 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
return return
} }
func (self *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) { func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) {
r, c := scanner.lastPos() r, c := scanner.lastPos()
args := make([]*term, 0) args := make([]*term, 0)
lastSym := SymUnknown lastSym := SymUnknown
@@ -112,7 +112,7 @@ func (self *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef
for lastSym != SymClosedSquare && lastSym != SymEos { for lastSym != SymClosedSquare && lastSym != SymEos {
var subTree *ast var subTree *ast
zeroRequired := scanner.current.Sym == SymColon zeroRequired := scanner.current.Sym == SymColon
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil { if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
root := subTree.root root := subTree.root
if root != nil { if root != nil {
if !parsingIndeces && root.symbol() == SymColon { if !parsingIndeces && root.symbol() == SymColon {
@@ -153,14 +153,14 @@ func (self *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef
return return
} }
func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) { func (parser *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
tk := scanner.Previous() tk := scanner.Previous()
args := make([]*term, 0) args := make([]*term, 0)
lastSym := SymUnknown lastSym := SymUnknown
itemExpected := false itemExpected := false
for lastSym != SymClosedRound && lastSym != SymEos { for lastSym != SymClosedRound && lastSym != SymEos {
var subTree *ast var subTree *ast
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil { if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
if subTree.root != nil { if subTree.root != nil {
args = append(args, subTree.root) args = append(args, subTree.root)
} else if itemExpected { } else if itemExpected {
@@ -184,7 +184,7 @@ func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *t
return return
} }
func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, err error) { func (parser *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, err error) {
tk := scanner.Next() tk := scanner.Next()
if tk.Sym == SymError { if tk.Sym == SymError {
err = tk.Error() err = tk.Error()
@@ -206,14 +206,14 @@ func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, e
return return
} }
func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) { func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
args := make(map[any]*term, 0) args := make(map[any]*term, 0)
lastSym := SymUnknown lastSym := SymUnknown
itemExpected := false itemExpected := false
for lastSym != SymClosedBrace && lastSym != SymEos { for lastSym != SymClosedBrace && lastSym != SymEos {
var subTree *ast var subTree *ast
var key any var key any
if key, err = self.parseDictKey(scanner, allowVarRef); err != nil { if key, err = parser.parseDictKey(scanner, allowVarRef); err != nil {
break break
} else if key == nil { } else if key == nil {
tk := scanner.Previous() tk := scanner.Previous()
@@ -223,10 +223,10 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree
} }
break break
} }
if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil { if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
if subTree.root != nil { if subTree.root != nil {
args[key] = subTree.root args[key] = subTree.root
} else if key != nil { } else /*if key != nil*/ {
prev := scanner.Previous() prev := scanner.Previous()
err = prev.ErrorExpectedGot("dictionary-value") err = prev.ErrorExpectedGot("dictionary-value")
break break
@@ -248,7 +248,7 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree
return return
} }
func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) { func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
var filterList *term var filterList *term
var caseExpr *ast var caseExpr *ast
tk := scanner.Next() tk := scanner.Next()
@@ -259,7 +259,7 @@ func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaul
err = tk.Errorf("case list in default clause") err = tk.Errorf("case list in default clause")
return return
} }
if filterList, err = self.parseList(scanner, false, allowVarRef); err != nil { if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil {
return return
} }
tk = scanner.Next() tk = scanner.Next()
@@ -270,7 +270,7 @@ func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaul
} }
if tk.Sym == SymOpenBrace { if tk.Sym == SymOpenBrace {
if caseExpr, err = self.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil { if caseExpr, err = parser.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil {
return return
} }
} else { } else {
@@ -296,25 +296,25 @@ func addSelectorCase(selectorTerm, caseTerm *term) {
caseTerm.parent = selectorTerm caseTerm.parent = selectorTerm
} }
func (self *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) { func (parser *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
var caseTerm *term var caseTerm *term
tk := scanner.makeToken(SymSelector, '?') tk := scanner.makeToken(SymSelector, '?')
if selectorTerm, err = tree.addToken2(tk); err != nil { if selectorTerm, err = tree.addToken2(tk); err != nil {
return return
} }
if caseTerm, err = self.parseSelectorCase(scanner, allowVarRef, false); err == nil { if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, false); err == nil {
addSelectorCase(selectorTerm, caseTerm) addSelectorCase(selectorTerm, caseTerm)
} }
return return
} }
func (self *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) { func (parser *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
return self.parseGeneral(scanner, false, allowVarRef, termSymbols...) return parser.parseGeneral(scanner, false, allowVarRef, termSymbols...)
} }
func (self *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) { func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
return self.parseGeneral(scanner, true, false, termSymbols...) return parser.parseGeneral(scanner, true, false, termSymbols...)
} }
func couldBeACollection(t *term) bool { func couldBeACollection(t *term) bool {
@@ -333,7 +333,7 @@ func couldBeACollection(t *term) bool {
// return areOut // return areOut
// } // }
func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) { func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
var selectorTerm *term = nil var selectorTerm *term = nil
var currentTerm *term = nil var currentTerm *term = nil
var tk *Token var tk *Token
@@ -371,21 +371,21 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
switch tk.Sym { switch tk.Sym {
case SymOpenRound: case SymOpenRound:
var subTree *ast var subTree *ast
if subTree, err = self.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil { if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil {
subTree.root.priority = priValue subTree.root.priority = priValue
err = tree.addTerm(newExprTerm(subTree.root)) err = tree.addTerm(newExprTerm(subTree.root))
currentTerm = subTree.root currentTerm = subTree.root
} }
case SymFuncCall: case SymFuncCall:
var funcCallTerm *term var funcCallTerm *term
if funcCallTerm, err = self.parseFuncCall(scanner, allowVarRef, tk); err == nil { if funcCallTerm, err = parser.parseFuncCall(scanner, allowVarRef, tk); err == nil {
err = tree.addTerm(funcCallTerm) err = tree.addTerm(funcCallTerm)
currentTerm = funcCallTerm currentTerm = funcCallTerm
} }
case SymOpenSquare: case SymOpenSquare:
var listTerm *term var listTerm *term
parsingIndeces := couldBeACollection(currentTerm) parsingIndeces := couldBeACollection(currentTerm)
if listTerm, err = self.parseList(scanner, parsingIndeces, allowVarRef); err == nil { if listTerm, err = parser.parseList(scanner, parsingIndeces, allowVarRef); err == nil {
if parsingIndeces { if parsingIndeces {
indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source()) indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
indexTerm := newTerm(indexTk) indexTerm := newTerm(indexTk)
@@ -402,7 +402,7 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
err = currentTerm.Errorf(`selector-case outside of a selector context`) err = currentTerm.Errorf(`selector-case outside of a selector context`)
} else { } else {
var mapTerm *term var mapTerm *term
if mapTerm, err = self.parseDictionary(scanner, allowVarRef); err == nil { if mapTerm, err = parser.parseDictionary(scanner, allowVarRef); err == nil {
err = tree.addTerm(mapTerm) err = tree.addTerm(mapTerm)
currentTerm = mapTerm currentTerm = mapTerm
} }
@@ -413,13 +413,13 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
// } // }
case SymFuncDef: case SymFuncDef:
var funcDefTerm *term var funcDefTerm *term
if funcDefTerm, err = self.parseFuncDef(scanner); err == nil { if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
err = tree.addTerm(funcDefTerm) err = tree.addTerm(funcDefTerm)
currentTerm = funcDefTerm currentTerm = funcDefTerm
} }
case SymDollarRound: case SymDollarRound:
var iterDefTerm *term var iterDefTerm *term
if iterDefTerm, err = self.parseIterDef(scanner, allowVarRef); err == nil { if iterDefTerm, err = parser.parseIterDef(scanner, allowVarRef); err == nil {
err = tree.addTerm(iterDefTerm) err = tree.addTerm(iterDefTerm)
currentTerm = iterDefTerm currentTerm = iterDefTerm
} }
@@ -430,13 +430,13 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
currentTerm, err = tree.addToken2(tk) currentTerm, err = tree.addToken2(tk)
} }
case SymQuestion: case SymQuestion:
if selectorTerm, err = self.parseSelector(scanner, tree, allowVarRef); err == nil { if selectorTerm, err = parser.parseSelector(scanner, tree, allowVarRef); err == nil {
currentTerm = selectorTerm currentTerm = selectorTerm
} }
case SymColon, SymDoubleColon: case SymColon, SymDoubleColon:
var caseTerm *term var caseTerm *term
if selectorTerm != nil { if selectorTerm != nil {
if caseTerm, err = self.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil { if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
addSelectorCase(selectorTerm, caseTerm) addSelectorCase(selectorTerm, caseTerm)
currentTerm = caseTerm currentTerm = caseTerm
if tk.Sym == SymDoubleColon { if tk.Sym == SymDoubleColon {
+1 -1
View File
@@ -29,7 +29,7 @@ func pluginExists(name string) (exists bool) {
func makePluginName(name string) (decorated string) { func makePluginName(name string) (decorated string) {
var template string var template string
if execName, err := os.Executable(); err != nil || strings.Index(execName, "debug") < 0 { if execName, err := os.Executable(); err != nil || !strings.Contains(execName, "debug") {
template = "expr-%s-plugin.so" template = "expr-%s-plugin.so"
} else { } else {
template = "expr-%s-plugin.so.debug" template = "expr-%s-plugin.so.debug"
+189 -187
View File
@@ -49,255 +49,257 @@ func DefaultTranslations() map[Symbol]Symbol {
// return self.current // return self.current
// } // }
func (self *scanner) readChar() (ch byte, err error) { func (scanner *scanner) readChar() (ch byte, err error) {
if ch, err = self.stream.ReadByte(); err == nil { if ch, err = scanner.stream.ReadByte(); err == nil {
if ch == '\n' { if ch == '\n' {
self.row++ scanner.row++
self.column = 0 scanner.column = 0
} else { } else {
self.column++ scanner.column++
} }
} }
return return
} }
func (self *scanner) unreadChar() (err error) { func (scanner *scanner) unreadChar() (err error) {
if err = self.stream.UnreadByte(); err == nil { if err = scanner.stream.UnreadByte(); err == nil {
if self.column--; self.column == 0 { if scanner.column--; scanner.column == 0 {
if self.row--; self.row == 0 { if scanner.row--; scanner.row == 0 {
err = errors.New("unread beyond the stream boundary") err = errors.New("unread beyond the stream boundary")
} else { } else {
self.column = 1 scanner.column = 1
} }
} }
} }
return return
} }
func (self *scanner) lastPos() (r, c int) { func (scanner *scanner) lastPos() (r, c int) {
if self.prev != nil { if scanner.prev != nil {
r = self.prev.row r = scanner.prev.row
c = self.prev.col c = scanner.prev.col
} }
return return
} }
func (self *scanner) Previous() *Token { func (scanner *scanner) Previous() *Token {
return self.prev return scanner.prev
} }
func (self *scanner) Next() (tk *Token) { func (scanner *scanner) Next() (tk *Token) {
self.prev = self.current scanner.prev = scanner.current
tk = self.current tk = scanner.current
self.current = self.fetchNextToken() scanner.current = scanner.fetchNextToken()
return tk return tk
} }
func (self *scanner) fetchNextToken() (tk *Token) { func (scanner *scanner) fetchNextToken() (tk *Token) {
var ch byte var ch byte
if err := self.skipBlanks(); err != nil { if err := scanner.skipBlanks(); err != nil {
return self.makeErrorToken(err) return scanner.makeErrorToken(err)
} }
escape := false escape := false
for { for {
ch, _ = self.readChar() ch, _ = scanner.readChar()
switch ch { switch ch {
case '+': case '+':
if next, _ := self.peek(); next == '+' { if next, _ := scanner.peek(); next == '+' {
tk = self.moveOn(SymDoublePlus, ch, next) tk = scanner.moveOn(SymDoublePlus, ch, next)
} else if next == '=' { } else if next == '=' {
tk = self.moveOn(SymPlusEqual, ch, next) tk = scanner.moveOn(SymPlusEqual, ch, next)
} else { } else {
tk = self.makeToken(SymPlus, ch) tk = scanner.makeToken(SymPlus, ch)
} }
case '-': case '-':
if next, _ := self.peek(); next == '-' { if next, _ := scanner.peek(); next == '-' {
tk = self.moveOn(SymDoubleMinus, ch, next) tk = scanner.moveOn(SymDoubleMinus, ch, next)
} else if next == '=' { } else if next == '=' {
tk = self.moveOn(SymMinusEqual, ch, next) tk = scanner.moveOn(SymMinusEqual, ch, next)
} else { } else {
tk = self.makeToken(SymMinus, ch) tk = scanner.makeToken(SymMinus, ch)
} }
case '*': case '*':
if next, _ := self.peek(); next == '*' { if next, _ := scanner.peek(); next == '*' {
tk = self.moveOn(SymDoubleStar, ch, next) tk = scanner.moveOn(SymDoubleStar, ch, next)
// } else if next == '/' { // } else if next == '/' {
// tk = self.moveOn(SymClosedComment, ch, next) // tk = self.moveOn(SymClosedComment, ch, next)
} else { } else {
tk = self.makeToken(SymStar, ch) tk = scanner.makeToken(SymStar, ch)
} }
case '/': case '/':
if next, _ := self.peek(); next == '*' { if next, _ := scanner.peek(); next == '*' {
self.readChar() scanner.readChar()
tk = self.fetchBlockComment() tk = scanner.fetchBlockComment()
} else if next == '/' { } else if next == '/' {
self.readChar() scanner.readChar()
tk = self.fetchOnLineComment() tk = scanner.fetchOnLineComment()
} else { } else {
tk = self.makeToken(SymSlash, ch) tk = scanner.makeToken(SymSlash, ch)
} }
case '\\': case '\\':
if escape { if escape {
tk = self.makeToken(SymBackSlash, ch) tk = scanner.makeToken(SymBackSlash, ch)
escape = false escape = false
} else { } else {
escape = true escape = true
} }
case '|': case '|':
if next, _ := self.peek(); next == '|' { if next, _ := scanner.peek(); next == '|' {
tk = self.moveOn(SymDoubleVertBar, ch, next) tk = scanner.moveOn(SymDoubleVertBar, ch, next)
} else { } else {
tk = self.makeToken(SymVertBar, ch) tk = scanner.makeToken(SymVertBar, ch)
} }
case ',': case ',':
tk = self.makeToken(SymComma, ch) tk = scanner.makeToken(SymComma, ch)
case '^': case '^':
tk = self.makeToken(SymCaret, ch) tk = scanner.makeToken(SymCaret, ch)
case ':': case ':':
if next, _ := self.peek(); next == ':' { if next, _ := scanner.peek(); next == ':' {
tk = self.moveOn(SymDoubleColon, ch, next) tk = scanner.moveOn(SymDoubleColon, ch, next)
} else { } else {
tk = self.makeToken(SymColon, ch) tk = scanner.makeToken(SymColon, ch)
} }
case ';': case ';':
tk = self.makeToken(SymSemiColon, ch) tk = scanner.makeToken(SymSemiColon, ch)
case '.': case '.':
//if next, _ := self.peek(); next >= '0' && next <= '9' { //if next, _ := self.peek(); next >= '0' && next <= '9' {
// tk = self.parseNumber(ch) // tk = self.parseNumber(ch)
//} else if next == '/' { //} else if next == '/' {
if next, _ := self.peek(); next == '/' { if next, _ := scanner.peek(); next == '/' {
tk = self.moveOn(SymDotSlash, ch, next) tk = scanner.moveOn(SymDotSlash, ch, next)
} else if next == '.' { } else if next == '.' {
if next1, _ := self.peek(); next1 == '.' { if next1, _ := scanner.peek(); next1 == '.' {
tk = self.moveOn(SymTripleDot, ch, next, next1) tk = scanner.moveOn(SymTripleDot, ch, next, next1)
} else { } else {
tk = self.moveOn(SymDoubleDot, ch, next) tk = scanner.moveOn(SymDoubleDot, ch, next)
} }
} else { } else {
tk = self.makeToken(SymDot, ch) tk = scanner.makeToken(SymDot, ch)
} }
case '\'': case '\'':
if escape { if escape {
tk = self.makeToken(SymQuote, ch) tk = scanner.makeToken(SymQuote, ch)
escape = false escape = false
} else { } else {
tk = self.fetchString(ch) tk = scanner.fetchString(ch)
} }
case '"': case '"':
if escape { if escape {
tk = self.makeToken(SymDoubleQuote, ch) tk = scanner.makeToken(SymDoubleQuote, ch)
escape = false escape = false
} else { } else {
tk = self.fetchString(ch) tk = scanner.fetchString(ch)
} }
case '`': case '`':
tk = self.makeToken(SymBackTick, ch) tk = scanner.makeToken(SymBackTick, ch)
case '!': case '!':
if next, _ := self.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = self.moveOn(SymNotEqual, ch, next) tk = scanner.moveOn(SymNotEqual, ch, next)
} else { } else {
tk = self.makeToken(SymExclamation, ch) tk = scanner.makeToken(SymExclamation, ch)
} }
case '?': case '?':
if next, _ := self.peek(); next == '?' { if next, _ := scanner.peek(); next == '?' {
tk = self.moveOn(SymDoubleQuestion, ch, next) tk = scanner.moveOn(SymDoubleQuestion, ch, next)
} else if next, _ := self.peek(); next == '=' { } else if next == '=' {
tk = self.moveOn(SymQuestionEqual, ch, next) tk = scanner.moveOn(SymQuestionEqual, ch, next)
} else if next == '!' {
tk = scanner.moveOn(SymQuestionExclam, ch, next)
} else { } else {
tk = self.makeToken(SymQuestion, ch) tk = scanner.makeToken(SymQuestion, ch)
} }
case '&': case '&':
if next, _ := self.peek(); next == '&' { if next, _ := scanner.peek(); next == '&' {
tk = self.moveOn(SymDoubleAmpersand, ch, next) tk = scanner.moveOn(SymDoubleAmpersand, ch, next)
} else { } else {
tk = self.makeToken(SymAmpersand, ch) tk = scanner.makeToken(SymAmpersand, ch)
} }
case '%': case '%':
tk = self.makeToken(SymPercent, ch) tk = scanner.makeToken(SymPercent, ch)
case '#': case '#':
tk = self.makeToken(SymHash, ch) tk = scanner.makeToken(SymHash, ch)
case '@': case '@':
if next, _ := self.peek(); (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') { if next, _ := scanner.peek(); (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') {
self.readChar() scanner.readChar()
if tk = self.fetchIdentifier(next); tk.Sym == SymIdentifier { if tk = scanner.fetchIdentifier(next); tk.Sym == SymIdentifier {
//tk.Sym = SymIdRef //tk.Sym = SymIdRef
tk.source = "@" + tk.source tk.source = "@" + tk.source
} else { } else {
tk = self.makeErrorToken(fmt.Errorf("invalid variable reference %q", tk.source)) tk = scanner.makeErrorToken(fmt.Errorf("invalid variable reference %q", tk.source))
} }
} else if next == '@' { } else if next == '@' {
tk = self.moveOn(SymDoubleAt, ch, next) tk = scanner.moveOn(SymDoubleAt, ch, next)
} else { } else {
tk = self.makeToken(SymAt, ch) tk = scanner.makeToken(SymAt, ch)
} }
case '_': case '_':
tk = self.makeToken(SymUndescore, ch) tk = scanner.makeToken(SymUndescore, ch)
case '=': case '=':
if next, _ := self.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = self.moveOn(SymDoubleEqual, ch, next) tk = scanner.moveOn(SymDoubleEqual, ch, next)
} else { } else {
tk = self.makeToken(SymEqual, ch) tk = scanner.makeToken(SymEqual, ch)
} }
case '<': case '<':
if next, _ := self.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = self.moveOn(SymLessOrEqual, ch, next) tk = scanner.moveOn(SymLessOrEqual, ch, next)
} else if next == '<' { } else if next == '<' {
tk = self.moveOn(SymAppend, ch, next) tk = scanner.moveOn(SymAppend, ch, next)
} else if next == '>' { } else if next == '>' {
tk = self.moveOn(SymLessGreater, ch, next) tk = scanner.moveOn(SymLessGreater, ch, next)
} else { } else {
tk = self.makeToken(SymLess, ch) tk = scanner.makeToken(SymLess, ch)
} }
case '>': case '>':
if next, _ := self.peek(); next == '=' { if next, _ := scanner.peek(); next == '=' {
tk = self.moveOn(SymGreaterOrEqual, ch, next) tk = scanner.moveOn(SymGreaterOrEqual, ch, next)
} else if next == '>' { } else if next == '>' {
tk = self.moveOn(SymInsert, ch, next) tk = scanner.moveOn(SymInsert, ch, next)
} else { } else {
tk = self.makeToken(SymGreater, ch) tk = scanner.makeToken(SymGreater, ch)
} }
case '$': case '$':
if next, _ := self.peek(); next == '(' { if next, _ := scanner.peek(); next == '(' {
tk = self.moveOn(SymDollarRound, ch, next) tk = scanner.moveOn(SymDollarRound, ch, next)
tk.source += ")" tk.source += ")"
} else if next == '$' { } else if next == '$' {
tk = self.moveOn(SymDoubleDollar, ch, next) tk = scanner.moveOn(SymDoubleDollar, ch, next)
} else { } else {
tk = self.makeToken(SymDollar, ch) tk = scanner.makeToken(SymDollar, ch)
} }
case '(': case '(':
if next, _ := self.peek(); next == ')' { // if next, _ := scanner.peek(); next == ')' {
tk = self.moveOn(SymOpenClosedRound, ch, next) // tk = scanner.moveOn(SymOpenClosedRound, ch, next)
} else { // } else {
tk = self.makeToken(SymOpenRound, ch) tk = scanner.makeToken(SymOpenRound, ch)
} // }
case ')': case ')':
tk = self.makeToken(SymClosedRound, ch) tk = scanner.makeToken(SymClosedRound, ch)
case '[': case '[':
tk = self.makeToken(SymOpenSquare, ch) tk = scanner.makeToken(SymOpenSquare, ch)
case ']': case ']':
tk = self.makeToken(SymClosedSquare, ch) tk = scanner.makeToken(SymClosedSquare, ch)
case '{': case '{':
tk = self.makeToken(SymOpenBrace, ch) tk = scanner.makeToken(SymOpenBrace, ch)
case '}': case '}':
tk = self.makeToken(SymClosedBrace, ch) tk = scanner.makeToken(SymClosedBrace, ch)
case '~': case '~':
tk = self.makeToken(SymTilde, ch) tk = scanner.makeToken(SymTilde, ch)
case 0: case 0:
if escape { if escape {
tk = self.makeErrorToken(errors.New("incomplete escape sequence")) tk = scanner.makeErrorToken(errors.New("incomplete escape sequence"))
} }
escape = false escape = false
default: default:
if /*ch == '_' ||*/ (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') { if /*ch == '_' ||*/ (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') {
if tk = self.fetchIdentifier(ch); tk.Sym == SymKwFunc { if tk = scanner.fetchIdentifier(ch); tk.Sym == SymKwFunc {
if next, _ := self.peek(); next == '(' { if next, _ := scanner.peek(); next == '(' {
tk = self.moveOn(SymFuncDef, ch, next) tk = scanner.moveOn(SymFuncDef, ch, next)
} }
} }
} else if ch >= '0' && ch <= '9' { } else if ch >= '0' && ch <= '9' {
tk = self.parseNumber(ch) tk = scanner.parseNumber(ch)
} }
} }
if !escape { if !escape {
@@ -305,14 +307,14 @@ func (self *scanner) fetchNextToken() (tk *Token) {
} }
} }
if tk == nil { if tk == nil {
tk = NewErrorToken(self.row, self.column, fmt.Errorf("unknown symbol '%c'", ch)) tk = NewErrorToken(scanner.row, scanner.column, fmt.Errorf("unknown symbol '%c'", ch))
} }
return return
} }
func (self *scanner) sync(err error) error { func (scanner *scanner) sync(err error) error {
if err == nil { if err == nil {
err = self.unreadChar() err = scanner.unreadChar()
} }
return err return err
} }
@@ -333,32 +335,32 @@ func isHexDigit(ch byte) bool {
return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')
} }
func (self *scanner) initBase(sb *strings.Builder, currentFirstCh byte) (firstCh byte, numBase int, digitFunc func(byte) bool, err error) { func (scanner *scanner) initBase(currentFirstCh byte) (firstCh byte, numBase int, digitFunc func(byte) bool, err error) {
var ch byte var ch byte
var digitType string var digitType string
firstCh = currentFirstCh firstCh = currentFirstCh
digitFunc = isDecimalDigit digitFunc = isDecimalDigit
numBase = 10 numBase = 10
if ch, err = self.peek(); err == nil { if ch, err = scanner.peek(); err == nil {
if ch == 'b' || ch == 'B' { if ch == 'b' || ch == 'B' {
numBase = 2 numBase = 2
digitType = "binary" digitType = "binary"
self.readChar() scanner.readChar()
digitFunc = isBinaryDigit digitFunc = isBinaryDigit
firstCh, err = self.readChar() firstCh, err = scanner.readChar()
} else if ch == 'o' || ch == 'O' { } else if ch == 'o' || ch == 'O' {
numBase = 8 numBase = 8
digitType = "octal" digitType = "octal"
self.readChar() scanner.readChar()
digitFunc = isOctalDigit digitFunc = isOctalDigit
firstCh, err = self.readChar() firstCh, err = scanner.readChar()
} else if ch == 'x' || ch == 'X' { } else if ch == 'x' || ch == 'X' {
numBase = 16 numBase = 16
digitType = "hex" digitType = "hex"
self.readChar() scanner.readChar()
digitFunc = isHexDigit digitFunc = isHexDigit
firstCh, err = self.readChar() firstCh, err = scanner.readChar()
} }
if err == nil && !digitFunc(firstCh) { if err == nil && !digitFunc(firstCh) {
if len(digitType) == 0 { if len(digitType) == 0 {
@@ -372,7 +374,7 @@ func (self *scanner) initBase(sb *strings.Builder, currentFirstCh byte) (firstCh
return return
} }
func (self *scanner) parseNumber(firstCh byte) (tk *Token) { func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) {
var err error var err error
var ch byte var ch byte
var sym Symbol = SymInteger var sym Symbol = SymInteger
@@ -381,9 +383,9 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
var numBase = 10 var numBase = 10
if firstCh == '0' { if firstCh == '0' {
firstCh, numBase, isDigit, err = self.initBase(&sb, firstCh) firstCh, numBase, isDigit, err = scanner.initBase(firstCh)
} }
for ch = firstCh; err == nil && isDigit(ch); ch, err = self.readChar() { for ch = firstCh; err == nil && isDigit(ch); ch, err = scanner.readChar() {
sb.WriteByte(ch) sb.WriteByte(ch)
} }
@@ -391,9 +393,9 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
if err == nil && ch == '.' { if err == nil && ch == '.' {
sym = SymFloat sym = SymFloat
sb.WriteByte(ch) sb.WriteByte(ch)
ch, err = self.readChar() ch, err = scanner.readChar()
if ch >= '0' && ch <= '9' { if ch >= '0' && ch <= '9' {
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() { for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = scanner.readChar() {
sb.WriteByte(ch) sb.WriteByte(ch)
} }
} }
@@ -402,32 +404,32 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
if ch == 'e' || ch == 'E' { if ch == 'e' || ch == 'E' {
sym = SymFloat sym = SymFloat
sb.WriteByte(ch) sb.WriteByte(ch)
if ch, err = self.readChar(); err == nil { if ch, err = scanner.readChar(); err == nil {
if ch == '+' || ch == '-' { if ch == '+' || ch == '-' {
sb.WriteByte(ch) sb.WriteByte(ch)
ch, err = self.readChar() ch, err = scanner.readChar()
} }
if ch >= '0' && ch <= '9' { if ch >= '0' && ch <= '9' {
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() { for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = scanner.readChar() {
sb.WriteByte(ch) sb.WriteByte(ch)
} }
} else { } else {
err = fmt.Errorf("[%d:%d] expected integer exponent, got %c", self.row, self.column, ch) err = fmt.Errorf("[%d:%d] expected integer exponent, got %c", scanner.row, scanner.column, ch)
} }
} }
} else if ch == '(' { } else if ch == '(' {
sym = SymFraction sym = SymFraction
sb.WriteByte(ch) sb.WriteByte(ch)
ch, err = self.readChar() ch, err = scanner.readChar()
for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() { for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = scanner.readChar() {
sb.WriteByte(ch) sb.WriteByte(ch)
} }
if err == nil { if err == nil {
if ch != ')' { if ch != ')' {
err = fmt.Errorf("[%d:%d] expected ')', got '%c'", self.row, self.column, ch) err = fmt.Errorf("[%d:%d] expected ')', got '%c'", scanner.row, scanner.column, ch)
} else { } else {
sb.WriteByte(ch) sb.WriteByte(ch)
_, err = self.readChar() _, err = scanner.readChar()
} }
} }
} }
@@ -435,10 +437,10 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
} }
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
tk = self.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} else { } else {
var value any var value any
err = self.sync(err) err = scanner.sync(err) // TODO: Check this function
txt := sb.String() txt := sb.String()
if sym == SymFloat { if sym == SymFloat {
value, err = strconv.ParseFloat(txt, 64) value, err = strconv.ParseFloat(txt, 64)
@@ -448,39 +450,39 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
value, err = strconv.ParseInt(txt, numBase, 64) value, err = strconv.ParseInt(txt, numBase, 64)
} }
if err == nil { if err == nil {
tk = self.makeValueToken(sym, txt, value) tk = scanner.makeValueToken(sym, txt, value)
} else { } else {
tk = self.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} }
} }
return return
} }
func (self *scanner) fetchIdentifier(firstCh byte) (tk *Token) { func (scanner *scanner) fetchIdentifier(firstCh byte) (tk *Token) {
var err error var err error
var sb strings.Builder var sb strings.Builder
for ch := firstCh; err == nil && (ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')); ch, err = self.readChar() { for ch := firstCh; err == nil && (ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')); ch, err = scanner.readChar() {
sb.WriteByte(ch) sb.WriteByte(ch)
} }
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
tk = self.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} else if err = self.sync(err); err != nil && err != io.EOF { } else if err = scanner.sync(err); err != nil && err != io.EOF {
tk = self.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} else { } else {
txt := sb.String() txt := sb.String()
uptxt := strings.ToUpper(txt) uptxt := strings.ToUpper(txt)
if sym, ok := keywords[uptxt]; ok { if sym, ok := keywords[uptxt]; ok {
tk = self.makeKeywordToken(sym, uptxt) tk = scanner.makeKeywordToken(sym, uptxt)
} else if uptxt == `TRUE` { } else if uptxt == `TRUE` {
tk = self.makeValueToken(SymBool, txt, true) tk = scanner.makeValueToken(SymBool, txt, true)
} else if uptxt == `FALSE` { } else if uptxt == `FALSE` {
tk = self.makeValueToken(SymBool, txt, false) tk = scanner.makeValueToken(SymBool, txt, false)
} else if ch, _ := self.peek(); ch == '(' { } else if ch, _ := scanner.peek(); ch == '(' {
self.readChar() scanner.readChar()
tk = self.makeValueToken(SymFuncCall, txt+"(", txt) tk = scanner.makeValueToken(SymFuncCall, txt+"(", txt)
} else { } else {
tk = self.makeValueToken(SymIdentifier, txt, txt) tk = scanner.makeValueToken(SymIdentifier, txt, txt)
} }
} }
@@ -500,29 +502,29 @@ func (self *scanner) fetchIdentifier(firstCh byte) (tk *Token) {
return return
} }
func (self *scanner) fetchBlockComment() *Token { func (scanner *scanner) fetchBlockComment() *Token {
return self.fetchUntil(SymComment, false, '*', '/') return scanner.fetchUntil(SymComment, false, '*', '/')
} }
func (self *scanner) fetchOnLineComment() *Token { func (scanner *scanner) fetchOnLineComment() *Token {
return self.fetchUntil(SymComment, true, '\n') return scanner.fetchUntil(SymComment, true, '\n')
} }
func (self *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk *Token) { func (scanner *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk *Token) {
var err error var err error
var ch byte var ch byte
var sb strings.Builder var sb strings.Builder
var value string var value string
ring := NewByteSlider(len(endings)) ring := NewByteSlider(len(endings))
endReached := false endReached := false
for ch, err = self.readChar(); err == nil && !endReached; { for ch, err = scanner.readChar(); err == nil && !endReached; {
sb.WriteByte(ch) sb.WriteByte(ch)
ring.PushEnd(ch) ring.PushEnd(ch)
if ring.Equal(endings) { if ring.Equal(endings) {
value = sb.String()[0 : sb.Len()-len(endings)] value = sb.String()[0 : sb.Len()-len(endings)]
endReached = true endReached = true
} else { } else {
ch, err = self.readChar() ch, err = scanner.readChar()
} }
} }
if !endReached && allowEos { if !endReached && allowEos {
@@ -531,18 +533,18 @@ func (self *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk
} }
if endReached { if endReached {
tk = self.makeValueToken(sym, "", value) tk = scanner.makeValueToken(sym, "", value)
} else { } else {
tk = self.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} }
return return
} }
func (self *scanner) fetchString(termCh byte) (tk *Token) { func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
var err error var err error
var ch, prev byte var ch, prev byte
var sb strings.Builder var sb strings.Builder
for ch, err = self.readChar(); err == nil; ch, err = self.readChar() { for ch, err = scanner.readChar(); err == nil; ch, err = scanner.readChar() {
if prev == '\\' { if prev == '\\' {
switch ch { switch ch {
case '"': case '"':
@@ -570,65 +572,65 @@ func (self *scanner) fetchString(termCh byte) (tk *Token) {
} }
if err != nil { if err != nil {
if err == io.EOF { if err == io.EOF {
tk = self.makeErrorToken(errors.New("missing string termination \"")) tk = scanner.makeErrorToken(errors.New("missing string termination \""))
} else { } else {
tk = self.makeErrorToken(err) tk = scanner.makeErrorToken(err)
} }
} else { } else {
txt := sb.String() txt := sb.String()
tk = self.makeValueToken(SymString, `"`+txt+`"`, txt) tk = scanner.makeValueToken(SymString, `"`+txt+`"`, txt)
} }
return return
} }
func (self *scanner) peek() (next byte, err error) { func (scanner *scanner) peek() (next byte, err error) {
var one []byte var one []byte
if one, err = self.stream.Peek(1); err == nil { if one, err = scanner.stream.Peek(1); err == nil {
next = one[0] next = one[0]
} }
return return
} }
func (self *scanner) skipBlanks() (err error) { func (scanner *scanner) skipBlanks() (err error) {
var one []byte var one []byte
for one, err = self.stream.Peek(1); err == nil && one[0] <= 32; one, err = self.stream.Peek(1) { for one, err = scanner.stream.Peek(1); err == nil && one[0] <= 32; one, err = scanner.stream.Peek(1) {
self.readChar() scanner.readChar()
} }
return return
} }
func (self *scanner) translate(sym Symbol) Symbol { func (scanner *scanner) translate(sym Symbol) Symbol {
if self.translations != nil { if scanner.translations != nil {
if translatedSym, ok := self.translations[sym]; ok { if translatedSym, ok := scanner.translations[sym]; ok {
return translatedSym return translatedSym
} }
} }
return sym return sym
} }
func (self *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) { func (scanner *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
tk = NewToken(self.row, self.column, self.translate(sym), string(chars)) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
for i := 1; i < len(chars); i++ { for i := 1; i < len(chars); i++ {
self.readChar() scanner.readChar()
} }
return return
} }
func (self *scanner) makeToken(sym Symbol, chars ...byte) (tk *Token) { func (scanner *scanner) makeToken(sym Symbol, chars ...byte) (tk *Token) {
tk = NewToken(self.row, self.column, self.translate(sym), string(chars)) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
return return
} }
func (self *scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) { func (scanner *scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) {
tk = NewToken(self.row, self.column, self.translate(sym), upperCaseKeyword) tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), upperCaseKeyword)
return return
} }
func (self *scanner) makeValueToken(sym Symbol, source string, value any) (tk *Token) { func (scanner *scanner) makeValueToken(sym Symbol, source string, value any) (tk *Token) {
tk = NewValueToken(self.row, self.column, self.translate(sym), source, value) tk = NewValueToken(scanner.row, scanner.column, scanner.translate(sym), source, value)
return return
} }
func (self *scanner) makeErrorToken(err error) *Token { func (scanner *scanner) makeErrorToken(err error) *Token {
return NewErrorToken(self.row, self.column, err) return NewErrorToken(scanner.row, scanner.column, err)
} }
+17 -1
View File
@@ -25,7 +25,7 @@ func NewSimpleStore() *SimpleStore {
} }
func filterRefName(name string) bool { return name[0] != '@' } func filterRefName(name string) bool { return name[0] != '@' }
func filterPrivName(name string) bool { return name[0] != '_' } //func filterPrivName(name string) bool { return name[0] != '_' }
func (ctx *SimpleStore) SetParent(parentCtx ExprContext) { func (ctx *SimpleStore) SetParent(parentCtx ExprContext) {
ctx.parent = parentCtx ctx.parent = parentCtx
@@ -132,6 +132,14 @@ func (ctx *SimpleStore) EnumVars(acceptor func(name string) (accept bool)) (varN
return return
} }
func (ctx *SimpleStore) VarCount() int {
return len(ctx.varStore)
}
func (ctx *SimpleStore) DeleteVar(varName string) {
delete(ctx.varStore, varName)
}
func (ctx *SimpleStore) GetFuncInfo(name string) (info ExprFunc, exists bool) { func (ctx *SimpleStore) GetFuncInfo(name string) (info ExprFunc, exists bool) {
info, exists = ctx.funcStore[name] info, exists = ctx.funcStore[name]
return return
@@ -163,6 +171,14 @@ func (ctx *SimpleStore) EnumFuncs(acceptor func(name string) (accept bool)) (fun
return return
} }
func (ctx *SimpleStore) FuncCount() int {
return len(ctx.funcStore)
}
func (ctx *SimpleStore) DeleteFunc(funcName string) {
delete(ctx.funcStore, funcName)
}
func (ctx *SimpleStore) Call(name string, args []any) (result any, err error) { func (ctx *SimpleStore) Call(name string, args []any) (result any, err error) {
if info, exists := GetLocalFuncInfo(ctx, name); exists { if info, exists := GetLocalFuncInfo(ctx, name); exists {
functor := info.Functor() functor := info.Functor()
+13 -10
View File
@@ -57,16 +57,17 @@ const (
SymTilde // 46: '~' SymTilde // 46: '~'
SymDoubleQuestion // 47: '??' SymDoubleQuestion // 47: '??'
SymQuestionEqual // 48: '?=' SymQuestionEqual // 48: '?='
SymDoubleAt // 49: '@@' SymQuestionExclam // 49: '?!'
SymDoubleColon // 50: '::' SymDoubleAt // 50: '@@'
SymInsert // 51: '>>' SymDoubleColon // 51: '::'
SymAppend // 52: '<<' SymInsert // 52: '>>'
SymCaret // 53: '^' SymAppend // 53: '<<'
SymDollarRound // 54: '$(' SymCaret // 54: '^'
SymOpenClosedRound // 55: '()' SymDollarRound // 55: '$('
SymDoubleDollar // 56: '$$' SymOpenClosedRound // 56: '()'
SymDoubleDot // 57: '..' SymDoubleDollar // 57: '$$'
SymTripleDot // 58: '...' SymDoubleDot // 58: '..'
SymTripleDot // 59: '...'
SymChangeSign SymChangeSign
SymUnchangeSign SymUnchangeSign
SymIdentifier SymIdentifier
@@ -105,6 +106,7 @@ const (
SymKwIn SymKwIn
SymKwInclude SymKwInclude
SymKwNil SymKwNil
SymKwUnset
) )
var keywords map[string]Symbol var keywords map[string]Symbol
@@ -122,5 +124,6 @@ func init() {
"NOT": SymKwNot, "NOT": SymKwNot,
"OR": SymKwOr, "OR": SymKwOr,
"NIL": SymKwNil, "NIL": SymKwNil,
"UNSET": SymKwUnset,
} }
} }
+5 -5
View File
@@ -45,13 +45,13 @@ func TestBoolNoShortcut(t *testing.T) {
/* 5 */ {`not "true"`, false, nil}, /* 5 */ {`not "true"`, false, nil},
/* 6 */ {`not "false"`, false, nil}, /* 6 */ {`not "false"`, false, nil},
/* 7 */ {`not ""`, true, nil}, /* 7 */ {`not ""`, true, nil},
/* 8 */ {`not []`, nil, errors.New(`[1:4] prefix/postfix operator "NOT" do not support operand '[]' [list]`)}, /* 8 */ {`not []`, nil, `[1:4] prefix/postfix operator "NOT" do not support operand '[]' [list]`},
/* 9 */ {`true and false`, false, nil}, /* 9 */ {`true and false`, false, nil},
/* 10 */ {`true and []`, nil, errors.New(`[1:9] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "AND"`)}, /* 10 */ {`true and []`, nil, `[1:9] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "AND"`},
/* 11 */ {`[] and false`, nil, errors.New(`[1:7] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "AND"`)}, /* 11 */ {`[] and false`, nil, `[1:7] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "AND"`},
/* 12 */ {`true or false`, true, nil}, /* 12 */ {`true or false`, true, nil},
/* 13 */ {`true or []`, nil, errors.New(`[1:8] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "OR"`)}, /* 13 */ {`true or []`, nil, `[1:8] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "OR"`},
/* 14 */ {`[] or false`, nil, errors.New(`[1:6] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "OR"`)}, /* 14 */ {`[] or false`, nil, `[1:6] left operand '[]' [list] and right operand 'false' [bool] are not compatible with operator "OR"`},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
+9 -11
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -21,9 +20,9 @@ func TestFuncBase(t *testing.T) {
/* 6 */ {`int(3.1)`, int64(3), nil}, /* 6 */ {`int(3.1)`, int64(3), nil},
/* 7 */ {`int(3.9)`, int64(3), nil}, /* 7 */ {`int(3.9)`, int64(3), nil},
/* 8 */ {`int("432")`, int64(432), nil}, /* 8 */ {`int("432")`, int64(432), nil},
/* 9 */ {`int("1.5")`, nil, errors.New(`strconv.Atoi: parsing "1.5": invalid syntax`)}, /* 9 */ {`int("1.5")`, nil, `strconv.Atoi: parsing "1.5": invalid syntax`},
/* 10 */ {`int("432", 4)`, nil, errors.New(`int(): too much params -- expected 1, got 2`)}, /* 10 */ {`int("432", 4)`, nil, `int(): too much params -- expected 1, got 2`},
/* 11 */ {`int(nil)`, nil, errors.New(`int(): can't convert nil to int`)}, /* 11 */ {`int(nil)`, nil, `int(): can't convert nil to int`},
/* 12 */ {`isInt(2+1)`, true, nil}, /* 12 */ {`isInt(2+1)`, true, nil},
/* 13 */ {`isInt(3.1)`, false, nil}, /* 13 */ {`isInt(3.1)`, false, nil},
/* 14 */ {`isFloat(3.1)`, true, nil}, /* 14 */ {`isFloat(3.1)`, true, nil},
@@ -42,9 +41,9 @@ func TestFuncBase(t *testing.T) {
/* 27 */ {`dec(2.0)`, float64(2), nil}, /* 27 */ {`dec(2.0)`, float64(2), nil},
/* 28 */ {`dec("2.0")`, float64(2), nil}, /* 28 */ {`dec("2.0")`, float64(2), nil},
/* 29 */ {`dec(true)`, float64(1), nil}, /* 29 */ {`dec(true)`, float64(1), nil},
/* 30 */ {`dec(true")`, nil, errors.New("[1:11] missing string termination \"")}, /* 30 */ {`dec(true")`, nil, `[1:11] missing string termination "`},
/* 31 */ {`dec()`, nil, errors.New(`dec(): too few params -- expected 1, got 0`)}, /* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`},
/* 32 */ {`dec(1,2,3)`, nil, errors.New(`dec(): too much params -- expected 1, got 3`)}, /* 32 */ {`dec(1,2,3)`, nil, `dec(): too much params -- expected 1, got 3`},
/* 33 */ {`isBool(false)`, true, nil}, /* 33 */ {`isBool(false)`, true, nil},
/* 34 */ {`fract(1|2)`, newFraction(1, 2), nil}, /* 34 */ {`fract(1|2)`, newFraction(1, 2), nil},
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil}, /* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
@@ -53,15 +52,14 @@ func TestFuncBase(t *testing.T) {
/* 38 */ {`bool(1.0)`, true, nil}, /* 38 */ {`bool(1.0)`, true, nil},
/* 39 */ {`bool("1")`, true, nil}, /* 39 */ {`bool("1")`, true, nil},
/* 40 */ {`bool(false)`, false, nil}, /* 40 */ {`bool(false)`, false, nil},
/* 41 */ {`bool([1])`, nil, errors.New(`bool(): can't convert list to bool`)}, /* 41 */ {`bool([1])`, nil, `bool(): can't convert list to bool`},
/* 42 */ {`dec(false)`, float64(0), nil}, /* 42 */ {`dec(false)`, float64(0), nil},
/* 43 */ {`dec(1|2)`, float64(0.5), nil}, /* 43 */ {`dec(1|2)`, float64(0.5), nil},
/* 44 */ {`dec([1])`, nil, errors.New(`dec(): can't convert list to float`)}, /* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
// /* 45 */ {`string([1])`, nil, errors.New(`string(): can't convert list to string`)}, // /* 45 */ {`string([1])`, nil, `string(): can't convert list to string`},
} }
t.Setenv("EXPR_PATH", ".") t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 2)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+1 -1
View File
@@ -21,7 +21,7 @@ func TestFuncFmt(t *testing.T) {
//t.Setenv("EXPR_PATH", ".") //t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 19) // runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+1 -1
View File
@@ -19,6 +19,6 @@ func TestFuncImport(t *testing.T) {
t.Setenv("EXPR_PATH", "test-resources") t.Setenv("EXPR_PATH", "test-resources")
// parserTestSpec(t, section, inputs, 69) // runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+50
View File
@@ -0,0 +1,50 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// t_builtin-iterator.go
package expr
import (
"testing"
)
func TestFuncRun(t *testing.T) {
section := "Builtin-Iterator"
inputs := []inputType{
/* 1 */ {`builtin "iterator"; it=$(1,2,3); run(it)`, nil, nil},
/* 2 */ {`builtin "iterator"; run($(1,2,3), func(index,item){item+10})`, nil, nil},
/* 3 */ {`builtin "iterator"; run($(1,2,3), func(index,item){it_status=it_status+item; true}, {"it_status":0})`, int64(6), nil},
/* 4 */ {`builtin ["iterator", "fmt"]; run($(1,2,3), func(index,item){println(item+10)})`, nil, nil},
/* 5 */ {`builtin "iterator"; run(nil)`, nil, `paramter "iterator" must be an iterator, passed <nil> [nil]`},
/* 6 */ {`builtin "iterator"; run($(1,2,3), nil)`, nil, `paramter "operator" must be a function, passed <nil> [nil]`},
/* 7 */ {`builtin "iterator"; run($(1,2,3), func(){1}, "prrr")`, nil, `paramter "vars" must be a dictionary, passed prrr [string]`},
}
//t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 3)
runTestSuite(t, section, inputs)
}
// func TestFmt(t *testing.T) {
// section := "Builtin-Fmt"
// text := "ciao mondo"
// inputs := []inputType{
// /* 1 */ {fmt.Sprintf(`println("%s")`, text), int64(11), nil},
// }
// // t.Setenv("EXPR_PATH", ".")
// var b bytes.Buffer
// ctx := NewSimpleStore()
// currentStdout := SetCtrl(ctx, ControlStdout, &b)
// runCtxTestSuite(t, ctx, section, inputs)
// SetCtrl(ctx, ControlStdout, currentStdout)
// if b.String() != text+"\n" {
// t.Errorf("println(): Got: %q, Want: %q", b.String(), text+"\n")
// }
// }
+2 -3
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -14,7 +13,7 @@ func TestFuncMathArith(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil}, /* 1 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil},
/* 2 */ {`builtin "math.arith"; add(1,2,3)`, int64(6), nil}, /* 2 */ {`builtin "math.arith"; add(1,2,3)`, int64(6), nil},
/* 3 */ {`builtin "math.arith"; mulX(1,2,3)`, nil, errors.New(`unknown function mulX()`)}, /* 3 */ {`builtin "math.arith"; mulX(1,2,3)`, nil, `unknown function mulX()`},
/* 4 */ {`builtin "math.arith"; add(1+4,3+2,5*(3-2))`, int64(15), nil}, /* 4 */ {`builtin "math.arith"; add(1+4,3+2,5*(3-2))`, int64(15), nil},
/* 5 */ {`builtin "math.arith"; add(add(1+4),3+2,5*(3-2))`, int64(15), nil}, /* 5 */ {`builtin "math.arith"; add(add(1+4),3+2,5*(3-2))`, int64(15), nil},
/* 6 */ {`builtin "math.arith"; add(add(1,4),/*3+2,*/5*(3-2))`, int64(10), nil}, /* 6 */ {`builtin "math.arith"; add(add(1,4),/*3+2,*/5*(3-2))`, int64(10), nil},
@@ -24,6 +23,6 @@ func TestFuncMathArith(t *testing.T) {
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 69) // runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+10 -11
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -14,23 +13,23 @@ func TestFuncOs(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`builtin "os.file"`, int64(1), nil}, /* 1 */ {`builtin "os.file"`, int64(1), nil},
/* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil}, /* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil},
/* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, errors.New(`open /etc/hostsX: no such file or directory`)}, /* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, `open /etc/hostsX: no such file or directory`},
/* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil}, /* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil},
/* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWriteText(handle, "bye-bye"); fileClose(handle)`, true, nil}, /* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWriteText(handle, "bye-bye"); fileClose(handle)`, true, nil},
/* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileReadText(handle, "-"); fileClose(handle);word`, "bye", nil}, /* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileReadText(handle, "-"); fileClose(handle);word`, "bye", nil},
/* 7 */ {`builtin "os.file"; word=fileReadText(nil, "-")`, nil, errors.New(`fileReadText(): invalid file handle`)}, /* 7 */ {`builtin "os.file"; word=fileReadText(nil, "-")`, nil, `fileReadText(): invalid file handle`},
/* 8 */ {`builtin "os.file"; fileWriteText(nil, "bye")`, nil, errors.New(`fileWriteText(): invalid file handle`)}, /* 8 */ {`builtin "os.file"; fileWriteText(nil, "bye")`, nil, `fileWriteText(): invalid file handle`},
/* 9 */ {`builtin "os.file"; handle=fileOpen()`, nil, errors.New(`fileOpen(): too few params -- expected 1, got 0`)}, /* 9 */ {`builtin "os.file"; handle=fileOpen()`, nil, `fileOpen(): too few params -- expected 1, got 0`},
/* 10 */ {`builtin "os.file"; handle=fileOpen(123)`, nil, errors.New(`fileOpen(): missing or invalid file path`)}, /* 10 */ {`builtin "os.file"; handle=fileOpen(123)`, nil, `fileOpen(): missing or invalid file path`},
/* 11 */ {`builtin "os.file"; handle=fileCreate(123)`, nil, errors.New(`fileCreate(): missing or invalid file path`)}, /* 11 */ {`builtin "os.file"; handle=fileCreate(123)`, nil, `fileCreate(): missing or invalid file path`},
/* 12 */ {`builtin "os.file"; handle=fileAppend(123)`, nil, errors.New(`fileAppend(): missing or invalid file path`)}, /* 12 */ {`builtin "os.file"; handle=fileAppend(123)`, nil, `fileAppend(): missing or invalid file path`},
/* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, errors.New(`fileClose(): invalid file handle`)}, /* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, `fileClose(): invalid file handle`},
/* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil}, /* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil},
/* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, errors.New(`fileReadTextAll(): invalid file handle 123 [int64]`)}, /* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, `fileReadTextAll(): invalid file handle 123 [int64]`},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 69) // runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+6 -7
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -16,8 +15,8 @@ func TestFuncString(t *testing.T) {
/* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil}, /* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil},
/* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil}, /* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil},
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil}, /* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil},
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, errors.New(`strJoin(): the "separator" parameter must be a string, got a integer (1)`)}, /* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, `strJoin(): the "separator" parameter must be a string, got a integer (1)`},
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, errors.New(`strJoin(): expected string, got integer (2)`)}, /* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, `strJoin(): expected string, got integer (2)`},
/* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil}, /* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil},
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil}, /* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
/* 8 */ {`builtin "string"; strSub("0123456789", -3,2)`, "78", nil}, /* 8 */ {`builtin "string"; strSub("0123456789", -3,2)`, "78", nil},
@@ -25,13 +24,13 @@ func TestFuncString(t *testing.T) {
/* 10 */ {`builtin "string"; strSub("0123456789")`, "0123456789", nil}, /* 10 */ {`builtin "string"; strSub("0123456789")`, "0123456789", nil},
/* 11 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "012")`, true, nil}, /* 11 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "012")`, true, nil},
/* 12 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "0125")`, false, nil}, /* 12 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "0125")`, false, nil},
/* 13 */ {`builtin "string"; strStartsWith("0123456789")`, nil, errors.New(`strStartsWith(): too few params -- expected 2 or more, got 1`)}, /* 13 */ {`builtin "string"; strStartsWith("0123456789")`, nil, `strStartsWith(): too few params -- expected 2 or more, got 1`},
/* 14 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "789")`, true, nil}, /* 14 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "789")`, true, nil},
/* 15 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "0125")`, false, nil}, /* 15 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "0125")`, false, nil},
/* 16 */ {`builtin "string"; strEndsWith("0123456789")`, nil, errors.New(`strEndsWith(): too few params -- expected 2 or more, got 1`)}, /* 16 */ {`builtin "string"; strEndsWith("0123456789")`, nil, `strEndsWith(): too few params -- expected 2 or more, got 1`},
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil}, /* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil},
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, errors.New(`strJoin(): expected string, got integer (1)`)}, /* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, `strJoin(): expected string, got integer (1)`},
/* 19 */ {`builtin "string"; strJoin()`, nil, errors.New(`strJoin(): too few params -- expected 1 or more, got 0`)}, /* 19 */ {`builtin "string"; strJoin()`, nil, `strJoin(): too few params -- expected 1 or more, got 0`},
/* 69 */ /*{`builtin "string"; $$global`, `vars: { /* 69 */ /*{`builtin "string"; $$global`, `vars: {
} }
+16 -16
View File
@@ -5,6 +5,7 @@
package expr package expr
import ( import (
"errors"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
@@ -13,7 +14,7 @@ import (
type inputType struct { type inputType struct {
source string source string
wantResult any wantResult any
wantErr error wantErr any
} }
func runCtxTestSuiteSpec(t *testing.T, ctx ExprContext, section string, inputs []inputType, spec ...int) { func runCtxTestSuiteSpec(t *testing.T, ctx ExprContext, section string, inputs []inputType, spec ...int) {
@@ -52,20 +53,17 @@ func runCtxTestSuite(t *testing.T, ctx ExprContext, section string, inputs []inp
} }
func runTestSuite(t *testing.T, section string, inputs []inputType) { func runTestSuite(t *testing.T, section string, inputs []inputType) {
runCtxTestSuite(t, nil, section, inputs) runCtxTestSuite(t, nil, section, inputs)
/* }
succeeded := 0
failed := 0
for i, input := range inputs { func getWantedError(input *inputType) error {
good := doTest(t, nil, section, &input, i+1) var wantErr error
if good { var ok bool
succeeded++ if wantErr, ok = input.wantErr.(error); !ok {
} else { if msg, ok := input.wantErr.(string); ok {
failed++ wantErr = errors.New(msg)
} }
} }
t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed) return wantErr
*/
} }
func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, count int) (good bool) { func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, count int) (good bool) {
@@ -73,12 +71,14 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
var gotResult any var gotResult any
var gotErr error var gotErr error
wantErr := getWantedError(input)
parser := NewParser() parser := NewParser()
if ctx == nil { if ctx == nil {
ctx = NewSimpleStore() ctx = NewSimpleStore()
} }
logTest(t, count, section, input.source, input.wantResult, input.wantErr) logTest(t, count, section, input.source, input.wantResult, wantErr)
r := strings.NewReader(input.source) r := strings.NewReader(input.source)
scanner := NewScanner(r, DefaultTranslations()) scanner := NewScanner(r, DefaultTranslations())
@@ -95,9 +95,9 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
good = false good = false
} }
if gotErr != input.wantErr { if gotErr != wantErr {
if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) { if wantErr == nil || gotErr == nil || (gotErr.Error() != wantErr.Error()) {
t.Errorf("%d: %q -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, input.wantErr) t.Errorf("%d: %q -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, wantErr)
good = false good = false
} }
} }
+8 -8
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -18,21 +17,22 @@ func TestFractionsParser(t *testing.T) {
/* 4 */ {`1|2 * 1`, newFraction(1, 2), nil}, /* 4 */ {`1|2 * 1`, newFraction(1, 2), nil},
/* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil}, /* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil},
/* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil}, /* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil},
/* 7 */ {`1|"5"`, nil, errors.New(`denominator must be integer, got string (5)`)}, /* 7 */ {`1|"5"`, nil, `denominator must be integer, got string (5)`},
/* 8 */ {`"1"|5`, nil, errors.New(`numerator must be integer, got string (1)`)}, /* 8 */ {`"1"|5`, nil, `numerator must be integer, got string (1)`},
/* 9 */ {`1|+5`, nil, errors.New(`[1:3] infix operator "|" requires two non-nil operands, got 1`)}, /* 9 */ {`1|+5`, nil, `[1:3] infix operator "|" requires two non-nil operands, got 1`},
/* 10 */ {`1|(-2)`, newFraction(-1, 2), nil}, /* 10 */ {`1|(-2)`, newFraction(-1, 2), nil},
/* 11 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil}, /* 11 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil},
/* 12 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil}, /* 12 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil},
/* 13 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil}, /* 13 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil},
/* 14 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil}, /* 14 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil},
/* 15 */ {`1|0`, nil, errors.New(`division by zero`)}, /* 15 */ {`1|0`, nil, `division by zero`},
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil}, /* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
/* 17 */ {`fract("")`, (*FractionType)(nil), errors.New(`bad syntax`)}, /* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil}, /* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil}, /* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
/* 20 */ {`fract("1a")`, (*FractionType)(nil), errors.New(`strconv.ParseInt: parsing "1a": invalid syntax`)}, /* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
/* 21 */ {`fract(1,0)`, nil, errors.New(`fract(): division by zero`)}, /* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`},
/* 22 */ {`string(1|2)`, "1|2", nil},
} }
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+9 -7
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -17,26 +16,28 @@ func TestFuncs(t *testing.T) {
/* 3 */ {`double=func(x){2*x}; double(3)`, int64(6), nil}, /* 3 */ {`double=func(x){2*x}; double(3)`, int64(6), nil},
/* 4 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil}, /* 4 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil},
/* 5 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil}, /* 5 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil},
/* 6 */ {`@x="hello"; @x`, nil, errors.New(`[1:3] variable references are not allowed in top level expressions: "@x"`)}, /* 6 */ {`@x="hello"; @x`, nil, `[1:3] variable references are not allowed in top level expressions: "@x"`},
/* 7 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil}, /* 7 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil},
/* 8 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil}, /* 8 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil},
/* 9 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, errors.New(`undefined variable or function "x"`)}, /* 9 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, `undefined variable or function "x"`},
/* 10 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil}, /* 10 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil},
/* 11 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil}, /* 11 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil},
/* 12 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil}, /* 12 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
/* 13 */ {`two=func(){2}; four=func(f){f()+f()}; four(two)`, int64(4), nil}, /* 13 */ {`two=func(){2}; four=func(f){f()+f()}; four(two)`, int64(4), nil},
/* 14 */ {`two=func(){2}; two(123)`, nil, errors.New(`two(): too much params -- expected 0, got 1`)}, /* 14 */ {`two=func(){2}; two(123)`, nil, `two(): too much params -- expected 0, got 1`},
/* 15 */ {`f=func(x,n=2){x+n}; f(3)`, int64(5), nil}, /* 15 */ {`f=func(x,n=2){x+n}; f(3)`, int64(5), nil},
/* 16 */ {`f=func(x,n=2,y){x+n}`, nil, errors.New(`[1:16] can't mix default and non-default parameters`)}, /* 16 */ {`f=func(x,n=2,y){x+n}`, nil, `[1:16] can't mix default and non-default parameters`},
/* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)}, /* 17 */ {`f=func(x,n){1}; f(3,4,)`, nil, `[1:24] expected "function-param-value", got ")"`},
/* 18 */ {`factory=func(base){func(){@base=base+1}}; inc10=factory(10); inc5=factory(5); inc10(); inc5(); inc10()`, int64(12), nil}, /* 18 */ {`factory=func(base){func(){@base=base+1}}; inc10=factory(10); inc5=factory(5); inc10(); inc5(); inc10()`, int64(12), nil},
/* 19 */ {`f=func(a,y=1,z="sos"){}; string(f)`, `f(a, y=1, z="sos"):any{}`, nil}, /* 19 */ {`f=func(a,y=1,z="sos"){}; string(f)`, `f(a, y=1, z="sos"):any{}`, nil},
// /* 20 */ {`a=[func(){3}]; a[0]()`, int64(3), nil},
// /* 20 */ {`m={}; m["f"]=func(){3}; m["f"]()`, int64(3), nil},
// /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)}, // /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 17) //runTestSuiteSpec(t, section, inputs, 20)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
@@ -53,6 +54,7 @@ func TestFunctionToStringSimple(t *testing.T) {
} }
} }
func TestFunctionGetFunc(t *testing.T) { func TestFunctionGetFunc(t *testing.T) {
source := NewGolangFunctor(dummy) source := NewGolangFunctor(dummy)
want := ExprFunc(nil) want := ExprFunc(nil)
+1 -2
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -17,7 +16,7 @@ func TestCollections(t *testing.T) {
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil}, /* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
/* 4 */ {`"abcdef"[:]`, "abcdef", nil}, /* 4 */ {`"abcdef"[:]`, "abcdef", nil},
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil}, // /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
/* 5 */ {`"abcdef"[1:2:3]`, nil, errors.New(`[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`)}, /* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`},
} }
t.Setenv("EXPR_PATH", ".") t.Setenv("EXPR_PATH", ".")
+14 -8
View File
@@ -7,20 +7,26 @@ package expr
import "testing" import "testing"
func TestIteratorParser(t *testing.T) { func TestIteratorParser(t *testing.T) {
section := "Iterator"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ()it`, int64(0), nil}, /* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ^it`, int64(0), nil},
/* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil}, /* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
/* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil}, /* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil},
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil}, /* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ^it`, int64(0), nil},
/* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil}, /* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil}, /* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil}, /* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it++; it.index`, int64(0), nil}, /* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it++; it.index`, int64(0), nil},
/* 10 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, true, nil}, /* 9 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, true, nil},
/* 11 */ {`it=$(1,2,3); it++`, int64(1), nil}, /* 10 */ {`it=$(1,2,3); it++`, int64(1), nil},
/* 11 */ {`it=$(1,2,3); it++; it.reset; it++`, int64(1), nil},
/* 12 */ {`it=$([1,2,3,4],1); it++`, int64(2), nil},
/* 13 */ {`it=$([1,2,3,4],1,3); it++; it++;`, int64(3), nil},
/* 14 */ {`it=$([1,2,3,4],1,3,2); it++; it++;`, int64(4), nil},
/* 15 */ {`it=$([1,2,3,4],1,2,2); it++; it++;`, nil, `EOF`},
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
} }
// inputs1 := []inputType{
// /* 1 */ {`0?{}`, nil, nil}, //runTestSuiteSpec(t, section, inputs, 4)
// } runTestSuite(t, section, inputs)
runTestSuite(t, "Iterator", inputs)
} }
+11 -12
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -22,14 +21,14 @@ func TestListParser(t *testing.T) {
/* 7 */ {`add([1,4,3,2])`, int64(10), nil}, /* 7 */ {`add([1,4,3,2])`, int64(10), nil},
/* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil}, /* 8 */ {`add([1,[2,2],3,2])`, int64(10), nil},
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil}, /* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
/* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 (2 in 1) has wrong type string, number expected`)}, /* 10 */ {`add([1,"hello"])`, nil, `add(): param nr 2 (2 in 1) has wrong type string, number expected`},
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil}, /* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
/* 12 */ {`[1,2,3] << 2+2`, newListA(int64(1), int64(2), int64(3), int64(4)), nil}, /* 12 */ {`[1,2,3] << 2+2`, newListA(int64(1), int64(2), int64(3), int64(4)), nil},
/* 13 */ {`2-1 >> [2,3]`, newListA(int64(1), int64(2), int64(3)), nil}, /* 13 */ {`2-1 >> [2,3]`, newListA(int64(1), int64(2), int64(3)), nil},
/* 14 */ {`[1,2,3][1]`, int64(2), nil}, /* 14 */ {`[1,2,3][1]`, int64(2), nil},
/* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil}, /* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil},
/* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil}, /* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil},
/* 17 */ {`list=["one","two","three"]; list[10]`, nil, errors.New(`[1:34] index 10 out of bounds`)}, /* 17 */ {`list=["one","two","three"]; list[10]`, nil, `[1:34] index 10 out of bounds`},
/* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil}, /* 18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil},
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil}, /* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil}, /* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
@@ -37,18 +36,18 @@ func TestListParser(t *testing.T) {
/* 22 */ {`a=[1,2]; (a)<<3`, newListA(int64(1), int64(2), int64(3)), nil}, /* 22 */ {`a=[1,2]; (a)<<3`, newListA(int64(1), int64(2), int64(3)), nil},
/* 23 */ {`a=[1,2]; (a)<<3; a`, newListA(int64(1), int64(2)), nil}, /* 23 */ {`a=[1,2]; (a)<<3; a`, newListA(int64(1), int64(2)), nil},
/* 24 */ {`["a","b","c","d"][1]`, "b", nil}, /* 24 */ {`["a","b","c","d"][1]`, "b", nil},
/* 25 */ {`["a","b","c","d"][1,1]`, nil, errors.New(`[1:19] one index only is allowed`)}, /* 25 */ {`["a","b","c","d"][1,1]`, nil, `[1:19] one index only is allowed`},
/* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil}, /* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
/* 27 */ {`["a", "b", "c"] << ;`, nil, errors.New(`[1:18] infix operator "<<" requires two non-nil operands, got 1`)}, /* 27 */ {`["a", "b", "c"] << ;`, nil, `[1:18] infix operator "<<" requires two non-nil operands, got 1`},
/* 28 */ {`2 << 3;`, nil, errors.New(`[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator "<<"`)}, /* 28 */ {`2 << 3;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator "<<"`},
/* 29 */ {`but >> ["a", "b", "c"]`, nil, errors.New(`[1:6] infix operator ">>" requires two non-nil operands, got 0`)}, /* 29 */ {`but >> ["a", "b", "c"]`, nil, `[1:6] infix operator ">>" requires two non-nil operands, got 0`},
/* 30 */ {`2 >> 3;`, nil, errors.New(`[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator ">>"`)}, /* 30 */ {`2 >> 3;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator ">>"`},
/* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), nil}, /* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), nil},
/* 33 */ {`a=[1,2]; 5>>a`, newListA(int64(5), int64(1), int64(2)), nil}, /* 33 */ {`a=[1,2]; 5>>a`, newListA(int64(5), int64(1), int64(2)), nil},
/* 34 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil}, /* 34 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
/* 35 */ {`L=[1,2]; L[5]=9; L`, nil, errors.New(`index 5 out of bounds (0, 1)`)}, /* 35 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`},
/* 36 */ {`L=[1,2]; L[]=9; L`, nil, errors.New(`[1:12] index/key specification expected, got [] [list]`)}, /* 36 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [list]`},
/* 37 */ {`L=[1,2]; L[nil]=9;`, nil, errors.New(`[1:12] index/key is nil`)}, /* 37 */ {`L=[1,2]; L[nil]=9;`, nil, `[1:12] index/key is nil`},
/* 38 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil}, /* 38 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil},
/* 39 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil}, /* 39 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil},
/* 40 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil}, /* 40 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil},
@@ -57,6 +56,6 @@ func TestListParser(t *testing.T) {
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 17) // runTestSuiteSpec(t, section, inputs, 1)
runTestSuite(t, section, inputs) runTestSuite(t, section, inputs)
} }
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// t_operator_test.go
package expr
import (
"testing"
)
func TestOperator(t *testing.T) {
section := "Operator"
inputs := []inputType{
/* 1 */ {`a=1; unset "a"; a`, nil, `undefined variable or function "a"`},
/* 2 */ {`a=1; unset ["a", "b"]`, int64(1), nil},
/* 3 */ {`f=func(){3}; unset "f()"`, int64(1), nil},
}
// t.Setenv("EXPR_PATH", ".")
//runTestSuiteSpec(t, section, inputs, 3)
runTestSuite(t, section, inputs)
}
+35 -30
View File
@@ -5,7 +5,6 @@
package expr package expr
import ( import (
"errors"
"testing" "testing"
) )
@@ -49,13 +48,13 @@ func TestGeneralParser(t *testing.T) {
/* 34 */ {`(((1)))`, int64(1), nil}, /* 34 */ {`(((1)))`, int64(1), nil},
/* 35 */ {`var2="abc"; "uno_" + var2`, `uno_abc`, nil}, /* 35 */ {`var2="abc"; "uno_" + var2`, `uno_abc`, nil},
/* 36 */ {`0 || 0.0 && "hello"`, false, nil}, /* 36 */ {`0 || 0.0 && "hello"`, false, nil},
/* 37 */ {`"s" + true`, nil, errors.New(`[1:6] left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`)}, /* 37 */ {`"s" + true`, nil, `[1:6] left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`},
/* 38 */ {`+false`, nil, errors.New(`[1:2] prefix/postfix operator "+" do not support operand 'false' [bool]`)}, /* 38 */ {`+false`, nil, `[1:2] prefix/postfix operator "+" do not support operand 'false' [bool]`},
/* 39 */ {`false // very simple expression`, false, nil}, /* 39 */ {`false // very simple expression`, false, nil},
/* 40 */ {`1 + // Missing right operator`, nil, errors.New(`[1:4] infix operator "+" requires two non-nil operands, got 1`)}, /* 40 */ {`1 + // Missing right operator`, nil, `[1:4] infix operator "+" requires two non-nil operands, got 1`},
/* 41 */ {"", nil, nil}, /* 41 */ {"", nil, nil},
/* 42 */ {"4!", int64(24), nil}, /* 42 */ {"4!", int64(24), nil},
/* 43 */ {"(-4)!", nil, errors.New(`factorial of a negative integer (-4) is not allowed`)}, /* 43 */ {"(-4)!", nil, `factorial of a negative integer (-4) is not allowed`},
/* 44 */ {"-4!", int64(-24), nil}, /* 44 */ {"-4!", int64(-24), nil},
/* 45 */ {"1.5 < 7", true, nil}, /* 45 */ {"1.5 < 7", true, nil},
/* 46 */ {"1.5 > 7", false, nil}, /* 46 */ {"1.5 > 7", false, nil},
@@ -67,20 +66,20 @@ func TestGeneralParser(t *testing.T) {
/* 52 */ {`"1.5" > "7"`, false, nil}, /* 52 */ {`"1.5" > "7"`, false, nil},
/* 53 */ {`"1.5" == "7"`, false, nil}, /* 53 */ {`"1.5" == "7"`, false, nil},
/* 54 */ {`"1.5" != "7"`, true, nil}, /* 54 */ {`"1.5" != "7"`, true, nil},
/* 55 */ {"1.5 < ", nil, errors.New(`[1:6] infix operator "<" requires two non-nil operands, got 1`)}, /* 55 */ {"1.5 < ", nil, `[1:6] infix operator "<" requires two non-nil operands, got 1`},
/* 56 */ {"1.5 > ", nil, errors.New(`[1:6] infix operator ">" requires two non-nil operands, got 1`)}, /* 56 */ {"1.5 > ", nil, `[1:6] infix operator ">" requires two non-nil operands, got 1`},
/* 57 */ {"1.5 <= ", nil, errors.New(`[1:6] infix operator "<=" requires two non-nil operands, got 1`)}, /* 57 */ {"1.5 <= ", nil, `[1:6] infix operator "<=" requires two non-nil operands, got 1`},
/* 58 */ {"1.5 >= ", nil, errors.New(`[1:6] infix operator ">=" requires two non-nil operands, got 1`)}, /* 58 */ {"1.5 >= ", nil, `[1:6] infix operator ">=" requires two non-nil operands, got 1`},
/* 59 */ {"1.5 != ", nil, errors.New(`[1:6] infix operator "!=" requires two non-nil operands, got 1`)}, /* 59 */ {"1.5 != ", nil, `[1:6] infix operator "!=" requires two non-nil operands, got 1`},
/* 60 */ {"1.5 == ", nil, errors.New(`[1:6] infix operator "==" requires two non-nil operands, got 1`)}, /* 60 */ {"1.5 == ", nil, `[1:6] infix operator "==" requires two non-nil operands, got 1`},
/* 61 */ {`"1.5" < `, nil, errors.New(`[1:8] infix operator "<" requires two non-nil operands, got 1`)}, /* 61 */ {`"1.5" < `, nil, `[1:8] infix operator "<" requires two non-nil operands, got 1`},
/* 62 */ {`"1.5" > `, nil, errors.New(`[1:8] infix operator ">" requires two non-nil operands, got 1`)}, /* 62 */ {`"1.5" > `, nil, `[1:8] infix operator ">" requires two non-nil operands, got 1`},
/* 63 */ {`"1.5" == `, nil, errors.New(`[1:8] infix operator "==" requires two non-nil operands, got 1`)}, /* 63 */ {`"1.5" == `, nil, `[1:8] infix operator "==" requires two non-nil operands, got 1`},
/* 64 */ {`"1.5" != `, nil, errors.New(`[1:8] infix operator "!=" requires two non-nil operands, got 1`)}, /* 64 */ {`"1.5" != `, nil, `[1:8] infix operator "!=" requires two non-nil operands, got 1`},
/* 65 */ {"+1.5", float64(1.5), nil}, /* 65 */ {"+1.5", float64(1.5), nil},
/* 66 */ {"+", nil, errors.New(`[1:2] prefix operator "+" requires one not nil operand`)}, /* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one not nil operand`},
/* 67 */ {"4 / 0", nil, errors.New(`division by zero`)}, /* 67 */ {"4 / 0", nil, `division by zero`},
/* 68 */ {"4.0 / 0", nil, errors.New(`division by zero`)}, /* 68 */ {"4.0 / 0", nil, `division by zero`},
/* 69 */ {"4.0 / \n2", float64(2.0), nil}, /* 69 */ {"4.0 / \n2", float64(2.0), nil},
/* 70 */ {`123`, int64(123), nil}, /* 70 */ {`123`, int64(123), nil},
/* 71 */ {`1.`, float64(1.0), nil}, /* 71 */ {`1.`, float64(1.0), nil},
@@ -92,7 +91,7 @@ func TestGeneralParser(t *testing.T) {
/* 77 */ {`5 % 2`, int64(1), nil}, /* 77 */ {`5 % 2`, int64(1), nil},
/* 78 */ {`5 % (-2)`, int64(1), nil}, /* 78 */ {`5 % (-2)`, int64(1), nil},
/* 79 */ {`-5 % 2`, int64(-1), nil}, /* 79 */ {`-5 % 2`, int64(-1), nil},
/* 80 */ {`5 % 2.0`, nil, errors.New(`[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`)}, /* 80 */ {`5 % 2.0`, nil, `[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`},
/* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil}, /* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil}, /* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
/* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil}, /* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
@@ -105,12 +104,12 @@ func TestGeneralParser(t *testing.T) {
/* 90 */ {`x=2 but x*10`, int64(20), nil}, /* 90 */ {`x=2 but x*10`, int64(20), nil},
/* 91 */ {`false and true`, false, nil}, /* 91 */ {`false and true`, false, nil},
/* 92 */ {`false and (x==2)`, false, nil}, /* 92 */ {`false and (x==2)`, false, nil},
/* 93 */ {`false and (x=2 but x==2) or x==2`, nil, errors.New(`undefined variable or function "x"`)}, /* 93 */ {`false and (x=2 but x==2) or x==2`, nil, `undefined variable or function "x"`},
/* 94 */ {`false or true`, true, nil}, /* 94 */ {`false or true`, true, nil},
/* 95 */ {`false or (x==2)`, nil, errors.New(`undefined variable or function "x"`)}, /* 95 */ {`false or (x==2)`, nil, `undefined variable or function "x"`},
/* 96 */ {`a=5; a`, int64(5), nil}, /* 96 */ {`a=5; a`, int64(5), nil},
/* 97 */ {`2=5`, nil, errors.New(`[1:2] left operand of "=" must be a variable or a collection's item`)}, /* 97 */ {`2=5`, nil, `[1:2] left operand of "=" must be a variable or a collection's item`},
/* 98 */ {`2+a=5`, nil, errors.New(`[1:3] left operand of "=" must be a variable or a collection's item`)}, /* 98 */ {`2+a=5`, nil, `[1:3] left operand of "=" must be a variable or a collection's item`},
/* 99 */ {`2+(a=5)`, int64(7), nil}, /* 99 */ {`2+(a=5)`, int64(7), nil},
/* 100 */ {`x ?? "default"`, "default", nil}, /* 100 */ {`x ?? "default"`, "default", nil},
/* 101 */ {`x="hello"; x ?? "default"`, "hello", nil}, /* 101 */ {`x="hello"; x ?? "default"`, "hello", nil},
@@ -120,22 +119,28 @@ func TestGeneralParser(t *testing.T) {
/* 105 */ {`1 ? {"a"} : {"b"}`, "b", nil}, /* 105 */ {`1 ? {"a"} : {"b"}`, "b", nil},
/* 106 */ {`10 ? {"a"} : {"b"} :: {"c"}`, "c", nil}, /* 106 */ {`10 ? {"a"} : {"b"} :: {"c"}`, "c", nil},
/* 107 */ {`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`, "b", nil}, /* 107 */ {`10 ? {"a"} :[true, 2+8] {"b"} :: {"c"}`, "b", nil},
/* 108 */ {`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}`, nil, errors.New(`[1:34] case list in default clause`)}, /* 108 */ {`10 ? {"a"} :[true, 2+8] {"b"} ::[10] {"c"}`, nil, `[1:34] case list in default clause`},
/* 109 */ {`10 ? {"a"} :[10] {x="b" but x} :: {"c"}`, "b", nil}, /* 109 */ {`10 ? {"a"} :[10] {x="b" but x} :: {"c"}`, "b", nil},
/* 110 */ {`10 ? {"a"} :[10] {x="b"; x} :: {"c"}`, "b", nil}, /* 110 */ {`10 ? {"a"} :[10] {x="b"; x} :: {"c"}`, "b", nil},
/* 111 */ {`10 ? {"a"} : {"b"}`, nil, errors.New(`[1:3] no case catches the value (10) of the selection expression`)}, /* 111 */ {`10 ? {"a"} : {"b"}`, nil, `[1:3] no case catches the value (10) of the selection expression`},
/* 112 */ {`10 ? {"a"} :: {"b"} : {"c"}`, nil, errors.New(`[1:22] selector-case outside of a selector context`)}, /* 112 */ {`10 ? {"a"} :: {"b"} : {"c"}`, nil, `[1:22] selector-case outside of a selector context`},
/* 113 */ {`1 ? {"a"} : {"b"} ? ["a"] {"A"} :["b"] {"B"}`, "B", nil}, /* 113 */ {`1 ? {"a"} : {"b"} ? ["a"] {"A"} :["b"] {"B"}`, "B", nil},
/* 114 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil}, /* 114 */ {`2 + 1 ? {"a"} : {"b"} * 3`, "2bbb", nil},
/* 115 */ {`nil`, nil, nil}, /* 115 */ {`nil`, nil, nil},
/* 116 */ {`null`, nil, errors.New(`undefined variable or function "null"`)}, /* 116 */ {`null`, nil, `undefined variable or function "null"`},
/* 117 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)}, /* 117 */ {`{"key"}`, nil, `[1:8] expected ":", got "}"`},
/* 118 */ {`{"key":}`, nil, errors.New(`[1:9] expected "dictionary-value", got "}"`)}, /* 118 */ {`{"key":}`, nil, `[1:9] expected "dictionary-value", got "}"`},
/* 119 */ {`{}`, &DictType{}, nil}, /* 119 */ {`{}`, &DictType{}, nil},
/* 120 */ {`v=10; v++; v`, int64(11), nil}, /* 120 */ {`v=10; v++; v`, int64(11), nil},
/* 121 */ {`1+1|2+0.5`, float64(2), nil}, /* 121 */ {`1+1|2+0.5`, float64(2), nil},
/* 122 */ {`1.2()`, newFraction(6, 5), nil}, /* 122 */ {`1.2()`, newFraction(6, 5), nil},
/* 123 */ {`1|(2-2)`, nil, errors.New(`division by zero`)}, /* 123 */ {`1|(2-2)`, nil, `division by zero`},
/* 124 */ {`x="abc"; x ?! #x`, int64(3), nil},
/* 125 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
/* 126 */ {`x ?! (x+1)`, nil, nil},
/* 127 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
/* 128 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
/* 129 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
} }
// t.Setenv("EXPR_PATH", ".") // t.Setenv("EXPR_PATH", ".")
+2 -3
View File
@@ -6,7 +6,6 @@ package expr
import ( import (
"errors" "errors"
"fmt"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
@@ -69,9 +68,9 @@ func TestScanner(t *testing.T) {
// continue // continue
// } // }
if input.wantErr == nil { if input.wantErr == nil {
t.Log(fmt.Sprintf("[+]Test nr %2d -- %q", i+1, input.source)) t.Logf("[+]Test nr %2d -- %q", i+1, input.source)
} else { } else {
t.Log(fmt.Sprintf("[-]Test nr %2d -- %q", i+1, input.source)) t.Logf("[-]Test nr %2d -- %q", i+1, input.source)
} }
r := strings.NewReader(input.source) r := strings.NewReader(input.source)
+76 -76
View File
@@ -26,7 +26,7 @@ const (
priSign priSign
priFact priFact
priIterValue priIterValue
priCoalesce priDefault
priIncDec priIncDec
priDot priDot
priValue priValue
@@ -53,21 +53,21 @@ type term struct {
evalFunc evalFuncType evalFunc evalFuncType
} }
func (self *term) String() string { func (term *term) String() string {
var sb strings.Builder var sb strings.Builder
self.toString(&sb) term.toString(&sb)
return sb.String() return sb.String()
} }
func (self *term) toString(sb *strings.Builder) { func (term *term) toString(sb *strings.Builder) {
if self.position == posLeaf { if term.position == posLeaf {
sb.WriteString(self.tk.String()) sb.WriteString(term.tk.String())
} else { } else {
sb.WriteByte('[') sb.WriteByte('[')
sb.WriteString(self.tk.String()) sb.WriteString(term.tk.String())
if self.children != nil { if term.children != nil {
sb.WriteByte('(') sb.WriteByte('(')
for i, c := range self.children { for i, c := range term.children {
if i > 0 { if i > 0 {
sb.WriteByte(' ') sb.WriteByte(' ')
} }
@@ -79,17 +79,17 @@ func (self *term) toString(sb *strings.Builder) {
} }
} }
func (self *term) getChildrenCount() (count int) { func (term *term) getChildrenCount() (count int) {
if self.position == posLeaf || self.children == nil { if term.position == posLeaf || term.children == nil {
count = 0 count = 0
} else { } else {
count = len(self.children) count = len(term.children)
} }
return return
} }
func (self *term) getRoom() (room int) { func (term *term) getRoom() (room int) {
switch self.position { switch term.position {
case posLeaf: case posLeaf:
room = 0 room = 0
case posInfix: case posInfix:
@@ -102,139 +102,139 @@ func (self *term) getRoom() (room int) {
return return
} }
func (self *term) isComplete() bool { func (term *term) isComplete() bool {
return self.getChildrenCount() == self.getRoom() return term.getChildrenCount() == term.getRoom()
} }
func (self *term) removeLastChild() (child *term) { func (term *term) removeLastChild() (child *term) {
if self.children != nil { if term.children != nil {
child = self.children[len(self.children)-1] child = term.children[len(term.children)-1]
self.children = self.children[0 : len(self.children)-1] term.children = term.children[0 : len(term.children)-1]
} else { } else {
panic("Can't get last child") panic("Can't get last child")
} }
return return
} }
func (self *term) isLeaf() bool { func (term *term) isLeaf() bool {
return self.position == posLeaf return term.position == posLeaf
} }
func (self *term) getPriority() termPriority { func (term *term) getPriority() termPriority {
return self.priority return term.priority
} }
func (self *term) setParent(parent *term) { func (term *term) setParent(parent *term) {
self.parent = parent term.parent = parent
if parent != nil && len(parent.children) < cap(parent.children) { if parent != nil && len(parent.children) < cap(parent.children) {
parent.children = append(parent.children, self) parent.children = append(parent.children, term)
} }
} }
func (self *term) symbol() Symbol { func (term *term) symbol() Symbol {
return self.tk.Sym return term.tk.Sym
} }
func (self *term) source() string { func (term *term) source() string {
return self.tk.source return term.tk.source
} }
func (self *term) value() any { func (term *term) value() any {
return self.tk.Value return term.tk.Value
} }
func (self *term) compute(ctx ExprContext) (v any, err error) { func (term *term) compute(ctx ExprContext) (v any, err error) {
if self.evalFunc == nil { if term.evalFunc == nil {
err = self.tk.Errorf("undefined eval-func for %q term", self.source()) err = term.Errorf("undefined eval-func for %q term", term.source())
} else { } else {
v, err = self.evalFunc(ctx, self) v, err = term.evalFunc(ctx, term)
} }
return return
} }
func (self *term) toInt(computedValue any, valueDescription string) (i int, err error) { // func (term *term) toInt(computedValue any, valueDescription string) (i int, err error) {
if index64, ok := computedValue.(int64); ok { // if index64, ok := computedValue.(int64); ok {
i = int(index64) // i = int(index64)
} else { // } else {
err = self.Errorf("%s, got %s (%v)", valueDescription, TypeName(computedValue), computedValue) // err = term.Errorf("%s, got %s (%v)", valueDescription, TypeName(computedValue), computedValue)
} // }
return // return
} // }
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error { func (term *term) errIncompatibleTypes(leftValue, rightValue any) error {
leftType := TypeName(leftValue) leftType := TypeName(leftValue)
leftText := getFormatted(leftValue, Truncate) leftText := getFormatted(leftValue, Truncate)
rightType := TypeName(rightValue) rightType := TypeName(rightValue)
rightText := getFormatted(rightValue, Truncate) rightText := getFormatted(rightValue, Truncate)
return self.tk.Errorf( return term.tk.Errorf(
"left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q", "left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q",
leftText, leftType, leftText, leftType,
rightText, rightType, rightText, rightType,
self.source()) term.source())
} }
func (self *term) errIncompatibleType(value any) error { func (term *term) errIncompatibleType(value any) error {
return self.tk.Errorf( return term.tk.Errorf(
"prefix/postfix operator %q do not support operand '%v' [%s]", "prefix/postfix operator %q do not support operand '%v' [%s]",
self.source(), value, TypeName(value)) term.source(), value, TypeName(value))
} }
func (self *term) Errorf(template string, args ...any) (err error) { func (term *term) Errorf(template string, args ...any) (err error) {
err = self.tk.Errorf(template, args...) err = term.tk.Errorf(template, args...)
return return
} }
func (self *term) checkOperands() (err error) { func (term *term) checkOperands() (err error) {
switch self.position { switch term.position {
case posInfix: case posInfix:
if self.children == nil || len(self.children) != 2 || self.anyChildrenNil() { if term.children == nil || len(term.children) != 2 || term.anyChildrenNil() {
err = self.tk.Errorf("infix operator %q requires two non-nil operands, got %d", self.source(), self.getChildrenCount()) err = term.tk.Errorf("infix operator %q requires two non-nil operands, got %d", term.source(), term.getChildrenCount())
} }
case posPrefix: case posPrefix:
if self.children == nil || len(self.children) != 1 || self.children[0] == nil { if term.children == nil || len(term.children) != 1 || term.children[0] == nil {
err = self.tk.Errorf("prefix operator %q requires one not nil operand", self.tk.String()) err = term.tk.Errorf("prefix operator %q requires one not nil operand", term.tk.String())
} }
case posPostfix: case posPostfix:
if self.children == nil || len(self.children) != 1 || self.anyChildrenNil() { if term.children == nil || len(term.children) != 1 || term.anyChildrenNil() {
err = self.tk.Errorf("postfix operator %q requires one not nil operand", self.tk.String()) err = term.tk.Errorf("postfix operator %q requires one not nil operand", term.tk.String())
} }
case posMultifix: case posMultifix:
if self.children == nil || len(self.children) < 3 || self.anyChildrenNil() { if term.children == nil || len(term.children) < 3 || term.anyChildrenNil() {
err = self.tk.Errorf("infix operator %q requires at least three not operands, got %d", self.source(), self.getChildrenCount()) err = term.tk.Errorf("infix operator %q requires at least three not operands, got %d", term.source(), term.getChildrenCount())
} }
} }
return return
} }
func (self *term) anyChildrenNil() bool { func (term *term) anyChildrenNil() bool {
for _, child := range self.children { for _, child := range term.children {
if child == nil { if child == nil {
return true return true
} }
} }
return false return false
} }
func (self *term) evalInfix(ctx ExprContext) (leftValue, rightValue any, err error) { func (term *term) evalInfix(ctx ExprContext) (leftValue, rightValue any, err error) {
if err = self.checkOperands(); err == nil { if err = term.checkOperands(); err == nil {
if leftValue, err = self.children[0].compute(ctx); err == nil { if leftValue, err = term.children[0].compute(ctx); err == nil {
rightValue, err = self.children[1].compute(ctx) rightValue, err = term.children[1].compute(ctx)
} }
} }
return return
} }
func (self *term) evalPrefix(ctx ExprContext) (childValue any, err error) { func (term *term) evalPrefix(ctx ExprContext) (childValue any, err error) {
if err = self.checkOperands(); err == nil { if err = term.checkOperands(); err == nil {
childValue, err = self.children[0].compute(ctx) childValue, err = term.children[0].compute(ctx)
} }
return return
} }
// NOTE Temporary solution to support function parameters with default value // NOTE Temporary solution to support function parameters with default value
func (self *term) forceChild(c *term) { func (t *term) forceChild(c *term) {
if self.children == nil { if t.children == nil {
self.children = make([]*term, 0, 1) t.children = make([]*term, 0, 1)
} }
self.children = append(self.children, c) t.children = append(t.children, c)
} }
+15
View File
@@ -0,0 +1,15 @@
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}
}
, "filter":func(item,index) { item > 0 }
, "map": func(item,index) { item * 2 }
}
/* Example
;
it=$(ds,3);
add(it)
*/
+13 -13
View File
@@ -33,7 +33,7 @@ func (tk *Token) String() string {
return fmt.Sprintf("%v", tk.Value) return fmt.Sprintf("%v", tk.Value)
} }
} }
return fmt.Sprintf("%s", tk.source) return tk.source
} }
func NewToken(row, col int, sym Symbol, source string) *Token { func NewToken(row, col int, sym Symbol, source string) *Token {
@@ -71,31 +71,31 @@ func (tk *Token) IsSymbol(sym Symbol) bool {
return tk.Sym == sym return tk.Sym == sym
} }
func (self *Token) Errorf(template string, args ...any) (err error) { func (tk *Token) Errorf(template string, args ...any) (err error) {
err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", self.row, self.col)+template, args...) err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", tk.row, tk.col)+template, args...)
return return
} }
func (self *Token) Error() (err error) { func (tk *Token) Error() (err error) {
if self.Sym == SymError { if tk.Sym == SymError {
if msg, ok := self.Value.(error); ok { if msg, ok := tk.Value.(error); ok {
err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg) err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
} }
} }
return return
} }
func (self *Token) Errors(msg string) (err error) { func (tk *Token) Errors(msg string) (err error) {
err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg) err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
return return
} }
func (self *Token) ErrorExpectedGot(symbol string) (err error) { func (tk *Token) ErrorExpectedGot(symbol string) (err error) {
err = fmt.Errorf("[%d:%d] expected %q, got %q", self.row, self.col, symbol, self) err = fmt.Errorf("[%d:%d] expected %q, got %q", tk.row, tk.col, symbol, tk)
return return
} }
func (self *Token) ErrorExpectedGotString(symbol, got string) (err error) { func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
err = fmt.Errorf("[%d:%d] expected %q, got %q", self.row, self.col, symbol, got) err = fmt.Errorf("[%d:%d] expected %q, got %q", tk.row, tk.col, symbol, got)
return return
} }