diff options
| -rw-r--r-- | src/compiler.rs | 84 | ||||
| -rw-r--r-- | src/error.rs | 48 | ||||
| -rw-r--r-- | src/main.rs | 5 | ||||
| -rw-r--r-- | src/vm.rs | 84 |
4 files changed, 128 insertions, 93 deletions
diff --git a/src/compiler.rs b/src/compiler.rs index 5808abe..0b7ff55 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,15 +1,11 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use crate::tokenizer::{Token, TokenStream}; use crate::vm::{Value, Block, Op}; - -struct Compiler { - curr: usize, - tokens: TokenStream, -} +use crate::error::{Error, ErrorKind}; macro_rules! nextable_enum { - ( $name:ident, $( $thing:ident ),* ) => { + ( $name:ident { $( $thing:ident ),* } ) => { #[derive(PartialEq, PartialOrd, Clone, Copy, Debug)] enum $name { $( $thing, )* @@ -25,25 +21,62 @@ macro_rules! nextable_enum { }; } -nextable_enum!(Prec, +macro_rules! error { + ( $thing:expr, $msg:expr) => { + $thing.error(ErrorKind::SyntaxError($thing.line().unwrap()), Some(String::from($msg))) + }; +} + +macro_rules! expect { + ($thing:expr, $exp:pat, $msg:expr) => { + match $thing.peek() { + $exp => $thing.eat(), + _ => error!($thing, $msg), + } + }; +} + +nextable_enum!(Prec { No, Assert, Bool, Comp, Term, Factor -); +}); + +struct Compiler { + curr: usize, + tokens: TokenStream, + current_file: PathBuf, + panic: bool, + errors: Vec<Error>, +} impl Compiler { - pub fn new(tokens: TokenStream) -> Self { + pub fn new(current_file: &Path, tokens: TokenStream) -> Self { Self { curr: 0, tokens, + current_file: PathBuf::from(current_file), + panic: false, + errors: vec![], } } - fn error(&self, msg: &str) -> ! { - println!("ERROR: {} line {:?}", msg, self.line()); + fn clear_panic(&mut self) { + self.panic = false; + } + + fn error(&mut self, kind: ErrorKind, message: Option<String>) -> ! { + if self.panic { panic!(); } + self.panic = true; + self.errors.push(Error { + kind: kind, + file: self.current_file.clone(), + line: self.line().unwrap(), + message: message, + }); panic!(); } @@ -132,28 +165,24 @@ impl Compiler { Token::Float(f) => { Value::Float(f) }, Token::Int(i) => { Value::Int(i) } Token::Bool(b) => { Value::Bool(b) } - _ => { self.error("Invalid value.") } + _ => { error!(self, "Cannot parse value.") } }; block.add(Op::Constant(value), self.line()); } fn grouping(&mut self, block: &mut Block) { - if Token::LeftParen != self.eat() { - self.error("Expected left parenthesis around expression."); - } + expect!(self, Token::LeftParen, "Expected '(' around expression."); self.expression(block); - if Token::RightParen != self.eat() { - self.error("Expected closing parenthesis after expression."); - } + expect!(self, Token::RightParen, "Expected ')' around expression."); } fn unary(&mut self, block: &mut Block) { let op = match self.eat() { Token::Minus => Op::Neg, Token::Not => Op::Not, - _ => self.error("Invalid unary operator"), + _ => error!(self, "Invalid unary operator"), }; self.parse_precedence(block, Prec::Factor); block.add(op, self.line()); @@ -176,7 +205,7 @@ impl Compiler { Token::NotEqual => &[Op::Equal, Op::Not], Token::LessEqual => &[Op::Greater, Op::Not], Token::GreaterEqual => &[Op::Less, Op::Not], - _ => { self.error("Illegal operator"); } + _ => { error!(self, "Illegal operator"); } }; block.add_from(op, self.line()); } @@ -186,9 +215,8 @@ impl Compiler { } fn parse_precedence(&mut self, block: &mut Block, precedence: Prec) { - println!("-- {:?}", self.peek()); if !self.prefix(self.peek(), block) { - self.error("Expected expression."); + error!(self, "Invalid expression."); } while precedence <= self.precedence(self.peek()) { @@ -204,17 +232,13 @@ impl Compiler { self.eat(); self.expression(block); block.add(Op::Print, self.line()); - if self.eat() != Token::Newline { - self.error("Expect newline after expression."); - } + expect!(self, Token::Newline, "Expect newline after expression."); }, _ => { self.expression(block); - if self.eat() != Token::Newline { - self.error("Expect newline after expression."); - } block.add(Op::Pop, None); + expect!(self, Token::Newline, "Expect newline after expression."); } } } @@ -237,5 +261,5 @@ impl Compiler { } pub fn compile(name: &str, file: &Path, tokens: TokenStream) -> Block { - Compiler::new(tokens).compile(name, file) + Compiler::new(file, tokens).compile(name, file) } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..277be5f --- /dev/null +++ b/src/error.rs @@ -0,0 +1,48 @@ +use std::fmt; +use std::path::PathBuf; +use crate::vm::{Op, Value}; + +#[derive(Debug)] +pub enum ErrorKind { + TypeError(Op, Vec<Value>), + AssertFailed(Value, Value), + SyntaxError(usize), +} + +#[derive(Debug)] +pub struct Error { + pub kind: ErrorKind, + pub file: PathBuf, + pub line: usize, + pub message: Option<String>, +} + +impl fmt::Display for ErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ErrorKind::TypeError(op, values) => { + let values = values + .iter() + .fold(String::new(), |a, v| { format!("{}, {:?}", a, v) }); + write!(f, "Cannot apply {:?} to values {}", op, values) + } + ErrorKind::AssertFailed(a, b) => { + write!(f, "Assertion failed, {:?} != {:?}.", a, b) + } + ErrorKind::SyntaxError(line) => { + write!(f, "Syntax error on line {}", line) + } + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let message = match &self.message { + Some(s) => format!("\n{}", s), + None => String::from(""), + }; + write!(f, "{:?}:{} [Runtime Error] {}{}", self.file, self.line, self.kind, message) + } +} + diff --git a/src/main.rs b/src/main.rs index ed85961..bcda641 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,12 @@ use std::path::{Path, PathBuf}; +mod error; mod tokenizer; mod vm; mod compiler; +use error::Error; + fn main() { let file = file_from_args().unwrap_or_else(|| Path::new("tests/simple.tdy").to_owned()); if let Err(err) = run_file(&file) { @@ -15,7 +18,7 @@ fn file_from_args() -> Option<PathBuf> { std::env::args().skip(1).map(|s| Path::new(&s).to_owned()).find(|p| p.is_file()) } -fn run_file(path: &Path) -> Result<(), vm::Error> { +fn run_file(path: &Path) -> Result<(), Error> { let tokens = tokenizer::file_to_tokens(path); let block = compiler::compile("main", path, tokens); // path -> str might fail vm::run_block(block) @@ -1,6 +1,16 @@ -use std::collections::HashMap; -use std::fmt; use std::path::{Path, PathBuf}; +use std::collections::HashMap; + +use crate::error::{Error, ErrorKind}; + +macro_rules! error { + ( $thing:expr, $kind:expr) => { + return Err($thing.error($kind, None)); + }; + ( $thing:expr, $kind:expr, $msg:expr) => { + return Err($thing.error($kind, Some($msg))); + }; +} #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] pub enum Value { @@ -82,47 +92,6 @@ pub struct VM { ip: usize, } -#[derive(Debug)] -pub enum VMErrorKind { - TypeError(Op, Vec<Value>), - AssertFailed(Value, Value), -} - -#[derive(Debug)] -pub struct Error { - kind: VMErrorKind, - file: PathBuf, - line: usize, - message: Option<String>, -} - -impl fmt::Display for VMErrorKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - VMErrorKind::TypeError(op, values) => { - let values = values - .iter() - .fold(String::new(), |a, v| { format!("{}, {:?}", a, v) }); - write!(f, "Cannot apply {:?} to values {}", op, values) - } - VMErrorKind::AssertFailed(a, b) => { - write!(f, "Assertion failed, {:?} != {:?}.", a, b) - } - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let message = match &self.message { - Some(s) => format!("\n{}", s), - None => String::from(""), - }; - write!(f, "{:?}:{} [Runtime Error] {}{}", self.file, self.line, self.kind, message) - } -} - - pub fn run_block(block: Block) -> Result<(), Error> { let mut vm = VM { stack: Vec::new(), @@ -134,15 +103,6 @@ pub fn run_block(block: Block) -> Result<(), Error> { vm.run() } -macro_rules! error { - ( $vm:expr, $kind:expr) => { - return Err($vm.error($kind, None)); - }; - ( $vm:expr, $kind:expr, $msg:expr) => { - return Err($vm.error($kind, Some($msg))); - }; -} - impl VM { fn pop_twice(&mut self) -> (Value, Value) { let (a, b) = (self.stack.pop().unwrap(), self.stack.pop().unwrap()); @@ -153,7 +113,7 @@ impl VM { self.stack.get(self.stack.len() - amount) } - fn error(&self, kind: VMErrorKind, message: Option<String>) -> Error { + fn error(&self, kind: ErrorKind, message: Option<String>) -> Error { let find_line = || { for i in (0..=self.ip).rev() { if let Some(line) = self.block.line_offsets.get(&i) { @@ -208,7 +168,7 @@ impl VM { match self.stack.pop().unwrap() { Value::Float(a) => self.stack.push(Value::Float(-a)), Value::Int(a) => self.stack.push(Value::Int(-a)), - a => error!(self, VMErrorKind::TypeError(op, vec![a])), + a => error!(self, ErrorKind::TypeError(op, vec![a])), } } @@ -216,7 +176,7 @@ impl VM { match self.pop_twice() { (Value::Float(a), Value::Float(b)) => self.stack.push(Value::Float(b + a)), (Value::Int(a), Value::Int(b)) => self.stack.push(Value::Int(b + a)), - (a, b) => error!(self, VMErrorKind::TypeError(op, vec![a, b])), + (a, b) => error!(self, ErrorKind::TypeError(op, vec![a, b])), } } @@ -224,7 +184,7 @@ impl VM { match self.pop_twice() { (Value::Float(a), Value::Float(b)) => self.stack.push(Value::Float(b - a)), (Value::Int(a), Value::Int(b)) => self.stack.push(Value::Int(b - a)), - (a, b) => error!(self, VMErrorKind::TypeError(op, vec![a, b])), + (a, b) => error!(self, ErrorKind::TypeError(op, vec![a, b])), } } @@ -232,7 +192,7 @@ impl VM { match self.pop_twice() { (Value::Float(a), Value::Float(b)) => self.stack.push(Value::Float(b * a)), (Value::Int(a), Value::Int(b)) => self.stack.push(Value::Int(b * a)), - (a, b) => error!(self, VMErrorKind::TypeError(op, vec![a, b])), + (a, b) => error!(self, ErrorKind::TypeError(op, vec![a, b])), } } @@ -240,7 +200,7 @@ impl VM { match self.pop_twice() { (Value::Float(a), Value::Float(b)) => self.stack.push(Value::Float(b / a)), (Value::Int(a), Value::Int(b)) => self.stack.push(Value::Int(b / a)), - (a, b) => error!(self, VMErrorKind::TypeError(op, vec![a, b])), + (a, b) => error!(self, ErrorKind::TypeError(op, vec![a, b])), } } @@ -262,28 +222,28 @@ impl VM { Op::And => { match self.pop_twice() { (Value::Bool(a), Value::Bool(b)) => self.stack.push(Value::Bool(a && b)), - (a, b) => error!(self, VMErrorKind::TypeError(op, vec![a, b])), + (a, b) => error!(self, ErrorKind::TypeError(op, vec![a, b])), } } Op::Or => { match self.pop_twice() { (Value::Bool(a), Value::Bool(b)) => self.stack.push(Value::Bool(a || b)), - (a, b) => error!(self, VMErrorKind::TypeError(op, vec![a, b])), + (a, b) => error!(self, ErrorKind::TypeError(op, vec![a, b])), } } Op::Not => { match self.stack.pop().unwrap() { Value::Bool(a) => self.stack.push(Value::Bool(!a)), - a => error!(self, VMErrorKind::TypeError(op, vec![a])), + a => error!(self, ErrorKind::TypeError(op, vec![a])), } } Op::AssertEqual => { let (a, b) = self.pop_twice(); if a != b { - error!(self, VMErrorKind::AssertFailed(a, b)); + error!(self, ErrorKind::AssertFailed(a, b)); } self.stack.push(Value::Bool(a == b)); } |
