From d498cc627b87e0f6b48da82ca0f35240b7400cce Mon Sep 17 00:00:00 2001 From: Brandon Dyck Date: Wed, 5 Jul 2023 08:52:14 -0600 Subject: [PATCH] Parse when-expressions --- Expr.g.cs | 8 ++++++ Parser.cs | 69 +++++++++++++++++++++++++++++++++++++++---------- ast_classes.fsx | 4 ++- grammar.txt | 8 +++--- 4 files changed, 69 insertions(+), 20 deletions(-) diff --git a/Expr.g.cs b/Expr.g.cs index 82eabff..c7acb80 100644 --- a/Expr.g.cs +++ b/Expr.g.cs @@ -25,6 +25,7 @@ public interface IExprVisitor { T visitIndexerExpr(Indexer expr); T visitCallExpr(Call expr); T visitLetExpr(Let expr); + T visitWhenExpr(When expr); } public partial record Sequence(Expr Left, Expr Right) : Expr() { @@ -124,3 +125,10 @@ public partial record Let(Binding[] Bindings, Expr Body) : Expr() return visitor.visitLetExpr(this); } } +public partial record When(Expr Head, Binding[] Cases) : Expr() +{ + public override T accept(IExprVisitor visitor) + { + return visitor.visitWhenExpr(this); + } +} diff --git a/Parser.cs b/Parser.cs index 5762602..ec56bdb 100644 --- a/Parser.cs +++ b/Parser.cs @@ -245,9 +245,10 @@ class Parser { if (!match(TokenType.Let)) { - return control(); + return ifExpr(); } + // TODO Add function bindings. List bindings = new List(); Pattern p = pattern(); consume(TokenType.Equal, "Expect '=' after pattern."); @@ -263,23 +264,63 @@ class Parser return new Let(bindings.ToArray(), body); } - private Expr control() + private Expr ifExpr() { - switch (peek().type) + if (!match(TokenType.If)) { - case TokenType.If: - advance(); - Expr condition = expression(); - consume(TokenType.Then, "Expect 'then' after condition."); - Expr thenCase = expression(); - consume(TokenType.Else, "Expect 'else' after 'then' case."); - Expr elseCase = expression(); - return new If(condition, thenCase, elseCase); - case TokenType.When: - throw new NotImplementedException("TODO when"); + return when(); } + Expr condition = expression(); + consume(TokenType.Then, "Expect 'then' after condition."); + Expr thenCase = expression(); + consume(TokenType.Else, "Expect 'else' after 'then' case."); + Expr elseCase = expression(); + return new If(condition, thenCase, elseCase); + } - return primary(); + // private Expr control() + // { + // switch (peek().type) + // { + // case TokenType.If: + // advance(); + // Expr condition = expression(); + // consume(TokenType.Then, "Expect 'then' after condition."); + // Expr thenCase = expression(); + // consume(TokenType.Else, "Expect 'else' after 'then' case."); + // Expr elseCase = expression(); + // return new If(condition, thenCase, elseCase); + // case TokenType.When: + // throw new NotImplementedException("TODO when"); + // } + + // return primary(); + // } + + private Expr when() + { + if (!match(TokenType.When)) + { + return primary(); + } + Expr head = expression(); + consume(TokenType.Is, "Expect 'is' after expression."); + + List cases = new List(); + cases.Add(parseCase()); + while (match(TokenType.Comma)) + { + cases.Add(parseCase()); + } + return new When(head, cases.ToArray()); + + Binding parseCase() + { + Pattern pat = pattern(); + consume(TokenType.DoubleArrow, "Expect '=>' after pattern."); + Expr value = expression(); + return new(pat, value); + } } private Name? name() diff --git a/ast_classes.fsx b/ast_classes.fsx index 2cc9396..474211d 100644 --- a/ast_classes.fsx +++ b/ast_classes.fsx @@ -43,7 +43,9 @@ let exprTypes = Fields = [ { Type = "Binding[]" Name = "Bindings" } - { Type = "Expr"; Name = "Body" } ] } ] + { Type = "Expr"; Name = "Body" } ] } + { Name = "When" + Fields = [ { Type = "Expr"; Name = "Head" }; { Type = "Binding[]"; Name = "Cases" } ] } ] let patternTypes = [ { Name = "SimplePattern" diff --git a/grammar.txt b/grammar.txt index 783e62a..6e162fd 100644 --- a/grammar.txt +++ b/grammar.txt @@ -4,7 +4,8 @@ expression -> | unary | binary | let - | control + | if + | when | primary grouping -> "(" expression ")" ; @@ -19,11 +20,8 @@ record_pattern -> "{" ( identifier ( "=" pattern )? ( "," identifier ( "=" patte variant_pattern -> "`" identifier ( "(" ( "_" | compound_pattern | identifier ) ")" )? ; parameters -> "(" ( identifier ( "," identifier )* )?")" let -> "let" ( identifier parameters? | pattern ) "=" expression ( "and" ( identifier parameters? | pattern ) "=" expression )* "in" expression ; -control -> - | if - | when if -> "if" expression "then" expression "else" expression ; -when -> "when" expression "is" ( ( identifier | pattern ) "=>" expression )+ ; +when -> "when" expression "is" ( identifier | pattern ) "=>" expression ( "," ( identifier | pattern ) "=>" expression )* ; variant -> "`" identifier ( "(" expression ")" )? ; base_record -> expression ( "with" identifier "=" expression ( "," identifier "=" expression )* ","? )? record ->