Parse let exprs with simple and variant patterns

This commit is contained in:
Brandon Dyck 2023-07-02 16:37:06 -06:00
parent ef3de7a707
commit b5bdd95605
5 changed files with 121 additions and 9 deletions

23
AST.cs
View File

@ -1,4 +1,5 @@
using System.CodeDom.Compiler; using System.CodeDom.Compiler;
using System.Collections.Generic;
namespace Finn.AST; namespace Finn.AST;
@ -38,3 +39,25 @@ public partial record Literal
return this.Value.ToString()!; return this.Value.ToString()!;
} }
} }
public record Binding(Pattern Pattern, Expr Value);
public partial record SimplePattern
{
public override string ToString()
{
if (this.Identifier == null)
{
return "_";
}
return this.Identifier.ToString();
}
}
public partial record Let
{
public override string ToString()
{
return $"Let {{ Bindings = {string.Join(", ", (IEnumerable<object?>)(this.Bindings))}, Body = {this.Body} }}";
}
}

View File

@ -24,6 +24,7 @@ public interface IExprVisitor<T> {
T visitSelectorExpr(Selector expr); T visitSelectorExpr(Selector expr);
T visitIndexerExpr(Indexer expr); T visitIndexerExpr(Indexer expr);
T visitCallExpr(Call expr); T visitCallExpr(Call expr);
T visitLetExpr(Let expr);
} }
public partial record Sequence(Expr Left, Expr Right) : Expr() public partial record Sequence(Expr Left, Expr Right) : Expr()
{ {
@ -116,3 +117,10 @@ public partial record Call(Expr Left, Expr?[] Arguments) : Expr()
return visitor.visitCallExpr(this); return visitor.visitCallExpr(this);
} }
} }
public partial record Let(Binding[] Bindings, Expr Body) : Expr()
{
public override T accept<T>(IExprVisitor<T> visitor)
{
return visitor.visitLetExpr(this);
}
}

View File

@ -158,9 +158,85 @@ class Parser
Expr right = unary(); Expr right = unary();
return new Unary(op, right); return new Unary(op, right);
} }
return let();
}
private RecordPattern? recordPattern()
{
if (!match(TokenType.LBrace))
{
return null;
}
throw new NotImplementedException("TODO record pattern");
}
private VariantPattern? variantPattern()
{
if (!match(TokenType.Backtick))
{
return null;
}
Name? tag = name();
if (tag == null)
{
throw error(peek(), "Expect identifier as tag name.");
}
if (!match(TokenType.LParen))
{
return new(tag, null);
}
Pattern argument = pattern();
consume(TokenType.RParen, "Expect ')' after variant argument.");
return new(tag, argument);
}
private Pattern pattern()
{
if (match(TokenType.Blank))
{
return new SimplePattern(null);
}
var identifier = name();
if (identifier != null)
{
return new SimplePattern(identifier);
}
Pattern? p = recordPattern();
if (p != null)
{
return p;
}
if ((p = variantPattern()) != null)
{
return p;
}
throw error(peek(), "Expect pattern after 'let'.");
}
private Expr let()
{
if (!match(TokenType.Let))
{
return control(); return control();
} }
List<Binding> bindings = new List<Binding>();
Pattern p = pattern();
consume(TokenType.Equal, "Expect '=' after pattern.");
bindings.Add(new(p, expression()));
while (match(TokenType.And))
{
p = pattern();
consume(TokenType.Equal, "Expect '=' after pattern.");
bindings.Add(new(p, expression()));
}
consume(TokenType.In, "Expect 'in' after let-bindings.");
Expr body = expression();
return new Let(bindings.ToArray(), body);
}
private Expr control() private Expr control()
{ {
switch (peek().type) switch (peek().type)
@ -322,7 +398,7 @@ class Parser
Expr? argument = null; Expr? argument = null;
if (tag == null) if (tag == null)
{ {
throw error(peek(), "Expect identifier after backtick."); throw error(peek(), "Expect identifier as tag name.");
} }
if (match(TokenType.LParen)) if (match(TokenType.LParen))
{ {

View File

@ -11,16 +11,16 @@ public abstract record Pattern() {
public abstract T accept<T>(IPatternVisitor<T> visitor); public abstract T accept<T>(IPatternVisitor<T> visitor);
} }
public interface IPatternVisitor<T> { public interface IPatternVisitor<T> {
T visitIdentifierPatternPattern(IdentifierPattern pattern); T visitSimplePatternPattern(SimplePattern pattern);
T visitVariantPatternPattern(VariantPattern pattern); T visitVariantPatternPattern(VariantPattern pattern);
T visitFieldPatternPattern(FieldPattern pattern); T visitFieldPatternPattern(FieldPattern pattern);
T visitRecordPatternPattern(RecordPattern pattern); T visitRecordPatternPattern(RecordPattern pattern);
} }
public partial record IdentifierPattern(Name? Identifier) : Pattern() public partial record SimplePattern(Name? Identifier) : Pattern()
{ {
public override T accept<T>(IPatternVisitor<T> visitor) public override T accept<T>(IPatternVisitor<T> visitor)
{ {
return visitor.visitIdentifierPatternPattern(this); return visitor.visitSimplePatternPattern(this);
} }
} }
public partial record VariantPattern(Name Tag, Pattern? Argument) : Pattern() public partial record VariantPattern(Name Tag, Pattern? Argument) : Pattern()
@ -37,7 +37,7 @@ public partial record FieldPattern(Name Name, Pattern? Pattern) : Pattern()
return visitor.visitFieldPatternPattern(this); return visitor.visitFieldPatternPattern(this);
} }
} }
public partial record RecordPattern(FieldPattern[] Fields, IdentifierPattern Rest) : Pattern() public partial record RecordPattern(FieldPattern[] Fields, SimplePattern? Rest) : Pattern()
{ {
public override T accept<T>(IPatternVisitor<T> visitor) public override T accept<T>(IPatternVisitor<T> visitor)
{ {

View File

@ -38,10 +38,15 @@ let exprTypes =
{ Name = "Indexer" { Name = "Indexer"
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr"; Name = "Index" } ] } Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr"; Name = "Index" } ] }
{ Name = "Call" { Name = "Call"
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr?[]"; Name = "Arguments" } ] } ] Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr?[]"; Name = "Arguments" } ] }
{ Name = "Let"
Fields =
[ { Type = "Binding[]"
Name = "Bindings" }
{ Type = "Expr"; Name = "Body" } ] } ]
let patternTypes = let patternTypes =
[ { Name = "IdentifierPattern" [ { Name = "SimplePattern"
Fields = [ { Type = "Name?"; Name = "Identifier" } ] } Fields = [ { Type = "Name?"; Name = "Identifier" } ] }
{ Name = "VariantPattern" { Name = "VariantPattern"
Fields = [ { Type = "Name"; Name = "Tag" }; { Type = "Pattern?"; Name = "Argument" } ] } Fields = [ { Type = "Name"; Name = "Tag" }; { Type = "Pattern?"; Name = "Argument" } ] }
@ -51,7 +56,7 @@ let patternTypes =
Fields = Fields =
[ { Type = "FieldPattern[]" [ { Type = "FieldPattern[]"
Name = "Fields" } Name = "Fields" }
{ Type = "IdentifierPattern" { Type = "SimplePattern?"
Name = "Rest" } ] } ] Name = "Rest" } ] } ]
let visitorMethod baseName t = $"visit{t.Name}{baseName}" let visitorMethod baseName t = $"visit{t.Name}{baseName}"