Skip to content

Commit

Permalink
Add function definitions
Browse files Browse the repository at this point in the history
  • Loading branch information
lgyanf committed Oct 29, 2023
1 parent 333244e commit 520d402
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod expr;
mod functon_kind;

Check failure on line 2 in src/ast/mod.rs

View workflow job for this annotation

GitHub Actions / Check

file not found for module `functon_kind`

Check failure on line 2 in src/ast/mod.rs

View workflow job for this annotation

GitHub Actions / Clippy

file not found for module `functon_kind`

Check failure on line 2 in src/ast/mod.rs

View workflow job for this annotation

GitHub Actions / Test Suite

file not found for module `functon_kind`
pub mod parser;
pub mod printer;
pub mod statement;
Expand Down
77 changes: 77 additions & 0 deletions src/ast/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ use crate::position::{Position, PositionRange};
use crate::token::{Token, TokenType};

use super::expr::{BinaryOp, ExprType, UnaryOp};
use super::statement::FunctionParameter;
use super::token_iterator::TokenIterator;
use super::Statement;
use super::functon_kind::FunctionKind;

Check failure on line 10 in src/ast/parser.rs

View workflow job for this annotation

GitHub Actions / Check

unresolved import `super::functon_kind::FunctionKind`

Check failure on line 10 in src/ast/parser.rs

View workflow job for this annotation

GitHub Actions / Clippy

unresolved import `super::functon_kind::FunctionKind`

Check failure on line 10 in src/ast/parser.rs

View workflow job for this annotation

GitHub Actions / Test Suite

unresolved import `super::functon_kind::FunctionKind`

struct Parser<'a> {
token_iterator: TokenIterator<'a>,
Expand Down Expand Up @@ -36,6 +38,7 @@ impl Parser<'_> {
let peek = self.token_iterator.peek();
match peek {
Some(t) if t.type_ == TokenType::Var => self.var_declaration(),
Some(t) if t.type_ == TokenType::Fun => self.function_declaration(&FunctionKind::Function),
_ => self.statement(),
}
}
Expand Down Expand Up @@ -89,6 +92,80 @@ impl Parser<'_> {
})
}

fn function_declaration(&mut self, kind: &FunctionKind) -> Result<Statement, LoxError> {
let fun_keyword = self.token_iterator.next().unwrap();
let peek = self.token_iterator.peek_clone();

let name = match peek {
Some(t) => match t.type_ {
TokenType::Identifier(name) => {
self.token_iterator.next().unwrap();
name
},
_ => return Err(self.make_expected_function_name_error(&fun_keyword.position, kind))
},
None => return Err(
self.make_expected_function_name_error(&fun_keyword.position, kind),
),
};
self.consume_or_error(
&TokenType::LeftParen,
&fun_keyword.position,
"'fun'",
)?;
let mut parameters: Vec<FunctionParameter> = Vec::new();
let peek = self.token_iterator.peek();
if let Some(peek) = peek {
if peek.type_ != TokenType::RightParen {
// todo: fill functon parameter names
parameters.push(self.expression()?);
while self.consume_if_matches(&[TokenType::Comma]).is_some() {
parameters.push(self.expression()?);
if parameters.len() > 255 {
return Err(LoxError {
kind: LoxErrorKind::Syntax,
message: "Functions cannot have more than 255 arguments".to_owned(),
position: PositionRange::from_bounds(&fun_keyword.position, &self.token_iterator.last_position().unwrap())
})
}
}
}
}
// consume right paren
self.consume_or_error(
&TokenType::RightParen,
&fun_keyword.position,
"arguments",
)?;
self.consume_or_error(
&TokenType::LeftBrace,
&fun_keyword.position,
"function arguments"
)?;
let body = self.block()?;
let body_position = body.position().clone();
Ok(Statement::FunctionDef {
name,
parameters,
body: body.boxed(),
position: PositionRange::from_bounds(&fun_keyword.position, &body_position),
})
}

fn make_expected_function_name_error(&mut self, start: &PositionRange, kind: &FunctionKind) -> LoxError {
let function_kind_str = match kind {
FunctionKind::Function => "function"
};
LoxError {
kind: LoxErrorKind::Syntax,
message: format!("Expected {} name", function_kind_str),
position: PositionRange::from_bounds(
start,
&self.token_iterator.last_position().unwrap(),
),
}
}

fn statement(&mut self) -> Result<Statement, LoxError> {
match self.token_iterator.peek() {
Some(token) if token.type_ == TokenType::Print => self.print_statement(),
Expand Down
11 changes: 10 additions & 1 deletion src/ast/statement.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::position::PositionRange;
use crate::{position::PositionRange};

use super::expr::Expr;

pub type FunctionParameter = Expr;

#[derive(Debug, Clone, PartialEq)]
pub enum Statement {
Expression {
Expand Down Expand Up @@ -38,6 +40,12 @@ pub enum Statement {
increment: Option<Box<Statement>>,
body: Box<Statement>,
position: PositionRange,
},
FunctionDef {
name: String,
parameters: Vec<FunctionParameter>,
body: Box<Statement>,
position: PositionRange,
}
}

Expand All @@ -58,6 +66,7 @@ impl Statement {
},
Statement::While { condition: _, body: _, position } => position,
Statement::For {initializer: _, condition: _, increment: _, body: _, position} => position,
Statement::FunctionDef { name: _, parameters: _, body: _, position } => position,
}
}

Expand Down
29 changes: 28 additions & 1 deletion src/interpreter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use crate::error::LoxErrorKind;
use crate::position::PositionRange;
use crate::scanner;
use self::native_functons::inject_native_functions;
use self::value::CallableType;

use super::interpreter::environment::Environment;
use super::interpreter::value::Value;
Expand Down Expand Up @@ -255,6 +256,21 @@ impl StatementVisitor for Interpreter<'_> {
}
}
},
ast::Statement::FunctionDef {
name,
parameters,
body,
position,
} => {
self.environment.set_in_current_scope(name.clone(), Value::Callable(
CallableType::Function {
name: name.clone(),
body: body.clone(),
parameters: parameters.clone(),
position: position.clone(),
}
))
},
};
Ok(())
}
Expand Down Expand Up @@ -578,12 +594,23 @@ hello\n",
1
2\n",
),
clock_native_fn: (
clock_native_function: (
"
var time = clock();
print time > 0;",
Ok(()),
"true\n",
),
define_and_print_lox_function: (
"
fun add(a, b) {
print a + b;
}
print(add);
",
Ok(()),
"<fun add>\n",
),
);
}
3 changes: 2 additions & 1 deletion src/interpreter/native_functons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ pub fn inject_native_functions(env: &mut Environment) {
] {
match native_functon {
CallableType::Native { name, arity: _, call: _ } =>
env.set_in_current_scope(name.to_owned(), Value::Callable(native_functon))
env.set_in_current_scope(name.to_owned(), Value::Callable(native_functon)),
CallableType::Function { name: _, parameters: _, body: _, position: _ } => unreachable!("Lox functions cannot be injected"),
};
}
}
13 changes: 11 additions & 2 deletions src/interpreter/value.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::fmt::Display;

use crate::error::LoxError;
use crate::{error::LoxError, ast::{statement::FunctionParameter, Statement}, position::PositionRange};

#[derive(PartialEq, Clone)]
pub enum CallableType {
Expand All @@ -9,26 +9,35 @@ pub enum CallableType {
arity: usize,
call: fn(&Vec<Value>) -> Value,
},
Function {
name: String,
parameters: Vec<FunctionParameter>,
body: Box<Statement>,
position: PositionRange,
}
}

impl CallableType {
pub fn arity(&self) -> usize {
match self {
CallableType::Native { name: _, arity, call: _ } => *arity,
CallableType::Function { name: _, parameters, body, position } => parameters.len(),
}
}

pub fn call(&self, args: &Vec<Value>) -> Result<Value, LoxError> {
match self {
CallableType::Native { name: _, arity: _, call } => Ok(call(args)),
CallableType::Function { name, parameters, body, position } => todo!(),
}
}
}

impl Display for CallableType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CallableType::Native { name, arity: _, call: _ } => write!(f, "NativeCallable({})", name),
CallableType::Native { name, arity: _, call: _ } => write!(f, "<fun {} (native)>", name),
CallableType::Function { name, parameters, body, position } => write!(f, "<fun {}>", name),
}
}
}
Expand Down

0 comments on commit 520d402

Please sign in to comment.