Expressions now support function definition
This commit is contained in:
parent
f58ec3ac32
commit
072dab4144
12
ast.go
12
ast.go
@ -10,6 +10,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Expr interface {
|
||||||
|
Eval(ctx exprContext, preset bool) (result any, err error)
|
||||||
|
}
|
||||||
|
|
||||||
//-------- ast
|
//-------- ast
|
||||||
|
|
||||||
type ast struct {
|
type ast struct {
|
||||||
@ -54,7 +58,7 @@ func (self *ast) addToken(tk *Token) (err error) {
|
|||||||
if t := newTerm(tk, nil); t != nil {
|
if t := newTerm(tk, nil); t != nil {
|
||||||
err = self.addTerm(t)
|
err = self.addTerm(t)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("No term constructor for token %q", tk.String())
|
err = tk.Errorf("No term constructor for token %q", tk.String())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -83,7 +87,7 @@ func (self *ast) insert(tree, node *term) (root *term, err error) {
|
|||||||
root = node
|
root = node
|
||||||
tree.setParent(node)
|
tree.setParent(node)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("two adjacent operators: %q and %q", tree, node)
|
err = node.Errorf("two adjacent operators: %q and %q", tree, node)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -95,11 +99,13 @@ func (self *ast) Finish() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *ast) Eval(ctx exprContext) (result any, err error) {
|
func (self *ast) Eval(ctx exprContext, preset bool) (result any, err error) {
|
||||||
self.Finish()
|
self.Finish()
|
||||||
|
|
||||||
if self.root != nil {
|
if self.root != nil {
|
||||||
|
if preset {
|
||||||
initDefaultVars(ctx)
|
initDefaultVars(ctx)
|
||||||
|
}
|
||||||
if self.forest != nil {
|
if self.forest != nil {
|
||||||
for i, root := range self.forest {
|
for i, root := range self.forest {
|
||||||
if result, err = root.compute(ctx); err == nil {
|
if result, err = root.compute(ctx); err == nil {
|
||||||
|
18
ast_test.go
18
ast_test.go
@ -17,9 +17,9 @@ func TestAstString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAddTokensGood(t *testing.T) {
|
func TestAddTokensGood(t *testing.T) {
|
||||||
tk1 := NewValueToken(SymInteger, "100", 100)
|
tk1 := NewValueToken(0, 0, SymInteger, "100", 100)
|
||||||
tk2 := NewToken(SymPlus, "+")
|
tk2 := NewToken(0, 0, SymPlus, "+")
|
||||||
tk3 := NewValueToken(SymInteger, "50", 500)
|
tk3 := NewValueToken(0, 0, SymInteger, "50", 500)
|
||||||
|
|
||||||
tree := NewAst()
|
tree := NewAst()
|
||||||
if gotErr := tree.addTokens(tk1, tk2, tk3); gotErr != nil {
|
if gotErr := tree.addTokens(tk1, tk2, tk3); gotErr != nil {
|
||||||
@ -28,12 +28,12 @@ func TestAddTokensGood(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAddTokensBad(t *testing.T) {
|
func TestAddTokensBad(t *testing.T) {
|
||||||
tk0 := NewValueToken(SymInteger, "200", 200)
|
tk0 := NewValueToken(0, 0, SymInteger, "200", 200)
|
||||||
tk1 := NewValueToken(SymInteger, "100", 100)
|
tk1 := NewValueToken(0, 0, SymInteger, "100", 100)
|
||||||
tk2 := NewToken(SymPlus, "+")
|
tk2 := NewToken(0, 0, SymPlus, "+")
|
||||||
tk3 := NewValueToken(SymInteger, "50", 500)
|
tk3 := NewValueToken(0, 0, SymInteger, "50", 500)
|
||||||
|
|
||||||
wantErr := errors.New(`two adjacent operators: "200" and "100"`)
|
wantErr := errors.New(`[0:0] two adjacent operators: "200" and "100"`)
|
||||||
|
|
||||||
tree := NewAst()
|
tree := NewAst()
|
||||||
if gotErr := tree.addTokens(tk0, tk1, tk2, tk3); gotErr != nil && gotErr.Error() != wantErr.Error() {
|
if gotErr := tree.addTokens(tk0, tk1, tk2, tk3); gotErr != nil && gotErr.Error() != wantErr.Error() {
|
||||||
@ -42,7 +42,7 @@ func TestAddTokensBad(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAddUnknownTokens(t *testing.T) {
|
func TestAddUnknownTokens(t *testing.T) {
|
||||||
tk0 := NewToken(SymPercent, "%")
|
tk0 := NewToken(0, 0, SymPercent, "%")
|
||||||
|
|
||||||
wantErr := errors.New(`No term constructor for token "%"`)
|
wantErr := errors.New(`No term constructor for token "%"`)
|
||||||
|
|
||||||
|
19
context.go
19
context.go
@ -4,18 +4,35 @@
|
|||||||
// context.go
|
// context.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
|
// ---- Function template
|
||||||
type FuncTemplate func(ctx exprContext, name string, args []any) (result any, err error)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
type exprFunc interface {
|
||||||
Name() string
|
Name() string
|
||||||
MinArgs() int
|
MinArgs() int
|
||||||
MaxArgs() int
|
MaxArgs() int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----Expression Context
|
||||||
type exprContext interface {
|
type exprContext interface {
|
||||||
|
Clone() exprContext
|
||||||
GetValue(varName string) (value any, exists bool)
|
GetValue(varName string) (value any, exists bool)
|
||||||
SetValue(varName string, value any)
|
SetValue(varName string, value any)
|
||||||
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 FuncTemplate, minArgs, maxArgs int)
|
RegisterFunc(name string, f Functor, minArgs, maxArgs int)
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,6 @@ func mulFunc(ctx exprContext, name string, args []any) (result any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func importMathFuncs(ctx exprContext) {
|
func importMathFuncs(ctx exprContext) {
|
||||||
ctx.RegisterFunc("add", addFunc, 0, -1)
|
ctx.RegisterFunc("add", &simpleFunctor{f: addFunc}, 0, -1)
|
||||||
ctx.RegisterFunc("mul", mulFunc, 0, -1)
|
ctx.RegisterFunc("mul", &simpleFunctor{f: mulFunc}, 0, -1)
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ func EvalString(ctx exprContext, source string) (result any, err error) {
|
|||||||
parser := NewParser(ctx)
|
parser := NewParser(ctx)
|
||||||
|
|
||||||
if tree, err = parser.parse(scanner); err == nil {
|
if tree, err = parser.parse(scanner); err == nil {
|
||||||
result, err = tree.Eval(ctx)
|
result, err = tree.Eval(ctx, true)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -33,7 +33,8 @@ func EvalStringV(source string, args []EvalArg) (result any, err error) {
|
|||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
if isFunc(arg.value) {
|
if isFunc(arg.value) {
|
||||||
if f, ok := arg.value.(FuncTemplate); ok {
|
if f, ok := arg.value.(FuncTemplate); ok {
|
||||||
ctx.RegisterFunc(arg.name, f, 0, -1)
|
functor := &simpleFunctor{f: f}
|
||||||
|
ctx.RegisterFunc(arg.name, functor, 0, -1)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("invalid function specification: %q", arg.name)
|
err = fmt.Errorf("invalid function specification: %q", arg.name)
|
||||||
}
|
}
|
||||||
|
36
operand-expr.go
Normal file
36
operand-expr.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
||||||
|
// All rights reserved.
|
||||||
|
|
||||||
|
// operand-expr.go
|
||||||
|
package expr
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
// -------- expr term
|
||||||
|
func newExprTerm(tk *Token) *term {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
class: classVar,
|
||||||
|
kind: kindUnknown,
|
||||||
|
parent: nil,
|
||||||
|
children: nil,
|
||||||
|
position: posLeaf,
|
||||||
|
priority: priValue,
|
||||||
|
evalFunc: evalExpr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- eval expr
|
||||||
|
func evalExpr(ctx exprContext, self *term) (v any, err error) {
|
||||||
|
if expr, ok := self.value().(Expr); ok {
|
||||||
|
v, err = expr.Eval(ctx, false)
|
||||||
|
} else {
|
||||||
|
err = errors.New("invalid body of function definition")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// init
|
||||||
|
// func init() {
|
||||||
|
// registerTermConstructor(SymExpression, newExprTerm)
|
||||||
|
// }
|
@ -4,8 +4,13 @@
|
|||||||
// operand-func.go
|
// operand-func.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
// -------- function term
|
import (
|
||||||
func newFuncTerm(tk *Token, args []*term) *term {
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -------- function call term
|
||||||
|
func newFuncCallTerm(tk *Token, args []*term) *term {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *tk,
|
tk: *tk,
|
||||||
class: classVar,
|
class: classVar,
|
||||||
@ -14,12 +19,12 @@ func newFuncTerm(tk *Token, args []*term) *term {
|
|||||||
children: args,
|
children: args,
|
||||||
position: posLeaf,
|
position: posLeaf,
|
||||||
priority: priValue,
|
priority: priValue,
|
||||||
evalFunc: evalFunc,
|
evalFunc: evalFuncCall,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------- eval func
|
// -------- eval func call
|
||||||
func evalFunc(ctx exprContext, self *term) (v any, err error) {
|
func evalFuncCall(ctx exprContext, self *term) (v any, err error) {
|
||||||
name, _ := self.tk.Value.(string)
|
name, _ := self.tk.Value.(string)
|
||||||
params := make([]any, len(self.children))
|
params := make([]any, len(self.children))
|
||||||
for i, tree := range self.children {
|
for i, tree := range self.children {
|
||||||
@ -34,3 +39,59 @@ func evalFunc(ctx exprContext, self *term) (v any, err error) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------- function definition term
|
||||||
|
func newFuncDefTerm(tk *Token, args []*term) *term {
|
||||||
|
return &term{
|
||||||
|
tk: *tk,
|
||||||
|
class: classVar,
|
||||||
|
kind: kindUnknown,
|
||||||
|
parent: nil,
|
||||||
|
children: args, // arg[0]=formal-param-list, arg[1]=*ast
|
||||||
|
position: posLeaf,
|
||||||
|
priority: priValue,
|
||||||
|
evalFunc: evalFuncDef,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- eval func def
|
||||||
|
// TODO
|
||||||
|
type funcDefFunctor struct {
|
||||||
|
params []string
|
||||||
|
expr Expr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (functor *funcDefFunctor) Invoke(parentCtx exprContext, name string, args []any) (result any, err error) {
|
||||||
|
ctx := parentCtx.Clone()
|
||||||
|
for i, p := range functor.params {
|
||||||
|
if i < len(args) {
|
||||||
|
ctx.SetValue(p, args[i])
|
||||||
|
} else {
|
||||||
|
ctx.SetValue(p, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result, err = functor.expr.Eval(ctx, false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalFuncDef(ctx exprContext, self *term) (v any, err error) {
|
||||||
|
bodySpec := self.value()
|
||||||
|
if expr, ok := bodySpec.(*ast); ok {
|
||||||
|
paramList := make([]string, 0, len(self.children))
|
||||||
|
for i, param := range self.children {
|
||||||
|
if paramName, ok := param.value().(string); ok {
|
||||||
|
paramList = append(paramList, paramName)
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("invalid function definition: formal param nr %d must be an identifiers", i+1)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v = &funcDefFunctor{
|
||||||
|
params: paramList,
|
||||||
|
expr: expr,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = errors.New("invalid function definition: the body specification must be an expression")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -7,7 +7,7 @@ package expr
|
|||||||
// -------- list term
|
// -------- list term
|
||||||
func newListTerm(args []*term) *term {
|
func newListTerm(args []*term) *term {
|
||||||
return &term{
|
return &term{
|
||||||
tk: *NewToken(SymList, "[]"),
|
tk: *NewToken(0, 0, SymList, "[]"),
|
||||||
class: classVar,
|
class: classVar,
|
||||||
kind: kindUnknown,
|
kind: kindUnknown,
|
||||||
parent: nil,
|
parent: nil,
|
||||||
|
@ -4,8 +4,6 @@
|
|||||||
// operator-assign.go
|
// operator-assign.go
|
||||||
package expr
|
package expr
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
//-------- assign term
|
//-------- assign term
|
||||||
|
|
||||||
func newAssignTerm(tk *Token) (inst *term) {
|
func newAssignTerm(tk *Token) (inst *term) {
|
||||||
@ -27,13 +25,17 @@ func evalAssign(ctx exprContext, self *term) (v any, err error) {
|
|||||||
|
|
||||||
leftTerm := self.children[0]
|
leftTerm := self.children[0]
|
||||||
if leftTerm.tk.Sym != SymIdentifier {
|
if leftTerm.tk.Sym != SymIdentifier {
|
||||||
err = fmt.Errorf("left operand of %q must be a variable", self.tk.source)
|
err = leftTerm.tk.Errorf("left operand of %q must be a variable", self.tk.source)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err = self.children[1].compute(ctx); err == nil {
|
if v, err = self.children[1].compute(ctx); err == nil {
|
||||||
|
if functor, ok := v.(Functor); ok {
|
||||||
|
ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
|
||||||
|
} else {
|
||||||
ctx.SetValue(leftTerm.tk.source, v)
|
ctx.SetValue(leftTerm.tk.source, v)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
120
parser.go
120
parser.go
@ -5,6 +5,7 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,22 +22,23 @@ func NewParser(ctx exprContext) (p *parser) {
|
|||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parseFunction(scanner *scanner, tk *Token) (tree *term, err error) {
|
func (self *parser) parseFuncCall(scanner *scanner, tk *Token) (tree *term, err error) {
|
||||||
name, _ := tk.Value.(string)
|
// name, _ := tk.Value.(string)
|
||||||
funcObj := self.ctx.GetFuncInfo(name)
|
// funcObj := self.ctx.GetFuncInfo(name)
|
||||||
if funcObj == nil {
|
// if funcObj == nil {
|
||||||
err = fmt.Errorf("unknown function %s()", name)
|
// err = fmt.Errorf("unknown function %s()", name)
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
maxArgs := funcObj.MaxArgs()
|
// maxArgs := funcObj.MaxArgs()
|
||||||
if maxArgs < 0 {
|
// if maxArgs < 0 {
|
||||||
maxArgs = funcObj.MinArgs() + 10
|
// maxArgs = funcObj.MinArgs() + 10
|
||||||
}
|
// }
|
||||||
args := make([]*term, 0, maxArgs)
|
// args := make([]*term, 0, maxArgs)
|
||||||
|
args := make([]*term, 0, 10)
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
for lastSym != SymClosedRound && lastSym != SymEos {
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = self.parse(scanner, SymComma, SymClosedRound); err == nil {
|
if subTree, err = self.parseItem(scanner, SymComma, SymClosedRound); err == nil {
|
||||||
if subTree.root != nil {
|
if subTree.root != nil {
|
||||||
args = append(args, subTree.root)
|
args = append(args, subTree.root)
|
||||||
}
|
}
|
||||||
@ -47,7 +49,47 @@ func (self *parser) parseFunction(scanner *scanner, tk *Token) (tree *term, err
|
|||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO Check arguments
|
// TODO Check arguments
|
||||||
tree = newFuncTerm(tk, args)
|
if lastSym != SymClosedRound {
|
||||||
|
err = errors.New("unterminate arguments list")
|
||||||
|
} else {
|
||||||
|
tree = newFuncCallTerm(tk, args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
||||||
|
// Example: "add = func(x,y) {x+y}
|
||||||
|
var body *ast
|
||||||
|
args := make([]*term, 0)
|
||||||
|
tk := scanner.Next()
|
||||||
|
for tk.Sym != SymClosedRound && tk.Sym != SymEos {
|
||||||
|
if tk.Sym == SymIdentifier {
|
||||||
|
t := newTerm(tk, nil)
|
||||||
|
args = append(args, t)
|
||||||
|
} else {
|
||||||
|
err = tk.Errorf("invalid param %q, variable identifier expected", tk.source)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tk = scanner.Next()
|
||||||
|
}
|
||||||
|
if err == nil && tk.Sym != SymClosedRound {
|
||||||
|
err = tk.Errorf("unterminate function params list")
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
tk = scanner.Next()
|
||||||
|
if tk.Sym == SymOpenBrace {
|
||||||
|
body, err = self.parseGeneral(scanner, true, SymClosedBrace)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
// TODO Check arguments
|
||||||
|
if scanner.Previous().Sym != SymClosedBrace {
|
||||||
|
err = scanner.Previous().Errorf("unterminate function body")
|
||||||
|
} else {
|
||||||
|
tk = scanner.makeValueToken(SymExpression, "", body)
|
||||||
|
tree = newFuncDefTerm(tk, args)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -57,7 +99,7 @@ func (self *parser) parseList(scanner *scanner) (tree *term, err error) {
|
|||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
for lastSym != SymClosedSquare && lastSym != SymEos {
|
for lastSym != SymClosedSquare && lastSym != SymEos {
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
if subTree, err = self.parse(scanner, SymComma, SymClosedSquare); err == nil {
|
if subTree, err = self.parseItem(scanner, SymComma, SymClosedSquare); err == nil {
|
||||||
if subTree.root != nil {
|
if subTree.root != nil {
|
||||||
args = append(args, subTree.root)
|
args = append(args, subTree.root)
|
||||||
}
|
}
|
||||||
@ -68,12 +110,24 @@ func (self *parser) parseList(scanner *scanner) (tree *term, err error) {
|
|||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO Check arguments
|
// TODO Check arguments
|
||||||
|
if lastSym != SymClosedSquare {
|
||||||
|
err = scanner.Previous().Errorf("unterminate items list")
|
||||||
|
} else {
|
||||||
tree = newListTerm(args)
|
tree = newListTerm(args)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
|
return self.parseGeneral(scanner, true, termSymbols...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *parser) parseItem(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
|
return self.parseGeneral(scanner, false, termSymbols...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *parser) parseGeneral(scanner *scanner, allowForset bool, termSymbols ...Symbol) (tree *ast, err error) {
|
||||||
tree = NewAst()
|
tree = NewAst()
|
||||||
firstToken := true
|
firstToken := true
|
||||||
lastSym := SymUnknown
|
lastSym := SymUnknown
|
||||||
@ -83,19 +137,25 @@ func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tk.Sym == SymSemiColon {
|
if tk.Sym == SymSemiColon {
|
||||||
|
if allowForset {
|
||||||
tree.ToForest()
|
tree.ToForest()
|
||||||
continue
|
continue
|
||||||
|
} else {
|
||||||
|
err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.source)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//fmt.Println("Token:", tk)
|
//fmt.Println("Token:", tk)
|
||||||
if firstToken && (tk.Sym == SymMinus || tk.Sym == SymPlus) {
|
if firstToken {
|
||||||
if tk.Sym == SymMinus {
|
if tk.Sym == SymMinus {
|
||||||
tk.Sym = SymChangeSign
|
tk.Sym = SymChangeSign
|
||||||
} else {
|
} else if tk.Sym == SymPlus {
|
||||||
tk.Sym = SymUnchangeSign
|
tk.Sym = SymUnchangeSign
|
||||||
}
|
}
|
||||||
}
|
|
||||||
firstToken = false
|
firstToken = false
|
||||||
|
}
|
||||||
|
|
||||||
switch tk.Sym {
|
switch tk.Sym {
|
||||||
case SymOpenRound:
|
case SymOpenRound:
|
||||||
var subTree *ast
|
var subTree *ast
|
||||||
@ -103,10 +163,10 @@ func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, e
|
|||||||
subTree.root.priority = priValue
|
subTree.root.priority = priValue
|
||||||
tree.addTerm(subTree.root)
|
tree.addTerm(subTree.root)
|
||||||
}
|
}
|
||||||
case SymFunction:
|
case SymFuncCall:
|
||||||
var funcTerm *term
|
var funcCallTerm *term
|
||||||
if funcTerm, err = self.parseFunction(scanner, tk); err == nil {
|
if funcCallTerm, err = self.parseFuncCall(scanner, tk); err == nil {
|
||||||
err = tree.addTerm(funcTerm)
|
err = tree.addTerm(funcCallTerm)
|
||||||
}
|
}
|
||||||
case SymOpenSquare:
|
case SymOpenSquare:
|
||||||
var listTerm *term
|
var listTerm *term
|
||||||
@ -114,10 +174,13 @@ func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, e
|
|||||||
err = tree.addTerm(listTerm)
|
err = tree.addTerm(listTerm)
|
||||||
}
|
}
|
||||||
case SymEqual:
|
case SymEqual:
|
||||||
if lastSym == SymIdentifier {
|
if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
||||||
err = tree.addToken(tk)
|
err = tree.addToken(tk)
|
||||||
} else {
|
}
|
||||||
err = fmt.Errorf(`assign operator (%q) must be preceded by a variable`, tk.source)
|
case SymFuncDef:
|
||||||
|
var funcDefTerm *term
|
||||||
|
if funcDefTerm, err = self.parseFuncDef(scanner); err == nil {
|
||||||
|
err = tree.addTerm(funcDefTerm)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err = tree.addToken(tk)
|
err = tree.addToken(tk)
|
||||||
@ -126,3 +189,10 @@ func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, e
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func checkPrevSymbol(lastSym, wantedSym Symbol, tk *Token) (err error) {
|
||||||
|
if lastSym != wantedSym {
|
||||||
|
err = fmt.Errorf(`assign operator (%q) must be preceded by a variable`, tk.source)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -18,12 +18,6 @@ func TestParser(t *testing.T) {
|
|||||||
wantErr error
|
wantErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
// inputs1 := []inputType{
|
|
||||||
// {`a=5; a`, int64(5), nil},
|
|
||||||
// {`a=5; b=2; add(a, b*3)`, int64(11), nil},
|
|
||||||
// {`"a" < "b" AND ~ 2 == 1`, true, nil},
|
|
||||||
// }
|
|
||||||
|
|
||||||
inputs := []inputType{
|
inputs := []inputType{
|
||||||
/* 1 */ {`1+/*5*/2`, int64(3), nil},
|
/* 1 */ {`1+/*5*/2`, int64(3), nil},
|
||||||
/* 2 */ {`3 == 4`, false, nil},
|
/* 2 */ {`3 == 4`, false, nil},
|
||||||
@ -66,10 +60,10 @@ func TestParser(t *testing.T) {
|
|||||||
/* 39 */ {`(((1)))`, int64(1), nil},
|
/* 39 */ {`(((1)))`, int64(1), nil},
|
||||||
/* 40 */ {`"uno_" + var2`, `uno_abc`, nil},
|
/* 40 */ {`"uno_" + var2`, `uno_abc`, nil},
|
||||||
/* 41 */ {`0 || 0.0 && "hello"`, false, nil},
|
/* 41 */ {`0 || 0.0 && "hello"`, false, nil},
|
||||||
/* 42 */ {`"s" + true`, nil, errors.New(`left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`)},
|
/* 42 */ {`"s" + true`, nil, errors.New(`[1:6] left operand 's' [string] and right operand 'true' [bool] are not compatible with operator "+"`)},
|
||||||
/* 43 */ {`+false`, nil, errors.New(`prefix/postfix operator "+" do not support operand 'false' [bool]`)},
|
/* 43 */ {`+false`, nil, errors.New(`[1:2] prefix/postfix operator "+" do not support operand 'false' [bool]`)},
|
||||||
/* 44 */ {`false // very simple expression`, false, nil},
|
/* 44 */ {`false // very simple expression`, false, nil},
|
||||||
/* 45 */ {`1 + // Missing right operator`, nil, errors.New(`infix operator "+" requires two operands, got 1`)},
|
/* 45 */ {`1 + // Missing right operator`, nil, errors.New(`[1:4] infix operator "+" requires two operands, got 1`)},
|
||||||
/* 46 */ {"", nil, errors.New(`empty expression`)},
|
/* 46 */ {"", nil, errors.New(`empty expression`)},
|
||||||
/* 47 */ {"4!", int64(24), nil},
|
/* 47 */ {"4!", int64(24), nil},
|
||||||
/* 48 */ {"(-4)!", nil, errors.New(`factorial of a negative integer (-4) is not allowed`)},
|
/* 48 */ {"(-4)!", nil, errors.New(`factorial of a negative integer (-4) is not allowed`)},
|
||||||
@ -84,18 +78,18 @@ func TestParser(t *testing.T) {
|
|||||||
/* 57 */ {`"1.5" > "7"`, false, nil},
|
/* 57 */ {`"1.5" > "7"`, false, nil},
|
||||||
/* 58 */ {`"1.5" == "7"`, false, nil},
|
/* 58 */ {`"1.5" == "7"`, false, nil},
|
||||||
/* 59 */ {`"1.5" != "7"`, true, nil},
|
/* 59 */ {`"1.5" != "7"`, true, nil},
|
||||||
/* 60 */ {"1.5 < ", nil, errors.New(`infix operator "<" requires two operands, got 1`)},
|
/* 60 */ {"1.5 < ", nil, errors.New(`[1:6] infix operator "<" requires two operands, got 1`)},
|
||||||
/* 61 */ {"1.5 > ", nil, errors.New(`infix operator ">" requires two operands, got 1`)},
|
/* 61 */ {"1.5 > ", nil, errors.New(`[1:6] infix operator ">" requires two operands, got 1`)},
|
||||||
/* 62 */ {"1.5 <= ", nil, errors.New(`infix operator "<=" requires two operands, got 1`)},
|
/* 62 */ {"1.5 <= ", nil, errors.New(`[1:6] infix operator "<=" requires two operands, got 1`)},
|
||||||
/* 63 */ {"1.5 >= ", nil, errors.New(`infix operator ">=" requires two operands, got 1`)},
|
/* 63 */ {"1.5 >= ", nil, errors.New(`[1:6] infix operator ">=" requires two operands, got 1`)},
|
||||||
/* 64 */ {"1.5 != ", nil, errors.New(`infix operator "!=" requires two operands, got 1`)},
|
/* 64 */ {"1.5 != ", nil, errors.New(`[1:6] infix operator "!=" requires two operands, got 1`)},
|
||||||
/* 65 */ {"1.5 == ", nil, errors.New(`infix operator "==" requires two operands, got 1`)},
|
/* 65 */ {"1.5 == ", nil, errors.New(`[1:6] infix operator "==" requires two operands, got 1`)},
|
||||||
/* 66 */ {`"1.5" < `, nil, errors.New(`infix operator "<" requires two operands, got 1`)},
|
/* 66 */ {`"1.5" < `, nil, errors.New(`[1:8] infix operator "<" requires two operands, got 1`)},
|
||||||
/* 67 */ {`"1.5" > `, nil, errors.New(`infix operator ">" requires two operands, got 1`)},
|
/* 67 */ {`"1.5" > `, nil, errors.New(`[1:8] infix operator ">" requires two operands, got 1`)},
|
||||||
/* 68 */ {`"1.5" == `, nil, errors.New(`infix operator "==" requires two operands, got 1`)},
|
/* 68 */ {`"1.5" == `, nil, errors.New(`[1:8] infix operator "==" requires two operands, got 1`)},
|
||||||
/* 69 */ {`"1.5" != `, nil, errors.New(`infix operator "!=" requires two operands, got 1`)},
|
/* 69 */ {`"1.5" != `, nil, errors.New(`[1:8] infix operator "!=" requires two operands, got 1`)},
|
||||||
/* 70 */ {"+1.5", float64(1.5), nil},
|
/* 70 */ {"+1.5", float64(1.5), nil},
|
||||||
/* 71 */ {"+", nil, errors.New(`prefix operator "+" requires one operand`)},
|
/* 71 */ {"+", nil, errors.New(`[1:2] prefix operator "+" requires one operand`)},
|
||||||
/* 72 */ {"4 / 0", nil, errors.New(`division by zero`)},
|
/* 72 */ {"4 / 0", nil, errors.New(`division by zero`)},
|
||||||
/* 73 */ {"4.0 / 0", nil, errors.New(`division by zero`)},
|
/* 73 */ {"4.0 / 0", nil, errors.New(`division by zero`)},
|
||||||
/* 74 */ {"4.0 / \n2", float64(2.0), nil},
|
/* 74 */ {"4.0 / \n2", float64(2.0), nil},
|
||||||
@ -109,7 +103,7 @@ func TestParser(t *testing.T) {
|
|||||||
/* 82 */ {`5 % 2`, int64(1), nil},
|
/* 82 */ {`5 % 2`, int64(1), nil},
|
||||||
/* 83 */ {`5 % (-2)`, int64(1), nil},
|
/* 83 */ {`5 % (-2)`, int64(1), nil},
|
||||||
/* 84 */ {`-5 % 2`, int64(-1), nil},
|
/* 84 */ {`-5 % 2`, int64(-1), nil},
|
||||||
/* 85 */ {`5 % 2.0`, nil, errors.New(`left operand '5' [int64] and right operand '2' [float64] are not compatible with operator "%"`)},
|
/* 85 */ {`5 % 2.0`, nil, errors.New(`[1:4] left operand '5' [int64] and right operand '2' [float64] are not compatible with operator "%"`)},
|
||||||
/* 86 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
|
/* 86 */ {`"a" < "b" AND NOT (2 < 1)`, true, nil},
|
||||||
/* 87 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
/* 87 */ {`"a" < "b" AND NOT (2 == 1)`, true, nil},
|
||||||
/* 88 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
|
/* 88 */ {`"a" < "b" AND ~ 2 == 1`, true, nil},
|
||||||
@ -129,12 +123,24 @@ func TestParser(t *testing.T) {
|
|||||||
/* 102 */ {`a=5; a`, int64(5), nil},
|
/* 102 */ {`a=5; a`, int64(5), nil},
|
||||||
/* 103 */ {`a=5; b=2; add(a, b*3)`, int64(11), nil},
|
/* 103 */ {`a=5; b=2; add(a, b*3)`, int64(11), nil},
|
||||||
/* 104 */ {`2=5`, nil, errors.New(`assign operator ("=") must be preceded by a variable`)},
|
/* 104 */ {`2=5`, nil, errors.New(`assign operator ("=") must be preceded by a variable`)},
|
||||||
/* 105 */ {`2+a=5`, nil, errors.New(`left operand of "=" must be a variable`)},
|
/* 105 */ {`2+a=5`, nil, errors.New(`[1:3] left operand of "=" must be a variable`)},
|
||||||
/* 106 */ {`2+(a=5)`, int64(7), nil},
|
/* 106 */ {`2+(a=5)`, int64(7), nil},
|
||||||
|
/* 107 */ {`two=func(){2}; two()`, int64(2), nil},
|
||||||
|
/* 108 */ {`double=func(x) {2*x}; (double(3))`, int64(6), nil},
|
||||||
|
/* 109 */ {`double=func(x){2*x}; double(3)`, int64(6), nil},
|
||||||
|
/* 110 */ {`double=func(x){2*x}; a=5; double(3+a) + 1`, int64(17), nil},
|
||||||
|
/* 111 */ {`double=func(x){2*x}; a=5; two=func() {2}; (double(3+a) + 1) * two()`, int64(34), nil},
|
||||||
}
|
}
|
||||||
succeeded := 0
|
succeeded := 0
|
||||||
failed := 0
|
failed := 0
|
||||||
|
|
||||||
|
// inputs1 := []inputType{
|
||||||
|
// {`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},
|
||||||
|
// }
|
||||||
|
|
||||||
for i, input := range inputs {
|
for i, input := range inputs {
|
||||||
var expr *ast
|
var expr *ast
|
||||||
var gotResult any
|
var gotResult any
|
||||||
@ -153,7 +159,7 @@ func TestParser(t *testing.T) {
|
|||||||
|
|
||||||
good := true
|
good := true
|
||||||
if expr, gotErr = parser.parse(scanner); gotErr == nil {
|
if expr, gotErr = parser.parse(scanner); gotErr == nil {
|
||||||
gotResult, gotErr = expr.Eval(ctx)
|
gotResult, gotErr = expr.Eval(ctx, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if gotResult != input.wantResult {
|
if gotResult != input.wantResult {
|
||||||
@ -177,7 +183,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
|
||||||
@ -227,7 +233,7 @@ func NoTestListParser(t *testing.T) {
|
|||||||
|
|
||||||
good := true
|
good := true
|
||||||
if expr, gotErr = parser.parse(scanner); gotErr == nil {
|
if expr, gotErr = parser.parse(scanner); gotErr == nil {
|
||||||
gotResult, gotErr = expr.Eval(ctx)
|
gotResult, gotErr = expr.Eval(ctx, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gotResult == nil && input.wantResult != nil) || (gotResult != nil && input.wantResult == nil) {
|
if (gotResult == nil && input.wantResult != nil) || (gotResult != nil && input.wantResult == nil) {
|
||||||
|
18
scanner.go
18
scanner.go
@ -229,8 +229,12 @@ func (self *scanner) fetchNextToken() (tk *Token) {
|
|||||||
}
|
}
|
||||||
escape = false
|
escape = false
|
||||||
default:
|
default:
|
||||||
if ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') {
|
if /*ch == '_' ||*/ (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') {
|
||||||
tk = self.fetchIdentifier(ch)
|
if tk = self.fetchIdentifier(ch); tk.Sym == SymKwFunc {
|
||||||
|
if next, _ := self.peek(); next == '(' {
|
||||||
|
tk = self.moveOn(SymFuncDef, ch, next)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if ch >= '0' && ch <= '9' {
|
} else if ch >= '0' && ch <= '9' {
|
||||||
tk = self.parseNumber(ch)
|
tk = self.parseNumber(ch)
|
||||||
}
|
}
|
||||||
@ -337,7 +341,7 @@ func (self *scanner) fetchIdentifier(firstCh byte) (tk *Token) {
|
|||||||
tk = self.makeValueToken(SymBool, txt, false)
|
tk = self.makeValueToken(SymBool, txt, false)
|
||||||
} else if ch, _ := self.peek(); ch == '(' {
|
} else if ch, _ := self.peek(); ch == '(' {
|
||||||
self.readChar()
|
self.readChar()
|
||||||
tk = self.makeValueToken(SymFunction, txt+"(", txt)
|
tk = self.makeValueToken(SymFuncCall, txt+"(", txt)
|
||||||
} else {
|
} else {
|
||||||
tk = self.makeValueToken(SymIdentifier, txt, txt)
|
tk = self.makeValueToken(SymIdentifier, txt, txt)
|
||||||
}
|
}
|
||||||
@ -533,7 +537,7 @@ func (self *scanner) translate(sym Symbol) Symbol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
|
func (self *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
|
||||||
tk = NewToken(self.translate(sym), string(chars))
|
tk = NewToken(self.row, self.column, self.translate(sym), string(chars))
|
||||||
for i := 1; i < len(chars); i++ {
|
for i := 1; i < len(chars); i++ {
|
||||||
self.readChar()
|
self.readChar()
|
||||||
}
|
}
|
||||||
@ -541,17 +545,17 @@ func (self *scanner) moveOn(sym Symbol, chars ...byte) (tk *Token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) makeToken(sym Symbol, chars ...byte) (tk *Token) {
|
func (self *scanner) makeToken(sym Symbol, chars ...byte) (tk *Token) {
|
||||||
tk = NewToken(self.translate(sym), string(chars))
|
tk = NewToken(self.row, self.column, self.translate(sym), string(chars))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) {
|
func (self *scanner) makeKeywordToken(sym Symbol, upperCaseKeyword string) (tk *Token) {
|
||||||
tk = NewToken(self.translate(sym), upperCaseKeyword)
|
tk = NewToken(self.row, self.column, self.translate(sym), upperCaseKeyword)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *scanner) makeValueToken(sym Symbol, source string, value any) (tk *Token) {
|
func (self *scanner) makeValueToken(sym Symbol, source string, value any) (tk *Token) {
|
||||||
tk = NewValueToken(self.translate(sym), source, value)
|
tk = NewValueToken(self.row, self.column, self.translate(sym), source, value)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import "fmt"
|
|||||||
|
|
||||||
type SimpleFuncStore struct {
|
type SimpleFuncStore struct {
|
||||||
varStore map[string]any
|
varStore map[string]any
|
||||||
funcStore map[string]FuncTemplate
|
funcStore map[string]Functor
|
||||||
}
|
}
|
||||||
|
|
||||||
type funcInfo struct {
|
type funcInfo struct {
|
||||||
@ -29,7 +29,14 @@ func (info *funcInfo) MaxArgs() int {
|
|||||||
func NewSimpleFuncStore() *SimpleFuncStore {
|
func NewSimpleFuncStore() *SimpleFuncStore {
|
||||||
return &SimpleFuncStore{
|
return &SimpleFuncStore{
|
||||||
varStore: make(map[string]any),
|
varStore: make(map[string]any),
|
||||||
funcStore: make(map[string]FuncTemplate),
|
funcStore: make(map[string]Functor),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleFuncStore) Clone() exprContext {
|
||||||
|
return &SimpleFuncStore{
|
||||||
|
varStore: CloneMap(ctx.varStore),
|
||||||
|
funcStore: CloneMap(ctx.funcStore),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,13 +57,13 @@ func (ctx *SimpleFuncStore) GetFuncInfo(name string) (f exprFunc) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *SimpleFuncStore) RegisterFunc(name string, f FuncTemplate, minArgs, maxArgs int) {
|
func (ctx *SimpleFuncStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) {
|
||||||
ctx.funcStore[name] = f
|
ctx.funcStore[name] = functor
|
||||||
}
|
}
|
||||||
|
|
||||||
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 f, exists := ctx.funcStore[name]; exists {
|
if functor, exists := ctx.funcStore[name]; exists {
|
||||||
result, err = f(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)
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,13 @@ func NewSimpleVarStore() *SimpleVarStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *SimpleVarStore) Clone() (clone exprContext) {
|
||||||
|
clone = &SimpleVarStore{
|
||||||
|
store: CloneMap(ctx.store),
|
||||||
|
}
|
||||||
|
return clone
|
||||||
|
}
|
||||||
|
|
||||||
func (ctx *SimpleVarStore) GetValue(varName string) (v any, exists bool) {
|
func (ctx *SimpleVarStore) GetValue(varName string) (v any, exists bool) {
|
||||||
v, exists = ctx.store[varName]
|
v, exists = ctx.store[varName]
|
||||||
return
|
return
|
||||||
@ -28,5 +35,5 @@ func (ctx *SimpleVarStore) Call(name string, args []any) (result any, err error)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *SimpleVarStore) RegisterFunc(name string, f FuncTemplate, minArgs, maxArgs int) {
|
func (ctx *SimpleVarStore) RegisterFunc(name string, functor Functor, minArgs, maxArgs int) {
|
||||||
}
|
}
|
||||||
|
@ -69,9 +69,12 @@ const (
|
|||||||
SymAnd
|
SymAnd
|
||||||
SymNot
|
SymNot
|
||||||
SymComment
|
SymComment
|
||||||
SymFunction
|
SymFuncCall
|
||||||
|
SymFuncDef
|
||||||
SymList
|
SymList
|
||||||
SymKwBut
|
SymKwBut
|
||||||
|
SymKwFunc
|
||||||
|
SymExpression
|
||||||
// SymOpenComment // 0: '/*'
|
// SymOpenComment // 0: '/*'
|
||||||
// SymClosedComment // 0: '*/'
|
// SymClosedComment // 0: '*/'
|
||||||
// SymOneLineComment // 0: '//'
|
// SymOneLineComment // 0: '//'
|
||||||
@ -84,6 +87,7 @@ func init() {
|
|||||||
keywords = map[string]Symbol{
|
keywords = map[string]Symbol{
|
||||||
"AND": SymKwAnd,
|
"AND": SymKwAnd,
|
||||||
"BUT": SymKwBut,
|
"BUT": SymKwBut,
|
||||||
|
"FUNC": SymKwFunc,
|
||||||
"NOT": SymKwNot,
|
"NOT": SymKwNot,
|
||||||
"OR": SymKwOr,
|
"OR": SymKwOr,
|
||||||
}
|
}
|
||||||
|
41
term.go
41
term.go
@ -5,7 +5,6 @@
|
|||||||
package expr
|
package expr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -146,14 +145,6 @@ func (self *term) isLeaf() bool {
|
|||||||
return self.position == posLeaf
|
return self.position == posLeaf
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (self *term) getKind() termKind {
|
|
||||||
// return self.kind
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (self *term) getClass() termClass {
|
|
||||||
// return self.class
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (self *term) getPriority() termPriority {
|
func (self *term) getPriority() termPriority {
|
||||||
return self.priority
|
return self.priority
|
||||||
}
|
}
|
||||||
@ -165,21 +156,17 @@ func (self *term) setParent(parent *term) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (self *term) isOperand() bool {
|
|
||||||
// return self.getClass() != classOperator
|
|
||||||
// }
|
|
||||||
|
|
||||||
// func (self *term) isOperator() bool {
|
|
||||||
// return self.getClass() == classOperator
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (self *term) source() string {
|
func (self *term) source() string {
|
||||||
return self.tk.source
|
return self.tk.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *term) value() any {
|
||||||
|
return self.tk.Value
|
||||||
|
}
|
||||||
|
|
||||||
func (self *term) compute(ctx exprContext) (v any, err error) {
|
func (self *term) compute(ctx exprContext) (v any, err error) {
|
||||||
if self.evalFunc == nil {
|
if self.evalFunc == nil {
|
||||||
err = fmt.Errorf("undefined eval-func for %v term type", self.kind)
|
err = self.tk.Errorf("undefined eval-func for %v term type", self.kind)
|
||||||
} else {
|
} else {
|
||||||
v, err = self.evalFunc(ctx, self)
|
v, err = self.evalFunc(ctx, self)
|
||||||
}
|
}
|
||||||
@ -187,7 +174,7 @@ func (self *term) compute(ctx exprContext) (v any, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
||||||
return fmt.Errorf(
|
return self.tk.Errorf(
|
||||||
"left operand '%v' [%T] and right operand '%v' [%T] are not compatible with operator %q",
|
"left operand '%v' [%T] and right operand '%v' [%T] are not compatible with operator %q",
|
||||||
leftValue, leftValue,
|
leftValue, leftValue,
|
||||||
rightValue, rightValue,
|
rightValue, rightValue,
|
||||||
@ -195,23 +182,29 @@ func (self *term) errIncompatibleTypes(leftValue, rightValue any) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) errIncompatibleType(value any) error {
|
func (self *term) errIncompatibleType(value any) error {
|
||||||
return fmt.Errorf(
|
return self.tk.Errorf(
|
||||||
"prefix/postfix operator %q do not support operand '%v' [%T]", self.source(), value, value)
|
"prefix/postfix operator %q do not support operand '%v' [%T]",
|
||||||
|
self.source(), value, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *term) Errorf(template string, args ...any) (err error) {
|
||||||
|
err = self.tk.Errorf(template, args...)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *term) checkOperands() (err error) {
|
func (self *term) checkOperands() (err error) {
|
||||||
switch self.position {
|
switch self.position {
|
||||||
case posInfix:
|
case posInfix:
|
||||||
if self.children == nil || len(self.children) != 2 || self.children[0] == nil || self.children[1] == nil {
|
if self.children == nil || len(self.children) != 2 || self.children[0] == nil || self.children[1] == nil {
|
||||||
err = fmt.Errorf("infix operator %q requires two operands, got %d", self.source(), self.getChildrenCount())
|
err = self.tk.Errorf("infix operator %q requires two operands, got %d", self.source(), self.getChildrenCount())
|
||||||
}
|
}
|
||||||
case posPrefix:
|
case posPrefix:
|
||||||
if self.children == nil || len(self.children) != 1 || self.children[0] == nil {
|
if self.children == nil || len(self.children) != 1 || self.children[0] == nil {
|
||||||
err = fmt.Errorf("prefix operator %q requires one operand", self.tk.String())
|
err = self.tk.Errorf("prefix operator %q requires one operand", self.tk.String())
|
||||||
}
|
}
|
||||||
case posPostfix:
|
case posPostfix:
|
||||||
if self.children == nil || len(self.children) != 1 || self.children[0] == nil {
|
if self.children == nil || len(self.children) != 1 || self.children[0] == nil {
|
||||||
err = fmt.Errorf("postfix operator %q requires one operand", self.tk.String())
|
err = self.tk.Errorf("postfix operator %q requires one operand", self.tk.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
10
term_test.go
10
term_test.go
@ -10,9 +10,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestString(t *testing.T) {
|
func TestString(t *testing.T) {
|
||||||
tk1 := NewValueToken(SymInteger, "100", 100)
|
tk1 := NewValueToken(0, 0, SymInteger, "100", 100)
|
||||||
tk2 := NewToken(SymPlus, "+")
|
tk2 := NewToken(0, 0, SymPlus, "+")
|
||||||
tk3 := NewValueToken(SymInteger, "50", 500)
|
tk3 := NewValueToken(0, 0, SymInteger, "50", 500)
|
||||||
|
|
||||||
tree := NewAst()
|
tree := NewAst()
|
||||||
if gotErr := tree.addTokens(tk1, tk2, tk3); gotErr == nil {
|
if gotErr := tree.addTokens(tk1, tk2, tk3); gotErr == nil {
|
||||||
@ -23,7 +23,7 @@ func TestString(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetRoom(t *testing.T) {
|
func TestGetRoom(t *testing.T) {
|
||||||
tk1 := NewValueToken(SymInteger, "100", 100)
|
tk1 := NewValueToken(0, 0, SymInteger, "100", 100)
|
||||||
|
|
||||||
tree := NewAst()
|
tree := NewAst()
|
||||||
if gotErr := tree.addTokens(tk1); gotErr == nil {
|
if gotErr := tree.addTokens(tk1); gotErr == nil {
|
||||||
@ -33,7 +33,7 @@ func TestGetRoom(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
func TestGetChildrenCount(t *testing.T) {
|
func TestGetChildrenCount(t *testing.T) {
|
||||||
tk1 := NewValueToken(SymInteger, "100", 100)
|
tk1 := NewValueToken(0, 0, SymInteger, "100", 100)
|
||||||
|
|
||||||
tree := NewAst()
|
tree := NewAst()
|
||||||
if gotErr := tree.addTokens(tk1); gotErr == nil {
|
if gotErr := tree.addTokens(tk1); gotErr == nil {
|
||||||
|
19
token.go
19
token.go
@ -11,6 +11,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Token struct {
|
type Token struct {
|
||||||
|
row int
|
||||||
|
col int
|
||||||
Sym Symbol
|
Sym Symbol
|
||||||
source string
|
source string
|
||||||
Value any
|
Value any
|
||||||
@ -30,19 +32,19 @@ func (tk *Token) String() string {
|
|||||||
return fmt.Sprintf("%s", tk.source)
|
return fmt.Sprintf("%s", tk.source)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewToken(sym Symbol, source string) *Token {
|
func NewToken(row, col int, sym Symbol, source string) *Token {
|
||||||
return &Token{Sym: sym, source: source}
|
return &Token{row: row, col: col, Sym: sym, source: source}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewValueToken(sym Symbol, source string, value any) *Token {
|
func NewValueToken(row, col int, sym Symbol, source string, value any) *Token {
|
||||||
return &Token{Sym: sym, source: source, Value: value}
|
return &Token{row: row, col: col, Sym: sym, source: source, Value: value}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewErrorToken(row, col int, err error) *Token {
|
func NewErrorToken(row, col int, err error) *Token {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
return NewToken(SymEos, "")
|
return NewToken(row, col, SymEos, "")
|
||||||
}
|
}
|
||||||
return NewValueToken(SymError, fmt.Sprintf("[%d:%d]", row, col), err)
|
return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tk *Token) IsEos() bool {
|
func (tk *Token) IsEos() bool {
|
||||||
@ -56,3 +58,8 @@ func (tk *Token) IsError() bool {
|
|||||||
func (tk *Token) IsTerm(termSymbols []Symbol) bool {
|
func (tk *Token) IsTerm(termSymbols []Symbol) bool {
|
||||||
return tk.IsEos() || tk.IsError() || (termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0)
|
return tk.IsEos() || tk.IsError() || (termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Token) Errorf(template string, args ...any) (err error) {
|
||||||
|
err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", self.row, self.col)+template, args...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
@ -10,8 +10,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestDevString(t *testing.T) {
|
func TestDevString(t *testing.T) {
|
||||||
tk1 := NewValueToken(SymInteger, "100", 100)
|
tk1 := NewValueToken(0, 0, SymInteger, "100", 100)
|
||||||
tk2 := NewToken(SymPlus, "+")
|
tk2 := NewToken(0, 0, SymPlus, "+")
|
||||||
|
|
||||||
fmt.Println("Token '100':", tk1.DevString())
|
fmt.Println("Token '100':", tk1.DevString())
|
||||||
fmt.Println("Token '+':", tk2.DevString())
|
fmt.Println("Token '+':", tk2.DevString())
|
||||||
|
12
utils.go
12
utils.go
@ -101,3 +101,15 @@ func anyFloat(v any) (float float64, ok bool) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CopyMap[K comparable, V any](dest, source map[K]V) map[K]V {
|
||||||
|
for k, v := range source {
|
||||||
|
dest[k] = v
|
||||||
|
}
|
||||||
|
return dest
|
||||||
|
}
|
||||||
|
|
||||||
|
func CloneMap[K comparable, V any](source map[K]V) map[K]V {
|
||||||
|
dest := make(map[K]V, len(source))
|
||||||
|
return CopyMap(dest, source)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user