Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7430e9bdf7 | |||
| 8084ffbc19 | |||
| e896e06a93 | |||
| 7df7d1463f | |||
| 948bad6f96 | |||
| 1c4ba5ef0b | |||
| 648533cbe3 | |||
| 8e596d5979 | |||
| 07aecfc4e7 | |||
| 2f616f349b | |||
| 8c55f4ac4b | |||
| bf2c7df0f0 | |||
| 6b561fea45 | |||
| daca05e637 | |||
| 4df222496d | |||
| 072fd9af39 | |||
| 77f36642b4 | |||
| b6da9bcad4 | |||
| fec7cc546c | |||
| d8dc2939b4 | |||
| dba8e01aa0 | |||
| 6e868b568d | |||
| 73e4ad7b87 | |||
| 6a6c897268 | |||
| 9eb3e2d072 | |||
| b86ce6fe62 | |||
| 38316cec0b | |||
| ee7f488ebb | |||
| 02ea20cdb5 | |||
| 20cffbddfa | |||
| cd8f331a32 | |||
| 4b2b573420 | |||
| 7ed5a806f9 | |||
| ac5c97bfd3 | |||
| e1c24daac4 | |||
| a62f27b104 | |||
| 1055569dd6 | |||
| 1aea1c14d2 | |||
| 081395be5f | |||
| 35a599b284 | |||
| eda3037855 | |||
| 47c181546a | |||
| a8a5d6aaa6 | |||
| 84b255a51b | |||
| 9efdeffcac | |||
| d34b9d8a48 | |||
| 1bf8015da1 | |||
| 3ccbeb3978 | |||
| 0c719025cd | |||
| 08617378e0 | |||
| e6844ad1e8 | |||
| 3a4e217891 | |||
| 0f293fdbbc | |||
| 45faea7620 | |||
| e5a61b5638 | |||
| 5285b61320 | |||
| 5950630cf7 | |||
| 78e431f2b9 | |||
| c7dce8288f | |||
| c10053253c | |||
| a3c7cf2efa | |||
| dfa1491093 |
@@ -10,10 +10,12 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ListIterator struct {
|
type ListIterator struct {
|
||||||
a *kern.ListType
|
a *array.ArrayType
|
||||||
count int64
|
count int64
|
||||||
index int64
|
index int64
|
||||||
start int64
|
start int64
|
||||||
@@ -21,7 +23,7 @@ type ListIterator struct {
|
|||||||
step int64
|
step int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewListIterator(list *kern.ListType, args []any) (it *ListIterator) {
|
func NewListIterator(list *array.ArrayType, args []any) (it *ListIterator) {
|
||||||
var argc int = 0
|
var argc int = 0
|
||||||
listLen := int64(len(([]any)(*list)))
|
listLen := int64(len(([]any)(*list)))
|
||||||
if args != nil {
|
if args != nil {
|
||||||
@@ -29,21 +31,21 @@ func NewListIterator(list *kern.ListType, args []any) (it *ListIterator) {
|
|||||||
}
|
}
|
||||||
it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1}
|
it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1}
|
||||||
if argc >= 1 {
|
if argc >= 1 {
|
||||||
if i, err := kern.ToGoInt64(args[0], "start index"); err == nil {
|
if i, err := types.ToGoInt64(args[0], "start index"); err == nil {
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
i = listLen + i
|
i = listLen + i
|
||||||
}
|
}
|
||||||
it.start = i
|
it.start = i
|
||||||
}
|
}
|
||||||
if argc >= 2 {
|
if argc >= 2 {
|
||||||
if i, err := kern.ToGoInt64(args[1], "stop index"); err == nil {
|
if i, err := types.ToGoInt64(args[1], "stop index"); err == nil {
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
i = listLen + i
|
i = listLen + i
|
||||||
}
|
}
|
||||||
it.stop = i
|
it.stop = i
|
||||||
}
|
}
|
||||||
if argc >= 3 {
|
if argc >= 3 {
|
||||||
if i, err := kern.ToGoInt64(args[2], "step"); err == nil {
|
if i, err := types.ToGoInt64(args[2], "step"); err == nil {
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
i = -i
|
i = -i
|
||||||
}
|
}
|
||||||
@@ -60,8 +62,8 @@ func NewListIterator(list *kern.ListType, args []any) (it *ListIterator) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewArrayIterator(array []any) (it *ListIterator) {
|
func NewArrayIterator(a []any) (it *ListIterator) {
|
||||||
it = &ListIterator{a: (*kern.ListType)(&array), count: 0, index: -1, start: 0, stop: int64(len(array)) - 1, step: 1}
|
it = &ListIterator{a: (*array.ArrayType)(&a), count: 0, index: -1, start: 0, stop: int64(len(a)) - 1, step: 1}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
+73
-39
@@ -12,6 +12,14 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/boolean"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/float"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/fract"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/list"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/str"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -24,42 +32,47 @@ func isNilFunc(ctx kern.ExprContext, name string, args map[string]any) (result a
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isIntFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func isIntFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = kern.IsInteger(args[kern.ParamValue])
|
result = types.IsInteger(args[kern.ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isFloatFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func isFloatFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = kern.IsFloat(args[kern.ParamValue])
|
result = float.IsFloat(args[kern.ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isBoolFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func isBoolFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = kern.IsBool(args[kern.ParamValue])
|
result = boolean.IsBool(args[kern.ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isStringFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func isStringFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = kern.IsString(args[kern.ParamValue])
|
result = str.IsString(args[kern.ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isFractionFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func isFractionFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = kern.IsFraction(args[kern.ParamValue])
|
result = fract.IsFraction(args[kern.ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isRationalFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func isRationalFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = kern.IsRational(args[kern.ParamValue])
|
result = fract.IsRational(args[kern.ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isListFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func isArrayFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = kern.IsList(args[kern.ParamValue])
|
result = array.IsArray(args[kern.ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDictionaryFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func isDictionaryFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
result = kern.IsDict(args[kern.ParamValue])
|
result = dict.IsDict(args[kern.ParamValue])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func isListFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
result = list.IsLinkedList(args[kern.ParamValue])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +80,7 @@ func boolFunc(ctx kern.ExprContext, name string, args map[string]any) (result an
|
|||||||
switch v := args[kern.ParamValue].(type) {
|
switch v := args[kern.ParamValue].(type) {
|
||||||
case int64:
|
case int64:
|
||||||
result = (v != 0)
|
result = (v != 0)
|
||||||
case *kern.FractionType:
|
case *fract.FractionType:
|
||||||
result = v.N() != 0
|
result = v.N() != 0
|
||||||
case float64:
|
case float64:
|
||||||
result = v != 0.0
|
result = v != 0.0
|
||||||
@@ -75,9 +88,9 @@ func boolFunc(ctx kern.ExprContext, name string, args map[string]any) (result an
|
|||||||
result = v
|
result = v
|
||||||
case string:
|
case string:
|
||||||
result = len(v) > 0
|
result = len(v) > 0
|
||||||
case *kern.ListType:
|
case *array.ArrayType:
|
||||||
result = len(*v) > 0
|
result = len(*v) > 0
|
||||||
case *kern.DictType:
|
case *dict.DictType:
|
||||||
result = len(*v) > 0
|
result = len(*v) > 0
|
||||||
default:
|
default:
|
||||||
err = kern.ErrCantConvert(name, v, "bool")
|
err = kern.ErrCantConvert(name, v, "bool")
|
||||||
@@ -102,7 +115,7 @@ func intFunc(ctx kern.ExprContext, name string, args map[string]any) (result any
|
|||||||
if i, err = strconv.Atoi(v); err == nil {
|
if i, err = strconv.Atoi(v); err == nil {
|
||||||
result = int64(i)
|
result = int64(i)
|
||||||
}
|
}
|
||||||
case *kern.FractionType:
|
case *fract.FractionType:
|
||||||
result = int64(v.N() / v.D())
|
result = int64(v.N() / v.D())
|
||||||
default:
|
default:
|
||||||
err = kern.ErrCantConvert(name, v, "int")
|
err = kern.ErrCantConvert(name, v, "int")
|
||||||
@@ -110,7 +123,7 @@ func intFunc(ctx kern.ExprContext, name string, args map[string]any) (result any
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func decFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func floatFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
switch v := args[kern.ParamValue].(type) {
|
switch v := args[kern.ParamValue].(type) {
|
||||||
case int64:
|
case int64:
|
||||||
result = float64(v)
|
result = float64(v)
|
||||||
@@ -127,7 +140,7 @@ func decFunc(ctx kern.ExprContext, name string, args map[string]any) (result any
|
|||||||
if f, err = strconv.ParseFloat(v, 64); err == nil {
|
if f, err = strconv.ParseFloat(v, 64); err == nil {
|
||||||
result = f
|
result = f
|
||||||
}
|
}
|
||||||
case *kern.FractionType:
|
case *fract.FractionType:
|
||||||
result = v.ToFloat()
|
result = v.ToFloat()
|
||||||
default:
|
default:
|
||||||
err = kern.ErrCantConvert(name, v, "float")
|
err = kern.ErrCantConvert(name, v, "float")
|
||||||
@@ -149,7 +162,7 @@ func stringFunc(ctx kern.ExprContext, name string, args map[string]any) (result
|
|||||||
}
|
}
|
||||||
case string:
|
case string:
|
||||||
result = v
|
result = v
|
||||||
case *kern.FractionType:
|
case *fract.FractionType:
|
||||||
result = v.ToString(0)
|
result = v.ToString(0)
|
||||||
case kern.Formatter:
|
case kern.Formatter:
|
||||||
result = v.ToString(0)
|
result = v.ToString(0)
|
||||||
@@ -174,19 +187,19 @@ func fractFunc(ctx kern.ExprContext, name string, args map[string]any) (result a
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
result = kern.NewFraction(v, den)
|
result = fract.NewFraction(v, den)
|
||||||
}
|
}
|
||||||
case float64:
|
case float64:
|
||||||
result, err = kern.Float64ToFraction(v)
|
result, err = fract.Float64ToFraction(v)
|
||||||
case bool:
|
case bool:
|
||||||
if v {
|
if v {
|
||||||
result = kern.NewFraction(1, 1)
|
result = fract.NewFraction(1, 1)
|
||||||
} else {
|
} else {
|
||||||
result = kern.NewFraction(0, 1)
|
result = fract.NewFraction(0, 1)
|
||||||
}
|
}
|
||||||
case string:
|
case string:
|
||||||
result, err = kern.MakeGeneratingFraction(v)
|
result, err = fract.MakeGeneratingFraction(v)
|
||||||
case *kern.FractionType:
|
case *fract.FractionType:
|
||||||
result = v
|
result = v
|
||||||
default:
|
default:
|
||||||
err = kern.ErrCantConvert(name, v, "float")
|
err = kern.ErrCantConvert(name, v, "float")
|
||||||
@@ -255,18 +268,35 @@ func setFunc(ctx kern.ExprContext, name string, args map[string]any) (result any
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// func unsetFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func charFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
// var varName string
|
var ord int
|
||||||
// var ok bool
|
|
||||||
|
|
||||||
// if varName, ok = args[kern.ParamName].(string); !ok {
|
if n, ok := args[kern.ParamValue].(byte); ok {
|
||||||
// return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
|
ord = int(n)
|
||||||
// } else {
|
} else if n, ok := args[kern.ParamValue].(int64); ok {
|
||||||
// ctx.GetParent().DeleteVar(varName)
|
ord = int(n)
|
||||||
// result = nil
|
} else if n, ok := args[kern.ParamValue].(int); ok {
|
||||||
// }
|
ord = n
|
||||||
// return
|
} else {
|
||||||
// }
|
return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
|
||||||
|
}
|
||||||
|
if ord < 0 || ord > 255 {
|
||||||
|
err = kern.ErrFuncInvalidArg(name, fmt.Sprintf("character code must be in range 0-255, got %d", ord))
|
||||||
|
} else {
|
||||||
|
result = string(rune(ord))
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func seqFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
list := list.NewLinkedList()
|
||||||
|
items := args[kern.ParamValue].([]any)
|
||||||
|
for _, arg := range items {
|
||||||
|
list.PushBack(arg)
|
||||||
|
}
|
||||||
|
result = list
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
//// import
|
//// import
|
||||||
|
|
||||||
@@ -282,12 +312,13 @@ func ImportBuiltinsFuncs(ctx kern.ExprContext) {
|
|||||||
ctx.RegisterFunc("isString", kern.NewGolangFunctor(isStringFunc), kern.TypeBoolean, anyParams)
|
ctx.RegisterFunc("isString", kern.NewGolangFunctor(isStringFunc), kern.TypeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("isFract", kern.NewGolangFunctor(isFractionFunc), kern.TypeBoolean, anyParams)
|
ctx.RegisterFunc("isFract", kern.NewGolangFunctor(isFractionFunc), kern.TypeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("isRational", kern.NewGolangFunctor(isRationalFunc), kern.TypeBoolean, anyParams)
|
ctx.RegisterFunc("isRational", kern.NewGolangFunctor(isRationalFunc), kern.TypeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("isList", kern.NewGolangFunctor(isListFunc), kern.TypeBoolean, anyParams)
|
ctx.RegisterFunc("isArray", kern.NewGolangFunctor(isArrayFunc), kern.TypeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("isDict", kern.NewGolangFunctor(isDictionaryFunc), kern.TypeBoolean, anyParams)
|
ctx.RegisterFunc("isDict", kern.NewGolangFunctor(isDictionaryFunc), kern.TypeBoolean, anyParams)
|
||||||
|
ctx.RegisterFunc("isList", kern.NewGolangFunctor(isListFunc), kern.TypeBoolean, anyParams)
|
||||||
|
|
||||||
ctx.RegisterFunc("bool", kern.NewGolangFunctor(boolFunc), kern.TypeBoolean, anyParams)
|
ctx.RegisterFunc("bool", kern.NewGolangFunctor(boolFunc), kern.TypeBoolean, anyParams)
|
||||||
ctx.RegisterFunc("int", kern.NewGolangFunctor(intFunc), kern.TypeInt, anyParams)
|
ctx.RegisterFunc("int", kern.NewGolangFunctor(intFunc), kern.TypeInt, anyParams)
|
||||||
ctx.RegisterFunc("dec", kern.NewGolangFunctor(decFunc), kern.TypeFloat, anyParams)
|
ctx.RegisterFunc("float", kern.NewGolangFunctor(floatFunc), kern.TypeFloat, anyParams)
|
||||||
ctx.RegisterFunc("string", kern.NewGolangFunctor(stringFunc), kern.TypeString, anyParams)
|
ctx.RegisterFunc("string", kern.NewGolangFunctor(stringFunc), kern.TypeString, anyParams)
|
||||||
ctx.RegisterFunc("fract", kern.NewGolangFunctor(fractFunc), kern.TypeFraction, []kern.ExprFuncParam{
|
ctx.RegisterFunc("fract", kern.NewGolangFunctor(fractFunc), kern.TypeFraction, []kern.ExprFuncParam{
|
||||||
kern.NewFuncParam(kern.ParamValue),
|
kern.NewFuncParam(kern.ParamValue),
|
||||||
@@ -308,10 +339,13 @@ func ImportBuiltinsFuncs(ctx kern.ExprContext) {
|
|||||||
kern.NewFuncParam(kern.ParamValue),
|
kern.NewFuncParam(kern.ParamValue),
|
||||||
})
|
})
|
||||||
|
|
||||||
// ctx.RegisterFunc("unset", kern.NewGolangFunctor(unsetFunc), kern.TypeAny, []kern.ExprFuncParam{
|
ctx.RegisterFunc("char", kern.NewGolangFunctor(charFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||||
// kern.NewFuncParam(kern.ParamName),
|
kern.NewFuncParam(kern.ParamValue),
|
||||||
// kern.NewFuncParam(kern.ParamValue),
|
})
|
||||||
// })
|
|
||||||
|
ctx.RegisterFunc("seq", kern.NewGolangFunctor(seqFunc), kern.TypeLinkedList, []kern.ExprFuncParam{
|
||||||
|
kern.NewFuncParamFlag(kern.ParamValue, kern.PfRepeat),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
+2
-1
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
)
|
)
|
||||||
|
|
||||||
func importFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func importFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
@@ -47,7 +48,7 @@ func doImport(ctx kern.ExprContext, name string, dirList []string, it kern.Itera
|
|||||||
var expr *scan.Ast
|
var expr *scan.Ast
|
||||||
scanner := scan.NewScanner(file, scan.DefaultTranslations())
|
scanner := scan.NewScanner(file, scan.DefaultTranslations())
|
||||||
parser := NewParser()
|
parser := NewParser()
|
||||||
if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, scan.SymEos); err == nil {
|
if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, sym.SymEos); err == nil {
|
||||||
result, err = expr.Eval(ctx)
|
result, err = expr.Eval(ctx)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+36
-14
@@ -9,32 +9,39 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/boolean"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
iterParamOperator = "operator"
|
iterParamOperator = "operator"
|
||||||
iterParamVars = "vars"
|
iterParamVars = "vars"
|
||||||
iterVarStatus = "status"
|
iterVarStatus = "status"
|
||||||
|
iterParamStatus = "status"
|
||||||
|
iterVarAbort = "__abort"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseRunArgs(localCtx kern.ExprContext, args map[string]any) (it kern.Iterator, op kern.Functor, err error) {
|
func parseRunArgs(localCtx kern.ExprContext, args map[string]any) (it kern.Iterator, op kern.Functor, err error) {
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if it, ok = args[kern.ParamIterator].(kern.Iterator); !ok {
|
if it, ok = args[kern.ParamIterator].(kern.Iterator); !ok {
|
||||||
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", kern.ParamIterator, args[kern.ParamIterator], kern.TypeName(args[kern.ParamIterator]))
|
// err = fmt.Errorf("parameter %q must be an iterator, passed %v [%s]", kern.ParamIterator, args[kern.ParamIterator], kern.TypeName(args[kern.ParamIterator]))
|
||||||
|
err = errBadParamType(kern.ParamIterator, kern.TypeIterator, args[kern.ParamIterator])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if args[iterParamOperator] != nil {
|
if args[iterParamOperator] != nil {
|
||||||
if op, ok = args[iterParamOperator].(kern.Functor); !ok || op == nil {
|
if op, ok = args[iterParamOperator].(kern.Functor); !ok || op == nil {
|
||||||
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], kern.TypeName(args[iterParamOperator]))
|
// err = fmt.Errorf("parameter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], kern.TypeName(args[iterParamOperator]))
|
||||||
|
err = errBadParamType(iterParamOperator, "function", args[iterParamOperator])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var vars *kern.DictType
|
var vars *dict.DictType
|
||||||
if vars, ok = args[iterParamVars].(*kern.DictType); !ok && args[iterParamVars] != nil {
|
if vars, ok = args[iterParamVars].(*dict.DictType); !ok && args[iterParamVars] != nil {
|
||||||
err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[iterParamVars], kern.TypeName(args[iterParamVars]))
|
// err = fmt.Errorf("parameter %q must be a dictionary, passed %v [%s]", iterParamVars, args[iterParamVars], kern.TypeName(args[iterParamVars]))
|
||||||
|
err = errBadParamType(iterParamVars, kern.TypeDict, args[iterParamVars])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,12 +57,21 @@ func parseRunArgs(localCtx kern.ExprContext, args map[string]any) (it kern.Itera
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func errBadParamType(paramName, wantedType string, wantedValue any) error {
|
||||||
|
return fmt.Errorf("type of %q must be %s, passed %v [%s]", paramName, wantedType, wantedValue, kern.TypeName(wantedValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
func abortFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
if status, provided := args[iterParamStatus]; provided {
|
||||||
|
ctx.UnsafeSetVar("@"+iterVarStatus, status)
|
||||||
|
}
|
||||||
|
ctx.UnsafeSetVar("@"+iterVarAbort, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func runFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func runFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var it kern.Iterator
|
var it kern.Iterator
|
||||||
var ok bool
|
|
||||||
var op kern.Functor
|
var op kern.Functor
|
||||||
var v any
|
|
||||||
// var usingDefaultOp = false
|
|
||||||
var params map[string]any
|
var params map[string]any
|
||||||
var item any
|
var item any
|
||||||
|
|
||||||
@@ -66,14 +82,20 @@ func runFunc(ctx kern.ExprContext, name string, args map[string]any) (result any
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
localCtx.RegisterFunc("abort", kern.NewGolangFunctor(abortFunc), kern.TypeAny, []kern.ExprFuncParam{
|
||||||
|
kern.NewFuncParamFlagDef(iterParamStatus, kern.PfOptional, "aborted"),
|
||||||
|
})
|
||||||
|
|
||||||
|
params = map[string]any{kern.ParamIndex: 0, kern.ParamItem: nil}
|
||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||||
if op != nil {
|
if op != nil {
|
||||||
params = map[string]any{kern.ParamIndex: it.Index(), kern.ParamItem: item}
|
// params = map[string]any{kern.ParamIndex: it.Index(), kern.ParamItem: item}
|
||||||
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
|
params[kern.ParamIndex] = it.Index()
|
||||||
|
params[kern.ParamItem] = item
|
||||||
|
if _, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
|
||||||
break
|
break
|
||||||
} else {
|
} else if abortAny, exists := localCtx.GetVar(iterVarAbort); exists {
|
||||||
var success bool
|
if abort, ok := boolean.ToBool(abortAny); ok && abort {
|
||||||
if success, ok = kern.ToBool(v); !success || !ok {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,7 +107,7 @@ func runFunc(ctx kern.ExprContext, name string, args map[string]any) (result any
|
|||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if op == nil {
|
if op == nil {
|
||||||
ctx.UnsafeSetVar(iterVarStatus, it.Count())
|
localCtx.UnsafeSetVar(iterVarStatus, it.Count())
|
||||||
}
|
}
|
||||||
result, _ = localCtx.GetVar(iterVarStatus)
|
result, _ = localCtx.GetVar(iterVarStatus)
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-21
@@ -9,10 +9,14 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/float"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/fract"
|
||||||
)
|
)
|
||||||
|
|
||||||
func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) {
|
func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) {
|
||||||
if !(kern.IsNumber(paramValue) || kern.IsFraction(paramValue)) /*|| isList(paramValue)*/ {
|
if !(types.IsNumber(paramValue) || fract.IsFraction(paramValue)) /*|| isArray(paramValue)*/ {
|
||||||
err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected",
|
err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected",
|
||||||
funcName, paramPos+1, subPos+1, level, paramValue)
|
funcName, paramPos+1, subPos+1, level, paramValue)
|
||||||
}
|
}
|
||||||
@@ -23,13 +27,13 @@ func doAdd(ctx kern.ExprContext, name string, it kern.Iterator, count, level int
|
|||||||
var sumAsFloat, sumAsFract bool
|
var sumAsFloat, sumAsFract bool
|
||||||
var floatSum float64 = 0.0
|
var floatSum float64 = 0.0
|
||||||
var intSum int64 = 0
|
var intSum int64 = 0
|
||||||
var fractSum *kern.FractionType
|
var fractSum *fract.FractionType
|
||||||
var v any
|
var v any
|
||||||
|
|
||||||
level++
|
level++
|
||||||
|
|
||||||
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
||||||
if list, ok := v.(*kern.ListType); ok {
|
if list, ok := v.(*array.ArrayType); ok {
|
||||||
v = NewListIterator(list, nil)
|
v = NewListIterator(list, nil)
|
||||||
}
|
}
|
||||||
if subIter, ok := v.(kern.Iterator); ok {
|
if subIter, ok := v.(kern.Iterator); ok {
|
||||||
@@ -47,29 +51,29 @@ func doAdd(ctx kern.ExprContext, name string, it kern.Iterator, count, level int
|
|||||||
count++
|
count++
|
||||||
|
|
||||||
if !sumAsFloat {
|
if !sumAsFloat {
|
||||||
if kern.IsFloat(v) {
|
if float.IsFloat(v) {
|
||||||
sumAsFloat = true
|
sumAsFloat = true
|
||||||
if sumAsFract {
|
if sumAsFract {
|
||||||
floatSum = fractSum.ToFloat()
|
floatSum = fractSum.ToFloat()
|
||||||
} else {
|
} else {
|
||||||
floatSum = float64(intSum)
|
floatSum = float64(intSum)
|
||||||
}
|
}
|
||||||
} else if !sumAsFract && kern.IsFraction(v) {
|
} else if !sumAsFract && fract.IsFraction(v) {
|
||||||
fractSum = kern.NewFraction(intSum, 1)
|
fractSum = fract.NewFraction(intSum, 1)
|
||||||
sumAsFract = true
|
sumAsFract = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sumAsFloat {
|
if sumAsFloat {
|
||||||
floatSum += kern.NumAsFloat(v)
|
floatSum += types.NumAsFloat(v)
|
||||||
} else if sumAsFract {
|
} else if sumAsFract {
|
||||||
var item *kern.FractionType
|
var item *fract.FractionType
|
||||||
var ok bool
|
var ok bool
|
||||||
if item, ok = v.(*kern.FractionType); !ok {
|
if item, ok = v.(*fract.FractionType); !ok {
|
||||||
iv, _ := v.(int64)
|
iv, _ := v.(int64)
|
||||||
item = kern.NewFraction(iv, 1)
|
item = fract.NewFraction(iv, 1)
|
||||||
}
|
}
|
||||||
fractSum = kern.SumFract(fractSum, item)
|
fractSum = fract.SumFract(fractSum, item)
|
||||||
} else {
|
} else {
|
||||||
iv, _ := v.(int64)
|
iv, _ := v.(int64)
|
||||||
intSum += iv
|
intSum += iv
|
||||||
@@ -98,12 +102,12 @@ func doMul(ctx kern.ExprContext, name string, it kern.Iterator, count, level int
|
|||||||
var mulAsFloat, mulAsFract bool
|
var mulAsFloat, mulAsFract bool
|
||||||
var floatProd float64 = 1.0
|
var floatProd float64 = 1.0
|
||||||
var intProd int64 = 1
|
var intProd int64 = 1
|
||||||
var fractProd *kern.FractionType
|
var fractProd *fract.FractionType
|
||||||
var v any
|
var v any
|
||||||
|
|
||||||
level++
|
level++
|
||||||
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
for v, err = it.Next(); err == nil; v, err = it.Next() {
|
||||||
if list, ok := v.(*kern.ListType); ok {
|
if list, ok := v.(*array.ArrayType); ok {
|
||||||
v = NewListIterator(list, nil)
|
v = NewListIterator(list, nil)
|
||||||
}
|
}
|
||||||
if subIter, ok := v.(kern.Iterator); ok {
|
if subIter, ok := v.(kern.Iterator); ok {
|
||||||
@@ -123,29 +127,29 @@ func doMul(ctx kern.ExprContext, name string, it kern.Iterator, count, level int
|
|||||||
count++
|
count++
|
||||||
|
|
||||||
if !mulAsFloat {
|
if !mulAsFloat {
|
||||||
if kern.IsFloat(v) {
|
if float.IsFloat(v) {
|
||||||
mulAsFloat = true
|
mulAsFloat = true
|
||||||
if mulAsFract {
|
if mulAsFract {
|
||||||
floatProd = fractProd.ToFloat()
|
floatProd = fractProd.ToFloat()
|
||||||
} else {
|
} else {
|
||||||
floatProd = float64(intProd)
|
floatProd = float64(intProd)
|
||||||
}
|
}
|
||||||
} else if !mulAsFract && kern.IsFraction(v) {
|
} else if !mulAsFract && fract.IsFraction(v) {
|
||||||
fractProd = kern.NewFraction(intProd, 1)
|
fractProd = fract.NewFraction(intProd, 1)
|
||||||
mulAsFract = true
|
mulAsFract = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if mulAsFloat {
|
if mulAsFloat {
|
||||||
floatProd *= kern.NumAsFloat(v)
|
floatProd *= types.NumAsFloat(v)
|
||||||
} else if mulAsFract {
|
} else if mulAsFract {
|
||||||
var item *kern.FractionType
|
var item *fract.FractionType
|
||||||
var ok bool
|
var ok bool
|
||||||
if item, ok = v.(*kern.FractionType); !ok {
|
if item, ok = v.(*fract.FractionType); !ok {
|
||||||
iv, _ := v.(int64)
|
iv, _ := v.(int64)
|
||||||
item = kern.NewFraction(iv, 1)
|
item = fract.NewFraction(iv, 1)
|
||||||
}
|
}
|
||||||
fractProd = kern.MulFract(fractProd, item)
|
fractProd = fract.MulFract(fractProd, item)
|
||||||
} else {
|
} else {
|
||||||
iv, _ := v.(int64)
|
iv, _ := v.(int64)
|
||||||
intProd *= iv
|
intProd *= iv
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// builtin-os-file.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.portale-stac.it/go-pkg/expr/file"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
)
|
||||||
|
|
||||||
|
const fileByteIteratorType = "fileByteIterator"
|
||||||
|
|
||||||
|
type fileFileByteIterator struct {
|
||||||
|
fileIterBase
|
||||||
|
b byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFileByteIterator(r *file.Reader, autoClose bool) *fileFileByteIterator {
|
||||||
|
return &fileFileByteIterator{
|
||||||
|
fileIterBase: fileIterBase{reader: r, index: -1, count: 0, autoClose: autoClose},
|
||||||
|
b: 0}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileByteIterator) TypeName() string {
|
||||||
|
return fileByteIteratorType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileByteIterator) String() string {
|
||||||
|
return it.repr(fileByteIteratorType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileByteIterator) Next() (item any, err error) { // must return io.EOF after the last item
|
||||||
|
if it.b, err = it.reader.ReadByte(); err == nil {
|
||||||
|
it.increment()
|
||||||
|
item = it.b
|
||||||
|
} else if it.autoClose {
|
||||||
|
it.Clean()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileByteIterator) Current() (item any, err error) {
|
||||||
|
item = it.b
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileByteIterator) Reset() (err error) {
|
||||||
|
if err = it.reader.Reset(); err == nil {
|
||||||
|
it.reset()
|
||||||
|
it.b = 0
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileByteIterator) Clean() (err error) {
|
||||||
|
if it.reader.Valid() {
|
||||||
|
if err = it.reader.GetFile().Close(); err == nil {
|
||||||
|
it.reader = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// it.reset()
|
||||||
|
// it.b = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileByteIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
||||||
|
switch name {
|
||||||
|
case kern.NextName:
|
||||||
|
v, err = it.Next()
|
||||||
|
case kern.ResetName:
|
||||||
|
err = it.Reset()
|
||||||
|
case kern.CleanName:
|
||||||
|
err = it.Clean()
|
||||||
|
case kern.IndexName:
|
||||||
|
v = int64(it.Index())
|
||||||
|
case kern.CurrentName:
|
||||||
|
v, err = it.Current()
|
||||||
|
case kern.CountName:
|
||||||
|
v = it.count
|
||||||
|
default:
|
||||||
|
err = kern.ErrNoOperation(name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileByteIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
var handle *file.Reader
|
||||||
|
var invalidFileHandle any
|
||||||
|
var autoClose bool
|
||||||
|
|
||||||
|
if handle, invalidFileHandle, autoClose, err = initFileHandle(ctx, name, args); err == nil {
|
||||||
|
if handle != nil {
|
||||||
|
result = newFileByteIterator(handle, autoClose)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
||||||
|
err = errInvalidFileHandle(name, invalidFileHandle)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// builtin-os-file.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.portale-stac.it/go-pkg/expr/file"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
)
|
||||||
|
|
||||||
|
const fileLineIteratorType = "fileLineIterator"
|
||||||
|
|
||||||
|
type fileFileLineIterator struct {
|
||||||
|
fileIterBase
|
||||||
|
line string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFileLineIterator(r *file.Reader, autoClose bool) *fileFileLineIterator {
|
||||||
|
return &fileFileLineIterator{
|
||||||
|
fileIterBase: fileIterBase{reader: r, index: -1, count: 0, autoClose: autoClose},
|
||||||
|
line: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileLineIterator) TypeName() string {
|
||||||
|
return fileLineIteratorType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileLineIterator) String() string {
|
||||||
|
return it.repr(fileLineIteratorType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileLineIterator) Next() (item any, err error) { // must return io.EOF after the last item
|
||||||
|
if it.line, err = it.reader.ReadString('\n'); err == nil {
|
||||||
|
it.increment()
|
||||||
|
item = it.line[0 : len(it.line)-1]
|
||||||
|
} else if it.autoClose {
|
||||||
|
it.Clean()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileLineIterator) Current() (item any, err error) {
|
||||||
|
if len(it.line) > 0 {
|
||||||
|
item = it.line[0 : len(it.line)-1]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileLineIterator) Reset() (err error) {
|
||||||
|
if err = it.reader.Reset(); err == nil {
|
||||||
|
it.reset()
|
||||||
|
it.line = ""
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileLineIterator) Clean() (err error) {
|
||||||
|
if it.reader != nil {
|
||||||
|
if err = it.reader.Close(); err == nil {
|
||||||
|
it.reader = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// it.reset()
|
||||||
|
// it.line = ""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *fileFileLineIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
||||||
|
switch name {
|
||||||
|
case kern.NextName:
|
||||||
|
v, err = it.Next()
|
||||||
|
case kern.ResetName:
|
||||||
|
err = it.Reset()
|
||||||
|
case kern.CleanName:
|
||||||
|
err = it.Clean()
|
||||||
|
case kern.IndexName:
|
||||||
|
v = int64(it.Index())
|
||||||
|
case kern.CurrentName:
|
||||||
|
v, err = it.Current()
|
||||||
|
case kern.CountName:
|
||||||
|
v = it.count
|
||||||
|
default:
|
||||||
|
err = kern.ErrNoOperation(name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileLineIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
var handle *file.Reader
|
||||||
|
var invalidFileHandle any
|
||||||
|
var autoClose bool
|
||||||
|
|
||||||
|
if handle, invalidFileHandle, autoClose, err = initFileHandle(ctx, name, args); err == nil {
|
||||||
|
if handle != nil {
|
||||||
|
result = newFileLineIterator(handle, autoClose)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
||||||
|
err = errInvalidFileHandle(name, invalidFileHandle)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
+23
-103
@@ -6,146 +6,66 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr/file"
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
)
|
)
|
||||||
|
|
||||||
const paramHandleOrPath = "handle-or-path"
|
const paramHandleOrPath = "handle-or-path"
|
||||||
const fileReadTextIteratorType = "fileReadTextIterator"
|
|
||||||
|
|
||||||
type fileReadTextIterator struct {
|
type fileIterBase struct {
|
||||||
osReader *osReader
|
reader *file.Reader
|
||||||
index int64
|
index int64
|
||||||
count int64
|
count int64
|
||||||
line string
|
|
||||||
autoClose bool
|
autoClose bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newReadTextIterator(r *osReader, autoClose bool) *fileReadTextIterator {
|
func (it *fileIterBase) Count() int64 {
|
||||||
return &fileReadTextIterator{osReader: r, index: -1, autoClose: autoClose}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) TypeName() string {
|
|
||||||
return fileReadTextIteratorType
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) String() string {
|
|
||||||
if it.osReader != nil && it.osReader.fh != nil {
|
|
||||||
return fmt.Sprintf("$(%s@%q)", fileReadTextIteratorType, it.osReader.fh.Name())
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("$(%s@<nil>)", fileReadTextIteratorType)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) Count() int64 {
|
|
||||||
return it.count
|
return it.count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *fileReadTextIterator) Next() (item any, err error) { // must return io.EOF after the last item
|
func (it *fileIterBase) Index() int64 {
|
||||||
if it.osReader.fh != nil {
|
|
||||||
if it.line, err = it.osReader.reader.ReadString('\n'); err == nil {
|
|
||||||
it.index++
|
|
||||||
it.count++
|
|
||||||
item = it.line[0 : len(it.line)-1]
|
|
||||||
} else if it.autoClose {
|
|
||||||
it.Clean()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) Current() (item any, err error) {
|
|
||||||
if len(it.line) > 0 {
|
|
||||||
item = it.line[0 : len(it.line)-1]
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) Index() int64 {
|
|
||||||
return it.index
|
return it.index
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *fileReadTextIterator) Reset() (err error) {
|
func (it *fileIterBase) HasOperation(name string) bool {
|
||||||
if _, err = it.osReader.fh.Seek(0, io.SeekStart); err == nil {
|
|
||||||
it.index = -1
|
|
||||||
it.count = 0
|
|
||||||
it.line = ""
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *fileReadTextIterator) HasOperation(name string) bool {
|
|
||||||
return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
|
return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *fileReadTextIterator) Clean() (err error) {
|
func (it *fileIterBase) reset() {
|
||||||
if it.osReader.fh != nil {
|
it.index = -1
|
||||||
if err = it.osReader.fh.Close(); err == nil {
|
it.count = 0
|
||||||
it.osReader = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *fileReadTextIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
func (it *fileIterBase) increment() {
|
||||||
switch name {
|
it.index++
|
||||||
case kern.NextName:
|
it.count++
|
||||||
v, err = it.Next()
|
|
||||||
case kern.ResetName:
|
|
||||||
err = it.Reset()
|
|
||||||
case kern.CleanName:
|
|
||||||
err = it.Clean()
|
|
||||||
case kern.IndexName:
|
|
||||||
v = int64(it.Index())
|
|
||||||
case kern.CurrentName:
|
|
||||||
v, err = it.Current()
|
|
||||||
case kern.CountName:
|
|
||||||
v = it.count
|
|
||||||
default:
|
|
||||||
err = kern.ErrNoOperation(name)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileReadIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func (it *fileIterBase) repr(typeName string) string {
|
||||||
var handle *osReader
|
if it.reader.Valid() {
|
||||||
var invalidFileHandle any
|
return fmt.Sprintf("$(%s@%q)", typeName, it.reader.GetName())
|
||||||
var ok, autoClose bool
|
}
|
||||||
|
return fmt.Sprintf("$(%s@<nil>)", typeName)
|
||||||
|
}
|
||||||
|
|
||||||
result = nil
|
func initFileHandle(ctx kern.ExprContext, name string, args map[string]any) (handle *file.Reader, invalidFileHandle any, autoClose bool, err error) {
|
||||||
if handle, ok = args[paramHandleOrPath].(*osReader); !ok {
|
var ok bool
|
||||||
|
|
||||||
|
if handle, ok = args[paramHandleOrPath].(*file.Reader); !ok {
|
||||||
if fileName, ok := args[paramHandleOrPath].(string); ok && len(fileName) > 0 {
|
if fileName, ok := args[paramHandleOrPath].(string); ok && len(fileName) > 0 {
|
||||||
var handleAny any
|
var handleAny any
|
||||||
if handleAny, err = openFileFunc(ctx, name, map[string]any{kern.ParamFilepath: fileName}); err != nil {
|
if handleAny, err = openFileFunc(ctx, name, map[string]any{kern.ParamFilepath: fileName}); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if handleAny != nil {
|
if handleAny != nil {
|
||||||
handle = handleAny.(*osReader)
|
handle = handleAny.(*file.Reader)
|
||||||
autoClose = true
|
autoClose = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
invalidFileHandle = args[paramHandleOrPath]
|
invalidFileHandle = args[paramHandleOrPath]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if handle != nil {
|
|
||||||
result = newReadTextIterator(handle, autoClose)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if err == nil && (handle == nil || invalidFileHandle != nil) {
|
|
||||||
err = errInvalidFileHandle(name, invalidFileHandle)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// func ImportOsIterFuncs(ctx ExprContext) {
|
|
||||||
// ctx.RegisterFunc("fileReadIterator", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{
|
|
||||||
// NewFuncParam(paramHandleOrPath),
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func init() {
|
|
||||||
// RegisterBuiltinModule("os.file", ImportOsIterFuncs, "Operating system file iterator functions")
|
|
||||||
// }
|
|
||||||
|
|||||||
+42
-72
@@ -5,11 +5,10 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr/file"
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,44 +16,6 @@ const (
|
|||||||
osLimitCh = "limitCh"
|
osLimitCh = "limitCh"
|
||||||
)
|
)
|
||||||
|
|
||||||
type osHandle interface {
|
|
||||||
getFile() *os.File
|
|
||||||
}
|
|
||||||
|
|
||||||
type osWriter struct {
|
|
||||||
fh *os.File
|
|
||||||
writer *bufio.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *osWriter) TypeName() string {
|
|
||||||
return "osWriter"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *osWriter) String() string {
|
|
||||||
return "writer"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *osWriter) getFile() *os.File {
|
|
||||||
return h.fh
|
|
||||||
}
|
|
||||||
|
|
||||||
type osReader struct {
|
|
||||||
fh *os.File
|
|
||||||
reader *bufio.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *osReader) TypeName() string {
|
|
||||||
return "osReader"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *osReader) String() string {
|
|
||||||
return "reader"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *osReader) getFile() *os.File {
|
|
||||||
return h.fh
|
|
||||||
}
|
|
||||||
|
|
||||||
func errMissingFilePath(funcName string) error {
|
func errMissingFilePath(funcName string) error {
|
||||||
return fmt.Errorf("%s(): missing or invalid file path", funcName)
|
return fmt.Errorf("%s(): missing or invalid file path", funcName)
|
||||||
}
|
}
|
||||||
@@ -67,24 +28,26 @@ func errInvalidFileHandle(funcName string, v any) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func openFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
|
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
|
||||||
var fh *os.File
|
// var fh *os.File
|
||||||
if fh, err = os.Create(filePath); err == nil {
|
// if fh, err = os.Open(filePath); err == nil {
|
||||||
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
// result = file.NewReader(fh)
|
||||||
}
|
// }
|
||||||
|
result, err = file.OpenReader(filePath)
|
||||||
} else {
|
} else {
|
||||||
err = errMissingFilePath(name)
|
err = errMissingFilePath(name)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func openFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func createFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
|
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
|
||||||
var fh *os.File
|
// var fh *os.File
|
||||||
if fh, err = os.Open(filePath); err == nil {
|
// if fh, err = os.Create(filePath); err == nil {
|
||||||
result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
|
// result = file.NewWriter(fh)
|
||||||
}
|
// }
|
||||||
|
result, err = file.CreateWriter(filePath)
|
||||||
} else {
|
} else {
|
||||||
err = errMissingFilePath(name)
|
err = errMissingFilePath(name)
|
||||||
}
|
}
|
||||||
@@ -93,10 +56,11 @@ func openFileFunc(ctx kern.ExprContext, name string, args map[string]any) (resul
|
|||||||
|
|
||||||
func appendFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func appendFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
|
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
|
||||||
var fh *os.File
|
// var fh *os.File
|
||||||
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil {
|
// if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil {
|
||||||
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
|
// result = file.NewWriter(fh)
|
||||||
}
|
// }
|
||||||
|
result, err = file.AppendWriter(filePath)
|
||||||
} else {
|
} else {
|
||||||
err = errMissingFilePath(name)
|
err = errMissingFilePath(name)
|
||||||
}
|
}
|
||||||
@@ -104,18 +68,18 @@ func appendFileFunc(ctx kern.ExprContext, name string, args map[string]any) (res
|
|||||||
}
|
}
|
||||||
|
|
||||||
func closeFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func closeFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var handle osHandle
|
var handle file.Handle
|
||||||
var invalidFileHandle any
|
var invalidFileHandle any
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if handle, ok = args[kern.ParamHandle].(osHandle); !ok {
|
if handle, ok = args[kern.ParamHandle].(file.Handle); !ok {
|
||||||
invalidFileHandle = args[kern.ParamHandle]
|
invalidFileHandle = args[kern.ParamHandle]
|
||||||
}
|
}
|
||||||
|
|
||||||
if handle != nil {
|
if handle != nil {
|
||||||
if fh := handle.getFile(); fh != nil {
|
if fh := handle.GetFile(); fh != nil {
|
||||||
if w, ok := handle.(*osWriter); ok {
|
if w, ok := handle.(*file.Writer); ok {
|
||||||
err = w.writer.Flush()
|
err = w.Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -131,19 +95,20 @@ func closeFileFunc(ctx kern.ExprContext, name string, args map[string]any) (resu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fileWriteTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func fileWriteTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var handle osHandle
|
var handle file.Handle
|
||||||
var invalidFileHandle any
|
var invalidFileHandle any
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if handle, ok = args[kern.ParamHandle].(osHandle); !ok {
|
if handle, ok = args[kern.ParamHandle].(file.Handle); !ok {
|
||||||
invalidFileHandle = args[kern.ParamHandle]
|
invalidFileHandle = args[kern.ParamHandle]
|
||||||
}
|
}
|
||||||
|
|
||||||
if handle != nil {
|
if handle != nil {
|
||||||
if w, ok := handle.(*osWriter); ok {
|
if w, ok := handle.(*file.Writer); ok {
|
||||||
if v, exists := args[kern.ParamItem]; exists {
|
if v, exists := args[kern.ParamItem]; exists {
|
||||||
argv := v.([]any)
|
argv := v.([]any)
|
||||||
result, err = fmt.Fprint(w.writer, argv...)
|
// result, err = fmt.Fprint(w.writer, argv...)
|
||||||
|
result, err = w.Write(argv...)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
invalidFileHandle = handle
|
invalidFileHandle = handle
|
||||||
@@ -157,24 +122,24 @@ func fileWriteTextFunc(ctx kern.ExprContext, name string, args map[string]any) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fileReadTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func fileReadTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var handle osHandle
|
var handle file.Handle
|
||||||
var invalidFileHandle any
|
var invalidFileHandle any
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
result = nil
|
result = nil
|
||||||
if handle, ok = args[kern.ParamHandle].(osHandle); !ok || args[kern.ParamHandle] == nil {
|
if handle, ok = args[kern.ParamHandle].(file.Handle); !ok || args[kern.ParamHandle] == nil {
|
||||||
invalidFileHandle = args[kern.ParamHandle]
|
invalidFileHandle = args[kern.ParamHandle]
|
||||||
}
|
}
|
||||||
|
|
||||||
if handle != nil {
|
if handle != nil {
|
||||||
if r, ok := handle.(*osReader); ok {
|
if r, ok := handle.(*file.Reader); ok {
|
||||||
var limit byte = '\n'
|
var limit byte = '\n'
|
||||||
var v string
|
var v string
|
||||||
if s, ok := args[osLimitCh].(string); ok && len(s) > 0 {
|
if s, ok := args[osLimitCh].(string); ok && len(s) > 0 {
|
||||||
limit = s[0]
|
limit = s[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err = r.reader.ReadString(limit)
|
v, err = r.ReadString(limit)
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
@@ -197,19 +162,19 @@ func fileReadTextFunc(ctx kern.ExprContext, name string, args map[string]any) (r
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fileReadTextAllFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
func fileReadTextAllFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
var handle osHandle
|
var handle file.Handle
|
||||||
var invalidFileHandle any
|
var invalidFileHandle any
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
result = nil
|
result = nil
|
||||||
if handle, ok = args[kern.ParamHandle].(osHandle); !ok || args[kern.ParamHandle] == nil {
|
if handle, ok = args[kern.ParamHandle].(file.Handle); !ok || args[kern.ParamHandle] == nil {
|
||||||
invalidFileHandle = args[kern.ParamHandle]
|
invalidFileHandle = args[kern.ParamHandle]
|
||||||
}
|
}
|
||||||
|
|
||||||
if handle != nil {
|
if handle != nil {
|
||||||
if r, ok := handle.(*osReader); ok {
|
if r, ok := handle.(*file.Reader); ok {
|
||||||
var b []byte
|
var b []byte
|
||||||
b, err = io.ReadAll(r.reader)
|
b, err = r.ReadAll()
|
||||||
result = string(b)
|
result = string(b)
|
||||||
} else {
|
} else {
|
||||||
invalidFileHandle = handle
|
invalidFileHandle = handle
|
||||||
@@ -253,9 +218,14 @@ func ImportOsFuncs(ctx kern.ExprContext) {
|
|||||||
kern.NewFuncParam(kern.ParamHandle),
|
kern.NewFuncParam(kern.ParamHandle),
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx.RegisterFunc("fileReadIterator", kern.NewGolangFunctor(fileReadIteratorFunc), kern.TypeIterator, []kern.ExprFuncParam{
|
ctx.RegisterFunc("fileLineIterator", kern.NewGolangFunctor(fileLineIteratorFunc), kern.TypeIterator, []kern.ExprFuncParam{
|
||||||
kern.NewFuncParam(paramHandleOrPath),
|
kern.NewFuncParam(paramHandleOrPath),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ctx.RegisterFunc("fileByteIterator", kern.NewGolangFunctor(fileByteIteratorFunc), kern.TypeIterator, []kern.ExprFuncParam{
|
||||||
|
kern.NewFuncParam(paramHandleOrPath),
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
+9
-7
@@ -10,6 +10,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -43,8 +45,8 @@ func joinStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result
|
|||||||
if v, exists := args[kern.ParamItem]; exists {
|
if v, exists := args[kern.ParamItem]; exists {
|
||||||
argv := v.([]any)
|
argv := v.([]any)
|
||||||
if len(argv) == 1 {
|
if len(argv) == 1 {
|
||||||
if ls, ok := argv[0].(*kern.ListType); ok {
|
if a, ok := argv[0].(*array.ArrayType); ok {
|
||||||
result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
|
result, err = doJoinStr(name, sep, NewListIterator(a, nil))
|
||||||
} else if it, ok := argv[0].(kern.Iterator); ok {
|
} else if it, ok := argv[0].(kern.Iterator); ok {
|
||||||
result, err = doJoinStr(name, sep, it)
|
result, err = doJoinStr(name, sep, it)
|
||||||
} else if s, ok := argv[0].(string); ok {
|
} else if s, ok := argv[0].(string); ok {
|
||||||
@@ -72,11 +74,11 @@ func subStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result
|
|||||||
return nil, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
return nil, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
|
||||||
}
|
}
|
||||||
|
|
||||||
if start, err = kern.ToGoInt(args[kern.ParamStart], name+"()"); err != nil {
|
if start, err = types.ToGoInt(args[kern.ParamStart], name+"()"); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if count, err = kern.ToGoInt(args[kern.ParamCount], name+"()"); err != nil {
|
if count, err = types.ToGoInt(args[kern.ParamCount], name+"()"); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,11 +196,11 @@ func splitStrFunc(ctx kern.ExprContext, name string, args map[string]any) (resul
|
|||||||
} else {
|
} else {
|
||||||
parts = []string{}
|
parts = []string{}
|
||||||
}
|
}
|
||||||
list := make(kern.ListType, len(parts))
|
a := make(array.ArrayType, len(parts))
|
||||||
for i, part := range parts {
|
for i, part := range parts {
|
||||||
list[i] = part
|
a[i] = part
|
||||||
}
|
}
|
||||||
result = &list
|
result = &a
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# Builder resource file
|
||||||
|
# Created on gio 21 mag 2026, 15:35:18, CEST
|
||||||
|
|
||||||
|
# Program name
|
||||||
|
PROGRAM_NAME="ecli"
|
||||||
|
|
||||||
|
# Program version
|
||||||
|
PROGRAM_VERSION="$(<version.txt)"
|
||||||
|
|
||||||
|
# Preset platform
|
||||||
|
platform=("linux/amd64" "darwin/arm64")
|
||||||
|
|
||||||
|
#--- end of file ---
|
||||||
|
|
||||||
Executable
+199
@@ -0,0 +1,199 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
# All rights reserved.
|
||||||
|
|
||||||
|
|
||||||
|
RESOURCE_FILE=".build.rc"
|
||||||
|
BUILD_REGISTER=".build_register"
|
||||||
|
|
||||||
|
PROGRAM_NAME=""
|
||||||
|
PROGRAM_VERSION=""
|
||||||
|
|
||||||
|
function usage() {
|
||||||
|
prog=$(basename "${0}")
|
||||||
|
msgln "USAGE:"
|
||||||
|
#msgln " ${prog} <program-name> <program-version> <os>/<platform>..."
|
||||||
|
msgln " ${prog} <os>/<platform>..."
|
||||||
|
msgln " ${prog} --local Build the local exec"
|
||||||
|
msgln " ${prog} --init Create the resource file in the current directory"
|
||||||
|
msgln
|
||||||
|
if [ -r "${RESOURCE_FILE}" ]; then
|
||||||
|
msgln "Resource file '${RESOURCE_FILE}' content:"
|
||||||
|
cat >&2 ${RESOURCE_FILE}
|
||||||
|
else
|
||||||
|
msgln "Resource file '${RESOURCE_FILE}' not found"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function msgln() {
|
||||||
|
echo >&2 "${1}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function exitUsage() {
|
||||||
|
echo >&2 "${1}"
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function exitMsg() {
|
||||||
|
echo >&2 "${1}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# CMDLINE: help
|
||||||
|
if [ "${1}" == "help,," ] || [ "${1,,}" == "--help" ] || [ "${1,,}" == "-h" ]; then
|
||||||
|
usage
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# CMDLINE: init
|
||||||
|
if [ "${1,,}" == "--init" ]; then
|
||||||
|
cat >"${RESOURCE_FILE}" <<eot
|
||||||
|
# Builder resource file
|
||||||
|
# Created on $(date)
|
||||||
|
|
||||||
|
# Program name
|
||||||
|
PROGRAM_NAME="name"
|
||||||
|
|
||||||
|
# Program version
|
||||||
|
PROGRAM_VERSION="version"
|
||||||
|
|
||||||
|
# Preset platform
|
||||||
|
platform=("linux/amd64" "darwin/arm64")
|
||||||
|
|
||||||
|
#--- end of file ---
|
||||||
|
|
||||||
|
eot
|
||||||
|
msgln "Resource file '${RESOURCE_FILE}' create. Edit it to set valid values."
|
||||||
|
#${EDITOR-vi} "${RESOURCE_FILE}"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -r "${RESOURCE_FILE}" ]; then
|
||||||
|
if ! source "${RESOURCE_FILE}"; then
|
||||||
|
exitMsg "Can't load build resource file '${RESOURCE_FILE}'"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${PROGRAM_NAME}" ]; then
|
||||||
|
exitUsage "Missing program name"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${PROGRAM_VERSION}" ]; then
|
||||||
|
exitUsage "Missing program version"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
function getBuildCount() {
|
||||||
|
local reg ver count
|
||||||
|
if [ -r "${BUILD_REGISTER}" ]; then
|
||||||
|
reg=$(<"${BUILD_REGISTER}")
|
||||||
|
else
|
||||||
|
reg="${PROGRAM_VERSION} 0"
|
||||||
|
fi
|
||||||
|
read ver count <<<"${reg}"
|
||||||
|
if [ "${ver}" != "${PROGRAM_VERSION}" ]; then
|
||||||
|
count=0
|
||||||
|
fi
|
||||||
|
count=$((count+1))
|
||||||
|
echo >"${BUILD_REGISTER}" "${PROGRAM_VERSION} ${count}"
|
||||||
|
echo ${count}
|
||||||
|
}
|
||||||
|
|
||||||
|
function build() {
|
||||||
|
local target=${1} ext cmd
|
||||||
|
IFS=/ read os cpu <<<"${p}"
|
||||||
|
#msgln "OS=${os}; CPU=${cpu}"
|
||||||
|
ext=""
|
||||||
|
if [ "${os}" == 'windows' ]; then
|
||||||
|
ext=".exe"
|
||||||
|
fi
|
||||||
|
cmd="GOOS='${os}' GOARCH='${cpu}' go build -o '${PROGRAM_NAME}_v${PROGRAM_VERSION}_${os}_${cpu}${ext}'"
|
||||||
|
eval "${cmd}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildLocal() {
|
||||||
|
local ext cmd
|
||||||
|
ext=""
|
||||||
|
if [[ "${OSTYPE}" =~ win.* ]]; then
|
||||||
|
ext=".exe"
|
||||||
|
fi
|
||||||
|
cmd="go build -o '${PROGRAM_NAME}${ext}'"
|
||||||
|
eval "${cmd}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function gitTag() {
|
||||||
|
local gopath gopkg mod
|
||||||
|
local tag
|
||||||
|
|
||||||
|
if ! tag=$(git tag -l --sort=-version:refname "v[0-9]*.[0-9]*.[0-9]*"|head -1) || [ -z "${tag}" ]; then
|
||||||
|
gopath=$(go env GOPATH)
|
||||||
|
gopkg="${gopath}/pkg/mod/git.portale-stac.it/go-pkg"
|
||||||
|
if cd "${gopkg}" 2>/dev/null; then
|
||||||
|
mod=$(ls -1v |grep expr@|tail -1)
|
||||||
|
tag=${mod##*@}
|
||||||
|
cd - >/dev/null
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo ${tag}
|
||||||
|
}
|
||||||
|
|
||||||
|
function gitTagDate() {
|
||||||
|
local tag_name=${1}
|
||||||
|
local tag_date
|
||||||
|
|
||||||
|
if ! tag_date=$(git show --no-patch --format=%ci "${tag_name}") || [ -z "${tag_date}" ]; then
|
||||||
|
tag_date="n/a"
|
||||||
|
fi
|
||||||
|
echo ${tag_date}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createVersionSource() {
|
||||||
|
local tag tag_date
|
||||||
|
tag=$(gitTag)
|
||||||
|
if [ -z "${tag}" ]; then
|
||||||
|
tag="n/a"
|
||||||
|
else
|
||||||
|
tag_date=$(gitTagDate "${tag}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat >version.go <<eot
|
||||||
|
// Copyright (c) 2024-$(date +%Y) Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// version.go
|
||||||
|
package main
|
||||||
|
|
||||||
|
const (
|
||||||
|
PROGNAME = "${PROGRAM_NAME}"
|
||||||
|
VERSION = "v${PROGRAM_VERSION}(build $(getBuildCount)),$(date +"%Y/%m/%d") (celestino.amoroso@portale-stac.it)"
|
||||||
|
EXPR_VERSION = "${tag}"
|
||||||
|
EXPR_DATE = "${tag_date}"
|
||||||
|
)
|
||||||
|
eot
|
||||||
|
}
|
||||||
|
|
||||||
|
## -- TEST -- ##
|
||||||
|
|
||||||
|
# echo "Tag: $(gitTag)"
|
||||||
|
# echo "Tag Date: $(gitTagDate $(gitTag))"
|
||||||
|
# exit 0
|
||||||
|
|
||||||
|
## -- MAIN -- ##
|
||||||
|
createVersionSource
|
||||||
|
|
||||||
|
if [ "${1}" == "--local" ]; then
|
||||||
|
buildLocal
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${#} -gt 0 ]; then
|
||||||
|
for p; do
|
||||||
|
build "${p}"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
for p in ${platform[@]}; do
|
||||||
|
build "${p}"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
Executable
+84
@@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
# All rights reserved
|
||||||
|
|
||||||
|
RESOURCE_FILE=".build.rc"
|
||||||
|
BUILD_REGISTER=".build_register"
|
||||||
|
|
||||||
|
|
||||||
|
PASSWORD=
|
||||||
|
GITEA_USER="camoroso"
|
||||||
|
GITEA_PASSWORD_FILE="${HOME}/.gitea_password"
|
||||||
|
GITEA_OWNER="go-pkg"
|
||||||
|
GITEA_HOST="https://git.portale-stac.it"
|
||||||
|
GITEA_BASE_PATH="api/v1/packages"
|
||||||
|
GITEA_PKG_TYPE="generic"
|
||||||
|
|
||||||
|
|
||||||
|
function msgln() {
|
||||||
|
echo >&2 "${1}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function exitMsg() {
|
||||||
|
echo >&2 "${1}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function readBuildCount() {
|
||||||
|
local reg ver count
|
||||||
|
if [ -r "${BUILD_REGISTER}" ]; then
|
||||||
|
reg=$(<"${BUILD_REGISTER}")
|
||||||
|
else
|
||||||
|
reg="${PROGRAM_VERSION} 0"
|
||||||
|
fi
|
||||||
|
read ver count <<<"${reg}"
|
||||||
|
if [ "${ver}" != "${PROGRAM_VERSION}" ]; then
|
||||||
|
count=0
|
||||||
|
fi
|
||||||
|
echo ${count}
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -r "${GITEA_PASSWORD_FILE}" ]; then
|
||||||
|
if ! PASSWORD=$(<"${GITEA_PASSWORD_FILE}"); then
|
||||||
|
exitMsg "Can're password file '${GITEA_PASSWORD_FILE}'"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
exitMsg "Password file '${GITEA_PASSWORD_FILE}' not found"
|
||||||
|
fi
|
||||||
|
if [ -z "${PASSWORD}" ]; then
|
||||||
|
exitMsg "Empty password. Please, check file '${GITEA_PASSWORD_FILE}'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -r "${RESOURCE_FILE}" ]; then
|
||||||
|
source "${RESOURCE_FILE}"
|
||||||
|
else
|
||||||
|
exitMsg "resource file '${RESOURCE_FILE}' not found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -r "${BUILD_REGISTER}" ]; then
|
||||||
|
BUILD_TAG=$(<"${BUILD_REGISTER}")
|
||||||
|
else
|
||||||
|
exitMsg "build register file '${BUILD_REGISTER}' not found"
|
||||||
|
fi
|
||||||
|
url="${GITEA_HOST}/${GITEA_BASE_PATH}/${GITEA_OWNER}/${GITEA_PKG_TYPE}/${PROGRAM_NAME}/${PROGRAM_VERSION}/files"
|
||||||
|
#echo "URL: ${url}"
|
||||||
|
#echo $(curl --user "${GITEA_USER}:${PASSWORD}" -X GET ${url}|jq '.[]."name"')
|
||||||
|
|
||||||
|
declare -a files=(
|
||||||
|
$(curl --no-progress-meter --user "${GITEA_USER}:${PASSWORD}" -X GET ${url}|jq '.[]."name"')
|
||||||
|
)
|
||||||
|
|
||||||
|
for name in ${files[@]}; do
|
||||||
|
filename=${name:1:${#name}-2}
|
||||||
|
name_terminal=${filename##*_}
|
||||||
|
filever=${name_terminal%%.*}
|
||||||
|
|
||||||
|
if [ "${BUILD_TAG}" != "${PROGRAM_VERSION} ${filever}" ]; then
|
||||||
|
msgln "Deleting ${name}"
|
||||||
|
curl --no-progress-meter --user "${GITEA_USER}:${PASSWORD}" -X DELETE ${GITEA_HOST}/api/packages/${GITEA_OWNER}/${GITEA_PKG_TYPE}/${PROGRAM_NAME}/${PROGRAM_VERSION}/${filename}
|
||||||
|
# else
|
||||||
|
# echo "most recent version"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
#curl --user "${GITEA_USER}:${PASSWORD}" -X GET https://git.portale-stac.it/api/v1/packages/go-pkg/generic/ecli/1.7.0/files
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// commands.go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type commandFunction func(opt *Options, ctx kern.ExprContext, args []string) (err error)
|
||||||
|
|
||||||
|
type command struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
code commandFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cmd *command) exec(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||||
|
return cmd.code(opt, ctx, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
type commandHandler struct {
|
||||||
|
cmdIndex []string
|
||||||
|
commands map[string]*command
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCommandHandler() *commandHandler {
|
||||||
|
return &commandHandler{
|
||||||
|
cmdIndex: make([]string, 0, 20),
|
||||||
|
commands: make(map[string]*command),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (h *commandHandler) setContext(ctx expr.ExprContext) {
|
||||||
|
// h.ctx = ctx
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (h *commandHandler) add(name, description string, f commandFunction) {
|
||||||
|
h.cmdIndex = append(h.cmdIndex, name)
|
||||||
|
h.commands[name] = &command{name: name, description: description, code: f}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *commandHandler) get(cmdLine string) (cmd *command, args []string) {
|
||||||
|
if len(cmdLine) > 0 {
|
||||||
|
tokens := strings.Split(cmdLine, " ")
|
||||||
|
name := tokens[0]
|
||||||
|
args = make([]string, 0, len(tokens)-1)
|
||||||
|
if cmd = h.commands[name]; cmd != nil && len(tokens) > 1 {
|
||||||
|
for _, tk := range tokens[1:] {
|
||||||
|
if tk != "" {
|
||||||
|
args = append(args, tk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------
|
||||||
|
var cmdHandler *commandHandler
|
||||||
|
|
||||||
|
func (h *commandHandler) help() {
|
||||||
|
fmt.Fprintln(os.Stderr, `--- REPL commands:`)
|
||||||
|
for _, name := range h.cmdIndex {
|
||||||
|
cmd := h.commands[name]
|
||||||
|
fmt.Fprintf(os.Stderr, "%12s -- %s\n", cmd.name, cmd.description)
|
||||||
|
}
|
||||||
|
fmt.Fprint(os.Stderr, `
|
||||||
|
--- Command line options:
|
||||||
|
-b <builtin> Import builtin modules.
|
||||||
|
<builtin> can be a list of module names or a glob-pattern.
|
||||||
|
Use the special value 'all' or the pattern '*' to import all modules.
|
||||||
|
-B, --list-builtins List all builtin module names
|
||||||
|
-e <expression> Evaluate <expression> instead of standard-input
|
||||||
|
-i Force REPL operation when all -e occurences have been processed
|
||||||
|
-h, --help, help Show this help menu
|
||||||
|
-m, --modules List all builtin modules
|
||||||
|
--noout Disable printing of expression results
|
||||||
|
-p Print prefix form
|
||||||
|
-t Print tree form
|
||||||
|
-v, --version Show program version
|
||||||
|
`)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------
|
||||||
|
|
||||||
|
func cmdExit(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdHelp(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||||
|
cmdHandler.help()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdMultiLine(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||||
|
if opt.formOpt&kern.MultiLine == 0 {
|
||||||
|
opt.formOpt |= kern.MultiLine
|
||||||
|
} else {
|
||||||
|
opt.formOpt &= ^kern.MultiLine
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdTty(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||||
|
if opt.formOpt&kern.TTY == 0 {
|
||||||
|
opt.formOpt |= kern.TTY
|
||||||
|
} else {
|
||||||
|
opt.formOpt &= ^kern.TTY
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func execFile(opt *Options, ctx kern.ExprContext, fileName string) (err error) {
|
||||||
|
var fh *os.File
|
||||||
|
if fh, err = os.Open(fileName); err == nil {
|
||||||
|
goBatch(opt, ctx, fh, false)
|
||||||
|
fh.Close()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdSource(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||||
|
var target string
|
||||||
|
for _, arg := range args {
|
||||||
|
if len(arg) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// TODO migliorare questa parte: eventualmente valutare un'espressione
|
||||||
|
if target, err = checkStringLiteral(arg); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if target, err = utils.ExpandPath(target); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if isPattern(target) {
|
||||||
|
var fileNames []string
|
||||||
|
if fileNames, err = matchPathPattern(target); err == nil {
|
||||||
|
for _, fileName := range fileNames {
|
||||||
|
if err = execFile(opt, ctx, fileName); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = execFile(opt, ctx, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdModules(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||||
|
expr.IterateBuiltinModules(func(name, description string, imported bool) bool {
|
||||||
|
var check rune = ' '
|
||||||
|
if imported {
|
||||||
|
check = '*'
|
||||||
|
}
|
||||||
|
fmt.Printf("%c %20q: %s\n", check, name, description)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdBase(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||||
|
if len(args) == 0 {
|
||||||
|
fmt.Println(opt.base)
|
||||||
|
} else if args[0] == "2" {
|
||||||
|
opt.baseVerb = "0b%b"
|
||||||
|
opt.base = 2
|
||||||
|
} else if args[0] == "8" {
|
||||||
|
opt.baseVerb = "0o%o"
|
||||||
|
opt.base = 8
|
||||||
|
} else if args[0] == "10" {
|
||||||
|
opt.baseVerb = "%d"
|
||||||
|
opt.base = 10
|
||||||
|
} else if args[0] == "16" {
|
||||||
|
opt.baseVerb = "0x%x"
|
||||||
|
opt.base = 16
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("invalid number base %s", args[0])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cmdOutput(opt *Options, ctx kern.ExprContext, args []string) (err error) {
|
||||||
|
var outputArg string
|
||||||
|
if len(args) == 0 {
|
||||||
|
outputArg = "status"
|
||||||
|
} else {
|
||||||
|
outputArg = strings.ToLower(args[0])
|
||||||
|
}
|
||||||
|
switch outputArg {
|
||||||
|
case "on":
|
||||||
|
opt.output = true
|
||||||
|
case "off":
|
||||||
|
opt.output = false
|
||||||
|
case "status":
|
||||||
|
if opt.output {
|
||||||
|
fmt.Println("on")
|
||||||
|
} else {
|
||||||
|
fmt.Println("off")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("output: unknown option %q", outputArg)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------
|
||||||
|
|
||||||
|
func setupCommands() {
|
||||||
|
cmdHandler = NewCommandHandler()
|
||||||
|
cmdHandler.add("base", "Set the integer output base: 2, 8, 10, or 16", cmdBase)
|
||||||
|
cmdHandler.add("exit", "Exit the program", cmdExit)
|
||||||
|
cmdHandler.add("help", "Show command list", cmdHelp)
|
||||||
|
cmdHandler.add("ml", "Enable/Disable multi-line output", cmdMultiLine)
|
||||||
|
cmdHandler.add("mods", "List builtin modules", cmdModules)
|
||||||
|
cmdHandler.add("output", "Enable/Disable printing expression results. Options 'on', 'off', 'status'", cmdOutput)
|
||||||
|
cmdHandler.add("source", "Load a file as input", cmdSource)
|
||||||
|
cmdHandler.add("tty", "Enable/Disable ansi output", cmdTty)
|
||||||
|
}
|
||||||
@@ -0,0 +1,339 @@
|
|||||||
|
= Ecli
|
||||||
|
Expression Calculator Interactive Tool
|
||||||
|
:authors: Celestino Amoroso
|
||||||
|
:email: celestino.amoroso@gmail.com
|
||||||
|
:docinfo: shared
|
||||||
|
:encoding: utf-8
|
||||||
|
:toc: right
|
||||||
|
:toclevels: 4
|
||||||
|
:icons: font
|
||||||
|
:icon-set: fi
|
||||||
|
:numbered:
|
||||||
|
:data-uri:
|
||||||
|
:docinfo1:
|
||||||
|
:sectlinks:
|
||||||
|
:sectanchors:
|
||||||
|
:source-highlighter: rouge
|
||||||
|
:rouge-style: manni
|
||||||
|
:stylesdir: /home/share/s3-howto/styles
|
||||||
|
:stylesheet: adoc-colony.css
|
||||||
|
|
||||||
|
// Workaround to manage double-column in back-tick quotes
|
||||||
|
:2c: ::
|
||||||
|
// Workaround to manage double-plus in back-tick quotes
|
||||||
|
:plusplus: ++
|
||||||
|
// Workaround to manage asterisk in back-tick quotes
|
||||||
|
:star: *
|
||||||
|
|
||||||
|
#Generated by Copilot#
|
||||||
|
|
||||||
|
== Overview
|
||||||
|
|
||||||
|
`ecli` (Expression Calculator Interactive Tool) is an interactive REPL (Read-Eval-Print Loop) application for evaluating expressions using the Expr package. It provides a powerful command-line interface for expression evaluation with support for multiple builtin modules, file operations, and interactive scripting.
|
||||||
|
|
||||||
|
The tool combines the expression evaluation capabilities of the Expr package with an interactive shell environment, making it ideal for:
|
||||||
|
|
||||||
|
- Interactive expression testing and prototyping
|
||||||
|
- Command history survival across sessions
|
||||||
|
- Batch expression evaluation from scripts
|
||||||
|
- Data processing and transformation
|
||||||
|
- Mathematical computations with fractions and complex operators
|
||||||
|
|
||||||
|
== Getting Started
|
||||||
|
|
||||||
|
=== Installation
|
||||||
|
|
||||||
|
To build and install `ecli`:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
cd cmd/ecli
|
||||||
|
./build.bash
|
||||||
|
----
|
||||||
|
|
||||||
|
The compiled binary will be available as `ecli` in the current directory.
|
||||||
|
|
||||||
|
=== Basic Usage
|
||||||
|
|
||||||
|
Start the interactive REPL:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
./ecli
|
||||||
|
----
|
||||||
|
|
||||||
|
You'll see the prompt `>>>` where you can enter expressions to evaluate.
|
||||||
|
|
||||||
|
=== Command Line Options
|
||||||
|
|
||||||
|
[cols="1,4", options="header"]
|
||||||
|
|===
|
||||||
|
| Option | Description
|
||||||
|
|
||||||
|
| `-e <expression>` | Evaluate an expression directly without entering REPL mode
|
||||||
|
| `-i` | Force REPL operation after processing all `-e` options
|
||||||
|
| `-b <builtin>` | Import builtin modules (comma-separated list, glob patterns, or 'all')
|
||||||
|
| `-B, --list-builtins` | List all available builtin module names
|
||||||
|
| `-m, --modules` | List all builtin modules
|
||||||
|
| `-p` | Print expressions in prefix form
|
||||||
|
| `-t` | Print expressions in tree form
|
||||||
|
| `--noout` | Disable printing of expression results
|
||||||
|
| `-h, --help` | Show help message
|
||||||
|
| `-v, --version` | Show program version
|
||||||
|
|===
|
||||||
|
|
||||||
|
== Interactive Commands
|
||||||
|
|
||||||
|
Within the REPL, you can use the following commands:
|
||||||
|
|
||||||
|
[cols="2,5", options="header"]
|
||||||
|
|===
|
||||||
|
| Command | Description
|
||||||
|
|
||||||
|
| `base [2\|8\|10\|16]` | Set the integer output base. Use without arguments to show current base
|
||||||
|
| `exit` | Exit the REPL
|
||||||
|
| `help` | Display available commands and command-line options
|
||||||
|
| `ml` | Enable/Disable multi-line input mode for complex expressions
|
||||||
|
| `mods` | List all builtin modules (with `*` marking imported ones)
|
||||||
|
| `output [on\|off\|status]` | Enable/Disable printing of expression results
|
||||||
|
| `source <file>` | Load and execute expressions from a file
|
||||||
|
| `tty` | Enable/Disable ANSI terminal output
|
||||||
|
|===
|
||||||
|
|
||||||
|
== Features
|
||||||
|
|
||||||
|
=== Expression Evaluation
|
||||||
|
|
||||||
|
`ecli` supports the full expression language provided by the Expr package, including:
|
||||||
|
|
||||||
|
- **Arithmetic Operations**: Addition, subtraction, multiplication, division, modulo
|
||||||
|
- **Bitwise Operations**: AND, OR, XOR, NOT, shift operations
|
||||||
|
- **Boolean Logic**: AND, OR, NOT operations
|
||||||
|
- **Relational Operators**: Comparison and equality operators
|
||||||
|
- **String Operations**: Concatenation and string manipulation
|
||||||
|
- **Iterators**: Range, list, and custom iterators
|
||||||
|
- **Functions**: Builtin and user-defined functions
|
||||||
|
- **Collections**: Lists, dictionaries, and linked lists
|
||||||
|
- **Fractions**: Support for fractional arithmetic
|
||||||
|
|
||||||
|
=== Builtin Modules
|
||||||
|
|
||||||
|
`ecli` provides access to various builtin modules through the `-b` option:
|
||||||
|
|
||||||
|
[cols="1,4", options="header"]
|
||||||
|
|===
|
||||||
|
| Module | Functionality
|
||||||
|
|
||||||
|
| `base` | Core expression evaluation functions
|
||||||
|
| `fmt` | String formatting and output functions
|
||||||
|
| `string` | String manipulation functions
|
||||||
|
| `math-arith` | Mathematical and arithmetic operations
|
||||||
|
| `iterator` | Iterator-related functions
|
||||||
|
| `os-file` | File I/O operations
|
||||||
|
| `import` | Module import functionality
|
||||||
|
|===
|
||||||
|
|
||||||
|
Use `-B` or `--list-builtins` to see all available modules:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
./ecli --list-builtins
|
||||||
|
----
|
||||||
|
|
||||||
|
== Examples
|
||||||
|
|
||||||
|
=== Basic Arithmetic
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
>>> 2 + 3 * 4
|
||||||
|
14
|
||||||
|
>>> (2 + 3) * 4
|
||||||
|
20
|
||||||
|
----
|
||||||
|
|
||||||
|
=== String Operations
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
>>> "hello" + " " + "world"
|
||||||
|
hello world
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Using Iterators
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
>>> it = [1, 2, 3, 4, 5] map $_ * 2
|
||||||
|
$($([#5]))
|
||||||
|
>>> $$(it)
|
||||||
|
[<
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
6,
|
||||||
|
8,
|
||||||
|
10
|
||||||
|
>]
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Evaluating from Command Line
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
./ecli -e "2 + 2"
|
||||||
|
4
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Loading Builtin Modules
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
./ecli -b "math-arith,string"
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Loading Expressions from Files
|
||||||
|
|
||||||
|
Inside the REPL:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
>>> source "expressions.expr"
|
||||||
|
----
|
||||||
|
|
||||||
|
Or from command line:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
./ecli -e "@expressions.expr"
|
||||||
|
----
|
||||||
|
|
||||||
|
== Configuration
|
||||||
|
|
||||||
|
=== Resource Files
|
||||||
|
|
||||||
|
`ecli` supports startup resource files:
|
||||||
|
|
||||||
|
- `.ecli.rc` - Main configuration file
|
||||||
|
- `.ecli.rc.d/` - Directory for modular configuration files
|
||||||
|
|
||||||
|
These files are automatically loaded at startup if they exist in the current directory or home directory.
|
||||||
|
|
||||||
|
== Building from Source
|
||||||
|
|
||||||
|
=== Prerequisites
|
||||||
|
|
||||||
|
- Go 1.18 or later
|
||||||
|
- Make or bash shell
|
||||||
|
|
||||||
|
=== Build Steps
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
cd cmd/ecli
|
||||||
|
./build.bash
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Build Artifacts
|
||||||
|
|
||||||
|
The build process generates:
|
||||||
|
|
||||||
|
- `ecli` - The main executable
|
||||||
|
- `version.txt` - Version information
|
||||||
|
- Platform-specific binaries (e.g., `ecli_v1.17.0_linux_amd64`, `ecli_v1.17.0_darwin_arm64`)
|
||||||
|
|
||||||
|
== Advanced Usage
|
||||||
|
|
||||||
|
=== Multi-line Input
|
||||||
|
|
||||||
|
For complex expressions, split operations across multiple lines terminating with a backslash (`\`):
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
>>> ml
|
||||||
|
>>> result = [1, 2, 3, 4, 5] \
|
||||||
|
... filter $_ > 2 \
|
||||||
|
... map $_ * 2
|
||||||
|
>>> $$(result)
|
||||||
|
[<6, 8, 10>
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Script Execution
|
||||||
|
|
||||||
|
Create a file `calculations.expr`:
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
x = 10
|
||||||
|
y = 20
|
||||||
|
result = x + y * 2
|
||||||
|
----
|
||||||
|
|
||||||
|
Execute it:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
./ecli -e '@calculations.expr' -e 'result'
|
||||||
|
50
|
||||||
|
----
|
||||||
|
|
||||||
|
=== Other Iterator Examples
|
||||||
|
|
||||||
|
[source]
|
||||||
|
----
|
||||||
|
>>> data = [{"name": "alice", "age": 30}, {"name": "bob", "age": 25}]
|
||||||
|
|
||||||
|
// Extract names using map iterator
|
||||||
|
>>> $$(data map ($_).name)
|
||||||
|
[<
|
||||||
|
"alice",
|
||||||
|
"bob"
|
||||||
|
>]
|
||||||
|
|
||||||
|
// Filter ages greater than 28
|
||||||
|
>>> $$(data filter ($_).age > 28 map ($_).name)
|
||||||
|
[<
|
||||||
|
"alice"
|
||||||
|
>]
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
== Troubleshooting
|
||||||
|
|
||||||
|
=== Expression Parsing Errors
|
||||||
|
|
||||||
|
If you encounter parsing errors, check:
|
||||||
|
|
||||||
|
- Bracket matching and quotation marks
|
||||||
|
- Operator precedence
|
||||||
|
- Variable and function names
|
||||||
|
|
||||||
|
=== Module Loading Issues
|
||||||
|
|
||||||
|
Verify available modules:
|
||||||
|
|
||||||
|
[source,bash]
|
||||||
|
----
|
||||||
|
./ecli --list-builtins
|
||||||
|
----
|
||||||
|
|
||||||
|
=== File Not Found Errors
|
||||||
|
|
||||||
|
Ensure file paths are:
|
||||||
|
|
||||||
|
- Properly quoted in expressions
|
||||||
|
- Relative to the current working directory or absolute paths
|
||||||
|
- Readable by the current user
|
||||||
|
|
||||||
|
== Related Documentation
|
||||||
|
|
||||||
|
- link:../../../README.adoc[Expr Package Documentation]
|
||||||
|
- Expr Expression Language Syntax
|
||||||
|
- Builtin Modules Reference
|
||||||
|
|
||||||
|
== Version History
|
||||||
|
|
||||||
|
For version information and changes, see the link:version.txt[version file].
|
||||||
|
|
||||||
|
== License
|
||||||
|
|
||||||
|
Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com). All rights reserved.
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
module ecli
|
||||||
|
|
||||||
|
go 1.24.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.portale-stac.it/go-pkg/expr v0.33.0
|
||||||
|
git.portale-stac.it/go-pkg/utils v0.3.0
|
||||||
|
github.com/ergochat/readline v0.1.3
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect
|
||||||
|
golang.org/x/sys v0.38.0 // indirect
|
||||||
|
golang.org/x/text v0.31.0 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
git.portale-stac.it/go-pkg/expr v0.1.0 h1:7xGEuUhdh6RRFaRbRnLVqVJBmHJWHfqjDBm2K0fIW2s=
|
||||||
|
git.portale-stac.it/go-pkg/expr v0.1.0/go.mod h1:kUFEQkUMCJ1IiUKkL0P5/vznaAIzFI26Xf5P0rTXqR0=
|
||||||
|
git.portale-stac.it/go-pkg/expr v0.2.0 h1:AAaVsV0uaC4EikKU91VuubIpbIN7wuya7t4avyFgg+0=
|
||||||
|
git.portale-stac.it/go-pkg/expr v0.2.0/go.mod h1:DZqqZ3A9h4qEOs7yMvG4VZq7B/xhFsYqC3IKd3M2VKc=
|
||||||
|
git.portale-stac.it/go-pkg/expr v0.17.0 h1:4ANGwJfwJO3AmnKka4Cf1AO9/ckGLMj8RIWeoDFKawQ=
|
||||||
|
git.portale-stac.it/go-pkg/expr v0.17.0/go.mod h1:DZqqZ3A9h4qEOs7yMvG4VZq7B/xhFsYqC3IKd3M2VKc=
|
||||||
|
git.portale-stac.it/go-pkg/expr v0.32.0 h1:ikXqHjJslIGkD79G1/51xe+c25TFi2CslJ6nu8mOuJY=
|
||||||
|
git.portale-stac.it/go-pkg/expr v0.32.0/go.mod h1:R2TYIahLtD8YDgNEHtgHCQdoEUZ7yCQsMHyJXhJijmw=
|
||||||
|
git.portale-stac.it/go-pkg/expr v0.33.0 h1:GJ7PPgA1689GSC/cUWGYm08jn7qMmkp0FMQf/As5sCw=
|
||||||
|
git.portale-stac.it/go-pkg/expr v0.33.0/go.mod h1:R2TYIahLtD8YDgNEHtgHCQdoEUZ7yCQsMHyJXhJijmw=
|
||||||
|
git.portale-stac.it/go-pkg/utils v0.2.0 h1:2l4IVUhElzjaIUJlahPG2DZTGb9x7OXuFTO4z1K6LmY=
|
||||||
|
git.portale-stac.it/go-pkg/utils v0.2.0/go.mod h1:PebQ45Qbe89aMTd3wcbcx1bkpNRW4/frNLnpuyZYovU=
|
||||||
|
git.portale-stac.it/go-pkg/utils v0.3.0 h1:kCJ3+XcekV7in/SieJjiswdtJKMBS0RTJMlG2fW5mK0=
|
||||||
|
git.portale-stac.it/go-pkg/utils v0.3.0/go.mod h1:PebQ45Qbe89aMTd3wcbcx1bkpNRW4/frNLnpuyZYovU=
|
||||||
|
github.com/ergochat/readline v0.1.0 h1:KEIiAnyH9qGZB4K8oq5mgDcExlEKwmZDcyyocgJiABc=
|
||||||
|
github.com/ergochat/readline v0.1.0/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY=
|
||||||
|
github.com/ergochat/readline v0.1.1 h1:C8Uuo3ybB23GWOt0uxmHbGzKM9owmtXary6Clrj84s0=
|
||||||
|
github.com/ergochat/readline v0.1.1/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY=
|
||||||
|
github.com/ergochat/readline v0.1.3 h1:/DytGTmwdUJcLAe3k3VJgowh5vNnsdifYT6uVaf4pSo=
|
||||||
|
github.com/ergochat/readline v0.1.3/go.mod h1:o3ux9QLHLm77bq7hDB21UTm6HlV2++IPDMfIfKDuOgY=
|
||||||
|
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 h1:zfMcR1Cs4KNuomFFgGefv5N0czO2XZpUbxGUy8i8ug0=
|
||||||
|
golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0=
|
||||||
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||||
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||||
|
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||||
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
|
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||||
|
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
|
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||||
|
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
//go:build graph
|
||||||
|
|
||||||
|
// NOTE: Enable this build tag to print the graph of the expression instead of evaluating it.
|
||||||
|
// go run -tags graph cmd/ecli/main.go
|
||||||
|
|
||||||
|
// graph.go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printGraph() {
|
||||||
|
r := expr.NewExprReticle(ast)
|
||||||
|
fmt.Println(r.String())
|
||||||
|
}
|
||||||
@@ -0,0 +1,418 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// main.go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/str"
|
||||||
|
"git.portale-stac.it/go-pkg/utils"
|
||||||
|
|
||||||
|
// https://pkg.go.dev/github.com/ergochat/readline#section-readme
|
||||||
|
"github.com/ergochat/readline"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
intro = PROGNAME + ` -- Expressions calculator ` + VERSION + `
|
||||||
|
Based on the Expr package ` + EXPR_VERSION + ` (` + EXPR_DATE + `)
|
||||||
|
Type help to get the list of available commands
|
||||||
|
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
|
||||||
|
`
|
||||||
|
mainPrompt = ">>> "
|
||||||
|
contPrompt = "... "
|
||||||
|
|
||||||
|
historyFile = "~/.expr_history"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ------
|
||||||
|
|
||||||
|
func errOptValueRequired(opt string) error {
|
||||||
|
return fmt.Errorf("option %q requires a value", opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func about() string {
|
||||||
|
return PROGNAME + " -- " + VERSION + "; Expr package " + EXPR_VERSION
|
||||||
|
}
|
||||||
|
|
||||||
|
func importBuiltins(ctx kern.ExprContext, opt *Options) (err error) {
|
||||||
|
for _, spec := range opt.builtin {
|
||||||
|
if moduleSpec, ok := spec.(string); ok {
|
||||||
|
if moduleSpec == "all" {
|
||||||
|
moduleSpec = "*"
|
||||||
|
}
|
||||||
|
_, err = expr.ImportInContextByGlobPattern(ctx, moduleSpec)
|
||||||
|
} else if moduleSpec, ok := spec.([]string); ok {
|
||||||
|
notFoundList := make([]string, 0)
|
||||||
|
for _, name := range moduleSpec {
|
||||||
|
if !expr.ImportInContext(ctx, name) {
|
||||||
|
notFoundList = append(notFoundList, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(notFoundList) > 0 {
|
||||||
|
err = fmt.Errorf("not found modules: %s", strings.Join(notFoundList, ","))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func initReadlineConfig(cfg *readline.Config) {
|
||||||
|
if histfile, err := utils.ExpandPath(historyFile); err == nil {
|
||||||
|
cfg.HistoryFile = histfile
|
||||||
|
}
|
||||||
|
cfg.Undo = true
|
||||||
|
cfg.DisableAutoSaveHistory = true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func goInteractiveReadline(opt *Options, ctx kern.ExprContext, r io.Reader) {
|
||||||
|
var sb strings.Builder
|
||||||
|
var cfg readline.Config
|
||||||
|
initReadlineConfig(&cfg)
|
||||||
|
rl, err := readline.NewFromConfig(&cfg)
|
||||||
|
if err != nil {
|
||||||
|
goInteractive(opt, ctx, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rl.Close()
|
||||||
|
|
||||||
|
fmt.Print(intro)
|
||||||
|
rl.SetPrompt(mainPrompt)
|
||||||
|
for line, err := rl.ReadLine(); err == nil; line, err = rl.ReadLine() {
|
||||||
|
if continuation(&sb, line) {
|
||||||
|
rl.SetPrompt(contPrompt)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.SetPrompt(mainPrompt)
|
||||||
|
sb.WriteString(line)
|
||||||
|
source := strings.TrimSpace(sb.String())
|
||||||
|
if source != "" && !strings.HasPrefix(source, "//") {
|
||||||
|
if cmd, args := cmdHandler.get(source); cmd != nil {
|
||||||
|
rl.SaveToHistory(source)
|
||||||
|
if err = cmd.exec(opt, ctx, args); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(os.Stderr, "Eval Error:", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rl.SaveToHistory(source)
|
||||||
|
r := strings.NewReader(source)
|
||||||
|
compute(opt, ctx, r, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.Reset()
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
func goInteractive(opt *Options, ctx kern.ExprContext, r io.Reader) {
|
||||||
|
var sb strings.Builder
|
||||||
|
fmt.Print(intro)
|
||||||
|
fmt.Print(mainPrompt)
|
||||||
|
reader := bufio.NewReaderSize(r, 1024)
|
||||||
|
for line, err := reader.ReadString('\n'); err == nil && line != "exit\n"; line, err = reader.ReadString('\n') {
|
||||||
|
if continuation(&sb, line) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.WriteString(line)
|
||||||
|
source := strings.TrimSpace(sb.String())
|
||||||
|
// fmt.Printf("source=%q\n", source)
|
||||||
|
if source != "" && !strings.HasPrefix(source, "//") {
|
||||||
|
if cmd, args := cmdHandler.get(source); cmd != nil {
|
||||||
|
if err = cmd.exec(opt, ctx, args); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r := strings.NewReader(source)
|
||||||
|
compute(opt, ctx, r, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.Reset()
|
||||||
|
fmt.Print(mainPrompt)
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
|
||||||
|
func continuation(sb *strings.Builder, line string) (cont bool) {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if strings.HasSuffix(line, "\\") {
|
||||||
|
sb.WriteString(line[0 : len(line)-1])
|
||||||
|
cont = true
|
||||||
|
} else if strings.HasSuffix(line, ";") {
|
||||||
|
sb.WriteString(line)
|
||||||
|
cont = true
|
||||||
|
} else if len(line) > 0 {
|
||||||
|
if scan.StringEndsWithOperator(line) {
|
||||||
|
sb.WriteString(line)
|
||||||
|
cont = true
|
||||||
|
} else {
|
||||||
|
fullInput := sb.String() + line
|
||||||
|
if strings.Count(fullInput, "(") > strings.Count(fullInput, ")") ||
|
||||||
|
strings.Count(fullInput, "[") > strings.Count(fullInput, "]") ||
|
||||||
|
strings.Count(fullInput, "{") > strings.Count(fullInput, "}") {
|
||||||
|
sb.WriteString(line)
|
||||||
|
cont = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func goBatch(opt *Options, ctx kern.ExprContext, r io.Reader, outputEnabled bool) {
|
||||||
|
var sb strings.Builder
|
||||||
|
reader := bufio.NewReaderSize(r, 1024)
|
||||||
|
for line, err := reader.ReadString('\n'); err == nil && line != "exit\n"; line, err = reader.ReadString('\n') {
|
||||||
|
if continuation(&sb, line) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sb.WriteString(line)
|
||||||
|
source := strings.TrimSpace(sb.String())
|
||||||
|
// fmt.Printf("source=%q\n", source)
|
||||||
|
if source != "" && !strings.HasPrefix(source, "//") {
|
||||||
|
if cmd, args := cmdHandler.get(source); cmd != nil {
|
||||||
|
if err = cmd.exec(opt, ctx, args); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "Eval Error:", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r := strings.NewReader(source)
|
||||||
|
compute(opt, ctx, r, outputEnabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func compute(opt *Options, ctx kern.ExprContext, r io.Reader, outputEnabled bool) {
|
||||||
|
scanner := scan.NewScanner(r, scan.DefaultTranslations())
|
||||||
|
parser := expr.NewParser()
|
||||||
|
|
||||||
|
if ast, err := parser.Parse(scanner); err == nil {
|
||||||
|
if opt.printPrefix {
|
||||||
|
fmt.Println(ast)
|
||||||
|
}
|
||||||
|
if opt.printTree {
|
||||||
|
printGraph()
|
||||||
|
}
|
||||||
|
|
||||||
|
if result, err := ast.Eval(ctx); err == nil {
|
||||||
|
if outputEnabled && opt.output {
|
||||||
|
printResult(opt, result)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(os.Stderr, "Eval Error:", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(os.Stderr, "Parse Error:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printResult(opt *Options, result any) {
|
||||||
|
if f, ok := result.(kern.Formatter); ok {
|
||||||
|
fmt.Println(f.ToString(opt.formOpt))
|
||||||
|
} else if types.IsInteger(result) {
|
||||||
|
fmt.Printf(opt.baseVerb, result)
|
||||||
|
fmt.Println()
|
||||||
|
} else if str.IsString(result) {
|
||||||
|
fmt.Printf("\"%s\"\n", result)
|
||||||
|
} else {
|
||||||
|
fmt.Println(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isReaderTerminal(r io.Reader) bool {
|
||||||
|
if fh, ok := r.(*os.File); ok {
|
||||||
|
return utils.StreamIsTerminal(fh)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func registerLocalFunctions(ctx kern.ExprContext) {
|
||||||
|
const (
|
||||||
|
devParamProp = "prop"
|
||||||
|
devParamDigits = "digits"
|
||||||
|
)
|
||||||
|
|
||||||
|
aboutFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
result = about()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrlListFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
vars := ctx.EnumVars(func(name string) bool {
|
||||||
|
return len(name) > 0 && name[0] == '_'
|
||||||
|
})
|
||||||
|
result = array.ArrayFromStrings(vars)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctrlFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
varName, _ := args[devParamProp].(string)
|
||||||
|
if len(args) == 1 {
|
||||||
|
result = expr.GlobalCtrlGet(ctx, varName)
|
||||||
|
} else {
|
||||||
|
result = expr.GlobalCtrlSet(ctx, varName, args[kern.ParamValue])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
envSetFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
var varName, value string
|
||||||
|
var ok bool
|
||||||
|
if varName, ok = args[kern.ParamName].(string); !ok {
|
||||||
|
err = kern.ErrExpectedGot(name, kern.TypeString, args[kern.ParamName])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if value, ok = args[kern.ParamValue].(string); !ok {
|
||||||
|
err = kern.ErrExpectedGot(name, kern.TypeString, args[kern.ParamValue])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = os.Setenv(varName, value); err == nil {
|
||||||
|
result = value
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
envGetFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
var varName string
|
||||||
|
var ok bool
|
||||||
|
if varName, ok = args[kern.ParamName].(string); !ok {
|
||||||
|
err = kern.ErrExpectedGot(name, kern.TypeString, args[kern.ParamName])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if result, ok = os.LookupEnv(varName); !ok {
|
||||||
|
err = fmt.Errorf("environment variable %q does not exist", varName)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
envListFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
env := os.Environ()
|
||||||
|
vars := make([]string, 0, len(env))
|
||||||
|
for _, e := range env {
|
||||||
|
name, _, _ := strings.Cut(e, "=")
|
||||||
|
vars = append(vars, name)
|
||||||
|
}
|
||||||
|
result = array.ArrayFromStrings(vars)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
binFunc := func(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
|
||||||
|
var value, digits int64
|
||||||
|
var ok bool
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
if value, ok = args[kern.ParamValue].(int64); !ok {
|
||||||
|
err = kern.ErrExpectedGot(name, kern.TypeInt, args[kern.ParamValue])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if digits, ok = args[devParamDigits].(int64); !ok {
|
||||||
|
err = kern.ErrExpectedGot(name, kern.TypeInt, args[devParamDigits])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if digits != 64 && digits != 32 && digits != 16 && digits != 8 {
|
||||||
|
err = fmt.Errorf("%s param allows 8, 16, 32, or 64 values only", devParamDigits)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mask := uint64(0)
|
||||||
|
for i := 0; i < int(digits); i++ {
|
||||||
|
mask |= (1 << i)
|
||||||
|
}
|
||||||
|
maskedValue := uint64(value) & mask
|
||||||
|
// if maskedValue != uint64(value) {
|
||||||
|
// err = fmt.Errorf("%s param (%d) is not compatible with the value (%d) of %s param", expr.ParamValue, value, digits, devParamDigits)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
for i := int(digits) - 1; i >= 0; i-- {
|
||||||
|
if maskedValue&(1<<i) == 0 {
|
||||||
|
sb.WriteByte('0')
|
||||||
|
} else {
|
||||||
|
sb.WriteByte('1')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = sb.String()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.RegisterFunc("about", kern.NewGolangFunctor(aboutFunc), kern.TypeString, []kern.ExprFuncParam{})
|
||||||
|
ctx.RegisterFunc("ctrlList", kern.NewGolangFunctor(ctrlListFunc), kern.TypeListOfStrings, []kern.ExprFuncParam{})
|
||||||
|
ctx.RegisterFunc("ctrl", kern.NewGolangFunctor(ctrlFunc), kern.TypeAny, []kern.ExprFuncParam{
|
||||||
|
kern.NewFuncParam(devParamProp),
|
||||||
|
kern.NewFuncParamFlag(kern.ParamValue, kern.PfOptional),
|
||||||
|
})
|
||||||
|
ctx.RegisterFunc("envSet", kern.NewGolangFunctor(envSetFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||||
|
kern.NewFuncParam(kern.ParamName),
|
||||||
|
kern.NewFuncParam(kern.ParamValue),
|
||||||
|
})
|
||||||
|
ctx.RegisterFunc("envGet", kern.NewGolangFunctor(envGetFunc), kern.TypeString, []kern.ExprFuncParam{
|
||||||
|
kern.NewFuncParam(kern.ParamName),
|
||||||
|
})
|
||||||
|
ctx.RegisterFunc("envList", kern.NewGolangFunctor(envListFunc), kern.TypeListOfStrings, []kern.ExprFuncParam{})
|
||||||
|
|
||||||
|
ctx.RegisterFunc("bin", kern.NewGolangFunctor(binFunc), kern.TypeInt, []kern.ExprFuncParam{
|
||||||
|
kern.NewFuncParam(kern.ParamValue),
|
||||||
|
kern.NewFuncParamFlagDef(devParamDigits, kern.PfOptional|kern.PfDefault, int64(8)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
setupCommands()
|
||||||
|
opt := NewOptions()
|
||||||
|
opt.loadRc()
|
||||||
|
if err := opt.parseArgs(); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := expr.NewSimpleStore()
|
||||||
|
|
||||||
|
if err := importBuiltins(ctx, opt); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
registerLocalFunctions(ctx)
|
||||||
|
|
||||||
|
if len(opt.expressions) == opt.rcCount || opt.forceInteractive {
|
||||||
|
opt.expressions = append(opt.expressions, os.Stdin)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, input := range opt.expressions {
|
||||||
|
if isReaderTerminal(input) {
|
||||||
|
goInteractiveReadline(opt, ctx, input)
|
||||||
|
} else {
|
||||||
|
_, enableOutput := input.(*strings.Reader)
|
||||||
|
goBatch(opt, ctx, input, enableOutput)
|
||||||
|
if f, ok := input.(*os.File); ok {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: why did I added these lines?
|
||||||
|
// if opt.output {
|
||||||
|
// printResult(opt, ctx.GetLast())
|
||||||
|
// }
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// match.go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func matchFilePattern(dirName string, pattern string, join bool) (fileList []string, err error) {
|
||||||
|
var entries []os.DirEntry
|
||||||
|
if entries, err = os.ReadDir(dirName); err == nil {
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.Type().IsRegular() {
|
||||||
|
var match bool
|
||||||
|
if match, err =filepath.Match(pattern, entry.Name()); err != nil {
|
||||||
|
fileList = nil
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if match {
|
||||||
|
if fileList == nil {
|
||||||
|
fileList = make([]string, 0, 1)
|
||||||
|
}
|
||||||
|
if join {
|
||||||
|
fileList = append(fileList, path.Join(dirName, entry.Name()))
|
||||||
|
} else {
|
||||||
|
fileList = append(fileList, entry.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchPathPattern(pathPattern string) (fileList []string, err error) {
|
||||||
|
dirName := path.Dir(pathPattern)
|
||||||
|
pattern := path.Base(pathPattern)
|
||||||
|
return matchFilePattern(dirName, pattern, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func isPattern(name string) bool{
|
||||||
|
return strings.ContainsAny(name, "*?[]")
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// main.go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsPattern(t *testing.T) {
|
||||||
|
target := "non-pattern"
|
||||||
|
if isPattern(target) {
|
||||||
|
t.Errorf("%q recognized as a pattern", target)
|
||||||
|
}
|
||||||
|
|
||||||
|
target = "pattern/*.expr"
|
||||||
|
if !isPattern(target) {
|
||||||
|
t.Errorf("%q not recognized as a pattern", target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMatchFilePattern(t *testing.T) {
|
||||||
|
target := "./go.*sum"
|
||||||
|
dirName := path.Dir(target)
|
||||||
|
pattern := path.Base(target)
|
||||||
|
if matchedFiles, err := matchFilePattern(dirName, pattern, true); err == nil {
|
||||||
|
if slices.Compare(matchedFiles, []string{"go.sum", "go.work.sum"}) != 0 {
|
||||||
|
t.Errorf("Matched file list is not correct: %v", matchedFiles)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("Got error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// options.go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
printTree bool
|
||||||
|
printPrefix bool
|
||||||
|
forceInteractive bool
|
||||||
|
builtin []any
|
||||||
|
expressions []io.Reader
|
||||||
|
formOpt kern.FmtOpt
|
||||||
|
baseVerb string
|
||||||
|
base int
|
||||||
|
output bool
|
||||||
|
rcCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOptions() *Options {
|
||||||
|
return &Options{
|
||||||
|
expressions: make([]io.Reader, 0),
|
||||||
|
builtin: make([]any, 0),
|
||||||
|
formOpt: kern.Base10,
|
||||||
|
baseVerb: "%d",
|
||||||
|
base: 10,
|
||||||
|
output: true,
|
||||||
|
rcCount: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opt *Options) loadRc() {
|
||||||
|
var rcPath string
|
||||||
|
var fh *os.File
|
||||||
|
var err error
|
||||||
|
|
||||||
|
rcList := []string{
|
||||||
|
".ecli.rc",
|
||||||
|
"~/.ecli.rc",
|
||||||
|
"~/.config/expr/ecli.rc",
|
||||||
|
"~/.dev-expr.rc", // OBSOLETE, to be removed in future releases
|
||||||
|
}
|
||||||
|
for _, rcFile := range rcList {
|
||||||
|
if rcPath, err = utils.ExpandPath(rcFile); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if fh, err = os.Open(rcPath); err == nil {
|
||||||
|
opt.expressions = append(opt.expressions, fh) // rc should be the first source to be read
|
||||||
|
opt.rcCount++
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listBuiltins() {
|
||||||
|
expr.IterateBuiltinModules(func(name, description string, imported bool) bool {
|
||||||
|
if imported {
|
||||||
|
name = "*" + name
|
||||||
|
}
|
||||||
|
fmt.Printf("%20q: %s\n", name, description)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opt *Options) parseArgs() (err error) {
|
||||||
|
for i := 1; i < len(os.Args) && err == nil; i++ {
|
||||||
|
arg := os.Args[i]
|
||||||
|
switch arg {
|
||||||
|
case "-i":
|
||||||
|
opt.forceInteractive = true
|
||||||
|
case "-t":
|
||||||
|
opt.printTree = true
|
||||||
|
case "-p":
|
||||||
|
opt.printPrefix = true
|
||||||
|
case "-e":
|
||||||
|
if i+1 < len(os.Args) {
|
||||||
|
i++
|
||||||
|
spec := os.Args[i]
|
||||||
|
if strings.HasPrefix(spec, "@") {
|
||||||
|
var f *os.File
|
||||||
|
if f, err = os.Open(spec[1:]); err == nil {
|
||||||
|
opt.expressions = append(opt.expressions, f)
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(spec) > 0 && spec[len(spec)-1] != '\n' {
|
||||||
|
spec += "\n"
|
||||||
|
}
|
||||||
|
opt.expressions = append(opt.expressions, strings.NewReader(spec))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = errOptValueRequired(arg)
|
||||||
|
}
|
||||||
|
case "-b":
|
||||||
|
if i+1 < len(os.Args) {
|
||||||
|
i++
|
||||||
|
specs := strings.Split(os.Args[i], ",")
|
||||||
|
if len(specs) == 1 {
|
||||||
|
opt.builtin = append(opt.builtin, specs[0])
|
||||||
|
} else {
|
||||||
|
opt.builtin = append(opt.builtin, specs)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = errOptValueRequired(arg)
|
||||||
|
}
|
||||||
|
case "-B", "--list-builtins":
|
||||||
|
listBuiltins()
|
||||||
|
os.Exit(0)
|
||||||
|
case "-m", "--modules":
|
||||||
|
expr.IterateBuiltinModules(func(name, description string, _ bool) bool {
|
||||||
|
fmt.Printf("%20q: %s\n", name, description)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
os.Exit(0)
|
||||||
|
case "--noout":
|
||||||
|
opt.output = false
|
||||||
|
case "-h", "--help", "help":
|
||||||
|
cmdHandler.help()
|
||||||
|
os.Exit(0)
|
||||||
|
case "-v", "--version", "version", "about":
|
||||||
|
fmt.Println(about())
|
||||||
|
os.Exit(0)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("invalid option nr %d %q", i+1, arg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
Executable
+79
@@ -0,0 +1,79 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
# All rights reserved.
|
||||||
|
|
||||||
|
RESOURCE_FILE=".build.rc"
|
||||||
|
BUILD_REGISTER=".build_register"
|
||||||
|
PASSWORD=
|
||||||
|
GITEA_USER="camoroso"
|
||||||
|
GITEA_PASSWORD_FILE="${HOME}/.gitea_password"
|
||||||
|
GITEA_OWNER="go-pkg"
|
||||||
|
GITEA_HOST="https://git.portale-stac.it"
|
||||||
|
GITEA_BASE_PATH="api/packages"
|
||||||
|
GITEA_PKG_TYPE="generic"
|
||||||
|
|
||||||
|
function exitMsg() {
|
||||||
|
echo >&2 "${1}"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function readBuildCount() {
|
||||||
|
local reg ver count
|
||||||
|
if [ -r "${BUILD_REGISTER}" ]; then
|
||||||
|
reg=$(<"${BUILD_REGISTER}")
|
||||||
|
else
|
||||||
|
reg="${PROGRAM_VERSION} 0"
|
||||||
|
fi
|
||||||
|
read ver count <<<"${reg}"
|
||||||
|
if [ "${ver}" != "${PROGRAM_VERSION}" ]; then
|
||||||
|
count=0
|
||||||
|
fi
|
||||||
|
echo ${count}
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -r "${GITEA_PASSWORD_FILE}" ]; then
|
||||||
|
if ! PASSWORD=$(<"${GITEA_PASSWORD_FILE}"); then
|
||||||
|
exitMsg "Can're password file '${GITEA_PASSWORD_FILE}'"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
exitMsg "Password file '${GITEA_PASSWORD_FILE}' not found"
|
||||||
|
fi
|
||||||
|
if [ -z "${PASSWORD}" ]; then
|
||||||
|
exitMsg "Empty password. Please, check file '${GITEA_PASSWORD_FILE}'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! ./build.bash; then
|
||||||
|
exitMsg "Build program failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! source "${RESOURCE_FILE}"; then
|
||||||
|
exitMsg "Loading resource file failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! exeList=$(echo 2>/dev/null ${PROGRAM_NAME}_v${PROGRAM_VERSION}_*); then
|
||||||
|
exitMsg "No executable found"
|
||||||
|
fi
|
||||||
|
|
||||||
|
buildCount=$(readBuildCount)
|
||||||
|
fileCount=0
|
||||||
|
for exe in ${exeList}; do
|
||||||
|
if [ "${exe/tar.gz/}" != "${exe}" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
((fileCount++))
|
||||||
|
dir="${exe}_${buildCount}"
|
||||||
|
dist="${dir}.tar.gz"
|
||||||
|
rm -f "${dist}"
|
||||||
|
printf "%2d: %-30s --> %s\n" "${fileCount}" "${exe}" "${dist}"
|
||||||
|
mkdir "${dir}"
|
||||||
|
cp "${exe}" "${dir}/${PROGRAM_NAME}"
|
||||||
|
tar czf "${dist}" "${dir}"
|
||||||
|
rm -fR "${dir}"
|
||||||
|
|
||||||
|
url="${GITEA_HOST}/${GITEA_BASE_PATH}/${GITEA_OWNER}/${GITEA_PKG_TYPE}/${PROGRAM_NAME}/${PROGRAM_VERSION}/${dist}"
|
||||||
|
# echo "${url}"
|
||||||
|
curl --user "${USER}:${PASSWORD}" --upload-file "${dist}" "${url}"
|
||||||
|
|
||||||
|
rm -f "${dist}"
|
||||||
|
done
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// util-string.go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkStringLiteral(literal string) (value string, err error) {
|
||||||
|
length := len(literal)
|
||||||
|
if length >= 2 {
|
||||||
|
if (literal[0] == '"' && literal[length-1] == '"') || literal[0] == '\'' && literal[length-1] == '\'' {
|
||||||
|
value = literal[1 : length-1]
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("unquoted or partially quoted string literal: `%s`", literal)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("invalid string literal: `%s`", literal)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
1.17.0
|
||||||
+9
-6
@@ -11,6 +11,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/str"
|
||||||
)
|
)
|
||||||
|
|
||||||
type dictIterMode int
|
type dictIterMode int
|
||||||
@@ -22,7 +25,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type DictIterator struct {
|
type DictIterator struct {
|
||||||
a *kern.DictType
|
a *dict.DictType
|
||||||
count int64
|
count int64
|
||||||
index int64
|
index int64
|
||||||
keys []any
|
keys []any
|
||||||
@@ -67,7 +70,7 @@ func (it *DictIterator) makeKeys(m map[any]any, sort sortType) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDictIterator(dict *kern.DictType, args []any) (it *DictIterator, err error) {
|
func NewDictIterator(dict *dict.DictType, args []any) (it *DictIterator, err error) {
|
||||||
var sortType = sortTypeNone
|
var sortType = sortTypeNone
|
||||||
var s string
|
var s string
|
||||||
var argAny any
|
var argAny any
|
||||||
@@ -78,7 +81,7 @@ func NewDictIterator(dict *kern.DictType, args []any) (it *DictIterator, err err
|
|||||||
} else {
|
} else {
|
||||||
argAny = "default"
|
argAny = "default"
|
||||||
}
|
}
|
||||||
if s, err = kern.ToGoString(argAny, "sort type"); err == nil {
|
if s, err = str.ToGoString(argAny, "sort type"); err == nil {
|
||||||
switch strings.ToLower(s) {
|
switch strings.ToLower(s) {
|
||||||
case "a", "asc":
|
case "a", "asc":
|
||||||
sortType = sortTypeAsc
|
sortType = sortTypeAsc
|
||||||
@@ -99,7 +102,7 @@ func NewDictIterator(dict *kern.DictType, args []any) (it *DictIterator, err err
|
|||||||
argAny = "default"
|
argAny = "default"
|
||||||
}
|
}
|
||||||
|
|
||||||
if s, err = kern.ToGoString(argAny, "iteration mode"); err == nil {
|
if s, err = str.ToGoString(argAny, "iteration mode"); err == nil {
|
||||||
switch strings.ToLower(s) {
|
switch strings.ToLower(s) {
|
||||||
case "k", "key", "keys":
|
case "k", "key", "keys":
|
||||||
dictIt.iterMode = dictIterModeKeys
|
dictIt.iterMode = dictIterModeKeys
|
||||||
@@ -124,7 +127,7 @@ func NewDictIterator(dict *kern.DictType, args []any) (it *DictIterator, err err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewMapIterator(m map[any]any) (it *DictIterator) {
|
func NewMapIterator(m map[any]any) (it *DictIterator) {
|
||||||
it = &DictIterator{a: (*kern.DictType)(&m), count: 0, index: -1, keys: nil}
|
it = &DictIterator{a: (*dict.DictType)(&m), count: 0, index: -1, keys: nil}
|
||||||
it.makeKeys(m, sortTypeNone)
|
it.makeKeys(m, sortTypeNone)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -190,7 +193,7 @@ func (it *DictIterator) Current() (item any, err error) {
|
|||||||
case dictIterModeItems:
|
case dictIterModeItems:
|
||||||
a := *(it.a)
|
a := *(it.a)
|
||||||
pair := []any{it.keys[it.index], a[it.keys[it.index]]}
|
pair := []any{it.keys[it.index], a[it.keys[it.index]]}
|
||||||
item = kern.NewList(pair)
|
item = array.NewArray(pair)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = io.EOF
|
err = io.EOF
|
||||||
|
|||||||
+604
-189
File diff suppressed because it is too large
Load Diff
+460
-30
@@ -581,7 +581,11 @@ pre.rouge .ss {
|
|||||||
<li><a href="#_operator">4.1. <code class="blue">;</code> operator</a></li>
|
<li><a href="#_operator">4.1. <code class="blue">;</code> operator</a></li>
|
||||||
<li><a href="#_but_operator">4.2. <code class="blue">but</code> operator</a></li>
|
<li><a href="#_but_operator">4.2. <code class="blue">but</code> operator</a></li>
|
||||||
<li><a href="#_assignment_operator">4.3. Assignment operator <code class="blue">=</code></a></li>
|
<li><a href="#_assignment_operator">4.3. Assignment operator <code class="blue">=</code></a></li>
|
||||||
<li><a href="#_selector_operator">4.4. Selector operator <code class="blue">? : ::</code></a></li>
|
<li><a href="#_selector_operator">4.4. Selector operator <code class="blue">? : ::</code></a>
|
||||||
|
<ul class="sectlevel3">
|
||||||
|
<li><a href="#_triple_special_case_of_the_selector_operator">4.4.1. Triple special case of the selector operator</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
<li><a href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code>, <code class="blue">?=</code>, and <code class="blue">?!</code></a></li>
|
<li><a href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code>, <code class="blue">?=</code>, and <code class="blue">?!</code></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
@@ -613,8 +617,10 @@ pre.rouge .ss {
|
|||||||
<li><a href="#_dec">dec()</a></li>
|
<li><a href="#_dec">dec()</a></li>
|
||||||
<li><a href="#_string">string()</a></li>
|
<li><a href="#_string">string()</a></li>
|
||||||
<li><a href="#_fract">fract()</a></li>
|
<li><a href="#_fract">fract()</a></li>
|
||||||
|
<li><a href="#_char">char()</a></li>
|
||||||
<li><a href="#_eval">eval()</a></li>
|
<li><a href="#_eval">eval()</a></li>
|
||||||
<li><a href="#_var">var()</a></li>
|
<li><a href="#_var">var()</a></li>
|
||||||
|
<li><a href="#_set">set</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#_module_fmt">7.1.2. Module "fmt"</a>
|
<li><a href="#_module_fmt">7.1.2. Module "fmt"</a>
|
||||||
@@ -649,6 +655,8 @@ pre.rouge .ss {
|
|||||||
<li><a href="#_filewritetext">fileWriteText()</a></li>
|
<li><a href="#_filewritetext">fileWriteText()</a></li>
|
||||||
<li><a href="#_filereadtext">fileReadText()</a></li>
|
<li><a href="#_filereadtext">fileReadText()</a></li>
|
||||||
<li><a href="#_filereadtextall">fileReadTextAll()</a></li>
|
<li><a href="#_filereadtextall">fileReadTextAll()</a></li>
|
||||||
|
<li><a href="#_filebyteiterator">fileByteIterator()</a></li>
|
||||||
|
<li><a href="#_filelineiterator">fileLineIterator()</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#_module_string">7.1.7. Module "string"</a>
|
<li><a href="#_module_string">7.1.7. Module "string"</a>
|
||||||
@@ -674,7 +682,15 @@ pre.rouge .ss {
|
|||||||
<li><a href="#_named_operators">8.1.1. Named operators</a></li>
|
<li><a href="#_named_operators">8.1.1. Named operators</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#_iterator_over_custom_data_sources">8.2. Iterator over custom data-sources</a></li>
|
<li><a href="#_infixed_operators_on_iterators">8.2. Infixed operators on iterators</a>
|
||||||
|
<ul class="sectlevel3">
|
||||||
|
<li><a href="#_cat_operator">8.2.1. <code class="blue">cat</code> operator</a></li>
|
||||||
|
<li><a href="#_filter_operator">8.2.2. <code class="blue">filter</code> operator</a></li>
|
||||||
|
<li><a href="#_groupby_operator">8.2.3. <code class="blue">groupby</code> operator</a></li>
|
||||||
|
<li><a href="#_map_operator">8.2.4. <code class="blue">map</code> operator</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li><a href="#_iterator_over_custom_data_sources">8.3. Iterator over custom data-sources</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#_plugins">9. Plugins</a></li>
|
<li><a href="#_plugins">9. Plugins</a></li>
|
||||||
@@ -686,7 +702,7 @@ pre.rouge .ss {
|
|||||||
<div class="sectionbody">
|
<div class="sectionbody">
|
||||||
<!-- toc disabled -->
|
<!-- toc disabled -->
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><mark>TODO: Work in progress (last update on 2026/04/15, 6:02 p.m.)</mark></p>
|
<p><mark>TODO: Work in progress (last update on 2026/05/08)</mark></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -788,7 +804,26 @@ pre.rouge .ss {
|
|||||||
<code class="green">}</code></p>
|
<code class="green">}</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>In order to inspect the global context issue the <code class="blue">$$global</code> operator.</p>
|
<p>In order to inspect the global context issue the <code class="blue">$$ global</code> operation.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Example: list all functions whose name starts with "str"</div>
|
||||||
|
<p><code>>>></code> <code class="blue">builtin "string</code> <em class="gray">// most function in the builtin module <strong>string</strong> have names starting with "str".</em><br>
|
||||||
|
<code class="green">1</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><code>>>></code> <code class="blue">($$$global).functions) filter strStartsWith($_, "str"</code><br>
|
||||||
|
<code class="green">[</code><br>
|
||||||
|
<code class="green">  "strEndsWith",</code><br>
|
||||||
|
<code class="green">  "strJoin",</code><br>
|
||||||
|
<code class="green">  "strLower",</code><br>
|
||||||
|
<code class="green">  "strSplit",</code><br>
|
||||||
|
<code class="green">  "strStartsWith",</code><br>
|
||||||
|
<code class="green">  "strSub",</code><br>
|
||||||
|
<code class="green">  "strTrim",</code><br>
|
||||||
|
<code class="green">  "strUpper",</code><br>
|
||||||
|
<code class="green">  "string"</code><br>
|
||||||
|
<code class="green">]</code></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1880,10 +1915,10 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
|
|||||||
<p>The <em>selector operator</em> is very similar to the <em>switch/case/default</em> statement available in many programming languages.</p>
|
<p>The <em>selector operator</em> is very similar to the <em>switch/case/default</em> statement available in many programming languages.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="exampleblock">
|
<div class="exampleblock">
|
||||||
<div class="title">Example 13. Selector literal Syntax</div>
|
<div class="title">Example 13. Selector literal syntax</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><em>selector-operator</em> = <em>select-expression</em> "<strong>?</strong>" <em>selector-case</em> { "<strong>:</strong>" <em>selector-case</em> } ["<strong>::</strong>" <em>default-multi-expression</em>]<br>
|
<p><strong><em>selector-operator</em></strong> = <em>select-expression</em> "<strong>?</strong>" <em>selector-case</em> { "<strong>:</strong>" <em>selector-case</em> } ["<strong>::</strong>" <em>default-multi-expression</em>]<br>
|
||||||
<em>selector-case</em> = [<em>match-list</em>] <em>case-value</em><br>
|
<em>selector-case</em> = [<em>match-list</em>] <em>case-value</em><br>
|
||||||
<em>match-list</em> = "<strong>[</strong>" <em>item</em> {"<strong>,</strong>" <em>items</em>} "<strong>]</strong>"<br>
|
<em>match-list</em> = "<strong>[</strong>" <em>item</em> {"<strong>,</strong>" <em>items</em>} "<strong>]</strong>"<br>
|
||||||
<em>item</em> = <em>expression</em><br>
|
<em>item</em> = <em>expression</em><br>
|
||||||
@@ -1931,11 +1966,45 @@ Technically <code class="blue">;</code> is not treated as a real operator. It ac
|
|||||||
<p><code>>>></code> <code class="blue">10 ? {"a"} : {"b"}</code><br>
|
<p><code>>>></code> <code class="blue">10 ? {"a"} : {"b"}</code><br>
|
||||||
<code class="red">Eval Error: [1:3] no case catches the value (10) of the selection expression</code></p>
|
<code class="red">Eval Error: [1:3] no case catches the value (10) of the selection expression</code></p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="sect3">
|
||||||
|
<h4 id="_triple_special_case_of_the_selector_operator"><a class="anchor" href="#_triple_special_case_of_the_selector_operator"></a><a class="link" href="#_triple_special_case_of_the_selector_operator">4.4.1. Triple special case of the selector operator</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>If the <em>select-expression</em> is a boolean expression, the selector operator can be used as a sort of <em>if-then-else</em> statement. In this case, the first case is evaluated if the <em>select-expression</em> is true, and the second case is evaluated if the <em>select-expression</em> is false. In this special case, the <em>match-list</em> of both cases must be empty.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Example</div>
|
||||||
|
<p><code>>>></code> <code class="blue">(true) ? {"T"}: {"F"}</code><br>
|
||||||
|
<code class="green">T</code><br>
|
||||||
|
<code>>>></code> <code class="blue">(2 > 1) ? {"a"} : {"b"}</code><br>
|
||||||
|
<code class="green">a</code><br>
|
||||||
|
<code>>>></code> <code class="blue">(2 < 1) ? {"a"} : {"b"}</code><br>
|
||||||
|
<code class="green">b</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="admonitionblock warning">
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<td class="icon">
|
||||||
|
<i class="fa icon-warning" title="Warning"></i>
|
||||||
|
</td>
|
||||||
|
<td class="content">
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>The triple special case of the selector operator is very useful, but it only works with boolean expressions.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Example of confusion</div>
|
||||||
|
<p><code>>>></code> <code class="blue">int(true) ? {"T"}: {"F"}</code><br>
|
||||||
|
<code class="green">F</code></p>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect2">
|
<div class="sect2">
|
||||||
<h3 id="_variable_default_value_and"><a class="anchor" href="#_variable_default_value_and"></a><a class="link" href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code>, <code class="blue">?=</code>, and <code class="blue">?!</code></a></h3>
|
<h3 id="_variable_default_value_and"><a class="anchor" href="#_variable_default_value_and"></a><a class="link" href="#_variable_default_value_and">4.5. Variable default value <code class="blue">??</code>, <code class="blue">?=</code>, and <code class="blue">?!</code></a></h3>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>The left operand of the first two operators, <code class="blue">??</code> and <code class="blue">?=</code>, 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.</p>
|
<p>The left operand of the first two operators, <code class="blue">??</code> and <code class="blue">?=</code>, must be a variable. The right operatand can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="admonitionblock important">
|
<div class="admonitionblock important">
|
||||||
<table>
|
<table>
|
||||||
@@ -1953,7 +2022,7 @@ If the left variable is defined, the right expression is not evaluated at all.
|
|||||||
<p>The <code class="blue">??</code> operator do not change the status of the left variable.</p>
|
<p>The <code class="blue">??</code> operator do not change the status of the left variable.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>The <code class="blue">?=</code> assigns the calculated value of the right expression to the left variable.</p>
|
<p>The <code class="blue">?=</code> assigns the calculated value of the right expression to the variable on the left side.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>The third one, <code class="blue">?!</code>, is the alternate operator. If the variable on the left size is not defined, it returns <em class="blue">nil</em>. Otherwise it returns the result of the expressione on the right side.</p>
|
<p>The third one, <code class="blue">?!</code>, is the alternate operator. If the variable on the left size is not defined, it returns <em class="blue">nil</em>. Otherwise it returns the result of the expressione on the right side.</p>
|
||||||
@@ -1965,7 +2034,7 @@ If the left variable is defined, the right expression is not evaluated at all.
|
|||||||
<i class="fa icon-important" title="Important"></i>
|
<i class="fa icon-important" title="Important"></i>
|
||||||
</td>
|
</td>
|
||||||
<td class="content">
|
<td class="content">
|
||||||
If the left variable is NOT defined, the right expression is not evaluated at all.
|
If the variable <code class="blue">?!</code> is NOT defined, the expression is not evaluated at all.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@@ -2371,7 +2440,7 @@ These operators have a high priority, in particular higher than the operator <co
|
|||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>identifier</em> <code>=</code> <em>any</em> → <em>any</em></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>identifier</em> <code>=</code> <em>any</em> → <em>any</em></p></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="tableblock halign-center valign-top" colspan="4"><p class="tableblock"><em>See also the table of special allocation operators below</em></p></td>
|
<td class="tableblock halign-center valign-top" colspan="4"><p class="tableblock"><em>See also the table of special assignment operators below</em></p></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>BUT</strong></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>BUT</strong></p></td>
|
||||||
@@ -2381,6 +2450,40 @@ These operators have a high priority, in particular higher than the operator <co
|
|||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>but</code> <em>any</em> → <em>any</em></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>any</em> <code>but</code> <em>any</em> → <em>any</em></p></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td class="tableblock halign-center valign-top" rowspan="6"><p class="tableblock"><strong>ITER-OP</strong></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">digest</code></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item-digesting</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>digest</code> <em>expr</em> → <em>any</em></p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">filter</code></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item-filtering</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>filter</code> <em>expr</em> → <em>list</em></p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">groupby</code></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Dict-grouping</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>groupby</code> <em>key-expr</em> → <em>dict</em></p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">cat</code></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item-concatenation</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> `cat ` <em>iterable</em> → <em>list</em></p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">map</code></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Item-mapping</em></p></td>
|
||||||
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>iterable</em> <code>map</code> <em>-expr</em> → <em>list</em></p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="tableblock halign-center valign-top" colspan="4"><p class="tableblock"><em>See iterators section for examples</em></p></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>RANGE</strong></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><strong>RANGE</strong></p></td>
|
||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">:</code></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><code class="blue">:</code></p></td>
|
||||||
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
<td class="tableblock halign-center valign-top"><p class="tableblock"><em>Infix</em></p></td>
|
||||||
@@ -2536,14 +2639,14 @@ short for<br>
|
|||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><code>>>></code> <em class="gray">// Required and optional parameters</em><br>
|
<p><code>>>></code> <em class="gray">// Required and optional parameters</em><br>
|
||||||
<code>>>></code> <code class="blue">measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? [true] {"s"} :: {""}}</code><br>
|
<code>>>></code> <code class="blue">measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? {"s"} :: {""}}</code><br>
|
||||||
<code class="green">measure(value, unit="meter"):any{}</code></p>
|
<code class="green">measure(value, unit="meter"):any{}</code></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect2">
|
<div class="sect2">
|
||||||
<h3 id="_golang_function_definition"><a class="anchor" href="#_golang_function_definition"></a><a class="link" href="#_golang_function_definition">6.2. <em>Golang</em> function definition</a></h3>
|
<h3 id="_golang_function_definition"><a class="anchor" href="#_golang_function_definition"></a><a class="link" href="#_golang_function_definition">6.2. <em>Golang</em> function definition</a></h3>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>Description of how to define Golang functions and how to bind them to <em>Expr</em> are topics covered in another document that I’ll write, one day, maybe.</p>
|
<p>Description of how to define Golang functions and how to bind them to <em>Expr</em> are topics covered in another documents that I’ll write, one day, maybe.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect2">
|
<div class="sect2">
|
||||||
@@ -2642,7 +2745,7 @@ short for<br>
|
|||||||
<div class="title">Example</div>
|
<div class="title">Example</div>
|
||||||
<p><code>>>></code> <code class="blue">f = func() { @x = 3; x = 5 }</code> <em class="gray">// f() declares two <strong>different</strong> local variables: <code>@x</code> and <code>x</code></em><br>
|
<p><code>>>></code> <code class="blue">f = func() { @x = 3; x = 5 }</code> <em class="gray">// f() declares two <strong>different</strong> local variables: <code>@x</code> and <code>x</code></em><br>
|
||||||
<code class="green">f():any{}</code><br>
|
<code class="green">f():any{}</code><br>
|
||||||
<code>>>></code> <code class="blue">f()</code> <em class="gray">// The multi-expression (two) in f() is calculated and the last result is returned</em><br>
|
<code>>>></code> <code class="blue">f()</code> <em class="gray">// The multi-expression (two expressions) in f() is calculated and the last result is returned</em><br>
|
||||||
<code class="green">5</code><br>
|
<code class="green">5</code><br>
|
||||||
<code>>>></code> <code class="blue">x</code> <em class="gray">// The <code>x</code> variable was not defined in the main context before the f() invocation. It appears in the main context by cloning the <code>@x</code> variable, local to f() after its termnation.</em><br>
|
<code>>>></code> <code class="blue">x</code> <em class="gray">// The <code>x</code> variable was not defined in the main context before the f() invocation. It appears in the main context by cloning the <code>@x</code> variable, local to f() after its termnation.</em><br>
|
||||||
<code class="green">3</code></p>
|
<code class="green">3</code></p>
|
||||||
@@ -2727,7 +2830,7 @@ The clone modifier <code class="blue">@</code> does not make a variable a refere
|
|||||||
<div class="title">Example 16. Builtin activation syntax</div>
|
<div class="title">Example 16. Builtin activation syntax</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><strong><em>builtin-activation</em></strong> = <code class="blue">BUILTIN</code> (<em>builtin-name</em> | <em>list-of-builtin-names</em>)<br>
|
<p><strong><em>builtin-activation</em></strong> = <code class="blue">BUILTIN</code> (<em>builtin-name</em> | <em>list-of-builtin-names</em> | <strong>"*"</strong>)<br>
|
||||||
<em>builtin-name</em> = <em>string</em><br>
|
<em>builtin-name</em> = <em>string</em><br>
|
||||||
<em>list-of-builtin-names</em> = <strong>[</strong> <em>string</em> { "<strong>,</strong>" <em>string</em> } <strong>]</strong></p>
|
<em>list-of-builtin-names</em> = <strong>[</strong> <em>string</em> { "<strong>,</strong>" <em>string</em> } <strong>]</strong></p>
|
||||||
</div>
|
</div>
|
||||||
@@ -2815,9 +2918,15 @@ To avoid the need to activate builtin modules one by one, it is possible to acti
|
|||||||
<div class="title">Other functions</div>
|
<div class="title">Other functions</div>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
|
<p><a href="#_char">char()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
<p><a href="#_eval">eval()</a></p>
|
<p><a href="#_eval">eval()</a></p>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
|
<p><a href="#_set">set()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
<p><a href="#_var">var()</a></p>
|
<p><a href="#_var">var()</a></p>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -2833,7 +2942,9 @@ Returns <em>true</em> if the value type of <em><expr></em> is boolean, fal
|
|||||||
<p><code>>>></code> <code class="blue">isBool(true)</code><br>
|
<p><code>>>></code> <code class="blue">isBool(true)</code><br>
|
||||||
<code class="green">true</code><br>
|
<code class="green">true</code><br>
|
||||||
<code>>>></code> <code class="blue">isBool(3==2)</code><br>
|
<code>>>></code> <code class="blue">isBool(3==2)</code><br>
|
||||||
<code class="green">true</code></p>
|
<code class="green">true</code><br>
|
||||||
|
<code>>>></code> <code class="blue">isBool(3 + 2)</code><br>
|
||||||
|
<code class="green">false</code></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
@@ -2938,7 +3049,7 @@ Returns <em>true</em> if the value type of <em><expr></em> is fraction or
|
|||||||
<h5 id="_isstring"><a class="anchor" href="#_isstring"></a><a class="link" href="#_isstring">isString()</a></h5>
|
<h5 id="_isstring"><a class="anchor" href="#_isstring"></a><a class="link" href="#_isstring">isString()</a></h5>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>Syntax: <code>isString(<expr>) → bool</code><br>
|
<p>Syntax: <code>isString(<expr>) → bool</code><br>
|
||||||
Returns a boolean value , false otherwise.</p>
|
Returns <em>true</em> if the value type of <em><expr></em> is string, false otherwise.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<div class="title">Examples</div>
|
<div class="title">Examples</div>
|
||||||
@@ -2954,7 +3065,7 @@ Returns a boolean value , false otherwise.</p>
|
|||||||
<h5 id="_bool"><a class="anchor" href="#_bool"></a><a class="link" href="#_bool">bool()</a></h5>
|
<h5 id="_bool"><a class="anchor" href="#_bool"></a><a class="link" href="#_bool">bool()</a></h5>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>Syntax: <code>bool(<expr>) → bool</code><br>
|
<p>Syntax: <code>bool(<expr>) → bool</code><br>
|
||||||
Returns a <em>boolean</em> value consisent to the value of the expression.</p>
|
Returns a <em>boolean</em> value consisent with the value of the expression.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<div class="title">Examples</div>
|
<div class="title">Examples</div>
|
||||||
@@ -3077,6 +3188,20 @@ Returns a <em>fraction</em> value consistent with the value of the expression.</
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
|
<h5 id="_char"><a class="anchor" href="#_char"></a><a class="link" href="#_char">char()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax: <code>char(<intexpr>) → string</code><br>
|
||||||
|
Returns the character whose ASCII (soon Unicode too) code point is specified by the integer expression.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Examples</div>
|
||||||
|
<p><code>>>></code> <code class="blue">char(65)</code><br>
|
||||||
|
<code class="green">"A"</code><br>
|
||||||
|
<code>>>></code> <code class="blue">char(97)</code><br>
|
||||||
|
<code class="green">"a"</code></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect4">
|
||||||
<h5 id="_eval"><a class="anchor" href="#_eval"></a><a class="link" href="#_eval">eval()</a></h5>
|
<h5 id="_eval"><a class="anchor" href="#_eval"></a><a class="link" href="#_eval">eval()</a></h5>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>Syntax: <code>eval(<string-expr>) → any</code><br>
|
<p>Syntax: <code>eval(<string-expr>) → any</code><br>
|
||||||
@@ -3096,13 +3221,13 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
|||||||
<code>    var(<string-expr>) → any</code></p>
|
<code>    var(<string-expr>) → any</code></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>This function allows you to define variables whose names must include special characters. The first form of the function allows you to define a variable with a name specified by the first parameter and assign it the value of the second parameter. The second form only returns the value of the variable with the specified name.</p>
|
<p>This function allows you to define variables whose names can include special characters. The first form of the function allows you to define a variable with a name specified by the first parameter and assign it the value of the second parameter. The second form only returns the value of the variable with the specified name.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<div class="title">Examples</div>
|
<div class="title">Examples</div>
|
||||||
<p><code>>>></code> <code class="blue">var("$x", 3+9)</code><br>
|
<p><code>>>></code> <code class="blue">var("$x", 3+9)</code><br>
|
||||||
<code class="green">12</code><br>
|
<code class="green">12</code><br>
|
||||||
<code>>>></code> <code class="blue">var("$x")</code><br>
|
<code>>>></code> <code class="blue">var("$"+"x")</code><br>
|
||||||
<code class="green">12</code><br>
|
<code class="green">12</code><br>
|
||||||
<code>>>></code> <code class="blue">var("gain%", var("$x"))</code><br>
|
<code>>>></code> <code class="blue">var("gain%", var("$x"))</code><br>
|
||||||
<code class="green">12</code><br>
|
<code class="green">12</code><br>
|
||||||
@@ -3110,9 +3235,32 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
|||||||
<code class="green">13</code></p>
|
<code class="green">13</code></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="sect4">
|
||||||
|
<h5 id="_set"><a class="anchor" href="#_set"></a><a class="link" href="#_set">set</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    set(<string-expr>, <expr>) → any</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>This function allows you to set the value of a variable whose name can include special characters. The first parameter is the name of the variable and the second parameter is the new value to assign to that variable.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>It is equivalent to the first form of the var() function, but it is more explicit about the intent of changing the value of an existing variable.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Examples</div>
|
||||||
|
<p><code>>>></code> <code class="blue">set("$x", 100)</code><br>
|
||||||
|
<code class="green">100</code><br>
|
||||||
|
<code>>>></code> <code class="blue">var("$x")</code><br>
|
||||||
|
<code class="green">100</code><br></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect3">
|
<div class="sect3">
|
||||||
<h4 id="_module_fmt"><a class="anchor" href="#_module_fmt"></a><a class="link" href="#_module_fmt">7.1.2. Module "fmt"</a></h4>
|
<h4 id="_module_fmt"><a class="anchor" href="#_module_fmt"></a><a class="link" href="#_module_fmt">7.1.2. Module "fmt"</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><mark>to-do</mark></p>
|
||||||
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_print"><a class="anchor" href="#_print"></a><a class="link" href="#_print">print()</a></h5>
|
<h5 id="_print"><a class="anchor" href="#_print"></a><a class="link" href="#_print">print()</a></h5>
|
||||||
|
|
||||||
@@ -3124,10 +3272,18 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
|||||||
</div>
|
</div>
|
||||||
<div class="sect3">
|
<div class="sect3">
|
||||||
<h4 id="_module_import"><a class="anchor" href="#_module_import"></a><a class="link" href="#_module_import">7.1.3. Module "import"</a></h4>
|
<h4 id="_module_import"><a class="anchor" href="#_module_import"></a><a class="link" href="#_module_import">7.1.3. Module "import"</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Module activation:<br>
|
||||||
|
<code>    BUILTIN "import"</code></p>
|
||||||
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_import"><a class="anchor" href="#_import"></a><a class="link" href="#_import"><em>import()</em></a></h5>
|
<h5 id="_import"><a class="anchor" href="#_import"></a><a class="link" href="#_import"><em>import()</em></a></h5>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p><em class="blue">import(<span class="grey"><source-file></span>)</em> — loads the multi-expression contained in the specified source and returns its value.</p>
|
<p>Syntax:<br>
|
||||||
|
<code>    import(<source-file>)</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Loads the multi-expression contained in the specified source and returns its value.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
@@ -3137,16 +3293,40 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
|||||||
</div>
|
</div>
|
||||||
<div class="sect3">
|
<div class="sect3">
|
||||||
<h4 id="_module_iterator"><a class="anchor" href="#_module_iterator"></a><a class="link" href="#_module_iterator">7.1.4. Module "iterator"</a></h4>
|
<h4 id="_module_iterator"><a class="anchor" href="#_module_iterator"></a><a class="link" href="#_module_iterator">7.1.4. Module "iterator"</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Module activation:<br>
|
||||||
|
<code>    BUILTIN "iterator"</code></p>
|
||||||
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_run"><a class="anchor" href="#_run"></a><a class="link" href="#_run">run()</a></h5>
|
<h5 id="_run"><a class="anchor" href="#_run"></a><a class="link" href="#_run">run()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    run(<iterator>, <operator>, <vars>) → any</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Iterates over the specified iterator and applies the specified operator to the current value of the iterator.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect3">
|
<div class="sect3">
|
||||||
<h4 id="_module_math_arith"><a class="anchor" href="#_module_math_arith"></a><a class="link" href="#_module_math_arith">7.1.5. Module "math.arith"</a></h4>
|
<h4 id="_module_math_arith"><a class="anchor" href="#_module_math_arith"></a><a class="link" href="#_module_math_arith">7.1.5. Module "math.arith"</a></h4>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
|
<p>Module activation:<br>
|
||||||
|
<code>    BUILTIN "math.arith"</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
<p>Currently, the "math.arith" module provides two functions, add() and mul(), that perform addition and multiplication of an arbitrary number of parameters. More functions will be added in the future.</p>
|
<p>Currently, the "math.arith" module provides two functions, add() and mul(), that perform addition and multiplication of an arbitrary number of parameters. More functions will be added in the future.</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ulist">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_add">add()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_mul">mul()</a></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_add"><a class="anchor" href="#_add"></a><a class="link" href="#_add">add()</a></h5>
|
<h5 id="_add"><a class="anchor" href="#_add"></a><a class="link" href="#_add">add()</a></h5>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -3202,37 +3382,213 @@ Computes and returns the value of the <span class="underline">string</span> expr
|
|||||||
</div>
|
</div>
|
||||||
<div class="sect3">
|
<div class="sect3">
|
||||||
<h4 id="_module_os_file"><a class="anchor" href="#_module_os_file"></a><a class="link" href="#_module_os_file">7.1.6. Module "os.file"</a></h4>
|
<h4 id="_module_os_file"><a class="anchor" href="#_module_os_file"></a><a class="link" href="#_module_os_file">7.1.6. Module "os.file"</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Module activation:<br>
|
||||||
|
<code>    BUILTIN "os.file"</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>The "os.file" module provides functions for working with files.</p>
|
||||||
|
</div>
|
||||||
|
<div class="ulist">
|
||||||
|
<div class="title">File related functions</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileOpen">fileOpen()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileAppend">fileAppend()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileCreate">fileCreate()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileClose">fileClose()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileWriteText">fileWriteText()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileReadText">fileReadText()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileReadTextAll">fileReadTextAll()</a></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="ulist">
|
||||||
|
<div class="title">Iterator functions for files</div>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileByteIterator">fileByteIterator()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_fileLineIterator">fileLineIterator()</a></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>More functions will be added in the future.</p>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_fileopen"><a class="anchor" href="#_fileopen"></a><a class="link" href="#_fileopen">fileOpen()</a></h5>
|
<h5 id="_fileopen"><a class="anchor" href="#_fileopen"></a><a class="link" href="#_fileopen">fileOpen()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    fileOpen(<file-path>) → file-handle</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Returns a file handle for the specified file path. The file is opened in read-write mode. If the file does not exist, it is created.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_fileappend"><a class="anchor" href="#_fileappend"></a><a class="link" href="#_fileappend">fileAppend()</a></h5>
|
<h5 id="_fileappend"><a class="anchor" href="#_fileappend"></a><a class="link" href="#_fileappend">fileAppend()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    fileAppend(<file-path>) → any</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Like <a href="#_fileCreate">fileCreate()</a> but write operations happen at the end of the file.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_filecreate"><a class="anchor" href="#_filecreate"></a><a class="link" href="#_filecreate">fileCreate()</a></h5>
|
<h5 id="_filecreate"><a class="anchor" href="#_filecreate"></a><a class="link" href="#_filecreate">fileCreate()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    fileCreate(<file-path>) → file-handle</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Creates or truncates the named <em><file-path></em>. If the file already exists, it is truncated. If the file does not exist, it is created with mode 0o666 (before umask). The associated file descriptor has mode [O_RDWR]. The directory containing the file must already exist.</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_fileclose"><a class="anchor" href="#_fileclose"></a><a class="link" href="#_fileclose">fileClose()</a></h5>
|
<h5 id="_fileclose"><a class="anchor" href="#_fileclose"></a><a class="link" href="#_fileclose">fileClose()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><mark>to-do</mark></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_filewritetext"><a class="anchor" href="#_filewritetext"></a><a class="link" href="#_filewritetext">fileWriteText()</a></h5>
|
<h5 id="_filewritetext"><a class="anchor" href="#_filewritetext"></a><a class="link" href="#_filewritetext">fileWriteText()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><mark>to-do</mark></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_filereadtext"><a class="anchor" href="#_filereadtext"></a><a class="link" href="#_filereadtext">fileReadText()</a></h5>
|
<h5 id="_filereadtext"><a class="anchor" href="#_filereadtext"></a><a class="link" href="#_filereadtext">fileReadText()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><mark>to-do</mark></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_filereadtextall"><a class="anchor" href="#_filereadtextall"></a><a class="link" href="#_filereadtextall">fileReadTextAll()</a></h5>
|
<h5 id="_filereadtextall"><a class="anchor" href="#_filereadtextall"></a><a class="link" href="#_filereadtextall">fileReadTextAll()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><mark>to-do</mark></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect4">
|
||||||
|
<h5 id="_filebyteiterator"><a class="anchor" href="#_filebyteiterator"></a><a class="link" href="#_filebyteiterator">fileByteIterator()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    fileByteIterator(handle-or-path) → iterator</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Returns an iterator that produces the bytes of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Examples</div>
|
||||||
|
<p>>>> builtin "os.file"<br>
|
||||||
|
<code class="green">1</code><br>
|
||||||
|
<code>>>></code> <code class="blue">fileByteIterator("test-file.txt") map $_</code><br>
|
||||||
|
<code class="green">[
|
||||||
|
117,
|
||||||
|
110,
|
||||||
|
111,
|
||||||
|
10,
|
||||||
|
100,
|
||||||
|
117,
|
||||||
|
101,
|
||||||
|
10
|
||||||
|
]</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>>>> builtin ["os.file", "string"]<br>
|
||||||
|
<code class="green">2</code><br>
|
||||||
|
<code>>>></code> <code class="blue">fileByteIterator("test-file.txt") map char($_)</code><br>
|
||||||
|
<code class="green">[
|
||||||
|
"u",
|
||||||
|
"n",
|
||||||
|
"o",
|
||||||
|
"
|
||||||
|
",
|
||||||
|
"d",
|
||||||
|
"u",
|
||||||
|
"e",
|
||||||
|
"
|
||||||
|
",
|
||||||
|
]</code></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect4">
|
||||||
|
<h5 id="_filelineiterator"><a class="anchor" href="#_filelineiterator"></a><a class="link" href="#_filelineiterator">fileLineIterator()</a></h5>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    fileLineIterator(handle-or-path) → iterator</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Returns an iterator that produces the lines of the specified file. The parameter can be either a file handle or a file path. If a file path is provided, the file is opened and closed automatically by the iterator.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Examples</div>
|
||||||
|
<p><code>>>></code> <code class="blue">builtin "os.file"</code><br>
|
||||||
|
<code class="green">1</code><br>
|
||||||
|
<code>>>></code> <code class="blue">fileLineIterator("test-file.txt") map $_</code><br>
|
||||||
|
<code class="green">[
|
||||||
|
"uno",
|
||||||
|
"due"
|
||||||
|
]</code></p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect3">
|
<div class="sect3">
|
||||||
<h4 id="_module_string"><a class="anchor" href="#_module_string"></a><a class="link" href="#_module_string">7.1.7. Module "string"</a></h4>
|
<h4 id="_module_string"><a class="anchor" href="#_module_string"></a><a class="link" href="#_module_string">7.1.7. Module "string"</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Module activation:<br>
|
||||||
|
<code>    BUILTIN "string"</code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>This module provides functions for working with strings.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Currently available functions:</p>
|
||||||
|
</div>
|
||||||
|
<div class="ulist">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strJoin">strJoin()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strSub">strSub()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strSplit">strSplit()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strTrim">strTrim()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strStartsWith">strStartsWith()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strEndsWith">strEndsWith()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strUpper">strUpper()</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_strLower">strLower()</a></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
<div class="sect4">
|
<div class="sect4">
|
||||||
<h5 id="_strjoin"><a class="anchor" href="#_strjoin"></a><a class="link" href="#_strjoin">strJoin()</a></h5>
|
<h5 id="_strjoin"><a class="anchor" href="#_strjoin"></a><a class="link" href="#_strjoin">strJoin()</a></h5>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
@@ -3589,7 +3945,81 @@ Iterators built on custom data-sources can provide additional named operators, d
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sect2">
|
<div class="sect2">
|
||||||
<h3 id="_iterator_over_custom_data_sources"><a class="anchor" href="#_iterator_over_custom_data_sources"></a><a class="link" href="#_iterator_over_custom_data_sources">8.2. Iterator over custom data-sources</a></h3>
|
<h3 id="_infixed_operators_on_iterators"><a class="anchor" href="#_infixed_operators_on_iterators"></a><a class="link" href="#_infixed_operators_on_iterators">8.2. Infixed operators on iterators</a></h3>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>There are also some infixed operators that can be used with iterators. They are defined as follows.</p>
|
||||||
|
</div>
|
||||||
|
<div class="ulist">
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_cat">cat operator</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_diget">digest operator</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_filter">filter operator</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_groupby">groupby operator</a></p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p><a href="#_map">map operator</a></p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<hr>
|
||||||
|
<div class="sect3">
|
||||||
|
<h4 id="_cat_operator"><a class="anchor" href="#_cat_operator"></a><a class="link" href="#_cat_operator">8.2.1. <code class="blue">cat</code> operator</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    <iterable> cat <iterable> → <iterator>    </code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><code class="blue">cat</code> operator takes two iterators or iterables and returns a new iterator that produces the elements of the first iterator followed by the elements of the second iterator.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect3">
|
||||||
|
<h4 id="_filter_operator"><a class="anchor" href="#_filter_operator"></a><a class="link" href="#_filter_operator">8.2.2. <code class="blue">filter</code> operator</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Examples</div>
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    <iterable> filter <expr> → <iterator>    </code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><code class="blue">filter</code> applies a boolean expression to each element of the iterator and returns a list of the elements for which the expression evaluates to true.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect3">
|
||||||
|
<h4 id="_groupby_operator"><a class="anchor" href="#_groupby_operator"></a><a class="link" href="#_groupby_operator">8.2.3. <code class="blue">groupby</code> operator</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    <dict> groupby <key> → <dict>    </code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><code class="blue">groupby</code> operator groups the elements of the iterator based on the value of a specified expression and returns a dictionary where the keys are the group values and the values are lists of the elements in each group.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect3">
|
||||||
|
<h4 id="_map_operator"><a class="anchor" href="#_map_operator"></a><a class="link" href="#_map_operator">8.2.4. <code class="blue">map</code> operator</a></h4>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p>Syntax:<br>
|
||||||
|
<code>    <iterable> map <expr> → <list>    </code></p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<p><code class="blue">map</code> operator iterates over the elements of the iterator and evaluates the expressions provided on the right side for each element. Its result is a list of the computed values of the that expression. The current element of the iterator is available in the expression as the variable <code>$_</code>.</p>
|
||||||
|
</div>
|
||||||
|
<div class="paragraph">
|
||||||
|
<div class="title">Example: using the <code class="blue">map</code> operator</div>
|
||||||
|
<p><code>>>></code> <code class="blue">it = $(["one", "two", "three"])</code><br>
|
||||||
|
<code class="green">$(#3)</code><br>
|
||||||
|
<code>>>></code> <code class="blue">it map $_ + "!"</code><br>
|
||||||
|
<code class="green">["one!", "two!", "three!"]</code></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="sect2">
|
||||||
|
<h3 id="_iterator_over_custom_data_sources"><a class="anchor" href="#_iterator_over_custom_data_sources"></a><a class="link" href="#_iterator_over_custom_data_sources">8.3. Iterator over custom data-sources</a></h3>
|
||||||
<div class="paragraph">
|
<div class="paragraph">
|
||||||
<p>It is possible to create iterators over custom data-sources by defining a dictionary that has the key <code>next</code> whose value is a function that returns the next element of the collection and updates the state of the iterator. The syntax for creating an iterator over a custom data-source is as follows.</p>
|
<p>It is possible to create iterators over custom data-sources by defining a dictionary that has the key <code>next</code> whose value is a function that returns the next element of the collection and updates the state of the iterator. The syntax for creating an iterator over a custom data-source is as follows.</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -3610,7 +4040,7 @@ Iterators built on custom data-sources can provide additional named operators, d
|
|||||||
</div>
|
</div>
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
<div id="footer-text">
|
<div id="footer-text">
|
||||||
Last updated 2026-04-21 06:35:14 +0200
|
Last updated 2026-05-12 16:25:27 +0200
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -0,0 +1,322 @@
|
|||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<title>file: Go Coverage Report</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: black;
|
||||||
|
color: rgb(80, 80, 80);
|
||||||
|
}
|
||||||
|
body, pre, #legend span {
|
||||||
|
font-family: Menlo, monospace;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
#topbar {
|
||||||
|
background: black;
|
||||||
|
position: fixed;
|
||||||
|
top: 0; left: 0; right: 0;
|
||||||
|
height: 42px;
|
||||||
|
border-bottom: 1px solid rgb(80, 80, 80);
|
||||||
|
}
|
||||||
|
#content {
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
#nav, #legend {
|
||||||
|
float: left;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
#legend {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
#nav {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
#legend span {
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
.cov0 { color: rgb(192, 0, 0) }
|
||||||
|
.cov1 { color: rgb(128, 128, 128) }
|
||||||
|
.cov2 { color: rgb(116, 140, 131) }
|
||||||
|
.cov3 { color: rgb(104, 152, 134) }
|
||||||
|
.cov4 { color: rgb(92, 164, 137) }
|
||||||
|
.cov5 { color: rgb(80, 176, 140) }
|
||||||
|
.cov6 { color: rgb(68, 188, 143) }
|
||||||
|
.cov7 { color: rgb(56, 200, 146) }
|
||||||
|
.cov8 { color: rgb(44, 212, 149) }
|
||||||
|
.cov9 { color: rgb(32, 224, 152) }
|
||||||
|
.cov10 { color: rgb(20, 236, 155) }
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="topbar">
|
||||||
|
<div id="nav">
|
||||||
|
<select id="files">
|
||||||
|
|
||||||
|
<option value="file0">git.portale-stac.it/go-pkg/expr/file/file.go (88.9%)</option>
|
||||||
|
|
||||||
|
<option value="file1">git.portale-stac.it/go-pkg/expr/file/reader.go (77.8%)</option>
|
||||||
|
|
||||||
|
<option value="file2">git.portale-stac.it/go-pkg/expr/file/writer.go (100.0%)</option>
|
||||||
|
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div id="legend">
|
||||||
|
<span>not tracked</span>
|
||||||
|
|
||||||
|
<span class="cov0">not covered</span>
|
||||||
|
<span class="cov8">covered</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="content">
|
||||||
|
|
||||||
|
<pre class="file" id="file0" style="display: none">// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// file.go
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handle interface {
|
||||||
|
kern.Typer
|
||||||
|
GetFile() *os.File
|
||||||
|
GetName() string
|
||||||
|
Valid() bool
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type handleBase struct {
|
||||||
|
fh *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handleBase) GetFile() *os.File <span class="cov0" title="0">{
|
||||||
|
return h.fh
|
||||||
|
}</span>
|
||||||
|
|
||||||
|
func (h *handleBase) GetName() (name string) <span class="cov8" title="1">{
|
||||||
|
if h.fh != nil </span><span class="cov8" title="1">{
|
||||||
|
name = h.fh.Name()
|
||||||
|
}</span>
|
||||||
|
<span class="cov8" title="1">return</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handleBase) Valid() bool <span class="cov8" title="1">{
|
||||||
|
return h.fh != nil
|
||||||
|
}</span>
|
||||||
|
|
||||||
|
func (h *handleBase) Close() (err error) <span class="cov8" title="1">{
|
||||||
|
if h.fh != nil </span><span class="cov8" title="1">{
|
||||||
|
err = h.fh.Close()
|
||||||
|
h.fh = nil
|
||||||
|
}</span>
|
||||||
|
<span class="cov8" title="1">return</span>
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<pre class="file" id="file1" style="display: none">// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// reader.go
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Reader struct {
|
||||||
|
// fh *os.File
|
||||||
|
handleBase
|
||||||
|
reader *bufio.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReader(fh *os.File) *Reader <span class="cov8" title="1">{
|
||||||
|
return &Reader{handleBase: handleBase{fh: fh}, reader: bufio.NewReader(fh)}
|
||||||
|
}</span>
|
||||||
|
|
||||||
|
func OpenReader(filePath string) (r *Reader, err error) <span class="cov8" title="1">{
|
||||||
|
var fh *os.File
|
||||||
|
if fh, err = os.Open(filePath); err == nil </span><span class="cov8" title="1">{
|
||||||
|
r = NewReader(fh)
|
||||||
|
}</span>
|
||||||
|
<span class="cov8" title="1">return</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Reader) TypeName() string <span class="cov8" title="1">{
|
||||||
|
return "fileReader"
|
||||||
|
}</span>
|
||||||
|
|
||||||
|
func (h *Reader) String() string <span class="cov8" title="1">{
|
||||||
|
return "reader"
|
||||||
|
}</span>
|
||||||
|
|
||||||
|
func (h *Reader) Valid() bool <span class="cov8" title="1">{
|
||||||
|
return h.handleBase.Valid() && h.reader != nil
|
||||||
|
}</span>
|
||||||
|
|
||||||
|
func (w *Reader) Close() (err error) <span class="cov8" title="1">{
|
||||||
|
w.reader = nil
|
||||||
|
err = w.handleBase.Close()
|
||||||
|
return
|
||||||
|
}</span>
|
||||||
|
|
||||||
|
func (h *Reader) ReadByte() (b byte, err error) <span class="cov8" title="1">{
|
||||||
|
if h.reader != nil </span><span class="cov8" title="1">{
|
||||||
|
b, err = h.reader.ReadByte()
|
||||||
|
}</span> else<span class="cov0" title="0"> {
|
||||||
|
err = io.ErrClosedPipe
|
||||||
|
}</span>
|
||||||
|
<span class="cov8" title="1">return</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Reader) ReadAll() (p []byte, err error) <span class="cov8" title="1">{
|
||||||
|
if h.reader != nil </span><span class="cov8" title="1">{
|
||||||
|
p, err = io.ReadAll(h.reader)
|
||||||
|
}</span> else<span class="cov0" title="0"> {
|
||||||
|
err = io.ErrClosedPipe
|
||||||
|
}</span>
|
||||||
|
<span class="cov8" title="1">return</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Reader) ReadString(delim byte) (s string, err error) <span class="cov0" title="0">{
|
||||||
|
if h.reader != nil </span><span class="cov0" title="0">{
|
||||||
|
s, err = h.reader.ReadString(delim)
|
||||||
|
}</span> else<span class="cov0" title="0"> {
|
||||||
|
err = io.ErrClosedPipe
|
||||||
|
}</span>
|
||||||
|
<span class="cov0" title="0">return</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Reader) Reset() (err error) <span class="cov8" title="1">{
|
||||||
|
if h.fh != nil </span><span class="cov8" title="1">{
|
||||||
|
if _, err = h.fh.Seek(0, 0); err == nil </span><span class="cov8" title="1">{
|
||||||
|
h.reader.Reset(h.fh)
|
||||||
|
}</span>
|
||||||
|
}
|
||||||
|
<span class="cov8" title="1">return</span>
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<pre class="file" id="file2" style="display: none">// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// writer.go
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Writer struct {
|
||||||
|
// fh *os.File
|
||||||
|
handleBase
|
||||||
|
writer *bufio.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWriter(fh *os.File) *Writer <span class="cov8" title="1">{
|
||||||
|
return &Writer{handleBase: handleBase{fh: fh}, writer: bufio.NewWriter(fh)}
|
||||||
|
}</span>
|
||||||
|
|
||||||
|
func CreateWriter(filePath string) (w *Writer, err error) <span class="cov8" title="1">{
|
||||||
|
var fh *os.File
|
||||||
|
if fh, err = os.Create(filePath); err == nil </span><span class="cov8" title="1">{
|
||||||
|
w = NewWriter(fh)
|
||||||
|
}</span>
|
||||||
|
<span class="cov8" title="1">return</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
func AppendWriter(filePath string) (w *Writer, err error) <span class="cov8" title="1">{
|
||||||
|
var fh *os.File
|
||||||
|
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644); err == nil </span><span class="cov8" title="1">{
|
||||||
|
w = NewWriter(fh)
|
||||||
|
}</span>
|
||||||
|
<span class="cov8" title="1">return</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) TypeName() string <span class="cov8" title="1">{
|
||||||
|
return "fileWriter"
|
||||||
|
}</span>
|
||||||
|
|
||||||
|
func (w *Writer) String() string <span class="cov8" title="1">{
|
||||||
|
return "writer"
|
||||||
|
}</span>
|
||||||
|
|
||||||
|
func (w *Writer) Valid() bool <span class="cov8" title="1">{
|
||||||
|
return w.handleBase.Valid() && w.writer != nil
|
||||||
|
}</span>
|
||||||
|
|
||||||
|
func (w *Writer) Close() (err error) <span class="cov8" title="1">{
|
||||||
|
var err1 error
|
||||||
|
if w.writer != nil </span><span class="cov8" title="1">{
|
||||||
|
err1 = w.Flush()
|
||||||
|
w.writer = nil
|
||||||
|
}</span>
|
||||||
|
<span class="cov8" title="1">if err = w.handleBase.Close(); err == nil </span><span class="cov8" title="1">{
|
||||||
|
err = err1
|
||||||
|
}</span>
|
||||||
|
<span class="cov8" title="1">return</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Flush() (err error) <span class="cov8" title="1">{
|
||||||
|
if w.writer != nil </span><span class="cov8" title="1">{
|
||||||
|
err = w.writer.Flush()
|
||||||
|
}</span>
|
||||||
|
<span class="cov8" title="1">return</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Write(args ...any) (n int, err error) <span class="cov8" title="1">{
|
||||||
|
if w.writer != nil </span><span class="cov8" title="1">{
|
||||||
|
n, err = fmt.Fprint(w.writer, args...)
|
||||||
|
}</span>
|
||||||
|
<span class="cov8" title="1">return</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Writef(format string, args ...any) (n int, err error) <span class="cov8" title="1">{
|
||||||
|
if w.writer != nil </span><span class="cov8" title="1">{
|
||||||
|
n, err = fmt.Fprintf(w.writer, format, args...)
|
||||||
|
}</span>
|
||||||
|
<span class="cov8" title="1">return</span>
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
var files = document.getElementById('files');
|
||||||
|
var visible;
|
||||||
|
files.addEventListener('change', onChange, false);
|
||||||
|
function select(part) {
|
||||||
|
if (visible)
|
||||||
|
visible.style.display = 'none';
|
||||||
|
visible = document.getElementById(part);
|
||||||
|
if (!visible)
|
||||||
|
return;
|
||||||
|
files.value = part;
|
||||||
|
visible.style.display = 'block';
|
||||||
|
location.hash = part;
|
||||||
|
}
|
||||||
|
function onChange() {
|
||||||
|
select(files.value);
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
}
|
||||||
|
if (location.hash != "") {
|
||||||
|
select(location.hash.substr(1));
|
||||||
|
}
|
||||||
|
if (!visible) {
|
||||||
|
select("file0");
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// file.go
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handle interface {
|
||||||
|
kern.Typer
|
||||||
|
GetFile() *os.File
|
||||||
|
GetName() string
|
||||||
|
Valid() bool
|
||||||
|
Close() error
|
||||||
|
}
|
||||||
|
|
||||||
|
type handleBase struct {
|
||||||
|
fh *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handleBase) GetFile() *os.File {
|
||||||
|
return h.fh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handleBase) GetName() (name string) {
|
||||||
|
if h.fh != nil {
|
||||||
|
name = h.fh.Name()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handleBase) Valid() bool {
|
||||||
|
return h.fh != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *handleBase) Close() (err error) {
|
||||||
|
if h.fh != nil {
|
||||||
|
err = h.fh.Close()
|
||||||
|
h.fh = nil
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// reader.go
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Reader struct {
|
||||||
|
// fh *os.File
|
||||||
|
handleBase
|
||||||
|
reader *bufio.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReader(fh *os.File) *Reader {
|
||||||
|
return &Reader{handleBase: handleBase{fh: fh}, reader: bufio.NewReader(fh)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenReader(filePath string) (r *Reader, err error) {
|
||||||
|
var fh *os.File
|
||||||
|
if fh, err = os.Open(filePath); err == nil {
|
||||||
|
r = NewReader(fh)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Reader) TypeName() string {
|
||||||
|
return "fileReader"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Reader) String() string {
|
||||||
|
return "reader"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Reader) Valid() bool {
|
||||||
|
return h.handleBase.Valid() && h.reader != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Reader) Close() (err error) {
|
||||||
|
w.reader = nil
|
||||||
|
err = w.handleBase.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Reader) ReadByte() (b byte, err error) {
|
||||||
|
if h.reader != nil {
|
||||||
|
b, err = h.reader.ReadByte()
|
||||||
|
} else {
|
||||||
|
err = io.ErrClosedPipe
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Reader) ReadAll() (p []byte, err error) {
|
||||||
|
if h.reader != nil {
|
||||||
|
p, err = io.ReadAll(h.reader)
|
||||||
|
} else {
|
||||||
|
err = io.ErrClosedPipe
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Reader) ReadString(delim byte) (s string, err error) {
|
||||||
|
if h.reader != nil {
|
||||||
|
s, err = h.reader.ReadString(delim)
|
||||||
|
} else {
|
||||||
|
err = io.ErrClosedPipe
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Reader) Reset() (err error) {
|
||||||
|
if h.fh != nil {
|
||||||
|
if _, err = h.fh.Seek(0, 0); err == nil {
|
||||||
|
h.reader.Reset(h.fh)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// reader.go
|
||||||
|
package file
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestOpenReader(t *testing.T) {
|
||||||
|
r, err := OpenReader("t_reader_test.go")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("OpenReader failed: %v", err)
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
if !r.Valid() {
|
||||||
|
t.Fatal("Reader should be valid after opening")
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.TypeName() != "fileReader" {
|
||||||
|
t.Fatalf("Expected TypeName 'fileReader', got '%s'", r.TypeName())
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.String() != "reader" {
|
||||||
|
t.Fatalf("Expected String 'reader', got '%s'", r.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetName may return either "t_reader_test.go" or "./t_reader_test.go" depending on the environment
|
||||||
|
name := r.GetName()
|
||||||
|
if (name != "t_reader_test.go") && (name != "./t_reader_test.go") {
|
||||||
|
t.Fatalf("Expected GetName 't_reader_test.go' or './t_reader_test.go', got '%s'", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test reading a byte
|
||||||
|
b, err := r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ReadByte failed: %v", err)
|
||||||
|
}
|
||||||
|
if b == 0 {
|
||||||
|
t.Fatal("ReadByte should not return zero byte")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = r.Reset()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Reset failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s, err := r.ReadString('\n'); err != nil {
|
||||||
|
t.Fatalf("ReadString failed: %v", err)
|
||||||
|
} else {
|
||||||
|
t.Logf("ReadString: %s", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test reading all content
|
||||||
|
content, err := r.ReadAll()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ReadAll failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(content) == 0 {
|
||||||
|
t.Fatal("ReadAll should return non-empty content")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// writer.go
|
||||||
|
package file
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestCreateWriter(t *testing.T) {
|
||||||
|
w, err := CreateWriter("/tmp/test_writer.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("CreateWriter failed: %v", err)
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
if !w.Valid() {
|
||||||
|
t.Fatal("Writer should be valid after creation")
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.TypeName() != "fileWriter" {
|
||||||
|
t.Fatalf("Expected TypeName 'fileWriter', got '%s'", w.TypeName())
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.String() != "writer" {
|
||||||
|
t.Fatalf("Expected String 'writer', got '%s'", w.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
name := w.GetName()
|
||||||
|
if name != "/tmp/test_writer.txt" {
|
||||||
|
t.Fatalf("Expected GetName '/tmp/test_writer.txt', got '%s'", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n, err := w.Write("Hello, World!\n"); err != nil {
|
||||||
|
t.Fatalf("Write failed: %v", err)
|
||||||
|
} else if n != len("Hello, World!\n") {
|
||||||
|
t.Fatalf("Expected to write %d bytes, wrote %d", len("Hello, World!\n"), n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n, err := w.Writef("This is a %s.\n", "test"); err != nil {
|
||||||
|
t.Fatalf("Writef failed: %v", err)
|
||||||
|
} else if n != len("This is a test.\n") {
|
||||||
|
t.Fatalf("Expected to write %d bytes, wrote %d", len("This is a test.\n"), n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAppendWriter(t *testing.T) {
|
||||||
|
w, err := AppendWriter("/tmp/test_writer.txt")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("AppendWriter failed: %v", err)
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
if !w.Valid() {
|
||||||
|
t.Fatal("Writer should be valid after opening for append")
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.TypeName() != "fileWriter" {
|
||||||
|
t.Fatalf("Expected TypeName 'fileWriter', got '%s'", w.TypeName())
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.String() != "writer" {
|
||||||
|
t.Fatalf("Expected String 'writer', got '%s'", w.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
name := w.GetName()
|
||||||
|
if name != "/tmp/test_writer.txt" {
|
||||||
|
t.Fatalf("Expected GetName '/tmp/test_writer.txt', got '%s'", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// writer.go
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Writer struct {
|
||||||
|
// fh *os.File
|
||||||
|
handleBase
|
||||||
|
writer *bufio.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWriter(fh *os.File) *Writer {
|
||||||
|
return &Writer{handleBase: handleBase{fh: fh}, writer: bufio.NewWriter(fh)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateWriter(filePath string) (w *Writer, err error) {
|
||||||
|
var fh *os.File
|
||||||
|
if fh, err = os.Create(filePath); err == nil {
|
||||||
|
w = NewWriter(fh)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func AppendWriter(filePath string) (w *Writer, err error) {
|
||||||
|
var fh *os.File
|
||||||
|
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644); err == nil {
|
||||||
|
w = NewWriter(fh)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) TypeName() string {
|
||||||
|
return "fileWriter"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) String() string {
|
||||||
|
return "writer"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Valid() bool {
|
||||||
|
return w.handleBase.Valid() && w.writer != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Close() (err error) {
|
||||||
|
var err1 error
|
||||||
|
if w.writer != nil {
|
||||||
|
err1 = w.Flush()
|
||||||
|
w.writer = nil
|
||||||
|
}
|
||||||
|
if err = w.handleBase.Close(); err == nil {
|
||||||
|
err = err1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Flush() (err error) {
|
||||||
|
if w.writer != nil {
|
||||||
|
err = w.writer.Flush()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Write(args ...any) (n int, err error) {
|
||||||
|
if w.writer != nil {
|
||||||
|
n, err = fmt.Fprint(w.writer, args...)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Writer) Writef(format string, args ...any) (n int, err error) {
|
||||||
|
if w.writer != nil {
|
||||||
|
n, err = fmt.Fprintf(w.writer, format, args...)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
+14
-15
@@ -9,6 +9,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/boolean"
|
||||||
)
|
)
|
||||||
|
|
||||||
//var globalCtx *SimpleStore
|
//var globalCtx *SimpleStore
|
||||||
@@ -42,12 +43,16 @@ func ImportInContextByGlobPattern(ctx kern.ExprContext, pattern string) (count i
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fixCtrlVar(name string) string {
|
||||||
|
if !strings.HasPrefix(name, "_") {
|
||||||
|
name = "_" + name
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValue any) {
|
func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValue any) {
|
||||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||||
if !strings.HasPrefix(name, "_") {
|
name = fixCtrlVar(name)
|
||||||
name = "_" + name
|
|
||||||
}
|
|
||||||
|
|
||||||
currentValue, _ = globalCtx.GetVar(name)
|
currentValue, _ = globalCtx.GetVar(name)
|
||||||
globalCtx.SetVar(name, newValue)
|
globalCtx.SetVar(name, newValue)
|
||||||
}
|
}
|
||||||
@@ -56,19 +61,15 @@ func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValu
|
|||||||
|
|
||||||
func GlobalCtrlGet(ctx kern.ExprContext, name string) (currentValue any) {
|
func GlobalCtrlGet(ctx kern.ExprContext, name string) (currentValue any) {
|
||||||
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
|
||||||
if !strings.HasPrefix(name, "_") {
|
name = fixCtrlVar(name)
|
||||||
name = "_" + name
|
|
||||||
}
|
|
||||||
currentValue, _ = globalCtx.GetVar(name)
|
currentValue, _ = globalCtx.GetVar(name)
|
||||||
}
|
}
|
||||||
return currentValue
|
return currentValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
||||||
if !strings.HasPrefix(name, "_") {
|
name = fixCtrlVar(name)
|
||||||
name = "_" + name
|
if v, exists := ctx.GetVar(name); exists && boolean.IsBool(v) {
|
||||||
}
|
|
||||||
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
|
|
||||||
currentStatus, _ = v.(bool)
|
currentStatus, _ = v.(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,10 +78,8 @@ func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CtrlDisable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
func CtrlDisable(ctx kern.ExprContext, name string) (currentStatus bool) {
|
||||||
if !strings.HasPrefix(name, "_") {
|
name = fixCtrlVar(name)
|
||||||
name = "_" + name
|
if v, exists := ctx.GetVar(name); exists && boolean.IsBool(v) {
|
||||||
}
|
|
||||||
if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
|
|
||||||
currentStatus, _ = v.(bool)
|
currentStatus, _ = v.(bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
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 h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
|
||||||
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||||
|
|||||||
+4
-2
@@ -12,6 +12,8 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/float"
|
||||||
"git.portale-stac.it/go-pkg/expr/util"
|
"git.portale-stac.it/go-pkg/expr/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -50,9 +52,9 @@ func EvalStringV(source string, args []Arg) (result any, err error) {
|
|||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("invalid function specification: %q", arg.Name)
|
err = fmt.Errorf("invalid function specification: %q", arg.Name)
|
||||||
}
|
}
|
||||||
} else if integer, ok := kern.AnyInteger(arg.Value); ok {
|
} else if integer, ok := types.AnyInteger(arg.Value); ok {
|
||||||
ctx.SetVar(arg.Name, integer)
|
ctx.SetVar(arg.Name, integer)
|
||||||
} else if float, ok := kern.AnyFloat(arg.Value); ok {
|
} else if float, ok := float.AnyFloat(arg.Value); ok {
|
||||||
ctx.SetVar(arg.Name, float)
|
ctx.SetVar(arg.Name, float)
|
||||||
} else if _, ok := arg.Value.(string); ok {
|
} else if _, ok := arg.Value.(string); ok {
|
||||||
ctx.SetVar(arg.Name, arg.Value)
|
ctx.SetVar(arg.Name, arg.Value)
|
||||||
|
|||||||
+9
-7
@@ -13,6 +13,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/str"
|
||||||
"git.portale-stac.it/go-pkg/expr/util"
|
"git.portale-stac.it/go-pkg/expr/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) {
|
func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) {
|
||||||
if !(kern.IsString(paramValue) /*|| isList(paramValue)*/) {
|
if !(str.IsString(paramValue) /*|| isArray(paramValue)*/) {
|
||||||
err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, kern.TypeName(paramValue))
|
err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, kern.TypeName(paramValue))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -89,16 +90,17 @@ func searchAmongPath(filename string, dirList []string) (filePath string) {
|
|||||||
if dir, err = util.ExpandPath(dir); err != nil {
|
if dir, err = util.ExpandPath(dir); err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if fullPath := path.Join(dir, filename); isFile(fullPath) {
|
fullPath := path.Join(dir, filename)
|
||||||
|
if isFile(fullPath) {
|
||||||
filePath = fullPath
|
filePath = fullPath
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
subdir := strings.TrimSuffix(filename, suffix)
|
// subdir := strings.TrimSuffix(filename, suffix)
|
||||||
if fullPath := path.Join(dir, subdir, filename); isFile(fullPath) {
|
// if fullPath := path.Join(dir, subdir, filename); isFile(fullPath) {
|
||||||
filePath = fullPath
|
// filePath = fullPath
|
||||||
break
|
// break
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-3
@@ -10,6 +10,8 @@ import (
|
|||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/interval"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IntIterator struct {
|
type IntIterator struct {
|
||||||
@@ -20,6 +22,20 @@ type IntIterator struct {
|
|||||||
step int64
|
step int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewIntIteratorFromInterval(p *interval.IntervalType) (it *IntIterator, err error) {
|
||||||
|
b := p.Begin()
|
||||||
|
e := p.End()
|
||||||
|
s := p.Step()
|
||||||
|
if b > e {
|
||||||
|
s = -s
|
||||||
|
}
|
||||||
|
return NewIntIteratorA(b, e, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIntIteratorA(args ...any) (it *IntIterator, err error) {
|
||||||
|
return NewIntIterator(args)
|
||||||
|
}
|
||||||
|
|
||||||
func NewIntIterator(args []any) (it *IntIterator, err error) {
|
func NewIntIterator(args []any) (it *IntIterator, err error) {
|
||||||
var argc int = 0
|
var argc int = 0
|
||||||
if args != nil {
|
if args != nil {
|
||||||
@@ -27,16 +43,16 @@ func NewIntIterator(args []any) (it *IntIterator, err error) {
|
|||||||
}
|
}
|
||||||
it = &IntIterator{count: 0, index: -1, start: 0, stop: 0, step: 1}
|
it = &IntIterator{count: 0, index: -1, start: 0, stop: 0, step: 1}
|
||||||
if argc >= 1 {
|
if argc >= 1 {
|
||||||
if it.stop, err = kern.ToGoInt64(args[0], "start index"); err != nil {
|
if it.stop, err = types.ToGoInt64(args[0], "start index"); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if argc >= 2 {
|
if argc >= 2 {
|
||||||
it.start = it.stop
|
it.start = it.stop
|
||||||
if it.stop, err = kern.ToGoInt64(args[1], "stop index"); err != nil {
|
if it.stop, err = types.ToGoInt64(args[1], "stop index"); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if argc >= 3 {
|
if argc >= 3 {
|
||||||
if it.step, err = kern.ToGoInt64(args[2], "step"); err != nil {
|
if it.step, err = types.ToGoInt64(args[2], "step"); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if it.start > it.stop {
|
} else if it.start > it.stop {
|
||||||
|
|||||||
+22
-8
@@ -9,17 +9,31 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/list"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func NewFormalIterator(value any) (it kern.Iterator) {
|
||||||
|
if value == nil {
|
||||||
|
it = NewArrayIterator([]any{})
|
||||||
|
} else {
|
||||||
|
it = NewArrayIterator([]any{value})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func NewIterator(ctx kern.ExprContext, value any, ops []*scan.Term) (it kern.Iterator, err error) {
|
func NewIterator(ctx kern.ExprContext, value any, ops []*scan.Term) (it kern.Iterator, err error) {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return NewArrayIterator([]any{}), nil
|
return NewArrayIterator([]any{}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v := value.(type) {
|
switch v := value.(type) {
|
||||||
case *kern.ListType:
|
case *array.ArrayType:
|
||||||
it = NewListIterator(v, nil)
|
it = NewListIterator(v, nil)
|
||||||
case *kern.DictType:
|
case *list.LinkedList:
|
||||||
|
it = NewLinkedListIterator(v, nil)
|
||||||
|
case *dict.DictType:
|
||||||
it, err = NewDictIterator(v, nil)
|
it, err = NewDictIterator(v, nil)
|
||||||
case []any:
|
case []any:
|
||||||
it = NewArrayIterator(v)
|
it = NewArrayIterator(v)
|
||||||
@@ -36,9 +50,9 @@ func HasIterStandardOperations(name string) bool {
|
|||||||
return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
|
return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func HasIterOperations(name string, ops ...string) bool {
|
// func HasIterOperations(name string, ops ...string) bool {
|
||||||
return slices.Contains([]string{
|
// return slices.Contains([]string{
|
||||||
kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName,
|
// kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName,
|
||||||
}, name) ||
|
// }, name) ||
|
||||||
slices.Contains(ops, name)
|
// slices.Contains(ops, name)
|
||||||
}
|
// }
|
||||||
|
|||||||
+10
-2
@@ -12,6 +12,8 @@ import (
|
|||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const iterIterType = "IterIter"
|
||||||
|
|
||||||
type IterIter struct {
|
type IterIter struct {
|
||||||
it kern.Iterator
|
it kern.Iterator
|
||||||
count int64
|
count int64
|
||||||
@@ -22,7 +24,13 @@ type IterIter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewIterIter(it kern.Iterator, ctx kern.ExprContext, exprs []*scan.Term) (iter kern.Iterator, err error) {
|
func NewIterIter(it kern.Iterator, ctx kern.ExprContext, exprs []*scan.Term) (iter kern.Iterator, err error) {
|
||||||
iter = &IterIter{it: it, count: 0, index: -1, ctx: ctx, exprList: exprs, current: nil}
|
if ctx == nil {
|
||||||
|
err = fmt.Errorf("context is required for %s", iterIterType)
|
||||||
|
} else if it == nil {
|
||||||
|
err = fmt.Errorf("source iterator is required for %s", iterIterType)
|
||||||
|
} else {
|
||||||
|
iter = &IterIter{it: it, count: 0, index: -1, ctx: ctx, exprList: exprs, current: nil}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +39,7 @@ func (it *IterIter) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (it *IterIter) TypeName() string {
|
func (it *IterIter) TypeName() string {
|
||||||
return "IterIter"
|
return iterIterType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *IterIter) HasOperation(name string) bool {
|
func (it *IterIter) HasOperation(name string) bool {
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// cloner.go
|
||||||
|
package kern
|
||||||
|
|
||||||
|
type Cloner interface {
|
||||||
|
Clone() Cloner
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsCloner(v any) (ok bool) {
|
||||||
|
_, ok = v.(Cloner)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Clone(a any) (clone any) {
|
||||||
|
if IsCloner(a) {
|
||||||
|
clone = a.(Cloner).Clone()
|
||||||
|
} else {
|
||||||
|
clone = a
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -84,6 +84,10 @@ func ErrUnknownVar(funcName, varName string) error {
|
|||||||
return fmt.Errorf("%s(): unknown variable %q", funcName, varName)
|
return fmt.Errorf("%s(): unknown variable %q", funcName, varName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ErrFuncInvalidArg(funcName, details string) error {
|
||||||
|
return fmt.Errorf("%s(): invalid argument -- %s", funcName, details)
|
||||||
|
}
|
||||||
|
|
||||||
// --- Operator errors
|
// --- Operator errors
|
||||||
|
|
||||||
func ErrLeftOperandMustBeVariable(leftTerm, opTerm Term) error {
|
func ErrLeftOperandMustBeVariable(leftTerm, opTerm Term) error {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package kern
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
TypeAny = "any"
|
TypeAny = "any"
|
||||||
|
TypeArray = "array"
|
||||||
TypeNil = "nil"
|
TypeNil = "nil"
|
||||||
TypeBoolean = "boolean"
|
TypeBoolean = "boolean"
|
||||||
TypeFloat = "float"
|
TypeFloat = "float"
|
||||||
@@ -15,9 +16,10 @@ const (
|
|||||||
TypeItem = "item"
|
TypeItem = "item"
|
||||||
TypeIterator = "iterator"
|
TypeIterator = "iterator"
|
||||||
TypeNumber = "number"
|
TypeNumber = "number"
|
||||||
TypePair = "pair"
|
TypeIntPair = "int-pair"
|
||||||
TypeString = "string"
|
TypeString = "string"
|
||||||
TypeDict = "dict"
|
TypeDict = "dict"
|
||||||
TypeListOf = "list-of-"
|
TypeListOf = "list-of-"
|
||||||
TypeListOfStrings = "list-of-strings"
|
TypeListOfStrings = "list-of-strings"
|
||||||
|
TypeLinkedList = "linked-list"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
package kern
|
|
||||||
|
|
||||||
import "reflect"
|
|
||||||
|
|
||||||
func Equal(value1, value2 any) (equal bool) {
|
|
||||||
if value1 == nil && value2 == nil {
|
|
||||||
equal = true
|
|
||||||
} else if value1 == nil || value2 == nil {
|
|
||||||
equal = false
|
|
||||||
} else if IsBool(value1) && IsBool(value2) {
|
|
||||||
equal = value1.(bool) == value2.(bool)
|
|
||||||
} else if IsList(value1) && IsList(value2) {
|
|
||||||
ls1 := value1.(*ListType)
|
|
||||||
ls2 := value2.(*ListType)
|
|
||||||
equal = ls1.Equals(*ls2)
|
|
||||||
} else if IsDict(value1) && IsDict(value2) {
|
|
||||||
d1 := value1.(*DictType)
|
|
||||||
d2 := value2.(*DictType)
|
|
||||||
equal = d1.Equals(*d2)
|
|
||||||
} else if IsInteger(value1) && IsInteger(value2) {
|
|
||||||
equal = value1.(int64) == value2.(int64)
|
|
||||||
} else if IsString(value1) && IsString(value2) {
|
|
||||||
equal = value1.(string) == value2.(string)
|
|
||||||
} else if IsFloat(value1) && IsFloat(value2) {
|
|
||||||
equal = value1.(float64) == value2.(float64)
|
|
||||||
} else if IsNumOrFract(value1) && IsNumOrFract(value2) {
|
|
||||||
if eq, err := CmpAnyFract(value1, value2); err == nil {
|
|
||||||
equal = eq == 0
|
|
||||||
}
|
|
||||||
} else if !reflect.DeepEqual(value1, value2) {
|
|
||||||
equal = false
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// consts.go
|
||||||
|
package kern
|
||||||
|
|
||||||
|
const MaxUint64Allowed = uint64(9_223_372_036_854_775_807)
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// equaler.go
|
||||||
|
package kern
|
||||||
|
|
||||||
|
type Equaler interface {
|
||||||
|
EqualTo(other Equaler) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsEqualer(v any) (ok bool) {
|
||||||
|
_, ok = v.(Equaler)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Equal(a, b any) (equal bool) {
|
||||||
|
if IsEqualer(a) && IsEqualer(b) {
|
||||||
|
equal = a.(Equaler).EqualTo(b.(Equaler))
|
||||||
|
} else {
|
||||||
|
equal = a == b
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -28,4 +28,7 @@ type ExprContext interface {
|
|||||||
Call(name string, args map[string]any) (result any, err error)
|
Call(name string, args map[string]any) (result any, err error)
|
||||||
RegisterFuncInfo(info ExprFunc)
|
RegisterFuncInfo(info ExprFunc)
|
||||||
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error)
|
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error)
|
||||||
|
|
||||||
|
ToDict() (dict any) // must be a *DictType
|
||||||
|
ToString(opt FmtOpt) string
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-22
@@ -4,7 +4,10 @@
|
|||||||
// formatter.go
|
// formatter.go
|
||||||
package kern
|
package kern
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
type FmtOpt uint32 // lower 16 bits hold a bit-mask, higher 16 bits hold an indentation number
|
type FmtOpt uint32 // lower 16 bits hold a bit-mask, higher 16 bits hold an indentation number
|
||||||
|
|
||||||
@@ -18,6 +21,10 @@ const (
|
|||||||
Base16
|
Base16
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Formatter interface {
|
||||||
|
ToString(options FmtOpt) string
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TruncateEllipsis = "(...)"
|
TruncateEllipsis = "(...)"
|
||||||
MinTruncateSize = 10
|
MinTruncateSize = 10
|
||||||
@@ -42,8 +49,16 @@ func GetFormatIndent(opt FmtOpt) int {
|
|||||||
return int(opt >> 16)
|
return int(opt >> 16)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Formatter interface {
|
func Format(sb *strings.Builder, item any, opt FmtOpt) {
|
||||||
ToString(options FmtOpt) string
|
if s, ok := item.(string); ok {
|
||||||
|
sb.WriteByte('"')
|
||||||
|
sb.WriteString(s)
|
||||||
|
sb.WriteByte('"')
|
||||||
|
} else if formatter, ok := item.(Formatter); ok {
|
||||||
|
sb.WriteString(formatter.ToString(opt))
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(sb, "%v", item)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFormatted(v any, opt FmtOpt) (text string) {
|
func GetFormatted(v any, opt FmtOpt) (text string) {
|
||||||
@@ -58,22 +73,3 @@ func GetFormatted(v any, opt FmtOpt) (text string) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type Typer interface {
|
|
||||||
TypeName() string
|
|
||||||
}
|
|
||||||
|
|
||||||
func TypeName(v any) (name string) {
|
|
||||||
if v == nil {
|
|
||||||
name = "nil"
|
|
||||||
} else if typer, ok := v.(Typer); ok {
|
|
||||||
name = typer.TypeName()
|
|
||||||
} else if IsInteger(v) {
|
|
||||||
name = "integer"
|
|
||||||
} else if IsFloat(v) {
|
|
||||||
name = "float"
|
|
||||||
} else {
|
|
||||||
name = fmt.Sprintf("%T", v)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package kern
|
|||||||
import (
|
import (
|
||||||
// "errors"
|
// "errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"slices"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Operator names
|
// Operator names
|
||||||
@@ -50,3 +51,45 @@ func IsIterator(v any) (ok bool) {
|
|||||||
_, ok = v.(Iterator)
|
_, ok = v.(Iterator)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IteratorBase struct {
|
||||||
|
ItemIndex int64
|
||||||
|
ItemCount int64
|
||||||
|
current any
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *IteratorBase) Current() (item any, err error) {
|
||||||
|
return it.current, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *IteratorBase) Index() int64 {
|
||||||
|
return it.ItemIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *IteratorBase) Count() int64 {
|
||||||
|
return it.ItemCount
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *IteratorBase) HasOperation(name string) bool {
|
||||||
|
return slices.Contains([]string{NextName, IndexName, CountName, CurrentName}, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *IteratorBase) Clean() (err error) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *IteratorBase) Reset() (err error) {
|
||||||
|
it.ItemIndex = -1
|
||||||
|
it.ItemCount = 0
|
||||||
|
it.current = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *IteratorBase) Increment() {
|
||||||
|
it.ItemIndex++
|
||||||
|
it.ItemCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *IteratorBase) SetCurrent(v any) {
|
||||||
|
it.current = v
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,202 +0,0 @@
|
|||||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// list-type.go
|
|
||||||
package kern
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ListType []any
|
|
||||||
|
|
||||||
func IsList(v any) (ok bool) {
|
|
||||||
_, ok = v.(*ListType)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewListA(listAny ...any) (list *ListType) {
|
|
||||||
if listAny == nil {
|
|
||||||
listAny = []any{}
|
|
||||||
}
|
|
||||||
return NewList(listAny)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewList(listAny []any) (list *ListType) {
|
|
||||||
if listAny != nil {
|
|
||||||
ls := make(ListType, len(listAny))
|
|
||||||
copy(ls, listAny)
|
|
||||||
list = &ls
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func MakeList(length, capacity int) (list *ListType) {
|
|
||||||
if capacity < length {
|
|
||||||
capacity = length
|
|
||||||
}
|
|
||||||
ls := make(ListType, length, capacity)
|
|
||||||
list = &ls
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListFromStrings(stringList []string) (list *ListType) {
|
|
||||||
list = MakeList(len(stringList), 0)
|
|
||||||
for i, s := range stringList {
|
|
||||||
(*list)[i] = s
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) ToString(opt FmtOpt) (s string) {
|
|
||||||
indent := GetFormatIndent(opt)
|
|
||||||
flags := GetFormatFlags(opt)
|
|
||||||
|
|
||||||
var sb strings.Builder
|
|
||||||
sb.WriteByte('[')
|
|
||||||
if len(*dict) > 0 {
|
|
||||||
innerOpt := MakeFormatOptions(flags, indent+1)
|
|
||||||
nest := strings.Repeat(" ", indent+1)
|
|
||||||
|
|
||||||
if flags&MultiLine != 0 {
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
sb.WriteString(nest)
|
|
||||||
}
|
|
||||||
for i, item := range []any(*dict) {
|
|
||||||
if i > 0 {
|
|
||||||
if flags&MultiLine != 0 {
|
|
||||||
sb.WriteString(",\n")
|
|
||||||
sb.WriteString(nest)
|
|
||||||
} else {
|
|
||||||
sb.WriteString(", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if s, ok := item.(string); ok {
|
|
||||||
sb.WriteByte('"')
|
|
||||||
sb.WriteString(s)
|
|
||||||
sb.WriteByte('"')
|
|
||||||
} else if formatter, ok := item.(Formatter); ok {
|
|
||||||
sb.WriteString(formatter.ToString(innerOpt))
|
|
||||||
} else {
|
|
||||||
sb.WriteString(fmt.Sprintf("%v", item))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if flags&MultiLine != 0 {
|
|
||||||
sb.WriteByte('\n')
|
|
||||||
sb.WriteString(strings.Repeat(" ", indent))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sb.WriteByte(']')
|
|
||||||
s = sb.String()
|
|
||||||
if flags&Truncate != 0 && len(s) > TruncateSize {
|
|
||||||
s = TruncateString(s)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) String() string {
|
|
||||||
return dict.ToString(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) TypeName() string {
|
|
||||||
return "list"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) Contains(t *ListType) (answer bool) {
|
|
||||||
if len(*dict) >= len(*t) {
|
|
||||||
answer = true
|
|
||||||
for _, item := range *t {
|
|
||||||
if answer = dict.IndexDeepSameCmp(item) >= 0; !answer {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
|
|
||||||
if ls2 != nil && len(*ls1) == len(ls2) {
|
|
||||||
answer = true
|
|
||||||
for index, i1 := range *ls1 {
|
|
||||||
// if !reflect.DeepEqual(i1, ls2[index]) {
|
|
||||||
// answer = false
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
if !Equal(i1, ls2[index]) {
|
|
||||||
answer = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) IndexDeepSameCmp(target any) (index int) {
|
|
||||||
var eq bool
|
|
||||||
var err error
|
|
||||||
index = -1
|
|
||||||
for i, item := range *dict {
|
|
||||||
if eq, err = deepSame(item, target, SameContent); err != nil {
|
|
||||||
break
|
|
||||||
} else if eq {
|
|
||||||
index = i
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func SameContent(a, b any) (same bool, err error) {
|
|
||||||
la, _ := a.(*ListType)
|
|
||||||
lb, _ := b.(*ListType)
|
|
||||||
if len(*la) == len(*lb) {
|
|
||||||
same = true
|
|
||||||
for _, item := range *la {
|
|
||||||
if pos := lb.IndexDeepSameCmp(item); pos < 0 {
|
|
||||||
same = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func deepSame(a, b any, deepCmp DeepFuncTemplate) (eq bool, err error) {
|
|
||||||
if IsNumOrFract(a) && IsNumOrFract(b) {
|
|
||||||
if IsNumber(a) && IsNumber(b) {
|
|
||||||
if IsInteger(a) && IsInteger(b) {
|
|
||||||
li, _ := a.(int64)
|
|
||||||
ri, _ := b.(int64)
|
|
||||||
eq = li == ri
|
|
||||||
} else {
|
|
||||||
eq = NumAsFloat(a) == NumAsFloat(b)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var cmp int
|
|
||||||
if cmp, err = CmpAnyFract(a, b); err == nil {
|
|
||||||
eq = cmp == 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if deepCmp != nil && IsList(a) && IsList(b) {
|
|
||||||
eq, err = deepCmp(a, b)
|
|
||||||
} else {
|
|
||||||
eq = reflect.DeepEqual(a, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) SetItem(index int64, value any) (err error) {
|
|
||||||
if index >= 0 && index < int64(len(*dict)) {
|
|
||||||
(*dict)[index] = value
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*dict)-1)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dict *ListType) AppendItem(value any) {
|
|
||||||
*dict = append(*dict, value)
|
|
||||||
}
|
|
||||||
+12
-1
@@ -6,6 +6,8 @@ package kern
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Term interface {
|
type Term interface {
|
||||||
@@ -15,7 +17,16 @@ type Term interface {
|
|||||||
GetChildCount() (count int)
|
GetChildCount() (count int)
|
||||||
GetChild(index int) Term
|
GetChild(index int) Term
|
||||||
GetChildSource(index int) string
|
GetChildSource(index int) string
|
||||||
Compute(ctx ExprContext) (result any, err error)
|
GetLeftChild() (c Term)
|
||||||
|
GetRightChild() (c Term)
|
||||||
|
Symbol() (symbol sym.Symbol)
|
||||||
|
IsSymbol(symbol sym.Symbol) bool
|
||||||
|
IsOneOf(symbols ...sym.Symbol) bool
|
||||||
IsAssign() bool
|
IsAssign() bool
|
||||||
|
IsVar() bool
|
||||||
|
SetSymbol(symbol sym.Symbol)
|
||||||
|
Compute(ctx ExprContext) (result any, err error)
|
||||||
|
EvalInfix(ctx ExprContext) (leftValue, rightValue any, err error)
|
||||||
Errorf(template string, args ...any) (err error)
|
Errorf(template string, args ...any) (err error)
|
||||||
|
ErrIncompatibleTypes(leftValue, rightValue any) error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// typer.go
|
||||||
|
package kern
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type Typer interface {
|
||||||
|
TypeName() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TypeName(v any) (name string) {
|
||||||
|
if v == nil {
|
||||||
|
name = "nil"
|
||||||
|
} else if typer, ok := v.(Typer); ok {
|
||||||
|
name = typer.TypeName()
|
||||||
|
} else if _, ok := v.(int64); ok {
|
||||||
|
name = "integer"
|
||||||
|
} else if _, ok := v.(float64); ok {
|
||||||
|
name = "float"
|
||||||
|
} else {
|
||||||
|
name = fmt.Sprintf("%T", v)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// util.go
|
||||||
|
package kern
|
||||||
|
|
||||||
|
func FixAnyNumber(v any) (fixed any) {
|
||||||
|
switch unboxed := v.(type) {
|
||||||
|
case int:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case int8:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case int16:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case int32:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case uint:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case uint8:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case uint16:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case uint32:
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
case uint64:
|
||||||
|
if unboxed <= MaxUint64Allowed {
|
||||||
|
fixed = int64(unboxed)
|
||||||
|
} else {
|
||||||
|
fixed = float64(unboxed)
|
||||||
|
}
|
||||||
|
case float32:
|
||||||
|
fixed = float64(unboxed)
|
||||||
|
default:
|
||||||
|
fixed = v
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// list-iterator.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LinkedListIterator struct {
|
||||||
|
a *list.LinkedList
|
||||||
|
count int64
|
||||||
|
index int64
|
||||||
|
current *list.ListNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLinkedListIterator(list *list.LinkedList, args []any) (it *LinkedListIterator) {
|
||||||
|
it = &LinkedListIterator{a: list, count: 0, index: -1, current: list.FirstNode()}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) String() string {
|
||||||
|
var l = int64(0)
|
||||||
|
if it.a != nil {
|
||||||
|
l = int64(it.a.Len())
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("$([<#%d>])", l)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) TypeName() string {
|
||||||
|
return "LinkedListIterator"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) HasOperation(name string) bool {
|
||||||
|
yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName}, name)
|
||||||
|
return yes
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
||||||
|
switch name {
|
||||||
|
case kern.NextName:
|
||||||
|
v, err = it.Next()
|
||||||
|
case kern.ResetName:
|
||||||
|
err = it.Reset()
|
||||||
|
case kern.CleanName:
|
||||||
|
err = it.Clean()
|
||||||
|
case kern.IndexName:
|
||||||
|
v = int64(it.Index())
|
||||||
|
case kern.CurrentName:
|
||||||
|
v, err = it.Current()
|
||||||
|
case kern.CountName:
|
||||||
|
v = it.count
|
||||||
|
default:
|
||||||
|
err = kern.ErrNoOperation(name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) Current() (item any, err error) {
|
||||||
|
if it.current != nil {
|
||||||
|
item = it.current.Data()
|
||||||
|
} else {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) Next() (item any, err error) {
|
||||||
|
if it.current != nil {
|
||||||
|
item = it.current.Data()
|
||||||
|
it.current = it.current.Next()
|
||||||
|
it.index++
|
||||||
|
it.count++
|
||||||
|
} else {
|
||||||
|
err = io.EOF
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) Index() int64 {
|
||||||
|
return it.index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) Count() int64 {
|
||||||
|
return it.count
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) Reset() error {
|
||||||
|
it.current = it.a.FirstNode()
|
||||||
|
it.index = -1
|
||||||
|
it.count = 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *LinkedListIterator) Clean() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+16
-5
@@ -7,12 +7,16 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/str"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- dict term
|
// -------- dict term
|
||||||
func newDictTerm(args map[any]*scan.Term) *scan.Term {
|
func newDictTerm(args map[any]*scan.Term) *scan.Term {
|
||||||
return &scan.Term{
|
return &scan.Term{
|
||||||
Tk: *scan.NewValueToken(0, 0, scan.SymDict, "{}", args),
|
Tk: *scan.NewValueToken(0, 0, sym.SymDict, "{}", args),
|
||||||
Parent: nil,
|
Parent: nil,
|
||||||
Children: nil,
|
Children: nil,
|
||||||
Position: scan.PosLeaf,
|
Position: scan.PosLeaf,
|
||||||
@@ -23,14 +27,21 @@ func newDictTerm(args map[any]*scan.Term) *scan.Term {
|
|||||||
|
|
||||||
// -------- dict func
|
// -------- dict func
|
||||||
func evalDict(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
func evalDict(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
dict, _ := opTerm.Value().(map[any]*scan.Term)
|
dt, _ := opTerm.Value().(map[any]*scan.Term)
|
||||||
items := make(kern.DictType, len(dict))
|
items := make(dict.DictType, len(dt))
|
||||||
for key, tree := range dict {
|
for key, tree := range dt {
|
||||||
var param any
|
var param any
|
||||||
if param, err = tree.Compute(ctx); err != nil {
|
if param, err = tree.Compute(ctx); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
items[key] = param
|
var keyValue any
|
||||||
|
if keyValue, err = (key.(*scan.Term)).Compute(ctx); err == nil {
|
||||||
|
if types.IsInteger(keyValue) || str.IsString(keyValue) {
|
||||||
|
items[keyValue] = param
|
||||||
|
} else {
|
||||||
|
err = key.(*scan.Term).Errorf("dict key can be integer or string, got %s", kern.TypeName(keyValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
v = &items
|
v = &items
|
||||||
|
|||||||
+2
-1
@@ -9,11 +9,12 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- expr term
|
// -------- expr term
|
||||||
func newExprTerm(root *scan.Term) *scan.Term {
|
func newExprTerm(root *scan.Term) *scan.Term {
|
||||||
tk := scan.NewValueToken(root.Tk.Row(), root.Tk.Col(), scan.SymExpression, root.Source(), root)
|
tk := scan.NewValueToken(root.Tk.Row(), root.Tk.Col(), sym.SymExpression, root.Source(), root)
|
||||||
return &scan.Term{
|
return &scan.Term{
|
||||||
Tk: *tk,
|
Tk: *tk,
|
||||||
Parent: nil,
|
Parent: nil,
|
||||||
|
|||||||
+20
-8
@@ -10,12 +10,17 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/interval"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/list"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- iterator term
|
// -------- iterator term
|
||||||
|
|
||||||
func newIteratorTerm(tk *scan.Token, args []*scan.Term) *scan.Term {
|
func newIteratorTerm(tk *scan.Token, args []*scan.Term) *scan.Term {
|
||||||
tk.Sym = scan.SymIterator
|
tk.Sym = sym.SymIterator
|
||||||
return &scan.Term{
|
return &scan.Term{
|
||||||
Tk: *tk,
|
Tk: *tk,
|
||||||
Parent: nil,
|
Parent: nil,
|
||||||
@@ -52,7 +57,7 @@ func evalFirstChild(ctx kern.ExprContext, iteratorTerm *scan.Term) (value any, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getDataSourceDict(iteratorTerm *scan.Term, firstChildValue any) (ds map[string]kern.Functor, err error) {
|
func getDataSourceDict(iteratorTerm *scan.Term, firstChildValue any) (ds map[string]kern.Functor, err error) {
|
||||||
if dictAny, ok := firstChildValue.(*kern.DictType); ok {
|
if dictAny, ok := firstChildValue.(*dict.DictType); ok {
|
||||||
requiredFields := []string{kern.NextName}
|
requiredFields := []string{kern.NextName}
|
||||||
fieldsMask := 0b1
|
fieldsMask := 0b1
|
||||||
foundFields := 0
|
foundFields := 0
|
||||||
@@ -123,7 +128,7 @@ func evalIterator(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
v = dc
|
v = dc
|
||||||
} else {
|
} else {
|
||||||
if dictIt, ok := firstChildValue.(*kern.DictType); ok {
|
if dictIt, ok := firstChildValue.(*dict.DictType); ok {
|
||||||
var args []any
|
var args []any
|
||||||
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
||||||
v, err = NewDictIterator(dictIt, args)
|
v, err = NewDictIterator(dictIt, args)
|
||||||
@@ -132,16 +137,23 @@ func evalIterator(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
err = opTerm.Children[0].Errorf("the data-source must be a dictionary")
|
err = opTerm.Children[0].Errorf("the data-source must be a dictionary")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if list, ok := firstChildValue.(*kern.ListType); ok {
|
} else if a, ok := firstChildValue.(*array.ArrayType); ok {
|
||||||
var args []any
|
var args []any
|
||||||
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
||||||
v = NewListIterator(list, args)
|
v = NewListIterator(a, args)
|
||||||
}
|
}
|
||||||
} else if intVal, ok := firstChildValue.(int64); ok {
|
} else if ll, ok := firstChildValue.(*list.LinkedList); ok {
|
||||||
var args []any
|
var args []any
|
||||||
if args, err = evalSiblings(ctx, opTerm.Children, intVal); err == nil {
|
if args, err = evalSiblings(ctx, opTerm.Children, nil); err == nil {
|
||||||
v, err = NewIntIterator(args)
|
v = NewLinkedListIterator(ll, args)
|
||||||
}
|
}
|
||||||
|
} else if intervalVal, ok := firstChildValue.(*interval.IntervalType); ok {
|
||||||
|
v, err = NewIntIteratorFromInterval(intervalVal)
|
||||||
|
// } else if intVal, ok := firstChildValue.(int64); ok {
|
||||||
|
// var args []any
|
||||||
|
// if args, err = evalSiblings(ctx, opTerm.Children, intVal); err == nil {
|
||||||
|
// v, err = NewIntIterator(args)
|
||||||
|
// }
|
||||||
} else if it, ok := firstChildValue.(kern.Iterator); ok {
|
} else if it, ok := firstChildValue.(kern.Iterator); ok {
|
||||||
v, err = NewIterIter(it, ctx, opTerm.Children[1:])
|
v, err = NewIterIter(it, ctx, opTerm.Children[1:])
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operand-list.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -------- list term
|
||||||
|
// func newLinkedListTermA(args ...*scan.Term) *scan.Term {
|
||||||
|
// return newLinkedListTerm(0, 0, args)
|
||||||
|
// }
|
||||||
|
|
||||||
|
func newLinkedListTerm(row, col int, args []*scan.Term) *scan.Term {
|
||||||
|
return &scan.Term{
|
||||||
|
Tk: *scan.NewValueToken(row, col, sym.SymLinkedList, "[<>]", args),
|
||||||
|
Parent: nil,
|
||||||
|
Children: nil,
|
||||||
|
Position: scan.PosLeaf,
|
||||||
|
Priority: scan.PriValue,
|
||||||
|
EvalFunc: evalLinkedList,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- list func
|
||||||
|
func evalLinkedList(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
|
listTerm, _ := opTerm.Value().([]*scan.Term)
|
||||||
|
items := list.NewLinkedList()
|
||||||
|
for _, tree := range listTerm {
|
||||||
|
var param any
|
||||||
|
if param, err = tree.Compute(ctx); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
items.PushBack(param)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
v = items
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
+4
-2
@@ -7,6 +7,8 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- list term
|
// -------- list term
|
||||||
@@ -16,7 +18,7 @@ func newListTermA(args ...*scan.Term) *scan.Term {
|
|||||||
|
|
||||||
func newListTerm(row, col int, args []*scan.Term) *scan.Term {
|
func newListTerm(row, col int, args []*scan.Term) *scan.Term {
|
||||||
return &scan.Term{
|
return &scan.Term{
|
||||||
Tk: *scan.NewValueToken(row, col, scan.SymList, "[]", args),
|
Tk: *scan.NewValueToken(row, col, sym.SymList, "[]", args),
|
||||||
Parent: nil,
|
Parent: nil,
|
||||||
Children: nil,
|
Children: nil,
|
||||||
Position: scan.PosLeaf,
|
Position: scan.PosLeaf,
|
||||||
@@ -28,7 +30,7 @@ func newListTerm(row, col int, args []*scan.Term) *scan.Term {
|
|||||||
// -------- list func
|
// -------- list func
|
||||||
func evalList(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
func evalList(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
list, _ := opTerm.Value().([]*scan.Term)
|
list, _ := opTerm.Value().([]*scan.Term)
|
||||||
items := make(kern.ListType, len(list))
|
items := make(array.ArrayType, len(list))
|
||||||
for i, tree := range list {
|
for i, tree := range list {
|
||||||
var param any
|
var param any
|
||||||
if param, err = tree.Compute(ctx); err != nil {
|
if param, err = tree.Compute(ctx); err != nil {
|
||||||
|
|||||||
+18
-6
@@ -7,10 +7,22 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/fract"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- literal term
|
// -------- literal term
|
||||||
func newLiteralTerm(tk *scan.Token) *scan.Term {
|
func newLiteralTerm(tk *scan.Token) *scan.Term {
|
||||||
|
|
||||||
|
if tk.Sym == sym.SymFraction {
|
||||||
|
if v, isString := tk.Value.(string); isString {
|
||||||
|
var err error
|
||||||
|
if tk.Value, err = fract.MakeGeneratingFraction(v); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &scan.Term{
|
return &scan.Term{
|
||||||
Tk: *tk,
|
Tk: *tk,
|
||||||
Parent: nil,
|
Parent: nil,
|
||||||
@@ -29,10 +41,10 @@ func evalLiteral(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymString, newLiteralTerm)
|
scan.RegisterTermConstructor(sym.SymString, newLiteralTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymInteger, newLiteralTerm)
|
scan.RegisterTermConstructor(sym.SymInteger, newLiteralTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymFloat, newLiteralTerm)
|
scan.RegisterTermConstructor(sym.SymFloat, newLiteralTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymFraction, newLiteralTerm)
|
scan.RegisterTermConstructor(sym.SymFraction, newLiteralTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymBool, newLiteralTerm)
|
scan.RegisterTermConstructor(sym.SymBool, newLiteralTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymKwNil, newLiteralTerm)
|
scan.RegisterTermConstructor(sym.SymKwNil, newLiteralTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-19
@@ -5,11 +5,9 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- selector case term
|
// -------- selector case term
|
||||||
@@ -19,20 +17,20 @@ type selectorCase struct {
|
|||||||
caseExpr kern.Expr
|
caseExpr kern.Expr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *selectorCase) String() string {
|
// func (sc *selectorCase) String() string {
|
||||||
var sb strings.Builder
|
// var sb strings.Builder
|
||||||
if sc.filterList != nil {
|
// if sc.filterList != nil {
|
||||||
sc.filterList.ToString(&sb)
|
// sc.filterList.ToString(&sb)
|
||||||
sb.WriteByte(' ')
|
// sb.WriteByte(' ')
|
||||||
}
|
// }
|
||||||
sb.WriteByte('{')
|
// sb.WriteByte('{')
|
||||||
sb.WriteString(sc.caseExpr.String())
|
// sb.WriteString(sc.caseExpr.String())
|
||||||
sb.WriteByte('}')
|
// sb.WriteByte('}')
|
||||||
return sb.String()
|
// return sb.String()
|
||||||
}
|
// }
|
||||||
|
|
||||||
func newSelectorCaseTerm(row, col int, filterList *scan.Term, caseExpr kern.Expr) *scan.Term {
|
func newSelectorCaseTerm(row, col int, filterList *scan.Term, caseExpr kern.Expr) *scan.Term {
|
||||||
tk := scan.NewValueToken(row, col, scan.SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr})
|
tk := scan.NewValueToken(row, col, sym.SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr})
|
||||||
return &scan.Term{
|
return &scan.Term{
|
||||||
Tk: *tk,
|
Tk: *tk,
|
||||||
Parent: nil,
|
Parent: nil,
|
||||||
@@ -45,9 +43,14 @@ func newSelectorCaseTerm(row, col int, filterList *scan.Term, caseExpr kern.Expr
|
|||||||
|
|
||||||
// -------- eval selector case
|
// -------- eval selector case
|
||||||
func evalSelectorCase(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
func evalSelectorCase(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
var ok bool
|
v, _ = opTerm.Value().(*selectorCase)
|
||||||
if v, ok = opTerm.Value().(*selectorCase); !ok {
|
|
||||||
err = fmt.Errorf("selector-case expected, got %T", opTerm.Value())
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func evalSelectorCase(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
|
// var ok bool
|
||||||
|
// if v, ok = opTerm.Value().(*selectorCase); !ok {
|
||||||
|
// err = fmt.Errorf("selector-case expected, got %T", opTerm.Value())
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|||||||
+3
-2
@@ -9,6 +9,7 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- variable term
|
// -------- variable term
|
||||||
@@ -21,7 +22,7 @@ func newVarTerm(tk *scan.Token) *scan.Term {
|
|||||||
Priority: scan.PriValue,
|
Priority: scan.PriValue,
|
||||||
EvalFunc: evalVar,
|
EvalFunc: evalVar,
|
||||||
}
|
}
|
||||||
t.Tk.Sym = scan.SymVariable
|
t.Tk.Sym = sym.SymVariable
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,5 +42,5 @@ func evalVar(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymIdentifier, newVarTerm)
|
scan.RegisterTermConstructor(sym.SymIdentifier, newVarTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+109
-39
@@ -7,6 +7,10 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/list"
|
||||||
"git.portale-stac.it/go-pkg/expr/util"
|
"git.portale-stac.it/go-pkg/expr/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,9 +26,29 @@ func newAssignTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
|
v, err = generalEvalAssign(ctx, opTerm, false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDeepCopyAssignTerm(tk *scan.Token) (inst *scan.Term) {
|
||||||
|
return &scan.Term{
|
||||||
|
Tk: *tk,
|
||||||
|
Children: make([]*scan.Term, 0, 2),
|
||||||
|
Position: scan.PosInfix,
|
||||||
|
Priority: scan.PriAssign,
|
||||||
|
EvalFunc: evalDeepCopyAssign,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalDeepCopyAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
|
v, err = generalEvalAssign(ctx, opTerm, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term, value any) (err error) {
|
func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term, value any) (err error) {
|
||||||
var collectionValue, keyListValue, keyValue any
|
var collectionValue, keyListValue, keyValue any
|
||||||
var keyList *kern.ListType
|
var keyList *array.ArrayType
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
|
if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
|
||||||
@@ -33,7 +57,7 @@ func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *sca
|
|||||||
|
|
||||||
if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
|
if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
|
||||||
return
|
return
|
||||||
} else if keyList, ok = keyListValue.(*kern.ListType); !ok || len(*keyList) != 1 {
|
} else if keyList, ok = keyListValue.(*array.ArrayType); !ok || len(*keyList) != 1 {
|
||||||
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue))
|
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -43,37 +67,82 @@ func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *sca
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch collection := collectionValue.(type) {
|
switch collection := collectionValue.(type) {
|
||||||
case *kern.ListType:
|
case *array.ArrayType:
|
||||||
if index, ok := keyValue.(int64); ok {
|
if index, ok := keyValue.(int64); ok {
|
||||||
err = collection.SetItem(index, value)
|
err = collection.SetItem(index, value)
|
||||||
} else {
|
} else {
|
||||||
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
|
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
|
||||||
}
|
}
|
||||||
case *kern.DictType:
|
case *dict.DictType:
|
||||||
collection.SetItem(keyValue, value)
|
collection.SetItem(keyValue, value)
|
||||||
|
case *list.LinkedList:
|
||||||
|
if index, ok := keyValue.(int64); ok {
|
||||||
|
err = collection.SetItem(index, value)
|
||||||
|
} else {
|
||||||
|
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
err = collectionTerm.Errorf("collection expected")
|
err = collectionTerm.Errorf("collection expected")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any) (err error) {
|
func assignValue(ctx kern.ExprContext, leftTerm *scan.Term, v any, deepCopy bool) (err error) {
|
||||||
if leftTerm.Symbol() == scan.SymIndex {
|
if leftTerm.Symbol() == sym.SymIndex {
|
||||||
err = assignCollectionItem(ctx, leftTerm.Children[0], leftTerm.Children[1], v)
|
err = assignCollectionItem(ctx, leftTerm.Children[0], leftTerm.Children[1], v)
|
||||||
} else {
|
} else {
|
||||||
|
if deepCopy {
|
||||||
|
v = kern.Clone(v)
|
||||||
|
}
|
||||||
ctx.UnsafeSetVar(leftTerm.Source(), v)
|
ctx.UnsafeSetVar(leftTerm.Source(), v)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
func evalAssignDictItem(ctx kern.ExprContext, dotTerm *scan.Term, valueTerm kern.Term) (v any, err error) {
|
||||||
|
var ok bool
|
||||||
|
var dictAny, dotKey any
|
||||||
|
var dt *dict.DictType
|
||||||
|
|
||||||
|
if err = dotTerm.CheckOperands(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dotLeftTerm := dotTerm.GetChild(0)
|
||||||
|
if dictAny, err = dotLeftTerm.Compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if dt, ok = dictAny.(*dict.DictType); !ok {
|
||||||
|
err = dotTerm.Tk.ErrorExpectedGot(dict.DictTypeName)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dotRightTerm := dotTerm.Children[1]
|
||||||
|
dotRightSym := dotRightTerm.Symbol()
|
||||||
|
if dotRightSym == sym.SymVariable || dotRightSym == sym.SymString {
|
||||||
|
dotKey = util.UnquoteString(dotRightTerm.Source())
|
||||||
|
} else if dotKey, err = dotRightTerm.Compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err = valueTerm.Compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dt.SetItem(dotKey, v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func generalEvalAssign(ctx kern.ExprContext, opTerm *scan.Term, deepCopy bool) (v any, err error) {
|
||||||
if err = opTerm.CheckOperands(); err != nil {
|
if err = opTerm.CheckOperands(); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
leftTerm := opTerm.Children[0]
|
leftTerm := opTerm.Children[0]
|
||||||
leftSym := leftTerm.Symbol()
|
leftSym := leftTerm.Symbol()
|
||||||
if leftSym != scan.SymVariable && leftSym != scan.SymIndex {
|
if leftSym == sym.SymDot {
|
||||||
|
return evalAssignDictItem(ctx, opTerm.Children[0], opTerm.GetChild(1))
|
||||||
|
} else if leftSym != sym.SymVariable && leftSym != sym.SymIndex {
|
||||||
err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source())
|
err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -82,7 +151,7 @@ func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
if v, err = rightChild.Compute(ctx); err == nil {
|
if v, err = rightChild.Compute(ctx); err == nil {
|
||||||
if functor, ok := v.(kern.Functor); ok {
|
if functor, ok := v.(kern.Functor); ok {
|
||||||
if leftSym == scan.SymVariable {
|
if leftSym == sym.SymVariable {
|
||||||
if info := functor.GetFunc(); info != nil {
|
if info := functor.GetFunc(); info != nil {
|
||||||
ctx.RegisterFunc(leftTerm.Source(), info.Functor(), info.ReturnType(), info.Params())
|
ctx.RegisterFunc(leftTerm.Source(), info.Functor(), info.ReturnType(), info.Params())
|
||||||
} else if funcDef, ok := functor.(*exprFunctor); ok {
|
} else if funcDef, ok := functor.(*exprFunctor); ok {
|
||||||
@@ -93,10 +162,10 @@ func evalAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
err = opTerm.Errorf("unknown function %s()", rightChild.Source())
|
err = opTerm.Errorf("unknown function %s()", rightChild.Source())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = assignValue(ctx, leftTerm, v)
|
err = assignValue(ctx, leftTerm, v, deepCopy)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = assignValue(ctx, leftTerm, v)
|
err = assignValue(ctx, leftTerm, v, deepCopy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -119,7 +188,7 @@ func newOpAssignTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
|
|
||||||
func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term) (value any, err error) {
|
func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *scan.Term) (value any, err error) {
|
||||||
var collectionValue, keyListValue, keyValue any
|
var collectionValue, keyListValue, keyValue any
|
||||||
var keyList *kern.ListType
|
var keyList *array.ArrayType
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
|
if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
|
||||||
@@ -128,7 +197,7 @@ func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *s
|
|||||||
|
|
||||||
if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
|
if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
|
||||||
return
|
return
|
||||||
} else if keyList, ok = keyListValue.(*kern.ListType); !ok || len(*keyList) != 1 {
|
} else if keyList, ok = keyListValue.(*array.ArrayType); !ok || len(*keyList) != 1 {
|
||||||
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue))
|
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -138,13 +207,13 @@ func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *s
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch collection := collectionValue.(type) {
|
switch collection := collectionValue.(type) {
|
||||||
case *kern.ListType:
|
case *array.ArrayType:
|
||||||
if index, ok := keyValue.(int64); ok {
|
if index, ok := keyValue.(int64); ok {
|
||||||
value = (*collection)[index]
|
value = (*collection)[index]
|
||||||
} else {
|
} else {
|
||||||
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
|
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
|
||||||
}
|
}
|
||||||
case *kern.DictType:
|
case *dict.DictType:
|
||||||
value = (*collection)[keyValue]
|
value = (*collection)[keyValue]
|
||||||
default:
|
default:
|
||||||
err = collectionTerm.Errorf("collection expected")
|
err = collectionTerm.Errorf("collection expected")
|
||||||
@@ -153,7 +222,7 @@ func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *s
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getAssignValue(ctx kern.ExprContext, leftTerm *scan.Term) (value any, err error) {
|
func getAssignValue(ctx kern.ExprContext, leftTerm *scan.Term) (value any, err error) {
|
||||||
if leftTerm.Symbol() == scan.SymIndex {
|
if leftTerm.Symbol() == sym.SymIndex {
|
||||||
value, err = getCollectionItemValue(ctx, leftTerm.Children[0], leftTerm.Children[1])
|
value, err = getCollectionItemValue(ctx, leftTerm.Children[0], leftTerm.Children[1])
|
||||||
} else {
|
} else {
|
||||||
value, _ = ctx.GetVar(leftTerm.Source())
|
value, _ = ctx.GetVar(leftTerm.Source())
|
||||||
@@ -169,7 +238,7 @@ func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
leftTerm := opTerm.Children[0]
|
leftTerm := opTerm.Children[0]
|
||||||
leftSym := leftTerm.Symbol()
|
leftSym := leftTerm.Symbol()
|
||||||
if leftSym != scan.SymVariable && leftSym != scan.SymIndex {
|
if leftSym != sym.SymVariable && leftSym != sym.SymIndex {
|
||||||
err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source())
|
err = leftTerm.Tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.Tk.Source())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -179,31 +248,31 @@ func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
if rightValue, err = rightChild.Compute(ctx); err == nil {
|
if rightValue, err = rightChild.Compute(ctx); err == nil {
|
||||||
if leftValue, err = getAssignValue(ctx, leftTerm); err == nil {
|
if leftValue, err = getAssignValue(ctx, leftTerm); err == nil {
|
||||||
switch opTerm.Symbol() {
|
switch opTerm.Symbol() {
|
||||||
case scan.SymPlusEqual:
|
case sym.SymPlusEqual:
|
||||||
v, err = sumValues(opTerm, leftValue, rightValue)
|
v, err = sumValues(opTerm, leftValue, rightValue)
|
||||||
case scan.SymMinusEqual:
|
case sym.SymMinusEqual:
|
||||||
v, err = diffValues(opTerm, leftValue, rightValue)
|
v, err = diffValues(opTerm, leftValue, rightValue)
|
||||||
case scan.SymStarEqual:
|
case sym.SymStarEqual:
|
||||||
v, err = mulValues(opTerm, leftValue, rightValue)
|
v, err = mulValues(opTerm, leftValue, rightValue)
|
||||||
case scan.SymSlashEqual:
|
case sym.SymSlashEqual:
|
||||||
v, err = divValues(opTerm, leftValue, rightValue)
|
v, err = divValues(opTerm, leftValue, rightValue)
|
||||||
case scan.SymPercEqual:
|
case sym.SymPercEqual:
|
||||||
v, err = remainderValues(opTerm, leftValue, rightValue)
|
v, err = remainderValues(opTerm, leftValue, rightValue)
|
||||||
case scan.SymAmpersandEqual:
|
case sym.SymAmpersandEqual:
|
||||||
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
|
||||||
case scan.SymVertBarEqual:
|
case sym.SymVertBarEqual:
|
||||||
v, err = bitwiseOr(opTerm, leftValue, rightValue)
|
v, err = bitwiseOr(opTerm, leftValue, rightValue)
|
||||||
case scan.SymCaretEqual:
|
case sym.SymCaretEqual:
|
||||||
v, err = bitwiseXor(opTerm, leftValue, rightValue)
|
v, err = bitwiseXor(opTerm, leftValue, rightValue)
|
||||||
case scan.SymDoubleLessEqual:
|
case sym.SymDoubleLessEqual:
|
||||||
v, err = bitLeftShift(opTerm, leftValue, rightValue)
|
v, err = bitLeftShift(opTerm, leftValue, rightValue)
|
||||||
case scan.SymDoubleGreaterEqual:
|
case sym.SymDoubleGreaterEqual:
|
||||||
v, err = bitRightShift(opTerm, leftValue, rightValue)
|
v, err = bitRightShift(opTerm, leftValue, rightValue)
|
||||||
default:
|
default:
|
||||||
err = opTerm.Errorf("unsupported assign operator %q", opTerm.Source())
|
err = opTerm.Errorf("unsupported assign operator %q", opTerm.Source())
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
err = assignValue(ctx, leftTerm, v)
|
err = assignValue(ctx, leftTerm, v, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -212,15 +281,16 @@ func evalOpAssign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymEqual, newAssignTerm)
|
scan.RegisterTermConstructor(sym.SymEqual, newAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymPlusEqual, newOpAssignTerm)
|
scan.RegisterTermConstructor(sym.SymColonEqual, newDeepCopyAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymMinusEqual, newOpAssignTerm)
|
scan.RegisterTermConstructor(sym.SymPlusEqual, newOpAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymStarEqual, newOpAssignTerm)
|
scan.RegisterTermConstructor(sym.SymMinusEqual, newOpAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymSlashEqual, newOpAssignTerm)
|
scan.RegisterTermConstructor(sym.SymStarEqual, newOpAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymPercEqual, newOpAssignTerm)
|
scan.RegisterTermConstructor(sym.SymSlashEqual, newOpAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymDoubleLessEqual, newOpAssignTerm)
|
scan.RegisterTermConstructor(sym.SymPercEqual, newOpAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymDoubleGreaterEqual, newOpAssignTerm)
|
scan.RegisterTermConstructor(sym.SymDoubleLessEqual, newOpAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymAmpersandEqual, newOpAssignTerm)
|
scan.RegisterTermConstructor(sym.SymDoubleGreaterEqual, newOpAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymVertBarEqual, newOpAssignTerm)
|
scan.RegisterTermConstructor(sym.SymAmpersandEqual, newOpAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymCaretEqual, newOpAssignTerm)
|
scan.RegisterTermConstructor(sym.SymVertBarEqual, newOpAssignTerm)
|
||||||
|
scan.RegisterTermConstructor(sym.SymCaretEqual, newOpAssignTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-at.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/list"
|
||||||
|
)
|
||||||
|
|
||||||
|
//-------- at term
|
||||||
|
|
||||||
|
func newAtTerm(tk *scan.Token) (inst *scan.Term) {
|
||||||
|
return &scan.Term{
|
||||||
|
Tk: *tk,
|
||||||
|
Children: make([]*scan.Term, 0, 2),
|
||||||
|
Position: scan.PosInfix,
|
||||||
|
Priority: scan.PriRelational,
|
||||||
|
EvalFunc: evalAt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// func hasKey(d map[any]any, target any) (ok bool) {
|
||||||
|
// _, ok = d[target]
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
func evalAt(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
|
||||||
|
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
v = int64(-1) // default value if not found
|
||||||
|
if array.IsArray(rightValue) {
|
||||||
|
a, _ := rightValue.(*array.ArrayType)
|
||||||
|
if index := a.IndexDeepSameCmp(leftValue); index >= 0 {
|
||||||
|
v = index
|
||||||
|
}
|
||||||
|
} else if dict.IsDict(rightValue) {
|
||||||
|
dict, _ := rightValue.(*dict.DictType)
|
||||||
|
if k, exists := dict.FindKey(leftValue); exists {
|
||||||
|
v = k
|
||||||
|
} else {
|
||||||
|
v = nil
|
||||||
|
}
|
||||||
|
} else if list.IsLinkedList(rightValue) {
|
||||||
|
ls, _ := rightValue.(*list.LinkedList)
|
||||||
|
if index := ls.Index(leftValue); index >= 0 {
|
||||||
|
v = index
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v = nil
|
||||||
|
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
scan.RegisterTermConstructor(sym.SymKwAt, newAtTerm)
|
||||||
|
}
|
||||||
+7
-5
@@ -7,6 +7,8 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- Bitwise NOT term
|
//-------- Bitwise NOT term
|
||||||
@@ -28,7 +30,7 @@ func evalBitwiseNot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsInteger(value) {
|
if types.IsInteger(value) {
|
||||||
i, _ := value.(int64)
|
i, _ := value.(int64)
|
||||||
v = ^i
|
v = ^i
|
||||||
} else {
|
} else {
|
||||||
@@ -152,8 +154,8 @@ func evalBitwiseXor(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error)
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymTilde, newBitwiseNotTerm)
|
scan.RegisterTermConstructor(sym.SymTilde, newBitwiseNotTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymAmpersand, newBitwiseAndTerm)
|
scan.RegisterTermConstructor(sym.SymAmpersand, newBitwiseAndTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymVertBar, newBitwiseOrTerm)
|
scan.RegisterTermConstructor(sym.SymVertBar, newBitwiseOrTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymCaret, newBitwiseXorTerm)
|
scan.RegisterTermConstructor(sym.SymCaret, newBitwiseXorTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-12
@@ -9,6 +9,8 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/boolean"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- NOT term
|
//-------- NOT term
|
||||||
@@ -30,7 +32,7 @@ func evalNot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if b, ok := kern.ToBool(rightValue); ok {
|
if b, ok := boolean.ToBool(rightValue); ok {
|
||||||
v = !b
|
v = !b
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.ErrIncompatiblePrefixPostfixType(rightValue)
|
err = opTerm.ErrIncompatiblePrefixPostfixType(rightValue)
|
||||||
@@ -68,8 +70,8 @@ func evalAndWithoutShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err e
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
leftBool, lok = kern.ToBool(leftValue)
|
leftBool, lok = boolean.ToBool(leftValue)
|
||||||
rightBool, rok = kern.ToBool(rightValue)
|
rightBool, rok = boolean.ToBool(rightValue)
|
||||||
|
|
||||||
if lok && rok {
|
if lok && rok {
|
||||||
v = leftBool && rightBool
|
v = leftBool && rightBool
|
||||||
@@ -90,14 +92,14 @@ func evalAndWithShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err erro
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if leftBool, lok := kern.ToBool(leftValue); !lok {
|
if leftBool, lok := boolean.ToBool(leftValue); !lok {
|
||||||
// err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", expr.TypeName(leftValue))
|
// err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", expr.TypeName(leftValue))
|
||||||
// return
|
// return
|
||||||
err = self.ErrIncompatibleType(leftValue, "left")
|
err = self.ErrIncompatibleType(leftValue, "left")
|
||||||
} else if !leftBool {
|
} else if !leftBool {
|
||||||
v = false
|
v = false
|
||||||
} else if rightValue, err = self.Children[1].Compute(ctx); err == nil {
|
} else if rightValue, err = self.Children[1].Compute(ctx); err == nil {
|
||||||
if rightBool, rok := kern.ToBool(rightValue); rok {
|
if rightBool, rok := boolean.ToBool(rightValue); rok {
|
||||||
v = rightBool
|
v = rightBool
|
||||||
} else {
|
} else {
|
||||||
err = self.ErrIncompatibleTypes(leftValue, rightValue)
|
err = self.ErrIncompatibleTypes(leftValue, rightValue)
|
||||||
@@ -136,8 +138,8 @@ func evalOrWithoutShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
leftBool, lok = kern.ToBool(leftValue)
|
leftBool, lok = boolean.ToBool(leftValue)
|
||||||
rightBool, rok = kern.ToBool(rightValue)
|
rightBool, rok = boolean.ToBool(rightValue)
|
||||||
|
|
||||||
if lok && rok {
|
if lok && rok {
|
||||||
v = leftBool || rightBool
|
v = leftBool || rightBool
|
||||||
@@ -158,13 +160,13 @@ func evalOrWithShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err error
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if leftBool, lok := kern.ToBool(leftValue); !lok {
|
if leftBool, lok := boolean.ToBool(leftValue); !lok {
|
||||||
err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", kern.TypeName(leftValue))
|
err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", kern.TypeName(leftValue))
|
||||||
return
|
return
|
||||||
} else if leftBool {
|
} else if leftBool {
|
||||||
v = true
|
v = true
|
||||||
} else if rightValue, err = self.Children[1].Compute(ctx); err == nil {
|
} else if rightValue, err = self.Children[1].Compute(ctx); err == nil {
|
||||||
if rightBool, rok := kern.ToBool(rightValue); rok {
|
if rightBool, rok := boolean.ToBool(rightValue); rok {
|
||||||
v = rightBool
|
v = rightBool
|
||||||
} else {
|
} else {
|
||||||
err = self.ErrIncompatibleTypes(leftValue, rightValue)
|
err = self.ErrIncompatibleTypes(leftValue, rightValue)
|
||||||
@@ -175,7 +177,7 @@ func evalOrWithShortcut(ctx kern.ExprContext, self *scan.Term) (v any, err error
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymNot, newNotTerm)
|
scan.RegisterTermConstructor(sym.SymNot, newNotTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymAnd, newAndTerm)
|
scan.RegisterTermConstructor(sym.SymAnd, newAndTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymOr, newOrTerm)
|
scan.RegisterTermConstructor(sym.SymOr, newOrTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -9,6 +9,8 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/str"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- builtin term
|
//-------- builtin term
|
||||||
@@ -31,7 +33,7 @@ func evalBuiltin(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
if kern.IsString(childValue) {
|
if str.IsString(childValue) {
|
||||||
module, _ := childValue.(string)
|
module, _ := childValue.(string)
|
||||||
count, err = ImportInContextByGlobPattern(ctx, module)
|
count, err = ImportInContextByGlobPattern(ctx, module)
|
||||||
} else {
|
} else {
|
||||||
@@ -65,5 +67,5 @@ func evalBuiltin(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymKwBuiltin, newBuiltinTerm)
|
scan.RegisterTermConstructor(sym.SymKwBuiltin, newBuiltinTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -7,6 +7,7 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- but term
|
//-------- but term
|
||||||
@@ -28,5 +29,5 @@ func evalBut(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymKwBut, newButTerm)
|
scan.RegisterTermConstructor(sym.SymKwBut, newButTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+152
@@ -0,0 +1,152 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-cat.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
)
|
||||||
|
|
||||||
|
//-------- cat term
|
||||||
|
|
||||||
|
func newCatTerm(tk *scan.Token) (inst *scan.Term) {
|
||||||
|
return &scan.Term{
|
||||||
|
Tk: *tk,
|
||||||
|
Children: make([]*scan.Term, 0, 2),
|
||||||
|
Position: scan.PosInfix,
|
||||||
|
Priority: scan.PriIterOp,
|
||||||
|
EvalFunc: evalCat,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalCat(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
|
var leftValue, rightValue any
|
||||||
|
var itLeft, itRight kern.Iterator
|
||||||
|
// var item any
|
||||||
|
var ok bool
|
||||||
|
|
||||||
|
if err = opTerm.CheckOperands(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if rightValue, err = opTerm.Children[1].Compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if itLeft, ok = leftValue.(kern.Iterator); !ok {
|
||||||
|
if itLeft, err = NewIterator(ctx, leftValue, nil); err != nil {
|
||||||
|
return nil, fmt.Errorf("left operand of JOIN must be an iterable data-source; got %s", kern.TypeName(leftValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if itRight, ok = rightValue.(kern.Iterator); !ok {
|
||||||
|
if itRight, err = NewIterator(ctx, rightValue, nil); err != nil {
|
||||||
|
return nil, fmt.Errorf("right operand of JOIN must be an iterable data-source; got %s", kern.TypeName(rightValue))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// values := kern.NewListA()
|
||||||
|
// for _, it := range []kern.Iterator{itLeft, itRight} {
|
||||||
|
// for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||||
|
// values.AppendItem(item)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if err == io.EOF {
|
||||||
|
// err = nil
|
||||||
|
// }
|
||||||
|
// v = values
|
||||||
|
v = newCatIterator(itLeft, itRight)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const catIteratorType = "cat"
|
||||||
|
|
||||||
|
type catIterator struct {
|
||||||
|
kern.IteratorBase
|
||||||
|
itLeft kern.Iterator
|
||||||
|
itRight kern.Iterator
|
||||||
|
itCurrent kern.Iterator
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCatIterator(itLeft, itRight kern.Iterator) (it *catIterator) {
|
||||||
|
it = &catIterator{
|
||||||
|
IteratorBase: kern.IteratorBase{},
|
||||||
|
itLeft: itLeft,
|
||||||
|
itRight: itRight,
|
||||||
|
itCurrent: itLeft,
|
||||||
|
}
|
||||||
|
it.Reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *catIterator) TypeName() string {
|
||||||
|
return catIteratorType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *catIterator) String() string {
|
||||||
|
return fmt.Sprintf("$(%s %s %s)", it.itLeft, catIteratorType, it.itRight)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *catIterator) Next() (item any, err error) {
|
||||||
|
if it.itCurrent == nil {
|
||||||
|
err = io.EOF
|
||||||
|
} else {
|
||||||
|
if item, err = it.itCurrent.Next(); err == nil {
|
||||||
|
it.Increment()
|
||||||
|
} else if err == io.EOF {
|
||||||
|
if it.itCurrent == it.itLeft {
|
||||||
|
it.itCurrent = it.itRight
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if item, err = it.itCurrent.Next(); err == nil {
|
||||||
|
it.Increment()
|
||||||
|
} else {
|
||||||
|
it.itCurrent = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it.SetCurrent(item)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (it *catIterator) Reset() (err error) {
|
||||||
|
// err = it.itLeft.Reset()
|
||||||
|
// it.IteratorBase.Reset()
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (it *catIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
||||||
|
switch name {
|
||||||
|
case kern.NextName:
|
||||||
|
v, err = it.Next()
|
||||||
|
// case kern.ResetName:
|
||||||
|
// err = it.Reset()
|
||||||
|
// case kern.CleanName:
|
||||||
|
// err = it.Clean()
|
||||||
|
case kern.IndexName:
|
||||||
|
v = it.Index()
|
||||||
|
case kern.CurrentName:
|
||||||
|
v, err = it.Current()
|
||||||
|
case kern.CountName:
|
||||||
|
v = it.Count()
|
||||||
|
default:
|
||||||
|
err = kern.ErrNoOperation(name)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
scan.RegisterTermConstructor(sym.SymKwCat, newCatTerm)
|
||||||
|
}
|
||||||
+23
-17
@@ -5,8 +5,12 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/list"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- context term
|
//-------- context term
|
||||||
@@ -23,35 +27,37 @@ func newContextTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
|
|
||||||
func evalContextValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
func evalContextValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
var childValue any
|
var childValue any
|
||||||
|
|
||||||
var sourceCtx kern.ExprContext
|
var sourceCtx kern.ExprContext
|
||||||
|
|
||||||
if len(opTerm.Children) == 0 {
|
if len(opTerm.Children) == 0 {
|
||||||
sourceCtx = ctx
|
sourceCtx = ctx
|
||||||
} else if opTerm.Children[0].Symbol() == scan.SymVariable && opTerm.Children[0].Source() == "global" {
|
} else if opTerm.Children[0].Symbol() == sym.SymVariable && opTerm.Children[0].Source() == "global" {
|
||||||
sourceCtx = ctx.GetGlobal()
|
sourceCtx = ctx.GetGlobal()
|
||||||
} else if childValue, err = opTerm.EvalPrefix(ctx); err == nil {
|
} else if childValue, err = opTerm.EvalPrefix(ctx); err == nil {
|
||||||
if dc, ok := childValue.(*dataCursor); ok {
|
if dc, ok := childValue.(*dataCursor); ok {
|
||||||
sourceCtx = dc.ctx
|
sourceCtx = dc.ctx
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if sourceCtx != nil {
|
if sourceCtx != nil {
|
||||||
if formatter, ok := sourceCtx.(kern.DictFormat); ok {
|
v = sourceCtx.ToDict()
|
||||||
v = formatter.ToDict()
|
} else if childValue != nil {
|
||||||
} else if formatter, ok := sourceCtx.(kern.Formatter); ok {
|
it, ok := childValue.(kern.Iterator)
|
||||||
v = formatter.ToString(0)
|
if !ok {
|
||||||
} else {
|
it, err = NewIterator(ctx, childValue, nil)
|
||||||
// keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
|
}
|
||||||
keys := sourceCtx.EnumVars(nil)
|
if err == nil {
|
||||||
d := make(map[string]any)
|
var item any
|
||||||
for _, key := range keys {
|
values := list.NewLinkedListA()
|
||||||
d[key], _ = sourceCtx.GetVar(key)
|
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||||
|
values.PushBack(item)
|
||||||
}
|
}
|
||||||
keys = sourceCtx.EnumFuncs(func(name string) bool { return true })
|
if err == io.EOF {
|
||||||
for _, key := range keys {
|
err = nil
|
||||||
d[key], _ = sourceCtx.GetFuncInfo(key)
|
v = values
|
||||||
}
|
}
|
||||||
v = d
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||||
@@ -61,5 +67,5 @@ func evalContextValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymDoubleDollar, newContextTerm)
|
scan.RegisterTermConstructor(sym.SymDoubleDollar, newContextTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -7,6 +7,7 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- export all term
|
//-------- export all term
|
||||||
@@ -28,5 +29,5 @@ func evalExportAll(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymDoubleAt, newExportAllTerm)
|
scan.RegisterTermConstructor(sym.SymDoubleAt, newExportAllTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-6
@@ -7,6 +7,7 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- default term
|
//-------- default term
|
||||||
@@ -29,7 +30,7 @@ func evalDefault(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
leftTerm := opTerm.Children[0]
|
leftTerm := opTerm.Children[0]
|
||||||
if leftTerm.Tk.Sym != scan.SymVariable {
|
if leftTerm.Tk.Sym != sym.SymVariable {
|
||||||
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||||
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||||
return
|
return
|
||||||
@@ -63,7 +64,7 @@ func evalAlternate(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
leftTerm := opTerm.Children[0]
|
leftTerm := opTerm.Children[0]
|
||||||
if leftTerm.Tk.Sym != scan.SymVariable {
|
if leftTerm.Tk.Sym != sym.SymVariable {
|
||||||
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||||
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||||
return
|
return
|
||||||
@@ -99,7 +100,7 @@ func evalAssignDefault(ctx kern.ExprContext, opTerm *scan.Term) (v any, err erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
leftTerm := opTerm.Children[0]
|
leftTerm := opTerm.Children[0]
|
||||||
if leftTerm.Tk.Sym != scan.SymVariable {
|
if leftTerm.Tk.Sym != sym.SymVariable {
|
||||||
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||||
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
|
||||||
return
|
return
|
||||||
@@ -123,7 +124,7 @@ func evalAssignDefault(ctx kern.ExprContext, opTerm *scan.Term) (v any, err erro
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymDoubleQuestion, newDefaultTerm)
|
scan.RegisterTermConstructor(sym.SymDoubleQuestion, newDefaultTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymQuestionEqual, newDefaultAssignTerm)
|
scan.RegisterTermConstructor(sym.SymQuestionEqual, newDefaultAssignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymQuestionExclam, newAlternateTerm)
|
scan.RegisterTermConstructor(sym.SymQuestionExclam, newAlternateTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- digest term
|
//-------- digest term
|
||||||
@@ -45,15 +46,16 @@ func evalDigest(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||||
ctx.SetVar("_", item)
|
ctx.SetVar("_", item)
|
||||||
ctx.SetVar("__", it.Index())
|
ctx.SetVar("__", it.Index())
|
||||||
ctx.SetVar("_#", it.Count())
|
ctx.SetVar("#", it.Count())
|
||||||
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||||
if rightValue == nil {
|
if rightValue == nil {
|
||||||
break
|
err = io.EOF
|
||||||
} else {
|
} else {
|
||||||
lastValue = rightValue
|
lastValue = rightValue
|
||||||
|
ctx.SetVar(kern.ControlLastResult, lastValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.DeleteVar("_#")
|
ctx.DeleteVar("#")
|
||||||
ctx.DeleteVar("__")
|
ctx.DeleteVar("__")
|
||||||
ctx.DeleteVar("_")
|
ctx.DeleteVar("_")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -69,5 +71,5 @@ func evalDigest(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymKwDigest, newDigestTerm)
|
scan.RegisterTermConstructor(sym.SymKwDigest, newDigestTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-18
@@ -7,6 +7,9 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- dot term
|
// -------- dot term
|
||||||
@@ -34,7 +37,7 @@ func evalDot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
switch unboxedValue := leftValue.(type) {
|
switch unboxedValue := leftValue.(type) {
|
||||||
case kern.ExtIterator:
|
case kern.ExtIterator:
|
||||||
if indexTerm.Tk.Sym == scan.SymVariable /*|| indexTerm.Tk.Sym == scan.SymString */ {
|
if indexTerm.Symbol() == sym.SymVariable /*|| indexTerm.Tk.Sym == sym.SymString */ {
|
||||||
opName := indexTerm.Source()
|
opName := indexTerm.Source()
|
||||||
if unboxedValue.HasOperation(opName) {
|
if unboxedValue.HasOperation(opName) {
|
||||||
v, err = unboxedValue.CallOperation(opName, map[string]any{})
|
v, err = unboxedValue.CallOperation(opName, map[string]any{})
|
||||||
@@ -45,22 +48,8 @@ func evalDot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
} else {
|
} else {
|
||||||
err = indexTerm.Tk.ErrorExpectedGot("identifier")
|
err = indexTerm.Tk.ErrorExpectedGot("identifier")
|
||||||
}
|
}
|
||||||
case *kern.DictType:
|
case *dict.DictType:
|
||||||
var ok bool
|
v, err = dotGetDictItemValue(ctx, unboxedValue, opTerm.Children[1])
|
||||||
s := opTerm.Children[1].Tk.Sym
|
|
||||||
if s == scan.SymVariable || s == scan.SymString {
|
|
||||||
src := opTerm.Children[1].Source()
|
|
||||||
if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
|
|
||||||
src = src[1 : len(src)-1]
|
|
||||||
}
|
|
||||||
if v, ok = unboxedValue.GetItem(src); !ok {
|
|
||||||
err = opTerm.Errorf("key %q not found", src)
|
|
||||||
}
|
|
||||||
} else if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
|
||||||
if v, ok = unboxedValue.GetItem(rightValue); !ok {
|
|
||||||
err = opTerm.Errorf("key %q not found", rightValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||||
err = opTerm.Errorf("incompatible types: %s and %s", kern.TypeName(leftValue), kern.TypeName(rightValue))
|
err = opTerm.Errorf("incompatible types: %s and %s", kern.TypeName(leftValue), kern.TypeName(rightValue))
|
||||||
@@ -69,7 +58,35 @@ func evalDot(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dotGetDictItemValue(ctx kern.ExprContext, d *dict.DictType, rightTerm *scan.Term) (v any, err error) {
|
||||||
|
var ok bool
|
||||||
|
var rightValue any
|
||||||
|
s := rightTerm.Symbol()
|
||||||
|
if s == sym.SymVariable || s == sym.SymString {
|
||||||
|
// src := rightTerm.Source()
|
||||||
|
// if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
|
||||||
|
// src = src[1 : len(src)-1]
|
||||||
|
// }
|
||||||
|
src := util.UnquoteString(rightTerm.Source())
|
||||||
|
if v, ok = d.GetItem(src); !ok {
|
||||||
|
err = errDictKeyNotFound(rightTerm, src)
|
||||||
|
}
|
||||||
|
} else if rightValue, err = rightTerm.Compute(ctx); err == nil {
|
||||||
|
if v, ok = d.GetItem(rightValue); !ok {
|
||||||
|
err = errDictKeyNotFound(rightTerm, rightValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func errDictKeyNotFound(term *scan.Term, key any) error {
|
||||||
|
if s, ok := key.(string); ok {
|
||||||
|
return term.Errorf("key %q not found", s)
|
||||||
|
}
|
||||||
|
return term.Errorf("key %v not found", key)
|
||||||
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymDot, newDotTerm)
|
scan.RegisterTermConstructor(sym.SymDot, newDotTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -9,6 +9,8 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- fact term
|
//-------- fact term
|
||||||
@@ -30,7 +32,7 @@ func evalFact(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsInteger(leftValue) {
|
if types.IsInteger(leftValue) {
|
||||||
if i, _ := leftValue.(int64); i >= 0 {
|
if i, _ := leftValue.(int64); i >= 0 {
|
||||||
f := int64(1)
|
f := int64(1)
|
||||||
for k := int64(1); k <= i; k++ {
|
for k := int64(1); k <= i; k++ {
|
||||||
@@ -48,5 +50,5 @@ func evalFact(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymExclamation, newFactTerm)
|
scan.RegisterTermConstructor(sym.SymExclamation, newFactTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+112
-21
@@ -6,10 +6,11 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/boolean"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- map term
|
//-------- map term
|
||||||
@@ -25,9 +26,10 @@ func newFilterTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func evalFilter(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
func evalFilter(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
// var leftValue, rightValue any
|
||||||
|
var leftValue any
|
||||||
var it kern.Iterator
|
var it kern.Iterator
|
||||||
var item any
|
// var item any
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if err = opTerm.CheckOperands(); err != nil {
|
if err = opTerm.CheckOperands(); err != nil {
|
||||||
@@ -44,35 +46,124 @@ func evalFilter(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
values := kern.NewListA()
|
// values := kern.NewListA()
|
||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
// for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||||
ctx.SetVar("_", item)
|
// ctx.SetVar("_", item)
|
||||||
|
// ctx.SetVar("__", it.Index())
|
||||||
|
// ctx.SetVar("_#", it.Count())
|
||||||
|
// if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
||||||
|
// if success, valid := kern.ToBool(rightValue); valid {
|
||||||
|
// if success {
|
||||||
|
// values.AppendItem(item)
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// err = fmt.Errorf("filter expression must return a boolean or a castable to boolean, got %v [%T]", rightValue, rightValue)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ctx.DeleteVar("_#")
|
||||||
|
// ctx.DeleteVar("__")
|
||||||
|
// ctx.DeleteVar("_")
|
||||||
|
// if err != nil {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if err == io.EOF {
|
||||||
|
// err = nil
|
||||||
|
// }
|
||||||
|
// v = values
|
||||||
|
|
||||||
|
v = newFilerIterator(ctx, it, opTerm.Children[1])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterIteratorType = "filter"
|
||||||
|
|
||||||
|
type filterIterator struct {
|
||||||
|
kern.IteratorBase
|
||||||
|
ctx kern.ExprContext
|
||||||
|
itSrc kern.Iterator
|
||||||
|
expr *scan.Term
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFilerIterator(ctx kern.ExprContext, itSrc kern.Iterator, filterExpr *scan.Term) (it *filterIterator) {
|
||||||
|
it = &filterIterator{
|
||||||
|
IteratorBase: kern.IteratorBase{},
|
||||||
|
ctx: ctx,
|
||||||
|
itSrc: itSrc,
|
||||||
|
expr: filterExpr,
|
||||||
|
}
|
||||||
|
it.IteratorBase.Reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *filterIterator) TypeName() string {
|
||||||
|
return filterIteratorType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *filterIterator) String() string {
|
||||||
|
return fmt.Sprintf("$(%s %s bool-expr)", it.itSrc, filterIteratorType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *filterIterator) Next() (item any, err error) {
|
||||||
|
var attempt, result any
|
||||||
|
|
||||||
|
ctx := it.ctx
|
||||||
|
|
||||||
|
for attempt, err = it.itSrc.Next(); err == nil; attempt, err = it.itSrc.Next() {
|
||||||
|
ctx.SetVar("_", attempt)
|
||||||
ctx.SetVar("__", it.Index())
|
ctx.SetVar("__", it.Index())
|
||||||
ctx.SetVar("_#", it.Count())
|
ctx.SetVar("#", it.Count())
|
||||||
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
result, err = it.expr.Compute(ctx)
|
||||||
if success, valid := kern.ToBool(rightValue); valid {
|
ctx.DeleteVar("#")
|
||||||
if success {
|
|
||||||
values.AppendItem(item)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("filter expression must return a boolean or a castable to boolean, got %v [%T]", rightValue, rightValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ctx.DeleteVar("_#")
|
|
||||||
ctx.DeleteVar("__")
|
ctx.DeleteVar("__")
|
||||||
ctx.DeleteVar("_")
|
ctx.DeleteVar("_")
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if success, valid := boolean.ToBool(result); valid {
|
||||||
|
if success {
|
||||||
|
item = attempt
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("filter expression must return a boolean or a castable to boolean, got %v [%T]", result, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
it.SetCurrent(item)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// func (it *filterIterator) Reset() (err error) {
|
||||||
|
// err = it.itLeft.Reset()
|
||||||
|
// it.IteratorBase.Reset()
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (it *filterIterator) CallOperation(name string, args map[string]any) (v any, err error) {
|
||||||
|
switch name {
|
||||||
|
case kern.NextName:
|
||||||
|
v, err = it.Next()
|
||||||
|
// case kern.ResetName:
|
||||||
|
// err = it.Reset()
|
||||||
|
// case kern.CleanName:
|
||||||
|
// err = it.Clean()
|
||||||
|
case kern.IndexName:
|
||||||
|
v = it.Index()
|
||||||
|
case kern.CurrentName:
|
||||||
|
v, err = it.Current()
|
||||||
|
case kern.CountName:
|
||||||
|
v = it.Count()
|
||||||
|
default:
|
||||||
|
err = kern.ErrNoOperation(name)
|
||||||
}
|
}
|
||||||
v = values
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymKwFilter, newFilterTerm)
|
scan.RegisterTermConstructor(sym.SymKwFilter, newFilterTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/fract"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- fraction term
|
// -------- fraction term
|
||||||
@@ -51,7 +53,7 @@ func evalFraction(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
num = -num
|
num = -num
|
||||||
}
|
}
|
||||||
if num != 0 {
|
if num != 0 {
|
||||||
if g := kern.Gcd(num, den); g != 1 {
|
if g := fract.Gcd(num, den); g != 1 {
|
||||||
num = num / g
|
num = num / g
|
||||||
den = den / g
|
den = den / g
|
||||||
}
|
}
|
||||||
@@ -59,11 +61,11 @@ func evalFraction(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
v = num
|
v = num
|
||||||
} else {
|
} else {
|
||||||
// v = &expr.FractionType{num, den}
|
// v = &expr.FractionType{num, den}
|
||||||
v = kern.NewFraction(num, den)
|
v = fract.NewFraction(num, den)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// v = &FractionType{0, den}
|
// v = &FractionType{0, den}
|
||||||
v = kern.NewFraction(0, den)
|
v = fract.NewFraction(0, den)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -71,5 +73,5 @@ func evalFraction(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
// registerTermConstructor(SymVertBar, newFractionTerm)
|
// registerTermConstructor(SymVertBar, newFractionTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymColon, newFractionTerm)
|
scan.RegisterTermConstructor(sym.SymColon, newFractionTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-11
@@ -11,6 +11,10 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/str"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- group by term
|
//-------- group by term
|
||||||
@@ -48,30 +52,34 @@ func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rightTk := opTerm.Children[1].Tk
|
rightTk := opTerm.Children[1].Tk
|
||||||
if rightTk.IsSymbol(scan.SymVariable) && rightTk.Source() == "__" {
|
if rightTk.IsSymbol(sym.SymVariable) && rightTk.Source() == "__" {
|
||||||
keyByIndex = true
|
keyByIndex = true
|
||||||
} else if rightValue, err = opTerm.Children[1].Compute(ctx); err != nil {
|
} else if rightValue, err = opTerm.Children[1].Compute(ctx); err != nil {
|
||||||
return
|
return
|
||||||
} else if kern.IsString(rightValue) {
|
} else if str.IsString(rightValue) {
|
||||||
sKey = rightValue.(string)
|
sKey = rightValue.(string)
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("right operand of GROUPBY must be a string or identifier '__'; got %s", kern.TypeName(rightValue))
|
return nil, fmt.Errorf("right operand of GROUPBY must be a string or identifier '__'; got %s", kern.TypeName(rightValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
values := kern.MakeDict()
|
values := dict.MakeDict()
|
||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
||||||
ctx.SetVar("_", item)
|
ctx.SetVar("_", item)
|
||||||
ctx.SetVar("__", it.Index())
|
ctx.SetVar("__", it.Index())
|
||||||
ctx.SetVar("_#", it.Count())
|
ctx.SetVar("#", it.Count())
|
||||||
|
|
||||||
var sItemKey string
|
var sItemKey string
|
||||||
|
|
||||||
if d, ok := item.(*kern.DictType); ok {
|
if d, ok := item.(*dict.DictType); ok {
|
||||||
if keyByIndex || len(sKey) == 0 {
|
if keyByIndex || len(sKey) == 0 {
|
||||||
sItemKey = strconv.Itoa(int(it.Index()))
|
sItemKey = strconv.Itoa(int(it.Index()))
|
||||||
} else if d.HasKey(sKey) {
|
} else if d.HasKey(sKey) {
|
||||||
if keyValue, exists := d.GetItem(sKey); exists {
|
if keyValue, exists := d.GetItem(sKey); exists {
|
||||||
sItemKey = fmt.Sprintf("%v", keyValue)
|
if s, ok := keyValue.(string); ok {
|
||||||
|
sItemKey = s
|
||||||
|
} else {
|
||||||
|
sItemKey = fmt.Sprintf("%v", keyValue)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
sItemKey = "_"
|
sItemKey = "_"
|
||||||
}
|
}
|
||||||
@@ -82,17 +90,17 @@ func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
sItemKey = strconv.Itoa(int(it.Index()))
|
sItemKey = strconv.Itoa(int(it.Index()))
|
||||||
}
|
}
|
||||||
|
|
||||||
var ls *kern.ListType
|
var ls *array.ArrayType
|
||||||
if lsAny, exists := values.GetItem(sItemKey); exists && lsAny != nil {
|
if lsAny, exists := values.GetItem(sItemKey); exists && lsAny != nil {
|
||||||
ls = lsAny.(*kern.ListType)
|
ls = lsAny.(*array.ArrayType)
|
||||||
}
|
}
|
||||||
if ls == nil {
|
if ls == nil {
|
||||||
ls = kern.NewListA()
|
ls = array.NewArrayA()
|
||||||
}
|
}
|
||||||
ls.AppendItem(item)
|
ls.AppendItem(item)
|
||||||
values.SetItem(sItemKey, ls)
|
values.SetItem(sItemKey, ls)
|
||||||
|
|
||||||
ctx.DeleteVar("_#")
|
ctx.DeleteVar("#")
|
||||||
ctx.DeleteVar("__")
|
ctx.DeleteVar("__")
|
||||||
ctx.DeleteVar("_")
|
ctx.DeleteVar("_")
|
||||||
}
|
}
|
||||||
@@ -105,5 +113,5 @@ func evalGroupBy(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymKwGroupBy, newGroupByTerm)
|
scan.RegisterTermConstructor(sym.SymKwGroupBy, newGroupByTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-6
@@ -7,6 +7,10 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/list"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- in term
|
//-------- in term
|
||||||
@@ -33,12 +37,15 @@ func evalIn(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsList(rightValue) {
|
if array.IsArray(rightValue) {
|
||||||
list, _ := rightValue.(*kern.ListType)
|
a, _ := rightValue.(*array.ArrayType)
|
||||||
v = list.IndexDeepSameCmp(leftValue) >= 0
|
v = a.IndexDeepSameCmp(leftValue) >= 0
|
||||||
} else if kern.IsDict(rightValue) {
|
} else if dict.IsDict(rightValue) {
|
||||||
dict, _ := rightValue.(*kern.DictType)
|
dict, _ := rightValue.(*dict.DictType)
|
||||||
v = dict.HasKey(leftValue)
|
v = dict.HasKey(leftValue)
|
||||||
|
} else if list.IsLinkedList(rightValue) {
|
||||||
|
ls, _ := rightValue.(*list.LinkedList)
|
||||||
|
v = ls.HasValue(leftValue)
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
@@ -47,5 +54,5 @@ func evalIn(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymKwIn, newInTerm)
|
scan.RegisterTermConstructor(sym.SymKwIn, newInTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-5
@@ -7,6 +7,9 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/str"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- include term
|
//-------- include term
|
||||||
@@ -29,9 +32,9 @@ func evalInclude(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
if kern.IsList(childValue) {
|
if array.IsArray(childValue) {
|
||||||
list, _ := childValue.(*kern.ListType)
|
a, _ := childValue.(*array.ArrayType)
|
||||||
for i, filePathSpec := range *list {
|
for i, filePathSpec := range *a {
|
||||||
if filePath, ok := filePathSpec.(string); ok {
|
if filePath, ok := filePathSpec.(string); ok {
|
||||||
if v, err = EvalFile(ctx, filePath); err == nil {
|
if v, err = EvalFile(ctx, filePath); err == nil {
|
||||||
count++
|
count++
|
||||||
@@ -44,7 +47,7 @@ func evalInclude(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if kern.IsString(childValue) {
|
} else if str.IsString(childValue) {
|
||||||
filePath, _ := childValue.(string)
|
filePath, _ := childValue.(string)
|
||||||
if v, err = EvalFile(ctx, filePath); err == nil {
|
if v, err = EvalFile(ctx, filePath); err == nil {
|
||||||
count++
|
count++
|
||||||
@@ -61,5 +64,5 @@ func evalInclude(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymKwInclude, newIncludeTerm)
|
scan.RegisterTermConstructor(sym.SymKwInclude, newIncludeTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+163
-65
@@ -5,8 +5,16 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/interval"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/list"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- index term
|
// -------- index term
|
||||||
@@ -20,15 +28,15 @@ func newIndexTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyKey(indexList *kern.ListType) (index any, err error) {
|
func verifyKey(indexList *array.ArrayType) (index any, err error) {
|
||||||
index = (*indexList)[0]
|
index = (*indexList)[0]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyIndex(indexTerm *scan.Term, indexList *kern.ListType, maxValue int) (index int, err error) {
|
func verifyIndex(indexTerm kern.Term, indexList *array.ArrayType, maxValue int64) (index int64, err error) {
|
||||||
var v int
|
var v int64
|
||||||
|
|
||||||
if v, err = kern.ToGoInt((*indexList)[0], "index expression"); err == nil {
|
if v, err = types.ToGoInt64((*indexList)[0], "index expression"); err == nil {
|
||||||
if v < 0 && v >= -maxValue {
|
if v < 0 && v >= -maxValue {
|
||||||
v = maxValue + v
|
v = maxValue + v
|
||||||
}
|
}
|
||||||
@@ -41,32 +49,32 @@ func verifyIndex(indexTerm *scan.Term, indexList *kern.ListType, maxValue int) (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyRange(indexTerm *scan.Term, indexList *kern.ListType, maxValue int) (startIndex, endIndex int, err error) {
|
// func verifyInterval(indexTerm kern.Term, indexList *array.ArrayType, maxValue int64) (startIndex, endIndex int64, err error) {
|
||||||
v, _ := ((*indexList)[0]).(*intPair)
|
// v, _ := ((*indexList)[0]).(*interval.IntervalType)
|
||||||
startIndex = v.a
|
// startIndex = v.Begin()
|
||||||
endIndex = v.b
|
// endIndex = v.End()
|
||||||
if endIndex == kern.ConstLastIndex {
|
// if endIndex == kern.ConstLastIndex {
|
||||||
endIndex = maxValue
|
// endIndex = int64(maxValue)
|
||||||
}
|
// }
|
||||||
if startIndex < 0 && startIndex >= -maxValue {
|
// if startIndex < 0 && startIndex >= -maxValue {
|
||||||
startIndex = maxValue + startIndex
|
// startIndex = int64(maxValue) + startIndex
|
||||||
}
|
// }
|
||||||
if endIndex < 0 && endIndex >= -maxValue {
|
// if endIndex < 0 && endIndex >= -maxValue {
|
||||||
endIndex = maxValue + endIndex
|
// endIndex = int64(maxValue) + endIndex
|
||||||
}
|
// }
|
||||||
if startIndex < 0 || startIndex > maxValue {
|
// if startIndex < 0 || startIndex > maxValue {
|
||||||
err = indexTerm.Errorf("range start-index %d is out of bounds", startIndex)
|
// err = indexTerm.Errorf("%s start-index %d is out of bounds", interval.TypeName, startIndex)
|
||||||
} else if endIndex < 0 || endIndex > maxValue {
|
// } else if endIndex < 0 || endIndex > maxValue {
|
||||||
err = indexTerm.Errorf("range end-index %d is out of bounds", endIndex)
|
// err = indexTerm.Errorf("%s end-index %d is out of bounds", interval.TypeName, endIndex)
|
||||||
} else if startIndex > endIndex {
|
// } else if startIndex > endIndex {
|
||||||
err = indexTerm.Errorf("range start-index %d must not be greater than end-index %d", startIndex, endIndex)
|
// err = indexTerm.Errorf("%s start-index %d must not be greater than end-index %d", interval.TypeName, startIndex, endIndex)
|
||||||
}
|
// }
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
var indexList *kern.ListType
|
var indexList *array.ArrayType
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||||
@@ -74,7 +82,7 @@ func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
indexTerm := opTerm.Children[1]
|
indexTerm := opTerm.Children[1]
|
||||||
if indexList, ok = rightValue.(*kern.ListType); !ok {
|
if indexList, ok = rightValue.(*array.ArrayType); !ok {
|
||||||
err = opTerm.Errorf("invalid index expression")
|
err = opTerm.Errorf("invalid index expression")
|
||||||
return
|
return
|
||||||
} else if len(*indexList) != 1 {
|
} else if len(*indexList) != 1 {
|
||||||
@@ -82,41 +90,12 @@ func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsInteger((*indexList)[0]) {
|
if types.IsInteger((*indexList)[0]) {
|
||||||
switch unboxedValue := leftValue.(type) {
|
v, err = collectionItemByIndex(opTerm, leftValue, rightValue, indexList)
|
||||||
case *kern.ListType:
|
} else if interval.IsInterval((*indexList)[0]) {
|
||||||
var index int
|
v, err = subCollectionByIndex(opTerm, leftValue, rightValue, indexList)
|
||||||
if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
} else if dict.IsDict(leftValue) {
|
||||||
v = (*unboxedValue)[index]
|
d := leftValue.(*dict.DictType)
|
||||||
}
|
|
||||||
case string:
|
|
||||||
var index int
|
|
||||||
if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil {
|
|
||||||
v = string(unboxedValue[index])
|
|
||||||
}
|
|
||||||
case *kern.DictType:
|
|
||||||
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
|
|
||||||
default:
|
|
||||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
} else if isIntPair((*indexList)[0]) {
|
|
||||||
switch unboxedValue := leftValue.(type) {
|
|
||||||
case *kern.ListType:
|
|
||||||
var start, end int
|
|
||||||
if start, end, err = verifyRange(indexTerm, indexList, len(*unboxedValue)); err == nil {
|
|
||||||
sublist := kern.ListType((*unboxedValue)[start:end])
|
|
||||||
v = &sublist
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
var start, end int
|
|
||||||
if start, end, err = verifyRange(indexTerm, indexList, len(unboxedValue)); err == nil {
|
|
||||||
v = unboxedValue[start:end]
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
} else if kern.IsDict(leftValue) {
|
|
||||||
d := leftValue.(*kern.DictType)
|
|
||||||
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
v, err = getDictItem(d, indexTerm, indexList, rightValue)
|
||||||
} else {
|
} else {
|
||||||
rightChild := opTerm.Children[1]
|
rightChild := opTerm.Children[1]
|
||||||
@@ -125,7 +104,126 @@ func evalIndex(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDictItem(d *kern.DictType, indexTerm *scan.Term, indexList *kern.ListType, rightValue any) (v any, err error) {
|
func collectionItemByIndex(opTerm kern.Term, leftValue, rightValue any, indexList *array.ArrayType) (v any, err error) {
|
||||||
|
indexTerm := opTerm.GetChild(1)
|
||||||
|
switch unboxedValue := leftValue.(type) {
|
||||||
|
case *array.ArrayType:
|
||||||
|
var index int64
|
||||||
|
if index, err = verifyIndex(indexTerm, indexList, int64(len(*unboxedValue))); err == nil {
|
||||||
|
v = (*unboxedValue)[index]
|
||||||
|
}
|
||||||
|
case *list.LinkedList:
|
||||||
|
var index int64
|
||||||
|
if index, err = verifyIndex(indexTerm, indexList, int64(unboxedValue.Len())); err == nil {
|
||||||
|
v, err = unboxedValue.At(index)
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
var index int64
|
||||||
|
if index, err = verifyIndex(indexTerm, indexList, int64(len(unboxedValue))); err == nil {
|
||||||
|
v = string(unboxedValue[index])
|
||||||
|
}
|
||||||
|
case *dict.DictType:
|
||||||
|
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
|
||||||
|
default:
|
||||||
|
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func subCollectionByIndex(opTerm kern.Term, leftValue, rightValue any, indexList *array.ArrayType) (v any, err error) {
|
||||||
|
triple := (*indexList)[0].(*interval.IntervalType)
|
||||||
|
switch unboxedValue := leftValue.(type) {
|
||||||
|
case *array.ArrayType:
|
||||||
|
v = intervalSubArray(unboxedValue, triple)
|
||||||
|
case *list.LinkedList:
|
||||||
|
v = intervalSubLinkedList(unboxedValue, triple)
|
||||||
|
case string:
|
||||||
|
v = intervalSubString(unboxedValue, triple)
|
||||||
|
default:
|
||||||
|
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func fixInterval(p *interval.IntervalType, maxLength int) (b, e, s int) {
|
||||||
|
b, e, s = p.ToIntTriple()
|
||||||
|
if b < 0 {
|
||||||
|
b = maxLength + b
|
||||||
|
}
|
||||||
|
if e < 0 {
|
||||||
|
e = maxLength + e //+ 1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func intervalSubString(source string, p *interval.IntervalType) (result string) {
|
||||||
|
b, e, s := fixInterval(p, len(source))
|
||||||
|
if b <= e {
|
||||||
|
e = min(e, len(source))
|
||||||
|
if s == 1 {
|
||||||
|
result = source[b:e]
|
||||||
|
} else {
|
||||||
|
var sb strings.Builder
|
||||||
|
for i := b; i < e; i += s {
|
||||||
|
sb.WriteByte(source[i])
|
||||||
|
}
|
||||||
|
result = sb.String()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var sb strings.Builder
|
||||||
|
for i := b; i >= e; i -= s {
|
||||||
|
sb.WriteByte(source[i])
|
||||||
|
}
|
||||||
|
result = sb.String()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func intervalSubArray(source *array.ArrayType, p *interval.IntervalType) (result *array.ArrayType) {
|
||||||
|
var a array.ArrayType
|
||||||
|
b, e, s := fixInterval(p, len(*source))
|
||||||
|
if b <= e {
|
||||||
|
e = min(e, len(*source))
|
||||||
|
if s == 1 {
|
||||||
|
a = (*source)[b:e]
|
||||||
|
} else {
|
||||||
|
size := (e-b)/s + 1
|
||||||
|
a = make(array.ArrayType, 0, size)
|
||||||
|
for i := b; i < e; i += s {
|
||||||
|
a = append(a, (*source)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
size := (b-e)/s + 1
|
||||||
|
a = make(array.ArrayType, 0, size)
|
||||||
|
for i := b; i > e; i -= s {
|
||||||
|
a = append(a, (*source)[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = &a
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func intervalSubLinkedList(source *list.LinkedList, p *interval.IntervalType) (result *list.LinkedList) {
|
||||||
|
b, e, s := fixInterval(p, source.Len())
|
||||||
|
revert := b > e
|
||||||
|
if revert {
|
||||||
|
b, e = e, b
|
||||||
|
source = source.Revert()
|
||||||
|
}
|
||||||
|
e = min(e, source.Len())
|
||||||
|
if s == 1 {
|
||||||
|
result = source.Sub(int64(b), int64(e))
|
||||||
|
} else {
|
||||||
|
result = source.SubStep(int64(b), int64(e), int64(s))
|
||||||
|
}
|
||||||
|
if revert {
|
||||||
|
source = source.Revert()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDictItem(d *dict.DictType, indexTerm kern.Term, indexList *array.ArrayType, rightValue any) (v any, err error) {
|
||||||
var ok bool
|
var ok bool
|
||||||
var indexValue any
|
var indexValue any
|
||||||
|
|
||||||
@@ -139,5 +237,5 @@ func getDictItem(d *kern.DictType, indexTerm *scan.Term, indexList *kern.ListTyp
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymIndex, newIndexTerm)
|
scan.RegisterTermConstructor(sym.SymIndex, newIndexTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+80
-36
@@ -7,6 +7,9 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/list"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- prepend term
|
//-------- prepend term
|
||||||
@@ -31,6 +34,44 @@ func newAppendTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prependToList(opTerm kern.Term, leftValue, rightValue any) (result any, err error) {
|
||||||
|
if a, ok := rightValue.(*array.ArrayType); ok {
|
||||||
|
var it kern.Iterator
|
||||||
|
if it, ok = leftValue.(kern.Iterator); !ok {
|
||||||
|
it = NewFormalIterator(leftValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
ls := list.NewLinkedList()
|
||||||
|
ls.SeqPushBack(it)
|
||||||
|
|
||||||
|
newArray := array.ArrayType(nil)
|
||||||
|
if newSize := len(*a) + int(ls.Len()); newSize > cap(*a) {
|
||||||
|
newArray = make([]any, 0, newSize)
|
||||||
|
for node := ls.FirstNode(); node != nil; node = node.Next() {
|
||||||
|
newArray = append(newArray, node.Data())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, item := range *a {
|
||||||
|
newArray = append(newArray, item)
|
||||||
|
}
|
||||||
|
if opTerm.GetRightChild().IsVar() {
|
||||||
|
// Update the original array if the right operand is a variable
|
||||||
|
*a = newArray
|
||||||
|
}
|
||||||
|
result = &newArray
|
||||||
|
} else if ll, ok := rightValue.(*list.LinkedList); ok {
|
||||||
|
var it kern.Iterator
|
||||||
|
if it, ok = leftValue.(kern.Iterator); !ok {
|
||||||
|
it = NewFormalIterator(leftValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ll.SeqPushBack(it)
|
||||||
|
} else {
|
||||||
|
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func evalPrepend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
func evalPrepend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
var leftValue, rightValue any
|
||||||
|
|
||||||
@@ -38,13 +79,42 @@ func evalPrepend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsList(rightValue) {
|
v, err = prependToList(opTerm, leftValue, rightValue)
|
||||||
list, _ := rightValue.(*kern.ListType)
|
return
|
||||||
newList := append(kern.ListType{leftValue}, *list...)
|
}
|
||||||
v = &newList
|
|
||||||
if opTerm.Children[1].Symbol() == scan.SymVariable {
|
func appendToList(opTerm kern.Term, leftValue, rightValue any) (result any, err error) {
|
||||||
ctx.UnsafeSetVar(opTerm.Children[1].Source(), v)
|
if a, ok := leftValue.(*array.ArrayType); ok {
|
||||||
|
var it kern.Iterator
|
||||||
|
if it, ok = rightValue.(kern.Iterator); !ok {
|
||||||
|
it = NewFormalIterator(rightValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ls := list.NewLinkedList()
|
||||||
|
ls.SeqPushBack(it)
|
||||||
|
|
||||||
|
newArray := *a
|
||||||
|
if newSize := len(*a) + int(ls.Len()); newSize > cap(*a) {
|
||||||
|
newArray = make([]any, 0, newSize)
|
||||||
|
for _, item := range *a {
|
||||||
|
newArray = append(newArray, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for node := ls.FirstNode(); node != nil; node = node.Next() {
|
||||||
|
newArray = append(newArray, node.Data())
|
||||||
|
}
|
||||||
|
if opTerm.GetLeftChild().IsVar() {
|
||||||
|
// Update the original array if the left operand is a variable
|
||||||
|
*a = newArray
|
||||||
|
}
|
||||||
|
result = &newArray
|
||||||
|
} else if ll, ok := leftValue.(*list.LinkedList); ok {
|
||||||
|
var it kern.Iterator
|
||||||
|
if it, ok = rightValue.(kern.Iterator); !ok {
|
||||||
|
it = NewFormalIterator(rightValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ll.SeqPushBack(it)
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
@@ -58,39 +128,13 @@ func evalAppend(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsList(leftValue) {
|
v, err = appendToList(opTerm, leftValue, rightValue)
|
||||||
list, _ := leftValue.(*kern.ListType)
|
|
||||||
newList := append(*list, rightValue)
|
|
||||||
v = &newList
|
|
||||||
if opTerm.Children[0].Symbol() == scan.SymVariable {
|
|
||||||
ctx.UnsafeSetVar(opTerm.Children[0].Source(), v)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// func evalAssignAppend(ctx ExprContext, self *term) (v any, err error) {
|
|
||||||
// var leftValue, rightValue any
|
|
||||||
|
|
||||||
// if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if IsList(leftValue) {
|
|
||||||
// list, _ := leftValue.(*ListType)
|
|
||||||
// newList := append(*list, rightValue)
|
|
||||||
// v = &newList
|
|
||||||
// if
|
|
||||||
// } else {
|
|
||||||
// err = self.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymPlusGreater, newPrependTerm)
|
scan.RegisterTermConstructor(sym.SymPlusGreater, newPrependTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymLessPlus, newAppendTerm)
|
scan.RegisterTermConstructor(sym.SymLessPlus, newAppendTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operator-interval.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/interval"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -------- interval term
|
||||||
|
|
||||||
|
func newIntervalTerm(tk *scan.Token) (inst *scan.Term) {
|
||||||
|
return &scan.Term{
|
||||||
|
Tk: *tk,
|
||||||
|
Children: make([]*scan.Term, 0, 2),
|
||||||
|
Position: scan.PosInfix,
|
||||||
|
Priority: scan.PriInterval,
|
||||||
|
EvalFunc: evalInterval,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func changeColonToInterval(t *scan.Term) {
|
||||||
|
if t.IsSymbol(sym.SymDoubleDot) {
|
||||||
|
t.SetSymbol(sym.SymInterval)
|
||||||
|
t.EvalFunc = evalInterval
|
||||||
|
if child := t.GetLeftChild(); child != nil && child.IsSymbol(sym.SymDoubleDot) {
|
||||||
|
child.SetSymbol(sym.SymInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalInterval(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
|
var begin, end int64
|
||||||
|
|
||||||
|
step := int64(1)
|
||||||
|
switch opTerm.GetChildCount() {
|
||||||
|
case 0:
|
||||||
|
begin = 0
|
||||||
|
end = int64(kern.ConstLastIndex)
|
||||||
|
case 1:
|
||||||
|
var leftValue any
|
||||||
|
if leftValue, err = opTerm.GetChild(0).Compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if begin, err = checkIntervalComponent(opTerm, leftValue); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
end = int64(kern.ConstLastIndex)
|
||||||
|
default:
|
||||||
|
var beginValue, endValue, stepValue any
|
||||||
|
subTerm := opTerm.GetLeftChild()
|
||||||
|
if subTerm.IsOneOf(sym.SymInterval, sym.SymDoubleDot) {
|
||||||
|
if beginValue, endValue, err = subTerm.EvalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if stepValue, err = opTerm.GetRightChild().Compute(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
step, err = checkIntervalComponent(opTerm.GetRightChild(), stepValue)
|
||||||
|
} else if beginValue, endValue, err = opTerm.EvalInfix(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if begin, err = checkIntervalComponent(opTerm, beginValue); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if end, err = checkIntervalComponent(opTerm, endValue); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if step <= 0 {
|
||||||
|
err = opTerm.Errorf("invalid %s specification: step must be positive", interval.TypeName)
|
||||||
|
} else {
|
||||||
|
v = interval.NewInterval(begin, end, step)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkIntervalComponent(t kern.Term, value any) (component int64, err error) {
|
||||||
|
if component, err = types.ToGoInt64(value, "%s expression"); err != nil {
|
||||||
|
err = t.Errorf(err.Error(), interval.TypeName)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// func errIntervalInvalidSpecification(t kern.Term) error {
|
||||||
|
// return t.Errorf("invalid %s specification", interval.TypeName)
|
||||||
|
// }
|
||||||
|
|
||||||
|
func errIntervalUnexpectedExpression(t *scan.Term) error {
|
||||||
|
return t.Errorf("unexpected %s expression", interval.TypeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
func init() {
|
||||||
|
scan.RegisterTermConstructor(sym.SymInterval, newIntervalTerm)
|
||||||
|
scan.RegisterTermConstructor(sym.SymDoubleDot, newIntervalTerm)
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- iter value term
|
//-------- iter value term
|
||||||
@@ -39,5 +40,5 @@ func evalIterValue(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
// registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymDereference, newIterValueTerm)
|
scan.RegisterTermConstructor(sym.SymDereference, newIterValueTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-join.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
|
||||||
)
|
|
||||||
|
|
||||||
//-------- join term
|
|
||||||
|
|
||||||
func newJoinTerm(tk *scan.Token) (inst *scan.Term) {
|
|
||||||
return &scan.Term{
|
|
||||||
Tk: *tk,
|
|
||||||
Children: make([]*scan.Term, 0, 2),
|
|
||||||
Position: scan.PosInfix,
|
|
||||||
Priority: scan.PriIterOp,
|
|
||||||
EvalFunc: evalJoin,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalJoin(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
var itLeft, itRight kern.Iterator
|
|
||||||
var item any
|
|
||||||
var ok bool
|
|
||||||
|
|
||||||
if err = opTerm.CheckOperands(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if rightValue, err = opTerm.Children[1].Compute(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if itLeft, ok = leftValue.(kern.Iterator); !ok {
|
|
||||||
if itLeft, err = NewIterator(ctx, leftValue, nil); err != nil {
|
|
||||||
return nil, fmt.Errorf("left operand of JOIN must be an iterable data-source; got %s", kern.TypeName(leftValue))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if itRight, ok = rightValue.(kern.Iterator); !ok {
|
|
||||||
if itRight, err = NewIterator(ctx, rightValue, nil); err != nil {
|
|
||||||
return nil, fmt.Errorf("right operand of JOIN must be an iterable data-source; got %s", kern.TypeName(rightValue))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
values := kern.NewListA()
|
|
||||||
for _, it := range []kern.Iterator{itLeft, itRight} {
|
|
||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
|
||||||
values.AppendItem(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
v = values
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
scan.RegisterTermConstructor(scan.SymKwJoin, newJoinTerm)
|
|
||||||
}
|
|
||||||
+13
-12
@@ -7,6 +7,11 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/list"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/str"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- length term
|
//-------- length term
|
||||||
@@ -28,23 +33,19 @@ func evalLength(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsList(childValue) {
|
if array.IsArray(childValue) {
|
||||||
ls, _ := childValue.(*kern.ListType)
|
ls, _ := childValue.(*array.ArrayType)
|
||||||
v = int64(len(*ls))
|
v = int64(len(*ls))
|
||||||
} else if kern.IsString(childValue) {
|
} else if str.IsString(childValue) {
|
||||||
s, _ := childValue.(string)
|
s, _ := childValue.(string)
|
||||||
v = int64(len(s))
|
v = int64(len(s))
|
||||||
} else if kern.IsDict(childValue) {
|
} else if dict.IsDict(childValue) {
|
||||||
m, _ := childValue.(*kern.DictType)
|
m, _ := childValue.(*dict.DictType)
|
||||||
v = int64(len(*m))
|
v = int64(len(*m))
|
||||||
|
} else if lls, ok := childValue.(*list.LinkedList); ok {
|
||||||
|
v = int64(lls.Len())
|
||||||
} else if it, ok := childValue.(kern.Iterator); ok {
|
} else if it, ok := childValue.(kern.Iterator); ok {
|
||||||
v = int64(it.Count())
|
v = int64(it.Count())
|
||||||
// if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
|
|
||||||
// count, _ := extIt.CallOperation(CountName, nil)
|
|
||||||
// v, _ = ToGoInt(count, "")
|
|
||||||
// } else {
|
|
||||||
// v = int64(it.Index() + 1)
|
|
||||||
// }
|
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
err = opTerm.ErrIncompatiblePrefixPostfixType(childValue)
|
||||||
}
|
}
|
||||||
@@ -53,5 +54,5 @@ func evalLength(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymHash, newLengthTerm)
|
scan.RegisterTermConstructor(sym.SymHash, newLengthTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-25
@@ -6,10 +6,10 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- map term
|
//-------- map term
|
||||||
@@ -25,9 +25,10 @@ func newMapTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func evalMap(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
func evalMap(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
var leftValue, rightValue any
|
// var leftValue, rightValue any
|
||||||
|
var leftValue any
|
||||||
var it kern.Iterator
|
var it kern.Iterator
|
||||||
var item any
|
// var item any
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|
||||||
if err = opTerm.CheckOperands(); err != nil {
|
if err = opTerm.CheckOperands(); err != nil {
|
||||||
@@ -44,31 +45,11 @@ func evalMap(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
values := kern.NewListA()
|
v, err = NewIterIter(it, ctx, opTerm.Children[1:])
|
||||||
for item, err = it.Next(); err == nil; item, err = it.Next() {
|
|
||||||
ctx.SetVar("_", item)
|
|
||||||
ctx.SetVar("__", it.Index())
|
|
||||||
ctx.SetVar("_#", it.Count())
|
|
||||||
if rightValue, err = opTerm.Children[1].Compute(ctx); err == nil {
|
|
||||||
values.AppendItem(rightValue)
|
|
||||||
}
|
|
||||||
ctx.DeleteVar("_#")
|
|
||||||
ctx.DeleteVar("__")
|
|
||||||
ctx.DeleteVar("_")
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
v = values
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymKwMap, newMapTerm)
|
scan.RegisterTermConstructor(sym.SymKwMap, newMapTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -7,6 +7,7 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- plugin term
|
//-------- plugin term
|
||||||
@@ -37,5 +38,5 @@ func evalPlugin(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymKwPlugin, newPluginTerm)
|
scan.RegisterTermConstructor(sym.SymKwPlugin, newPluginTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- post increment term
|
// -------- post increment term
|
||||||
@@ -31,7 +33,7 @@ func evalPostInc(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
var namePrefix string
|
var namePrefix string
|
||||||
v, err = it.Next()
|
v, err = it.Next()
|
||||||
|
|
||||||
if opTerm.Children[0].Symbol() == scan.SymVariable {
|
if opTerm.Children[0].Symbol() == sym.SymVariable {
|
||||||
namePrefix = opTerm.Children[0].Source()
|
namePrefix = opTerm.Children[0].Source()
|
||||||
}
|
}
|
||||||
ctx.UnsafeSetVar(namePrefix+"_index", it.Index())
|
ctx.UnsafeSetVar(namePrefix+"_index", it.Index())
|
||||||
@@ -49,7 +51,7 @@ func evalPostInc(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
ctx.UnsafeSetVar(namePrefix+"_value", v1)
|
ctx.UnsafeSetVar(namePrefix+"_value", v1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable {
|
} else if types.IsInteger(childValue) && opTerm.Children[0].Symbol() == sym.SymVariable {
|
||||||
v = childValue
|
v = childValue
|
||||||
i, _ := childValue.(int64)
|
i, _ := childValue.(int64)
|
||||||
ctx.SetVar(opTerm.Children[0].Source(), i+1)
|
ctx.SetVar(opTerm.Children[0].Source(), i+1)
|
||||||
@@ -79,7 +81,7 @@ func evalPostDec(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
/* if it, ok := childValue.(Iterator); ok {
|
/* if it, ok := childValue.(Iterator); ok {
|
||||||
v, err = it.Next()
|
v, err = it.Next()
|
||||||
} else */if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable {
|
} else */if types.IsInteger(childValue) && opTerm.Children[0].Symbol() == sym.SymVariable {
|
||||||
v = childValue
|
v = childValue
|
||||||
i, _ := childValue.(int64)
|
i, _ := childValue.(int64)
|
||||||
ctx.SetVar(opTerm.Children[0].Source(), i-1)
|
ctx.SetVar(opTerm.Children[0].Source(), i-1)
|
||||||
@@ -91,6 +93,6 @@ func evalPostDec(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymDoublePlus, newPostIncTerm)
|
scan.RegisterTermConstructor(sym.SymDoublePlus, newPostIncTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymDoubleMinus, newPostDecTerm)
|
scan.RegisterTermConstructor(sym.SymDoubleMinus, newPostDecTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// -------- pre increment term
|
// -------- pre increment term
|
||||||
@@ -27,7 +29,7 @@ func evalPreInc(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable {
|
if types.IsInteger(childValue) && opTerm.Children[0].Symbol() == sym.SymVariable {
|
||||||
i := childValue.(int64) + 1
|
i := childValue.(int64) + 1
|
||||||
ctx.SetVar(opTerm.Children[0].Source(), i)
|
ctx.SetVar(opTerm.Children[0].Source(), i)
|
||||||
v = i
|
v = i
|
||||||
@@ -56,7 +58,7 @@ func evalPreDec(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsInteger(childValue) && opTerm.Children[0].Symbol() == scan.SymVariable {
|
if types.IsInteger(childValue) && opTerm.Children[0].Symbol() == sym.SymVariable {
|
||||||
i := childValue.(int64) - 1
|
i := childValue.(int64) - 1
|
||||||
ctx.SetVar(opTerm.Children[0].Source(), i)
|
ctx.SetVar(opTerm.Children[0].Source(), i)
|
||||||
v = i
|
v = i
|
||||||
@@ -68,6 +70,6 @@ func evalPreDec(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymPreInc, newPreIncTerm)
|
scan.RegisterTermConstructor(sym.SymPreInc, newPreIncTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymPreDec, newPreDecTerm)
|
scan.RegisterTermConstructor(sym.SymPreDec, newPreDecTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-24
@@ -9,6 +9,12 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/float"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/fract"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/str"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- multiply term
|
//-------- multiply term
|
||||||
@@ -24,15 +30,15 @@ func newMultiplyTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func mulValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
func mulValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||||
if kern.IsString(leftValue) && kern.IsInteger(rightValue) {
|
if str.IsString(leftValue) && types.IsInteger(rightValue) {
|
||||||
s, _ := leftValue.(string)
|
s, _ := leftValue.(string)
|
||||||
n, _ := rightValue.(int64)
|
n, _ := rightValue.(int64)
|
||||||
v = strings.Repeat(s, int(n))
|
v = strings.Repeat(s, int(n))
|
||||||
} else if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
|
} else if types.IsNumOrFract(leftValue) && types.IsNumOrFract(rightValue) {
|
||||||
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
|
if float.IsFloat(leftValue) || float.IsFloat(rightValue) {
|
||||||
v = kern.NumAsFloat(leftValue) * kern.NumAsFloat(rightValue)
|
v = types.NumAsFloat(leftValue) * types.NumAsFloat(rightValue)
|
||||||
} else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) {
|
} else if fract.IsFraction(leftValue) || fract.IsFraction(rightValue) {
|
||||||
v, err = kern.MulAnyFract(leftValue, rightValue)
|
v, err = fract.MulAnyFract(leftValue, rightValue)
|
||||||
} else {
|
} else {
|
||||||
leftInt, _ := leftValue.(int64)
|
leftInt, _ := leftValue.(int64)
|
||||||
rightInt, _ := rightValue.(int64)
|
rightInt, _ := rightValue.(int64)
|
||||||
@@ -67,16 +73,16 @@ func newDivideTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func divValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
func divValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||||
if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
|
if types.IsNumOrFract(leftValue) && types.IsNumOrFract(rightValue) {
|
||||||
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
|
if float.IsFloat(leftValue) || float.IsFloat(rightValue) {
|
||||||
d := kern.NumAsFloat(rightValue)
|
d := types.NumAsFloat(rightValue)
|
||||||
if d == 0.0 {
|
if d == 0.0 {
|
||||||
err = opTerm.ErrDivisionByZero()
|
err = opTerm.ErrDivisionByZero()
|
||||||
} else {
|
} else {
|
||||||
v = kern.NumAsFloat(leftValue) / d
|
v = types.NumAsFloat(leftValue) / d
|
||||||
}
|
}
|
||||||
} else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) {
|
} else if fract.IsFraction(leftValue) || fract.IsFraction(rightValue) {
|
||||||
v, err = kern.DivAnyFract(leftValue, rightValue)
|
v, err = fract.DivAnyFract(leftValue, rightValue)
|
||||||
} else {
|
} else {
|
||||||
leftInt, _ := leftValue.(int64)
|
leftInt, _ := leftValue.(int64)
|
||||||
if rightInt, _ := rightValue.(int64); rightInt == 0 {
|
if rightInt, _ := rightValue.(int64); rightInt == 0 {
|
||||||
@@ -85,11 +91,11 @@ func divValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error)
|
|||||||
v = leftInt / rightInt
|
v = leftInt / rightInt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if kern.IsString(leftValue) && kern.IsString(rightValue) {
|
} else if str.IsString(leftValue) && str.IsString(rightValue) {
|
||||||
source := leftValue.(string)
|
source := leftValue.(string)
|
||||||
sep := rightValue.(string)
|
sep := rightValue.(string)
|
||||||
v = kern.ListFromStrings(strings.Split(source, sep))
|
v = array.ArrayFromStrings(strings.Split(source, sep))
|
||||||
} else if kern.IsString(leftValue) && kern.IsInteger(rightValue) {
|
} else if str.IsString(leftValue) && types.IsInteger(rightValue) {
|
||||||
source := leftValue.(string)
|
source := leftValue.(string)
|
||||||
partSize := int(rightValue.(int64))
|
partSize := int(rightValue.(int64))
|
||||||
if partSize == 0 {
|
if partSize == 0 {
|
||||||
@@ -108,7 +114,7 @@ func divValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error)
|
|||||||
if remainder > 0 {
|
if remainder > 0 {
|
||||||
parts = append(parts, source[len(source)-remainder:])
|
parts = append(parts, source[len(source)-remainder:])
|
||||||
}
|
}
|
||||||
v = kern.NewList(parts)
|
v = array.NewArray(parts)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
err = opTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||||
@@ -145,12 +151,12 @@ func evalDivideAsFloat(ctx kern.ExprContext, floatDivTerm *scan.Term) (v any, er
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
|
if types.IsNumOrFract(leftValue) && types.IsNumOrFract(rightValue) {
|
||||||
d := kern.NumAsFloat(rightValue)
|
d := types.NumAsFloat(rightValue)
|
||||||
if d == 0.0 {
|
if d == 0.0 {
|
||||||
err = floatDivTerm.ErrDivisionByZero()
|
err = floatDivTerm.ErrDivisionByZero()
|
||||||
} else {
|
} else {
|
||||||
v = kern.NumAsFloat(leftValue) / d
|
v = types.NumAsFloat(leftValue) / d
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = floatDivTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
err = floatDivTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||||
@@ -170,7 +176,7 @@ func newRemainderTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
func remainderValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
func remainderValues(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||||
if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) {
|
if types.IsInteger(leftValue) && types.IsInteger(rightValue) {
|
||||||
rightInt, _ := rightValue.(int64)
|
rightInt, _ := rightValue.(int64)
|
||||||
if rightInt == 0 {
|
if rightInt == 0 {
|
||||||
err = opTerm.ErrDivisionByZero()
|
err = opTerm.ErrDivisionByZero()
|
||||||
@@ -196,8 +202,8 @@ func evalRemainder(ctx kern.ExprContext, remainderTerm *scan.Term) (v any, err e
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymStar, newMultiplyTerm)
|
scan.RegisterTermConstructor(sym.SymStar, newMultiplyTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymSlash, newDivideTerm)
|
scan.RegisterTermConstructor(sym.SymSlash, newDivideTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymDotSlash, newDivideAsFloatTerm)
|
scan.RegisterTermConstructor(sym.SymDotSlash, newDivideAsFloatTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymPercent, newRemainderTerm)
|
scan.RegisterTermConstructor(sym.SymPercent, newRemainderTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
||||||
// All rights reserved.
|
|
||||||
|
|
||||||
// operator-range.go
|
|
||||||
package expr
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
|
||||||
)
|
|
||||||
|
|
||||||
// -------- range term
|
|
||||||
type intPair struct {
|
|
||||||
a, b int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *intPair) TypeName() string {
|
|
||||||
return kern.TypePair
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *intPair) ToString(opt kern.FmtOpt) string {
|
|
||||||
return fmt.Sprintf("(%d, %d)", p.a, p.b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isIntPair(v any) bool {
|
|
||||||
_, ok := v.(*intPair)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func newRangeTerm(tk *scan.Token) (inst *scan.Term) {
|
|
||||||
return &scan.Term{
|
|
||||||
Tk: *tk,
|
|
||||||
Children: make([]*scan.Term, 0, 2),
|
|
||||||
Position: scan.PosInfix,
|
|
||||||
Priority: scan.PriRange,
|
|
||||||
EvalFunc: evalRange,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func changeColonToRange(t *scan.Term) {
|
|
||||||
if t.Tk.IsSymbol(scan.SymColon) {
|
|
||||||
t.Tk.Sym = scan.SymRange
|
|
||||||
t.EvalFunc = evalRange
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func evalRange(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|
||||||
var leftValue, rightValue any
|
|
||||||
|
|
||||||
if len(opTerm.Children) == 0 {
|
|
||||||
leftValue = int64(0)
|
|
||||||
rightValue = int64(-1)
|
|
||||||
} else if len(opTerm.Children) == 1 {
|
|
||||||
if leftValue, err = opTerm.Children[0].Compute(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rightValue = int64(kern.ConstLastIndex)
|
|
||||||
} else if leftValue, rightValue, err = opTerm.EvalInfix(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !(kern.IsInteger(leftValue) && kern.IsInteger(rightValue)) {
|
|
||||||
// err = opTerm.errIncompatibleTypes(leftValue, rightValue)
|
|
||||||
err = errRangeInvalidSpecification(opTerm)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
startIndex, _ := leftValue.(int64)
|
|
||||||
endIndex, _ := rightValue.(int64)
|
|
||||||
|
|
||||||
v = &intPair{int(startIndex), int(endIndex)}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func errRangeInvalidSpecification(t *scan.Term) error {
|
|
||||||
return t.Errorf("invalid range specification")
|
|
||||||
}
|
|
||||||
|
|
||||||
func errRangeUnexpectedExpression(t *scan.Term) error {
|
|
||||||
return t.Errorf("unexpected range expression")
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
|
||||||
func init() {
|
|
||||||
scan.RegisterTermConstructor(scan.SymRange, newRangeTerm)
|
|
||||||
}
|
|
||||||
+30
-24
@@ -9,6 +9,12 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/boolean"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/fract"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/str"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- equal term
|
//-------- equal term
|
||||||
@@ -26,22 +32,22 @@ func newEqualTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
// type deepFuncTemplate func(a, b any) (eq bool, err error)
|
// type deepFuncTemplate func(a, b any) (eq bool, err error)
|
||||||
|
|
||||||
func equals(a, b any, deepCmp kern.DeepFuncTemplate) (eq bool, err error) {
|
func equals(a, b any, deepCmp kern.DeepFuncTemplate) (eq bool, err error) {
|
||||||
if kern.IsNumOrFract(a) && kern.IsNumOrFract(b) {
|
if types.IsNumOrFract(a) && types.IsNumOrFract(b) {
|
||||||
if kern.IsNumber(a) && kern.IsNumber(b) {
|
if types.IsNumber(a) && types.IsNumber(b) {
|
||||||
if kern.IsInteger(a) && kern.IsInteger(b) {
|
if types.IsInteger(a) && types.IsInteger(b) {
|
||||||
li, _ := a.(int64)
|
li, _ := a.(int64)
|
||||||
ri, _ := b.(int64)
|
ri, _ := b.(int64)
|
||||||
eq = li == ri
|
eq = li == ri
|
||||||
} else {
|
} else {
|
||||||
eq = kern.NumAsFloat(a) == kern.NumAsFloat(b)
|
eq = types.NumAsFloat(a) == types.NumAsFloat(b)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var cmp int
|
var cmp int
|
||||||
if cmp, err = kern.CmpAnyFract(a, b); err == nil {
|
if cmp, err = fract.CmpAnyFract(a, b); err == nil {
|
||||||
eq = cmp == 0
|
eq = cmp == 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if deepCmp != nil && kern.IsList(a) && kern.IsList(b) {
|
} else if deepCmp != nil && array.IsArray(a) && array.IsArray(b) {
|
||||||
eq, err = deepCmp(a, b)
|
eq, err = deepCmp(a, b)
|
||||||
} else {
|
} else {
|
||||||
eq = reflect.DeepEqual(a, b)
|
eq = reflect.DeepEqual(a, b)
|
||||||
@@ -75,7 +81,7 @@ func newNotEqualTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
|
|
||||||
func evalNotEqual(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
func evalNotEqual(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
||||||
if v, err = evalEqual(ctx, opTerm); err == nil {
|
if v, err = evalEqual(ctx, opTerm); err == nil {
|
||||||
b, _ := kern.ToBool(v)
|
b, _ := boolean.ToBool(v)
|
||||||
v = !b
|
v = !b
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -94,29 +100,29 @@ func newLessTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func lessThan(self *scan.Term, a, b any) (isLess bool, err error) {
|
func lessThan(self *scan.Term, a, b any) (isLess bool, err error) {
|
||||||
if kern.IsNumOrFract(a) && kern.IsNumOrFract(b) {
|
if types.IsNumOrFract(a) && types.IsNumOrFract(b) {
|
||||||
if kern.IsNumber(a) && kern.IsNumber(b) {
|
if types.IsNumber(a) && types.IsNumber(b) {
|
||||||
if kern.IsInteger(a) && kern.IsInteger(b) {
|
if types.IsInteger(a) && types.IsInteger(b) {
|
||||||
li, _ := a.(int64)
|
li, _ := a.(int64)
|
||||||
ri, _ := b.(int64)
|
ri, _ := b.(int64)
|
||||||
isLess = li < ri
|
isLess = li < ri
|
||||||
} else {
|
} else {
|
||||||
isLess = kern.NumAsFloat(a) < kern.NumAsFloat(b)
|
isLess = types.NumAsFloat(a) < types.NumAsFloat(b)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var cmp int
|
var cmp int
|
||||||
if cmp, err = kern.CmpAnyFract(a, b); err == nil {
|
if cmp, err = fract.CmpAnyFract(a, b); err == nil {
|
||||||
isLess = cmp < 0
|
isLess = cmp < 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if kern.IsString(a) && kern.IsString(b) {
|
} else if str.IsString(a) && str.IsString(b) {
|
||||||
ls, _ := a.(string)
|
ls, _ := a.(string)
|
||||||
rs, _ := b.(string)
|
rs, _ := b.(string)
|
||||||
isLess = ls < rs
|
isLess = ls < rs
|
||||||
// Inclusion test
|
// Inclusion test
|
||||||
} else if kern.IsList(a) && kern.IsList(b) {
|
} else if array.IsArray(a) && array.IsArray(b) {
|
||||||
aList, _ := a.(*kern.ListType)
|
aList, _ := a.(*array.ArrayType)
|
||||||
bList, _ := b.(*kern.ListType)
|
bList, _ := b.(*array.ArrayType)
|
||||||
isLess = len(*aList) < len(*bList) && bList.Contains(aList)
|
isLess = len(*aList) < len(*bList) && bList.Contains(aList)
|
||||||
} else {
|
} else {
|
||||||
err = self.ErrIncompatibleTypes(a, b)
|
err = self.ErrIncompatibleTypes(a, b)
|
||||||
@@ -149,8 +155,8 @@ func newLessEqualTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
func lessThanOrEqual(self *scan.Term, a, b any) (isLessEq bool, err error) {
|
func lessThanOrEqual(self *scan.Term, a, b any) (isLessEq bool, err error) {
|
||||||
if isLessEq, err = lessThan(self, a, b); err == nil {
|
if isLessEq, err = lessThan(self, a, b); err == nil {
|
||||||
if !isLessEq {
|
if !isLessEq {
|
||||||
if kern.IsList(a) && kern.IsList(b) {
|
if array.IsArray(a) && array.IsArray(b) {
|
||||||
isLessEq, err = kern.SameContent(a, b)
|
isLessEq, err = array.SameContent(a, b)
|
||||||
} else {
|
} else {
|
||||||
isLessEq, err = equals(a, b, nil)
|
isLessEq, err = equals(a, b, nil)
|
||||||
}
|
}
|
||||||
@@ -219,10 +225,10 @@ func evalGreaterEqual(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymDoubleEqual, newEqualTerm)
|
scan.RegisterTermConstructor(sym.SymDoubleEqual, newEqualTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymNotEqual, newNotEqualTerm)
|
scan.RegisterTermConstructor(sym.SymNotEqual, newNotEqualTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymLess, newLessTerm)
|
scan.RegisterTermConstructor(sym.SymLess, newLessTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymLessOrEqual, newLessEqualTerm)
|
scan.RegisterTermConstructor(sym.SymLessOrEqual, newLessEqualTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymGreater, newGreaterTerm)
|
scan.RegisterTermConstructor(sym.SymGreater, newGreaterTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymGreaterOrEqual, newGreaterEqualTerm)
|
scan.RegisterTermConstructor(sym.SymGreaterOrEqual, newGreaterEqualTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- selector term
|
//-------- selector term
|
||||||
@@ -82,5 +83,5 @@ func evalSelector(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymSelector, newSelectorTerm)
|
scan.RegisterTermConstructor(sym.SymSelector, newSelectorTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-6
@@ -7,6 +7,8 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- bit right shift term
|
//-------- bit right shift term
|
||||||
@@ -22,7 +24,7 @@ func newRightShiftTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func bitRightShift(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
func bitRightShift(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||||
if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) {
|
if types.IsInteger(leftValue) && types.IsInteger(rightValue) {
|
||||||
leftInt := leftValue.(int64)
|
leftInt := leftValue.(int64)
|
||||||
rightInt := rightValue.(int64)
|
rightInt := rightValue.(int64)
|
||||||
v = leftInt >> rightInt
|
v = leftInt >> rightInt
|
||||||
@@ -39,7 +41,9 @@ func evalRightShift(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err = bitRightShift(opTerm, leftValue, rightValue)
|
if v, err = bitRightShift(opTerm, leftValue, rightValue); err != nil {
|
||||||
|
v, err = prependToList(opTerm, leftValue, rightValue)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +58,7 @@ func newLeftShiftTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func bitLeftShift(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
func bitLeftShift(opTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||||
if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) {
|
if types.IsInteger(leftValue) && types.IsInteger(rightValue) {
|
||||||
leftInt := leftValue.(int64)
|
leftInt := leftValue.(int64)
|
||||||
rightInt := rightValue.(int64)
|
rightInt := rightValue.(int64)
|
||||||
v = leftInt << rightInt
|
v = leftInt << rightInt
|
||||||
@@ -71,12 +75,14 @@ func evalLeftShift(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
v, err = bitLeftShift(opTerm, leftValue, rightValue)
|
if v, err = bitLeftShift(opTerm, leftValue, rightValue); err != nil {
|
||||||
|
v, err = appendToList(opTerm, leftValue, rightValue)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymDoubleGreater, newRightShiftTerm)
|
scan.RegisterTermConstructor(sym.SymDoubleGreater, newRightShiftTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymDoubleLess, newLeftShiftTerm)
|
scan.RegisterTermConstructor(sym.SymDoubleLess, newLeftShiftTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-6
@@ -7,6 +7,9 @@ package expr
|
|||||||
import (
|
import (
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/float"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- plus sign term
|
//-------- plus sign term
|
||||||
@@ -38,15 +41,15 @@ func evalSign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if kern.IsFloat(rightValue) {
|
if float.IsFloat(rightValue) {
|
||||||
if opTerm.Tk.Sym == scan.SymChangeSign {
|
if opTerm.Tk.Sym == sym.SymChangeSign {
|
||||||
f, _ := rightValue.(float64)
|
f, _ := rightValue.(float64)
|
||||||
v = -f
|
v = -f
|
||||||
} else {
|
} else {
|
||||||
v = rightValue
|
v = rightValue
|
||||||
}
|
}
|
||||||
} else if kern.IsInteger(rightValue) {
|
} else if types.IsInteger(rightValue) {
|
||||||
if opTerm.Tk.Sym == scan.SymChangeSign {
|
if opTerm.Tk.Sym == sym.SymChangeSign {
|
||||||
i, _ := rightValue.(int64)
|
i, _ := rightValue.(int64)
|
||||||
v = -i
|
v = -i
|
||||||
} else {
|
} else {
|
||||||
@@ -60,6 +63,6 @@ func evalSign(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymUnchangeSign, newPlusSignTerm)
|
scan.RegisterTermConstructor(sym.SymUnchangeSign, newPlusSignTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymChangeSign, newMinusSignTerm)
|
scan.RegisterTermConstructor(sym.SymChangeSign, newMinusSignTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-30
@@ -10,6 +10,14 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/dict"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/float"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/fract"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/list"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/str"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- plus term
|
//-------- plus term
|
||||||
@@ -25,39 +33,46 @@ func newPlusTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func sumValues(plusTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
func sumValues(plusTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||||
if (kern.IsString(leftValue) && kern.IsNumberString(rightValue)) || (kern.IsString(rightValue) && kern.IsNumberString(leftValue)) {
|
if (str.IsString(leftValue) && types.IsNumberString(rightValue)) || (str.IsString(rightValue) && types.IsNumberString(leftValue)) {
|
||||||
v = fmt.Sprintf("%v%v", leftValue, rightValue)
|
v = fmt.Sprintf("%v%v", leftValue, rightValue)
|
||||||
} else if kern.IsNumber(leftValue) && kern.IsNumber(rightValue) {
|
} else if types.IsNumber(leftValue) && types.IsNumber(rightValue) {
|
||||||
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
|
if float.IsFloat(leftValue) || float.IsFloat(rightValue) {
|
||||||
v = kern.NumAsFloat(leftValue) + kern.NumAsFloat(rightValue)
|
v = types.NumAsFloat(leftValue) + types.NumAsFloat(rightValue)
|
||||||
} else {
|
} else {
|
||||||
leftInt, _ := leftValue.(int64)
|
leftInt, _ := leftValue.(int64)
|
||||||
rightInt, _ := rightValue.(int64)
|
rightInt, _ := rightValue.(int64)
|
||||||
v = leftInt + rightInt
|
v = leftInt + rightInt
|
||||||
}
|
}
|
||||||
} else if kern.IsList(leftValue) && kern.IsList(rightValue) {
|
} else if array.IsArray(leftValue) && array.IsArray(rightValue) {
|
||||||
var leftList, rightList *kern.ListType
|
var leftList, rightList *array.ArrayType
|
||||||
leftList, _ = leftValue.(*kern.ListType)
|
leftList, _ = leftValue.(*array.ArrayType)
|
||||||
rightList, _ = rightValue.(*kern.ListType)
|
rightList, _ = rightValue.(*array.ArrayType)
|
||||||
|
|
||||||
sumList := make(kern.ListType, 0, len(*leftList)+len(*rightList))
|
sumList := make(array.ArrayType, 0, len(*leftList)+len(*rightList))
|
||||||
sumList = append(sumList, *leftList...)
|
sumList = append(sumList, *leftList...)
|
||||||
sumList = append(sumList, *rightList...)
|
sumList = append(sumList, *rightList...)
|
||||||
v = &sumList
|
v = &sumList
|
||||||
} else if (kern.IsFraction(leftValue) && kern.IsNumber(rightValue)) || (kern.IsFraction(rightValue) && kern.IsNumber(leftValue)) {
|
} else if list.IsLinkedList(leftValue) && list.IsLinkedList(rightValue) {
|
||||||
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
|
lsLeft := leftValue.(*list.LinkedList)
|
||||||
v = kern.NumAsFloat(leftValue) + kern.NumAsFloat(rightValue)
|
lsRight := rightValue.(*list.LinkedList)
|
||||||
|
ls := lsLeft.Clone()
|
||||||
|
ls.SeqClonePushBack(NewLinkedListIterator(lsRight, nil))
|
||||||
|
v = ls
|
||||||
|
} else if (fract.IsFraction(leftValue) && types.IsNumber(rightValue)) || (fract.IsFraction(rightValue) && types.IsNumber(leftValue)) {
|
||||||
|
if float.IsFloat(leftValue) || float.IsFloat(rightValue) {
|
||||||
|
v = types.NumAsFloat(leftValue) + types.NumAsFloat(rightValue)
|
||||||
} else {
|
} else {
|
||||||
v, err = kern.SumAnyFract(leftValue, rightValue)
|
v, err = fract.SumAnyFract(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
} else if kern.IsDict(leftValue) && kern.IsDict(rightValue) {
|
} else if dict.IsDict(leftValue) && dict.IsDict(rightValue) {
|
||||||
leftDict, _ := leftValue.(*kern.DictType)
|
leftDict, _ := leftValue.(*dict.DictType)
|
||||||
rightDict, _ := rightValue.(*kern.DictType)
|
rightDict, _ := rightValue.(*dict.DictType)
|
||||||
c := leftDict.Clone()
|
clone := leftDict.Clone()
|
||||||
|
c := clone.(*dict.DictType)
|
||||||
c.Merge(rightDict)
|
c.Merge(rightDict)
|
||||||
v = c
|
v = c
|
||||||
} else if kern.IsFraction(leftValue) && kern.IsFraction(rightValue) {
|
} else if fract.IsFraction(leftValue) && fract.IsFraction(rightValue) {
|
||||||
v, err = kern.SumAnyFract(leftValue, rightValue)
|
v, err = fract.SumAnyFract(leftValue, rightValue)
|
||||||
} else {
|
} else {
|
||||||
err = plusTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
err = plusTerm.ErrIncompatibleTypes(leftValue, rightValue)
|
||||||
}
|
}
|
||||||
@@ -87,20 +102,20 @@ func newMinusTerm(tk *scan.Token) (inst *scan.Term) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func diffValues(minusTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
func diffValues(minusTerm *scan.Term, leftValue, rightValue any) (v any, err error) {
|
||||||
if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
|
if types.IsNumOrFract(leftValue) && types.IsNumOrFract(rightValue) {
|
||||||
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
|
if float.IsFloat(leftValue) || float.IsFloat(rightValue) {
|
||||||
v = kern.NumAsFloat(leftValue) - kern.NumAsFloat(rightValue)
|
v = types.NumAsFloat(leftValue) - types.NumAsFloat(rightValue)
|
||||||
} else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) {
|
} else if fract.IsFraction(leftValue) || fract.IsFraction(rightValue) {
|
||||||
v, err = kern.SubAnyFract(leftValue, rightValue)
|
v, err = fract.SubAnyFract(leftValue, rightValue)
|
||||||
} else {
|
} else {
|
||||||
leftInt, _ := leftValue.(int64)
|
leftInt, _ := leftValue.(int64)
|
||||||
rightInt, _ := rightValue.(int64)
|
rightInt, _ := rightValue.(int64)
|
||||||
v = leftInt - rightInt
|
v = leftInt - rightInt
|
||||||
}
|
}
|
||||||
} else if kern.IsList(leftValue) && kern.IsList(rightValue) {
|
} else if array.IsArray(leftValue) && array.IsArray(rightValue) {
|
||||||
leftList, _ := leftValue.(*kern.ListType)
|
leftList, _ := leftValue.(*array.ArrayType)
|
||||||
rightList, _ := rightValue.(*kern.ListType)
|
rightList, _ := rightValue.(*array.ArrayType)
|
||||||
diffList := make(kern.ListType, 0, len(*leftList)-len(*rightList))
|
diffList := make(array.ArrayType, 0, len(*leftList)-len(*rightList))
|
||||||
for _, item := range *leftList {
|
for _, item := range *leftList {
|
||||||
if slices.Index(*rightList, item) < 0 {
|
if slices.Index(*rightList, item) < 0 {
|
||||||
diffList = append(diffList, item)
|
diffList = append(diffList, item)
|
||||||
@@ -125,6 +140,6 @@ func evalMinus(ctx kern.ExprContext, minusTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymPlus, newPlusTerm)
|
scan.RegisterTermConstructor(sym.SymPlus, newPlusTerm)
|
||||||
scan.RegisterTermConstructor(scan.SymMinus, newMinusTerm)
|
scan.RegisterTermConstructor(sym.SymMinus, newMinusTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-3
@@ -9,6 +9,8 @@ import (
|
|||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/kern"
|
"git.portale-stac.it/go-pkg/expr/kern"
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/types/array"
|
||||||
)
|
)
|
||||||
|
|
||||||
//-------- unset term
|
//-------- unset term
|
||||||
@@ -50,8 +52,8 @@ func evalUnset(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
if kern.IsList(childValue) {
|
if array.IsArray(childValue) {
|
||||||
list, _ := childValue.(*kern.ListType)
|
list, _ := childValue.(*array.ArrayType)
|
||||||
for _, item := range *list {
|
for _, item := range *list {
|
||||||
if deleted, err = deleteContextItem(ctx, opTerm, item); err != nil {
|
if deleted, err = deleteContextItem(ctx, opTerm, item); err != nil {
|
||||||
break
|
break
|
||||||
@@ -70,5 +72,5 @@ func evalUnset(ctx kern.ExprContext, opTerm *scan.Term) (v any, err error) {
|
|||||||
|
|
||||||
// init
|
// init
|
||||||
func init() {
|
func init() {
|
||||||
scan.RegisterTermConstructor(scan.SymKwUnset, newUnsetTerm)
|
scan.RegisterTermConstructor(sym.SymKwUnset, newUnsetTerm)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,11 @@ package expr
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"git.portale-stac.it/go-pkg/expr/scan"
|
"git.portale-stac.it/go-pkg/expr/scan"
|
||||||
|
"git.portale-stac.it/go-pkg/expr/sym"
|
||||||
"golang.org/x/exp/constraints"
|
"golang.org/x/exp/constraints"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -56,7 +59,7 @@ func NewParser() (p *parser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) Next(scanner *scan.Scanner) (tk *scan.Token) {
|
func (parser *parser) Next(scanner *scan.Scanner) (tk *scan.Token) {
|
||||||
for tk = scanner.Next(); tk.IsSymbol(scan.SymComment); tk = scanner.Next() {
|
for tk = scanner.Next(); tk.IsSymbol(sym.SymComment); tk = scanner.Next() {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -64,10 +67,10 @@ func (parser *parser) Next(scanner *scan.Scanner) (tk *scan.Token) {
|
|||||||
func (parser *parser) parseFuncCall(scanner *scan.Scanner, ctx parserContext, tk *scan.Token) (tree *scan.Term, err error) {
|
func (parser *parser) parseFuncCall(scanner *scan.Scanner, ctx parserContext, tk *scan.Token) (tree *scan.Term, err error) {
|
||||||
args := make([]*scan.Term, 0, 10)
|
args := make([]*scan.Term, 0, 10)
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
lastSym := scan.SymUnknown
|
lastSym := sym.SymUnknown
|
||||||
for lastSym != scan.SymClosedRound && lastSym != scan.SymEos {
|
for lastSym != sym.SymClosedRound && lastSym != sym.SymEos {
|
||||||
var subTree *scan.Ast
|
var subTree *scan.Ast
|
||||||
if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedRound); err != nil {
|
if subTree, err = parser.parseItem(scanner, ctx, sym.SymComma, sym.SymClosedRound); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
@@ -78,11 +81,11 @@ func (parser *parser) parseFuncCall(scanner *scan.Scanner, ctx parserContext, tk
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
itemExpected = prev.Sym == scan.SymComma
|
itemExpected = prev.Sym == sym.SymComma
|
||||||
lastSym = scanner.Previous().Sym
|
lastSym = scanner.Previous().Sym
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if lastSym != scan.SymClosedRound {
|
if lastSym != sym.SymClosedRound {
|
||||||
err = errors.New("unterminated arguments list")
|
err = errors.New("unterminated arguments list")
|
||||||
} else {
|
} else {
|
||||||
tree = newFuncCallTerm(tk, args)
|
tree = newFuncCallTerm(tk, args)
|
||||||
@@ -95,13 +98,13 @@ func (parser *parser) parseFuncDef(scanner *scan.Scanner) (tree *scan.Term, err
|
|||||||
// Example: "add = func(x,y) {x+y}
|
// Example: "add = func(x,y) {x+y}
|
||||||
var body *scan.Ast
|
var body *scan.Ast
|
||||||
args := make([]*scan.Term, 0)
|
args := make([]*scan.Term, 0)
|
||||||
lastSym := scan.SymUnknown
|
lastSym := sym.SymUnknown
|
||||||
defaultParamsStarted := false
|
defaultParamsStarted := false
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
for lastSym != scan.SymClosedRound && lastSym != scan.SymEos {
|
for lastSym != sym.SymClosedRound && lastSym != sym.SymEos {
|
||||||
tk = parser.Next(scanner)
|
tk = parser.Next(scanner)
|
||||||
if tk.IsSymbol(scan.SymIdentifier) {
|
if tk.IsSymbol(sym.SymIdentifier) {
|
||||||
param := scan.NewTerm(tk)
|
param := scan.NewTerm(tk)
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
if pos := paramAlreadyDefined(args, param); pos > 0 {
|
if pos := paramAlreadyDefined(args, param); pos > 0 {
|
||||||
@@ -111,10 +114,10 @@ func (parser *parser) parseFuncDef(scanner *scan.Scanner) (tree *scan.Term, err
|
|||||||
}
|
}
|
||||||
args = append(args, param)
|
args = append(args, param)
|
||||||
tk = parser.Next(scanner)
|
tk = parser.Next(scanner)
|
||||||
if tk.Sym == scan.SymEqual {
|
if tk.Sym == sym.SymEqual {
|
||||||
var paramExpr *scan.Ast
|
var paramExpr *scan.Ast
|
||||||
defaultParamsStarted = true
|
defaultParamsStarted = true
|
||||||
if paramExpr, err = parser.parseItem(scanner, parserNoFlags, scan.SymComma, scan.SymClosedRound); err != nil {
|
if paramExpr, err = parser.parseItem(scanner, parserNoFlags, sym.SymComma, sym.SymClosedRound); err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
param.ForceChild(paramExpr.Root())
|
param.ForceChild(paramExpr.Root())
|
||||||
@@ -128,25 +131,25 @@ func (parser *parser) parseFuncDef(scanner *scan.Scanner) (tree *scan.Term, err
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
lastSym = scanner.Previous().Sym
|
lastSym = scanner.Previous().Sym
|
||||||
itemExpected = lastSym == scan.SymComma
|
itemExpected = lastSym == sym.SymComma
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && lastSym != scan.SymClosedRound {
|
if err == nil && lastSym != sym.SymClosedRound {
|
||||||
err = tk.ErrorExpectedGot(")")
|
err = tk.ErrorExpectedGot(")")
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tk = parser.Next(scanner)
|
tk = parser.Next(scanner)
|
||||||
if tk.IsSymbol(scan.SymOpenBrace) {
|
if tk.IsSymbol(sym.SymOpenBrace) {
|
||||||
body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, scan.SymClosedBrace)
|
body, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, sym.SymClosedBrace)
|
||||||
} else {
|
} else {
|
||||||
err = tk.ErrorExpectedGot("{")
|
err = tk.ErrorExpectedGot("{")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if scanner.Previous().Sym != scan.SymClosedBrace {
|
if scanner.Previous().Sym != sym.SymClosedBrace {
|
||||||
err = scanner.Previous().ErrorExpectedGot("}")
|
err = scanner.Previous().ErrorExpectedGot("}")
|
||||||
} else {
|
} else {
|
||||||
tk = scanner.MakeValueToken(scan.SymExpression, "", body)
|
tk = scanner.MakeValueToken(sym.SymExpression, "", body)
|
||||||
tree = newFuncDefTerm(tk, args)
|
tree = newFuncDefTerm(tk, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,40 +166,35 @@ func paramAlreadyDefined(args []*scan.Term, param *scan.Term) (position int) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext) (listTerm *scan.Term, err error) {
|
func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext, termSym sym.Symbol) (listTerm *scan.Term, err error) {
|
||||||
r, c := scanner.LastPos()
|
r, c := scanner.LastPos()
|
||||||
args := make([]*scan.Term, 0)
|
args := make([]*scan.Term, 0)
|
||||||
lastSym := scan.SymUnknown
|
lastSym := sym.SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
itemCtx := remFlags(ctx, allowIndex)
|
itemCtx := remFlags(ctx, allowIndex)
|
||||||
for lastSym != scan.SymClosedSquare && lastSym != scan.SymEos {
|
for lastSym != termSym && lastSym != sym.SymEos {
|
||||||
zeroRequired := scanner.Current().Sym == scan.SymColon
|
|
||||||
var itemTree *scan.Ast
|
var itemTree *scan.Ast
|
||||||
if itemTree, err = parser.parseItem(scanner, itemCtx, scan.SymComma, scan.SymClosedSquare); err == nil {
|
if itemTree, err = parser.parseItem(scanner, itemCtx, sym.SymComma, termSym); err == nil {
|
||||||
root := itemTree.Root()
|
root := itemTree.Root()
|
||||||
if root != nil {
|
if root != nil {
|
||||||
if hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymColon {
|
if root.Symbol() == sym.SymDoubleDot {
|
||||||
changeColonToRange(root)
|
if hasFlag(ctx, allowIndex) {
|
||||||
}
|
changeColonToInterval(root)
|
||||||
if !hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymRange {
|
} else {
|
||||||
// err = root.Errorf("unexpected range expression")
|
// err = root.Errorf("unexpected range expression")
|
||||||
err = errRangeUnexpectedExpression(root)
|
err = errIntervalUnexpectedExpression(root)
|
||||||
break
|
|
||||||
}
|
|
||||||
args = append(args, root)
|
|
||||||
if hasFlag(ctx, allowIndex) && root.Symbol() == scan.SymRange && zeroRequired { //len(root.children) == 0 {
|
|
||||||
if len(root.Children) == 1 {
|
|
||||||
root.Children = append(root.Children, root.Children[0])
|
|
||||||
} else if len(root.Children) > 1 {
|
|
||||||
// err = root.Errorf("invalid range specification")
|
|
||||||
err = errRangeInvalidSpecification(root)
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
zeroTk := scan.NewValueToken(root.Tk.Row(), root.Tk.Col(), scan.SymInteger, "0", int64(0))
|
|
||||||
zeroTerm := scan.NewTerm(zeroTk)
|
|
||||||
zeroTerm.SetParent(root)
|
|
||||||
root.Children[0] = zeroTerm
|
|
||||||
}
|
}
|
||||||
|
// if hasFlag(ctx, allowIndex) && root.Symbol() == sym.SymDoubleDot {
|
||||||
|
// changeColonToInterval(root)
|
||||||
|
// }
|
||||||
|
// if !hasFlag(ctx, allowIndex) && root.Symbol() == sym.SymInterval {
|
||||||
|
// // err = root.Errorf("unexpected range expression")
|
||||||
|
// err = errIntervalUnexpectedExpression(root)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
args = append(args, root)
|
||||||
} else if itemExpected {
|
} else if itemExpected {
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
err = prev.ErrorExpectedGot("list-item")
|
err = prev.ErrorExpectedGot("list-item")
|
||||||
@@ -206,15 +204,19 @@ func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext) (listT
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
lastSym = scanner.Previous().Sym
|
lastSym = scanner.Previous().Sym
|
||||||
if itemExpected = lastSym == scan.SymComma; itemExpected {
|
if itemExpected = lastSym == sym.SymComma; itemExpected {
|
||||||
remFlags(ctx, allowIndex)
|
remFlags(ctx, allowIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if lastSym != scan.SymClosedSquare {
|
if lastSym != termSym {
|
||||||
err = scanner.Previous().ErrorExpectedGot("]")
|
err = scanner.Previous().ErrorExpectedGot("]")
|
||||||
} else {
|
} else if termSym == sym.SymClosedSquare {
|
||||||
listTerm = newListTerm(r, c, args)
|
listTerm = newListTerm(r, c, args)
|
||||||
|
} else if termSym == sym.SymGreaterClosedSquare {
|
||||||
|
listTerm = newLinkedListTerm(r, c, args)
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("[%d:%d] unknown list type", r, c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -223,11 +225,11 @@ func (parser *parser) parseList(scanner *scan.Scanner, ctx parserContext) (listT
|
|||||||
func (parser *parser) parseIterDef(scanner *scan.Scanner, ctx parserContext) (subtree *scan.Term, err error) {
|
func (parser *parser) parseIterDef(scanner *scan.Scanner, ctx parserContext) (subtree *scan.Term, err error) {
|
||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
args := make([]*scan.Term, 0)
|
args := make([]*scan.Term, 0)
|
||||||
lastSym := scan.SymUnknown
|
lastSym := sym.SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
for lastSym != scan.SymClosedRound && lastSym != scan.SymEos {
|
for lastSym != sym.SymClosedRound && lastSym != sym.SymEos {
|
||||||
var subTree *scan.Ast
|
var subTree *scan.Ast
|
||||||
if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedRound); err == nil {
|
if subTree, err = parser.parseItem(scanner, ctx, sym.SymComma, sym.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 {
|
||||||
@@ -239,10 +241,10 @@ func (parser *parser) parseIterDef(scanner *scan.Scanner, ctx parserContext) (su
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
lastSym = scanner.Previous().Sym
|
lastSym = scanner.Previous().Sym
|
||||||
itemExpected = lastSym == scan.SymComma
|
itemExpected = lastSym == sym.SymComma
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if lastSym != scan.SymClosedRound {
|
if lastSym != sym.SymClosedRound {
|
||||||
err = scanner.Previous().ErrorExpectedGot(")")
|
err = scanner.Previous().ErrorExpectedGot(")")
|
||||||
} else {
|
} else {
|
||||||
subtree = newIteratorTerm(tk, args)
|
subtree = newIteratorTerm(tk, args)
|
||||||
@@ -251,38 +253,55 @@ func (parser *parser) parseIterDef(scanner *scan.Scanner, ctx parserContext) (su
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseDictKey(scanner *scan.Scanner) (key any, err error) {
|
// func (parser *parser) parseDictKey(scanner *scan.Scanner) (key any, err error) {
|
||||||
tk := parser.Next(scanner)
|
// tk := parser.Next(scanner)
|
||||||
if tk.Sym == scan.SymError {
|
// if tk.Sym == sym.SymError {
|
||||||
err = tk.Error()
|
// err = tk.Error()
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if tk.Sym == scan.SymClosedBrace || tk.Sym == scan.SymEos {
|
// if tk.Sym == sym.SymClosedBrace || tk.Sym == sym.SymEos {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
if tk.Sym == scan.SymInteger || tk.Sym == scan.SymString {
|
// if tk.Sym == sym.SymInteger || tk.Sym == sym.SymString || tk.Sym == sym.SymIdentifier {
|
||||||
tkSep := parser.Next(scanner)
|
// tkSep := parser.Next(scanner)
|
||||||
if tkSep.Sym != scan.SymColon {
|
// if tkSep.Sym != sym.SymColon {
|
||||||
|
// err = tkSep.ErrorExpectedGot(":")
|
||||||
|
// } else {
|
||||||
|
// key = tk.Value
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// err = tk.ErrorExpectedGot("dictionary-key or }")
|
||||||
|
// }
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
func (parser *parser) parseDictKey(scanner *scan.Scanner) (key *scan.Term, err error) {
|
||||||
|
var keyTree *scan.Ast
|
||||||
|
if keyTree, err = parser.parseItem(scanner, parserNoFlags, sym.SymColon, sym.SymClosedBrace); err == nil {
|
||||||
|
key = keyTree.Root()
|
||||||
|
tkSep := scanner.Previous()
|
||||||
|
symbol := tkSep.Sym
|
||||||
|
if symbol == sym.SymClosedBrace || symbol == sym.SymEos {
|
||||||
|
if key != nil {
|
||||||
|
err = tkSep.ErrorExpectedGot(":")
|
||||||
|
}
|
||||||
|
} else if symbol != sym.SymColon {
|
||||||
err = tkSep.ErrorExpectedGot(":")
|
err = tkSep.ErrorExpectedGot(":")
|
||||||
} else {
|
|
||||||
key = tk.Value
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
err = tk.ErrorExpectedGot("dictionary-key or }")
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext) (subtree *scan.Term, err error) {
|
func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext) (subtree *scan.Term, err error) {
|
||||||
args := make(map[any]*scan.Term, 0)
|
args := make(map[any]*scan.Term, 0)
|
||||||
lastSym := scan.SymUnknown
|
lastSym := sym.SymUnknown
|
||||||
itemExpected := false
|
itemExpected := false
|
||||||
for lastSym != scan.SymClosedBrace && lastSym != scan.SymEos {
|
for lastSym != sym.SymClosedBrace && lastSym != sym.SymEos {
|
||||||
var subTree *scan.Ast
|
var valueTree *scan.Ast
|
||||||
var key any
|
var key any
|
||||||
if key, err = parser.parseDictKey(scanner); err != nil {
|
if key, err = parser.parseDictKey(scanner); err != nil {
|
||||||
break
|
break
|
||||||
} else if key == nil {
|
} else if key.(*scan.Term) == nil {
|
||||||
tk := scanner.Previous()
|
tk := scanner.Previous()
|
||||||
lastSym = tk.Sym
|
lastSym = tk.Sym
|
||||||
if itemExpected {
|
if itemExpected {
|
||||||
@@ -290,9 +309,9 @@ func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext)
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if subTree, err = parser.parseItem(scanner, ctx, scan.SymComma, scan.SymClosedBrace); err == nil {
|
if valueTree, err = parser.parseItem(scanner, ctx, sym.SymComma, sym.SymClosedBrace); err == nil {
|
||||||
if subTree.Root() != nil {
|
if valueTree.Root() != nil {
|
||||||
args[key] = subTree.Root()
|
args[key] = valueTree.Root()
|
||||||
} else /*if key != nil*/ {
|
} else /*if key != nil*/ {
|
||||||
prev := scanner.Previous()
|
prev := scanner.Previous()
|
||||||
err = prev.ErrorExpectedGot("dictionary-value")
|
err = prev.ErrorExpectedGot("dictionary-value")
|
||||||
@@ -302,10 +321,10 @@ func (parser *parser) parseDictionary(scanner *scan.Scanner, ctx parserContext)
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
lastSym = scanner.Previous().Sym
|
lastSym = scanner.Previous().Sym
|
||||||
itemExpected = lastSym == scan.SymComma
|
itemExpected = lastSym == sym.SymComma
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if lastSym != scan.SymClosedBrace {
|
if lastSym != sym.SymClosedBrace {
|
||||||
err = scanner.Previous().ErrorExpectedGot("}")
|
err = scanner.Previous().ErrorExpectedGot("}")
|
||||||
} else {
|
} else {
|
||||||
subtree = newDictTerm(args)
|
subtree = newDictTerm(args)
|
||||||
@@ -321,12 +340,12 @@ func (parser *parser) parseSelectorCase(scanner *scan.Scanner, ctx parserContext
|
|||||||
tk := parser.Next(scanner)
|
tk := parser.Next(scanner)
|
||||||
startRow := tk.Row()
|
startRow := tk.Row()
|
||||||
startCol := tk.Col()
|
startCol := tk.Col()
|
||||||
if tk.Sym == scan.SymOpenSquare {
|
if tk.Sym == sym.SymOpenSquare {
|
||||||
if defaultCase {
|
if defaultCase {
|
||||||
err = tk.Errorf("case list in default clause")
|
err = tk.Errorf("case list in default clause")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex)); err != nil {
|
if filterList, err = parser.parseList(scanner, remFlags(ctx, allowIndex), sym.SymClosedSquare); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
tk = parser.Next(scanner)
|
tk = parser.Next(scanner)
|
||||||
@@ -336,8 +355,8 @@ func (parser *parser) parseSelectorCase(scanner *scan.Scanner, ctx parserContext
|
|||||||
filterList = newListTerm(startRow, startCol, make([]*scan.Term, 0))
|
filterList = newListTerm(startRow, startCol, make([]*scan.Term, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
if tk.Sym == scan.SymOpenBrace {
|
if tk.Sym == sym.SymOpenBrace {
|
||||||
if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, scan.SymClosedBrace); err != nil {
|
if caseExpr, err = parser.parseGeneral(scanner, ctx|allowMultiExpr, sym.SymClosedBrace); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -367,7 +386,7 @@ func (parser *parser) parseSelector(scanner *scan.Scanner, tree *scan.Ast, ctx p
|
|||||||
var caseTerm *scan.Term
|
var caseTerm *scan.Term
|
||||||
|
|
||||||
ctx = remFlags(ctx, allowIndex)
|
ctx = remFlags(ctx, allowIndex)
|
||||||
tk := scanner.MakeToken(scan.SymSelector, '?')
|
tk := scanner.MakeToken(sym.SymSelector, '?')
|
||||||
if selectorTerm, err = tree.AddToken(tk); err != nil {
|
if selectorTerm, err = tree.AddToken(tk); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -378,11 +397,11 @@ func (parser *parser) parseSelector(scanner *scan.Scanner, tree *scan.Ast, ctx p
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseItem(scanner *scan.Scanner, ctx parserContext, termSymbols ...scan.Symbol) (tree *scan.Ast, err error) {
|
func (parser *parser) parseItem(scanner *scan.Scanner, ctx parserContext, termSymbols ...sym.Symbol) (tree *scan.Ast, err error) {
|
||||||
return parser.parseGeneral(scanner, ctx|allowVarRef, termSymbols...)
|
return parser.parseGeneral(scanner, ctx|allowVarRef, termSymbols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) Parse(scanner *scan.Scanner, termSymbols ...scan.Symbol) (tree *scan.Ast, err error) {
|
func (parser *parser) Parse(scanner *scan.Scanner, termSymbols ...sym.Symbol) (tree *scan.Ast, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
if errVal, ok := r.(error); ok {
|
if errVal, ok := r.(error); ok {
|
||||||
@@ -392,22 +411,23 @@ func (parser *parser) Parse(scanner *scan.Scanner, termSymbols ...scan.Symbol) (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
termSymbols = append(termSymbols, scan.SymEos)
|
termSymbols = append(termSymbols, sym.SymEos)
|
||||||
return parser.parseGeneral(scanner, allowMultiExpr, termSymbols...)
|
return parser.parseGeneral(scanner, allowMultiExpr, termSymbols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func couldBeACollection(t *scan.Term) bool {
|
func couldBeACollection(t *scan.Term) bool {
|
||||||
var sym = scan.SymUnknown
|
var symbol = sym.SymUnknown
|
||||||
if t != nil {
|
if t != nil {
|
||||||
sym = t.Symbol()
|
symbol = t.Symbol()
|
||||||
}
|
}
|
||||||
return sym == scan.SymList || sym == scan.SymString || sym == scan.SymDict || sym == scan.SymExpression || sym == scan.SymVariable
|
// return sym == sym.SymList || sym == sym.SymString || sym == sym.SymDict || sym == sym.SymExpression || sym == sym.SymVariable
|
||||||
|
return slices.Contains([]sym.Symbol{sym.SymList, sym.SymLinkedList, sym.SymString, sym.SymDict, sym.SymExpression, sym.SymVariable, sym.SymIndex}, symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
func listSubTree(tree *scan.Ast, listTerm *scan.Term, allowIndeces bool) (root *scan.Term, err error) {
|
func listSubTree(tree *scan.Ast, listTerm *scan.Term, allowIndeces bool) (root *scan.Term, err error) {
|
||||||
var tk *scan.Token
|
var tk *scan.Token
|
||||||
if allowIndeces {
|
if allowIndeces {
|
||||||
tk = scan.NewToken(listTerm.Tk.Row(), listTerm.Tk.Col(), scan.SymIndex, listTerm.Source())
|
tk = scan.NewToken(listTerm.Tk.Row(), listTerm.Tk.Col(), sym.SymIndex, listTerm.Source())
|
||||||
root = scan.NewTerm(tk)
|
root = scan.NewTerm(tk)
|
||||||
if err = tree.AddTerm(root); err == nil {
|
if err = tree.AddTerm(root); err == nil {
|
||||||
err = tree.AddTerm(listTerm)
|
err = tree.AddTerm(listTerm)
|
||||||
@@ -421,22 +441,22 @@ func listSubTree(tree *scan.Ast, listTerm *scan.Term, allowIndeces bool) (root *
|
|||||||
|
|
||||||
func changePrefix(tk *scan.Token) {
|
func changePrefix(tk *scan.Token) {
|
||||||
switch tk.Sym {
|
switch tk.Sym {
|
||||||
case scan.SymMinus:
|
case sym.SymMinus:
|
||||||
tk.SetSymbol(scan.SymChangeSign)
|
tk.SetSymbol(sym.SymChangeSign)
|
||||||
case scan.SymPlus:
|
case sym.SymPlus:
|
||||||
tk.SetSymbol(scan.SymUnchangeSign)
|
tk.SetSymbol(sym.SymUnchangeSign)
|
||||||
case scan.SymStar:
|
case sym.SymStar:
|
||||||
tk.SetSymbol(scan.SymDereference)
|
tk.SetSymbol(sym.SymDereference)
|
||||||
case scan.SymExclamation:
|
case sym.SymExclamation:
|
||||||
tk.SetSymbol(scan.SymNot)
|
tk.SetSymbol(sym.SymNot)
|
||||||
case scan.SymDoublePlus:
|
case sym.SymDoublePlus:
|
||||||
tk.SetSymbol(scan.SymPreInc)
|
tk.SetSymbol(sym.SymPreInc)
|
||||||
case scan.SymDoubleMinus:
|
case sym.SymDoubleMinus:
|
||||||
tk.SetSymbol(scan.SymPreDec)
|
tk.SetSymbol(sym.SymPreDec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, termSymbols ...scan.Symbol) (tree *scan.Ast, err error) {
|
func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, termSymbols ...sym.Symbol) (tree *scan.Ast, err error) {
|
||||||
var selectorTerm *scan.Term = nil
|
var selectorTerm *scan.Term = nil
|
||||||
var currentTerm *scan.Term = nil
|
var currentTerm *scan.Term = nil
|
||||||
var tk *scan.Token
|
var tk *scan.Token
|
||||||
@@ -444,12 +464,12 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
|||||||
tree = scan.NewAst()
|
tree = scan.NewAst()
|
||||||
firstToken := true
|
firstToken := true
|
||||||
// lastSym := SymUnknown
|
// lastSym := SymUnknown
|
||||||
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) {
|
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); tk = parser.Next(scanner) {
|
||||||
// if tk.Sym == SymComment {
|
// if tk.Sym == SymComment {
|
||||||
// continue
|
// continue
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if tk.Sym == scan.SymSemiColon {
|
if tk.Sym == sym.SymSemiColon {
|
||||||
if hasFlag(ctx, allowMultiExpr) {
|
if hasFlag(ctx, allowMultiExpr) {
|
||||||
tree.ToForest()
|
tree.ToForest()
|
||||||
firstToken = true
|
firstToken = true
|
||||||
@@ -457,7 +477,7 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
|||||||
selectorTerm = nil
|
selectorTerm = nil
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.Source())
|
err = tk.ErrorExpectedOneOfGot(termSymbols...)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -469,9 +489,9 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch tk.Sym {
|
switch tk.Sym {
|
||||||
case scan.SymOpenRound:
|
case sym.SymOpenRound:
|
||||||
var subTree *scan.Ast
|
var subTree *scan.Ast
|
||||||
if subTree, err = parser.parseGeneral(scanner, ctx, scan.SymClosedRound); err == nil {
|
if subTree, err = parser.parseGeneral(scanner, ctx, sym.SymClosedRound); err == nil {
|
||||||
if subTree.Root() == nil {
|
if subTree.Root() == nil {
|
||||||
err = tk.ErrorExpectedGotString("expression", "()")
|
err = tk.ErrorExpectedGotString("expression", "()")
|
||||||
} else {
|
} else {
|
||||||
@@ -480,20 +500,26 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
|||||||
currentTerm = exprTerm
|
currentTerm = exprTerm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case scan.SymFuncCall:
|
case sym.SymFuncCall:
|
||||||
var funcCallTerm *scan.Term
|
var funcCallTerm *scan.Term
|
||||||
if funcCallTerm, err = parser.parseFuncCall(scanner, ctx, 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 scan.SymOpenSquare:
|
case sym.SymOpenSquare:
|
||||||
var listTerm *scan.Term
|
var listTerm *scan.Term
|
||||||
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
|
newCtx := addFlagsCond(addFlags(ctx, squareContext), allowIndex, couldBeACollection(currentTerm))
|
||||||
if listTerm, err = parser.parseList(scanner, newCtx); err == nil {
|
if listTerm, err = parser.parseList(scanner, newCtx, sym.SymClosedSquare); err == nil {
|
||||||
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||||
}
|
}
|
||||||
case scan.SymOpenBrace:
|
case sym.SymOpenSquareLess:
|
||||||
if currentTerm != nil && currentTerm.Symbol() == scan.SymColon {
|
var listTerm *scan.Term
|
||||||
|
newCtx := addFlagsCond(addFlags(ctx, listContext), allowIndex, false)
|
||||||
|
if listTerm, err = parser.parseList(scanner, newCtx, sym.SymGreaterClosedSquare); err == nil {
|
||||||
|
currentTerm, err = listSubTree(tree, listTerm, hasFlag(newCtx, allowIndex))
|
||||||
|
}
|
||||||
|
case sym.SymOpenBrace:
|
||||||
|
if currentTerm != nil && currentTerm.Symbol() == sym.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 *scan.Term
|
var mapTerm *scan.Term
|
||||||
@@ -502,54 +528,61 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
|||||||
currentTerm = mapTerm
|
currentTerm = mapTerm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case scan.SymEqual, scan.SymPlusEqual, scan.SymMinusEqual, scan.SymStarEqual, scan.SymSlashEqual, scan.SymPercEqual, scan.SymAmpersandEqual, scan.SymVertBarEqual, scan.SymDoubleLessEqual, scan.SymDoubleGreaterEqual, scan.SymCaretEqual:
|
case sym.SymEqual, sym.SymPlusEqual, sym.SymMinusEqual, sym.SymStarEqual, sym.SymSlashEqual, sym.SymPercEqual, sym.SymAmpersandEqual, sym.SymVertBarEqual, sym.SymDoubleLessEqual, sym.SymDoubleGreaterEqual, sym.SymCaretEqual:
|
||||||
currentTerm, err = tree.AddToken(tk)
|
currentTerm, err = tree.AddToken(tk)
|
||||||
firstToken = true
|
firstToken = true
|
||||||
case scan.SymFuncDef:
|
case sym.SymFuncDef:
|
||||||
var funcDefTerm *scan.Term
|
var funcDefTerm *scan.Term
|
||||||
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
|
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
|
||||||
err = tree.AddTerm(funcDefTerm)
|
err = tree.AddTerm(funcDefTerm)
|
||||||
currentTerm = funcDefTerm
|
currentTerm = funcDefTerm
|
||||||
}
|
}
|
||||||
case scan.SymDollarRound:
|
case sym.SymDollarRound:
|
||||||
var iterDefTerm *scan.Term
|
var iterDefTerm *scan.Term
|
||||||
if iterDefTerm, err = parser.parseIterDef(scanner, ctx); err == nil {
|
if iterDefTerm, err = parser.parseIterDef(scanner, ctx); err == nil {
|
||||||
err = tree.AddTerm(iterDefTerm)
|
err = tree.AddTerm(iterDefTerm)
|
||||||
currentTerm = iterDefTerm
|
currentTerm = iterDefTerm
|
||||||
}
|
}
|
||||||
case scan.SymIdentifier:
|
case sym.SymIdentifier:
|
||||||
if tk.Source()[0] == '@' && !hasFlag(ctx, 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 scan.SymQuestion:
|
case sym.SymQuestion:
|
||||||
if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil {
|
if selectorTerm, err = parser.parseSelector(scanner, tree, ctx); err == nil {
|
||||||
currentTerm = selectorTerm
|
currentTerm = selectorTerm
|
||||||
addFlags(ctx, selectorContext)
|
addFlags(ctx, selectorContext)
|
||||||
}
|
}
|
||||||
case scan.SymColon, scan.SymDoubleColon:
|
case sym.SymColon, sym.SymDoubleColon:
|
||||||
var caseTerm *scan.Term
|
var caseTerm *scan.Term
|
||||||
if selectorTerm != nil {
|
if selectorTerm != nil {
|
||||||
if caseTerm, err = parser.parseSelectorCase(scanner, ctx, tk.Sym == scan.SymDoubleColon); err == nil {
|
if caseTerm, err = parser.parseSelectorCase(scanner, ctx, tk.Sym == sym.SymDoubleColon); err == nil {
|
||||||
addSelectorCase(selectorTerm, caseTerm)
|
addSelectorCase(selectorTerm, caseTerm)
|
||||||
currentTerm = caseTerm
|
currentTerm = caseTerm
|
||||||
if tk.Sym == scan.SymDoubleColon {
|
if tk.Sym == sym.SymDoubleColon {
|
||||||
selectorTerm = nil
|
selectorTerm = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// err = tk.Errorf("colon outside a selector context")
|
||||||
currentTerm, err = tree.AddToken(tk)
|
currentTerm, err = tree.AddToken(tk)
|
||||||
if tk.IsOneOfA(scan.SymColon, scan.SymRange) {
|
// Colon outside a selector term acts like a separator
|
||||||
// Colon outside a selector term acts like a separator
|
firstToken = tk.IsOneOfA(sym.SymColon, sym.SymInterval)
|
||||||
firstToken = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
case sym.SymDoubleDot:
|
||||||
|
currentTerm, err = tree.AddToken(tk)
|
||||||
|
if currentTerm.GetLeftChild() == nil {
|
||||||
|
zeroTk := scan.NewValueToken(tk.Row(), tk.Col(), sym.SymInteger, "0", int64(0))
|
||||||
|
tree.AddToken(zeroTk)
|
||||||
|
}
|
||||||
|
// Double dot acts like a separator
|
||||||
|
firstToken = tk.IsOneOfA(sym.SymDoubleDot, sym.SymInterval)
|
||||||
default:
|
default:
|
||||||
currentTerm, err = tree.AddToken(tk)
|
currentTerm, err = tree.AddToken(tk)
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentTerm != nil && currentTerm.Tk.Sym != scan.SymSelector && currentTerm.Parent != nil && currentTerm.Parent.Tk.Sym != scan.SymSelector {
|
if currentTerm != nil && currentTerm.Tk.Sym != sym.SymSelector && currentTerm.Parent != nil && currentTerm.Parent.Tk.Sym != sym.SymSelector {
|
||||||
selectorTerm = nil
|
selectorTerm = nil
|
||||||
remFlags(ctx, selectorContext)
|
remFlags(ctx, selectorContext)
|
||||||
}
|
}
|
||||||
@@ -559,7 +592,7 @@ func (parser *parser) parseGeneral(scanner *scan.Scanner, ctx parserContext, ter
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
if !tk.IsOneOf(termSymbols) {
|
if !tk.IsOneOf(termSymbols) {
|
||||||
var symDesc string
|
var symDesc string
|
||||||
if tk.IsSymbol(scan.SymError) {
|
if tk.IsSymbol(sym.SymError) {
|
||||||
symDesc = tk.ErrorText()
|
symDesc = tk.ErrorText()
|
||||||
} else {
|
} else {
|
||||||
symDesc = scan.SymToString(tk.Sym)
|
symDesc = scan.SymToString(tk.Sym)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user