diff --git a/Interpreter.cs b/Interpreter.cs index bed3074..0c82e0a 100644 --- a/Interpreter.cs +++ b/Interpreter.cs @@ -6,6 +6,7 @@ using System.Collections.Immutable; using AST = Finn.AST; using System.Runtime.CompilerServices; using System.Net; +using Finn.AST; namespace Finn; @@ -80,14 +81,6 @@ public record NativeFunction(Func Function) : Callable public override string ToString() => ""; } -public class UserFunction : Callable -{ - public object Call(Interpreter interpreter, object[] arguments) - { - throw new NotImplementedException(); - } -} - public class Interpreter : AST.IExprVisitor { private class PatternMismatchException : Exception @@ -299,11 +292,25 @@ public class Interpreter : AST.IExprVisitor } } - private readonly Env globals = new Env(); + public record FinnFunction(FuncBinding binding, Env closure) : Callable + { + public object Call(Interpreter interpreter, object[] arguments) + { + Env env = new Env(closure); + for (int i = 0; i < binding.Params.Length; i++) + { + binding.Params[i].accept((arguments[i], env), new PatternBinder()); + } + return interpreter.evaluate(env, binding.Value); + // throw new NotImplementedException(); + } + } + + protected internal readonly Env Globals = new Env(); public Interpreter() { - globals["clock"] = new NativeFunction((args) => + Globals["clock"] = new NativeFunction((args) => { return (double)DateTimeOffset.Now.ToUnixTimeSeconds(); }); @@ -313,7 +320,7 @@ public class Interpreter : AST.IExprVisitor { try { - var value = evaluate(globals, expression); + var value = evaluate(Globals, expression); Console.WriteLine(value); } catch (RuntimeError err) @@ -475,14 +482,14 @@ public class Interpreter : AST.IExprVisitor catch { return new Variant("nothing", null); } } - public object visitLetExpr(Env env, AST.Let expr) + public object visitLetExpr(Env env, Let expr) { var newEnv = new Env(env); foreach (var binding in expr.Bindings) { switch (binding) { - case AST.VarBinding(var pattern, var valueExpr): + case VarBinding(var pattern, var valueExpr): var value = evaluate(env, valueExpr); try { @@ -496,8 +503,11 @@ public class Interpreter : AST.IExprVisitor throw new RuntimeError(start, e.Message); } break; + case FuncBinding fb: + newEnv[(string)fb.Name.Literal!] = new FinnFunction(fb, newEnv); + break; default: - throw new NotImplementedException("TODO function bindings"); + throw new Exception("wtf there are no other binding types"); } } return evaluate(newEnv, expr.Body); diff --git a/Parser.cs b/Parser.cs index 0bfff10..359484b 100644 --- a/Parser.cs +++ b/Parser.cs @@ -237,8 +237,7 @@ class Parser } var letToken = previous(); - List bindings = new List(); - bindings.Add(parseBinding()); + List bindings = new List { parseBinding() }; while (match(TokenType.And)) { bindings.Add(parseBinding()); @@ -252,7 +251,7 @@ class Parser Pattern p = pattern(); switch (p) { - case (SimplePattern(var funcName)) when funcName != null && match(TokenType.LParen): + case SimplePattern(var funcName) when funcName != null && match(TokenType.LParen): List funcParams = new List(); while (!check(TokenType.RParen)) { diff --git a/TODO.txt b/TODO.txt index 359602a..94df5db 100644 --- a/TODO.txt +++ b/TODO.txt @@ -1,4 +1,4 @@ -Implement Finn functions +Allow variable defs to reference earlier ones in same binding list Figure out multiple-binding let-expr semantics Inject error handling into parser and scanner Think about a way to hide record fields