Refactored the parser

Added error checking to the parser and changed it from being a struct to
independent functions with a context struct instead.
This commit is contained in:
PaddiM8 2020-05-29 23:21:59 +02:00
parent aacb787bce
commit 1c65fbed08
4 changed files with 255 additions and 247 deletions

View File

@ -12,7 +12,6 @@ pub struct Interpreter<'a> {
impl<'a> Interpreter<'a> { impl<'a> Interpreter<'a> {
pub fn new(angle_unit: Unit, symbol_table: &'a mut SymbolTable) -> Self { pub fn new(angle_unit: Unit, symbol_table: &'a mut SymbolTable) -> Self {
//let mut hashmap: HashMap<String, Stmt> = HashMap::new();
for constant in prelude::CONSTANTS { for constant in prelude::CONSTANTS {
symbol_table.insert( symbol_table.insert(
constant.0, constant.0,

View File

@ -6,16 +6,14 @@ mod parser;
mod prelude; mod prelude;
mod symbol_table; mod symbol_table;
mod visitor; mod visitor;
use parser::{Parser, Unit}; use parser::{ParserContext, Unit};
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::Editor; use rustyline::Editor;
#[allow(unused_assignments)] // The compiler gives a warning that is not valid. #[allow(unused_assignments)] // The compiler gives a warning that is not valid.
fn main() { fn main() {
let angle_unit = get_angle_unit(); let mut parser = ParserContext::new();
let mut parser = Parser::new();
parser.angle_unit = angle_unit;
// Command line argument input, execute it and exit. // Command line argument input, execute it and exit.
if let Some(expr) = env::args().skip(1).next() { if let Some(expr) = env::args().skip(1).next() {
@ -40,7 +38,7 @@ fn main() {
} }
} }
fn eval_repl(parser: &mut Parser, input: &str) { fn eval_repl(parser: &mut ParserContext, input: &str) {
match input { match input {
"" => eprint!(""), "" => eprint!(""),
"clear" => print!("\x1B[2J"), "clear" => print!("\x1B[2J"),
@ -49,9 +47,10 @@ fn eval_repl(parser: &mut Parser, input: &str) {
} }
} }
fn eval(parser: &mut Parser, input: &str) { fn eval(parser: &mut ParserContext, input: &str) {
if let Some(result) = parser.parse(input) { match parser::parse(parser, input, get_angle_unit()) {
println!("{}", result); Ok(result) => println!("{}", result),
Err(_) => println!("Invalid expression"),
} }
} }

View File

@ -3,7 +3,6 @@ use std::mem;
use crate::{ use crate::{
interpreter::Interpreter, interpreter::Interpreter,
lexer::{Lexer, Token, TokenKind}, lexer::{Lexer, Token, TokenKind},
prelude,
symbol_table::SymbolTable, symbol_table::SymbolTable,
}; };
@ -31,8 +30,8 @@ pub enum Unit {
Degrees, Degrees,
} }
pub struct Parser { pub struct ParserContext {
pub angle_unit: Unit, //angle_unit: Unit,
tokens: Vec<Token>, tokens: Vec<Token>,
pos: usize, pos: usize,
symbol_table: SymbolTable, symbol_table: SymbolTable,
@ -51,7 +50,7 @@ impl TokenKind {
} }
} }
impl Parser { /*impl Parser {
pub fn new() -> Parser { pub fn new() -> Parser {
Parser { Parser {
tokens: Vec::new(), tokens: Vec::new(),
@ -60,39 +59,50 @@ impl Parser {
angle_unit: prelude::DEFAULT_ANGLE_UNIT, angle_unit: prelude::DEFAULT_ANGLE_UNIT,
} }
} }
}*/
pub fn parse(&mut self, input: &str) -> Option<f64> { impl ParserContext {
self.tokens = Lexer::lex(input); pub fn new() -> Self {
self.pos = 0; ParserContext {
tokens: Vec::new(),
pos: 0,
symbol_table: SymbolTable::new(),
}
}
}
pub fn parse(context: &mut ParserContext, input: &str, angle_unit: Unit) -> Result<f64, String> {
context.tokens = Lexer::lex(input);
let mut statements: Vec<Stmt> = Vec::new(); let mut statements: Vec<Stmt> = Vec::new();
while !self.is_at_end() { while !is_at_end(context) {
statements.push(self.parse_stmt()); statements.push(parse_stmt(context)?);
} }
Interpreter::new(self.angle_unit.clone(), &mut self.symbol_table).interpret(statements) let mut interpreter = Interpreter::new(angle_unit, &mut context.symbol_table);
Ok(interpreter.interpret(statements).unwrap())
}
fn parse_stmt(context: &mut ParserContext) -> Result<Stmt, String> {
if match_token(context, TokenKind::Identifier) {
return Ok(match peek_next(context).kind {
TokenKind::Equals => parse_var_decl_stmt(context)?,
TokenKind::OpenParenthesis => parse_identifier_stmt(context)?,
_ => Stmt::Expr(Box::new(parse_expr(context)?)),
});
} }
fn parse_stmt(&mut self) -> Stmt { Ok(Stmt::Expr(Box::new(parse_expr(context)?)))
if self.match_token(TokenKind::Identifier) { }
return match self.peek_next().kind {
TokenKind::Equals => self.parse_var_decl_stmt(),
TokenKind::OpenParenthesis => self.parse_identifier_stmt(),
_ => Stmt::Expr(Box::new(self.parse_expr())),
};
}
Stmt::Expr(Box::new(self.parse_expr())) fn parse_identifier_stmt(context: &mut ParserContext) -> Result<Stmt, String> {
} let began_at = context.pos;
let primary = parse_primary(context)?; // Since function declarations and function calls look the same at first, simply parse a "function call", and re-use the data.
fn parse_identifier_stmt(&mut self) -> Stmt {
let began_at = self.pos;
let primary = self.parse_primary(); // Since function declarations and function calls look the same at first, simply parse a "function call", and re-use the data.
// If `primary` is followed by an equal sign, it is a function declaration. // If `primary` is followed by an equal sign, it is a function declaration.
if let TokenKind::Equals = self.peek().kind { if let TokenKind::Equals = peek(context).kind {
self.advance(); advance(context);
let expr = self.parse_expr(); let expr = parse_expr(context)?;
// Use the "function call" expression that was parsed, and put its values into a function declaration statement instead. // Use the "function call" expression that was parsed, and put its values into a function declaration statement instead.
if let Expr::FnCall(identifier, parameters) = primary { if let Expr::FnCall(identifier, parameters) = primary {
@ -107,193 +117,194 @@ impl Parser {
} }
} }
let fn_decl = let fn_decl = Stmt::FnDecl(identifier.clone(), parameter_identifiers, Box::new(expr));
Stmt::FnDecl(identifier.clone(), parameter_identifiers, Box::new(expr));
// Insert the function declaration into the symbol table during parsing // Insert the function declaration into the symbol table during parsing
// so that the parser can find out if particular functions exist. // so that the parser can find out if particular functions exist.
self.symbol_table context
.symbol_table
.insert(&format!("{}()", identifier), fn_decl.clone()); .insert(&format!("{}()", identifier), fn_decl.clone());
return fn_decl; return Ok(fn_decl);
} }
panic!("Unexpected error."); Err("Parsing error.".into())
} else { } else {
// It is a function call, not a function declaration. // It is a function call, not a function declaration.
// Redo the parsing for this specific part. // Redo the parsing for this specific part.
self.pos = began_at; context.pos = began_at;
Stmt::Expr(Box::new(self.parse_expr())) Ok(Stmt::Expr(Box::new(parse_expr(context)?)))
}
} }
}
fn parse_var_decl_stmt(&mut self) -> Stmt { fn parse_var_decl_stmt(context: &mut ParserContext) -> Result<Stmt, String> {
let identifier = self.advance().clone(); let identifier = advance(context).clone();
self.advance(); // Equal sign advance(context); // Equal sign
let expr = self.parse_expr(); let expr = parse_expr(context)?;
Stmt::VarDecl(identifier.value, Box::new(expr)) Ok(Stmt::VarDecl(identifier.value, Box::new(expr)))
} }
fn parse_expr(&mut self) -> Expr { fn parse_expr(context: &mut ParserContext) -> Result<Expr, String> {
self.parse_sum() Ok(parse_sum(context)?)
} }
fn parse_sum(&mut self) -> Expr { fn parse_sum(context: &mut ParserContext) -> Result<Expr, String> {
let mut left = self.parse_factor(); let mut left = parse_factor(context)?;
while self.match_token(TokenKind::Plus) || self.match_token(TokenKind::Minus) { while match_token(context, TokenKind::Plus) || match_token(context, TokenKind::Minus) {
let op = self.peek().kind.clone(); let op = peek(context).kind.clone();
self.advance(); advance(context);
let right = self.parse_factor(); let right = parse_factor(context)?;
left = Expr::Binary(Box::new(left), op, Box::new(right)); left = Expr::Binary(Box::new(left), op, Box::new(right));
} }
left Ok(left)
} }
fn parse_factor(&mut self) -> Expr { fn parse_factor(context: &mut ParserContext) -> Result<Expr, String> {
let mut left = self.parse_unary(); let mut left = parse_unary(context)?;
while self.match_token(TokenKind::Star) while match_token(context, TokenKind::Star)
|| self.match_token(TokenKind::Slash) || match_token(context, TokenKind::Slash)
|| self.match_token(TokenKind::Identifier) || match_token(context, TokenKind::Identifier)
{ {
let mut op = self.peek().kind.clone(); let mut op = peek(context).kind.clone();
// If the next token is an identifier, assume it's multiplication. Eg. 3y // If the next token is an identifier, assume it's multiplication. Eg. 3y
if let TokenKind::Identifier = op { if let TokenKind::Identifier = op {
op = TokenKind::Star; op = TokenKind::Star;
} else { } else {
self.advance(); advance(context);
} }
let right = self.parse_unary(); let right = parse_unary(context)?;
left = Expr::Binary(Box::new(left), op, Box::new(right)); left = Expr::Binary(Box::new(left), op, Box::new(right));
} }
left Ok(left)
}
fn parse_unary(context: &mut ParserContext) -> Result<Expr, String> {
if match_token(context, TokenKind::Minus) {
let op = advance(context).kind.clone();
let expr = Box::new(parse_unary(context)?);
return Ok(Expr::Unary(op, expr));
} }
fn parse_unary(&mut self) -> Expr { Ok(parse_exponent(context)?)
if self.match_token(TokenKind::Minus) { }
let op = self.advance().kind.clone();
return Expr::Unary(op, Box::new(self.parse_unary())); fn parse_exponent(context: &mut ParserContext) -> Result<Expr, String> {
let left = parse_primary(context)?;
if match_token(context, TokenKind::Power) {
let op = advance(context).kind.clone();
let right = Box::new(parse_exponent(context)?);
return Ok(Expr::Binary(Box::new(left), op, right));
} }
self.parse_exponent() Ok(left)
} }
fn parse_exponent(&mut self) -> Expr { fn parse_primary(context: &mut ParserContext) -> Result<Expr, String> {
let left = self.parse_primary(); let expr = match peek(context).kind {
TokenKind::OpenParenthesis => parse_group(context)?,
if self.match_token(TokenKind::Power) { TokenKind::Pipe => parse_abs(context)?,
let op = self.advance().kind.clone(); TokenKind::Identifier => parse_identifier(context)?,
return Expr::Binary(Box::new(left), op, Box::new(self.parse_exponent())); _ => Expr::Literal(advance(context).value.clone()),
}
left
}
fn parse_primary(&mut self) -> Expr {
let expr = match self.peek().kind {
TokenKind::OpenParenthesis => self.parse_group(),
TokenKind::Pipe => self.parse_abs(),
TokenKind::Identifier => self.parse_identifier(),
_ => Expr::Literal(self.advance().value.clone()),
}; };
if !self.is_at_end() && self.peek().kind.is_unit() { if !is_at_end(context) && peek(context).kind.is_unit() {
Expr::Unit(Box::new(expr), self.advance().kind.clone()) Ok(Expr::Unit(Box::new(expr), advance(context).kind.clone()))
} else { } else {
expr Ok(expr)
}
} }
}
fn parse_group(&mut self) -> Expr { fn parse_group(context: &mut ParserContext) -> Result<Expr, String> {
self.advance(); advance(context);
let group_expr = Expr::Group(Box::new(self.parse_expr())); let group_expr = Expr::Group(Box::new(parse_expr(context)?));
self.consume(TokenKind::ClosedParenthesis); consume(context, TokenKind::ClosedParenthesis)?;
group_expr Ok(group_expr)
} }
fn parse_abs(&mut self) -> Expr { fn parse_abs(context: &mut ParserContext) -> Result<Expr, String> {
self.advance(); advance(context);
let group_expr = Expr::Group(Box::new(self.parse_expr())); let group_expr = Expr::Group(Box::new(parse_expr(context)?));
self.consume(TokenKind::Pipe); consume(context, TokenKind::Pipe)?;
Expr::FnCall(String::from("abs"), vec![group_expr]) Ok(Expr::FnCall(String::from("abs"), vec![group_expr]))
} }
fn parse_identifier(&mut self) -> Expr { fn parse_identifier(context: &mut ParserContext) -> Result<Expr, String> {
let identifier = self.advance().clone(); let identifier = advance(context).clone();
// Eg. sqrt64 // Eg. sqrt64
if self.match_token(TokenKind::Literal) { if match_token(context, TokenKind::Literal) {
// If there is a function with this name, parse it as a function, with the next token as the argument. // If there is a function with this name, parse it as a function, with the next token as the argument.
if self.symbol_table.contains_func(&identifier.value) { if context.symbol_table.contains_func(&identifier.value) {
let parameter = Expr::Literal(self.advance().value.clone()); let parameter = Expr::Literal(advance(context).value.clone());
return Expr::FnCall(identifier.value, vec![parameter]); return Ok(Expr::FnCall(identifier.value, vec![parameter]));
} }
} }
// Eg. sqrt(64) // Eg. sqrt(64)
if self.match_token(TokenKind::OpenParenthesis) { if match_token(context, TokenKind::OpenParenthesis) {
self.advance(); advance(context);
let mut parameters = Vec::new(); let mut parameters = Vec::new();
parameters.push(self.parse_expr()); parameters.push(parse_expr(context)?);
while self.match_token(TokenKind::Comma) { while match_token(context, TokenKind::Comma) {
self.advance(); advance(context);
parameters.push(self.parse_expr()); parameters.push(parse_expr(context)?);
} }
self.consume(TokenKind::ClosedParenthesis); consume(context, TokenKind::ClosedParenthesis)?;
return Expr::FnCall(identifier.value, parameters); return Ok(Expr::FnCall(identifier.value, parameters));
} }
// Eg. x // Eg. x
Expr::Var(identifier.value) Ok(Expr::Var(identifier.value))
} }
fn peek(&self) -> &Token { fn peek<'a>(context: &'a mut ParserContext) -> &'a Token {
&self.tokens[self.pos] &context.tokens[context.pos]
} }
fn peek_next(&self) -> &Token { fn peek_next<'a>(context: &'a mut ParserContext) -> &'a Token {
&self.tokens[self.pos + 1] &context.tokens[context.pos + 1]
} }
fn previous(&self) -> &Token { fn previous<'a>(context: &'a mut ParserContext) -> &'a Token {
&self.tokens[self.pos - 1] &context.tokens[context.pos - 1]
} }
fn match_token(&self, kind: TokenKind) -> bool { fn match_token(context: &mut ParserContext, kind: TokenKind) -> bool {
if self.is_at_end() { if is_at_end(context) {
return false; return false;
} }
self.peek().kind.compare(&kind) peek(context).kind.compare(&kind)
} }
fn advance(&mut self) -> &Token { fn advance<'a>(context: &'a mut ParserContext) -> &'a Token {
self.pos += 1; context.pos += 1;
self.previous() previous(context)
} }
fn consume(&mut self, kind: TokenKind) -> &Token { fn consume<'a>(context: &'a mut ParserContext, kind: TokenKind) -> Result<&'a Token, String> {
if self.match_token(kind) { if match_token(context, kind) {
return self.advance(); return Ok(advance(context));
} }
panic!("Unexpected token."); Err("Unexpected token".into())
} }
fn is_at_end(&self) -> bool { fn is_at_end(context: &mut ParserContext) -> bool {
self.pos >= self.tokens.len() || self.peek().kind.compare(&TokenKind::EOF) context.pos >= context.tokens.len() || peek(context).kind.compare(&TokenKind::EOF)
}
} }

View File

@ -1,7 +1,6 @@
use crate::parser::Unit; use crate::parser::Unit;
use FuncType::*; use FuncType::*;
pub const DEFAULT_ANGLE_UNIT: Unit = Unit::Radians;
pub const CONSTANTS: &[(&str, &str)] = &[ pub const CONSTANTS: &[(&str, &str)] = &[
("pi", "3.14159265"), ("pi", "3.14159265"),
("π", "3.14159265"), ("π", "3.14159265"),