From c12a0cffc86558510217b6008bc6b2ac9ef77687 Mon Sep 17 00:00:00 2001 From: Brandon Dyck Date: Tue, 11 Jul 2023 12:59:15 -0600 Subject: [PATCH] Implemented pattern matching in let-exprs --- Interpreter.cs | 103 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 92 insertions(+), 11 deletions(-) diff --git a/Interpreter.cs b/Interpreter.cs index b0261ae..45aeee7 100644 --- a/Interpreter.cs +++ b/Interpreter.cs @@ -75,6 +75,79 @@ public class Interpreter : AST.IExprVisitor } } + 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(); + 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 Items { get; init; } @@ -138,6 +211,11 @@ public class Interpreter : AST.IExprVisitor return values.Peek(); } + public Record Without(IEnumerable labels) + { + return new Record { Fields = Fields.RemoveRange(labels) }; + } + public Record Remove(string name) { ImmutableStack? values; @@ -364,17 +442,16 @@ public class Interpreter : AST.IExprVisitor 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 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)