Skip to content

Commit

Permalink
Parse variable declaration and reference syntax
Browse files Browse the repository at this point in the history
Gamelan currently playing: Sabi Lulungan
co-authored-by: Connor Walsh <[email protected]>
  • Loading branch information
mouse-reeve committed Apr 11, 2018
1 parent 6f14d3d commit 90dd582
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 18 deletions.
11 changes: 11 additions & 0 deletions evil.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package dockerlang

import (
"context"
"encoding/json"
"fmt"

"github.com/docker/docker/api/types/container"
)
Expand All @@ -18,6 +20,15 @@ import (
// Local, Args, Global

func (c *Compterpreter) Evaluate() error {
b, _ := json.MarshalIndent(c.StackTree, " ", " ")
fmt.Println(string(b))

/*
for _, operand := range c.StackTree.Operands {
b, _ := json.MarshalIndent(operand, " ", " ")
fmt.Println(string(b))
}
*/
r, err := c.StackTree.Operands[0].Eval()

wait, errChan := executer.Docker.ContainerWait(
Expand Down
8 changes: 4 additions & 4 deletions forest.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,16 @@ func (e *Expr) Eval() (string, error) {
// but is should overwrite the Eval function since it does that differently.
type IfConditional struct{}

type Variable struct {
Literal
type Identifier struct {
Type string
Name string
Bound bool
}

func (v *Variable) Eval() (string, error) {
func (i *Identifier) Eval() (string, error) {
return executer.Run(
&ExecutionData{
ComputationType: VARIABLE_IDENTIFIER,
ComputationType: i.Type,
},
)
}
Expand Down
1 change: 1 addition & 0 deletions lexer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ func (s *LexerSuite) TestTokenizeIdentifier_Keyword() {
break
}
s.EqualValues(compt.CurrentToken.Value, op)
s.EqualValues(compt.CurrentToken.Type, KEYWORD)
}
}

Expand Down
115 changes: 106 additions & 9 deletions parser.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package dockerlang

import "fmt"

type Stack struct {
Elements []AST
}
Expand Down Expand Up @@ -38,23 +40,39 @@ func (s *Stack) Length() int {
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 = NewStack()
exprStack = NewStack()
opsStack = NewStack()
exprStack = NewStack()
parenCount = 0
)
// TODO make the root expression an EXIT_OPERATOR which will operate
// on all program roots.
c.StackTree = &Expr{}

for _, token := range c.Tokens {
if parenCount < 0 {
fmt.Println("UNBALANCED PARENS")
// TODO (cw,mr|4.11.2018) be more specific -____-
return DockerlangSyntaxError
}

switch token.Type {
case OPERATOR:
opsStack.Push(&Expr{Op: token.Value, Arity: OP_TO_ARITY[token.Value]})
case INT:
exprStack.Push(&Literal{Type: INT, Value: token.Value})
case IDENTIFIER:
// is this a declaration or a reference?
identifier, err := c.StackTree.ParseIdentifier(token, opsStack)
if err != nil {
return err
}

exprStack.Push(identifier)
case PUNCTUATION:
switch token.Value {
case "(":
// TODO: eventually check a puntaution stack for syntax checking
parenCount++
case ")":
// shit gets real
var opsExpr = opsStack.Pop().(*Expr)
Expand All @@ -68,23 +86,102 @@ func (c *Compterpreter) Parse() error {
}
// push modified ops expr onto the expr stack
exprStack.Push(opsExpr)

parenCount--
default:
// whatever
}
}
// if there is only 1 element in the expressionStack, we have successfully parsed
// a single expression
if exprStack.Length() == 1 && parenCount == 0 {
// there should be nothing on the operations stack!
if opsStack.Peek() != nil {
fmt.Println("error in loop")
fmt.Println(opsStack.Peek())
fmt.Println(exprStack.Peek())
// oh noooo!
return DockerlangSyntaxError
}

// add this expression to the sequential list of expressions in the
// programs execution
c.StackTree.Operands = append(
c.StackTree.Operands,
exprStack.Pop().(*Expr),
)
}
}

// there should only be one expr in exprStack
if exprStack.Length() != 1 {
// there should be nothing on the expression stack or operation stack
if exprStack.Length() != 0 || opsStack.Peek() != nil {
fmt.Println("SOMETHING IS AWRY")
fmt.Println(opsStack.Peek())
fmt.Println(exprStack.Peek())
// oh no!
return DockerlangSyntaxError
}
if opsStack.Peek() != nil {
// oh noooo!
return DockerlangSyntaxError

return nil
}

// TODO (cw,mr|4.11.2018) refactor this so that there is better separation of concerns
// i.e. it *would* be useful to have a function for adding local, global, args, etc. to
// an expression, but we might not want to do all this parsing in that function.
// NOTE: once we implement functions, we are going to want to check globals and args!
func (e *Expr) ParseIdentifier(token Token, opsStack *Stack) (*Identifier, error) {
var (
isDefined bool = false
knownIdentifier *Identifier
)

// check all locals to see if we've already defined this identifier
for name, ast := range e.Locals {
if token.Value == name {
// this means we have already defined this identifer
isDefined = true
knownIdentifier = ast.(*Identifier)
break
}
}

c.StackTree.Operands = []AST{exprStack.Pop().(*Expr)}
prev := opsStack.Peek().(*Expr)

return nil
// this is an identifier reference
if prev.Op != VARIABLE_INITIALIZATION && prev.Op != FUNCTION_KEYWORD {
if !isDefined {
// TODO (cw,mr|4.11.2018) make this error more informative
fmt.Println("TRYING TO USE AN UNDEFINED THING")
return nil, DockerlangSyntaxError
}

// we are assuming that if an identifier is defined, then it is also bounded (or whatever)
return knownIdentifier, nil
}

// if we are here, this is an identifier definition

// we are trying to re-define this identifier
if isDefined {
fmt.Println("")
return nil, DockerlangSyntaxError
}

// actually define this identifier
switch prev.Op {
case VARIABLE_INITIALIZATION:
knownIdentifier = &Identifier{Type: VARIABLE_IDENTIFIER, Name: token.Value, Bound: true}
case FUNCTION_KEYWORD:
knownIdentifier = &Identifier{Type: FUNCTION_IDENTIFIER, Name: token.Value, Bound: true}
}

// TODO (cw, mr|4.11.2018) maybe put this in the Expr constructor once one exists
if e.Locals == nil {
e.Locals = map[string]AST{}
}

// add this to the local scope of the current expression
e.Locals[knownIdentifier.Name] = knownIdentifier

return knownIdentifier, nil
}
6 changes: 4 additions & 2 deletions symbols.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ var (
MULTIPLICATION_OPERATOR: 2,
DIVISION_OPERATOR: 2,
MODULO_OPERATOR: 2,
VARIABLE_INITIALIZATION: 1,
VARIABLE_ASSIGNMENT: 1,
VARIABLE_INITIALIZATION: 2,
VARIABLE_ASSIGNMENT: 2,
EXIT_OPERATOR: 1,
NOOP: 1,
}
Expand All @@ -55,6 +55,8 @@ func PopulateSymbols() *Symbols {
MULTIPLICATION_OPERATOR,
DIVISION_OPERATOR,
MODULO_OPERATOR,
VARIABLE_INITIALIZATION,
VARIABLE_ASSIGNMENT,
EXIT_OPERATOR,
NOOP,
},
Expand Down
1 change: 1 addition & 0 deletions test/test_identifiers_keyword.doc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
if
5 changes: 2 additions & 3 deletions test/variable.doc
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
(≡ aVariable 0)

if asdf
ifIamKewl
(= aVariable 23)
(ꙮ (+ aVariable 2))

0 comments on commit 90dd582

Please sign in to comment.