parser implementation for conditional expression
This commit is contained in:
parent
c0bb733c67
commit
69eb7da51e
6 changed files with 119 additions and 1 deletions
30
compiler/ast/cond_expr.go
Normal file
30
compiler/ast/cond_expr.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
package ast
|
||||
|
||||
import (
|
||||
"github.com/d5/tengo/compiler/source"
|
||||
)
|
||||
|
||||
// CondExpr represents a ternary conditional expression.
|
||||
type CondExpr struct {
|
||||
Cond Expr
|
||||
True Expr
|
||||
False Expr
|
||||
QuestionPos source.Pos
|
||||
ColonPos source.Pos
|
||||
}
|
||||
|
||||
func (e *CondExpr) exprNode() {}
|
||||
|
||||
// Pos returns the position of first character belonging to the node.
|
||||
func (e *CondExpr) Pos() source.Pos {
|
||||
return e.Cond.Pos()
|
||||
}
|
||||
|
||||
// End returns the position of first character immediately after the node.
|
||||
func (e *CondExpr) End() source.Pos {
|
||||
return e.False.End()
|
||||
}
|
||||
|
||||
func (e *CondExpr) String() string {
|
||||
return "(" + e.Cond.String() + " ? " + e.True.String() + " : " + e.False.String() + ")"
|
||||
}
|
|
@ -82,7 +82,14 @@ func (p *Parser) parseExpr() ast.Expr {
|
|||
defer un(trace(p, "Expression"))
|
||||
}
|
||||
|
||||
return p.parseBinaryExpr(token.LowestPrec + 1)
|
||||
expr := p.parseBinaryExpr(token.LowestPrec + 1)
|
||||
|
||||
// ternary conditional expression
|
||||
if p.token == token.Question {
|
||||
return p.parseCondExpr(expr)
|
||||
}
|
||||
|
||||
return expr
|
||||
}
|
||||
|
||||
func (p *Parser) parseBinaryExpr(prec1 int) ast.Expr {
|
||||
|
@ -101,6 +108,7 @@ func (p *Parser) parseBinaryExpr(prec1 int) ast.Expr {
|
|||
pos := p.expect(op)
|
||||
|
||||
y := p.parseBinaryExpr(prec + 1)
|
||||
|
||||
x = &ast.BinaryExpr{
|
||||
LHS: x,
|
||||
RHS: y,
|
||||
|
@ -110,6 +118,24 @@ func (p *Parser) parseBinaryExpr(prec1 int) ast.Expr {
|
|||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseCondExpr(cond ast.Expr) ast.Expr {
|
||||
questionPos := p.expect(token.Question)
|
||||
|
||||
trueExpr := p.parseExpr()
|
||||
|
||||
colonPos := p.expect(token.Colon)
|
||||
|
||||
falseExpr := p.parseExpr()
|
||||
|
||||
return &ast.CondExpr{
|
||||
Cond: cond,
|
||||
True: trueExpr,
|
||||
False: falseExpr,
|
||||
QuestionPos: questionPos,
|
||||
ColonPos: colonPos,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseUnaryExpr() ast.Expr {
|
||||
if p.trace {
|
||||
defer un(trace(p, "UnaryExpression"))
|
||||
|
|
48
compiler/parser/parser_cond_test.go
Normal file
48
compiler/parser/parser_cond_test.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package parser_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/d5/tengo/compiler/ast"
|
||||
)
|
||||
|
||||
func TestCondExpr(t *testing.T) {
|
||||
expect(t, "a ? b : c", func(p pfn) []ast.Stmt {
|
||||
return stmts(
|
||||
exprStmt(
|
||||
condExpr(
|
||||
ident("a", p(1, 1)),
|
||||
ident("b", p(1, 5)),
|
||||
ident("c", p(1, 9)),
|
||||
p(1, 3),
|
||||
p(1, 7))))
|
||||
})
|
||||
expect(t, `a ?
|
||||
b :
|
||||
c`, func(p pfn) []ast.Stmt {
|
||||
return stmts(
|
||||
exprStmt(
|
||||
condExpr(
|
||||
ident("a", p(1, 1)),
|
||||
ident("b", p(1, 5)),
|
||||
ident("c", p(1, 9)),
|
||||
p(1, 3),
|
||||
p(1, 7))))
|
||||
})
|
||||
|
||||
expectString(t, `a ? b : c`, "(a ? b : c)")
|
||||
expectString(t, `a + b ? c - d : e * f`, "((a + b) ? (c - d) : (e * f))")
|
||||
expectString(t, `a == b ? c + (d / e) : f ? g : h + i`, "((a == b) ? (c + ((d / e))) : (f ? g : (h + i)))")
|
||||
expectString(t, `(a + b) ? (c - d) : (e * f)`, "(((a + b)) ? ((c - d)) : ((e * f)))")
|
||||
expectString(t, `a + (b ? c : d) - e`, "((a + ((b ? c : d))) - e)")
|
||||
expectString(t, `a ? b ? c : d : e`, "(a ? (b ? c : d) : e)")
|
||||
expectString(t, `a := b ? c : d`, "a := (b ? c : d)")
|
||||
expectString(t, `x := a ? b ? c : d : e`, "x := (a ? (b ? c : d) : e)")
|
||||
|
||||
// ? : should be at the end of each line if it's multi-line
|
||||
expectError(t, `a
|
||||
? b
|
||||
: c`)
|
||||
expectError(t, `a ? (b : e)`)
|
||||
expectError(t, `(a ? b) : e`)
|
||||
}
|
|
@ -184,6 +184,10 @@ func binaryExpr(x, y ast.Expr, op token.Token, pos source.Pos) *ast.BinaryExpr {
|
|||
return &ast.BinaryExpr{LHS: x, RHS: y, Token: op, TokenPos: pos}
|
||||
}
|
||||
|
||||
func condExpr(cond, trueExpr, falseExpr ast.Expr, questionPos, colonPos source.Pos) *ast.CondExpr {
|
||||
return &ast.CondExpr{Cond: cond, True: trueExpr, False: falseExpr, QuestionPos: questionPos, ColonPos: colonPos}
|
||||
}
|
||||
|
||||
func unaryExpr(x ast.Expr, op token.Token, pos source.Pos) *ast.UnaryExpr {
|
||||
return &ast.UnaryExpr{Expr: x, Token: op, TokenPos: pos}
|
||||
}
|
||||
|
@ -398,6 +402,12 @@ func equalExpr(t *testing.T, expected, actual ast.Expr) bool {
|
|||
assert.Equal(t, int(expected.ErrorPos), int(actual.(*ast.ErrorExpr).ErrorPos)) &&
|
||||
assert.Equal(t, int(expected.LParen), int(actual.(*ast.ErrorExpr).LParen)) &&
|
||||
assert.Equal(t, int(expected.RParen), int(actual.(*ast.ErrorExpr).RParen))
|
||||
case *ast.CondExpr:
|
||||
return equalExpr(t, expected.Cond, actual.(*ast.CondExpr).Cond) &&
|
||||
equalExpr(t, expected.True, actual.(*ast.CondExpr).True) &&
|
||||
equalExpr(t, expected.False, actual.(*ast.CondExpr).False) &&
|
||||
assert.Equal(t, expected.QuestionPos, actual.(*ast.CondExpr).QuestionPos) &&
|
||||
assert.Equal(t, expected.ColonPos, actual.(*ast.CondExpr).ColonPos)
|
||||
default:
|
||||
panic(fmt.Errorf("unknown type: %T", expected))
|
||||
}
|
||||
|
|
|
@ -125,6 +125,8 @@ func (s *Scanner) Scan() (tok token.Token, literal string, pos source.Pos) {
|
|||
}
|
||||
case ',':
|
||||
tok = token.Comma
|
||||
case '?':
|
||||
tok = token.Question
|
||||
case ';':
|
||||
tok = token.Semicolon
|
||||
literal = ";"
|
||||
|
|
|
@ -64,6 +64,7 @@ const (
|
|||
RBrace // }
|
||||
Semicolon // ;
|
||||
Colon // :
|
||||
Question // ?
|
||||
_operatorEnd
|
||||
_keywordBeg
|
||||
Break
|
||||
|
@ -142,6 +143,7 @@ var tokens = [...]string{
|
|||
RBrace: "}",
|
||||
Semicolon: ";",
|
||||
Colon: ":",
|
||||
Question: "?",
|
||||
Break: "break",
|
||||
Case: "case",
|
||||
Continue: "continue",
|
||||
|
|
Loading…
Reference in a new issue