Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ee365bacc | |||
| 20d8236325 | |||
| c39ee7cec0 | |||
| 9e4252173b | |||
| 2b80ba6789 | |||
| d7247f97c5 | |||
| 02df7f1c1f | |||
| b6b09b2fb1 | |||
| 0677180456 | |||
| b6952444ab | |||
| 6c3a071a02 | |||
| d7d03b4af8 | |||
| 49c5cb6a22 | |||
| 53d805a832 | |||
| b99ad5def1 | |||
| 0d44b8697b | |||
| 1a7e537921 | |||
| 807df0f3a8 | |||
| d7dd628fc9 | |||
| 1f57ba28dd | |||
| 92bd366380 | |||
| 7b93c5b4ac | |||
| 3ba8194ddb | |||
| 037565c41e | |||
| f8d12b1a93 | |||
| 518d4d8d65 | |||
| d64602cb00 | |||
| 4709248828 | |||
| 5ecf81412e | |||
| ff4db34f7b | |||
| 0f848071c2 | |||
| 6b3351b324 | |||
| 760c1ee6da | |||
| 5ab6876ea1 | |||
| 6268abda8c | |||
| d25bd325b7 | |||
| 01c04feea5 | |||
| 6fc689c46c | |||
| eccb0c4dc9 | |||
| e43823740f | |||
| 526982a564 | |||
| 252514943e | |||
| 32686fac62 | |||
| 52a627c747 | |||
| d91e7eb979 | |||
| cca3b76baa | |||
| 24e31997fc | |||
| 646710e180 | |||
| b38327b841 | |||
| fd912b2eb1 | |||
| 0e55f83d56 | |||
| 4725145d1c | |||
| edf8818f51 | |||
| 6211be8a8f | |||
| f50ddf48db | |||
| 76e01f12d2 | |||
| 406bced450 | |||
| 409dc86a92 | |||
| 4184221428 | |||
| 8cf8b36a26 | |||
| de87050188 | |||
| a1ec0cc611 | |||
| 8e5550bfa7 | |||
| 6ee21e10af | |||
| 5c44532790 | |||
| cb66c1ab19 | |||
| a28d24ba68 | |||
| 523349a204 | |||
| b185f1df3a | |||
| 5da5a61a42 | |||
| 6e9205abc4 | |||
| f61004fb5d | |||
| 321030c8d3 | |||
| 98fc89e84f | |||
| 778d00677d | |||
| ba3dbb7f02 | |||
| 7285109115 |
+1
-1
@@ -144,4 +144,4 @@ Variables and functions can be added to a context both programmatically and ad a
|
||||
|
||||
== Expressions syntax
|
||||
|
||||
See #TODO link to doc/Expr.html#
|
||||
See https://cdn.paas.portale-stac.it/howto/go-pkg/expr/doc/Expr.html[Expr documentation] for a complete reference of the expression syntax and available functions.
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -71,6 +72,10 @@ func boolFunc(ctx ExprContext, name string, args map[string]any) (result any, er
|
||||
result = v
|
||||
case string:
|
||||
result = len(v) > 0
|
||||
case *ListType:
|
||||
result = len(*v) > 0
|
||||
case *DictType:
|
||||
result = len(*v) > 0
|
||||
default:
|
||||
err = ErrCantConvert(name, v, "bool")
|
||||
}
|
||||
@@ -94,6 +99,8 @@ func intFunc(ctx ExprContext, name string, args map[string]any) (result any, err
|
||||
if i, err = strconv.Atoi(v); err == nil {
|
||||
result = int64(i)
|
||||
}
|
||||
case *FractionType:
|
||||
result = int64(v.num / v.den)
|
||||
default:
|
||||
err = ErrCantConvert(name, v, "int")
|
||||
}
|
||||
@@ -188,6 +195,78 @@ func fractFunc(ctx ExprContext, name string, args map[string]any) (result any, e
|
||||
// return
|
||||
// }
|
||||
|
||||
func evalFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[ParamSource].(string); ok {
|
||||
var expr Expr
|
||||
|
||||
parser := NewParser()
|
||||
if ctx == nil {
|
||||
ctx = NewSimpleStore()
|
||||
}
|
||||
|
||||
r := strings.NewReader(source)
|
||||
scanner := NewScanner(r, DefaultTranslations())
|
||||
|
||||
if expr, err = parser.Parse(scanner); err == nil {
|
||||
CtrlEnable(ctx, control_export_all)
|
||||
result, err = expr.Eval(ctx)
|
||||
}
|
||||
} else {
|
||||
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func varFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var varName string
|
||||
var ok bool
|
||||
|
||||
if varName, ok = args[ParamName].(string); !ok {
|
||||
return nil, ErrWrongParamType(name, ParamName, TypeString, args[ParamName])
|
||||
}
|
||||
|
||||
if result, ok = args[ParamValue]; ok && result != nil {
|
||||
ctx.GetParent().UnsafeSetVar(varName, result)
|
||||
// } else {
|
||||
// err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
// }
|
||||
} else if result, ok = ctx.GetVar(varName); !ok {
|
||||
err = ErrUnknownVar(name, varName)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var varName string
|
||||
var ok bool
|
||||
|
||||
if varName, ok = args[ParamName].(string); !ok {
|
||||
return nil, ErrWrongParamType(name, ParamName, TypeString, args[ParamName])
|
||||
}
|
||||
|
||||
if result, ok = args[ParamValue]; ok {
|
||||
ctx.GetParent().UnsafeSetVar(varName, result)
|
||||
} else {
|
||||
err = ErrWrongParamType(name, ParamValue, TypeAny, args[ParamValue])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func unsetFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var varName string
|
||||
var ok bool
|
||||
|
||||
if varName, ok = args[ParamName].(string); !ok {
|
||||
return nil, ErrWrongParamType(name, ParamName, TypeString, args[ParamName])
|
||||
} else {
|
||||
ctx.GetParent().DeleteVar(varName)
|
||||
result = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//// import
|
||||
|
||||
func ImportBuiltinsFuncs(ctx ExprContext) {
|
||||
anyParams := []ExprFuncParam{
|
||||
NewFuncParam(ParamValue),
|
||||
@@ -211,6 +290,25 @@ func ImportBuiltinsFuncs(ctx ExprContext) {
|
||||
NewFuncParam(ParamValue),
|
||||
NewFuncParamFlagDef(ParamDenominator, PfDefault, int64(1)),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("eval", NewGolangFunctor(evalFunc), TypeAny, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("var", NewGolangFunctor(varFunc), TypeAny, []ExprFuncParam{
|
||||
NewFuncParam(ParamName),
|
||||
NewFuncParamFlagDef(ParamValue, PfDefault, nil),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("set", NewGolangFunctor(setFunc), TypeAny, []ExprFuncParam{
|
||||
NewFuncParam(ParamName),
|
||||
NewFuncParam(ParamValue),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("unset", NewGolangFunctor(unsetFunc), TypeAny, []ExprFuncParam{
|
||||
NewFuncParam(ParamName),
|
||||
NewFuncParam(ParamValue),
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
|
||||
var expr *ast
|
||||
scanner := NewScanner(file, DefaultTranslations())
|
||||
parser := NewParser()
|
||||
if expr, err = parser.parseGeneral(scanner, true, true); err == nil {
|
||||
if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymEos); err == nil {
|
||||
result, err = expr.Eval(ctx)
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
+25
-17
@@ -23,9 +23,11 @@ func parseRunArgs(localCtx ExprContext, args map[string]any) (it Iterator, op Fu
|
||||
return
|
||||
}
|
||||
|
||||
if op, ok = args[iterParamOperator].(Functor); !ok && args[iterParamOperator] != nil {
|
||||
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], TypeName(args[iterParamOperator]))
|
||||
return
|
||||
if args[iterParamOperator] != nil {
|
||||
if op, ok = args[iterParamOperator].(Functor); !ok || op == nil {
|
||||
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], TypeName(args[iterParamOperator]))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var vars *DictType
|
||||
@@ -51,7 +53,7 @@ func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err
|
||||
var ok bool
|
||||
var op Functor
|
||||
var v any
|
||||
var usingDefaultOp = false
|
||||
// var usingDefaultOp = false
|
||||
var params map[string]any
|
||||
var item any
|
||||
|
||||
@@ -60,24 +62,27 @@ func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err
|
||||
|
||||
if it, op, err = parseRunArgs(localCtx, args); err != nil {
|
||||
return
|
||||
} else if op == nil {
|
||||
op = NewGolangFunctor(printLnFunc)
|
||||
usingDefaultOp = true
|
||||
// } else if op == nil {
|
||||
// op = NewGolangFunctor(printLnFunc)
|
||||
// usingDefaultOp = true
|
||||
}
|
||||
|
||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||
if usingDefaultOp {
|
||||
params = map[string]any{ParamItem: []any{item}}
|
||||
} else {
|
||||
params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
|
||||
}
|
||||
// if usingDefaultOp {
|
||||
// params = map[string]any{ParamItem: []any{item}}
|
||||
// } else {
|
||||
// params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
|
||||
// }
|
||||
|
||||
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
|
||||
break
|
||||
} else {
|
||||
var success bool
|
||||
if success, ok = ToBool(v); !success || !ok {
|
||||
if op != nil {
|
||||
params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
|
||||
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
|
||||
break
|
||||
} else {
|
||||
var success bool
|
||||
if success, ok = ToBool(v); !success || !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,6 +91,9 @@ func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err
|
||||
err = nil
|
||||
}
|
||||
if err == nil {
|
||||
if op == nil {
|
||||
ctx.UnsafeSetVar(iterVarStatus, it.Count())
|
||||
}
|
||||
result, _ = localCtx.GetVar(iterVarStatus)
|
||||
}
|
||||
return
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// builtin-os-file.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
)
|
||||
|
||||
const paramHandleOrPath = "handle-or-path"
|
||||
const fileReadTextIteratorType = "fileReadTextIterator"
|
||||
|
||||
type fileReadTextIterator struct {
|
||||
osReader *osReader
|
||||
index int
|
||||
count int
|
||||
line string
|
||||
autoClose bool
|
||||
}
|
||||
|
||||
func newReadTextIterator(r *osReader, autoClose bool) *fileReadTextIterator {
|
||||
return &fileReadTextIterator{osReader: r, index: -1, autoClose: autoClose}
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) TypeName() string {
|
||||
return fileReadTextIteratorType
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) String() string {
|
||||
if it.osReader != nil && it.osReader.fh != nil {
|
||||
return fmt.Sprintf("$(%s@%q)", fileReadTextIteratorType, it.osReader.fh.Name())
|
||||
}
|
||||
return fmt.Sprintf("$(%s@<nil>)", fileReadTextIteratorType)
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) Count() int {
|
||||
return it.count
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) Next() (item any, err error) { // must return io.EOF after the last item
|
||||
if it.osReader.fh != nil {
|
||||
if it.line, err = it.osReader.reader.ReadString('\n'); err == nil {
|
||||
it.index++
|
||||
it.count++
|
||||
item = it.line[0 : len(it.line)-1]
|
||||
} else if it.autoClose {
|
||||
it.Clean()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) Current() (item any, err error) {
|
||||
if len(it.line) > 0 {
|
||||
item = it.line[0 : len(it.line)-1]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) Index() int {
|
||||
return it.index
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) Reset() (err error) {
|
||||
if _, err = it.osReader.fh.Seek(0, io.SeekStart); err == nil {
|
||||
it.index = -1
|
||||
it.count = 0
|
||||
it.line = ""
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) HasOperation(name string) bool {
|
||||
return slices.Contains([]string{NextName, ResetName, IndexName, CountName, CurrentName, CleanName}, name)
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) Clean() (err error) {
|
||||
if it.osReader.fh != nil {
|
||||
if err = it.osReader.fh.Close(); err == nil {
|
||||
it.osReader = nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (it *fileReadTextIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
||||
switch name {
|
||||
case NextName:
|
||||
v, err = it.Next()
|
||||
case ResetName:
|
||||
err = it.Reset()
|
||||
case CleanName:
|
||||
err = it.Clean()
|
||||
case IndexName:
|
||||
v = int64(it.Index())
|
||||
case CurrentName:
|
||||
v, err = it.Current()
|
||||
case CountName:
|
||||
v = it.count
|
||||
default:
|
||||
err = errNoOperation(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func fileReadIteratorFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var handle *osReader
|
||||
var invalidFileHandle any
|
||||
var ok, autoClose bool
|
||||
|
||||
result = nil
|
||||
if handle, ok = args[paramHandleOrPath].(*osReader); !ok {
|
||||
if fileName, ok := args[paramHandleOrPath].(string); ok && len(fileName) > 0 {
|
||||
var handleAny any
|
||||
if handleAny, err = openFileFunc(ctx, name, map[string]any{ParamFilepath: fileName}); err != nil {
|
||||
return
|
||||
}
|
||||
if handleAny != nil {
|
||||
handle = handleAny.(*osReader)
|
||||
autoClose = true
|
||||
}
|
||||
} else {
|
||||
invalidFileHandle = args[paramHandleOrPath]
|
||||
}
|
||||
}
|
||||
|
||||
if handle != nil {
|
||||
result = newReadTextIterator(handle, autoClose)
|
||||
|
||||
}
|
||||
|
||||
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
||||
err = errInvalidFileHandle(name, invalidFileHandle)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// func ImportOsIterFuncs(ctx ExprContext) {
|
||||
// ctx.RegisterFunc("fileReadIterator", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{
|
||||
// NewFuncParam(paramHandleOrPath),
|
||||
// })
|
||||
// }
|
||||
|
||||
// func init() {
|
||||
// RegisterBuiltinModule("os.file", ImportOsIterFuncs, "Operating system file iterator functions")
|
||||
// }
|
||||
@@ -250,6 +250,10 @@ func ImportOsFuncs(ctx ExprContext) {
|
||||
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamHandle),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("fileReadIterator", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{
|
||||
NewFuncParam(paramHandleOrPath),
|
||||
})
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
@@ -200,6 +200,24 @@ func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any
|
||||
return
|
||||
}
|
||||
|
||||
func upperStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[ParamSource].(string); ok {
|
||||
result = strings.ToUpper(source)
|
||||
} else {
|
||||
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func lowerStrFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
if source, ok := args[ParamSource].(string); ok {
|
||||
result = strings.ToLower(source)
|
||||
} else {
|
||||
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// --- End of function definitions
|
||||
|
||||
// Import above functions in the context
|
||||
@@ -236,6 +254,14 @@ func ImportStringFuncs(ctx ExprContext) {
|
||||
NewFuncParam(ParamSuffix),
|
||||
NewFuncParamFlag(strParamOther, PfRepeat),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strUpper", NewGolangFunctor(upperStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
})
|
||||
|
||||
ctx.RegisterFunc("strLower", NewGolangFunctor(lowerStrFunc), TypeString, []ExprFuncParam{
|
||||
NewFuncParam(ParamSource),
|
||||
})
|
||||
}
|
||||
|
||||
// Register the import function in the import-register.
|
||||
|
||||
+22
-1
@@ -55,14 +55,35 @@ func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error
|
||||
return fmt.Errorf("%s(): invalid value %s (%v) for parameter %q", funcName, TypeName(paramValue), paramValue, paramName)
|
||||
}
|
||||
|
||||
func undefArticle(s string) (article string) {
|
||||
if len(s) > 0 && strings.Contains("aeiou", s[0:1]) {
|
||||
article = "an"
|
||||
} else {
|
||||
article = "a"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func prependUndefArticle(s string) (result string) {
|
||||
return undefArticle(s) + " " + s
|
||||
}
|
||||
|
||||
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)
|
||||
var artWantType, artGotType string
|
||||
gotType := TypeName(paramValue)
|
||||
artGotType = prependUndefArticle(gotType)
|
||||
artWantType = prependUndefArticle(paramType)
|
||||
return fmt.Errorf("%s(): the %q parameter must be %s, got %s (%v)", funcName, paramName, artWantType, artGotType, paramValue)
|
||||
}
|
||||
|
||||
func ErrUnknownParam(funcName, paramName string) error {
|
||||
return fmt.Errorf("%s(): unknown parameter %q", funcName, paramName)
|
||||
}
|
||||
|
||||
func ErrUnknownVar(funcName, varName string) error {
|
||||
return fmt.Errorf("%s(): unknown variable %q", funcName, varName)
|
||||
}
|
||||
|
||||
// --- Operator errors
|
||||
|
||||
func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error {
|
||||
|
||||
@@ -13,6 +13,7 @@ const (
|
||||
TypeFileHandle = "file-handle"
|
||||
TypeInt = "integer"
|
||||
TypeItem = "item"
|
||||
TypeIterator = "iterator"
|
||||
TypeNumber = "number"
|
||||
TypePair = "pair"
|
||||
TypeString = "string"
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ func exportObjects(destCtx, sourceCtx ExprContext) {
|
||||
exportAll := CtrlIsEnabled(sourceCtx, control_export_all)
|
||||
// fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
|
||||
// Export variables
|
||||
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) {
|
||||
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return (exportAll || name[0] == '@') && !(name[0] == '_') }) {
|
||||
// fmt.Printf("\tExporting %q\n", refName)
|
||||
refValue, _ := sourceCtx.GetVar(refName)
|
||||
exportVar(destCtx, refName, refValue)
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// dict-iterator.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type dictIterMode int
|
||||
|
||||
const (
|
||||
dictIterModeKeys dictIterMode = iota
|
||||
dictIterModeValues
|
||||
dictIterModeItems
|
||||
)
|
||||
|
||||
type DictIterator struct {
|
||||
a *DictType
|
||||
count int
|
||||
index int
|
||||
keys []any
|
||||
iterMode dictIterMode
|
||||
}
|
||||
|
||||
type sortType int
|
||||
|
||||
const (
|
||||
sortTypeNone sortType = iota
|
||||
sortTypeAsc
|
||||
sortTypeDesc
|
||||
sortTypeDefault = sortTypeAsc
|
||||
)
|
||||
|
||||
func (it *DictIterator) makeKeys(m map[any]any, sort sortType) {
|
||||
it.keys = make([]any, 0, len(m))
|
||||
|
||||
if sort == sortTypeNone {
|
||||
for keyAny := range m {
|
||||
it.keys = append(it.keys, keyAny)
|
||||
}
|
||||
} else {
|
||||
scalarMap := make(map[string]any, len(m))
|
||||
scalerKeys := make([]string, 0, len(m))
|
||||
for keyAny := range m {
|
||||
keyStr := fmt.Sprint(keyAny)
|
||||
scalarMap[keyStr] = keyAny
|
||||
scalerKeys = append(scalerKeys, keyStr)
|
||||
}
|
||||
|
||||
switch sort {
|
||||
case sortTypeAsc:
|
||||
slices.Sort(scalerKeys)
|
||||
case sortTypeDesc:
|
||||
slices.Sort(scalerKeys)
|
||||
slices.Reverse(scalerKeys)
|
||||
}
|
||||
for _, keyStr := range scalerKeys {
|
||||
it.keys = append(it.keys, scalarMap[keyStr])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func NewDictIterator(dict *DictType, args []any) (it *DictIterator, err error) {
|
||||
var sortType = sortTypeNone
|
||||
var s string
|
||||
var argAny any
|
||||
|
||||
dictIt := &DictIterator{a: dict, count: 0, index: -1, keys: nil, iterMode: dictIterModeKeys}
|
||||
if len(args) > 0 {
|
||||
argAny = args[0]
|
||||
} else {
|
||||
argAny = "default"
|
||||
}
|
||||
if s, err = ToGoString(argAny, "sort type"); err == nil {
|
||||
switch strings.ToLower(s) {
|
||||
case "a", "asc":
|
||||
sortType = sortTypeAsc
|
||||
case "d", "desc":
|
||||
sortType = sortTypeDesc
|
||||
case "n", "none", "nosort", "no-sort":
|
||||
sortType = sortTypeNone
|
||||
case "", "default":
|
||||
sortType = sortTypeDefault
|
||||
default:
|
||||
err = fmt.Errorf("invalid sort type %q", s)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if len(args) > 1 {
|
||||
argAny = args[1]
|
||||
} else {
|
||||
argAny = "default"
|
||||
}
|
||||
|
||||
if s, err = ToGoString(argAny, "iteration mode"); err == nil {
|
||||
switch strings.ToLower(s) {
|
||||
case "k", "key", "keys":
|
||||
dictIt.iterMode = dictIterModeKeys
|
||||
case "v", "value", "values":
|
||||
dictIt.iterMode = dictIterModeValues
|
||||
case "i", "item", "items":
|
||||
dictIt.iterMode = dictIterModeItems
|
||||
case "", "default":
|
||||
dictIt.iterMode = dictIterModeKeys
|
||||
default:
|
||||
err = fmt.Errorf("invalid iteration mode %q", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dictIt.makeKeys(*dict, sortType)
|
||||
return dictIt, err
|
||||
}
|
||||
|
||||
func NewMapIterator(m map[any]any) (it *DictIterator) {
|
||||
it = &DictIterator{a: (*DictType)(&m), count: 0, index: -1, keys: nil}
|
||||
it.makeKeys(m, sortTypeNone)
|
||||
return
|
||||
}
|
||||
|
||||
func (it *DictIterator) String() string {
|
||||
var l = 0
|
||||
if it.a != nil {
|
||||
l = len(*it.a)
|
||||
}
|
||||
return fmt.Sprintf("$({#%d})", l)
|
||||
}
|
||||
|
||||
func (it *DictIterator) TypeName() string {
|
||||
return "DictIterator"
|
||||
}
|
||||
|
||||
func (it *DictIterator) HasOperation(name string) bool {
|
||||
// yes := name == NextName || name == ResetName || name == IndexName || name == CountName || name == CurrentName
|
||||
yes := slices.Contains([]string{NextName, ResetName, IndexName, CountName, CurrentName, CleanName, KeyName, ValueName}, name)
|
||||
return yes
|
||||
}
|
||||
|
||||
func (it *DictIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
||||
switch name {
|
||||
case NextName:
|
||||
v, err = it.Next()
|
||||
case ResetName:
|
||||
err = it.Reset()
|
||||
case CleanName:
|
||||
err = it.Clean()
|
||||
case IndexName:
|
||||
v = int64(it.Index())
|
||||
case CurrentName:
|
||||
v, err = it.Current()
|
||||
case CountName:
|
||||
v = it.count
|
||||
case KeyName:
|
||||
if it.index >= 0 && it.index < len(it.keys) {
|
||||
v = it.keys[it.index]
|
||||
} else {
|
||||
err = io.EOF
|
||||
}
|
||||
case ValueName:
|
||||
if it.index >= 0 && it.index < len(it.keys) {
|
||||
a := *(it.a)
|
||||
v = a[it.keys[it.index]]
|
||||
} else {
|
||||
err = io.EOF
|
||||
}
|
||||
default:
|
||||
err = errNoOperation(name)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *DictIterator) Current() (item any, err error) {
|
||||
if it.index >= 0 && it.index < len(it.keys) {
|
||||
switch it.iterMode {
|
||||
case dictIterModeKeys:
|
||||
item = it.keys[it.index]
|
||||
case dictIterModeValues:
|
||||
a := *(it.a)
|
||||
item = a[it.keys[it.index]]
|
||||
case dictIterModeItems:
|
||||
a := *(it.a)
|
||||
pair := []any{it.keys[it.index], a[it.keys[it.index]]}
|
||||
item = newList(pair)
|
||||
}
|
||||
} else {
|
||||
err = io.EOF
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *DictIterator) Next() (item any, err error) {
|
||||
it.index++
|
||||
if item, err = it.Current(); err != io.EOF {
|
||||
it.count++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *DictIterator) Index() int {
|
||||
return it.index
|
||||
}
|
||||
|
||||
func (it *DictIterator) Count() int {
|
||||
return it.count
|
||||
}
|
||||
|
||||
func (it *DictIterator) Reset() error {
|
||||
it.index = -1
|
||||
it.count = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (it *DictIterator) Clean() error {
|
||||
return nil
|
||||
}
|
||||
+1
-1
@@ -33,7 +33,7 @@ func NewDict(dictAny map[any]any) (dict *DictType) {
|
||||
}
|
||||
|
||||
func newDict(dictAny map[any]*term) (dict *DictType) {
|
||||
// TODO Change wi a call to NewDict()
|
||||
// TODO Change with a call to NewDict()
|
||||
var d DictType
|
||||
if dictAny != nil {
|
||||
d = make(DictType, len(dictAny))
|
||||
|
||||
+999
-97
File diff suppressed because it is too large
Load Diff
+1553
-147
File diff suppressed because it is too large
Load Diff
+14
-12
@@ -50,9 +50,9 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
|
||||
} else if s[0] == '+' {
|
||||
s = s[1:]
|
||||
}
|
||||
// if strings.HasSuffix(s, "()") {
|
||||
// s = s[0 : len(s)-2]
|
||||
// }
|
||||
// if strings.HasSuffix(s, "()") {
|
||||
// s = s[0 : len(s)-2]
|
||||
// }
|
||||
s = strings.TrimSuffix(s, "()")
|
||||
parts = strings.SplitN(s, ".", 2)
|
||||
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
||||
@@ -124,36 +124,38 @@ func (f *FractionType) String() string {
|
||||
func (f *FractionType) ToString(opt FmtOpt) string {
|
||||
var sb strings.Builder
|
||||
if opt&MultiLine == 0 {
|
||||
sb.WriteString(fmt.Sprintf("%d|%d", f.num, f.den))
|
||||
sb.WriteString(fmt.Sprintf("%d:%d", f.num, f.den))
|
||||
} else {
|
||||
var s, num string
|
||||
var sign, num string
|
||||
if f.num < 0 && opt&TTY == 0 {
|
||||
num = strconv.FormatInt(-f.num, 10)
|
||||
s = "-"
|
||||
sign = "-"
|
||||
} else {
|
||||
num = strconv.FormatInt(f.num, 10)
|
||||
}
|
||||
den := strconv.FormatInt(f.den, 10)
|
||||
size := max(len(num), len(den))
|
||||
if opt&TTY != 0 {
|
||||
sb.WriteString(fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, s+num)))
|
||||
sNum := fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, sign+num))
|
||||
sb.WriteString(sNum)
|
||||
} else {
|
||||
if len(s) > 0 {
|
||||
if len(sign) > 0 {
|
||||
sb.WriteString(" ")
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, num)))
|
||||
sb.WriteByte('\n')
|
||||
if len(s) > 0 {
|
||||
sb.WriteString(s)
|
||||
if len(sign) > 0 {
|
||||
sb.WriteString(sign)
|
||||
sb.WriteByte(' ')
|
||||
}
|
||||
sb.WriteString(strings.Repeat("-", size))
|
||||
sb.WriteByte('\n')
|
||||
if len(s) > 0 {
|
||||
if len(sign) > 0 {
|
||||
sb.WriteString(" ")
|
||||
}
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den)))
|
||||
sDen := fmt.Sprintf("%[1]*s", size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den))
|
||||
sb.WriteString(sDen)
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
module git.portale-stac.it/go-pkg/expr
|
||||
|
||||
go 1.21.6
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.23.3
|
||||
|
||||
require golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe h1:bWYrKmmfv37uNgXTdwkLSKYiYPJ1yfWmjBnvtMyAYzk=
|
||||
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe/go.mod h1:alTKUpAJ/zbp17qvZwcFNwzufrb5DljMDY4mgJlIHao=
|
||||
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
|
||||
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
@@ -76,6 +76,12 @@ func isFile(filePath string) bool {
|
||||
|
||||
func searchAmongPath(filename string, dirList []string) (filePath string) {
|
||||
var err error
|
||||
|
||||
suffix := SHAREDLIBRARY_EXTENSION
|
||||
if strings.HasSuffix(filename, ".debug") {
|
||||
suffix += ".debug"
|
||||
}
|
||||
|
||||
for _, dir := range dirList {
|
||||
if dir, err = ExpandPath(dir); err != nil {
|
||||
continue
|
||||
@@ -84,6 +90,12 @@ func searchAmongPath(filename string, dirList []string) (filePath string) {
|
||||
filePath = fullPath
|
||||
break
|
||||
}
|
||||
|
||||
subdir := strings.TrimSuffix(filename, suffix)
|
||||
if fullPath := path.Join(dir, subdir, filename); isFile(fullPath) {
|
||||
filePath = fullPath
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// iter-factory.go
|
||||
package expr
|
||||
|
||||
func NewIterator(value any) (it Iterator, err error) {
|
||||
if value == nil {
|
||||
return NewArrayIterator([]any{}), nil
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case *ListType:
|
||||
it = NewListIterator(v, nil)
|
||||
case *DictType:
|
||||
it, err = NewDictIterator(v, nil)
|
||||
case []any:
|
||||
it = NewArrayIterator(v)
|
||||
case Iterator:
|
||||
it = v
|
||||
default:
|
||||
it = NewArrayIterator([]any{value})
|
||||
}
|
||||
return
|
||||
}
|
||||
+9
-6
@@ -5,7 +5,7 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
// "errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
@@ -21,28 +21,31 @@ const (
|
||||
CountName = "count"
|
||||
FilterName = "filter"
|
||||
MapName = "map"
|
||||
KeyName = "key"
|
||||
ValueName = "value"
|
||||
)
|
||||
|
||||
type Iterator interface {
|
||||
Typer
|
||||
fmt.Stringer
|
||||
Next() (item any, err error) // must return io.EOF after the last item
|
||||
Current() (item any, err error)
|
||||
Index() int
|
||||
Count() int
|
||||
HasOperation(name string) bool
|
||||
CallOperation(name string, args map[string]any) (value any, err error)
|
||||
}
|
||||
|
||||
type ExtIterator interface {
|
||||
Iterator
|
||||
Reset() error
|
||||
Clean() error
|
||||
HasOperation(name string) bool
|
||||
CallOperation(name string, args map[string]any) (value any, err error)
|
||||
}
|
||||
|
||||
func errNoOperation(name string) error {
|
||||
return fmt.Errorf("no %s() function defined in the data-source", name)
|
||||
}
|
||||
|
||||
func errInvalidDataSource() error {
|
||||
return errors.New("invalid data-source")
|
||||
}
|
||||
// func errInvalidDataSource() error {
|
||||
// return errors.New("invalid data-source")
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build darwin
|
||||
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// lib-ext-darwin.go
|
||||
package expr
|
||||
|
||||
const SHAREDLIBRARY_EXTENSION = ".dylib"
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build linux
|
||||
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// lib-ext-linux.go
|
||||
package expr
|
||||
|
||||
const SHAREDLIBRARY_EXTENSION = ".so"
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build windows
|
||||
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// lib-ext-windows.go
|
||||
package expr
|
||||
|
||||
const SHAREDLIBRARY_EXTENSION = ".dll"
|
||||
+3
-18
@@ -62,27 +62,12 @@ func NewArrayIterator(array []any) (it *ListIterator) {
|
||||
return
|
||||
}
|
||||
|
||||
func NewAnyIterator(value any) (it *ListIterator) {
|
||||
if value == nil {
|
||||
it = NewArrayIterator([]any{})
|
||||
} else if list, ok := value.(*ListType); ok {
|
||||
it = NewListIterator(list, nil)
|
||||
} else if array, ok := value.([]any); ok {
|
||||
it = NewArrayIterator(array)
|
||||
} else if it1, ok := value.(*ListIterator); ok {
|
||||
it = it1
|
||||
} else {
|
||||
it = NewArrayIterator([]any{value})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (it *ListIterator) String() string {
|
||||
var l = 0
|
||||
if it.a != nil {
|
||||
l = len(*it.a)
|
||||
}
|
||||
return fmt.Sprintf("$(#%d)", l)
|
||||
return fmt.Sprintf("$([#%d])", l)
|
||||
}
|
||||
|
||||
func (it *ListIterator) TypeName() string {
|
||||
@@ -149,12 +134,12 @@ func (it *ListIterator) Count() int {
|
||||
return it.count
|
||||
}
|
||||
|
||||
func (it *ListIterator) Reset() (error) {
|
||||
func (it *ListIterator) Reset() error {
|
||||
it.index = it.start - it.step
|
||||
it.count = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
func (it *ListIterator) Clean() (error) {
|
||||
func (it *ListIterator) Clean() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
+24
-7
@@ -26,9 +26,9 @@ func newList(listAny []any) (list *ListType) {
|
||||
func NewList(listAny []any) (list *ListType) {
|
||||
if listAny != nil {
|
||||
ls := make(ListType, len(listAny))
|
||||
// for i, item := range listAny {
|
||||
// ls[i] = item
|
||||
// }
|
||||
// for i, item := range listAny {
|
||||
// ls[i] = item
|
||||
// }
|
||||
copy(ls, listAny)
|
||||
list = &ls
|
||||
}
|
||||
@@ -53,14 +53,14 @@ func ListFromStrings(stringList []string) (list *ListType) {
|
||||
}
|
||||
|
||||
func (ls *ListType) ToString(opt FmtOpt) (s string) {
|
||||
indent := GetFormatIndent(opt)
|
||||
flags := GetFormatFlags(opt)
|
||||
indent := GetFormatIndent(opt)
|
||||
flags := GetFormatFlags(opt)
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteByte('[')
|
||||
if len(*ls) > 0 {
|
||||
innerOpt := MakeFormatOptions(flags, indent+1)
|
||||
nest := strings.Repeat(" ", indent+1)
|
||||
innerOpt := MakeFormatOptions(flags, indent+1)
|
||||
nest := strings.Repeat(" ", indent+1)
|
||||
|
||||
if flags&MultiLine != 0 {
|
||||
sb.WriteByte('\n')
|
||||
@@ -129,6 +129,20 @@ func (ls *ListType) contains(t *ListType) (answer bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
|
||||
if ls2 != nil && len(*ls1) == len(ls2) {
|
||||
answer = true
|
||||
for index, i1 := range *ls1 {
|
||||
// if i1 != (ls2)[index] {
|
||||
if reflect.DeepEqual(i1, ls2[index]) {
|
||||
answer = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (list *ListType) indexDeepSameCmp(target any) (index int) {
|
||||
var eq bool
|
||||
var err error
|
||||
@@ -193,3 +207,6 @@ func (list *ListType) setItem(index int64, value any) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (list *ListType) appendItem(value any) {
|
||||
*list = append(*list, value)
|
||||
}
|
||||
|
||||
+32
-20
@@ -86,37 +86,49 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if ds, err = getDataSourceDict(opTerm, firstChildValue); err != nil {
|
||||
if ds, err = getDataSourceDict(opTerm, firstChildValue); err != nil && ds == nil {
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
|
||||
if ds != nil {
|
||||
var dc *dataCursor
|
||||
dcCtx := ctx.Clone()
|
||||
if initFunc, exists := ds[InitName]; exists && initFunc != nil {
|
||||
var args []any
|
||||
var resource any
|
||||
if len(opTerm.children) > 1 {
|
||||
if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil {
|
||||
if len(ds) > 0 {
|
||||
var dc *dataCursor
|
||||
dcCtx := ctx.Clone()
|
||||
if initFunc, exists := ds[InitName]; exists && initFunc != nil {
|
||||
var args []any
|
||||
var resource any
|
||||
if len(opTerm.children) > 1 {
|
||||
if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
args = []any{}
|
||||
}
|
||||
|
||||
actualParams := bindActualParams(initFunc, args)
|
||||
|
||||
initCtx := ctx.Clone()
|
||||
if resource, err = initFunc.InvokeNamed(initCtx, InitName, actualParams); err != nil {
|
||||
return
|
||||
}
|
||||
exportObjects(dcCtx, initCtx)
|
||||
dc = NewDataCursor(dcCtx, ds, resource)
|
||||
} else {
|
||||
args = []any{}
|
||||
dc = NewDataCursor(dcCtx, ds, nil)
|
||||
}
|
||||
|
||||
actualParams := bindActualParams(initFunc, args)
|
||||
|
||||
initCtx := ctx.Clone()
|
||||
if resource, err = initFunc.InvokeNamed(initCtx, InitName, actualParams); err != nil {
|
||||
return
|
||||
}
|
||||
exportObjects(dcCtx, initCtx)
|
||||
dc = NewDataCursor(dcCtx, ds, resource)
|
||||
v = dc
|
||||
} else {
|
||||
dc = NewDataCursor(dcCtx, ds, nil)
|
||||
if dictIt, ok := firstChildValue.(*DictType); ok {
|
||||
var args []any
|
||||
if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
|
||||
v, err = NewDictIterator(dictIt, args)
|
||||
}
|
||||
} else {
|
||||
err = opTerm.children[0].Errorf("the data-source must be a dictionary")
|
||||
}
|
||||
}
|
||||
|
||||
v = dc
|
||||
} else if list, ok := firstChildValue.(*ListType); ok {
|
||||
var args []any
|
||||
if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
|
||||
|
||||
@@ -99,7 +99,122 @@ func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
//-------- assign term
|
||||
|
||||
func newOpAssignTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priAssign,
|
||||
evalFunc: evalOpAssign,
|
||||
}
|
||||
}
|
||||
|
||||
func getCollectionItemValue(ctx ExprContext, collectionTerm, keyListTerm *term) (value any, err error) {
|
||||
var collectionValue, keyListValue, keyValue any
|
||||
var keyList *ListType
|
||||
var ok bool
|
||||
|
||||
if collectionValue, err = collectionTerm.compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if keyListValue, err = keyListTerm.compute(ctx); err != nil {
|
||||
return
|
||||
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 {
|
||||
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue))
|
||||
return
|
||||
}
|
||||
if keyValue = (*keyList)[0]; keyValue == nil {
|
||||
err = keyListTerm.Errorf("index/key is nil")
|
||||
return
|
||||
}
|
||||
|
||||
switch collection := collectionValue.(type) {
|
||||
case *ListType:
|
||||
if index, ok := keyValue.(int64); ok {
|
||||
value = (*collection)[index]
|
||||
} else {
|
||||
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue))
|
||||
}
|
||||
case *DictType:
|
||||
value = (*collection)[keyValue]
|
||||
default:
|
||||
err = collectionTerm.Errorf("collection expected")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getAssignValue(ctx ExprContext, leftTerm *term) (value any, err error) {
|
||||
if leftTerm.symbol() == SymIndex {
|
||||
value, err = getCollectionItemValue(ctx, leftTerm.children[0], leftTerm.children[1])
|
||||
} else {
|
||||
value, _ = ctx.GetVar(leftTerm.source())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalOpAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var rightValue, leftValue any
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
leftTerm := opTerm.children[0]
|
||||
leftSym := leftTerm.symbol()
|
||||
if leftSym != SymVariable && leftSym != SymIndex {
|
||||
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
|
||||
return
|
||||
}
|
||||
|
||||
rightChild := opTerm.children[1]
|
||||
|
||||
if rightValue, err = rightChild.compute(ctx); err == nil {
|
||||
if leftValue, err = getAssignValue(ctx, leftTerm); err == nil {
|
||||
switch opTerm.symbol() {
|
||||
case SymPlusEqual:
|
||||
v, err = sumValues(opTerm, leftValue, rightValue)
|
||||
case SymMinusEqual:
|
||||
v, err = diffValues(opTerm, leftValue, rightValue)
|
||||
case SymStarEqual:
|
||||
v, err = mulValues(opTerm, leftValue, rightValue)
|
||||
case SymSlashEqual:
|
||||
v, err = divValues(opTerm, leftValue, rightValue)
|
||||
case SymPercEqual:
|
||||
v, err = remainderValues(opTerm, leftValue, rightValue)
|
||||
case SymAmpersandEqual:
|
||||
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
||||
case SymVertBarEqual:
|
||||
v, err = bitwiseOr(opTerm, leftValue, rightValue)
|
||||
case SymCaretEqual:
|
||||
v, err = bitwiseXor(opTerm, leftValue, rightValue)
|
||||
case SymDoubleLessEqual:
|
||||
v, err = bitLeftShift(opTerm, leftValue, rightValue)
|
||||
case SymDoubleGreaterEqual:
|
||||
v, err = bitRightShift(opTerm, leftValue, rightValue)
|
||||
default:
|
||||
err = opTerm.Errorf("unsupported assign operator %q", opTerm.source())
|
||||
}
|
||||
if err == nil {
|
||||
err = assignValue(ctx, leftTerm, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymEqual, newAssignTerm)
|
||||
registerTermConstructor(SymPlusEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymMinusEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymStarEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymSlashEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymPercEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymDoubleLessEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymDoubleGreaterEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymAmpersandEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymVertBarEqual, newOpAssignTerm)
|
||||
registerTermConstructor(SymCaretEqual, newOpAssignTerm)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-bitwise.go
|
||||
package expr
|
||||
|
||||
//-------- Bitwise NOT term
|
||||
|
||||
func newBitwiseNotTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priBitwiseNot,
|
||||
evalFunc: evalBitwiseNot,
|
||||
}
|
||||
}
|
||||
|
||||
func evalBitwiseNot(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var value any
|
||||
|
||||
if value, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger(value) {
|
||||
i, _ := value.(int64)
|
||||
v = ^i
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(value)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//-------- Bitwise AND term
|
||||
|
||||
func newBitwiseAndTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBitwiseAnd,
|
||||
evalFunc: evalBitwiseAnd,
|
||||
}
|
||||
}
|
||||
|
||||
func bitwiseAnd(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
var leftInt, rightInt int64
|
||||
var lok, rok bool
|
||||
|
||||
leftInt, lok = leftValue.(int64)
|
||||
rightInt, rok = rightValue.(int64)
|
||||
|
||||
if lok && rok {
|
||||
v = leftInt & rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalBitwiseAnd(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
||||
return
|
||||
}
|
||||
|
||||
//-------- Bitwise OR term
|
||||
|
||||
func newBitwiseOrTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBitwiseOr,
|
||||
evalFunc: evalBitwiseOr,
|
||||
}
|
||||
}
|
||||
|
||||
func bitwiseOr(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
var leftInt, rightInt int64
|
||||
var lok, rok bool
|
||||
|
||||
leftInt, lok = leftValue.(int64)
|
||||
rightInt, rok = rightValue.(int64)
|
||||
|
||||
if lok && rok {
|
||||
v = leftInt | rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalBitwiseOr(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v, err = bitwiseOr(opTerm, leftValue, rightValue)
|
||||
return
|
||||
}
|
||||
|
||||
//-------- Bitwise XOR term
|
||||
|
||||
func newBitwiseXorTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBitwiseOr,
|
||||
evalFunc: evalBitwiseXor,
|
||||
}
|
||||
}
|
||||
|
||||
func bitwiseXor(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
var leftInt, rightInt int64
|
||||
var lok, rok bool
|
||||
|
||||
leftInt, lok = leftValue.(int64)
|
||||
rightInt, rok = rightValue.(int64)
|
||||
|
||||
if lok && rok {
|
||||
v = leftInt ^ rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalBitwiseXor(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v, err = bitwiseXor(opTerm, leftValue, rightValue)
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymTilde, newBitwiseNotTerm)
|
||||
registerTermConstructor(SymAmpersand, newBitwiseAndTerm)
|
||||
registerTermConstructor(SymVertBar, newBitwiseOrTerm)
|
||||
registerTermConstructor(SymCaret, newBitwiseXorTerm)
|
||||
}
|
||||
+4
-1
@@ -31,7 +31,10 @@ func evalBuiltin(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
count, err = ImportInContextByGlobPattern(module)
|
||||
} else {
|
||||
var moduleSpec any
|
||||
it := NewAnyIterator(childValue)
|
||||
var it Iterator
|
||||
if it, err = NewIterator(childValue); err != nil {
|
||||
return
|
||||
}
|
||||
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
||||
if module, ok := moduleSpec.(string); ok {
|
||||
if ImportInContext(module) {
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ func evalContextValue(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var childValue any
|
||||
|
||||
var sourceCtx ExprContext
|
||||
if opTerm.children == nil || len(opTerm.children) == 0 {
|
||||
if len(opTerm.children) == 0 {
|
||||
sourceCtx = ctx
|
||||
} else if opTerm.children[0].symbol() == SymVariable && opTerm.children[0].source() == "global" {
|
||||
sourceCtx = globalCtx
|
||||
|
||||
+13
-9
@@ -7,7 +7,6 @@ package expr
|
||||
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
@@ -41,7 +40,7 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
if den == 0 {
|
||||
err = errors.New("division by zero")
|
||||
err = opTerm.errDivisionByZero()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -49,18 +48,23 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
den = -den
|
||||
num = -num
|
||||
}
|
||||
g := gcd(num, den)
|
||||
num = num / g
|
||||
den = den / g
|
||||
if den == 1 {
|
||||
v = num
|
||||
if num != 0 {
|
||||
g := gcd(num, den)
|
||||
num = num / g
|
||||
den = den / g
|
||||
if den == 1 {
|
||||
v = num
|
||||
} else {
|
||||
v = &FractionType{num, den}
|
||||
}
|
||||
} else {
|
||||
v = &FractionType{num, den}
|
||||
v = &FractionType{0, den}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymVertBar, newFractionTerm)
|
||||
// registerTermConstructor(SymVertBar, newFractionTerm)
|
||||
registerTermConstructor(SymColon, newFractionTerm)
|
||||
}
|
||||
|
||||
@@ -113,6 +113,9 @@ func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
} else if IsDict(leftValue) {
|
||||
d := leftValue.(*DictType)
|
||||
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
||||
} else {
|
||||
rightChild := opTerm.children[1]
|
||||
err = rightChild.Errorf("invalid index type: %v", (*indexList)[0])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+8
-8
@@ -4,15 +4,15 @@
|
||||
// operator-insert.go
|
||||
package expr
|
||||
|
||||
//-------- insert term
|
||||
//-------- prepend term
|
||||
|
||||
func newInsertTerm(tk *Token) (inst *term) {
|
||||
func newPrependTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priAssign,
|
||||
evalFunc: evalInsert,
|
||||
priority: priInsert,
|
||||
evalFunc: evalPrepend,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,12 @@ func newAppendTerm(tk *Token) (inst *term) {
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priAssign,
|
||||
priority: priInsert,
|
||||
evalFunc: evalAppend,
|
||||
}
|
||||
}
|
||||
|
||||
func evalInsert(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
func evalPrepend(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
@@ -86,6 +86,6 @@ func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymInsert, newInsertTerm)
|
||||
registerTermConstructor(SymAppend, newAppendTerm)
|
||||
registerTermConstructor(SymPlusGreater, newPrependTerm)
|
||||
registerTermConstructor(SymLessPlus, newAppendTerm)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ func newIterValueTerm(tk *Token) (inst *term) {
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priIterValue,
|
||||
priority: priDereference,
|
||||
evalFunc: evalIterValue,
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,6 @@ func evalIterValue(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
|
||||
// init
|
||||
func init() {
|
||||
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
||||
registerTermConstructor(SymCaret, newIterValueTerm)
|
||||
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
||||
registerTermConstructor(SymDereference, newIterValueTerm)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-map.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
//-------- map term
|
||||
|
||||
func newMapTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priIterOp,
|
||||
evalFunc: evalMap,
|
||||
}
|
||||
}
|
||||
|
||||
func evalMap(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
var it Iterator
|
||||
var item any
|
||||
|
||||
if err = opTerm.checkOperands(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if leftValue, err = opTerm.children[0].compute(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if it, err = NewIterator(leftValue); err != nil {
|
||||
return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", TypeName(leftValue))
|
||||
}
|
||||
|
||||
values := newListA()
|
||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||
ctx.SetVar("_", item)
|
||||
ctx.SetVar("_index", it.Index())
|
||||
ctx.SetVar("_count", it.Count())
|
||||
if rightValue, err = opTerm.children[1].compute(ctx); err == nil {
|
||||
values.appendItem(rightValue)
|
||||
}
|
||||
ctx.DeleteVar("_")
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
v = values
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymKwMap, newMapTerm)
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-post-inc.go
|
||||
// operator-post-inc-dec.go
|
||||
package expr
|
||||
|
||||
// -------- post increment term
|
||||
@@ -24,7 +24,27 @@ func evalPostInc(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
}
|
||||
|
||||
if it, ok := childValue.(Iterator); ok {
|
||||
var namePrefix string
|
||||
v, err = it.Next()
|
||||
|
||||
if opTerm.children[0].symbol() == SymVariable {
|
||||
namePrefix = opTerm.children[0].source()
|
||||
}
|
||||
ctx.UnsafeSetVar(namePrefix+"_index", it.Index())
|
||||
if c, err1 := it.Current(); err1 == nil {
|
||||
ctx.UnsafeSetVar(namePrefix+"_current", c)
|
||||
}
|
||||
|
||||
if it.HasOperation(KeyName) {
|
||||
if k, err1 := it.CallOperation(KeyName, nil); err1 == nil {
|
||||
ctx.UnsafeSetVar(namePrefix+"_key", k)
|
||||
}
|
||||
}
|
||||
if it.HasOperation(ValueName) {
|
||||
if v1, err1 := it.CallOperation(ValueName, nil); err1 == nil {
|
||||
ctx.UnsafeSetVar(namePrefix+"_value", v1)
|
||||
}
|
||||
}
|
||||
} else if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
|
||||
v = childValue
|
||||
i, _ := childValue.(int64)
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-pre-inc-dec.go
|
||||
package expr
|
||||
|
||||
// -------- pre increment term
|
||||
|
||||
func newPreIncTerm(tk *Token) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priIncDec,
|
||||
evalFunc: evalPreInc,
|
||||
}
|
||||
}
|
||||
|
||||
func evalPreInc(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var childValue any
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
|
||||
i := childValue.(int64) + 1
|
||||
ctx.SetVar(opTerm.children[0].source(), i)
|
||||
v = i
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(childValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// -------- pre decrement term
|
||||
|
||||
func newPreDecTerm(tk *Token) *term {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
parent: nil,
|
||||
children: make([]*term, 0, 1),
|
||||
position: posPrefix,
|
||||
priority: priIncDec,
|
||||
evalFunc: evalPreDec,
|
||||
}
|
||||
}
|
||||
|
||||
func evalPreDec(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var childValue any
|
||||
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
|
||||
i := childValue.(int64) - 1
|
||||
ctx.SetVar(opTerm.children[0].source(), i)
|
||||
v = i
|
||||
} else {
|
||||
err = opTerm.errIncompatibleType(childValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymPreInc, newPreIncTerm)
|
||||
registerTermConstructor(SymPreDec, newPreDecTerm)
|
||||
}
|
||||
+81
-46
@@ -5,7 +5,6 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -13,7 +12,7 @@ import (
|
||||
|
||||
func newMultiplyTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priProduct,
|
||||
@@ -21,13 +20,7 @@ func newMultiplyTerm(tk *Token) (inst *term) {
|
||||
}
|
||||
}
|
||||
|
||||
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func mulValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if IsString(leftValue) && IsInteger(rightValue) {
|
||||
s, _ := leftValue.(string)
|
||||
n, _ := rightValue.(int64)
|
||||
@@ -43,16 +36,26 @@ func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
||||
v = leftInt * rightInt
|
||||
}
|
||||
} else {
|
||||
err = prodTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return mulValues(prodTerm, leftValue, rightValue)
|
||||
}
|
||||
|
||||
//-------- divide term
|
||||
|
||||
func newDivideTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priProduct,
|
||||
@@ -60,6 +63,56 @@ func newDivideTerm(tk *Token) (inst *term) {
|
||||
}
|
||||
}
|
||||
|
||||
func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||
d := numAsFloat(rightValue)
|
||||
if d == 0.0 {
|
||||
err = opTerm.errDivisionByZero()
|
||||
} else {
|
||||
v = numAsFloat(leftValue) / d
|
||||
}
|
||||
} else if isFraction(leftValue) || isFraction(rightValue) {
|
||||
v, err = divAnyFract(leftValue, rightValue)
|
||||
} else {
|
||||
leftInt, _ := leftValue.(int64)
|
||||
if rightInt, _ := rightValue.(int64); rightInt == 0 {
|
||||
err = opTerm.errDivisionByZero()
|
||||
} else {
|
||||
v = leftInt / rightInt
|
||||
}
|
||||
}
|
||||
} else if IsString(leftValue) && IsString(rightValue) {
|
||||
source := leftValue.(string)
|
||||
sep := rightValue.(string)
|
||||
v = ListFromStrings(strings.Split(source, sep))
|
||||
} else if IsString(leftValue) && IsInteger(rightValue) {
|
||||
source := leftValue.(string)
|
||||
partSize := int(rightValue.(int64))
|
||||
if partSize == 0 {
|
||||
err = opTerm.errDivisionByZero()
|
||||
} else {
|
||||
partCount := len(source) / partSize
|
||||
remainder := len(source) % partSize
|
||||
listSize := partCount
|
||||
if remainder > 0 {
|
||||
listSize++
|
||||
}
|
||||
parts := make([]any, 0, listSize)
|
||||
for i := 0; i < partCount; i++ {
|
||||
parts = append(parts, source[i*partSize:(i+1)*partSize])
|
||||
}
|
||||
if remainder > 0 {
|
||||
parts = append(parts, source[len(source)-remainder:])
|
||||
}
|
||||
v = newList(parts)
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
@@ -67,28 +120,7 @@ func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||
d := numAsFloat(rightValue)
|
||||
if d == 0.0 {
|
||||
err = errors.New("division by zero")
|
||||
} else {
|
||||
v = numAsFloat(leftValue) / d
|
||||
}
|
||||
} else if isFraction(leftValue) || isFraction(rightValue) {
|
||||
v, err = divAnyFract(leftValue, rightValue)
|
||||
} else {
|
||||
leftInt, _ := leftValue.(int64)
|
||||
if rightInt, _ := rightValue.(int64); rightInt == 0 {
|
||||
err = errors.New("division by zero")
|
||||
} else {
|
||||
v = leftInt / rightInt
|
||||
}
|
||||
}
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
return divValues(opTerm, leftValue, rightValue)
|
||||
}
|
||||
|
||||
//-------- divide as float term
|
||||
@@ -113,7 +145,7 @@ func evalDivideAsFloat(ctx ExprContext, floatDivTerm *term) (v any, err error) {
|
||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||
d := numAsFloat(rightValue)
|
||||
if d == 0.0 {
|
||||
err = errors.New("division by zero")
|
||||
err = floatDivTerm.errDivisionByZero()
|
||||
} else {
|
||||
v = numAsFloat(leftValue) / d
|
||||
}
|
||||
@@ -127,35 +159,38 @@ func evalDivideAsFloat(ctx ExprContext, floatDivTerm *term) (v any, err error) {
|
||||
|
||||
func newRemainderTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priProduct,
|
||||
evalFunc: evalReminder,
|
||||
evalFunc: evalRemainder,
|
||||
}
|
||||
}
|
||||
|
||||
func evalReminder(ctx ExprContext, ramainderTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = ramainderTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func remainderValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
rightInt, _ := rightValue.(int64)
|
||||
if rightInt == 0 {
|
||||
err = errors.New("division by zero")
|
||||
err = opTerm.errDivisionByZero()
|
||||
} else {
|
||||
leftInt, _ := leftValue.(int64)
|
||||
v = leftInt % rightInt
|
||||
}
|
||||
} else {
|
||||
err = ramainderTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalRemainder(ctx ExprContext, remainderTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = remainderTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return remainderValues(remainderTerm, leftValue, rightValue)
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymStar, newMultiplyTerm)
|
||||
|
||||
+18
-5
@@ -34,12 +34,16 @@ func newRangeTerm(tk *Token) (inst *term) {
|
||||
}
|
||||
}
|
||||
|
||||
func changeColonToRange(t *term) {
|
||||
if t.tk.IsSymbol(SymColon) {
|
||||
t.tk.Sym = SymRange
|
||||
t.evalFunc = evalRange
|
||||
}
|
||||
}
|
||||
|
||||
func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
// if err = self.checkOperands(); err != nil {
|
||||
// return
|
||||
// }
|
||||
if len(opTerm.children) == 0 {
|
||||
leftValue = int64(0)
|
||||
rightValue = int64(-1)
|
||||
@@ -52,7 +56,8 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
if !(IsInteger(leftValue) && IsInteger(rightValue)) {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
// err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
err = errRangeInvalidSpecification(opTerm)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -63,7 +68,15 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func errRangeInvalidSpecification(t *term) error {
|
||||
return t.Errorf("invalid range specification")
|
||||
}
|
||||
|
||||
func errRangeUnexpectedExpression(t *term) error {
|
||||
return t.Errorf("unexpected range expression")
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymColon, newRangeTerm)
|
||||
registerTermConstructor(SymRange, newRangeTerm)
|
||||
}
|
||||
|
||||
+13
-3
@@ -22,9 +22,19 @@ func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (ma
|
||||
selectedValue, err = caseData.caseExpr.Eval(ctx)
|
||||
match = true
|
||||
} else if filterList, ok := caseData.filterList.value().([]*term); ok {
|
||||
if len(filterList) == 0 && exprValue == int64(caseIndex) {
|
||||
selectedValue, err = caseData.caseExpr.Eval(ctx)
|
||||
match = true
|
||||
if len(filterList) == 0 {
|
||||
var valueAsInt = int64(0)
|
||||
if b, ok := exprValue.(bool); ok {
|
||||
if !b {
|
||||
valueAsInt = 1
|
||||
}
|
||||
} else if valueAsInt, ok = exprValue.(int64); !ok {
|
||||
return
|
||||
}
|
||||
if valueAsInt == int64(caseIndex) {
|
||||
selectedValue, err = caseData.caseExpr.Eval(ctx)
|
||||
match = true
|
||||
}
|
||||
} else {
|
||||
var caseValue any
|
||||
for _, caseTerm := range filterList {
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// operator-shift.go
|
||||
package expr
|
||||
|
||||
//-------- bit right shift term
|
||||
|
||||
func newRightShiftTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBinShift,
|
||||
evalFunc: evalRightShift,
|
||||
}
|
||||
}
|
||||
|
||||
func bitRightShift(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
leftInt := leftValue.(int64)
|
||||
rightInt := rightValue.(int64)
|
||||
v = leftInt >> rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalRightShift(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v, err = bitRightShift(opTerm, leftValue, rightValue)
|
||||
return
|
||||
}
|
||||
|
||||
func newLeftShiftTerm(tk *Token) (inst *term) {
|
||||
return &term{
|
||||
tk: *tk,
|
||||
children: make([]*term, 0, 2),
|
||||
position: posInfix,
|
||||
priority: priBinShift,
|
||||
evalFunc: evalLeftShift,
|
||||
}
|
||||
}
|
||||
|
||||
func bitLeftShift(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||
leftInt := leftValue.(int64)
|
||||
rightInt := rightValue.(int64)
|
||||
v = leftInt << rightInt
|
||||
} else {
|
||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func evalLeftShift(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
v, err = bitLeftShift(opTerm, leftValue, rightValue)
|
||||
return
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymDoubleGreater, newRightShiftTerm)
|
||||
registerTermConstructor(SymDoubleLess, newLeftShiftTerm)
|
||||
}
|
||||
+25
-15
@@ -21,13 +21,7 @@ func newPlusTerm(tk *Token) (inst *term) {
|
||||
}
|
||||
}
|
||||
|
||||
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func sumValues(plusTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if (IsString(leftValue) && isNumberString(rightValue)) || (IsString(rightValue) && isNumberString(leftValue)) {
|
||||
v = fmt.Sprintf("%v%v", leftValue, rightValue)
|
||||
} else if IsNumber(leftValue) && IsNumber(rightValue) {
|
||||
@@ -59,10 +53,22 @@ func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
||||
c := leftDict.clone()
|
||||
c.merge(rightDict)
|
||||
v = c
|
||||
} else if isFraction(leftValue) && isFraction(rightValue) {
|
||||
v, err = sumAnyFract(leftValue, rightValue)
|
||||
} else {
|
||||
err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||
}
|
||||
return
|
||||
return v, err
|
||||
}
|
||||
|
||||
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return sumValues(plusTerm, leftValue, rightValue)
|
||||
}
|
||||
|
||||
//-------- minus term
|
||||
@@ -77,13 +83,7 @@ func newMinusTerm(tk *Token) (inst *term) {
|
||||
}
|
||||
}
|
||||
|
||||
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func diffValues(minusTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||
v = numAsFloat(leftValue) - numAsFloat(rightValue)
|
||||
@@ -110,6 +110,16 @@ func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
||||
var leftValue, rightValue any
|
||||
|
||||
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return diffValues(minusTerm, leftValue, rightValue)
|
||||
}
|
||||
|
||||
// init
|
||||
func init() {
|
||||
registerTermConstructor(SymPlus, newPlusTerm)
|
||||
|
||||
@@ -6,10 +6,46 @@ package expr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
//-------- parser
|
||||
|
||||
type parserContext uint16
|
||||
|
||||
const (
|
||||
parserNoFlags = 0
|
||||
allowMultiExpr parserContext = 1 << iota
|
||||
allowVarRef
|
||||
selectorContext
|
||||
listContext // squareContext for list
|
||||
indexContext // squareContext for index
|
||||
allowIndex // allow index in squareContext
|
||||
squareContext = listContext | indexContext // Square parenthesis for list or index
|
||||
)
|
||||
|
||||
func hasFlag[T constraints.Unsigned](set T, singleFlag T) bool {
|
||||
return (set & singleFlag) != 0
|
||||
}
|
||||
|
||||
func addFlags[T constraints.Unsigned](set T, flags T) T {
|
||||
return set | flags
|
||||
}
|
||||
|
||||
func addFlagsCond[T constraints.Unsigned](set T, flags T, cond bool) (newSet T) {
|
||||
if cond {
|
||||
newSet = set | flags
|
||||
} else {
|
||||
newSet = set
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func remFlags[T constraints.Unsigned](set T, flags T) T {
|
||||
return set & (^flags)
|
||||
}
|
||||
|
||||
type parser struct {
|
||||
}
|
||||
|
||||
@@ -24,13 +60,13 @@ func (parser *parser) Next(scanner *scanner) (tk *Token) {
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
|
||||
func (parser *parser) parseFuncCall(scanner *scanner, ctx parserContext, tk *Token) (tree *term, err error) {
|
||||
args := make([]*term, 0, 10)
|
||||
itemExpected := false
|
||||
lastSym := SymUnknown
|
||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||
var subTree *ast
|
||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil {
|
||||
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err != nil {
|
||||
break
|
||||
}
|
||||
prev := scanner.Previous()
|
||||
@@ -66,12 +102,18 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||
tk = parser.Next(scanner)
|
||||
if tk.IsSymbol(SymIdentifier) {
|
||||
param := newTerm(tk)
|
||||
if len(args) > 0 {
|
||||
if pos := paramAlreadyDefined(args, param); pos > 0 {
|
||||
err = tk.Errorf("parameter %q at position %d already defined at position %d", param.source(), len(args)+1, pos)
|
||||
break
|
||||
}
|
||||
}
|
||||
args = append(args, param)
|
||||
tk = parser.Next(scanner)
|
||||
if tk.Sym == SymEqual {
|
||||
var paramExpr *ast
|
||||
defaultParamsStarted = true
|
||||
if paramExpr, err = parser.parseItem(scanner, false, SymComma, SymClosedRound); err != nil {
|
||||
if paramExpr, err = parser.parseItem(scanner, parserNoFlags, SymComma, SymClosedRound); err != nil {
|
||||
break
|
||||
}
|
||||
param.forceChild(paramExpr.root)
|
||||
@@ -94,7 +136,7 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||
if err == nil {
|
||||
tk = parser.Next(scanner)
|
||||
if tk.IsSymbol(SymOpenBrace) {
|
||||
body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace)
|
||||
body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymClosedBrace)
|
||||
} else {
|
||||
err = tk.ErrorExpectedGot("{")
|
||||
}
|
||||
@@ -110,27 +152,43 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) {
|
||||
func paramAlreadyDefined(args []*term, param *term) (position int) {
|
||||
position = 0
|
||||
for i, arg := range args {
|
||||
if arg.source() == param.source() {
|
||||
position = i + 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseList(scanner *scanner, ctx parserContext) (listTerm *term, err error) {
|
||||
r, c := scanner.lastPos()
|
||||
args := make([]*term, 0)
|
||||
lastSym := SymUnknown
|
||||
itemExpected := false
|
||||
itemCtx := remFlags(ctx, allowIndex)
|
||||
for lastSym != SymClosedSquare && lastSym != SymEos {
|
||||
var subTree *ast
|
||||
zeroRequired := scanner.current.Sym == SymColon
|
||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
||||
root := subTree.root
|
||||
var itemTree *ast
|
||||
if itemTree, err = parser.parseItem(scanner, itemCtx, SymComma, SymClosedSquare); err == nil {
|
||||
root := itemTree.root
|
||||
if root != nil {
|
||||
if !parsingIndeces && root.symbol() == SymColon {
|
||||
err = root.Errorf("unexpected range expression")
|
||||
if hasFlag(ctx, allowIndex) && root.symbol() == SymColon {
|
||||
changeColonToRange(root)
|
||||
}
|
||||
if !hasFlag(ctx, allowIndex) && root.symbol() == SymRange {
|
||||
// err = root.Errorf("unexpected range expression")
|
||||
err = errRangeUnexpectedExpression(root)
|
||||
break
|
||||
}
|
||||
args = append(args, root)
|
||||
if parsingIndeces && root.symbol() == SymColon && zeroRequired { //len(root.children) == 0 {
|
||||
if hasFlag(ctx, allowIndex) && root.symbol() == SymRange && zeroRequired { //len(root.children) == 0 {
|
||||
if len(root.children) == 1 {
|
||||
root.children = append(root.children, root.children[0])
|
||||
} else if len(root.children) > 1 {
|
||||
err = root.Errorf("invalid range specification")
|
||||
// err = root.Errorf("invalid range specification")
|
||||
err = errRangeInvalidSpecification(root)
|
||||
break
|
||||
}
|
||||
zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0))
|
||||
@@ -147,26 +205,28 @@ func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarR
|
||||
break
|
||||
}
|
||||
lastSym = scanner.Previous().Sym
|
||||
itemExpected = lastSym == SymComma
|
||||
if itemExpected = lastSym == SymComma; itemExpected {
|
||||
remFlags(ctx, allowIndex)
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
if lastSym != SymClosedSquare {
|
||||
err = scanner.Previous().ErrorExpectedGot("]")
|
||||
} else {
|
||||
subtree = newListTerm(r, c, args)
|
||||
listTerm = newListTerm(r, c, args)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
||||
func (parser *parser) parseIterDef(scanner *scanner, ctx parserContext) (subtree *term, err error) {
|
||||
tk := scanner.Previous()
|
||||
args := make([]*term, 0)
|
||||
lastSym := SymUnknown
|
||||
itemExpected := false
|
||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||
var subTree *ast
|
||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
|
||||
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err == nil {
|
||||
if subTree.root != nil {
|
||||
args = append(args, subTree.root)
|
||||
} else if itemExpected {
|
||||
@@ -212,7 +272,7 @@ func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
||||
func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subtree *term, err error) {
|
||||
args := make(map[any]*term, 0)
|
||||
lastSym := SymUnknown
|
||||
itemExpected := false
|
||||
@@ -229,7 +289,7 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
|
||||
}
|
||||
break
|
||||
}
|
||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
|
||||
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedBrace); err == nil {
|
||||
if subTree.root != nil {
|
||||
args[key] = subTree.root
|
||||
} else /*if key != nil*/ {
|
||||
@@ -248,15 +308,15 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
|
||||
err = scanner.Previous().ErrorExpectedGot("}")
|
||||
} else {
|
||||
subtree = newDictTerm(args)
|
||||
// subtree = newMapTerm(args)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
|
||||
func (parser *parser) parseSelectorCase(scanner *scanner, ctx parserContext, defaultCase bool) (caseTerm *term, err error) {
|
||||
var filterList *term
|
||||
var caseExpr *ast
|
||||
ctx = remFlags(ctx, allowIndex)
|
||||
tk := parser.Next(scanner)
|
||||
startRow := tk.row
|
||||
startCol := tk.col
|
||||
@@ -265,7 +325,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
|
||||
err = tk.Errorf("case list in default clause")
|
||||
return
|
||||
}
|
||||
if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil {
|
||||
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex)); err != nil {
|
||||
return
|
||||
}
|
||||
tk = parser.Next(scanner)
|
||||
@@ -276,7 +336,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
|
||||
}
|
||||
|
||||
if tk.Sym == SymOpenBrace {
|
||||
if caseExpr, err = parser.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil {
|
||||
if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, SymClosedBrace); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@@ -302,25 +362,28 @@ func addSelectorCase(selectorTerm, caseTerm *term) {
|
||||
caseTerm.parent = selectorTerm
|
||||
}
|
||||
|
||||
func (parser *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
|
||||
func (parser *parser) parseSelector(scanner *scanner, tree *ast, ctx parserContext) (selectorTerm *term, err error) {
|
||||
var caseTerm *term
|
||||
|
||||
ctx = remFlags(ctx, allowIndex)
|
||||
tk := scanner.makeToken(SymSelector, '?')
|
||||
if selectorTerm, err = tree.addToken(tk); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, false); err == nil {
|
||||
if caseTerm, err = parser.parseSelectorCase(scanner, ctx|allowVarRef, false); err == nil {
|
||||
addSelectorCase(selectorTerm, caseTerm)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
||||
return parser.parseGeneral(scanner, false, allowVarRef, termSymbols...)
|
||||
func (parser *parser) parseItem(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
|
||||
return parser.parseGeneral(scanner, ctx|allowVarRef, termSymbols...)
|
||||
}
|
||||
|
||||
func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
||||
return parser.parseGeneral(scanner, true, false, termSymbols...)
|
||||
termSymbols = append(termSymbols, SymEos)
|
||||
return parser.parseGeneral(scanner, allowMultiExpr, termSymbols...)
|
||||
}
|
||||
|
||||
func couldBeACollection(t *term) bool {
|
||||
@@ -331,15 +394,39 @@ func couldBeACollection(t *term) bool {
|
||||
return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable
|
||||
}
|
||||
|
||||
// func areSymbolsOutOfCtx(tk *Token, ctxTerm *term, syms ...Symbol) bool {
|
||||
// var areOut = false
|
||||
// if ctxTerm != nil {
|
||||
// areOut = tk.IsOneOf(syms)
|
||||
// }
|
||||
// return areOut
|
||||
// }
|
||||
func listSubTree(tree *ast, listTerm *term, allowIndeces bool) (root *term, err error) {
|
||||
var tk *Token
|
||||
if allowIndeces {
|
||||
tk = NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
||||
root = newTerm(tk)
|
||||
if err = tree.addTerm(root); err == nil {
|
||||
err = tree.addTerm(listTerm)
|
||||
}
|
||||
} else {
|
||||
root = listTerm
|
||||
err = tree.addTerm(listTerm)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
||||
func changePrefix(tk *Token) {
|
||||
switch tk.Sym {
|
||||
case SymMinus:
|
||||
tk.SetSymbol(SymChangeSign)
|
||||
case SymPlus:
|
||||
tk.SetSymbol(SymUnchangeSign)
|
||||
case SymStar:
|
||||
tk.SetSymbol(SymDereference)
|
||||
case SymExclamation:
|
||||
tk.SetSymbol(SymNot)
|
||||
case SymDoublePlus:
|
||||
tk.SetSymbol(SymPreInc)
|
||||
case SymDoubleMinus:
|
||||
tk.SetSymbol(SymPreDec)
|
||||
}
|
||||
}
|
||||
|
||||
func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
|
||||
var selectorTerm *term = nil
|
||||
var currentTerm *term = nil
|
||||
var tk *Token
|
||||
@@ -353,7 +440,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
||||
// }
|
||||
|
||||
if tk.Sym == SymSemiColon {
|
||||
if allowForest {
|
||||
if hasFlag(ctx, allowMultiExpr) {
|
||||
tree.ToForest()
|
||||
firstToken = true
|
||||
currentTerm = nil
|
||||
@@ -367,57 +454,55 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
||||
|
||||
//fmt.Println("Token:", tk)
|
||||
if firstToken {
|
||||
if tk.Sym == SymMinus {
|
||||
tk.Sym = SymChangeSign
|
||||
} else if tk.Sym == SymPlus {
|
||||
tk.Sym = SymUnchangeSign
|
||||
}
|
||||
changePrefix(tk)
|
||||
// if tk.Sym == SymMinus {
|
||||
// tk.Sym = SymChangeSign
|
||||
// } else if tk.Sym == SymPlus {
|
||||
// tk.Sym = SymUnchangeSign
|
||||
// } else if tk.IsSymbol(SymStar) {
|
||||
// tk.SetSymbol(SymDereference)
|
||||
// } else if tk.IsSymbol(SymExclamation) {
|
||||
// tk.SetSymbol(SymNot)
|
||||
// }
|
||||
firstToken = false
|
||||
}
|
||||
|
||||
switch tk.Sym {
|
||||
case SymOpenRound:
|
||||
var subTree *ast
|
||||
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil {
|
||||
subTree.root.priority = priValue
|
||||
err = tree.addTerm(newExprTerm(subTree.root))
|
||||
currentTerm = subTree.root
|
||||
if subTree, err = parser.parseGeneral(scanner, ctx, SymClosedRound); err == nil {
|
||||
exprTerm := newExprTerm(subTree.root)
|
||||
err = tree.addTerm(exprTerm)
|
||||
currentTerm = exprTerm
|
||||
// subTree.root.priority = priValue
|
||||
// err = tree.addTerm(newExprTerm(subTree.root))
|
||||
// currentTerm = subTree.root
|
||||
}
|
||||
case SymFuncCall:
|
||||
var funcCallTerm *term
|
||||
if funcCallTerm, err = parser.parseFuncCall(scanner, allowVarRef, tk); err == nil {
|
||||
if funcCallTerm, err = parser.parseFuncCall(scanner, ctx, tk); err == nil {
|
||||
err = tree.addTerm(funcCallTerm)
|
||||
currentTerm = funcCallTerm
|
||||
}
|
||||
case SymOpenSquare:
|
||||
var listTerm *term
|
||||
parsingIndeces := couldBeACollection(currentTerm)
|
||||
if listTerm, err = parser.parseList(scanner, parsingIndeces, allowVarRef); err == nil {
|
||||
if parsingIndeces {
|
||||
indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
||||
indexTerm := newTerm(indexTk)
|
||||
if err = tree.addTerm(indexTerm); err == nil {
|
||||
err = tree.addTerm(listTerm)
|
||||
}
|
||||
} else {
|
||||
err = tree.addTerm(listTerm)
|
||||
}
|
||||
currentTerm = listTerm
|
||||
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
|
||||
if listTerm, err = parser.parseList(scanner, newCtx); err == nil {
|
||||
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||
}
|
||||
case SymOpenBrace:
|
||||
if currentTerm != nil && currentTerm.symbol() == SymColon {
|
||||
err = currentTerm.Errorf(`selector-case outside of a selector context`)
|
||||
} else {
|
||||
var mapTerm *term
|
||||
if mapTerm, err = parser.parseDictionary(scanner, allowVarRef); err == nil {
|
||||
if mapTerm, err = parser.parseDictionary(scanner, ctx); err == nil {
|
||||
err = tree.addTerm(mapTerm)
|
||||
currentTerm = mapTerm
|
||||
}
|
||||
}
|
||||
case SymEqual:
|
||||
// if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
||||
case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual, SymAmpersandEqual, SymVertBarEqual, SymDoubleLessEqual, SymDoubleGreaterEqual, SymCaretEqual:
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
// }
|
||||
firstToken = true
|
||||
case SymFuncDef:
|
||||
var funcDefTerm *term
|
||||
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
|
||||
@@ -426,24 +511,25 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
||||
}
|
||||
case SymDollarRound:
|
||||
var iterDefTerm *term
|
||||
if iterDefTerm, err = parser.parseIterDef(scanner, allowVarRef); err == nil {
|
||||
if iterDefTerm, err = parser.parseIterDef(scanner, ctx); err == nil {
|
||||
err = tree.addTerm(iterDefTerm)
|
||||
currentTerm = iterDefTerm
|
||||
}
|
||||
case SymIdentifier:
|
||||
if tk.source[0] == '@' && !allowVarRef {
|
||||
if tk.source[0] == '@' && !hasFlag(ctx, allowVarRef) {
|
||||
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
||||
} else {
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
}
|
||||
case SymQuestion:
|
||||
if selectorTerm, err = parser.parseSelector(scanner, tree, allowVarRef); err == nil {
|
||||
if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil {
|
||||
currentTerm = selectorTerm
|
||||
addFlags(ctx, selectorContext)
|
||||
}
|
||||
case SymColon, SymDoubleColon:
|
||||
var caseTerm *term
|
||||
if selectorTerm != nil {
|
||||
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
|
||||
if caseTerm, err = parser.parseSelectorCase(scanner, ctx, tk.Sym == SymDoubleColon); err == nil {
|
||||
addSelectorCase(selectorTerm, caseTerm)
|
||||
currentTerm = caseTerm
|
||||
if tk.Sym == SymDoubleColon {
|
||||
@@ -452,79 +538,34 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
||||
}
|
||||
} else {
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
if tk.IsOneOfA(SymColon, SymRange) {
|
||||
// Colon outside a selector term acts like a separator
|
||||
firstToken = true
|
||||
}
|
||||
}
|
||||
if tk.IsSymbol(SymColon) {
|
||||
// Colon outside a selector term acts like a separator
|
||||
firstToken = true
|
||||
}
|
||||
case SymPlusEqual, SymMinusEqual, SymStarEqual:
|
||||
currentTerm, err = parser.expandOpAssign(scanner, tree, tk, allowVarRef)
|
||||
default:
|
||||
currentTerm, err = tree.addToken(tk)
|
||||
}
|
||||
|
||||
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
|
||||
selectorTerm = nil
|
||||
|
||||
remFlags(ctx, selectorContext)
|
||||
}
|
||||
// lastSym = tk.Sym
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = tk.Error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// func checkPrevSymbol(lastSym, wantedSym Symbol, tk *Token) (err error) {
|
||||
// if lastSym != wantedSym {
|
||||
// err = fmt.Errorf(`assign operator (%q) must be preceded by a variable`, tk.source)
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
|
||||
func (parser *parser) expandOpAssign(scanner * scanner, tree *ast, tk *Token, allowVarRef bool) (t *term, err error) {
|
||||
var opSym Symbol
|
||||
var opString string
|
||||
|
||||
if tree.root != nil {
|
||||
switch tk.Sym {
|
||||
case SymPlusEqual:
|
||||
opString = "+"
|
||||
opSym = SymPlus
|
||||
case SymMinusEqual:
|
||||
opString = "-"
|
||||
opSym = SymMinus
|
||||
case SymStarEqual:
|
||||
opString = "*"
|
||||
opSym = SymStar
|
||||
default:
|
||||
err = tk.Errorf("unsopported operator %q", tk.source)
|
||||
return
|
||||
}
|
||||
leftExpr := tree.root.Clone()
|
||||
leftExpr.setParent(nil)
|
||||
if t, err = tree.addToken(NewToken(tk.row, tk.col, SymEqual, "=")); err == nil {
|
||||
t = leftExpr
|
||||
if err = tree.addTerm(leftExpr); err == nil {
|
||||
if t, err = tree.addToken(NewToken(tk.row, tk.col, opSym, opString)); err == nil {
|
||||
|
||||
var subTree *ast
|
||||
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare); err == nil {
|
||||
if scanner.Previous().IsOneOfA(SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare) {
|
||||
if err = scanner.UnreadToken(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
subTree.root.priority = priValue
|
||||
err = tree.addTerm(newExprTerm(subTree.root))
|
||||
t = subTree.root
|
||||
}
|
||||
}
|
||||
if !tk.IsOneOf(termSymbols) {
|
||||
var symDesc string
|
||||
if tk.IsSymbol(SymError) {
|
||||
symDesc = tk.ErrorText()
|
||||
} else {
|
||||
symDesc = SymToString(tk.Sym)
|
||||
}
|
||||
err = tk.ErrorExpectedGotStringWithPrefix("expected one of", SymListToString(termSymbols, true), symDesc)
|
||||
} else {
|
||||
err = tk.Error()
|
||||
}
|
||||
} else {
|
||||
err = tk.Errorf("left operand of %q must be a variable or a variable expression", tk)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+7
-5
@@ -31,11 +31,11 @@ func pluginExists(name string) (exists bool) {
|
||||
func makePluginName(name string) (decorated string) {
|
||||
var template string
|
||||
if execName, err := os.Executable(); err != nil || !strings.Contains(execName, "debug") {
|
||||
template = "expr-%s-plugin.so"
|
||||
template = "expr-%s-plugin%s"
|
||||
} else {
|
||||
template = "expr-%s-plugin.so.debug"
|
||||
template = "expr-%s-plugin%s.debug"
|
||||
}
|
||||
decorated = fmt.Sprintf(template, name)
|
||||
decorated = fmt.Sprintf(template, name, SHAREDLIBRARY_EXTENSION)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -93,10 +93,12 @@ func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err erro
|
||||
|
||||
func importPluginFromSearchPath(name any) (count int, err error) {
|
||||
var moduleSpec any
|
||||
|
||||
var it Iterator
|
||||
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH)
|
||||
count = 0
|
||||
it := NewAnyIterator(name)
|
||||
if it, err = NewIterator(name); err != nil {
|
||||
return
|
||||
}
|
||||
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
||||
if module, ok := moduleSpec.(string); ok {
|
||||
if err = importPlugin(dirList, module); err != nil {
|
||||
|
||||
+65
-14
@@ -40,9 +40,9 @@ func DefaultTranslations() map[Symbol]Symbol {
|
||||
SymKwAnd: SymAnd,
|
||||
SymDoubleVertBar: SymOr,
|
||||
SymKwOr: SymOr,
|
||||
SymTilde: SymNot,
|
||||
SymKwNot: SymNot,
|
||||
SymLessGreater: SymNotEqual,
|
||||
// SymTilde: SymNot,
|
||||
SymKwNot: SymNot,
|
||||
SymLessGreater: SymNotEqual,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,6 +124,8 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
tk = scanner.moveOn(SymDoublePlus, ch, next)
|
||||
} else if next == '=' {
|
||||
tk = scanner.moveOn(SymPlusEqual, ch, next)
|
||||
} else if next == '>' {
|
||||
tk = scanner.moveOn(SymPlusGreater, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymPlus, ch)
|
||||
}
|
||||
@@ -149,6 +151,8 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
if next, _ := scanner.peek(); next == '*' {
|
||||
scanner.readChar()
|
||||
tk = scanner.fetchBlockComment()
|
||||
} else if next, _ = scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymSlashEqual, ch, next)
|
||||
} else if next == '/' {
|
||||
scanner.readChar()
|
||||
tk = scanner.fetchOnLineComment()
|
||||
@@ -165,13 +169,19 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
case '|':
|
||||
if next, _ := scanner.peek(); next == '|' {
|
||||
tk = scanner.moveOn(SymDoubleVertBar, ch, next)
|
||||
} else if next, _ = scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymVertBarEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymVertBar, ch)
|
||||
}
|
||||
case ',':
|
||||
tk = scanner.makeToken(SymComma, ch)
|
||||
case '^':
|
||||
tk = scanner.makeToken(SymCaret, ch)
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymCaretEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymCaret, ch)
|
||||
}
|
||||
case ':':
|
||||
if next, _ := scanner.peek(); next == ':' {
|
||||
tk = scanner.moveOn(SymDoubleColon, ch, next)
|
||||
@@ -200,14 +210,14 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
tk = scanner.makeToken(SymQuote, ch)
|
||||
escape = false
|
||||
} else {
|
||||
tk = scanner.fetchString(ch)
|
||||
tk = scanner.fetchString(ch, true)
|
||||
}
|
||||
case '"':
|
||||
if escape {
|
||||
tk = scanner.makeToken(SymDoubleQuote, ch)
|
||||
escape = false
|
||||
} else {
|
||||
tk = scanner.fetchString(ch)
|
||||
tk = scanner.fetchString(ch, true)
|
||||
}
|
||||
case '`':
|
||||
tk = scanner.makeToken(SymBackTick, ch)
|
||||
@@ -230,11 +240,17 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
case '&':
|
||||
if next, _ := scanner.peek(); next == '&' {
|
||||
tk = scanner.moveOn(SymDoubleAmpersand, ch, next)
|
||||
} else if next, _ = scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymAmpersandEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymAmpersand, ch)
|
||||
}
|
||||
case '%':
|
||||
tk = scanner.makeToken(SymPercent, ch)
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymPercEqual, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymPercent, ch)
|
||||
}
|
||||
case '#':
|
||||
tk = scanner.makeToken(SymHash, ch)
|
||||
case '@':
|
||||
@@ -263,9 +279,18 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymLessOrEqual, ch, next)
|
||||
} else if next == '<' {
|
||||
tk = scanner.moveOn(SymAppend, ch, next)
|
||||
scanner.readChar()
|
||||
next2, _ := scanner.readChar()
|
||||
scanner.unreadChar()
|
||||
if next2 == '=' {
|
||||
tk = scanner.moveOn(SymDoubleLessEqual, ch, next, next2)
|
||||
} else {
|
||||
tk = scanner.accept(SymDoubleLess, ch, next)
|
||||
}
|
||||
} else if next == '>' {
|
||||
tk = scanner.moveOn(SymLessGreater, ch, next)
|
||||
} else if next == '+' {
|
||||
tk = scanner.moveOn(SymLessPlus, ch, next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymLess, ch)
|
||||
}
|
||||
@@ -273,7 +298,14 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
if next, _ := scanner.peek(); next == '=' {
|
||||
tk = scanner.moveOn(SymGreaterOrEqual, ch, next)
|
||||
} else if next == '>' {
|
||||
tk = scanner.moveOn(SymInsert, ch, next)
|
||||
scanner.readChar()
|
||||
next2, _ := scanner.readChar()
|
||||
scanner.unreadChar()
|
||||
if next2 == '=' {
|
||||
tk = scanner.moveOn(SymDoubleGreaterEqual, ch, next, next2)
|
||||
} else {
|
||||
tk = scanner.accept(SymDoubleGreater, ch, next)
|
||||
}
|
||||
} else {
|
||||
tk = scanner.makeToken(SymGreater, ch)
|
||||
}
|
||||
@@ -283,6 +315,14 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
||||
tk.source += ")"
|
||||
} else if next == '$' {
|
||||
tk = scanner.moveOn(SymDoubleDollar, ch, next)
|
||||
} else if next == '{' {
|
||||
scanner.readChar()
|
||||
if tk = scanner.fetchString('}', false); tk != nil {
|
||||
tk.Sym = SymIdentifier
|
||||
}
|
||||
} else if next == '_' || (next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z') {
|
||||
scanner.readChar()
|
||||
tk = scanner.fetchIdentifier(next)
|
||||
} else {
|
||||
tk = scanner.makeToken(SymDollar, ch)
|
||||
}
|
||||
@@ -458,7 +498,7 @@ func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) {
|
||||
tk = scanner.makeErrorToken(err)
|
||||
} else {
|
||||
var value any
|
||||
err = scanner.sync(err) // TODO: Check this function
|
||||
_ = scanner.sync(err) // TODO: Check this function
|
||||
txt := sb.String()
|
||||
if sym == SymFloat {
|
||||
value, err = strconv.ParseFloat(txt, 64)
|
||||
@@ -558,7 +598,7 @@ func (scanner *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
|
||||
func (scanner *scanner) fetchString(termCh byte, addQuote bool) (tk *Token) {
|
||||
var err error
|
||||
var ch, prev byte
|
||||
var sb strings.Builder
|
||||
@@ -590,13 +630,17 @@ func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
tk = scanner.makeErrorToken(errors.New("missing string termination \""))
|
||||
tk = scanner.makeErrorToken(errors.New(string(termCh)))
|
||||
} else {
|
||||
tk = scanner.makeErrorToken(err)
|
||||
}
|
||||
} else {
|
||||
txt := sb.String()
|
||||
tk = scanner.makeValueToken(SymString, `"`+txt+`"`, txt)
|
||||
if addQuote {
|
||||
tk = scanner.makeValueToken(SymString, `"`+txt+`"`, txt)
|
||||
} else {
|
||||
tk = scanner.makeValueToken(SymString, txt, txt)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -628,9 +672,16 @@ func (scanner *scanner) translate(sym Symbol) Symbol {
|
||||
|
||||
func (scanner *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
|
||||
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++ {
|
||||
if len(chars) > 1 {
|
||||
scanner.readChar()
|
||||
}
|
||||
// }
|
||||
return
|
||||
}
|
||||
|
||||
func (scanner *scanner) accept(sym Symbol, chars ...byte) (tk *Token) {
|
||||
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,11 @@ func NewSimpleStore() *SimpleStore {
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (ss *SimpleStore) Init() {
|
||||
ss.varStore = make(map[string]any)
|
||||
ss.funcStore = make(map[string]ExprFunc)
|
||||
}
|
||||
|
||||
func filterRefName(name string) bool { return name[0] != '@' }
|
||||
//func filterPrivName(name string) bool { return name[0] != '_' }
|
||||
|
||||
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||
// All rights reserved.
|
||||
|
||||
// Symbol.go
|
||||
package expr
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var symbolMap map[Symbol]symbolSpec
|
||||
|
||||
type symbolClass int16
|
||||
|
||||
const (
|
||||
symClassOperator symbolClass = iota
|
||||
symClassCommand
|
||||
symClassIdentifier
|
||||
symClassDelimiter
|
||||
symClassParenthesis
|
||||
symClassDeclaration
|
||||
symClassValue
|
||||
symClassOther
|
||||
)
|
||||
|
||||
type symbolSpec struct {
|
||||
repr string
|
||||
kind symbolClass
|
||||
opType termPosition
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
||||
symbolMap = map[Symbol]symbolSpec{
|
||||
SymUnknown: {"<unknown>", symClassOther, posLeaf}, // -1: Unknown symbol
|
||||
SymNone: {"<null>", symClassOther, posLeaf}, // 0: Null value for variable of type symbol
|
||||
SymError: {"<error>", symClassOther, posLeaf}, // 1: Error reading from stream
|
||||
SymEos: {"<eos>", symClassOther, posLeaf}, // 2: End of stream
|
||||
SymMinus: {"-", symClassOperator, posInfix}, // 3: '-'
|
||||
SymMinusEqual: {"-=", symClassOperator, posInfix}, // 4: '-='
|
||||
SymDoubleMinus: {"--", symClassOperator, posPostfix}, // 5: '--'
|
||||
SymPlus: {"+", symClassOperator, posInfix}, // 6: '+'
|
||||
SymPlusEqual: {"+=", symClassOperator, posInfix}, // 7: '+='
|
||||
SymDoublePlus: {"++", symClassOperator, posPostfix}, // 8: '++'
|
||||
SymStar: {"*", symClassOperator, posInfix}, // 9: '*'
|
||||
SymDoubleStar: {"**", symClassOperator, posInfix}, // 10: '**'
|
||||
SymSlash: {"/", symClassOperator, posInfix}, // 11: '/'
|
||||
SymBackSlash: {"\\", symClassOperator, posLeaf}, // 12: '\'
|
||||
SymVertBar: {"|", symClassOperator, posInfix}, // 13: '|'
|
||||
SymDoubleVertBar: {"||", symClassOperator, posInfix}, // 14: '||'
|
||||
SymComma: {",", symClassOperator, posInfix}, // 15: ','
|
||||
SymColon: {":", symClassOperator, posInfix}, // 16: ':'
|
||||
SymSemiColon: {";", symClassOperator, posInfix}, // 17: ';'
|
||||
SymDot: {".", symClassOperator, posInfix}, // 18: '.'
|
||||
SymDotSlash: {"./", symClassOperator, posInfix}, // 19: './'
|
||||
SymQuote: {"'", symClassDelimiter, posLeaf}, // 20: '\''
|
||||
SymDoubleQuote: {"\"", symClassDelimiter, posLeaf}, // 21: '"'
|
||||
SymBackTick: {"`", symClassDelimiter, posLeaf}, // 22: '`'
|
||||
SymExclamation: {"!", symClassOperator, posPostfix}, // 23: '!'
|
||||
SymQuestion: {"?", symClassOperator, posInfix}, // 24: '?'
|
||||
SymAmpersand: {"&", symClassOperator, posInfix}, // 25: '&'
|
||||
SymDoubleAmpersand: {"&&", symClassOperator, posInfix}, // 26: '&&'
|
||||
SymPercent: {"%", symClassOperator, posInfix}, // 27: '%'
|
||||
SymAt: {"@", symClassOperator, posPrefix}, // 28: '@'
|
||||
SymUndescore: {"_", symClassIdentifier, posLeaf}, // 29: '_'
|
||||
SymEqual: {"=", symClassOperator, posInfix}, // 30: '='
|
||||
SymDoubleEqual: {"==", symClassOperator, posInfix}, // 31: '=='
|
||||
SymLess: {"<", symClassOperator, posInfix}, // 32: '<'
|
||||
SymLessOrEqual: {"<=", symClassOperator, posInfix}, // 33: '<='
|
||||
SymGreater: {">", symClassOperator, posInfix}, // 34: '>'
|
||||
SymGreaterOrEqual: {">=", symClassOperator, posInfix}, // 35: '>='
|
||||
SymLessGreater: {"<>", symClassOperator, posInfix}, // 36: '<>'
|
||||
SymNotEqual: {"!=", symClassOperator, posInfix}, // 37: '!='
|
||||
SymDollar: {"$", symClassOperator, posPrefix}, // 38: '$'
|
||||
SymHash: {"#", symClassOperator, posPrefix}, // 39: '#'
|
||||
SymOpenRound: {"(", symClassParenthesis, posPrefix}, // 40: '('
|
||||
SymClosedRound: {")", symClassParenthesis, posPostfix}, // 41: ')'
|
||||
SymOpenSquare: {"[", symClassParenthesis, posPrefix}, // 42: '['
|
||||
SymClosedSquare: {"]", symClassParenthesis, posPostfix}, // 43: ']'
|
||||
SymOpenBrace: {"{", symClassParenthesis, posPrefix}, // 44: '{'
|
||||
SymClosedBrace: {"}", symClassParenthesis, posPostfix}, // 45: '}'
|
||||
SymTilde: {"~", symClassOperator, posPrefix}, // 46: '~'
|
||||
SymDoubleQuestion: {"??", symClassOperator, posInfix}, // 47: '??'
|
||||
SymQuestionEqual: {"?=", symClassOperator, posInfix}, // 48: '?='
|
||||
SymQuestionExclam: {"?!", symClassOperator, posInfix}, // 49: '?!'
|
||||
SymDoubleAt: {"@@", symClassCommand, posLeaf}, // 50: '@@'
|
||||
SymDoubleColon: {"::", symClassOperator, posInfix}, // 51: '::'
|
||||
SymDoubleGreater: {">>", symClassOperator, posInfix}, // 52: '>>'
|
||||
SymDoubleLess: {"<<", symClassOperator, posInfix}, // 53: '<<'
|
||||
SymCaret: {"^", symClassOperator, posInfix}, // 54: '^'
|
||||
SymDollarRound: {"$(", symClassOperator, posPrefix}, // 55: '$('
|
||||
SymOpenClosedRound: {"()", symClassOperator, posPostfix}, // 56: '()'
|
||||
SymDoubleDollar: {"$$", symClassCommand, posLeaf}, // 57: '$$'
|
||||
SymDoubleDot: {"..", symClassOperator, posInfix}, // 58: '..'
|
||||
SymTripleDot: {"...", symClassOperator, posPostfix}, // 59: '...'
|
||||
SymStarEqual: {"*=", symClassOperator, posInfix}, // 60: '*='
|
||||
SymSlashEqual: {"/=", symClassOperator, posInfix}, // 61: '/='
|
||||
SymPercEqual: {"%=", symClassOperator, posInfix}, // 62: '%='
|
||||
SymDoubleLessEqual: {"<<=", symClassOperator, posInfix}, // 63: '<<='
|
||||
SymDoubleGreaterEqual: {">>=", symClassOperator, posInfix}, // 64: '>>='
|
||||
SymAmpersandEqual: {"&=", symClassOperator, posInfix}, // 65: '&='
|
||||
SymVertBarEqual: {"|=", symClassOperator, posInfix}, // 65: '|='
|
||||
SymCaretEqual: {"^=", symClassOperator, posInfix}, // 66: '^='
|
||||
SymPlusGreater: {"+>", symClassOperator, posInfix}, // 67: '+>'
|
||||
SymLessPlus: {"<+", symClassOperator, posInfix}, // 68: '<+'
|
||||
SymPreInc: {"++", symClassOperator, posPrefix}, // : '++'
|
||||
SymPreDec: {"--", symClassOperator, posPrefix}, // : '--'
|
||||
// SymChangeSign
|
||||
// SymUnchangeSign
|
||||
// SymIdentifier
|
||||
// SymBool
|
||||
// SymInteger
|
||||
// SymVariable
|
||||
// SymFloat
|
||||
// SymFraction
|
||||
// SymString
|
||||
// SymIterator
|
||||
// SymOr: "or",
|
||||
// SymAnd: "and",
|
||||
// SymNot: "not",
|
||||
// SymComment
|
||||
// SymFuncCall
|
||||
// SymFuncDef
|
||||
// SymList
|
||||
// SymDict
|
||||
// SymIndex
|
||||
// SymExpression
|
||||
// SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||
// SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||
// // SymOpenComment // 0: '/*'
|
||||
// // SymClosedComment // 0: '*/'
|
||||
// // SymOneLineComment // 0: '//'
|
||||
// keywordBase
|
||||
SymKwAnd: {"and", symClassOperator, posInfix},
|
||||
SymKwNot: {"not", symClassOperator, posInfix},
|
||||
SymKwOr: {"or", symClassOperator, posInfix},
|
||||
SymKwBut: {"but", symClassOperator, posInfix},
|
||||
SymKwFunc: {"func(", symClassDeclaration, posPrefix},
|
||||
SymKwBuiltin: {"builtin", symClassOperator, posPrefix},
|
||||
SymKwPlugin: {"plugin", symClassOperator, posPrefix},
|
||||
SymKwIn: {"in", symClassOperator, posInfix},
|
||||
SymKwInclude: {"include", symClassOperator, posPrefix},
|
||||
SymKwNil: {"nil", symClassValue, posLeaf},
|
||||
SymKwUnset: {"unset", symClassOperator, posPrefix},
|
||||
}
|
||||
}
|
||||
|
||||
func SymToString(sym Symbol) string {
|
||||
if s, ok := symbolMap[sym]; ok {
|
||||
return s.repr
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func SymListToString(symList []Symbol, quote bool) string {
|
||||
var sb strings.Builder
|
||||
if len(symList) == 0 {
|
||||
sb.WriteString("<nothing>")
|
||||
} else {
|
||||
for _, sym := range symList {
|
||||
if sb.Len() > 0 {
|
||||
sb.WriteByte(',')
|
||||
sb.WriteByte(' ')
|
||||
}
|
||||
if quote {
|
||||
sb.WriteByte('`')
|
||||
}
|
||||
sb.WriteString(SymToString(sym))
|
||||
if quote {
|
||||
sb.WriteByte('`')
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func StringEndsWithOperator(s string) bool {
|
||||
return endingOperator(s) != SymNone
|
||||
}
|
||||
|
||||
func endingOperator(s string) (sym Symbol) {
|
||||
var matchLength = 0
|
||||
sym = SymNone
|
||||
lower := strings.TrimRight(strings.ToLower(s), " \t")
|
||||
for symbol, spec := range symbolMap {
|
||||
if strings.HasSuffix(lower, spec.repr) {
|
||||
if len(spec.repr) > matchLength {
|
||||
matchLength = len(spec.repr)
|
||||
if spec.kind == symClassOperator && (spec.opType == posInfix || spec.opType == posPrefix) {
|
||||
sym = symbol
|
||||
} else {
|
||||
sym = SymNone
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -7,70 +7,82 @@ package expr
|
||||
type Symbol int16
|
||||
|
||||
const (
|
||||
SymUnknown Symbol = iota - 1 // -1: Unknown symbol
|
||||
SymNone // 0: Null value for variable of type symbol
|
||||
SymError // 1: Error reading from stream
|
||||
SymEos // 2: End of stream
|
||||
SymMinus // 3: '-'
|
||||
SymMinusEqual // 4: '-='
|
||||
SymDoubleMinus // 5: '--'
|
||||
SymPlus // 6: '+'
|
||||
SymPlusEqual // 7: '+='
|
||||
SymDoublePlus // 8: '++'
|
||||
SymStar // 9: '*'
|
||||
SymDoubleStar // 10: '**'
|
||||
SymSlash // 11: '/'
|
||||
SymBackSlash // 12: '\'
|
||||
SymVertBar // 13: '|'
|
||||
SymDoubleVertBar // 14: '||'
|
||||
SymComma // 15: ','
|
||||
SymColon // 16: ':'
|
||||
SymSemiColon // 17: ';'
|
||||
SymDot // 18: '.'
|
||||
SymDotSlash // 19: './'
|
||||
SymQuote // 20: '\''
|
||||
SymDoubleQuote // 21: '"'
|
||||
SymBackTick // 22: '`'
|
||||
SymExclamation // 23: '!'
|
||||
SymQuestion // 24: '?'
|
||||
SymAmpersand // 25: '&'
|
||||
SymDoubleAmpersand // 26: '&&'
|
||||
SymPercent // 27: '%'
|
||||
SymAt // 28: '@'
|
||||
SymUndescore // 29: '_'
|
||||
SymEqual // 30: '='
|
||||
SymDoubleEqual // 31: '=='
|
||||
SymLess // 32: '<'
|
||||
SymLessOrEqual // 33: '<='
|
||||
SymGreater // 34: '>'
|
||||
SymGreaterOrEqual // 35: '>='
|
||||
SymLessGreater // 36: '<>'
|
||||
SymNotEqual // 37: '!='
|
||||
SymDollar // 38: '$'
|
||||
SymHash // 39: '#'
|
||||
SymOpenRound // 40: '('
|
||||
SymClosedRound // 41: ')'
|
||||
SymOpenSquare // 42: '['
|
||||
SymClosedSquare // 43: ']'
|
||||
SymOpenBrace // 44: '{'
|
||||
SymClosedBrace // 45: '}'
|
||||
SymTilde // 46: '~'
|
||||
SymDoubleQuestion // 47: '??'
|
||||
SymQuestionEqual // 48: '?='
|
||||
SymQuestionExclam // 49: '?!'
|
||||
SymDoubleAt // 50: '@@'
|
||||
SymDoubleColon // 51: '::'
|
||||
SymInsert // 52: '>>'
|
||||
SymAppend // 53: '<<'
|
||||
SymCaret // 54: '^'
|
||||
SymDollarRound // 55: '$('
|
||||
SymOpenClosedRound // 56: '()'
|
||||
SymDoubleDollar // 57: '$$'
|
||||
SymDoubleDot // 58: '..'
|
||||
SymTripleDot // 59: '...'
|
||||
SymStarEqual // 60: '*='
|
||||
SymUnknown Symbol = iota - 1 // -1: Unknown symbol
|
||||
SymNone // 0: Null value for variable of type symbol
|
||||
SymError // 1: Error reading from stream
|
||||
SymEos // 2: End of stream
|
||||
SymMinus // 3: '-'
|
||||
SymMinusEqual // 4: '-='
|
||||
SymDoubleMinus // 5: '--'
|
||||
SymPlus // 6: '+'
|
||||
SymPlusEqual // 7: '+='
|
||||
SymDoublePlus // 8: '++'
|
||||
SymStar // 9: '*'
|
||||
SymDoubleStar // 10: '**'
|
||||
SymSlash // 11: '/'
|
||||
SymBackSlash // 12: '\'
|
||||
SymVertBar // 13: '|'
|
||||
SymDoubleVertBar // 14: '||'
|
||||
SymComma // 15: ','
|
||||
SymColon // 16: ':'
|
||||
SymSemiColon // 17: ';'
|
||||
SymDot // 18: '.'
|
||||
SymDotSlash // 19: './'
|
||||
SymQuote // 20: '\''
|
||||
SymDoubleQuote // 21: '"'
|
||||
SymBackTick // 22: '`'
|
||||
SymExclamation // 23: '!'
|
||||
SymQuestion // 24: '?'
|
||||
SymAmpersand // 25: '&'
|
||||
SymDoubleAmpersand // 26: '&&'
|
||||
SymPercent // 27: '%'
|
||||
SymAt // 28: '@'
|
||||
SymUndescore // 29: '_'
|
||||
SymEqual // 30: '='
|
||||
SymDoubleEqual // 31: '=='
|
||||
SymLess // 32: '<'
|
||||
SymLessOrEqual // 33: '<='
|
||||
SymGreater // 34: '>'
|
||||
SymGreaterOrEqual // 35: '>='
|
||||
SymLessGreater // 36: '<>'
|
||||
SymNotEqual // 37: '!='
|
||||
SymDollar // 38: '$'
|
||||
SymHash // 39: '#'
|
||||
SymOpenRound // 40: '('
|
||||
SymClosedRound // 41: ')'
|
||||
SymOpenSquare // 42: '['
|
||||
SymClosedSquare // 43: ']'
|
||||
SymOpenBrace // 44: '{'
|
||||
SymClosedBrace // 45: '}'
|
||||
SymTilde // 46: '~'
|
||||
SymDoubleQuestion // 47: '??'
|
||||
SymQuestionEqual // 48: '?='
|
||||
SymQuestionExclam // 49: '?!'
|
||||
SymDoubleAt // 50: '@@'
|
||||
SymDoubleColon // 51: '::'
|
||||
SymDoubleGreater // 52: '>>'
|
||||
SymDoubleLess // 53: '<<'
|
||||
SymCaret // 54: '^'
|
||||
SymDollarRound // 55: '$('
|
||||
SymOpenClosedRound // 56: '()'
|
||||
SymDoubleDollar // 57: '$$'
|
||||
SymDoubleDot // 58: '..'
|
||||
SymTripleDot // 59: '...'
|
||||
SymStarEqual // 60: '*='
|
||||
SymSlashEqual // 61: '/='
|
||||
SymPercEqual // 62: '%='
|
||||
SymDoubleLessEqual // 63: '<<='
|
||||
SymDoubleGreaterEqual // 64: '>>='
|
||||
SymAmpersandEqual // 65: '&='
|
||||
SymVertBarEqual // 65: '|='
|
||||
SymCaretEqual // 66: '^='
|
||||
SymPlusGreater // 67: '+>'
|
||||
SymLessPlus // 68: '<+'
|
||||
SymChangeSign
|
||||
SymUnchangeSign
|
||||
SymDereference
|
||||
SymPreInc
|
||||
SymPreDec
|
||||
SymIdentifier
|
||||
SymBool
|
||||
SymInteger
|
||||
@@ -88,6 +100,7 @@ const (
|
||||
SymList
|
||||
SymDict
|
||||
SymIndex
|
||||
SymRange // [index : index]
|
||||
SymExpression
|
||||
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||
@@ -106,6 +119,7 @@ const (
|
||||
SymKwPlugin
|
||||
SymKwIn
|
||||
SymKwInclude
|
||||
SymKwMap
|
||||
SymKwNil
|
||||
SymKwUnset
|
||||
)
|
||||
@@ -122,6 +136,7 @@ func init() {
|
||||
"FUNC": SymKwFunc,
|
||||
"IN": SymKwIn,
|
||||
"INCLUDE": SymKwInclude,
|
||||
"MAP": SymKwMap,
|
||||
"NOT": SymKwNot,
|
||||
"OR": SymKwOr,
|
||||
"NIL": SymKwNil,
|
||||
|
||||
+2
-1
@@ -26,12 +26,13 @@ func TestBool(t *testing.T) {
|
||||
/* 12 */ {`true or false`, true, nil},
|
||||
/* 13 */ {`true or []`, true, nil},
|
||||
/* 14 */ {`[] or false`, nil, errors.New(`got list as left operand type of 'OR' operator, it must be bool`)},
|
||||
/* 15 */ {`!true`, false, nil},
|
||||
/* 13 */ //{`true or []`, nil, errors.New(`[1:8] left operand 'true' [bool] and right operand '[]' [list] are not compatible with operator "OR"`)},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 1)
|
||||
// runTestSuiteSpec(t, section, inputs, 15)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
|
||||
+17
-11
@@ -30,9 +30,9 @@ func TestFuncBase(t *testing.T) {
|
||||
/* 16 */ {`isString("3" + 1)`, true, nil},
|
||||
/* 17 */ {`isList(["3", 1])`, true, nil},
|
||||
/* 18 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
||||
/* 19 */ {`isFract(1|3)`, true, nil},
|
||||
/* 20 */ {`isFract(3|1)`, false, nil},
|
||||
/* 21 */ {`isRational(3|1)`, true, nil},
|
||||
/* 19 */ {`isFract(1:3)`, true, nil},
|
||||
/* 20 */ {`isFract(3:1)`, false, nil},
|
||||
/* 21 */ {`isRational(3:1)`, true, nil},
|
||||
/* 22 */ {`fract("2.2(3)")`, newFraction(67, 30), nil},
|
||||
/* 23 */ {`fract("1.21(3)")`, newFraction(91, 75), nil},
|
||||
/* 24 */ {`fract(1.21(3))`, newFraction(91, 75), nil},
|
||||
@@ -41,26 +41,32 @@ func TestFuncBase(t *testing.T) {
|
||||
/* 27 */ {`dec(2.0)`, float64(2), nil},
|
||||
/* 28 */ {`dec("2.0")`, float64(2), nil},
|
||||
/* 29 */ {`dec(true)`, float64(1), nil},
|
||||
/* 30 */ {`dec(true")`, nil, `[1:11] missing string termination "`},
|
||||
/* 30 */ {`dec(true")`, nil, "[1:11] expected one of `,`, `)`, got `\"`"},
|
||||
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`},
|
||||
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`},
|
||||
/* 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},
|
||||
/* 36 */ {`bool(2)`, true, nil},
|
||||
/* 37 */ {`bool(1|2)`, true, nil},
|
||||
/* 37 */ {`bool(1:2)`, true, nil},
|
||||
/* 38 */ {`bool(1.0)`, true, nil},
|
||||
/* 39 */ {`bool("1")`, true, nil},
|
||||
/* 40 */ {`bool(false)`, false, nil},
|
||||
/* 41 */ {`bool([1])`, nil, `bool(): can't convert list to bool`},
|
||||
/* 42 */ {`dec(false)`, float64(0), nil},
|
||||
/* 43 */ {`dec(1|2)`, float64(0.5), nil},
|
||||
/* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
|
||||
/* 41 */ {`bool([1])`, true, nil},
|
||||
/* 42 */ {`bool([])`, false, nil},
|
||||
/* 43 */ {`bool({})`, false, nil},
|
||||
/* 44 */ {`bool({1:"one"})`, true, nil},
|
||||
/* 45 */ {`dec(false)`, float64(0), nil},
|
||||
/* 46 */ {`dec(1:2)`, float64(0.5), nil},
|
||||
/* 47 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
|
||||
/* 48 */ {`eval("a=3"); a`, int64(3), nil},
|
||||
/* 49 */ {`int(5:2)`, int64(2), nil},
|
||||
|
||||
// /* 45 */ {`string([1])`, nil, `string(): can't convert list to string`},
|
||||
}
|
||||
|
||||
t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
//runTestSuiteSpec(t, section, inputs, 10)
|
||||
// runTestSuiteSpec(t, section, inputs, 49)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
@@ -21,10 +21,11 @@ func TestFuncRun(t *testing.T) {
|
||||
/* 7 */ {`builtin "iterator"; run($(1,2,3), func(){1}, "prrr")`, nil, `paramter "vars" must be a dictionary, passed prrr [string]`},
|
||||
/* 8 */ {`builtin "iterator"; run($(1,2,3), operator=nil)`, nil, nil},
|
||||
/* 9 */ {`builtin "iterator"; run($(1,2,3), operatorx=nil)`, nil, `run(): unknown param "operatorx"`},
|
||||
/* 10 */ {`builtin ["os.file", "iterator"]; it = fileReadIterator("test-file.txt"); run(it)`, nil, nil},
|
||||
}
|
||||
|
||||
//t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
//runTestSuiteSpec(t, section, inputs, 1)
|
||||
runTestSuite(t, section, inputs)
|
||||
runTestSuiteSpec(t, section, inputs, 10)
|
||||
// runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -26,10 +27,13 @@ func TestFuncOs(t *testing.T) {
|
||||
/* 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},
|
||||
/* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, `fileReadTextAll(): invalid file handle 123 [int64]`},
|
||||
/* 16 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); it++`, "uno", nil},
|
||||
/* 17 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); it++;it++;it++`, nil, io.EOF.Error()},
|
||||
/* 18 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); it.clean`, nil, nil},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
//runTestSuiteSpec(t, section, inputs, 2)
|
||||
runTestSuite(t, section, inputs)
|
||||
runTestSuiteSpec(t, section, inputs, 18)
|
||||
// runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ func TestFuncString(t *testing.T) {
|
||||
/* 1 */ {`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},
|
||||
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, `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 an integer (1)`},
|
||||
/* 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},
|
||||
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
|
||||
@@ -31,6 +31,8 @@ func TestFuncString(t *testing.T) {
|
||||
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil},
|
||||
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, `strJoin(): expected string, got integer (1)`},
|
||||
/* 19 */ {`builtin "string"; strJoin()`, nil, `strJoin(): too few params -- expected 1 or more, got 0`},
|
||||
/* 20 */ {`builtin "string"; strUpper("StOp")`, "STOP", nil},
|
||||
/* 21 */ {`builtin "string"; strLower("StOp")`, "stop", nil},
|
||||
|
||||
/* 69 */ /*{`builtin "string"; $$global`, `vars: {
|
||||
}
|
||||
|
||||
+13
-1
@@ -72,6 +72,7 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
|
||||
var expr Expr
|
||||
var gotResult any
|
||||
var gotErr error
|
||||
var eq, eqDone bool
|
||||
|
||||
wantErr := getWantedError(input)
|
||||
|
||||
@@ -90,7 +91,18 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
|
||||
gotResult, gotErr = expr.Eval(ctx)
|
||||
}
|
||||
|
||||
eq := reflect.DeepEqual(gotResult, input.wantResult)
|
||||
if input.wantResult != nil && gotResult != nil {
|
||||
if ls1, ok := input.wantResult.(*ListType); ok {
|
||||
if ls2, ok := gotResult.(*ListType); ok {
|
||||
eq = ls1.Equals(*ls2)
|
||||
eqDone = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !eqDone {
|
||||
eq = reflect.DeepEqual(gotResult, input.wantResult)
|
||||
}
|
||||
|
||||
if !eq /*gotResult != input.wantResult*/ {
|
||||
t.Errorf("%d: `%s` -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, TypeName(gotResult), input.wantResult, TypeName(input.wantResult))
|
||||
|
||||
+12
-7
@@ -17,18 +17,21 @@ func TestExpr(t *testing.T) {
|
||||
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
|
||||
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
|
||||
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
|
||||
/* 6 */ {`a=3; a+=1`, int64(4), nil},
|
||||
/* 7 */ {`a=3; a-=1`, int64(2), nil},
|
||||
/* 8 */ {`a=3; a*=2`, int64(6), nil},
|
||||
/* 6 */ {`a=3; a+=1; a`, int64(4), nil},
|
||||
/* 7 */ {`a=3; a-=1; a`, int64(2), nil},
|
||||
/* 8 */ {`a=3; a*=2; a`, int64(6), nil},
|
||||
/* 9 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil},
|
||||
/* 10 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil},
|
||||
/* 11 */ {`a=3; a/=2`, nil, `[1:8] left operand of "=" must be a variable or a collection's item`},
|
||||
/* 12 */ {`*=2`, nil, `[1:2] left operand of "*=" must be a variable or a variable expression`},
|
||||
/* 11 */ {`a=3; a/=2`, int64(1), nil},
|
||||
/* 12 */ {`*=2`, nil, `[1:2] infix operator "*=" requires two non-nil operands, got 1`},
|
||||
/* 13 */ {`a=3; a*=2+1; a`, int64(9), nil},
|
||||
/* 14 */ {`a=3; (a*=2)+1; a`, int64(6), nil},
|
||||
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
|
||||
/* 16 */ {`v=[2]; a=1; v[a-=1]=5; v[0]`, int64(5), nil},
|
||||
/* 17 */ {`
|
||||
/* 17 */ {`true ? {"a"} :: {"b"}`, "a", nil},
|
||||
/* 18 */ {`$$`, NewDict(map[any]any{"variables": NewDict(nil), "functions": NewDict(nil)}), nil},
|
||||
///* 19 */ {`$$global`, NewDict(map[any]any{"variables": NewDict(nil), "functions": NewDict(nil)}), nil},
|
||||
/* 19 */ {`
|
||||
ds={
|
||||
"init":func(@end){@current=0 but true},
|
||||
//"current":func(){current},
|
||||
@@ -40,9 +43,11 @@ func TestExpr(t *testing.T) {
|
||||
it++;
|
||||
it++
|
||||
`, int64(1), nil},
|
||||
/* 20 */ {`a=2; ${a}`, int64(2), nil},
|
||||
/* 21 */ {`$_=2; $_`, int64(2), nil},
|
||||
}
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 17)
|
||||
// runTestSuiteSpec(t, section, inputs, 21)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
+23
-20
@@ -11,35 +11,39 @@ import (
|
||||
func TestFractionsParser(t *testing.T) {
|
||||
section := "Fraction"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`1|2`, newFraction(1, 2), nil},
|
||||
/* 2 */ {`1|2 + 1`, newFraction(3, 2), nil},
|
||||
/* 3 */ {`1|2 - 1`, newFraction(-1, 2), nil},
|
||||
/* 4 */ {`1|2 * 1`, newFraction(1, 2), nil},
|
||||
/* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil},
|
||||
/* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil},
|
||||
/* 7 */ {`1|"5"`, nil, `denominator must be integer, got string (5)`},
|
||||
/* 8 */ {`"1"|5`, nil, `numerator must be integer, got string (1)`},
|
||||
/* 9 */ {`1|+5`, nil, `[1:3] infix operator "|" requires two non-nil operands, got 1`},
|
||||
/* 10 */ {`1|(-2)`, newFraction(-1, 2), 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},
|
||||
/* 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},
|
||||
/* 15 */ {`1|0`, nil, `division by zero`},
|
||||
/* 1 */ {`1:2`, newFraction(1, 2), nil},
|
||||
/* 2 */ {`1:2 + 1`, newFraction(3, 2), nil},
|
||||
/* 3 */ {`1:2 - 1`, newFraction(-1, 2), nil},
|
||||
/* 4 */ {`1:2 * 1`, newFraction(1, 2), nil},
|
||||
/* 5 */ {`1:2 * 2:3`, newFraction(2, 6), nil},
|
||||
/* 6 */ {`1:2 / 2:3`, newFraction(3, 4), nil},
|
||||
/* 7 */ {`1:"5"`, nil, `denominator must be integer, got string (5)`},
|
||||
/* 8 */ {`"1":5`, nil, `numerator must be integer, got string (1)`},
|
||||
/* 9 */ {`1:+5`, newFraction(1, 5), nil},
|
||||
/* 10 */ {`1:(-2)`, newFraction(-1, 2), 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},
|
||||
/* 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},
|
||||
/* 15 */ {`1:0`, nil, `[1:3] division by zero`},
|
||||
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
|
||||
/* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
|
||||
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
|
||||
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
|
||||
/* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
|
||||
/* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`},
|
||||
/* 22 */ {`string(1|2)`, "1|2", nil},
|
||||
/* 22 */ {`string(1:2)`, "1:2", nil},
|
||||
/* 23 */ {`1+1:2+0.5`, float64(2), nil},
|
||||
/* 24 */ {`1:(2-2)`, nil, `[1:3] division by zero`},
|
||||
/* 25 */ {`[0,1][1-1]:1`, newFraction(0, 1), nil},
|
||||
}
|
||||
// runTestSuiteSpec(t, section, inputs, 25)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
func TestFractionToStringSimple(t *testing.T) {
|
||||
source := newFraction(1, 2)
|
||||
want := "1|2"
|
||||
want := "1:2"
|
||||
got := source.ToString(0)
|
||||
if got != want {
|
||||
t.Errorf(`(1,2) -> result = %v [%T], want = %v [%T]`, got, got, want, want)
|
||||
@@ -55,10 +59,9 @@ func TestFractionToStringMultiline(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Check this test: the output string ends with a space
|
||||
func _TestToStringMultilineTty(t *testing.T) {
|
||||
func TestToStringMultilineTty(t *testing.T) {
|
||||
source := newFraction(-1, 2)
|
||||
want := "\x1b[4m-1\x1b[0m\n2"
|
||||
want := "\x1b[4m-1\x1b[0m\n 2"
|
||||
got := source.ToString(MultiLine | TTY)
|
||||
if got != want {
|
||||
t.Errorf(`(1,2) -> result = %#v [%T], want = %#v [%T]`, got, got, want, want)
|
||||
|
||||
@@ -35,6 +35,7 @@ func TestFuncs(t *testing.T) {
|
||||
/* 22 */ {`f=func(a,b){a*2+b}; f(b=2,a=1)`, int64(4), nil},
|
||||
/* 23 */ {`f=func(a=10,b=10){a*2+b}; f(b=1)`, int64(21), nil},
|
||||
/* 24 */ {`f=func(a,b){a*2+b}; f(a=1,2)`, nil, `f(): positional param nr 2 not allowed after named params`},
|
||||
/* 25 */ {`f=func(x,x){x}`, nil, `[1:11] parameter "x" at position 2 already defined at position 1`},
|
||||
// /* 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 ")"`)},
|
||||
@@ -67,3 +68,27 @@ func TestFunctionGetFunc(t *testing.T) {
|
||||
t.Errorf(`(func() -> result = %v [%T], want = %v [%T]`, got, got, want, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoFunction(t *testing.T) {
|
||||
section := "Funcs"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`myName()`, "Celestino Amoroso", nil},
|
||||
/* 2 */ {`myName("Peppino")`, "Peppino", nil},
|
||||
}
|
||||
|
||||
myName := func(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||
var ok bool
|
||||
if result, ok = args["name"].(string); !ok {
|
||||
err = ErrWrongParamType(name, "name", TypeString, args["name"])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx := NewSimpleStore()
|
||||
ctx.RegisterFunc("myName", NewGolangFunctor(myName), TypeString, []ExprFuncParam{
|
||||
NewFuncParamFlagDef("name", PfOptional|PfDefault, "Celestino Amoroso"),
|
||||
})
|
||||
|
||||
runCtxTestSuite(t, ctx, section, inputs)
|
||||
|
||||
}
|
||||
|
||||
+4
-2
@@ -16,11 +16,13 @@ func TestCollections(t *testing.T) {
|
||||
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
||||
/* 4 */ {`"abcdef"[:]`, "abcdef", 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, `[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] invalid range specification`},
|
||||
/* 6 */ {`"abcdef"[((1>0)?{1}:{0}):3]`, "bc", nil},
|
||||
/* 7 */ {`"abcdef"[[0,1][0]:1]`, "a", nil},
|
||||
}
|
||||
|
||||
t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
// parserTestSpec(t, section, inputs, 5)
|
||||
// runTestSuiteSpec(t, section, inputs, 5)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
+8
-8
@@ -59,7 +59,7 @@ func TestNewIterList2(t *testing.T) {
|
||||
|
||||
func TestNewIterList3(t *testing.T) {
|
||||
list := []any{"a", "b", "c", "d"}
|
||||
it := NewAnyIterator(list)
|
||||
it, _ := NewIterator(list)
|
||||
if item, err := it.Next(); err != nil {
|
||||
t.Errorf("error: %v", err)
|
||||
} else if item != "a" {
|
||||
@@ -71,7 +71,7 @@ func TestNewIterList3(t *testing.T) {
|
||||
|
||||
func TestNewIterList4(t *testing.T) {
|
||||
list := any(nil)
|
||||
it := NewAnyIterator(list)
|
||||
it, _ := NewIterator(list)
|
||||
if _, err := it.Next(); err != io.EOF {
|
||||
t.Errorf("error: %v", err)
|
||||
}
|
||||
@@ -79,7 +79,7 @@ func TestNewIterList4(t *testing.T) {
|
||||
|
||||
func TestNewIterList5(t *testing.T) {
|
||||
list := "123"
|
||||
it := NewAnyIterator(list)
|
||||
it, _ := NewIterator(list)
|
||||
if item, err := it.Next(); err != nil {
|
||||
t.Errorf("error: %v", err)
|
||||
} else if item != "123" {
|
||||
@@ -91,8 +91,8 @@ func TestNewIterList5(t *testing.T) {
|
||||
|
||||
func TestNewIterList6(t *testing.T) {
|
||||
list := newListA("a", "b", "c", "d")
|
||||
it1 := NewAnyIterator(list)
|
||||
it := NewAnyIterator(it1)
|
||||
it1, _ := NewIterator(list)
|
||||
it, _ := NewIterator(it1)
|
||||
if item, err := it.Next(); err != nil {
|
||||
t.Errorf("error: %v", err)
|
||||
} else if item != "a" {
|
||||
@@ -103,9 +103,9 @@ func TestNewIterList6(t *testing.T) {
|
||||
}
|
||||
func TestNewString(t *testing.T) {
|
||||
list := "123"
|
||||
it := NewAnyIterator(list)
|
||||
if s := it.String(); s != "$(#1)" {
|
||||
t.Errorf("expected $(#1), got %s", s)
|
||||
it, _ := NewIterator(list)
|
||||
if s := it.String(); s != "$([#1])" {
|
||||
t.Errorf("expected $([#1]), got %s", s)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-4
@@ -9,10 +9,10 @@ import "testing"
|
||||
func TestIteratorParser(t *testing.T) {
|
||||
section := "Iterator"
|
||||
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},
|
||||
/* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(3), nil},
|
||||
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ^it`, int64(0), nil},
|
||||
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; *it`, int64(0), nil},
|
||||
/* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
|
||||
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
|
||||
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
|
||||
@@ -27,8 +27,13 @@ func TestIteratorParser(t *testing.T) {
|
||||
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
|
||||
/* 17 */ {`it=$({"next":func(){5}}); it++`, int64(5), nil},
|
||||
/* 18 */ {`it=$({"next":func(){5}}); it.clean`, nil, nil},
|
||||
/* 19 */ {`it=$({1:"one",2:"two",3:"three"}); it++`, int64(1), nil},
|
||||
/* 20 */ {`it=$({1:"one",2:"two",3:"three"}, "default", "value"); it++`, "one", nil},
|
||||
/* 21 */ {`it=$({1:"one",2:"two",3:"three"}, "desc", "key"); it++`, int64(3), nil},
|
||||
/* 22 */ {`it=$({1:"one",2:"two",3:"three"}, "asc", "item"); it++`, NewList([]any{int64(1), "one"}), nil},
|
||||
/* 23 */ {`builtin "os.file"; fileReadIterator("test-file.txt") map ${_index}`, NewList([]any{int64(0), int64(1)}), nil},
|
||||
}
|
||||
|
||||
//runTestSuiteSpec(t, section, inputs, 18)
|
||||
runTestSuite(t, section, inputs)
|
||||
runTestSuiteSpec(t, section, inputs, 23)
|
||||
// runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
+18
-18
@@ -23,8 +23,8 @@ func TestListParser(t *testing.T) {
|
||||
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
||||
/* 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},
|
||||
/* 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},
|
||||
/* 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},
|
||||
/* 14 */ {`[1,2,3][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},
|
||||
@@ -33,25 +33,25 @@ func TestListParser(t *testing.T) {
|
||||
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
|
||||
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
|
||||
/* 21 */ {`"b" in ["a", "b", "c"]`, true, 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},
|
||||
/* 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},
|
||||
/* 24 */ {`["a","b","c","d"][1]`, "b", nil},
|
||||
/* 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},
|
||||
/* 27 */ {`["a", "b", "c"] << ;`, nil, `[1:18] infix operator "<<" requires two non-nil operands, got 1`},
|
||||
/* 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, `[1:6] infix operator ">>" requires two non-nil operands, got 0`},
|
||||
/* 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},
|
||||
/* 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},
|
||||
/* 35 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`},
|
||||
/* 36 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [list]`},
|
||||
/* 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},
|
||||
/* 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},
|
||||
/* 41 */ {`[0,1,2,3,4][0:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||
/* 27 */ {`["a", "b", "c"] <+ ;`, nil, `[1:18] infix operator "<+" requires two non-nil operands, got 1`},
|
||||
/* 28 */ {`2 << 3;`, int64(16), nil},
|
||||
/* 29 */ {`but +> ["a", "b", "c"]`, nil, `[1:6] infix operator "+>" requires two non-nil operands, got 0`},
|
||||
/* 30 */ {`2 >> 3;`, int64(0), nil},
|
||||
/* 31 */ {`a=[1,2]; a<+3`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||
/* 32 */ {`a=[1,2]; 5+>a`, newListA(int64(5), int64(1), int64(2)), nil},
|
||||
/* 33 */ {`L=[1,2]; L[0]=9; L`, newListA(int64(9), int64(2)), nil},
|
||||
/* 34 */ {`L=[1,2]; L[5]=9; L`, nil, `index 5 out of bounds (0, 1)`},
|
||||
/* 35 */ {`L=[1,2]; L[]=9; L`, nil, `[1:12] index/key specification expected, got [] [list]`},
|
||||
/* 36 */ {`L=[1,2]; L[nil]=9;`, nil, `[1:12] index/key is nil`},
|
||||
/* 37 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil},
|
||||
/* 38 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil},
|
||||
/* 30 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil},
|
||||
/* 40 */ {`[0,1,2,3,4][0:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
+23
-2
@@ -14,10 +14,31 @@ func TestOperator(t *testing.T) {
|
||||
/* 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},
|
||||
/* 4 */ {`a=1; a<<=1+0`, int64(2), nil},
|
||||
/* 5 */ {`a=2; a>>=1+0`, int64(1), nil},
|
||||
/* 6 */ {`1<<1`, int64(2), nil},
|
||||
/* 7 */ {`1>>1`, int64(0), nil},
|
||||
/* 8 */ {`1|2`, int64(3), nil},
|
||||
/* 9 */ {`a=1; a|=2`, int64(3), nil},
|
||||
/* 10 */ {`3&1`, int64(1), nil},
|
||||
/* 11 */ {`a=3; a&=1`, int64(1), nil},
|
||||
/* 12 */ {`~1`, int64(-2), nil},
|
||||
/* 13 */ {`0x10`, int64(16), nil},
|
||||
/* 14 */ {`0x1X`, nil, `[1:5] two adjacent operators: "1" and "X"`},
|
||||
/* 15 */ {`0o10`, int64(8), nil},
|
||||
/* 16 */ {`0b10`, int64(2), nil},
|
||||
/* 17 */ {`~true`, nil, `[1:2] prefix/postfix operator "~" do not support operand 'true' [bool]`},
|
||||
/* 18 */ {`1^2`, int64(3), nil},
|
||||
/* 19 */ {`3^2`, int64(1), nil},
|
||||
/* 20 */ {`a=1; a^=2`, int64(3), nil},
|
||||
/* 21 */ {`a=1; ++a`, int64(2), nil},
|
||||
/* 22 */ {`a=1; --a`, int64(0), nil},
|
||||
/* 23 */ {`[1,2,3] map var("_")`, []any{1, 2, 3}, nil},
|
||||
/* 24 */ {`[1,2,3] map $_`, newList([]any{1, 2, 3}), nil},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
|
||||
//runTestSuiteSpec(t, section, inputs, 3)
|
||||
runTestSuite(t, section, inputs)
|
||||
runTestSuiteSpec(t, section, inputs, 24)
|
||||
// runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
+15
-17
@@ -77,9 +77,9 @@ func TestGeneralParser(t *testing.T) {
|
||||
/* 63 */ {`"1.5" == `, nil, `[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},
|
||||
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one not nil operand`},
|
||||
/* 67 */ {"4 / 0", nil, `division by zero`},
|
||||
/* 68 */ {"4.0 / 0", nil, `division by zero`},
|
||||
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one non-nil operand`},
|
||||
/* 67 */ {"4 / 0", nil, `[1:4] division by zero`},
|
||||
/* 68 */ {"4.0 / 0", nil, `[1:6] division by zero`},
|
||||
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
|
||||
/* 70 */ {`123`, int64(123), nil},
|
||||
/* 71 */ {`1.`, float64(1.0), nil},
|
||||
@@ -94,10 +94,10 @@ func TestGeneralParser(t *testing.T) {
|
||||
/* 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},
|
||||
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
||||
/* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
|
||||
/* 84 */ {`~ 2 > 1`, false, nil},
|
||||
/* 85 */ {`~ true && true`, false, nil},
|
||||
/* 86 */ {`~ false || true`, true, nil},
|
||||
/* 83 */ {`"a" < "b" AND NOT 2 == 1`, true, nil},
|
||||
/* 84 */ {`NOT 2 > 1`, false, nil},
|
||||
/* 85 */ {`nOT true && true`, false, nil},
|
||||
/* 86 */ {`NOT false || true`, true, nil},
|
||||
/* 87 */ {`false but true`, true, nil},
|
||||
/* 88 */ {`2+3 but 5*2`, int64(10), nil},
|
||||
/* 89 */ {`x=2`, int64(2), nil},
|
||||
@@ -132,18 +132,16 @@ func TestGeneralParser(t *testing.T) {
|
||||
/* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
|
||||
/* 119 */ {`{}`, &DictType{}, nil},
|
||||
/* 120 */ {`v=10; v++; v`, int64(11), nil},
|
||||
/* 121 */ {`1+1|2+0.5`, float64(2), nil},
|
||||
/* 122 */ {`1.2()`, newFraction(6, 5), nil},
|
||||
/* 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`},
|
||||
/* 121 */ {`1.2()`, newFraction(6, 5), nil},
|
||||
/* 122 */ {`x="abc"; x ?! #x`, int64(3), nil},
|
||||
/* 123 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
|
||||
/* 124 */ {`x ?! (x+1)`, nil, nil},
|
||||
/* 125 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
|
||||
/* 126 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
|
||||
/* 127 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
|
||||
}
|
||||
|
||||
// t.Setenv("EXPR_PATH", ".")
|
||||
//runTestSuiteSpec(t, section, inputs, 130)
|
||||
// runTestSuiteSpec(t, section, inputs, 114)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
+32
-12
@@ -8,18 +8,18 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func _TestImportPlugin(t *testing.T) {
|
||||
t.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
|
||||
t.Setenv("EXPR_PLUGIN_PATH","${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
|
||||
// func TestImportPlugin(t *testing.T) {
|
||||
// t.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
|
||||
// t.Setenv("EXPR_PLUGIN_PATH","${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
|
||||
|
||||
gotCount, gotErr := importPluginFromSearchPath("json")
|
||||
if gotCount != 1 {
|
||||
t.Errorf("Import count: got=%d, want=1", gotCount)
|
||||
}
|
||||
if gotErr != nil {
|
||||
t.Errorf("importPlugin() failed: %v", gotErr)
|
||||
}
|
||||
}
|
||||
// gotCount, gotErr := importPluginFromSearchPath("json")
|
||||
// if gotCount != 1 {
|
||||
// t.Errorf("Import count: got=%d, want=1", gotCount)
|
||||
// }
|
||||
// if gotErr != nil {
|
||||
// t.Errorf("importPlugin() failed: %v", gotErr)
|
||||
// }
|
||||
// }
|
||||
|
||||
func TestPluginExists(t *testing.T) {
|
||||
name := "json"
|
||||
@@ -30,9 +30,29 @@ func TestPluginExists(t *testing.T) {
|
||||
|
||||
func TestMakePluginName(t *testing.T) {
|
||||
name := "json"
|
||||
want := "expr-" + name + "-plugin.so"
|
||||
want := "expr-" + name + "-plugin" + SHAREDLIBRARY_EXTENSION
|
||||
|
||||
if got := makePluginName(name); got != want {
|
||||
t.Errorf("makePluginName(%q) failed: Got: %q, Want: %q", name, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// func TestLoadPluginName(t *testing.T) {
|
||||
// name := "json"
|
||||
// // want := "expr-" + name + "-plugin
|
||||
// want := 1
|
||||
|
||||
// os.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
|
||||
// // os.Setenv("EXPR_PLUGIN_PATH", "${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
|
||||
// os.Setenv("EXPR_PLUGIN_PATH", "${PLUGINS}")
|
||||
// got, err := importPluginFromSearchPath(name)
|
||||
|
||||
// if err != nil {
|
||||
// t.Errorf("importPluginFromSearchPath(%q) failed: %v", name, err)
|
||||
// return
|
||||
// }
|
||||
|
||||
// if got != want {
|
||||
// t.Errorf("importPluginFromSearchPath(%q) failed: Got: %q, Want: %q", name, got, want)
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -21,9 +21,9 @@ func TestRelational(t *testing.T) {
|
||||
/* 8 */ {`true != false`, true, nil},
|
||||
/* 9 */ {`1.0 != 3.0-2`, false, nil},
|
||||
/* 10 */ {`[1,2] != [2,1]`, true, nil},
|
||||
/* 11 */ {`1|2 == 1|3`, false, nil},
|
||||
/* 12 */ {`1|2 != 1|3`, true, nil},
|
||||
/* 13 */ {`1|2 == 4|8`, true, nil},
|
||||
/* 11 */ {`1:2 == 1:3`, false, nil},
|
||||
/* 12 */ {`1:2 != 1:3`, true, nil},
|
||||
/* 13 */ {`1:2 == 4:8`, true, nil},
|
||||
/* 14 */ {`1 < 8`, true, nil},
|
||||
/* 15 */ {`1 <= 8`, true, nil},
|
||||
/* 16 */ {`"a" < "b"`, true, nil},
|
||||
@@ -32,10 +32,10 @@ func TestRelational(t *testing.T) {
|
||||
/* 19 */ {`1.0 <= 8`, true, nil},
|
||||
/* 20 */ {`1.0 <= 1.0`, true, nil},
|
||||
/* 21 */ {`1.0 == 1`, true, nil},
|
||||
/* 22 */ {`1|2 < 1|3`, false, nil},
|
||||
/* 23 */ {`1|2 <= 1|3`, false, nil},
|
||||
/* 24 */ {`1|2 > 1|3`, true, nil},
|
||||
/* 25 */ {`1|2 >= 1|3`, true, nil},
|
||||
/* 22 */ {`1:2 < 1:3`, false, nil},
|
||||
/* 23 */ {`1:2 <= 1:3`, false, nil},
|
||||
/* 24 */ {`1:2 > 1:3`, true, nil},
|
||||
/* 25 */ {`1:2 >= 1:3`, true, nil},
|
||||
/* 26 */ {`[1,2,3] > [2]`, true, nil},
|
||||
/* 27 */ {`[1,2,3] > [9]`, false, nil},
|
||||
/* 28 */ {`[1,2,3] >= [6]`, false, nil},
|
||||
|
||||
+8
-1
@@ -9,6 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func TestStringsParser(t *testing.T) {
|
||||
section := "String"
|
||||
inputs := []inputType{
|
||||
/* 1 */ {`"uno" + "due"`, `unodue`, nil},
|
||||
/* 2 */ {`"uno" + 2`, `uno2`, nil},
|
||||
@@ -16,6 +17,12 @@ func TestStringsParser(t *testing.T) {
|
||||
/* 4 */ {`"uno" * (2+1)`, `unounouno`, nil},
|
||||
/* 5 */ {`"abc"[1]`, `b`, nil},
|
||||
/* 6 */ {`#"abc"`, int64(3), nil},
|
||||
/* 7 */ {`"192.168.0.240" / "."`, newListA("192", "168", "0", "240"), nil},
|
||||
/* 8 */ {`("192.168.0.240" / ".")[1]`, "168", nil},
|
||||
/* 9 */ {`"AF3B0Dz" / 2`, newListA("AF", "3B", "0D", "z"), nil},
|
||||
/* 10 */ {`"AF3B0Dz" / 0`, nil, "[1:12] division by zero"},
|
||||
}
|
||||
runTestSuite(t, "String", inputs)
|
||||
|
||||
// runTestSuiteSpec(t, section, inputs, 8)
|
||||
runTestSuite(t, section, inputs)
|
||||
}
|
||||
|
||||
@@ -13,22 +13,29 @@ type termPriority uint32
|
||||
const (
|
||||
priNone termPriority = iota
|
||||
priRange
|
||||
priIterOp // map, filter, digest, etc
|
||||
priBut
|
||||
priAssign
|
||||
priInsert
|
||||
priOr
|
||||
priAnd
|
||||
priNot
|
||||
priRelational
|
||||
priBitwiseOr
|
||||
priBitwiseAnd
|
||||
priBitwiseNot
|
||||
priSum
|
||||
priProduct
|
||||
priFraction
|
||||
priSelector
|
||||
priBinShift
|
||||
priSign
|
||||
priFact
|
||||
priIterValue
|
||||
priDefault
|
||||
priIncDec
|
||||
priDot
|
||||
priDereference
|
||||
priValue
|
||||
)
|
||||
|
||||
@@ -62,8 +69,8 @@ func (s *term) Clone() (d *term) {
|
||||
}
|
||||
}
|
||||
d = &term{
|
||||
tk: *s.tk.Clone(),
|
||||
parent: s.parent,
|
||||
tk: *s.tk.Clone(),
|
||||
parent: s.parent,
|
||||
children: children,
|
||||
position: s.position,
|
||||
priority: s.priority,
|
||||
@@ -198,6 +205,10 @@ func (term *term) errIncompatibleType(value any) error {
|
||||
term.source(), value, TypeName(value))
|
||||
}
|
||||
|
||||
func (term *term) errDivisionByZero() error {
|
||||
return term.tk.Errorf("division by zero")
|
||||
}
|
||||
|
||||
func (term *term) Errorf(template string, args ...any) (err error) {
|
||||
err = term.tk.Errorf(template, args...)
|
||||
return
|
||||
@@ -211,11 +222,11 @@ func (term *term) checkOperands() (err error) {
|
||||
}
|
||||
case posPrefix:
|
||||
if term.children == nil || len(term.children) != 1 || term.children[0] == nil {
|
||||
err = term.tk.Errorf("prefix operator %q requires one not nil operand", term.tk.String())
|
||||
err = term.tk.Errorf("prefix operator %q requires one non-nil operand", term.tk.String())
|
||||
}
|
||||
case posPostfix:
|
||||
if term.children == nil || len(term.children) != 1 || term.anyChildrenNil() {
|
||||
err = term.tk.Errorf("postfix operator %q requires one not nil operand", term.tk.String())
|
||||
err = term.tk.Errorf("postfix operator %q requires one non-nil operand", term.tk.String())
|
||||
}
|
||||
case posMultifix:
|
||||
if term.children == nil || len(term.children) < 3 || term.anyChildrenNil() {
|
||||
|
||||
@@ -79,6 +79,10 @@ func (tk *Token) IsSymbol(sym Symbol) bool {
|
||||
return tk.Sym == sym
|
||||
}
|
||||
|
||||
func (tk *Token) SetSymbol(sym Symbol) {
|
||||
tk.Sym = sym
|
||||
}
|
||||
|
||||
func (tk *Token) Errorf(template string, args ...any) (err error) {
|
||||
err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", tk.row, tk.col)+template, args...)
|
||||
return
|
||||
@@ -93,6 +97,15 @@ func (tk *Token) Error() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (tk *Token) ErrorText() (err string) {
|
||||
if tk.Sym == SymError {
|
||||
if msg, ok := tk.Value.(error); ok {
|
||||
err = msg.Error()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tk *Token) Errors(msg string) (err error) {
|
||||
err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
|
||||
return
|
||||
@@ -104,6 +117,10 @@ func (tk *Token) ErrorExpectedGot(symbol string) (err error) {
|
||||
}
|
||||
|
||||
func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
|
||||
err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, got)
|
||||
return tk.ErrorExpectedGotStringWithPrefix("expected", symbol, got)
|
||||
}
|
||||
|
||||
func (tk *Token) ErrorExpectedGotStringWithPrefix(prefix, symbol, got string) (err error) {
|
||||
err = fmt.Errorf("[%d:%d] %s %s, got `%s`", tk.row, tk.col, prefix, symbol, got)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -218,6 +218,15 @@ func ToGoInt(value any, description string) (i int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func ToGoString(value any, description string) (s string, err error) {
|
||||
if s, ok := value.(string); ok {
|
||||
return s, nil
|
||||
} else {
|
||||
err = fmt.Errorf("%s expected string, got %s (%v)", description, TypeName(value), value)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ForAll[T, V any](ts []T, fn func(T) V) []V {
|
||||
result := make([]V, len(ts))
|
||||
for i, t := range ts {
|
||||
|
||||
Reference in New Issue
Block a user