Skip to content

Commit

Permalink
Fixed some panics in the lexer (#242)
Browse files Browse the repository at this point in the history
* Fixed some panics in the lexer
* Applied Requested Fixes
* Applied Requested Fixes
* Gave `ParseError` a basic `Display` impl
  • Loading branch information
adumbidiot committed Feb 4, 2020
1 parent 6947122 commit 18523c5
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 26 deletions.
21 changes: 14 additions & 7 deletions src/lib/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Expr, String> {
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(),
Expand All @@ -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)
}

Expand Down
38 changes: 19 additions & 19 deletions src/lib/syntax/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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();
Expand All @@ -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()?;
Expand Down Expand Up @@ -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<u64, LexerError> {
self.next();
while let Some(ch) = self.preview_next() {
if ch.is_digit(base) {
Expand All @@ -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> {
Expand Down Expand Up @@ -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 +=
Expand Down Expand Up @@ -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);
}
Expand All @@ -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)
Expand Down Expand Up @@ -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 == '_' => {
Expand Down Expand Up @@ -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 });
},
}
}
}
Expand Down
23 changes: 23 additions & 0 deletions src/lib/syntax/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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<Expr, ParseError>;

#[derive(Debug)]
Expand Down

0 comments on commit 18523c5

Please sign in to comment.