Implemented pattern matching in let-exprs

This commit is contained in:
Brandon Dyck 2023-07-11 12:59:15 -06:00
parent ca97c93181
commit c12a0cffc8

View File

@ -75,6 +75,79 @@ public class Interpreter : AST.IExprVisitor<Env, object>
}
}
private class PatternTagMismatchException : Exception
{
public PatternTagMismatchException(string patternTag, string valueTag) : base($"Pattern tag {patternTag} does not match value tag {valueTag}.") { }
}
private class PatternTypeMismatchException : Exception { }
class PatternBinder : AST.IPatternVisitor<(object, Env), ValueTuple>
{
public ValueTuple visitFieldPatternPattern((object, Env) context, AST.FieldPattern pattern)
{
return (pattern.Pattern ?? new AST.SimplePattern(pattern.Name)).accept(context, this);
}
public ValueTuple visitRecordPatternPattern((object, Env) context, AST.RecordPattern pattern)
{
var (obj, env) = context;
switch (obj)
{
case Record r:
var removedLabels = new List<string>();
foreach (var field in pattern.Fields)
{
removedLabels.Add(field.Name.Value);
var fieldValue = r.Get(field.Name.Value);
field.accept((fieldValue, env), this);
}
if (pattern.Rest != null)
{
var rest = r.Without(removedLabels);
pattern.Rest.accept((rest, env), this);
}
return ValueTuple.Create();
}
throw new PatternTypeMismatchException();
}
public ValueTuple visitSimplePatternPattern((object, Env) context, AST.SimplePattern pattern)
{
var (obj, env) = context;
if (pattern.Identifier == null)
{
return ValueTuple.Create();
}
env[pattern.Identifier] = obj;
return ValueTuple.Create();
}
public ValueTuple visitVariantPatternPattern((object, Env) context, AST.VariantPattern pattern)
{
var (obj, env) = context;
switch (obj)
{
case Variant v:
if (v.Tag != pattern.Tag.Value)
{
throw new PatternTagMismatchException(pattern.Tag.Value, v.Tag);
}
if (v.Value == null && pattern.Argument == null)
{
return ValueTuple.Create();
}
if (v.Value != null && pattern.Argument != null)
{
return pattern.Argument.accept((v.Value, env), this);
}
throw new PatternTypeMismatchException();
}
// TODO throw a better exception
throw new Exception($"Not a variant.");
}
}
private class List
{
public required ImmutableList<object> Items { get; init; }
@ -138,6 +211,11 @@ public class Interpreter : AST.IExprVisitor<Env, object>
return values.Peek();
}
public Record Without(IEnumerable<string> labels)
{
return new Record { Fields = Fields.RemoveRange(labels) };
}
public Record Remove(string name)
{
ImmutableStack<object>? values;
@ -364,17 +442,16 @@ public class Interpreter : AST.IExprVisitor<Env, object>
switch (binding)
{
case AST.VarBinding(var pattern, var valueExpr):
switch (pattern)
var value = evaluate(env, valueExpr);
try
{
case AST.SimplePattern(var identifier):
var value = evaluate(env, valueExpr);
if (identifier != null)
{
newEnv[identifier] = value;
}
break;
default:
throw new NotImplementedException("TODO moar patternz");
pattern.accept((value, newEnv), new PatternBinder());
}
catch (Exception e)
{
// TODO use real info
var tok = new Token(TokenType.Let, "let", null, 1);
throw new RuntimeError(tok, e.Message);
}
break;
default:
@ -509,7 +586,11 @@ public class Interpreter : AST.IExprVisitor<Env, object>
public object visitVariantExpr(Env env, AST.Variant expr)
{
return new Variant(expr.Tag.Value, expr.Argument);
if (expr.Argument == null)
{
return new Variant(expr.Tag.Value, null);
}
return new Variant(expr.Tag.Value, evaluate(env, expr.Argument));
}
public object visitWhenExpr(Env env, AST.When expr)