Skip to content

Commit

Permalink
Support else if (#58)
Browse files Browse the repository at this point in the history
  • Loading branch information
ruudk authored and markbates committed Jun 19, 2018
1 parent 709c13e commit 2ec029f
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 9 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,14 @@ You can add comments like this:

## If/Else Statements

The basic syntax of `if/else` statements is as follows:
The basic syntax of `if/else if/else` statements is as follows:

```erb
<%
if (true) {
# do something
} else if (false) {
# do something
} else {
# do something else
}
Expand Down
15 changes: 15 additions & 0 deletions ast/if_expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,16 @@ type IfExpression struct {
TokenAble
Condition Expression
Block *BlockStatement
ElseIf []*ElseIfExpression
ElseBlock *BlockStatement
}

type ElseIfExpression struct {
TokenAble
Condition Expression
Block *BlockStatement
}

func (ie *IfExpression) expressionNode() {}

func (ie *IfExpression) String() string {
Expand All @@ -22,6 +29,14 @@ func (ie *IfExpression) String() string {
out.WriteString(ie.Block.String())
out.WriteString(" }")

for _, elseIf := range ie.ElseIf {
out.WriteString(" } else if (")
out.WriteString(elseIf.Condition.String())
out.WriteString(") { ")
out.WriteString(elseIf.Block.String())
out.WriteString(" }")
}

if ie.ElseBlock != nil {
out.WriteString(" } else { ")
out.WriteString(ie.ElseBlock.String())
Expand Down
26 changes: 22 additions & 4 deletions compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,13 +182,31 @@ func (c *compiler) evalIfExpression(node *ast.IfExpression) (interface{}, error)
}
}

var r interface{}
if c.isTruthy(con) {
return c.evalBlockStatement(node.Block)
} else {
if node.ElseBlock != nil {
return c.evalBlockStatement(node.ElseBlock)
}

return c.evalElseAndElseIfExpressions(node)
}

func (c *compiler) evalElseAndElseIfExpressions(node *ast.IfExpression) (interface{}, error) {
// fmt.Println("evalElseIfExpression")
var r interface{}
for _, eiNode := range node.ElseIf {
eiCon, err := c.evalExpression(eiNode.Condition)
if err != nil {
if errors.Cause(err) != ErrUnknownIdentifier {
return nil, errors.WithStack(err)
}
}

if c.isTruthy(eiCon) {
return c.evalBlockStatement(eiNode.Block)
}
}

if node.ElseBlock != nil {
return c.evalBlockStatement(node.ElseBlock)
}

return r, nil
Expand Down
26 changes: 26 additions & 0 deletions if_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,32 @@ func Test_Render_If_Else_True(t *testing.T) {
r.Equal("<p>hi</p>", s)
}

func Test_Render_If_Else_If_Else_True(t *testing.T) {
r := require.New(t)
ctx := NewContext()
input := `<p><%= if (state == "foo") { %>hi foo<% } else if (state == "bar") { %>hi bar<% } else if (state == "fizz") { %>hi fizz<% } else { %>hi buzz<% } %></p>`

ctx.Set("state", "foo")
s, err := Render(input, ctx)
r.NoError(err)
r.Equal("<p>hi foo</p>", s)

ctx.Set("state", "bar")
s, err = Render(input, ctx)
r.NoError(err)
r.Equal("<p>hi bar</p>", s)

ctx.Set("state", "fizz")
s, err = Render(input, ctx)
r.NoError(err)
r.Equal("<p>hi fizz</p>", s)

ctx.Set("state", "buzz")
s, err = Render(input, ctx)
r.NoError(err)
r.Equal("<p>hi buzz</p>", s)
}

func Test_Render_If_Matches(t *testing.T) {
r := require.New(t)
input := `<p><%= if ("foo" ~= "bar") { return "hi" } else { return "bye" } %></p>`
Expand Down
44 changes: 40 additions & 4 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,16 +473,52 @@ func (p *parser) parseIfExpression() ast.Expression {

expression.Block = p.parseBlockStatement()

if p.peekTokenIs(token.ELSE) {
for p.peekTokenIs(token.ELSE) {
p.nextToken()

if !p.expectPeek(token.LBRACE) {
return nil
if p.peekTokenIs(token.IF) {
p.nextToken()

ifElseExp := p.parseElseIfExpression()

if ifElseExp == nil {
return nil
}

expression.ElseIf = append(expression.ElseIf, ifElseExp)
} else {
if !p.expectPeek(token.LBRACE) {
return nil
}

expression.ElseBlock = p.parseBlockStatement()
}
}

return expression
}

func (p *parser) parseElseIfExpression() *ast.ElseIfExpression {
// fmt.Println("parseElseIfExpression")
expression := &ast.ElseIfExpression{TokenAble: ast.TokenAble{p.curToken}}

if !p.expectPeek(token.LPAREN) {
return nil
}

p.nextToken()
expression.Condition = p.parseExpression(LOWEST)

if !p.expectPeek(token.RPAREN) {
return nil
}

expression.ElseBlock = p.parseBlockStatement()
if !p.expectPeek(token.LBRACE) {
return nil
}

expression.Block = p.parseBlockStatement()

return expression
}

Expand Down

0 comments on commit 2ec029f

Please sign in to comment.