Skip to content

Commit

Permalink
Tidying up parser, starting eval
Browse files Browse the repository at this point in the history
* Refactor AST model to remove StackTree
* Generalize Expr to represent StackTrees too
* Add types for literals
* Wrote simple Eval method mixin

Gamelan music currently playing: Meisou
  • Loading branch information
mouse-reeve committed Mar 21, 2018
1 parent 3b59e1d commit e6342c3
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 77 deletions.
7 changes: 5 additions & 2 deletions compterpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type Compterpreter struct {
CurrentToken Token
Symbols *Symbols
Tokens []Token
StackTree *StackTree
StackTree *Expr
}

func NewCompterpreter(c *Config) *Compterpreter {
Expand Down Expand Up @@ -75,9 +75,12 @@ func (c *Compterpreter) LoadSourceCode() error {
}

func (c *Compterpreter) Interpret() error {
// Identifies tokens in the provided .doc code
c.Lex()

// Creates c.StackTree representing the provided .doc code
c.Parse()
// Actually dockerize and evaluate the StackTree
c.Evaluate()

return nil
}
4 changes: 4 additions & 0 deletions evil.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@ package dockerlang

// Precedence for variable lookup is:
// Local, Args, Global

func (c *Compterpreter) Evaluate() error {
return c.StackTree.Operands[0].Eval()
}
75 changes: 58 additions & 17 deletions forest.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,77 @@ package dockerlang
// /\ /\ /\ \
// AST map AST map empty AST map (AST of parsed code)

type StackTree struct {
Name string
Args map[string]AST
Locals map[string]AST
Globals map[string]AST
Body AST
type AST interface {
Eval() error
GetChildren() []AST
}

func NewStackTree(name string) *StackTree {
return &StackTree{
Name: name,
}
// we should never actually instantiate this on its own
// it should *always* be an embedded structure (this is a mixin)
type BaseAST struct{}

func (b *BaseAST) GetChildren() []AST {
return []AST{}
}

func (s *StackTree) Eval() ([]interface{}, []interface{}, error) {
return nil, nil, nil
func (b *BaseAST) Eval() error {
var (
err error
)

// we need to evaluate all child expressions in order
// to evaluate the current expression. So, evaluate
// all the child ASTs from left to right
for _, child := range b.GetChildren() {
err = child.Eval()
if err != nil {
return err
}
}

// we've computed all dependencies, now lets eval this thang
err = b.Eval()
if err != nil {
return err
}

return nil
}

type Expr struct {
BaseAST
Name string
Op string
DLII string
Arity int
Operands []interface{}
Operands []AST
Args map[string]AST
Locals map[string]AST
Globals map[string]AST
}

func (e *Expr) Eval() ([]interface{}, []interface{}, error) {
return nil, nil, nil
func NewExpr(name string) *Expr {
return &Expr{
Name: name,
}
}

type AST interface {
Eval() ([]interface{}, []interface{}, error)
type IfConditional struct{}

func (c *IfConditional) Eval() error {

return nil
}

type Variable struct {
BaseAST
Literal
Name string
Bound bool
}

type Literal struct {
BaseAST
Type string
Value interface{}
}
37 changes: 16 additions & 21 deletions parser.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package dockerlang

type ExprStack struct {
Elements []*Expr
type Stack struct {
Elements []AST
}

func NewExprStack() *ExprStack {
return &ExprStack{Elements: []*Expr{}}
func NewStack() *Stack {
return &Stack{Elements: []AST{}}
}

func (s *ExprStack) Push(e *Expr) {
func (s *Stack) Push(e AST) {
s.Elements = append(s.Elements, e)
}

func (s *ExprStack) Pop() *Expr {
func (s *Stack) Pop() AST {
e := s.Peek()
if e == nil {
return nil
Expand All @@ -23,51 +23,46 @@ func (s *ExprStack) Pop() *Expr {
return e
}

func (s *ExprStack) Peek() *Expr {
func (s *Stack) Peek() AST {
if len(s.Elements) == 0 {
return nil
}

return s.Elements[len(s.Elements)-1]
}

func (s *ExprStack) Length() int {
func (s *Stack) Length() int {
return len(s.Elements)
}

func (c *Compterpreter) Parse() error {
// build the global StackTree, for all expressions in the global scope as part of an implicit anonymous function
var (
opsStack = NewExprStack()
exprStack = NewExprStack()
opsStack = NewStack()
exprStack = NewStack()
)
c.StackTree = NewStackTree(c.Config.SrcFileName)
c.StackTree = NewExpr(c.Config.SrcFileName)

for _, token := range c.Tokens {
switch token.Type {
case OPERATOR:
opsStack.Push(&Expr{Op: token.Value, Arity: OP_TO_ARITY[token.Value]})
case INT:
exprStack.Push(&Expr{Op: NOOP, Arity: OP_TO_ARITY[NOOP], Operands: []interface{}{token.Value}})
exprStack.Push(&Literal{Type: INT, Value: token.Value})
case PUNCTUATION:
switch token.Value {
case "(":
opsStack.Push(&Expr{Op: token.Value, Arity: 1, Operands: []interface{}{token.Value}})
// TODO: eventually check a puntaution stack for syntax checking
case ")":
// shit gets real
var opsExpr = opsStack.Pop()
var opsExpr = opsStack.Pop().(*Expr)
// pop a count of arity items off exprStack
for i := 0; i < opsExpr.Arity; i++ {
// make sure we're not popping nil into exprs
if exprStack.Peek() == nil {
return DockerlangSyntaxError
}
opsExpr.Operands = append([]interface{}{exprStack.Pop()}, opsExpr.Operands...)
}
// update the stacks
var betterBeAnOpenParen = opsStack.Pop()
if betterBeAnOpenParen == nil || betterBeAnOpenParen.Op != "(" {
return DockerlangSyntaxError
opsExpr.Operands = append([]AST{exprStack.Pop()}, opsExpr.Operands...)
}
// push modified ops expr onto the expr stack
exprStack.Push(opsExpr)
Expand All @@ -86,7 +81,7 @@ func (c *Compterpreter) Parse() error {
// oh noooo!
return DockerlangSyntaxError
}
c.StackTree.Body = exprStack.Pop()
c.StackTree.Operands = []AST{exprStack.Pop().(*Expr)}

return nil
}
51 changes: 14 additions & 37 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,51 +25,28 @@ func TestParser(t *testing.T) {
t.Error(err)
}

expectedStackTree := &StackTree{
expectedTree := &Expr{
Name: "src.doc",
Body: &Expr{
Op: DIVISION_OPERATOR,
Arity: OP_TO_ARITY[DIVISION_OPERATOR],
Operands: []interface{}{
&Expr{
Op: ADDITION_OPERATOR,
Arity: OP_TO_ARITY[ADDITION_OPERATOR],
Operands: []interface{}{&Expr{
Op: NOOP,
Arity: OP_TO_ARITY[NOOP],
Operands: []interface{}{"2"},
},
&Expr{
Op: NOOP,
Arity: OP_TO_ARITY[NOOP],
Operands: []interface{}{"3"},
Operands: []AST{
&Expr{
Op: DIVISION_OPERATOR,
Arity: OP_TO_ARITY[DIVISION_OPERATOR],
Operands: []AST{
&Expr{
Op: ADDITION_OPERATOR,
Arity: OP_TO_ARITY[ADDITION_OPERATOR],
Operands: []AST{
&Literal{Type: INT, Value: "2"},
&Literal{Type: INT, Value: "3"},
},
},
},
&Expr{
Op: NOOP,
Arity: OP_TO_ARITY[NOOP],
Operands: []interface{}{"1"},
&Literal{Type: INT, Value: "1"},
},
},
},
}

assert.EqualValues(t, expectedStackTree, compt.StackTree)
}

func TestParser_ImbalancedParens(t *testing.T) {
compt := &Compterpreter{Config: &Config{SrcFileName: "src.doc"}}
compt.Tokens = []Token{
{Type: OPERATOR, Value: "+"},
// is whint? ;)
{Type: INT, Value: "2"},
{Type: INT, Value: "3"},
{Type: PUNCTUATION, Value: ")"},
}

err := compt.Parse()
assert.EqualValues(t, err, DockerlangSyntaxError)
assert.EqualValues(t, expectedTree, compt.StackTree)
}

func TestParser_SyntaxError(t *testing.T) {
Expand Down

0 comments on commit e6342c3

Please sign in to comment.