Added identifiers and field selectors to grammar
This commit is contained in:
parent
9b2f0cb968
commit
6f3c82cad1
3
AST.cs
Normal file
3
AST.cs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
namespace Finn.AST;
|
||||||
|
|
||||||
|
public record Name(string Value, bool Quoted);
|
@ -39,6 +39,17 @@ class ASTPrinter : IVisitor<string>
|
|||||||
return expr.Value.ToString() ?? "";
|
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)
|
public string visitUnaryExpr(Unary expr)
|
||||||
{
|
{
|
||||||
return parenthesize(expr.Op.lexeme, expr.Right);
|
return parenthesize(expr.Op.lexeme, expr.Right);
|
||||||
@ -53,4 +64,9 @@ class ASTPrinter : IVisitor<string>
|
|||||||
{
|
{
|
||||||
return parenthesize("seq", expr.Left, expr.Right);
|
return parenthesize("seq", expr.Left, expr.Right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string visitSelectorExpr(Selector expr)
|
||||||
|
{
|
||||||
|
return parenthesize(".", expr.Left, new Identifier { Value = expr.FieldName });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
19
Expr.g.cs
19
Expr.g.cs
@ -16,6 +16,8 @@ public interface IVisitor<T> {
|
|||||||
T visitLiteralExpr(Literal expr);
|
T visitLiteralExpr(Literal expr);
|
||||||
T visitUnaryExpr(Unary expr);
|
T visitUnaryExpr(Unary expr);
|
||||||
T visitIfExpr(If expr);
|
T visitIfExpr(If expr);
|
||||||
|
T visitIdentifierExpr(Identifier expr);
|
||||||
|
T visitSelectorExpr(Selector expr);
|
||||||
}
|
}
|
||||||
public class Sequence : Expr
|
public class Sequence : Expr
|
||||||
{
|
{
|
||||||
@ -71,3 +73,20 @@ public class If : Expr
|
|||||||
return visitor.visitIfExpr(this);
|
return visitor.visitIfExpr(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public class Identifier : Expr
|
||||||
|
{
|
||||||
|
public required Name Value { get; init; }
|
||||||
|
public override T accept<T>(IVisitor<T> visitor)
|
||||||
|
{
|
||||||
|
return visitor.visitIdentifierExpr(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class Selector : 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
58
Parser.cs
58
Parser.cs
@ -72,8 +72,16 @@ class Parser
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Token consume(TokenType type, string message)
|
private Token consume(TokenType type, string message)
|
||||||
|
{
|
||||||
|
return consume(new TokenType[] { type }, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Token consume(TokenType[] types, string message)
|
||||||
|
{
|
||||||
|
foreach (var type in types)
|
||||||
{
|
{
|
||||||
if (check(type)) return advance();
|
if (check(type)) return advance();
|
||||||
|
}
|
||||||
|
|
||||||
throw error(peek(), message);
|
throw error(peek(), message);
|
||||||
}
|
}
|
||||||
@ -160,7 +168,7 @@ class Parser
|
|||||||
Expr right = unary();
|
Expr right = unary();
|
||||||
return new Unary { Op = op, Right = right };
|
return new Unary { Op = op, Right = right };
|
||||||
}
|
}
|
||||||
return ifExpr();
|
return control();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expr control()
|
private Expr control()
|
||||||
@ -182,28 +190,56 @@ class Parser
|
|||||||
return primary();
|
return primary();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expr ifExpr()
|
private Name? name()
|
||||||
{
|
{
|
||||||
if (match(TokenType.If))
|
if (match(TokenType.Identifier))
|
||||||
{
|
{
|
||||||
Expr condition = expression();
|
return new(previous().lexeme, false);
|
||||||
consume(TokenType.Then, "Expect 'then' after condition.");
|
|
||||||
Expr thenCase = expression();
|
|
||||||
consume(TokenType.Else, "Expect 'else' after 'then' case.");
|
|
||||||
Expr elseCase = expression();
|
|
||||||
return new If { Condition = condition, Then = thenCase, Else = elseCase };
|
|
||||||
}
|
}
|
||||||
|
if (match(TokenType.At))
|
||||||
return primary();
|
{
|
||||||
|
Token literal = consume(TokenType.String, "Expect string literal after '@'.");
|
||||||
|
return new((string)(literal.literal ?? ""), true);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expr primary()
|
private Expr primary()
|
||||||
|
{
|
||||||
|
Expr expr = operand();
|
||||||
|
if (match(TokenType.Period))
|
||||||
|
{
|
||||||
|
Name? ident = name();
|
||||||
|
if (ident == null)
|
||||||
|
{
|
||||||
|
throw error(advance(), "Expect identifier after dot.");
|
||||||
|
}
|
||||||
|
return new Selector { Left = expr, FieldName = ident };
|
||||||
|
}
|
||||||
|
if (match(TokenType.LBracket))
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("TODO index");
|
||||||
|
}
|
||||||
|
if (match(TokenType.LParen))
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("TODO apply");
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expr operand()
|
||||||
{
|
{
|
||||||
if (match(TokenType.Number, TokenType.String))
|
if (match(TokenType.Number, TokenType.String))
|
||||||
{
|
{
|
||||||
return new Literal { Value = previous().literal };
|
return new Literal { Value = previous().literal };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ident = name();
|
||||||
|
if (ident != null)
|
||||||
|
{
|
||||||
|
return new Identifier { Value = ident };
|
||||||
|
}
|
||||||
|
|
||||||
if (match(TokenType.LParen))
|
if (match(TokenType.LParen))
|
||||||
{
|
{
|
||||||
Expr expr = expression();
|
Expr expr = expression();
|
||||||
|
@ -21,7 +21,11 @@ let types =
|
|||||||
Fields =
|
Fields =
|
||||||
[ { Type = "Expr"; Name = "Condition" }
|
[ { Type = "Expr"; Name = "Condition" }
|
||||||
{ Type = "Expr"; Name = "Then" }
|
{ Type = "Expr"; Name = "Then" }
|
||||||
{ Type = "Expr"; Name = "Else" } ] } ]
|
{ Type = "Expr"; Name = "Else" } ] }
|
||||||
|
{ Name = "Identifier"
|
||||||
|
Fields = [ { Type = "Name"; Name = "Value" } ] }
|
||||||
|
{ Name = "Selector"
|
||||||
|
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Name"; Name = "FieldName" } ] } ]
|
||||||
|
|
||||||
let visitorMethod baseName t = $"visit{t.Name}{baseName}"
|
let visitorMethod baseName t = $"visit{t.Name}{baseName}"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user