diff --git a/AST.cs b/AST.cs index d9d85b1..1b2e2bb 100644 --- a/AST.cs +++ b/AST.cs @@ -42,9 +42,9 @@ public partial record SimplePattern } } -public abstract record Binding(Expr Value); -public record VarBinding(Pattern Pattern, Expr Value) : Binding(Value); -public record FuncBinding(Token Name, Pattern[] Params, Expr Value) : Binding(Value) +public abstract record Binding(Expr Value, Token Start); +public record VarBinding(Pattern Pattern, Expr Value) : Binding(Value, Pattern.Start); +public record FuncBinding(Token Name, Pattern[] Params, Expr Value) : Binding(Value, Name) { public override string ToString() { diff --git a/Interpreter.cs b/Interpreter.cs index adb19b7..a1ba45a 100644 --- a/Interpreter.cs +++ b/Interpreter.cs @@ -38,7 +38,6 @@ public class Env var name = (string)identifier.Literal!; if (values.ContainsKey(name)) { - // TODO use real location info throw new RuntimeError(identifier, $"Cannot redefine variable {name} in same scope."); } values[name] = value; @@ -56,7 +55,6 @@ public class Env { return enclosing[identifier]; } - // TODO use real location info throw new RuntimeError(identifier, $"Undefined variable {name}."); } } @@ -75,12 +73,19 @@ public class Interpreter : AST.IExprVisitor } } - private class PatternTagMismatchException : Exception + private class PatternMismatchException : Exception { - public PatternTagMismatchException(string patternTag, string valueTag) : base($"Pattern tag {patternTag} does not match value tag {valueTag}.") { } + public readonly Token Start; + public PatternMismatchException(Token start, string message) : base(message) + { + Start = start; + } } - private class PatternTypeMismatchException : Exception { } + private class VariantTagMismatchException : PatternMismatchException + { + public VariantTagMismatchException(Token start, string patternTag, string valueTag) : base(start, $"Pattern tag {patternTag} does not match value tag {valueTag}.") { } + } class PatternBinder : AST.IPatternVisitor<(object, Env), ValueTuple> { @@ -110,7 +115,7 @@ public class Interpreter : AST.IExprVisitor } return ValueTuple.Create(); } - throw new PatternTypeMismatchException(); + throw new PatternMismatchException(pattern.Start, "Matched value {obj} is not a record."); } public ValueTuple visitSimplePatternPattern((object, Env) context, AST.SimplePattern pattern) @@ -133,7 +138,7 @@ public class Interpreter : AST.IExprVisitor var tag = (string)pattern.Tag.Literal!; if (v.Tag != tag) { - throw new PatternTagMismatchException(tag, v.Tag); + throw new VariantTagMismatchException(pattern.Start, tag, v.Tag); } if (v.Value == null && pattern.Argument == null) { @@ -143,10 +148,9 @@ public class Interpreter : AST.IExprVisitor { return pattern.Argument.accept((v.Value, env), this); } - throw new PatternTypeMismatchException(); + throw new PatternMismatchException(pattern.Start, "Variant pattern arity does not match variant value."); } - // TODO throw a better exception - throw new Exception($"Not a variant."); + throw new PatternMismatchException(pattern.Start, $"Matched value {obj} is not a variant."); } } @@ -448,9 +452,10 @@ public class Interpreter : AST.IExprVisitor } catch (Exception e) { - // TODO use real info - var tok = new Token(TokenType.Let, "let", null, new(0, 1, 1), new(0, 1, 1)); - throw new RuntimeError(tok, e.Message); + var start = e is PatternMismatchException ? + ((PatternMismatchException)e).Start : + binding.Start; + throw new RuntimeError(start, e.Message); } break; default: @@ -593,8 +598,6 @@ public class Interpreter : AST.IExprVisitor public object visitWhenExpr(Env env, AST.When expr) { var head = evaluate(env, expr.Head); - // TODO use real info - var tok = new Token(TokenType.When, "when", null, new(0, 1, 1), new(0, 1, 1)); foreach (var c in expr.Cases) { try @@ -603,15 +606,15 @@ public class Interpreter : AST.IExprVisitor c.Pattern.accept((head, newEnv), new PatternBinder()); return evaluate(newEnv, c.Value); } - catch (PatternTagMismatchException) + catch (VariantTagMismatchException) { continue; } - catch (PatternTypeMismatchException e) + catch (PatternMismatchException e) { - throw new RuntimeError(tok, e.Message); + throw new RuntimeError(e.Start, e.Message); } } - throw new RuntimeError(tok, "No matching patterns."); + throw new RuntimeError(expr.Start, "No matching patterns."); } } \ No newline at end of file