Skip to content

Commit

Permalink
map exprs
Browse files Browse the repository at this point in the history
  • Loading branch information
jmoiron committed May 28, 2014
1 parent 2f2916e commit 0efeedd
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 21 deletions.
53 changes: 53 additions & 0 deletions neo/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const (
NodeString
NodeAdd
NodeMul
NodeMapExpr
NodeMapElem
)

// This is a stack of nodes starting at a position. It has the default NodeType
Expand Down Expand Up @@ -222,3 +224,54 @@ func (m *MulExpr) String() string {
func (m *MulExpr) Copy() Node {
return newMulExpr(m.lhs, m.rhs, m.operator)
}

// complex literals

type MapExpr struct {
NodeType
Pos
Elems []*MapElem
}

func newMapExpr(pos Pos) *MapExpr {
return &MapExpr{NodeType: NodeMapExpr, Pos: pos}
}

func (m *MapExpr) append(n *MapElem) {
m.Elems = append(m.Elems, n)
}

func (m *MapExpr) String() string {
// FIXME: do it right
return fmt.Sprintf("{%s}", m.Elems)
}

func (m *MapExpr) Copy() Node {
if m == nil {
return m
}
n := newMapExpr(m.Pos)
for _, elem := range m.Elems {
n.append(elem.Copy().(*MapElem))
}
return n
}

type MapElem struct {
NodeType
Pos
Key Node
Value Node
}

func newMapElem(lhs, rhs Node) *MapElem {
return &MapElem{NodeMapElem, lhs.Position(), lhs, rhs}
}

func (m *MapElem) String() string {
return fmt.Sprintf("%s: %s", m.Key, m.Value)
}

func (m *MapElem) Copy() Node {
return newMapElem(m.Key, m.Value)
}
63 changes: 49 additions & 14 deletions neo/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ func (t *Tree) skipComment() {
continue
case tokenCommentEnd:
default:
t.unexpected(token, "end commend")
t.unexpected(token, "end comment")
}
break
}
Expand Down Expand Up @@ -398,20 +398,25 @@ func (t *Tree) parseExpr(stack *nodeStack, terminator itemType) Node {
if stack == nil {
stack = newStack(token.pos)
}
var unary item
for {
if unary.typ != 0 && stack.len() > 0 {
// apply the unary to the expression
}
token = t.peekNonSpace()
switch token.typ {
case terminator:
if stack.len() != 1 {
if stack.len() == 0 {
fmt.Printf("Stack: %#v\n", stack)
t.unexpected(token, "zero length expression")
}
fmt.Println(stack, stack.len())
return stack.pop()
case tokenName:
stack.push(t.lookupExpr())
case tokenLparen:
t.expect(tokenLparen)
stack.push(t.parseExpr(tokenRparen))
stack.push(t.parseExpr(nil, tokenRparen))
case tokenLbrace:
stack.push(t.mapExpr())
case tokenLbracket:
Expand All @@ -425,26 +430,38 @@ func (t *Tree) parseExpr(stack *nodeStack, terminator itemType) Node {
case tokenAdd, tokenSub:
t.nextNonSpace()
if stack.len() > 0 {
lhs := stack.pop()
rhs := t.parseExpr(terminator)
// TODO: we must peek to see if the next oper is a mul oper
// in order to conserve order of operations
stack.push(newAddExpr(lhs, rhs, token))
rhs := t.parseExpr(stack, terminator)
// if the next token is an operator with greater precedence, push rhs
// to the stack and continue going from there
if tok := t.peekNonSpace(); tok.precedence() > token.precedence() {
stack.push(rhs)
return t.parseExpr(stack, terminator)
} else {
lhs := stack.pop()
stack.push(newAddExpr(lhs, rhs, token))
}
} else {
t.unexpected(token, "binary op")
unary = token
}
// FIXME: unary + is a noop, but unary - isn't..
case tokenMul, tokenMod, tokenDiv, tokenFloordiv:
t.nextNonSpace()
if stack.len() > 0 {
lhs := stack.pop()
rhs := t.parseExpr(terminator)
rhs := t.parseExpr(stack, terminator)
// we know this strongly binds ltr so no need to peek
fmt.Println(lhs, rhs)
stack.push(newMulExpr(lhs, rhs, token))
panic("exit")
} else {
t.unexpected(token, "binary op")
}
case tokenComma:
// if we are terminating a map, param list, or list, return the expression
if terminator == tokenRbracket || terminator == tokenRparen || terminator == tokenRbrace {
if stack.len() != 1 {
t.unexpected(token, "expression")
}
return stack.pop()
}
default:
t.unexpected(token, "expression")
}
Expand Down Expand Up @@ -475,15 +492,33 @@ func (t *Tree) parenExpr() Node {
}

func (t *Tree) mapExpr() Node {
tok := t.expect(tokenLbrace)
node := newMapExpr(tok.pos)
for {
token := t.nextNonSpace()
token := t.peekNonSpace()
switch token.typ {
case tokenRbracket:
case tokenRbrace:
t.next()
return nil
default:
elem := t.mapElem()
node.append(elem.(*MapElem))
}
}
}

// parse a single map element; assume that the next token is not '}'
func (t *Tree) mapElem() Node {
key := t.parseExpr(nil, tokenColon)
colon := t.nextNonSpace()
if colon.typ != tokenColon {
t.unexpected(colon, "map key expr")
}
val := t.parseExpr(nil, tokenRbrace)
return newMapElem(key, val)

}

func (t *Tree) listExpr() Node {
t.next()
return nil
Expand Down
12 changes: 5 additions & 7 deletions neo/parse_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package jigo

import (
"fmt"
"testing"
)
import "testing"

func (n NodeType) String() string {
switch n {
Expand All @@ -30,6 +27,7 @@ type parsetest struct {
func (p *parsetest) Test(input string, test parseTest) {
t := p.T
e := NewEnvironment()
t.Log(input)
tree, err := e.parse(input, "test", "test.jigo")

/*
Expand Down Expand Up @@ -67,6 +65,7 @@ func (p *parsetest) Test(input string, test parseTest) {
t.Errorf("Type mismatch: expecting %dth to be %s, but was %s", i, nt, rnt)
}
}
t.Log(tree.Root)
}

func TestParser(t *testing.T) {
Expand Down Expand Up @@ -97,15 +96,14 @@ func TestParser(t *testing.T) {
parseTest{nodeTypes: []NodeType{NodeVar}},
)

fmt.Println("WHEE")
tester.Test(
`{{ 1 + 2 * 3 + 4}}`,
parseTest{nodeTypes: []NodeType{NodeVar}},
)

tester.Test(
`{{ {"hello": "world"}[choice] }}`,
parseTest{nodeTypes: []NodeType{}},
`{{ {"hello": "world"} }}`,
parseTest{nodeTypes: []NodeType{NodeVar}},
)

}

0 comments on commit 0efeedd

Please sign in to comment.