Add dynamic bindings and better error positions

This commit is contained in:
Brandon Dyck 2023-08-06 15:44:15 -06:00
parent a7421927ae
commit 7d006d8889
8 changed files with 136 additions and 100 deletions

1
AST.cs
View File

@ -1,4 +1,3 @@
using System.CodeDom.Compiler;
using System.Collections.Generic; using System.Collections.Generic;
namespace Finn.AST; namespace Finn.AST;

View File

@ -7,7 +7,7 @@
namespace Finn.AST; 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 abstract TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor);
} }
public interface IExprVisitor<TContext, TResult> { public interface IExprVisitor<TContext, TResult> {
@ -27,105 +27,105 @@ public interface IExprVisitor<TContext, TResult> {
TResult visitLetExpr(TContext context, Let expr); TResult visitLetExpr(TContext context, Let expr);
TResult visitWhenExpr(TContext context, When 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitSequenceExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitBinaryExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitGroupingExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitLiteralExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitUnaryExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitIfExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitVariableExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitListExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitVariantExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitRecordExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitSelectorExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitIndexerExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitCallExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {
return visitor.visitLetExpr(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{ {

View File

@ -86,7 +86,7 @@ public class Interpreter : AST.IExprVisitor<Env, object>
{ {
public ValueTuple visitFieldPatternPattern((object, Env) context, AST.FieldPattern pattern) 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) 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) public object visitIfExpr(Env env, AST.If expr)
{ {
var cond = evaluate(env, expr.Condition); var cond = evaluate(env, expr.Condition);
// TODO Maybe I should token info in the AST. var vb = checkBoolOperand(expr.Condition.Start, cond);
var vb = checkBoolOperand(new Token(TokenType.If, "if", null, new(0, 1, 1)), cond);
if (vb == Variant.True) if (vb == Variant.True)
{ {
return evaluate(env, expr.Then); return evaluate(env, expr.Then);
@ -424,10 +423,8 @@ public class Interpreter : AST.IExprVisitor<Env, object>
public object visitIndexerExpr(Env env, AST.Indexer expr) public object visitIndexerExpr(Env env, AST.Indexer expr)
{ {
// TODO use real token var left = checkListOperand(expr.Left.Start, evaluate(env, expr.Left));
var tok = new Token(TokenType.LBracket, "[", null, new(0, 1, 1)); var index = checkNumberOperand(expr.Index.Start, evaluate(env, expr.Index));
var left = checkListOperand(tok, evaluate(env, expr.Left));
var index = checkNumberOperand(tok, evaluate(env, expr.Index));
try try
{ {
var item = left[index]; var item = left[index];
@ -452,7 +449,7 @@ public class Interpreter : AST.IExprVisitor<Env, object>
catch (Exception e) catch (Exception e)
{ {
// TODO use real info // 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); throw new RuntimeError(tok, e.Message);
} }
break; break;
@ -465,8 +462,6 @@ public class Interpreter : AST.IExprVisitor<Env, object>
public object visitListExpr(Env env, AST.List expr) 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; List l = List.Empty;
foreach (var itemExpr in expr.Elements) foreach (var itemExpr in expr.Elements)
{ {
@ -474,7 +469,7 @@ public class Interpreter : AST.IExprVisitor<Env, object>
if (!l.IsEmpty) if (!l.IsEmpty)
{ {
try { checkTypesEqual(l[0], item); } 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); l = l.Add(item);
} }
@ -488,15 +483,13 @@ public class Interpreter : AST.IExprVisitor<Env, object>
public object visitRecordExpr(Env env, AST.Record expr) 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; Record rec = Record.Empty;
if (expr.Base != null) if (expr.Base != null)
{ {
var baseRecValue = evaluate(env, expr.Base.Value); var baseRecValue = evaluate(env, expr.Base.Value);
if (baseRecValue is not Record) 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; var baseRec = (Record)baseRecValue;
@ -507,17 +500,20 @@ public class Interpreter : AST.IExprVisitor<Env, object>
var label = (string)update.Name.Literal!; var label = (string)update.Name.Literal!;
if (updateLabels.Contains(label)) 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); 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 try
{ {
baseRec = baseRec.Update(label, updateValue); baseRec = baseRec.Update(label, updateValue);
} }
catch 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; rec = baseRec;
@ -530,7 +526,7 @@ public class Interpreter : AST.IExprVisitor<Env, object>
var label = (string)extension.Name.Literal!; var label = (string)extension.Name.Literal!;
if (extLabels.Contains(label)) 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); extLabels.Add(label);
@ -544,16 +540,14 @@ public class Interpreter : AST.IExprVisitor<Env, object>
public object visitSelectorExpr(Env env, AST.Selector expr) public object visitSelectorExpr(Env env, AST.Selector expr)
{ {
var left = evaluate(env, expr.Left); var left = evaluate(env, expr.Left);
// TODO Use real token. var r = checkRecordOperand(expr.Left.Start, left);
var tok = new Token(TokenType.Period, ".", null, new(0, 1, 1));
var r = checkRecordOperand(tok, left);
try try
{ {
return r.Get((string)expr.FieldName.Literal!); return r.Get((string)expr.FieldName.Literal!);
} }
catch 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); var head = evaluate(env, expr.Head);
// TODO use real info // 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) foreach (var c in expr.Cases)
{ {
try try

View File

@ -158,6 +158,7 @@ class Parser
{ {
return null; return null;
} }
var lbrace = previous();
List<FieldPattern> fields = new List<FieldPattern>(); List<FieldPattern> fields = new List<FieldPattern>();
while (!check(TokenType.RBrace, TokenType.Pipe)) while (!check(TokenType.RBrace, TokenType.Pipe))
@ -172,7 +173,7 @@ class Parser
} }
var restPattern = match(TokenType.Pipe) ? simplePattern() : null; var restPattern = match(TokenType.Pipe) ? simplePattern() : null;
consume("Expect '}' at end of record pattern.", TokenType.RBrace); consume("Expect '}' at end of record pattern.", TokenType.RBrace);
return new(fields.ToArray(), restPattern); return new(fields.ToArray(), restPattern, lbrace);
} }
private VariantPattern? variantPattern() private VariantPattern? variantPattern()
@ -181,24 +182,25 @@ class Parser
{ {
return null; return null;
} }
var backtick = previous();
var tag = consume("Expect identifier as tag name.", TokenType.Identifier, TokenType.QuotedIdentifier); var tag = consume("Expect identifier as tag name.", TokenType.Identifier, TokenType.QuotedIdentifier);
if (!match(TokenType.LParen)) if (!match(TokenType.LParen))
{ {
return new(tag, null); return new(tag, null, backtick);
} }
Pattern argument = pattern(); Pattern argument = pattern();
consume("Expect ')' after variant argument.", TokenType.RParen); consume("Expect ')' after variant argument.", TokenType.RParen);
return new(tag, argument); return new(tag, argument, backtick);
} }
private SimplePattern? simplePattern() private SimplePattern? simplePattern()
{ {
if (match(TokenType.Blank)) 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() private Pattern pattern()
@ -233,6 +235,7 @@ class Parser
{ {
return ifExpr(); return ifExpr();
} }
var letToken = previous();
List<Binding> bindings = new List<Binding>(); List<Binding> bindings = new List<Binding>();
bindings.Add(parseBinding()); bindings.Add(parseBinding());
@ -242,7 +245,7 @@ class Parser
} }
consume("Expect 'in' after let-bindings.", TokenType.In); consume("Expect 'in' after let-bindings.", TokenType.In);
Expr body = expression(); Expr body = expression();
return new Let(bindings.ToArray(), body); return new Let(bindings.ToArray(), body, letToken);
Binding parseBinding() Binding parseBinding()
{ {
@ -277,12 +280,13 @@ class Parser
{ {
return when(); return when();
} }
var ifToken = previous();
Expr condition = expression(); Expr condition = expression();
consume("Expect 'then' after condition.", TokenType.Then); consume("Expect 'then' after condition.", TokenType.Then);
Expr thenCase = expression(); Expr thenCase = expression();
consume("Expect 'else' after 'then' case.", TokenType.Else); consume("Expect 'else' after 'then' case.", TokenType.Else);
Expr elseCase = expression(); Expr elseCase = expression();
return new If(condition, thenCase, elseCase); return new If(condition, thenCase, elseCase, ifToken);
} }
private Expr when() private Expr when()
@ -291,6 +295,7 @@ class Parser
{ {
return primary(); return primary();
} }
var whenToken = previous();
Expr head = expression(); Expr head = expression();
consume("Expect 'is' after expression.", TokenType.Is); consume("Expect 'is' after expression.", TokenType.Is);
@ -300,7 +305,7 @@ class Parser
{ {
cases.Add(parseCase()); cases.Add(parseCase());
} }
return new When(head, cases.ToArray()); return new When(head, cases.ToArray(), whenToken);
VarBinding parseCase() VarBinding parseCase()
{ {
@ -357,8 +362,9 @@ class Parser
{ {
if (match(TokenType.Number, TokenType.String)) if (match(TokenType.Number, TokenType.String))
{ {
object literal = previous().Literal!; var token = previous();
return new Literal(previous().Literal!); object literal = token.Literal!;
return new Literal(previous().Literal!, token);
} }
if (match(TokenType.Identifier, TokenType.QuotedIdentifier)) if (match(TokenType.Identifier, TokenType.QuotedIdentifier))
@ -368,9 +374,10 @@ class Parser
if (match(TokenType.LParen)) if (match(TokenType.LParen))
{ {
var lparen = previous();
Expr groupedExpr = expression(); Expr groupedExpr = expression();
consume("Expect ')' after expression.", TokenType.RParen); consume("Expect ')' after expression.", TokenType.RParen);
return new Grouping(groupedExpr); return new Grouping(groupedExpr, lparen);
} }
Expr? expr; Expr? expr;
@ -396,6 +403,7 @@ class Parser
{ {
return null; return null;
} }
var lbracket = previous();
List<Expr> elements = new List<Expr>(); 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() private Expr? variant()
@ -430,6 +438,7 @@ class Parser
{ {
return null; return null;
} }
var backtick = previous();
var tag = consume("Expect identifier as tag name.", TokenType.QuotedIdentifier, TokenType.Identifier); var tag = consume("Expect identifier as tag name.", TokenType.QuotedIdentifier, TokenType.Identifier);
Expr? argument = null; Expr? argument = null;
if (match(TokenType.LParen)) if (match(TokenType.LParen))
@ -440,7 +449,7 @@ class Parser
consume("Expect ')' after variant argument.", TokenType.RParen); consume("Expect ')' after variant argument.", TokenType.RParen);
} }
} }
return new Variant(tag, argument); return new Variant(tag, argument, backtick);
} }
private Expr? record() private Expr? record()
@ -449,6 +458,7 @@ class Parser
{ {
return null; return null;
} }
var lbrace = previous();
var extensions = parseFields(TokenType.RBrace, TokenType.Pipe); var extensions = parseFields(TokenType.RBrace, TokenType.Pipe);
BaseRecord? baseRecord = null; BaseRecord? baseRecord = null;
@ -461,7 +471,7 @@ class Parser
} }
consume("Expect '}' at end of record literal.", TokenType.RBrace); 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) Field[] parseFields(params TokenType[] endAt)
{ {

View File

@ -7,7 +7,7 @@
namespace Finn.AST; 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 abstract TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor);
} }
public interface IPatternVisitor<TContext, TResult> { public interface IPatternVisitor<TContext, TResult> {
@ -16,28 +16,28 @@ public interface IPatternVisitor<TContext, TResult> {
TResult visitFieldPatternPattern(TContext context, FieldPattern pattern); TResult visitFieldPatternPattern(TContext context, FieldPattern pattern);
TResult visitRecordPatternPattern(TContext context, RecordPattern 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) public override TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor)
{ {
return visitor.visitSimplePatternPattern(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor)
{ {
return visitor.visitVariantPatternPattern(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor)
{ {
return visitor.visitFieldPatternPattern(context, this); 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) public override TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor)
{ {

View File

@ -41,16 +41,16 @@ class Program
{ {
var scanner = new Scanner(src); var scanner = new Scanner(src);
List<Token> tokens = scanner.scanTokens(); List<Token> tokens = scanner.scanTokens();
Console.WriteLine("TOKENS\n======="); // Console.WriteLine("TOKENS\n=======");
foreach (var token in tokens) // foreach (var token in tokens)
{ // {
Console.WriteLine(token); // Console.WriteLine(token);
} // }
Parser parser = new Parser(tokens); Parser parser = new Parser(tokens);
Expr? expression = parser.parse(); Expr? expression = parser.parse();
Console.WriteLine("\nAST\n======="); // Console.WriteLine("\nAST\n=======");
Console.WriteLine(expression); // Console.WriteLine(expression);
Console.WriteLine(); // Console.WriteLine();
if (hadError) if (hadError)
{ {
@ -84,17 +84,17 @@ class Program
{ {
if (token.Type == TokenType.EOF) if (token.Type == TokenType.EOF)
{ {
report(token.Position, " at end", message); report(token.Start, " at end", message);
} }
else else
{ {
report(token.Position, " at '" + token.Lexeme + "'", message); report(token.Start, " at '" + token.Lexeme + "'", message);
} }
} }
public static void runtimeError(RuntimeError err) 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; 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 class Scanner
{ {
@ -222,7 +222,7 @@ class Scanner
private void addToken(TokenType type, Object? literal) 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) private bool match(char expected)
@ -409,7 +409,7 @@ class Scanner
scanToken(); scanToken();
} }
tokens.Add(new Token(TokenType.EOF, "", null, current)); tokens.Add(new Token(TokenType.EOF, "", null, current, current));
return tokens; return tokens;
} }
} }

View File

@ -1,65 +1,93 @@
type Field = { Type: string; Name: string } 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" [ { 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" { Name = "Binary"
Fields = Fields =
[ { Type = "Expr"; Name = "Left" } [ { Type = "Expr"; Name = "Left" }
{ Type = "Token"; Name = "Op" } { Type = "Token"; Name = "Op" }
{ Type = "Expr"; Name = "Right" } ] } { Type = "Expr"; Name = "Right" } ]
Start = "Left.Start" }
{ Name = "Grouping" { Name = "Grouping"
Fields = [ { Type = "Expr"; Name = "Expression" } ] } Fields = [ { Type = "Expr"; Name = "Expression" } ;{Type="Token";Name="LParen"}]
Start = "LParen"}
{ Name = "Literal" { Name = "Literal"
Fields = Fields =
[ { Type = "System.Object" [ { Type = "System.Object"
Name = "Value" } ] } Name = "Value" }; {Type="Token"; Name="Token"} ]
Start = "Token"}
{ Name = "Unary" { Name = "Unary"
Fields = [ { Type = "Token"; Name = "Op" }; { Type = "Expr"; Name = "Right" } ] } Fields = [ { Type = "Token"; Name = "Op" }; { Type = "Expr"; Name = "Right" } ]
Start = "Op" }
{ Name = "If" { Name = "If"
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" }
{ Type = "Token"; Name = "IfToken" } ]
Start = "IfToken" }
{ Name = "Variable" { Name = "Variable"
Fields = [ { Type = "Token"; Name = "Value" } ] } Fields = [ { Type = "Token"; Name = "Value" } ]
Start = "Value" }
{ Name = "List" { Name = "List"
Fields = [ { Type = "Expr[]"; Name = "Elements" } ] } Fields = [ { Type = "Expr[]"; Name = "Elements" }; {Type="Token"; Name = "LBracket"} ]
Start = "LBracket" }
{ Name = "Variant" { 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" { Name = "Record"
Fields = Fields =
[ { Type = "Field[]" [ { Type = "Field[]"
Name = "Extensions" } Name = "Extensions" }
{ Type = "BaseRecord?"; Name = "Base" } ] } { Type = "BaseRecord?"; Name = "Base" }
{ Type = "Token"; Name= "LBrace"} ]
Start = "LBrace"}
{ Name = "Selector" { 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" { 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" { 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" { Name = "Let"
Fields = Fields =
[ { Type = "Binding[]" [ { Type = "Binding[]"
Name = "Bindings" } Name = "Bindings" }
{ Type = "Expr"; Name = "Body" } ] } { Type = "Expr"; Name = "Body" }
{ Type = "Token"; Name = "LetToken"} ]
Start = "LetToken" }
{ Name = "When" { 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 = let patternTypes =
[ { Name = "SimplePattern" [ { Name = "SimplePattern"
Fields = [ { Type = "Token?"; Name = "Identifier" } ] } Fields = [ { Type = "Token?"; Name = "Identifier" }; {Type = "Token"; Name = "Token"}]
Start = "Token" }
{ Name = "VariantPattern" { 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" { Name = "FieldPattern"
Fields = [ { Type = "Token"; Name = "Name" }; { Type = "Pattern?"; Name = "Pattern" } ] } Fields = [ { Type = "Token"; Name = "Name" }; { Type = "Pattern?"; Name = "Pattern" } ]
Start = "Name" }
{ Name = "RecordPattern" { Name = "RecordPattern"
Fields = Fields =
[ { Type = "FieldPattern[]" [ { Type = "FieldPattern[]"
Name = "Fields" } Name = "Fields" }
{ Type = "SimplePattern?" { Type = "SimplePattern?"
Name = "Rest" } ] } ] Name = "Rest" }
{ Type = "Token"
Name = "LBrace"} ]
Start = "LBrace" } ]
let visitorMethod baseName t = $"visit{t.Name}{baseName}" 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 List.iteri writeField t.Fields
sw.Write sw.Write
$") : {baseName}() $") : {baseName}({t.Start})
{{\n" {{\n"
sw.Write sw.Write
@ -107,7 +135,7 @@ let renderAST outputDir baseName types =
namespace Finn.AST; 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); \tpublic abstract TResult accept<TContext, TResult>(TContext context, I{baseName}Visitor<TContext, TResult> visitor);
}}\n" }}\n"

View File

@ -1,3 +1,6 @@
# v5
# Added implicit (dynamic) bindings
# v4 # v4
# Moved type annotations into bindings. # Moved type annotations into bindings.
# Added def keyword for top-level 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 maybe 'a = <some: 'a, nothing>
type alias unit = {} type alias unit = {}
implicit print(s: string) -> unit
# Type aliases should appear in compiler messages. # Type aliases should appear in compiler messages.
# binary_tree takes a comparison function and returns a record (like a JS # 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 # 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, empty: tree 'a,
insert: fn(tree 'a, 'a) -> tree 'a, insert: fn(tree 'a, 'a) -> tree 'a,
find: fn(tree 'a, 'a) -> maybe '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, # 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. # 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"} in {empty, insert, find, @"func we don't care about"}
# Prints "`some(2)". # Prints "`some(2)".
def do_tree_things() = def do_tree_things() -> |print| unit =
# Assume that int_compare is in the stdlib or something. # Assume that int_compare is in the stdlib or something.
let {insert, empty, find | @"stuff we don't need"} = binary_tree(int_compare) let {insert, empty, find | @"stuff we don't need"} = binary_tree(int_compare)
# I invented this fancy partial-application syntax with _, and then found # I invented this fancy partial-application syntax with _, and then found
@ -76,7 +81,7 @@ def do_tree_things() =
@"stuff we don't need" @"stuff we don't need"
# Prints "`some(2)\nignore me". # 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} = def annotate(note: 'a, obj: {|'b}) -> {note: 'a | 'b} =
{ note | obj } { 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 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])