Variable reference

This commit is contained in:
Celestino Amoroso 2024-04-06 01:00:29 +02:00
parent 574a6f5215
commit 0ba96e65a5
7 changed files with 60 additions and 25 deletions

View File

@ -25,6 +25,7 @@ type exprFunc interface {
Name() string Name() string
MinArgs() int MinArgs() int
MaxArgs() int MaxArgs() int
Functor() Functor
} }
// ----Expression Context // ----Expression Context
@ -33,6 +34,7 @@ type exprContext interface {
GetVar(varName string) (value any, exists bool) GetVar(varName string) (value any, exists bool)
SetVar(varName string, value any) SetVar(varName string, value any)
EnumVars(func(name string) (accept bool)) (varNames []string) EnumVars(func(name string) (accept bool)) (varNames []string)
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
GetFuncInfo(name string) exprFunc GetFuncInfo(name string) exprFunc
Call(name string, args []any) (result any, err error) Call(name string, args []any) (result any, err error)
RegisterFunc(name string, f Functor, minArgs, maxArgs int) RegisterFunc(name string, f Functor, minArgs, maxArgs int)

View File

@ -104,9 +104,14 @@ func doImport(ctx exprContext, name string, dirList []string, it Iterator) (resu
var expr *ast var expr *ast
scanner := NewScanner(file, DefaultTranslations()) scanner := NewScanner(file, DefaultTranslations())
parser := NewParser(ctx) parser := NewParser(ctx)
if expr, err = parser.parse(scanner); err == nil { if expr, err = parser.parseGeneral(scanner, true, true); err == nil {
_, err = expr.Eval(ctx, false) _, err = expr.Eval(ctx, false)
} }
if err != nil {
break
}
} else {
break
} }
} }
if err != nil && err == io.EOF { if err != nil && err == io.EOF {

View File

@ -36,10 +36,17 @@ func evalFuncCall(parentCtx exprContext, self *term) (v any, err error) {
} }
if err == nil { if err == nil {
if v, err = ctx.Call(name, params); err == nil { if v, err = ctx.Call(name, params); err == nil {
// Export variables
for _, refName := range ctx.EnumVars(func(name string) bool { return name[0] == '@' }) { for _, refName := range ctx.EnumVars(func(name string) bool { return name[0] == '@' }) {
refValue, _ := ctx.GetVar(refName) refValue, _ := ctx.GetVar(refName)
parentCtx.SetVar(refName[1:], refValue) parentCtx.SetVar(refName[1:], refValue)
} }
// Export functions
for _, refName := range ctx.EnumFuncs(func(name string) bool { return name[0] == '@' }) {
if info := ctx.GetFuncInfo(refName); info != nil {
parentCtx.RegisterFunc(refName[1:], info.Functor(), info.MinArgs(), info.MaxArgs())
}
}
} }
} }
return return

View File

@ -139,11 +139,11 @@ func TestParser(t *testing.T) {
/* 118 */ {`x="hello"; x ?= "default"; x`, "hello", nil}, /* 118 */ {`x="hello"; x ?= "default"; x`, "hello", nil},
/* 119 */ {`@x="hello"; @x`, nil, errors.New(`[1:3] variable references are not allowed in top level expressions: "@x"`)}, /* 119 */ {`@x="hello"; @x`, nil, errors.New(`[1:3] variable references are not allowed in top level expressions: "@x"`)},
/* 120 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil}, /* 120 */ {`f=func(){@x="hello"}; f(); x`, "hello", nil},
/* 120 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil}, /* 121 */ {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil},
/* 121 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, errors.New(`undefined variable "x"`)}, /* 122 */ {`f=func(@y){g=func(){@x=5}; @y=@y+g()}; f(2); y+x`, nil, errors.New(`undefined variable "x"`)},
/* 122 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil}, /* 123 */ {`f=func(@y){g=func(){@x=5}; @z=g(); @y=@y+@z}; f(2); y+z`, int64(12), nil},
/* 123 */ {`f=func(@y){g=func(){@x=5}; g(); @z=@x; @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},
// TODO Fix this: /* 124 */ {`f=func(@y){g=func(){@x=5}; g(); @z=@x; @y=@y+@z}; f(2); y+x`, nil, errors.New(`undefined variable "x"`)}, /* 125 */ {`f=func(@y){g=func(){@x=5}; g(); @z=x; @x=@y+@z}; f(2); y+x`, int64(9), nil},
} }
check_env_expr_path := 113 check_env_expr_path := 113
@ -151,6 +151,7 @@ func TestParser(t *testing.T) {
failed := 0 failed := 0
// inputs1 := []inputType{ // 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}, // {`f=func(@y){@y=@y+1}; f(2); y`, int64(3), nil},
// {`import("./test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil}, // {`import("./test-funcs.expr"); (double(3+a) + 1) * two()`, int64(34), nil},
// {`add(1,2,3)`, int64(6), nil}, // {`add(1,2,3)`, int64(6), nil},
@ -188,7 +189,7 @@ func TestParser(t *testing.T) {
if gotErr != input.wantErr { if gotErr != input.wantErr {
if input.wantErr == nil || gotErr == nil || (gotErr.Error() != input.wantErr.Error()) { 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) t.Errorf("%d: %q -> err = <%v>, want <%v>", i+1, input.source, gotErr, input.wantErr)
good = false good = false
} }
} }
@ -205,7 +206,7 @@ func TestParser(t *testing.T) {
t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed)) t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed))
} }
func NoTestListParser(t *testing.T) { func TestListParser(t *testing.T) {
type inputType struct { type inputType struct {
source string source string
wantResult any wantResult any
@ -261,7 +262,6 @@ func NoTestListParser(t *testing.T) {
good = false good = false
} }
// if good {}
if gotList, okGot := gotResult.([]any); okGot { if gotList, okGot := gotResult.([]any); okGot {
if wantList, okWant := input.wantResult.([]any); okWant { if wantList, okWant := input.wantResult.([]any); okWant {
if (gotList == nil && wantList != nil) || (gotList != nil && wantList == nil) { if (gotList == nil && wantList != nil) || (gotList != nil && wantList == nil) {

View File

@ -5,13 +5,14 @@ import "fmt"
type SimpleFuncStore struct { type SimpleFuncStore struct {
varStore map[string]any varStore map[string]any
funcStore map[string]Functor funcStore map[string]*funcInfo
} }
type funcInfo struct { type funcInfo struct {
name string name string
// minArgs int minArgs int
// maxArgs int maxArgs int
functor Functor
} }
func (info *funcInfo) Name() string { func (info *funcInfo) Name() string {
@ -19,17 +20,21 @@ func (info *funcInfo) Name() string {
} }
func (info *funcInfo) MinArgs() int { func (info *funcInfo) MinArgs() int {
return 0 return info.minArgs
} }
func (info *funcInfo) MaxArgs() int { func (info *funcInfo) MaxArgs() int {
return -1 return info.maxArgs
}
func (info *funcInfo) Functor() Functor {
return info.functor
} }
func NewSimpleFuncStore() *SimpleFuncStore { func NewSimpleFuncStore() *SimpleFuncStore {
return &SimpleFuncStore{ return &SimpleFuncStore{
varStore: make(map[string]any), varStore: make(map[string]any),
funcStore: make(map[string]Functor), funcStore: make(map[string]*funcInfo),
} }
} }
@ -63,20 +68,32 @@ func (ctx *SimpleFuncStore) EnumVars(acceptor func(name string) (accept bool)) (
return return
} }
func (ctx *SimpleFuncStore) GetFuncInfo(name string) (f exprFunc) { func (ctx *SimpleFuncStore) GetFuncInfo(name string) (info exprFunc) {
var exists bool info, _ = ctx.funcStore[name]
if _, exists = ctx.funcStore[name]; exists {
f = &funcInfo{name: name}
}
return return
} }
func (ctx *SimpleFuncStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) { func (ctx *SimpleFuncStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) {
ctx.funcStore[name] = functor 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) { func (ctx *SimpleFuncStore) Call(name string, args []any) (result any, err error) {
if functor, exists := ctx.funcStore[name]; exists { if info, exists := ctx.funcStore[name]; exists {
functor := info.functor
result, err = functor.Invoke(ctx, name, args) result, err = functor.Invoke(ctx, name, args)
} else { } else {
err = fmt.Errorf("unknown function %s()", name) err = fmt.Errorf("unknown function %s()", name)

View File

@ -51,3 +51,7 @@ func (ctx *SimpleVarStore) Call(name string, args []any) (result any, err error)
func (ctx *SimpleVarStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) { func (ctx *SimpleVarStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) {
} }
func (ctx *SimpleVarStore) EnumFuncs(acceptor func(name string) (accept bool)) (funcNames []string) {
return
}

View File

@ -3,10 +3,10 @@
*/ */
// double(x): returns 2*x // double(x): returns 2*x
double=func(x){2*x}; @double=func(x){2*x};
// Define variable 'a' wth value 5 // Define variable 'a' wth value 5
a=5; @a=5;
// two(): returns 2 // two(): returns 2
two=func() {2}; @two=func() {2};