diff --git a/AST.cs b/AST.cs index de46020..41efef7 100644 --- a/AST.cs +++ b/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()!; + } +} diff --git a/ASTPrinter.cs b/ASTPrinter.cs deleted file mode 100644 index 4e7ffdd..0000000 --- a/ASTPrinter.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System; -using System.IO; -using Finn.AST; - -namespace Finn; - -class ASTPrinter : IVisitor -{ - 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(); - } -} diff --git a/Expr.g.cs b/Expr.g.cs index b2c5169..d517e1a 100644 --- a/Expr.g.cs +++ b/Expr.g.cs @@ -7,7 +7,7 @@ namespace Finn.AST; -public abstract class Expr { +public abstract record Expr() { public abstract T accept(IVisitor visitor); } public interface IVisitor { @@ -25,116 +25,92 @@ public interface IVisitor { 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(IVisitor 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(IVisitor 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(IVisitor 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(IVisitor 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(IVisitor 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(IVisitor 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(IVisitor 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(IVisitor 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(IVisitor 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(IVisitor 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(IVisitor 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(IVisitor 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(IVisitor visitor) { return visitor.visitCallExpr(this); diff --git a/Parser.cs b/Parser.cs index a63ce43..20b4c84 100644 --- a/Parser.cs +++ b/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) { diff --git a/Program.cs b/Program.cs index cb8be40..da2f8e1 100644 --- a/Program.cs +++ b/Program.cs @@ -45,7 +45,7 @@ class Program return; } - Console.WriteLine(new ASTPrinter().print(expression)); + Console.WriteLine(expression); } static void runPrompt() diff --git a/ast_classes.fsx b/ast_classes.fsx index abe7d3d..e19018a 100644 --- a/ast_classes.fsx +++ b/ast_classes.fsx @@ -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(IVisitor 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(IVisitor visitor); }}\n"