Skip to content

Commit

Permalink
Add interpreter for function calls and a builtin clock function
Browse files Browse the repository at this point in the history
  • Loading branch information
froth committed Mar 10, 2024
1 parent 84f942b commit bd7e805
Show file tree
Hide file tree
Showing 16 changed files with 150 additions and 66 deletions.
8 changes: 7 additions & 1 deletion src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ impl ExprType {
#[derive(Debug, Clone)]
pub enum Literal {
String(String),
Number(f32),
Number(f64),
Boolean(bool),
Nil,
}
Expand All @@ -209,6 +209,12 @@ impl From<bool> for Literal {

impl From<f32> for Literal {
fn from(value: f32) -> Self {
Literal::Number(value.into())
}
}

impl From<f64> for Literal {
fn from(value: f64) -> Self {
Literal::Number(value)
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/ast/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub enum TokenType {
// Literals
Identifier(String),
String(String),
Number(f32),
Number(f64),

// Keywords.
And,
Expand Down
17 changes: 17 additions & 0 deletions src/interpreter/builtins.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use std::{collections::HashMap, time::SystemTime};

use crate::ast::expr::Name;

use super::{callable::Callable, value::Value, Interpreter, Result};
pub fn builtins() -> HashMap<Name, Callable> {
let mut builtins = HashMap::new();
builtins.insert("clock".into(), Callable::Builtin(clock));
builtins
}

fn clock(_: &mut Interpreter, _: Vec<Value>) -> Result<Value> {
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
Ok(Value::Number(now.as_secs_f64()))
}
13 changes: 13 additions & 0 deletions src/interpreter/callable.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use super::{value::Value, Interpreter, Result};
#[derive(Debug, Clone, PartialEq)]
pub enum Callable {
Builtin(fn(interpreter: &mut Interpreter, arguments: Vec<Value>) -> Result<Value>),
}

impl Callable {
pub fn call(&self, interpreter: &mut Interpreter, arguments: Vec<Value>) -> Result<Value> {
match self {
Callable::Builtin(function) => function(interpreter, arguments),
}
}
}
17 changes: 15 additions & 2 deletions src/interpreter/environment.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::collections::HashMap;

use crate::{ast::expr::Name, value::Value};
use crate::ast::expr::Name;

use super::{builtins::builtins, value::Value};

#[derive(Default)]
pub struct Environment {
Expand All @@ -9,6 +11,14 @@ pub struct Environment {
}

impl Environment {
pub fn with_native_functions() -> Self {
let mut env = Environment::default();
builtins()
.into_iter()
.for_each(|(k, v)| env.define(&k, Value::Callable(v)));
env
}

pub fn define(&mut self, key: &Name, value: Value) {
self.values.insert(key.clone(), value);
}
Expand Down Expand Up @@ -41,7 +51,10 @@ impl Environment {
mod environment_tests {
use std::collections::HashMap;

use crate::{ast::expr::Name, interpreter::environment::Environment, value::Value};
use crate::{
ast::expr::Name,
interpreter::{environment::Environment, value::Value},
};

pub fn local(parent: Environment) -> Environment {
Environment {
Expand Down
67 changes: 40 additions & 27 deletions src/interpreter/expression.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use crate::{
ast::{
expr::{Expr, ExprType, Name, NameExpr},
token::{Token, TokenType},
},
types::Type,
value::Value,
use crate::ast::{
expr::{Expr, ExprType, Name, NameExpr},
token::{Token, TokenType},
};

use super::{literal::LiteralInterpreter, runtime_error::RuntimeError::*};
use super::{
literal::LiteralInterpreter, runtime_error::RuntimeError::*, types::Type, value::Value,
};
use super::{Interpreter, Result};

impl Interpreter {
Expand All @@ -21,7 +19,7 @@ impl Interpreter {
Unary(token, expr) => self.interpret_unary(token, expr),
Variable(name) => self.read_variable(name, expr),
Assign(name, expr) => self.assign_variable(name, expr),
Call(callee, arguments) => todo!(),
Call(callee, arguments) => self.call(callee, arguments),
}
}

Expand All @@ -47,12 +45,29 @@ impl Interpreter {
}
}

fn call(&mut self, callee: &Expr, arguments: &[Expr]) -> Result<Value> {
let callee_value = self.interpret_expr(callee)?;
if let Value::Callable(callable) = callee_value {
let args: Vec<Value> = arguments
.iter()
.map(|a| self.interpret_expr(a))
.collect::<Result<_>>()?;
callable.call(self, args)
} else {
Err(CallingNonCallable {
actual: callee_value.into(),
src: callee.src.clone(),
location: callee.location,
})
}
}

fn handle_numbers(
&mut self,
left: &Expr,
token: &Token,
right: &Expr,
f: fn(f32, f32) -> Value,
f: fn(f64, f64) -> Value,
) -> Result<Value> {
let left_value = self.interpret_expr(left);
let right_value = self.interpret_expr(right);
Expand Down Expand Up @@ -185,30 +200,28 @@ mod value_interpreter_tests {
},
interpreter::{
environment::Environment, printer::vec_printer::VecPrinter,
runtime_error::RuntimeError::*, Interpreter,
runtime_error::RuntimeError::*, types::Type, value::Value, Interpreter,
},
types::Type,
value::Value,
};

#[test]
fn string_literal() {
let expr = literal(Literal::String("Test".to_string()));
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(under_test.interpret_expr(&expr).unwrap(), Value::String(string) if string == "Test");
}
#[test]
fn minus_one() {
let expr = literal(1.0.into());
let expr = Expr::unary(token(TokenType::Minus), expr);
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(under_test.interpret_expr(&expr).unwrap(), Value::Number(number) if number == -1.0);
}
#[test]
fn bang_one() {
let expr = literal(1.0.into());
let expr = Expr::unary(token(TokenType::Bang), expr);
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(
under_test.interpret_expr(&expr).unwrap(),
Value::Boolean(false)
Expand All @@ -218,7 +231,7 @@ mod value_interpreter_tests {
fn bang_false() {
let expr = literal(Literal::Boolean(false));
let expr = Expr::unary(token(TokenType::Bang), expr);
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(
under_test.interpret_expr(&expr).unwrap(),
Value::Boolean(true)
Expand All @@ -228,7 +241,7 @@ mod value_interpreter_tests {
fn bang_nil() {
let expr = literal(Literal::Nil);
let expr = Expr::unary(token(TokenType::Bang), expr);
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(
under_test.interpret_expr(&expr).unwrap(),
Value::Boolean(true)
Expand All @@ -239,7 +252,7 @@ mod value_interpreter_tests {
let one = literal(1.0.into());
let five = literal(5.0.into());
let expr = Expr::binary(five, token(TokenType::Minus), one);
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(under_test.interpret_expr(&expr).unwrap(), Value::Number(number) if float_eq!(number, 4.0, ulps_all <= 4));
}
#[test]
Expand All @@ -248,7 +261,7 @@ mod value_interpreter_tests {
let right = literal("sdfsdf".into());
let operator = token(TokenType::Minus);
let expr = Expr::binary(left, operator, right);
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(
under_test.interpret_expr(&expr).unwrap_err(),
WrongType {
Expand All @@ -265,7 +278,7 @@ mod value_interpreter_tests {
let right = literal("sdfsdf".into());
let operator = token(TokenType::EqualEqual);
let expr = Expr::binary(left, operator, right);
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(
under_test.interpret_expr(&expr).unwrap(),
Value::Boolean(false)
Expand All @@ -277,7 +290,7 @@ mod value_interpreter_tests {
let right = literal(1.0.into());
let operator = token(TokenType::Minus);
let expr = Expr::binary(left, operator, right);
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(
under_test.interpret_expr(&expr).unwrap_err(),
WrongType {
Expand All @@ -294,7 +307,7 @@ mod value_interpreter_tests {
let right = literal(Literal::Nil);
let operator = token(TokenType::Minus);
let expr = Expr::binary(left, operator, right);
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(
under_test.interpret_expr(&expr).unwrap_err(),
WrongTypes {
Expand All @@ -312,15 +325,15 @@ mod value_interpreter_tests {
let right = literal("are good".into());
let operator = token(TokenType::Plus);
let expr = Expr::binary(left, operator, right);
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(under_test.interpret_expr(&expr).unwrap(), Value::String(string) if string == "dogs are good");
}
#[test]
fn five_plus() {
let one = literal(1.0.into());
let five = literal(5.0.into());
let expr = Expr::binary(five, token(TokenType::Plus), one);
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(under_test.interpret_expr(&expr).unwrap(), Value::Number(number) if float_eq!(number, 6.0, ulps_all <= 4));
}
#[test]
Expand All @@ -329,7 +342,7 @@ mod value_interpreter_tests {
let right = literal(1.0.into());
let operator = token(TokenType::Plus);
let expr = Expr::binary(left, operator, right);
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(
under_test.interpret_expr(&expr).unwrap_err(),
PlusOperatorWrongTypes {
Expand All @@ -344,7 +357,7 @@ mod value_interpreter_tests {
fn assign_unassigned_var() {
let right = literal(1.0.into());
let expr = Expr::assign(name_expr("a".into()), right);
let mut under_test = Interpreter::new(Box::new(VecPrinter::new()));
let mut under_test = Interpreter::from_printer(Box::new(VecPrinter::new()));
assert_matches!(
under_test.interpret_expr(&expr).unwrap_err(),
UndefinedVariable { .. }
Expand Down
4 changes: 2 additions & 2 deletions src/interpreter/literal.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::Result;
use crate::{ast::expr::Literal, value::Value};
use super::{value::Value, Result};
use crate::ast::expr::Literal;
pub(super) trait LiteralInterpreter {
fn interpret(&self) -> Result<Value>;
}
Expand Down
20 changes: 17 additions & 3 deletions src/interpreter/mod.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,38 @@
mod builtins;
mod callable;
mod environment;
mod expression;
mod literal;
pub mod printer;
pub mod runtime_error;
mod statement;
mod types;
pub mod value;

use std::mem;

use crate::ast::stmt::Stmt;

use self::{environment::Environment, printer::Printer, runtime_error::RuntimeError};
use self::{
environment::Environment,
printer::{ConsolePrinter, Printer},
runtime_error::RuntimeError,
};

type Result<T> = std::result::Result<T, RuntimeError>;
#[derive(Default)]
pub struct Interpreter {
printer: Box<dyn Printer>,
environment: Environment,
}

impl Interpreter {
pub fn new() -> Self {
Self {
printer: Box::new(ConsolePrinter),
environment: Environment::with_native_functions(),
}
}

pub fn interpret(&mut self, statements: Vec<Stmt>) -> Result<()> {
statements.iter().try_for_each(|s| self.interpret_stmt(s))
}
Expand All @@ -36,7 +50,7 @@ impl Interpreter {
}

#[cfg(test)]
pub fn new(printer: Box<dyn Printer>) -> Self {
pub fn from_printer(printer: Box<dyn Printer>) -> Self {
Self {
printer,
environment: Environment::default(),
Expand Down
10 changes: 2 additions & 8 deletions src/interpreter/printer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::value::Value;
use super::value::Value;

pub trait Printer {
fn print(&self, value: Value);
Expand All @@ -11,17 +11,11 @@ impl Printer for ConsolePrinter {
}
}

impl Default for Box<dyn Printer> {
fn default() -> Self {
Box::new(ConsolePrinter)
}
}

#[cfg(test)]
pub mod vec_printer {
use std::{cell::RefCell, ops::Add, rc::Rc};

use crate::value::Value;
use crate::interpreter::value::Value;

use super::Printer;

Expand Down
11 changes: 10 additions & 1 deletion src/interpreter/runtime_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::Arc;

use miette::{Diagnostic, NamedSource, SourceSpan};

use crate::{ast::expr::Name, types::Type};
use crate::{ast::expr::Name, interpreter::types::Type};

#[derive(thiserror::Error, Debug, Diagnostic)]
pub enum RuntimeError {
Expand Down Expand Up @@ -58,4 +58,13 @@ pub enum RuntimeError {
#[label("here")]
location: SourceSpan,
},

#[error("Can only call functions and classes but got {actual}")]
CallingNonCallable {
actual: Type,
#[source_code]
src: Arc<NamedSource<String>>,
#[label("not callable {actual}, change to class of function")]
location: SourceSpan,
},
}
Loading

0 comments on commit bd7e805

Please sign in to comment.