Add dynamic bindings and better error positions
This commit is contained in:
parent
a7421927ae
commit
7d006d8889
1
AST.cs
1
AST.cs
@ -1,4 +1,3 @@
|
||||
using System.CodeDom.Compiler;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Finn.AST;
|
||||
|
32
Expr.g.cs
32
Expr.g.cs
@ -7,7 +7,7 @@
|
||||
|
||||
namespace Finn.AST;
|
||||
|
||||
public abstract record Expr() {
|
||||
public abstract record Expr(Token Start) {
|
||||
public abstract TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor);
|
||||
}
|
||||
public interface IExprVisitor<TContext, TResult> {
|
||||
@ -27,105 +27,105 @@ public interface IExprVisitor<TContext, TResult> {
|
||||
TResult visitLetExpr(TContext context, Let expr);
|
||||
TResult visitWhenExpr(TContext context, When expr);
|
||||
}
|
||||
public partial record Sequence(Expr Left, Expr Right) : Expr()
|
||||
public partial record Sequence(Expr Left, Expr Right) : Expr(Left.Start)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitSequenceExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record Binary(Expr Left, Token Op, Expr Right) : Expr()
|
||||
public partial record Binary(Expr Left, Token Op, Expr Right) : Expr(Left.Start)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitBinaryExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record Grouping(Expr Expression) : Expr()
|
||||
public partial record Grouping(Expr Expression, Token LParen) : Expr(LParen)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitGroupingExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record Literal(System.Object Value) : Expr()
|
||||
public partial record Literal(System.Object Value, Token Token) : Expr(Token)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitLiteralExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record Unary(Token Op, Expr Right) : Expr()
|
||||
public partial record Unary(Token Op, Expr Right) : Expr(Op)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitUnaryExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record If(Expr Condition, Expr Then, Expr Else) : Expr()
|
||||
public partial record If(Expr Condition, Expr Then, Expr Else, Token IfToken) : Expr(IfToken)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitIfExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record Variable(Token Value) : Expr()
|
||||
public partial record Variable(Token Value) : Expr(Value)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitVariableExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record List(Expr[] Elements) : Expr()
|
||||
public partial record List(Expr[] Elements, Token LBracket) : Expr(LBracket)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitListExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record Variant(Token Tag, Expr? Argument) : Expr()
|
||||
public partial record Variant(Token Tag, Expr? Argument, Token Backtick) : Expr(Backtick)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitVariantExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record Record(Field[] Extensions, BaseRecord? Base) : Expr()
|
||||
public partial record Record(Field[] Extensions, BaseRecord? Base, Token LBrace) : Expr(LBrace)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitRecordExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record Selector(Expr Left, Token FieldName) : Expr()
|
||||
public partial record Selector(Expr Left, Token FieldName) : Expr(Left.Start)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitSelectorExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record Indexer(Expr Left, Expr Index) : Expr()
|
||||
public partial record Indexer(Expr Left, Expr Index) : Expr(Left.Start)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitIndexerExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record Call(Expr Left, Expr?[] Arguments) : Expr()
|
||||
public partial record Call(Expr Left, Expr?[] Arguments) : Expr(Left.Start)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitCallExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record Let(Binding[] Bindings, Expr Body) : Expr()
|
||||
public partial record Let(Binding[] Bindings, Expr Body, Token LetToken) : Expr(LetToken)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitLetExpr(context, this);
|
||||
}
|
||||
}
|
||||
public partial record When(Expr Head, VarBinding[] Cases) : Expr()
|
||||
public partial record When(Expr Head, VarBinding[] Cases, Token WhenToken) : Expr(WhenToken)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
|
@ -86,7 +86,7 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
||||
{
|
||||
public ValueTuple visitFieldPatternPattern((object, Env) context, AST.FieldPattern pattern)
|
||||
{
|
||||
return (pattern.Pattern ?? new AST.SimplePattern(pattern.Name)).accept(context, this);
|
||||
return (pattern.Pattern ?? new AST.SimplePattern(pattern.Name, pattern.Name)).accept(context, this);
|
||||
}
|
||||
|
||||
public ValueTuple visitRecordPatternPattern((object, Env) context, AST.RecordPattern pattern)
|
||||
@ -410,8 +410,7 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
||||
public object visitIfExpr(Env env, AST.If expr)
|
||||
{
|
||||
var cond = evaluate(env, expr.Condition);
|
||||
// TODO Maybe I should token info in the AST.
|
||||
var vb = checkBoolOperand(new Token(TokenType.If, "if", null, new(0, 1, 1)), cond);
|
||||
var vb = checkBoolOperand(expr.Condition.Start, cond);
|
||||
if (vb == Variant.True)
|
||||
{
|
||||
return evaluate(env, expr.Then);
|
||||
@ -424,10 +423,8 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
||||
|
||||
public object visitIndexerExpr(Env env, AST.Indexer expr)
|
||||
{
|
||||
// TODO use real token
|
||||
var tok = new Token(TokenType.LBracket, "[", null, new(0, 1, 1));
|
||||
var left = checkListOperand(tok, evaluate(env, expr.Left));
|
||||
var index = checkNumberOperand(tok, evaluate(env, expr.Index));
|
||||
var left = checkListOperand(expr.Left.Start, evaluate(env, expr.Left));
|
||||
var index = checkNumberOperand(expr.Index.Start, evaluate(env, expr.Index));
|
||||
try
|
||||
{
|
||||
var item = left[index];
|
||||
@ -452,7 +449,7 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
||||
catch (Exception e)
|
||||
{
|
||||
// TODO use real info
|
||||
var tok = new Token(TokenType.Let, "let", null, new(0, 1, 1));
|
||||
var tok = new Token(TokenType.Let, "let", null, new(0, 1, 1), new(0, 1, 1));
|
||||
throw new RuntimeError(tok, e.Message);
|
||||
}
|
||||
break;
|
||||
@ -465,8 +462,6 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
||||
|
||||
public object visitListExpr(Env env, AST.List expr)
|
||||
{
|
||||
// TODO use real token
|
||||
var tok = new Token(TokenType.LBracket, "[", null, new(0, 1, 1));
|
||||
List l = List.Empty;
|
||||
foreach (var itemExpr in expr.Elements)
|
||||
{
|
||||
@ -474,7 +469,7 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
||||
if (!l.IsEmpty)
|
||||
{
|
||||
try { checkTypesEqual(l[0], item); }
|
||||
catch { throw new RuntimeError(tok, "List items must all have same type."); }
|
||||
catch { throw new RuntimeError(itemExpr.Start, "List items must all have same type."); }
|
||||
}
|
||||
l = l.Add(item);
|
||||
}
|
||||
@ -488,15 +483,13 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
||||
|
||||
public object visitRecordExpr(Env env, AST.Record expr)
|
||||
{
|
||||
// TODO use real token
|
||||
Token tok = new Token(TokenType.LBrace, "{", null, new(0, 1, 1));
|
||||
Record rec = Record.Empty;
|
||||
if (expr.Base != null)
|
||||
{
|
||||
var baseRecValue = evaluate(env, expr.Base.Value);
|
||||
if (baseRecValue is not Record)
|
||||
{
|
||||
throw new RuntimeError(tok, "Base record must be a record.");
|
||||
throw new RuntimeError(expr.Base.Value.Start, "Base record must be a record.");
|
||||
}
|
||||
var baseRec = (Record)baseRecValue;
|
||||
|
||||
@ -507,17 +500,20 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
||||
var label = (string)update.Name.Literal!;
|
||||
if (updateLabels.Contains(label))
|
||||
{
|
||||
throw new RuntimeError(tok, "Record updates must be to unique fields.");
|
||||
throw new RuntimeError(update.Name, "Record updates must be to unique fields.");
|
||||
}
|
||||
updateLabels.Add(label);
|
||||
var updateValue = update.Value == null ? env[update.Name] : evaluate(env, update.Value);
|
||||
(var updateValue, var updateValueToken) =
|
||||
update.Value == null ?
|
||||
(env[update.Name], update.Name) :
|
||||
(evaluate(env, update.Value), update.Value.Start);
|
||||
try
|
||||
{
|
||||
baseRec = baseRec.Update(label, updateValue);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new RuntimeError(tok, "Field update must have same type as previous value.");
|
||||
throw new RuntimeError(updateValueToken, "Field update must have same type as previous value.");
|
||||
}
|
||||
}
|
||||
rec = baseRec;
|
||||
@ -530,7 +526,7 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
||||
var label = (string)extension.Name.Literal!;
|
||||
if (extLabels.Contains(label))
|
||||
{
|
||||
throw new RuntimeError(tok, "Record extensions must have unique field names.");
|
||||
throw new RuntimeError(extension.Name, "Record extensions must have unique field names.");
|
||||
|
||||
}
|
||||
extLabels.Add(label);
|
||||
@ -544,16 +540,14 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
||||
public object visitSelectorExpr(Env env, AST.Selector expr)
|
||||
{
|
||||
var left = evaluate(env, expr.Left);
|
||||
// TODO Use real token.
|
||||
var tok = new Token(TokenType.Period, ".", null, new(0, 1, 1));
|
||||
var r = checkRecordOperand(tok, left);
|
||||
var r = checkRecordOperand(expr.Left.Start, left);
|
||||
try
|
||||
{
|
||||
return r.Get((string)expr.FieldName.Literal!);
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new RuntimeError(tok, "Operand must have selected field.");
|
||||
throw new RuntimeError(expr.FieldName, "Operand must have selected field.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -600,7 +594,7 @@ public class Interpreter : AST.IExprVisitor<Env, object>
|
||||
{
|
||||
var head = evaluate(env, expr.Head);
|
||||
// TODO use real info
|
||||
var tok = new Token(TokenType.When, "when", null, new(0, 1, 1));
|
||||
var tok = new Token(TokenType.When, "when", null, new(0, 1, 1), new(0, 1, 1));
|
||||
foreach (var c in expr.Cases)
|
||||
{
|
||||
try
|
||||
|
38
Parser.cs
38
Parser.cs
@ -158,6 +158,7 @@ class Parser
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var lbrace = previous();
|
||||
|
||||
List<FieldPattern> fields = new List<FieldPattern>();
|
||||
while (!check(TokenType.RBrace, TokenType.Pipe))
|
||||
@ -172,7 +173,7 @@ class Parser
|
||||
}
|
||||
var restPattern = match(TokenType.Pipe) ? simplePattern() : null;
|
||||
consume("Expect '}' at end of record pattern.", TokenType.RBrace);
|
||||
return new(fields.ToArray(), restPattern);
|
||||
return new(fields.ToArray(), restPattern, lbrace);
|
||||
}
|
||||
|
||||
private VariantPattern? variantPattern()
|
||||
@ -181,24 +182,25 @@ class Parser
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var backtick = previous();
|
||||
var tag = consume("Expect identifier as tag name.", TokenType.Identifier, TokenType.QuotedIdentifier);
|
||||
if (!match(TokenType.LParen))
|
||||
{
|
||||
return new(tag, null);
|
||||
return new(tag, null, backtick);
|
||||
}
|
||||
Pattern argument = pattern();
|
||||
consume("Expect ')' after variant argument.", TokenType.RParen);
|
||||
return new(tag, argument);
|
||||
return new(tag, argument, backtick);
|
||||
}
|
||||
|
||||
private SimplePattern? simplePattern()
|
||||
{
|
||||
if (match(TokenType.Blank))
|
||||
{
|
||||
return new SimplePattern(null);
|
||||
return new SimplePattern(null, previous());
|
||||
}
|
||||
|
||||
return match(TokenType.Identifier, TokenType.QuotedIdentifier) ? new(previous()) : null;
|
||||
return match(TokenType.Identifier, TokenType.QuotedIdentifier) ? new(previous(), previous()) : null;
|
||||
}
|
||||
|
||||
private Pattern pattern()
|
||||
@ -233,6 +235,7 @@ class Parser
|
||||
{
|
||||
return ifExpr();
|
||||
}
|
||||
var letToken = previous();
|
||||
|
||||
List<Binding> bindings = new List<Binding>();
|
||||
bindings.Add(parseBinding());
|
||||
@ -242,7 +245,7 @@ class Parser
|
||||
}
|
||||
consume("Expect 'in' after let-bindings.", TokenType.In);
|
||||
Expr body = expression();
|
||||
return new Let(bindings.ToArray(), body);
|
||||
return new Let(bindings.ToArray(), body, letToken);
|
||||
|
||||
Binding parseBinding()
|
||||
{
|
||||
@ -277,12 +280,13 @@ class Parser
|
||||
{
|
||||
return when();
|
||||
}
|
||||
var ifToken = previous();
|
||||
Expr condition = expression();
|
||||
consume("Expect 'then' after condition.", TokenType.Then);
|
||||
Expr thenCase = expression();
|
||||
consume("Expect 'else' after 'then' case.", TokenType.Else);
|
||||
Expr elseCase = expression();
|
||||
return new If(condition, thenCase, elseCase);
|
||||
return new If(condition, thenCase, elseCase, ifToken);
|
||||
}
|
||||
|
||||
private Expr when()
|
||||
@ -291,6 +295,7 @@ class Parser
|
||||
{
|
||||
return primary();
|
||||
}
|
||||
var whenToken = previous();
|
||||
Expr head = expression();
|
||||
consume("Expect 'is' after expression.", TokenType.Is);
|
||||
|
||||
@ -300,7 +305,7 @@ class Parser
|
||||
{
|
||||
cases.Add(parseCase());
|
||||
}
|
||||
return new When(head, cases.ToArray());
|
||||
return new When(head, cases.ToArray(), whenToken);
|
||||
|
||||
VarBinding parseCase()
|
||||
{
|
||||
@ -357,8 +362,9 @@ class Parser
|
||||
{
|
||||
if (match(TokenType.Number, TokenType.String))
|
||||
{
|
||||
object literal = previous().Literal!;
|
||||
return new Literal(previous().Literal!);
|
||||
var token = previous();
|
||||
object literal = token.Literal!;
|
||||
return new Literal(previous().Literal!, token);
|
||||
}
|
||||
|
||||
if (match(TokenType.Identifier, TokenType.QuotedIdentifier))
|
||||
@ -368,9 +374,10 @@ class Parser
|
||||
|
||||
if (match(TokenType.LParen))
|
||||
{
|
||||
var lparen = previous();
|
||||
Expr groupedExpr = expression();
|
||||
consume("Expect ')' after expression.", TokenType.RParen);
|
||||
return new Grouping(groupedExpr);
|
||||
return new Grouping(groupedExpr, lparen);
|
||||
}
|
||||
|
||||
Expr? expr;
|
||||
@ -396,6 +403,7 @@ class Parser
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var lbracket = previous();
|
||||
|
||||
List<Expr> elements = new List<Expr>();
|
||||
|
||||
@ -421,7 +429,7 @@ class Parser
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Finn.AST.List(elements.ToArray());
|
||||
return new List(elements.ToArray(), lbracket);
|
||||
}
|
||||
|
||||
private Expr? variant()
|
||||
@ -430,6 +438,7 @@ class Parser
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var backtick = previous();
|
||||
var tag = consume("Expect identifier as tag name.", TokenType.QuotedIdentifier, TokenType.Identifier);
|
||||
Expr? argument = null;
|
||||
if (match(TokenType.LParen))
|
||||
@ -440,7 +449,7 @@ class Parser
|
||||
consume("Expect ')' after variant argument.", TokenType.RParen);
|
||||
}
|
||||
}
|
||||
return new Variant(tag, argument);
|
||||
return new Variant(tag, argument, backtick);
|
||||
}
|
||||
|
||||
private Expr? record()
|
||||
@ -449,6 +458,7 @@ class Parser
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var lbrace = previous();
|
||||
|
||||
var extensions = parseFields(TokenType.RBrace, TokenType.Pipe);
|
||||
BaseRecord? baseRecord = null;
|
||||
@ -461,7 +471,7 @@ class Parser
|
||||
}
|
||||
|
||||
consume("Expect '}' at end of record literal.", TokenType.RBrace);
|
||||
return new Record(extensions, baseRecord);
|
||||
return new Record(extensions, baseRecord, lbrace);
|
||||
|
||||
Field[] parseFields(params TokenType[] endAt)
|
||||
{
|
||||
|
10
Pattern.g.cs
10
Pattern.g.cs
@ -7,7 +7,7 @@
|
||||
|
||||
namespace Finn.AST;
|
||||
|
||||
public abstract record Pattern() {
|
||||
public abstract record Pattern(Token Start) {
|
||||
public abstract TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor);
|
||||
}
|
||||
public interface IPatternVisitor<TContext, TResult> {
|
||||
@ -16,28 +16,28 @@ public interface IPatternVisitor<TContext, TResult> {
|
||||
TResult visitFieldPatternPattern(TContext context, FieldPattern pattern);
|
||||
TResult visitRecordPatternPattern(TContext context, RecordPattern pattern);
|
||||
}
|
||||
public partial record SimplePattern(Token? Identifier) : Pattern()
|
||||
public partial record SimplePattern(Token? Identifier, Token Token) : Pattern(Token)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitSimplePatternPattern(context, this);
|
||||
}
|
||||
}
|
||||
public partial record VariantPattern(Token Tag, Pattern? Argument) : Pattern()
|
||||
public partial record VariantPattern(Token Tag, Pattern? Argument, Token Backtick) : Pattern(Backtick)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitVariantPatternPattern(context, this);
|
||||
}
|
||||
}
|
||||
public partial record FieldPattern(Token Name, Pattern? Pattern) : Pattern()
|
||||
public partial record FieldPattern(Token Name, Pattern? Pattern) : Pattern(Name)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
return visitor.visitFieldPatternPattern(context, this);
|
||||
}
|
||||
}
|
||||
public partial record RecordPattern(FieldPattern[] Fields, SimplePattern? Rest) : Pattern()
|
||||
public partial record RecordPattern(FieldPattern[] Fields, SimplePattern? Rest, Token LBrace) : Pattern(LBrace)
|
||||
{
|
||||
public override TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor)
|
||||
{
|
||||
|
28
Program.cs
28
Program.cs
@ -41,16 +41,16 @@ class Program
|
||||
{
|
||||
var scanner = new Scanner(src);
|
||||
List<Token> tokens = scanner.scanTokens();
|
||||
Console.WriteLine("TOKENS\n=======");
|
||||
foreach (var token in tokens)
|
||||
{
|
||||
Console.WriteLine(token);
|
||||
}
|
||||
// Console.WriteLine("TOKENS\n=======");
|
||||
// foreach (var token in tokens)
|
||||
// {
|
||||
// Console.WriteLine(token);
|
||||
// }
|
||||
Parser parser = new Parser(tokens);
|
||||
Expr? expression = parser.parse();
|
||||
Console.WriteLine("\nAST\n=======");
|
||||
Console.WriteLine(expression);
|
||||
Console.WriteLine();
|
||||
// Console.WriteLine("\nAST\n=======");
|
||||
// Console.WriteLine(expression);
|
||||
// Console.WriteLine();
|
||||
|
||||
if (hadError)
|
||||
{
|
||||
@ -84,17 +84,17 @@ class Program
|
||||
{
|
||||
if (token.Type == TokenType.EOF)
|
||||
{
|
||||
report(token.Position, " at end", message);
|
||||
report(token.Start, " at end", message);
|
||||
}
|
||||
else
|
||||
{
|
||||
report(token.Position, " at '" + token.Lexeme + "'", message);
|
||||
report(token.Start, " at '" + token.Lexeme + "'", message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void runtimeError(RuntimeError err)
|
||||
{
|
||||
Console.Error.WriteLine($"{err.Message}\n[{err.Token.Position}]");
|
||||
Console.Error.WriteLine($"{err.Message}\n[{err.Token.Start}]");
|
||||
hadRuntimeError = true;
|
||||
}
|
||||
|
||||
@ -167,7 +167,7 @@ public record struct Position(int Offset, int Line, int Column)
|
||||
}
|
||||
}
|
||||
|
||||
public record Token(TokenType Type, String Lexeme, Object? Literal, Position Position);
|
||||
public record Token(TokenType Type, String Lexeme, Object? Literal, Position Start, Position End);
|
||||
|
||||
class Scanner
|
||||
{
|
||||
@ -222,7 +222,7 @@ class Scanner
|
||||
|
||||
private void addToken(TokenType type, Object? literal)
|
||||
{
|
||||
tokens.Add(new Token(type, lexeme, literal, start));
|
||||
tokens.Add(new Token(type, lexeme, literal, start, current));
|
||||
}
|
||||
|
||||
private bool match(char expected)
|
||||
@ -409,7 +409,7 @@ class Scanner
|
||||
scanToken();
|
||||
}
|
||||
|
||||
tokens.Add(new Token(TokenType.EOF, "", null, current));
|
||||
tokens.Add(new Token(TokenType.EOF, "", null, current, current));
|
||||
return tokens;
|
||||
}
|
||||
}
|
||||
|
@ -1,65 +1,93 @@
|
||||
type Field = { Type: string; Name: string }
|
||||
type Type = { Name: string; Fields: list<Field> }
|
||||
type Type = { Name: string; Fields: list<Field>; Start: string }
|
||||
|
||||
let exprTypes =
|
||||
let exprTypes: Type list =
|
||||
[ { Name = "Sequence"
|
||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr"; Name = "Right" } ] }
|
||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr"; Name = "Right" } ]
|
||||
Start = "Left.Start" }
|
||||
{ Name = "Binary"
|
||||
Fields =
|
||||
[ { Type = "Expr"; Name = "Left" }
|
||||
{ Type = "Token"; Name = "Op" }
|
||||
{ Type = "Expr"; Name = "Right" } ] }
|
||||
{ Type = "Expr"; Name = "Right" } ]
|
||||
Start = "Left.Start" }
|
||||
{ Name = "Grouping"
|
||||
Fields = [ { Type = "Expr"; Name = "Expression" } ] }
|
||||
Fields = [ { Type = "Expr"; Name = "Expression" } ;{Type="Token";Name="LParen"}]
|
||||
Start = "LParen"}
|
||||
{ Name = "Literal"
|
||||
Fields =
|
||||
[ { Type = "System.Object"
|
||||
Name = "Value" } ] }
|
||||
Name = "Value" }; {Type="Token"; Name="Token"} ]
|
||||
Start = "Token"}
|
||||
{ Name = "Unary"
|
||||
Fields = [ { Type = "Token"; Name = "Op" }; { Type = "Expr"; Name = "Right" } ] }
|
||||
Fields = [ { Type = "Token"; Name = "Op" }; { Type = "Expr"; Name = "Right" } ]
|
||||
Start = "Op" }
|
||||
{ Name = "If"
|
||||
Fields =
|
||||
[ { Type = "Expr"; Name = "Condition" }
|
||||
{ Type = "Expr"; Name = "Then" }
|
||||
{ Type = "Expr"; Name = "Else" } ] }
|
||||
{ Type = "Expr"; Name = "Else" }
|
||||
{ Type = "Token"; Name = "IfToken" } ]
|
||||
Start = "IfToken" }
|
||||
{ Name = "Variable"
|
||||
Fields = [ { Type = "Token"; Name = "Value" } ] }
|
||||
Fields = [ { Type = "Token"; Name = "Value" } ]
|
||||
Start = "Value" }
|
||||
{ Name = "List"
|
||||
Fields = [ { Type = "Expr[]"; Name = "Elements" } ] }
|
||||
Fields = [ { Type = "Expr[]"; Name = "Elements" }; {Type="Token"; Name = "LBracket"} ]
|
||||
Start = "LBracket" }
|
||||
{ Name = "Variant"
|
||||
Fields = [ { Type = "Token"; Name = "Tag" }; { Type = "Expr?"; Name = "Argument" } ] }
|
||||
Fields = [ { Type = "Token"; Name = "Tag" }; { Type = "Expr?"; Name = "Argument" }; {Type="Token";Name="Backtick"}]
|
||||
Start = "Backtick" }
|
||||
{ Name = "Record"
|
||||
Fields =
|
||||
[ { Type = "Field[]"
|
||||
Name = "Extensions" }
|
||||
{ Type = "BaseRecord?"; Name = "Base" } ] }
|
||||
{ Type = "BaseRecord?"; Name = "Base" }
|
||||
{ Type = "Token"; Name= "LBrace"} ]
|
||||
Start = "LBrace"}
|
||||
{ Name = "Selector"
|
||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Token"; Name = "FieldName" } ] }
|
||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Token"; Name = "FieldName" } ]
|
||||
Start = "Left.Start" }
|
||||
{ Name = "Indexer"
|
||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr"; Name = "Index" } ] }
|
||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr"; Name = "Index" } ]
|
||||
Start = "Left.Start" }
|
||||
{ Name = "Call"
|
||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr?[]"; Name = "Arguments" } ] }
|
||||
Fields = [ { Type = "Expr"; Name = "Left" }; { Type = "Expr?[]"; Name = "Arguments" } ]
|
||||
Start = "Left.Start" }
|
||||
{ Name = "Let"
|
||||
Fields =
|
||||
[ { Type = "Binding[]"
|
||||
Name = "Bindings" }
|
||||
{ Type = "Expr"; Name = "Body" } ] }
|
||||
{ Type = "Expr"; Name = "Body" }
|
||||
{ Type = "Token"; Name = "LetToken"} ]
|
||||
Start = "LetToken" }
|
||||
{ Name = "When"
|
||||
Fields = [ { Type = "Expr"; Name = "Head" }; { Type = "VarBinding[]"; Name = "Cases" } ] } ]
|
||||
Fields =
|
||||
[ { Type = "Expr"; Name = "Head" }
|
||||
{ Type = "VarBinding[]"
|
||||
Name = "Cases" }
|
||||
{ Type = "Token"; Name = "WhenToken"} ]
|
||||
Start = "WhenToken" } ]
|
||||
|
||||
let patternTypes =
|
||||
[ { Name = "SimplePattern"
|
||||
Fields = [ { Type = "Token?"; Name = "Identifier" } ] }
|
||||
Fields = [ { Type = "Token?"; Name = "Identifier" }; {Type = "Token"; Name = "Token"}]
|
||||
Start = "Token" }
|
||||
{ Name = "VariantPattern"
|
||||
Fields = [ { Type = "Token"; Name = "Tag" }; { Type = "Pattern?"; Name = "Argument" } ] }
|
||||
Fields = [ { Type = "Token"; Name = "Tag" }; { Type = "Pattern?"; Name = "Argument" }; {Type="Token"; Name = "Backtick"} ]
|
||||
Start = "Backtick" }
|
||||
{ Name = "FieldPattern"
|
||||
Fields = [ { Type = "Token"; Name = "Name" }; { Type = "Pattern?"; Name = "Pattern" } ] }
|
||||
Fields = [ { Type = "Token"; Name = "Name" }; { Type = "Pattern?"; Name = "Pattern" } ]
|
||||
Start = "Name" }
|
||||
{ Name = "RecordPattern"
|
||||
Fields =
|
||||
[ { Type = "FieldPattern[]"
|
||||
Name = "Fields" }
|
||||
{ Type = "SimplePattern?"
|
||||
Name = "Rest" } ] } ]
|
||||
Name = "Rest" }
|
||||
{ Type = "Token"
|
||||
Name = "LBrace"} ]
|
||||
Start = "LBrace" } ]
|
||||
|
||||
let visitorMethod baseName t = $"visit{t.Name}{baseName}"
|
||||
|
||||
@ -82,7 +110,7 @@ let renderType (sw: System.IO.StreamWriter) baseName t =
|
||||
List.iteri writeField t.Fields
|
||||
|
||||
sw.Write
|
||||
$") : {baseName}()
|
||||
$") : {baseName}({t.Start})
|
||||
{{\n"
|
||||
|
||||
sw.Write
|
||||
@ -107,7 +135,7 @@ let renderAST outputDir baseName types =
|
||||
|
||||
namespace Finn.AST;
|
||||
|
||||
public abstract record {baseName}() {{
|
||||
public abstract record {baseName}(Token Start) {{
|
||||
\tpublic abstract TResult accept<TContext, TResult>(TContext context, I{baseName}Visitor<TContext, TResult> visitor);
|
||||
}}\n"
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# v5
|
||||
# Added implicit (dynamic) bindings
|
||||
|
||||
# v4
|
||||
# Moved type annotations into bindings.
|
||||
# Added def keyword for top-level bindings.
|
||||
@ -30,6 +33,8 @@ type alias tree 'a = recurse 'T in <leaf, branch: {left: 'T, value: 'a, right: '
|
||||
type alias maybe 'a = <some: 'a, nothing>
|
||||
type alias unit = {}
|
||||
|
||||
implicit print(s: string) -> unit
|
||||
|
||||
# Type aliases should appear in compiler messages.
|
||||
# binary_tree takes a comparison function and returns a record (like a JS
|
||||
# object) as a module for handling binary trees of values of the same type
|
||||
@ -38,7 +43,7 @@ def binary_tree(compare: fn('a) -> 'a) -> {
|
||||
empty: tree 'a,
|
||||
insert: fn(tree 'a, 'a) -> tree 'a,
|
||||
find: fn(tree 'a, 'a) -> maybe 'a,
|
||||
@"func we don't care about": fn() -> unit
|
||||
@"func we don't care about": fn() -> |print| unit
|
||||
} =
|
||||
# A let expression can bind multiple names. Functions have access to all names within the expression,
|
||||
# while other values only have access to names bound before them.
|
||||
@ -65,7 +70,7 @@ def binary_tree(compare: fn('a) -> 'a) -> {
|
||||
in {empty, insert, find, @"func we don't care about"}
|
||||
|
||||
# Prints "`some(2)".
|
||||
def do_tree_things() =
|
||||
def do_tree_things() -> |print| unit =
|
||||
# Assume that int_compare is in the stdlib or something.
|
||||
let {insert, empty, find | @"stuff we don't need"} = binary_tree(int_compare)
|
||||
# I invented this fancy partial-application syntax with _, and then found
|
||||
@ -76,7 +81,7 @@ def do_tree_things() =
|
||||
@"stuff we don't need"
|
||||
|
||||
# Prints "`some(2)\nignore me".
|
||||
def main() = do_tree_things().@"func we don't care about"()
|
||||
def main() = with print = native_print in do_tree_things().@"func we don't care about"()
|
||||
|
||||
def annotate(note: 'a, obj: {|'b}) -> {note: 'a | 'b} =
|
||||
{ note | obj }
|
||||
@ -88,4 +93,4 @@ def reannotate(note: 'a, obj: {note: 'a | 'b}) -> {note: 'a | 'b} =
|
||||
|
||||
def map(f: fn('a) -> 'b, xs: ['a]) -> ['b] = crash("TODO")
|
||||
|
||||
def doubled = let double(n) = 2 * n in map(double, [1,2,3,4,5])
|
||||
def doubled = let double(n) = 2 * n in map(double, [1,2,3,4,5])
|
||||
|
Loading…
Reference in New Issue
Block a user