Compare commits

..

No commits in common. "129d03b74d74236327f299feb635b940717785eb" and "3cfde406d3821943a8819403eec3bf0aa851d4c5" have entirely different histories.

7 changed files with 30 additions and 323 deletions

View File

@ -22,27 +22,4 @@ pub enum Expr {
op: Token,
right: Box<Expr>,
},
Variable {
name: Token,
},
Assign {
name: Token,
value: Box<Expr>,
},
}
pub enum Stmt {
Block {
statements: Vec<Stmt>,
},
Expression {
expression: Expr,
},
Print {
expression: Expr,
},
Var {
name: Token,
initializer: Option<Expr>,
},
}

View File

@ -1,65 +0,0 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use crate::token::{LiteralType, Token};
pub struct Environment {
values: HashMap<String, LiteralType>,
enclosing: Option<Rc<RefCell<Environment>>>,
}
pub enum EnvironmentError {
AssignError,
}
impl Environment {
pub fn new() -> Self {
Self {
values: HashMap::new(),
enclosing: None,
}
}
pub fn with_enclosing(enclosing: &Rc<RefCell<Environment>>) -> Self {
Self {
values: HashMap::new(),
enclosing: Some(Rc::clone(enclosing)),
}
}
pub fn define(&mut self, name: &str, val: LiteralType) {
// do not like this at all. String is allocated each time a variable is defined. Might be
// bad or might be good. I don't know :D
self.values.insert(name.to_string(), val);
}
pub fn assign(&mut self, name: &Token, val: LiteralType) -> Result<(), EnvironmentError> {
let cloned = val.clone();
let assigned = self
.values
.get_mut(&name.lexeme)
.map(|l| *l = val)
.ok_or(EnvironmentError::AssignError);
if assigned.is_err() {
if let Some(enclosing) = &mut self.enclosing {
return enclosing.borrow_mut().assign(name, cloned);
}
}
assigned
}
pub fn get(&self, name: &Token) -> Option<LiteralType> {
//self.values.get(&name.lexeme).cloned()
let value = self.values.get(&name.lexeme);
if value.is_none() {
if let Some(enclosing) = &self.enclosing {
return enclosing.borrow().get(name);
}
}
value.cloned()
}
}

View File

@ -1,8 +1,5 @@
use std::{cell::RefCell, rc::Rc};
use crate::{
ast::{Expr, Stmt},
environment::Environment,
ast::Expr,
token::{LiteralType, Token, TokenType},
};
@ -21,99 +18,27 @@ impl RuntimeError {
}
}
pub fn interpret(
statements: &Vec<Stmt>,
environment: &Rc<RefCell<Environment>>,
) -> Result<(), RuntimeError> {
for statement in statements {
execute(statement, &environment)?;
}
Ok(())
}
fn execute(statement: &Stmt, environment: &Rc<RefCell<Environment>>) -> Result<(), RuntimeError> {
match statement {
Stmt::Expression { expression } => {
evaluate(expression, &mut environment.borrow_mut())?;
}
Stmt::Print { expression } => {
let expr = evaluate(expression, &mut environment.borrow_mut())?;
println!("{expr}");
}
Stmt::Var { name, initializer } => {
let value = if let Some(initializer) = initializer {
evaluate(initializer, &mut environment.borrow_mut())?
} else {
LiteralType::Nil
};
environment.borrow_mut().define(&name.lexeme, value);
}
Stmt::Block { statements } => {
execute_block(statements, environment)?;
}
}
Ok(())
}
fn execute_block(
statements: &Vec<Stmt>,
environment: &Rc<RefCell<Environment>>,
) -> Result<(), RuntimeError> {
let block_enviroment = Rc::new(RefCell::new(Environment::with_enclosing(environment)));
for stmt in statements {
execute(stmt, &block_enviroment)?;
}
Ok(())
}
fn evaluate(expr: &Expr, environment: &mut Environment) -> Result<LiteralType, RuntimeError> {
pub fn interpret(expr: &Expr) -> Result<LiteralType, RuntimeError> {
match expr {
Expr::Ternary {
first,
second,
third,
..
} => ternary(first, second, third, environment),
Expr::Binary { left, op, right } => binary(
&evaluate(left, environment)?,
&evaluate(right, environment)?,
op,
),
Expr::Grouping { expression } => evaluate(expression, environment),
} => ternary(first, second, third),
Expr::Binary { left, op, right } => binary(&interpret(left)?, &interpret(right)?, op),
Expr::Grouping { expression } => interpret(expression),
Expr::Literal { value } => Ok(value.clone()),
Expr::Unary { op, right } => Ok(unary(&evaluate(right, environment)?, op)),
Expr::Variable { name } => environment.get(name).ok_or_else(|| RuntimeError {
token: name.clone(),
message: format!("Undefined variable {}.", name.lexeme),
}),
Expr::Assign { name, value } => {
let value = evaluate(value, environment)?;
environment
.assign(name, value.clone())
.map_err(|_| RuntimeError {
token: name.clone(),
message: format!("Undefined variable {}.", name.lexeme),
})?;
Ok(value)
}
Expr::Unary { op, right } => Ok(unary(&interpret(right)?, op)),
}
}
fn ternary(
first: &Expr,
second: &Expr,
third: &Expr,
environment: &mut Environment,
) -> Result<LiteralType, RuntimeError> {
let first = evaluate(first, environment)?;
fn ternary(first: &Expr, second: &Expr, third: &Expr) -> Result<LiteralType, RuntimeError> {
let first = interpret(first)?;
if is_truthy(&first) {
return evaluate(second, environment);
return interpret(second);
}
evaluate(third, environment)
interpret(third)
}
fn binary(
@ -146,7 +71,7 @@ fn binary(
(Greater | GreaterEqual | Less | LessEqual | Minus | Slash | Star, _, _) => Err(RuntimeError::new(op, "Operands must be numbers")),
(Plus, _, _) => Err(RuntimeError::new(op, "Operands must be two numbers or two strings")),
_ => unreachable!("Shouldn't happen. Expr::Binary for evaluate. Some case is a binary operation that wasn't matched")
_ => unreachable!("Shouldn't happen. Expr::Binary for interpret. Some case is a binary operation that wasn't matched")
}
}
@ -154,7 +79,7 @@ fn unary(right: &LiteralType, op: &Token) -> LiteralType {
match (op.t_type, &right) {
(TokenType::Minus, LiteralType::Number(num)) => LiteralType::Number(-num),
(TokenType::Bang, _) => LiteralType::Bool(!is_truthy(right)),
_ => unreachable!("Shouldn't happen. Expr::Unary for evaluate"),
_ => unreachable!("Shouldn't happen. Expr::Unary for interpret"),
}
}

View File

@ -1,19 +1,15 @@
use std::{
cell::RefCell,
error::Error,
fs,
io::{self, Write},
rc::Rc,
};
use environment::Environment;
use interpreter::RuntimeError;
use parser::{ParseError, Parser};
use parser::Parser;
use scanner::Scanner;
use token::{Token, TokenType};
mod ast;
mod environment;
mod interpreter;
mod parser;
mod printer;
@ -26,7 +22,6 @@ pub enum RunError {
FileReadError(io::Error),
OtherError(Box<dyn Error>), // to be added,
RuntimeError(RuntimeError),
ParseError,
}
impl<E: Error + 'static> From<E> for RunError {
@ -37,53 +32,37 @@ impl<E: Error + 'static> From<E> for RunError {
pub fn run_file(path: &str) -> Result<(), RunError> {
let file = fs::read_to_string(path).map_err(RunError::FileReadError)?;
let environment = Rc::new(RefCell::new(Environment::new()));
run(&file, &environment)?;
run(&file)?;
Ok(())
}
pub fn run(src: &str, environment: &Rc<RefCell<Environment>>) -> Result<(), RunError> {
pub fn run(src: &str) -> Result<(), RunError> {
let mut scanner = Scanner::new(src.to_string());
let tokens = scanner.scan_tokens()?;
let mut parser = Parser::new(tokens);
let statements = parser.parse();
let expression = parser.parse().inspect_err(|e| error(&e.token, &e.msg))?;
// i don't want to collect the errors and allocate a vec
let mut p_error = false;
for err in statements.iter().filter_map(|x| x.as_ref().err()) {
if !p_error {
p_error = true;
}
error(err);
}
if p_error {
return Err(RunError::ParseError);
}
let statements = statements.into_iter().flatten().collect();
interpreter::interpret(&statements, environment)
let interpreted_value = interpreter::interpret(&expression)
.inspect_err(runtime_error)
.map_err(RunError::RuntimeError)?;
println!("{interpreted_value}");
Ok(())
}
pub fn run_prompt() -> Result<(), Box<dyn Error>> {
let stdin = io::stdin();
let input = &mut String::new();
let environment = Rc::new(RefCell::new(Environment::new()));
loop {
input.clear();
print!("> ");
io::stdout().flush()?;
stdin.read_line(input)?;
let _ = run(input, &environment);
let _ = run(input);
}
}
@ -97,10 +76,10 @@ pub fn report(line: usize, location: &str, message: &str) {
eprintln!("[line {line}] Error {location}: {message}");
}
fn error(ParseError { token, msg }: &ParseError) {
fn error(token: &Token, message: &str) {
match token.t_type {
TokenType::EOF => report(token.line, " at end", msg),
_ => report(token.line, &format!("at '{}'", token.lexeme), msg),
TokenType::EOF => report(token.line, " at end", message),
_ => report(token.line, &format!(" at '{}'", token.lexeme), message),
}
}

View File

@ -23,10 +23,6 @@ fn main() -> ExitCode {
if let Err(RunError::RuntimeError(r)) = result {
return ExitCode::from(70);
}
if let Err(RunError::ParseError) = result {
return ExitCode::from(75);
}
} else {
let result = run_prompt();

View File

@ -1,7 +1,7 @@
use std::fmt::Display;
use crate::{
ast::{Expr, Stmt},
ast::Expr,
token::{LiteralType, Token, TokenType},
};
@ -29,85 +29,8 @@ impl Parser<'_> {
Parser { tokens, current: 0 }
}
//pub fn parse(&mut self) -> Result<Expr, ParseError> {
// self.expression()
//}
// maps to program rule in the grammar
pub fn parse(&mut self) -> Vec<Result<Stmt, ParseError>> {
let mut statements = Vec::new();
while !self.is_at_end() {
statements.push(self.declaration());
}
statements
}
fn declaration(&mut self) -> Result<Stmt, ParseError> {
let stmt = if self.match_token(&[TokenType::Var]) {
self.var_declaration()
} else {
self.statement()
};
stmt.inspect_err(|_| self.synchronize())
}
fn var_declaration(&mut self) -> Result<Stmt, ParseError> {
let name = self.consume(TokenType::Identifier, "Expect variable name")?;
let initializer = if self.match_token(&[TokenType::Equal]) {
Some(self.expression()?)
} else {
None
};
self.consume(
TokenType::Semicolon,
"Expect ';' after variable declaration.",
)?;
Ok(Stmt::Var { name, initializer })
}
fn statement(&mut self) -> Result<Stmt, ParseError> {
if self.match_token(&[TokenType::Print]) {
return self.print_statement();
}
if self.match_token(&[TokenType::LeftBrace]) {
return Ok(Stmt::Block {
statements: self.block()?,
});
}
self.expression_statement()
}
fn block(&mut self) -> Result<Vec<Stmt>, ParseError> {
let mut statements = Vec::new();
while !self.check(TokenType::RightBrace) && !self.is_at_end() {
statements.push(self.declaration()?);
}
self.consume(TokenType::RightBrace, "Expect '}' after block.")?;
Ok(statements)
}
fn print_statement(&mut self) -> Result<Stmt, ParseError> {
let expression = self.expression()?;
self.consume(TokenType::Semicolon, "Expect ';' after value.")?;
Ok(Stmt::Print { expression })
}
fn expression_statement(&mut self) -> Result<Stmt, ParseError> {
let expression = self.expression()?;
self.consume(TokenType::Semicolon, "Expect ';' after expression.")?;
Ok(Stmt::Expression { expression })
pub fn parse(&mut self) -> Result<Expr, ParseError> {
self.expression()
}
fn expression(&mut self) -> Result<Expr, ParseError> {
@ -118,29 +41,7 @@ impl Parser<'_> {
// comma -> equality ("," equality)* ; // expression grammar
fn comma(&mut self) -> Result<Expr, ParseError> {
use TokenType::*;
self.left_association_binary(&[Comma], Self::assignment)
}
fn assignment(&mut self) -> Result<Expr, ParseError> {
let expr = self.ternary()?;
if self.match_token(&[TokenType::Equal]) {
let value = self.assignment()?;
let equals = self.previous();
if let Expr::Variable { name } = expr {
return Ok(Expr::Assign {
name,
value: Box::new(value),
});
}
return Err(ParseError {
token: equals.clone(),
msg: "Invalid assignment target.".to_string(),
});
}
Ok(expr)
self.left_association_binary(&[Comma], Self::ternary)
}
// ternary -> equality ("?" expression : ternary)? // expression grammar
@ -239,12 +140,6 @@ impl Parser<'_> {
});
}
if self.match_token(&[Identifier]) {
return Ok(Expr::Variable {
name: self.previous().clone(),
});
}
if self.match_token(&[Equal, BangEqual]) {
let _ = self.equality();
return Err(ParseError {
@ -294,6 +189,7 @@ impl Parser<'_> {
})
}
// will not be used for the time being (per the book)
// used for error recovery
fn synchronize(&mut self) {
use TokenType::*;
@ -307,8 +203,9 @@ impl Parser<'_> {
if let Class | Fun | Var | For | If | While | Print | Return = self.peek().t_type {
return;
}
self.advance();
}
self.advance();
}
fn left_association_binary(

View File

@ -16,8 +16,6 @@ pub fn pretty_print(expr: &Expr) -> String {
second,
third,
} => parenthesize("?:", &[first, second, third]),
Expr::Variable { name } => name.lexeme.clone(),
Expr::Assign { name, value } => parenthesize(&name.lexeme, &[value]),
}
}