diff --git a/ast.go b/ast.go index 91f3fce..9cb4afc 100644 --- a/ast.go +++ b/ast.go @@ -108,7 +108,7 @@ func (self *ast) Eval(ctx exprContext, preset bool) (result any, err error) { if self.forest != nil { for _, root := range self.forest { if result, err = root.compute(ctx); err == nil { - ctx.SetVar(preset_last_result, result) + ctx.SetVar(control_last_result, result) } else { //err = fmt.Errorf("error in expression nr %d: %v", i+1, err) break diff --git a/func-import.go b/func-import.go index 01d75e2..d07c7c0 100644 --- a/func-import.go +++ b/func-import.go @@ -14,6 +14,15 @@ import ( const ENV_EXPR_PATH = "EXPR_PATH" func importFunc(ctx exprContext, name string, args []any) (result any, err error) { + return importGeneral(ctx, name, args) +} + +func includeFunc(ctx exprContext, name string, args []any) (result any, err error) { + enable(ctx, control_export_all) + return importGeneral(ctx, name, args) +} + +func importGeneral(ctx exprContext, name string, args []any) (result any, err error) { var dirList []string dirList = addEnvImportDirs(dirList) @@ -42,7 +51,7 @@ func addEnvImportDirs(dirList []string) []string { } func addPresetImportDirs(ctx exprContext, dirList []string) []string { - if dirSpec, exists := getPresetString(ctx, preset_import_path); exists { + if dirSpec, exists := getControlString(ctx, control_import_path); exists { dirs := strings.Split(dirSpec, ":") if dirList == nil { dirList = dirs @@ -105,7 +114,7 @@ func doImport(ctx exprContext, name string, dirList []string, it Iterator) (resu scanner := NewScanner(file, DefaultTranslations()) parser := NewParser(ctx) if expr, err = parser.parseGeneral(scanner, true, true); err == nil { - _, err = expr.Eval(ctx, false) + result, err = expr.Eval(ctx, false) } if err != nil { break @@ -114,12 +123,17 @@ func doImport(ctx exprContext, name string, dirList []string, it Iterator) (resu break } } - if err != nil && err == io.EOF { - err = nil + if err != nil { + if err == io.EOF { + err = nil + } else { + result = nil + } } return } func importImportFunc(ctx exprContext) { ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1) + ctx.RegisterFunc("include", &simpleFunctor{f: includeFunc}, 1, -1) } diff --git a/operand-func.go b/operand-func.go index 614957d..21c23ab 100644 --- a/operand-func.go +++ b/operand-func.go @@ -36,15 +36,16 @@ func evalFuncCall(parentCtx exprContext, self *term) (v any, err error) { } if err == nil { if v, err = ctx.Call(name, params); err == nil { + exportAll := isEnabled(ctx, control_export_all) // Export variables - for _, refName := range ctx.EnumVars(func(name string) bool { return name[0] == '@' }) { + for _, refName := range ctx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) { refValue, _ := ctx.GetVar(refName) - parentCtx.SetVar(refName[1:], refValue) + exportVar(parentCtx, refName, refValue) } // Export functions - for _, refName := range ctx.EnumFuncs(func(name string) bool { return name[0] == '@' }) { + for _, refName := range ctx.EnumFuncs(func(name string) bool { return exportAll || name[0] == '@' }) { if info := ctx.GetFuncInfo(refName); info != nil { - parentCtx.RegisterFunc(refName[1:], info.Functor(), info.MinArgs(), info.MaxArgs()) + exportFunc(parentCtx, refName, info) } } } @@ -52,6 +53,20 @@ func evalFuncCall(parentCtx exprContext, self *term) (v any, err error) { return } +func exportVar(ctx exprContext, name string, value any) { + if name[0] == '@' { + name = name[1:] + } + ctx.SetVar(name, value) +} + +func exportFunc(ctx exprContext, name string, info exprFunc) { + if name[0] == '@' { + name = name[1:] + } + ctx.RegisterFunc(name, info.Functor(), info.MinArgs(), info.MaxArgs()) +} + // -------- function definition term func newFuncDefTerm(tk *Token, args []*term) *term { return &term{ diff --git a/operator-bool.go b/operator-bool.go index 15f32e4..a8b9778 100644 --- a/operator-bool.go +++ b/operator-bool.go @@ -50,7 +50,7 @@ func newAndTerm(tk *Token) (inst *term) { } func evalAnd(ctx exprContext, self *term) (v any, err error) { - if isEnabled(ctx, preset_bool_shortcut) { + if isEnabled(ctx, control_bool_shortcut) { v, err = evalAndWithShortcut(ctx, self) } else { v, err = evalAndWithoutShortcut(ctx, self) @@ -119,7 +119,7 @@ func newOrTerm(tk *Token) (inst *term) { } func evalOr(ctx exprContext, self *term) (v any, err error) { - if isEnabled(ctx, preset_bool_shortcut) { + if isEnabled(ctx, control_bool_shortcut) { v, err = evalOrWithShortcut(ctx, self) } else { v, err = evalOrWithoutShortcut(ctx, self) diff --git a/operator-ctrl.go b/operator-ctrl.go new file mode 100644 index 0000000..7fa900d --- /dev/null +++ b/operator-ctrl.go @@ -0,0 +1,29 @@ +// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). +// All rights reserved. + +// operator-ctrl.go +package expr + +//-------- export all term + +func newExportAllTerm(tk *Token) (inst *term) { + return &term{ + tk: *tk, + class: classOperator, + kind: kindUnknown, + children: nil, + position: posLeaf, + priority: priValue, + evalFunc: evalExportAll, + } +} + +func evalExportAll(ctx exprContext, self *term) (v any, err error) { + enable(ctx, control_export_all) + return +} + +// init +func init() { + registerTermConstructor(SymDoubleAt, newExportAllTerm) +} diff --git a/parser_test.go b/parser_test.go index 303f26f..0a7e2aa 100644 --- a/parser_test.go +++ b/parser_test.go @@ -144,23 +144,18 @@ func TestParser(t *testing.T) { /* 123 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil}, /* 124 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @y=@y+@z}; f(2); y+z`, int64(12), nil}, /* 125 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil}, + /* 126 */ {`include("./test-funcs.expr"); six()`, int64(6), nil}, } check_env_expr_path := 113 succeeded := 0 failed := 0 - // inputs1 := []inputType{ - // {`f=func(@y){g=func(){@x=5}; g(); @z=x}; f(2)`, int64(5), nil}, - // {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil}, - // {`import("./test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil}, - // {`add(1,2,3)`, int64(6), nil}, - // {`a=5; a`, int64(5), nil}, - // {`a=5; b=2; add(a, b*3)`, int64(11), nil}, - // {`"a" < "b" AND ~ 2 == 1`, true, nil}, - // } + inputs1 := []inputType{ + {`import("./test-funcs.expr"); six()`, int64(6), nil}, + } - for i, input := range inputs { + for i, input := range inputs1 { var expr *ast var gotResult any var gotErr error @@ -206,7 +201,7 @@ func TestParser(t *testing.T) { t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed)) } -func TestListParser(t *testing.T) { +func NoTestListParser(t *testing.T) { type inputType struct { source string wantResult any diff --git a/sample-export-all.expr b/sample-export-all.expr new file mode 100644 index 0000000..8cf35e0 --- /dev/null +++ b/sample-export-all.expr @@ -0,0 +1,17 @@ +/* + sample-export-all.expr: example source file +*/ + +@@; // Enable export all mode + +// double(x): returns 2*x +double=func(x){2*x}; + +// Define variable 'a' wth value 5 +a=5; + +// two(): returns 2 +two=func() {2}; + +// six(): returns 6 +six=func() {6}; diff --git a/scanner.go b/scanner.go index a5b0a9e..1b364b0 100644 --- a/scanner.go +++ b/scanner.go @@ -199,6 +199,8 @@ func (self *scanner) fetchNextToken() (tk *Token) { } else { tk = self.makeErrorToken(fmt.Errorf("invalid variable reference %q", tk.source)) } + } else if next == '@' { + tk = self.moveOn(SymDoubleAt, ch, next) } else { tk = self.makeToken(SymAt, ch) } diff --git a/symbol.go b/symbol.go index 49547b9..87f67f2 100644 --- a/symbol.go +++ b/symbol.go @@ -55,8 +55,9 @@ const ( SymOpenBrace // 44: '{' SymClosedBrace // 45: '}' SymTilde // 46: '~' - SymDoubleQuestion - SymQuestionEqual + SymDoubleQuestion // 47: '??' + SymQuestionEqual // 48: '?=' + SymDoubleAt // 49: '@@' SymChangeSign SymUnchangeSign SymIdentifier @@ -64,9 +65,6 @@ const ( SymInteger SymFloat SymString - SymKwAnd - SymKwNot - SymKwOr SymOr SymAnd SymNot @@ -74,12 +72,18 @@ const ( SymFuncCall SymFuncDef SymList - SymKwBut - SymKwFunc SymExpression // SymOpenComment // 0: '/*' // SymClosedComment // 0: '*/' // SymOneLineComment // 0: '//' + keywordBase +) +const ( + SymKwAnd = keywordBase + iota + SymKwNot + SymKwOr + SymKwBut + SymKwFunc ) var keywords map[string]Symbol diff --git a/test-funcs.expr b/test-funcs.expr index 99c42c6..1d1b83d 100644 --- a/test-funcs.expr +++ b/test-funcs.expr @@ -10,3 +10,6 @@ // two(): returns 2 @two=func() {2}; + +// six(): returns 6 +six=func() {6};