feat: finish challenge #3 of ch9

This commit is contained in:
Seymur Bagirov 2025-01-19 01:51:32 +04:00
parent 2230c468e4
commit 04e8d5b8cb
7 changed files with 108 additions and 13 deletions

View File

@ -40,6 +40,7 @@ pub enum Stmt {
Block { Block {
statements: Vec<Stmt>, statements: Vec<Stmt>,
}, },
Break,
Expression { Expression {
expression: Expr, expression: Expr,
}, },

View File

@ -1,8 +1,9 @@
use core::panic;
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, rc::Rc};
use crate::{ use crate::{
ast::{Expr, Stmt}, ast::{Expr, Stmt},
environment::{self, Environment}, environment::Environment,
token::{LiteralType, Token, TokenType}, 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( pub fn interpret(
statements: &Vec<Stmt>, statements: &Vec<Stmt>,
environment: &Rc<RefCell<Environment>>, environment: &Rc<RefCell<Environment>>,
) -> Result<(), RuntimeError> { ) -> Result<(), InterpreterError> {
for statement in statements { for statement in statements {
execute(statement, environment)?; execute(statement, environment)?;
} }
@ -32,7 +53,10 @@ pub fn interpret(
Ok(()) Ok(())
} }
fn execute(statement: &Stmt, environment: &Rc<RefCell<Environment>>) -> Result<(), RuntimeError> { fn execute(
statement: &Stmt,
environment: &Rc<RefCell<Environment>>,
) -> Result<(), InterpreterError> {
match statement { match statement {
Stmt::Expression { expression } => { Stmt::Expression { expression } => {
evaluate(expression, &mut environment.borrow_mut())?; evaluate(expression, &mut environment.borrow_mut())?;
@ -65,10 +89,14 @@ fn execute(statement: &Stmt, environment: &Rc<RefCell<Environment>>) -> Result<(
} }
Stmt::While { condition, body } => { Stmt::While { condition, body } => {
while is_truthy(&evaluate(condition, &mut environment.borrow_mut())?) { 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(()) Ok(())
} }
@ -76,7 +104,7 @@ fn execute(statement: &Stmt, environment: &Rc<RefCell<Environment>>) -> Result<(
fn execute_block( fn execute_block(
statements: &Vec<Stmt>, statements: &Vec<Stmt>,
environment: &Rc<RefCell<Environment>>, environment: &Rc<RefCell<Environment>>,
) -> Result<(), RuntimeError> { ) -> Result<(), InterpreterError> {
let block_enviroment = Rc::new(RefCell::new(Environment::with_enclosing(environment))); let block_enviroment = Rc::new(RefCell::new(Environment::with_enclosing(environment)));
for stmt in statements { for stmt in statements {
execute(stmt, &block_enviroment)?; execute(stmt, &block_enviroment)?;
@ -85,7 +113,7 @@ fn execute_block(
Ok(()) Ok(())
} }
fn evaluate(expr: &Expr, environment: &mut Environment) -> Result<LiteralType, RuntimeError> { fn evaluate(expr: &Expr, environment: &mut Environment) -> Result<LiteralType, InterpreterError> {
match expr { match expr {
Expr::Ternary { Expr::Ternary {
first, first,
@ -112,7 +140,8 @@ fn evaluate(expr: &Expr, environment: &mut Environment) -> Result<LiteralType, R
token: name.clone(), token: name.clone(),
message: format!("Uninitialized variable {}.", name.lexeme), message: format!("Uninitialized variable {}.", name.lexeme),
}) })
}), })
.map_err(InterpreterError::RuntimeError),
Expr::Assign { name, value } => { Expr::Assign { name, value } => {
let value = evaluate(value, environment)?; let value = evaluate(value, environment)?;
environment environment
@ -145,7 +174,7 @@ fn ternary(
second: &Expr, second: &Expr,
third: &Expr, third: &Expr,
environment: &mut Environment, environment: &mut Environment,
) -> Result<LiteralType, RuntimeError> { ) -> Result<LiteralType, InterpreterError> {
let first = evaluate(first, environment)?; let first = evaluate(first, environment)?;
if is_truthy(&first) { if is_truthy(&first) {
return evaluate(second, environment); return evaluate(second, environment);
@ -157,7 +186,7 @@ fn binary(
left: &LiteralType, left: &LiteralType,
right: &LiteralType, right: &LiteralType,
op: &Token, op: &Token,
) -> Result<LiteralType, RuntimeError> { ) -> Result<LiteralType, InterpreterError> {
use LiteralType::{Bool, Number, String}; use LiteralType::{Bool, Number, String};
use TokenType::{ use TokenType::{
BangEqual, Comma, EqualEqual, Greater, GreaterEqual, Less, LessEqual, Minus, Plus, Slash, 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)), (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 operator discard the left operand, so we just return the evaluation of the right operand */
(Comma, _,_) => Ok(right.clone()), (Comma, _,_) => Ok(right.clone()),
(Greater | GreaterEqual | Less | LessEqual | Minus | Slash | Star, _, _) => Err(RuntimeError::new(op, "Operands must be numbers")), (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")), (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 evaluate. Some case is a binary operation that wasn't matched")
} }

View File

@ -10,7 +10,7 @@ use environment::Environment;
use interpreter::RuntimeError; use interpreter::RuntimeError;
use parser::{ParseError, Parser}; use parser::{ParseError, Parser};
use scanner::Scanner; use scanner::Scanner;
use token::{Token, TokenType}; use token::TokenType;
mod ast; mod ast;
mod environment; mod environment;
@ -68,6 +68,7 @@ pub fn run(src: &str, environment: &Rc<RefCell<Environment>>) -> Result<(), RunE
let statements = statements.into_iter().flatten().collect(); let statements = statements.into_iter().flatten().collect();
interpreter::interpret(&statements, environment) interpreter::interpret(&statements, environment)
.map_err(|x| x.into())
.inspect_err(runtime_error) .inspect_err(runtime_error)
.map_err(RunError::RuntimeError)?; .map_err(RunError::RuntimeError)?;

View File

@ -3,11 +3,13 @@ use std::fmt::Display;
use crate::{ use crate::{
ast::{Expr, Stmt}, ast::{Expr, Stmt},
token::{LiteralType, Token, TokenType}, token::{LiteralType, Token, TokenType},
utils::{defer, expr, ScopeCall},
}; };
pub struct Parser<'a> { pub struct Parser<'a> {
tokens: &'a Vec<Token>, tokens: &'a Vec<Token>,
current: usize, current: usize,
loop_depth: u32,
} }
#[derive(Debug)] #[derive(Debug)]
@ -26,7 +28,15 @@ impl std::error::Error for ParseError {}
impl Parser<'_> { impl Parser<'_> {
pub fn new(tokens: &Vec<Token>) -> 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> { //pub fn parse(&mut self) -> Result<Expr, ParseError> {
@ -84,6 +94,10 @@ impl Parser<'_> {
return self.while_statement(); return self.while_statement();
} }
if self.match_token(&[TokenType::Break]) {
return self.break_statement();
}
if self.match_token(&[TokenType::LeftBrace]) { if self.match_token(&[TokenType::LeftBrace]) {
return Ok(Stmt::Block { return Ok(Stmt::Block {
statements: self.block()?, statements: self.block()?,
@ -105,6 +119,18 @@ impl Parser<'_> {
Ok(statements) 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> { fn if_statement(&mut self) -> Result<Stmt, ParseError> {
self.consume(TokenType::LeftParen, "Expect '(' after 'if'.")?; self.consume(TokenType::LeftParen, "Expect '(' after 'if'.")?;
let condition = self.expression()?; let condition = self.expression()?;
@ -125,15 +151,20 @@ impl Parser<'_> {
} }
fn while_statement(&mut self) -> Result<Stmt, ParseError> { fn while_statement(&mut self) -> Result<Stmt, ParseError> {
self.loop_depth += 1;
self.consume(TokenType::LeftParen, "Expect '(' after 'while'.")?; self.consume(TokenType::LeftParen, "Expect '(' after 'while'.")?;
let condition = self.expression()?; let condition = self.expression()?;
self.consume(TokenType::RightParen, "Expect ')' after while condition.")?; self.consume(TokenType::RightParen, "Expect ')' after while condition.")?;
let body = Box::new(self.statement()?); let body = Box::new(self.statement()?);
defer! {
*self.loop_depth() += 1;
}
Ok(Stmt::While { condition, body }) Ok(Stmt::While { condition, body })
} }
fn for_statement(&mut self) -> Result<Stmt, ParseError> { fn for_statement(&mut self) -> Result<Stmt, ParseError> {
self.loop_depth += 1;
self.consume(TokenType::LeftParen, "Expect '(' after 'for'.")?; self.consume(TokenType::LeftParen, "Expect '(' after 'for'.")?;
let initializer = if self.match_token(&[TokenType::Semicolon]) { let initializer = if self.match_token(&[TokenType::Semicolon]) {
None None
@ -182,6 +213,10 @@ impl Parser<'_> {
None => body, None => body,
}; };
defer! {
*self.loop_depth() -= 1;
}
Ok(body) Ok(body)
} }

View File

@ -266,6 +266,7 @@ fn get_identified_keyword(identifier: &str) -> Option<TokenType> {
"true" => Some(TokenType::True), "true" => Some(TokenType::True),
"var" => Some(TokenType::Var), "var" => Some(TokenType::Var),
"while" => Some(TokenType::While), "while" => Some(TokenType::While),
"break" => Some(TokenType::Break),
_ => None, _ => None,
} }
} }

View File

@ -30,6 +30,7 @@ pub enum TokenType {
Number, Number,
And, And,
Break,
Class, Class,
Else, Else,
False, False,

View File

@ -50,3 +50,30 @@ impl StringUtils for String {
self.substring(start, len) 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;