Operator '@@' (export-all) added. Experimental include() function also added

This commit is contained in:
Celestino Amoroso 2024-04-06 03:06:07 +02:00
parent ce6b88ccdd
commit 7612a59757
10 changed files with 108 additions and 29 deletions

2
ast.go
View File

@ -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

View File

@ -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)
}

View File

@ -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{

View File

@ -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)

29
operator-ctrl.go Normal file
View File

@ -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)
}

View File

@ -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

17
sample-export-all.expr Normal file
View File

@ -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};

View File

@ -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)
}

View File

@ -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

View File

@ -10,3 +10,6 @@
// two(): returns 2
@two=func() {2};
// six(): returns 6
six=func() {6};