|
|
|
@ -26,13 +26,30 @@ const (
|
|
|
|
|
|
|
|
|
|
var indent = " "
|
|
|
|
|
|
|
|
|
|
type context map[string]interface{}
|
|
|
|
|
type context struct {
|
|
|
|
|
public map[string]interface{}
|
|
|
|
|
private map[string]interface{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func newContext() *context {
|
|
|
|
|
return &context{make(map[string]interface{}), make(map[string]interface{})}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (c *context) get(name string) (interface{}, bool) {
|
|
|
|
|
if v, ok := c.public[name]; ok {
|
|
|
|
|
return v, true
|
|
|
|
|
}
|
|
|
|
|
if v, ok := c.private[name]; ok {
|
|
|
|
|
return v, true
|
|
|
|
|
}
|
|
|
|
|
return nil, false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type node interface {
|
|
|
|
|
Type() nodeType
|
|
|
|
|
parse(*parser) error
|
|
|
|
|
pretty(io.Writer, string) error
|
|
|
|
|
eval(context) (interface{}, error)
|
|
|
|
|
eval(*context) (interface{}, error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type rootNode struct {
|
|
|
|
@ -57,12 +74,18 @@ func (n *rootNode) parse(p *parser) error {
|
|
|
|
|
return nil
|
|
|
|
|
case t_comment:
|
|
|
|
|
n.addChild(&commentNode{t.s})
|
|
|
|
|
case t_name, t_variable:
|
|
|
|
|
case t_name:
|
|
|
|
|
nn := &assignmentNode{name: t.s}
|
|
|
|
|
if err := nn.parse(p); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
n.addChild(nn)
|
|
|
|
|
case t_variable:
|
|
|
|
|
nn := &assignmentNode{name: t.s, unexported: true}
|
|
|
|
|
if err := nn.parse(p); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
n.addChild(nn)
|
|
|
|
|
default:
|
|
|
|
|
return fmt.Errorf("parse error: unexpected token type %v while parsing root node", t.t)
|
|
|
|
|
}
|
|
|
|
@ -99,7 +122,7 @@ func (n *rootNode) pretty(w io.Writer, prefix string) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *rootNode) eval(ctx context) (interface{}, error) {
|
|
|
|
|
func (n *rootNode) eval(ctx *context) (interface{}, error) {
|
|
|
|
|
for _, child := range n.children {
|
|
|
|
|
if _, err := child.eval(ctx); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
@ -143,13 +166,14 @@ func (n *commentNode) pretty(w io.Writer, prefix string) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *commentNode) eval(ctx context) (interface{}, error) {
|
|
|
|
|
func (n *commentNode) eval(ctx *context) (interface{}, error) {
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type assignmentNode struct {
|
|
|
|
|
name string
|
|
|
|
|
value node
|
|
|
|
|
name string
|
|
|
|
|
value node
|
|
|
|
|
unexported bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *assignmentNode) Type() nodeType {
|
|
|
|
@ -191,15 +215,19 @@ func (n *assignmentNode) pretty(w io.Writer, prefix string) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *assignmentNode) eval(ctx context) (interface{}, error) {
|
|
|
|
|
if _, ok := ctx[n.name]; ok {
|
|
|
|
|
func (n *assignmentNode) eval(ctx *context) (interface{}, error) {
|
|
|
|
|
if _, ok := ctx.get(n.name); ok {
|
|
|
|
|
return nil, fmt.Errorf("invalid re-declaration: %s", n.name)
|
|
|
|
|
}
|
|
|
|
|
v, err := n.value.eval(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
ctx[n.name] = v
|
|
|
|
|
if n.unexported {
|
|
|
|
|
ctx.private[n.name] = v
|
|
|
|
|
} else {
|
|
|
|
|
ctx.public[n.name] = v
|
|
|
|
|
}
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -228,7 +256,7 @@ func (s *stringNode) pretty(w io.Writer, prefix string) error {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *stringNode) eval(ctx context) (interface{}, error) {
|
|
|
|
|
func (s *stringNode) eval(ctx *context) (interface{}, error) {
|
|
|
|
|
return string(*s), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -292,7 +320,7 @@ func (n *numberNode) pretty(w io.Writer, prefix string) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *numberNode) eval(ctx context) (interface{}, error) {
|
|
|
|
|
func (n *numberNode) eval(ctx *context) (interface{}, error) {
|
|
|
|
|
switch n.t {
|
|
|
|
|
case num_int:
|
|
|
|
|
return n.i, nil
|
|
|
|
@ -342,7 +370,7 @@ func (l *listNode) pretty(w io.Writer, prefix string) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *listNode) eval(ctx context) (interface{}, error) {
|
|
|
|
|
func (l *listNode) eval(ctx *context) (interface{}, error) {
|
|
|
|
|
out := make([]interface{}, 0, len(*l))
|
|
|
|
|
for _, n := range *l {
|
|
|
|
|
v, err := n.eval(ctx)
|
|
|
|
@ -406,7 +434,7 @@ func (o *objectNode) pretty(w io.Writer, prefix string) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (o *objectNode) eval(ctx context) (interface{}, error) {
|
|
|
|
|
func (o *objectNode) eval(ctx *context) (interface{}, error) {
|
|
|
|
|
out := make(map[string]interface{}, len(*o))
|
|
|
|
|
for name, node := range *o {
|
|
|
|
|
v, err := node.eval(ctx)
|
|
|
|
@ -441,8 +469,8 @@ func (v *variableNode) pretty(w io.Writer, prefix string) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (v *variableNode) eval(ctx context) (interface{}, error) {
|
|
|
|
|
value, ok := ctx[v.name]
|
|
|
|
|
func (v *variableNode) eval(ctx *context) (interface{}, error) {
|
|
|
|
|
value, ok := ctx.get(v.name)
|
|
|
|
|
if !ok {
|
|
|
|
|
return nil, fmt.Errorf("undefined variable: %s", *v)
|
|
|
|
|
}
|
|
|
|
|