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; namespace Finn.AST;
public abstract record Expr() { 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> { public interface IExprVisitor<TContext, TResult> {
T visitSequenceExpr(Sequence expr); TResult visitSequenceExpr(TContext context, Sequence expr);
T visitBinaryExpr(Binary expr); TResult visitBinaryExpr(TContext context, Binary expr);
T visitGroupingExpr(Grouping expr); TResult visitGroupingExpr(TContext context, Grouping expr);
T visitLiteralExpr(Literal expr); TResult visitLiteralExpr(TContext context, Literal expr);
T visitUnaryExpr(Unary expr); TResult visitUnaryExpr(TContext context, Unary expr);
T visitIfExpr(If expr); TResult visitIfExpr(TContext context, If expr);
T visitVariableExpr(Variable expr); TResult visitVariableExpr(TContext context, Variable expr);
T visitListExpr(List expr); TResult visitListExpr(TContext context, List expr);
T visitVariantExpr(Variant expr); TResult visitVariantExpr(TContext context, Variant expr);
T visitRecordExpr(Record expr); TResult visitRecordExpr(TContext context, Record expr);
T visitSelectorExpr(Selector expr); TResult visitSelectorExpr(TContext context, Selector expr);
T visitIndexerExpr(Indexer expr); TResult visitIndexerExpr(TContext context, Indexer expr);
T visitCallExpr(Call expr); TResult visitCallExpr(TContext context, Call expr);
T visitLetExpr(Let expr); TResult visitLetExpr(TContext context, Let expr);
T visitWhenExpr(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()
{ {
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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; namespace Finn;
public class Interpreter : AST.IExprVisitor<object>
{
public class RuntimeError : Exception public class RuntimeError : Exception
{ {
public readonly Token Token; 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) private static void checkTypesEqual(object a, object b)
{ {
var aType = a.GetType(); var aType = a.GetType();
@ -156,7 +203,7 @@ public class Interpreter : AST.IExprVisitor<object>
{ {
try try
{ {
var value = evaluate(expression); var value = evaluate(new Env(), expression);
Console.WriteLine(value); Console.WriteLine(value);
} }
catch (RuntimeError err) 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) 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>."); 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 left = evaluate(env, expr.Left);
var right = evaluate(expr.Right); var right = evaluate(env, expr.Right);
switch (expr.Op.type) switch (expr.Op.type)
{ {
@ -265,42 +312,42 @@ public class Interpreter : AST.IExprVisitor<object>
throw new ArgumentException($"bad binary op: {expr.Op}"); 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(); 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. // TODO Maybe I should token info in the AST.
var vb = checkBoolOperand(new Token(TokenType.If, "if", null, 1), cond); var vb = checkBoolOperand(new Token(TokenType.If, "if", null, 1), cond);
if (vb == Variant.True) if (vb == Variant.True)
{ {
return evaluate(expr.Then); return evaluate(env, expr.Then);
} }
else 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 // TODO use real token
var tok = new Token(TokenType.LBracket, "[", null, 1); var tok = new Token(TokenType.LBracket, "[", null, 1);
var left = checkListOperand(tok, evaluate(expr.Left)); var left = checkListOperand(tok, evaluate(env, expr.Left));
var index = checkNumberOperand(tok, evaluate(expr.Index)); var index = checkNumberOperand(tok, evaluate(env, expr.Index));
try try
{ {
var item = left[index]; var item = left[index];
@ -309,19 +356,42 @@ public class Interpreter : AST.IExprVisitor<object>
catch { return new Variant("nothing", null); } 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 // TODO use real token
var tok = new Token(TokenType.LBracket, "[", null, 1); var tok = new Token(TokenType.LBracket, "[", null, 1);
List l = List.Empty; List l = List.Empty;
foreach (var itemExpr in expr.Elements) foreach (var itemExpr in expr.Elements)
{ {
var item = evaluate(itemExpr); var item = evaluate(env, itemExpr);
if (!l.IsEmpty) if (!l.IsEmpty)
{ {
try { checkTypesEqual(l[0], item); } try { checkTypesEqual(l[0], item); }
@ -332,19 +402,19 @@ public class Interpreter : AST.IExprVisitor<object>
return l; return l;
} }
public object visitLiteralExpr(AST.Literal expr) public object visitLiteralExpr(Env env, AST.Literal expr)
{ {
return expr.Value; return expr.Value;
} }
public object visitRecordExpr(AST.Record expr) public object visitRecordExpr(Env env, AST.Record expr)
{ {
// TODO use real token // TODO use real token
Token tok = new Token(TokenType.LBrace, "{", null, 1); Token tok = new Token(TokenType.LBrace, "{", null, 1);
Record rec = Record.Empty; Record rec = Record.Empty;
if (expr.Base != null) if (expr.Base != null)
{ {
var baseRecValue = evaluate(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(tok, "Base record must be a record.");
@ -364,7 +434,7 @@ public class Interpreter : AST.IExprVisitor<object>
if (update.Value == null) throw new NotImplementedException(); if (update.Value == null) throw new NotImplementedException();
try try
{ {
baseRec = baseRec.Update(label, evaluate(update.Value)); baseRec = baseRec.Update(label, evaluate(env, update.Value));
} }
catch catch
{ {
@ -386,15 +456,15 @@ public class Interpreter : AST.IExprVisitor<object>
} }
extLabels.Add(label); extLabels.Add(label);
if (extension.Value == null) throw new NotImplementedException(); if (extension.Value == null) throw new NotImplementedException();
rec = rec.Extend(label, evaluate(extension.Value)); rec = rec.Extend(label, evaluate(env, extension.Value));
} }
return rec; 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. // TODO Use real token.
var tok = new Token(TokenType.Period, ".", null, 1); var tok = new Token(TokenType.Period, ".", null, 1);
var r = checkRecordOperand(tok, left); 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); evaluate(env, expr.Left);
return evaluate(expr.Right); 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) switch (expr.Op.type)
{ {
case TokenType.Minus: 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); 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(); throw new System.NotImplementedException();
} }

View File

@ -8,39 +8,39 @@
namespace Finn.AST; namespace Finn.AST;
public abstract record Pattern() { 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> { public interface IPatternVisitor<TContext, TResult> {
T visitSimplePatternPattern(SimplePattern pattern); TResult visitSimplePatternPattern(TContext context, SimplePattern pattern);
T visitVariantPatternPattern(VariantPattern pattern); TResult visitVariantPatternPattern(TContext context, VariantPattern pattern);
T visitFieldPatternPattern(FieldPattern pattern); TResult visitFieldPatternPattern(TContext context, FieldPattern pattern);
T visitRecordPatternPattern(RecordPattern pattern); TResult visitRecordPatternPattern(TContext context, RecordPattern pattern);
} }
public partial record SimplePattern(Name? Identifier) : 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 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 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 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}]"); Console.Error.WriteLine($"{err.Message}\n[line {err.Token.line}]");
hadRuntimeError = true; hadRuntimeError = true;

View File

@ -64,10 +64,10 @@ let patternTypes =
let visitorMethod baseName t = $"visit{t.Name}{baseName}" let visitorMethod baseName t = $"visit{t.Name}{baseName}"
let renderIVisitor (sw: System.IO.StreamWriter) (baseName: string) types = 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 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") sw.Write("}\n")
@ -86,9 +86,9 @@ let renderType (sw: System.IO.StreamWriter) baseName t =
{{\n" {{\n"
sw.Write 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{{
\t\treturn visitor.{visitorMethod baseName t}(this); \t\treturn visitor.{visitorMethod baseName t}(context, this);
\t}}\n" \t}}\n"
sw.Write("}\n") sw.Write("}\n")
@ -108,7 +108,7 @@ let renderAST outputDir baseName types =
namespace Finn.AST; namespace Finn.AST;
public abstract record {baseName}() {{ 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" }}\n"
renderIVisitor sw baseName types renderIVisitor sw baseName types