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 {
statements: Vec<Stmt>,
},
Break,
Expression {
expression: Expr,
},

View File

@ -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,10 +89,14 @@ 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")
}

View File

@ -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)?;

View File

@ -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)
}

View File

@ -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,
}
}

View File

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

View File

@ -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;