From 49ec2ce94c5998e2f3f592a2ce1fe4638c8f340d Mon Sep 17 00:00:00 2001 From: Olga Novgorodova Date: Wed, 4 Dec 2024 21:51:17 +0100 Subject: [PATCH] add if-else conditional --- docs/language-definition.md | 2 +- expr_test.go | 15 +++++++++++++++ parser/lexer/lexer_test.go | 36 ++++++++++++++++++++++++++++++++++++ parser/lexer/state.go | 4 +--- parser/parser.go | 22 ++++++++++++++++++++++ parser/parser_test.go | 11 +++++++++++ 6 files changed, 86 insertions(+), 4 deletions(-) diff --git a/docs/language-definition.md b/docs/language-definition.md index 9c277e9a..d0112a80 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) {