From ca97c931813adc1e5423f61fb0062ceaffbe9263 Mon Sep 17 00:00:00 2001 From: Brandon Dyck Date: Tue, 11 Jul 2023 00:51:51 -0600 Subject: [PATCH] Evaluate let-exprs with simple bindings --- Expr.g.cs | 94 ++++++++++++++-------------- Interpreter.cs | 158 ++++++++++++++++++++++++++++++++++-------------- Pattern.g.cs | 28 ++++----- Program.cs | 2 +- ast_classes.fsx | 10 +-- 5 files changed, 181 insertions(+), 111 deletions(-) diff --git a/Expr.g.cs b/Expr.g.cs index 3a2f5d0..ffd5a41 100644 --- a/Expr.g.cs +++ b/Expr.g.cs @@ -8,127 +8,127 @@ namespace Finn.AST; public abstract record Expr() { - public abstract T accept(IExprVisitor visitor); + public abstract TResult accept(TContext context, IExprVisitor visitor); } -public interface IExprVisitor { - T visitSequenceExpr(Sequence expr); - T visitBinaryExpr(Binary expr); - T visitGroupingExpr(Grouping expr); - T visitLiteralExpr(Literal expr); - T visitUnaryExpr(Unary expr); - T visitIfExpr(If expr); - T visitVariableExpr(Variable expr); - T visitListExpr(List expr); - T visitVariantExpr(Variant expr); - T visitRecordExpr(Record expr); - T visitSelectorExpr(Selector expr); - T visitIndexerExpr(Indexer expr); - T visitCallExpr(Call expr); - T visitLetExpr(Let expr); - T visitWhenExpr(When expr); +public interface IExprVisitor { + TResult visitSequenceExpr(TContext context, Sequence expr); + TResult visitBinaryExpr(TContext context, Binary expr); + TResult visitGroupingExpr(TContext context, Grouping expr); + TResult visitLiteralExpr(TContext context, Literal expr); + TResult visitUnaryExpr(TContext context, Unary expr); + TResult visitIfExpr(TContext context, If expr); + TResult visitVariableExpr(TContext context, Variable expr); + TResult visitListExpr(TContext context, List expr); + TResult visitVariantExpr(TContext context, Variant expr); + TResult visitRecordExpr(TContext context, Record expr); + TResult visitSelectorExpr(TContext context, Selector expr); + TResult visitIndexerExpr(TContext context, Indexer expr); + TResult visitCallExpr(TContext context, Call expr); + TResult visitLetExpr(TContext context, Let expr); + TResult visitWhenExpr(TContext context, When expr); } public partial record Sequence(Expr Left, Expr Right) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitSequenceExpr(this); + return visitor.visitSequenceExpr(context, this); } } public partial record Binary(Expr Left, Token Op, Expr Right) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitBinaryExpr(this); + return visitor.visitBinaryExpr(context, this); } } public partial record Grouping(Expr Expression) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitGroupingExpr(this); + return visitor.visitGroupingExpr(context, this); } } public partial record Literal(System.Object Value) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitLiteralExpr(this); + return visitor.visitLiteralExpr(context, this); } } public partial record Unary(Token Op, Expr Right) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitUnaryExpr(this); + return visitor.visitUnaryExpr(context, this); } } public partial record If(Expr Condition, Expr Then, Expr Else) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitIfExpr(this); + return visitor.visitIfExpr(context, this); } } public partial record Variable(Name Value) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitVariableExpr(this); + return visitor.visitVariableExpr(context, this); } } public partial record List(Expr[] Elements) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitListExpr(this); + return visitor.visitListExpr(context, this); } } public partial record Variant(Name Tag, Expr? Argument) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitVariantExpr(this); + return visitor.visitVariantExpr(context, this); } } public partial record Record(Field[] Extensions, BaseRecord? Base) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitRecordExpr(this); + return visitor.visitRecordExpr(context, this); } } public partial record Selector(Expr Left, Name FieldName) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitSelectorExpr(this); + return visitor.visitSelectorExpr(context, this); } } public partial record Indexer(Expr Left, Expr Index) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitIndexerExpr(this); + return visitor.visitIndexerExpr(context, this); } } public partial record Call(Expr Left, Expr?[] Arguments) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitCallExpr(this); + return visitor.visitCallExpr(context, this); } } public partial record Let(Binding[] Bindings, Expr Body) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitLetExpr(this); + return visitor.visitLetExpr(context, this); } } public partial record When(Expr Head, Binding[] Cases) : Expr() { - public override T accept(IExprVisitor visitor) + public override TResult accept(TContext context, IExprVisitor visitor) { - return visitor.visitWhenExpr(this); + return visitor.visitWhenExpr(context, this); } } diff --git a/Interpreter.cs b/Interpreter.cs index f5441be..b0261ae 100644 --- a/Interpreter.cs +++ b/Interpreter.cs @@ -6,18 +6,65 @@ using AST = Finn.AST; namespace Finn; -public class Interpreter : AST.IExprVisitor +public class RuntimeError : Exception { - public class RuntimeError : Exception - { - public readonly Token Token; + public readonly Token Token; - internal RuntimeError(Token token, String message) : base(message) - { - Token = token; - } + internal RuntimeError(Token token, String message) : base(message) + { + Token = token; + } +} + +public class Env +{ + private readonly Env? enclosing; + private readonly Dictionary values = new Dictionary(); + + public Env() + { + this.enclosing = null; } + public Env(Env enclosing) + { + this.enclosing = enclosing; + } + + public object this[AST.Name name] + { + set + { + if (values.ContainsKey(name.Value)) + { + // TODO use real location info + var tok = new Token(TokenType.Identifier, name.Value, null, 1); + throw new RuntimeError(tok, $"Cannot redefine variable {name} in same scope."); + } + values[name.Value] = value; + } + get + { + try + { + return values[name.Value]; + } + catch + { + if (enclosing != null) + { + return enclosing[name]; + } + // TODO use real location info + var tok = new Token(TokenType.Identifier, name.Value, null, 1); + throw new RuntimeError(tok, $"Undefined variable {name}."); + } + } + } +} + +public class Interpreter : AST.IExprVisitor +{ private static void checkTypesEqual(object a, object b) { var aType = a.GetType(); @@ -156,7 +203,7 @@ public class Interpreter : AST.IExprVisitor { try { - var value = evaluate(expression); + var value = evaluate(new Env(), expression); Console.WriteLine(value); } catch (RuntimeError err) @@ -165,9 +212,9 @@ public class Interpreter : AST.IExprVisitor } } - private object evaluate(AST.Expr expr) + private object evaluate(Env env, AST.Expr expr) { - return expr.accept(this); + return expr.accept(env, this); } private List checkListOperand(Token op, Object operand) @@ -209,10 +256,10 @@ public class Interpreter : AST.IExprVisitor throw new RuntimeError(op, "Operand must be ."); } - public object visitBinaryExpr(AST.Binary expr) + public object visitBinaryExpr(Env env, AST.Binary expr) { - var left = evaluate(expr.Left); - var right = evaluate(expr.Right); + var left = evaluate(env, expr.Left); + var right = evaluate(env, expr.Right); switch (expr.Op.type) { @@ -265,42 +312,42 @@ public class Interpreter : AST.IExprVisitor throw new ArgumentException($"bad binary op: {expr.Op}"); } - public object visitCallExpr(AST.Call expr) + public object visitCallExpr(Env env, AST.Call expr) { throw new System.NotImplementedException(); } - public object visitGroupingExpr(AST.Grouping expr) + public object visitGroupingExpr(Env env, AST.Grouping expr) { - return evaluate(expr.Expression); + return evaluate(env, expr.Expression); } - public object visitVariableExpr(AST.Variable expr) + public object visitVariableExpr(Env env, AST.Variable expr) { - throw new System.NotImplementedException(); + return env[expr.Value]; } - public object visitIfExpr(AST.If expr) + public object visitIfExpr(Env env, AST.If expr) { - var cond = evaluate(expr.Condition); + var cond = evaluate(env, expr.Condition); // TODO Maybe I should token info in the AST. var vb = checkBoolOperand(new Token(TokenType.If, "if", null, 1), cond); if (vb == Variant.True) { - return evaluate(expr.Then); + return evaluate(env, expr.Then); } else { - return evaluate(expr.Else); + return evaluate(env, expr.Else); } } - public object visitIndexerExpr(AST.Indexer expr) + public object visitIndexerExpr(Env env, AST.Indexer expr) { // TODO use real token var tok = new Token(TokenType.LBracket, "[", null, 1); - var left = checkListOperand(tok, evaluate(expr.Left)); - var index = checkNumberOperand(tok, evaluate(expr.Index)); + var left = checkListOperand(tok, evaluate(env, expr.Left)); + var index = checkNumberOperand(tok, evaluate(env, expr.Index)); try { var item = left[index]; @@ -309,19 +356,42 @@ public class Interpreter : AST.IExprVisitor catch { return new Variant("nothing", null); } } - public object visitLetExpr(AST.Let expr) + public object visitLetExpr(Env env, AST.Let expr) { - throw new System.NotImplementedException(); + var newEnv = new Env(env); + foreach (var binding in expr.Bindings) + { + switch (binding) + { + case AST.VarBinding(var pattern, var valueExpr): + switch (pattern) + { + case AST.SimplePattern(var identifier): + var value = evaluate(env, valueExpr); + if (identifier != null) + { + newEnv[identifier] = value; + } + break; + default: + throw new NotImplementedException("TODO moar patternz"); + } + break; + default: + throw new NotImplementedException("TODO function bindings"); + } + } + return evaluate(newEnv, expr.Body); } - public object visitListExpr(AST.List expr) + public object visitListExpr(Env env, AST.List expr) { // TODO use real token var tok = new Token(TokenType.LBracket, "[", null, 1); List l = List.Empty; foreach (var itemExpr in expr.Elements) { - var item = evaluate(itemExpr); + var item = evaluate(env, itemExpr); if (!l.IsEmpty) { try { checkTypesEqual(l[0], item); } @@ -332,19 +402,19 @@ public class Interpreter : AST.IExprVisitor return l; } - public object visitLiteralExpr(AST.Literal expr) + public object visitLiteralExpr(Env env, AST.Literal expr) { return expr.Value; } - public object visitRecordExpr(AST.Record expr) + public object visitRecordExpr(Env env, AST.Record expr) { // TODO use real token Token tok = new Token(TokenType.LBrace, "{", null, 1); Record rec = Record.Empty; if (expr.Base != null) { - var baseRecValue = evaluate(expr.Base.Value); + var baseRecValue = evaluate(env, expr.Base.Value); if (baseRecValue is not Record) { throw new RuntimeError(tok, "Base record must be a record."); @@ -364,7 +434,7 @@ public class Interpreter : AST.IExprVisitor if (update.Value == null) throw new NotImplementedException(); try { - baseRec = baseRec.Update(label, evaluate(update.Value)); + baseRec = baseRec.Update(label, evaluate(env, update.Value)); } catch { @@ -386,15 +456,15 @@ public class Interpreter : AST.IExprVisitor } extLabels.Add(label); if (extension.Value == null) throw new NotImplementedException(); - rec = rec.Extend(label, evaluate(extension.Value)); + rec = rec.Extend(label, evaluate(env, extension.Value)); } return rec; } - public object visitSelectorExpr(AST.Selector expr) + public object visitSelectorExpr(Env env, AST.Selector expr) { - var left = evaluate(expr.Left); + var left = evaluate(env, expr.Left); // TODO Use real token. var tok = new Token(TokenType.Period, ".", null, 1); var r = checkRecordOperand(tok, left); @@ -408,15 +478,15 @@ public class Interpreter : AST.IExprVisitor } } - public object visitSequenceExpr(AST.Sequence expr) + public object visitSequenceExpr(Env env, AST.Sequence expr) { - evaluate(expr.Left); - return evaluate(expr.Right); + evaluate(env, expr.Left); + return evaluate(env, expr.Right); } - public object visitUnaryExpr(AST.Unary expr) + public object visitUnaryExpr(Env env, AST.Unary expr) { - object right = evaluate(expr.Right); + object right = evaluate(env, expr.Right); switch (expr.Op.type) { case TokenType.Minus: @@ -437,12 +507,12 @@ public class Interpreter : AST.IExprVisitor } } - public object visitVariantExpr(AST.Variant expr) + public object visitVariantExpr(Env env, AST.Variant expr) { return new Variant(expr.Tag.Value, expr.Argument); } - public object visitWhenExpr(AST.When expr) + public object visitWhenExpr(Env env, AST.When expr) { throw new System.NotImplementedException(); } diff --git a/Pattern.g.cs b/Pattern.g.cs index bde6150..78e8b50 100644 --- a/Pattern.g.cs +++ b/Pattern.g.cs @@ -8,39 +8,39 @@ namespace Finn.AST; public abstract record Pattern() { - public abstract T accept(IPatternVisitor visitor); + public abstract TResult accept(TContext context, IPatternVisitor visitor); } -public interface IPatternVisitor { - T visitSimplePatternPattern(SimplePattern pattern); - T visitVariantPatternPattern(VariantPattern pattern); - T visitFieldPatternPattern(FieldPattern pattern); - T visitRecordPatternPattern(RecordPattern pattern); +public interface IPatternVisitor { + TResult visitSimplePatternPattern(TContext context, SimplePattern pattern); + TResult visitVariantPatternPattern(TContext context, VariantPattern pattern); + TResult visitFieldPatternPattern(TContext context, FieldPattern pattern); + TResult visitRecordPatternPattern(TContext context, RecordPattern pattern); } public partial record SimplePattern(Name? Identifier) : Pattern() { - public override T accept(IPatternVisitor visitor) + public override TResult accept(TContext context, IPatternVisitor visitor) { - return visitor.visitSimplePatternPattern(this); + return visitor.visitSimplePatternPattern(context, this); } } public partial record VariantPattern(Name Tag, Pattern? Argument) : Pattern() { - public override T accept(IPatternVisitor visitor) + public override TResult accept(TContext context, IPatternVisitor visitor) { - return visitor.visitVariantPatternPattern(this); + return visitor.visitVariantPatternPattern(context, this); } } public partial record FieldPattern(Name Name, Pattern? Pattern) : Pattern() { - public override T accept(IPatternVisitor visitor) + public override TResult accept(TContext context, IPatternVisitor visitor) { - return visitor.visitFieldPatternPattern(this); + return visitor.visitFieldPatternPattern(context, this); } } public partial record RecordPattern(FieldPattern[] Fields, SimplePattern? Rest) : Pattern() { - public override T accept(IPatternVisitor visitor) + public override TResult accept(TContext context, IPatternVisitor visitor) { - return visitor.visitRecordPatternPattern(this); + return visitor.visitRecordPatternPattern(context, this); } } diff --git a/Program.cs b/Program.cs index 6ed5463..3859f5c 100644 --- a/Program.cs +++ b/Program.cs @@ -84,7 +84,7 @@ class Program } } - public static void runtimeError(Interpreter.RuntimeError err) + public static void runtimeError(RuntimeError err) { Console.Error.WriteLine($"{err.Message}\n[line {err.Token.line}]"); hadRuntimeError = true; diff --git a/ast_classes.fsx b/ast_classes.fsx index 988dcae..170afb3 100644 --- a/ast_classes.fsx +++ b/ast_classes.fsx @@ -64,10 +64,10 @@ let patternTypes = let visitorMethod baseName t = $"visit{t.Name}{baseName}" let renderIVisitor (sw: System.IO.StreamWriter) (baseName: string) types = - sw.Write($"public interface I{baseName}Visitor {{\n") + sw.Write($"public interface I{baseName}Visitor {{\n") for t in types do - sw.Write($"\tT {visitorMethod baseName t}({t.Name} {baseName.ToLower()});\n") + sw.Write($"\tTResult {visitorMethod baseName t}(TContext context, {t.Name} {baseName.ToLower()});\n") sw.Write("}\n") @@ -86,9 +86,9 @@ let renderType (sw: System.IO.StreamWriter) baseName t = {{\n" sw.Write - $"\tpublic override T accept(I{baseName}Visitor visitor) + $"\tpublic override TResult accept(TContext context, I{baseName}Visitor visitor) \t{{ -\t\treturn visitor.{visitorMethod baseName t}(this); +\t\treturn visitor.{visitorMethod baseName t}(context, this); \t}}\n" sw.Write("}\n") @@ -108,7 +108,7 @@ let renderAST outputDir baseName types = namespace Finn.AST; public abstract record {baseName}() {{ -\tpublic abstract T accept(I{baseName}Visitor visitor); +\tpublic abstract TResult accept(TContext context, I{baseName}Visitor visitor); }}\n" renderIVisitor sw baseName types