Skip to content

Commit eb73788

Browse files
authored
Add else if support (#879)
* Add `else if` support * Add test for nested `else if` handling * Add `Ternary` field to `ConditionalNode` and update tests and printing logic
1 parent cf53913 commit eb73788

File tree

6 files changed

+102
-35
lines changed

6 files changed

+102
-35
lines changed

ast/node.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,12 +200,13 @@ type PointerNode struct {
200200
Name string // Name of the pointer. Like "index" in "#index".
201201
}
202202

203-
// ConditionalNode represents a ternary operator.
203+
// ConditionalNode represents a ternary operator or if/else operator.
204204
type ConditionalNode struct {
205205
base
206-
Cond Node // Condition of the ternary operator. Like "foo" in "foo ? bar : baz".
207-
Exp1 Node // Expression 1 of the ternary operator. Like "bar" in "foo ? bar : baz".
208-
Exp2 Node // Expression 2 of the ternary operator. Like "baz" in "foo ? bar : baz".
206+
Ternary bool // Is it ternary or if/else operator?
207+
Cond Node // Condition
208+
Exp1 Node // Expression 1
209+
Exp2 Node // Expression 2
209210
}
210211

211212
// VariableDeclaratorNode represents a variable declaration.

ast/print.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,16 @@ func (n *SequenceNode) String() string {
207207
}
208208

209209
func (n *ConditionalNode) String() string {
210+
if !n.Ternary {
211+
cond := n.Cond.String()
212+
exp1 := n.Exp1.String()
213+
if c2, ok := n.Exp2.(*ConditionalNode); ok && !c2.Ternary {
214+
return fmt.Sprintf("if %s { %s } else %s", cond, exp1, c2.String())
215+
}
216+
exp2 := n.Exp2.String()
217+
return fmt.Sprintf("if %s { %s } else { %s }", cond, exp1, exp2)
218+
}
219+
210220
var cond, exp1, exp2 string
211221
if _, ok := n.Cond.(*ConditionalNode); ok {
212222
cond = fmt.Sprintf("(%s)", n.Cond.String())

ast/print_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ func TestPrint(t *testing.T) {
8383
{`(2 ** 2) ** 3`, `(2 ** 2) ** 3`},
8484
{`(3 + 5) / (5 % 3)`, `(3 + 5) / (5 % 3)`},
8585
{`(-(1+1)) == 2`, `-(1 + 1) == 2`},
86-
{`if true { 1 } else { 2 }`, `true ? 1 : 2`},
86+
{`if true { 1 } else { 2 }`, `if true { 1 } else { 2 }`},
87+
{`if true { 1 } else if false { 2 } else { 3 }`, `if true { 1 } else if false { 2 } else { 3 }`},
8788
}
8889

8990
for _, tt := range tests {

expr_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,6 +1390,10 @@ func TestExpr(t *testing.T) {
13901390
`if "a" < "b" {let x = "a"; x} else {"abc"}`,
13911391
"a",
13921392
},
1393+
{
1394+
`if 1 == 2 { "no" } else if 1 == 1 { "yes" } else { "maybe" }`,
1395+
"yes",
1396+
},
13931397
{
13941398
`1; 2; 3`,
13951399
3,

parser/parser.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -334,9 +334,15 @@ func (p *Parser) parseConditionalIf() Node {
334334
expr1 := p.parseSequenceExpression()
335335
p.expect(Bracket, "}")
336336
p.expect(Operator, "else")
337-
p.expect(Bracket, "{")
338-
expr2 := p.parseSequenceExpression()
339-
p.expect(Bracket, "}")
337+
338+
var expr2 Node
339+
if p.current.Is(Operator, "if") {
340+
expr2 = p.parseConditionalIf()
341+
} else {
342+
p.expect(Bracket, "{")
343+
expr2 = p.parseSequenceExpression()
344+
p.expect(Bracket, "}")
345+
}
340346

341347
return &ConditionalNode{
342348
Cond: nodeCondition,
@@ -362,9 +368,10 @@ func (p *Parser) parseConditional(node Node) Node {
362368
}
363369

364370
node = p.createNode(&ConditionalNode{
365-
Cond: node,
366-
Exp1: expr1,
367-
Exp2: expr2,
371+
Ternary: true,
372+
Cond: node,
373+
Exp1: expr1,
374+
Exp2: expr2,
368375
}, p.current.Location)
369376
if node == nil {
370377
return nil

parser/parser_test.go

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -197,15 +197,19 @@ world`},
197197
},
198198
{
199199
"true ? true : false",
200-
&ConditionalNode{Cond: &BoolNode{Value: true},
201-
Exp1: &BoolNode{Value: true},
202-
Exp2: &BoolNode{}},
200+
&ConditionalNode{
201+
Ternary: true,
202+
Cond: &BoolNode{Value: true},
203+
Exp1: &BoolNode{Value: true},
204+
Exp2: &BoolNode{}},
203205
},
204206
{
205207
"a?[b]:c",
206-
&ConditionalNode{Cond: &IdentifierNode{Value: "a"},
207-
Exp1: &ArrayNode{Nodes: []Node{&IdentifierNode{Value: "b"}}},
208-
Exp2: &IdentifierNode{Value: "c"}},
208+
&ConditionalNode{
209+
Ternary: true,
210+
Cond: &IdentifierNode{Value: "a"},
211+
Exp1: &ArrayNode{Nodes: []Node{&IdentifierNode{Value: "b"}}},
212+
Exp2: &IdentifierNode{Value: "c"}},
209213
},
210214
{
211215
"a.b().c().d[33]",
@@ -396,6 +400,7 @@ world`},
396400
{
397401
"2==2 ? false : 3 not in [1, 2, 5]",
398402
&ConditionalNode{
403+
Ternary: true,
399404
Cond: &BinaryNode{
400405
Operator: "==",
401406
Left: &IntegerNode{Value: 2},
@@ -659,6 +664,29 @@ world`},
659664
Exp1: &BoolNode{Value: true},
660665
Exp2: &IdentifierNode{Value: "x"}},
661666
},
667+
{
668+
"if a { 1 } else if b { 2 } else { 3 }",
669+
&ConditionalNode{
670+
Cond: &IdentifierNode{Value: "a"},
671+
Exp1: &IntegerNode{Value: 1},
672+
Exp2: &ConditionalNode{
673+
Cond: &IdentifierNode{Value: "b"},
674+
Exp1: &IntegerNode{Value: 2},
675+
Exp2: &IntegerNode{Value: 3}}},
676+
},
677+
{
678+
"if a { 1 } else if b { 2 } else if c { 3 } else { 4 }",
679+
&ConditionalNode{
680+
Cond: &IdentifierNode{Value: "a"},
681+
Exp1: &IntegerNode{Value: 1},
682+
Exp2: &ConditionalNode{
683+
Cond: &IdentifierNode{Value: "b"},
684+
Exp1: &IntegerNode{Value: 2},
685+
Exp2: &ConditionalNode{
686+
Cond: &IdentifierNode{Value: "c"},
687+
Exp1: &IntegerNode{Value: 3},
688+
Exp2: &IntegerNode{Value: 4}}}},
689+
},
662690
{
663691
"1; 2; 3",
664692
&SequenceNode{
@@ -687,9 +715,10 @@ world`},
687715
&SequenceNode{
688716
Nodes: []Node{
689717
&ConditionalNode{
690-
Cond: &BoolNode{Value: true},
691-
Exp1: &IntegerNode{Value: 1},
692-
Exp2: &IntegerNode{Value: 2}},
718+
Ternary: true,
719+
Cond: &BoolNode{Value: true},
720+
Exp1: &IntegerNode{Value: 1},
721+
Exp2: &IntegerNode{Value: 2}},
693722
&IntegerNode{Value: 3},
694723
&IntegerNode{Value: 4},
695724
},
@@ -698,8 +727,9 @@ world`},
698727
{
699728
"true ? 1 : ( 2; 3; 4 )",
700729
&ConditionalNode{
701-
Cond: &BoolNode{Value: true},
702-
Exp1: &IntegerNode{Value: 1},
730+
Ternary: true,
731+
Cond: &BoolNode{Value: true},
732+
Exp1: &IntegerNode{Value: 1},
703733
Exp2: &SequenceNode{
704734
Nodes: []Node{
705735
&IntegerNode{Value: 2},
@@ -714,9 +744,10 @@ world`},
714744
&SequenceNode{
715745
Nodes: []Node{
716746
&ConditionalNode{
717-
Cond: &BoolNode{Value: true},
718-
Exp1: &BoolNode{Value: true},
719-
Exp2: &IntegerNode{Value: 1}},
747+
Ternary: true,
748+
Cond: &BoolNode{Value: true},
749+
Exp1: &BoolNode{Value: true},
750+
Exp2: &IntegerNode{Value: 1}},
720751
&IntegerNode{Value: 2},
721752
&IntegerNode{Value: 3},
722753
},
@@ -727,18 +758,20 @@ world`},
727758
&VariableDeclaratorNode{
728759
Name: "x",
729760
Value: &ConditionalNode{
730-
Cond: &BoolNode{Value: true},
731-
Exp1: &IntegerNode{Value: 1},
732-
Exp2: &IntegerNode{Value: 2}},
761+
Ternary: true,
762+
Cond: &BoolNode{Value: true},
763+
Exp1: &IntegerNode{Value: 1},
764+
Exp2: &IntegerNode{Value: 2}},
733765
Expr: &IdentifierNode{Value: "x"}},
734766
},
735767
{
736768
"let x = true ? 1 : ( 2; 3; 4 ); x",
737769
&VariableDeclaratorNode{
738770
Name: "x",
739771
Value: &ConditionalNode{
740-
Cond: &BoolNode{Value: true},
741-
Exp1: &IntegerNode{Value: 1},
772+
Ternary: true,
773+
Cond: &BoolNode{Value: true},
774+
Exp1: &IntegerNode{Value: 1},
742775
Exp2: &SequenceNode{
743776
Nodes: []Node{
744777
&IntegerNode{Value: 2},
@@ -762,7 +795,8 @@ world`},
762795
Nodes: []Node{
763796
&IntegerNode{Value: 4},
764797
&IntegerNode{Value: 5},
765-
&IntegerNode{Value: 6}}}},
798+
&IntegerNode{Value: 6}}},
799+
},
766800
},
767801
{
768802
`all(ls, if true { 1 } else { 2 })`,
@@ -774,7 +808,8 @@ world`},
774808
Node: &ConditionalNode{
775809
Cond: &BoolNode{Value: true},
776810
Exp1: &IntegerNode{Value: 1},
777-
Exp2: &IntegerNode{Value: 2}}}}},
811+
Exp2: &IntegerNode{Value: 2},
812+
}}}},
778813
},
779814
{
780815
`let x = if true { 1 } else { 2 }; x`,
@@ -783,7 +818,8 @@ world`},
783818
Value: &ConditionalNode{
784819
Cond: &BoolNode{Value: true},
785820
Exp1: &IntegerNode{Value: 1},
786-
Exp2: &IntegerNode{Value: 2}},
821+
Exp2: &IntegerNode{Value: 2},
822+
},
787823
Expr: &IdentifierNode{Value: "x"}},
788824
},
789825
{
@@ -794,7 +830,8 @@ world`},
794830
&ConditionalNode{
795831
Cond: &BoolNode{Value: true},
796832
Exp1: &IntegerNode{Value: 1},
797-
Exp2: &IntegerNode{Value: 2}}}},
833+
Exp2: &IntegerNode{Value: 2},
834+
}}},
798835
},
799836
{
800837
`[if true { 1 } else { 2 }]`,
@@ -803,7 +840,8 @@ world`},
803840
&ConditionalNode{
804841
Cond: &BoolNode{Value: true},
805842
Exp1: &IntegerNode{Value: 1},
806-
Exp2: &IntegerNode{Value: 2}}}},
843+
Exp2: &IntegerNode{Value: 2},
844+
}}},
807845
},
808846
{
809847
`map(ls, { 1; 2; 3 })`,
@@ -1019,6 +1057,12 @@ func TestParse_error(t *testing.T) {
10191057
`unexpected token Operator("if") (1:5)
10201058
| 1 + if true { 2 } else { 3 }
10211059
| ....^`,
1060+
},
1061+
{
1062+
`if a { 1 } else b`,
1063+
`unexpected token Identifier("b") (1:17)
1064+
| if a { 1 } else b
1065+
| ................^`,
10221066
},
10231067
{
10241068
`list | all(#,,)`,

0 commit comments

Comments
 (0)