about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVincent Ambo <mail@tazj.in>2021-01-18T00·21+0300
committertazjin <mail@tazj.in>2021-01-18T00·24+0000
commit1ff7a2686c2d7e405e597f9ac8a96189ec161d58 (patch)
tree1e6613d559744b9722329fa927dd78ce670581ca
parentd6d3c12efbcec61b3d868bc7d3f861fdb91835a5 (diff)
refactor(tazjin/rlox): Add Interpreter trait for switching impls r/2129
Change-Id: Iae28d64ce879014c5e5d7e145c536c1f16ad307d
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2418
Reviewed-by: tazjin <mail@tazj.in>
Tested-by: BuildkiteCI
-rw-r--r--users/tazjin/rlox/src/bytecode/errors.rs8
-rw-r--r--users/tazjin/rlox/src/bytecode/mod.rs34
-rw-r--r--users/tazjin/rlox/src/bytecode/vm.rs9
-rw-r--r--users/tazjin/rlox/src/main.rs59
-rw-r--r--users/tazjin/rlox/src/treewalk/errors.rs7
-rw-r--r--users/tazjin/rlox/src/treewalk/interpreter.rs46
-rw-r--r--users/tazjin/rlox/src/treewalk/interpreter/tests.rs8
-rw-r--r--users/tazjin/rlox/src/treewalk/mod.rs56
8 files changed, 125 insertions, 102 deletions
diff --git a/users/tazjin/rlox/src/bytecode/errors.rs b/users/tazjin/rlox/src/bytecode/errors.rs
index 89ab1867a4..c7871bc384 100644
--- a/users/tazjin/rlox/src/bytecode/errors.rs
+++ b/users/tazjin/rlox/src/bytecode/errors.rs
@@ -1,3 +1,5 @@
+use std::fmt;
+
 #[derive(Debug)]
 pub enum ErrorKind {
     // CompileError,
@@ -10,4 +12,10 @@ pub struct Error {
     pub kind: ErrorKind,
 }
 
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "[line NYI] Error: {:?}", self.kind)
+    }
+}
+
 pub type LoxResult<T> = Result<T, Error>;
diff --git a/users/tazjin/rlox/src/bytecode/mod.rs b/users/tazjin/rlox/src/bytecode/mod.rs
index 2d83c42722..31278f8c4b 100644
--- a/users/tazjin/rlox/src/bytecode/mod.rs
+++ b/users/tazjin/rlox/src/bytecode/mod.rs
@@ -9,19 +9,29 @@ mod value;
 mod vm;
 
 use chunk::Chunk;
-use opcode::OpCode;
+pub struct Interpreter {}
 
-pub fn main() {
-    let mut chunk: Chunk = Default::default();
+impl crate::Lox for Interpreter {
+    type Error = errors::Error;
+    type Value = value::Value;
 
-    let constant = chunk.add_constant(1.2);
-    chunk.add_op(OpCode::OpConstant(constant), 1);
+    fn create() -> Self {
+        Interpreter {}
+    }
 
-    let constant = chunk.add_constant(2.0);
-    chunk.add_op(OpCode::OpConstant(constant), 2);
-
-    chunk.add_op(OpCode::OpAdd, 3);
-    chunk.add_op(OpCode::OpReturn, 4);
-
-    vm::interpret(chunk).expect("it should work");
+    fn interpret(&mut self, _: String) -> Result<Self::Value, Vec<Self::Error>> {
+        let chunk: Chunk = Default::default();
+        vm::interpret(chunk).map_err(|e| vec![e])
+    }
 }
+
+// pub fn main() {
+//     let mut chunk: Chunk = Default::default();
+//     let constant = chunk.add_constant(1.2);
+//     chunk.add_op(OpCode::OpConstant(constant), 1);
+//     let constant = chunk.add_constant(2.0);
+//     chunk.add_op(OpCode::OpConstant(constant), 2);
+//     chunk.add_op(OpCode::OpAdd, 3);
+//     chunk.add_op(OpCode::OpReturn, 4);
+//     vm::interpret(chunk).expect("it should work");
+// }
diff --git a/users/tazjin/rlox/src/bytecode/vm.rs b/users/tazjin/rlox/src/bytecode/vm.rs
index fd91cd7be6..33bc358f44 100644
--- a/users/tazjin/rlox/src/bytecode/vm.rs
+++ b/users/tazjin/rlox/src/bytecode/vm.rs
@@ -32,7 +32,7 @@ macro_rules! binary_op {
 }
 
 impl VM {
-    fn run(&mut self) -> LoxResult<()> {
+    fn run(&mut self) -> LoxResult<Value> {
         loop {
             let op = &self.chunk.code[self.ip];
 
@@ -42,10 +42,7 @@ impl VM {
             self.ip += 1;
 
             match op {
-                OpCode::OpReturn => {
-                    println!("{:?}", self.pop());
-                    return Ok(());
-                }
+                OpCode::OpReturn => return Ok(self.pop()),
 
                 OpCode::OpConstant(idx) => {
                     let c = *self.chunk.constant(*idx);
@@ -66,7 +63,7 @@ impl VM {
     }
 }
 
-pub fn interpret(chunk: chunk::Chunk) -> LoxResult<()> {
+pub fn interpret(chunk: chunk::Chunk) -> LoxResult<Value> {
     let mut vm = VM {
         chunk,
         ip: 0,
diff --git a/users/tazjin/rlox/src/main.rs b/users/tazjin/rlox/src/main.rs
index e3bc278315..c9cc96d2e6 100644
--- a/users/tazjin/rlox/src/main.rs
+++ b/users/tazjin/rlox/src/main.rs
@@ -7,9 +7,64 @@ use std::process;
 mod bytecode;
 mod treewalk;
 
+/// Trait for making the different interpreters callable in the same
+/// way.
+pub trait Lox {
+    type Value;
+    type Error: std::fmt::Display;
+
+    fn create() -> Self;
+    fn interpret(&mut self, source: String) -> Result<Self::Value, Vec<Self::Error>>;
+}
+
 fn main() {
+    let mut args = env::args();
+    if args.len() > 2 {
+        println!("Usage: rlox [script]");
+        process::exit(1);
+    }
+
     match env::var("LOX_INTERPRETER").as_ref().map(String::as_str) {
-        Ok("treewalk") => treewalk::main(),
-        _ => bytecode::main(),
+        Ok("treewalk") => pick::<treewalk::interpreter::Interpreter>(args.nth(1)),
+        _ => pick::<bytecode::Interpreter>(args.nth(1)),
+    }
+}
+
+fn pick<I: Lox>(file_arg: Option<String>) {
+    if let Some(file) = file_arg {
+        run_file::<I>(&file);
+    } else {
+        run_prompt::<I>();
+    }
+}
+
+// Run Lox code from a file and print results to stdout
+fn run_file<I: Lox>(file: &str) {
+    let contents = fs::read_to_string(file).expect("failed to read the input file");
+    let mut lox = I::create();
+    run(&mut lox, contents);
+}
+
+// Evaluate Lox code interactively in a shitty REPL.
+fn run_prompt<I: Lox>() {
+    let mut line = String::new();
+    let mut lox = I::create();
+
+    loop {
+        print!("> ");
+        io::stdout().flush().unwrap();
+        io::stdin()
+            .read_line(&mut line)
+            .expect("failed to read user input");
+        run(&mut lox, std::mem::take(&mut line));
+        line.clear();
+    }
+}
+
+fn run<I: Lox>(lox: &mut I, code: String) {
+    if let Err(errors) = lox.interpret(code) {
+        for error in errors {
+            eprintln!("{}", error);
+        }
     }
 }
diff --git a/users/tazjin/rlox/src/treewalk/errors.rs b/users/tazjin/rlox/src/treewalk/errors.rs
index 3d5c28f9f3..54d2718eed 100644
--- a/users/tazjin/rlox/src/treewalk/errors.rs
+++ b/users/tazjin/rlox/src/treewalk/errors.rs
@@ -1,4 +1,5 @@
 use crate::treewalk::interpreter::Value;
+use std::fmt;
 
 #[derive(Debug)]
 pub enum ErrorKind {
@@ -33,6 +34,8 @@ pub struct Error {
     pub kind: ErrorKind,
 }
 
-pub fn report(err: &Error) {
-    eprintln!("[line {}] Error: {:?}", err.line, err.kind);
+impl fmt::Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "[line {}] Error: {:?}", self.line, self.kind)
+    }
 }
diff --git a/users/tazjin/rlox/src/treewalk/interpreter.rs b/users/tazjin/rlox/src/treewalk/interpreter.rs
index a096716d91..1263e6cb81 100644
--- a/users/tazjin/rlox/src/treewalk/interpreter.rs
+++ b/users/tazjin/rlox/src/treewalk/interpreter.rs
@@ -1,7 +1,8 @@
 use crate::treewalk::errors::{Error, ErrorKind};
 use crate::treewalk::parser::{self, Block, Expr, Literal, Statement};
+use crate::treewalk::resolver;
 use crate::treewalk::scanner::{self, TokenKind};
-use crate::treewalk::treewalk::resolver;
+use crate::Lox;
 use std::collections::HashMap;
 use std::rc::Rc;
 use std::sync::RwLock;
@@ -174,10 +175,13 @@ pub struct Interpreter {
     env: Rc<RwLock<Environment>>,
 }
 
-impl Interpreter {
+impl Lox for Interpreter {
+    type Value = Value;
+    type Error = Error;
+
     /// Create a new interpreter and configure the initial global
     /// variable set.
-    pub fn create() -> Self {
+    fn create() -> Self {
         let mut globals = HashMap::new();
 
         globals.insert(
@@ -193,6 +197,27 @@ impl Interpreter {
         }
     }
 
+    fn interpret(&mut self, code: String) -> Result<Value, Vec<Error>> {
+        let chars: Vec<char> = code.chars().collect();
+
+        let mut program = scanner::scan(&chars).and_then(|tokens| parser::parse(tokens))?;
+
+        let globals = self
+            .env
+            .read()
+            .expect("static globals lock poisoned")
+            .values
+            .keys()
+            .map(Clone::clone)
+            .collect::<Vec<String>>();
+
+        resolver::resolve(&globals, &mut program).map_err(|e| vec![e])?;
+        self.interpret_block_with_env(None, &program)
+            .map_err(|e| vec![e])
+    }
+}
+
+impl Interpreter {
     // Environment modification helpers
     fn define_var(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> {
         self.env
@@ -221,21 +246,6 @@ impl Interpreter {
             .get(ident, var.name.line, depth)
     }
 
-    // Interpreter itself
-    pub fn interpret(&mut self, mut program: Block) -> Result<Value, Error> {
-        let globals = self
-            .env
-            .read()
-            .expect("static globals lock poisoned")
-            .values
-            .keys()
-            .map(Clone::clone)
-            .collect::<Vec<String>>();
-
-        resolver::resolve(&globals, &mut program)?;
-        self.interpret_block_with_env(None, &program)
-    }
-
     /// Interpret the block in the supplied environment. If no
     /// environment is supplied, a new one is created using the
     /// current one as its parent.
diff --git a/users/tazjin/rlox/src/treewalk/interpreter/tests.rs b/users/tazjin/rlox/src/treewalk/interpreter/tests.rs
index 34b1df34b0..2fc6f4fee9 100644
--- a/users/tazjin/rlox/src/treewalk/interpreter/tests.rs
+++ b/users/tazjin/rlox/src/treewalk/interpreter/tests.rs
@@ -2,13 +2,9 @@ use super::*;
 
 /// Evaluate a code snippet, returning a value.
 fn parse_eval(code: &str) -> Value {
-    let chars: Vec<char> = code.chars().collect();
-    let tokens = scanner::scan(&chars).expect("could not scan code");
-    let program = parser::parse(tokens).expect("could not parse code");
-
     Interpreter::create()
-        .interpret(program)
-        .expect("could not eval code")
+        .interpret(code.into())
+        .expect("could not interpret code")
 }
 
 #[test]
diff --git a/users/tazjin/rlox/src/treewalk/mod.rs b/users/tazjin/rlox/src/treewalk/mod.rs
index b5db454ccc..d53bd13f8e 100644
--- a/users/tazjin/rlox/src/treewalk/mod.rs
+++ b/users/tazjin/rlox/src/treewalk/mod.rs
@@ -1,61 +1,5 @@
-use crate::*;
-
 mod errors;
 pub mod interpreter;
 mod parser;
 mod resolver;
 mod scanner;
-
-pub fn main() {
-    let mut args = env::args();
-
-    if args.len() > 2 {
-        println!("Usage: rlox [script]");
-        process::exit(1);
-    } else if let Some(file) = args.nth(1) {
-        run_file(&file);
-    } else {
-        run_prompt();
-    }
-}
-
-// Run Lox code from a file and print results to stdout
-fn run_file(file: &str) {
-    let contents = fs::read_to_string(file).expect("failed to read the input file");
-    let mut lox = treewalk::interpreter::Interpreter::create();
-    run(&mut lox, &contents);
-}
-
-// Evaluate Lox code interactively in a shitty REPL.
-fn run_prompt() {
-    let mut line = String::new();
-    let mut lox = treewalk::interpreter::Interpreter::create();
-
-    loop {
-        print!("> ");
-        io::stdout().flush().unwrap();
-        io::stdin()
-            .read_line(&mut line)
-            .expect("failed to read user input");
-        run(&mut lox, &line);
-        line.clear();
-    }
-}
-
-fn run(lox: &mut treewalk::interpreter::Interpreter, code: &str) {
-    let chars: Vec<char> = code.chars().collect();
-
-    let result = scanner::scan(&chars)
-        .and_then(|tokens| parser::parse(tokens))
-        .and_then(|program| lox.interpret(program).map_err(|e| vec![e]));
-
-    if let Err(errors) = result {
-        report_errors(errors);
-    }
-}
-
-fn report_errors(errors: Vec<errors::Error>) {
-    for error in errors {
-        errors::report(&error);
-    }
-}