diff --git a/AST.cs b/AST.cs index 18ba0a6..d9d85b1 100644 --- a/AST.cs +++ b/AST.cs @@ -1,4 +1,3 @@ -using System.CodeDom.Compiler; using System.Collections.Generic; namespace Finn.AST; diff --git a/Expr.g.cs b/Expr.g.cs index 5611b2e..91b55a1 100644 --- a/Expr.g.cs +++ b/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 context, IExprVisitor visitor); } public interface IExprVisitor { @@ -27,105 +27,105 @@ public interface IExprVisitor { 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 context, IExprVisitor 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 context, IExprVisitor 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 context, IExprVisitor 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 context, IExprVisitor 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 context, IExprVisitor 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 context, IExprVisitor 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 context, IExprVisitor 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 context, IExprVisitor 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 context, IExprVisitor 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 context, IExprVisitor 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 context, IExprVisitor 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 context, IExprVisitor 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 context, IExprVisitor 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 context, IExprVisitor 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 context, IExprVisitor visitor) { diff --git a/Interpreter.cs b/Interpreter.cs index 03d3b55..adb19b7 100644 --- a/Interpreter.cs +++ b/Interpreter.cs @@ -86,7 +86,7 @@ public class Interpreter : AST.IExprVisitor { 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 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 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 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 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 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 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 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 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 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 { 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 diff --git a/Parser.cs b/Parser.cs index 0792dd0..7d24687 100644 --- a/Parser.cs +++ b/Parser.cs @@ -158,6 +158,7 @@ class Parser { return null; } + var lbrace = previous(); List fields = new List(); 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 bindings = new List(); 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 elements = new List(); @@ -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) { diff --git a/Pattern.g.cs b/Pattern.g.cs index 992a0db..e501208 100644 --- a/Pattern.g.cs +++ b/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 context, IPatternVisitor visitor); } public interface IPatternVisitor { @@ -16,28 +16,28 @@ public interface IPatternVisitor { 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 context, IPatternVisitor 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 context, IPatternVisitor 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 context, IPatternVisitor 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 context, IPatternVisitor visitor) { diff --git a/Program.cs b/Program.cs index 77b4548..c49f851 100644 --- a/Program.cs +++ b/Program.cs @@ -41,16 +41,16 @@ class Program { var scanner = new Scanner(src); List 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; } } diff --git a/ast_classes.fsx b/ast_classes.fsx index f4eb5ce..151153b 100644 --- a/ast_classes.fsx +++ b/ast_classes.fsx @@ -1,65 +1,93 @@ type Field = { Type: string; Name: string } -type Type = { Name: string; Fields: list } +type Type = { Name: string; Fields: list; 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 context, I{baseName}Visitor visitor); }}\n" diff --git a/strawman4.txt b/strawman4.txt index 2683fff..9f2289c 100644 --- a/strawman4.txt +++ b/strawman4.txt @@ -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 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]) \ No newline at end of file +def doubled = let double(n) = 2 * n in map(double, [1,2,3,4,5])