Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 760c1ee6da | |||
| 5ab6876ea1 | |||
| 6268abda8c | |||
| d25bd325b7 | |||
| 01c04feea5 | |||
| 6fc689c46c | |||
| eccb0c4dc9 | |||
| e43823740f | |||
| 526982a564 | |||
| 252514943e | |||
| 32686fac62 | |||
| 52a627c747 | |||
| d91e7eb979 | |||
| cca3b76baa | |||
| 24e31997fc | |||
| 646710e180 | |||
| b38327b841 | |||
| fd912b2eb1 | |||
| 0e55f83d56 | |||
| 4725145d1c | |||
| edf8818f51 | |||
| 6211be8a8f | |||
| f50ddf48db | |||
| 76e01f12d2 | |||
| 406bced450 | |||
| 409dc86a92 | |||
| 4184221428 | |||
| 8cf8b36a26 | |||
| de87050188 | |||
| a1ec0cc611 | |||
| 8e5550bfa7 | |||
| 6ee21e10af | |||
| 5c44532790 | |||
| cb66c1ab19 | |||
| a28d24ba68 | |||
| 523349a204 | |||
| b185f1df3a | |||
| 5da5a61a42 | |||
| 6e9205abc4 | |||
| f61004fb5d | |||
| 321030c8d3 | |||
| 98fc89e84f | |||
| 778d00677d | |||
| ba3dbb7f02 | |||
| 7285109115 | |||
| 4755774edd | |||
| d215d837f6 | |||
| ad3c1e5a60 | |||
| d6bf5ee500 | |||
| 4b176eb868 |
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -188,6 +189,30 @@ func fractFunc(ctx ExprContext, name string, args map[string]any) (result any, e
|
|||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
func evalFunc(ctx ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
if source, ok := args[ParamSource].(string); ok {
|
||||||
|
var expr Expr
|
||||||
|
|
||||||
|
parser := NewParser()
|
||||||
|
if ctx == nil {
|
||||||
|
ctx = NewSimpleStore()
|
||||||
|
}
|
||||||
|
|
||||||
|
r := strings.NewReader(source)
|
||||||
|
scanner := NewScanner(r, DefaultTranslations())
|
||||||
|
|
||||||
|
if expr, err = parser.Parse(scanner); err == nil {
|
||||||
|
CtrlEnable(ctx, control_export_all)
|
||||||
|
result, err = expr.Eval(ctx)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = ErrWrongParamType(name, ParamSource, TypeString, args[ParamSource])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//// import
|
||||||
|
|
||||||
func ImportBuiltinsFuncs(ctx ExprContext) {
|
func ImportBuiltinsFuncs(ctx ExprContext) {
|
||||||
anyParams := []ExprFuncParam{
|
anyParams := []ExprFuncParam{
|
||||||
NewFuncParam(ParamValue),
|
NewFuncParam(ParamValue),
|
||||||
@@ -211,6 +236,10 @@ func ImportBuiltinsFuncs(ctx ExprContext) {
|
|||||||
NewFuncParam(ParamValue),
|
NewFuncParam(ParamValue),
|
||||||
NewFuncParamFlagDef(ParamDenominator, PfDefault, int64(1)),
|
NewFuncParamFlagDef(ParamDenominator, PfDefault, int64(1)),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ctx.RegisterFunc("eval", NewGolangFunctor(evalFunc), TypeAny, []ExprFuncParam{
|
||||||
|
NewFuncParam(ParamSource),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
+1
-1
@@ -44,7 +44,7 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
|
|||||||
var expr *ast
|
var expr *ast
|
||||||
scanner := NewScanner(file, DefaultTranslations())
|
scanner := NewScanner(file, DefaultTranslations())
|
||||||
parser := NewParser()
|
parser := NewParser()
|
||||||
if expr, err = parser.parseGeneral(scanner, true, true); err == nil {
|
if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymEos); err == nil {
|
||||||
result, err = expr.Eval(ctx)
|
result, err = expr.Eval(ctx)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+18
-1
@@ -55,8 +55,25 @@ 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 undefArticle(s string) (article string) {
|
||||||
|
if len(s) > 0 && strings.Contains("aeiou", s[0:1]) {
|
||||||
|
article = "an"
|
||||||
|
} else {
|
||||||
|
article = "a"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func prependUndefArticle(s string) (result string) {
|
||||||
|
return undefArticle(s) + " " + s
|
||||||
|
}
|
||||||
|
|
||||||
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
|
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
|
||||||
return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue)
|
var artWantType, artGotType string
|
||||||
|
gotType := TypeName(paramValue)
|
||||||
|
artGotType = prependUndefArticle(gotType)
|
||||||
|
artWantType = prependUndefArticle(paramType)
|
||||||
|
return fmt.Errorf("%s(): the %q parameter must be %s, got %s (%v)", funcName, paramName, artWantType, artGotType, paramValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ErrUnknownParam(funcName, paramName string) error {
|
func ErrUnknownParam(funcName, paramName string) error {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package expr
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
TypeAny = "any"
|
TypeAny = "any"
|
||||||
|
TypeNil = "nil"
|
||||||
TypeBoolean = "boolean"
|
TypeBoolean = "boolean"
|
||||||
TypeFloat = "float"
|
TypeFloat = "float"
|
||||||
TypeFraction = "fraction"
|
TypeFraction = "fraction"
|
||||||
@@ -15,6 +16,7 @@ const (
|
|||||||
TypeNumber = "number"
|
TypeNumber = "number"
|
||||||
TypePair = "pair"
|
TypePair = "pair"
|
||||||
TypeString = "string"
|
TypeString = "string"
|
||||||
|
TypeDict = "dict"
|
||||||
TypeListOf = "list-of-"
|
TypeListOf = "list-of-"
|
||||||
TypeListOfStrings = "list-of-strings"
|
TypeListOfStrings = "list-of-strings"
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@ func exportObjects(destCtx, sourceCtx ExprContext) {
|
|||||||
exportAll := CtrlIsEnabled(sourceCtx, control_export_all)
|
exportAll := CtrlIsEnabled(sourceCtx, control_export_all)
|
||||||
// fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
|
// fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
|
||||||
// Export variables
|
// Export variables
|
||||||
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) {
|
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return (exportAll || name[0] == '@') && !(name[0] == '_') }) {
|
||||||
// fmt.Printf("\tExporting %q\n", refName)
|
// fmt.Printf("\tExporting %q\n", refName)
|
||||||
refValue, _ := sourceCtx.GetVar(refName)
|
refValue, _ := sourceCtx.GetVar(refName)
|
||||||
exportVar(destCtx, refName, refValue)
|
exportVar(destCtx, refName, refValue)
|
||||||
|
|||||||
+139
-74
@@ -6,12 +6,14 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
type dataCursor struct {
|
type dataCursor struct {
|
||||||
ds map[string]Functor
|
ds map[string]Functor
|
||||||
ctx ExprContext
|
ctx ExprContext
|
||||||
initState bool // true if no item has prodiced yet (this replace di initial Next() call in the contructor)
|
initState bool // true if no item has produced yet (this replace di initial Next() call in the contructor)
|
||||||
|
// cursorValid bool // true if resource is nil or if clean has not yet been called
|
||||||
index int
|
index int
|
||||||
count int
|
count int
|
||||||
current any
|
current any
|
||||||
@@ -26,6 +28,7 @@ func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *da
|
|||||||
dc = &dataCursor{
|
dc = &dataCursor{
|
||||||
ds: ds,
|
ds: ds,
|
||||||
initState: true,
|
initState: true,
|
||||||
|
// cursorValid: true,
|
||||||
index: -1,
|
index: -1,
|
||||||
count: 0,
|
count: 0,
|
||||||
current: nil,
|
current: nil,
|
||||||
@@ -36,7 +39,6 @@ func NewDataCursor(ctx ExprContext, ds map[string]Functor, resource any) (dc *da
|
|||||||
cleanFunc: ds[CleanName],
|
cleanFunc: ds[CleanName],
|
||||||
resetFunc: ds[ResetName],
|
resetFunc: ds[ResetName],
|
||||||
}
|
}
|
||||||
//dc.Next()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,7 +79,7 @@ func (dc *dataCursor) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dc *dataCursor) HasOperation(name string) (exists bool) {
|
func (dc *dataCursor) HasOperation(name string) (exists bool) {
|
||||||
exists = name == IndexName
|
exists = slices.Contains([]string{CleanName, ResetName, CurrentName, IndexName}, name)
|
||||||
if !exists {
|
if !exists {
|
||||||
f, ok := dc.ds[name]
|
f, ok := dc.ds[name]
|
||||||
exists = ok && isFunctor(f)
|
exists = ok && isFunctor(f)
|
||||||
@@ -88,63 +90,83 @@ func (dc *dataCursor) HasOperation(name string) (exists bool) {
|
|||||||
func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any, err error) {
|
func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any, err error) {
|
||||||
if name == IndexName {
|
if name == IndexName {
|
||||||
value = int64(dc.Index())
|
value = int64(dc.Index())
|
||||||
|
} else if name == CleanName {
|
||||||
|
err = dc.Clean()
|
||||||
|
} else if name == ResetName {
|
||||||
|
err = dc.Reset()
|
||||||
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
|
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) {
|
||||||
if functor == dc.cleanFunc {
|
ctx := cloneContext(dc.ctx)
|
||||||
value, err = dc.Clean()
|
value, err = functor.InvokeNamed(ctx, name, args)
|
||||||
} else if functor == dc.resetFunc {
|
exportObjects(dc.ctx, ctx)
|
||||||
value, err = dc.Reset()
|
|
||||||
} else {
|
|
||||||
ctx := cloneContext(dc.ctx)
|
|
||||||
value, err = functor.InvokeNamed(ctx, name, args)
|
|
||||||
exportObjects(dc.ctx, ctx)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
err = errNoOperation(name)
|
err = errNoOperation(name)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *dataCursor) Reset() (success bool, err error) {
|
// func (dc *dataCursor) Reset() (err error) {
|
||||||
|
// if dc.resetFunc != nil {
|
||||||
|
// if dc.resource != nil {
|
||||||
|
// ctx := cloneContext(dc.ctx)
|
||||||
|
// actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
|
||||||
|
// _, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
|
||||||
|
// exportObjects(dc.ctx, ctx)
|
||||||
|
// dc.index = -1
|
||||||
|
// dc.count = 0
|
||||||
|
// dc.initState = true
|
||||||
|
// dc.current = nil
|
||||||
|
// dc.lastErr = nil
|
||||||
|
// } else {
|
||||||
|
// err = errInvalidDataSource()
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// err = errNoOperation(ResetName)
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (dc *dataCursor) Reset() (err error) {
|
||||||
if dc.resetFunc != nil {
|
if dc.resetFunc != nil {
|
||||||
if dc.resource != nil {
|
ctx := cloneContext(dc.ctx)
|
||||||
ctx := cloneContext(dc.ctx)
|
actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
|
||||||
actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
|
_, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
|
||||||
_, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
|
exportObjects(dc.ctx, ctx)
|
||||||
exportObjects(dc.ctx, ctx)
|
|
||||||
dc.index = -1
|
|
||||||
dc.count = 0
|
|
||||||
dc.initState = true
|
|
||||||
dc.current = nil
|
|
||||||
dc.lastErr = nil
|
|
||||||
//dc.Next()
|
|
||||||
} else {
|
|
||||||
err = errInvalidDataSource()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = errNoOperation(ResetName)
|
|
||||||
}
|
}
|
||||||
success = err == nil
|
dc.index = -1
|
||||||
|
dc.count = 0
|
||||||
|
dc.initState = true
|
||||||
|
dc.current = nil
|
||||||
|
dc.lastErr = nil
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dc *dataCursor) Clean() (success bool, err error) {
|
func (dc *dataCursor) Clean() (err error) {
|
||||||
if dc.cleanFunc != nil {
|
if dc.cleanFunc != nil {
|
||||||
if dc.resource != nil {
|
ctx := cloneContext(dc.ctx)
|
||||||
ctx := cloneContext(dc.ctx)
|
actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
|
||||||
actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
|
_, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
|
||||||
_, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
|
exportObjects(dc.ctx, ctx)
|
||||||
// dc.resource = nil
|
|
||||||
exportObjects(dc.ctx, ctx)
|
|
||||||
} else {
|
|
||||||
err = errInvalidDataSource()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = errNoOperation(CleanName)
|
|
||||||
}
|
}
|
||||||
success = err == nil
|
dc.lastErr = io.EOF
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func (dc *dataCursor) Clean() (err error) {
|
||||||
|
// if dc.cleanFunc != nil {
|
||||||
|
// if dc.resource != nil {
|
||||||
|
// ctx := cloneContext(dc.ctx)
|
||||||
|
// actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
|
||||||
|
// _, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
|
||||||
|
// exportObjects(dc.ctx, ctx)
|
||||||
|
// } else {
|
||||||
|
// err = errInvalidDataSource()
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// err = errNoOperation(CleanName)
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
|
func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
|
||||||
dc.init()
|
dc.init()
|
||||||
|
|
||||||
@@ -191,46 +213,89 @@ func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF af
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
current = dc.current
|
current = dc.current
|
||||||
if dc.resource != nil {
|
filter := dc.ds[FilterName]
|
||||||
filter := dc.ds[FilterName]
|
mapper := dc.ds[MapName]
|
||||||
mapper := dc.ds[MapName]
|
var item any
|
||||||
var item any
|
for item == nil && dc.lastErr == nil {
|
||||||
for item == nil && dc.lastErr == nil {
|
ctx := cloneContext(dc.ctx)
|
||||||
ctx := cloneContext(dc.ctx)
|
dc.index++
|
||||||
dc.index++
|
|
||||||
|
|
||||||
actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
|
actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
|
||||||
if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil {
|
if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil {
|
||||||
if item == nil {
|
if item == nil {
|
||||||
dc.lastErr = io.EOF
|
dc.lastErr = io.EOF
|
||||||
} else {
|
} else {
|
||||||
accepted := true
|
accepted := true
|
||||||
if filter != nil {
|
if filter != nil {
|
||||||
if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
|
if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
|
||||||
item = nil
|
item = nil
|
||||||
}
|
|
||||||
}
|
|
||||||
if accepted {
|
|
||||||
dc.count++
|
|
||||||
}
|
|
||||||
if item != nil && mapper != nil {
|
|
||||||
item, dc.lastErr = dc.mapItem(mapper, item)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if accepted {
|
||||||
|
dc.count++
|
||||||
|
}
|
||||||
|
if item != nil && mapper != nil {
|
||||||
|
item, dc.lastErr = dc.mapItem(mapper, item)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exportObjects(dc.ctx, ctx)
|
|
||||||
}
|
}
|
||||||
dc.current = item
|
exportObjects(dc.ctx, ctx)
|
||||||
if dc.lastErr != nil {
|
}
|
||||||
dc.index--
|
dc.current = item
|
||||||
dc.Clean()
|
if dc.lastErr != nil {
|
||||||
}
|
dc.index--
|
||||||
} else {
|
dc.Clean()
|
||||||
dc.lastErr = errInvalidDataSource()
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF after the last item
|
||||||
|
// if dc.initState {
|
||||||
|
// dc.init()
|
||||||
|
// } else if err = dc.lastErr; err != nil {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// current = dc.current
|
||||||
|
// if dc.resource != nil {
|
||||||
|
// filter := dc.ds[FilterName]
|
||||||
|
// mapper := dc.ds[MapName]
|
||||||
|
// var item any
|
||||||
|
// for item == nil && dc.lastErr == nil {
|
||||||
|
// ctx := cloneContext(dc.ctx)
|
||||||
|
// dc.index++
|
||||||
|
|
||||||
|
// actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
|
||||||
|
// if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil {
|
||||||
|
// if item == nil {
|
||||||
|
// dc.lastErr = io.EOF
|
||||||
|
// } else {
|
||||||
|
// accepted := true
|
||||||
|
// if filter != nil {
|
||||||
|
// if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
|
||||||
|
// item = nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if accepted {
|
||||||
|
// dc.count++
|
||||||
|
// }
|
||||||
|
// if item != nil && mapper != nil {
|
||||||
|
// item, dc.lastErr = dc.mapItem(mapper, item)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// exportObjects(dc.ctx, ctx)
|
||||||
|
// }
|
||||||
|
// dc.current = item
|
||||||
|
// if dc.lastErr != nil {
|
||||||
|
// dc.index--
|
||||||
|
// dc.Clean()
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// dc.lastErr = errInvalidDataSource()
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
func (dc *dataCursor) Index() int {
|
func (dc *dataCursor) Index() int {
|
||||||
return dc.index - 1
|
return dc.index - 1
|
||||||
}
|
}
|
||||||
|
|||||||
+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))
|
||||||
|
|||||||
+335
-122
@@ -58,7 +58,7 @@ The expression context is analogous to the stack-frame of other programming lang
|
|||||||
|
|
||||||
Function contexts are created by cloning the calling context. More details on this topic are given later in this document.
|
Function contexts are created by cloning the calling context. More details on this topic are given later in this document.
|
||||||
|
|
||||||
_Expr_ creates and keeps a inner _global context_ where it stores imported functions, either from builtin or plugin modules. To perform calculations, the calling program must provide its own context; this is the _main context_. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The createt context can be called _function context_.
|
_Expr_ creates and keeps a inner _global context_ where it stores imported functions, either from builtin or plugin modules. To perform calculations, the user program must provide its own context; this is the _main context_. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The created context can be called _function context_.
|
||||||
|
|
||||||
Imported functions are registerd in the _global context_. When an expression first calls an imported function, that function is linked to the current context; this can be the _main context_ or a _function context_.
|
Imported functions are registerd in the _global context_. When an expression first calls an imported function, that function is linked to the current context; this can be the _main context_ or a _function context_.
|
||||||
|
|
||||||
@@ -79,20 +79,20 @@ Here are some examples of execution.
|
|||||||
# Type 'exit' or Ctrl+D to quit the program.
|
# Type 'exit' or Ctrl+D to quit the program.
|
||||||
|
|
||||||
[user]$ ./dev-expr
|
[user]$ ./dev-expr
|
||||||
dev-expr -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoroso@portale-stac.it)
|
dev-expr -- Expressions calculator v1.12.0(build 1),2024/09/14 (celestino.amoroso@portale-stac.it)
|
||||||
Based on the Expr package v0.19.0
|
Based on the Expr package v0.26.0
|
||||||
Type help to get the list of available commands
|
Type help to get the list of available commands
|
||||||
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||||
>>> help
|
>>> help
|
||||||
--- REPL commands:
|
--- REPL commands:
|
||||||
source -- Load a file as input
|
|
||||||
tty -- Enable/Disable ansi output <1>
|
|
||||||
base -- Set the integer output base: 2, 8, 10, or 16
|
base -- Set the integer output base: 2, 8, 10, or 16
|
||||||
exit -- Exit the program
|
exit -- Exit the program
|
||||||
help -- Show command list
|
help -- Show command list
|
||||||
ml -- Enable/Disable multi-line output
|
ml -- Enable/Disable multi-line output
|
||||||
mods -- List builtin modules
|
mods -- List builtin modules
|
||||||
output -- Enable/Disable printing expression results. Options 'on', 'off', 'status'
|
output -- Enable/Disable printing expression results. Options 'on', 'off', 'status'
|
||||||
|
source -- Load a file as input
|
||||||
|
tty -- Enable/Disable ansi output <1>
|
||||||
|
|
||||||
--- Command line options:
|
--- Command line options:
|
||||||
-b <builtin> Import builtin modules.
|
-b <builtin> Import builtin modules.
|
||||||
@@ -127,22 +127,22 @@ dev-expr -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoro
|
|||||||
9.5
|
9.5
|
||||||
>>> 0xFD + 0b1 + 0o1 <1>
|
>>> 0xFD + 0b1 + 0o1 <1>
|
||||||
255
|
255
|
||||||
>>> 1|2 + 2|3 <2>
|
>>> 1:2 + 2:3 <2>
|
||||||
7|6
|
7:6
|
||||||
>>> ml <3>
|
>>> ml <3>
|
||||||
>>> 1|2 + 2|3
|
>>> 1:2 + 2:3
|
||||||
7
|
7
|
||||||
-
|
-
|
||||||
6
|
6
|
||||||
>>> 4+2 but 5|2+0.5 <4>
|
>>> 4+2 but 5:2+0.5 <4>
|
||||||
3
|
3
|
||||||
>>> 4+2; 5|2+0.5 <5>
|
>>> 4+2; 5:2+0.5 <5>
|
||||||
3
|
3
|
||||||
>>>
|
>>>
|
||||||
----
|
----
|
||||||
|
|
||||||
<1> Number bases: 0x = _hexadecimal_, 0o = _octal_, 0b = _binary_.
|
<1> Number bases: 0x = _hexadecimal_, 0o = _octal_, 0b = _binary_.
|
||||||
<2> Fractions: _numerator_ | _denominator_.
|
<2> Fractions: _numerator_ : _denominator_.
|
||||||
<3> Activate multi-line output of fractions.
|
<3> Activate multi-line output of fractions.
|
||||||
<4> But operator, see <<_but_operator>>.
|
<4> But operator, see <<_but_operator>>.
|
||||||
<5> Multi-expression: the same result of the previous single expression but this it is obtained with two separated calculations.
|
<5> Multi-expression: the same result of the previous single expression but this it is obtained with two separated calculations.
|
||||||
@@ -155,9 +155,9 @@ _Expr_ supports three type of numbers:
|
|||||||
|
|
||||||
. [blue]#Integers#
|
. [blue]#Integers#
|
||||||
. [blue]#Floats#
|
. [blue]#Floats#
|
||||||
. [blue]#Factions#
|
. [blue]#Fractions#
|
||||||
|
|
||||||
In mixed operations involving integers, fractions and floats, automatic type promotion to the largest type take place.
|
In mixed operations involving integers, fractions and floats, automatic type promotion to the largest type is performed.
|
||||||
|
|
||||||
==== Integers
|
==== Integers
|
||||||
__Expr__'s integers are a subset of the integer set. Internally they are stored as Golang _int64_ values.
|
__Expr__'s integers are a subset of the integer set. Internally they are stored as Golang _int64_ values.
|
||||||
@@ -183,11 +183,11 @@ Value range: *-9223372036854775808* to *9223372036854775807*
|
|||||||
[cols="^1,^2,6,4"]
|
[cols="^1,^2,6,4"]
|
||||||
|===
|
|===
|
||||||
| Symbol | Operation | Description | Examples
|
| Symbol | Operation | Description | Examples
|
||||||
| [blue]`+` | _sum_ | Add two values | [blue]`-1 + 2` -> 1
|
| [blue]`+` | _Sum_ | Add two values | [blue]`-1 + 2` -> _1_
|
||||||
| [blue]`-` | _subtraction_ | Subtract the right value from the left one | [blue]`3 - 1` -> 2
|
| [blue]`-` | _Subtraction_ | Subtract the right value from the left one | [blue]`3 - 1` -> _2_
|
||||||
| [blue]`*` | _product_ | Multiply two values | [blue]`-1 * 2` -> -2
|
| [blue]`*` | _Product_ | Multiply two values | [blue]`-1 * 2` -> _-2_
|
||||||
| [blue]`/` | _Division_ | Divide the left value by the right one^(*)^ | [blue]`-10 / 2` -> 5
|
| [blue]`/` | _Integer division_ | Divide the left value by the right one^(*)^ | [blue]`-11 / 2` -> _-5_
|
||||||
| [blue]`%` | _Modulo_ | Remainder of the integer division | [blue]`5 % 2` -> 1
|
| [blue]`%` | _Modulo_ | Remainder of the integer division | [blue]`5 % 2` -> _1_
|
||||||
|===
|
|===
|
||||||
|
|
||||||
^(*)^ See also the _float division_ [blue]`./` below.
|
^(*)^ See also the _float division_ [blue]`./` below.
|
||||||
@@ -228,19 +228,19 @@ _dec-seq_ = _see-integer-literal-syntax_
|
|||||||
[cols="^1,^2,6,4"]
|
[cols="^1,^2,6,4"]
|
||||||
|===
|
|===
|
||||||
| Symbol | Operation | Description | Examples
|
| Symbol | Operation | Description | Examples
|
||||||
| [blue]`+` | _sum_ | Add two values | [blue]`4 + 0.5` -> 4.5
|
| [blue]`+` | _Sum_ | Add two values | [blue]`4 + 0.5` -> 4.5
|
||||||
| [blue]`-` | _subtraction_ | Subtract the right value from the left one | [blue]`4 - 0.5` -> 3.5
|
| [blue]`-` | _Subtraction_ | Subtract the right value from the left one | [blue]`4 - 0.5` -> 3.5
|
||||||
| [blue]`*` | _product_ | Multiply two values | [blue]`4 * 0.5` -> 2.0
|
| [blue]`*` | _Product_ | Multiply two values | [blue]`4 * 0.5` -> 2.0
|
||||||
| [blue]`/` | _Division_ | Divide the left value by the right one | [blue]`1.0 / 2` -> 0.5
|
| [blue]`/` | _Float division_ | Divide the left value by the right one | [blue]`1.0 / 2` -> 0.5
|
||||||
| [blue]`./`| _Float division_ | Force float division | [blue]`-1 ./ 2` -> -0.5
|
| [blue]`./`| _Forced float division_ | Force float division | [blue]`-1 ./ 2` -> -0.5
|
||||||
|===
|
|===
|
||||||
|
|
||||||
==== Fractions
|
==== Fractions
|
||||||
_Expr_ also supports fractions. Fraction literals are made with two integers separated by a vertical bar `|`.
|
_Expr_ also supports fractions. Fraction literals are made with two integers separated by a colon character `:`.
|
||||||
|
|
||||||
.Fraction literal syntax
|
.Fraction literal syntax
|
||||||
====
|
====
|
||||||
*_fraction_* = [__sign__] (_num-den-spec_ | _float-spec_) +
|
*_fraction_* = [__sign__] (_num-den-spec_ "**:**" _float-spec_) +
|
||||||
_sign_ = "**+**" | "**-**" +
|
_sign_ = "**+**" | "**-**" +
|
||||||
_num-den-spec_ = _digit-seq_ "**|**" _digit-seq_ +
|
_num-den-spec_ = _digit-seq_ "**|**" _digit-seq_ +
|
||||||
_float-spec_ = _dec-seq_ "**.**" [_dec-seq_] "**(**" _dec-seq_ "**)**" +
|
_float-spec_ = _dec-seq_ "**.**" [_dec-seq_] "**(**" _dec-seq_ "**)**" +
|
||||||
@@ -249,44 +249,44 @@ _digit-seq_ = _see-integer-literal-syntax_
|
|||||||
====
|
====
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
`>>>` [blue]`1 | 2` +
|
`>>>` [blue]`1 : 2` +
|
||||||
[green]`1|2`
|
[green]`1:2`
|
||||||
|
|
||||||
`>>>` [blue]`4|6` [gray]_// Fractions are always reduced to their lowest terms_ +
|
`>>>` [blue]`4:6` [gray]_// Fractions are always reduced to their lowest terms_ +
|
||||||
[green]`2|3`
|
[green]`2:3`
|
||||||
|
|
||||||
`>>>` [blue]`1|2 + 2|3` +
|
`>>>` [blue]`1:2 + 2:3` +
|
||||||
[green]`7|6`
|
[green]`7:6`
|
||||||
|
|
||||||
`>>>` [blue]`1|2 * 2|3` +
|
`>>>` [blue]`1:2 * 2:3` +
|
||||||
[green]`1|3`
|
[green]`1:3`
|
||||||
|
|
||||||
`>>>` [blue]`1|2 / 1|3` +
|
`>>>` [blue]`1:2 / 1:3` +
|
||||||
[green]`3|2`
|
[green]`3:2`
|
||||||
|
|
||||||
`>>>` [blue]`1|2 ./ 1|3` [gray]_// Force decimal division_ +
|
`>>>` [blue]`1:2 ./ 1:3` [gray]_// Force decimal division_ +
|
||||||
[green]`1.5`
|
[green]`1.5`
|
||||||
|
|
||||||
`>>>` [blue]`-1|2` +
|
`>>>` [blue]`-1:2` +
|
||||||
[green]`-1|2`
|
[green]`-1:2`
|
||||||
|
|
||||||
`>>>` [blue]`1|-2` [gray]_// Invalid sign specification_ +
|
`>>>` [blue]`1:-2` [gray]_// Invalid sign specification_ +
|
||||||
[red]_Eval Error: [1:3] infix operator "|" requires two non-nil operands, got 1_
|
[red]_Eval Error: [1:3] infix operator ":" requires two non-nil operands, got 1_
|
||||||
|
|
||||||
`>>>` [blue]`1|(-2)` +
|
`>>>` [blue]`1:(-2)` +
|
||||||
[green]`-1|2`
|
[green]`-1:2`
|
||||||
|
|
||||||
|
|
||||||
Fractions can be used together with integers and floats in expressions.
|
Fractions can be used together with integers and floats in expressions.
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
`>>>` [blue]`1|2 + 5` +
|
`>>>` [blue]`1:2 + 5` +
|
||||||
[green]`11|2`
|
[green]`11:2`
|
||||||
|
|
||||||
`>>>` [blue]`4 - 1|2` +
|
`>>>` [blue]`4 - 1:2` +
|
||||||
[green]`7|2`
|
[green]`7:2`
|
||||||
|
|
||||||
`>>>` [blue]`1.0 + 1|2` +
|
`>>>` [blue]`1.0 + 1:2` +
|
||||||
[green]`1.5`
|
[green]`1.5`
|
||||||
|
|
||||||
|
|
||||||
@@ -308,7 +308,7 @@ Strings are character sequences enclosed between two double quote [blue]`"`.
|
|||||||
`>>>` [blue]`"123\tabc"` +
|
`>>>` [blue]`"123\tabc"` +
|
||||||
[green]`123{nbsp}{nbsp}{nbsp}{nbsp}abc`
|
[green]`123{nbsp}{nbsp}{nbsp}{nbsp}abc`
|
||||||
|
|
||||||
Some arithmetic operators can also be used with strings.
|
Some arithmetic operators also apply to strings.
|
||||||
|
|
||||||
.String operators
|
.String operators
|
||||||
[cols="^1,^2,6,4"]
|
[cols="^1,^2,6,4"]
|
||||||
@@ -321,7 +321,7 @@ Some arithmetic operators can also be used with strings.
|
|||||||
| [blue]`*` | _repeat_ | Make _n_ copy of a string | [blue]`"one" * 2` -> _"oneone"_
|
| [blue]`*` | _repeat_ | Make _n_ copy of a string | [blue]`"one" * 2` -> _"oneone"_
|
||||||
|===
|
|===
|
||||||
|
|
||||||
The items of strings can be accessed using the square `[]` operator.
|
The charanters in a string can be accessed using the square `[]` operator.
|
||||||
|
|
||||||
.Item access syntax
|
.Item access syntax
|
||||||
====
|
====
|
||||||
@@ -340,10 +340,10 @@ The items of strings can be accessed using the square `[]` operator.
|
|||||||
`>>>` [blue]`s[1]` [gray]_// char at position 1 (starting from 0)_ +
|
`>>>` [blue]`s[1]` [gray]_// char at position 1 (starting from 0)_ +
|
||||||
[green]`"b"`
|
[green]`"b"`
|
||||||
|
|
||||||
`>>>` [blue]`s.[-1]` [gray]_// char at position -1, the rightmost one_ +
|
`>>>` [blue]`s[-1]` [gray]_// char at position -1, the rightmost one_ +
|
||||||
[green]`"d"`
|
[green]`"d"`
|
||||||
|
|
||||||
`>>>` [blue]`\#s` [gray]_// number of chars_ +
|
`>>>` [blue]`#s` [gray]_// number of chars_ +
|
||||||
[gren]`4`
|
[gren]`4`
|
||||||
|
|
||||||
`>>>` [blue]`#"abc"` [gray]_// number of chars_ +
|
`>>>` [blue]`#"abc"` [gray]_// number of chars_ +
|
||||||
@@ -369,9 +369,9 @@ Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relationa
|
|||||||
| [blue]`\<=` | _Less or Equal_ | True if the left value is less than or equal to the right one | [blue]`5 \<= 2` -> _false_ +
|
| [blue]`\<=` | _Less or Equal_ | True if the left value is less than or equal to the right one | [blue]`5 \<= 2` -> _false_ +
|
||||||
[blue]`"b" \<= "b"` -> _true_
|
[blue]`"b" \<= "b"` -> _true_
|
||||||
| [blue]`>` | _Greater_ | True if the left value is greater than the right one | [blue]`5 > 2` -> _true_ +
|
| [blue]`>` | _Greater_ | True if the left value is greater than the right one | [blue]`5 > 2` -> _true_ +
|
||||||
[blue]`"a" < "b"` -> _false_
|
[blue]`"a" > "b"` -> _false_
|
||||||
| [blue]`>=` | _Greater or Equal_ | True if the left value is greater than or equal to the right one | [blue]`5 >= 2` -> _true_ +
|
| [blue]`>=` | _Greater or Equal_ | True if the left value is greater than or equal to the right one | [blue]`5 >= 2` -> _true_ +
|
||||||
[blue]`"b" \<= "b"` -> _true_
|
[blue]`"b" >= "b"` -> _true_
|
||||||
|===
|
|===
|
||||||
|
|
||||||
^(*)^ See also the [blue]`in` operator in the _list_ and _dictionary_ sections.
|
^(*)^ See also the [blue]`in` operator in the _list_ and _dictionary_ sections.
|
||||||
@@ -388,7 +388,7 @@ Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relationa
|
|||||||
| [blue]`AND` / [blue]`&&` | _And_ | True if both left and right values are true | [blue]`false && true` -> _false_ +
|
| [blue]`AND` / [blue]`&&` | _And_ | True if both left and right values are true | [blue]`false && true` -> _false_ +
|
||||||
[blue]`"a" < "b" AND NOT (2 < 1)` -> _true_
|
[blue]`"a" < "b" AND NOT (2 < 1)` -> _true_
|
||||||
|
|
||||||
| [blue]`OR` / [blue]`\|\|` | _Or_ | True if at least one of the left and right values integers true| [blue]`false or true` -> _true_ +
|
| [blue]`OR` / [blue]`\|\|` | _Or_ | True if at least one of the left and right values integers is true| [blue]`false or true` -> _true_ +
|
||||||
[blue]`"a" == "b" OR (2 == 1)` -> _false_
|
[blue]`"a" == "b" OR (2 == 1)` -> _false_
|
||||||
|===
|
|===
|
||||||
|
|
||||||
@@ -413,7 +413,7 @@ _Expr_ supports list of mixed-type values, also specified by normal expressions.
|
|||||||
====
|
====
|
||||||
*_list_* = _empty-list_ | _non-empty-list_ +
|
*_list_* = _empty-list_ | _non-empty-list_ +
|
||||||
_empty-list_ = "**[]**" +
|
_empty-list_ = "**[]**" +
|
||||||
_non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value} "**]**" +
|
_non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value_} "**]**" +
|
||||||
====
|
====
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
@@ -439,11 +439,12 @@ _non-empty-list_ = "**[**" _any-value_ {"**,**" _any-value} "**]**" +
|
|||||||
|
|
||||||
| [blue]`+` | _Join_ | Joins two lists | [blue]`[1,2] + [3]` -> _[1,2,3]_
|
| [blue]`+` | _Join_ | Joins two lists | [blue]`[1,2] + [3]` -> _[1,2,3]_
|
||||||
| [blue]`-` | _Difference_ | Left list without elements in the right list | [blue]`[1,2,3] - [2]` -> _[1,3]_
|
| [blue]`-` | _Difference_ | Left list without elements in the right list | [blue]`[1,2,3] - [2]` -> _[1,3]_
|
||||||
| [blue]`>>` | _Front insertion_ | Insert an item in front | [blue]`0 >> [1,2]` -> _[0,1,2]_
|
| [blue]`+>` | _Front insertion_ | Insert an item in front | [blue]`0 +> [1,2]` -> _[0,1,2]_
|
||||||
| [blue]`<<` | _Back insertion_ | Insert an item at end | [blue]`[1,2] << 3` -> _[1,2,3]_
|
| [blue]`<+` | _Back insertion_ | Insert an item at end | [blue]`[1,2] <+ 3` -> _[1,2,3]_
|
||||||
| [blue]`[]` | _Item at index_ | Item at given position | [blue]`[1,2,3][1]` -> _2_
|
| [blue]`[]` | _Item at index_ | Item at given position | [blue]`[1,2,3][1]` -> _2_
|
||||||
| [blue]`in` | _Item in list_ | True if item is in list | [blue]`2 in [1,2,3]` -> _true_ +
|
| [blue]`in` | _Item in list_ | True if item is in list | [blue]`2 in [1,2,3]` -> _true_ +
|
||||||
[blue]`6 in [1,2,3]` -> _false_
|
[blue]`6 in [1,2,3]` -> _false_
|
||||||
|
| [blue]`#` | _Size_ | Number of items in a list | [blue]`#[1,2,3]` -> _3_
|
||||||
|===
|
|===
|
||||||
|
|
||||||
Array's items can be accessed using the index `[]` operator.
|
Array's items can be accessed using the index `[]` operator.
|
||||||
@@ -458,38 +459,63 @@ Array's items can be accessed using the index `[]` operator.
|
|||||||
*_slice_* = _string-expr_ "**[**" _integer-expr_ "**:**" _integer-expr_ "**]**"
|
*_slice_* = _string-expr_ "**[**" _integer-expr_ "**:**" _integer-expr_ "**]**"
|
||||||
====
|
====
|
||||||
|
|
||||||
.Items of list
|
.Examples: Getting items from lists
|
||||||
`>>>` [blue]`[1,2,3].1` +
|
`>>>` [blue]`[1,2,3][1]` +
|
||||||
[green]`2`
|
[green]`2`
|
||||||
|
|
||||||
`>>>` [blue]`list=[1,2,3]; list.1` +
|
|
||||||
[green]`2`
|
|
||||||
|
|
||||||
`>>>` [blue]`["one","two","three"].1` +
|
|
||||||
[green]`two`
|
|
||||||
|
|
||||||
`>>>` [blue]`list=["one","two","three"]; list.(2-1)` +
|
|
||||||
[green]`two`
|
|
||||||
|
|
||||||
`>>>` [blue]`list.(-1)` +
|
|
||||||
[green]`three`
|
|
||||||
|
|
||||||
`>>>` [blue]`list.(10)` +
|
|
||||||
[red]`Eval Error: [1:9] index 10 out of bounds`
|
|
||||||
|
|
||||||
`>>>` [blue]`#list` +
|
|
||||||
[green]`3`
|
|
||||||
|
|
||||||
`>>>` [blue]`index=2; ["a", "b", "c", "d"][index]` +
|
`>>>` [blue]`index=2; ["a", "b", "c", "d"][index]` +
|
||||||
[green]`c`
|
[green]`c`
|
||||||
|
|
||||||
|
|
||||||
`>>>` [blue]`["a", "b", "c", "d"][2:]` +
|
`>>>` [blue]`["a", "b", "c", "d"][2:]` +
|
||||||
[green]`["c", "d"]`
|
[green]`["c", "d"]`
|
||||||
|
|
||||||
|
`>>>` [blue]`list=[1,2,3]; list[1]` +
|
||||||
|
[green]`2`
|
||||||
|
|
||||||
|
`>>>` [blue]`["one","two","three"][1]` +
|
||||||
|
[green]`two`
|
||||||
|
|
||||||
|
`>>>` [blue]`list=["one","two","three"]; list[2-1]` +
|
||||||
|
[green]`two`
|
||||||
|
|
||||||
|
`>>>` [blue]`list[1]="six"; list` +
|
||||||
|
[green]`["one", "six", "three"]`
|
||||||
|
|
||||||
|
`>>>` [blue]`list[-1]` +
|
||||||
|
[green]`three`
|
||||||
|
|
||||||
|
`>>>` [blue]`list[10]` +
|
||||||
|
[red]`Eval Error: [1:9] index 10 out of bounds`
|
||||||
|
|
||||||
|
.Example: Number of elements in a list
|
||||||
|
`>>>` [blue]`#list` +
|
||||||
|
[green]`3`
|
||||||
|
|
||||||
|
.Examples: Element insertion
|
||||||
|
`>>>` [blue]`"first" >> list` +
|
||||||
|
[green]`["first", "one", "six", "three"]`
|
||||||
|
|
||||||
|
`>>>` [blue]`list << "last"` +
|
||||||
|
[green]`["first", "one", "six", "three", "last"]`
|
||||||
|
|
||||||
|
.Examples: Element in list
|
||||||
|
`>>>` [blue]`"six" in list` +
|
||||||
|
[green]`true`
|
||||||
|
|
||||||
|
`>>>` [blue]`"ten" in list` +
|
||||||
|
[green]`false`
|
||||||
|
|
||||||
|
.Examples: Concatenation and filtering
|
||||||
|
`>>>` [blue]`[1,2,3] + ["one", "two", "three"]` +
|
||||||
|
[green]`[1, 2, 3, "one", "two", "three"]`
|
||||||
|
|
||||||
|
`>>>` [blue]`[1,2,3,4] - [2,4]` +
|
||||||
|
[green]`[1, 3]`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
=== Dictionaries
|
=== Dictionaries
|
||||||
The _dictionary_, or _dict_, data-type is set of pairs _key/value_. It is also known as _map_ or _associative array_.
|
The _dictionary_, or _dict_, data-type represents sets of pairs _key/value_. It is also known as _map_ or _associative array_.
|
||||||
|
|
||||||
Dictionary literals are sequences of pairs separated by comma [blue]`,` enclosed between brace brackets.
|
Dictionary literals are sequences of pairs separated by comma [blue]`,` enclosed between brace brackets.
|
||||||
|
|
||||||
@@ -497,7 +523,7 @@ Dictionary literals are sequences of pairs separated by comma [blue]`,` enclosed
|
|||||||
====
|
====
|
||||||
*_dict_* = _empty-dict_ | _non-empty-dict_ +
|
*_dict_* = _empty-dict_ | _non-empty-dict_ +
|
||||||
_empty-dict_ = "**{}**" +
|
_empty-dict_ = "**{}**" +
|
||||||
_non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar_ "**:**" _any-value} "**}**" +
|
_non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar_ "**:**" _any-value_} "**}**" +
|
||||||
====
|
====
|
||||||
|
|
||||||
|
|
||||||
@@ -515,6 +541,7 @@ _non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar
|
|||||||
| [blue]`[]` | _Dict item value_ | Item value of given key | [blue]`{"one":1, "two":2}["two"]` -> _2_
|
| [blue]`[]` | _Dict item value_ | Item value of given key | [blue]`{"one":1, "two":2}["two"]` -> _2_
|
||||||
| [blue]`in` | _Key in dict_ | True if key is in dict | [blue]`"one" in {"one":1, "two":2}` -> _true_ +
|
| [blue]`in` | _Key in dict_ | True if key is in dict | [blue]`"one" in {"one":1, "two":2}` -> _true_ +
|
||||||
[blue]`"six" in {"one":1, "two":2}` -> _false_
|
[blue]`"six" in {"one":1, "two":2}` -> _false_
|
||||||
|
| [blue]`#` | _Size_ | Number of items in a dict | [blue]`#{1:"a",2:"b",3:"c"}` -> _3_
|
||||||
|===
|
|===
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
@@ -533,6 +560,9 @@ _non-empty-dict_ = "**{**" _key-scalar_ "**:**" _any-value_ {"**,**" _key-scalar
|
|||||||
`>>>` [blue]`d={"one":1, "two":2}; d["six"]=6; d` +
|
`>>>` [blue]`d={"one":1, "two":2}; d["six"]=6; d` +
|
||||||
[green]`{"two": 2, "one": 1, "six": 6}`
|
[green]`{"two": 2, "one": 1, "six": 6}`
|
||||||
|
|
||||||
|
`>>>` [blue]`#d` +
|
||||||
|
[green]`3`
|
||||||
|
|
||||||
|
|
||||||
== Variables
|
== Variables
|
||||||
_Expr_, like most programming languages, supports variables. A variable is an identifier with an assigned value. Variables are stored in _contexts_.
|
_Expr_, like most programming languages, supports variables. A variable is an identifier with an assigned value. Variables are stored in _contexts_.
|
||||||
@@ -551,18 +581,18 @@ NOTE: The assign operator [blue]`=` returns the value assigned to the variable.
|
|||||||
[green]`1`
|
[green]`1`
|
||||||
|
|
||||||
`>>>` [blue]`a_b=1+2` +
|
`>>>` [blue]`a_b=1+2` +
|
||||||
[green]`1+2`
|
[green]`3`
|
||||||
|
|
||||||
`>>>` [blue]`a_b` +
|
`>>>` [blue]`a_b` +
|
||||||
[green]`3`
|
[green]`3`
|
||||||
|
|
||||||
`>>>` [blue]`x = 5.2 * (9-3)` [gray]_// The assigned value has the typical approximation error of the float data-type_ +
|
`>>>` [blue]`x = 5.2 * (9-3)` [gray]_// The assigned value here has the typical approximation error of the float data-type_ +
|
||||||
[green]`31.200000000000003`
|
[green]`31.200000000000003`
|
||||||
|
|
||||||
`>>>` [blue]`x = 1; y = 2*x` +
|
`>>>` [blue]`x = 1; y = 2*x` +
|
||||||
[green]`2`
|
[green]`2`
|
||||||
|
|
||||||
`>>>` [blue]`_a=2` +
|
`>>>` [blue]`\_a=2` +
|
||||||
[red]`Parse Error: [1:2] unexpected token "_"`
|
[red]`Parse Error: [1:2] unexpected token "_"`
|
||||||
|
|
||||||
`>>>` [blue]`1=2` +
|
`>>>` [blue]`1=2` +
|
||||||
@@ -574,12 +604,12 @@ NOTE: The assign operator [blue]`=` returns the value assigned to the variable.
|
|||||||
=== [blue]`;` operator
|
=== [blue]`;` operator
|
||||||
The semicolon operator [blue]`;` is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The value of the latter is the final result.
|
The semicolon operator [blue]`;` is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The value of the latter is the final result.
|
||||||
|
|
||||||
.Mult-expression syntax
|
.Multi-expression syntax
|
||||||
====
|
====
|
||||||
*_multi-expression_* = _expression_ {"**;**" _expression_ }
|
*_multi-expression_* = _expression_ {"**;**" _expression_ }
|
||||||
====
|
====
|
||||||
|
|
||||||
An expression that contains [blue]`;` is called a _multi-expression_ and each component expressione is called a _sub-expression_.
|
An expression that contains [blue]`;` is called a _multi-expression_ and each component expression is called a _sub-expression_.
|
||||||
|
|
||||||
IMPORTANT: Technically [blue]`;` is not treated as a real operator. It acts as a separator in lists of expressions.
|
IMPORTANT: Technically [blue]`;` is not treated as a real operator. It acts as a separator in lists of expressions.
|
||||||
|
|
||||||
@@ -589,7 +619,7 @@ TIP: [blue]`;` can be used to set some variables before the final calculation.
|
|||||||
`>>>` [blue]`a=1; b=2; c=3; a+b+c` +
|
`>>>` [blue]`a=1; b=2; c=3; a+b+c` +
|
||||||
[green]`6`
|
[green]`6`
|
||||||
|
|
||||||
The value of each sub-expression is stored in the automatica variable _last_.
|
The value of each sub-expression is stored in the automatic variable _last_.
|
||||||
|
|
||||||
.Example
|
.Example
|
||||||
`>>>` [blue]`2+3; b=last+10; last` +
|
`>>>` [blue]`2+3; b=last+10; last` +
|
||||||
@@ -600,9 +630,10 @@ The value of each sub-expression is stored in the automatica variable _last_.
|
|||||||
[blue]`but` is an infixed operator. Its operands can be expressions of any type. It evaluates the left expression first, then the right expression. The value of the right expression is the final result.
|
[blue]`but` is an infixed operator. Its operands can be expressions of any type. It evaluates the left expression first, then the right expression. The value of the right expression is the final result.
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
[blue]`5 but 2` +
|
`>>>` [blue]`5 but 2` +
|
||||||
[green]`2` +
|
[green]`2`
|
||||||
[blue]`x=2*3 but x-1` +
|
|
||||||
|
`>>>` [blue]`x=2*3 but x-1` +
|
||||||
[green]`5`.
|
[green]`5`.
|
||||||
|
|
||||||
[blue]`but` behavior is very similar to [blue]`;`. The only difference is that [blue]`;` is not a true operator and can't be used inside parenthesis [blue]`(` and [blue]`)`.
|
[blue]`but` behavior is very similar to [blue]`;`. The only difference is that [blue]`;` is not a true operator and can't be used inside parenthesis [blue]`(` and [blue]`)`.
|
||||||
@@ -610,17 +641,21 @@ The value of each sub-expression is stored in the automatica variable _last_.
|
|||||||
=== Assignment operator [blue]`=`
|
=== Assignment operator [blue]`=`
|
||||||
The assignment operator [blue]`=` is used to define variables or to change their value in the evaluation context (see _ExprContext_).
|
The assignment operator [blue]`=` is used to define variables or to change their value in the evaluation context (see _ExprContext_).
|
||||||
|
|
||||||
The value on the left side of [blue]`=` must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation.
|
The value on the left side of [blue]`=` must be a variable identifier or an expression that evalutes to a variable. The value on the right side can be any expression and it becomes the result of the assignment operation.
|
||||||
|
|
||||||
.Example
|
.Examples
|
||||||
`>>>` [blue]`a=15+1`
|
`>>>` [blue]`a=15+1` +
|
||||||
[green]`16`
|
[green]`16`
|
||||||
|
|
||||||
|
`>>>` [blue]`L=[1,2,3]; L[1]=5; L` +
|
||||||
|
[green]`[1, 5, 3]`
|
||||||
|
|
||||||
|
|
||||||
=== Selector operator [blue]`? : ::`
|
=== Selector operator [blue]`? : ::`
|
||||||
The _selector operator_ is very similar to the _switch/case/default_ statement available in many programming languages.
|
The _selector operator_ is very similar to the _switch/case/default_ statement available in many programming languages.
|
||||||
|
|
||||||
.Selector literal Syntax
|
.Selector literal Syntax
|
||||||
|
====
|
||||||
_selector-operator_ = _select-expression_ "*?*" _selector-case_ { "*:*" _selector-case_ } ["*::*" _default-multi-expression_] +
|
_selector-operator_ = _select-expression_ "*?*" _selector-case_ { "*:*" _selector-case_ } ["*::*" _default-multi-expression_] +
|
||||||
_selector-case_ = [_match-list_] _case-value_ +
|
_selector-case_ = [_match-list_] _case-value_ +
|
||||||
_match-list_ = "*[*" _item_ {"*,*" _items_} "*]*" +
|
_match-list_ = "*[*" _item_ {"*,*" _items_} "*]*" +
|
||||||
@@ -628,6 +663,7 @@ _item_ = _expression_ +
|
|||||||
_case-multi-expression_ = "*{*" _multi-expression_ "*}*" +
|
_case-multi-expression_ = "*{*" _multi-expression_ "*}*" +
|
||||||
_multi-expression_ = _expression_ { "*;*" _expression_ } +
|
_multi-expression_ = _expression_ { "*;*" _expression_ } +
|
||||||
_default-multi-expression_ = _multi-expression_
|
_default-multi-expression_ = _multi-expression_
|
||||||
|
====
|
||||||
|
|
||||||
In other words, the selector operator evaluates the _select-expression_ on the left-hand side of the [blue]`?` symbol; it then compares the result obtained with the values listed in the __match-list__'s, from left to right. If the comparision finds a match with a value in a _match-list_, the associated _case-multi-expression_ is evaluted, and its result will be the final result of the selection operation.
|
In other words, the selector operator evaluates the _select-expression_ on the left-hand side of the [blue]`?` symbol; it then compares the result obtained with the values listed in the __match-list__'s, from left to right. If the comparision finds a match with a value in a _match-list_, the associated _case-multi-expression_ is evaluted, and its result will be the final result of the selection operation.
|
||||||
|
|
||||||
@@ -658,8 +694,8 @@ The [blue]`:` symbol (colon) is the separator of the selector-cases. Note that i
|
|||||||
[red]`Eval Error: [1:3] no case catches the value (10) of the selection expression`
|
[red]`Eval Error: [1:3] no case catches the value (10) of the selection expression`
|
||||||
|
|
||||||
|
|
||||||
=== Variable default value [blue]`??` and [blue]`?=`
|
=== Variable default value [blue]`??`, [blue]`?=`, and [blue]`?!`
|
||||||
The left operand of these two operators must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.
|
The left operand of first two operators, [blue]`??` and [blue]`?=`, must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.
|
||||||
|
|
||||||
IMPORTANT: If the left variable is defined, the right expression is not evaluated at all.
|
IMPORTANT: If the left variable is defined, the right expression is not evaluated at all.
|
||||||
|
|
||||||
@@ -667,8 +703,12 @@ The [blue]`??` operator do not change the status of the left variable.
|
|||||||
|
|
||||||
The [blue]`?=` assigns the calculated value of the right expression to the left variable.
|
The [blue]`?=` assigns the calculated value of the right expression to the left variable.
|
||||||
|
|
||||||
|
The third one, [blue]`?!`, is the alternate operator. If the variable on the left size is not defined, it returns [blue]_nil_. Otherwise it returns the result of the expressione on the right side.
|
||||||
|
|
||||||
|
IMPORTANT: If the left variable is NOT defined, the right expression is not evaluated at all.
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
`>>>` [blue]`var ?? (1+2)`' +
|
`>>>` [blue]`var ?? (1+2)` +
|
||||||
[green]`3`
|
[green]`3`
|
||||||
|
|
||||||
`>>>` [blue]`var` +
|
`>>>` [blue]`var` +
|
||||||
@@ -677,37 +717,51 @@ The [blue]`?=` assigns the calculated value of the right expression to the left
|
|||||||
`>>>` [blue]`var ?= (1+2)` +
|
`>>>` [blue]`var ?= (1+2)` +
|
||||||
[green]`3`
|
[green]`3`
|
||||||
|
|
||||||
`>>>` [blue]`var`
|
`>>>` [blue]`var` +
|
||||||
[green]`3`
|
[green]`3`
|
||||||
|
|
||||||
|
`>>>` [blue]`x ?! 5` +
|
||||||
|
[green]`nil`
|
||||||
|
|
||||||
|
`>>>` [blue]`x=1; x ?! 5` +
|
||||||
|
[green]`5`
|
||||||
|
|
||||||
|
`>>>` [blue]`y ?! (c=5); c` +
|
||||||
|
[red]`Eval Error: undefined variable or function "c"`
|
||||||
|
|
||||||
NOTE: These operators have a high priority, in particular higher than the operator [blue]`=`.
|
NOTE: These operators have a high priority, in particular higher than the operator [blue]`=`.
|
||||||
|
|
||||||
== Priorities of operators
|
== Priorities of operators
|
||||||
The table below shows all supported operators by decreasing priorities.
|
The table below shows all supported operators by decreasing priorities.
|
||||||
|
|
||||||
.Operators priorities
|
.Operators priorities
|
||||||
[cols="^2,^2,^2,^5,^6"]
|
[cols="^3,^2,^2,^5,^6"]
|
||||||
|===
|
|===
|
||||||
| Priority | Operators | Position | Operation | Operands and results
|
| Priority | Operator | Position | Operation | Operands and results
|
||||||
|
|
||||||
.2+|*ITEM*| [blue]`[`...`]` | _Postfix_ | _List item_| _list_ `[` _integer_ `]` -> _any_
|
.2+|*ITEM*| [blue]`[`...`]` | _Postfix_ | _List item_| _list_ `[` _integer_ `]` -> _any_
|
||||||
| [blue]`[`...`]` | _Postfix_ | _Dict item_ | _dict_ `[` _any_ `]` -> _any_
|
| [blue]`[`...`]` | _Postfix_ | _Dict item_ | _dict_ `[` _any_ `]` -> _any_
|
||||||
.2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `++` -> _integer_
|
.2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `++` -> _integer_
|
||||||
| [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `++` -> _any_
|
| [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `++` -> _any_
|
||||||
.2+|*DEFAULT*| [blue]`??` | _Infix_ | _Default value_| _variable_ `??` _any-expr_ -> _any_
|
.3+|*DEFAULT*| [blue]`??` | _Infix_ | _Default value_| _variable_ `??` _any-expr_ -> _any_
|
||||||
| [blue]`?=` | _Infix_ | _Default/assign value_| _variable_ `?=` _any-expr_ -> _any_
|
| [blue]`?=` | _Infix_ | _Default/assign value_| _variable_ `?=` _any-expr_ -> _any_
|
||||||
.1+| *ITER*^1^| [blue]`()` | _Prefix_ | _Iterator value_ | `()` _iterator_ -> _any_
|
| [blue]`?!` | _Infix_ | _Alternate value_| _variable_ `?!` _any-expr_ -> _any_
|
||||||
|
//.1+| *ITER*^1^| [blue]`()` | _Prefix_ | _Iterator value_ | `()` _iterator_ -> _any_
|
||||||
.1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ `!` -> _integer_
|
.1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ `!` -> _integer_
|
||||||
.3+|*SIGN*| [blue]`+`, [blue]`-` | _Prefix_ | _Change-sign_| (`+`\|`-`) _number_ -> _number_
|
.3+|*SIGN*| [blue]`+`, [blue]`-` | _Prefix_ | _Change-sign_| (`+`\|`-`) _number_ -> _number_
|
||||||
| [blue]`#` | _Prefix_ | _Lenght-of_ | `#` _collection_ -> _integer_
|
| [blue]`#` | _Prefix_ | _Lenght-of_ | `#` _collection_ -> _integer_
|
||||||
| [blue]`#` | _Prefix_ | _Size-of_ | `#` _iterator_ -> _integer_
|
| [blue]`#` | _Prefix_ | _Size-of_ | `#` _iterator_ -> _integer_
|
||||||
|
.2+|*BIT SHIFT*| [blue]`<<` | _Infix_ | _Left-Shift_ | _integer_ `<<` _integer_ -> _integer_
|
||||||
|
| [blue]`>>` | _Infix_ | _Right-Shift_ | _integer_ `>>` _iterator_ -> _integer_
|
||||||
.2+|*SELECT*| [blue]`? : ::` | _Multi-Infix_ | _Case-Selector_ | _any-expr_ `?` _case-list_ _case-expr_ `:` _case-list_ _case-expr_ ... `::` _default-expr_ -> _any_
|
.2+|*SELECT*| [blue]`? : ::` | _Multi-Infix_ | _Case-Selector_ | _any-expr_ `?` _case-list_ _case-expr_ `:` _case-list_ _case-expr_ ... `::` _default-expr_ -> _any_
|
||||||
| [blue]`? : ::` | _Multi-Infix_ | _Index-Selector_ | _int-expr_ `?` _case-expr_ `:` _case-expr_ ... `::` _default-expr_ -> _any_
|
| [blue]`? : ::` | _Multi-Infix_ | _Index-Selector_ | _int-expr_ `?` _case-expr_ `:` _case-expr_ ... `::` _default-expr_ -> _any_
|
||||||
.1+|*FRACT*| [blue]`\|` | _Infix_ | _Fraction_ | _integer_ `\|` _integer_ -> _fraction_
|
.1+|*FRACT*| [blue]`:` | _Infix_ | _Fraction_ | _integer_ `:` _integer_ -> _fraction_
|
||||||
.5+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ `*` _number_ -> _number_
|
.7+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ `*` _number_ -> _number_
|
||||||
| [blue]`*` | _Infix_ | _String-repeat_ | _string_ `*` _integer_ -> _string_
|
| [blue]`*` | _Infix_ | _String-repeat_ | _string_ `*` _integer_ -> _string_
|
||||||
| [blue]`/` | _Infix_ | _Division_ | _number_ `/` _number_ -> _number_
|
| [blue]`/` | _Infix_ | _Division_ | _number_ `/` _number_ -> _number_
|
||||||
| [blue]`./` | _Infix_ | _Float-division_ | __number__ `./` _number_ -> _float_
|
| [blue]`./` | _Infix_ | _Float-division_ | __number__ `./` _number_ -> _float_
|
||||||
|
| [blue]`/` | _Infix_ | _Split_ | _string_ `/` _string_ -> _list_
|
||||||
|
| [blue]`/` | _Infix_ | _Split_ | _string_ `/` integer -> _list_
|
||||||
| [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ `%` _integer_ -> _integer_
|
| [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ `%` _integer_ -> _integer_
|
||||||
.6+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ `+` _number_ -> _number_
|
.6+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ `+` _number_ -> _number_
|
||||||
| [blue]`+` | _Infix_ | _String-concat_ | (_string_\|_number_) `+` (_string_\|_number_) -> _string_
|
| [blue]`+` | _Infix_ | _String-concat_ | (_string_\|_number_) `+` (_string_\|_number_) -> _string_
|
||||||
@@ -715,6 +769,9 @@ The table below shows all supported operators by decreasing priorities.
|
|||||||
| [blue]`+` | _Infix_ | _Dict-join_ | _dict_ `+` _dict_ -> _dict_
|
| [blue]`+` | _Infix_ | _Dict-join_ | _dict_ `+` _dict_ -> _dict_
|
||||||
| [blue]`-` | _Infix_ | _Subtraction_ | _number_ `-` _number_ -> _number_
|
| [blue]`-` | _Infix_ | _Subtraction_ | _number_ `-` _number_ -> _number_
|
||||||
| [blue]`-` | _Infix_ | _List-difference_ | _list_ `-` _list_ -> _list_
|
| [blue]`-` | _Infix_ | _List-difference_ | _list_ `-` _list_ -> _list_
|
||||||
|
.1+|*BITWISE NOT*| [blue]`~` | _Prefix_ | _Binary Not_ | `~` _number_ -> _number_
|
||||||
|
.1+|*BITWISE AND*| [blue]`&` | _Infix_ | _Binary And_ | _number_ `&` _number_ -> _number_
|
||||||
|
.1+|*BITWISE OR*| [blue]`\|` | _Infix_ | _Binary Or_ | _number_ `\|` _number_ -> _number_
|
||||||
.8+|*RELATION*| [blue]`<` | _Infix_ | _Less_ | _comparable_ `<` _comparable_ -> _boolean_
|
.8+|*RELATION*| [blue]`<` | _Infix_ | _Less_ | _comparable_ `<` _comparable_ -> _boolean_
|
||||||
| [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ `\<=` _comparable_ -> _boolean_
|
| [blue]`\<=` | _Infix_ | _less-equal_ | _comparable_ `\<=` _comparable_ -> _boolean_
|
||||||
| [blue]`>` | _Infix_ | _Greater_ | _comparable_ `>` _comparable_ -> _boolean_
|
| [blue]`>` | _Infix_ | _Greater_ | _comparable_ `>` _comparable_ -> _boolean_
|
||||||
@@ -723,51 +780,207 @@ The table below shows all supported operators by decreasing priorities.
|
|||||||
| [blue]`!=` | _Infix_ | _Not-equal_ | _comparable_ `!=` _comparable_ -> _boolean_
|
| [blue]`!=` | _Infix_ | _Not-equal_ | _comparable_ `!=` _comparable_ -> _boolean_
|
||||||
| [blue]`in` | _Infix_ | _Member-of-list_ | _any_ `in` _list_ -> _boolean_
|
| [blue]`in` | _Infix_ | _Member-of-list_ | _any_ `in` _list_ -> _boolean_
|
||||||
| [blue]`in` | _Infix_ | _Key-of-dict_ | _any_ `in` _dict_ -> _boolean_
|
| [blue]`in` | _Infix_ | _Key-of-dict_ | _any_ `in` _dict_ -> _boolean_
|
||||||
.1+|*NOT*| [blue]`not` | _Prefix_ | _Not_ | `not` _boolean_ -> _boolean_
|
.1+|*LOGIC NOT*| [blue]`not` | _Prefix_ | _Not_ | `not` _boolean_ -> _boolean_
|
||||||
.2+|*AND*| [blue]`and` | _Infix_ | _And_ | _boolean_ `and` _boolean_ -> _boolean_
|
.2+|*LOGIC AND*| [blue]`and` | _Infix_ | _And_ | _boolean_ `and` _boolean_ -> _boolean_
|
||||||
| [blue]`&&` | _Infix_ | _And_ | _boolean_ `&&` _boolean_ -> _boolean_
|
| [blue]`&&` | _Infix_ | _And_ | _boolean_ `&&` _boolean_ -> _boolean_
|
||||||
.2+|*OR*| [blue]`or` | _Infix_ | _Or_ | _boolean_ `or` _boolean_ -> _boolean_
|
.2+|*LOGIC OR*| [blue]`or` | _Infix_ | _Or_ | _boolean_ `or` _boolean_ -> _boolean_
|
||||||
| [blue]`\|\|` | _Infix_ | _Or_ | _boolean_ `\|\|` _boolean_ -> _boolean_
|
| [blue]`\|\|` | _Infix_ | _Or_ | _boolean_ `\|\|` _boolean_ -> _boolean_
|
||||||
.3+|*ASSIGN*| [blue]`=` | _Infix_ | _Assignment_ | _identifier_ `=` _any_ -> _any_
|
.2+|*INSERT*| [blue]`+>` | _Infix_ | _Prepend_ | _any_ `+>` _list_ -> _list_
|
||||||
| [blue]`>>` | _Infix_ | _Front-insert_ | _any_ `>>` _list_ -> _list_
|
| [blue]`<+` | _Infix_ | _Append_ | _list_ `<+` _any_ -> _list_
|
||||||
| [blue]`<<` | _Infix_ | _Back-insert_ | _list_ `<<` _any_ -> _list_
|
.2+|*ASSIGN*| [blue]`=` | _Infix_ | _Assignment_ | _identifier_ `=` _any_ -> _any_
|
||||||
|
4+| _See also the special assignment operators table below_
|
||||||
.1+|*BUT*| [blue]`but` | _Infix_ | _But_ | _any_ `but` _any_ -> _any_
|
.1+|*BUT*| [blue]`but` | _Infix_ | _But_ | _any_ `but` _any_ -> _any_
|
||||||
.1+|*RANGE*| [blue]`:` | _Infix_ | _Index-range_ | _integer_ `:` _integer_ -> _integer-pair_
|
.1+|*RANGE*| [blue]`:` | _Infix_ | _Index-range_ | _integer_ `:` _integer_ -> _integer-pair_
|
||||||
|===
|
|===
|
||||||
|
|
||||||
^1^ Experimental
|
//^1^ Experimental
|
||||||
|
|
||||||
|
.Special assignment perators
|
||||||
|
[cols="^2,^2,^4,^6"]
|
||||||
|
|===
|
||||||
|
| Priority | Operator | Operation |Equivalent operation
|
||||||
|
|
||||||
|
.9+|*ASSIGN*| [blue]`+=` | _Sum & Assign_ | _var_ `\+=` _expr_ +
|
||||||
|
short for +
|
||||||
|
_var_ `=` _value-of-var_ `+` _expr_
|
||||||
|
| [blue]`-=` | _Subtract & Assign_ | _var_ `-=` _expr_ +
|
||||||
|
short for +
|
||||||
|
_var_ `=` _value-of-var_ `-` _expr_
|
||||||
|
| [blue]`*=` | _Multiply & Assign_ | _var_ `\*=` _expr_ +
|
||||||
|
short for +
|
||||||
|
_var_ `=` _value-of-var_ `*` _expr_
|
||||||
|
| [blue]`/=` | _Divide & Assign_ | _var_ `/=` _expr_ +
|
||||||
|
short for +
|
||||||
|
_var_ `=` _value-of-var_ `/` _expr_
|
||||||
|
| [blue]`%=` | _Remainder & Assign_ | _var_ `%=` _expr_ +
|
||||||
|
short for +
|
||||||
|
_var_ `=` _value-of-var_ `%` _expr_
|
||||||
|
| [blue]`&=` | _Bitwise and & Assign_ | _var_ `&=` _expr_ +
|
||||||
|
short for +
|
||||||
|
_var_ `=` _value-of-var_ `&` _expr_
|
||||||
|
| [blue]`\|=` | _Bitwise or & Assign_ | _var_ `\|=` _expr_ +
|
||||||
|
short for +
|
||||||
|
_var_ `=` _value-of-var_ `\|` _expr_
|
||||||
|
| [blue]`<\<=` | _Left shift & Assign_ | _var_ `<\<=` _expr_ +
|
||||||
|
short for +
|
||||||
|
_var_ `=` _value-of-var_ `<<` _expr_
|
||||||
|
| [blue]`>>=` | _Right shift & Assign_ | _var_ `>>=` _expr_ +
|
||||||
|
short for +
|
||||||
|
_var_ `=` _value-of-var_ `>>` _expr_
|
||||||
|
|===
|
||||||
|
|
||||||
== Functions
|
== Functions
|
||||||
Functions in _Expr_ are very similar to functions available in many programming languages. Actually, _Expr_ supports two types of function, _expr-functions_ and _go-functions_.
|
Functions in _Expr_ are very similar to functions available in many programming languages. Currently, _Expr_ supports two types of function, _expr-functions_ and _go-functions_.
|
||||||
|
|
||||||
* _expr-functions_ are defined using _Expr_'s syntax. They can be passed as arguments to other functions and can be returned from functions. Moreover, they bind themselves to the defining context, thus becoming closures.
|
* _expr-functions_ are defined using _Expr_'s syntax. They can be passed as arguments to other functions and can be returned from functions. Moreover, they bind themselves to the defining context, thus becoming closures.
|
||||||
* _go-functions_ are regular Golang functions callable from _Expr_ expressions. They are defined in Golang source files called _modules_ and compiled within the _Expr_ package. To make Golang functions available in _Expr_ contextes, it is required to _import_ the module in which they are defined.
|
* _go-functions_ are regular Golang functions callable from _Expr_ expressions. They are defined in Golang source files called _modules_ and compiled within the _Expr_ package. To make Golang functions available in _Expr_ contextes, it is required to activate the builtin module or to load the plugin module in which they are defined.
|
||||||
|
|
||||||
|
|
||||||
=== _Expr_ function definition
|
=== _Expr_ function definition
|
||||||
A function is identified and referenced by its name. It can have zero or more parameter. _Expr_ functions also support optional parameters.
|
A function is identified and referenced by its name. It can have zero or more parameter. _Expr_ functions also support optional parameters and passing paramters by name.
|
||||||
|
|
||||||
. Expr's function definition syntax
|
.Expr's function definition syntax
|
||||||
====
|
====
|
||||||
*_function-definition_* = _identifier_ "**=**" "**func(**" [_param-list_] "**)**" "**{**" _multi-expression_ "**}**"
|
*_function-definition_* = _identifier_ "**=**" "**func(**" [_formal-param-list_] "**)**" "**{**" _multi-expression_ "**}**" +
|
||||||
_param_list_ = _required-param-list_ [ "**,**" _optional-param-list_ ]
|
_formal-param_list_ = _required-param-list_ [ "**,**" _optional-param-list_ ] +
|
||||||
_required-param-list_ = _identifier_ { "**,**" _identifier_ }
|
_required-param-list_ = _identifier_ { "**,**" _identifier_ } +
|
||||||
_optional-param-list_ = _optional-parm_ { "**,**" _optional-param_ }
|
_optional-param-list_ = _optional-parm_ { "**,**" _optional-param_ } +
|
||||||
_optional-param_ = _identifier_ "**=**" _any-expr_
|
_optional-param_ = _param-name_ "**=**" _any-expr_ +
|
||||||
|
_param-name_ = _identifier_
|
||||||
====
|
====
|
||||||
|
|
||||||
.Examples
|
.Examples
|
||||||
#TODO#
|
`>>>` [gray]_// A simple function: it takes two parameters and returns their "sum"_**^(*)^** +
|
||||||
|
`>>>` [blue]`sum = func(a, b){ a + b }` +
|
||||||
|
[green]`sum(a, b):any{}`
|
||||||
|
|
||||||
|
^(\*)^ Since the plus, *+*, operator is defined for multiple data-types, the _sum()_ function can be used for any pair of that types.
|
||||||
|
|
||||||
|
`>>>` [gray]_// A more complex example: recursive calculation of the n-th value of Fibonacci's sequence_ +
|
||||||
|
`>>>` [blue]`fib = func(n){ n ? [0] {0}: [1] {1} :: {fib(n-1)+fib(n-2)} }` +
|
||||||
|
[green]`fib(n):any{}`
|
||||||
|
|
||||||
|
|
||||||
|
`>>>` [gray]_// Same function fib() but entered by splitting it over mulple text lines_ +
|
||||||
|
`>>>` [blue]`fib = func(n){ \` +
|
||||||
|
`\...` [blue]`{nbsp}{nbsp}n ? \` +
|
||||||
|
`\...` [blue]`{nbsp}{nbsp}{nbsp}{nbsp}[0] {0} : \` +
|
||||||
|
`\...` [blue]`{nbsp}{nbsp}{nbsp}{nbsp}[1] {1} :: \` +
|
||||||
|
`\...` [blue]`{nbsp}{nbsp}{nbsp}{nbsp}{ \` +
|
||||||
|
`\...` [blue]`{nbsp}{nbsp}{nbsp}{nbsp}{nbsp}{nbsp}fib(n-1) + fib(n-2) \` +
|
||||||
|
`\...` [blue]`{nbsp}{nbsp}{nbsp}{nbsp}} \` +
|
||||||
|
`\...` [blue]`}` +
|
||||||
|
[green]`fib(n):any{}`
|
||||||
|
|
||||||
|
`>>>` [gray]_// Required and optional parameters_ +
|
||||||
|
`>>>` [blue]`measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? [true] {"s"} :: {""}}` +
|
||||||
|
[green]`measure(value, unit="meter"):any{}`
|
||||||
|
|
||||||
|
|
||||||
=== _Golang_ function definition
|
=== _Golang_ function definition
|
||||||
Description of how to define Golan functions and how to bind them to _Expr_ are topics treated in another document that I'll write, one day, maybe.
|
Description of how to define Golang functions and how to bind them to _Expr_ are topics covered in another document that I'll write, one day, maybe.
|
||||||
|
|
||||||
=== Function calls
|
=== Function calls
|
||||||
#TODO: function calls operations#
|
To call a function, either Expr or Golang type, it is necessary to specify its name and, at least, its required parameters.
|
||||||
|
|
||||||
Functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the reference operator [blue]`@` it is possibile to export local definition to the calling context.
|
.Function invocation syntax
|
||||||
|
====
|
||||||
|
*_function-call_* = _identifier_ "**(**" _actual-param-list_ "**)**" +
|
||||||
|
_actual-param-list_ = [_positional-params_] [_named-parameters_] +
|
||||||
|
_positional-params_ = _any-value_ { "*,*" _any-value_ } +
|
||||||
|
_named-params_ = _param-name_ "**=**" _any-value_ { "*,*" _param-name_ "**=**" _any-value_ } +
|
||||||
|
_param-name_ = _identifier_
|
||||||
|
====
|
||||||
|
|
||||||
|
.Examples of calling the `sum()` functions defined above
|
||||||
|
`>>>` [gray]_// sum of two integers_ +
|
||||||
|
`>>>` [blue]`sum(-6, 2)` +
|
||||||
|
[green]`-4` +
|
||||||
|
`>>>` [gray]_// same as above but passing the parameters by name_ +
|
||||||
|
`>>>` [blue]`sum(a=-6, b=2)` +
|
||||||
|
[green]`-4` +
|
||||||
|
`>>>` [gray]_// again, but swapping parameter positions (see the diff() examples below)_ +
|
||||||
|
`>>>` [blue]`sum(b=2, a=-6)` +
|
||||||
|
[green]`-4` +
|
||||||
|
`>>>` [gray]_// sum of a fraction and an integer_ +
|
||||||
|
`>>>` [blue]`sum(3|2, 2)` +
|
||||||
|
[green]`7|2` +
|
||||||
|
`>>>` [gray]_// sum of two strings_ +
|
||||||
|
`>>>` [blue]`sum("bye", "-bye")` +
|
||||||
|
[green]`"bye-bye"` +
|
||||||
|
`>>>` [gray]_// sum of two lists_ +
|
||||||
|
`>>>` [blue]`sum(["one", 1], ["two", 2])` +
|
||||||
|
[green]`["one", 1, "two", 2]`
|
||||||
|
|
||||||
|
.Examples of calling a function with parameters passed by name
|
||||||
|
`>>>` [gray]_// diff(a,b) calculates a-b_ +
|
||||||
|
`>>>` [blue]`diff = func(a,b){a-b}` +
|
||||||
|
[green]`diff(a, b):any{}` +
|
||||||
|
`>>>` [gray]_// simple invocation_ +
|
||||||
|
`>>>` [blue]`diff(10,8)` +
|
||||||
|
[green]`2` +
|
||||||
|
`>>>` [gray]_// swapped parameters passed by name_ +
|
||||||
|
`>>>` [blue]`diff(b=8,a=10)` +
|
||||||
|
[green]`2`
|
||||||
|
|
||||||
|
.Examples of calling the `fib()` function defined above
|
||||||
|
`>>>` [gray]_// Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ..._ +
|
||||||
|
`>>>` [blue]`fib(6)` +
|
||||||
|
[green]`8` +
|
||||||
|
`>>>` [blue]`fib(9)` +
|
||||||
|
[green]`34`
|
||||||
|
|
||||||
|
.Examples of calling the `measure()` functions defined above
|
||||||
|
`>>>` [gray]_// simple call_ +
|
||||||
|
`>>>` [blue]`measure(10,"litre")` +
|
||||||
|
[green]`"10 litres"` +
|
||||||
|
`>>>` [gray]_// accept the default unit_ +
|
||||||
|
`>>>` [blue]`measure(8)` +
|
||||||
|
[green]`"8 meters"` +
|
||||||
|
`>>>` [gray]_// without the required parameter 'value'_ +
|
||||||
|
`>>>` [blue]`measure(unit="degrees"))` +
|
||||||
|
[red]`Eval Error: measure(): missing params -- value`
|
||||||
|
|
||||||
|
.Examples of context binding (closures)
|
||||||
|
`>>>` [blue]`factory = func(n=2){ func(x){x*n} }` +
|
||||||
|
[green]`factory(n=2):any{}` +
|
||||||
|
`>>>` [blue]`double = factory()` +
|
||||||
|
[green]`double(x):any{}` +
|
||||||
|
`>>>` [blue]`triple = factory(3)` +
|
||||||
|
[green]`triple(x):any{}` +
|
||||||
|
`>>>` [blue]`double(5)` +
|
||||||
|
[green]`10` +
|
||||||
|
`>>>` [blue]`triple(5)` +
|
||||||
|
[green]`15`
|
||||||
|
|
||||||
|
|
||||||
|
=== Function context
|
||||||
|
Functions compute values in a local context (scope) that do not make effects on the calling context. This is the normal behavior. Using the _clone_ modifier [blue]`@` it is possibile to export local definition to the calling context. The clone modifier must be used as prefix to variable names and it is part of the name. E.g. [blue]`@x` is not the same as [blue]`x`; they are two different and un related variables.
|
||||||
|
|
||||||
|
Clone variables are normal local variables. The only diffence will appear when the defining function terminate, just before the destruction of its local context. At that point, all local clone variables are cloned in the calling context with the same names but the [blue]`@` symbol.
|
||||||
|
|
||||||
|
.Example
|
||||||
|
`>>>` [blue]`f = func() { @x = 3; x = 5 }` [gray]_// f() declares two *different* local variables: ``@x`` and ``x``_ +
|
||||||
|
[green]`f():any{}` +
|
||||||
|
`>>>` [blue]`f()` [gray]_// The multi-expression (two) in f() is calculated and the last result is returned_ +
|
||||||
|
[green]`5` +
|
||||||
|
`>>>` [blue]`x` [gray]_// The `x` variable was not defined in the main context before the f() invocation. It appears in the main context by cloning the `@x` variable, local to f() after its termnation._ +
|
||||||
|
[green]`3`
|
||||||
|
|
||||||
|
NOTE: The clone modifier [blue]`@` does not make a variable a reference variable, as the ampersand character `&` does in languages such as C and C++.
|
||||||
|
|
||||||
|
[IMPORTANT]
|
||||||
|
====
|
||||||
|
The clone modifier can also be used to declare the formal parameters of functions, because they are local variables too.
|
||||||
|
|
||||||
|
.Example
|
||||||
|
`>>>` [blue]`g = func(@p) {2+@p}`
|
||||||
|
g(@p):any{}`
|
||||||
|
`>>>` [blue]`g(9)`
|
||||||
|
11`
|
||||||
|
`>>>` [blue]`p
|
||||||
|
9
|
||||||
|
====
|
||||||
|
|
||||||
== Iterators
|
== Iterators
|
||||||
#TODO: function calls operations#
|
#TODO: function calls operations#
|
||||||
|
|||||||
+538
-163
File diff suppressed because it is too large
Load Diff
+14
-12
@@ -50,9 +50,9 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
|
|||||||
} else if s[0] == '+' {
|
} else if s[0] == '+' {
|
||||||
s = s[1:]
|
s = s[1:]
|
||||||
}
|
}
|
||||||
// if strings.HasSuffix(s, "()") {
|
// if strings.HasSuffix(s, "()") {
|
||||||
// s = s[0 : len(s)-2]
|
// s = s[0 : len(s)-2]
|
||||||
// }
|
// }
|
||||||
s = strings.TrimSuffix(s, "()")
|
s = strings.TrimSuffix(s, "()")
|
||||||
parts = strings.SplitN(s, ".", 2)
|
parts = strings.SplitN(s, ".", 2)
|
||||||
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
|
||||||
@@ -124,36 +124,38 @@ func (f *FractionType) String() string {
|
|||||||
func (f *FractionType) ToString(opt FmtOpt) string {
|
func (f *FractionType) ToString(opt FmtOpt) string {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
if opt&MultiLine == 0 {
|
if opt&MultiLine == 0 {
|
||||||
sb.WriteString(fmt.Sprintf("%d|%d", f.num, f.den))
|
sb.WriteString(fmt.Sprintf("%d:%d", f.num, f.den))
|
||||||
} else {
|
} else {
|
||||||
var s, num string
|
var sign, num string
|
||||||
if f.num < 0 && opt&TTY == 0 {
|
if f.num < 0 && opt&TTY == 0 {
|
||||||
num = strconv.FormatInt(-f.num, 10)
|
num = strconv.FormatInt(-f.num, 10)
|
||||||
s = "-"
|
sign = "-"
|
||||||
} else {
|
} else {
|
||||||
num = strconv.FormatInt(f.num, 10)
|
num = strconv.FormatInt(f.num, 10)
|
||||||
}
|
}
|
||||||
den := strconv.FormatInt(f.den, 10)
|
den := strconv.FormatInt(f.den, 10)
|
||||||
size := max(len(num), len(den))
|
size := max(len(num), len(den))
|
||||||
if opt&TTY != 0 {
|
if opt&TTY != 0 {
|
||||||
sb.WriteString(fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, s+num)))
|
sNum := fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, sign+num))
|
||||||
|
sb.WriteString(sNum)
|
||||||
} else {
|
} else {
|
||||||
if len(s) > 0 {
|
if len(sign) > 0 {
|
||||||
sb.WriteString(" ")
|
sb.WriteString(" ")
|
||||||
}
|
}
|
||||||
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, num)))
|
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, num)))
|
||||||
sb.WriteByte('\n')
|
sb.WriteByte('\n')
|
||||||
if len(s) > 0 {
|
if len(sign) > 0 {
|
||||||
sb.WriteString(s)
|
sb.WriteString(sign)
|
||||||
sb.WriteByte(' ')
|
sb.WriteByte(' ')
|
||||||
}
|
}
|
||||||
sb.WriteString(strings.Repeat("-", size))
|
sb.WriteString(strings.Repeat("-", size))
|
||||||
sb.WriteByte('\n')
|
sb.WriteByte('\n')
|
||||||
if len(s) > 0 {
|
if len(sign) > 0 {
|
||||||
sb.WriteString(" ")
|
sb.WriteString(" ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den)))
|
sDen := fmt.Sprintf("%[1]*s", size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den))
|
||||||
|
sb.WriteString(sDen)
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.String()
|
return sb.String()
|
||||||
|
|||||||
+2
-2
@@ -348,8 +348,8 @@ func CallFunctionByArgs(parentCtx ExprContext, name string, args []any) (result
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func CallFunctionByParams(parentCtx ExprContext, name string, params map[string]any) (result any, err error) {
|
func CallFunctionByParams(parentCtx ExprContext, name string, actualParams map[string]any) (result any, err error) {
|
||||||
var actualParams map[string]any
|
//var actualParams map[string]any
|
||||||
if info, exists := GetFuncInfo(parentCtx, name); exists {
|
if info, exists := GetFuncInfo(parentCtx, name); exists {
|
||||||
functor := info.Functor()
|
functor := info.Functor()
|
||||||
ctx := info.AllocContext(parentCtx)
|
ctx := info.AllocContext(parentCtx)
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
module git.portale-stac.it/go-pkg/expr
|
module git.portale-stac.it/go-pkg/expr
|
||||||
|
|
||||||
go 1.21.6
|
go 1.22.0
|
||||||
|
|
||||||
|
toolchain go1.23.3
|
||||||
|
|
||||||
|
require golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe h1:bWYrKmmfv37uNgXTdwkLSKYiYPJ1yfWmjBnvtMyAYzk=
|
||||||
|
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe/go.mod h1:alTKUpAJ/zbp17qvZwcFNwzufrb5DljMDY4mgJlIHao=
|
||||||
|
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
|
||||||
|
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||||
+6
-4
@@ -5,7 +5,7 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
// "errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -33,6 +33,8 @@ type Iterator interface {
|
|||||||
|
|
||||||
type ExtIterator interface {
|
type ExtIterator interface {
|
||||||
Iterator
|
Iterator
|
||||||
|
Reset() error
|
||||||
|
Clean() error
|
||||||
HasOperation(name string) bool
|
HasOperation(name string) bool
|
||||||
CallOperation(name string, args map[string]any) (value any, err error)
|
CallOperation(name string, args map[string]any) (value any, err error)
|
||||||
}
|
}
|
||||||
@@ -41,6 +43,6 @@ func errNoOperation(name string) error {
|
|||||||
return fmt.Errorf("no %s() function defined in the data-source", name)
|
return fmt.Errorf("no %s() function defined in the data-source", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func errInvalidDataSource() error {
|
// func errInvalidDataSource() error {
|
||||||
return errors.New("invalid data-source")
|
// return errors.New("invalid data-source")
|
||||||
}
|
// }
|
||||||
|
|||||||
+9
-3
@@ -99,7 +99,9 @@ func (it *ListIterator) CallOperation(name string, args map[string]any) (v any,
|
|||||||
case NextName:
|
case NextName:
|
||||||
v, err = it.Next()
|
v, err = it.Next()
|
||||||
case ResetName:
|
case ResetName:
|
||||||
v, err = it.Reset()
|
err = it.Reset()
|
||||||
|
case CleanName:
|
||||||
|
err = it.Clean()
|
||||||
case IndexName:
|
case IndexName:
|
||||||
v = int64(it.Index())
|
v = int64(it.Index())
|
||||||
case CurrentName:
|
case CurrentName:
|
||||||
@@ -147,8 +149,12 @@ func (it *ListIterator) Count() int {
|
|||||||
return it.count
|
return it.count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *ListIterator) Reset() (bool, 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 true, nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *ListIterator) Clean() (error) {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -92,6 +92,7 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
|
|
||||||
if ds != nil {
|
if ds != nil {
|
||||||
var dc *dataCursor
|
var dc *dataCursor
|
||||||
|
dcCtx := ctx.Clone()
|
||||||
if initFunc, exists := ds[InitName]; exists && initFunc != nil {
|
if initFunc, exists := ds[InitName]; exists && initFunc != nil {
|
||||||
var args []any
|
var args []any
|
||||||
var resource any
|
var resource any
|
||||||
@@ -109,9 +110,10 @@ func evalIterator(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
if resource, err = initFunc.InvokeNamed(initCtx, InitName, actualParams); err != nil {
|
if resource, err = initFunc.InvokeNamed(initCtx, InitName, actualParams); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dcCtx := ctx.Clone()
|
|
||||||
exportObjects(dcCtx, initCtx)
|
exportObjects(dcCtx, initCtx)
|
||||||
dc = NewDataCursor(dcCtx, ds, resource)
|
dc = NewDataCursor(dcCtx, ds, resource)
|
||||||
|
} else {
|
||||||
|
dc = NewDataCursor(dcCtx, ds, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
v = dc
|
v = dc
|
||||||
|
|||||||
@@ -99,7 +99,122 @@ func evalAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-------- assign term
|
||||||
|
|
||||||
|
func newOpAssignTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priAssign,
|
||||||
|
evalFunc: evalOpAssign,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCollectionItemValue(ctx ExprContext, collectionTerm, keyListTerm *term) (value any, err error) {
|
||||||
|
var collectionValue, keyListValue, keyValue any
|
||||||
|
var keyList *ListType
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if collectionValue, err = collectionTerm.compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if keyListValue, err = keyListTerm.compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 {
|
||||||
|
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if keyValue = (*keyList)[0]; keyValue == nil {
|
||||||
|
err = keyListTerm.Errorf("index/key is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch collection := collectionValue.(type) {
|
||||||
|
case *ListType:
|
||||||
|
if index, ok := keyValue.(int64); ok {
|
||||||
|
value = (*collection)[index]
|
||||||
|
} else {
|
||||||
|
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue))
|
||||||
|
}
|
||||||
|
case *DictType:
|
||||||
|
value = (*collection)[keyValue]
|
||||||
|
default:
|
||||||
|
err = collectionTerm.Errorf("collection expected")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAssignValue(ctx ExprContext, leftTerm *term) (value any, err error) {
|
||||||
|
if leftTerm.symbol() == SymIndex {
|
||||||
|
value, err = getCollectionItemValue(ctx, leftTerm.children[0], leftTerm.children[1])
|
||||||
|
} else {
|
||||||
|
value, _ = ctx.GetVar(leftTerm.source())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalOpAssign(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var rightValue, leftValue any
|
||||||
|
if err = opTerm.checkOperands(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
leftTerm := opTerm.children[0]
|
||||||
|
leftSym := leftTerm.symbol()
|
||||||
|
if leftSym != SymVariable && leftSym != SymIndex {
|
||||||
|
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rightChild := opTerm.children[1]
|
||||||
|
|
||||||
|
if rightValue, err = rightChild.compute(ctx); err == nil {
|
||||||
|
if leftValue, err = getAssignValue(ctx, leftTerm); err == nil {
|
||||||
|
switch opTerm.symbol() {
|
||||||
|
case SymPlusEqual:
|
||||||
|
v, err = sumValues(opTerm, leftValue, rightValue)
|
||||||
|
case SymMinusEqual:
|
||||||
|
v, err = diffValues(opTerm, leftValue, rightValue)
|
||||||
|
case SymStarEqual:
|
||||||
|
v, err = mulValues(opTerm, leftValue, rightValue)
|
||||||
|
case SymSlashEqual:
|
||||||
|
v, err = divValues(opTerm, leftValue, rightValue)
|
||||||
|
case SymPercEqual:
|
||||||
|
v, err = remainderValues(opTerm, leftValue, rightValue)
|
||||||
|
case SymAmpersandEqual:
|
||||||
|
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
||||||
|
case SymVertBarEqual:
|
||||||
|
v, err = bitwiseOr(opTerm, leftValue, rightValue)
|
||||||
|
case SymCaretEqual:
|
||||||
|
v, err = bitwiseXor(opTerm, leftValue, rightValue)
|
||||||
|
case SymDoubleLessEqual:
|
||||||
|
v, err = bitLeftShift(opTerm, leftValue, rightValue)
|
||||||
|
case SymDoubleGreaterEqual:
|
||||||
|
v, err = bitRightShift(opTerm, leftValue, rightValue)
|
||||||
|
default:
|
||||||
|
err = opTerm.Errorf("unsupported assign operator %q", opTerm.source())
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = assignValue(ctx, leftTerm, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymEqual, newAssignTerm)
|
registerTermConstructor(SymEqual, newAssignTerm)
|
||||||
|
registerTermConstructor(SymPlusEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymMinusEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymStarEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymSlashEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymPercEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymDoubleLessEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymDoubleGreaterEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymAmpersandEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymVertBarEqual, newOpAssignTerm)
|
||||||
|
registerTermConstructor(SymCaretEqual, newOpAssignTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-bitwise.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
//-------- Bitwise NOT term
|
||||||
|
|
||||||
|
func newBitwiseNotTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 1),
|
||||||
|
position: posPrefix,
|
||||||
|
priority: priBitwiseNot,
|
||||||
|
evalFunc: evalBitwiseNot,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalBitwiseNot(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var value any
|
||||||
|
|
||||||
|
if value, err = opTerm.evalPrefix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsInteger(value) {
|
||||||
|
i, _ := value.(int64)
|
||||||
|
v = ^i
|
||||||
|
} else {
|
||||||
|
err = opTerm.errIncompatibleType(value)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------- Bitwise AND term
|
||||||
|
|
||||||
|
func newBitwiseAndTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priBitwiseAnd,
|
||||||
|
evalFunc: evalBitwiseAnd,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bitwiseAnd(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
|
var leftInt, rightInt int64
|
||||||
|
var lok, rok bool
|
||||||
|
|
||||||
|
leftInt, lok = leftValue.(int64)
|
||||||
|
rightInt, rok = rightValue.(int64)
|
||||||
|
|
||||||
|
if lok && rok {
|
||||||
|
v = leftInt & rightInt
|
||||||
|
} else {
|
||||||
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalBitwiseAnd(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------- Bitwise OR term
|
||||||
|
|
||||||
|
func newBitwiseOrTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priBitwiseOr,
|
||||||
|
evalFunc: evalBitwiseOr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bitwiseOr(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
|
var leftInt, rightInt int64
|
||||||
|
var lok, rok bool
|
||||||
|
|
||||||
|
leftInt, lok = leftValue.(int64)
|
||||||
|
rightInt, rok = rightValue.(int64)
|
||||||
|
|
||||||
|
if lok && rok {
|
||||||
|
v = leftInt | rightInt
|
||||||
|
} else {
|
||||||
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalBitwiseOr(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err = bitwiseOr(opTerm, leftValue, rightValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------- Bitwise XOR term
|
||||||
|
|
||||||
|
func newBitwiseXorTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priBitwiseOr,
|
||||||
|
evalFunc: evalBitwiseXor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bitwiseXor(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
|
var leftInt, rightInt int64
|
||||||
|
var lok, rok bool
|
||||||
|
|
||||||
|
leftInt, lok = leftValue.(int64)
|
||||||
|
rightInt, rok = rightValue.(int64)
|
||||||
|
|
||||||
|
if lok && rok {
|
||||||
|
v = leftInt ^ rightInt
|
||||||
|
} else {
|
||||||
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalBitwiseXor(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err = bitwiseXor(opTerm, leftValue, rightValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
registerTermConstructor(SymTilde, newBitwiseNotTerm)
|
||||||
|
registerTermConstructor(SymAmpersand, newBitwiseAndTerm)
|
||||||
|
registerTermConstructor(SymVertBar, newBitwiseOrTerm)
|
||||||
|
registerTermConstructor(SymCaret, newBitwiseXorTerm)
|
||||||
|
}
|
||||||
+1
-1
@@ -20,7 +20,7 @@ func evalContextValue(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
var childValue any
|
var childValue any
|
||||||
|
|
||||||
var sourceCtx ExprContext
|
var sourceCtx ExprContext
|
||||||
if opTerm.children == nil || len(opTerm.children) == 0 {
|
if len(opTerm.children) == 0 {
|
||||||
sourceCtx = ctx
|
sourceCtx = ctx
|
||||||
} else if opTerm.children[0].symbol() == SymVariable && opTerm.children[0].source() == "global" {
|
} else if opTerm.children[0].symbol() == SymVariable && opTerm.children[0].source() == "global" {
|
||||||
sourceCtx = globalCtx
|
sourceCtx = globalCtx
|
||||||
|
|||||||
+13
-9
@@ -7,7 +7,6 @@ package expr
|
|||||||
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
|
//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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,18 +48,23 @@ func evalFraction(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
den = -den
|
den = -den
|
||||||
num = -num
|
num = -num
|
||||||
}
|
}
|
||||||
g := gcd(num, den)
|
if num != 0 {
|
||||||
num = num / g
|
g := gcd(num, den)
|
||||||
den = den / g
|
num = num / g
|
||||||
if den == 1 {
|
den = den / g
|
||||||
v = num
|
if den == 1 {
|
||||||
|
v = num
|
||||||
|
} else {
|
||||||
|
v = &FractionType{num, den}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
v = &FractionType{num, den}
|
v = &FractionType{0, den}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymVertBar, newFractionTerm)
|
// registerTermConstructor(SymVertBar, newFractionTerm)
|
||||||
|
registerTermConstructor(SymColon, newFractionTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,6 +113,9 @@ func evalIndex(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
} else if IsDict(leftValue) {
|
} else if IsDict(leftValue) {
|
||||||
d := leftValue.(*DictType)
|
d := leftValue.(*DictType)
|
||||||
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
||||||
|
} else {
|
||||||
|
rightChild := opTerm.children[1]
|
||||||
|
err = rightChild.Errorf("invalid index type: %v", (*indexList)[0])
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-8
@@ -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 {
|
||||||
@@ -86,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)
|
||||||
}
|
}
|
||||||
|
|||||||
+81
-46
@@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,7 +12,7 @@ import (
|
|||||||
|
|
||||||
func newMultiplyTerm(tk *Token) (inst *term) {
|
func newMultiplyTerm(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: priProduct,
|
priority: priProduct,
|
||||||
@@ -21,13 +20,7 @@ func newMultiplyTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
func mulValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsString(leftValue) && IsInteger(rightValue) {
|
if IsString(leftValue) && IsInteger(rightValue) {
|
||||||
s, _ := leftValue.(string)
|
s, _ := leftValue.(string)
|
||||||
n, _ := rightValue.(int64)
|
n, _ := rightValue.(int64)
|
||||||
@@ -43,16 +36,26 @@ func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
|||||||
v = leftInt * rightInt
|
v = leftInt * rightInt
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = prodTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalMultiply(ctx ExprContext, prodTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return mulValues(prodTerm, leftValue, rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
//-------- divide term
|
//-------- divide term
|
||||||
|
|
||||||
func newDivideTerm(tk *Token) (inst *term) {
|
func newDivideTerm(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: priProduct,
|
priority: priProduct,
|
||||||
@@ -60,6 +63,56 @@ func newDivideTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
|
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||||
|
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||||
|
d := numAsFloat(rightValue)
|
||||||
|
if d == 0.0 {
|
||||||
|
err = opTerm.errDivisionByZero()
|
||||||
|
} else {
|
||||||
|
v = numAsFloat(leftValue) / d
|
||||||
|
}
|
||||||
|
} else if isFraction(leftValue) || isFraction(rightValue) {
|
||||||
|
v, err = divAnyFract(leftValue, rightValue)
|
||||||
|
} else {
|
||||||
|
leftInt, _ := leftValue.(int64)
|
||||||
|
if rightInt, _ := rightValue.(int64); rightInt == 0 {
|
||||||
|
err = opTerm.errDivisionByZero()
|
||||||
|
} else {
|
||||||
|
v = leftInt / rightInt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if IsString(leftValue) && IsString(rightValue) {
|
||||||
|
source := leftValue.(string)
|
||||||
|
sep := rightValue.(string)
|
||||||
|
v = ListFromStrings(strings.Split(source, sep))
|
||||||
|
} else if IsString(leftValue) && IsInteger(rightValue) {
|
||||||
|
source := leftValue.(string)
|
||||||
|
partSize := int(rightValue.(int64))
|
||||||
|
if partSize == 0 {
|
||||||
|
err = opTerm.errDivisionByZero()
|
||||||
|
} else {
|
||||||
|
partCount := len(source) / partSize
|
||||||
|
remainder := len(source) % partSize
|
||||||
|
listSize := partCount
|
||||||
|
if remainder > 0 {
|
||||||
|
listSize++
|
||||||
|
}
|
||||||
|
parts := make([]any, 0, listSize)
|
||||||
|
for i := 0; i < partCount; i++ {
|
||||||
|
parts = append(parts, source[i*partSize:(i+1)*partSize])
|
||||||
|
}
|
||||||
|
if remainder > 0 {
|
||||||
|
parts = append(parts, source[len(source)-remainder:])
|
||||||
|
}
|
||||||
|
v = newList(parts)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
@@ -67,28 +120,7 @@ func evalDivide(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
return divValues(opTerm, leftValue, rightValue)
|
||||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
|
||||||
d := numAsFloat(rightValue)
|
|
||||||
if d == 0.0 {
|
|
||||||
err = errors.New("division by zero")
|
|
||||||
} else {
|
|
||||||
v = numAsFloat(leftValue) / d
|
|
||||||
}
|
|
||||||
} else if isFraction(leftValue) || isFraction(rightValue) {
|
|
||||||
v, err = divAnyFract(leftValue, rightValue)
|
|
||||||
} else {
|
|
||||||
leftInt, _ := leftValue.(int64)
|
|
||||||
if rightInt, _ := rightValue.(int64); rightInt == 0 {
|
|
||||||
err = errors.New("division by zero")
|
|
||||||
} else {
|
|
||||||
v = leftInt / rightInt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------- divide as float term
|
//-------- divide as float term
|
||||||
@@ -113,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
|
||||||
}
|
}
|
||||||
@@ -127,35 +159,38 @@ func evalDivideAsFloat(ctx ExprContext, floatDivTerm *term) (v any, err error) {
|
|||||||
|
|
||||||
func newRemainderTerm(tk *Token) (inst *term) {
|
func newRemainderTerm(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: priProduct,
|
priority: priProduct,
|
||||||
evalFunc: evalReminder,
|
evalFunc: evalRemainder,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func remainderValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
func evalReminder(ctx ExprContext, ramainderTerm *term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = ramainderTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = ramainderTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalRemainder(ctx ExprContext, remainderTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = remainderTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return remainderValues(remainderTerm, leftValue, rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymStar, newMultiplyTerm)
|
registerTermConstructor(SymStar, newMultiplyTerm)
|
||||||
|
|||||||
+18
-5
@@ -34,12 +34,16 @@ func newRangeTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func changeColonToRange(t *term) {
|
||||||
|
if t.tk.IsSymbol(SymColon) {
|
||||||
|
t.tk.Sym = SymRange
|
||||||
|
t.evalFunc = evalRange
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
// if err = self.checkOperands(); err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
if len(opTerm.children) == 0 {
|
if len(opTerm.children) == 0 {
|
||||||
leftValue = int64(0)
|
leftValue = int64(0)
|
||||||
rightValue = int64(-1)
|
rightValue = int64(-1)
|
||||||
@@ -52,7 +56,8 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !(IsInteger(leftValue) && IsInteger(rightValue)) {
|
if !(IsInteger(leftValue) && IsInteger(rightValue)) {
|
||||||
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
// err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
err = errRangeInvalidSpecification(opTerm)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +68,15 @@ func evalRange(ctx ExprContext, opTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func errRangeInvalidSpecification(t *term) error {
|
||||||
|
return t.Errorf("invalid range specification")
|
||||||
|
}
|
||||||
|
|
||||||
|
func errRangeUnexpectedExpression(t *term) error {
|
||||||
|
return t.Errorf("unexpected range expression")
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymColon, newRangeTerm)
|
registerTermConstructor(SymRange, newRangeTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-3
@@ -22,9 +22,19 @@ func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (ma
|
|||||||
selectedValue, err = caseData.caseExpr.Eval(ctx)
|
selectedValue, err = caseData.caseExpr.Eval(ctx)
|
||||||
match = true
|
match = true
|
||||||
} else if filterList, ok := caseData.filterList.value().([]*term); ok {
|
} else if filterList, ok := caseData.filterList.value().([]*term); ok {
|
||||||
if len(filterList) == 0 && exprValue == int64(caseIndex) {
|
if len(filterList) == 0 {
|
||||||
selectedValue, err = caseData.caseExpr.Eval(ctx)
|
var valueAsInt = int64(0)
|
||||||
match = true
|
if b, ok := exprValue.(bool); ok {
|
||||||
|
if !b {
|
||||||
|
valueAsInt = 1
|
||||||
|
}
|
||||||
|
} else if valueAsInt, ok = exprValue.(int64); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if valueAsInt == int64(caseIndex) {
|
||||||
|
selectedValue, err = caseData.caseExpr.Eval(ctx)
|
||||||
|
match = true
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
var caseValue any
|
var caseValue any
|
||||||
for _, caseTerm := range filterList {
|
for _, caseTerm := range filterList {
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-shift.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
//-------- bit right shift term
|
||||||
|
|
||||||
|
func newRightShiftTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priBinShift,
|
||||||
|
evalFunc: evalRightShift,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bitRightShift(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
|
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||||
|
leftInt := leftValue.(int64)
|
||||||
|
rightInt := rightValue.(int64)
|
||||||
|
v = leftInt >> rightInt
|
||||||
|
} else {
|
||||||
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalRightShift(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err = bitRightShift(opTerm, leftValue, rightValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLeftShiftTerm(tk *Token) (inst *term) {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
children: make([]*term, 0, 2),
|
||||||
|
position: posInfix,
|
||||||
|
priority: priBinShift,
|
||||||
|
evalFunc: evalLeftShift,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bitLeftShift(opTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
|
if IsInteger(leftValue) && IsInteger(rightValue) {
|
||||||
|
leftInt := leftValue.(int64)
|
||||||
|
rightInt := rightValue.(int64)
|
||||||
|
v = leftInt << rightInt
|
||||||
|
} else {
|
||||||
|
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalLeftShift(ctx ExprContext, opTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err = bitLeftShift(opTerm, leftValue, rightValue)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
registerTermConstructor(SymDoubleGreater, newRightShiftTerm)
|
||||||
|
registerTermConstructor(SymDoubleLess, newLeftShiftTerm)
|
||||||
|
}
|
||||||
+25
-15
@@ -21,13 +21,7 @@ func newPlusTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
func sumValues(plusTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsString(leftValue) && isNumberString(rightValue)) || (IsString(rightValue) && isNumberString(leftValue)) {
|
if (IsString(leftValue) && isNumberString(rightValue)) || (IsString(rightValue) && isNumberString(leftValue)) {
|
||||||
v = fmt.Sprintf("%v%v", leftValue, rightValue)
|
v = fmt.Sprintf("%v%v", leftValue, rightValue)
|
||||||
} else if IsNumber(leftValue) && IsNumber(rightValue) {
|
} else if IsNumber(leftValue) && IsNumber(rightValue) {
|
||||||
@@ -59,10 +53,22 @@ func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
|||||||
c := leftDict.clone()
|
c := leftDict.clone()
|
||||||
c.merge(rightDict)
|
c.merge(rightDict)
|
||||||
v = c
|
v = c
|
||||||
|
} else if isFraction(leftValue) && isFraction(rightValue) {
|
||||||
|
v, err = sumAnyFract(leftValue, rightValue)
|
||||||
} else {
|
} else {
|
||||||
err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
|
err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
return
|
return v, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalPlus(ctx ExprContext, plusTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return sumValues(plusTerm, leftValue, rightValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------- minus term
|
//-------- minus term
|
||||||
@@ -77,13 +83,7 @@ func newMinusTerm(tk *Token) (inst *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
func diffValues(minusTerm *term, leftValue, rightValue any) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
|
||||||
if IsFloat(leftValue) || IsFloat(rightValue) {
|
if IsFloat(leftValue) || IsFloat(rightValue) {
|
||||||
v = numAsFloat(leftValue) - numAsFloat(rightValue)
|
v = numAsFloat(leftValue) - numAsFloat(rightValue)
|
||||||
@@ -110,6 +110,16 @@ func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalMinus(ctx ExprContext, minusTerm *term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return diffValues(minusTerm, leftValue, rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
registerTermConstructor(SymPlus, newPlusTerm)
|
registerTermConstructor(SymPlus, newPlusTerm)
|
||||||
|
|||||||
@@ -6,10 +6,46 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- parser
|
//-------- parser
|
||||||
|
|
||||||
|
type parserContext uint16
|
||||||
|
|
||||||
|
const (
|
||||||
|
parserNoFlags = 0
|
||||||
|
allowMultiExpr parserContext = 1 << iota
|
||||||
|
allowVarRef
|
||||||
|
selectorContext
|
||||||
|
listContext // squareContext for list
|
||||||
|
indexContext // squareContext for index
|
||||||
|
allowIndex // allow index in squareContext
|
||||||
|
squareContext = listContext | indexContext // Square parenthesis for list or index
|
||||||
|
)
|
||||||
|
|
||||||
|
func hasFlag[T constraints.Unsigned](set T, singleFlag T) bool {
|
||||||
|
return (set & singleFlag) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFlags[T constraints.Unsigned](set T, flags T) T {
|
||||||
|
return set | flags
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFlagsCond[T constraints.Unsigned](set T, flags T, cond bool) (newSet T) {
|
||||||
|
if cond {
|
||||||
|
newSet = set | flags
|
||||||
|
} else {
|
||||||
|
newSet = set
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func remFlags[T constraints.Unsigned](set T, flags T) T {
|
||||||
|
return set & (^flags)
|
||||||
|
}
|
||||||
|
|
||||||
type parser struct {
|
type parser struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,13 +60,13 @@ func (parser *parser) Next(scanner *scanner) (tk *Token) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
|
func (parser *parser) parseFuncCall(scanner *scanner, ctx parserContext, tk *Token) (tree *term, err error) {
|
||||||
args := make([]*term, 0, 10)
|
args := make([]*term, 0, 10)
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil {
|
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
@@ -66,12 +102,18 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
tk = parser.Next(scanner)
|
tk = parser.Next(scanner)
|
||||||
if tk.IsSymbol(SymIdentifier) {
|
if tk.IsSymbol(SymIdentifier) {
|
||||||
param := newTerm(tk)
|
param := newTerm(tk)
|
||||||
|
if len(args) > 0 {
|
||||||
|
if pos := paramAlreadyDefined(args, param); pos > 0 {
|
||||||
|
err = tk.Errorf("parameter %q at position %d already defined at position %d", param.source(), len(args)+1, pos)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
args = append(args, param)
|
args = append(args, param)
|
||||||
tk = parser.Next(scanner)
|
tk = parser.Next(scanner)
|
||||||
if tk.Sym == SymEqual {
|
if tk.Sym == SymEqual {
|
||||||
var paramExpr *ast
|
var paramExpr *ast
|
||||||
defaultParamsStarted = true
|
defaultParamsStarted = true
|
||||||
if paramExpr, err = parser.parseItem(scanner, false, SymComma, SymClosedRound); err != nil {
|
if paramExpr, err = parser.parseItem(scanner, parserNoFlags, SymComma, SymClosedRound); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
param.forceChild(paramExpr.root)
|
param.forceChild(paramExpr.root)
|
||||||
@@ -94,7 +136,7 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
tk = parser.Next(scanner)
|
tk = parser.Next(scanner)
|
||||||
if tk.IsSymbol(SymOpenBrace) {
|
if tk.IsSymbol(SymOpenBrace) {
|
||||||
body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace)
|
body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymClosedBrace)
|
||||||
} else {
|
} else {
|
||||||
err = tk.ErrorExpectedGot("{")
|
err = tk.ErrorExpectedGot("{")
|
||||||
}
|
}
|
||||||
@@ -110,27 +152,43 @@ func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) {
|
func paramAlreadyDefined(args []*term, param *term) (position int) {
|
||||||
|
position = 0
|
||||||
|
for i, arg := range args {
|
||||||
|
if arg.source() == param.source() {
|
||||||
|
position = i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (parser *parser) parseList(scanner *scanner, ctx parserContext) (listTerm *term, err error) {
|
||||||
r, c := scanner.lastPos()
|
r, c := scanner.lastPos()
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
|
itemCtx := remFlags(ctx, allowIndex)
|
||||||
for lastSym != SymClosedSquare && lastSym != SymEos {
|
for lastSym != SymClosedSquare && lastSym != SymEos {
|
||||||
var subTree *ast
|
|
||||||
zeroRequired := scanner.current.Sym == SymColon
|
zeroRequired := scanner.current.Sym == SymColon
|
||||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
var itemTree *ast
|
||||||
root := subTree.root
|
if itemTree, err = parser.parseItem(scanner, itemCtx, SymComma, SymClosedSquare); err == nil {
|
||||||
|
root := itemTree.root
|
||||||
if root != nil {
|
if root != nil {
|
||||||
if !parsingIndeces && root.symbol() == SymColon {
|
if hasFlag(ctx, allowIndex) && root.symbol() == SymColon {
|
||||||
err = root.Errorf("unexpected range expression")
|
changeColonToRange(root)
|
||||||
|
}
|
||||||
|
if !hasFlag(ctx, allowIndex) && root.symbol() == SymRange {
|
||||||
|
// err = root.Errorf("unexpected range expression")
|
||||||
|
err = errRangeUnexpectedExpression(root)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
args = append(args, root)
|
args = append(args, root)
|
||||||
if parsingIndeces && root.symbol() == SymColon && zeroRequired { //len(root.children) == 0 {
|
if hasFlag(ctx, allowIndex) && root.symbol() == SymRange && zeroRequired { //len(root.children) == 0 {
|
||||||
if len(root.children) == 1 {
|
if len(root.children) == 1 {
|
||||||
root.children = append(root.children, root.children[0])
|
root.children = append(root.children, root.children[0])
|
||||||
} else if len(root.children) > 1 {
|
} else if len(root.children) > 1 {
|
||||||
err = root.Errorf("invalid range specification")
|
// err = root.Errorf("invalid range specification")
|
||||||
|
err = errRangeInvalidSpecification(root)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0))
|
zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0))
|
||||||
@@ -147,26 +205,28 @@ func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarR
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
lastSym = scanner.Previous().Sym
|
lastSym = scanner.Previous().Sym
|
||||||
itemExpected = lastSym == SymComma
|
if itemExpected = lastSym == SymComma; itemExpected {
|
||||||
|
remFlags(ctx, allowIndex)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if lastSym != SymClosedSquare {
|
if lastSym != SymClosedSquare {
|
||||||
err = scanner.Previous().ErrorExpectedGot("]")
|
err = scanner.Previous().ErrorExpectedGot("]")
|
||||||
} else {
|
} else {
|
||||||
subtree = newListTerm(r, c, args)
|
listTerm = newListTerm(r, c, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
func (parser *parser) parseIterDef(scanner *scanner, ctx parserContext) (subtree *term, err error) {
|
||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
args := make([]*term, 0)
|
args := make([]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
|
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedRound); err == nil {
|
||||||
if subTree.root != nil {
|
if subTree.root != nil {
|
||||||
args = append(args, subTree.root)
|
args = append(args, subTree.root)
|
||||||
} else if itemExpected {
|
} else if itemExpected {
|
||||||
@@ -212,7 +272,7 @@ func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
func (parser *parser) parseDictionary(scanner *scanner, ctx parserContext) (subtree *term, err error) {
|
||||||
args := make(map[any]*term, 0)
|
args := make(map[any]*term, 0)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
@@ -229,7 +289,7 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
|
if subTree, err = parser.parseItem(scanner, ctx, SymComma, SymClosedBrace); err == nil {
|
||||||
if subTree.root != nil {
|
if subTree.root != nil {
|
||||||
args[key] = subTree.root
|
args[key] = subTree.root
|
||||||
} else /*if key != nil*/ {
|
} else /*if key != nil*/ {
|
||||||
@@ -248,15 +308,15 @@ func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtr
|
|||||||
err = scanner.Previous().ErrorExpectedGot("}")
|
err = scanner.Previous().ErrorExpectedGot("}")
|
||||||
} else {
|
} else {
|
||||||
subtree = newDictTerm(args)
|
subtree = newDictTerm(args)
|
||||||
// subtree = newMapTerm(args)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
|
func (parser *parser) parseSelectorCase(scanner *scanner, ctx parserContext, defaultCase bool) (caseTerm *term, err error) {
|
||||||
var filterList *term
|
var filterList *term
|
||||||
var caseExpr *ast
|
var caseExpr *ast
|
||||||
|
ctx = remFlags(ctx, allowIndex)
|
||||||
tk := parser.Next(scanner)
|
tk := parser.Next(scanner)
|
||||||
startRow := tk.row
|
startRow := tk.row
|
||||||
startCol := tk.col
|
startCol := tk.col
|
||||||
@@ -265,7 +325,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
|
|||||||
err = tk.Errorf("case list in default clause")
|
err = tk.Errorf("case list in default clause")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil {
|
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex)); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tk = parser.Next(scanner)
|
tk = parser.Next(scanner)
|
||||||
@@ -276,7 +336,7 @@ func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defa
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tk.Sym == SymOpenBrace {
|
if tk.Sym == SymOpenBrace {
|
||||||
if caseExpr, err = parser.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil {
|
if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, SymClosedBrace); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -302,25 +362,28 @@ func addSelectorCase(selectorTerm, caseTerm *term) {
|
|||||||
caseTerm.parent = selectorTerm
|
caseTerm.parent = selectorTerm
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
|
func (parser *parser) parseSelector(scanner *scanner, tree *ast, ctx parserContext) (selectorTerm *term, err error) {
|
||||||
var caseTerm *term
|
var caseTerm *term
|
||||||
|
|
||||||
|
ctx = remFlags(ctx, allowIndex)
|
||||||
tk := scanner.makeToken(SymSelector, '?')
|
tk := scanner.makeToken(SymSelector, '?')
|
||||||
if selectorTerm, err = tree.addToken(tk); err != nil {
|
if selectorTerm, err = tree.addToken(tk); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, false); err == nil {
|
if caseTerm, err = parser.parseSelectorCase(scanner, ctx|allowVarRef, false); err == nil {
|
||||||
addSelectorCase(selectorTerm, caseTerm)
|
addSelectorCase(selectorTerm, caseTerm)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
func (parser *parser) parseItem(scanner *scanner, ctx parserContext, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
return parser.parseGeneral(scanner, false, allowVarRef, termSymbols...)
|
return parser.parseGeneral(scanner, ctx|allowVarRef, termSymbols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
return parser.parseGeneral(scanner, true, false, termSymbols...)
|
termSymbols = append(termSymbols, SymEos)
|
||||||
|
return parser.parseGeneral(scanner, allowMultiExpr, termSymbols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func couldBeACollection(t *term) bool {
|
func couldBeACollection(t *term) bool {
|
||||||
@@ -331,15 +394,22 @@ func couldBeACollection(t *term) bool {
|
|||||||
return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable
|
return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable
|
||||||
}
|
}
|
||||||
|
|
||||||
// func areSymbolsOutOfCtx(tk *Token, ctxTerm *term, syms ...Symbol) bool {
|
func listSubTree(tree *ast, listTerm *term, allowIndeces bool) (root *term, err error) {
|
||||||
// var areOut = false
|
var tk *Token
|
||||||
// if ctxTerm != nil {
|
if allowIndeces {
|
||||||
// areOut = tk.IsOneOf(syms)
|
tk = NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
||||||
// }
|
root = newTerm(tk)
|
||||||
// return areOut
|
if err = tree.addTerm(root); err == nil {
|
||||||
// }
|
err = tree.addTerm(listTerm)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
root = listTerm
|
||||||
|
err = tree.addTerm(listTerm)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
func (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
|
||||||
var tk *Token
|
var tk *Token
|
||||||
@@ -353,7 +423,7 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
if tk.Sym == SymSemiColon {
|
if tk.Sym == SymSemiColon {
|
||||||
if allowForest {
|
if hasFlag(ctx, allowMultiExpr) {
|
||||||
tree.ToForest()
|
tree.ToForest()
|
||||||
firstToken = true
|
firstToken = true
|
||||||
currentTerm = nil
|
currentTerm = nil
|
||||||
@@ -371,6 +441,11 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
tk.Sym = SymChangeSign
|
tk.Sym = SymChangeSign
|
||||||
} else if tk.Sym == SymPlus {
|
} else if tk.Sym == SymPlus {
|
||||||
tk.Sym = SymUnchangeSign
|
tk.Sym = SymUnchangeSign
|
||||||
|
} else if tk.IsSymbol(SymStar) {
|
||||||
|
tk.SetSymbol(SymDereference)
|
||||||
|
} else if tk.IsSymbol(SymExclamation) {
|
||||||
|
err = tk.Errorf("postfix opertor %q requires an operand on its left side", tk)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
firstToken = false
|
firstToken = false
|
||||||
}
|
}
|
||||||
@@ -378,46 +453,39 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
switch tk.Sym {
|
switch tk.Sym {
|
||||||
case SymOpenRound:
|
case SymOpenRound:
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, 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
|
||||||
if funcCallTerm, err = parser.parseFuncCall(scanner, allowVarRef, tk); err == nil {
|
if funcCallTerm, err = parser.parseFuncCall(scanner, ctx, tk); err == nil {
|
||||||
err = tree.addTerm(funcCallTerm)
|
err = tree.addTerm(funcCallTerm)
|
||||||
currentTerm = funcCallTerm
|
currentTerm = funcCallTerm
|
||||||
}
|
}
|
||||||
case SymOpenSquare:
|
case SymOpenSquare:
|
||||||
var listTerm *term
|
var listTerm *term
|
||||||
parsingIndeces := couldBeACollection(currentTerm)
|
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
|
||||||
if listTerm, err = parser.parseList(scanner, parsingIndeces, allowVarRef); err == nil {
|
if listTerm, err = parser.parseList(scanner, newCtx); err == nil {
|
||||||
if parsingIndeces {
|
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||||
indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
|
||||||
indexTerm := newTerm(indexTk)
|
|
||||||
if err = tree.addTerm(indexTerm); err == nil {
|
|
||||||
err = tree.addTerm(listTerm)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = tree.addTerm(listTerm)
|
|
||||||
}
|
|
||||||
currentTerm = listTerm
|
|
||||||
}
|
}
|
||||||
case SymOpenBrace:
|
case SymOpenBrace:
|
||||||
if currentTerm != nil && currentTerm.symbol() == SymColon {
|
if currentTerm != nil && currentTerm.symbol() == SymColon {
|
||||||
err = currentTerm.Errorf(`selector-case outside of a selector context`)
|
err = currentTerm.Errorf(`selector-case outside of a selector context`)
|
||||||
} else {
|
} else {
|
||||||
var mapTerm *term
|
var mapTerm *term
|
||||||
if mapTerm, err = parser.parseDictionary(scanner, allowVarRef); err == nil {
|
if mapTerm, err = parser.parseDictionary(scanner, ctx); err == nil {
|
||||||
err = tree.addTerm(mapTerm)
|
err = tree.addTerm(mapTerm)
|
||||||
currentTerm = mapTerm
|
currentTerm = mapTerm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case SymEqual:
|
case SymEqual, SymPlusEqual, SymMinusEqual, SymStarEqual, SymSlashEqual, SymPercEqual, SymAmpersandEqual, SymVertBarEqual, SymDoubleLessEqual, SymDoubleGreaterEqual, SymCaretEqual:
|
||||||
// if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
// }
|
firstToken = true
|
||||||
case SymFuncDef:
|
case SymFuncDef:
|
||||||
var funcDefTerm *term
|
var funcDefTerm *term
|
||||||
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
|
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
|
||||||
@@ -426,24 +494,25 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
}
|
}
|
||||||
case SymDollarRound:
|
case SymDollarRound:
|
||||||
var iterDefTerm *term
|
var iterDefTerm *term
|
||||||
if iterDefTerm, err = parser.parseIterDef(scanner, allowVarRef); err == nil {
|
if iterDefTerm, err = parser.parseIterDef(scanner, ctx); err == nil {
|
||||||
err = tree.addTerm(iterDefTerm)
|
err = tree.addTerm(iterDefTerm)
|
||||||
currentTerm = iterDefTerm
|
currentTerm = iterDefTerm
|
||||||
}
|
}
|
||||||
case SymIdentifier:
|
case SymIdentifier:
|
||||||
if tk.source[0] == '@' && !allowVarRef {
|
if tk.source[0] == '@' && !hasFlag(ctx, allowVarRef) {
|
||||||
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
||||||
} else {
|
} else {
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
}
|
}
|
||||||
case SymQuestion:
|
case SymQuestion:
|
||||||
if selectorTerm, err = parser.parseSelector(scanner, tree, allowVarRef); err == nil {
|
if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil {
|
||||||
currentTerm = selectorTerm
|
currentTerm = selectorTerm
|
||||||
|
addFlags(ctx, selectorContext)
|
||||||
}
|
}
|
||||||
case SymColon, SymDoubleColon:
|
case SymColon, SymDoubleColon:
|
||||||
var caseTerm *term
|
var caseTerm *term
|
||||||
if selectorTerm != nil {
|
if selectorTerm != nil {
|
||||||
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
|
if caseTerm, err = parser.parseSelectorCase(scanner, ctx, tk.Sym == SymDoubleColon); err == nil {
|
||||||
addSelectorCase(selectorTerm, caseTerm)
|
addSelectorCase(selectorTerm, caseTerm)
|
||||||
currentTerm = caseTerm
|
currentTerm = caseTerm
|
||||||
if tk.Sym == SymDoubleColon {
|
if tk.Sym == SymDoubleColon {
|
||||||
@@ -452,79 +521,34 @@ func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarR
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
|
if tk.IsOneOfA(SymColon, SymRange) {
|
||||||
|
// Colon outside a selector term acts like a separator
|
||||||
|
firstToken = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if tk.IsSymbol(SymColon) {
|
|
||||||
// Colon outside a selector term acts like a separator
|
|
||||||
firstToken = true
|
|
||||||
}
|
|
||||||
case SymPlusEqual, SymMinusEqual, SymStarEqual:
|
|
||||||
currentTerm, err = parser.expandOpAssign(scanner, tree, tk, allowVarRef)
|
|
||||||
default:
|
default:
|
||||||
currentTerm, err = tree.addToken(tk)
|
currentTerm, err = tree.addToken(tk)
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
|
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
|
||||||
selectorTerm = nil
|
selectorTerm = nil
|
||||||
|
remFlags(ctx, selectorContext)
|
||||||
}
|
}
|
||||||
// lastSym = tk.Sym
|
// lastSym = tk.Sym
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = tk.Error()
|
if !tk.IsOneOf(termSymbols) {
|
||||||
}
|
var symDesc string
|
||||||
return
|
if tk.IsSymbol(SymError) {
|
||||||
}
|
symDesc = tk.ErrorText()
|
||||||
|
} else {
|
||||||
// func checkPrevSymbol(lastSym, wantedSym Symbol, tk *Token) (err error) {
|
symDesc = SymToString(tk.Sym)
|
||||||
// if lastSym != wantedSym {
|
|
||||||
// err = fmt.Errorf(`assign operator (%q) must be preceded by a variable`, tk.source)
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (parser *parser) expandOpAssign(scanner * scanner, tree *ast, tk *Token, allowVarRef bool) (t *term, err error) {
|
|
||||||
var opSym Symbol
|
|
||||||
var opString string
|
|
||||||
|
|
||||||
if tree.root != nil {
|
|
||||||
switch tk.Sym {
|
|
||||||
case SymPlusEqual:
|
|
||||||
opString = "+"
|
|
||||||
opSym = SymPlus
|
|
||||||
case SymMinusEqual:
|
|
||||||
opString = "-"
|
|
||||||
opSym = SymMinus
|
|
||||||
case SymStarEqual:
|
|
||||||
opString = "*"
|
|
||||||
opSym = SymStar
|
|
||||||
default:
|
|
||||||
err = tk.Errorf("unsopported operator %q", tk.source)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
leftExpr := tree.root.Clone()
|
|
||||||
leftExpr.setParent(nil)
|
|
||||||
if t, err = tree.addToken(NewToken(tk.row, tk.col, SymEqual, "=")); err == nil {
|
|
||||||
t = leftExpr
|
|
||||||
if err = tree.addTerm(leftExpr); err == nil {
|
|
||||||
if t, err = tree.addToken(NewToken(tk.row, tk.col, opSym, opString)); err == nil {
|
|
||||||
|
|
||||||
var subTree *ast
|
|
||||||
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare); err == nil {
|
|
||||||
if scanner.Previous().IsOneOfA(SymSemiColon, SymClosedRound, SymClosedBrace, SymClosedSquare) {
|
|
||||||
if err = scanner.UnreadToken(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subTree.root.priority = priValue
|
|
||||||
err = tree.addTerm(newExprTerm(subTree.root))
|
|
||||||
t = subTree.root
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
err = tk.ErrorExpectedGotStringWithPrefix("expected one of", SymListToString(termSymbols, true), symDesc)
|
||||||
|
} else {
|
||||||
|
err = tk.Error()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
err = tk.Errorf("left operand of %q must be a variable or a variable expression", tk)
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-10
@@ -40,9 +40,9 @@ func DefaultTranslations() map[Symbol]Symbol {
|
|||||||
SymKwAnd: SymAnd,
|
SymKwAnd: SymAnd,
|
||||||
SymDoubleVertBar: SymOr,
|
SymDoubleVertBar: SymOr,
|
||||||
SymKwOr: SymOr,
|
SymKwOr: SymOr,
|
||||||
SymTilde: SymNot,
|
// SymTilde: SymNot,
|
||||||
SymKwNot: SymNot,
|
SymKwNot: SymNot,
|
||||||
SymLessGreater: SymNotEqual,
|
SymLessGreater: SymNotEqual,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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)
|
||||||
}
|
}
|
||||||
@@ -149,6 +151,8 @@ func (scanner *scanner) fetchNextToken() (tk *Token) {
|
|||||||
if next, _ := scanner.peek(); next == '*' {
|
if next, _ := scanner.peek(); next == '*' {
|
||||||
scanner.readChar()
|
scanner.readChar()
|
||||||
tk = scanner.fetchBlockComment()
|
tk = scanner.fetchBlockComment()
|
||||||
|
} else if next, _ = scanner.peek(); next == '=' {
|
||||||
|
tk = scanner.moveOn(SymSlashEqual, ch, next)
|
||||||
} else if next == '/' {
|
} else if next == '/' {
|
||||||
scanner.readChar()
|
scanner.readChar()
|
||||||
tk = scanner.fetchOnLineComment()
|
tk = scanner.fetchOnLineComment()
|
||||||
@@ -165,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)
|
||||||
@@ -230,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 '@':
|
||||||
@@ -263,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)
|
||||||
}
|
}
|
||||||
@@ -273,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)
|
||||||
}
|
}
|
||||||
@@ -458,7 +490,7 @@ func (scanner *scanner) parseNumber(firstCh byte) (tk *Token) {
|
|||||||
tk = scanner.makeErrorToken(err)
|
tk = scanner.makeErrorToken(err)
|
||||||
} else {
|
} else {
|
||||||
var value any
|
var value any
|
||||||
err = scanner.sync(err) // TODO: Check this function
|
_ = scanner.sync(err) // TODO: Check this function
|
||||||
txt := sb.String()
|
txt := sb.String()
|
||||||
if sym == SymFloat {
|
if sym == SymFloat {
|
||||||
value, err = strconv.ParseFloat(txt, 64)
|
value, err = strconv.ParseFloat(txt, 64)
|
||||||
@@ -590,7 +622,7 @@ func (scanner *scanner) fetchString(termCh byte) (tk *Token) {
|
|||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
tk = scanner.makeErrorToken(errors.New("missing string termination \""))
|
tk = scanner.makeErrorToken(errors.New(string(termCh)))
|
||||||
} else {
|
} else {
|
||||||
tk = scanner.makeErrorToken(err)
|
tk = scanner.makeErrorToken(err)
|
||||||
}
|
}
|
||||||
@@ -628,9 +660,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+196
@@ -0,0 +1,196 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// Symbol.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var symbolMap map[Symbol]symbolSpec
|
||||||
|
|
||||||
|
type symbolClass int16
|
||||||
|
|
||||||
|
const (
|
||||||
|
symClassOperator symbolClass = iota
|
||||||
|
symClassCommand
|
||||||
|
symClassIdentifier
|
||||||
|
symClassDelimiter
|
||||||
|
symClassParenthesis
|
||||||
|
symClassDeclaration
|
||||||
|
symClassValue
|
||||||
|
symClassOther
|
||||||
|
)
|
||||||
|
|
||||||
|
type symbolSpec struct {
|
||||||
|
repr string
|
||||||
|
kind symbolClass
|
||||||
|
opType termPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
symbolMap = map[Symbol]symbolSpec{
|
||||||
|
SymUnknown: {"<unknown>", symClassOther, posLeaf}, // -1: Unknown symbol
|
||||||
|
SymNone: {"<null>", symClassOther, posLeaf}, // 0: Null value for variable of type symbol
|
||||||
|
SymError: {"<error>", symClassOther, posLeaf}, // 1: Error reading from stream
|
||||||
|
SymEos: {"<eos>", symClassOther, posLeaf}, // 2: End of stream
|
||||||
|
SymMinus: {"-", symClassOperator, posInfix}, // 3: '-'
|
||||||
|
SymMinusEqual: {"-=", symClassOperator, posInfix}, // 4: '-='
|
||||||
|
SymDoubleMinus: {"--", symClassOperator, posPostfix}, // 5: '--'
|
||||||
|
SymPlus: {"+", symClassOperator, posInfix}, // 6: '+'
|
||||||
|
SymPlusEqual: {"+=", symClassOperator, posInfix}, // 7: '+='
|
||||||
|
SymDoublePlus: {"++", symClassOperator, posPostfix}, // 8: '++'
|
||||||
|
SymStar: {"*", symClassOperator, posInfix}, // 9: '*'
|
||||||
|
SymDoubleStar: {"**", symClassOperator, posInfix}, // 10: '**'
|
||||||
|
SymSlash: {"/", symClassOperator, posInfix}, // 11: '/'
|
||||||
|
SymBackSlash: {"\\", symClassOperator, posLeaf}, // 12: '\'
|
||||||
|
SymVertBar: {"|", symClassOperator, posInfix}, // 13: '|'
|
||||||
|
SymDoubleVertBar: {"||", symClassOperator, posInfix}, // 14: '||'
|
||||||
|
SymComma: {",", symClassOperator, posInfix}, // 15: ','
|
||||||
|
SymColon: {":", symClassOperator, posInfix}, // 16: ':'
|
||||||
|
SymSemiColon: {";", symClassOperator, posInfix}, // 17: ';'
|
||||||
|
SymDot: {".", symClassOperator, posInfix}, // 18: '.'
|
||||||
|
SymDotSlash: {"./", symClassOperator, posInfix}, // 19: './'
|
||||||
|
SymQuote: {"'", symClassDelimiter, posLeaf}, // 20: '\''
|
||||||
|
SymDoubleQuote: {"\"", symClassDelimiter, posLeaf}, // 21: '"'
|
||||||
|
SymBackTick: {"`", symClassDelimiter, posLeaf}, // 22: '`'
|
||||||
|
SymExclamation: {"!", symClassOperator, posPostfix}, // 23: '!'
|
||||||
|
SymQuestion: {"?", symClassOperator, posInfix}, // 24: '?'
|
||||||
|
SymAmpersand: {"&", symClassOperator, posInfix}, // 25: '&'
|
||||||
|
SymDoubleAmpersand: {"&&", symClassOperator, posInfix}, // 26: '&&'
|
||||||
|
SymPercent: {"%", symClassOperator, posInfix}, // 27: '%'
|
||||||
|
SymAt: {"@", symClassOperator, posPrefix}, // 28: '@'
|
||||||
|
SymUndescore: {"_", symClassIdentifier, posLeaf}, // 29: '_'
|
||||||
|
SymEqual: {"=", symClassOperator, posInfix}, // 30: '='
|
||||||
|
SymDoubleEqual: {"==", symClassOperator, posInfix}, // 31: '=='
|
||||||
|
SymLess: {"<", symClassOperator, posInfix}, // 32: '<'
|
||||||
|
SymLessOrEqual: {"<=", symClassOperator, posInfix}, // 33: '<='
|
||||||
|
SymGreater: {">", symClassOperator, posInfix}, // 34: '>'
|
||||||
|
SymGreaterOrEqual: {">=", symClassOperator, posInfix}, // 35: '>='
|
||||||
|
SymLessGreater: {"<>", symClassOperator, posInfix}, // 36: '<>'
|
||||||
|
SymNotEqual: {"!=", symClassOperator, posInfix}, // 37: '!='
|
||||||
|
SymDollar: {"$", symClassOperator, posPrefix}, // 38: '$'
|
||||||
|
SymHash: {"#", symClassOperator, posPrefix}, // 39: '#'
|
||||||
|
SymOpenRound: {"(", symClassParenthesis, posPrefix}, // 40: '('
|
||||||
|
SymClosedRound: {")", symClassParenthesis, posPostfix}, // 41: ')'
|
||||||
|
SymOpenSquare: {"[", symClassParenthesis, posPrefix}, // 42: '['
|
||||||
|
SymClosedSquare: {"]", symClassParenthesis, posPostfix}, // 43: ']'
|
||||||
|
SymOpenBrace: {"{", symClassParenthesis, posPrefix}, // 44: '{'
|
||||||
|
SymClosedBrace: {"}", symClassParenthesis, posPostfix}, // 45: '}'
|
||||||
|
SymTilde: {"~", symClassOperator, posPrefix}, // 46: '~'
|
||||||
|
SymDoubleQuestion: {"??", symClassOperator, posInfix}, // 47: '??'
|
||||||
|
SymQuestionEqual: {"?=", symClassOperator, posInfix}, // 48: '?='
|
||||||
|
SymQuestionExclam: {"?!", symClassOperator, posInfix}, // 49: '?!'
|
||||||
|
SymDoubleAt: {"@@", symClassCommand, posLeaf}, // 50: '@@'
|
||||||
|
SymDoubleColon: {"::", symClassOperator, posInfix}, // 51: '::'
|
||||||
|
SymDoubleGreater: {">>", symClassOperator, posInfix}, // 52: '>>'
|
||||||
|
SymDoubleLess: {"<<", symClassOperator, posInfix}, // 53: '<<'
|
||||||
|
SymCaret: {"^", symClassOperator, posInfix}, // 54: '^'
|
||||||
|
SymDollarRound: {"$(", symClassOperator, posPrefix}, // 55: '$('
|
||||||
|
SymOpenClosedRound: {"()", symClassOperator, posPostfix}, // 56: '()'
|
||||||
|
SymDoubleDollar: {"$$", symClassCommand, posLeaf}, // 57: '$$'
|
||||||
|
SymDoubleDot: {"..", symClassOperator, posInfix}, // 58: '..'
|
||||||
|
SymTripleDot: {"...", symClassOperator, posPostfix}, // 59: '...'
|
||||||
|
SymStarEqual: {"*=", symClassOperator, posInfix}, // 60: '*='
|
||||||
|
SymSlashEqual: {"/=", symClassOperator, posInfix}, // 61: '/='
|
||||||
|
SymPercEqual: {"%=", symClassOperator, posInfix}, // 62: '%='
|
||||||
|
SymDoubleLessEqual: {"<<=", symClassOperator, posInfix}, // 63: '<<='
|
||||||
|
SymDoubleGreaterEqual: {">>=", symClassOperator, posInfix}, // 64: '>>='
|
||||||
|
SymAmpersandEqual: {"&=", symClassOperator, posInfix}, // 65: '&='
|
||||||
|
SymVertBarEqual: {"|=", symClassOperator, posInfix}, // 65: '|='
|
||||||
|
SymCaretEqual: {"^=", symClassOperator, posInfix}, // 66: '^='
|
||||||
|
SymPlusGreater: {"+>", symClassOperator, posInfix}, // 67: '+>'
|
||||||
|
SymLessPlus: {"<+", symClassOperator, posInfix}, // 68: '<+'
|
||||||
|
// SymChangeSign
|
||||||
|
// SymUnchangeSign
|
||||||
|
// SymIdentifier
|
||||||
|
// SymBool
|
||||||
|
// SymInteger
|
||||||
|
// SymVariable
|
||||||
|
// SymFloat
|
||||||
|
// SymFraction
|
||||||
|
// SymString
|
||||||
|
// SymIterator
|
||||||
|
// SymOr: "or",
|
||||||
|
// SymAnd: "and",
|
||||||
|
// SymNot: "not",
|
||||||
|
// SymComment
|
||||||
|
// SymFuncCall
|
||||||
|
// SymFuncDef
|
||||||
|
// SymList
|
||||||
|
// SymDict
|
||||||
|
// SymIndex
|
||||||
|
// SymExpression
|
||||||
|
// SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||||
|
// SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||||
|
// // SymOpenComment // 0: '/*'
|
||||||
|
// // SymClosedComment // 0: '*/'
|
||||||
|
// // SymOneLineComment // 0: '//'
|
||||||
|
// keywordBase
|
||||||
|
SymKwAnd: {"and", symClassOperator, posInfix},
|
||||||
|
SymKwNot: {"not", symClassOperator, posInfix},
|
||||||
|
SymKwOr: {"or", symClassOperator, posInfix},
|
||||||
|
SymKwBut: {"but", symClassOperator, posInfix},
|
||||||
|
SymKwFunc: {"func(", symClassDeclaration, posPrefix},
|
||||||
|
SymKwBuiltin: {"builtin", symClassOperator, posPrefix},
|
||||||
|
SymKwPlugin: {"plugin", symClassOperator, posPrefix},
|
||||||
|
SymKwIn: {"in", symClassOperator, posInfix},
|
||||||
|
SymKwInclude: {"include", symClassOperator, posPrefix},
|
||||||
|
SymKwNil: {"nil", symClassValue, posLeaf},
|
||||||
|
SymKwUnset: {"unset", symClassOperator, posPrefix},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SymToString(sym Symbol) string {
|
||||||
|
if s, ok := symbolMap[sym]; ok {
|
||||||
|
return s.repr
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func SymListToString(symList []Symbol, quote bool) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
if len(symList) == 0 {
|
||||||
|
sb.WriteString("<nothing>")
|
||||||
|
} else {
|
||||||
|
for _, sym := range symList {
|
||||||
|
if sb.Len() > 0 {
|
||||||
|
sb.WriteByte(',')
|
||||||
|
sb.WriteByte(' ')
|
||||||
|
}
|
||||||
|
if quote {
|
||||||
|
sb.WriteByte('`')
|
||||||
|
}
|
||||||
|
sb.WriteString(SymToString(sym))
|
||||||
|
if quote {
|
||||||
|
sb.WriteByte('`')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func StringEndsWithOperator(s string) bool {
|
||||||
|
return endingOperator(s) != SymNone
|
||||||
|
}
|
||||||
|
|
||||||
|
func endingOperator(s string) (sym Symbol) {
|
||||||
|
var matchLength = 0
|
||||||
|
sym = SymNone
|
||||||
|
lower := strings.TrimRight(strings.ToLower(s), " \t")
|
||||||
|
for symbol, spec := range symbolMap {
|
||||||
|
if strings.HasSuffix(lower, spec.repr) {
|
||||||
|
if len(spec.repr) > matchLength {
|
||||||
|
matchLength = len(spec.repr)
|
||||||
|
if spec.kind == symClassOperator && (spec.opType == posInfix || spec.opType == posPrefix) {
|
||||||
|
sym = symbol
|
||||||
|
} else {
|
||||||
|
sym = SymNone
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -7,70 +7,80 @@ 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: '/='
|
||||||
|
SymPercEqual // 62: '%='
|
||||||
|
SymDoubleLessEqual // 63: '<<='
|
||||||
|
SymDoubleGreaterEqual // 64: '>>='
|
||||||
|
SymAmpersandEqual // 65: '&='
|
||||||
|
SymVertBarEqual // 65: '|='
|
||||||
|
SymCaretEqual // 66: '^='
|
||||||
|
SymPlusGreater // 67: '+>'
|
||||||
|
SymLessPlus // 68: '<+'
|
||||||
SymChangeSign
|
SymChangeSign
|
||||||
SymUnchangeSign
|
SymUnchangeSign
|
||||||
|
SymDereference
|
||||||
SymIdentifier
|
SymIdentifier
|
||||||
SymBool
|
SymBool
|
||||||
SymInteger
|
SymInteger
|
||||||
@@ -88,6 +98,7 @@ const (
|
|||||||
SymList
|
SymList
|
||||||
SymDict
|
SymDict
|
||||||
SymIndex
|
SymIndex
|
||||||
|
SymRange // [index : index]
|
||||||
SymExpression
|
SymExpression
|
||||||
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
SymSelector // <selector> ::= <expr> "?" <selector-case> {":" <selector-case>} ["::" <default-selector-case>]
|
||||||
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
SymSelectorCase // <selector-case> ::= [<list>] "{" <multi-expr> "}"
|
||||||
|
|||||||
@@ -30,9 +30,9 @@ func TestFuncBase(t *testing.T) {
|
|||||||
/* 16 */ {`isString("3" + 1)`, true, nil},
|
/* 16 */ {`isString("3" + 1)`, true, nil},
|
||||||
/* 17 */ {`isList(["3", 1])`, true, nil},
|
/* 17 */ {`isList(["3", 1])`, true, nil},
|
||||||
/* 18 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
/* 18 */ {`isDict({"a":"3", "b":1})`, true, nil},
|
||||||
/* 19 */ {`isFract(1|3)`, true, nil},
|
/* 19 */ {`isFract(1:3)`, true, nil},
|
||||||
/* 20 */ {`isFract(3|1)`, false, nil},
|
/* 20 */ {`isFract(3:1)`, false, nil},
|
||||||
/* 21 */ {`isRational(3|1)`, true, nil},
|
/* 21 */ {`isRational(3:1)`, true, nil},
|
||||||
/* 22 */ {`fract("2.2(3)")`, newFraction(67, 30), nil},
|
/* 22 */ {`fract("2.2(3)")`, newFraction(67, 30), nil},
|
||||||
/* 23 */ {`fract("1.21(3)")`, newFraction(91, 75), nil},
|
/* 23 */ {`fract("1.21(3)")`, newFraction(91, 75), nil},
|
||||||
/* 24 */ {`fract(1.21(3))`, newFraction(91, 75), nil},
|
/* 24 */ {`fract(1.21(3))`, newFraction(91, 75), nil},
|
||||||
@@ -41,26 +41,27 @@ func TestFuncBase(t *testing.T) {
|
|||||||
/* 27 */ {`dec(2.0)`, float64(2), nil},
|
/* 27 */ {`dec(2.0)`, float64(2), nil},
|
||||||
/* 28 */ {`dec("2.0")`, float64(2), nil},
|
/* 28 */ {`dec("2.0")`, float64(2), nil},
|
||||||
/* 29 */ {`dec(true)`, float64(1), nil},
|
/* 29 */ {`dec(true)`, float64(1), nil},
|
||||||
/* 30 */ {`dec(true")`, nil, `[1:11] missing string termination "`},
|
/* 30 */ {`dec(true")`, nil, "[1:11] expected one of `,`, `)`, got `\"`"},
|
||||||
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`},
|
/* 31 */ {`dec()`, nil, `dec(): too few params -- expected 1, got 0`},
|
||||||
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`},
|
/* 32 */ {`dec(1,2,3)`, nil, `dec(): too many params -- expected 1, got 3`},
|
||||||
/* 33 */ {`isBool(false)`, true, nil},
|
/* 33 */ {`isBool(false)`, true, nil},
|
||||||
/* 34 */ {`fract(1|2)`, newFraction(1, 2), nil},
|
/* 34 */ {`fract(1:2)`, newFraction(1, 2), nil},
|
||||||
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
|
/* 35 */ {`fract(12,2)`, newFraction(6, 1), nil},
|
||||||
/* 36 */ {`bool(2)`, true, nil},
|
/* 36 */ {`bool(2)`, true, nil},
|
||||||
/* 37 */ {`bool(1|2)`, true, nil},
|
/* 37 */ {`bool(1:2)`, true, nil},
|
||||||
/* 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])`, nil, `bool(): can't convert list to bool`},
|
||||||
/* 42 */ {`dec(false)`, float64(0), nil},
|
/* 42 */ {`dec(false)`, float64(0), nil},
|
||||||
/* 43 */ {`dec(1|2)`, float64(0.5), nil},
|
/* 43 */ {`dec(1:2)`, float64(0.5), nil},
|
||||||
/* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
|
/* 44 */ {`dec([1])`, nil, `dec(): can't convert list to float`},
|
||||||
|
/* 45 */ {`eval("a=3"); a`, int64(3), 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, 10)
|
// runTestSuiteSpec(t, section, inputs, 45)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ func TestFuncString(t *testing.T) {
|
|||||||
/* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil},
|
/* 1 */ {`builtin "string"; strJoin("-", "one", "two", "three")`, "one-two-three", nil},
|
||||||
/* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil},
|
/* 2 */ {`builtin "string"; strJoin("-", ["one", "two", "three"])`, "one-two-three", nil},
|
||||||
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil},
|
/* 3 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin("-", ls)`, "one-two-three", nil},
|
||||||
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, `strJoin(): the "separator" parameter must be a string, got a integer (1)`},
|
/* 4 */ {`builtin "string"; ls= ["one", "two", "three"]; strJoin(1, ls)`, nil, `strJoin(): the "separator" parameter must be a string, got an integer (1)`},
|
||||||
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, `strJoin(): expected string, got integer (2)`},
|
/* 5 */ {`builtin "string"; ls= ["one", 2, "three"]; strJoin("-", ls)`, nil, `strJoin(): expected string, got integer (2)`},
|
||||||
/* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil},
|
/* 6 */ {`builtin "string"; "<"+strTrim(" bye bye ")+">"`, "<bye bye>", nil},
|
||||||
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
|
/* 7 */ {`builtin "string"; strSub("0123456789", 1,2)`, "12", nil},
|
||||||
|
|||||||
+10
-7
@@ -17,18 +17,21 @@ func TestExpr(t *testing.T) {
|
|||||||
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
|
/* 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 */ {`a=3; a+=1`, int64(4), nil},
|
/* 6 */ {`a=3; a+=1; a`, int64(4), nil},
|
||||||
/* 7 */ {`a=3; a-=1`, int64(2), nil},
|
/* 7 */ {`a=3; a-=1; a`, int64(2), nil},
|
||||||
/* 8 */ {`a=3; a*=2`, int64(6), nil},
|
/* 8 */ {`a=3; a*=2; a`, int64(6), nil},
|
||||||
/* 9 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil},
|
/* 9 */ {`v=[10,20,30]; v[0]+=1; v[0]`, int64(11), nil},
|
||||||
/* 10 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil},
|
/* 10 */ {`r={"a":10}; r["a"]+=1; r["a"]`, int64(11), nil},
|
||||||
/* 11 */ {`a=3; a/=2`, nil, `[1:8] left operand of "=" must be a variable or a collection's item`},
|
/* 11 */ {`a=3; a/=2`, int64(1), nil},
|
||||||
/* 12 */ {`*=2`, nil, `[1:2] left operand of "*=" must be a variable or a variable expression`},
|
/* 12 */ {`*=2`, nil, `[1:2] infix operator "*=" requires two non-nil operands, got 1`},
|
||||||
/* 13 */ {`a=3; a*=2+1; a`, int64(9), nil},
|
/* 13 */ {`a=3; a*=2+1; a`, int64(9), nil},
|
||||||
/* 14 */ {`a=3; (a*=2)+1; a`, int64(6), nil},
|
/* 14 */ {`a=3; (a*=2)+1; a`, int64(6), nil},
|
||||||
/* 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 */ {`
|
/* 17 */ {`true ? {"a"} :: {"b"}`, "a", nil},
|
||||||
|
/* 18 */ {`$$`, NewDict(map[any]any{"variables": NewDict(nil), "functions": NewDict(nil)}), nil},
|
||||||
|
///* 19 */ {`$$global`, NewDict(map[any]any{"variables": NewDict(nil), "functions": NewDict(nil)}), nil},
|
||||||
|
/* 19 */ {`
|
||||||
ds={
|
ds={
|
||||||
"init":func(@end){@current=0 but true},
|
"init":func(@end){@current=0 but true},
|
||||||
//"current":func(){current},
|
//"current":func(){current},
|
||||||
@@ -43,6 +46,6 @@ func TestExpr(t *testing.T) {
|
|||||||
}
|
}
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 17)
|
// runTestSuiteSpec(t, section, inputs, 18)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+23
-20
@@ -11,35 +11,39 @@ import (
|
|||||||
func TestFractionsParser(t *testing.T) {
|
func TestFractionsParser(t *testing.T) {
|
||||||
section := "Fraction"
|
section := "Fraction"
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`1|2`, newFraction(1, 2), nil},
|
/* 1 */ {`1:2`, newFraction(1, 2), nil},
|
||||||
/* 2 */ {`1|2 + 1`, newFraction(3, 2), nil},
|
/* 2 */ {`1:2 + 1`, newFraction(3, 2), nil},
|
||||||
/* 3 */ {`1|2 - 1`, newFraction(-1, 2), nil},
|
/* 3 */ {`1:2 - 1`, newFraction(-1, 2), nil},
|
||||||
/* 4 */ {`1|2 * 1`, newFraction(1, 2), nil},
|
/* 4 */ {`1:2 * 1`, newFraction(1, 2), nil},
|
||||||
/* 5 */ {`1|2 * 2|3`, newFraction(2, 6), nil},
|
/* 5 */ {`1:2 * 2:3`, newFraction(2, 6), nil},
|
||||||
/* 6 */ {`1|2 / 2|3`, newFraction(3, 4), nil},
|
/* 6 */ {`1:2 / 2:3`, newFraction(3, 4), nil},
|
||||||
/* 7 */ {`1|"5"`, nil, `denominator must be integer, got string (5)`},
|
/* 7 */ {`1:"5"`, nil, `denominator must be integer, got string (5)`},
|
||||||
/* 8 */ {`"1"|5`, nil, `numerator must be integer, got string (1)`},
|
/* 8 */ {`"1":5`, nil, `numerator must be integer, got string (1)`},
|
||||||
/* 9 */ {`1|+5`, nil, `[1:3] infix operator "|" requires two non-nil operands, got 1`},
|
/* 9 */ {`1:+5`, newFraction(1, 5), nil},
|
||||||
/* 10 */ {`1|(-2)`, newFraction(-1, 2), nil},
|
/* 10 */ {`1:(-2)`, newFraction(-1, 2), nil},
|
||||||
/* 11 */ {`builtin "math.arith"; add(1|2, 2|3)`, newFraction(7, 6), nil},
|
/* 11 */ {`builtin "math.arith"; add(1:2, 2:3)`, newFraction(7, 6), nil},
|
||||||
/* 12 */ {`builtin "math.arith"; add(1|2, 1.0, 2)`, float64(3.5), nil},
|
/* 12 */ {`builtin "math.arith"; add(1:2, 1.0, 2)`, float64(3.5), nil},
|
||||||
/* 13 */ {`builtin "math.arith"; mul(1|2, 2|3)`, newFraction(2, 6), nil},
|
/* 13 */ {`builtin "math.arith"; mul(1:2, 2:3)`, newFraction(2, 6), nil},
|
||||||
/* 14 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil},
|
/* 14 */ {`builtin "math.arith"; mul(1:2, 1.0, 2)`, float64(1.0), nil},
|
||||||
/* 15 */ {`1|0`, nil, `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},
|
||||||
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
|
/* 19 */ {`fract("+1")`, newFraction(1, 1), nil},
|
||||||
/* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
|
/* 20 */ {`fract("1a")`, (*FractionType)(nil), `strconv.ParseInt: parsing "1a": invalid syntax`},
|
||||||
/* 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},
|
||||||
|
/* 24 */ {`1:(2-2)`, nil, `[1:3] division by zero`},
|
||||||
|
/* 25 */ {`[0,1][1-1]:1`, newFraction(0, 1), nil},
|
||||||
}
|
}
|
||||||
|
// runTestSuiteSpec(t, section, inputs, 25)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFractionToStringSimple(t *testing.T) {
|
func TestFractionToStringSimple(t *testing.T) {
|
||||||
source := newFraction(1, 2)
|
source := newFraction(1, 2)
|
||||||
want := "1|2"
|
want := "1:2"
|
||||||
got := source.ToString(0)
|
got := source.ToString(0)
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf(`(1,2) -> result = %v [%T], want = %v [%T]`, got, got, want, want)
|
t.Errorf(`(1,2) -> result = %v [%T], want = %v [%T]`, got, got, want, want)
|
||||||
@@ -55,10 +59,9 @@ func TestFractionToStringMultiline(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Check this test: the output string ends with a space
|
func TestToStringMultilineTty(t *testing.T) {
|
||||||
func _TestToStringMultilineTty(t *testing.T) {
|
|
||||||
source := newFraction(-1, 2)
|
source := newFraction(-1, 2)
|
||||||
want := "\x1b[4m-1\x1b[0m\n2"
|
want := "\x1b[4m-1\x1b[0m\n 2"
|
||||||
got := source.ToString(MultiLine | TTY)
|
got := source.ToString(MultiLine | TTY)
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Errorf(`(1,2) -> result = %#v [%T], want = %#v [%T]`, got, got, want, want)
|
t.Errorf(`(1,2) -> result = %#v [%T], want = %#v [%T]`, got, got, want, want)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ func TestFuncs(t *testing.T) {
|
|||||||
/* 22 */ {`f=func(a,b){a*2+b}; f(b=2,a=1)`, int64(4), nil},
|
/* 22 */ {`f=func(a,b){a*2+b}; f(b=2,a=1)`, int64(4), nil},
|
||||||
/* 23 */ {`f=func(a=10,b=10){a*2+b}; f(b=1)`, int64(21), nil},
|
/* 23 */ {`f=func(a=10,b=10){a*2+b}; f(b=1)`, int64(21), nil},
|
||||||
/* 24 */ {`f=func(a,b){a*2+b}; f(a=1,2)`, nil, `f(): positional param nr 2 not allowed after named params`},
|
/* 24 */ {`f=func(a,b){a*2+b}; f(a=1,2)`, nil, `f(): positional param nr 2 not allowed after named params`},
|
||||||
|
/* 25 */ {`f=func(x,x){x}`, nil, `[1:11] parameter "x" at position 2 already defined at position 1`},
|
||||||
// /* 20 */ {`a=[func(){3}]; a[0]()`, int64(3), nil},
|
// /* 20 */ {`a=[func(){3}]; a[0]()`, int64(3), nil},
|
||||||
// /* 20 */ {`m={}; m["f"]=func(){3}; m["f"]()`, int64(3), nil},
|
// /* 20 */ {`m={}; m["f"]=func(){3}; m["f"]()`, int64(3), nil},
|
||||||
// /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
|
// /* 18 */ {`f=func(a){a*2}`, nil, errors.New(`[1:24] expected "function-param-value", got ")"`)},
|
||||||
|
|||||||
+4
-2
@@ -16,11 +16,13 @@ func TestCollections(t *testing.T) {
|
|||||||
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
/* 3 */ {`"abcdef"[1:]`, "bcdef", nil},
|
||||||
/* 4 */ {`"abcdef"[:]`, "abcdef", nil},
|
/* 4 */ {`"abcdef"[:]`, "abcdef", nil},
|
||||||
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
|
// /* 5 */ {`[0,1,2,3,4][:]`, ListType{int64(0), int64(1), int64(2), int64(3), int64(4)}, nil},
|
||||||
/* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] left operand '(1, 2)' [pair] and right operand '3' [integer] are not compatible with operator ":"`},
|
/* 5 */ {`"abcdef"[1:2:3]`, nil, `[1:14] invalid range specification`},
|
||||||
|
/* 6 */ {`"abcdef"[((1>0)?{1}:{0}):3]`, "bc", nil},
|
||||||
|
/* 7 */ {`"abcdef"[[0,1][0]:1]`, "a", nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("EXPR_PATH", ".")
|
t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
// parserTestSpec(t, section, inputs, 5)
|
// runTestSuiteSpec(t, section, inputs, 5)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -9,15 +9,15 @@ 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},
|
||||||
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it++; it.index`, int64(0), nil},
|
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it++; it.index`, int64(0), nil},
|
||||||
/* 9 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, true, nil},
|
/* 9 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, nil, nil},
|
||||||
/* 10 */ {`it=$(1,2,3); it++`, int64(1), nil},
|
/* 10 */ {`it=$(1,2,3); it++`, int64(1), nil},
|
||||||
/* 11 */ {`it=$(1,2,3); it++; it.reset; it++`, int64(1), nil},
|
/* 11 */ {`it=$(1,2,3); it++; it.reset; it++`, int64(1), nil},
|
||||||
/* 12 */ {`it=$([1,2,3,4],1); it++`, int64(2), nil},
|
/* 12 */ {`it=$([1,2,3,4],1); it++`, int64(2), nil},
|
||||||
@@ -25,8 +25,10 @@ func TestIteratorParser(t *testing.T) {
|
|||||||
/* 14 */ {`it=$([1,2,3,4],1,3,2); it++; it++;`, int64(4), nil},
|
/* 14 */ {`it=$([1,2,3,4],1,3,2); it++; it++;`, int64(4), nil},
|
||||||
/* 15 */ {`it=$([1,2,3,4],1,2,2); it++; it++;`, nil, `EOF`},
|
/* 15 */ {`it=$([1,2,3,4],1,2,2); it++; it++;`, nil, `EOF`},
|
||||||
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
|
/* 16 */ {`include "test-resources/filter.expr"; it=$(ds,10); it++`, int64(2), nil},
|
||||||
|
/* 17 */ {`it=$({"next":func(){5}}); it++`, int64(5), nil},
|
||||||
|
/* 18 */ {`it=$({"next":func(){5}}); it.clean`, nil, nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
// runTestSuiteSpec(t, section, inputs, 11)
|
//runTestSuiteSpec(t, section, inputs, 18)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-18
@@ -23,8 +23,8 @@ func TestListParser(t *testing.T) {
|
|||||||
/* 9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil},
|
/* 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;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator "<<"`},
|
/* 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;`, nil, `[1:4] left operand '2' [integer] and right operand '3' [integer] are not compatible with operator ">>"`},
|
/* 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", ".")
|
||||||
|
|||||||
+18
-1
@@ -14,10 +14,27 @@ 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},
|
||||||
|
/* 19 */ {`a=1; a^=2`, int64(3), nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
|
|
||||||
//runTestSuiteSpec(t, section, inputs, 3)
|
// runTestSuiteSpec(t, section, inputs, 4)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-17
@@ -77,9 +77,9 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 63 */ {`"1.5" == `, nil, `[1:8] infix operator "==" requires two non-nil operands, got 1`},
|
/* 63 */ {`"1.5" == `, nil, `[1:8] infix operator "==" requires two non-nil operands, got 1`},
|
||||||
/* 64 */ {`"1.5" != `, nil, `[1:8] infix operator "!=" requires two non-nil operands, got 1`},
|
/* 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 not 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},
|
||||||
@@ -94,10 +94,10 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 80 */ {`5 % 2.0`, nil, `[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`},
|
/* 80 */ {`5 % 2.0`, nil, `[1:4] left operand '5' [integer] and right operand '2' [float] are not compatible with operator "%"`},
|
||||||
/* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
|
/* 81 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
|
||||||
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
/* 82 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
||||||
/* 83 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
|
/* 83 */ {`"a" < "b" AND NOT 2 == 1`, true, nil},
|
||||||
/* 84 */ {`~ 2 > 1`, false, nil},
|
/* 84 */ {`NOT 2 > 1`, false, nil},
|
||||||
/* 85 */ {`~ true && true`, false, nil},
|
/* 85 */ {`nOT true && true`, false, nil},
|
||||||
/* 86 */ {`~ false || true`, true, nil},
|
/* 86 */ {`NOT false || true`, true, nil},
|
||||||
/* 87 */ {`false but true`, true, nil},
|
/* 87 */ {`false but true`, true, nil},
|
||||||
/* 88 */ {`2+3 but 5*2`, int64(10), nil},
|
/* 88 */ {`2+3 but 5*2`, int64(10), nil},
|
||||||
/* 89 */ {`x=2`, int64(2), nil},
|
/* 89 */ {`x=2`, int64(2), nil},
|
||||||
@@ -132,18 +132,16 @@ func TestGeneralParser(t *testing.T) {
|
|||||||
/* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
|
/* 118 */ {`{"key":}`, nil, "[1:9] expected `dictionary-value`, got `}`"},
|
||||||
/* 119 */ {`{}`, &DictType{}, nil},
|
/* 119 */ {`{}`, &DictType{}, nil},
|
||||||
/* 120 */ {`v=10; v++; v`, int64(11), nil},
|
/* 120 */ {`v=10; v++; v`, int64(11), nil},
|
||||||
/* 121 */ {`1+1|2+0.5`, float64(2), nil},
|
/* 121 */ {`1.2()`, newFraction(6, 5), nil},
|
||||||
/* 122 */ {`1.2()`, newFraction(6, 5), nil},
|
/* 122 */ {`x="abc"; x ?! #x`, int64(3), nil},
|
||||||
/* 123 */ {`1|(2-2)`, nil, `division by zero`},
|
/* 123 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
|
||||||
/* 124 */ {`x="abc"; x ?! #x`, int64(3), nil},
|
/* 124 */ {`x ?! (x+1)`, nil, nil},
|
||||||
/* 125 */ {`x ?! #x`, nil, `[1:7] prefix/postfix operator "#" do not support operand '<nil>' [nil]`},
|
/* 125 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
|
||||||
/* 126 */ {`x ?! (x+1)`, nil, nil},
|
/* 126 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
|
||||||
/* 127 */ {`"abx" ?! (x+1)`, nil, `[1:6] left operand of "?!" must be a variable`},
|
/* 127 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
|
||||||
/* 128 */ {`"abx" ?? "pqr"`, nil, `[1:6] left operand of "??" must be a variable`},
|
|
||||||
/* 129 */ {`"abx" ?= "pqr"`, nil, `[1:6] left operand of "?=" must be a variable`},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// t.Setenv("EXPR_PATH", ".")
|
// t.Setenv("EXPR_PATH", ".")
|
||||||
//runTestSuiteSpec(t, section, inputs, 130)
|
// runTestSuiteSpec(t, section, inputs, 114)
|
||||||
runTestSuite(t, section, inputs)
|
runTestSuite(t, section, inputs)
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-11
@@ -8,18 +8,18 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func _TestImportPlugin(t *testing.T) {
|
// func TestImportPlugin(t *testing.T) {
|
||||||
t.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
|
// t.Setenv("PLUGINS", "${HOME}/go/src/git.portale-stac.it/go")
|
||||||
t.Setenv("EXPR_PLUGIN_PATH","${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
|
// t.Setenv("EXPR_PLUGIN_PATH","${PLUGINS}/expr-json-plugin:${PLUGINS}/expr-csv-plugin")
|
||||||
|
|
||||||
gotCount, gotErr := importPluginFromSearchPath("json")
|
// gotCount, gotErr := importPluginFromSearchPath("json")
|
||||||
if gotCount != 1 {
|
// if gotCount != 1 {
|
||||||
t.Errorf("Import count: got=%d, want=1", gotCount)
|
// t.Errorf("Import count: got=%d, want=1", gotCount)
|
||||||
}
|
// }
|
||||||
if gotErr != nil {
|
// if gotErr != nil {
|
||||||
t.Errorf("importPlugin() failed: %v", gotErr)
|
// t.Errorf("importPlugin() failed: %v", gotErr)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func TestPluginExists(t *testing.T) {
|
func TestPluginExists(t *testing.T) {
|
||||||
name := "json"
|
name := "json"
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ func TestRelational(t *testing.T) {
|
|||||||
/* 8 */ {`true != false`, true, nil},
|
/* 8 */ {`true != false`, true, nil},
|
||||||
/* 9 */ {`1.0 != 3.0-2`, false, nil},
|
/* 9 */ {`1.0 != 3.0-2`, false, nil},
|
||||||
/* 10 */ {`[1,2] != [2,1]`, true, nil},
|
/* 10 */ {`[1,2] != [2,1]`, true, nil},
|
||||||
/* 11 */ {`1|2 == 1|3`, false, nil},
|
/* 11 */ {`1:2 == 1:3`, false, nil},
|
||||||
/* 12 */ {`1|2 != 1|3`, true, nil},
|
/* 12 */ {`1:2 != 1:3`, true, nil},
|
||||||
/* 13 */ {`1|2 == 4|8`, true, nil},
|
/* 13 */ {`1:2 == 4:8`, true, nil},
|
||||||
/* 14 */ {`1 < 8`, true, nil},
|
/* 14 */ {`1 < 8`, true, nil},
|
||||||
/* 15 */ {`1 <= 8`, true, nil},
|
/* 15 */ {`1 <= 8`, true, nil},
|
||||||
/* 16 */ {`"a" < "b"`, true, nil},
|
/* 16 */ {`"a" < "b"`, true, nil},
|
||||||
@@ -32,10 +32,10 @@ func TestRelational(t *testing.T) {
|
|||||||
/* 19 */ {`1.0 <= 8`, true, nil},
|
/* 19 */ {`1.0 <= 8`, true, nil},
|
||||||
/* 20 */ {`1.0 <= 1.0`, true, nil},
|
/* 20 */ {`1.0 <= 1.0`, true, nil},
|
||||||
/* 21 */ {`1.0 == 1`, true, nil},
|
/* 21 */ {`1.0 == 1`, true, nil},
|
||||||
/* 22 */ {`1|2 < 1|3`, false, nil},
|
/* 22 */ {`1:2 < 1:3`, false, nil},
|
||||||
/* 23 */ {`1|2 <= 1|3`, false, nil},
|
/* 23 */ {`1:2 <= 1:3`, false, nil},
|
||||||
/* 24 */ {`1|2 > 1|3`, true, nil},
|
/* 24 */ {`1:2 > 1:3`, true, nil},
|
||||||
/* 25 */ {`1|2 >= 1|3`, true, nil},
|
/* 25 */ {`1:2 >= 1:3`, true, nil},
|
||||||
/* 26 */ {`[1,2,3] > [2]`, true, nil},
|
/* 26 */ {`[1,2,3] > [2]`, true, nil},
|
||||||
/* 27 */ {`[1,2,3] > [9]`, false, nil},
|
/* 27 */ {`[1,2,3] > [9]`, false, nil},
|
||||||
/* 28 */ {`[1,2,3] >= [6]`, false, nil},
|
/* 28 */ {`[1,2,3] >= [6]`, false, nil},
|
||||||
|
|||||||
+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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,20 +15,26 @@ const (
|
|||||||
priRange
|
priRange
|
||||||
priBut
|
priBut
|
||||||
priAssign
|
priAssign
|
||||||
|
priInsert
|
||||||
priOr
|
priOr
|
||||||
priAnd
|
priAnd
|
||||||
priNot
|
priNot
|
||||||
priRelational
|
priRelational
|
||||||
|
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
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,8 +68,8 @@ func (s *term) Clone() (d *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
d = &term{
|
d = &term{
|
||||||
tk: *s.tk.Clone(),
|
tk: *s.tk.Clone(),
|
||||||
parent: s.parent,
|
parent: s.parent,
|
||||||
children: children,
|
children: children,
|
||||||
position: s.position,
|
position: s.position,
|
||||||
priority: s.priority,
|
priority: s.priority,
|
||||||
@@ -198,6 +204,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
|
||||||
@@ -211,11 +221,11 @@ func (term *term) checkOperands() (err error) {
|
|||||||
}
|
}
|
||||||
case posPrefix:
|
case posPrefix:
|
||||||
if term.children == nil || len(term.children) != 1 || term.children[0] == nil {
|
if term.children == nil || len(term.children) != 1 || term.children[0] == nil {
|
||||||
err = term.tk.Errorf("prefix operator %q requires one not nil operand", term.tk.String())
|
err = term.tk.Errorf("prefix operator %q requires one non-nil operand", term.tk.String())
|
||||||
}
|
}
|
||||||
case posPostfix:
|
case posPostfix:
|
||||||
if term.children == nil || len(term.children) != 1 || term.anyChildrenNil() {
|
if term.children == nil || len(term.children) != 1 || term.anyChildrenNil() {
|
||||||
err = term.tk.Errorf("postfix operator %q requires one not nil operand", term.tk.String())
|
err = term.tk.Errorf("postfix operator %q requires one non-nil operand", term.tk.String())
|
||||||
}
|
}
|
||||||
case posMultifix:
|
case posMultifix:
|
||||||
if term.children == nil || len(term.children) < 3 || term.anyChildrenNil() {
|
if term.children == nil || len(term.children) < 3 || term.anyChildrenNil() {
|
||||||
|
|||||||
@@ -79,6 +79,10 @@ func (tk *Token) IsSymbol(sym Symbol) bool {
|
|||||||
return tk.Sym == sym
|
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
|
||||||
@@ -93,6 +97,15 @@ func (tk *Token) Error() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tk *Token) ErrorText() (err string) {
|
||||||
|
if tk.Sym == SymError {
|
||||||
|
if msg, ok := tk.Value.(error); ok {
|
||||||
|
err = msg.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (tk *Token) Errors(msg string) (err error) {
|
func (tk *Token) Errors(msg string) (err error) {
|
||||||
err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
|
err = fmt.Errorf("[%d:%d] %v", tk.row, tk.col, msg)
|
||||||
return
|
return
|
||||||
@@ -104,6 +117,10 @@ func (tk *Token) ErrorExpectedGot(symbol string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
|
func (tk *Token) ErrorExpectedGotString(symbol, got string) (err error) {
|
||||||
err = fmt.Errorf("[%d:%d] expected `%s`, got `%s`", tk.row, tk.col, symbol, got)
|
return tk.ErrorExpectedGotStringWithPrefix("expected", symbol, got)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *Token) ErrorExpectedGotStringWithPrefix(prefix, symbol, got string) (err error) {
|
||||||
|
err = fmt.Errorf("[%d:%d] %s %s, got `%s`", tk.row, tk.col, prefix, symbol, got)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user