mirror of
https://github.com/TheM1Stery/izanami.git
synced 2025-05-14 00:30:11 +00:00
feat: finish chapter 10 (functions)
this was a big chapter, but the most fun i had! I plan to add some native functions in the future and do the challenges of course
This commit is contained in:
parent
04e8d5b8cb
commit
4bb2ce1af4
15
src/ast.rs
15
src/ast.rs
@ -12,6 +12,11 @@ pub enum Expr {
|
|||||||
op: Token,
|
op: Token,
|
||||||
right: Box<Expr>,
|
right: Box<Expr>,
|
||||||
},
|
},
|
||||||
|
Call {
|
||||||
|
callee: Box<Expr>,
|
||||||
|
paren: Token,
|
||||||
|
args: Vec<Expr>,
|
||||||
|
},
|
||||||
Grouping {
|
Grouping {
|
||||||
expression: Box<Expr>,
|
expression: Box<Expr>,
|
||||||
},
|
},
|
||||||
@ -36,6 +41,7 @@ pub enum Expr {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
Block {
|
Block {
|
||||||
statements: Vec<Stmt>,
|
statements: Vec<Stmt>,
|
||||||
@ -44,6 +50,11 @@ pub enum Stmt {
|
|||||||
Expression {
|
Expression {
|
||||||
expression: Expr,
|
expression: Expr,
|
||||||
},
|
},
|
||||||
|
Function {
|
||||||
|
name: Token,
|
||||||
|
params: Vec<Token>,
|
||||||
|
body: Vec<Stmt>,
|
||||||
|
},
|
||||||
If {
|
If {
|
||||||
condition: Expr,
|
condition: Expr,
|
||||||
then_branch: Box<Stmt>,
|
then_branch: Box<Stmt>,
|
||||||
@ -52,6 +63,10 @@ pub enum Stmt {
|
|||||||
Print {
|
Print {
|
||||||
expression: Expr,
|
expression: Expr,
|
||||||
},
|
},
|
||||||
|
Return {
|
||||||
|
keyword: Token,
|
||||||
|
value: Option<Expr>,
|
||||||
|
},
|
||||||
Var {
|
Var {
|
||||||
name: Token,
|
name: Token,
|
||||||
initializer: Option<Expr>,
|
initializer: Option<Expr>,
|
||||||
|
105
src/callable.rs
Normal file
105
src/callable.rs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
use std::{cell::RefCell, fmt::Display, rc::Rc};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ast::Stmt,
|
||||||
|
environment::Environment,
|
||||||
|
interpreter::{execute_block, InterpreterEnvironment, InterpreterSignal, RuntimeError},
|
||||||
|
token::{LiteralType, Token},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait CallableTrait {
|
||||||
|
fn arity(&self) -> u8;
|
||||||
|
fn call(
|
||||||
|
&self,
|
||||||
|
args: &[LiteralType],
|
||||||
|
env: &InterpreterEnvironment,
|
||||||
|
) -> Result<LiteralType, InterpreterSignal>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Callable {
|
||||||
|
Function {
|
||||||
|
name: Box<Token>,
|
||||||
|
params: Vec<Token>,
|
||||||
|
body: Vec<Stmt>,
|
||||||
|
closure: Rc<RefCell<Environment>>,
|
||||||
|
},
|
||||||
|
NativeFunction(NativeFunction),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CallableTrait for Callable {
|
||||||
|
fn arity(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
Callable::Function { params, .. } => params.len() as u8,
|
||||||
|
Callable::NativeFunction(native_function) => native_function.arity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(
|
||||||
|
&self,
|
||||||
|
args: &[LiteralType],
|
||||||
|
env: &InterpreterEnvironment,
|
||||||
|
) -> Result<LiteralType, InterpreterSignal> {
|
||||||
|
match self {
|
||||||
|
Callable::Function {
|
||||||
|
name: _,
|
||||||
|
params,
|
||||||
|
body,
|
||||||
|
closure,
|
||||||
|
} => {
|
||||||
|
let environment = Rc::new(RefCell::new(Environment::with_enclosing(closure)));
|
||||||
|
|
||||||
|
for (param, arg) in params.iter().zip(args) {
|
||||||
|
environment
|
||||||
|
.borrow_mut()
|
||||||
|
.define(¶m.lexeme, Some(arg.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let environment = InterpreterEnvironment {
|
||||||
|
globals: Rc::clone(&env.globals),
|
||||||
|
environment,
|
||||||
|
};
|
||||||
|
|
||||||
|
match execute_block(body, &environment) {
|
||||||
|
Err(InterpreterSignal::Return(v)) => Ok(v),
|
||||||
|
v => v.map(|_| LiteralType::Nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Callable::NativeFunction(native_function) => (native_function.call_impl)(args),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NativeFunction {
|
||||||
|
name: String,
|
||||||
|
arity: u8,
|
||||||
|
call_impl: fn(&[LiteralType]) -> Result<LiteralType, InterpreterSignal>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NativeFunction {
|
||||||
|
pub fn new(
|
||||||
|
name: String,
|
||||||
|
arity: u8,
|
||||||
|
call_impl: fn(&[LiteralType]) -> Result<LiteralType, InterpreterSignal>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
arity,
|
||||||
|
call_impl,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for Callable {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Callable::Function { name, .. } => {
|
||||||
|
write!(f, "{}", name.lexeme)
|
||||||
|
}
|
||||||
|
Callable::NativeFunction(native_function) => {
|
||||||
|
write!(f, "{}", native_function.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,11 @@
|
|||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use crate::token::{LiteralType, Token};
|
use crate::{
|
||||||
|
callable::Callable,
|
||||||
|
token::{LiteralType, Token},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
values: HashMap<String, Option<LiteralType>>,
|
values: HashMap<String, Option<LiteralType>>,
|
||||||
enclosing: Option<Rc<RefCell<Environment>>>,
|
enclosing: Option<Rc<RefCell<Environment>>>,
|
||||||
|
@ -1,18 +1,30 @@
|
|||||||
use core::panic;
|
use core::panic;
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
rc::Rc,
|
||||||
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Expr, Stmt},
|
ast::{Expr, Stmt},
|
||||||
|
callable::{Callable, CallableTrait, NativeFunction},
|
||||||
environment::Environment,
|
environment::Environment,
|
||||||
token::{LiteralType, Token, TokenType},
|
token::{LiteralType, Token, TokenType},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type InterpreterResult = Result<LiteralType, InterpreterSignal>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RuntimeError {
|
pub struct RuntimeError {
|
||||||
pub token: Token,
|
pub token: Token,
|
||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct InterpreterEnvironment {
|
||||||
|
pub globals: Rc<RefCell<Environment>>,
|
||||||
|
pub environment: Rc<RefCell<Environment>>,
|
||||||
|
}
|
||||||
|
|
||||||
impl RuntimeError {
|
impl RuntimeError {
|
||||||
pub fn new(token: &Token, message: &str) -> RuntimeError {
|
pub fn new(token: &Token, message: &str) -> RuntimeError {
|
||||||
RuntimeError {
|
RuntimeError {
|
||||||
@ -22,22 +34,29 @@ impl RuntimeError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum InterpreterError {
|
pub enum InterpreterSignal {
|
||||||
RuntimeError(RuntimeError),
|
RuntimeError(RuntimeError),
|
||||||
BreakSignal,
|
Break,
|
||||||
|
Return(LiteralType),
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
This two impl blocks are for the ? operator. I'm too lazy to write the wrapping code for the enums and it also looks ugly,
|
||||||
|
so i just abuse the ? operator lol
|
||||||
|
Instead of InterpreterError::RuntimeError(RuntimeError {...} ) i can just RuntimeError {...}? to turn it into a InterpreterError
|
||||||
|
*/
|
||||||
|
|
||||||
impl From<RuntimeError> for InterpreterError {
|
impl From<RuntimeError> for InterpreterSignal {
|
||||||
fn from(value: RuntimeError) -> Self {
|
fn from(value: RuntimeError) -> Self {
|
||||||
Self::RuntimeError(value)
|
Self::RuntimeError(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<InterpreterError> for RuntimeError {
|
impl From<InterpreterSignal> for RuntimeError {
|
||||||
fn from(value: InterpreterError) -> Self {
|
fn from(value: InterpreterSignal) -> Self {
|
||||||
match value {
|
match value {
|
||||||
InterpreterError::RuntimeError(runtime_error) => runtime_error,
|
InterpreterSignal::RuntimeError(runtime_error) => runtime_error,
|
||||||
InterpreterError::BreakSignal => panic!("Not a runtime error"),
|
InterpreterSignal::Break => panic!("Not a runtime error"),
|
||||||
|
InterpreterSignal::Return(_) => panic!("Not a runtime error"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,9 +64,30 @@ impl From<InterpreterError> for RuntimeError {
|
|||||||
pub fn interpret(
|
pub fn interpret(
|
||||||
statements: &Vec<Stmt>,
|
statements: &Vec<Stmt>,
|
||||||
environment: &Rc<RefCell<Environment>>,
|
environment: &Rc<RefCell<Environment>>,
|
||||||
) -> Result<(), InterpreterError> {
|
) -> Result<(), InterpreterSignal> {
|
||||||
|
let clock = |_arg: &[LiteralType]| {
|
||||||
|
Ok(LiteralType::Number(
|
||||||
|
SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.expect("Time went backwards")
|
||||||
|
.as_secs_f64()
|
||||||
|
/ 1000.0,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
let clock_function = NativeFunction::new("clock".to_string(), 0, clock);
|
||||||
|
let environment = InterpreterEnvironment {
|
||||||
|
globals: Rc::clone(environment),
|
||||||
|
environment: Rc::clone(environment),
|
||||||
|
};
|
||||||
|
environment.globals.borrow_mut().define(
|
||||||
|
"clock",
|
||||||
|
Some(LiteralType::Callable(Callable::NativeFunction(
|
||||||
|
clock_function,
|
||||||
|
))),
|
||||||
|
);
|
||||||
for statement in statements {
|
for statement in statements {
|
||||||
execute(statement, environment)?;
|
execute(statement, &environment)?
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -55,23 +95,24 @@ pub fn interpret(
|
|||||||
|
|
||||||
fn execute(
|
fn execute(
|
||||||
statement: &Stmt,
|
statement: &Stmt,
|
||||||
environment: &Rc<RefCell<Environment>>,
|
environment: &InterpreterEnvironment,
|
||||||
) -> Result<(), InterpreterError> {
|
) -> Result<(), InterpreterSignal> {
|
||||||
|
let curr_environment = &environment.environment;
|
||||||
match statement {
|
match statement {
|
||||||
Stmt::Expression { expression } => {
|
Stmt::Expression { expression } => {
|
||||||
evaluate(expression, &mut environment.borrow_mut())?;
|
evaluate(expression, environment)?;
|
||||||
}
|
}
|
||||||
Stmt::Print { expression } => {
|
Stmt::Print { expression } => {
|
||||||
let expr = evaluate(expression, &mut environment.borrow_mut())?;
|
let expr = evaluate(expression, environment)?;
|
||||||
println!("{expr}");
|
println!("{expr}");
|
||||||
}
|
}
|
||||||
Stmt::Var { name, initializer } => {
|
Stmt::Var { name, initializer } => {
|
||||||
let value = if let Some(initializer) = initializer {
|
let value = if let Some(initializer) = initializer {
|
||||||
Some(evaluate(initializer, &mut environment.borrow_mut())?)
|
Some(evaluate(initializer, environment)?)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
environment.borrow_mut().define(&name.lexeme, value);
|
curr_environment.borrow_mut().define(&name.lexeme, value);
|
||||||
}
|
}
|
||||||
Stmt::Block { statements } => {
|
Stmt::Block { statements } => {
|
||||||
execute_block(statements, environment)?;
|
execute_block(statements, environment)?;
|
||||||
@ -81,39 +122,69 @@ fn execute(
|
|||||||
then_branch,
|
then_branch,
|
||||||
else_branch,
|
else_branch,
|
||||||
} => {
|
} => {
|
||||||
if is_truthy(&evaluate(condition, &mut environment.borrow_mut())?) {
|
if is_truthy(&evaluate(condition, environment)?) {
|
||||||
execute(then_branch, environment)?;
|
execute(then_branch, environment)?;
|
||||||
} else if let Some(else_branch) = else_branch {
|
} else if let Some(else_branch) = else_branch {
|
||||||
execute(else_branch, environment)?;
|
execute(else_branch, environment)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::While { condition, body } => {
|
Stmt::While { condition, body } => {
|
||||||
while is_truthy(&evaluate(condition, &mut environment.borrow_mut())?) {
|
while is_truthy(&evaluate(condition, environment)?) {
|
||||||
let result = execute(body, environment);
|
let result = execute(body, environment);
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::Break => Err(InterpreterError::BreakSignal)?,
|
Stmt::Break => Err(InterpreterSignal::Break)?,
|
||||||
|
Stmt::Function { name, params, body } => {
|
||||||
|
let function = Callable::Function {
|
||||||
|
name: Box::new(name.clone()),
|
||||||
|
body: body.to_vec(),
|
||||||
|
params: params.to_vec(),
|
||||||
|
closure: Rc::clone(curr_environment),
|
||||||
|
};
|
||||||
|
environment
|
||||||
|
.globals
|
||||||
|
.borrow_mut()
|
||||||
|
.define(&name.lexeme, Some(LiteralType::Callable(function)));
|
||||||
|
}
|
||||||
|
Stmt::Return { value, .. } => {
|
||||||
|
let value = if let Some(v) = value {
|
||||||
|
evaluate(v, environment)?
|
||||||
|
} else {
|
||||||
|
LiteralType::Nil
|
||||||
|
};
|
||||||
|
|
||||||
|
return Err(InterpreterSignal::Return(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn execute_block(
|
pub fn execute_block(
|
||||||
statements: &Vec<Stmt>,
|
statements: &Vec<Stmt>,
|
||||||
environment: &Rc<RefCell<Environment>>,
|
environment: &InterpreterEnvironment,
|
||||||
) -> Result<(), InterpreterError> {
|
) -> Result<(), InterpreterSignal> {
|
||||||
let block_enviroment = Rc::new(RefCell::new(Environment::with_enclosing(environment)));
|
let block_enviroment = Rc::new(RefCell::new(Environment::with_enclosing(
|
||||||
|
&environment.environment,
|
||||||
|
)));
|
||||||
|
// we just move the block_enviroment to a new InterpreterEnvironment and clone the reference to
|
||||||
|
// globals, bcs outer environments might have the globals reference
|
||||||
|
let environment = InterpreterEnvironment {
|
||||||
|
globals: Rc::clone(&environment.globals),
|
||||||
|
environment: block_enviroment,
|
||||||
|
};
|
||||||
for stmt in statements {
|
for stmt in statements {
|
||||||
execute(stmt, &block_enviroment)?;
|
execute(stmt, &environment)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn evaluate(expr: &Expr, environment: &mut Environment) -> Result<LiteralType, InterpreterError> {
|
fn evaluate(expr: &Expr, environment: &InterpreterEnvironment) -> InterpreterResult {
|
||||||
|
let curr_environment = &environment.environment;
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Ternary {
|
Expr::Ternary {
|
||||||
first,
|
first,
|
||||||
@ -129,7 +200,8 @@ fn evaluate(expr: &Expr, environment: &mut Environment) -> Result<LiteralType, I
|
|||||||
Expr::Grouping { expression } => evaluate(expression, environment),
|
Expr::Grouping { expression } => evaluate(expression, environment),
|
||||||
Expr::Literal { value } => Ok(value.clone()),
|
Expr::Literal { value } => Ok(value.clone()),
|
||||||
Expr::Unary { op, right } => Ok(unary(&evaluate(right, environment)?, op)),
|
Expr::Unary { op, right } => Ok(unary(&evaluate(right, environment)?, op)),
|
||||||
Expr::Variable { name } => environment
|
Expr::Variable { name } => curr_environment
|
||||||
|
.borrow()
|
||||||
.get(name)
|
.get(name)
|
||||||
.ok_or_else(|| RuntimeError {
|
.ok_or_else(|| RuntimeError {
|
||||||
token: name.clone(),
|
token: name.clone(),
|
||||||
@ -141,10 +213,11 @@ fn evaluate(expr: &Expr, environment: &mut Environment) -> Result<LiteralType, I
|
|||||||
message: format!("Uninitialized variable {}.", name.lexeme),
|
message: format!("Uninitialized variable {}.", name.lexeme),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map_err(InterpreterError::RuntimeError),
|
.map_err(InterpreterSignal::RuntimeError),
|
||||||
Expr::Assign { name, value } => {
|
Expr::Assign { name, value } => {
|
||||||
let value = evaluate(value, environment)?;
|
let value = evaluate(value, environment)?;
|
||||||
environment
|
curr_environment
|
||||||
|
.borrow_mut()
|
||||||
.assign(name, value.clone())
|
.assign(name, value.clone())
|
||||||
.map_err(|_| RuntimeError {
|
.map_err(|_| RuntimeError {
|
||||||
token: name.clone(),
|
token: name.clone(),
|
||||||
@ -166,6 +239,38 @@ fn evaluate(expr: &Expr, environment: &mut Environment) -> Result<LiteralType, I
|
|||||||
|
|
||||||
evaluate(right, environment)
|
evaluate(right, environment)
|
||||||
}
|
}
|
||||||
|
Expr::Call {
|
||||||
|
callee,
|
||||||
|
paren,
|
||||||
|
args,
|
||||||
|
} => {
|
||||||
|
let callee_result = evaluate(callee, environment)?;
|
||||||
|
|
||||||
|
let mut arguments = Vec::new();
|
||||||
|
for arg in args {
|
||||||
|
arguments.push(evaluate(arg, environment)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
match callee_result {
|
||||||
|
LiteralType::Callable(function) => {
|
||||||
|
if arguments.len() as u8 != function.arity() {
|
||||||
|
Err(RuntimeError {
|
||||||
|
token: paren.clone(),
|
||||||
|
message: format!(
|
||||||
|
"Expected {} arguments but got {}.",
|
||||||
|
function.arity(),
|
||||||
|
args.len()
|
||||||
|
),
|
||||||
|
})?
|
||||||
|
}
|
||||||
|
Ok(function.call(&arguments, environment)?)
|
||||||
|
}
|
||||||
|
_ => Err(RuntimeError {
|
||||||
|
token: paren.clone(),
|
||||||
|
message: "Can only call functions and classes".to_string(),
|
||||||
|
})?,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,8 +278,8 @@ fn ternary(
|
|||||||
first: &Expr,
|
first: &Expr,
|
||||||
second: &Expr,
|
second: &Expr,
|
||||||
third: &Expr,
|
third: &Expr,
|
||||||
environment: &mut Environment,
|
environment: &InterpreterEnvironment,
|
||||||
) -> Result<LiteralType, InterpreterError> {
|
) -> InterpreterResult {
|
||||||
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);
|
||||||
@ -182,11 +287,7 @@ fn ternary(
|
|||||||
evaluate(third, environment)
|
evaluate(third, environment)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn binary(
|
fn binary(left: &LiteralType, right: &LiteralType, op: &Token) -> InterpreterResult {
|
||||||
left: &LiteralType,
|
|
||||||
right: &LiteralType,
|
|
||||||
op: &Token,
|
|
||||||
) -> 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,
|
||||||
@ -232,10 +333,14 @@ fn is_truthy(literal: &LiteralType) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_equal(left: &LiteralType, right: &LiteralType) -> bool {
|
pub fn is_equal(left: &LiteralType, right: &LiteralType) -> bool {
|
||||||
match (left, right) {
|
match (left, right) {
|
||||||
(LiteralType::Nil, LiteralType::Nil) => true,
|
(LiteralType::Nil, LiteralType::Nil) => true,
|
||||||
(LiteralType::Nil, _) => false,
|
(LiteralType::Nil, _) => false,
|
||||||
_ => left == right,
|
// i could've implemeneted PartialEq but it doesn't make sense for every LiteralType
|
||||||
|
(LiteralType::String(s), LiteralType::String(s2)) => s == s2,
|
||||||
|
(LiteralType::Number(n1), LiteralType::Number(n2)) => n1 == n2,
|
||||||
|
(LiteralType::Bool(t1), LiteralType::Bool(t2)) => t1 == t2,
|
||||||
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ use scanner::Scanner;
|
|||||||
use token::TokenType;
|
use token::TokenType;
|
||||||
|
|
||||||
mod ast;
|
mod ast;
|
||||||
|
mod callable;
|
||||||
mod environment;
|
mod environment;
|
||||||
mod interpreter;
|
mod interpreter;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
@ -55,7 +55,9 @@ impl Parser<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn declaration(&mut self) -> Result<Stmt, ParseError> {
|
fn declaration(&mut self) -> Result<Stmt, ParseError> {
|
||||||
let stmt = if self.match_token(&[TokenType::Var]) {
|
let stmt = if self.match_token(&[TokenType::Fun]) {
|
||||||
|
self.function("function")
|
||||||
|
} else if self.match_token(&[TokenType::Var]) {
|
||||||
self.var_declaration()
|
self.var_declaration()
|
||||||
} else {
|
} else {
|
||||||
self.statement()
|
self.statement()
|
||||||
@ -64,6 +66,42 @@ impl Parser<'_> {
|
|||||||
stmt.inspect_err(|_| self.synchronize())
|
stmt.inspect_err(|_| self.synchronize())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn function(&mut self, kind: &str) -> Result<Stmt, ParseError> {
|
||||||
|
let name = self.consume(TokenType::Identifier, &format!("Expect {} name.", kind))?;
|
||||||
|
self.consume(
|
||||||
|
TokenType::LeftParen,
|
||||||
|
&format!("Expect '(' after {kind} name."),
|
||||||
|
)?;
|
||||||
|
let mut params = Vec::new();
|
||||||
|
if !self.check(TokenType::RightParen) {
|
||||||
|
loop {
|
||||||
|
if params.len() >= 255 {
|
||||||
|
return Err(ParseError {
|
||||||
|
token: self.peek().clone(),
|
||||||
|
msg: "Can't have more than 255 parameters".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
params.push(self.consume(TokenType::Identifier, "Expect parameter name.")?);
|
||||||
|
|
||||||
|
if !self.match_token(&[TokenType::Comma]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.consume(TokenType::RightParen, "Expect ')' after parameters")?;
|
||||||
|
|
||||||
|
self.consume(
|
||||||
|
TokenType::LeftBrace,
|
||||||
|
&format!("Expect '{{' before {} body.", kind),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let body = self.block()?;
|
||||||
|
|
||||||
|
Ok(Stmt::Function { name, params, body })
|
||||||
|
}
|
||||||
|
|
||||||
fn var_declaration(&mut self) -> Result<Stmt, ParseError> {
|
fn var_declaration(&mut self) -> Result<Stmt, ParseError> {
|
||||||
let name = self.consume(TokenType::Identifier, "Expect variable name")?;
|
let name = self.consume(TokenType::Identifier, "Expect variable name")?;
|
||||||
let initializer = if self.match_token(&[TokenType::Equal]) {
|
let initializer = if self.match_token(&[TokenType::Equal]) {
|
||||||
@ -90,6 +128,9 @@ impl Parser<'_> {
|
|||||||
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::Return]) {
|
||||||
|
return self.return_statement();
|
||||||
|
}
|
||||||
if self.match_token(&[TokenType::While]) {
|
if self.match_token(&[TokenType::While]) {
|
||||||
return self.while_statement();
|
return self.while_statement();
|
||||||
}
|
}
|
||||||
@ -227,6 +268,19 @@ impl Parser<'_> {
|
|||||||
Ok(Stmt::Print { expression })
|
Ok(Stmt::Print { expression })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn return_statement(&mut self) -> Result<Stmt, ParseError> {
|
||||||
|
let keyword = self.previous().clone();
|
||||||
|
let value = if !self.check(TokenType::Semicolon) {
|
||||||
|
Some(self.expression()?)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
self.consume(TokenType::Semicolon, "Expect ';' after return value.");
|
||||||
|
|
||||||
|
Ok(Stmt::Return { keyword, value })
|
||||||
|
}
|
||||||
|
|
||||||
fn expression_statement(&mut self) -> Result<Stmt, ParseError> {
|
fn expression_statement(&mut self) -> Result<Stmt, ParseError> {
|
||||||
let expression = self.expression()?;
|
let expression = self.expression()?;
|
||||||
self.consume(TokenType::Semicolon, "Expect ';' after expression.")?;
|
self.consume(TokenType::Semicolon, "Expect ';' after expression.")?;
|
||||||
@ -348,7 +402,46 @@ impl Parser<'_> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self.primary()
|
self.call()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self) -> Result<Expr, ParseError> {
|
||||||
|
let mut expr = self.primary()?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if self.match_token(&[TokenType::LeftParen]) {
|
||||||
|
expr = self.finish_call(expr)?;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish_call(&mut self, callee: Expr) -> Result<Expr, ParseError> {
|
||||||
|
let mut args = Vec::new();
|
||||||
|
if !self.check(TokenType::RightParen) {
|
||||||
|
loop {
|
||||||
|
if args.len() >= 255 {
|
||||||
|
return Err(ParseError {
|
||||||
|
token: self.peek().clone(),
|
||||||
|
msg: "Can't have more than 255 arguments".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
args.push(self.equality()?);
|
||||||
|
if !self.match_token(&[TokenType::Comma]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let paren = self.consume(TokenType::RightParen, "Expect ')' after arguments")?;
|
||||||
|
|
||||||
|
Ok(Expr::Call {
|
||||||
|
callee: Box::new(callee),
|
||||||
|
paren,
|
||||||
|
args,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/* error boundaries:
|
/* error boundaries:
|
||||||
|
@ -9,6 +9,7 @@ pub fn pretty_print(expr: &Expr) -> String {
|
|||||||
LiteralType::Number(v) => v.to_string(),
|
LiteralType::Number(v) => v.to_string(),
|
||||||
LiteralType::Bool(v) => v.to_string(),
|
LiteralType::Bool(v) => v.to_string(),
|
||||||
LiteralType::Nil => "Nil".to_string(),
|
LiteralType::Nil => "Nil".to_string(),
|
||||||
|
LiteralType::Callable(_) => todo!(),
|
||||||
},
|
},
|
||||||
Expr::Unary { op, right } => parenthesize(&op.lexeme, &[right]),
|
Expr::Unary { op, right } => parenthesize(&op.lexeme, &[right]),
|
||||||
Expr::Ternary {
|
Expr::Ternary {
|
||||||
@ -19,6 +20,11 @@ pub fn pretty_print(expr: &Expr) -> String {
|
|||||||
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]),
|
Expr::Logical { left, op, right } => parenthesize(&op.lexeme, &[left, right]),
|
||||||
|
Expr::Call {
|
||||||
|
callee: _,
|
||||||
|
paren: _,
|
||||||
|
args: _,
|
||||||
|
} => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,6 +273,8 @@ fn get_identified_keyword(identifier: &str) -> Option<TokenType> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::interpreter::is_equal;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use TokenType::*;
|
use TokenType::*;
|
||||||
|
|
||||||
@ -325,7 +327,10 @@ mod tests {
|
|||||||
|
|
||||||
let expected = LiteralType::String("salam!".to_string());
|
let expected = LiteralType::String("salam!".to_string());
|
||||||
|
|
||||||
assert_eq!(expected, actual.literal.as_ref().unwrap().clone());
|
assert!(is_equal(
|
||||||
|
&expected,
|
||||||
|
&actual.literal.as_ref().unwrap().clone(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -375,7 +380,10 @@ mod tests {
|
|||||||
|
|
||||||
let actual_value = &token.literal;
|
let actual_value = &token.literal;
|
||||||
|
|
||||||
assert_eq!(expected_value, actual_value.as_ref().unwrap().clone())
|
assert!(is_equal(
|
||||||
|
&expected_value,
|
||||||
|
&actual_value.as_ref().unwrap().clone()
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -398,6 +406,9 @@ mod tests {
|
|||||||
|
|
||||||
let actual_value = &token.literal;
|
let actual_value = &token.literal;
|
||||||
|
|
||||||
assert_eq!(expected_value, actual_value.as_ref().unwrap().clone())
|
assert!(is_equal(
|
||||||
|
&expected_value,
|
||||||
|
&actual_value.as_ref().unwrap().clone()
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use crate::callable::Callable;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
pub enum TokenType {
|
pub enum TokenType {
|
||||||
LeftParen,
|
LeftParen,
|
||||||
@ -51,12 +53,13 @@ pub enum TokenType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// i've seen this implementation in the wild
|
// i've seen this implementation in the wild
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum LiteralType {
|
pub enum LiteralType {
|
||||||
String(String),
|
String(String),
|
||||||
Number(f64),
|
Number(f64),
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
Nil,
|
Nil,
|
||||||
|
Callable(Callable),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LiteralType {
|
impl LiteralType {
|
||||||
@ -76,6 +79,7 @@ impl Display for LiteralType {
|
|||||||
LiteralType::Number(v) => write!(f, "{v:.2}"),
|
LiteralType::Number(v) => write!(f, "{v:.2}"),
|
||||||
LiteralType::Bool(v) => write!(f, "{v}"),
|
LiteralType::Bool(v) => write!(f, "{v}"),
|
||||||
LiteralType::Nil => write!(f, "nil"),
|
LiteralType::Nil => write!(f, "nil"),
|
||||||
|
LiteralType::Callable(c) => write!(f, "<fn {c}>"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user