diff --git a/docs/language-definition.md b/docs/language-definition.md
index 2ceeab72..640c7d63 100644
--- a/docs/language-definition.md
+++ b/docs/language-definition.md
@@ -97,7 +97,7 @@ Backticks strings are raw strings, they do not support escape sequences.
Conditional |
- ?: (ternary), ?? (nil coalescing)
+ ?: (ternary), ?? (nil coalescing), if {} else {} (multiline)
|
diff --git a/expr_test.go b/expr_test.go
index ced182a9..bda5551c 100644
--- a/expr_test.go
+++ b/expr_test.go
@@ -1291,6 +1291,21 @@ func TestExpr(t *testing.T) {
`1 < 2 < 3 == true`,
true,
},
+ {
+ `if 1 > 2 { 333 * 2 + 1 } else { 444 }`,
+ 444,
+ },
+ {
+ `let a = 3;
+ let b = 2;
+ if a>b {let c = Add(a, b); c+1} else {Add(10, b)}
+ `,
+ 6,
+ },
+ {
+ `if "a" < "b" {let x = "a"; x} else {"abc"}`,
+ "a",
+ },
}
for _, tt := range tests {
diff --git a/parser/lexer/lexer_test.go b/parser/lexer/lexer_test.go
index 5edcbe5c..f1cb3f28 100644
--- a/parser/lexer/lexer_test.go
+++ b/parser/lexer/lexer_test.go
@@ -239,6 +239,42 @@ func TestLex(t *testing.T) {
{Kind: EOF},
},
},
+ {
+ `if a>b {x1+x2} else {x2}`,
+ []Token{
+ {Kind: Operator, Value: "if"},
+ {Kind: Identifier, Value: "a"},
+ {Kind: Operator, Value: ">"},
+ {Kind: Identifier, Value: "b"},
+ {Kind: Bracket, Value: "{"},
+ {Kind: Identifier, Value: "x1"},
+ {Kind: Operator, Value: "+"},
+ {Kind: Identifier, Value: "x2"},
+ {Kind: Bracket, Value: "}"},
+ {Kind: Operator, Value: "else"},
+ {Kind: Bracket, Value: "{"},
+ {Kind: Identifier, Value: "x2"},
+ {Kind: Bracket, Value: "}"},
+ {Kind: EOF},
+ },
+ },
+ {
+ `a>b if {x1} else {x2}`,
+ []Token{
+ {Kind: Identifier, Value: "a"},
+ {Kind: Operator, Value: ">"},
+ {Kind: Identifier, Value: "b"},
+ {Kind: Operator, Value: "if"},
+ {Kind: Bracket, Value: "{"},
+ {Kind: Identifier, Value: "x1"},
+ {Kind: Bracket, Value: "}"},
+ {Kind: Operator, Value: "else"},
+ {Kind: Bracket, Value: "{"},
+ {Kind: Identifier, Value: "x2"},
+ {Kind: Bracket, Value: "}"},
+ {Kind: EOF},
+ },
+ },
}
for _, test := range tests {
diff --git a/parser/lexer/state.go b/parser/lexer/state.go
index d351e2f5..c694a2ca 100644
--- a/parser/lexer/state.go
+++ b/parser/lexer/state.go
@@ -129,9 +129,7 @@ loop:
switch l.word() {
case "not":
return not
- case "in", "or", "and", "matches", "contains", "startsWith", "endsWith":
- l.emit(Operator)
- case "let":
+ case "in", "or", "and", "matches", "contains", "startsWith", "endsWith", "let", "if", "else":
l.emit(Operator)
default:
l.emit(Identifier)
diff --git a/parser/parser.go b/parser/parser.go
index 0817f6e4..917e0db4 100644
--- a/parser/parser.go
+++ b/parser/parser.go
@@ -132,6 +132,9 @@ func (p *parser) parseExpression(precedence int) Node {
if precedence == 0 && p.current.Is(Operator, "let") {
return p.parseVariableDeclaration()
}
+ if p.current.Is(Operator, "if") {
+ return p.parseConditionalIf()
+ }
nodeLeft := p.parsePrimary()
@@ -235,6 +238,25 @@ func (p *parser) parseVariableDeclaration() Node {
return let
}
+func (p *parser) parseConditionalIf() Node {
+ p.next()
+ nodeCondition := p.parseExpression(0)
+ p.expect(Bracket, "{")
+ expr1 := p.parseExpression(0)
+ p.expect(Bracket, "}")
+ p.expect(Operator, "else")
+ p.expect(Bracket, "{")
+ expr2 := p.parseExpression(0)
+ p.expect(Bracket, "}")
+
+ return &ConditionalNode{
+ Cond: nodeCondition,
+ Exp1: expr1,
+ Exp2: expr2,
+ }
+
+}
+
func (p *parser) parseConditional(node Node) Node {
var expr1, expr2 Node
for p.current.Is(Operator, "?") && p.err == nil {
diff --git a/parser/parser_test.go b/parser/parser_test.go
index a280bf39..6bb17e60 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -647,6 +647,17 @@ world`},
Right: &BoolNode{Value: true},
},
},
+ {
+ "if a>b {true} else {x}",
+ &ConditionalNode{
+ Cond: &BinaryNode{
+ Operator: ">",
+ Left: &IdentifierNode{Value: "a"},
+ Right: &IdentifierNode{Value: "b"},
+ },
+ Exp1: &BoolNode{Value: true},
+ Exp2: &IdentifierNode{Value: "x"}},
+ },
}
for _, test := range tests {
t.Run(test.input, func(t *testing.T) {