Converted AST classes to records

This commit is contained in:
Brandon Dyck 2023-07-02 14:27:12 -06:00
parent 04689beec3
commit 82d911d5dd
6 changed files with 71 additions and 229 deletions

35
AST.cs
View File

@ -1,7 +1,40 @@
using System.CodeDom.Compiler;
namespace Finn.AST; namespace Finn.AST;
public record Name(string Value, bool Quoted); public record Name(string Value, bool Quoted)
{
public override string ToString()
{
if (this.Quoted) return $"@\"{this.Value}\"";
return this.Value;
}
};
public record Field(Name Name, Expr? Value); public record Field(Name Name, Expr? Value);
public record BaseRecord(Expr Value, Field[] Updates); public record BaseRecord(Expr Value, Field[] Updates);
public partial record Identifier
{
public override string ToString()
{
return this.Value.ToString();
}
}
public partial record Binary
{
public override string ToString()
{
return $"Binary {{ Left = {this.Left}, Op = {this.Op.lexeme}, Right = {this.Right} }}";
}
}
public partial record Literal
{
public override string ToString()
{
return this.Value.ToString()!;
}
}

View File

@ -1,153 +0,0 @@
using System;
using System.IO;
using Finn.AST;
namespace Finn;
class ASTPrinter : IVisitor<string>
{
public string print(Expr expr)
{
return expr.accept(this);
}
private string parenthesize(string name, params Expr[] exprs)
{
var w = new StringWriter();
w.Write($"({name}");
foreach (Expr expr in exprs)
{
w.Write(" ");
w.Write(expr.accept(this));
}
w.Write(")");
return w.ToString();
}
public string visitBinaryExpr(Binary expr)
{
return parenthesize(expr.Op.lexeme, expr.Left, expr.Right);
}
public string visitGroupingExpr(Grouping expr)
{
return parenthesize("group", expr.Expression);
}
public string visitLiteralExpr(Literal expr)
{
return expr.Value.ToString() ?? "";
}
private string formatName(Name name)
{
if (name.Quoted) return $"@\"{name.Value}\"";
return name.Value;
}
public string visitIdentifierExpr(Identifier expr)
{
return formatName(expr.Value);
}
public string visitUnaryExpr(Unary expr)
{
return parenthesize(expr.Op.lexeme, expr.Right);
}
public string visitIfExpr(If expr)
{
return parenthesize("if", expr.Condition, expr.Then, expr.Else);
}
public string visitSequenceExpr(Sequence expr)
{
return parenthesize("seq", expr.Left, expr.Right);
}
public string visitSelectorExpr(Selector expr)
{
return parenthesize(".", expr.Left, new Identifier { Value = expr.FieldName });
}
public string visitListExpr(List expr)
{
return parenthesize("list", expr.Elements);
}
public string visitVariantExpr(Variant expr)
{
if (expr.Argument == null)
{
return parenthesize("variant", new Identifier { Value = expr.Tag });
}
return parenthesize("variant", new Identifier { Value = expr.Tag }, expr.Argument);
}
public string visitRecordExpr(Record expr)
{
StringWriter w = new StringWriter();
w.Write("(record");
writeFields("extend", expr.Extensions);
if (expr.Base != null)
{
w.Write(" (base ");
w.Write(print(expr.Base.Value));
writeFields("update", expr.Base.Updates);
w.Write(')');
}
w.Write(')');
return w.ToString();
void writeFields(string header, Field[] fields)
{
if (fields.Length != 0)
{
w.Write($" ({header}");
foreach (var field in fields)
{
w.Write(' ');
var name = formatName(field.Name);
if (field.Value == null)
{
w.Write(name);
}
else
{
w.Write(parenthesize(name, field.Value));
}
}
w.Write(')');
}
}
}
public string visitIndexerExpr(Indexer expr)
{
return parenthesize("index", expr.Left, expr.Index);
}
public string visitCallExpr(Call expr)
{
var w = new StringWriter();
w.Write("(call ");
w.Write(print(expr.Left));
foreach (var arg in expr.Arguments)
{
if (arg == null)
{
w.Write(" _");
}
else
{
w.Write($" {print(arg)}");
}
}
w.Write(')');
return w.ToString();
}
}

View File

@ -7,7 +7,7 @@
namespace Finn.AST; namespace Finn.AST;
public abstract class Expr { public abstract record Expr() {
public abstract T accept<T>(IVisitor<T> visitor); public abstract T accept<T>(IVisitor<T> visitor);
} }
public interface IVisitor<T> { public interface IVisitor<T> {
@ -25,116 +25,92 @@ public interface IVisitor<T> {
T visitIndexerExpr(Indexer expr); T visitIndexerExpr(Indexer expr);
T visitCallExpr(Call expr); T visitCallExpr(Call expr);
} }
public class Sequence : Expr public partial record Sequence(Expr Left, Expr Right) : Expr()
{ {
public required Expr Left { get; init; }
public required Expr Right { get; init; }
public override T accept<T>(IVisitor<T> visitor) public override T accept<T>(IVisitor<T> visitor)
{ {
return visitor.visitSequenceExpr(this); return visitor.visitSequenceExpr(this);
} }
} }
public class Binary : Expr public partial record Binary(Expr Left, Token Op, Expr Right) : Expr()
{ {
public required Expr Left { get; init; }
public required Token Op { get; init; }
public required Expr Right { get; init; }
public override T accept<T>(IVisitor<T> visitor) public override T accept<T>(IVisitor<T> visitor)
{ {
return visitor.visitBinaryExpr(this); return visitor.visitBinaryExpr(this);
} }
} }
public class Grouping : Expr public partial record Grouping(Expr Expression) : Expr()
{ {
public required Expr Expression { get; init; }
public override T accept<T>(IVisitor<T> visitor) public override T accept<T>(IVisitor<T> visitor)
{ {
return visitor.visitGroupingExpr(this); return visitor.visitGroupingExpr(this);
} }
} }
public class Literal : Expr public partial record Literal(System.Object Value) : Expr()
{ {
public required System.Object Value { get; init; }
public override T accept<T>(IVisitor<T> visitor) public override T accept<T>(IVisitor<T> visitor)
{ {
return visitor.visitLiteralExpr(this); return visitor.visitLiteralExpr(this);
} }
} }
public class Unary : Expr public partial record Unary(Token Op, Expr Right) : Expr()
{ {
public required Token Op { get; init; }
public required Expr Right { get; init; }
public override T accept<T>(IVisitor<T> visitor) public override T accept<T>(IVisitor<T> visitor)
{ {
return visitor.visitUnaryExpr(this); return visitor.visitUnaryExpr(this);
} }
} }
public class If : Expr public partial record If(Expr Condition, Expr Then, Expr Else) : Expr()
{ {
public required Expr Condition { get; init; }
public required Expr Then { get; init; }
public required Expr Else { get; init; }
public override T accept<T>(IVisitor<T> visitor) public override T accept<T>(IVisitor<T> visitor)
{ {
return visitor.visitIfExpr(this); return visitor.visitIfExpr(this);
} }
} }
public class Identifier : Expr public partial record Identifier(Name Value) : Expr()
{ {
public required Name Value { get; init; }
public override T accept<T>(IVisitor<T> visitor) public override T accept<T>(IVisitor<T> visitor)
{ {
return visitor.visitIdentifierExpr(this); return visitor.visitIdentifierExpr(this);
} }
} }
public class List : Expr public partial record List(Expr[] Elements) : Expr()
{ {
public required Expr[] Elements { get; init; }
public override T accept<T>(IVisitor<T> visitor) public override T accept<T>(IVisitor<T> visitor)
{ {
return visitor.visitListExpr(this); return visitor.visitListExpr(this);
} }
} }
public class Variant : Expr public partial record Variant(Name Tag, Expr? Argument) : Expr()
{ {
public required Name Tag { get; init; }
public required Expr? Argument { get; init; }
public override T accept<T>(IVisitor<T> visitor) public override T accept<T>(IVisitor<T> visitor)
{ {
return visitor.visitVariantExpr(this); return visitor.visitVariantExpr(this);
} }
} }
public class Record : Expr public partial record Record(Field[] Extensions, BaseRecord? Base) : Expr()
{ {
public required Field[] Extensions { get; init; }
public required BaseRecord? Base { get; init; }
public override T accept<T>(IVisitor<T> visitor) public override T accept<T>(IVisitor<T> visitor)
{ {
return visitor.visitRecordExpr(this); return visitor.visitRecordExpr(this);
} }
} }
public class Selector : Expr public partial record Selector(Expr Left, Name FieldName) : Expr()
{ {
public required Expr Left { get; init; }
public required Name FieldName { get; init; }
public override T accept<T>(IVisitor<T> visitor) public override T accept<T>(IVisitor<T> visitor)
{ {
return visitor.visitSelectorExpr(this); return visitor.visitSelectorExpr(this);
} }
} }
public class Indexer : Expr public partial record Indexer(Expr Left, Expr Index) : Expr()
{ {
public required Expr Left { get; init; }
public required Expr Index { get; init; }
public override T accept<T>(IVisitor<T> visitor) public override T accept<T>(IVisitor<T> visitor)
{ {
return visitor.visitIndexerExpr(this); return visitor.visitIndexerExpr(this);
} }
} }
public class Call : Expr public partial record Call(Expr Left, Expr?[] Arguments) : Expr()
{ {
public required Expr Left { get; init; }
public required Expr?[] Arguments { get; init; }
public override T accept<T>(IVisitor<T> visitor) public override T accept<T>(IVisitor<T> visitor)
{ {
return visitor.visitCallExpr(this); return visitor.visitCallExpr(this);

View File

@ -119,7 +119,7 @@ class Parser
{ {
Token op = previous(); Token op = previous();
Expr right = next(); Expr right = next();
expr = new Binary { Left = expr, Op = op, Right = right }; expr = new Binary(expr, op, right);
} }
return expr; return expr;
@ -133,23 +133,13 @@ class Parser
while (match(TokenType.Semicolon)) while (match(TokenType.Semicolon))
{ {
var right = equality(); var right = equality();
expr = new Sequence { Left = expr, Right = right }; expr = new Sequence(expr, right);
} }
return expr; return expr;
} }
private Expr equality() private Expr equality() =>
{ binaryLeft(comparison, TokenType.BangEqual, TokenType.DoubleEqual);
Expr expr = comparison();
while (match(TokenType.BangEqual, TokenType.DoubleEqual))
{
Token op = previous();
Expr right = comparison();
expr = new Binary { Left = expr, Op = op, Right = right };
}
return expr;
}
private Expr comparison() => private Expr comparison() =>
binaryLeft(sum, TokenType.Greater, TokenType.GreaterEqual, TokenType.Less, TokenType.LessEqual); binaryLeft(sum, TokenType.Greater, TokenType.GreaterEqual, TokenType.Less, TokenType.LessEqual);
@ -166,7 +156,7 @@ class Parser
{ {
Token op = previous(); Token op = previous();
Expr right = unary(); Expr right = unary();
return new Unary { Op = op, Right = right }; return new Unary(op, right);
} }
return control(); return control();
} }
@ -182,7 +172,7 @@ class Parser
Expr thenCase = expression(); Expr thenCase = expression();
consume(TokenType.Else, "Expect 'else' after 'then' case."); consume(TokenType.Else, "Expect 'else' after 'then' case.");
Expr elseCase = expression(); Expr elseCase = expression();
return new If { Condition = condition, Then = thenCase, Else = elseCase }; return new If(condition, thenCase, elseCase);
case TokenType.When: case TokenType.When:
throw new NotImplementedException("TODO when"); throw new NotImplementedException("TODO when");
} }
@ -215,14 +205,14 @@ class Parser
{ {
throw error(advance(), "Expect identifier after dot."); throw error(advance(), "Expect identifier after dot.");
} }
return new Selector { Left = expr, FieldName = ident }; return new Selector(expr, ident);
} }
if (match(TokenType.LBracket)) if (match(TokenType.LBracket))
{ {
var index = expression(); var index = expression();
consume(TokenType.RBracket, "Expect '[' after expression."); consume(TokenType.RBracket, "Expect '[' after expression.");
return new Indexer { Left = expr, Index = index }; return new Indexer(expr, index);
} }
if (match(TokenType.LParen)) if (match(TokenType.LParen))
@ -244,7 +234,7 @@ class Parser
} }
} }
consume(TokenType.RParen, "Expect '(' after arguments."); consume(TokenType.RParen, "Expect '(' after arguments.");
return new Call { Left = expr, Arguments = args.ToArray() }; return new Call(expr, args.ToArray());
} }
return expr; return expr;
@ -255,20 +245,20 @@ class Parser
if (match(TokenType.Number, TokenType.String)) if (match(TokenType.Number, TokenType.String))
{ {
object literal = previous().literal!; object literal = previous().literal!;
return new Literal { Value = previous().literal! }; return new Literal(previous().literal!);
} }
var ident = name(); var ident = name();
if (ident != null) if (ident != null)
{ {
return new Identifier { Value = ident }; return new Identifier(ident);
} }
if (match(TokenType.LParen)) if (match(TokenType.LParen))
{ {
Expr groupedExpr = expression(); Expr groupedExpr = expression();
consume(TokenType.RParen, "Expect ')' after expression."); consume(TokenType.RParen, "Expect ')' after expression.");
return new Grouping { Expression = groupedExpr }; return new Grouping(groupedExpr);
} }
Expr? expr; Expr? expr;
@ -319,7 +309,7 @@ class Parser
} }
} }
} }
return new Finn.AST.List { Elements = elements.ToArray() }; return new Finn.AST.List(elements.ToArray());
} }
private Expr? variant() private Expr? variant()
@ -342,7 +332,7 @@ class Parser
consume(TokenType.RParen, "Expect ')' after variant argument."); consume(TokenType.RParen, "Expect ')' after variant argument.");
} }
} }
return new Variant { Tag = tag, Argument = argument }; return new Variant(tag, argument);
} }
private Expr? record() private Expr? record()
@ -363,11 +353,7 @@ class Parser
} }
consume(TokenType.RBrace, "Expect '}' at end of record literal."); consume(TokenType.RBrace, "Expect '}' at end of record literal.");
return new Record return new Record(extensions, baseRecord);
{
Extensions = extensions,
Base = baseRecord,
};
Field[] parseFields(params TokenType[] endAt) Field[] parseFields(params TokenType[] endAt)
{ {

View File

@ -45,7 +45,7 @@ class Program
return; return;
} }
Console.WriteLine(new ASTPrinter().print(expression)); Console.WriteLine(expression);
} }
static void runPrompt() static void runPrompt()

View File

@ -51,13 +51,13 @@ let renderIVisitor (sw: System.IO.StreamWriter) (baseName: string) types =
sw.Write("}\n") sw.Write("}\n")
let renderType (sw: System.IO.StreamWriter) baseName t = let renderType (sw: System.IO.StreamWriter) baseName t =
sw.Write let writeField i field =
$"public class {t.Name} : {baseName} if i > 0 then sw.Write(", ")
sw.Write $"{field.Type} {field.Name}"
sw.Write $"public partial record {t.Name}("
List.iteri writeField t.Fields
sw.Write $") : {baseName}()
{{\n" {{\n"
for f in t.Fields do
sw.Write $"\tpublic required {f.Type} {f.Name} {{ get; init; }}\n"
sw.Write sw.Write
$"\tpublic override T accept<T>(IVisitor<T> visitor) $"\tpublic override T accept<T>(IVisitor<T> visitor)
\t{{ \t{{
@ -80,7 +80,7 @@ let renderAST outputDir baseName types =
namespace Finn.AST; namespace Finn.AST;
public abstract class {baseName} {{ public abstract record {baseName}() {{
\tpublic abstract T accept<T>(IVisitor<T> visitor); \tpublic abstract T accept<T>(IVisitor<T> visitor);
}}\n" }}\n"