#![allow(dead_code)]
use std::{borrow::Borrow, fmt::Debug, io::Write, path::Path};
use display_tree::{format_tree, Style};
use location::{Location, LocationContainer};
use thiserror::Error;
mod lexer;
mod location;
mod parser;
use lexer::{Lexer, Literal, Token, TokenType, BINARY_OPERATORS, UNARY_OPERATORS};
use parser::{Expression, Parser};
pub enum OutputStyle {
Tree(Style),
Sexp,
Evaluate,
}
pub struct LoxOptions {
pub output_style: OutputStyle,
}
#[derive(Error, Debug)]
pub enum EvaluationError {
#[error("[{location}] Invalid binary operator types: {left} {operator:?} {right}")]
InvalidBinaryOperatorType {
location: Location,
left: Literal,
operator: TokenType,
right: Literal,
},
#[error("[{location}] Invalid binary operator: {operator:?}")]
InvalidBinaryOperator {
location: Location,
operator: TokenType,
},
#[error("[{location}] Invalid unary operator type: {operator:?} {right}")]
InvalidUnaryOperatorType {
location: Location,
operator: TokenType,
right: Literal,
},
#[error("[{location}] Invalid unary operator: {operator:?}")]
InvalidUnaryOperator {
location: Location,
operator: TokenType,
},
}
pub struct Lox {
had_error: bool,
pub options: LoxOptions,
}
impl Debug for Lox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Lox {{ had_error: {} }}", self.had_error)
}
}
impl Lox {
pub fn new() -> Self {
Self {
had_error: false,
options: LoxOptions {
output_style: OutputStyle::Evaluate,
},
}
}
pub fn error_token(&mut self, token: Token, message: &str) {
if token.token_type == lexer::TokenType::Eof {
self.report(token.location.line, " at end", message);
} else {
self.report(
token.location.line,
&format!(" at '{}'", token.lexeme),
message,
);
}
}
pub fn error(&mut self, line: usize, message: &str) {
self.report(line, "", message);
}
fn report(&mut self, line: usize, location: &str, message: &str) {
eprintln!("[line {line}] Error {location}: {message}");
self.had_error = true;
}
fn evaluate(&self, expr: &Expression) -> Result<LocationContainer<Literal>, EvaluationError> {
match expr {
Expression::Binary {
left,
operator,
right,
} => {
let left_loc = self.evaluate(left.borrow())?;
let right_loc = self.evaluate(right.borrow())?;
let operator_tok = operator;
match (
left_loc.inner,
operator_tok.get_token_type(),
right_loc.inner,
) {
(Literal::Number(left), TokenType::Plus, Literal::Number(right)) => {
Ok(LocationContainer {
inner: Literal::Number(left + right),
location: left_loc.location.clone(),
})
}
(Literal::Number(left), TokenType::Minus, Literal::Number(right)) => {
Ok(LocationContainer {
inner: Literal::Number(left - right),
location: left_loc.location.clone(),
})
}
(Literal::Number(left), TokenType::Star, Literal::Number(right)) => {
Ok(LocationContainer {
inner: Literal::Number(left * right),
location: left_loc.location.clone(),
})
}
(Literal::Number(left), TokenType::Slash, Literal::Number(right)) => {
Ok(LocationContainer {
inner: Literal::Number(left / right),
location: left_loc.location.clone(),
})
}
(left, TokenType::Greater, right) => Ok(LocationContainer {
inner: Literal::Boolean(left > right),
location: left_loc.location.clone(),
}),
(left, TokenType::GreaterEqual, right) => Ok(LocationContainer {
inner: Literal::Boolean(left >= right),
location: left_loc.location.clone(),
}),
(left, TokenType::Less, right) => Ok(LocationContainer {
inner: Literal::Boolean(left < right),
location: left_loc.location.clone(),
}),
(left, TokenType::LessEqual, right) => Ok(LocationContainer {
inner: Literal::Boolean(left <= right),
location: left_loc.location.clone(),
}),
(left, TokenType::EqualEqual, right) => Ok(LocationContainer {
inner: Literal::Boolean(left == right),
location: left_loc.location.clone(),
}),
(left, TokenType::BangEqual, right) => Ok(LocationContainer {
inner: Literal::Boolean(left != right),
location: left_loc.location.clone(),
}),
(Literal::String(left), TokenType::Tilde, Literal::String(right)) => {
Ok(LocationContainer {
inner: Literal::String(format!("{}{}", left, right)),
location: left_loc.location.clone(),
})
}
(left, operator, right) if BINARY_OPERATORS.contains(&operator) => {
Err(EvaluationError::InvalidBinaryOperatorType {
location: right_loc.location.clone(),
left,
operator,
right,
})
}
(_, operator, _) => Err(EvaluationError::InvalidBinaryOperator {
location: operator_tok.location.clone(),
operator,
}),
}
}
Expression::Grouping { expression } => self.evaluate(expression),
Expression::Literal { value } => Ok(value.clone()),
Expression::Unary { operator, right } => {
let right_loc = self.evaluate(right.borrow())?;
let operator_tok = operator;
match (operator_tok.get_token_type(), right_loc.inner) {
(lexer::TokenType::Minus, Literal::Number(n)) => Ok(LocationContainer {
inner: Literal::Number(-n),
location: right_loc.location.clone(),
}),
(lexer::TokenType::Bang, Literal::Boolean(b)) => Ok(LocationContainer {
inner: Literal::Boolean(!b),
location: right_loc.location.clone(),
}),
(operator, right) if UNARY_OPERATORS.contains(&operator) => {
Err(EvaluationError::InvalidUnaryOperatorType {
location: right_loc.location.clone(),
operator,
right,
})
}
(operator, _) => Err(EvaluationError::InvalidUnaryOperator {
location: operator_tok.location.clone(),
operator,
}),
}
}
}
}
fn run(&mut self, source: String) {
let mut lexer = Lexer::new(self, &source);
let tokens = lexer.scan_tokens();
let mut parser = Parser::new(self, tokens);
let expression = parser.parse().unwrap();
match self.options.output_style {
OutputStyle::Tree(style) => println!("{}", format_tree!(expression, style)),
OutputStyle::Sexp => println!("{}", expression),
OutputStyle::Evaluate => {
let result = self.evaluate(&expression);
match result {
Ok(result) => println!("{}", result),
Err(e) => eprintln!("{}", e),
}
}
}
}
pub fn run_file<P: AsRef<Path>>(&mut self, file: P) -> anyhow::Result<()> {
let source = std::fs::read_to_string(file)?;
self.run(source);
if self.had_error {
std::process::exit(65);
}
Ok(())
}
pub fn run_prompt(&mut self) -> anyhow::Result<()> {
loop {
let mut input = String::new();
print!("> ");
std::io::stdout().flush()?;
match std::io::stdin().read_line(&mut input) {
Ok(0) => break,
Ok(_) => {
self.run(input);
self.had_error = false;
}
Err(e) => return Err(e.into()),
}
}
Ok(())
}
}