Parse record literals
This commit is contained in:
parent
5b0ca1ca6d
commit
63edbae650
4
AST.cs
4
AST.cs
@ -1,3 +1,7 @@
|
||||
namespace Finn.AST;
|
||||
|
||||
public record Name(string Value, bool Quoted);
|
||||
|
||||
public record Field(Name Name, Expr? Value);
|
||||
|
||||
public record BaseRecord(Expr Value, Field[] Updates);
|
||||
|
@ -83,4 +83,46 @@ class ASTPrinter : IVisitor<string>
|
||||
}
|
||||
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(')');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
Expr.g.cs
10
Expr.g.cs
@ -20,6 +20,7 @@ public interface IVisitor<T> {
|
||||
T visitIdentifierExpr(Identifier expr);
|
||||
T visitListExpr(List expr);
|
||||
T visitVariantExpr(Variant expr);
|
||||
T visitRecordExpr(Record expr);
|
||||
T visitSelectorExpr(Selector expr);
|
||||
}
|
||||
public class Sequence : Expr
|
||||
@ -101,6 +102,15 @@ public class Variant : Expr
|
||||
return visitor.visitVariantExpr(this);
|
||||
}
|
||||
}
|
||||
public class Record : 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 required Expr Left { get; init; }
|
||||
|
47
Parser.cs
47
Parser.cs
@ -44,10 +44,10 @@ class Parser
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool check(TokenType type)
|
||||
private bool check(params TokenType[] types)
|
||||
{
|
||||
if (isAtEnd()) return false;
|
||||
return peek().type == type;
|
||||
return Array.IndexOf(types, peek().type) >= 0;
|
||||
}
|
||||
|
||||
private Token advance()
|
||||
@ -326,8 +326,49 @@ class Parser
|
||||
}
|
||||
|
||||
private Expr? record()
|
||||
{
|
||||
if (!match(TokenType.LBrace))
|
||||
{
|
||||
return null;
|
||||
// TODO record
|
||||
}
|
||||
|
||||
var extensions = parseFields(TokenType.RBrace, TokenType.Pipe);
|
||||
BaseRecord? baseRecord = null;
|
||||
|
||||
if (match(TokenType.Pipe))
|
||||
{
|
||||
var baseExpr = expression();
|
||||
var updates = match(TokenType.With) ? parseFields(TokenType.RBrace) : Array.Empty<Field>();
|
||||
baseRecord = new(baseExpr, updates);
|
||||
}
|
||||
|
||||
consume(TokenType.RBrace, "Expect '}' at end of record literal.");
|
||||
return new Record
|
||||
{
|
||||
Extensions = extensions,
|
||||
Base = baseRecord,
|
||||
};
|
||||
|
||||
Field[] parseFields(params TokenType[] endAt)
|
||||
{
|
||||
List<Field> fields = new List<Field>();
|
||||
|
||||
while (!check(endAt))
|
||||
{
|
||||
var fieldName = name();
|
||||
if (fieldName == null)
|
||||
{
|
||||
throw error(peek(), "Expect identifier as field name.");
|
||||
}
|
||||
var value = match(TokenType.Equal) ? expression() : null;
|
||||
fields.Add(new(fieldName, value));
|
||||
if (!match(TokenType.Comma))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return fields.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,11 @@ class Program
|
||||
Parser parser = new Parser(tokens);
|
||||
Expr? expression = parser.parse();
|
||||
|
||||
if (hadError || expression == null) return;
|
||||
if (hadError || expression == null)
|
||||
{
|
||||
hadError = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine(new ASTPrinter().print(expression));
|
||||
}
|
||||
|
@ -28,6 +28,10 @@ let types =
|
||||
Fields = [ { Type = "Expr[]"; Name = "Elements" } ] }
|
||||
{ Name = "Variant"
|
||||
Fields = [ { Type = "Name"; Name = "Tag" }; { Type = "Expr?"; Name = "Argument" } ] }
|
||||
{ Name = "Record"
|
||||
Fields =
|
||||
[ { Type = "Field[]"; Name = "Extensions" }
|
||||
{ Type = "BaseRecord?"; Name = "Base" } ] }
|
||||
{ Name = "Selector"
|
||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Name"; Name = "FieldName" } ] } ]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user