Compare commits
45 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 |
+1
-1
@@ -144,4 +144,4 @@ Variables and functions can be added to a context both programmatically and ad a
|
|||||||
|
|
||||||
== Expressions syntax
|
== 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.
|
||||||
|
|||||||
@@ -72,6 +72,10 @@ func boolFunc(ctx ExprContext, name string, args map[string]any) (result any, er
|
|||||||
result = v
|
result = v
|
||||||
case string:
|
case string:
|
||||||
result = len(v) > 0
|
result = len(v) > 0
|
||||||
|
case *ListType:
|
||||||
|
result = len(*v) > 0
|
||||||
|
case *DictType:
|
||||||
|
result = len(*v) > 0
|
||||||
default:
|
default:
|
||||||
err = ErrCantConvert(name, v, "bool")
|
err = ErrCantConvert(name, v, "bool")
|
||||||
}
|
}
|
||||||
@@ -95,6 +99,8 @@ func intFunc(ctx ExprContext, name string, args map[string]any) (result any, err
|
|||||||
if i, err = strconv.Atoi(v); err == nil {
|
if i, err = strconv.Atoi(v); err == nil {
|
||||||
result = int64(i)
|
result = int64(i)
|
||||||
}
|
}
|
||||||
|
case *FractionType:
|
||||||
|
result = int64(v.num / v.den)
|
||||||
default:
|
default:
|
||||||
err = ErrCantConvert(name, v, "int")
|
err = ErrCantConvert(name, v, "int")
|
||||||
}
|
}
|
||||||
@@ -211,6 +217,54 @@ func evalFunc(ctx ExprContext, name string, args map[string]any) (result any, er
|
|||||||
return
|
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
|
//// import
|
||||||
|
|
||||||
func ImportBuiltinsFuncs(ctx ExprContext) {
|
func ImportBuiltinsFuncs(ctx ExprContext) {
|
||||||
@@ -240,6 +294,21 @@ func ImportBuiltinsFuncs(ctx ExprContext) {
|
|||||||
ctx.RegisterFunc("eval", NewGolangFunctor(evalFunc), TypeAny, []ExprFuncParam{
|
ctx.RegisterFunc("eval", NewGolangFunctor(evalFunc), TypeAny, []ExprFuncParam{
|
||||||
NewFuncParam(ParamSource),
|
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() {
|
func init() {
|
||||||
|
|||||||
+25
-17
@@ -23,9 +23,11 @@ func parseRunArgs(localCtx ExprContext, args map[string]any) (it Iterator, op Fu
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if op, ok = args[iterParamOperator].(Functor); !ok && args[iterParamOperator] != nil {
|
if args[iterParamOperator] != nil {
|
||||||
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], TypeName(args[iterParamOperator]))
|
if op, ok = args[iterParamOperator].(Functor); !ok || op == nil {
|
||||||
return
|
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], TypeName(args[iterParamOperator]))
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var vars *DictType
|
var vars *DictType
|
||||||
@@ -51,7 +53,7 @@ func runFunc(ctx ExprContext, name string, args map[string]any) (result any, err
|
|||||||
var ok bool
|
var ok bool
|
||||||
var op Functor
|
var op Functor
|
||||||
var v any
|
var v any
|
||||||
var usingDefaultOp = false
|
// var usingDefaultOp = false
|
||||||
var params map[string]any
|
var params map[string]any
|
||||||
var item 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 {
|
if it, op, err = parseRunArgs(localCtx, args); err != nil {
|
||||||
return
|
return
|
||||||
} else if op == nil {
|
// } else if op == nil {
|
||||||
op = NewGolangFunctor(printLnFunc)
|
// op = NewGolangFunctor(printLnFunc)
|
||||||
usingDefaultOp = true
|
// usingDefaultOp = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||||
if usingDefaultOp {
|
// if usingDefaultOp {
|
||||||
params = map[string]any{ParamItem: []any{item}}
|
// params = map[string]any{ParamItem: []any{item}}
|
||||||
} else {
|
// } else {
|
||||||
params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
|
// params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
|
||||||
}
|
// }
|
||||||
|
|
||||||
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
|
if op != nil {
|
||||||
break
|
params = map[string]any{ParamIndex: it.Index(), ParamItem: item}
|
||||||
} else {
|
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
|
||||||
var success bool
|
|
||||||
if success, ok = ToBool(v); !success || !ok {
|
|
||||||
break
|
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
|
err = nil
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
if op == nil {
|
||||||
|
ctx.UnsafeSetVar(iterVarStatus, it.Count())
|
||||||
|
}
|
||||||
result, _ = localCtx.GetVar(iterVarStatus)
|
result, _ = localCtx.GetVar(iterVarStatus)
|
||||||
}
|
}
|
||||||
return
|
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{
|
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
|
||||||
NewFuncParam(ParamHandle),
|
NewFuncParam(ParamHandle),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ctx.RegisterFunc("fileReadIterator", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{
|
||||||
|
NewFuncParam(paramHandleOrPath),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -200,6 +200,24 @@ func splitStrFunc(ctx ExprContext, name string, args map[string]any) (result any
|
|||||||
return
|
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
|
// --- End of function definitions
|
||||||
|
|
||||||
// Import above functions in the context
|
// Import above functions in the context
|
||||||
@@ -236,6 +254,14 @@ func ImportStringFuncs(ctx ExprContext) {
|
|||||||
NewFuncParam(ParamSuffix),
|
NewFuncParam(ParamSuffix),
|
||||||
NewFuncParamFlag(strParamOther, PfRepeat),
|
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.
|
// Register the import function in the import-register.
|
||||||
|
|||||||
@@ -80,6 +80,10 @@ func ErrUnknownParam(funcName, paramName string) error {
|
|||||||
return fmt.Errorf("%s(): unknown parameter %q", funcName, paramName)
|
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
|
// --- Operator errors
|
||||||
|
|
||||||
func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error {
|
func ErrLeftOperandMustBeVariable(leftTerm, opTerm *term) error {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const (
|
|||||||
TypeFileHandle = "file-handle"
|
TypeFileHandle = "file-handle"
|
||||||
TypeInt = "integer"
|
TypeInt = "integer"
|
||||||
TypeItem = "item"
|
TypeItem = "item"
|
||||||
|
TypeIterator = "iterator"
|
||||||
TypeNumber = "number"
|
TypeNumber = "number"
|
||||||
TypePair = "pair"
|
TypePair = "pair"
|
||||||
TypeString = "string"
|
TypeString = "string"
|
||||||
|
|||||||
@@ -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) {
|
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
|
var d DictType
|
||||||
if dictAny != nil {
|
if dictAny != nil {
|
||||||
d = make(DictType, len(dictAny))
|
d = make(DictType, len(dictAny))
|
||||||
|
|||||||
+784
-51
File diff suppressed because it is too large
Load Diff
+1241
-92
File diff suppressed because it is too large
Load Diff
@@ -76,6 +76,12 @@ func isFile(filePath string) bool {
|
|||||||
|
|
||||||
func searchAmongPath(filename string, dirList []string) (filePath string) {
|
func searchAmongPath(filename string, dirList []string) (filePath string) {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
suffix := SHAREDLIBRARY_EXTENSION
|
||||||
|
if strings.HasSuffix(filename, ".debug") {
|
||||||
|
suffix += ".debug"
|
||||||
|
}
|
||||||
|
|
||||||
for _, dir := range dirList {
|
for _, dir := range dirList {
|
||||||
if dir, err = ExpandPath(dir); err != nil {
|
if dir, err = ExpandPath(dir); err != nil {
|
||||||
continue
|
continue
|
||||||
@@ -84,6 +90,12 @@ func searchAmongPath(filename string, dirList []string) (filePath string) {
|
|||||||
filePath = fullPath
|
filePath = fullPath
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subdir := strings.TrimSuffix(filename, suffix)
|
||||||
|
if fullPath := path.Join(dir, subdir, filename); isFile(fullPath) {
|
||||||
|
filePath = fullPath
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
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
|
||||||
|
}
|
||||||
+5
-2
@@ -21,22 +21,25 @@ const (
|
|||||||
CountName = "count"
|
CountName = "count"
|
||||||
FilterName = "filter"
|
FilterName = "filter"
|
||||||
MapName = "map"
|
MapName = "map"
|
||||||
|
KeyName = "key"
|
||||||
|
ValueName = "value"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Iterator interface {
|
type Iterator interface {
|
||||||
Typer
|
Typer
|
||||||
|
fmt.Stringer
|
||||||
Next() (item any, err error) // must return io.EOF after the last item
|
Next() (item any, err error) // must return io.EOF after the last item
|
||||||
Current() (item any, err error)
|
Current() (item any, err error)
|
||||||
Index() int
|
Index() int
|
||||||
Count() int
|
Count() int
|
||||||
|
HasOperation(name string) bool
|
||||||
|
CallOperation(name string, args map[string]any) (value any, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExtIterator interface {
|
type ExtIterator interface {
|
||||||
Iterator
|
Iterator
|
||||||
Reset() error
|
Reset() error
|
||||||
Clean() error
|
Clean() error
|
||||||
HasOperation(name string) bool
|
|
||||||
CallOperation(name string, args map[string]any) (value any, err error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func errNoOperation(name string) error {
|
func errNoOperation(name string) error {
|
||||||
|
|||||||
@@ -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
|
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 {
|
func (it *ListIterator) String() string {
|
||||||
var l = 0
|
var l = 0
|
||||||
if it.a != nil {
|
if it.a != nil {
|
||||||
l = len(*it.a)
|
l = len(*it.a)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("$(#%d)", l)
|
return fmt.Sprintf("$([#%d])", l)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *ListIterator) TypeName() string {
|
func (it *ListIterator) TypeName() string {
|
||||||
@@ -149,12 +134,12 @@ func (it *ListIterator) Count() int {
|
|||||||
return it.count
|
return it.count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *ListIterator) Reset() (error) {
|
func (it *ListIterator) Reset() error {
|
||||||
it.index = it.start - it.step
|
it.index = it.start - it.step
|
||||||
it.count = 0
|
it.count = 0
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *ListIterator) Clean() (error) {
|
func (it *ListIterator) Clean() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-7
@@ -26,9 +26,9 @@ func newList(listAny []any) (list *ListType) {
|
|||||||
func NewList(listAny []any) (list *ListType) {
|
func NewList(listAny []any) (list *ListType) {
|
||||||
if listAny != nil {
|
if listAny != nil {
|
||||||
ls := make(ListType, len(listAny))
|
ls := make(ListType, len(listAny))
|
||||||
// for i, item := range listAny {
|
// for i, item := range listAny {
|
||||||
// ls[i] = item
|
// ls[i] = item
|
||||||
// }
|
// }
|
||||||
copy(ls, listAny)
|
copy(ls, listAny)
|
||||||
list = &ls
|
list = &ls
|
||||||
}
|
}
|
||||||
@@ -53,14 +53,14 @@ func ListFromStrings(stringList []string) (list *ListType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ls *ListType) ToString(opt FmtOpt) (s string) {
|
func (ls *ListType) ToString(opt FmtOpt) (s string) {
|
||||||
indent := GetFormatIndent(opt)
|
indent := GetFormatIndent(opt)
|
||||||
flags := GetFormatFlags(opt)
|
flags := GetFormatFlags(opt)
|
||||||
|
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
sb.WriteByte('[')
|
sb.WriteByte('[')
|
||||||
if len(*ls) > 0 {
|
if len(*ls) > 0 {
|
||||||
innerOpt := MakeFormatOptions(flags, indent+1)
|
innerOpt := MakeFormatOptions(flags, indent+1)
|
||||||
nest := strings.Repeat(" ", indent+1)
|
nest := strings.Repeat(" ", indent+1)
|
||||||
|
|
||||||
if flags&MultiLine != 0 {
|
if flags&MultiLine != 0 {
|
||||||
sb.WriteByte('\n')
|
sb.WriteByte('\n')
|
||||||
@@ -129,6 +129,20 @@ func (ls *ListType) contains(t *ListType) (answer bool) {
|
|||||||
return
|
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) {
|
func (list *ListType) indexDeepSameCmp(target any) (index int) {
|
||||||
var eq bool
|
var eq bool
|
||||||
var err error
|
var err error
|
||||||
@@ -193,3 +207,6 @@ func (list *ListType) setItem(index int64, value any) (err error) {
|
|||||||
return
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ds, err = getDataSourceDict(opTerm, firstChildValue); err != nil {
|
if ds, err = getDataSourceDict(opTerm, firstChildValue); err != nil && ds == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
err = nil
|
||||||
|
|
||||||
if ds != nil {
|
if ds != nil {
|
||||||
var dc *dataCursor
|
if len(ds) > 0 {
|
||||||
dcCtx := ctx.Clone()
|
var dc *dataCursor
|
||||||
if initFunc, exists := ds[InitName]; exists && initFunc != nil {
|
dcCtx := ctx.Clone()
|
||||||
var args []any
|
if initFunc, exists := ds[InitName]; exists && initFunc != nil {
|
||||||
var resource any
|
var args []any
|
||||||
if len(opTerm.children) > 1 {
|
var resource any
|
||||||
if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
exportObjects(dcCtx, initCtx)
|
||||||
|
dc = NewDataCursor(dcCtx, ds, resource)
|
||||||
} else {
|
} else {
|
||||||
args = []any{}
|
dc = NewDataCursor(dcCtx, ds, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
actualParams := bindActualParams(initFunc, args)
|
v = dc
|
||||||
|
|
||||||
initCtx := ctx.Clone()
|
|
||||||
if resource, err = initFunc.InvokeNamed(initCtx, InitName, actualParams); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
exportObjects(dcCtx, initCtx)
|
|
||||||
dc = NewDataCursor(dcCtx, ds, resource)
|
|
||||||
} else {
|
} 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 {
|
} else if list, ok := firstChildValue.(*ListType); ok {
|
||||||
var args []any
|
var args []any
|
||||||
if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
|
if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
|
||||||
|
|||||||
@@ -183,6 +183,16 @@ func evalOpAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
v, err = divValues(opTerm, leftValue, rightValue)
|
v, err = divValues(opTerm, leftValue, rightValue)
|
||||||
case SymPercEqual:
|
case SymPercEqual:
|
||||||
v, err = remainderValues(opTerm, leftValue, rightValue)
|
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:
|
default:
|
||||||
err = opTerm.Errorf("unsupported assign operator %q", opTerm.source())
|
err = opTerm.Errorf("unsupported assign operator %q", opTerm.source())
|
||||||
}
|
}
|
||||||
@@ -201,4 +211,10 @@ func init() {
|
|||||||
registerTermConstructor(SymMinusEqual, newOpAssignTerm)
|
registerTermConstructor(SymMinusEqual, newOpAssignTerm)
|
||||||
registerTermConstructor(SymStarEqual, newOpAssignTerm)
|
registerTermConstructor(SymStarEqual, newOpAssignTerm)
|
||||||
registerTermConstructor(SymSlashEqual, newOpAssignTerm)
|
registerTermConstructor(SymSlashEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymPercEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymDoubleLessEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymDoubleGreaterEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymAmpersandEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymVertBarEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymCaretEqual, newOpAssignTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,104 +0,0 @@
|
|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-binary.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
//-------- NOT term
|
|
||||||
|
|
||||||
func newBinNotTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 1),
|
|
||||||
position: posPrefix,
|
|
||||||
priority: priBinary,
|
|
||||||
evalFunc: evalBinaryNot,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalBinaryNot(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
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------- Binary AND term
|
|
||||||
|
|
||||||
func newBinAndTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priBinary,
|
|
||||||
evalFunc: evalBinaryAnd,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalBinaryAnd(ctx ExprContext, self *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
var leftInt, rightInt int64
|
|
||||||
var lok, rok bool
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
leftInt, lok = leftValue.(int64)
|
|
||||||
rightInt, rok = rightValue.(int64)
|
|
||||||
|
|
||||||
if lok && rok {
|
|
||||||
v = leftInt & rightInt
|
|
||||||
} else {
|
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------- Binary OR term
|
|
||||||
|
|
||||||
func newBinOrTerm(tk *Token) (inst *term) {
|
|
||||||
return &term{
|
|
||||||
tk: *tk,
|
|
||||||
children: make([]*term, 0, 2),
|
|
||||||
position: posInfix,
|
|
||||||
priority: priBinary,
|
|
||||||
evalFunc: evalBinaryOr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalBinaryOr(ctx ExprContext, self *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
var leftInt, rightInt int64
|
|
||||||
var lok, rok bool
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
leftInt, lok = leftValue.(int64)
|
|
||||||
rightInt, rok = rightValue.(int64)
|
|
||||||
|
|
||||||
if lok && rok {
|
|
||||||
v = leftInt | rightInt
|
|
||||||
} else {
|
|
||||||
err = self.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
registerTermConstructor(SymTilde, newBinNotTerm)
|
|
||||||
registerTermConstructor(SymAmpersand, newBinAndTerm)
|
|
||||||
registerTermConstructor(SymVertBar, newBinOrTerm)
|
|
||||||
}
|
|
||||||
@@ -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)
|
count, err = ImportInContextByGlobPattern(module)
|
||||||
} else {
|
} else {
|
||||||
var moduleSpec any
|
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() {
|
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
||||||
if module, ok := moduleSpec.(string); ok {
|
if module, ok := moduleSpec.(string); ok {
|
||||||
if ImportInContext(module) {
|
if ImportInContext(module) {
|
||||||
|
|||||||
@@ -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
|
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if den == 0 {
|
if den == 0 {
|
||||||
err = errors.New("division by zero")
|
err = opTerm.errDivisionByZero()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-16
@@ -4,15 +4,15 @@
|
|||||||
// operator-insert.go
|
// operator-insert.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
//-------- insert term
|
//-------- prepend term
|
||||||
|
|
||||||
func newInsertTerm(tk *Token) (inst *term) {
|
func newPrependTerm(tk *Token) (inst *term) {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priAssign,
|
priority: priInsert,
|
||||||
evalFunc: evalInsert,
|
evalFunc: evalPrepend,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,12 +21,12 @@ func newAppendTerm(tk *Token) (inst *term) {
|
|||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 2),
|
children: make([]*term, 0, 2),
|
||||||
position: posInfix,
|
position: posInfix,
|
||||||
priority: priAssign,
|
priority: priInsert,
|
||||||
evalFunc: evalAppend,
|
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
|
var leftValue, rightValue any
|
||||||
|
|
||||||
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
@@ -40,10 +40,6 @@ func evalInsert(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
if opTerm.children[1].symbol() == SymVariable {
|
if opTerm.children[1].symbol() == SymVariable {
|
||||||
ctx.UnsafeSetVar(opTerm.children[1].source(), v)
|
ctx.UnsafeSetVar(opTerm.children[1].source(), v)
|
||||||
}
|
}
|
||||||
} else if IsInteger(leftValue) && IsInteger(rightValue) {
|
|
||||||
leftInt := leftValue.(int64)
|
|
||||||
rightInt := rightValue.(int64)
|
|
||||||
v = leftInt >> rightInt
|
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
@@ -64,10 +60,6 @@ func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
if opTerm.children[0].symbol() == SymVariable {
|
if opTerm.children[0].symbol() == SymVariable {
|
||||||
ctx.UnsafeSetVar(opTerm.children[0].source(), v)
|
ctx.UnsafeSetVar(opTerm.children[0].source(), v)
|
||||||
}
|
}
|
||||||
} else if IsInteger(leftValue) && IsInteger(rightValue) {
|
|
||||||
leftInt := leftValue.(int64)
|
|
||||||
rightInt := rightValue.(int64)
|
|
||||||
v = leftInt << rightInt
|
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
@@ -94,6 +86,6 @@ func evalAppend(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymInsert, newInsertTerm)
|
registerTermConstructor(SymPlusGreater, newPrependTerm)
|
||||||
registerTermConstructor(SymAppend, newAppendTerm)
|
registerTermConstructor(SymLessPlus, newAppendTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ func newIterValueTerm(tk *Token) (inst *term) {
|
|||||||
tk: *tk,
|
tk: *tk,
|
||||||
children: make([]*term, 0, 1),
|
children: make([]*term, 0, 1),
|
||||||
position: posPrefix,
|
position: posPrefix,
|
||||||
priority: priIterValue,
|
priority: priDereference,
|
||||||
evalFunc: evalIterValue,
|
evalFunc: evalIterValue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,6 @@ func evalIterValue(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
||||||
registerTermConstructor(SymCaret, 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).
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
|
|
||||||
// operator-post-inc.go
|
// operator-post-inc-dec.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
// -------- post increment term
|
// -------- post increment term
|
||||||
@@ -24,7 +24,27 @@ func evalPostInc(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if it, ok := childValue.(Iterator); ok {
|
if it, ok := childValue.(Iterator); ok {
|
||||||
|
var namePrefix string
|
||||||
v, err = it.Next()
|
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 {
|
} else if IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
|
||||||
v = childValue
|
v = childValue
|
||||||
i, _ := childValue.(int64)
|
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)
|
||||||
|
}
|
||||||
+29
-5
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -69,7 +68,7 @@ func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
|||||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||||
d := numAsFloat(rightValue)
|
d := numAsFloat(rightValue)
|
||||||
if d == 0.0 {
|
if d == 0.0 {
|
||||||
err = errors.New("division by zero")
|
err = opTerm.errDivisionByZero()
|
||||||
} else {
|
} else {
|
||||||
v = numAsFloat(leftValue) / d
|
v = numAsFloat(leftValue) / d
|
||||||
}
|
}
|
||||||
@@ -78,11 +77,36 @@ func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
|||||||
} else {
|
} else {
|
||||||
leftInt, _ := leftValue.(int64)
|
leftInt, _ := leftValue.(int64)
|
||||||
if rightInt, _ := rightValue.(int64); rightInt == 0 {
|
if rightInt, _ := rightValue.(int64); rightInt == 0 {
|
||||||
err = errors.New("division by zero")
|
err = opTerm.errDivisionByZero()
|
||||||
} else {
|
} else {
|
||||||
v = leftInt / rightInt
|
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 {
|
} else {
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
@@ -121,7 +145,7 @@ func evalDivideAsFloat(ctx ExprContext, floatDivTerm *term) (v any, err error) {
|
|||||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||||
d := numAsFloat(rightValue)
|
d := numAsFloat(rightValue)
|
||||||
if d == 0.0 {
|
if d == 0.0 {
|
||||||
err = errors.New("division by zero")
|
err = floatDivTerm.errDivisionByZero()
|
||||||
} else {
|
} else {
|
||||||
v = numAsFloat(leftValue) / d
|
v = numAsFloat(leftValue) / d
|
||||||
}
|
}
|
||||||
@@ -146,7 +170,7 @@ func remainderValues(opTerm *term, leftValue, rightValue any) (v any, err error)
|
|||||||
if IsInteger(leftValue) && IsInteger(rightValue) {
|
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||||
rightInt, _ := rightValue.(int64)
|
rightInt, _ := rightValue.(int64)
|
||||||
if rightInt == 0 {
|
if rightInt == 0 {
|
||||||
err = errors.New("division by zero")
|
err = opTerm.errDivisionByZero()
|
||||||
} else {
|
} else {
|
||||||
leftInt, _ := leftValue.(int64)
|
leftInt, _ := leftValue.(int64)
|
||||||
v = leftInt % rightInt
|
v = leftInt % rightInt
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -409,6 +409,23 @@ func listSubTree(tree *ast, listTerm *term, allowIndeces bool) (root *term, err
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
var selectorTerm *term = nil
|
var selectorTerm *term = nil
|
||||||
var currentTerm *term = nil
|
var currentTerm *term = nil
|
||||||
@@ -437,14 +454,16 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
|
|||||||
|
|
||||||
//fmt.Println("Token:", tk)
|
//fmt.Println("Token:", tk)
|
||||||
if firstToken {
|
if firstToken {
|
||||||
if tk.Sym == SymMinus {
|
changePrefix(tk)
|
||||||
tk.Sym = SymChangeSign
|
// if tk.Sym == SymMinus {
|
||||||
} else if tk.Sym == SymPlus {
|
// tk.Sym = SymChangeSign
|
||||||
tk.Sym = SymUnchangeSign
|
// } else if tk.Sym == SymPlus {
|
||||||
} else if tk.IsSymbol(SymExclamation) {
|
// tk.Sym = SymUnchangeSign
|
||||||
err = tk.Errorf("postfix opertor %q requires an operand on its left", tk)
|
// } else if tk.IsSymbol(SymStar) {
|
||||||
break
|
// tk.SetSymbol(SymDereference)
|
||||||
}
|
// } else if tk.IsSymbol(SymExclamation) {
|
||||||
|
// tk.SetSymbol(SymNot)
|
||||||
|
// }
|
||||||
firstToken = false
|
firstToken = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,9 +471,12 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
|
|||||||
case SymOpenRound:
|
case SymOpenRound:
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = parser.parseGeneral(scanner, ctx, SymClosedRound); err == nil {
|
if subTree, err = parser.parseGeneral(scanner, ctx, SymClosedRound); err == nil {
|
||||||
subTree.root.priority = priValue
|
exprTerm := newExprTerm(subTree.root)
|
||||||
err = tree.addTerm(newExprTerm(subTree.root))
|
err = tree.addTerm(exprTerm)
|
||||||
currentTerm = subTree.root
|
currentTerm = exprTerm
|
||||||
|
// subTree.root.priority = priValue
|
||||||
|
// err = tree.addTerm(newExprTerm(subTree.root))
|
||||||
|
// currentTerm = subTree.root
|
||||||
}
|
}
|
||||||
case SymFuncCall:
|
case SymFuncCall:
|
||||||
var funcCallTerm *term
|
var funcCallTerm *term
|
||||||
@@ -478,7 +500,7 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
|
|||||||
currentTerm = mapTerm
|
currentTerm = mapTerm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual:
|
case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual, SymAmpersandEqual, SymVertBarEqual, SymDoubleLessEqual, SymDoubleGreaterEqual, SymCaretEqual:
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
firstToken = true
|
firstToken = true
|
||||||
case SymFuncDef:
|
case SymFuncDef:
|
||||||
@@ -515,14 +537,11 @@ func (parser *parser) parseGeneral(scanner *scanner, ctx parserContext, termSymb
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// if hasFlag(ctx, allowIndex) {
|
|
||||||
// tk.Sym = SymRange
|
|
||||||
// }
|
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
}
|
if tk.IsOneOfA(SymColon, SymRange) {
|
||||||
if tk.IsOneOfA(SymColon, SymRange) {
|
// Colon outside a selector term acts like a separator
|
||||||
// Colon outside a selector term acts like a separator
|
firstToken = true
|
||||||
firstToken = true
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
|
|||||||
+7
-5
@@ -31,11 +31,11 @@ func pluginExists(name string) (exists bool) {
|
|||||||
func makePluginName(name string) (decorated string) {
|
func makePluginName(name string) (decorated string) {
|
||||||
var template string
|
var template string
|
||||||
if execName, err := os.Executable(); err != nil || !strings.Contains(execName, "debug") {
|
if execName, err := os.Executable(); err != nil || !strings.Contains(execName, "debug") {
|
||||||
template = "expr-%s-plugin.so"
|
template = "expr-%s-plugin%s"
|
||||||
} else {
|
} 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,10 +93,12 @@ func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err erro
|
|||||||
|
|
||||||
func importPluginFromSearchPath(name any) (count int, err error) {
|
func importPluginFromSearchPath(name any) (count int, err error) {
|
||||||
var moduleSpec any
|
var moduleSpec any
|
||||||
|
var it Iterator
|
||||||
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH)
|
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH)
|
||||||
count = 0
|
count = 0
|
||||||
it := NewAnyIterator(name)
|
if it, err = NewIterator(name); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
|
||||||
if module, ok := moduleSpec.(string); ok {
|
if module, ok := moduleSpec.(string); ok {
|
||||||
if err = importPlugin(dirList, module); err != nil {
|
if err = importPlugin(dirList, module); err != nil {
|
||||||
|
|||||||
+58
-9
@@ -124,6 +124,8 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
|||||||
tk = scanner.moveOn(SymDoublePlus, ch, next)
|
tk = scanner.moveOn(SymDoublePlus, ch, next)
|
||||||
} else if next == '=' {
|
} else if next == '=' {
|
||||||
tk = scanner.moveOn(SymPlusEqual, ch, next)
|
tk = scanner.moveOn(SymPlusEqual, ch, next)
|
||||||
|
} else if next == '>' {
|
||||||
|
tk = scanner.moveOn(SymPlusGreater, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = scanner.makeToken(SymPlus, ch)
|
tk = scanner.makeToken(SymPlus, ch)
|
||||||
}
|
}
|
||||||
@@ -167,13 +169,19 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
|||||||
case '|':
|
case '|':
|
||||||
if next, _ := scanner.peek(); next == '|' {
|
if next, _ := scanner.peek(); next == '|' {
|
||||||
tk = scanner.moveOn(SymDoubleVertBar, ch, next)
|
tk = scanner.moveOn(SymDoubleVertBar, ch, next)
|
||||||
|
} else if next, _ = scanner.peek(); next == '=' {
|
||||||
|
tk = scanner.moveOn(SymVertBarEqual, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = scanner.makeToken(SymVertBar, ch)
|
tk = scanner.makeToken(SymVertBar, ch)
|
||||||
}
|
}
|
||||||
case ',':
|
case ',':
|
||||||
tk = scanner.makeToken(SymComma, ch)
|
tk = scanner.makeToken(SymComma, ch)
|
||||||
case '^':
|
case '^':
|
||||||
tk = scanner.makeToken(SymCaret, ch)
|
if next, _ := scanner.peek(); next == '=' {
|
||||||
|
tk = scanner.moveOn(SymCaretEqual, ch, next)
|
||||||
|
} else {
|
||||||
|
tk = scanner.makeToken(SymCaret, ch)
|
||||||
|
}
|
||||||
case ':':
|
case ':':
|
||||||
if next, _ := scanner.peek(); next == ':' {
|
if next, _ := scanner.peek(); next == ':' {
|
||||||
tk = scanner.moveOn(SymDoubleColon, ch, next)
|
tk = scanner.moveOn(SymDoubleColon, ch, next)
|
||||||
@@ -202,14 +210,14 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
|||||||
tk = scanner.makeToken(SymQuote, ch)
|
tk = scanner.makeToken(SymQuote, ch)
|
||||||
escape = false
|
escape = false
|
||||||
} else {
|
} else {
|
||||||
tk = scanner.fetchString(ch)
|
tk = scanner.fetchString(ch, true)
|
||||||
}
|
}
|
||||||
case '"':
|
case '"':
|
||||||
if escape {
|
if escape {
|
||||||
tk = scanner.makeToken(SymDoubleQuote, ch)
|
tk = scanner.makeToken(SymDoubleQuote, ch)
|
||||||
escape = false
|
escape = false
|
||||||
} else {
|
} else {
|
||||||
tk = scanner.fetchString(ch)
|
tk = scanner.fetchString(ch, true)
|
||||||
}
|
}
|
||||||
case '`':
|
case '`':
|
||||||
tk = scanner.makeToken(SymBackTick, ch)
|
tk = scanner.makeToken(SymBackTick, ch)
|
||||||
@@ -232,11 +240,17 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
|||||||
case '&':
|
case '&':
|
||||||
if next, _ := scanner.peek(); next == '&' {
|
if next, _ := scanner.peek(); next == '&' {
|
||||||
tk = scanner.moveOn(SymDoubleAmpersand, ch, next)
|
tk = scanner.moveOn(SymDoubleAmpersand, ch, next)
|
||||||
|
} else if next, _ = scanner.peek(); next == '=' {
|
||||||
|
tk = scanner.moveOn(SymAmpersandEqual, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = scanner.makeToken(SymAmpersand, ch)
|
tk = scanner.makeToken(SymAmpersand, ch)
|
||||||
}
|
}
|
||||||
case '%':
|
case '%':
|
||||||
tk = scanner.makeToken(SymPercent, ch)
|
if next, _ := scanner.peek(); next == '=' {
|
||||||
|
tk = scanner.moveOn(SymPercEqual, ch, next)
|
||||||
|
} else {
|
||||||
|
tk = scanner.makeToken(SymPercent, ch)
|
||||||
|
}
|
||||||
case '#':
|
case '#':
|
||||||
tk = scanner.makeToken(SymHash, ch)
|
tk = scanner.makeToken(SymHash, ch)
|
||||||
case '@':
|
case '@':
|
||||||
@@ -265,9 +279,18 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
|||||||
if next, _ := scanner.peek(); next == '=' {
|
if next, _ := scanner.peek(); next == '=' {
|
||||||
tk = scanner.moveOn(SymLessOrEqual, ch, next)
|
tk = scanner.moveOn(SymLessOrEqual, ch, next)
|
||||||
} else if 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 == '>' {
|
} else if next == '>' {
|
||||||
tk = scanner.moveOn(SymLessGreater, ch, next)
|
tk = scanner.moveOn(SymLessGreater, ch, next)
|
||||||
|
} else if next == '+' {
|
||||||
|
tk = scanner.moveOn(SymLessPlus, ch, next)
|
||||||
} else {
|
} else {
|
||||||
tk = scanner.makeToken(SymLess, ch)
|
tk = scanner.makeToken(SymLess, ch)
|
||||||
}
|
}
|
||||||
@@ -275,7 +298,14 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
|||||||
if next, _ := scanner.peek(); next == '=' {
|
if next, _ := scanner.peek(); next == '=' {
|
||||||
tk = scanner.moveOn(SymGreaterOrEqual, ch, next)
|
tk = scanner.moveOn(SymGreaterOrEqual, ch, next)
|
||||||
} else if 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 {
|
} else {
|
||||||
tk = scanner.makeToken(SymGreater, ch)
|
tk = scanner.makeToken(SymGreater, ch)
|
||||||
}
|
}
|
||||||
@@ -285,6 +315,14 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
|||||||
tk.source += ")"
|
tk.source += ")"
|
||||||
} else if next == '$' {
|
} else if next == '$' {
|
||||||
tk = scanner.moveOn(SymDoubleDollar, ch, 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 {
|
} else {
|
||||||
tk = scanner.makeToken(SymDollar, ch)
|
tk = scanner.makeToken(SymDollar, ch)
|
||||||
}
|
}
|
||||||
@@ -560,7 +598,7 @@ func (scanner *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
|
func (scanner *scanner) fetchString(termCh byte, addQuote bool) (tk *Token) {
|
||||||
var err error
|
var err error
|
||||||
var ch, prev byte
|
var ch, prev byte
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
@@ -598,7 +636,11 @@ func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
txt := sb.String()
|
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
|
return
|
||||||
}
|
}
|
||||||
@@ -630,9 +672,16 @@ func (scanner *scanner) translate(sym Symbol) Symbol {
|
|||||||
|
|
||||||
func (scanner *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
|
func (scanner *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
|
||||||
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
|
tk = NewToken(scanner.row, scanner.column, scanner.translate(sym), string(chars))
|
||||||
for i := 1; i < len(chars); i++ {
|
// for i := 1; i < len(chars); i++ {
|
||||||
|
if len(chars) > 1 {
|
||||||
scanner.readChar()
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ func NewSimpleStore() *SimpleStore {
|
|||||||
return ctx
|
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 filterRefName(name string) bool { return name[0] != '@' }
|
||||||
//func filterPrivName(name string) bool { return name[0] != '_' }
|
//func filterPrivName(name string) bool { return name[0] != '_' }
|
||||||
|
|
||||||
|
|||||||
+99
-82
@@ -14,7 +14,7 @@ type symbolClass int16
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
symClassOperator symbolClass = iota
|
symClassOperator symbolClass = iota
|
||||||
symClassPostOp
|
symClassCommand
|
||||||
symClassIdentifier
|
symClassIdentifier
|
||||||
symClassDelimiter
|
symClassDelimiter
|
||||||
symClassParenthesis
|
symClassParenthesis
|
||||||
@@ -24,77 +24,87 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type symbolSpec struct {
|
type symbolSpec struct {
|
||||||
repr string
|
repr string
|
||||||
kind symbolClass
|
kind symbolClass
|
||||||
|
opType termPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
||||||
symbolMap = map[Symbol]symbolSpec{
|
symbolMap = map[Symbol]symbolSpec{
|
||||||
SymUnknown: {"<unknown>", symClassOther}, // -1: Unknown symbol
|
SymUnknown: {"<unknown>", symClassOther, posLeaf}, // -1: Unknown symbol
|
||||||
SymNone: {"<null>", symClassOther}, // 0: Null value for variable of type symbol
|
SymNone: {"<null>", symClassOther, posLeaf}, // 0: Null value for variable of type symbol
|
||||||
SymError: {"<error>", symClassOther}, // 1: Error reading from stream
|
SymError: {"<error>", symClassOther, posLeaf}, // 1: Error reading from stream
|
||||||
SymEos: {"<eos>", symClassOther}, // 2: End of stream
|
SymEos: {"<eos>", symClassOther, posLeaf}, // 2: End of stream
|
||||||
SymMinus: {"-", symClassOperator}, // 3: '-'
|
SymMinus: {"-", symClassOperator, posInfix}, // 3: '-'
|
||||||
SymMinusEqual: {"-=", symClassOperator}, // 4: '-='
|
SymMinusEqual: {"-=", symClassOperator, posInfix}, // 4: '-='
|
||||||
SymDoubleMinus: {"--", symClassOperator}, // 5: '--'
|
SymDoubleMinus: {"--", symClassOperator, posPostfix}, // 5: '--'
|
||||||
SymPlus: {"+", symClassOperator}, // 6: '+'
|
SymPlus: {"+", symClassOperator, posInfix}, // 6: '+'
|
||||||
SymPlusEqual: {"+=", symClassOperator}, // 7: '+='
|
SymPlusEqual: {"+=", symClassOperator, posInfix}, // 7: '+='
|
||||||
SymDoublePlus: {"++", symClassOperator}, // 8: '++'
|
SymDoublePlus: {"++", symClassOperator, posPostfix}, // 8: '++'
|
||||||
SymStar: {"*", symClassOperator}, // 9: '*'
|
SymStar: {"*", symClassOperator, posInfix}, // 9: '*'
|
||||||
SymDoubleStar: {"**", symClassOperator}, // 10: '**'
|
SymDoubleStar: {"**", symClassOperator, posInfix}, // 10: '**'
|
||||||
SymSlash: {"/", symClassOperator}, // 11: '/'
|
SymSlash: {"/", symClassOperator, posInfix}, // 11: '/'
|
||||||
SymBackSlash: {"\\", symClassOperator}, // 12: '\'
|
SymBackSlash: {"\\", symClassOperator, posLeaf}, // 12: '\'
|
||||||
SymVertBar: {"|", symClassOperator}, // 13: '|'
|
SymVertBar: {"|", symClassOperator, posInfix}, // 13: '|'
|
||||||
SymDoubleVertBar: {"||", symClassOperator}, // 14: '||'
|
SymDoubleVertBar: {"||", symClassOperator, posInfix}, // 14: '||'
|
||||||
SymComma: {",", symClassOperator}, // 15: ','
|
SymComma: {",", symClassOperator, posInfix}, // 15: ','
|
||||||
SymColon: {":", symClassOperator}, // 16: ':'
|
SymColon: {":", symClassOperator, posInfix}, // 16: ':'
|
||||||
SymSemiColon: {";", symClassOperator}, // 17: ';'
|
SymSemiColon: {";", symClassOperator, posInfix}, // 17: ';'
|
||||||
SymDot: {".", symClassOperator}, // 18: '.'
|
SymDot: {".", symClassOperator, posInfix}, // 18: '.'
|
||||||
SymDotSlash: {"./", symClassOperator}, // 19: './'
|
SymDotSlash: {"./", symClassOperator, posInfix}, // 19: './'
|
||||||
SymQuote: {"'", symClassDelimiter}, // 20: '\''
|
SymQuote: {"'", symClassDelimiter, posLeaf}, // 20: '\''
|
||||||
SymDoubleQuote: {"\"", symClassDelimiter}, // 21: '"'
|
SymDoubleQuote: {"\"", symClassDelimiter, posLeaf}, // 21: '"'
|
||||||
SymBackTick: {"`", symClassOperator}, // 22: '`'
|
SymBackTick: {"`", symClassDelimiter, posLeaf}, // 22: '`'
|
||||||
SymExclamation: {"!", symClassPostOp}, // 23: '!'
|
SymExclamation: {"!", symClassOperator, posPostfix}, // 23: '!'
|
||||||
SymQuestion: {"?", symClassOperator}, // 24: '?'
|
SymQuestion: {"?", symClassOperator, posInfix}, // 24: '?'
|
||||||
SymAmpersand: {"&", symClassOperator}, // 25: '&'
|
SymAmpersand: {"&", symClassOperator, posInfix}, // 25: '&'
|
||||||
SymDoubleAmpersand: {"&&", symClassOperator}, // 26: '&&'
|
SymDoubleAmpersand: {"&&", symClassOperator, posInfix}, // 26: '&&'
|
||||||
SymPercent: {"%", symClassOperator}, // 27: '%'
|
SymPercent: {"%", symClassOperator, posInfix}, // 27: '%'
|
||||||
SymAt: {"@", symClassOperator}, // 28: '@'
|
SymAt: {"@", symClassOperator, posPrefix}, // 28: '@'
|
||||||
SymUndescore: {"_", symClassOperator}, // 29: '_'
|
SymUndescore: {"_", symClassIdentifier, posLeaf}, // 29: '_'
|
||||||
SymEqual: {"=", symClassOperator}, // 30: '='
|
SymEqual: {"=", symClassOperator, posInfix}, // 30: '='
|
||||||
SymDoubleEqual: {"==", symClassOperator}, // 31: '=='
|
SymDoubleEqual: {"==", symClassOperator, posInfix}, // 31: '=='
|
||||||
SymLess: {"<", symClassOperator}, // 32: '<'
|
SymLess: {"<", symClassOperator, posInfix}, // 32: '<'
|
||||||
SymLessOrEqual: {"<=", symClassOperator}, // 33: '<='
|
SymLessOrEqual: {"<=", symClassOperator, posInfix}, // 33: '<='
|
||||||
SymGreater: {">", symClassOperator}, // 34: '>'
|
SymGreater: {">", symClassOperator, posInfix}, // 34: '>'
|
||||||
SymGreaterOrEqual: {">=", symClassOperator}, // 35: '>='
|
SymGreaterOrEqual: {">=", symClassOperator, posInfix}, // 35: '>='
|
||||||
SymLessGreater: {"<>", symClassOperator}, // 36: '<>'
|
SymLessGreater: {"<>", symClassOperator, posInfix}, // 36: '<>'
|
||||||
SymNotEqual: {"!=", symClassOperator}, // 37: '!='
|
SymNotEqual: {"!=", symClassOperator, posInfix}, // 37: '!='
|
||||||
SymDollar: {"$", symClassOperator}, // 38: '$'
|
SymDollar: {"$", symClassOperator, posPrefix}, // 38: '$'
|
||||||
SymHash: {"#", symClassOperator}, // 39: '#'
|
SymHash: {"#", symClassOperator, posPrefix}, // 39: '#'
|
||||||
SymOpenRound: {"(", symClassParenthesis}, // 40: '('
|
SymOpenRound: {"(", symClassParenthesis, posPrefix}, // 40: '('
|
||||||
SymClosedRound: {")", symClassParenthesis}, // 41: ')'
|
SymClosedRound: {")", symClassParenthesis, posPostfix}, // 41: ')'
|
||||||
SymOpenSquare: {"[", symClassParenthesis}, // 42: '['
|
SymOpenSquare: {"[", symClassParenthesis, posPrefix}, // 42: '['
|
||||||
SymClosedSquare: {"]", symClassParenthesis}, // 43: ']'
|
SymClosedSquare: {"]", symClassParenthesis, posPostfix}, // 43: ']'
|
||||||
SymOpenBrace: {"{", symClassParenthesis}, // 44: '{'
|
SymOpenBrace: {"{", symClassParenthesis, posPrefix}, // 44: '{'
|
||||||
SymClosedBrace: {"}", symClassParenthesis}, // 45: '}'
|
SymClosedBrace: {"}", symClassParenthesis, posPostfix}, // 45: '}'
|
||||||
SymTilde: {"~", symClassOperator}, // 46: '~'
|
SymTilde: {"~", symClassOperator, posPrefix}, // 46: '~'
|
||||||
SymDoubleQuestion: {"??", symClassOperator}, // 47: '??'
|
SymDoubleQuestion: {"??", symClassOperator, posInfix}, // 47: '??'
|
||||||
SymQuestionEqual: {"?=", symClassOperator}, // 48: '?='
|
SymQuestionEqual: {"?=", symClassOperator, posInfix}, // 48: '?='
|
||||||
SymQuestionExclam: {"?!", symClassOperator}, // 49: '?!'
|
SymQuestionExclam: {"?!", symClassOperator, posInfix}, // 49: '?!'
|
||||||
SymDoubleAt: {"@@", symClassOperator}, // 50: '@@'
|
SymDoubleAt: {"@@", symClassCommand, posLeaf}, // 50: '@@'
|
||||||
SymDoubleColon: {"::", symClassOperator}, // 51: '::'
|
SymDoubleColon: {"::", symClassOperator, posInfix}, // 51: '::'
|
||||||
SymInsert: {">>", symClassOperator}, // 52: '>>'
|
SymDoubleGreater: {">>", symClassOperator, posInfix}, // 52: '>>'
|
||||||
SymAppend: {"<<", symClassOperator}, // 53: '<<'
|
SymDoubleLess: {"<<", symClassOperator, posInfix}, // 53: '<<'
|
||||||
SymCaret: {"^", symClassOperator}, // 54: '^'
|
SymCaret: {"^", symClassOperator, posInfix}, // 54: '^'
|
||||||
SymDollarRound: {"$(", symClassOperator}, // 55: '$('
|
SymDollarRound: {"$(", symClassOperator, posPrefix}, // 55: '$('
|
||||||
SymOpenClosedRound: {"()", symClassPostOp}, // 56: '()'
|
SymOpenClosedRound: {"()", symClassOperator, posPostfix}, // 56: '()'
|
||||||
SymDoubleDollar: {"$$", symClassOperator}, // 57: '$$'
|
SymDoubleDollar: {"$$", symClassCommand, posLeaf}, // 57: '$$'
|
||||||
SymDoubleDot: {"..", symClassOperator}, // 58: '..'
|
SymDoubleDot: {"..", symClassOperator, posInfix}, // 58: '..'
|
||||||
SymTripleDot: {"...", symClassOperator}, // 59: '...'
|
SymTripleDot: {"...", symClassOperator, posPostfix}, // 59: '...'
|
||||||
SymStarEqual: {"*=", symClassOperator}, // 60: '*='
|
SymStarEqual: {"*=", symClassOperator, posInfix}, // 60: '*='
|
||||||
SymSlashEqual: {"/=", symClassOperator}, // 61: '/='
|
SymSlashEqual: {"/=", symClassOperator, posInfix}, // 61: '/='
|
||||||
SymPercEqual: {"%=", symClassOperator}, // 62: '%='
|
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
|
// SymChangeSign
|
||||||
// SymUnchangeSign
|
// SymUnchangeSign
|
||||||
// SymIdentifier
|
// SymIdentifier
|
||||||
@@ -121,17 +131,17 @@ func init() {
|
|||||||
// // SymClosedComment // 0: '*/'
|
// // SymClosedComment // 0: '*/'
|
||||||
// // SymOneLineComment // 0: '//'
|
// // SymOneLineComment // 0: '//'
|
||||||
// keywordBase
|
// keywordBase
|
||||||
SymKwAnd: {"and", symClassOperator},
|
SymKwAnd: {"and", symClassOperator, posInfix},
|
||||||
SymKwNot: {"not", symClassOperator},
|
SymKwNot: {"not", symClassOperator, posInfix},
|
||||||
SymKwOr: {"or", symClassOperator},
|
SymKwOr: {"or", symClassOperator, posInfix},
|
||||||
SymKwBut: {"but", symClassOperator},
|
SymKwBut: {"but", symClassOperator, posInfix},
|
||||||
SymKwFunc: {"func(", symClassDeclaration},
|
SymKwFunc: {"func(", symClassDeclaration, posPrefix},
|
||||||
SymKwBuiltin: {"builtin", symClassOperator},
|
SymKwBuiltin: {"builtin", symClassOperator, posPrefix},
|
||||||
SymKwPlugin: {"plugin", symClassOperator},
|
SymKwPlugin: {"plugin", symClassOperator, posPrefix},
|
||||||
SymKwIn: {"in", symClassOperator},
|
SymKwIn: {"in", symClassOperator, posInfix},
|
||||||
SymKwInclude: {"include", symClassOperator},
|
SymKwInclude: {"include", symClassOperator, posPrefix},
|
||||||
SymKwNil: {"nil", symClassValue},
|
SymKwNil: {"nil", symClassValue, posLeaf},
|
||||||
SymKwUnset: {"unset", symClassOperator},
|
SymKwUnset: {"unset", symClassOperator, posPrefix},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,12 +179,19 @@ func StringEndsWithOperator(s string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func endingOperator(s string) (sym Symbol) {
|
func endingOperator(s string) (sym Symbol) {
|
||||||
|
var matchLength = 0
|
||||||
sym = SymNone
|
sym = SymNone
|
||||||
lower := strings.ToLower(s)
|
lower := strings.TrimRight(strings.ToLower(s), " \t")
|
||||||
for symbol, spec := range symbolMap {
|
for symbol, spec := range symbolMap {
|
||||||
if spec.kind == symClassOperator && strings.HasSuffix(lower, spec.repr) {
|
if strings.HasSuffix(lower, spec.repr) {
|
||||||
sym = symbol
|
if len(spec.repr) > matchLength {
|
||||||
break
|
matchLength = len(spec.repr)
|
||||||
|
if spec.kind == symClassOperator && (spec.opType == posInfix || spec.opType == posPrefix) {
|
||||||
|
sym = symbol
|
||||||
|
} else {
|
||||||
|
sym = SymNone
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -7,72 +7,82 @@ package expr
|
|||||||
type Symbol int16
|
type Symbol int16
|
||||||
|
|
||||||
const (
|
const (
|
||||||
SymUnknown Symbol = iota - 1 // -1: Unknown symbol
|
SymUnknown Symbol = iota - 1 // -1: Unknown symbol
|
||||||
SymNone // 0: Null value for variable of type symbol
|
SymNone // 0: Null value for variable of type symbol
|
||||||
SymError // 1: Error reading from stream
|
SymError // 1: Error reading from stream
|
||||||
SymEos // 2: End of stream
|
SymEos // 2: End of stream
|
||||||
SymMinus // 3: '-'
|
SymMinus // 3: '-'
|
||||||
SymMinusEqual // 4: '-='
|
SymMinusEqual // 4: '-='
|
||||||
SymDoubleMinus // 5: '--'
|
SymDoubleMinus // 5: '--'
|
||||||
SymPlus // 6: '+'
|
SymPlus // 6: '+'
|
||||||
SymPlusEqual // 7: '+='
|
SymPlusEqual // 7: '+='
|
||||||
SymDoublePlus // 8: '++'
|
SymDoublePlus // 8: '++'
|
||||||
SymStar // 9: '*'
|
SymStar // 9: '*'
|
||||||
SymDoubleStar // 10: '**'
|
SymDoubleStar // 10: '**'
|
||||||
SymSlash // 11: '/'
|
SymSlash // 11: '/'
|
||||||
SymBackSlash // 12: '\'
|
SymBackSlash // 12: '\'
|
||||||
SymVertBar // 13: '|'
|
SymVertBar // 13: '|'
|
||||||
SymDoubleVertBar // 14: '||'
|
SymDoubleVertBar // 14: '||'
|
||||||
SymComma // 15: ','
|
SymComma // 15: ','
|
||||||
SymColon // 16: ':'
|
SymColon // 16: ':'
|
||||||
SymSemiColon // 17: ';'
|
SymSemiColon // 17: ';'
|
||||||
SymDot // 18: '.'
|
SymDot // 18: '.'
|
||||||
SymDotSlash // 19: './'
|
SymDotSlash // 19: './'
|
||||||
SymQuote // 20: '\''
|
SymQuote // 20: '\''
|
||||||
SymDoubleQuote // 21: '"'
|
SymDoubleQuote // 21: '"'
|
||||||
SymBackTick // 22: '`'
|
SymBackTick // 22: '`'
|
||||||
SymExclamation // 23: '!'
|
SymExclamation // 23: '!'
|
||||||
SymQuestion // 24: '?'
|
SymQuestion // 24: '?'
|
||||||
SymAmpersand // 25: '&'
|
SymAmpersand // 25: '&'
|
||||||
SymDoubleAmpersand // 26: '&&'
|
SymDoubleAmpersand // 26: '&&'
|
||||||
SymPercent // 27: '%'
|
SymPercent // 27: '%'
|
||||||
SymAt // 28: '@'
|
SymAt // 28: '@'
|
||||||
SymUndescore // 29: '_'
|
SymUndescore // 29: '_'
|
||||||
SymEqual // 30: '='
|
SymEqual // 30: '='
|
||||||
SymDoubleEqual // 31: '=='
|
SymDoubleEqual // 31: '=='
|
||||||
SymLess // 32: '<'
|
SymLess // 32: '<'
|
||||||
SymLessOrEqual // 33: '<='
|
SymLessOrEqual // 33: '<='
|
||||||
SymGreater // 34: '>'
|
SymGreater // 34: '>'
|
||||||
SymGreaterOrEqual // 35: '>='
|
SymGreaterOrEqual // 35: '>='
|
||||||
SymLessGreater // 36: '<>'
|
SymLessGreater // 36: '<>'
|
||||||
SymNotEqual // 37: '!='
|
SymNotEqual // 37: '!='
|
||||||
SymDollar // 38: '$'
|
SymDollar // 38: '$'
|
||||||
SymHash // 39: '#'
|
SymHash // 39: '#'
|
||||||
SymOpenRound // 40: '('
|
SymOpenRound // 40: '('
|
||||||
SymClosedRound // 41: ')'
|
SymClosedRound // 41: ')'
|
||||||
SymOpenSquare // 42: '['
|
SymOpenSquare // 42: '['
|
||||||
SymClosedSquare // 43: ']'
|
SymClosedSquare // 43: ']'
|
||||||
SymOpenBrace // 44: '{'
|
SymOpenBrace // 44: '{'
|
||||||
SymClosedBrace // 45: '}'
|
SymClosedBrace // 45: '}'
|
||||||
SymTilde // 46: '~'
|
SymTilde // 46: '~'
|
||||||
SymDoubleQuestion // 47: '??'
|
SymDoubleQuestion // 47: '??'
|
||||||
SymQuestionEqual // 48: '?='
|
SymQuestionEqual // 48: '?='
|
||||||
SymQuestionExclam // 49: '?!'
|
SymQuestionExclam // 49: '?!'
|
||||||
SymDoubleAt // 50: '@@'
|
SymDoubleAt // 50: '@@'
|
||||||
SymDoubleColon // 51: '::'
|
SymDoubleColon // 51: '::'
|
||||||
SymInsert // 52: '>>'
|
SymDoubleGreater // 52: '>>'
|
||||||
SymAppend // 53: '<<'
|
SymDoubleLess // 53: '<<'
|
||||||
SymCaret // 54: '^'
|
SymCaret // 54: '^'
|
||||||
SymDollarRound // 55: '$('
|
SymDollarRound // 55: '$('
|
||||||
SymOpenClosedRound // 56: '()'
|
SymOpenClosedRound // 56: '()'
|
||||||
SymDoubleDollar // 57: '$$'
|
SymDoubleDollar // 57: '$$'
|
||||||
SymDoubleDot // 58: '..'
|
SymDoubleDot // 58: '..'
|
||||||
SymTripleDot // 59: '...'
|
SymTripleDot // 59: '...'
|
||||||
SymStarEqual // 60: '*='
|
SymStarEqual // 60: '*='
|
||||||
SymSlashEqual // 61: '/='
|
SymSlashEqual // 61: '/='
|
||||||
SymPercEqual // 62: '%='
|
SymPercEqual // 62: '%='
|
||||||
|
SymDoubleLessEqual // 63: '<<='
|
||||||
|
SymDoubleGreaterEqual // 64: '>>='
|
||||||
|
SymAmpersandEqual // 65: '&='
|
||||||
|
SymVertBarEqual // 65: '|='
|
||||||
|
SymCaretEqual // 66: '^='
|
||||||
|
SymPlusGreater // 67: '+>'
|
||||||
|
SymLessPlus // 68: '<+'
|
||||||
SymChangeSign
|
SymChangeSign
|
||||||
SymUnchangeSign
|
SymUnchangeSign
|
||||||
|
SymDereference
|
||||||
|
SymPreInc
|
||||||
|
SymPreDec
|
||||||
SymIdentifier
|
SymIdentifier
|
||||||
SymBool
|
SymBool
|
||||||
SymInteger
|
SymInteger
|
||||||
@@ -109,6 +119,7 @@ const (
|
|||||||
SymKwPlugin
|
SymKwPlugin
|
||||||
SymKwIn
|
SymKwIn
|
||||||
SymKwInclude
|
SymKwInclude
|
||||||
|
SymKwMap
|
||||||
SymKwNil
|
SymKwNil
|
||||||
SymKwUnset
|
SymKwUnset
|
||||||
)
|
)
|
||||||
@@ -125,6 +136,7 @@ func init() {
|
|||||||
"FUNC": SymKwFunc,
|
"FUNC": SymKwFunc,
|
||||||
"IN": SymKwIn,
|
"IN": SymKwIn,
|
||||||
"INCLUDE": SymKwInclude,
|
"INCLUDE": SymKwInclude,
|
||||||
|
"MAP": SymKwMap,
|
||||||
"NOT": SymKwNot,
|
"NOT": SymKwNot,
|
||||||
"OR": SymKwOr,
|
"OR": SymKwOr,
|
||||||
"NIL": SymKwNil,
|
"NIL": SymKwNil,
|
||||||
|
|||||||
+2
-1
@@ -26,12 +26,13 @@ func TestBool(t *testing.T) {
|
|||||||
/* 12 */ {`true or false`, true, nil},
|
/* 12 */ {`true or false`, true, nil},
|
||||||
/* 13 */ {`true or []`, 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`)},
|
/* 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"`)},
|
/* 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", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 1)
|
// runTestSuiteSpec(t, section, inputs, 15)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-6
@@ -52,16 +52,21 @@ func TestFuncBase(t *testing.T) {
|
|||||||
/* 38 */ {`bool(1.0)`, true, nil},
|
/* 38 */ {`bool(1.0)`, true, nil},
|
||||||
/* 39 */ {`bool("1")`, true, nil},
|
/* 39 */ {`bool("1")`, true, nil},
|
||||||
/* 40 */ {`bool(false)`, false, nil},
|
/* 40 */ {`bool(false)`, false, nil},
|
||||||
/* 41 */ {`bool([1])`, nil, `bool(): can't convert list to bool`},
|
/* 41 */ {`bool([1])`, true, nil},
|
||||||
/* 42 */ {`dec(false)`, float64(0), nil},
|
/* 42 */ {`bool([])`, false, nil},
|
||||||
/* 43 */ {`dec(1:2)`, float64(0.5), nil},
|
/* 43 */ {`bool({})`, false, nil},
|
||||||
/* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
|
/* 44 */ {`bool({1:"one"})`, true, nil},
|
||||||
/* 45 */ {`eval("a=3"); a`, int64(3), 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`},
|
// /* 45 */ {`string([1])`, nil, `string(): can't convert list to string`},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("EXPR_PATH", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 45)
|
// runTestSuiteSpec(t, section, inputs, 49)
|
||||||
runTestSuite(t, section, inputs)
|
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]`},
|
/* 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},
|
/* 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"`},
|
/* 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", ".")
|
//t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
//runTestSuiteSpec(t, section, inputs, 1)
|
runTestSuiteSpec(t, section, inputs, 10)
|
||||||
runTestSuite(t, section, inputs)
|
// runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,10 +27,13 @@ func TestFuncOs(t *testing.T) {
|
|||||||
/* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, `fileClose(): invalid file handle`},
|
/* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, `fileClose(): invalid file handle`},
|
||||||
/* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil},
|
/* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil},
|
||||||
/* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, `fileReadTextAll(): invalid file handle 123 [int64]`},
|
/* 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", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
//runTestSuiteSpec(t, section, inputs, 2)
|
runTestSuiteSpec(t, section, inputs, 18)
|
||||||
runTestSuite(t, section, inputs)
|
// runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ func TestFuncString(t *testing.T) {
|
|||||||
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil},
|
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil},
|
||||||
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, `strJoin(): expected string, got integer (1)`},
|
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, `strJoin(): expected string, got integer (1)`},
|
||||||
/* 19 */ {`builtin "string"; strJoin()`, nil, `strJoin(): too few params -- expected 1 or more, got 0`},
|
/* 19 */ {`builtin "string"; strJoin()`, nil, `strJoin(): too few params -- expected 1 or more, got 0`},
|
||||||
|
/* 20 */ {`builtin "string"; strUpper("StOp")`, "STOP", nil},
|
||||||
|
/* 21 */ {`builtin "string"; strLower("StOp")`, "stop", nil},
|
||||||
|
|
||||||
/* 69 */ /*{`builtin "string"; $$global`, `vars: {
|
/* 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 expr Expr
|
||||||
var gotResult any
|
var gotResult any
|
||||||
var gotErr error
|
var gotErr error
|
||||||
|
var eq, eqDone bool
|
||||||
|
|
||||||
wantErr := getWantedError(input)
|
wantErr := getWantedError(input)
|
||||||
|
|
||||||
@@ -90,7 +91,18 @@ func doTest(t *testing.T, ctx ExprContext, section string, input *inputType, cou
|
|||||||
gotResult, gotErr = expr.Eval(ctx)
|
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*/ {
|
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))
|
t.Errorf("%d: `%s` -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, TypeName(gotResult), input.wantResult, TypeName(input.wantResult))
|
||||||
|
|||||||
+6
-2
@@ -29,7 +29,9 @@ func TestExpr(t *testing.T) {
|
|||||||
/* 15 */ {`a=3; a*=2)+1; a`, nil, `[1:11] unexpected token ")"`},
|
/* 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},
|
/* 16 */ {`v=[2]; a=1; v[a-=1]=5; v[0]`, int64(5), nil},
|
||||||
/* 17 */ {`true ? {"a"} :: {"b"}`, "a", nil},
|
/* 17 */ {`true ? {"a"} :: {"b"}`, "a", nil},
|
||||||
/* 18 */ {`
|
/* 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={
|
ds={
|
||||||
"init":func(@end){@current=0 but true},
|
"init":func(@end){@current=0 but true},
|
||||||
//"current":func(){current},
|
//"current":func(){current},
|
||||||
@@ -41,9 +43,11 @@ func TestExpr(t *testing.T) {
|
|||||||
it++;
|
it++;
|
||||||
it++
|
it++
|
||||||
`, int64(1), nil},
|
`, int64(1), nil},
|
||||||
|
/* 20 */ {`a=2; ${a}`, int64(2), nil},
|
||||||
|
/* 21 */ {`$_=2; $_`, int64(2), nil},
|
||||||
}
|
}
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 6, 7, 8, 9)
|
// runTestSuiteSpec(t, section, inputs, 21)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -25,7 +25,7 @@ func TestFractionsParser(t *testing.T) {
|
|||||||
/* 12 */ {`builtin "math.arith"; add(1:2, 1.0, 2)`, float64(3.5), nil},
|
/* 12 */ {`builtin "math.arith"; add(1:2, 1.0, 2)`, float64(3.5), nil},
|
||||||
/* 13 */ {`builtin "math.arith"; mul(1:2, 2:3)`, newFraction(2, 6), nil},
|
/* 13 */ {`builtin "math.arith"; mul(1:2, 2:3)`, newFraction(2, 6), nil},
|
||||||
/* 14 */ {`builtin "math.arith"; mul(1:2, 1.0, 2)`, float64(1.0), nil},
|
/* 14 */ {`builtin "math.arith"; mul(1:2, 1.0, 2)`, float64(1.0), nil},
|
||||||
/* 15 */ {`1:0`, nil, `division by zero`},
|
/* 15 */ {`1:0`, nil, `[1:3] division by zero`},
|
||||||
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
|
/* 16 */ {`fract(-0.5)`, newFraction(-1, 2), nil},
|
||||||
/* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
|
/* 17 */ {`fract("")`, (*FractionType)(nil), `bad syntax`},
|
||||||
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
|
/* 18 */ {`fract("-1")`, newFraction(-1, 1), nil},
|
||||||
@@ -34,7 +34,7 @@ func TestFractionsParser(t *testing.T) {
|
|||||||
/* 21 */ {`fract(1,0)`, nil, `fract(): division by zero`},
|
/* 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},
|
/* 23 */ {`1+1:2+0.5`, float64(2), nil},
|
||||||
/* 24 */ {`1:(2-2)`, nil, `division by zero`},
|
/* 24 */ {`1:(2-2)`, nil, `[1:3] division by zero`},
|
||||||
/* 25 */ {`[0,1][1-1]:1`, newFraction(0, 1), nil},
|
/* 25 */ {`[0,1][1-1]:1`, newFraction(0, 1), nil},
|
||||||
}
|
}
|
||||||
// runTestSuiteSpec(t, section, inputs, 25)
|
// runTestSuiteSpec(t, section, inputs, 25)
|
||||||
|
|||||||
@@ -68,3 +68,27 @@ func TestFunctionGetFunc(t *testing.T) {
|
|||||||
t.Errorf(`(func() -> result = %v [%T], want = %v [%T]`, got, got, want, want)
|
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)
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
+8
-8
@@ -59,7 +59,7 @@ func TestNewIterList2(t *testing.T) {
|
|||||||
|
|
||||||
func TestNewIterList3(t *testing.T) {
|
func TestNewIterList3(t *testing.T) {
|
||||||
list := []any{"a", "b", "c", "d"}
|
list := []any{"a", "b", "c", "d"}
|
||||||
it := NewAnyIterator(list)
|
it, _ := NewIterator(list)
|
||||||
if item, err := it.Next(); err != nil {
|
if item, err := it.Next(); err != nil {
|
||||||
t.Errorf("error: %v", err)
|
t.Errorf("error: %v", err)
|
||||||
} else if item != "a" {
|
} else if item != "a" {
|
||||||
@@ -71,7 +71,7 @@ func TestNewIterList3(t *testing.T) {
|
|||||||
|
|
||||||
func TestNewIterList4(t *testing.T) {
|
func TestNewIterList4(t *testing.T) {
|
||||||
list := any(nil)
|
list := any(nil)
|
||||||
it := NewAnyIterator(list)
|
it, _ := NewIterator(list)
|
||||||
if _, err := it.Next(); err != io.EOF {
|
if _, err := it.Next(); err != io.EOF {
|
||||||
t.Errorf("error: %v", err)
|
t.Errorf("error: %v", err)
|
||||||
}
|
}
|
||||||
@@ -79,7 +79,7 @@ func TestNewIterList4(t *testing.T) {
|
|||||||
|
|
||||||
func TestNewIterList5(t *testing.T) {
|
func TestNewIterList5(t *testing.T) {
|
||||||
list := "123"
|
list := "123"
|
||||||
it := NewAnyIterator(list)
|
it, _ := NewIterator(list)
|
||||||
if item, err := it.Next(); err != nil {
|
if item, err := it.Next(); err != nil {
|
||||||
t.Errorf("error: %v", err)
|
t.Errorf("error: %v", err)
|
||||||
} else if item != "123" {
|
} else if item != "123" {
|
||||||
@@ -91,8 +91,8 @@ func TestNewIterList5(t *testing.T) {
|
|||||||
|
|
||||||
func TestNewIterList6(t *testing.T) {
|
func TestNewIterList6(t *testing.T) {
|
||||||
list := newListA("a", "b", "c", "d")
|
list := newListA("a", "b", "c", "d")
|
||||||
it1 := NewAnyIterator(list)
|
it1, _ := NewIterator(list)
|
||||||
it := NewAnyIterator(it1)
|
it, _ := NewIterator(it1)
|
||||||
if item, err := it.Next(); err != nil {
|
if item, err := it.Next(); err != nil {
|
||||||
t.Errorf("error: %v", err)
|
t.Errorf("error: %v", err)
|
||||||
} else if item != "a" {
|
} else if item != "a" {
|
||||||
@@ -103,9 +103,9 @@ func TestNewIterList6(t *testing.T) {
|
|||||||
}
|
}
|
||||||
func TestNewString(t *testing.T) {
|
func TestNewString(t *testing.T) {
|
||||||
list := "123"
|
list := "123"
|
||||||
it := NewAnyIterator(list)
|
it, _ := NewIterator(list)
|
||||||
if s := it.String(); s != "$(#1)" {
|
if s := it.String(); s != "$([#1])" {
|
||||||
t.Errorf("expected $(#1), got %s", s)
|
t.Errorf("expected $([#1]), got %s", s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-4
@@ -9,10 +9,10 @@ import "testing"
|
|||||||
func TestIteratorParser(t *testing.T) {
|
func TestIteratorParser(t *testing.T) {
|
||||||
section := "Iterator"
|
section := "Iterator"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ^it`, int64(0), nil},
|
/* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); *it`, int64(0), nil},
|
||||||
/* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
|
/* 2 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil},
|
||||||
/* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(3), 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},
|
/* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
|
||||||
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
|
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
|
||||||
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
|
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
|
||||||
@@ -27,8 +27,13 @@ func TestIteratorParser(t *testing.T) {
|
|||||||
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
|
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
|
||||||
/* 17 */ {`it=$({"next":func(){5}}); it++`, int64(5), nil},
|
/* 17 */ {`it=$({"next":func(){5}}); it++`, int64(5), nil},
|
||||||
/* 18 */ {`it=$({"next":func(){5}}); it.clean`, nil, 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)
|
runTestSuiteSpec(t, section, inputs, 23)
|
||||||
runTestSuite(t, section, inputs)
|
// runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-16
@@ -23,8 +23,8 @@ func TestListParser(t *testing.T) {
|
|||||||
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
||||||
/* 10 */ {`add([1,"hello"])`, nil, `add(): param nr 2 (2 in 1) has wrong type string, number expected`},
|
/* 10 */ {`add([1,"hello"])`, nil, `add(): param nr 2 (2 in 1) has wrong type string, number expected`},
|
||||||
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil},
|
||||||
/* 12 */ {`[1,2,3] << 2+2`, newListA(int64(1), int64(2), int64(3), int64(4)), nil},
|
/* 12 */ {`[1,2,3] <+ 2+2`, newListA(int64(1), int64(2), int64(3), int64(4)), nil},
|
||||||
/* 13 */ {`2-1 >> [2,3]`, newListA(int64(1), int64(2), int64(3)), nil},
|
/* 13 */ {`2-1 +> [2,3]`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||||
/* 14 */ {`[1,2,3][1]`, int64(2), nil},
|
/* 14 */ {`[1,2,3][1]`, int64(2), nil},
|
||||||
/* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil},
|
/* 15 */ {`ls=[1,2,3] but ls[1]`, int64(2), nil},
|
||||||
/* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil},
|
/* 16 */ {`ls=[1,2,3] but ls[-1]`, int64(3), nil},
|
||||||
@@ -33,25 +33,25 @@ func TestListParser(t *testing.T) {
|
|||||||
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
|
/* 19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), nil},
|
||||||
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
|
/* 20 */ {`#["a", "b", "c"]`, int64(3), nil},
|
||||||
/* 21 */ {`"b" in ["a", "b", "c"]`, true, nil},
|
/* 21 */ {`"b" in ["a", "b", "c"]`, true, nil},
|
||||||
/* 22 */ {`a=[1,2]; (a)<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
/* 22 */ {`a=[1,2]; (a)<+3`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||||
/* 23 */ {`a=[1,2]; (a)<<3; a`, newListA(int64(1), int64(2)), nil},
|
/* 23 */ {`a=[1,2]; (a)<+3; a`, newListA(int64(1), int64(2)), nil},
|
||||||
/* 24 */ {`["a","b","c","d"][1]`, "b", nil},
|
/* 24 */ {`["a","b","c","d"][1]`, "b", nil},
|
||||||
/* 25 */ {`["a","b","c","d"][1,1]`, nil, `[1:19] one index only is allowed`},
|
/* 25 */ {`["a","b","c","d"][1,1]`, nil, `[1:19] one index only is allowed`},
|
||||||
/* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
/* 26 */ {`[0,1,2,3,4][:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||||
/* 27 */ {`["a", "b", "c"] << ;`, nil, `[1:18] infix operator "<<" requires two non-nil operands, got 1`},
|
/* 27 */ {`["a", "b", "c"] <+ ;`, nil, `[1:18] infix operator "<+" requires two non-nil operands, got 1`},
|
||||||
/* 28 */ {`2 << 3;`, int64(16), nil},
|
/* 28 */ {`2 << 3;`, int64(16), nil},
|
||||||
/* 29 */ {`but >> ["a", "b", "c"]`, nil, `[1:6] infix operator ">>" requires two non-nil operands, got 0`},
|
/* 29 */ {`but +> ["a", "b", "c"]`, nil, `[1:6] infix operator "+>" requires two non-nil operands, got 0`},
|
||||||
/* 30 */ {`2 >> 3;`, int64(0), nil},
|
/* 30 */ {`2 >> 3;`, int64(0), nil},
|
||||||
/* 31 */ {`a=[1,2]; a<<3`, newListA(int64(1), int64(2), int64(3)), nil},
|
/* 31 */ {`a=[1,2]; a<+3`, newListA(int64(1), int64(2), int64(3)), nil},
|
||||||
/* 33 */ {`a=[1,2]; 5>>a`, newListA(int64(5), int64(1), int64(2)), nil},
|
/* 32 */ {`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},
|
/* 33 */ {`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)`},
|
/* 34 */ {`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]`},
|
/* 35 */ {`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`},
|
/* 36 */ {`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},
|
/* 37 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil},
|
||||||
/* 39 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil},
|
/* 38 */ {`[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},
|
/* 30 */ {`[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},
|
/* 40 */ {`[0,1,2,3,4][0:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// 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"`},
|
/* 1 */ {`a=1; unset "a"; a`, nil, `undefined variable or function "a"`},
|
||||||
/* 2 */ {`a=1; unset ["a", "b"]`, int64(1), nil},
|
/* 2 */ {`a=1; unset ["a", "b"]`, int64(1), nil},
|
||||||
/* 3 */ {`f=func(){3}; unset "f()"`, 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", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
//runTestSuiteSpec(t, section, inputs, 3)
|
runTestSuiteSpec(t, section, inputs, 24)
|
||||||
runTestSuite(t, section, inputs)
|
// runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -78,8 +78,8 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 64 */ {`"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},
|
/* 65 */ {"+1.5", float64(1.5), nil},
|
||||||
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one non-nil operand`},
|
/* 66 */ {"+", nil, `[1:2] prefix operator "+" requires one non-nil operand`},
|
||||||
/* 67 */ {"4 / 0", nil, `division by zero`},
|
/* 67 */ {"4 / 0", nil, `[1:4] division by zero`},
|
||||||
/* 68 */ {"4.0 / 0", nil, `division by zero`},
|
/* 68 */ {"4.0 / 0", nil, `[1:6] division by zero`},
|
||||||
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
|
/* 69 */ {"4.0 / \n2", float64(2.0), nil},
|
||||||
/* 70 */ {`123`, int64(123), nil},
|
/* 70 */ {`123`, int64(123), nil},
|
||||||
/* 71 */ {`1.`, float64(1.0), nil},
|
/* 71 */ {`1.`, float64(1.0), nil},
|
||||||
@@ -142,6 +142,6 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
//runTestSuiteSpec(t, section, inputs, 130)
|
// runTestSuiteSpec(t, section, inputs, 114)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-1
@@ -30,9 +30,29 @@ func TestPluginExists(t *testing.T) {
|
|||||||
|
|
||||||
func TestMakePluginName(t *testing.T) {
|
func TestMakePluginName(t *testing.T) {
|
||||||
name := "json"
|
name := "json"
|
||||||
want := "expr-" + name + "-plugin.so"
|
want := "expr-" + name + "-plugin" + SHAREDLIBRARY_EXTENSION
|
||||||
|
|
||||||
if got := makePluginName(name); got != want {
|
if got := makePluginName(name); got != want {
|
||||||
t.Errorf("makePluginName(%q) failed: Got: %q, Want: %q", 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)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|||||||
+8
-1
@@ -9,6 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestStringsParser(t *testing.T) {
|
func TestStringsParser(t *testing.T) {
|
||||||
|
section := "String"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`"uno" + "due"`, `unodue`, nil},
|
/* 1 */ {`"uno" + "due"`, `unodue`, nil},
|
||||||
/* 2 */ {`"uno" + 2`, `uno2`, nil},
|
/* 2 */ {`"uno" + 2`, `uno2`, nil},
|
||||||
@@ -16,6 +17,12 @@ func TestStringsParser(t *testing.T) {
|
|||||||
/* 4 */ {`"uno" * (2+1)`, `unounouno`, nil},
|
/* 4 */ {`"uno" * (2+1)`, `unounouno`, nil},
|
||||||
/* 5 */ {`"abc"[1]`, `b`, nil},
|
/* 5 */ {`"abc"[1]`, `b`, nil},
|
||||||
/* 6 */ {`#"abc"`, int64(3), 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,23 +13,29 @@ type termPriority uint32
|
|||||||
const (
|
const (
|
||||||
priNone termPriority = iota
|
priNone termPriority = iota
|
||||||
priRange
|
priRange
|
||||||
|
priIterOp // map, filter, digest, etc
|
||||||
priBut
|
priBut
|
||||||
priAssign
|
priAssign
|
||||||
|
priInsert
|
||||||
priOr
|
priOr
|
||||||
priAnd
|
priAnd
|
||||||
priNot
|
priNot
|
||||||
priRelational
|
priRelational
|
||||||
priBinary
|
priBitwiseOr
|
||||||
|
priBitwiseAnd
|
||||||
|
priBitwiseNot
|
||||||
priSum
|
priSum
|
||||||
priProduct
|
priProduct
|
||||||
priFraction
|
priFraction
|
||||||
priSelector
|
priSelector
|
||||||
|
priBinShift
|
||||||
priSign
|
priSign
|
||||||
priFact
|
priFact
|
||||||
priIterValue
|
priIterValue
|
||||||
priDefault
|
priDefault
|
||||||
priIncDec
|
priIncDec
|
||||||
priDot
|
priDot
|
||||||
|
priDereference
|
||||||
priValue
|
priValue
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -199,6 +205,10 @@ func (term *term) errIncompatibleType(value any) error {
|
|||||||
term.source(), value, TypeName(value))
|
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) {
|
func (term *term) Errorf(template string, args ...any) (err error) {
|
||||||
err = term.tk.Errorf(template, args...)
|
err = term.tk.Errorf(template, args...)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -79,6 +79,10 @@ func (tk *Token) IsSymbol(sym Symbol) bool {
|
|||||||
return tk.Sym == sym
|
return tk.Sym == sym
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tk *Token) SetSymbol(sym Symbol) {
|
||||||
|
tk.Sym = sym
|
||||||
|
}
|
||||||
|
|
||||||
func (tk *Token) Errorf(template string, args ...any) (err error) {
|
func (tk *Token) Errorf(template string, args ...any) (err error) {
|
||||||
err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", tk.row, tk.col)+template, args...)
|
err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", tk.row, tk.col)+template, args...)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -218,6 +218,15 @@ func ToGoInt(value any, description string) (i int, err error) {
|
|||||||
return
|
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 {
|
func ForAll[T, V any](ts []T, fn func(T) V) []V {
|
||||||
result := make([]V, len(ts))
|
result := make([]V, len(ts))
|
||||||
for i, t := range ts {
|
for i, t := range ts {
|
||||||
|
|||||||
Reference in New Issue
Block a user