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;
|
namespace Finn.AST;
|
||||||
|
|
||||||
public record Name(string Value, bool Quoted);
|
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);
|
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 visitIdentifierExpr(Identifier expr);
|
||||||
T visitListExpr(List expr);
|
T visitListExpr(List expr);
|
||||||
T visitVariantExpr(Variant expr);
|
T visitVariantExpr(Variant expr);
|
||||||
|
T visitRecordExpr(Record expr);
|
||||||
T visitSelectorExpr(Selector expr);
|
T visitSelectorExpr(Selector expr);
|
||||||
}
|
}
|
||||||
public class Sequence : Expr
|
public class Sequence : Expr
|
||||||
@ -101,6 +102,15 @@ public class Variant : Expr
|
|||||||
return visitor.visitVariantExpr(this);
|
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 class Selector : Expr
|
||||||
{
|
{
|
||||||
public required Expr Left { get; init; }
|
public required Expr Left { get; init; }
|
||||||
|
47
Parser.cs
47
Parser.cs
@ -44,10 +44,10 @@ class Parser
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool check(TokenType type)
|
private bool check(params TokenType[] types)
|
||||||
{
|
{
|
||||||
if (isAtEnd()) return false;
|
if (isAtEnd()) return false;
|
||||||
return peek().type == type;
|
return Array.IndexOf(types, peek().type) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Token advance()
|
private Token advance()
|
||||||
@ -326,8 +326,49 @@ class Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Expr? record()
|
private Expr? record()
|
||||||
|
{
|
||||||
|
if (!match(TokenType.LBrace))
|
||||||
{
|
{
|
||||||
return null;
|
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);
|
Parser parser = new Parser(tokens);
|
||||||
Expr? expression = parser.parse();
|
Expr? expression = parser.parse();
|
||||||
|
|
||||||
if (hadError || expression == null) return;
|
if (hadError || expression == null)
|
||||||
|
{
|
||||||
|
hadError = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine(new ASTPrinter().print(expression));
|
Console.WriteLine(new ASTPrinter().print(expression));
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,10 @@ let types =
|
|||||||
Fields = [ { Type = "Expr[]"; Name = "Elements" } ] }
|
Fields = [ { Type = "Expr[]"; Name = "Elements" } ] }
|
||||||
{ Name = "Variant"
|
{ Name = "Variant"
|
||||||
Fields = [ { Type = "Name"; Name = "Tag" }; { Type = "Expr?"; Name = "Argument" } ] }
|
Fields = [ { Type = "Name"; Name = "Tag" }; { Type = "Expr?"; Name = "Argument" } ] }
|
||||||
|
{ Name = "Record"
|
||||||
|
Fields =
|
||||||
|
[ { Type = "Field[]"; Name = "Extensions" }
|
||||||
|
{ Type = "BaseRecord?"; Name = "Base" } ] }
|
||||||
{ Name = "Selector"
|
{ Name = "Selector"
|
||||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Name"; Name = "FieldName" } ] } ]
|
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Name"; Name = "FieldName" } ] } ]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user