Compare commits

...

11 Commits

12 changed files with 70 additions and 33 deletions

3
ast.go
View File

@ -12,6 +12,7 @@ import (
type Expr interface { type Expr interface {
Eval(ctx ExprContext) (result any, err error) Eval(ctx ExprContext) (result any, err error)
eval(ctx ExprContext, preset bool) (result any, err error) eval(ctx ExprContext, preset bool) (result any, err error)
String() string
} }
//-------- ast //-------- ast
@ -118,7 +119,7 @@ func (self *ast) eval(ctx ExprContext, preset bool) (result any, err error) {
if self.forest != nil { if self.forest != nil {
for _, root := range self.forest { for _, root := range self.forest {
if result, err = root.compute(ctx); err == nil { if result, err = root.compute(ctx); err == nil {
ctx.setVar(control_last_result, result) ctx.setVar(ControlLastResult, result)
} else { } else {
//err = fmt.Errorf("error in expression nr %d: %v", i+1, err) //err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
break break

View File

@ -5,9 +5,9 @@ import "strings"
// Preset control variables // Preset control variables
const ( const (
control_last_result = "_last" ControlLastResult = "last"
control_bool_shortcut = "_bool_shortcut" ControlBoolShortcut = "_bool_shortcut"
control_import_path = "_import_path" ControlImportPath = "_import_path"
) )
// Other control variables // Other control variables
@ -21,23 +21,23 @@ const (
) )
func initDefaultVars(ctx ExprContext) { func initDefaultVars(ctx ExprContext) {
ctx.setVar(control_bool_shortcut, true) ctx.SetVar(ControlBoolShortcut, true)
ctx.setVar(control_import_path, init_import_path) ctx.SetVar(ControlImportPath, init_import_path)
} }
func enable(ctx ExprContext, name string) { func enable(ctx ExprContext, name string) {
if strings.HasPrefix(name, "_") { if strings.HasPrefix(name, "_") {
ctx.setVar(name, true) ctx.SetVar(name, true)
} else { } else {
ctx.setVar("_"+name, true) ctx.SetVar("_"+name, true)
} }
} }
func disable(ctx ExprContext, name string) { func disable(ctx ExprContext, name string) {
if strings.HasPrefix(name, "_") { if strings.HasPrefix(name, "_") {
ctx.setVar(name, false) ctx.SetVar(name, false)
} else { } else {
ctx.setVar("_"+name, false) ctx.SetVar("_"+name, false)
} }
} }

View File

@ -51,7 +51,7 @@ func addEnvImportDirs(dirList []string) []string {
} }
func addPresetImportDirs(ctx ExprContext, dirList []string) []string { func addPresetImportDirs(ctx ExprContext, dirList []string) []string {
if dirSpec, exists := getControlString(ctx, control_import_path); exists { if dirSpec, exists := getControlString(ctx, ControlImportPath); exists {
dirs := strings.Split(dirSpec, ":") dirs := strings.Split(dirSpec, ":")
if dirList == nil { if dirList == nil {
dirList = dirs dirList = dirs
@ -133,7 +133,7 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
return return
} }
func importImportFunc(ctx ExprContext) { func ImportImportFunc(ctx ExprContext) {
ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1) ctx.RegisterFunc("import", &simpleFunctor{f: importFunc}, 1, -1)
ctx.RegisterFunc("include", &simpleFunctor{f: includeFunc}, 1, -1) ctx.RegisterFunc("include", &simpleFunctor{f: includeFunc}, 1, -1)
} }

View File

@ -101,7 +101,7 @@ func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) {
return return
} }
func importMathFuncs(ctx ExprContext) { func ImportMathFuncs(ctx ExprContext) {
ctx.RegisterFunc("add", &simpleFunctor{f: addFunc}, 0, -1) ctx.RegisterFunc("add", &simpleFunctor{f: addFunc}, 0, -1)
ctx.RegisterFunc("mul", &simpleFunctor{f: mulFunc}, 0, -1) ctx.RegisterFunc("mul", &simpleFunctor{f: mulFunc}, 0, -1)
} }

View File

@ -45,7 +45,6 @@ func TestEvalStringA(t *testing.T) {
t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult) t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
t.Errorf("Error: %v", gotErr) t.Errorf("Error: %v", gotErr)
} }
fmt.Println("Hello World!")
} }
func TestEvalString(t *testing.T) { func TestEvalString(t *testing.T) {
@ -76,5 +75,4 @@ func TestEvalString(t *testing.T) {
t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult) t.Errorf("Source %q got %v, want %v", source, gotResult, wantResult)
t.Errorf("Error: %v", gotErr) t.Errorf("Error: %v", gotErr)
} }
fmt.Println("Hello World!")
} }

View File

@ -4,7 +4,10 @@
// operand-selector-case.go // operand-selector-case.go
package expr package expr
import "fmt" import (
"fmt"
"strings"
)
// -------- selector case term // -------- selector case term
@ -13,6 +16,18 @@ type selectorCase struct {
caseExpr Expr caseExpr Expr
} }
func (sc *selectorCase) String() string {
var sb strings.Builder
if sc.filterList != nil {
sc.filterList.toString(&sb)
sb.WriteByte(' ')
}
sb.WriteByte('{')
sb.WriteString(sc.caseExpr.String())
sb.WriteByte('}')
return sb.String()
}
func newSelectorCaseTerm(row, col int, filterList *term, caseExpr Expr) *term { func newSelectorCaseTerm(row, col int, filterList *term, caseExpr Expr) *term {
tk := NewValueToken(row, col, SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr}) tk := NewValueToken(row, col, SymSelectorCase, "", &selectorCase{filterList: filterList, caseExpr: caseExpr})
return &term{ return &term{

View File

@ -48,7 +48,7 @@ func newAndTerm(tk *Token) (inst *term) {
} }
func evalAnd(ctx ExprContext, self *term) (v any, err error) { func evalAnd(ctx ExprContext, self *term) (v any, err error) {
if isEnabled(ctx, control_bool_shortcut) { if isEnabled(ctx, ControlBoolShortcut) {
v, err = evalAndWithShortcut(ctx, self) v, err = evalAndWithShortcut(ctx, self)
} else { } else {
v, err = evalAndWithoutShortcut(ctx, self) v, err = evalAndWithoutShortcut(ctx, self)
@ -117,7 +117,7 @@ func newOrTerm(tk *Token) (inst *term) {
} }
func evalOr(ctx ExprContext, self *term) (v any, err error) { func evalOr(ctx ExprContext, self *term) (v any, err error) {
if isEnabled(ctx, control_bool_shortcut) { if isEnabled(ctx, ControlBoolShortcut) {
v, err = evalOrWithShortcut(ctx, self) v, err = evalOrWithShortcut(ctx, self)
} else { } else {
v, err = evalOrWithoutShortcut(ctx, self) v, err = evalOrWithoutShortcut(ctx, self)

View File

@ -268,7 +268,7 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
currentTerm, err = tree.addToken2(tk) currentTerm, err = tree.addToken2(tk)
} }
if currentTerm != nil && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector { if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
selectorTerm = nil selectorTerm = nil
} }

View File

@ -173,8 +173,8 @@ func TestParser(t *testing.T) {
ctx := NewSimpleFuncStore() ctx := NewSimpleFuncStore()
ctx.SetVar("var1", int64(123)) ctx.SetVar("var1", int64(123))
ctx.SetVar("var2", "abc") ctx.SetVar("var2", "abc")
importMathFuncs(ctx) ImportMathFuncs(ctx)
importImportFunc(ctx) ImportImportFunc(ctx)
parser := NewParser(ctx) parser := NewParser(ctx)
logTest(t, i+1, input.source, input.wantResult, input.wantErr) logTest(t, i+1, input.source, input.wantResult, input.wantErr)
@ -204,7 +204,7 @@ func TestParser(t *testing.T) {
} else { } else {
failed++ failed++
if i+1 == check_env_expr_path { if i+1 == check_env_expr_path {
t.Logf(`NOTICE: Test nr %d requires EXPR_PATH environment variable with value "."`, check_env_expr_path) t.Logf(`NOTICE: Test nr %d requires EXPR_PATH="."`, check_env_expr_path)
} }
} }
} }
@ -249,7 +249,7 @@ func TestListParser(t *testing.T) {
ctx := NewSimpleFuncStore() ctx := NewSimpleFuncStore()
ctx.SetVar("var1", int64(123)) ctx.SetVar("var1", int64(123))
ctx.SetVar("var2", "abc") ctx.SetVar("var2", "abc")
importMathFuncs(ctx) ImportMathFuncs(ctx)
parser := NewParser(ctx) parser := NewParser(ctx)
logTest(t, i+1, input.source, input.wantResult, input.wantErr) logTest(t, i+1, input.source, input.wantResult, input.wantErr)

View File

@ -5,7 +5,6 @@
package expr package expr
import ( import (
"fmt"
"testing" "testing"
) )
@ -16,7 +15,7 @@ func TestString(t *testing.T) {
tree := NewAst() tree := NewAst()
if gotErr := tree.addTokens(tk1, tk2, tk3); gotErr == nil { if gotErr := tree.addTokens(tk1, tk2, tk3); gotErr == nil {
fmt.Println("Tree:", tree) t.Log("Tree:", tree)
} else { } else {
t.Errorf("err: got <%v>, want <nil>", gotErr) t.Errorf("err: got <%v>, want <nil>", gotErr)
} }
@ -27,7 +26,7 @@ func TestGetRoom(t *testing.T) {
tree := NewAst() tree := NewAst()
if gotErr := tree.addTokens(tk1); gotErr == nil { if gotErr := tree.addTokens(tk1); gotErr == nil {
fmt.Println("Tree-root room:", tree.root.getRoom()) t.Log("Tree-root room:", tree.root.getRoom())
} else { } else {
t.Errorf("err: got <%v>, want <nil>", gotErr) t.Errorf("err: got <%v>, want <nil>", gotErr)
} }
@ -37,7 +36,7 @@ func TestGetChildrenCount(t *testing.T) {
tree := NewAst() tree := NewAst()
if gotErr := tree.addTokens(tk1); gotErr == nil { if gotErr := tree.addTokens(tk1); gotErr == nil {
fmt.Println("Tree-root children count:", tree.root.getChildrenCount()) t.Log("Tree-root children count:", tree.root.getChildrenCount())
} else { } else {
t.Errorf("err: got <%v>, want <nil>", gotErr) t.Errorf("err: got <%v>, want <nil>", gotErr)
} }

View File

@ -27,7 +27,11 @@ func (tk *Token) DevString() string {
func (tk *Token) String() string { func (tk *Token) String() string {
if tk.Value != nil { if tk.Value != nil {
return fmt.Sprintf("%#v", tk.Value) if s, ok := tk.Value.(string); ok {
return fmt.Sprintf("%q", s)
} else {
return fmt.Sprintf("%v", tk.Value)
}
} }
return fmt.Sprintf("%s", tk.source) return fmt.Sprintf("%s", tk.source)
} }

View File

@ -5,14 +5,34 @@
package expr package expr
import ( import (
"fmt"
"testing" "testing"
) )
func TestDevString(t *testing.T) { func TestDevString(t *testing.T) {
tk1 := NewValueToken(0, 0, SymInteger, "100", 100) type inputType struct {
tk2 := NewToken(0, 0, SymPlus, "+") source string
sym Symbol
value any
wantResult string
}
fmt.Println("Token '100':", tk1.DevString()) inputs := []inputType{
fmt.Println("Token '+':", tk2.DevString()) /* 1 */ {"100", SymInteger, 100, `[55]"100"{100}`},
/* 2 */ {"+", SymPlus, nil, `[6]"+"{}`},
}
for i, input := range inputs {
var tk *Token
if input.value == nil {
tk = NewToken(0, 0, input.sym, input.source)
} else {
tk = NewValueToken(0, 0, input.sym, input.source, input.value)
}
t.Logf("Test nr %2d: %q --> %q", i+1, input.source, input.wantResult)
if s := tk.DevString(); s != input.wantResult {
t.Errorf("wrong token from symbol '+': expected %q, got %q", input.wantResult, s)
}
}
} }