mirror of
https://github.com/TheM1Stery/izanami.git
synced 2025-04-20 08:51:12 +00:00
Compare commits
No commits in common. "129d03b74d74236327f299feb635b940717785eb" and "3cfde406d3821943a8819403eec3bf0aa851d4c5" have entirely different histories.
129d03b74d
...
3cfde406d3
23
src/ast.rs
23
src/ast.rs
@ -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>,
|
||||
},
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
|
43
src/lib.rs
43
src/lib.rs
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
117
src/parser.rs
117
src/parser.rs
@ -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(
|
||||
|
@ -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]),
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user