Compare commits
	
		
			No commits in common. "e5f63c3d9d5d74f8a0b5db4057b94f8d18ee27cf" and "f22b5a6f0ba998b5dff9bc7b7f7c3ec1fd6b881b" have entirely different histories.
		
	
	
		
			e5f63c3d9d
			...
			f22b5a6f0b
		
	
		
@ -5,26 +5,7 @@
 | 
			
		||||
package expr
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	paramCount     = "count"
 | 
			
		||||
	paramItem      = "item"
 | 
			
		||||
	paramParts     = "parts"
 | 
			
		||||
	paramSeparator = "separator"
 | 
			
		||||
	paramSource    = "source"
 | 
			
		||||
	paramSuffix    = "suffix"
 | 
			
		||||
	paramPrefix    = "prefix"
 | 
			
		||||
	paramStart     = "start"
 | 
			
		||||
	paramEnd       = "end"
 | 
			
		||||
	paramValue     = "value"
 | 
			
		||||
	paramEllipsis  = "..."
 | 
			
		||||
	typeFilepath   = "filepath"
 | 
			
		||||
	typeDirpath    = "dirpath"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// const (
 | 
			
		||||
// 	typeInteger  = "int"
 | 
			
		||||
// 	typeFloat    = "float"
 | 
			
		||||
// 	typeString   = "string"
 | 
			
		||||
// 	typeFraction = "fract"
 | 
			
		||||
// 	typeList     = "list"
 | 
			
		||||
// 	typeDict     = "dict"
 | 
			
		||||
// )
 | 
			
		||||
 | 
			
		||||
@ -5,13 +5,10 @@
 | 
			
		||||
package expr
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	typeAny      = "any"
 | 
			
		||||
	typeBoolean  = "boolean"
 | 
			
		||||
	typeFloat    = "float"
 | 
			
		||||
	typeFloat    = "decimal"
 | 
			
		||||
	typeFraction = "fraction"
 | 
			
		||||
	typeHandle   = "handle"
 | 
			
		||||
	typeInt      = "integer"
 | 
			
		||||
	typeItem     = "item"
 | 
			
		||||
	typeNumber   = "number"
 | 
			
		||||
	typeString   = "string"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@ -22,9 +22,7 @@ func exportFunc(ctx ExprContext, name string, info ExprFunc) {
 | 
			
		||||
	if name[0] == '@' {
 | 
			
		||||
		name = name[1:]
 | 
			
		||||
	}
 | 
			
		||||
	// ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
 | 
			
		||||
	// ctx.RegisterFuncInfo(name, info)
 | 
			
		||||
	ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params())
 | 
			
		||||
	ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func exportObjects(destCtx, sourceCtx ExprContext) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										25
									
								
								context.go
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								context.go
									
									
									
									
									
								
							@ -4,31 +4,28 @@
 | 
			
		||||
// context.go
 | 
			
		||||
package expr
 | 
			
		||||
 | 
			
		||||
// ---- Function template
 | 
			
		||||
type FuncTemplate func(ctx ExprContext, name string, args []any) (result any, err error)
 | 
			
		||||
 | 
			
		||||
// ---- Functor interface
 | 
			
		||||
type Functor interface {
 | 
			
		||||
	Invoke(ctx ExprContext, name string, args []any) (result any, err error)
 | 
			
		||||
	SetFunc(info ExprFunc)
 | 
			
		||||
	GetFunc() ExprFunc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ---- Function Param Info
 | 
			
		||||
type ExprFuncParam interface {
 | 
			
		||||
	Name() string
 | 
			
		||||
	Type() string
 | 
			
		||||
	IsOptional() bool
 | 
			
		||||
	IsRepeat() bool
 | 
			
		||||
	DefaultValue() any
 | 
			
		||||
type simpleFunctor struct {
 | 
			
		||||
	f FuncTemplate
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (functor *simpleFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
 | 
			
		||||
	return functor.f(ctx, name, args)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ---- Function Info
 | 
			
		||||
type ExprFunc interface {
 | 
			
		||||
	Formatter
 | 
			
		||||
	Name() string
 | 
			
		||||
	MinArgs() int
 | 
			
		||||
	MaxArgs() int
 | 
			
		||||
	Functor() Functor
 | 
			
		||||
	Params() []ExprFuncParam
 | 
			
		||||
	ReturnType() string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ----Expression Context
 | 
			
		||||
@ -41,7 +38,5 @@ type ExprContext interface {
 | 
			
		||||
	EnumFuncs(func(name string) (accept bool)) (funcNames []string)
 | 
			
		||||
	GetFuncInfo(name string) (item ExprFunc, exists bool)
 | 
			
		||||
	Call(name string, args []any) (result any, err error)
 | 
			
		||||
	// RegisterFunc(name string, f Functor, minArgs, maxArgs int)
 | 
			
		||||
	RegisterFuncInfo(info ExprFunc)
 | 
			
		||||
	RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) error
 | 
			
		||||
	RegisterFunc(name string, f Functor, minArgs, maxArgs int)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,7 @@ func TestDictParser(t *testing.T) {
 | 
			
		||||
		var gotResult any
 | 
			
		||||
		var gotErr error
 | 
			
		||||
 | 
			
		||||
		ctx := NewSimpleStore()
 | 
			
		||||
		ctx := NewSimpleFuncStore()
 | 
			
		||||
		ctx.SetVar("var1", int64(123))
 | 
			
		||||
		ctx.SetVar("var2", "abc")
 | 
			
		||||
		ImportMathFuncs(ctx)
 | 
			
		||||
 | 
			
		||||
@ -295,7 +295,7 @@ _item_ = _string-expr_ "**.**" _integer-expr_
 | 
			
		||||
=== Booleans
 | 
			
		||||
Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relational and boolean expressions result in boolean values.
 | 
			
		||||
 | 
			
		||||
.Relational operators^(*)^
 | 
			
		||||
.Relational operators
 | 
			
		||||
[cols="^1,^2,6,4"]
 | 
			
		||||
|===
 | 
			
		||||
| Symbol    | Operation     | Description               | Examples
 | 
			
		||||
@ -314,8 +314,6 @@ Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relationa
 | 
			
		||||
[blue]`"b" \<= "b"` -> _true_
 | 
			
		||||
|===
 | 
			
		||||
 | 
			
		||||
^(*)^ See also the  [blue]`in` operator in the _list_ and _dictionary_ sections.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.Boolean operators
 | 
			
		||||
[cols="^2,^2,5,4"]
 | 
			
		||||
@ -577,7 +575,7 @@ The table below shows all supported operators by decreasing priorities.
 | 
			
		||||
| Priority | Operators     | Position | Operation | Operands and results
 | 
			
		||||
 | 
			
		||||
.2+|*ITEM*| [blue]`.` | _Infix_ | _List item_| _list_ `"."` _integer_ -> _any_
 | 
			
		||||
| [blue]`.` | _Infix_ | _Dict item_ | _dict_ `"."` _any_ -> _any_
 | 
			
		||||
| [blue]`.` | _Infix_ | _Dict item_ | _dict_ `""` _any_ -> _any_
 | 
			
		||||
.2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `"++"` -> _integer_
 | 
			
		||||
| [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `"++"` -> _any_
 | 
			
		||||
.1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ `"!"` -> _integer_
 | 
			
		||||
 | 
			
		||||
@ -585,7 +585,7 @@ pre.rouge .ss {
 | 
			
		||||
<div class="sectionbody">
 | 
			
		||||
<!-- toc disabled -->
 | 
			
		||||
<div class="paragraph">
 | 
			
		||||
<p><mark>TODO: Work in progress (last update on 2024/05/20, 06:58 a.m.)</mark></p>
 | 
			
		||||
<p><mark>TODO: Work in progress (last update on 2024/05/17, 15:47 p.m.)</mark></p>
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
@ -1041,7 +1041,7 @@ pre.rouge .ss {
 | 
			
		||||
<p>Boolean data type has two values only: <em class="blue">true</em> and <em class="blue">false</em>. Relational and boolean expressions result in boolean values.</p>
 | 
			
		||||
</div>
 | 
			
		||||
<table class="tableblock frame-all grid-all stretch">
 | 
			
		||||
<caption class="title">Table 4. Relational operators<sup>(*)</sup></caption>
 | 
			
		||||
<caption class="title">Table 4. Relational operators</caption>
 | 
			
		||||
<colgroup>
 | 
			
		||||
<col style="width: 7.6923%;">
 | 
			
		||||
<col style="width: 15.3846%;">
 | 
			
		||||
@ -1101,9 +1101,6 @@ pre.rouge .ss {
 | 
			
		||||
</tr>
 | 
			
		||||
</tbody>
 | 
			
		||||
</table>
 | 
			
		||||
<div class="paragraph">
 | 
			
		||||
<p><sup>(*)</sup> See also the  <code class="blue">in</code> operator in the <em>list</em> and <em>dictionary</em> sections.</p>
 | 
			
		||||
</div>
 | 
			
		||||
<table class="tableblock frame-all grid-all stretch">
 | 
			
		||||
<caption class="title">Table 5. Boolean operators</caption>
 | 
			
		||||
<colgroup>
 | 
			
		||||
@ -1874,7 +1871,7 @@ These operators have a high priority, in particular higher than the operator <co
 | 
			
		||||
</div>
 | 
			
		||||
<div id="footer">
 | 
			
		||||
<div id="footer-text">
 | 
			
		||||
Last updated 2024-05-20 09:40:23 +0200
 | 
			
		||||
Last updated 2024-05-20 06:58:09 +0200
 | 
			
		||||
</div>
 | 
			
		||||
</div>
 | 
			
		||||
</body>
 | 
			
		||||
 | 
			
		||||
@ -44,7 +44,7 @@ func TestExpr(t *testing.T) {
 | 
			
		||||
		var gotResult any
 | 
			
		||||
		var gotErr error
 | 
			
		||||
 | 
			
		||||
		ctx := NewSimpleStore()
 | 
			
		||||
		ctx := NewSimpleFuncStore()
 | 
			
		||||
		// ImportMathFuncs(ctx)
 | 
			
		||||
		// ImportImportFunc(ctx)
 | 
			
		||||
		ImportOsFuncs(ctx)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										67
									
								
								func-base.go
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								func-base.go
									
									
									
									
									
								
							@ -54,24 +54,6 @@ func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func boolFunc(ctx ExprContext, name string, args []any) (result any, err error) {
 | 
			
		||||
	switch v := args[0].(type) {
 | 
			
		||||
	case int64:
 | 
			
		||||
		result = (v != 0)
 | 
			
		||||
	case *fraction:
 | 
			
		||||
		result = v.num != 0
 | 
			
		||||
	case float64:
 | 
			
		||||
		result = v != 0.0
 | 
			
		||||
	case bool:
 | 
			
		||||
		result = v
 | 
			
		||||
	case string:
 | 
			
		||||
		result = len(v) > 0
 | 
			
		||||
	default:
 | 
			
		||||
		err = errCantConvert(name, v, "bool")
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
 | 
			
		||||
	switch v := args[0].(type) {
 | 
			
		||||
	case int64:
 | 
			
		||||
@ -137,6 +119,10 @@ func fractFunc(ctx ExprContext, name string, args []any) (result any, err error)
 | 
			
		||||
		}
 | 
			
		||||
	case float64:
 | 
			
		||||
		result, err = float64ToFraction(v)
 | 
			
		||||
		// var n, d int64
 | 
			
		||||
		// if n, d, err = float64ToFraction(v); err == nil {
 | 
			
		||||
		//	result = newFraction(n, d)
 | 
			
		||||
		// }
 | 
			
		||||
	case bool:
 | 
			
		||||
		if v {
 | 
			
		||||
			result = newFraction(1, 1)
 | 
			
		||||
@ -145,6 +131,16 @@ func fractFunc(ctx ExprContext, name string, args []any) (result any, err error)
 | 
			
		||||
		}
 | 
			
		||||
	case string:
 | 
			
		||||
		result, err = makeGeneratingFraction(v)
 | 
			
		||||
		// var f float64
 | 
			
		||||
		// // TODO temporary implementation
 | 
			
		||||
		// if f, err = strconv.ParseFloat(v, 64); err == nil {
 | 
			
		||||
		// 	var n, d int64
 | 
			
		||||
		// 	if n, d, err = float64ToFraction(f); err == nil {
 | 
			
		||||
		// 		result = newFraction(n, d)
 | 
			
		||||
		// 	}
 | 
			
		||||
		// } else {
 | 
			
		||||
		// 	errors.New("convertion from string to float is ongoing")
 | 
			
		||||
		// }
 | 
			
		||||
	case *fraction:
 | 
			
		||||
		result = v
 | 
			
		||||
	default:
 | 
			
		||||
@ -158,27 +154,20 @@ func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ImportBuiltinsFuncs(ctx ExprContext) {
 | 
			
		||||
	anyParams := []ExprFuncParam{
 | 
			
		||||
		newFuncParam(paramValue),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ctx.RegisterFunc("isNil", newGolangFunctor(isNilFunc), typeBoolean, anyParams)
 | 
			
		||||
	ctx.RegisterFunc("isInt", newGolangFunctor(isIntFunc), typeBoolean, anyParams)
 | 
			
		||||
	ctx.RegisterFunc("isFloat", newGolangFunctor(isFloatFunc), typeBoolean, anyParams)
 | 
			
		||||
	ctx.RegisterFunc("isBool", newGolangFunctor(isBoolFunc), typeBoolean, anyParams)
 | 
			
		||||
	ctx.RegisterFunc("isString", newGolangFunctor(isStringFunc), typeBoolean, anyParams)
 | 
			
		||||
	ctx.RegisterFunc("isFract", newGolangFunctor(isFractionFunc), typeBoolean, anyParams)
 | 
			
		||||
	ctx.RegisterFunc("isRational", newGolangFunctor(isRationalFunc), typeBoolean, anyParams)
 | 
			
		||||
	ctx.RegisterFunc("isList", newGolangFunctor(isListFunc), typeBoolean, anyParams)
 | 
			
		||||
	ctx.RegisterFunc("isDict", newGolangFunctor(isDictionaryFunc), typeBoolean, anyParams)
 | 
			
		||||
 | 
			
		||||
	ctx.RegisterFunc("bool", newGolangFunctor(boolFunc), typeBoolean, anyParams)
 | 
			
		||||
	ctx.RegisterFunc("int", newGolangFunctor(intFunc), typeInt, anyParams)
 | 
			
		||||
	ctx.RegisterFunc("dec", newGolangFunctor(decFunc), typeFloat, anyParams)
 | 
			
		||||
	ctx.RegisterFunc("fract", newGolangFunctor(fractFunc), typeFraction, []ExprFuncParam{
 | 
			
		||||
		newFuncParam(paramValue),
 | 
			
		||||
		newFuncParamFlagDef("denominator", pfOptional, 1),
 | 
			
		||||
	})
 | 
			
		||||
	ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("isInt", &simpleFunctor{f: isIntFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("isFloat", &simpleFunctor{f: isFloatFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("isBool", &simpleFunctor{f: isBoolFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("isString", &simpleFunctor{f: isStringFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("isFraction", &simpleFunctor{f: isFractionFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("isFract", &simpleFunctor{f: isFractionFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("isRational", &simpleFunctor{f: isRationalFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("isList", &simpleFunctor{f: isListFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("isDictionary", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("isDict", &simpleFunctor{f: isDictionaryFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("dec", &simpleFunctor{f: decFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("fract", &simpleFunctor{f: fractFunc}, 1, 2)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
 | 
			
		||||
@ -137,12 +137,8 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ImportImportFuncs(ctx ExprContext) {
 | 
			
		||||
	ctx.RegisterFunc("import", newGolangFunctor(importFunc), typeAny, []ExprFuncParam{
 | 
			
		||||
		newFuncParamFlag(typeFilepath, pfRepeat),
 | 
			
		||||
	})
 | 
			
		||||
	ctx.RegisterFunc("importAll", newGolangFunctor(importAllFunc), typeAny, []ExprFuncParam{
 | 
			
		||||
		newFuncParamFlag(typeFilepath, pfRepeat),
 | 
			
		||||
	})
 | 
			
		||||
	ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1)
 | 
			
		||||
	ctx.RegisterFunc("importAll", &simpleFunctor{f: importAllFunc}, 1, -1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
 | 
			
		||||
@ -167,13 +167,8 @@ func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ImportMathFuncs(ctx ExprContext) {
 | 
			
		||||
	ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, typeNumber, []ExprFuncParam{
 | 
			
		||||
		newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 0),
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, typeNumber, []ExprFuncParam{
 | 
			
		||||
		newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 1),
 | 
			
		||||
	})
 | 
			
		||||
	ctx.RegisterFunc("add", &simpleFunctor{f: addFunc}, 0, -1)
 | 
			
		||||
	ctx.RegisterFunc("mul", &simpleFunctor{f: mulFunc}, 0, -1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								func-os.go
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								func-os.go
									
									
									
									
									
								
							@ -158,26 +158,12 @@ func readFileFunc(ctx ExprContext, name string, args []any) (result any, err err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ImportOsFuncs(ctx ExprContext) {
 | 
			
		||||
	ctx.RegisterFunc("openFile", newGolangFunctor(openFileFunc), typeHandle, []ExprFuncParam{
 | 
			
		||||
		newFuncParam(typeFilepath),
 | 
			
		||||
	})
 | 
			
		||||
	ctx.RegisterFunc("appendFile", newGolangFunctor(appendFileFunc), typeHandle, []ExprFuncParam{
 | 
			
		||||
		newFuncParam(typeFilepath),
 | 
			
		||||
	})
 | 
			
		||||
	ctx.RegisterFunc("createFile", newGolangFunctor(createFileFunc), typeHandle, []ExprFuncParam{
 | 
			
		||||
		newFuncParam(typeFilepath),
 | 
			
		||||
	})
 | 
			
		||||
	ctx.RegisterFunc("writeFile", newGolangFunctor(writeFileFunc), typeInt, []ExprFuncParam{
 | 
			
		||||
		newFuncParam(typeHandle),
 | 
			
		||||
		newFuncParamFlagDef(typeItem, pfOptional|pfRepeat, ""),
 | 
			
		||||
	})
 | 
			
		||||
	ctx.RegisterFunc("readFile", newGolangFunctor(readFileFunc), typeString, []ExprFuncParam{
 | 
			
		||||
		newFuncParam(typeHandle),
 | 
			
		||||
		newFuncParamFlagDef("limitCh", pfOptional, "\\n"),
 | 
			
		||||
	})
 | 
			
		||||
	ctx.RegisterFunc("closeFile", newGolangFunctor(closeFileFunc), typeBoolean, []ExprFuncParam{
 | 
			
		||||
		newFuncParam(typeHandle),
 | 
			
		||||
	})
 | 
			
		||||
	ctx.RegisterFunc("openFile", &simpleFunctor{f: openFileFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("appendFile", &simpleFunctor{f: appendFileFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("createFile", &simpleFunctor{f: createFileFunc}, 1, 1)
 | 
			
		||||
	ctx.RegisterFunc("writeFile", &simpleFunctor{f: writeFileFunc}, 1, -1)
 | 
			
		||||
	ctx.RegisterFunc("readFile", &simpleFunctor{f: readFileFunc}, 1, 2)
 | 
			
		||||
	ctx.RegisterFunc("closeFile", &simpleFunctor{f: closeFileFunc}, 1, 1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
 | 
			
		||||
@ -62,6 +62,9 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error
 | 
			
		||||
	var source string
 | 
			
		||||
	var ok bool
 | 
			
		||||
 | 
			
		||||
//	if len(args) < 1 {
 | 
			
		||||
//		return nil, errMissingRequiredParameter(name, paramSource)
 | 
			
		||||
//	}
 | 
			
		||||
	if source, ok = args[0].(string); !ok {
 | 
			
		||||
		return nil, errWrongParamType(name, paramSource, typeString, args[0])
 | 
			
		||||
	}
 | 
			
		||||
@ -90,6 +93,9 @@ func trimStrFunc(ctx ExprContext, name string, args []any) (result any, err erro
 | 
			
		||||
	var source string
 | 
			
		||||
	var ok bool
 | 
			
		||||
 | 
			
		||||
//	if len(args) < 1 {
 | 
			
		||||
//		return nil, errMissingRequiredParameter(name, paramSource)
 | 
			
		||||
//	}
 | 
			
		||||
	if source, ok = args[0].(string); !ok {
 | 
			
		||||
		return nil, errWrongParamType(name, paramSource, typeString, args[0])
 | 
			
		||||
	}
 | 
			
		||||
@ -102,7 +108,9 @@ func startsWithStrFunc(ctx ExprContext, name string, args []any) (result any, er
 | 
			
		||||
	var ok bool
 | 
			
		||||
 | 
			
		||||
	result = false
 | 
			
		||||
 | 
			
		||||
//	if len(args) < 1 {
 | 
			
		||||
//		return result, errMissingRequiredParameter(name, paramSource)
 | 
			
		||||
//	}
 | 
			
		||||
	if source, ok = args[0].(string); !ok {
 | 
			
		||||
		return result, errWrongParamType(name, paramSource, typeString, args[0])
 | 
			
		||||
	}
 | 
			
		||||
@ -125,7 +133,9 @@ func endsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err
 | 
			
		||||
	var ok bool
 | 
			
		||||
 | 
			
		||||
	result = false
 | 
			
		||||
 | 
			
		||||
//	if len(args) < 1 {
 | 
			
		||||
//		return result, errMissingRequiredParameter(name, paramSource)
 | 
			
		||||
//	}
 | 
			
		||||
	if source, ok = args[0].(string); !ok {
 | 
			
		||||
		return result, errWrongParamType(name, paramSource, typeString, args[0])
 | 
			
		||||
	}
 | 
			
		||||
@ -149,6 +159,9 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
 | 
			
		||||
	var parts []string
 | 
			
		||||
	var ok bool
 | 
			
		||||
 | 
			
		||||
//	if len(args) < 1 {
 | 
			
		||||
//		return result, errMissingRequiredParameter(name, paramSource)
 | 
			
		||||
//	}
 | 
			
		||||
	if source, ok = args[0].(string); !ok {
 | 
			
		||||
		return result, errWrongParamType(name, paramSource, typeString, args[0])
 | 
			
		||||
	}
 | 
			
		||||
@ -183,36 +196,12 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
 | 
			
		||||
 | 
			
		||||
// Import above functions in the context
 | 
			
		||||
func ImportStringFuncs(ctx ExprContext) {
 | 
			
		||||
	ctx.RegisterFunc("joinStr", newGolangFunctor(joinStrFunc), typeString, []ExprFuncParam{
 | 
			
		||||
		newFuncParam(paramSeparator),
 | 
			
		||||
		newFuncParamFlagDef(paramItem, pfOptional|pfRepeat, ""),
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	ctx.RegisterFunc("subStr", newGolangFunctor(subStrFunc), typeString, []ExprFuncParam{
 | 
			
		||||
		newFuncParam(paramSource),
 | 
			
		||||
		newFuncParamFlagDef(paramStart, pfOptional, 0),
 | 
			
		||||
		newFuncParamFlagDef(paramCount, pfOptional, -1),
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	ctx.RegisterFunc("splitStr", newGolangFunctor(splitStrFunc), "list of "+typeString, []ExprFuncParam{
 | 
			
		||||
		newFuncParam(paramSource),
 | 
			
		||||
		newFuncParamFlagDef(paramSeparator, pfOptional, ""),
 | 
			
		||||
		newFuncParamFlagDef(paramCount, pfOptional, -1),
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	ctx.RegisterFunc("trimStr", newGolangFunctor(trimStrFunc), typeString, []ExprFuncParam{
 | 
			
		||||
		newFuncParam(paramSource),
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	ctx.RegisterFunc("startsWithStr", newGolangFunctor(startsWithStrFunc), typeBoolean, []ExprFuncParam{
 | 
			
		||||
		newFuncParam(paramSource),
 | 
			
		||||
		newFuncParamFlag(paramPrefix, pfRepeat),
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	ctx.RegisterFunc("endsWithStr", newGolangFunctor(endsWithStrFunc), typeBoolean, []ExprFuncParam{
 | 
			
		||||
		newFuncParam(paramSource),
 | 
			
		||||
		newFuncParamFlag(paramSuffix, pfRepeat),
 | 
			
		||||
	})
 | 
			
		||||
	ctx.RegisterFunc("joinStr", &simpleFunctor{f: joinStrFunc}, 1, -1)
 | 
			
		||||
	ctx.RegisterFunc("subStr", &simpleFunctor{f: subStrFunc}, 1, -1)
 | 
			
		||||
	ctx.RegisterFunc("splitStr", &simpleFunctor{f: splitStrFunc}, 2, -1)
 | 
			
		||||
	ctx.RegisterFunc("trimStr", &simpleFunctor{f: trimStrFunc}, 1, -1)
 | 
			
		||||
	ctx.RegisterFunc("startsWithStr", &simpleFunctor{f: startsWithStrFunc}, 2, -1)
 | 
			
		||||
	ctx.RegisterFunc("endsWithStr", &simpleFunctor{f: endsWithStrFunc}, 2, -1)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Register the import function in the import-register.
 | 
			
		||||
 | 
			
		||||
@ -76,41 +76,14 @@ func TestFuncs(t *testing.T) {
 | 
			
		||||
		/*  63 */ {`dec(true)`, float64(1), nil},
 | 
			
		||||
		/*  64 */ {`dec(true")`, nil, errors.New("[1:11] missing string termination \"")},
 | 
			
		||||
		/*  65 */ {`builtin "string"; joinStr("-", [1, "two", "three"])`, nil, errors.New(`joinStr() expected string, got int64 (1)`)},
 | 
			
		||||
		/*  66 */ {`dec()`, nil, errors.New(`too few params -- expected 1, got 0`)},
 | 
			
		||||
		/*  67 */ {`dec(1,2,3)`, nil, errors.New(`too much params -- expected 1, got 3`)},
 | 
			
		||||
		/*  68 */ {`builtin "string"; joinStr()`, nil, errors.New(`too few params -- expected 1 or more, got 0`)},
 | 
			
		||||
		/*  69 */ /*{`builtin "string"; $$global`, `vars: {
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
		funcs: {
 | 
			
		||||
		    	add(any=0 ...) -> number,
 | 
			
		||||
		    	dec(any) -> decimal,
 | 
			
		||||
		    	endsWithStr(source, suffix) -> boolean,
 | 
			
		||||
		    	fract(any, denominator=1) -> fraction,
 | 
			
		||||
		    	import( ...) -> any,
 | 
			
		||||
		    	importAll( ...) -> any,
 | 
			
		||||
		    	int(any) -> integer,
 | 
			
		||||
		    	isBool(any) -> boolean,
 | 
			
		||||
		    	isDec(any) -> boolean,
 | 
			
		||||
		    	isDict(any) -> boolean,
 | 
			
		||||
		    	isFloat(any) -> boolean,
 | 
			
		||||
		    	isFract(any) -> boolean,
 | 
			
		||||
		    	isInt(any) -> boolean,
 | 
			
		||||
		    	isList(any) -> boolean,
 | 
			
		||||
		    	isNil(any) -> boolean,
 | 
			
		||||
		    	isString(any) -> boolean,
 | 
			
		||||
		    	joinStr(separator, item="" ...) -> string,
 | 
			
		||||
		    	mul(any=1 ...) -> number,
 | 
			
		||||
		    	splitStr(source, separator="", count=-1) -> list of string,
 | 
			
		||||
		    	startsWithStr(source, prefix) -> boolean,
 | 
			
		||||
		    	subStr(source, start=0, count=-1) -> string,
 | 
			
		||||
		    	trimStr(source) -> string
 | 
			
		||||
		}
 | 
			
		||||
		`, nil},*/
 | 
			
		||||
		/*  65 */ {`dec()`, nil, errors.New(`too few params -- expected 1, got 0`)},
 | 
			
		||||
		/*  66 */ {`dec(1,2,3)`, nil, errors.New(`too much params -- expected 1, got 3`)},
 | 
			
		||||
		/*  67 */ {`builtin "string"; joinStr()`, nil, errors.New(`too few params -- expected 1 or more, got 0`)},
 | 
			
		||||
		// /*  64 */ {`string(true)`, "true", nil},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t.Setenv("EXPR_PATH", ".")
 | 
			
		||||
 | 
			
		||||
	// parserTestSpec(t, "Func", inputs, 69)
 | 
			
		||||
	//parserTest(t, "Func", inputs[54:55])
 | 
			
		||||
	parserTest(t, "Func", inputs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										232
									
								
								function.go
									
									
									
									
									
								
							
							
						
						
									
										232
									
								
								function.go
									
									
									
									
									
								
							@ -1,232 +0,0 @@
 | 
			
		||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | 
			
		||||
// All rights reserved.
 | 
			
		||||
 | 
			
		||||
// function.go
 | 
			
		||||
package expr
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ---- Function template
 | 
			
		||||
type FuncTemplate func(ctx ExprContext, name string, args []any) (result any, err error)
 | 
			
		||||
 | 
			
		||||
// ---- Common functor definition
 | 
			
		||||
type baseFunctor struct {
 | 
			
		||||
	info ExprFunc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (functor *baseFunctor) ToString(opt FmtOpt) (s string) {
 | 
			
		||||
	if functor.info != nil {
 | 
			
		||||
		s = functor.info.ToString(opt)
 | 
			
		||||
	} else {
 | 
			
		||||
		s = "func() {<body>}"
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (functor *baseFunctor) SetFunc(info ExprFunc) {
 | 
			
		||||
	functor.info = info
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (functor *baseFunctor) GetFunc() ExprFunc {
 | 
			
		||||
	return functor.info
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ---- Linking with the functions of Go
 | 
			
		||||
type golangFunctor struct {
 | 
			
		||||
	baseFunctor
 | 
			
		||||
	f FuncTemplate
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newGolangFunctor(f FuncTemplate) *golangFunctor {
 | 
			
		||||
	return &golangFunctor{f: f}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
 | 
			
		||||
	return functor.f(ctx, name, args)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ---- Linking with the functions of Expr
 | 
			
		||||
type exprFunctor struct {
 | 
			
		||||
	baseFunctor
 | 
			
		||||
	params []string
 | 
			
		||||
	expr   Expr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newExprFunctor(e Expr, params []string) *exprFunctor {
 | 
			
		||||
	return &exprFunctor{expr: e, params: params}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
 | 
			
		||||
	for i, p := range functor.params {
 | 
			
		||||
		if i < len(args) {
 | 
			
		||||
			arg := args[i]
 | 
			
		||||
			if funcArg, ok := arg.(Functor); ok {
 | 
			
		||||
				// ctx.RegisterFunc(p, functor, 0, -1)
 | 
			
		||||
				ctx.RegisterFunc(p, funcArg, typeAny, []ExprFuncParam{
 | 
			
		||||
					newFuncParam(paramValue),
 | 
			
		||||
				})
 | 
			
		||||
			} else {
 | 
			
		||||
				ctx.UnsafeSetVar(p, arg)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			ctx.UnsafeSetVar(p, nil)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	result, err = functor.expr.eval(ctx, false)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ---- Function Parameters
 | 
			
		||||
type paramFlags uint16
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	pfOptional paramFlags = 1 << iota
 | 
			
		||||
	pfRepeat
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type funcParamInfo struct {
 | 
			
		||||
	name         string
 | 
			
		||||
	flags        paramFlags
 | 
			
		||||
	defaultValue any
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newFuncParam(name string) ExprFuncParam {
 | 
			
		||||
	return &funcParamInfo{name: name}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newFuncParamFlag(name string, flags paramFlags) ExprFuncParam {
 | 
			
		||||
	return &funcParamInfo{name: name, flags: flags}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newFuncParamFlagDef(name string, flags paramFlags, defValue any) *funcParamInfo {
 | 
			
		||||
	return &funcParamInfo{name: name, flags: flags, defaultValue: defValue}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (param *funcParamInfo) Name() string {
 | 
			
		||||
	return param.name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (param *funcParamInfo) Type() string {
 | 
			
		||||
	return "any"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (param *funcParamInfo) IsOptional() bool {
 | 
			
		||||
	return (param.flags & pfOptional) != 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (param *funcParamInfo) IsRepeat() bool {
 | 
			
		||||
	return (param.flags & pfRepeat) != 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (param *funcParamInfo) DefaultValue() any {
 | 
			
		||||
	return param.defaultValue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// --- Functions
 | 
			
		||||
type funcInfo struct {
 | 
			
		||||
	name       string
 | 
			
		||||
	minArgs    int
 | 
			
		||||
	maxArgs    int
 | 
			
		||||
	functor    Functor
 | 
			
		||||
	params     []ExprFuncParam
 | 
			
		||||
	returnType string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
 | 
			
		||||
	var minArgs = 0
 | 
			
		||||
	var maxArgs = 0
 | 
			
		||||
	if params != nil {
 | 
			
		||||
		for _, p := range params {
 | 
			
		||||
			if maxArgs == -1 {
 | 
			
		||||
				return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
 | 
			
		||||
			}
 | 
			
		||||
			if p.IsOptional() {
 | 
			
		||||
				maxArgs++
 | 
			
		||||
			} else if maxArgs == minArgs {
 | 
			
		||||
				minArgs++
 | 
			
		||||
				maxArgs++
 | 
			
		||||
			} else {
 | 
			
		||||
				return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name())
 | 
			
		||||
			}
 | 
			
		||||
			if p.IsRepeat() {
 | 
			
		||||
				maxArgs = -1
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	info = &funcInfo{
 | 
			
		||||
		name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, params: params,
 | 
			
		||||
	}
 | 
			
		||||
	functor.SetFunc(info)
 | 
			
		||||
	return info, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newUnnamedFuncInfo(functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) {
 | 
			
		||||
	return newFuncInfo("unnamed", functor, returnType, params)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) Params() []ExprFuncParam {
 | 
			
		||||
	return info.params
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) ReturnType() string {
 | 
			
		||||
	return info.returnType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) ToString(opt FmtOpt) string {
 | 
			
		||||
	var sb strings.Builder
 | 
			
		||||
	if len(info.Name()) == 0 {
 | 
			
		||||
		sb.WriteString("func")
 | 
			
		||||
	} else {
 | 
			
		||||
		sb.WriteString(info.Name())
 | 
			
		||||
	}
 | 
			
		||||
	sb.WriteByte('(')
 | 
			
		||||
	if info.params != nil {
 | 
			
		||||
		for i, p := range info.params {
 | 
			
		||||
			if i > 0 {
 | 
			
		||||
				sb.WriteString(", ")
 | 
			
		||||
			}
 | 
			
		||||
			sb.WriteString(p.Name())
 | 
			
		||||
 | 
			
		||||
			if p.IsOptional() {
 | 
			
		||||
				sb.WriteByte('=')
 | 
			
		||||
				if s, ok := p.DefaultValue().(string); ok {
 | 
			
		||||
					sb.WriteByte('"')
 | 
			
		||||
					sb.WriteString(s)
 | 
			
		||||
					sb.WriteByte('"')
 | 
			
		||||
				} else {
 | 
			
		||||
					sb.WriteString(fmt.Sprintf("%v", p.DefaultValue()))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if info.maxArgs < 0 {
 | 
			
		||||
		sb.WriteString(" ...")
 | 
			
		||||
	}
 | 
			
		||||
	sb.WriteString("): ")
 | 
			
		||||
	if len(info.returnType) > 0 {
 | 
			
		||||
		sb.WriteString(info.returnType)
 | 
			
		||||
	} else {
 | 
			
		||||
		sb.WriteString(typeAny)
 | 
			
		||||
	}
 | 
			
		||||
	sb.WriteString(" {<body>}")
 | 
			
		||||
	return sb.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) Name() string {
 | 
			
		||||
	return info.name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) MinArgs() int {
 | 
			
		||||
	return info.minArgs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) MaxArgs() int {
 | 
			
		||||
	return info.maxArgs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) Functor() Functor {
 | 
			
		||||
	return info.functor
 | 
			
		||||
}
 | 
			
		||||
@ -6,7 +6,7 @@ package expr
 | 
			
		||||
 | 
			
		||||
import "path/filepath"
 | 
			
		||||
 | 
			
		||||
var globalCtx *SimpleStore
 | 
			
		||||
var globalCtx *SimpleFuncStore
 | 
			
		||||
 | 
			
		||||
func ImportInContext(name string) (exists bool) {
 | 
			
		||||
	var mod *module
 | 
			
		||||
@ -50,6 +50,5 @@ func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool, owne
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	globalCtx = NewSimpleStore()
 | 
			
		||||
	ImportBuiltinsFuncs(globalCtx)
 | 
			
		||||
	globalCtx = NewSimpleFuncStore()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -34,15 +34,12 @@ func EvalStringA(source string, args ...Arg) (result any, err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func EvalStringV(source string, args []Arg) (result any, err error) {
 | 
			
		||||
	ctx := NewSimpleStore()
 | 
			
		||||
	ctx := NewSimpleFuncStore()
 | 
			
		||||
	for _, arg := range args {
 | 
			
		||||
		if isFunc(arg.Value) {
 | 
			
		||||
			if f, ok := arg.Value.(FuncTemplate); ok {
 | 
			
		||||
				functor := newGolangFunctor(f)
 | 
			
		||||
				// ctx.RegisterFunc(arg.Name, functor, 0, -1)
 | 
			
		||||
				ctx.RegisterFunc(arg.Name, functor, typeAny, []ExprFuncParam{
 | 
			
		||||
					newFuncParamFlagDef(paramValue, pfOptional|pfRepeat, 0),
 | 
			
		||||
				})
 | 
			
		||||
				functor := &simpleFunctor{f: f}
 | 
			
		||||
				ctx.RegisterFunc(arg.Name, functor, 0, -1)
 | 
			
		||||
			} else {
 | 
			
		||||
				err = fmt.Errorf("invalid function specification: %q", arg.Name)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@ -52,7 +52,7 @@ func TestEvalStringA(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestEvalString(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
	ctx := NewSimpleStore()
 | 
			
		||||
	ctx := NewSimpleVarStore()
 | 
			
		||||
	ctx.SetVar("a", uint8(1))
 | 
			
		||||
	ctx.SetVar("b", int8(2))
 | 
			
		||||
	ctx.SetVar("f", 2.0)
 | 
			
		||||
 | 
			
		||||
@ -60,7 +60,7 @@ func TestListParser(t *testing.T) {
 | 
			
		||||
		var gotResult any
 | 
			
		||||
		var gotErr error
 | 
			
		||||
 | 
			
		||||
		ctx := NewSimpleStore()
 | 
			
		||||
		ctx := NewSimpleFuncStore()
 | 
			
		||||
		// ctx.SetVar("var1", int64(123))
 | 
			
		||||
		// ctx.SetVar("var2", "abc")
 | 
			
		||||
		ImportMathFuncs(ctx)
 | 
			
		||||
 | 
			
		||||
@ -31,8 +31,7 @@ func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) {
 | 
			
		||||
			err = errTooMuchParams(info.MaxArgs(), len(params))
 | 
			
		||||
		}
 | 
			
		||||
		if err == nil && owner != ctx {
 | 
			
		||||
			// ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
 | 
			
		||||
			ctx.RegisterFuncInfo(info)
 | 
			
		||||
			ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs())
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		err = fmt.Errorf("unknown function %s()", name)
 | 
			
		||||
@ -76,30 +75,27 @@ func newFuncDefTerm(tk *Token, args []*term) *term {
 | 
			
		||||
 | 
			
		||||
// -------- eval func def
 | 
			
		||||
// TODO
 | 
			
		||||
// type funcDefFunctor struct {
 | 
			
		||||
// 	params []string
 | 
			
		||||
// 	expr   Expr
 | 
			
		||||
// }
 | 
			
		||||
type funcDefFunctor struct {
 | 
			
		||||
	params []string
 | 
			
		||||
	expr   Expr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// func (funcDef *funcDefFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
 | 
			
		||||
// 	for i, p := range funcDef.params {
 | 
			
		||||
// 		if i < len(args) {
 | 
			
		||||
// 			arg := args[i]
 | 
			
		||||
// 			if functor, ok := arg.(Functor); ok {
 | 
			
		||||
// 				// ctx.RegisterFunc(p, functor, 0, -1)
 | 
			
		||||
// 				ctx.RegisterFunc2(p, functor, typeAny, []ExprFuncParam{
 | 
			
		||||
// 					newFuncParam(paramValue),
 | 
			
		||||
// 				})
 | 
			
		||||
// 			} else {
 | 
			
		||||
// 				ctx.UnsafeSetVar(p, arg)
 | 
			
		||||
// 			}
 | 
			
		||||
// 		} else {
 | 
			
		||||
// 			ctx.UnsafeSetVar(p, nil)
 | 
			
		||||
// 		}
 | 
			
		||||
// 	}
 | 
			
		||||
// 	result, err = funcDef.expr.eval(ctx, false)
 | 
			
		||||
// 	return
 | 
			
		||||
// }
 | 
			
		||||
func (functor *funcDefFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) {
 | 
			
		||||
	for i, p := range functor.params {
 | 
			
		||||
		if i < len(args) {
 | 
			
		||||
			arg := args[i]
 | 
			
		||||
			if functor, ok := arg.(Functor); ok {
 | 
			
		||||
				ctx.RegisterFunc(p, functor, 0, -1)
 | 
			
		||||
			} else {
 | 
			
		||||
				ctx.UnsafeSetVar(p, arg)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			ctx.UnsafeSetVar(p, nil)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	result, err = functor.expr.eval(ctx, false)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
 | 
			
		||||
	bodySpec := self.value()
 | 
			
		||||
@ -108,11 +104,10 @@ func evalFuncDef(ctx ExprContext, self *term) (v any, err error) {
 | 
			
		||||
		for _, param := range self.children {
 | 
			
		||||
			paramList = append(paramList, param.source())
 | 
			
		||||
		}
 | 
			
		||||
		v = newExprFunctor(expr, paramList)
 | 
			
		||||
		// v = &funcDefFunctor{
 | 
			
		||||
		// 	params: paramList,
 | 
			
		||||
		// 	expr:   expr,
 | 
			
		||||
		// }
 | 
			
		||||
		v = &funcDefFunctor{
 | 
			
		||||
			params: paramList,
 | 
			
		||||
			expr:   expr,
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		err = errors.New("invalid function definition: the body specification must be an expression")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@ -74,8 +74,7 @@ func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map
 | 
			
		||||
		ds = make(map[string]Functor)
 | 
			
		||||
		for keyAny, item := range *dictAny {
 | 
			
		||||
			if key, ok := keyAny.(string); ok {
 | 
			
		||||
				//if functor, ok := item.(*funcDefFunctor); ok {
 | 
			
		||||
				if functor, ok := item.(Functor); ok {
 | 
			
		||||
				if functor, ok := item.(*funcDefFunctor); ok {
 | 
			
		||||
					ds[key] = functor
 | 
			
		||||
					if index := slices.Index(requiredFields, key); index >= 0 {
 | 
			
		||||
						foundFields |= 1 << index
 | 
			
		||||
 | 
			
		||||
@ -31,21 +31,18 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
 | 
			
		||||
 | 
			
		||||
	if v, err = rightChild.compute(ctx); err == nil {
 | 
			
		||||
		if functor, ok := v.(Functor); ok {
 | 
			
		||||
			var minArgs, maxArgs int = 0, -1
 | 
			
		||||
 | 
			
		||||
			funcName := rightChild.source()
 | 
			
		||||
			if info, exists, _ := GetFuncInfo(ctx, funcName); exists {
 | 
			
		||||
				// ctx.RegisterFuncInfo(info)
 | 
			
		||||
				ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params())
 | 
			
		||||
			} else if funcDef, ok := functor.(*exprFunctor); ok {
 | 
			
		||||
				paramSpecs := ForAll(funcDef.params, newFuncParam)
 | 
			
		||||
				// paramCount := len(funcDef.params)
 | 
			
		||||
				// paramSpecs := make([]ExprFuncParam, paramCount)
 | 
			
		||||
				// for i := range paramSpecs {
 | 
			
		||||
				// 	paramSpecs[i] = newFuncParam(funcDef.params[i])
 | 
			
		||||
				// }
 | 
			
		||||
				ctx.RegisterFunc(leftTerm.source(), functor, typeAny, paramSpecs)
 | 
			
		||||
			} else {
 | 
			
		||||
				err = self.Errorf("unknown function %s()", funcName)
 | 
			
		||||
			if info, exists := ctx.GetFuncInfo(funcName); exists {
 | 
			
		||||
				minArgs = info.MinArgs()
 | 
			
		||||
				maxArgs = info.MaxArgs()
 | 
			
		||||
			} else if funcDef, ok := functor.(*funcDefFunctor); ok {
 | 
			
		||||
				l := len(funcDef.params)
 | 
			
		||||
				minArgs = l
 | 
			
		||||
				maxArgs = l
 | 
			
		||||
			}
 | 
			
		||||
			ctx.RegisterFunc(leftTerm.source(), functor, minArgs, maxArgs)
 | 
			
		||||
		} else {
 | 
			
		||||
			ctx.UnsafeSetVar(leftTerm.source(), v)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -66,10 +66,7 @@ func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
 | 
			
		||||
		v = leftValue
 | 
			
		||||
	} else if rightValue, err = self.children[1].compute(ctx); err == nil {
 | 
			
		||||
		if functor, ok := rightValue.(Functor); ok {
 | 
			
		||||
			//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
 | 
			
		||||
			ctx.RegisterFunc(leftTerm.source(), functor, typeAny, []ExprFuncParam{
 | 
			
		||||
				newFuncParamFlag(paramValue, pfOptional|pfRepeat),
 | 
			
		||||
			})
 | 
			
		||||
			ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
 | 
			
		||||
		} else {
 | 
			
		||||
			v = rightValue
 | 
			
		||||
			ctx.UnsafeSetVar(leftTerm.source(), rightValue)
 | 
			
		||||
 | 
			
		||||
@ -20,18 +20,18 @@ func evalContextValue(ctx ExprContext, self *term) (v any, err error) {
 | 
			
		||||
	var childValue any
 | 
			
		||||
 | 
			
		||||
	var sourceCtx ExprContext
 | 
			
		||||
	if self.children == nil || len(self.children) == 0 {
 | 
			
		||||
	if len(self.children) == 0 {
 | 
			
		||||
		sourceCtx = ctx
 | 
			
		||||
	} else if self.children[0].symbol() == SymVariable && self.children[0].source() == "global" {
 | 
			
		||||
		sourceCtx = globalCtx
 | 
			
		||||
	} else if childValue, err = self.evalPrefix(ctx); err == nil {
 | 
			
		||||
		if dc, ok := childValue.(*dataCursor); ok {
 | 
			
		||||
			sourceCtx = dc.ctx
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if sourceCtx != nil {
 | 
			
		||||
		if formatter, ok := sourceCtx.(Formatter); ok {
 | 
			
		||||
		if formatter, ok := ctx.(Formatter); ok {
 | 
			
		||||
			v = formatter.ToString(0)
 | 
			
		||||
		} else {
 | 
			
		||||
			keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
 | 
			
		||||
 | 
			
		||||
@ -163,32 +163,46 @@ func TestGeneralParser(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// t.Setenv("EXPR_PATH", ".")
 | 
			
		||||
	// parserTestSpec(t, "General", inputs, 102)
 | 
			
		||||
	parserTest(t, "General", inputs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parserTestSpec(t *testing.T, section string, inputs []inputType, spec ...int) {
 | 
			
		||||
	succeeded := 0
 | 
			
		||||
	failed := 0
 | 
			
		||||
	for _, count := range spec {
 | 
			
		||||
		good := doTest(t, section, &inputs[count-1], count)
 | 
			
		||||
 | 
			
		||||
		if good {
 | 
			
		||||
			succeeded++
 | 
			
		||||
		} else {
 | 
			
		||||
			failed++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(spec), succeeded, failed)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parserTest(t *testing.T, section string, inputs []inputType) {
 | 
			
		||||
 | 
			
		||||
	succeeded := 0
 | 
			
		||||
	failed := 0
 | 
			
		||||
 | 
			
		||||
	for i, input := range inputs {
 | 
			
		||||
		good := doTest(t, section, &input, i+1)
 | 
			
		||||
		var expr Expr
 | 
			
		||||
		var gotResult any
 | 
			
		||||
		var gotErr error
 | 
			
		||||
 | 
			
		||||
		ctx := NewSimpleFuncStore()
 | 
			
		||||
		parser := NewParser(ctx)
 | 
			
		||||
 | 
			
		||||
		logTest(t, i+1, section, input.source, input.wantResult, input.wantErr)
 | 
			
		||||
 | 
			
		||||
		r := strings.NewReader(input.source)
 | 
			
		||||
		scanner := NewScanner(r, DefaultTranslations())
 | 
			
		||||
 | 
			
		||||
		good := true
 | 
			
		||||
		if expr, gotErr = parser.Parse(scanner); gotErr == nil {
 | 
			
		||||
			gotResult, gotErr = expr.Eval(ctx)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		eq := reflect.DeepEqual(gotResult, input.wantResult)
 | 
			
		||||
 | 
			
		||||
		if !eq /*gotResult != input.wantResult*/ {
 | 
			
		||||
			t.Errorf("%d: %q -> result = %v [%T], want %v [%T]", i+1, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
 | 
			
		||||
			good = false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if gotErr != input.wantErr {
 | 
			
		||||
			if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) {
 | 
			
		||||
				t.Errorf("%d: %q -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr)
 | 
			
		||||
				good = false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if good {
 | 
			
		||||
			succeeded++
 | 
			
		||||
		} else {
 | 
			
		||||
@ -198,40 +212,6 @@ func parserTest(t *testing.T, section string, inputs []inputType) {
 | 
			
		||||
	t.Logf("%s -- test count: %d, succeeded: %d, failed: %d", section, len(inputs), succeeded, failed)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func doTest(t *testing.T, section string, input *inputType, count int) (good bool) {
 | 
			
		||||
	var expr Expr
 | 
			
		||||
	var gotResult any
 | 
			
		||||
	var gotErr error
 | 
			
		||||
 | 
			
		||||
	ctx := NewSimpleStore()
 | 
			
		||||
	parser := NewParser(ctx)
 | 
			
		||||
 | 
			
		||||
	logTest(t, count, section, input.source, input.wantResult, input.wantErr)
 | 
			
		||||
 | 
			
		||||
	r := strings.NewReader(input.source)
 | 
			
		||||
	scanner := NewScanner(r, DefaultTranslations())
 | 
			
		||||
 | 
			
		||||
	good = true
 | 
			
		||||
	if expr, gotErr = parser.Parse(scanner); gotErr == nil {
 | 
			
		||||
		gotResult, gotErr = expr.Eval(ctx)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	eq := reflect.DeepEqual(gotResult, input.wantResult)
 | 
			
		||||
 | 
			
		||||
	if !eq /*gotResult != input.wantResult*/ {
 | 
			
		||||
		t.Errorf("%d: %q -> result = %v [%T], want = %v [%T]", count, input.source, gotResult, gotResult, input.wantResult, input.wantResult)
 | 
			
		||||
		good = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if gotErr != input.wantErr {
 | 
			
		||||
		if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) {
 | 
			
		||||
			t.Errorf("%d: %q -> got-err = <%v>, expected-err = <%v>", count, input.source, gotErr, input.wantErr)
 | 
			
		||||
			good = false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) {
 | 
			
		||||
	if wantErr == nil {
 | 
			
		||||
		t.Logf("[+]%s nr %3d -- %q  --> %v", section, n, source, wantResult)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										142
									
								
								simple-func-store.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										142
									
								
								simple-func-store.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,142 @@
 | 
			
		||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | 
			
		||||
// All rights reserved.
 | 
			
		||||
 | 
			
		||||
// simple-func-store.go
 | 
			
		||||
package expr
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SimpleFuncStore struct {
 | 
			
		||||
	SimpleVarStore
 | 
			
		||||
	funcStore map[string]*funcInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type funcInfo struct {
 | 
			
		||||
	name    string
 | 
			
		||||
	minArgs int
 | 
			
		||||
	maxArgs int
 | 
			
		||||
	functor Functor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) ToString(opt FmtOpt) string {
 | 
			
		||||
	var sb strings.Builder
 | 
			
		||||
	var i int
 | 
			
		||||
	sb.WriteString("func(")
 | 
			
		||||
	for i = 0; i < info.minArgs; i++ {
 | 
			
		||||
		if i > 0 {
 | 
			
		||||
			sb.WriteString(", ")
 | 
			
		||||
		}
 | 
			
		||||
		sb.WriteString(fmt.Sprintf("arg%d", i+1))
 | 
			
		||||
	}
 | 
			
		||||
	for ; i < info.maxArgs; i++ {
 | 
			
		||||
		sb.WriteString(fmt.Sprintf("arg%d", i+1))
 | 
			
		||||
	}
 | 
			
		||||
	if info.maxArgs < 0 {
 | 
			
		||||
		if info.minArgs > 0 {
 | 
			
		||||
			sb.WriteString(", ")
 | 
			
		||||
		}
 | 
			
		||||
		sb.WriteString("...")
 | 
			
		||||
	}
 | 
			
		||||
	sb.WriteString(") {...}")
 | 
			
		||||
	return sb.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) Name() string {
 | 
			
		||||
	return info.name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) MinArgs() int {
 | 
			
		||||
	return info.minArgs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) MaxArgs() int {
 | 
			
		||||
	return info.maxArgs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) Functor() Functor {
 | 
			
		||||
	return info.functor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSimpleFuncStore() *SimpleFuncStore {
 | 
			
		||||
	ctx := &SimpleFuncStore{
 | 
			
		||||
		SimpleVarStore: SimpleVarStore{varStore: make(map[string]any)},
 | 
			
		||||
		funcStore:      make(map[string]*funcInfo),
 | 
			
		||||
	}
 | 
			
		||||
	ImportBuiltinsFuncs(ctx)
 | 
			
		||||
	return ctx
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleFuncStore) Clone() ExprContext {
 | 
			
		||||
	svs := ctx.SimpleVarStore
 | 
			
		||||
	return &SimpleFuncStore{
 | 
			
		||||
		// SimpleVarStore: SimpleVarStore{varStore: CloneMap(ctx.varStore)},
 | 
			
		||||
		SimpleVarStore: SimpleVarStore{varStore: svs.cloneVars()},
 | 
			
		||||
		funcStore:      CloneFilteredMap(ctx.funcStore, func(name string) bool { return name[0] != '@' }),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func funcsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
 | 
			
		||||
	sb.WriteString("funcs: {\n")
 | 
			
		||||
	first := true
 | 
			
		||||
	for _, name := range ctx.EnumFuncs(func(name string) bool { return true }) {
 | 
			
		||||
		if first {
 | 
			
		||||
			first = false
 | 
			
		||||
		} else {
 | 
			
		||||
			sb.WriteByte(',')
 | 
			
		||||
			sb.WriteByte('\n')
 | 
			
		||||
		}
 | 
			
		||||
		value, _ := ctx.GetFuncInfo(name)
 | 
			
		||||
		sb.WriteString(strings.Repeat("\t", indent+1))
 | 
			
		||||
		sb.WriteString(name)
 | 
			
		||||
		sb.WriteString("=")
 | 
			
		||||
		if formatter, ok := value.(Formatter); ok {
 | 
			
		||||
			sb.WriteString(formatter.ToString(0))
 | 
			
		||||
		} else {
 | 
			
		||||
			sb.WriteString(fmt.Sprintf("%v", value))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	sb.WriteString("\n}\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleFuncStore) ToString(opt FmtOpt) string {
 | 
			
		||||
	var sb strings.Builder
 | 
			
		||||
	sb.WriteString(ctx.SimpleVarStore.ToString(opt))
 | 
			
		||||
	funcsCtxToBuilder(&sb, ctx, 0)
 | 
			
		||||
	return sb.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleFuncStore) GetFuncInfo(name string) (info ExprFunc, exists bool) {
 | 
			
		||||
	info, exists = ctx.funcStore[name]
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleFuncStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) {
 | 
			
		||||
	ctx.funcStore[name] = &funcInfo{name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleFuncStore) EnumFuncs(acceptor func(name string) (accept bool)) (funcNames []string) {
 | 
			
		||||
	funcNames = make([]string, 0)
 | 
			
		||||
	for name := range ctx.funcStore {
 | 
			
		||||
		if acceptor != nil {
 | 
			
		||||
			if acceptor(name) {
 | 
			
		||||
				funcNames = append(funcNames, name)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			funcNames = append(funcNames, name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleFuncStore) Call(name string, args []any) (result any, err error) {
 | 
			
		||||
	if info, exists := ctx.funcStore[name]; exists {
 | 
			
		||||
		functor := info.functor
 | 
			
		||||
		result, err = functor.Invoke(ctx, name, args)
 | 
			
		||||
	} else {
 | 
			
		||||
		err = fmt.Errorf("unknown function %s()", name)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@ -1,244 +0,0 @@
 | 
			
		||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | 
			
		||||
// All rights reserved.
 | 
			
		||||
 | 
			
		||||
// simple-func-store.go
 | 
			
		||||
package expr
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"slices"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SimpleFuncStore struct {
 | 
			
		||||
	SimpleVarStore
 | 
			
		||||
	funcStore map[string]*funcInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type paramFlags uint16
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	pfOptional paramFlags = 1 << iota
 | 
			
		||||
	pfRepeat
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type funcParamInfo struct {
 | 
			
		||||
	name         string
 | 
			
		||||
	flags        paramFlags
 | 
			
		||||
	defaultValue any
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newFuncParam(name string) *funcParamInfo {
 | 
			
		||||
	return &funcParamInfo{name: name}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newFuncParamFlag(name string, flags paramFlags) *funcParamInfo {
 | 
			
		||||
	return &funcParamInfo{name: name, flags: flags}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newFuncParamFlagDef(name string, flags paramFlags, defValue any) *funcParamInfo {
 | 
			
		||||
	return &funcParamInfo{name: name, flags: flags, defaultValue: defValue}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (param *funcParamInfo) Name() string {
 | 
			
		||||
	return param.name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (param *funcParamInfo) Type() string {
 | 
			
		||||
	return "any"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (param *funcParamInfo) IsOptional() bool {
 | 
			
		||||
	return (param.flags & pfOptional) != 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (param *funcParamInfo) IsRepeat() bool {
 | 
			
		||||
	return (param.flags & pfRepeat) != 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (param *funcParamInfo) DefaultValue() any {
 | 
			
		||||
	return param.defaultValue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type funcInfo struct {
 | 
			
		||||
	name       string
 | 
			
		||||
	minArgs    int
 | 
			
		||||
	maxArgs    int
 | 
			
		||||
	functor    Functor
 | 
			
		||||
	params     []ExprFuncParam
 | 
			
		||||
	returnType string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) Params() []ExprFuncParam {
 | 
			
		||||
	return info.params
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) ReturnType() string {
 | 
			
		||||
	return info.returnType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) ToString(opt FmtOpt) string {
 | 
			
		||||
	var sb strings.Builder
 | 
			
		||||
	sb.WriteByte('(')
 | 
			
		||||
	if info.params != nil {
 | 
			
		||||
		for i, p := range info.params {
 | 
			
		||||
			if i > 0 {
 | 
			
		||||
				sb.WriteString(", ")
 | 
			
		||||
			}
 | 
			
		||||
			sb.WriteString(p.Name())
 | 
			
		||||
			if p.IsOptional() {
 | 
			
		||||
				sb.WriteByte('=')
 | 
			
		||||
				if s, ok := p.DefaultValue().(string); ok {
 | 
			
		||||
					sb.WriteByte('"')
 | 
			
		||||
					sb.WriteString(s)
 | 
			
		||||
					sb.WriteByte('"')
 | 
			
		||||
				} else {
 | 
			
		||||
					sb.WriteString(fmt.Sprintf("%v", p.DefaultValue()))
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if info.maxArgs < 0 {
 | 
			
		||||
		sb.WriteString(" ...")
 | 
			
		||||
	}
 | 
			
		||||
	sb.WriteString(") -> ")
 | 
			
		||||
	if len(info.returnType) > 0 {
 | 
			
		||||
		sb.WriteString(info.returnType)
 | 
			
		||||
	} else {
 | 
			
		||||
		sb.WriteString(typeAny)
 | 
			
		||||
	}
 | 
			
		||||
	return sb.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) Name() string {
 | 
			
		||||
	return info.name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) MinArgs() int {
 | 
			
		||||
	return info.minArgs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) MaxArgs() int {
 | 
			
		||||
	return info.maxArgs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (info *funcInfo) Functor() Functor {
 | 
			
		||||
	return info.functor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSimpleFuncStore() *SimpleFuncStore {
 | 
			
		||||
	ctx := &SimpleFuncStore{
 | 
			
		||||
		SimpleVarStore: SimpleVarStore{varStore: make(map[string]any)},
 | 
			
		||||
		funcStore:      make(map[string]*funcInfo),
 | 
			
		||||
	}
 | 
			
		||||
	//ImportBuiltinsFuncs(ctx)
 | 
			
		||||
	return ctx
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleFuncStore) Clone() ExprContext {
 | 
			
		||||
	svs := ctx.SimpleVarStore
 | 
			
		||||
	return &SimpleFuncStore{
 | 
			
		||||
		// SimpleVarStore: SimpleVarStore{varStore: CloneMap(ctx.varStore)},
 | 
			
		||||
		SimpleVarStore: SimpleVarStore{varStore: svs.cloneVars()},
 | 
			
		||||
		funcStore:      CloneFilteredMap(ctx.funcStore, func(name string) bool { return name[0] != '@' }),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func funcsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
 | 
			
		||||
	sb.WriteString("funcs: {\n")
 | 
			
		||||
	first := true
 | 
			
		||||
	names := ctx.EnumFuncs(func(name string) bool { return true })
 | 
			
		||||
	slices.Sort(names)
 | 
			
		||||
	for _, name := range names {
 | 
			
		||||
		if first {
 | 
			
		||||
			first = false
 | 
			
		||||
		} else {
 | 
			
		||||
			sb.WriteByte(',')
 | 
			
		||||
			sb.WriteByte('\n')
 | 
			
		||||
		}
 | 
			
		||||
		value, _ := ctx.GetFuncInfo(name)
 | 
			
		||||
		sb.WriteString(strings.Repeat("\t", indent+1))
 | 
			
		||||
		sb.WriteString(name)
 | 
			
		||||
		//sb.WriteString("=")
 | 
			
		||||
		if formatter, ok := value.(Formatter); ok {
 | 
			
		||||
			sb.WriteString(formatter.ToString(0))
 | 
			
		||||
		} else {
 | 
			
		||||
			sb.WriteString(fmt.Sprintf("%v", value))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	sb.WriteString("\n}\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleFuncStore) ToString(opt FmtOpt) string {
 | 
			
		||||
	var sb strings.Builder
 | 
			
		||||
	sb.WriteString(ctx.SimpleVarStore.ToString(opt))
 | 
			
		||||
	funcsCtxToBuilder(&sb, ctx, 0)
 | 
			
		||||
	return sb.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleFuncStore) GetFuncInfo(name string) (info ExprFunc, exists bool) {
 | 
			
		||||
	info, exists = ctx.funcStore[name]
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// func (ctx *SimpleFuncStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) {
 | 
			
		||||
// 	ctx.funcStore[name] = &funcInfo{name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor}
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleFuncStore) RegisterFuncInfo(info ExprFunc) {
 | 
			
		||||
	ctx.funcStore[info.Name()], _ = info.(*funcInfo)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleFuncStore) RegisterFunc2(name string, functor Functor, returnType string, params []ExprFuncParam) error {
 | 
			
		||||
	var minArgs = 0
 | 
			
		||||
	var maxArgs = 0
 | 
			
		||||
	if params != nil {
 | 
			
		||||
		for _, p := range params {
 | 
			
		||||
			if maxArgs == -1 {
 | 
			
		||||
				return fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
 | 
			
		||||
				// } else if p.IsRepeat() {
 | 
			
		||||
				// 	maxArgs = -1
 | 
			
		||||
				// 	continue
 | 
			
		||||
			}
 | 
			
		||||
			if p.IsOptional() {
 | 
			
		||||
				maxArgs++
 | 
			
		||||
			} else if maxArgs == minArgs {
 | 
			
		||||
				minArgs++
 | 
			
		||||
				maxArgs++
 | 
			
		||||
			} else {
 | 
			
		||||
				return fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name())
 | 
			
		||||
			}
 | 
			
		||||
			if p.IsRepeat() {
 | 
			
		||||
				maxArgs = -1
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ctx.funcStore[name] = &funcInfo{
 | 
			
		||||
		name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, params: params,
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleFuncStore) EnumFuncs(acceptor func(name string) (accept bool)) (funcNames []string) {
 | 
			
		||||
	funcNames = make([]string, 0)
 | 
			
		||||
	for name := range ctx.funcStore {
 | 
			
		||||
		if acceptor != nil {
 | 
			
		||||
			if acceptor(name) {
 | 
			
		||||
				funcNames = append(funcNames, name)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			funcNames = append(funcNames, name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleFuncStore) Call(name string, args []any) (result any, err error) {
 | 
			
		||||
	if info, exists := ctx.funcStore[name]; exists {
 | 
			
		||||
		functor := info.functor
 | 
			
		||||
		result, err = functor.Invoke(ctx, name, args)
 | 
			
		||||
	} else {
 | 
			
		||||
		err = fmt.Errorf("unknown function %s()", name)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										173
									
								
								simple-store.go
									
									
									
									
									
								
							
							
						
						
									
										173
									
								
								simple-store.go
									
									
									
									
									
								
							@ -1,173 +0,0 @@
 | 
			
		||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | 
			
		||||
// All rights reserved.
 | 
			
		||||
 | 
			
		||||
// simple-store.go
 | 
			
		||||
package expr
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"slices"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type SimpleStore struct {
 | 
			
		||||
	varStore  map[string]any
 | 
			
		||||
	funcStore map[string]*funcInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func NewSimpleStore() *SimpleStore {
 | 
			
		||||
	ctx := &SimpleStore{
 | 
			
		||||
		varStore:  make(map[string]any),
 | 
			
		||||
		funcStore: make(map[string]*funcInfo),
 | 
			
		||||
	}
 | 
			
		||||
	//ImportBuiltinsFuncs(ctx)
 | 
			
		||||
	return ctx
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleStore) Clone() ExprContext {
 | 
			
		||||
	return &SimpleStore{
 | 
			
		||||
		varStore:  CloneFilteredMap(ctx.varStore, func(name string) bool { return name[0] != '@' }),
 | 
			
		||||
		funcStore: CloneFilteredMap(ctx.funcStore, func(name string) bool { return name[0] != '@' }),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
 | 
			
		||||
	sb.WriteString("vars: {\n")
 | 
			
		||||
	first := true
 | 
			
		||||
	for _, name := range ctx.EnumVars(func(name string) bool { return name[0] != '_' }) {
 | 
			
		||||
		if first {
 | 
			
		||||
			first = false
 | 
			
		||||
		} else {
 | 
			
		||||
			sb.WriteByte(',')
 | 
			
		||||
			sb.WriteByte('\n')
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		value, _ := ctx.GetVar(name)
 | 
			
		||||
		sb.WriteString(strings.Repeat("\t", indent+1))
 | 
			
		||||
		sb.WriteString(name)
 | 
			
		||||
		sb.WriteString(": ")
 | 
			
		||||
		if f, ok := value.(Formatter); ok {
 | 
			
		||||
			sb.WriteString(f.ToString(0))
 | 
			
		||||
		} else if _, ok = value.(Functor); ok {
 | 
			
		||||
			sb.WriteString("func(){}")
 | 
			
		||||
			// } else if _, ok = value.(map[any]any); ok {
 | 
			
		||||
			// 	sb.WriteString("dict{}")
 | 
			
		||||
		} else {
 | 
			
		||||
			sb.WriteString(fmt.Sprintf("%v", value))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	sb.WriteString(strings.Repeat("\t", indent))
 | 
			
		||||
	sb.WriteString("\n}\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func varsCtxToString(ctx ExprContext, indent int) string {
 | 
			
		||||
	var sb strings.Builder
 | 
			
		||||
	varsCtxToBuilder(&sb, ctx, indent)
 | 
			
		||||
	return sb.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func funcsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
 | 
			
		||||
	sb.WriteString("funcs: {\n")
 | 
			
		||||
	first := true
 | 
			
		||||
	names := ctx.EnumFuncs(func(name string) bool { return true })
 | 
			
		||||
	slices.Sort(names)
 | 
			
		||||
	for _, name := range names {
 | 
			
		||||
		if first {
 | 
			
		||||
			first = false
 | 
			
		||||
		} else {
 | 
			
		||||
			sb.WriteByte(',')
 | 
			
		||||
			sb.WriteByte('\n')
 | 
			
		||||
		}
 | 
			
		||||
		value, _ := ctx.GetFuncInfo(name)
 | 
			
		||||
		sb.WriteString(strings.Repeat("\t", indent+1))
 | 
			
		||||
		sb.WriteString(name)
 | 
			
		||||
		//sb.WriteString("=")
 | 
			
		||||
		if formatter, ok := value.(Formatter); ok {
 | 
			
		||||
			sb.WriteString(formatter.ToString(0))
 | 
			
		||||
		} else {
 | 
			
		||||
			sb.WriteString(fmt.Sprintf("%v", value))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	sb.WriteString("\n}\n")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleStore) ToString(opt FmtOpt) string {
 | 
			
		||||
	var sb strings.Builder
 | 
			
		||||
	varsCtxToBuilder(&sb, ctx, 0)
 | 
			
		||||
	funcsCtxToBuilder(&sb, ctx, 0)
 | 
			
		||||
	return sb.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleStore) GetVar(varName string) (v any, exists bool) {
 | 
			
		||||
	v, exists = ctx.varStore[varName]
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleStore) UnsafeSetVar(varName string, value any) {
 | 
			
		||||
	// fmt.Printf("[%p] setVar(%v, %v)\n", ctx, varName, value)
 | 
			
		||||
	ctx.varStore[varName] = value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleStore) SetVar(varName string, value any) {
 | 
			
		||||
	// fmt.Printf("[%p] SetVar(%v, %v)\n", ctx, varName, value)
 | 
			
		||||
	if allowedValue, ok := fromGenericAny(value); ok {
 | 
			
		||||
		ctx.varStore[varName] = allowedValue
 | 
			
		||||
	} else {
 | 
			
		||||
		panic(fmt.Errorf("unsupported type %T of value %v", value, value))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleStore) EnumVars(acceptor func(name string) (accept bool)) (varNames []string) {
 | 
			
		||||
	varNames = make([]string, 0)
 | 
			
		||||
	for name := range ctx.varStore {
 | 
			
		||||
		if acceptor != nil {
 | 
			
		||||
			if acceptor(name) {
 | 
			
		||||
				varNames = append(varNames, name)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			varNames = append(varNames, name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleStore) GetFuncInfo(name string) (info ExprFunc, exists bool) {
 | 
			
		||||
	info, exists = ctx.funcStore[name]
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleStore) RegisterFuncInfo(info ExprFunc) {
 | 
			
		||||
	ctx.funcStore[info.Name()], _ = info.(*funcInfo)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleStore) RegisterFunc(name string, functor Functor, returnType string, params []ExprFuncParam) (err error) {
 | 
			
		||||
	var info *funcInfo
 | 
			
		||||
	if info, err = newFuncInfo(name, functor, returnType, params); err == nil {
 | 
			
		||||
		ctx.funcStore[name] = info
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleStore) EnumFuncs(acceptor func(name string) (accept bool)) (funcNames []string) {
 | 
			
		||||
	funcNames = make([]string, 0)
 | 
			
		||||
	for name := range ctx.funcStore {
 | 
			
		||||
		if acceptor != nil {
 | 
			
		||||
			if acceptor(name) {
 | 
			
		||||
				funcNames = append(funcNames, name)
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			funcNames = append(funcNames, name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleStore) Call(name string, args []any) (result any, err error) {
 | 
			
		||||
	if info, exists := ctx.funcStore[name]; exists {
 | 
			
		||||
		functor := info.functor
 | 
			
		||||
		result, err = functor.Invoke(ctx, name, args)
 | 
			
		||||
	} else {
 | 
			
		||||
		err = fmt.Errorf("unknown function %s()", name)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
@ -72,12 +72,7 @@ func (ctx *SimpleVarStore) Call(name string, args []any) (result any, err error)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// func (ctx *SimpleVarStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) {
 | 
			
		||||
// }
 | 
			
		||||
func (ctx *SimpleVarStore) RegisterFuncInfo(info ExprFunc) {
 | 
			
		||||
}
 | 
			
		||||
func (ctx *SimpleVarStore) RegisterFunc2(name string, f Functor, returnType string, param []ExprFuncParam) error {
 | 
			
		||||
	return nil
 | 
			
		||||
func (ctx *SimpleVarStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ctx *SimpleVarStore) EnumFuncs(acceptor func(name string) (accept bool)) (funcNames []string) {
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user