Implemented pattern matching in let-exprs
This commit is contained in:
parent
ca97c93181
commit
c12a0cffc8
103
Interpreter.cs
103
Interpreter.cs
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user