diff --git a/src/lib/lib.rs b/src/lib/lib.rs index 90472e142b9..518aea994db 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -10,27 +10,34 @@ pub mod syntax; #[cfg(feature = "wasm-bindgen")] mod wasm; +#[cfg(feature = "wasm-bindgen")] +pub use crate::wasm::*; use crate::{ builtins::value::ResultValue, exec::{Executor, Interpreter}, realm::Realm, syntax::{ast::expr::Expr, lexer::Lexer, parser::Parser}, }; -#[cfg(feature = "wasm-bindgen")] -pub use wasm::*; -fn parser_expr(src: &str) -> Expr { +fn parser_expr(src: &str) -> Result { let mut lexer = Lexer::new(src); - lexer.lex().expect("lexing failed"); + lexer.lex().map_err(|e| format!("SyntaxError: {}", e))?; let tokens = lexer.tokens; - Parser::new(tokens).parse_all().expect("parsing failed") + Parser::new(tokens) + .parse_all() + .map_err(|e| format!("ParsingError: {}", e)) } /// Execute the code using an existing Interpreter /// The str is consumed and the state of the Interpreter is changed pub fn forward(engine: &mut Interpreter, src: &str) -> String { // Setup executor - let expr = parser_expr(src); + let expr = match parser_expr(src) { + Ok(v) => v, + Err(error_string) => { + return error_string; + } + }; let result = engine.run(&expr); match result { Ok(v) => v.to_string(), @@ -44,7 +51,7 @@ pub fn forward(engine: &mut Interpreter, src: &str) -> String { /// If the interpreter fails parsing an error value is returned instead (error object) pub fn forward_val(engine: &mut Interpreter, src: &str) -> ResultValue { // Setup executor - let expr = parser_expr(src); + let expr = parser_expr(src).unwrap(); engine.run(&expr) } diff --git a/src/lib/syntax/lexer.rs b/src/lib/syntax/lexer.rs index a512c8fa397..0fa7c2e2293 100644 --- a/src/lib/syntax/lexer.rs +++ b/src/lib/syntax/lexer.rs @@ -15,7 +15,7 @@ use std::{ macro_rules! vop { ($this:ident, $assign_op:expr, $op:expr) => ({ - let preview = $this.preview_next().unwrap(); + let preview = $this.preview_next().expect("Could not preview next value"); match preview { '=' => { $this.next(); @@ -25,7 +25,7 @@ macro_rules! vop { } }); ($this:ident, $assign_op:expr, $op:expr, {$($case:pat => $block:expr), +}) => ({ - let preview = $this.preview_next().unwrap(); + let preview = $this.preview_next().expect("Could not preview next value"); match preview { '=' => { $this.next(); @@ -39,7 +39,7 @@ macro_rules! vop { } }); ($this:ident, $op:expr, {$($case:pat => $block:expr),+}) => { - let preview = $this.preview_next().unwrap(); + let preview = $this.preview_next().expect("Could not preview next value"); match preview { $($case => { $this.next()?; @@ -197,7 +197,7 @@ impl<'a> Lexer<'a> { result } - fn read_integer_in_base(&mut self, base: u32, mut buf: String) -> u64 { + fn read_integer_in_base(&mut self, base: u32, mut buf: String) -> Result { self.next(); while let Some(ch) = self.preview_next() { if ch.is_digit(base) { @@ -206,7 +206,8 @@ impl<'a> Lexer<'a> { break; } } - u64::from_str_radix(&buf, base).expect("Could not convert value to u64") + u64::from_str_radix(&buf, base) + .map_err(|_| LexerError::new("Could not convert value to u64")) } fn check_after_numeric_literal(&mut self) -> Result<(), LexerError> { @@ -285,8 +286,7 @@ impl<'a> Lexer<'a> { Ok(v) => v, Err(_) => 0, }; - let c = from_u32(as_num) - .expect("Invalid Unicode escape sequence"); + let c = from_u32(as_num).ok_or_else(|| LexerError::new("Invalid Unicode escape sequence"))?; self.next(); // '}' self.column_number += @@ -326,10 +326,10 @@ impl<'a> Lexer<'a> { } } '\'' | '"' | '\\' => escape, - ch => panic!( - "{}:{}: Invalid escape `{}`", - self.line_number, self.column_number, ch - ), + ch => { + let details = format!("{}:{}: Invalid escape `{}`", self.line_number, self.column_number, ch); + return Err(LexerError { details }); + } }; buf.push(escaped_ch); } @@ -353,13 +353,13 @@ impl<'a> Lexer<'a> { return Ok(()); } Some('x') | Some('X') => { - self.read_integer_in_base(16, buf) + self.read_integer_in_base(16, buf)? } Some('o') | Some('O') => { - self.read_integer_in_base(8, buf) + self.read_integer_in_base(8, buf)? } Some('b') | Some('B') => { - self.read_integer_in_base(2, buf) + self.read_integer_in_base(2, buf)? } Some(ch) if ch.is_ascii_digit() => { // LEGACY OCTAL (ONLY FOR NON-STRICT MODE) @@ -427,7 +427,7 @@ impl<'a> Lexer<'a> { } // TODO make this a bit more safe -------------------------------VVVV self.push_token(TokenData::NumericLiteral( - f64::from_str(&buf).expect("Could not convert value to f64"), + f64::from_str(&buf).map_err(|_| LexerError::new("Could not convert value to f64"))?, )) } _ if ch.is_alphabetic() || ch == '$' || ch == '_' => { @@ -617,10 +617,10 @@ impl<'a> Lexer<'a> { '\u{0020}' | '\u{0009}' | '\u{000B}' | '\u{000C}' | '\u{00A0}' | '\u{FEFF}' | // Unicode Space_Seperator category (minus \u{0020} and \u{00A0} which are allready stated above) '\u{1680}' | '\u{2000}'..='\u{200A}' | '\u{202F}' | '\u{205F}' | '\u{3000}' => (), - _ => panic!( - "{}:{}: Unexpected '{}'", - self.line_number, self.column_number, ch - ), + _ => { + let details = format!("{}:{}: Unexpected '{}'", self.line_number, self.column_number, ch); + return Err(LexerError { details }); + }, } } } diff --git a/src/lib/syntax/parser.rs b/src/lib/syntax/parser.rs index 819f7f36ced..73b0dec4799 100644 --- a/src/lib/syntax/parser.rs +++ b/src/lib/syntax/parser.rs @@ -5,6 +5,7 @@ use crate::syntax::ast::op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, Opera use crate::syntax::ast::punc::Punctuator; use crate::syntax::ast::token::{Token, TokenData}; use std::collections::btree_map::BTreeMap; +use std::fmt; /// `ParseError` is an enum which represents errors encounted during parsing an expression #[derive(Debug, Clone)] @@ -19,6 +20,28 @@ pub enum ParseError { AbruptEnd, } +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ParseError::Expected(expected, actual, routine) => write!( + f, + "Expected token '{}', got '{}' in routine '{}'", + expected + .first() + .map(|t| t.to_string()) + .unwrap_or_else(String::new), + actual, + routine + ), + ParseError::ExpectedExpr(expected, actual) => { + write!(f, "Expected expression '{}', got '{}'", expected, actual) + } + ParseError::UnexpectedKeyword(keyword) => write!(f, "Unexpected keyword: {}", keyword), + ParseError::AbruptEnd => write!(f, "Abrupt End"), + } + } +} + pub type ParseResult = Result; #[derive(Debug)]