Implemented somewhat proper error handling for the interpreter. This involved removing the visitor.

This commit is contained in:
PaddiM8 2020-05-30 15:16:13 +02:00
parent 1a56ddac45
commit 41a41629a5
4 changed files with 161 additions and 163 deletions

View File

@ -2,15 +2,15 @@ use std::mem;
use crate::lexer::TokenKind;
use crate::parser::{Expr, Stmt, Unit};
use crate::prelude::{self};
use crate::{symbol_table::SymbolTable, visitor::Visitor};
use crate::prelude;
use crate::symbol_table::SymbolTable;
pub struct Interpreter<'a> {
pub struct Context<'a> {
symbol_table: &'a mut SymbolTable,
angle_unit: Unit,
}
impl<'a> Interpreter<'a> {
impl<'a> Context<'a> {
pub fn new(angle_unit: Unit, symbol_table: &'a mut SymbolTable) -> Self {
for constant in prelude::CONSTANTS {
symbol_table.insert(
@ -22,33 +22,33 @@ impl<'a> Interpreter<'a> {
);
}
Interpreter {
Context {
angle_unit: angle_unit.clone(),
symbol_table,
}
}
pub fn interpret(&mut self, statements: Vec<Stmt>) -> Option<f64> {
pub fn interpret(&mut self, statements: Vec<Stmt>) -> Result<Option<f64>, String> {
for (i, stmt) in statements.iter().enumerate() {
let value = self.visit_stmt(stmt);
let value = eval_stmt(self, stmt);
if i == statements.len() - 1 {
if let Stmt::Expr(_) = stmt {
return Some(value);
return Ok(Some(value?));
}
}
}
return None;
Ok(None)
}
}
impl TokenKind {
fn to_unit(&self) -> Unit {
fn to_unit(&self) -> Result<Unit, String> {
match self {
TokenKind::Deg => Unit::Degrees,
TokenKind::Rad => Unit::Radians,
_ => panic!("Invalid unit."),
TokenKind::Deg => Ok(Unit::Degrees),
TokenKind::Rad => Ok(Unit::Radians),
_ => Err(String::from("Invalid unit.")),
}
}
}
@ -60,142 +60,145 @@ impl Unit {
}
}
impl<'a> Visitor<f64, f64> for Interpreter<'a> {
fn visit_stmt(&mut self, stmt: &Stmt) -> f64 {
match stmt {
Stmt::VarDecl(identifier, _) => self.eval_var_decl_stmt(stmt, identifier),
Stmt::FnDecl(_, _, _) => self.eval_fn_decl_stmt(),
Stmt::Expr(expr) => self.eval_expr_stmt(&expr),
}
fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result<f64, String> {
match stmt {
Stmt::VarDecl(identifier, _) => eval_var_decl_stmt(context, stmt, identifier),
Stmt::FnDecl(_, _, _) => eval_fn_decl_stmt(context),
Stmt::Expr(expr) => eval_expr_stmt(context, &expr),
}
}
fn visit_expr(&mut self, expr: &Expr) -> f64 {
match expr {
Expr::Binary(left, op, right) => self.eval_binary_expr(&left, op, &right),
Expr::Unary(_, expr) => self.eval_unary_expr(expr),
Expr::Unit(expr, kind) => self.eval_unit_expr(expr, kind),
Expr::Var(identifier) => self.eval_var_expr(identifier),
Expr::Literal(value) => self.eval_literal_expr(value),
Expr::Group(expr) => self.eval_group_expr(&expr),
Expr::FnCall(identifier, expressions) => {
self.eval_fn_call_expr(identifier, expressions)
}
fn eval_var_decl_stmt(context: &mut Context, stmt: &Stmt, identifier: &str) -> Result<f64, String> {
context.symbol_table.insert(&identifier, stmt.clone());
Ok(0f64)
}
fn eval_fn_decl_stmt(_: &mut Context) -> Result<f64, String> {
Ok(0f64) // Nothing needs to happen here, since the parser will already have added the FnDecl's to the symbol table.
}
fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result<f64, String> {
eval_expr(context, &expr)
}
fn eval_expr(context: &mut Context, expr: &Expr) -> Result<f64, String> {
match expr {
Expr::Binary(left, op, right) => eval_binary_expr(context, &left, op, &right),
Expr::Unary(_, expr) => eval_unary_expr(context, expr),
Expr::Unit(expr, kind) => eval_unit_expr(context, expr, kind),
Expr::Var(identifier) => eval_var_expr(context, identifier),
Expr::Literal(value) => eval_literal_expr(context, value),
Expr::Group(expr) => eval_group_expr(context, &expr),
Expr::FnCall(identifier, expressions) => {
eval_fn_call_expr(context, identifier, expressions)
}
}
}
impl<'a> Interpreter<'a> {
fn eval_var_decl_stmt(&mut self, stmt: &Stmt, identifier: &str) -> f64 {
self.symbol_table.insert(&identifier, stmt.clone());
0f64
fn eval_binary_expr(
context: &mut Context,
left: &Expr,
op: &TokenKind,
right: &Expr,
) -> Result<f64, String> {
let left = eval_expr(context, &left)?;
let right = eval_expr(context, &right)?;
Ok(match op {
TokenKind::Plus => left + right,
TokenKind::Minus => left - right,
TokenKind::Star => left * right,
TokenKind::Slash => left / right,
TokenKind::Power => left.powf(right),
_ => 0f64,
})
}
fn eval_unary_expr(context: &mut Context, expr: &Expr) -> Result<f64, String> {
eval_expr(context, &expr).clone()
}
fn eval_unit_expr(context: &mut Context, expr: &Expr, kind: &TokenKind) -> Result<f64, String> {
let x = eval_expr(context, &expr);
let unit = kind.to_unit()?;
// Don't do any angle conversions if the defauly angle unit is the same as the unit kind
match unit {
Unit::Degrees | Unit::Radians => {
if context.angle_unit.compare(&unit) {
return x;
}
}
}
fn eval_fn_decl_stmt(&mut self) -> f64 {
0f64 // Nothing needs to happen here, since the parser will already have added the FnDecl's to the symbol table.
}
fn eval_expr_stmt(&mut self, expr: &Expr) -> f64 {
self.visit_expr(&expr)
match unit {
Unit::Degrees => Ok(x?.to_radians()),
Unit::Radians => Ok(x?.to_degrees()),
}
}
impl<'a> Interpreter<'a> {
fn eval_binary_expr(&mut self, left: &Expr, op: &TokenKind, right: &Expr) -> f64 {
let left = self.visit_expr(&left);
let right = self.visit_expr(&right);
fn eval_var_expr(context: &mut Context, identifier: &str) -> Result<f64, String> {
let var_decl = context.symbol_table.get(identifier).cloned();
match var_decl {
Some(Stmt::VarDecl(_, expr)) => eval_expr(context, &expr),
_ => Err(String::from("Undefined variable.")),
}
}
match op {
TokenKind::Plus => left + right,
TokenKind::Minus => left - right,
TokenKind::Star => left * right,
TokenKind::Slash => left / right,
TokenKind::Power => left.powf(right),
_ => 0f64,
fn eval_literal_expr(_: &mut Context, value: &str) -> Result<f64, String> {
match value.parse() {
Ok(parsed_value) => Ok(parsed_value),
Err(_) => Err(String::from("Invalid number literal.")),
}
}
fn eval_group_expr(context: &mut Context, expr: &Expr) -> Result<f64, String> {
eval_expr(context, expr)
}
fn eval_fn_call_expr(
context: &mut Context,
identifier: &str,
expressions: &Vec<Expr>,
) -> Result<f64, String> {
let prelude_func = match expressions.len() {
1 => {
let x = eval_expr(context, &expressions[0])?;
prelude::call_unary_func(identifier, x, &context.angle_unit)
}
}
fn eval_unary_expr(&mut self, expr: &Expr) -> f64 {
self.visit_expr(&expr).clone()
}
fn eval_unit_expr(&mut self, expr: &Expr, kind: &TokenKind) -> f64 {
let x = self.visit_expr(&expr);
// Don't do any angle conversions if the defauly angle unit is the same as the unit kind
if (kind.compare(&TokenKind::Deg) || kind.compare(&TokenKind::Rad))
&& self.angle_unit.compare(&kind.to_unit())
{
return x;
2 => {
let x = eval_expr(context, &expressions[0])?;
let y = eval_expr(context, &expressions[1])?;
prelude::call_binary_func(identifier, x, y, &context.angle_unit)
}
_ => None,
};
match kind {
TokenKind::Deg => x.to_radians(),
TokenKind::Rad => x.to_degrees(),
_ => panic!("Invalid unit."),
}
if let Some(result) = prelude_func {
return Ok(result);
}
fn eval_var_expr(&mut self, identifier: &str) -> f64 {
let value = self
.symbol_table
.get(identifier)
.expect("Undefined variable.")
.clone();
if let Stmt::VarDecl(_, expr) = value {
return self.visit_expr(&expr);
}
let stmt_definition = context
.symbol_table
.get(&format!("{}()", identifier))
.cloned();
panic!("Unknown error.");
}
fn eval_literal_expr(&mut self, value: &str) -> f64 {
value.parse().unwrap()
}
fn eval_group_expr(&mut self, expr: &Expr) -> f64 {
self.visit_expr(expr)
}
fn eval_fn_call_expr(&mut self, identifier: &str, expressions: &Vec<Expr>) -> f64 {
let prelude_func = match expressions.len() {
1 => {
let x = self.visit_expr(&expressions[0]);
prelude::call_unary_func(identifier, x, &self.angle_unit)
}
2 => {
let x = self.visit_expr(&expressions[0]);
let y = self.visit_expr(&expressions[1]);
prelude::call_binary_func(identifier, x, y, &self.angle_unit)
}
_ => None,
};
if let Some(result) = prelude_func {
return result;
}
let stmt = self
.symbol_table
.get(&format!("{}()", identifier))
.expect("Undefined function")
.clone();
if let Stmt::FnDecl(_, arguments, fn_body) = stmt {
match stmt_definition {
Some(Stmt::FnDecl(_, arguments, fn_body)) => {
if arguments.len() != expressions.len() {
panic!("Incorrect amount of arguments.");
return Err(String::from("Incorrect amount of arguments."));
}
// Initialise the arguments as their own variables.
for (i, argument) in arguments.iter().enumerate() {
self.visit_stmt(&Stmt::VarDecl(
argument.clone(),
Box::new(expressions[i].clone()),
));
eval_stmt(
context,
&Stmt::VarDecl(argument.clone(), Box::new(expressions[i].clone())),
)?;
}
return self.visit_expr(&*fn_body);
return eval_expr(context, &*fn_body);
}
panic!("Unexpected error.");
_ => Err(String::from("Undefined function.")),
}
}

View File

@ -5,14 +5,13 @@ mod lexer;
mod parser;
mod prelude;
mod symbol_table;
mod visitor;
use parser::{ParserContext, Unit};
use parser::Unit;
use rustyline::error::ReadlineError;
use rustyline::Editor;
fn main() {
let mut parser = ParserContext::new();
let mut parser = parser::Context::new();
// Command line argument input, execute it and exit.
if let Some(expr) = env::args().skip(1).next() {
@ -37,7 +36,7 @@ fn main() {
}
}
fn eval_repl(parser: &mut ParserContext, input: &str) {
fn eval_repl(parser: &mut parser::Context, input: &str) {
match input {
"" => eprint!(""),
"clear" => print!("\x1B[2J"),
@ -46,10 +45,11 @@ fn eval_repl(parser: &mut ParserContext, input: &str) {
}
}
fn eval(parser: &mut ParserContext, input: &str) {
fn eval(parser: &mut parser::Context, input: &str) {
match parser::parse(parser, input, get_angle_unit()) {
Ok(result) => println!("{}", result),
Err(_) => println!("Invalid expression"),
Ok(Some(result)) => println!("{}", result),
Ok(None) => print!(""),
Err(err) => println!("{}", err),
}
}

View File

@ -1,7 +1,7 @@
use std::mem;
use crate::{
interpreter::Interpreter,
interpreter,
lexer::{Lexer, Token, TokenKind},
symbol_table::SymbolTable,
};
@ -30,7 +30,7 @@ pub enum Unit {
Degrees,
}
pub struct ParserContext {
pub struct Context {
//angle_unit: Unit,
tokens: Vec<Token>,
pos: usize,
@ -50,9 +50,9 @@ impl TokenKind {
}
}
impl ParserContext {
impl Context {
pub fn new() -> Self {
ParserContext {
Context {
tokens: Vec::new(),
pos: 0,
symbol_table: SymbolTable::new(),
@ -60,19 +60,20 @@ impl ParserContext {
}
}
pub fn parse(context: &mut ParserContext, input: &str, angle_unit: Unit) -> Result<f64, String> {
pub fn parse(context: &mut Context, input: &str, angle_unit: Unit) -> Result<Option<f64>, String> {
context.tokens = Lexer::lex(input);
context.pos = 0;
let mut statements: Vec<Stmt> = Vec::new();
while !is_at_end(context) {
statements.push(parse_stmt(context)?);
}
let mut interpreter = Interpreter::new(angle_unit, &mut context.symbol_table);
Ok(interpreter.interpret(statements).unwrap())
let mut interpreter = interpreter::Context::new(angle_unit, &mut context.symbol_table);
interpreter.interpret(statements)
}
fn parse_stmt(context: &mut ParserContext) -> Result<Stmt, String> {
fn parse_stmt(context: &mut Context) -> Result<Stmt, String> {
if match_token(context, TokenKind::Identifier) {
return Ok(match peek_next(context).kind {
TokenKind::Equals => parse_var_decl_stmt(context)?,
@ -84,7 +85,7 @@ fn parse_stmt(context: &mut ParserContext) -> Result<Stmt, String> {
Ok(Stmt::Expr(Box::new(parse_expr(context)?)))
}
fn parse_identifier_stmt(context: &mut ParserContext) -> Result<Stmt, String> {
fn parse_identifier_stmt(context: &mut Context) -> 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.
@ -126,7 +127,7 @@ fn parse_identifier_stmt(context: &mut ParserContext) -> Result<Stmt, String> {
}
}
fn parse_var_decl_stmt(context: &mut ParserContext) -> Result<Stmt, String> {
fn parse_var_decl_stmt(context: &mut Context) -> Result<Stmt, String> {
let identifier = advance(context).clone();
advance(context); // Equal sign
let expr = parse_expr(context)?;
@ -134,11 +135,11 @@ fn parse_var_decl_stmt(context: &mut ParserContext) -> Result<Stmt, String> {
Ok(Stmt::VarDecl(identifier.value, Box::new(expr)))
}
fn parse_expr(context: &mut ParserContext) -> Result<Expr, String> {
fn parse_expr(context: &mut Context) -> Result<Expr, String> {
Ok(parse_sum(context)?)
}
fn parse_sum(context: &mut ParserContext) -> Result<Expr, String> {
fn parse_sum(context: &mut Context) -> Result<Expr, String> {
let mut left = parse_factor(context)?;
while match_token(context, TokenKind::Plus) || match_token(context, TokenKind::Minus) {
@ -152,7 +153,7 @@ fn parse_sum(context: &mut ParserContext) -> Result<Expr, String> {
Ok(left)
}
fn parse_factor(context: &mut ParserContext) -> Result<Expr, String> {
fn parse_factor(context: &mut Context) -> Result<Expr, String> {
let mut left = parse_unary(context)?;
while match_token(context, TokenKind::Star)
@ -175,7 +176,7 @@ fn parse_factor(context: &mut ParserContext) -> Result<Expr, String> {
Ok(left)
}
fn parse_unary(context: &mut ParserContext) -> Result<Expr, String> {
fn parse_unary(context: &mut Context) -> Result<Expr, String> {
if match_token(context, TokenKind::Minus) {
let op = advance(context).kind.clone();
let expr = Box::new(parse_unary(context)?);
@ -185,7 +186,7 @@ fn parse_unary(context: &mut ParserContext) -> Result<Expr, String> {
Ok(parse_exponent(context)?)
}
fn parse_exponent(context: &mut ParserContext) -> Result<Expr, String> {
fn parse_exponent(context: &mut Context) -> Result<Expr, String> {
let left = parse_primary(context)?;
if match_token(context, TokenKind::Power) {
@ -197,7 +198,7 @@ fn parse_exponent(context: &mut ParserContext) -> Result<Expr, String> {
Ok(left)
}
fn parse_primary(context: &mut ParserContext) -> Result<Expr, String> {
fn parse_primary(context: &mut Context) -> Result<Expr, String> {
let expr = match peek(context).kind {
TokenKind::OpenParenthesis => parse_group(context)?,
TokenKind::Pipe => parse_abs(context)?,
@ -212,7 +213,7 @@ fn parse_primary(context: &mut ParserContext) -> Result<Expr, String> {
}
}
fn parse_group(context: &mut ParserContext) -> Result<Expr, String> {
fn parse_group(context: &mut Context) -> Result<Expr, String> {
advance(context);
let group_expr = Expr::Group(Box::new(parse_expr(context)?));
consume(context, TokenKind::ClosedParenthesis)?;
@ -220,7 +221,7 @@ fn parse_group(context: &mut ParserContext) -> Result<Expr, String> {
Ok(group_expr)
}
fn parse_abs(context: &mut ParserContext) -> Result<Expr, String> {
fn parse_abs(context: &mut Context) -> Result<Expr, String> {
advance(context);
let group_expr = Expr::Group(Box::new(parse_expr(context)?));
consume(context, TokenKind::Pipe)?;
@ -228,7 +229,7 @@ fn parse_abs(context: &mut ParserContext) -> Result<Expr, String> {
Ok(Expr::FnCall(String::from("abs"), vec![group_expr]))
}
fn parse_identifier(context: &mut ParserContext) -> Result<Expr, String> {
fn parse_identifier(context: &mut Context) -> Result<Expr, String> {
let identifier = advance(context).clone();
// Eg. sqrt64
@ -261,19 +262,19 @@ fn parse_identifier(context: &mut ParserContext) -> Result<Expr, String> {
Ok(Expr::Var(identifier.value))
}
fn peek<'a>(context: &'a mut ParserContext) -> &'a Token {
fn peek<'a>(context: &'a mut Context) -> &'a Token {
&context.tokens[context.pos]
}
fn peek_next<'a>(context: &'a mut ParserContext) -> &'a Token {
fn peek_next<'a>(context: &'a mut Context) -> &'a Token {
&context.tokens[context.pos + 1]
}
fn previous<'a>(context: &'a mut ParserContext) -> &'a Token {
fn previous<'a>(context: &'a mut Context) -> &'a Token {
&context.tokens[context.pos - 1]
}
fn match_token(context: &mut ParserContext, kind: TokenKind) -> bool {
fn match_token(context: &mut Context, kind: TokenKind) -> bool {
if is_at_end(context) {
return false;
}
@ -281,12 +282,12 @@ fn match_token(context: &mut ParserContext, kind: TokenKind) -> bool {
peek(context).kind.compare(&kind)
}
fn advance<'a>(context: &'a mut ParserContext) -> &'a Token {
fn advance<'a>(context: &'a mut Context) -> &'a Token {
context.pos += 1;
previous(context)
}
fn consume<'a>(context: &'a mut ParserContext, kind: TokenKind) -> Result<&'a Token, String> {
fn consume<'a>(context: &'a mut Context, kind: TokenKind) -> Result<&'a Token, String> {
if match_token(context, kind) {
return Ok(advance(context));
}
@ -294,6 +295,6 @@ fn consume<'a>(context: &'a mut ParserContext, kind: TokenKind) -> Result<&'a To
Err("Unexpected token".into())
}
fn is_at_end(context: &mut ParserContext) -> bool {
fn is_at_end(context: &mut Context) -> bool {
context.pos >= context.tokens.len() || peek(context).kind.compare(&TokenKind::EOF)
}

View File

@ -1,6 +0,0 @@
use crate::parser::{Expr, Stmt};
pub trait Visitor<T, U> {
fn visit_stmt(&mut self, stmt: &Stmt) -> T;
fn visit_expr(&mut self, expr: &Expr) -> U;
}