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;
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 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;
public abstract class Expr {
public abstract record Expr() {
public abstract T accept<T>(IVisitor<T> visitor);
}
public interface IVisitor<T> {
@ -25,116 +25,92 @@ public interface IVisitor<T> {
T visitIndexerExpr(Indexer 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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
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)
{
return visitor.visitCallExpr(this);

View File

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

View File

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

View File

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