Evaluate let-exprs with simple bindings

This commit is contained in:
Brandon Dyck 2023-07-11 00:51:51 -06:00
parent 108870c731
commit ca97c93181
5 changed files with 181 additions and 111 deletions

View File

@ -8,127 +8,127 @@
namespace Finn.AST;
public abstract record Expr() {
public abstract T accept<T>(IExprVisitor<T> visitor);
public abstract TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor);
}
public interface IExprVisitor<T> {
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<TContext, TResult> {
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<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> 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<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{
return visitor.visitBinaryExpr(this);
return visitor.visitBinaryExpr(context, this);
}
}
public partial record Grouping(Expr Expression) : Expr()
{
public override T accept<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{
return visitor.visitGroupingExpr(this);
return visitor.visitGroupingExpr(context, this);
}
}
public partial record Literal(System.Object Value) : Expr()
{
public override T accept<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{
return visitor.visitLiteralExpr(this);
return visitor.visitLiteralExpr(context, this);
}
}
public partial record Unary(Token Op, Expr Right) : Expr()
{
public override T accept<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> 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<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{
return visitor.visitIfExpr(this);
return visitor.visitIfExpr(context, this);
}
}
public partial record Variable(Name Value) : Expr()
{
public override T accept<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{
return visitor.visitVariableExpr(this);
return visitor.visitVariableExpr(context, this);
}
}
public partial record List(Expr[] Elements) : Expr()
{
public override T accept<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{
return visitor.visitListExpr(this);
return visitor.visitListExpr(context, this);
}
}
public partial record Variant(Name Tag, Expr? Argument) : Expr()
{
public override T accept<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{
return visitor.visitVariantExpr(this);
return visitor.visitVariantExpr(context, this);
}
}
public partial record Record(Field[] Extensions, BaseRecord? Base) : Expr()
{
public override T accept<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{
return visitor.visitRecordExpr(this);
return visitor.visitRecordExpr(context, this);
}
}
public partial record Selector(Expr Left, Name FieldName) : Expr()
{
public override T accept<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{
return visitor.visitSelectorExpr(this);
return visitor.visitSelectorExpr(context, this);
}
}
public partial record Indexer(Expr Left, Expr Index) : Expr()
{
public override T accept<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{
return visitor.visitIndexerExpr(this);
return visitor.visitIndexerExpr(context, this);
}
}
public partial record Call(Expr Left, Expr?[] Arguments) : Expr()
{
public override T accept<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{
return visitor.visitCallExpr(this);
return visitor.visitCallExpr(context, this);
}
}
public partial record Let(Binding[] Bindings, Expr Body) : Expr()
{
public override T accept<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{
return visitor.visitLetExpr(this);
return visitor.visitLetExpr(context, this);
}
}
public partial record When(Expr Head, Binding[] Cases) : Expr()
{
public override T accept<T>(IExprVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IExprVisitor<TContext, TResult> visitor)
{
return visitor.visitWhenExpr(this);
return visitor.visitWhenExpr(context, this);
}
}

View File

@ -6,8 +6,6 @@ using AST = Finn.AST;
namespace Finn;
public class Interpreter : AST.IExprVisitor<object>
{
public class RuntimeError : Exception
{
public readonly Token Token;
@ -18,6 +16,55 @@ public class Interpreter : AST.IExprVisitor<object>
}
}
public class Env
{
private readonly Env? enclosing;
private readonly Dictionary<string, object> values = new Dictionary<string, object>();
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<Env, object>
{
private static void checkTypesEqual(object a, object b)
{
var aType = a.GetType();
@ -156,7 +203,7 @@ public class Interpreter : AST.IExprVisitor<object>
{
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<object>
}
}
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<object>
throw new RuntimeError(op, "Operand must be <true,false>.");
}
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<object>
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<object>
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<object>
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<object>
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<object>
}
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<object>
}
}
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<object>
}
}
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();
}

View File

@ -8,39 +8,39 @@
namespace Finn.AST;
public abstract record Pattern() {
public abstract T accept<T>(IPatternVisitor<T> visitor);
public abstract TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor);
}
public interface IPatternVisitor<T> {
T visitSimplePatternPattern(SimplePattern pattern);
T visitVariantPatternPattern(VariantPattern pattern);
T visitFieldPatternPattern(FieldPattern pattern);
T visitRecordPatternPattern(RecordPattern pattern);
public interface IPatternVisitor<TContext, TResult> {
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<T>(IPatternVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor)
{
return visitor.visitSimplePatternPattern(this);
return visitor.visitSimplePatternPattern(context, this);
}
}
public partial record VariantPattern(Name Tag, Pattern? Argument) : Pattern()
{
public override T accept<T>(IPatternVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor)
{
return visitor.visitVariantPatternPattern(this);
return visitor.visitVariantPatternPattern(context, this);
}
}
public partial record FieldPattern(Name Name, Pattern? Pattern) : Pattern()
{
public override T accept<T>(IPatternVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor)
{
return visitor.visitFieldPatternPattern(this);
return visitor.visitFieldPatternPattern(context, this);
}
}
public partial record RecordPattern(FieldPattern[] Fields, SimplePattern? Rest) : Pattern()
{
public override T accept<T>(IPatternVisitor<T> visitor)
public override TResult accept<TContext, TResult>(TContext context, IPatternVisitor<TContext, TResult> visitor)
{
return visitor.visitRecordPatternPattern(this);
return visitor.visitRecordPatternPattern(context, this);
}
}

View File

@ -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;

View File

@ -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<T> {{\n")
sw.Write($"public interface I{baseName}Visitor<TContext, TResult> {{\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<T>(I{baseName}Visitor<T> visitor)
$"\tpublic override TResult accept<TContext, TResult>(TContext context, I{baseName}Visitor<TContext, TResult> 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<T>(I{baseName}Visitor<T> visitor);
\tpublic abstract TResult accept<TContext, TResult>(TContext context, I{baseName}Visitor<TContext, TResult> visitor);
}}\n"
renderIVisitor sw baseName types