mirror of
https://github.com/TheM1Stery/izanami.git
synced 2025-04-19 16:31:11 +00:00
feat: finish challenge #3 of ch9
This commit is contained in:
parent
2230c468e4
commit
04e8d5b8cb
@ -40,6 +40,7 @@ pub enum Stmt {
|
||||
Block {
|
||||
statements: Vec<Stmt>,
|
||||
},
|
||||
Break,
|
||||
Expression {
|
||||
expression: Expr,
|
||||
},
|
||||
|
@ -1,8 +1,9 @@
|
||||
use core::panic;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
ast::{Expr, Stmt},
|
||||
environment::{self, Environment},
|
||||
environment::Environment,
|
||||
token::{LiteralType, Token, TokenType},
|
||||
};
|
||||
|
||||
@ -21,10 +22,30 @@ impl RuntimeError {
|
||||
}
|
||||
}
|
||||
|
||||
pub enum InterpreterError {
|
||||
RuntimeError(RuntimeError),
|
||||
BreakSignal,
|
||||
}
|
||||
|
||||
impl From<RuntimeError> for InterpreterError {
|
||||
fn from(value: RuntimeError) -> Self {
|
||||
Self::RuntimeError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InterpreterError> for RuntimeError {
|
||||
fn from(value: InterpreterError) -> Self {
|
||||
match value {
|
||||
InterpreterError::RuntimeError(runtime_error) => runtime_error,
|
||||
InterpreterError::BreakSignal => panic!("Not a runtime error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interpret(
|
||||
statements: &Vec<Stmt>,
|
||||
environment: &Rc<RefCell<Environment>>,
|
||||
) -> Result<(), RuntimeError> {
|
||||
) -> Result<(), InterpreterError> {
|
||||
for statement in statements {
|
||||
execute(statement, environment)?;
|
||||
}
|
||||
@ -32,7 +53,10 @@ pub fn interpret(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute(statement: &Stmt, environment: &Rc<RefCell<Environment>>) -> Result<(), RuntimeError> {
|
||||
fn execute(
|
||||
statement: &Stmt,
|
||||
environment: &Rc<RefCell<Environment>>,
|
||||
) -> Result<(), InterpreterError> {
|
||||
match statement {
|
||||
Stmt::Expression { expression } => {
|
||||
evaluate(expression, &mut environment.borrow_mut())?;
|
||||
@ -65,9 +89,13 @@ fn execute(statement: &Stmt, environment: &Rc<RefCell<Environment>>) -> Result<(
|
||||
}
|
||||
Stmt::While { condition, body } => {
|
||||
while is_truthy(&evaluate(condition, &mut environment.borrow_mut())?) {
|
||||
execute(body, environment)?;
|
||||
let result = execute(body, environment);
|
||||
if result.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Stmt::Break => Err(InterpreterError::BreakSignal)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -76,7 +104,7 @@ fn execute(statement: &Stmt, environment: &Rc<RefCell<Environment>>) -> Result<(
|
||||
fn execute_block(
|
||||
statements: &Vec<Stmt>,
|
||||
environment: &Rc<RefCell<Environment>>,
|
||||
) -> Result<(), RuntimeError> {
|
||||
) -> Result<(), InterpreterError> {
|
||||
let block_enviroment = Rc::new(RefCell::new(Environment::with_enclosing(environment)));
|
||||
for stmt in statements {
|
||||
execute(stmt, &block_enviroment)?;
|
||||
@ -85,7 +113,7 @@ fn execute_block(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn evaluate(expr: &Expr, environment: &mut Environment) -> Result<LiteralType, RuntimeError> {
|
||||
fn evaluate(expr: &Expr, environment: &mut Environment) -> Result<LiteralType, InterpreterError> {
|
||||
match expr {
|
||||
Expr::Ternary {
|
||||
first,
|
||||
@ -112,7 +140,8 @@ fn evaluate(expr: &Expr, environment: &mut Environment) -> Result<LiteralType, R
|
||||
token: name.clone(),
|
||||
message: format!("Uninitialized variable {}.", name.lexeme),
|
||||
})
|
||||
}),
|
||||
})
|
||||
.map_err(InterpreterError::RuntimeError),
|
||||
Expr::Assign { name, value } => {
|
||||
let value = evaluate(value, environment)?;
|
||||
environment
|
||||
@ -145,7 +174,7 @@ fn ternary(
|
||||
second: &Expr,
|
||||
third: &Expr,
|
||||
environment: &mut Environment,
|
||||
) -> Result<LiteralType, RuntimeError> {
|
||||
) -> Result<LiteralType, InterpreterError> {
|
||||
let first = evaluate(first, environment)?;
|
||||
if is_truthy(&first) {
|
||||
return evaluate(second, environment);
|
||||
@ -157,7 +186,7 @@ fn binary(
|
||||
left: &LiteralType,
|
||||
right: &LiteralType,
|
||||
op: &Token,
|
||||
) -> Result<LiteralType, RuntimeError> {
|
||||
) -> Result<LiteralType, InterpreterError> {
|
||||
use LiteralType::{Bool, Number, String};
|
||||
use TokenType::{
|
||||
BangEqual, Comma, EqualEqual, Greater, GreaterEqual, Less, LessEqual, Minus, Plus, Slash,
|
||||
@ -180,8 +209,8 @@ fn binary(
|
||||
(Star, Number(left), Number(right)) => Ok(Number(left * right)),
|
||||
/* comma operator discard the left operand, so we just return the evaluation of the right operand */
|
||||
(Comma, _,_) => Ok(right.clone()),
|
||||
(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")),
|
||||
(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")
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use environment::Environment;
|
||||
use interpreter::RuntimeError;
|
||||
use parser::{ParseError, Parser};
|
||||
use scanner::Scanner;
|
||||
use token::{Token, TokenType};
|
||||
use token::TokenType;
|
||||
|
||||
mod ast;
|
||||
mod environment;
|
||||
@ -68,6 +68,7 @@ pub fn run(src: &str, environment: &Rc<RefCell<Environment>>) -> Result<(), RunE
|
||||
let statements = statements.into_iter().flatten().collect();
|
||||
|
||||
interpreter::interpret(&statements, environment)
|
||||
.map_err(|x| x.into())
|
||||
.inspect_err(runtime_error)
|
||||
.map_err(RunError::RuntimeError)?;
|
||||
|
||||
|
@ -3,11 +3,13 @@ use std::fmt::Display;
|
||||
use crate::{
|
||||
ast::{Expr, Stmt},
|
||||
token::{LiteralType, Token, TokenType},
|
||||
utils::{defer, expr, ScopeCall},
|
||||
};
|
||||
|
||||
pub struct Parser<'a> {
|
||||
tokens: &'a Vec<Token>,
|
||||
current: usize,
|
||||
loop_depth: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -26,7 +28,15 @@ impl std::error::Error for ParseError {}
|
||||
|
||||
impl Parser<'_> {
|
||||
pub fn new(tokens: &Vec<Token>) -> Parser<'_> {
|
||||
Parser { tokens, current: 0 }
|
||||
Parser {
|
||||
tokens,
|
||||
current: 0,
|
||||
loop_depth: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn loop_depth(&mut self) -> &mut u32 {
|
||||
&mut self.loop_depth
|
||||
}
|
||||
|
||||
//pub fn parse(&mut self) -> Result<Expr, ParseError> {
|
||||
@ -84,6 +94,10 @@ impl Parser<'_> {
|
||||
return self.while_statement();
|
||||
}
|
||||
|
||||
if self.match_token(&[TokenType::Break]) {
|
||||
return self.break_statement();
|
||||
}
|
||||
|
||||
if self.match_token(&[TokenType::LeftBrace]) {
|
||||
return Ok(Stmt::Block {
|
||||
statements: self.block()?,
|
||||
@ -105,6 +119,18 @@ impl Parser<'_> {
|
||||
Ok(statements)
|
||||
}
|
||||
|
||||
fn break_statement(&mut self) -> Result<Stmt, ParseError> {
|
||||
if *self.loop_depth() == 0 {
|
||||
return Err(ParseError {
|
||||
token: self.previous().clone(),
|
||||
msg: "Must be inside a loop to use 'break'".to_string(),
|
||||
});
|
||||
}
|
||||
self.consume(TokenType::Semicolon, "Expect ';' after 'break'")?;
|
||||
|
||||
Ok(Stmt::Break)
|
||||
}
|
||||
|
||||
fn if_statement(&mut self) -> Result<Stmt, ParseError> {
|
||||
self.consume(TokenType::LeftParen, "Expect '(' after 'if'.")?;
|
||||
let condition = self.expression()?;
|
||||
@ -125,15 +151,20 @@ impl Parser<'_> {
|
||||
}
|
||||
|
||||
fn while_statement(&mut self) -> Result<Stmt, ParseError> {
|
||||
self.loop_depth += 1;
|
||||
self.consume(TokenType::LeftParen, "Expect '(' after 'while'.")?;
|
||||
let condition = self.expression()?;
|
||||
self.consume(TokenType::RightParen, "Expect ')' after while condition.")?;
|
||||
let body = Box::new(self.statement()?);
|
||||
defer! {
|
||||
*self.loop_depth() += 1;
|
||||
}
|
||||
|
||||
Ok(Stmt::While { condition, body })
|
||||
}
|
||||
|
||||
fn for_statement(&mut self) -> Result<Stmt, ParseError> {
|
||||
self.loop_depth += 1;
|
||||
self.consume(TokenType::LeftParen, "Expect '(' after 'for'.")?;
|
||||
let initializer = if self.match_token(&[TokenType::Semicolon]) {
|
||||
None
|
||||
@ -182,6 +213,10 @@ impl Parser<'_> {
|
||||
None => body,
|
||||
};
|
||||
|
||||
defer! {
|
||||
*self.loop_depth() -= 1;
|
||||
}
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
|
@ -266,6 +266,7 @@ fn get_identified_keyword(identifier: &str) -> Option<TokenType> {
|
||||
"true" => Some(TokenType::True),
|
||||
"var" => Some(TokenType::Var),
|
||||
"while" => Some(TokenType::While),
|
||||
"break" => Some(TokenType::Break),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ pub enum TokenType {
|
||||
Number,
|
||||
|
||||
And,
|
||||
Break,
|
||||
Class,
|
||||
Else,
|
||||
False,
|
||||
|
27
src/utils.rs
27
src/utils.rs
@ -50,3 +50,30 @@ impl StringUtils for String {
|
||||
self.substring(start, len)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ScopeCall<F: FnMut()> {
|
||||
pub c: Option<F>,
|
||||
}
|
||||
|
||||
impl<F: FnMut()> Drop for ScopeCall<F> {
|
||||
fn drop(&mut self) {
|
||||
self.c.take().unwrap()()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! expr {
|
||||
($e: expr) => {
|
||||
$e
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! defer {
|
||||
($($data: tt)*) => (
|
||||
let _scope_call = ScopeCall {
|
||||
c: Some(|| -> () { expr!({ $($data)* }) })
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) use defer;
|
||||
pub(crate) use expr;
|
||||
|
Loading…
Reference in New Issue
Block a user