Converted AST classes to records
This commit is contained in:
parent
04689beec3
commit
82d911d5dd
35
AST.cs
35
AST.cs
@ -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()!;
|
||||
}
|
||||
}
|
||||
|
153
ASTPrinter.cs
153
ASTPrinter.cs
@ -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();
|
||||
}
|
||||
}
|
52
Expr.g.cs
52
Expr.g.cs
@ -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);
|
||||
|
44
Parser.cs
44
Parser.cs
@ -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)
|
||||
{
|
||||
|
@ -45,7 +45,7 @@ class Program
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine(new ASTPrinter().print(expression));
|
||||
Console.WriteLine(expression);
|
||||
}
|
||||
|
||||
static void runPrompt()
|
||||
|
@ -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"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user