Compare commits

...

21 Commits

Author SHA1 Message Date
camoroso 866de759dd file-reader.expr: simpler implementation 2024-06-17 14:07:39 +02:00
camoroso b1d6b6de44 refactored dict's item access 2024-06-17 14:06:33 +02:00
camoroso 7e357eea62 changed to comply new builtin-os-file.go's function names 2024-06-17 09:54:20 +02:00
camoroso d6b4c79736 operator-index.go: dict item access by generic keys implemented 2024-06-17 09:05:23 +02:00
camoroso d066344af8 builtin-os-file.go: changed read and write function names; added fileReadTextAll 2024-06-17 06:59:15 +02:00
camoroso f41dba069e plugin.go: support Liteide and VScode to debug executable file name 2024-06-17 06:57:47 +02:00
camoroso 703ecf6829 Added utility function GetLast() to context 2024-06-17 06:56:32 +02:00
camoroso ba479a1b99 Function call moved from operand-func.go to function.go 2024-06-17 06:54:50 +02:00
camoroso 24e6a293b0 common-params.go: ParamName added 2024-06-12 11:16:57 +02:00
camoroso 28f464c4dc plugins.go: If the main program's file name ends with '.debug', plugins will be loaded from file with name ending with '.debug' 2024-06-12 11:15:31 +02:00
camoroso 9fb611aa20 Formatter option is now composed of two data: flags (lower 16 bits) and indentation size (higher 16 bits).
DictType and ListType formatter use both indent and flag options.
Simple-store now makes a DictType from its data and its ToString() function returns dict.ToString()
2024-06-11 16:32:01 +02:00
camoroso 56d6d06d15 simple-store.go: newline removed after context last brace in ToString() 2024-06-10 20:58:38 +02:00
camoroso d9f7e5b1ad common-errors.go: exported all error functions.
builtin-string.go: renamed all functions from somthingStr() to strSomething()
2024-06-10 20:37:58 +02:00
camoroso 0f54e01ef3 t_scanner_test.go: Test nr 25 changed because now single-quotes can enclose strings 2024-06-10 20:36:03 +02:00
camoroso 63f5db00b3 all test file on builtin functions have been renamed from t_func-*_test.go to t_builtin-*_test.go 2024-06-10 20:34:11 +02:00
camoroso 9745a5d909 utils.go: toInt() -> ToInt(); toBool() -> ToBool() 2024-06-10 19:03:39 +02:00
camoroso 0bb4c96481 scanner.go: Strings can be enclosed between two single-quotes too 2024-06-10 18:52:13 +02:00
camoroso 1757298eb4 formatter.go: typeName() renamed as TypeName() 2024-06-10 09:37:27 +02:00
camoroso 54041552d4 dict-type.go: added MakeDict() constructor 2024-06-10 09:35:48 +02:00
camoroso 5302907dcf external plugins can now request for dependencies 2024-06-09 17:12:57 +02:00
camoroso eb4b17f078 moved all test expression files in the test-resources forlder 2024-06-09 16:02:07 +02:00
50 changed files with 615 additions and 393 deletions
+14
View File
@@ -42,3 +42,17 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
result, err = functor.expr.Eval(ctx) result, err = functor.expr.Eval(ctx)
return return
} }
// func CallExprFunction(parentCtx ExprContext, funcName string, params ...any) (v any, err error) {
// ctx := cloneContext(parentCtx)
// ctx.SetParent(parentCtx)
// if err == nil {
// if err = checkFunctionCall(ctx, funcName, &params); err == nil {
// if v, err = ctx.Call(funcName, params); err == nil {
// exportObjects(parentCtx, ctx)
// }
// }
// }
// return
// }
+6 -6
View File
@@ -67,7 +67,7 @@ func boolFunc(ctx ExprContext, name string, args []any) (result any, err error)
case string: case string:
result = len(v) > 0 result = len(v) > 0
default: default:
err = errCantConvert(name, v, "bool") err = ErrCantConvert(name, v, "bool")
} }
return return
} }
@@ -90,7 +90,7 @@ func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
result = int64(i) result = int64(i)
} }
default: default:
err = errCantConvert(name, v, "int") err = ErrCantConvert(name, v, "int")
} }
return return
} }
@@ -115,7 +115,7 @@ func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
case *FractionType: case *FractionType:
result = v.toFloat() result = v.toFloat()
default: default:
err = errCantConvert(name, v, "float") err = ErrCantConvert(name, v, "float")
} }
return return
} }
@@ -127,9 +127,9 @@ func fractFunc(ctx ExprContext, name string, args []any) (result any, err error)
if len(args) > 1 { if len(args) > 1 {
var ok bool var ok bool
if den, ok = args[1].(int64); !ok { if den, ok = args[1].(int64); !ok {
err = errExpectedGot(name, "integer", args[1]) err = ErrExpectedGot(name, "integer", args[1])
} else if den == 0 { } else if den == 0 {
err = errFuncDivisionByZero(name) err = ErrFuncDivisionByZero(name)
} }
} }
if err == nil { if err == nil {
@@ -148,7 +148,7 @@ func fractFunc(ctx ExprContext, name string, args []any) (result any, err error)
case *FractionType: case *FractionType:
result = v result = v
default: default:
err = errCantConvert(name, v, "float") err = ErrCantConvert(name, v, "float")
} }
return return
} }
+55 -19
View File
@@ -68,7 +68,7 @@ func createFileFunc(ctx ExprContext, name string, args []any) (result any, err e
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)} result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
} }
} else { } else {
err = errMissingFilePath("createFile") err = errMissingFilePath(name)
} }
return return
} }
@@ -80,7 +80,7 @@ func openFileFunc(ctx ExprContext, name string, args []any) (result any, err err
result = &osReader{fh: fh, reader: bufio.NewReader(fh)} result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
} }
} else { } else {
err = errMissingFilePath("openFile") err = errMissingFilePath(name)
} }
return return
} }
@@ -92,7 +92,7 @@ func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err e
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)} result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
} }
} else { } else {
err = errMissingFilePath("openFile") err = errMissingFilePath(name)
} }
return return
} }
@@ -118,13 +118,13 @@ func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err er
} }
} }
if err == nil && (handle == nil || invalidFileHandle != nil) { if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle("closeFileFunc", handle) err = errInvalidFileHandle(name, handle)
} }
result = err == nil result = err == nil
return return
} }
func writeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { func fileWriteTextFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var handle osHandle var handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
@@ -142,12 +142,12 @@ func writeFileFunc(ctx ExprContext, name string, args []any) (result any, err er
} }
if err == nil && (handle == nil || invalidFileHandle != nil) { if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle("writeFileFunc", invalidFileHandle) err = errInvalidFileHandle(name, invalidFileHandle)
} }
return return
} }
func readFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { func fileReadTextFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var handle osHandle var handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
@@ -165,46 +165,82 @@ func readFileFunc(ctx ExprContext, name string, args []any) (result any, err err
limit = s[0] limit = s[0]
} }
if v, err = r.reader.ReadString(limit); err == nil { v, err = r.reader.ReadString(limit)
if len(v) > 0 && v[len(v)-1] == limit { if err == io.EOF {
err = nil
}
if len(v) > 0 {
if v[len(v)-1] == limit {
result = v[0 : len(v)-1] result = v[0 : len(v)-1]
} else { } else {
result = v result = v
} }
} }
if err == io.EOF {
err = nil
}
} else { } else {
invalidFileHandle = handle invalidFileHandle = handle
} }
} }
if err == nil && (handle == nil || invalidFileHandle != nil) { if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle("readFileFunc", invalidFileHandle) err = errInvalidFileHandle(name, invalidFileHandle)
}
return
}
func fileReadTextAllFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var handle osHandle
var invalidFileHandle any
var ok bool
result = nil
if handle, ok = args[0].(osHandle); !ok || args[0] == nil {
invalidFileHandle = args[0]
}
if handle != nil {
if r, ok := handle.(*osReader); ok {
var b []byte
b, err = io.ReadAll(r.reader)
result = string(b)
} else {
invalidFileHandle = handle
}
}
if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle(name, invalidFileHandle)
} }
return return
} }
func ImportOsFuncs(ctx ExprContext) { func ImportOsFuncs(ctx ExprContext) {
ctx.RegisterFunc("openFile", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{ ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath), NewFuncParam(ParamFilepath),
}) })
ctx.RegisterFunc("appendFile", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath), NewFuncParam(ParamFilepath),
}) })
ctx.RegisterFunc("createFile", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath), NewFuncParam(ParamFilepath),
}) })
ctx.RegisterFunc("writeFile", NewGolangFunctor(writeFileFunc), TypeInt, []ExprFuncParam{
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(TypeHandle),
})
ctx.RegisterFunc("fileWriteText", NewGolangFunctor(fileWriteTextFunc), TypeInt, []ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(TypeHandle),
NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""), NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""),
}) })
ctx.RegisterFunc("readFile", NewGolangFunctor(readFileFunc), TypeString, []ExprFuncParam{
ctx.RegisterFunc("fileReadText", NewGolangFunctor(fileReadTextFunc), TypeString, []ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(TypeHandle),
NewFuncParamFlagDef("limitCh", PfDefault, "\n"), NewFuncParamFlagDef("limitCh", PfDefault, "\n"),
}) })
ctx.RegisterFunc("closeFile", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(TypeHandle),
}) })
} }
+16 -16
View File
@@ -21,7 +21,7 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
if s, ok := v.(string); ok { if s, ok := v.(string); ok {
sb.WriteString(s) sb.WriteString(s)
} else { } else {
err = errExpectedGot(funcName, TypeString, v) err = ErrExpectedGot(funcName, TypeString, v)
return return
} }
} }
@@ -45,13 +45,13 @@ func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err erro
} else if it, ok := args[1].(Iterator); ok { } else if it, ok := args[1].(Iterator); ok {
result, err = doJoinStr(name, sep, it) result, err = doJoinStr(name, sep, it)
} else { } else {
err = errInvalidParameterValue(name, ParamParts, args[1]) err = ErrInvalidParameterValue(name, ParamParts, args[1])
} }
} else { } else {
result, err = doJoinStr(name, sep, NewArrayIterator(args[1:])) result, err = doJoinStr(name, sep, NewArrayIterator(args[1:]))
} }
} else { } else {
err = errWrongParamType(name, ParamSeparator, TypeString, args[0]) err = ErrWrongParamType(name, ParamSeparator, TypeString, args[0])
} }
return return
} }
@@ -63,14 +63,14 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error
var ok bool var ok bool
if source, ok = args[0].(string); !ok { if source, ok = args[0].(string); !ok {
return nil, errWrongParamType(name, ParamSource, TypeString, args[0]) return nil, ErrWrongParamType(name, ParamSource, TypeString, args[0])
} }
if start, err = toInt(args[1], name+"()"); err != nil { if start, err = ToInt(args[1], name+"()"); err != nil {
return return
} }
if count, err = toInt(args[2], name+"()"); err != nil { if count, err = ToInt(args[2], name+"()"); err != nil {
return return
} }
@@ -91,7 +91,7 @@ func trimStrFunc(ctx ExprContext, name string, args []any) (result any, err erro
var ok bool var ok bool
if source, ok = args[0].(string); !ok { if source, ok = args[0].(string); !ok {
return nil, errWrongParamType(name, ParamSource, TypeString, args[0]) return nil, ErrWrongParamType(name, ParamSource, TypeString, args[0])
} }
result = strings.TrimSpace(source) result = strings.TrimSpace(source)
return return
@@ -104,7 +104,7 @@ func startsWithStrFunc(ctx ExprContext, name string, args []any) (result any, er
result = false result = false
if source, ok = args[0].(string); !ok { if source, ok = args[0].(string); !ok {
return result, errWrongParamType(name, ParamSource, TypeString, args[0]) return result, ErrWrongParamType(name, ParamSource, TypeString, args[0])
} }
for i, targetSpec := range args[1:] { for i, targetSpec := range args[1:] {
if target, ok := targetSpec.(string); ok { if target, ok := targetSpec.(string); ok {
@@ -127,7 +127,7 @@ func endsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err
result = false result = false
if source, ok = args[0].(string); !ok { if source, ok = args[0].(string); !ok {
return result, errWrongParamType(name, ParamSource, TypeString, args[0]) return result, ErrWrongParamType(name, ParamSource, TypeString, args[0])
} }
for i, targetSpec := range args[1:] { for i, targetSpec := range args[1:] {
if target, ok := targetSpec.(string); ok { if target, ok := targetSpec.(string); ok {
@@ -150,7 +150,7 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
var ok bool var ok bool
if source, ok = args[0].(string); !ok { if source, ok = args[0].(string); !ok {
return result, errWrongParamType(name, ParamSource, TypeString, args[0]) return result, ErrWrongParamType(name, ParamSource, TypeString, args[0])
} }
if sep, ok = args[1].(string); !ok { if sep, ok = args[1].(string); !ok {
@@ -182,34 +182,34 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
// Import above functions in the context // Import above functions in the context
func ImportStringFuncs(ctx ExprContext) { func ImportStringFuncs(ctx ExprContext) {
ctx.RegisterFunc("joinStr", NewGolangFunctor(joinStrFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("strJoin", NewGolangFunctor(joinStrFunc), TypeString, []ExprFuncParam{
NewFuncParam(ParamSeparator), NewFuncParam(ParamSeparator),
NewFuncParamFlag(ParamItem, PfRepeat), NewFuncParamFlag(ParamItem, PfRepeat),
}) })
ctx.RegisterFunc("subStr", NewGolangFunctor(subStrFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("strSub", NewGolangFunctor(subStrFunc), TypeString, []ExprFuncParam{
NewFuncParam(ParamSource), NewFuncParam(ParamSource),
NewFuncParamFlagDef(ParamStart, PfDefault, int64(0)), NewFuncParamFlagDef(ParamStart, PfDefault, int64(0)),
NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)), NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)),
}) })
ctx.RegisterFunc("splitStr", NewGolangFunctor(splitStrFunc), "list of "+TypeString, []ExprFuncParam{ ctx.RegisterFunc("strSplit", NewGolangFunctor(splitStrFunc), "list of "+TypeString, []ExprFuncParam{
NewFuncParam(ParamSource), NewFuncParam(ParamSource),
NewFuncParamFlagDef(ParamSeparator, PfDefault, ""), NewFuncParamFlagDef(ParamSeparator, PfDefault, ""),
NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)), NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)),
}) })
ctx.RegisterFunc("trimStr", NewGolangFunctor(trimStrFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("strTrim", NewGolangFunctor(trimStrFunc), TypeString, []ExprFuncParam{
NewFuncParam(ParamSource), NewFuncParam(ParamSource),
}) })
ctx.RegisterFunc("startsWithStr", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{ ctx.RegisterFunc("strStartsWith", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(ParamSource), NewFuncParam(ParamSource),
NewFuncParam(ParamPrefix), NewFuncParam(ParamPrefix),
NewFuncParamFlag("other "+ParamPrefix, PfRepeat), NewFuncParamFlag("other "+ParamPrefix, PfRepeat),
}) })
ctx.RegisterFunc("endsWithStr", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{ ctx.RegisterFunc("strEndsWith", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(ParamSource), NewFuncParam(ParamSource),
NewFuncParam(ParamSuffix), NewFuncParam(ParamSuffix),
NewFuncParamFlag("other "+ParamSuffix, PfRepeat), NewFuncParamFlag("other "+ParamSuffix, PfRepeat),
+14 -14
View File
@@ -8,7 +8,7 @@ import (
"fmt" "fmt"
) )
func errTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error) { func ErrTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error) {
if maxArgs < 0 { if maxArgs < 0 {
err = fmt.Errorf("%s(): too few params -- expected %d or more, got %d", funcName, minArgs, argCount) err = fmt.Errorf("%s(): too few params -- expected %d or more, got %d", funcName, minArgs, argCount)
} else { } else {
@@ -17,39 +17,39 @@ func errTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error
return return
} }
func errTooMuchParams(funcName string, maxArgs, argCount int) (err error) { func ErrTooMuchParams(funcName string, maxArgs, argCount int) (err error) {
err = fmt.Errorf("%s(): too much params -- expected %d, got %d", funcName, maxArgs, argCount) err = fmt.Errorf("%s(): too much params -- expected %d, got %d", funcName, maxArgs, argCount)
return return
} }
// --- General errors // --- General errors
func errCantConvert(funcName string, value any, kind string) error { func ErrCantConvert(funcName string, value any, kind string) error {
return fmt.Errorf("%s(): can't convert %s to %s", funcName, typeName(value), kind) return fmt.Errorf("%s(): can't convert %s to %s", funcName, TypeName(value), kind)
} }
func errExpectedGot(funcName string, kind string, value any) error { func ErrExpectedGot(funcName string, kind string, value any) error {
return fmt.Errorf("%s() expected %s, got %s (%v)", funcName, kind, typeName(value), value) return fmt.Errorf("%s(): expected %s, got %s (%v)", funcName, kind, TypeName(value), value)
} }
func errFuncDivisionByZero(funcName string) error { func ErrFuncDivisionByZero(funcName string) error {
return fmt.Errorf("%s(): division by zero", funcName) return fmt.Errorf("%s(): division by zero", funcName)
} }
func errDivisionByZero() error { func ErrDivisionByZero() error {
return fmt.Errorf("division by zero") return fmt.Errorf("division by zero")
} }
// --- Parameter errors // --- Parameter errors
func errMissingRequiredParameter(funcName, paramName string) error { func ErrMissingRequiredParameter(funcName, paramName string) error {
return fmt.Errorf("%s() missing required parameter %q", funcName, paramName) return fmt.Errorf("%s(): missing required parameter %q", funcName, paramName)
} }
func errInvalidParameterValue(funcName, paramName string, paramValue any) error { func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error {
return fmt.Errorf("%s() invalid value %s (%v) for parameter %q", funcName, typeName(paramValue), paramValue, paramName) return fmt.Errorf("%s(): invalid value %s (%v) for parameter %q", funcName, TypeName(paramValue), paramValue, paramName)
} }
func errWrongParamType(funcName, paramName, paramType string, paramValue any) error { func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
return fmt.Errorf("%s() the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, typeName(paramValue), paramValue) return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue)
} }
+1
View File
@@ -15,6 +15,7 @@ const (
ParamStart = "start" ParamStart = "start"
ParamEnd = "end" ParamEnd = "end"
ParamValue = "value" ParamValue = "value"
ParamName = "name"
ParamEllipsis = "..." ParamEllipsis = "..."
ParamFilepath = "filepath" ParamFilepath = "filepath"
ParamDirpath = "dirpath" ParamDirpath = "dirpath"
+6
View File
@@ -41,3 +41,9 @@ func exportObjects(destCtx, sourceCtx ExprContext) {
} }
} }
} }
func exportObjectsToParent(sourceCtx ExprContext) {
if parentCtx := sourceCtx.GetParent(); parentCtx != nil {
exportObjects(parentCtx, sourceCtx)
}
}
+1
View File
@@ -40,6 +40,7 @@ type ExprContext interface {
SetParent(ctx ExprContext) SetParent(ctx ExprContext)
GetParent() (ctx ExprContext) GetParent() (ctx ExprContext)
GetVar(varName string) (value any, exists bool) GetVar(varName string) (value any, exists bool)
GetLast() any
SetVar(varName string, value any) SetVar(varName string, value any)
UnsafeSetVar(varName string, value any) UnsafeSetVar(varName string, value any)
EnumVars(func(name string) (accept bool)) (varNames []string) EnumVars(func(name string) (accept bool)) (varNames []string)
+50 -29
View File
@@ -12,6 +12,12 @@ import (
type DictType map[any]any type DictType map[any]any
func MakeDict() (dict *DictType) {
d := make(DictType)
dict = &d
return
}
func newDict(dictAny map[any]*term) (dict *DictType) { func newDict(dictAny map[any]*term) (dict *DictType) {
var d DictType var d DictType
if dictAny != nil { if dictAny != nil {
@@ -26,42 +32,52 @@ func newDict(dictAny map[any]*term) (dict *DictType) {
return return
} }
func (dict *DictType) toMultiLine(sb *strings.Builder, indent int) { func (dict *DictType) toMultiLine(sb *strings.Builder, opt FmtOpt) {
sb.WriteString(strings.Repeat("\t", indent)) indent := GetFormatIndent(opt)
sb.WriteString("{\n") flags := GetFormatFlags(opt)
//sb.WriteString(strings.Repeat(" ", indent))
sb.WriteByte('{')
first := true if len(*dict) > 0 {
for name, value := range *dict { innerOpt := MakeFormatOptions(flags, indent+1)
if first { nest := strings.Repeat(" ", indent+1)
first = false sb.WriteByte('\n')
} else {
sb.WriteByte(',')
sb.WriteByte('\n')
}
sb.WriteString(strings.Repeat("\t", indent+1)) first := true
if key, ok := name.(string); ok { for name, value := range *dict {
sb.WriteString(string('"') + key + string('"')) if first {
} else { first = false
sb.WriteString(fmt.Sprintf("%v", name)) } else {
} sb.WriteByte(',')
sb.WriteString(": ") sb.WriteByte('\n')
if f, ok := value.(Formatter); ok { }
sb.WriteString(f.ToString(MultiLine))
} else if _, ok = value.(Functor); ok { sb.WriteString(nest)
sb.WriteString("func(){}") if key, ok := name.(string); ok {
} else { sb.WriteString(string('"') + key + string('"'))
sb.WriteString(fmt.Sprintf("%v", value)) } else {
sb.WriteString(fmt.Sprintf("%v", name))
}
sb.WriteString(": ")
if f, ok := value.(Formatter); ok {
sb.WriteString(f.ToString(innerOpt))
} else if _, ok = value.(Functor); ok {
sb.WriteString("func(){}")
} else {
sb.WriteString(fmt.Sprintf("%v", value))
}
} }
sb.WriteByte('\n')
sb.WriteString(strings.Repeat(" ", indent))
} }
sb.WriteString(strings.Repeat("\t", indent)) sb.WriteString("}")
sb.WriteString("\n}")
} }
func (dict *DictType) ToString(opt FmtOpt) string { func (dict *DictType) ToString(opt FmtOpt) string {
var sb strings.Builder var sb strings.Builder
if opt&MultiLine != 0 { flags := GetFormatFlags(opt)
dict.toMultiLine(&sb, 0) if flags&MultiLine != 0 {
dict.toMultiLine(&sb, opt)
} else { } else {
sb.WriteByte('{') sb.WriteByte('{')
first := true first := true
@@ -124,7 +140,12 @@ func (dict *DictType) merge(second *DictType) {
} }
func (dict *DictType) setItem(key any, value any) (err error) { func (dict *DictType) setItem(key any, value any) (err error) {
(*dict)[key]=value (*dict)[key] = value
return return
} }
////////////////
type DictFormat interface {
ToDict() *DictType
}
-33
View File
@@ -1,33 +0,0 @@
builtin ["os.file", "base"];
readInt=func(fh){
line=readFile(fh);
line ? [nil] {nil} :: {int(line)}
};
ds={
"init":func(filename){
fh=openFile(filename);
fh ? [nil] {nil} :: { @current=readInt(fh); @prev=@current };
fh
},
"current":func(){
prev
},
"next":func(fh){
current ?
[nil] {current}
:: {@prev=current; @current=readInt(fh) but current}
},
"clean":func(fh){
closeFile(fh)
}
}
//;f=$(ds, "int.list")
/*
;f++
;f++
;f++
*/
//;add(f)
+14 -2
View File
@@ -6,7 +6,7 @@ package expr
import "fmt" import "fmt"
type FmtOpt uint16 type FmtOpt uint32 // lower 16 bits hold a bit-mask, higher 16 bits hold an indentation number
const ( const (
TTY FmtOpt = 1 << iota TTY FmtOpt = 1 << iota
@@ -30,6 +30,18 @@ func TruncateString(s string) (trunc string) {
return return
} }
func MakeFormatOptions(flags FmtOpt, indent int) FmtOpt {
return FmtOpt(indent<<16) | flags
}
func GetFormatFlags(opt FmtOpt) FmtOpt {
return opt & 0xFFFF
}
func GetFormatIndent(opt FmtOpt) int {
return int(opt >> 16)
}
type Formatter interface { type Formatter interface {
ToString(options FmtOpt) string ToString(options FmtOpt) string
} }
@@ -51,7 +63,7 @@ type Typer interface {
TypeName() string TypeName() string
} }
func typeName(v any) (name string) { func TypeName(v any) (name string) {
if v == nil { if v == nil {
name = "nil" name = "nil"
} else if typer, ok := v.(Typer); ok { } else if typer, ok := v.(Typer); ok {
+4 -4
View File
@@ -70,7 +70,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
} }
for _, c := range dec[0:lsd] { for _, c := range dec[0:lsd] {
if c < '0' || c > '9' { if c < '0' || c > '9' {
return nil, errExpectedGot("fract", "digit", c) return nil, ErrExpectedGot("fract", "digit", c)
} }
num = num*10 + int64(c-'0') num = num*10 + int64(c-'0')
den = den * 10 den = den * 10
@@ -81,7 +81,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
mul := int64(1) mul := int64(1)
for _, c := range subParts[0] { for _, c := range subParts[0] {
if c < '0' || c > '9' { if c < '0' || c > '9' {
return nil, errExpectedGot("fract", "digit", c) return nil, ErrExpectedGot("fract", "digit", c)
} }
num = num*10 + int64(c-'0') num = num*10 + int64(c-'0')
sub = sub*10 + int64(c-'0') sub = sub*10 + int64(c-'0')
@@ -94,7 +94,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
p := subParts[1][0 : len(subParts[1])-1] p := subParts[1][0 : len(subParts[1])-1]
for _, c := range p { for _, c := range p {
if c < '0' || c > '9' { if c < '0' || c > '9' {
return nil, errExpectedGot("fract", "digit", c) return nil, ErrExpectedGot("fract", "digit", c)
} }
num = num*10 + int64(c-'0') num = num*10 + int64(c-'0')
den = den*10 + 9 den = den*10 + 9
@@ -212,7 +212,7 @@ func anyToFract(v any) (f *FractionType, err error) {
} }
} }
if f == nil { if f == nil {
err = errExpectedGot("fract", TypeFraction, v) err = ErrExpectedGot("fract", TypeFraction, v)
} }
return return
} }
+41 -2
View File
@@ -21,7 +21,7 @@ func (functor *baseFunctor) ToString(opt FmtOpt) (s string) {
if functor.info != nil { if functor.info != nil {
s = functor.info.ToString(opt) s = functor.info.ToString(opt)
} else { } else {
s = "func() {<body>}" s = "func() {}"
} }
return s return s
} }
@@ -180,7 +180,7 @@ func (info *funcInfo) ToString(opt FmtOpt) string {
} else { } else {
sb.WriteString(TypeAny) sb.WriteString(TypeAny)
} }
sb.WriteString(" {<body>}") sb.WriteString(" {}")
return sb.String() return sb.String()
} }
@@ -199,3 +199,42 @@ func (info *funcInfo) MaxArgs() int {
func (info *funcInfo) Functor() Functor { func (info *funcInfo) Functor() Functor {
return info.functor return info.functor
} }
// ----- Call a function ---
func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err error) {
if info, exists, owner := GetFuncInfo(ctx, name); exists {
passedCount := len(*varParams)
if info.MinArgs() > passedCount {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
}
for i, p := range info.Params() {
if i >= passedCount {
if !p.IsDefault() {
break
}
*varParams = append(*varParams, p.DefaultValue())
}
}
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varParams) {
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varParams))
}
if err == nil && owner != ctx {
ctx.RegisterFuncInfo(info)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunction(parentCtx ExprContext, name string, params []any) (result any, err error) {
ctx := cloneContext(parentCtx)
ctx.SetParent(parentCtx)
if err = checkFunctionCall(ctx, name, &params); err == nil {
result, err = ctx.Call(name, params)
exportObjectsToParent(ctx)
}
return
}
+1 -1
View File
@@ -20,7 +20,7 @@ const (
func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) { func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) {
if !(IsString(paramValue) /*|| isList(paramValue)*/) { if !(IsString(paramValue) /*|| isList(paramValue)*/) {
err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, typeName(paramValue)) err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, TypeName(paramValue))
} }
return return
} }
+3 -3
View File
@@ -26,21 +26,21 @@ func NewListIterator(list *ListType, args []any) (it *ListIterator) {
} }
it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1} it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1}
if argc >= 1 { if argc >= 1 {
if i, err := toInt(args[0], "start index"); err == nil { if i, err := ToInt(args[0], "start index"); err == nil {
if i < 0 { if i < 0 {
i = listLen + i i = listLen + i
} }
it.start = i it.start = i
} }
if argc >= 2 { if argc >= 2 {
if i, err := toInt(args[1], "stop index"); err == nil { if i, err := ToInt(args[1], "stop index"); err == nil {
if i < 0 { if i < 0 {
i = listLen + i i = listLen + i
} }
it.stop = i it.stop = i
} }
if argc >= 3 { if argc >= 3 {
if i, err := toInt(args[2], "step"); err == nil { if i, err := ToInt(args[2], "step"); err == nil {
if i < 0 { if i < 0 {
i = -i i = -i
} }
+17 -6
View File
@@ -52,16 +52,24 @@ 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)
flags := GetFormatFlags(opt)
var sb strings.Builder var sb strings.Builder
sb.WriteByte('[') sb.WriteByte('[')
if len(*ls) > 0 { if len(*ls) > 0 {
if opt&MultiLine != 0 { innerOpt := MakeFormatOptions(flags, indent+1)
sb.WriteString("\n ") nest := strings.Repeat(" ", indent+1)
if flags&MultiLine != 0 {
sb.WriteByte('\n')
sb.WriteString(nest)
} }
for i, item := range []any(*ls) { for i, item := range []any(*ls) {
if i > 0 { if i > 0 {
if opt&MultiLine != 0 { if flags&MultiLine != 0 {
sb.WriteString(",\n ") sb.WriteString(",\n")
sb.WriteString(nest)
} else { } else {
sb.WriteString(", ") sb.WriteString(", ")
} }
@@ -70,17 +78,20 @@ func (ls *ListType) ToString(opt FmtOpt) (s string) {
sb.WriteByte('"') sb.WriteByte('"')
sb.WriteString(s) sb.WriteString(s)
sb.WriteByte('"') sb.WriteByte('"')
} else if formatter, ok := item.(Formatter); ok {
sb.WriteString(formatter.ToString(innerOpt))
} else { } else {
sb.WriteString(fmt.Sprintf("%v", item)) sb.WriteString(fmt.Sprintf("%v", item))
} }
} }
if opt&MultiLine != 0 { if flags&MultiLine != 0 {
sb.WriteByte('\n') sb.WriteByte('\n')
sb.WriteString(strings.Repeat(" ", indent))
} }
} }
sb.WriteByte(']') sb.WriteByte(']')
s = sb.String() s = sb.String()
if opt&Truncate != 0 && len(s) > TruncateSize { if flags&Truncate != 0 && len(s) > TruncateSize {
s = TruncateString(s) s = TruncateString(s)
} }
return return
+2 -35
View File
@@ -6,7 +6,6 @@ package expr
import ( import (
"errors" "errors"
"fmt"
) )
// -------- function call term // -------- function call term
@@ -22,35 +21,7 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
} }
// -------- eval func call // -------- eval func call
func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err error) { func evalFuncCall(ctx ExprContext, self *term) (v any, err error) {
if info, exists, owner := GetFuncInfo(ctx, name); exists {
passedCount := len(*varParams)
if info.MinArgs() > passedCount {
err = errTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
}
for i, p := range info.Params() {
if i >= passedCount {
if !p.IsDefault() {
break
}
*varParams = append(*varParams, p.DefaultValue())
}
}
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varParams) {
err = errTooMuchParams(name, info.MaxArgs(), len(*varParams))
}
if err == nil && owner != ctx {
ctx.RegisterFuncInfo(info)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
ctx := cloneContext(parentCtx)
ctx.SetParent(parentCtx)
name, _ := self.tk.Value.(string) name, _ := self.tk.Value.(string)
params := make([]any, len(self.children), len(self.children)+5) params := make([]any, len(self.children), len(self.children)+5)
for i, tree := range self.children { for i, tree := range self.children {
@@ -62,11 +33,7 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
} }
if err == nil { if err == nil {
if err = checkFunctionCall(ctx, name, &params); err == nil { v, err = CallFunction(ctx, name, params)
if v, err = ctx.Call(name, params); err == nil {
exportObjects(parentCtx, ctx)
}
}
} }
return return
} }
+2 -2
View File
@@ -28,7 +28,7 @@ func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, va
if keyListValue, err = keyListTerm.compute(ctx); err != nil { if keyListValue, err = keyListTerm.compute(ctx); err != nil {
return return
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 { } else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 {
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, typeName(keyListValue)) err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue))
return return
} }
if keyValue = (*keyList)[0]; keyValue == nil { if keyValue = (*keyList)[0]; keyValue == nil {
@@ -41,7 +41,7 @@ func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, va
if index, ok := keyValue.(int64); ok { if index, ok := keyValue.(int64); ok {
err = collection.setItem(index, value) err = collection.setItem(index, value)
} else { } else {
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, typeName(keyValue)) err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue))
} }
case *DictType: case *DictType:
err = collection.setItem(keyValue, value) err = collection.setItem(keyValue, value)
+9 -9
View File
@@ -25,7 +25,7 @@ func evalNot(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if b, ok := toBool(rightValue); ok { if b, ok := ToBool(rightValue); ok {
v = !b v = !b
} else { } else {
err = self.errIncompatibleType(rightValue) err = self.errIncompatibleType(rightValue)
@@ -65,8 +65,8 @@ func evalAndWithoutShortcut(ctx ExprContext, self *term) (v any, err error) {
return return
} }
leftBool, lok = toBool(leftValue) leftBool, lok = ToBool(leftValue)
rightBool, rok = toBool(rightValue) rightBool, rok = ToBool(rightValue)
if lok && rok { if lok && rok {
v = leftBool && rightBool v = leftBool && rightBool
@@ -87,13 +87,13 @@ func evalAndWithShortcut(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if leftBool, lok := toBool(leftValue); !lok { if leftBool, lok := ToBool(leftValue); !lok {
err = fmt.Errorf("got %T as left operand type of 'and' operator, it must be bool", leftBool) err = fmt.Errorf("got %T as left operand type of 'and' operator, it must be bool", leftBool)
return return
} else if !leftBool { } else if !leftBool {
v = false v = false
} else if rightValue, err = self.children[1].compute(ctx); err == nil { } else if rightValue, err = self.children[1].compute(ctx); err == nil {
if rightBool, rok := toBool(rightValue); rok { if rightBool, rok := ToBool(rightValue); rok {
v = rightBool v = rightBool
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.errIncompatibleTypes(leftValue, rightValue)
@@ -134,8 +134,8 @@ func evalOrWithoutShortcut(ctx ExprContext, self *term) (v any, err error) {
return return
} }
leftBool, lok = toBool(leftValue) leftBool, lok = ToBool(leftValue)
rightBool, rok = toBool(rightValue) rightBool, rok = ToBool(rightValue)
if lok && rok { if lok && rok {
v = leftBool || rightBool v = leftBool || rightBool
@@ -156,13 +156,13 @@ func evalOrWithShortcut(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if leftBool, lok := toBool(leftValue); !lok { if leftBool, lok := ToBool(leftValue); !lok {
err = fmt.Errorf("got %T as left operand type of 'or' operator, it must be bool", leftBool) err = fmt.Errorf("got %T as left operand type of 'or' operator, it must be bool", leftBool)
return return
} else if leftBool { } else if leftBool {
v = true v = true
} else if rightValue, err = self.children[1].compute(ctx); err == nil { } else if rightValue, err = self.children[1].compute(ctx); err == nil {
if rightBool, rok := toBool(rightValue); rok { if rightBool, rok := ToBool(rightValue); rok {
v = rightBool v = rightBool
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.errIncompatibleTypes(leftValue, rightValue)
+1 -1
View File
@@ -41,7 +41,7 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) {
break break
} }
} else { } else {
err = self.Errorf("expected string at item nr %d, got %s", it.Index()+1, typeName(moduleSpec)) err = self.Errorf("expected string at item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break break
} }
} }
+3 -1
View File
@@ -31,7 +31,9 @@ func evalContextValue(ctx ExprContext, self *term) (v any, err error) {
} }
if sourceCtx != nil { if sourceCtx != nil {
if formatter, ok := sourceCtx.(Formatter); ok { if formatter, ok := sourceCtx.(DictFormat); ok {
v = formatter.ToDict()
} else if formatter, ok := sourceCtx.(Formatter); ok {
v = formatter.ToString(0) v = formatter.ToString(0)
} else { } else {
// keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' }) // keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
+27 -3
View File
@@ -23,7 +23,7 @@ func verifyKey(indexTerm *term, indexList *ListType) (index any, err error) {
func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int, err error) { func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int, err error) {
var v int var v int
if v, err = toInt((*indexList)[0], "index expression"); err == nil { if v, err = ToInt((*indexList)[0], "index expression"); err == nil {
if v < 0 && v >= -maxValue { if v < 0 && v >= -maxValue {
v = maxValue + v v = maxValue + v
} }
@@ -87,13 +87,14 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
v = string(unboxedValue[index]) v = string(unboxedValue[index])
} }
case *DictType: case *DictType:
var ok bool /* var ok bool
var indexValue any var indexValue any
if indexValue, err = verifyKey(indexTerm, indexList); err == nil { if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
if v, ok = (*unboxedValue)[indexValue]; !ok { if v, ok = (*unboxedValue)[indexValue]; !ok {
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue) err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
} }
} } */
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
default: default:
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.errIncompatibleTypes(leftValue, rightValue)
} }
@@ -113,6 +114,29 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
default: default:
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.errIncompatibleTypes(leftValue, rightValue)
} }
} else if IsDict(leftValue) {
d := leftValue.(*DictType)
/* var ok bool
var indexValue any
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
if v, ok = (*d)[indexValue]; !ok {
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
}
}*/
v, err = getDictItem(d, indexTerm, indexList, rightValue)
}
return
}
func getDictItem(d *DictType, indexTerm *term, indexList *ListType, rightValue any) (v any, err error) {
var ok bool
var indexValue any
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
if v, ok = (*d)[indexValue]; !ok {
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
}
} }
return return
} }
+1 -1
View File
@@ -36,7 +36,7 @@ func evalLength(ctx ExprContext, self *term) (v any, err error) {
} else if it, ok := childValue.(Iterator); ok { } else if it, ok := childValue.(Iterator); ok {
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) { if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) {
count, _ := extIt.CallOperation(countName, nil) count, _ := extIt.CallOperation(countName, nil)
v, _ = toInt(count, "") v, _ = ToInt(count, "")
} else { } else {
v = int64(it.Index() + 1) v = int64(it.Index() + 1)
} }
+2 -46
View File
@@ -5,9 +5,7 @@
package expr package expr
import ( import (
"fmt"
"io" "io"
"plugin"
) )
//-------- plugin term //-------- plugin term
@@ -22,48 +20,6 @@ func newPluginTerm(tk *Token) (inst *term) {
} }
} }
func importPlugin(ctx ExprContext, dirList []string, name string) (err error) {
var filePath string
var p *plugin.Plugin
var sym plugin.Symbol
var moduleName string
var importFunc func(ctx ExprContext)
var ok bool
decoratedName := fmt.Sprintf("expr-%s-plugin.so", name)
if filePath, err = makeFilepath(decoratedName, dirList); err != nil {
return
}
if p, err = plugin.Open(filePath); err != nil {
return
}
if sym, err = p.Lookup("MODULE_NAME"); err != nil {
return
}
if moduleName = *sym.(*string); moduleName == "" {
err = fmt.Errorf("plugin %q does not provide a valid module name", decoratedName)
return
}
if sym, err = p.Lookup("ImportFuncs"); err != nil {
return
}
if importFunc, ok = sym.(func(ExprContext)); !ok {
err = fmt.Errorf("plugin %q does not provide a valid import function", decoratedName)
return
}
registerPlugin(moduleName, p)
importFunc(globalCtx)
return
}
func evalPlugin(ctx ExprContext, self *term) (v any, err error) { func evalPlugin(ctx ExprContext, self *term) (v any, err error) {
var childValue any var childValue any
var moduleSpec any var moduleSpec any
@@ -77,12 +33,12 @@ func evalPlugin(ctx ExprContext, self *term) (v any, err error) {
it := NewAnyIterator(childValue) it := NewAnyIterator(childValue)
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(ctx, dirList, module); err != nil { if err = importPlugin(dirList, module); err != nil {
break break
} }
count++ count++
} else { } else {
err = self.Errorf("expected string as item nr %d, got %s", it.Index()+1, typeName(moduleSpec)) err = self.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break break
} }
} }
+1 -1
View File
@@ -70,7 +70,7 @@ func newNotEqualTerm(tk *Token) (inst *term) {
func evalNotEqual(ctx ExprContext, self *term) (v any, err error) { func evalNotEqual(ctx ExprContext, self *term) (v any, err error) {
if v, err = evalEqual(ctx, self); err == nil { if v, err = evalEqual(ctx, self); err == nil {
b, _ := toBool(v) b, _ := ToBool(v)
v = !b v = !b
} }
return return
+77
View File
@@ -6,7 +6,9 @@ package expr
import ( import (
"fmt" "fmt"
"os"
"plugin" "plugin"
"strings"
) )
var pluginRegister map[string]*plugin.Plugin var pluginRegister map[string]*plugin.Plugin
@@ -25,6 +27,81 @@ func pluginExists(name string) (exists bool) {
return return
} }
func makePluginName(name string) (decorated string) {
var template string
if execName, err := os.Executable(); err != nil || strings.Index(execName, "debug") < 0 {
template = "expr-%s-plugin.so"
} else {
template = "expr-%s-plugin.so.debug"
}
decorated = fmt.Sprintf(template, name)
return
}
func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err error) {
var filePath string
var p *plugin.Plugin
var sym plugin.Symbol
var moduleName string
var importFunc func(ExprContext)
var ok bool
decoratedName := makePluginName(name)
if filePath, err = makeFilepath(decoratedName, dirList); err != nil {
return
}
if p, err = plugin.Open(filePath); err != nil {
return
}
if sym, err = p.Lookup("MODULE_NAME"); err != nil {
return
}
if moduleName = *sym.(*string); moduleName == "" {
err = fmt.Errorf("plugin %q does not provide a valid module name", decoratedName)
return
}
if sym, err = p.Lookup("DEPENDENCIES"); err != nil {
return
}
if deps := *sym.(*[]string); len(deps) > 0 {
// var count int
if err = loadModules(dirList, deps); err != nil {
return
}
}
if sym, err = p.Lookup("ImportFuncs"); err != nil {
return
}
if importFunc, ok = sym.(func(ExprContext)); !ok {
err = fmt.Errorf("plugin %q does not provide a valid import function", decoratedName)
return
}
registerPlugin(moduleName, p)
importFunc(globalCtx)
return
}
func loadModules(dirList []string, moduleNames []string) (err error) {
for _, name := range moduleNames {
if err1 := importPlugin(dirList, name); err1 != nil {
if !ImportInContext(name) {
err = err1
break
}
}
}
return
}
func init() { func init() {
pluginRegister = make(map[string]*plugin.Plugin) pluginRegister = make(map[string]*plugin.Plugin)
} }
+9 -4
View File
@@ -178,13 +178,18 @@ func (self *scanner) fetchNextToken() (tk *Token) {
tk = self.makeToken(SymDot, ch) tk = self.makeToken(SymDot, ch)
} }
case '\'': case '\'':
tk = self.makeToken(SymQuote, ch) if escape {
tk = self.makeToken(SymQuote, ch)
escape = false
} else {
tk = self.fetchString(ch)
}
case '"': case '"':
if escape { if escape {
tk = self.makeToken(SymDoubleQuote, ch) tk = self.makeToken(SymDoubleQuote, ch)
escape = false escape = false
} else { } else {
tk = self.fetchString() tk = self.fetchString(ch)
} }
case '`': case '`':
tk = self.makeToken(SymBackTick, ch) tk = self.makeToken(SymBackTick, ch)
@@ -533,7 +538,7 @@ func (self *scanner) fetchUntil(sym Symbol, allowEos bool, endings ...byte) (tk
return return
} }
func (self *scanner) fetchString() (tk *Token) { func (self *scanner) fetchString(termCh byte) (tk *Token) {
var err error var err error
var ch, prev byte var ch, prev byte
var sb strings.Builder var sb strings.Builder
@@ -554,7 +559,7 @@ func (self *scanner) fetchString() (tk *Token) {
sb.WriteByte(ch) sb.WriteByte(ch)
} }
prev = 0 prev = 0
} else if ch == '"' { } else if ch == termCh {
break break
} else { } else {
prev = ch prev = ch
+53 -3
View File
@@ -7,7 +7,7 @@ package expr
import ( import (
"fmt" "fmt"
"slices" "slices"
"strings" // "strings"
) )
type SimpleStore struct { type SimpleStore struct {
@@ -51,6 +51,7 @@ func (ctx *SimpleStore) Merge(src ExprContext) {
} }
} }
/*
func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) { func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
sb.WriteString("vars: {\n") sb.WriteString("vars: {\n")
first := true first := true
@@ -77,7 +78,7 @@ func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
} }
} }
sb.WriteString(strings.Repeat("\t", indent)) sb.WriteString(strings.Repeat("\t", indent))
sb.WriteString("\n}\n") sb.WriteString("\n}")
} }
func varsCtxToString(ctx ExprContext, indent int) string { func varsCtxToString(ctx ExprContext, indent int) string {
@@ -106,21 +107,70 @@ func funcsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
sb.WriteString(fmt.Sprintf("%v", value)) sb.WriteString(fmt.Sprintf("%v", value))
} }
} }
sb.WriteString("\n}\n") sb.WriteString("\n}")
} }
func (ctx *SimpleStore) ToString(opt FmtOpt) string { func (ctx *SimpleStore) ToString(opt FmtOpt) string {
var sb strings.Builder var sb strings.Builder
varsCtxToBuilder(&sb, ctx, 0) varsCtxToBuilder(&sb, ctx, 0)
sb.WriteByte('\n')
funcsCtxToBuilder(&sb, ctx, 0) funcsCtxToBuilder(&sb, ctx, 0)
return sb.String() return sb.String()
} }
*/
func (ctx *SimpleStore) ToString(opt FmtOpt) string {
dict := ctx.ToDict()
return dict.ToString(opt)
}
func (ctx *SimpleStore) varsToDict(dict *DictType) *DictType {
names := ctx.EnumVars(nil)
slices.Sort(names)
for _, name := range ctx.EnumVars(nil) {
value, _ := ctx.GetVar(name)
if f, ok := value.(Formatter); ok {
(*dict)[name] = f.ToString(0)
} else if _, ok = value.(Functor); ok {
(*dict)[name] = "func(){}"
} else {
(*dict)[name] = fmt.Sprintf("%v", value)
}
}
return dict
}
func (ctx *SimpleStore) funcsToDict(dict *DictType) *DictType {
names := ctx.EnumFuncs(func(name string) bool { return true })
slices.Sort(names)
for _, name := range names {
value, _ := ctx.GetFuncInfo(name)
if formatter, ok := value.(Formatter); ok {
(*dict)[name] = formatter.ToString(0)
} else {
(*dict)[name] = fmt.Sprintf("%v", value)
}
}
return dict
}
func (ctx *SimpleStore) ToDict() (dict *DictType) {
dict = MakeDict()
(*dict)["variables"] = ctx.varsToDict(MakeDict())
(*dict)["functions"] = ctx.funcsToDict(MakeDict())
return
}
func (ctx *SimpleStore) GetVar(varName string) (v any, exists bool) { func (ctx *SimpleStore) GetVar(varName string) (v any, exists bool) {
v, exists = ctx.varStore[varName] v, exists = ctx.varStore[varName]
return return
} }
func (ctx *SimpleStore) GetLast() (v any) {
v = ctx.varStore["last"]
return
}
func (ctx *SimpleStore) UnsafeSetVar(varName string, value any) { func (ctx *SimpleStore) UnsafeSetVar(varName string, value any) {
// fmt.Printf("[%p] setVar(%v, %v)\n", ctx, varName, value) // fmt.Printf("[%p] setVar(%v, %v)\n", ctx, varName, value)
ctx.varStore[varName] = value ctx.varStore[varName] = value
@@ -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.
// t_func-base_test.go // t_builtin-base_test.go
package expr package expr
import ( import (
@@ -10,7 +10,7 @@ import (
) )
func TestFuncBase(t *testing.T) { func TestFuncBase(t *testing.T) {
section := "Func-Base" section := "Builtin-Base"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`isNil(nil)`, true, nil}, /* 1 */ {`isNil(nil)`, true, nil},
+24
View File
@@ -0,0 +1,24 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// t_builtin-import_test.go
package expr
import (
"testing"
)
func TestFuncImport(t *testing.T) {
section := "Builtin-Import"
inputs := []inputType{
/* 1 */ {`builtin "import"; import("./test-resources/test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
/* 2 */ {`builtin "import"; import("test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
/* 3 */ {`builtin "import"; importAll("./test-resources/test-funcs.expr"); six()`, int64(6), nil},
/* 4 */ {`builtin "import"; import("./test-resources/sample-export-all.expr"); six()`, int64(6), nil},
}
t.Setenv("EXPR_PATH", "test-resources")
// parserTestSpec(t, section, inputs, 69)
parserTest(t, section, inputs)
}
@@ -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.
// t_func-math-arith_test.go // t_builtin-math-arith_test.go
package expr package expr
import ( import (
@@ -10,7 +10,7 @@ import (
) )
func TestFuncMathArith(t *testing.T) { func TestFuncMathArith(t *testing.T) {
section := "Func-Math-Arith" section := "Builtin-Math-Arith"
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil}, /* 1 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil},
/* 2 */ {`builtin "math.arith"; add(1,2,3)`, int64(6), nil}, /* 2 */ {`builtin "math.arith"; add(1,2,3)`, int64(6), nil},
+29
View File
@@ -0,0 +1,29 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// t_builtin-os-file_test.go
package expr
import (
"errors"
"testing"
)
func TestFuncOs(t *testing.T) {
section := "Builtin-OS-File"
inputs := []inputType{
/* 1 */ {`builtin "os.file"`, int64(1), nil},
/* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil},
/* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, errors.New(`open /etc/hostsX: no such file or directory`)},
/* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil},
/* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWriteText(handle, "bye-bye"); fileClose(handle)`, true, nil},
/* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileReadText(handle, "-"); fileClose(handle);word`, "bye", nil},
/* 7 */ {`builtin "os.file"; word=fileReadText(nil, "-")`, nil, errors.New(`fileReadText(): invalid file handle`)},
/* 7 */ {`builtin "os.file"; fileWriteText(nil, "bye")`, nil, errors.New(`fileWriteText(): invalid file handle`)},
}
// t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 69)
parserTest(t, section, inputs)
}
+69
View File
@@ -0,0 +1,69 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// t_builtin-string_test.go
package expr
import (
"errors"
"testing"
)
func TestFuncString(t *testing.T) {
section := "Builtin-String"
inputs := []inputType{
/* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil},
/* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil},
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil},
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, errors.New(`strJoin(): the "separator" parameter must be a string, got a integer (1)`)},
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, errors.New(`strJoin(): expected string, got integer (2)`)},
/* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil},
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
/* 8 */ {`builtin "string"; strSub("0123456789", -3,2)`, "78", nil},
/* 9 */ {`builtin "string"; strSub("0123456789", -3)`, "789", nil},
/* 10 */ {`builtin "string"; strSub("0123456789")`, "0123456789", nil},
/* 11 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "012")`, true, nil},
/* 12 */ {`builtin "string"; strStartsWith("0123456789", "xyz", "0125")`, false, nil},
/* 13 */ {`builtin "string"; strStartsWith("0123456789")`, nil, errors.New(`strStartsWith(): too few params -- expected 2 or more, got 1`)},
/* 14 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "789")`, true, nil},
/* 15 */ {`builtin "string"; strEndsWith("0123456789", "xyz", "0125")`, false, nil},
/* 16 */ {`builtin "string"; strEndsWith("0123456789")`, nil, errors.New(`strEndsWith(): too few params -- expected 2 or more, got 1`)},
/* 17 */ {`builtin "string"; strSplit("one-two-three", "-")`, newListA("one", "two", "three"), nil},
/* 18 */ {`builtin "string"; strJoin("-", [1, "two", "three"])`, nil, errors.New(`strJoin(): expected string, got integer (1)`)},
/* 19 */ {`builtin "string"; strJoin()`, nil, errors.New(`strJoin(): too few params -- expected 1 or more, got 0`)},
/* 69 */ /*{`builtin "string"; $$global`, `vars: {
}
funcs: {
add(any=0 ...) -> number,
dec(any) -> decimal,
endsWithStr(source, suffix) -> boolean,
fract(any, denominator=1) -> fraction,
import( ...) -> any,
importAll( ...) -> any,
int(any) -> integer,
isBool(any) -> boolean,
isDec(any) -> boolean,
isDict(any) -> boolean,
isFloat(any) -> boolean,
isFract(any) -> boolean,
isInt(any) -> boolean,
isList(any) -> boolean,
isNil(any) -> boolean,
isString(any) -> boolean,
joinStr(separator, item="" ...) -> string,
mul(any=1 ...) -> number,
splitStr(source, separator="", count=-1) -> list of string,
startsWithStr(source, prefix) -> boolean,
subStr(source, start=0, count=-1) -> string,
trimStr(source) -> string
}
`, nil},*/
}
//t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 19)
parserTest(t, section, inputs)
}
+2 -1
View File
@@ -31,6 +31,7 @@ func TestDictParser(t *testing.T) {
/* 8 */ {`D={"a":1, "b":2}; D["a"]=9; D`, map[any]any{"a": 9, "b": 2}, nil}, /* 8 */ {`D={"a":1, "b":2}; D["a"]=9; D`, map[any]any{"a": 9, "b": 2}, nil},
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil}, /* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil},
/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)}, /* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
} }
succeeded := 0 succeeded := 0
@@ -110,7 +111,7 @@ func TestDictToStringMultiLine(t *testing.T) {
var good bool var good bool
section := "dict-ToString-ML" section := "dict-ToString-ML"
want := `{ want := `{
"first": 1 "first": 1
}` }`
args := map[any]*term{ args := map[any]*term{
"first": newLiteralTerm(NewValueToken(0, 0, SymInteger, "1", 1)), "first": newLiteralTerm(NewValueToken(0, 0, SymInteger, "1", 1)),
+1 -1
View File
@@ -14,7 +14,7 @@ func TestExpr(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`0?{}`, nil, nil}, /* 1 */ {`0?{}`, nil, nil},
/* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil}, /* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil},
/* 3 */ {`builtin "os.file"; f=openFile("test-file.txt"); line=readFile(f); closeFile(f); line`, "uno", nil}, /* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil}, /* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil}, /* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
/* 6 */ {` /* 6 */ {`
-24
View File
@@ -1,24 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// t_func-import_test.go
package expr
import (
"testing"
)
func TestFuncImport(t *testing.T) {
section := "Func-Import"
inputs := []inputType{
/* 1 */ {`builtin "import"; import("./test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
/* 2 */ {`builtin "import"; import("test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
/* 3 */ {`builtin "import"; importAll("./test-funcs.expr"); six()`, int64(6), nil},
/* 4 */ {`builtin "import"; import("./sample-export-all.expr"); six()`, int64(6), nil},
}
t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 69)
parserTest(t, section, inputs)
}
-29
View File
@@ -1,29 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// t_func-os_test.go
package expr
import (
"errors"
"testing"
)
func TestFuncOs(t *testing.T) {
section := "Func-OS"
inputs := []inputType{
/* 1 */ {`builtin "os.file"`, int64(1), nil},
/* 2 */ {`builtin "os.file"; handle=openFile("/etc/hosts"); closeFile(handle)`, true, nil},
/* 3 */ {`builtin "os.file"; handle=openFile("/etc/hostsX")`, nil, errors.New(`open /etc/hostsX: no such file or directory`)},
/* 4 */ {`builtin "os.file"; handle=createFile("/tmp/dummy"); closeFile(handle)`, true, nil},
/* 5 */ {`builtin "os.file"; handle=appendFile("/tmp/dummy"); writeFile(handle, "bye-bye"); closeFile(handle)`, true, nil},
/* 6 */ {`builtin "os.file"; handle=openFile("/tmp/dummy"); word=readFile(handle, "-"); closeFile(handle);word`, "bye", nil},
/* 7 */ {`builtin "os.file"; word=readFile(nil, "-")`, nil, errors.New(`readFileFunc(): invalid file handle`)},
/* 7 */ {`builtin "os.file"; writeFile(nil, "bye")`, nil, errors.New(`writeFileFunc(): invalid file handle`)},
}
// t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 69)
parserTest(t, section, inputs)
}
-69
View File
@@ -1,69 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// t_func-string_test.go
package expr
import (
"errors"
"testing"
)
func TestFuncString(t *testing.T) {
section := "Func-String"
inputs := []inputType{
/* 1 */ {`builtin "string"; joinStr("-", "one", "two", "three")`, "one-two-three", nil},
/* 2 */ {`builtin "string"; joinStr("-", ["one", "two", "three"])`, "one-two-three", nil},
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; joinStr("-", ls)`, "one-two-three", nil},
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; joinStr(1, ls)`, nil, errors.New(`joinStr() the "separator" parameter must be a string, got a integer (1)`)},
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; joinStr("-", ls)`, nil, errors.New(`joinStr() expected string, got integer (2)`)},
/* 6 */ {`builtin "string"; "<"+trimStr(" bye bye ")+">"`, "<bye bye>", nil},
/* 7 */ {`builtin "string"; subStr("0123456789", 1,2)`, "12", nil},
/* 8 */ {`builtin "string"; subStr("0123456789", -3,2)`, "78", nil},
/* 9 */ {`builtin "string"; subStr("0123456789", -3)`, "789", nil},
/* 10 */ {`builtin "string"; subStr("0123456789")`, "0123456789", nil},
/* 11 */ {`builtin "string"; startsWithStr("0123456789", "xyz", "012")`, true, nil},
/* 12 */ {`builtin "string"; startsWithStr("0123456789", "xyz", "0125")`, false, nil},
/* 13 */ {`builtin "string"; startsWithStr("0123456789")`, nil, errors.New(`startsWithStr(): too few params -- expected 2 or more, got 1`)},
/* 14 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "789")`, true, nil},
/* 15 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "0125")`, false, nil},
/* 16 */ {`builtin "string"; endsWithStr("0123456789")`, nil, errors.New(`endsWithStr(): too few params -- expected 2 or more, got 1`)},
/* 17 */ {`builtin "string"; splitStr("one-two-three", "-")`, newListA("one", "two", "three"), nil},
/* 18 */ {`builtin "string"; joinStr("-", [1, "two", "three"])`, nil, errors.New(`joinStr() expected string, got integer (1)`)},
/* 19 */ {`builtin "string"; joinStr()`, nil, errors.New(`joinStr(): too few params -- expected 1 or more, got 0`)},
/* 69 */ /*{`builtin "string"; $$global`, `vars: {
}
funcs: {
add(any=0 ...) -> number,
dec(any) -> decimal,
endsWithStr(source, suffix) -> boolean,
fract(any, denominator=1) -> fraction,
import( ...) -> any,
importAll( ...) -> any,
int(any) -> integer,
isBool(any) -> boolean,
isDec(any) -> boolean,
isDict(any) -> boolean,
isFloat(any) -> boolean,
isFract(any) -> boolean,
isInt(any) -> boolean,
isList(any) -> boolean,
isNil(any) -> boolean,
isString(any) -> boolean,
joinStr(separator, item="" ...) -> string,
mul(any=1 ...) -> number,
splitStr(source, separator="", count=-1) -> list of string,
startsWithStr(source, prefix) -> boolean,
subStr(source, start=0, count=-1) -> string,
trimStr(source) -> string
}
`, nil},*/
}
//t.Setenv("EXPR_PATH", ".")
// parserTestSpec(t, section, inputs, 19)
parserTest(t, section, inputs)
}
+1 -1
View File
@@ -44,7 +44,7 @@ func dummy(ctx ExprContext, name string, args []any) (result any, err error) {
func TestFunctionToStringSimple(t *testing.T) { func TestFunctionToStringSimple(t *testing.T) {
source := NewGolangFunctor(dummy) source := NewGolangFunctor(dummy)
want := "func() {<body>}" want := "func() {}"
got := source.ToString(0) got := source.ToString(0)
if got != want { if got != want {
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)
+9 -9
View File
@@ -8,15 +8,15 @@ import "testing"
func TestIteratorParser(t *testing.T) { func TestIteratorParser(t *testing.T) {
inputs := []inputType{ inputs := []inputType{
/* 1 */ {`include "iterator.expr"; it=$(ds,3); ()it`, int64(0), nil}, /* 1 */ {`include "test-resources/iterator.expr"; it=$(ds,3); ()it`, int64(0), nil},
/* 2 */ {`include "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 "iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil}, /* 3 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil},
/* 4 */ {`include "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 "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 "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 "file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil}, /* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
/* 8 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it++; it.index`, int64(0), nil}, /* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it++; it.index`, int64(0), nil},
/* 10 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it.clean`, true, nil}, /* 10 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, true, nil},
/* 11 */ {`it=$(1,2,3); it++`, int64(1), nil}, /* 11 */ {`it=$(1,2,3); it++`, int64(1), nil},
} }
// inputs1 := []inputType{ // inputs1 := []inputType{
+1 -1
View File
@@ -203,7 +203,7 @@ func doTest(t *testing.T, section string, input *inputType, count int) (good boo
eq := reflect.DeepEqual(gotResult, input.wantResult) eq := reflect.DeepEqual(gotResult, input.wantResult)
if !eq /*gotResult != input.wantResult*/ { if !eq /*gotResult != input.wantResult*/ {
t.Errorf("%d: %q -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, typeName(gotResult), input.wantResult, typeName(input.wantResult)) t.Errorf("%d: %q -> result = %v [%s], want = %v [%s]", count, input.source, gotResult, TypeName(gotResult), input.wantResult, TypeName(input.wantResult))
good = false good = false
} }
+1 -1
View File
@@ -45,7 +45,7 @@ func TestScanner(t *testing.T) {
/* 22 */ {"@", SymAt, nil, nil}, /* 22 */ {"@", SymAt, nil, nil},
/* 23 */ {`#`, SymHash, nil, nil}, /* 23 */ {`#`, SymHash, nil, nil},
/* 24 */ {`%`, SymPercent, nil, nil}, /* 24 */ {`%`, SymPercent, nil, nil},
/* 25 */ {`'`, SymQuote, nil, nil}, /* 25 */ {`\'`, SymQuote, nil, nil},
/* 26 */ {`\"`, SymDoubleQuote, nil, nil}, /* 26 */ {`\"`, SymDoubleQuote, nil, nil},
/* 27 */ {`_`, SymUndescore, nil, nil}, /* 27 */ {`_`, SymUndescore, nil, nil},
/* 28 */ {`<>`, SymLessGreater, nil, nil}, /* 28 */ {`<>`, SymLessGreater, nil, nil},
+5 -5
View File
@@ -63,7 +63,7 @@ func TestIsString(t *testing.T) {
} }
count++ count++
b, ok := toBool(true) b, ok := ToBool(true)
if !(ok && b) { if !(ok && b) {
t.Errorf("%d: toBool(true) b=%v, ok=%v -> result = false, want true", count, b, ok) t.Errorf("%d: toBool(true) b=%v, ok=%v -> result = false, want true", count, b, ok)
failed++ failed++
@@ -72,7 +72,7 @@ func TestIsString(t *testing.T) {
} }
count++ count++
b, ok = toBool("abc") b, ok = ToBool("abc")
if !(ok && b) { if !(ok && b) {
t.Errorf("%d: toBool(\"abc\") b=%v, ok=%v -> result = false, want true", count, b, ok) t.Errorf("%d: toBool(\"abc\") b=%v, ok=%v -> result = false, want true", count, b, ok)
failed++ failed++
@@ -81,7 +81,7 @@ func TestIsString(t *testing.T) {
} }
count++ count++
b, ok = toBool([]int{}) b, ok = ToBool([]int{})
if ok { if ok {
t.Errorf("%d: toBool([]) b=%v, ok=%v -> result = true, want false", count, b, ok) t.Errorf("%d: toBool([]) b=%v, ok=%v -> result = true, want false", count, b, ok)
failed++ failed++
@@ -97,7 +97,7 @@ func TestToIntOk(t *testing.T) {
wantValue := int(64) wantValue := int(64)
wantErr := error(nil) wantErr := error(nil)
gotValue, gotErr := toInt(source, "test") gotValue, gotErr := ToInt(source, "test")
if gotErr != nil || gotValue != wantValue { if gotErr != nil || gotValue != wantValue {
t.Errorf("toInt(%v, \"test\") gotValue=%v, gotErr=%v -> wantValue=%v, wantErr=%v", t.Errorf("toInt(%v, \"test\") gotValue=%v, gotErr=%v -> wantValue=%v, wantErr=%v",
@@ -110,7 +110,7 @@ func TestToIntErr(t *testing.T) {
wantValue := 0 wantValue := 0
wantErr := errors.New(`test expected integer, got uint64 (64)`) wantErr := errors.New(`test expected integer, got uint64 (64)`)
gotValue, gotErr := toInt(source, "test") gotValue, gotErr := ToInt(source, "test")
if gotErr.Error() != wantErr.Error() || gotValue != wantValue { if gotErr.Error() != wantErr.Error() || gotValue != wantValue {
t.Errorf("toInt(%v, \"test\") gotValue=%v, gotErr=%v -> wantValue=%v, wantErr=%v", t.Errorf("toInt(%v, \"test\") gotValue=%v, gotErr=%v -> wantValue=%v, wantErr=%v",
+4 -4
View File
@@ -156,15 +156,15 @@ func (self *term) toInt(computedValue any, valueDescription string) (i int, err
if index64, ok := computedValue.(int64); ok { if index64, ok := computedValue.(int64); ok {
i = int(index64) i = int(index64)
} else { } else {
err = self.Errorf("%s, got %s (%v)", valueDescription, typeName(computedValue), computedValue) err = self.Errorf("%s, got %s (%v)", valueDescription, TypeName(computedValue), computedValue)
} }
return return
} }
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error { func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
leftType := typeName(leftValue) leftType := TypeName(leftValue)
leftText := getFormatted(leftValue, Truncate) leftText := getFormatted(leftValue, Truncate)
rightType := typeName(rightValue) rightType := TypeName(rightValue)
rightText := getFormatted(rightValue, Truncate) rightText := getFormatted(rightValue, Truncate)
return self.tk.Errorf( return self.tk.Errorf(
"left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q", "left operand '%s' [%s] and right operand '%s' [%s] are not compatible with operator %q",
@@ -176,7 +176,7 @@ func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
func (self *term) errIncompatibleType(value any) error { func (self *term) errIncompatibleType(value any) error {
return self.tk.Errorf( return self.tk.Errorf(
"prefix/postfix operator %q do not support operand '%v' [%s]", "prefix/postfix operator %q do not support operand '%v' [%s]",
self.source(), value, typeName(value)) self.source(), value, TypeName(value))
} }
func (self *term) Errorf(template string, args ...any) (err error) { func (self *term) Errorf(template string, args ...any) (err error) {
+32
View File
@@ -0,0 +1,32 @@
builtin ["os.file", "base"];
readInt=func(fh){
line=fileReadText(fh);
line ? [nil] {nil} :: {int(line)}
};
ds={
"init":func(filename){
fh=fileOpen(filename);
fh ? [nil] {nil} :: { @current=readInt(fh) };
fh
},
"current":func(){
current
},
"next":func(fh){
@current=readInt(fh);
current
},
"clean":func(fh){
fileClose(fh)
}
}
//;f=$(ds, "int.list")
//;f++
//;f++
//;f++
//*/
//;add(f)
+3 -3
View File
@@ -86,7 +86,7 @@ func numAsFloat(v any) (f float64) {
return return
} }
func toBool(v any) (b bool, ok bool) { func ToBool(v any) (b bool, ok bool) {
ok = true ok = true
switch x := v.(type) { switch x := v.(type) {
case string: case string:
@@ -201,13 +201,13 @@ func CloneFilteredMap[K comparable, V any](source map[K]V, filter func(key K) (a
return CopyFilteredMap(dest, source, filter) return CopyFilteredMap(dest, source, filter)
} }
func toInt(value any, description string) (i int, err error) { func ToInt(value any, description string) (i int, err error) {
if valueInt64, ok := value.(int64); ok { if valueInt64, ok := value.(int64); ok {
i = int(valueInt64) i = int(valueInt64)
} else if valueInt, ok := value.(int); ok { } else if valueInt, ok := value.(int); ok {
i = valueInt i = valueInt
} else { } else {
err = fmt.Errorf("%s expected integer, got %s (%v)", description, typeName(value), value) err = fmt.Errorf("%s expected integer, got %s (%v)", description, TypeName(value), value)
} }
return return
} }