Skip to content

Commit

Permalink
Merge pull request #7 from KPMGE/implement-builtin-functions
Browse files Browse the repository at this point in the history
Implement builtin functions
  • Loading branch information
KPMGE authored Mar 27, 2024
2 parents cc0eca5 + 4f7a793 commit c4e634c
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 8 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ edition = "2021"

[dependencies]
gflags = "0.3.12"
lazy_static = "1.4.0"
21 changes: 21 additions & 0 deletions src/builtin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use crate::evaluator::Object;
use lazy_static::lazy_static;
use std::collections::HashMap;

pub type BuiltinFn = fn(Vec<Object>) -> Object;

lazy_static! {
pub static ref BUILTIN_FUNCTIONS: HashMap<&'static str, BuiltinFn> =
HashMap::from([("len", len as BuiltinFn)]);
}

fn len(args: Vec<Object>) -> Object {
let first_obj = args
.first()
.expect("len function must be provided a string argument!");

match first_obj {
Object::String(str) => Object::Integer(str.len() as i32),
_ => panic!("len function must be provided a string argument!"),
}
}
27 changes: 21 additions & 6 deletions src/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use crate::ast::{AstNode, BlockStatement, Expression, Statement};
use crate::token::Token;
use std::collections::HashMap;

use crate::builtin::{BuiltinFn, BUILTIN_FUNCTIONS};

#[derive(Default)]
pub struct Evaluator {
context: HashMap<String, Object>,
Expand All @@ -13,12 +15,13 @@ pub enum Object {
Boolean(bool),
String(String),
Return(Box<Object>),
Builtin(BuiltinFn),
Null,
Function {
parameters: Vec<Token>,
body: BlockStatement,
scope: HashMap<String, Object>,
},
Null,
}

impl Evaluator {
Expand Down Expand Up @@ -72,11 +75,7 @@ impl Evaluator {

Object::Null
}
Expression::Identifier(let_name) => self
.context
.get(&let_name)
.expect("ERROR: Could not find identifer")
.clone(),
Expression::Identifier(name) => self.eval_identifier(name),
Expression::FunctionExpression {
parameters, body, ..
} => Object::Function {
Expand All @@ -92,6 +91,10 @@ impl Evaluator {
let function = self.eval(AstNode::Expression(*function));

match function {
Object::Builtin(builtin_fn) => {
let args = self.eval_expressions(arguments);
builtin_fn(args)
}
Object::Function {
parameters,
body,
Expand All @@ -103,6 +106,17 @@ impl Evaluator {
}
}

fn eval_identifier(&mut self, name: String) -> Object {
if let Some(function) = BUILTIN_FUNCTIONS.get(name.as_str()) {
return Object::Builtin(*function);
}

self.context
.get(&name)
.expect("ERROR: Could not find identifer")
.clone()
}

fn eval_function_call(
&mut self,
parameters: Vec<Token>,
Expand Down Expand Up @@ -238,6 +252,7 @@ impl Object {
Object::String(value) => value.to_string(),
Object::Return(value) => value.inspect(),
Object::Function { .. } => "function".to_string(),
Object::Builtin(..) => "null".to_string(),
Object::Null => "null".to_string(),
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,6 @@ impl Lexer {
self.read_char();
}

self.read_char();

str
}

Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod ast;
mod builtin;
pub mod evaluator;
pub mod lexer;
pub mod parser;
Expand Down
19 changes: 19 additions & 0 deletions tests/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,22 @@ fn given_a_string_expression_it_should_evaluate_correctly() {

assert_eq!(evaluated_obj, expected_obj);
}

#[test]
fn given_a_string_expression_when_calling_len_it_should_evaluate_correctly() {
let code = "len(\"kevin\")";
let expected_obj = Object::Integer(5);

let lexer = Lexer::new(code.to_string());
let mut parser = Parser::new(lexer);
let parsed_program = parser.parse_program();
let node = match parsed_program {
AstNode::Program { statements } => statements.first().unwrap().clone(),
_ => panic!("Unexpected AstNode!"),
};

let mut evaluator = Evaluator::new();
let evaluated_obj = evaluator.eval(node);

assert_eq!(evaluated_obj, expected_obj);
}

0 comments on commit c4e634c

Please sign in to comment.