about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2020-12-19T12·13+0100
committertazjin <mail@tazj.in>2020-12-20T16·25+0000
commit5ebe28cca271e2ca38faa2429cdc9bcb05c44c42 (patch)
tree488844874d3a3b4fae8020828a11ce1c0beaffb1
parentbc6775c318206e1bda5e40b5a2e2411f910ad336 (diff)
feat(tazjin/rlox): Add runtime type error handling r/2019
Change-Id: I0d52bc9ff5be6421cb4131265ed28ce1ea7d8ff3
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2282
Reviewed-by: tazjin <mail@tazj.in>
Tested-by: BuildkiteCI
-rw-r--r--users/tazjin/rlox/src/errors.rs1
-rw-r--r--users/tazjin/rlox/src/interpreter.rs45
2 files changed, 32 insertions, 14 deletions
diff --git a/users/tazjin/rlox/src/errors.rs b/users/tazjin/rlox/src/errors.rs
index 9ea303829e7e..ae7e0515a025 100644
--- a/users/tazjin/rlox/src/errors.rs
+++ b/users/tazjin/rlox/src/errors.rs
@@ -4,6 +4,7 @@ pub enum ErrorKind {
     UnterminatedString,
     UnmatchedParens,
     ExpectedExpression(String),
+    TypeError(String),
 }
 
 #[derive(Debug)]
diff --git a/users/tazjin/rlox/src/interpreter.rs b/users/tazjin/rlox/src/interpreter.rs
index e7853ada4915..6c411ded5e11 100644
--- a/users/tazjin/rlox/src/interpreter.rs
+++ b/users/tazjin/rlox/src/interpreter.rs
@@ -1,4 +1,4 @@
-use crate::errors::{report, Error};
+use crate::errors::{report, Error, ErrorKind};
 use crate::parser::{self, Expr, Literal};
 use crate::scanner::{self, Token, TokenKind};
 
@@ -44,21 +44,28 @@ fn eval_truthy(lit: &Literal) -> bool {
     }
 }
 
-fn eval_unary<'a>(expr: &parser::Unary<'a>) -> Literal {
-    let right = eval(&*expr.right);
+fn eval_unary<'a>(expr: &parser::Unary<'a>) -> Result<Literal, Error> {
+    let right = eval(&*expr.right)?;
 
     match (&expr.operator.kind, right) {
-        (TokenKind::Minus, Literal::Number(num)) => Literal::Number(-num),
-        (TokenKind::Bang, right) => Literal::Boolean(!eval_truthy(&right)),
-        _ => unimplemented!("no handling of type errors"),
+        (TokenKind::Minus, Literal::Number(num)) => Ok(Literal::Number(-num)),
+        (TokenKind::Bang, right) => Ok(Literal::Boolean(!eval_truthy(&right))),
+
+        (op, right) => Err(Error {
+            line: expr.operator.line,
+            kind: ErrorKind::TypeError(format!(
+                "Operator '{:?}' can not be called with argument '{:?}'",
+                op, right
+            )),
+        }),
     }
 }
 
-fn eval_binary<'a>(expr: &parser::Binary<'a>) -> Literal {
-    let left = eval(&*expr.left);
-    let right = eval(&*expr.right);
+fn eval_binary<'a>(expr: &parser::Binary<'a>) -> Result<Literal, Error> {
+    let left = eval(&*expr.left)?;
+    let right = eval(&*expr.right)?;
 
-    match (&expr.operator.kind, left, right) {
+    let result = match (&expr.operator.kind, left, right) {
         // Numeric
         (TokenKind::Minus, Literal::Number(l), Literal::Number(r)) => Literal::Number(l - r),
         (TokenKind::Slash, Literal::Number(l), Literal::Number(r)) => Literal::Number(l / r),
@@ -82,13 +89,23 @@ fn eval_binary<'a>(expr: &parser::Binary<'a>) -> Literal {
         (TokenKind::Equal, l, r) => Literal::Boolean(l == r),
         (TokenKind::BangEqual, l, r) => Literal::Boolean(l != r),
 
-        _ => unimplemented!("type errors unhandled"),
-    }
+        (op, left, right) => {
+            return Err(Error {
+                line: expr.operator.line,
+                kind: ErrorKind::TypeError(format!(
+                    "Operator '{:?}' can not be called with arguments '({:?}, {:?})'",
+                    op, left, right
+                )),
+            })
+        }
+    };
+
+    Ok(result)
 }
 
-fn eval<'a>(expr: &Expr<'a>) -> Literal {
+fn eval<'a>(expr: &Expr<'a>) -> Result<Literal, Error> {
     match expr {
-        Expr::Literal(lit) => lit.clone(),
+        Expr::Literal(lit) => Ok(lit.clone()),
         Expr::Grouping(grouping) => eval(&*grouping.0),
         Expr::Unary(unary) => eval_unary(unary),
         Expr::Binary(binary) => eval_binary(binary),