config/lang/ast: Eval
This commit is contained in:
parent
c4273974de
commit
c96b3b9ddc
|
@ -15,6 +15,10 @@ type Node interface {
|
||||||
|
|
||||||
// Type returns the type of this node for the given context.
|
// Type returns the type of this node for the given context.
|
||||||
Type(Scope) (Type, error)
|
Type(Scope) (Type, error)
|
||||||
|
|
||||||
|
// Eval evaluates this node, returning its final value. The type
|
||||||
|
// of the final value will match the result of Type with the same scope.
|
||||||
|
Eval(*EvalContext) (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pos is the starting position of an AST node
|
// Pos is the starting position of an AST node
|
||||||
|
@ -27,9 +31,11 @@ func (p Pos) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EvalContext is the context given for evaluation.
|
// EvalContext is the context given for evaluation.
|
||||||
|
//
|
||||||
|
// It is simple for now with just a Scope but we use a struct in case we
|
||||||
|
// plan on adding fields in the future.
|
||||||
type EvalContext struct {
|
type EvalContext struct {
|
||||||
Scope Scope
|
Scope Scope
|
||||||
Stack Stack
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visitors are just implementations of this function.
|
// Visitors are just implementations of this function.
|
||||||
|
|
|
@ -41,3 +41,22 @@ func (n *Call) Type(s Scope) (Type, error) {
|
||||||
|
|
||||||
return f.ReturnType, nil
|
return f.ReturnType, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *Call) Eval(ctx *EvalContext) (interface{}, error) {
|
||||||
|
f, ok := ctx.Scope.LookupFunc(n.Func)
|
||||||
|
if !ok {
|
||||||
|
return TypeInvalid, fmt.Errorf("unknown function: %s", n.Func)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := make([]interface{}, len(n.Args))
|
||||||
|
for i, arg := range n.Args {
|
||||||
|
result, err := arg.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
args[i] = result
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.Callback(args)
|
||||||
|
}
|
||||||
|
|
|
@ -34,3 +34,24 @@ func TestCallType_invalid(t *testing.T) {
|
||||||
t.Fatal("should error")
|
t.Fatal("should error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCallEval(t *testing.T) {
|
||||||
|
c := &Call{Func: "foo"}
|
||||||
|
scope := &BasicScope{
|
||||||
|
FuncMap: map[string]Function{
|
||||||
|
"foo": Function{
|
||||||
|
Callback: func([]interface{}) (interface{}, error) {
|
||||||
|
return "42", nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
actual, err := c.Eval(&EvalContext{Scope: scope})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if actual != "42" {
|
||||||
|
t.Fatalf("bad: %s", actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -40,3 +40,17 @@ func (n *Concat) String() string {
|
||||||
func (n *Concat) Type(Scope) (Type, error) {
|
func (n *Concat) Type(Scope) (Type, error) {
|
||||||
return TypeString, nil
|
return TypeString, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *Concat) Eval(ctx *EvalContext) (interface{}, error) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
for _, expr := range n.Exprs {
|
||||||
|
result, err := expr.Eval(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteString(result.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String(), nil
|
||||||
|
}
|
||||||
|
|
|
@ -14,3 +14,21 @@ func TestConcatType(t *testing.T) {
|
||||||
t.Fatalf("bad: %s", actual)
|
t.Fatalf("bad: %s", actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConcatEval(t *testing.T) {
|
||||||
|
c := &Concat{
|
||||||
|
Exprs: []Node{
|
||||||
|
&LiteralNode{Value: "foo"},
|
||||||
|
&LiteralNode{Value: "bar"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
scope := &BasicScope{}
|
||||||
|
|
||||||
|
actual, err := c.Eval(&EvalContext{Scope: scope})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if actual != "foobar" {
|
||||||
|
t.Fatalf("bad: %s", actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -31,3 +31,7 @@ func (n *LiteralNode) String() string {
|
||||||
func (n *LiteralNode) Type(Scope) (Type, error) {
|
func (n *LiteralNode) Type(Scope) (Type, error) {
|
||||||
return n.Typex, nil
|
return n.Typex, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *LiteralNode) Eval(*EvalContext) (interface{}, error) {
|
||||||
|
return n.Value, nil
|
||||||
|
}
|
||||||
|
|
|
@ -14,3 +14,16 @@ func TestLiteralNodeType(t *testing.T) {
|
||||||
t.Fatalf("bad: %s", actual)
|
t.Fatalf("bad: %s", actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLiteralNodeEval(t *testing.T) {
|
||||||
|
c := &LiteralNode{Value: "42", Typex: TypeString}
|
||||||
|
scope := &BasicScope{}
|
||||||
|
|
||||||
|
actual, err := c.Eval(&EvalContext{Scope: scope})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if actual != "42" {
|
||||||
|
t.Fatalf("bad: %s", actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -34,3 +34,12 @@ func (n *VariableAccess) Type(s Scope) (Type, error) {
|
||||||
|
|
||||||
return v.Type, nil
|
return v.Type, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *VariableAccess) Eval(ctx *EvalContext) (interface{}, error) {
|
||||||
|
v, ok := ctx.Scope.LookupVar(n.Name)
|
||||||
|
if !ok {
|
||||||
|
return TypeInvalid, fmt.Errorf("unknown variable: %s", n.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.Value, nil
|
||||||
|
}
|
||||||
|
|
|
@ -34,3 +34,20 @@ func TestVariableAccessType_invalid(t *testing.T) {
|
||||||
t.Fatal("should error")
|
t.Fatal("should error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVariableAccessEval(t *testing.T) {
|
||||||
|
c := &VariableAccess{Name: "foo"}
|
||||||
|
scope := &BasicScope{
|
||||||
|
VarMap: map[string]Variable{
|
||||||
|
"foo": Variable{Value: "42", Type: TypeString},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
actual, err := c.Eval(&EvalContext{Scope: scope})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
if actual != "42" {
|
||||||
|
t.Fatalf("bad: %s", actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue