diff --git a/ast/node.go b/ast/node.go index f156f69b..f17fd7e5 100644 --- a/ast/node.go +++ b/ast/node.go @@ -200,12 +200,13 @@ type PointerNode struct { Name string // Name of the pointer. Like "index" in "#index". } -// ConditionalNode represents a ternary operator. +// ConditionalNode represents a ternary operator or if/else operator. type ConditionalNode struct { base - Cond Node // Condition of the ternary operator. Like "foo" in "foo ? bar : baz". - Exp1 Node // Expression 1 of the ternary operator. Like "bar" in "foo ? bar : baz". - Exp2 Node // Expression 2 of the ternary operator. Like "baz" in "foo ? bar : baz". + Ternary bool // Is it ternary or if/else operator? + Cond Node // Condition + Exp1 Node // Expression 1 + Exp2 Node // Expression 2 } // VariableDeclaratorNode represents a variable declaration. diff --git a/ast/print.go b/ast/print.go index e4e45f0f..527a5b99 100644 --- a/ast/print.go +++ b/ast/print.go @@ -207,6 +207,16 @@ func (n *SequenceNode) String() string { } func (n *ConditionalNode) String() string { + if !n.Ternary { + cond := n.Cond.String() + exp1 := n.Exp1.String() + if c2, ok := n.Exp2.(*ConditionalNode); ok && !c2.Ternary { + return fmt.Sprintf("if %s { %s } else %s", cond, exp1, c2.String()) + } + exp2 := n.Exp2.String() + return fmt.Sprintf("if %s { %s } else { %s }", cond, exp1, exp2) + } + var cond, exp1, exp2 string if _, ok := n.Cond.(*ConditionalNode); ok { cond = fmt.Sprintf("(%s)", n.Cond.String()) diff --git a/ast/print_test.go b/ast/print_test.go index d0859606..bcdad782 100644 --- a/ast/print_test.go +++ b/ast/print_test.go @@ -83,7 +83,8 @@ func TestPrint(t *testing.T) { {`(2 ** 2) ** 3`, `(2 ** 2) ** 3`}, {`(3 + 5) / (5 % 3)`, `(3 + 5) / (5 % 3)`}, {`(-(1+1)) == 2`, `-(1 + 1) == 2`}, - {`if true { 1 } else { 2 }`, `true ? 1 : 2`}, + {`if true { 1 } else { 2 }`, `if true { 1 } else { 2 }`}, + {`if true { 1 } else if false { 2 } else { 3 }`, `if true { 1 } else if false { 2 } else { 3 }`}, } for _, tt := range tests { diff --git a/expr_test.go b/expr_test.go index a8be9ef3..c923d5a4 100644 --- a/expr_test.go +++ b/expr_test.go @@ -1390,6 +1390,10 @@ func TestExpr(t *testing.T) { `if "a" < "b" {let x = "a"; x} else {"abc"}`, "a", }, + { + `if 1 == 2 { "no" } else if 1 == 1 { "yes" } else { "maybe" }`, + "yes", + }, { `1; 2; 3`, 3, diff --git a/parser/parser.go b/parser/parser.go index 623ab9a0..90daae43 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -334,9 +334,15 @@ func (p *Parser) parseConditionalIf() Node { expr1 := p.parseSequenceExpression() p.expect(Bracket, "}") p.expect(Operator, "else") - p.expect(Bracket, "{") - expr2 := p.parseSequenceExpression() - p.expect(Bracket, "}") + + var expr2 Node + if p.current.Is(Operator, "if") { + expr2 = p.parseConditionalIf() + } else { + p.expect(Bracket, "{") + expr2 = p.parseSequenceExpression() + p.expect(Bracket, "}") + } return &ConditionalNode{ Cond: nodeCondition, @@ -362,9 +368,10 @@ func (p *Parser) parseConditional(node Node) Node { } node = p.createNode(&ConditionalNode{ - Cond: node, - Exp1: expr1, - Exp2: expr2, + Ternary: true, + Cond: node, + Exp1: expr1, + Exp2: expr2, }, p.current.Location) if node == nil { return nil diff --git a/parser/parser_test.go b/parser/parser_test.go index 16dac6b7..85c46575 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -197,15 +197,19 @@ world`}, }, { "true ? true : false", - &ConditionalNode{Cond: &BoolNode{Value: true}, - Exp1: &BoolNode{Value: true}, - Exp2: &BoolNode{}}, + &ConditionalNode{ + Ternary: true, + Cond: &BoolNode{Value: true}, + Exp1: &BoolNode{Value: true}, + Exp2: &BoolNode{}}, }, { "a?[b]:c", - &ConditionalNode{Cond: &IdentifierNode{Value: "a"}, - Exp1: &ArrayNode{Nodes: []Node{&IdentifierNode{Value: "b"}}}, - Exp2: &IdentifierNode{Value: "c"}}, + &ConditionalNode{ + Ternary: true, + Cond: &IdentifierNode{Value: "a"}, + Exp1: &ArrayNode{Nodes: []Node{&IdentifierNode{Value: "b"}}}, + Exp2: &IdentifierNode{Value: "c"}}, }, { "a.b().c().d[33]", @@ -396,6 +400,7 @@ world`}, { "2==2 ? false : 3 not in [1, 2, 5]", &ConditionalNode{ + Ternary: true, Cond: &BinaryNode{ Operator: "==", Left: &IntegerNode{Value: 2}, @@ -659,6 +664,29 @@ world`}, Exp1: &BoolNode{Value: true}, Exp2: &IdentifierNode{Value: "x"}}, }, + { + "if a { 1 } else if b { 2 } else { 3 }", + &ConditionalNode{ + Cond: &IdentifierNode{Value: "a"}, + Exp1: &IntegerNode{Value: 1}, + Exp2: &ConditionalNode{ + Cond: &IdentifierNode{Value: "b"}, + Exp1: &IntegerNode{Value: 2}, + Exp2: &IntegerNode{Value: 3}}}, + }, + { + "if a { 1 } else if b { 2 } else if c { 3 } else { 4 }", + &ConditionalNode{ + Cond: &IdentifierNode{Value: "a"}, + Exp1: &IntegerNode{Value: 1}, + Exp2: &ConditionalNode{ + Cond: &IdentifierNode{Value: "b"}, + Exp1: &IntegerNode{Value: 2}, + Exp2: &ConditionalNode{ + Cond: &IdentifierNode{Value: "c"}, + Exp1: &IntegerNode{Value: 3}, + Exp2: &IntegerNode{Value: 4}}}}, + }, { "1; 2; 3", &SequenceNode{ @@ -687,9 +715,10 @@ world`}, &SequenceNode{ Nodes: []Node{ &ConditionalNode{ - Cond: &BoolNode{Value: true}, - Exp1: &IntegerNode{Value: 1}, - Exp2: &IntegerNode{Value: 2}}, + Ternary: true, + Cond: &BoolNode{Value: true}, + Exp1: &IntegerNode{Value: 1}, + Exp2: &IntegerNode{Value: 2}}, &IntegerNode{Value: 3}, &IntegerNode{Value: 4}, }, @@ -698,8 +727,9 @@ world`}, { "true ? 1 : ( 2; 3; 4 )", &ConditionalNode{ - Cond: &BoolNode{Value: true}, - Exp1: &IntegerNode{Value: 1}, + Ternary: true, + Cond: &BoolNode{Value: true}, + Exp1: &IntegerNode{Value: 1}, Exp2: &SequenceNode{ Nodes: []Node{ &IntegerNode{Value: 2}, @@ -714,9 +744,10 @@ world`}, &SequenceNode{ Nodes: []Node{ &ConditionalNode{ - Cond: &BoolNode{Value: true}, - Exp1: &BoolNode{Value: true}, - Exp2: &IntegerNode{Value: 1}}, + Ternary: true, + Cond: &BoolNode{Value: true}, + Exp1: &BoolNode{Value: true}, + Exp2: &IntegerNode{Value: 1}}, &IntegerNode{Value: 2}, &IntegerNode{Value: 3}, }, @@ -727,9 +758,10 @@ world`}, &VariableDeclaratorNode{ Name: "x", Value: &ConditionalNode{ - Cond: &BoolNode{Value: true}, - Exp1: &IntegerNode{Value: 1}, - Exp2: &IntegerNode{Value: 2}}, + Ternary: true, + Cond: &BoolNode{Value: true}, + Exp1: &IntegerNode{Value: 1}, + Exp2: &IntegerNode{Value: 2}}, Expr: &IdentifierNode{Value: "x"}}, }, { @@ -737,8 +769,9 @@ world`}, &VariableDeclaratorNode{ Name: "x", Value: &ConditionalNode{ - Cond: &BoolNode{Value: true}, - Exp1: &IntegerNode{Value: 1}, + Ternary: true, + Cond: &BoolNode{Value: true}, + Exp1: &IntegerNode{Value: 1}, Exp2: &SequenceNode{ Nodes: []Node{ &IntegerNode{Value: 2}, @@ -762,7 +795,8 @@ world`}, Nodes: []Node{ &IntegerNode{Value: 4}, &IntegerNode{Value: 5}, - &IntegerNode{Value: 6}}}}, + &IntegerNode{Value: 6}}}, + }, }, { `all(ls, if true { 1 } else { 2 })`, @@ -774,7 +808,8 @@ world`}, Node: &ConditionalNode{ Cond: &BoolNode{Value: true}, Exp1: &IntegerNode{Value: 1}, - Exp2: &IntegerNode{Value: 2}}}}}, + Exp2: &IntegerNode{Value: 2}, + }}}}, }, { `let x = if true { 1 } else { 2 }; x`, @@ -783,7 +818,8 @@ world`}, Value: &ConditionalNode{ Cond: &BoolNode{Value: true}, Exp1: &IntegerNode{Value: 1}, - Exp2: &IntegerNode{Value: 2}}, + Exp2: &IntegerNode{Value: 2}, + }, Expr: &IdentifierNode{Value: "x"}}, }, { @@ -794,7 +830,8 @@ world`}, &ConditionalNode{ Cond: &BoolNode{Value: true}, Exp1: &IntegerNode{Value: 1}, - Exp2: &IntegerNode{Value: 2}}}}, + Exp2: &IntegerNode{Value: 2}, + }}}, }, { `[if true { 1 } else { 2 }]`, @@ -803,7 +840,8 @@ world`}, &ConditionalNode{ Cond: &BoolNode{Value: true}, Exp1: &IntegerNode{Value: 1}, - Exp2: &IntegerNode{Value: 2}}}}, + Exp2: &IntegerNode{Value: 2}, + }}}, }, { `map(ls, { 1; 2; 3 })`, @@ -1019,6 +1057,12 @@ func TestParse_error(t *testing.T) { `unexpected token Operator("if") (1:5) | 1 + if true { 2 } else { 3 } | ....^`, + }, + { + `if a { 1 } else b`, + `unexpected token Identifier("b") (1:17) + | if a { 1 } else b + | ................^`, }, { `list | all(#,,)`,