Compare commits

..

2 Commits

5 changed files with 195 additions and 27 deletions

View File

@ -29,6 +29,11 @@ pub enum Expr {
name: Token, name: Token,
value: Box<Expr>, value: Box<Expr>,
}, },
Logical {
left: Box<Expr>,
op: Token,
right: Box<Expr>,
},
} }
pub enum Stmt { pub enum Stmt {
@ -38,6 +43,11 @@ pub enum Stmt {
Expression { Expression {
expression: Expr, expression: Expr,
}, },
If {
condition: Expr,
then_branch: Box<Stmt>,
else_branch: Option<Box<Stmt>>,
},
Print { Print {
expression: Expr, expression: Expr,
}, },
@ -45,4 +55,8 @@ pub enum Stmt {
name: Token, name: Token,
initializer: Option<Expr>, initializer: Option<Expr>,
}, },
While {
condition: Expr,
body: Box<Stmt>,
},
} }

View File

@ -2,7 +2,7 @@ use std::{cell::RefCell, rc::Rc};
use crate::{ use crate::{
ast::{Expr, Stmt}, ast::{Expr, Stmt},
environment::Environment, environment::{self, Environment},
token::{LiteralType, Token, TokenType}, token::{LiteralType, Token, TokenType},
}; };
@ -52,6 +52,22 @@ fn execute(statement: &Stmt, environment: &Rc<RefCell<Environment>>) -> Result<(
Stmt::Block { statements } => { Stmt::Block { statements } => {
execute_block(statements, environment)?; execute_block(statements, environment)?;
} }
Stmt::If {
condition,
then_branch,
else_branch,
} => {
if is_truthy(&evaluate(condition, &mut environment.borrow_mut())?) {
execute(then_branch, environment)?;
} else if let Some(else_branch) = else_branch {
execute(else_branch, environment)?;
}
}
Stmt::While { condition, body } => {
while is_truthy(&evaluate(condition, &mut environment.borrow_mut())?) {
execute(body, environment)?;
}
}
} }
Ok(()) Ok(())
@ -108,6 +124,19 @@ fn evaluate(expr: &Expr, environment: &mut Environment) -> Result<LiteralType, R
Ok(value) Ok(value)
} }
Expr::Logical { left, op, right } => {
let left = evaluate(left, environment)?;
if op.t_type == TokenType::OR {
if is_truthy(&left) {
return Ok(left);
}
} else if !is_truthy(&left) {
return Ok(left);
}
evaluate(right, environment)
}
} }
} }

View File

@ -1,14 +1,16 @@
use std::{env::args_os, ffi::OsString, process::ExitCode}; use std::{cmp::Ordering, env::args_os, ffi::OsString, process::ExitCode};
use izanami::{run_file, run_prompt, RunError}; use izanami::{run_file, run_prompt, RunError};
fn main() -> ExitCode { fn main() -> ExitCode {
let args: Vec<OsString> = args_os().collect(); let args: Vec<OsString> = args_os().collect();
if args.len() > 2 { match args.len().cmp(&2) {
Ordering::Greater => {
println!("usage: izanami [script]"); println!("usage: izanami [script]");
return ExitCode::from(64); return ExitCode::from(64);
} else if args.len() == 2 { }
Ordering::Equal => {
let result = run_file(args[1].to_str().unwrap()); let result = run_file(args[1].to_str().unwrap());
if let Err(RunError::FileReadError(e)) = result { if let Err(RunError::FileReadError(e)) = result {
@ -27,7 +29,8 @@ fn main() -> ExitCode {
if let Err(RunError::ParseError) = result { if let Err(RunError::ParseError) = result {
return ExitCode::from(75); return ExitCode::from(75);
} }
} else { }
Ordering::Less => {
let result = run_prompt(); let result = run_prompt();
if let Err(res) = result { if let Err(res) = result {
@ -35,6 +38,7 @@ fn main() -> ExitCode {
return ExitCode::from(1); return ExitCode::from(1);
} }
} }
}
ExitCode::SUCCESS ExitCode::SUCCESS
} }

View File

@ -71,9 +71,18 @@ impl Parser<'_> {
} }
fn statement(&mut self) -> Result<Stmt, ParseError> { fn statement(&mut self) -> Result<Stmt, ParseError> {
if self.match_token(&[TokenType::For]) {
return self.for_statement();
}
if self.match_token(&[TokenType::If]) {
return self.if_statement();
}
if self.match_token(&[TokenType::Print]) { if self.match_token(&[TokenType::Print]) {
return self.print_statement(); return self.print_statement();
} }
if self.match_token(&[TokenType::While]) {
return self.while_statement();
}
if self.match_token(&[TokenType::LeftBrace]) { if self.match_token(&[TokenType::LeftBrace]) {
return Ok(Stmt::Block { return Ok(Stmt::Block {
@ -96,6 +105,86 @@ impl Parser<'_> {
Ok(statements) Ok(statements)
} }
fn if_statement(&mut self) -> Result<Stmt, ParseError> {
self.consume(TokenType::LeftParen, "Expect '(' after 'if'.")?;
let condition = self.expression()?;
self.consume(TokenType::RightParen, "Expect ')' after if condition.")?;
let then_branch = Box::new(self.statement()?);
let else_branch = if self.match_token(&[TokenType::Else]) {
Some(Box::new(self.statement()?))
} else {
None
};
Ok(Stmt::If {
condition,
then_branch,
else_branch,
})
}
fn while_statement(&mut self) -> Result<Stmt, ParseError> {
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()?);
Ok(Stmt::While { condition, body })
}
fn for_statement(&mut self) -> Result<Stmt, ParseError> {
self.consume(TokenType::LeftParen, "Expect '(' after 'for'.")?;
let initializer = if self.match_token(&[TokenType::Semicolon]) {
None
} else if self.match_token(&[TokenType::Var]) {
Some(self.var_declaration()?)
} else {
Some(self.expression_statement()?)
};
let condition = if !self.match_token(&[TokenType::Semicolon]) {
Some(self.expression()?)
} else {
None
};
self.consume(TokenType::Semicolon, "Expect ';' after loop condition")?;
let increment = if !self.match_token(&[TokenType::RightParen]) {
Some(self.expression()?)
} else {
None
};
self.consume(TokenType::RightParen, "Expect ')' after for clauses.")?;
let body = match increment {
Some(inc) => Stmt::Block {
statements: vec![self.statement()?, Stmt::Expression { expression: inc }],
},
None => self.statement()?,
};
let condition = condition.unwrap_or(Expr::Literal {
value: LiteralType::Bool(true),
});
let body = Stmt::While {
condition,
body: Box::new(body),
};
let body = match initializer {
Some(init) => Stmt::Block {
statements: vec![init, body],
},
None => body,
};
Ok(body)
}
fn print_statement(&mut self) -> Result<Stmt, ParseError> { fn print_statement(&mut self) -> Result<Stmt, ParseError> {
let expression = self.expression()?; let expression = self.expression()?;
self.consume(TokenType::Semicolon, "Expect ';' after value.")?; self.consume(TokenType::Semicolon, "Expect ';' after value.")?;
@ -146,7 +235,7 @@ impl Parser<'_> {
// ternary -> equality ("?" expression : ternary)? // expression grammar // ternary -> equality ("?" expression : ternary)? // expression grammar
fn ternary(&mut self) -> Result<Expr, ParseError> { fn ternary(&mut self) -> Result<Expr, ParseError> {
use TokenType::*; use TokenType::*;
let expr = self.equality()?; let expr = self.or()?;
if self.match_token(&[Question]) { if self.match_token(&[Question]) {
let second = self.expression()?; let second = self.expression()?;
@ -162,6 +251,37 @@ impl Parser<'_> {
Ok(expr) Ok(expr)
} }
fn or(&mut self) -> Result<Expr, ParseError> {
let mut expr = self.and()?;
while self.match_token(&[TokenType::OR]) {
let op = self.previous().clone();
let right = self.and()?;
expr = Expr::Logical {
left: Box::new(expr),
op,
right: Box::new(right),
};
}
Ok(expr)
}
fn and(&mut self) -> Result<Expr, ParseError> {
let mut expr = self.equality()?;
while self.match_token(&[TokenType::And]) {
let op = self.previous().clone();
let right = self.equality()?;
expr = Expr::Logical {
left: Box::new(expr),
op,
right: Box::new(right),
};
}
Ok(expr)
}
fn equality(&mut self) -> Result<Expr, ParseError> { fn equality(&mut self) -> Result<Expr, ParseError> {
use TokenType::*; use TokenType::*;
self.left_association_binary(&[BangEqual, EqualEqual], Self::comparison) self.left_association_binary(&[BangEqual, EqualEqual], Self::comparison)

View File

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