feat: add monkey-rs

This commit is contained in:
2022-06-02 00:32:03 +08:00
parent 6a8d8ca043
commit 2330a646b7
13 changed files with 2241 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
[package]
name = "monkey-rs"
version = "0.1.0"
authors = ["Vishal Patil <vpatil3@cisco.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
linefeed = "0.6.0"

View File

@@ -0,0 +1,12 @@
# monkey-rs
Another interpreter for the [monkey](https://interpreterbook.com/) programming language in Rust. **Writing An Interpreter In Go** is a phenomenal book to learn on how to write an interpreter for a programming language in **Go**. However instead of using Go I decided to implement the interpreter in Rust. One big advantage of using Rust versus using other garbage collected languages such as Go, Python, Java etc is that the garbage collection of the monkey objects is completely handled by the Rust runtime hence theortically the performance of the Monkey programs is expected to be much better on Rust.
# Demo
```
cargo build
```
[![asciicast](https://asciinema.org/a/403574.svg)](https://asciinema.org/a/403574)

View File

@@ -0,0 +1,3 @@
Clone from: https://github.com/vishpat/monkey-rs

View File

@@ -0,0 +1,24 @@
let s1 = "str1";
let s2 = "str2";
let s3 = s1 + " - " + s2;
s3;
len(s3);
let x = 10;
let y = 20;
let sum = fn(a, b){ a + b;};
sum(x, y);
let arr = [1, 2, 3, 4];
sum(arr[0], arr[3]);
let dict = {"one": 1, "two": 2, "three": 3};
sum(dict["one"], dict["two"]);
let fact = fn(x){ if (x > 1) { x * fact(x - 1);} else {1}};
fact(10);
exit

View File

@@ -0,0 +1,151 @@
#FIG 3.2 Produced by xfig version 3.2.7b
Landscape
Center
Inches
Letter
100.00
Single
-2
1200 2
6 9225 675 11775 900
6 9225 675 11775 900
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
10125 750 10575 750
4 0 0 50 -1 0 12 0.0000 4 150 990 10725 825 Statement*\001
4 0 0 50 -1 0 12 0.0000 4 195 795 9225 825 Program \001
-6
-6
6 4125 750 7425 2175
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
5325 1050 4575 1875
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
5775 1050 6750 1800
4 0 0 50 -1 0 12 0.0000 4 150 885 4125 2100 Statement\001
4 0 0 50 -1 0 12 0.0000 4 150 480 5325 900 Node\001
4 0 0 50 -1 0 12 0.0000 4 195 960 6450 2100 Expression\001
-6
6 1575 3000 3825 3750
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
2400 3225 2250 3450
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
2775 3225 3000 3450
4 0 0 50 -1 0 12 0.0000 4 150 300 2400 3150 Let\001
4 0 0 50 -1 0 12 0.0000 4 150 780 1575 3675 Identifier\001
4 0 0 50 -1 0 12 0.0000 4 195 960 2850 3675 Expression\001
-6
6 2100 4125 3075 4950
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
2475 4350 2475 4650
4 0 0 50 -1 0 12 0.0000 4 150 585 2250 4275 Return\001
4 0 0 50 -1 0 12 0.0000 4 195 960 2100 4875 Expression\001
-6
6 1800 5400 3675 6300
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
2625 5625 2625 6075
4 0 0 50 -1 0 12 0.0000 4 195 1845 1800 5550 ExpressionStatement\001
4 0 0 50 -1 0 12 0.0000 4 195 960 2250 6225 Expression\001
-6
6 1950 6825 3375 7650
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
2550 7125 2550 7425
4 0 0 50 -1 0 12 0.0000 4 150 1380 1950 6975 BlockStatement\001
4 0 0 50 -1 0 12 0.0000 4 150 990 2175 7650 Statement*\001
-6
6 6525 2625 7350 3375
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
6900 2850 6900 3150
4 0 0 50 -1 0 12 0.0000 4 150 780 6525 2775 Identifier\001
4 0 0 50 -1 0 12 0.0000 4 195 480 6675 3300 string\001
-6
6 7800 2625 8550 3300
2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
8175 2850 8175 3075
4 0 0 50 -1 0 12 0.0000 4 150 705 7800 2775 Boolean\001
4 0 0 50 -1 0 12 0.0000 4 150 360 8025 3300 bool\001
-6
6 9000 2625 9675 3300
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
9225 2850 9225 3075
4 0 0 50 -1 0 12 0.0000 4 195 630 9000 2775 Integer\001
4 0 0 50 -1 0 12 0.0000 4 150 435 9075 3300 usize\001
-6
6 7125 3750 9150 4500
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
7800 3975 7575 4200
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
8100 3975 8325 4125
4 0 0 50 -1 0 12 0.0000 4 195 1470 7275 3900 PrefixExpression\001
4 0 0 50 -1 0 12 0.0000 4 195 525 7125 4425 string \001
4 0 0 50 -1 0 12 0.0000 4 195 960 8175 4425 Expression\001
-6
6 6900 4875 9525 5625
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
7800 5100 7575 5325
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
8100 5100 8100 5325
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
8550 5100 8775 5325
4 0 0 50 -1 0 12 0.0000 4 195 1350 7575 5025 InfixExpression\001
4 0 0 50 -1 0 12 0.0000 4 195 2580 6900 5550 Expression string Expression\001
-6
6 6600 6000 10500 6825
6 6600 6000 10500 6825
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
7875 6225 7275 6525
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
8175 6225 8175 6525
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
8550 6225 9525 6450
4 0 0 50 -1 0 12 0.0000 4 195 1095 7650 6150 IfExpression\001
4 0 0 50 -1 0 12 0.0000 4 195 3855 6600 6750 Expression BlockStatement BlockStatement\001
-6
-6
6 7350 7200 9825 7875
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
7950 7425 7800 7575
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
8625 7425 8925 7575
4 0 0 50 -1 0 12 0.0000 4 150 1320 7725 7350 FunctionLiteral\001
4 0 0 50 -1 0 12 0.0000 4 150 2415 7350 7875 Identifier* BlockStatement*\001
-6
6 7500 8250 9600 8925
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
8250 8475 8100 8625
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
8700 8475 8850 8625
4 0 0 50 -1 0 12 0.0000 4 195 1305 7875 8400 CallExpression\001
4 0 0 50 -1 0 12 0.0000 4 195 2070 7500 8850 Expression Expression*\001
-6
2 2 1 1 0 22 50 -1 -1 4.000 0 0 -1 0 0 5
1350 2700 3900 2700 3900 8100 1350 8100 1350 2700
2 1 0 1 0 22 50 -1 -1 4.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
4350 2175 3900 2700
2 2 1 1 0 22 50 -1 -1 4.000 0 0 -1 0 0 5
6075 2475 10800 2475 10800 9225 6075 9225 6075 2475
2 1 0 1 0 22 50 -1 -1 0.000 0 0 -1 1 0 2
1 1 1.00 60.00 120.00
6975 2175 7200 2325

151
__lang/monkey-rs/src/ast.rs Normal file
View File

@@ -0,0 +1,151 @@
use std::fmt;
use std::fmt::Formatter;
#[derive(Debug, PartialEq, Clone)]
pub struct Program {
pub stmts: Vec<Statement>,
}
impl fmt::Display for Program {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for stmt in &self.stmts {
write!(f, "{}", stmt)?;
}
Ok(())
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct BlockStatement {
pub stmts: Vec<Statement>,
}
impl fmt::Display for BlockStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{{");
for stmt in &self.stmts {
write!(f, "{}", stmt)?;
}
write!(f, "}}");
Ok(())
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Prefix {
Minus,
Bang
}
impl fmt::Display for Prefix {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Prefix::Minus => write!(f, "-"),
Prefix::Bang => write!(f, "!"),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Infix {
Eq,
NotEq,
Lt,
Gt,
Plus,
Minus,
Asterisk,
Slash,
LBracket,
}
impl fmt::Display for Infix {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Infix::Eq => write!(f, "=="),
Infix::NotEq => write!(f, "!="),
Infix::Lt => write!(f, "<"),
Infix::Gt => write!(f, ">"),
Infix::Plus => write!(f, "+"),
Infix::Minus => write!(f, "-"),
Infix::Asterisk => write!(f, "*"),
Infix::Slash => write!(f, "/"),
Infix::LBracket => write!(f, "["),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Expression {
Identifier(String),
IntegerLiteral(i64),
String(String),
Boolean(bool),
Prefix(Prefix, Box<Expression>),
Infix(Infix, Box<Expression>, Box<Expression>),
If(Box<Expression>, Box<BlockStatement>, Option<Box<BlockStatement>>),
FunctionLiteral(Vec<String>, Box<BlockStatement>),
DictionaryLiteral(Vec<(Expression, Expression)>),
ArrayLiteral(Vec<Expression>),
Index(Box<Expression>, Box<Expression>),
Call(Box<Expression>, Vec<Expression>),
}
impl fmt::Display for Expression {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Expression::Identifier(s) => write!(f, "{}", s),
Expression::IntegerLiteral(i) => write!(f, "{}", i),
Expression::String(s) => write!(f, "\"{}\"", s),
Expression::Boolean(b) => write!(f, "{}", b),
Expression::Prefix(p, exp) => write!(f, "({}{})", p, exp),
Expression::Infix(op, left, right) =>
write!(f, "({} {} {})", left, op, right),
Expression::If(exp, true_blk, Some(false_blk)) =>
write!(f,"if ({}) {} else {}", exp, true_blk, false_blk),
Expression::If(exp, true_blk, None) =>
write!(f,"if ({}) {}", exp, true_blk),
Expression::DictionaryLiteral(key_values) => {
let mut str = String::new();
str.push_str("{");
for (k, v) in key_values{
str.push_str(format!("{}:{},", k, v).as_str());
}
if str.ends_with(',') {
str.pop();
}
str.push_str("}");
write!(f, "{}", str)
},
Expression::ArrayLiteral(members) => write!(f, "[{}]",
members.iter().map(|a| a.to_string()).collect::<Vec<String>>().join(",")),
Expression::Index(arr, idx) => write!(f, "{}[{}]", arr.to_string(), idx.to_string()),
Expression::FunctionLiteral(params, block) => write!(f, "fn({}){}", params.join(","), block),
Expression::Call(exp, params) => write!(f, "{}({})", exp,
params.iter().map(|a| a.to_string()).collect::<Vec<String>>().join(",")),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Statement {
Let(String, Box<Expression>),
Return(Option<Box<Expression>>),
Expression(Box<Expression>),
}
impl fmt::Display for Statement {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Statement::Let(s, exp) => write!(f, "let {} = {};", s, exp),
Statement::Return(None) => write!(f, "return;"),
Statement::Return(Some(val)) => write!(f, "return {};", val),
Statement::Expression(exp) => write!(f, "{};", exp),
}
}
}

View File

@@ -0,0 +1,37 @@
use crate::object::Object;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
#[derive(Debug, PartialEq, Default)]
pub struct Environment {
store: HashMap<String, Object>,
outer: Option<Rc<RefCell<Environment>>>,
}
impl Environment {
pub fn new() -> Self {
Default::default()
}
pub fn extend(outer: Rc<RefCell<Self>>) -> Environment {
Environment {
store: HashMap::new(),
outer: Some(outer),
}
}
pub fn get(&self, name: &str) -> Option<Object> {
match self.store.get(name) {
Some(value) => Some(value.clone()),
None => self
.outer
.as_ref()
.and_then(|o| o.borrow().get(name).clone()),
}
}
pub fn set(&mut self, name: &str, val: Object) {
self.store.insert(name.to_string(), val);
}
}

View File

@@ -0,0 +1,495 @@
use crate::ast::*;
use crate::object::Object;
use crate::environment::Environment;
use crate::inbuilt::{get_inbuilt_function, eval_inbuilt_function};
use std::cell::RefCell;
use std::rc::Rc;
use std::collections::HashMap;
pub fn eval_identifier(identifier: &Expression, env: &Rc<RefCell<Environment>>) -> Object
{
match identifier {
Expression::Identifier(i) => {
let id = env.borrow_mut().get(i.as_str());
if id.is_some() {
return id.unwrap();
}
let inbuilt_func = get_inbuilt_function(i.as_str());
if inbuilt_func.is_some() {
return inbuilt_func.unwrap();
}
}
_ => panic!("Expected identifier")
}
Object::Nil
}
pub fn eval_prefix_expression(prefix: &Prefix, expression: &Expression, env: &mut Rc<RefCell<Environment>>) -> Object {
let expr_val = eval_expression(expression, env);
match prefix {
Prefix::Minus => {
match expr_val {
Object::Integer(i) => Object::Integer(-1 * i),
_ => panic!("Invalid expression {} in prefix expression, expected integer", expr_val)
}
}
Prefix::Bang => {
match expr_val {
Object::Boolean(b) => Object::Boolean(!b),
_ => panic!("Invalid expression {} in prefix expression, expected boolean", expr_val)
}
}
}
}
pub fn eval_infix_expression(infix: &Infix, left: &Expression, right: &Expression, env: &mut Rc<RefCell<Environment>>) -> Object {
let left_obj = eval_expression(left, env);
let right_obj = eval_expression(right, env);
match left_obj {
Object::Integer(i) => {
let left_int = i;
let right_int;
match right_obj {
Object::Integer(i) => {
right_int = i;
match infix {
Infix::Plus => Object::Integer(left_int + right_int),
Infix::Minus => Object::Integer(left_int - right_int),
Infix::Asterisk => Object::Integer(left_int * right_int),
Infix::Slash => Object::Integer(left_int / right_int),
Infix::NotEq => Object::Boolean(left_int != right_int),
Infix::Eq => Object::Boolean(left_int == right_int),
Infix::Gt => Object::Boolean(left_int > right_int),
Infix::Lt => Object::Boolean(left_int < right_int),
_ => panic!("Inintid op {}", infix)
}
}
_ => panic!("Expected integer {}", right_obj),
}
}
Object::String(s) => {
let left_str = s;
let right_str;
match right_obj {
Object::String(s) => {
right_str = s;
match infix {
Infix::Plus => Object::String(left_str + &*right_str),
_ => panic!("Invalid op {} for strings", infix)
}
}
_ => panic!("Expected String{}", right_obj),
}
}
_ => panic!("Invalid value in expression {}, expected int", left_obj),
}
}
pub fn eval_block_statement(block: &BlockStatement, env: &mut Rc<RefCell<Environment>>) -> Object {
let mut val = Object::Nil;
for stmt in &block.stmts {
val = match stmt {
Statement::Let(x, expr) => eval_let_statement(x.to_string(), &*expr, env),
Statement::Return(Some(x)) => {
return eval_return_statement(&*x, env);
}
Statement::Return(None) => {
return Object::Nil;
}
Statement::Expression(expr) => eval_expression(&*expr, env),
}
}
val
}
pub fn eval_if_expression(expr: &Expression, true_block: &BlockStatement,
false_block: &Option<Box<BlockStatement>>, env: &mut Rc<RefCell<Environment>>) -> Object {
let expr_obj = eval_expression(expr, env);
let expr_val = match expr_obj {
Object::Boolean(v) => v,
_ => panic!("Expected boolean expression in if statement, found {}", expr_obj)
};
if expr_val {
eval_block_statement(true_block, env)
} else if false_block.is_some() {
eval_block_statement(false_block.as_ref().unwrap().as_ref(), env)
} else {
Object::Nil
}
}
pub fn eval_function_parameters(params: &Vec<Expression>, env: &mut Rc<RefCell<Environment>>) -> Vec<Object> {
let mut param_objs = vec![];
for param in params.iter() {
let param_obj = eval_expression(param, env);
if param_obj == Object::Nil {
panic!("Unable to evaluate parameter {}", param);
}
param_objs.push(eval_expression(param, env))
}
param_objs
}
pub fn eval_user_defined_function_call(func_params: &Vec<String>, param_objs: &Vec<Object>,
func_block: &BlockStatement,
env: &mut Rc<RefCell<Environment>>) -> Object {
let mut func_new_env = Rc::new(RefCell::new(Environment::extend(env.clone())));
let mut idx = 0;
while idx < param_objs.len() {
func_new_env.borrow_mut().set(&*func_params[idx], param_objs[idx].clone());
idx += 1;
}
eval_block_statement(&func_block, &mut func_new_env)
}
pub fn eval_dict_literal(dict_expr: &Vec<(Expression, Expression)>,
env: &mut Rc<RefCell<Environment>>) -> Object {
let mut dict = HashMap::new();
for (key_expr, val_expr) in dict_expr {
let key = eval_expression(key_expr, env);
let val = eval_expression(val_expr, env);
dict.insert(key, val);
}
Object::Dict(dict)
}
pub fn eval_array_literal(member_expr: &Vec<Expression>, env: &mut Rc<RefCell<Environment>>) -> Object {
let mut members = vec![];
for mem in member_expr.iter() {
members.push(eval_expression(mem, env));
}
Object::Array(members)
}
pub fn eval_arr_idx(arr: &Vec<Object>, idx: &Object) -> Object {
match idx {
Object::Integer(index) => {
if *index as usize > arr.len() - 1 {
panic!("Array index {} greater that array size {}", idx, arr.len());
}
let obj = arr.get(*index as usize).unwrap().clone();
obj
}
_ => panic!("Invalid array index {}, expected a positive integer", idx),
}
}
pub fn eval_dict_idx(dict: &HashMap<Object, Object>, idx: &Object) -> Object {
let mut ret_val = Object::Nil;
if dict.contains_key(idx) {
ret_val = dict.get(idx).unwrap().clone();
}
ret_val
}
pub fn eval_index(container_expr: &Box<Expression>, idx_expr: &Box<Expression>,
env: &mut Rc<RefCell<Environment>>) -> Object {
let container = eval_expression(container_expr, env);
let idx = eval_expression(idx_expr, env);
match container {
Object::Array(arr) => eval_arr_idx(&arr, &idx),
Object::Dict(dict) => eval_dict_idx(&dict, &idx),
_ => panic!("Expected array or dictionary got {}", container.to_string()),
}
}
pub fn eval_function_call(func_expr: &Box<Expression>, parameters: &Vec<Expression>,
env: &mut Rc<RefCell<Environment>>) -> Object {
let func_obj = eval_expression(func_expr, env);
let param_objs = eval_function_parameters(parameters, env);
match func_obj {
Object::FunctionLiteral(params, block, mut func_env) =>
{
if param_objs.len() != params.len() {
panic!("Did not find the expected number of arguments for the function");
}
eval_user_defined_function_call(&params, &param_objs, &block, &mut func_env)
}
Object::FunctionInBuilt(_) => eval_inbuilt_function(&func_obj, &param_objs),
_ => panic!("Invalid object type {}, expected function object", func_obj)
}
}
pub fn eval_expression(expr: &Expression, env: &mut Rc<RefCell<Environment>>) -> Object {
match expr {
Expression::IntegerLiteral(i) => Object::Integer(*i),
Expression::Identifier(_s) => eval_identifier(expr, env),
Expression::String(s) => Object::String(s.to_string()),
Expression::Boolean(b) => Object::Boolean(*b),
Expression::Prefix(prefix, expr) =>
eval_prefix_expression(prefix, expr, env),
Expression::Infix(infix, left, right) =>
eval_infix_expression(infix, left, right, env),
Expression::If(expr, true_block, false_block) =>
eval_if_expression(expr, true_block, false_block, env),
Expression::ArrayLiteral(arr) => eval_array_literal(arr, env),
Expression::DictionaryLiteral(dict) => eval_dict_literal(dict, env),
Expression::Index(arr, idx) => eval_index(arr, idx, env),
Expression::FunctionLiteral(params, block) =>
Object::FunctionLiteral(params.clone(), *block.clone(), env.clone()),
Expression::Call(func, params) => eval_function_call(func, params, env),
}
}
pub fn eval_let_statement(identifier: String, expr: &Expression, env: &mut Rc<RefCell<Environment>>) -> Object {
let expr_val = eval_expression(expr, env);
env.borrow_mut().set(identifier.as_str(), expr_val);
Object::Nil
}
pub fn eval_return_statement(expr: &Expression, env: &mut Rc<RefCell<Environment>>) -> Object {
eval_expression(expr, env)
}
pub fn eval_program(program: &Program, env: &mut Rc<RefCell<Environment>>) -> Object
{
let mut val = Object::Nil;
for stmt in &program.stmts {
val = match stmt {
Statement::Let(id, expr) => eval_let_statement(id.to_string(), &*expr, env),
Statement::Return(Some(expr)) => { return eval_return_statement(&*expr, env); }
Statement::Return(None) => { return Object::Nil; }
Statement::Expression(expr) => eval_expression(&*expr, env),
};
}
return val;
}
#[cfg(test)]
mod tests {
use crate::lexer::Lexer;
use crate::parser::Parser;
use crate::object::Object;
use crate::environment::Environment;
use crate::evaluator::*;
fn test_eval_program(input: &str) -> Object {
let mut env = Rc::new(RefCell::new(Environment::new()));
let lexer = Lexer::new(input);
let mut parser = Parser::new(lexer);
let program = parser.parse_program().unwrap();
eval_program(program.as_ref(), &mut env)
}
struct TestCase<'a> {
test_str: &'a str,
val: Object,
}
fn check_test_cases(test_cases: Vec<TestCase>) {
for test_case in test_cases {
assert_eq!(test_eval_program(test_case.test_str), test_case.val);
}
}
#[test]
fn test_eval_integer() {
let test_cases = vec![TestCase { test_str: "10", val: Object::Integer(10) }];
check_test_cases(test_cases);
}
#[test]
fn test_eval_boolean() {
let test_cases = vec![
TestCase { test_str: "true", val: Object::Boolean(true) },
TestCase { test_str: "false", val: Object::Boolean(false) },
];
check_test_cases(test_cases);
}
#[test]
fn test_eval_return() {
let test_cases = vec![
TestCase { test_str: "return 0;", val: Object::Integer(0) },
TestCase { test_str: "return;", val: Object::Nil },
];
check_test_cases(test_cases);
}
#[test]
fn test_eval_prefix() {
let test_cases = vec![
TestCase { test_str: "!true", val: Object::Boolean(false) },
TestCase { test_str: "!false", val: Object::Boolean(true) },
TestCase { test_str: "-1", val: Object::Integer(-1) }
];
check_test_cases(test_cases);
}
#[test]
fn test_eval_infix() {
let test_cases = vec![
TestCase { test_str: "10 > 20", val: Object::Boolean(false) },
TestCase { test_str: "-1 + 0", val: Object::Integer(-1) },
TestCase { test_str: "5 > 3", val: Object::Boolean(true) },
TestCase { test_str: "4 < 2", val: Object::Boolean(false) },
TestCase { test_str: "5 == 3", val: Object::Boolean(false) },
TestCase { test_str: "4 != 2", val: Object::Boolean(true) },
TestCase { test_str: "(2*2 + 1) == 5", val: Object::Boolean(true) },
TestCase { test_str: "(2 + 3)*2 == 10", val: Object::Boolean(true) },
TestCase { test_str: "\"ab\" + \"cd\"", val: Object::String(String::from("abcd")) },
TestCase {
test_str: "\"ab\" + \"cd\" + \"ef\"",
val: Object::String(String::from("abcdef")),
},
];
check_test_cases(test_cases);
}
#[test]
fn test_eval_if_expr() {
let test_cases = vec![
TestCase { test_str: "if (10 > 20) {20} else {10}", val: Object::Integer(10) },
TestCase { test_str: "if (21 > 20) {20} else {10}", val: Object::Integer(20) },
TestCase { test_str: "if (21 > 20) {20} ", val: Object::Integer(20) },
TestCase { test_str: "if (21 < 20) {20} ", val: Object::Nil },
TestCase { test_str: "if (21 > 20) {let x = 30; 20} ", val: Object::Integer(20) },
];
check_test_cases(test_cases);
}
#[test]
fn test_eval_let_statements() {
let test_cases = vec![
TestCase { test_str: "let x = 10; if (x > 20) {20} else {10}", val: Object::Integer(10) },
TestCase { test_str: "let y = 20; if (21 > y) {20} else {10}", val: Object::Integer(20) },
TestCase { test_str: "let z = 30; z*z; ", val: Object::Integer(900) },
TestCase { test_str: "let a = 30; let b = 40; a + b", val: Object::Integer(70) },
];
check_test_cases(test_cases);
}
#[test]
fn test_eval_functions() {
let test_cases = vec![
TestCase {
test_str: "let sum = fn(x, y){ x + y;}; \
sum(10, 20);",
val: Object::Integer(30),
},
TestCase {
test_str: "let square = fn(x){x*x}; \
square(10)",
val: Object::Integer(100),
},
TestCase {
test_str: "let gt = fn(x, y){ \
if (!(x > y)) {\
x*x\
} else {\
y\
};\
}; \
gt(3, 2)",
val: Object::Integer(2),
},
TestCase {
test_str: "let gt = fn(x, y){ \
if (x > y) \
{x*x} \
else {\
y\
};\
}; \
gt(3, 2)",
val: Object::Integer(9),
},
TestCase {
test_str: "let fact = fn(x) {\
if (x > 1) {\
x*fact(x - 1);\
} else {\
x\
};\
}; \
fact(8);",
val: Object::Integer(40320),
},
TestCase {
test_str: "let sum = fn(x,y){x + y;};\
let sqr = fn(x){let z = sum(x, x); z*z;};\
let z = sum(2, 3) + sqr(2);\
z;",
val: Object::Integer(21),
},
TestCase { test_str: "fn(x, y, z){x + y + z}(1, 2, 3);", val: Object::Integer(6) },
TestCase {
test_str: "let a = \"wx\";\
let b = \"yz\";\
let z = a + b;\
z;",
val: Object::String(String::from("wxyz")),
},
];
check_test_cases(test_cases);
}
#[test]
fn test_closures() {
let test_cases = vec![
TestCase {
test_str: "let adder = fn(x){fn(x,y) { x + y; };};\
let a2 = adder(2);
a2(10);",
val: Object::Integer(12),
},
];
}
#[test]
fn test_eval_inbuilt_functions() {
let test_cases = vec![
TestCase { test_str: "let x = \"cartman\"; len(x)", val: Object::Integer(7) },
TestCase { test_str: "len(\"cartman\");", val: Object::Integer(7) },
];
check_test_cases(test_cases);
}
#[test]
fn test_eval_arrays() {
let test_cases = vec![
TestCase { test_str: "let x = [1, 2, 3]; x[2]", val: Object::Integer(3) },
TestCase { test_str: "let x = [1, 2, [1, 3]]; x[2][0]", val: Object::Integer(1) },
TestCase {
test_str: "let x = [1, \"abc\", 32, 43]; x[1]",
val: Object::String(String::from("abc")),
},
];
check_test_cases(test_cases);
}
#[test]
fn test_eval_dict() {
let test_cases = vec![
TestCase { test_str: "let x = {1: 2, 2: 3, 3: 4}; x[2]", val: Object::Integer(3)},
TestCase { test_str: "{1: 2, 2: 3, 3: 4}[2]", val: Object::Integer(3)},
TestCase { test_str: "let x = {1: 2, \"test\": 3, 3: 4}; x[\"test\"]",
val: Object::Integer(3)},
TestCase { test_str: "let s = 3; let x = {1: 2, \"test\": 3, 3: (2*10)}; x[s]",
val: Object::Integer(20)},
TestCase { test_str: "let s = \"test\"; let x = {1: 2, \"test\": \"res\", 3: (2*10)}; x[s]",
val: Object::String(String::from("res"))},
];
check_test_cases(test_cases);
}
}

View File

@@ -0,0 +1,36 @@
use crate::object::Object;
pub fn get_inbuilt_function(identifier: &str) -> Option<Object> {
match identifier {
"len" => Some(Object::FunctionInBuilt(String::from("len"))),
_ => None
}
}
fn process_len_function(params: &Vec<Object>) -> Object {
if params.len() != 1 {
panic!("Expected a single argument to the len function found {} arguments",
params.len())
}
let argument = &params[0];
match argument {
Object::String(s) => Object::Integer(s.len() as i64),
_ => panic!("len expects a string argument")
}
}
pub fn eval_inbuilt_function(func_obj: &Object, params: &Vec<Object>) -> Object {
match func_obj {
Object::FunctionInBuilt(func_name) => {
match func_name.as_str() {
"len" => {
return process_len_function(params)
}
_ => panic!("Invalid inbuild function {}", func_name),
}
}
_ => panic!("Excepted a function object but found {}", func_obj)
}
}

View File

@@ -0,0 +1,399 @@
use std::fmt;
use std::fmt::{Debug};
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Token {
Illegal,
Eof,
// Identifiers + Literals
Ident(String),
String(String),
Int(i64),
// Operators
Assign,
Plus,
Bang,
Minus,
Slash,
Asterik,
Lt,
Gt,
Eq,
NotEq,
// Delimiters
Comma,
Colon,
Semicolon,
LParen,
RParen,
LBrace,
RBrace,
LBracket,
RBracket,
// Keywords
Function,
Let,
True,
False,
If,
Else,
Return,
}
fn token_str_repr(token: &Token) -> Box<String> {
let str_repr= match token {
Token::Illegal => String::from("Illegal"),
Token::Eof => String::from("Eof"),
// Identifiers + Literals
Token::Ident(s) => s.clone(),
Token::Int(i) => i.to_string(),
Token::String(s) => s.clone(),
// Operators
Token::Assign => String::from("="),
Token::Plus => String::from("+"),
Token::Bang => String::from("!"),
Token::Minus => String::from("-"),
Token::Slash => String::from("/"),
Token::Asterik => String::from("*"),
Token::Lt => String::from("<"),
Token::Gt => String::from(">"),
Token::Eq => String::from("=="),
Token::NotEq => String::from("!="),
// Delimiters
Token::Comma => String::from(","),
Token::Semicolon => String::from(";"),
Token::Colon => String::from(':'),
Token::LParen => String::from("("),
Token::RParen => String::from(")"),
Token::LBrace => String::from("{"),
Token::RBrace => String::from("}"),
Token::LBracket => String::from("["),
Token::RBracket => String::from("]"),
// Keywords
Token::Function => String::from("fn"),
Token::Let => String::from("let"),
Token::True => String::from("true"),
Token::False => String::from("false"),
Token::If => String::from("if"),
Token::Else => String::from("else"),
Token::Return => String::from("return"),
};
Box::new(str_repr)
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", token_str_repr(self))
}
}
pub struct Lexer {
tokens: Vec<Token>
}
impl Lexer {
pub fn new(input: &str) -> Box<Lexer> {
let mut tokens: Vec<Token> = vec![];
let size = input.len();
let mut index = 0;
let string_start = false;
while index < size {
while index < size && input.chars().nth(index).unwrap().is_ascii_whitespace() {
index += 1;
}
if index >= size {
break
}
let start = index;
let token = match input.chars().nth(index).unwrap() {
'=' => {
if size - index > 1 {
match input.chars().nth(index + 1).unwrap() {
'=' => {
index += 1;
Token::Eq
}
_ => Token::Assign
}
} else {
Token::Assign
}
}
';' => Token::Semicolon,
':' => Token::Colon,
'(' => Token::LParen,
')' => Token::RParen,
',' => Token::Comma,
'+' => Token::Plus,
'{' => Token::LBrace,
'}' => Token::RBrace,
'[' => Token::LBracket,
']' => Token::RBracket,
'>' => Token::Gt,
'<' => Token::Lt,
'*' => Token::Asterik,
'-' => Token::Minus,
'/' => Token::Slash,
'!' => {
if size - index > 1 {
match input.chars().nth(index + 1).unwrap() {
'=' => {
index += 1;
Token::NotEq
}
_ => Token::Bang
}
} else {
Token::Bang
}
}
_ => Token::Illegal
};
if token != Token::Illegal {
index += 1;
tokens.push(token);
continue;
}
if string_start == false && input.chars().nth(index).unwrap() == '"' {
let start = index;
index += 1;
while index < size && input.chars().nth(index).unwrap() != '"' {
index += 1;
}
let end= index;
if index < size {
index += 1;
}
let str_token = Token::String(input[(start + 1)..end].to_string());
tokens.push(str_token);
}
// Identifiers and keywords
if index < size && input.chars().nth(index).unwrap().is_alphabetic() {
while index < size && (input.chars().nth(index).unwrap().is_alphanumeric() ||
input.chars().nth(index).unwrap() == '_') {
index += 1;
}
if start < index {
let s = match &input[start..index] {
"let" => Token::Let,
"true" => Token::True,
"false" => Token::False,
"if" => Token::If,
"else" => Token::Else,
"return" => Token::Return,
"fn" => Token::Function,
_ => Token::Ident(input[start..index].to_string())
};
tokens.push(s);
}
}
// Numbers
if index < size && input.chars().nth(index).unwrap().is_ascii_digit() {
while index < size && (input.chars().nth(index).unwrap().is_ascii_digit()) {
index += 1;
}
if start < index {
tokens.push(Token::Int(input[start..index].to_string().parse::<i64>().unwrap()));
}
}
}
tokens.reverse();
Box::new(Lexer {
tokens
})
}
pub fn next(&mut self) -> Token {
self.tokens.pop().unwrap_or(Token::Eof)
}
}
#[cfg(test)]
mod tests {
use crate::lexer::{Lexer, Token};
const TEST_STR: &str = "
let five = 5;
let ten = 10;
let add = fn(x, y) {
x + y;
};
let result = add(five, ten);
!-/*5;
5 < 10 > 5;
if (5 < 10) {
return true;
} else {
return false;
}
10 == 10;
10 != 9;
let x = \"abcd\";
let y = \"\";
let arr = arr[1, 2, 3];
let y = arr[x];
let x = {z: \"vishal\", y: 1};
";
#[test]
fn test_tokens() {
let test_token_vec: Vec<Token> = vec![
Token::Let,
Token::Ident(String::from("five")),
Token::Assign,
Token::Int(5),
Token::Semicolon,
Token::Let,
Token::Ident(String::from("ten")),
Token::Assign,
Token::Int(10),
Token::Semicolon,
Token::Let,
Token::Ident(String::from("add")),
Token::Assign,
Token::Function,
Token::LParen,
Token::Ident(String::from("x")),
Token::Comma,
Token::Ident(String::from("y")),
Token::RParen,
Token::LBrace,
Token::Ident(String::from("x")),
Token::Plus,
Token::Ident(String::from("y")),
Token::Semicolon,
Token::RBrace,
Token::Semicolon,
Token::Let,
Token::Ident(String::from("result")),
Token::Assign,
Token::Ident(String::from("add")),
Token::LParen,
Token::Ident(String::from("five")),
Token::Comma,
Token::Ident(String::from("ten")),
Token::RParen,
Token::Semicolon,
Token::Bang,
Token::Minus,
Token::Slash,
Token::Asterik,
Token::Int(5),
Token::Semicolon,
Token::Int(5),
Token::Lt,
Token::Int(10),
Token::Gt,
Token::Int(5),
Token::Semicolon,
Token::If,
Token::LParen,
Token::Int(5),
Token::Lt,
Token::Int(10),
Token::RParen,
Token::LBrace,
Token::Return,
Token::True,
Token::Semicolon,
Token::RBrace,
Token::Else,
Token::LBrace,
Token::Return,
Token::False,
Token::Semicolon,
Token::RBrace,
Token::Int(10),
Token::Eq,
Token::Int(10),
Token::Semicolon,
Token::Int(10),
Token::NotEq,
Token::Int(9),
Token::Semicolon,
Token::Let,
Token::Ident(String::from("x")),
Token::Assign,
Token::String(String::from("abcd")),
Token::Semicolon,
Token::Let,
Token::Ident(String::from("y")),
Token::Assign,
Token::String(String::from("")),
Token::Semicolon,
Token::Let,
Token::Ident(String::from("arr")),
Token::Assign,
Token::Ident(String::from("arr")),
Token::LBracket,
Token::Int(1),
Token::Comma,
Token::Int(2),
Token::Comma,
Token::Int(3),
Token::RBracket,
Token::Semicolon,
Token::Let,
Token::Ident(String::from("y")),
Token::Assign,
Token::Ident(String::from("arr")),
Token::LBracket,
Token::Ident(String::from("x")),
Token::RBracket,
Token::Semicolon,
Token::Let,
Token::Ident(String::from("x")),
Token::Assign,
Token::LBrace,
Token::Ident(String::from("z")),
Token::Colon,
Token::String(String::from("vishal")),
Token::Comma,
Token::Ident(String::from("y")),
Token::Colon,
Token::Int(1),
Token::RBrace,
Token::Semicolon,
Token::Eof,
];
let mut lexer = Lexer::new(TEST_STR);
for test_token in test_token_vec.iter() {
let token = lexer.next();
assert_eq!(token, *test_token);
}
}
}

View File

@@ -0,0 +1,35 @@
mod lexer;
mod ast;
mod parser;
mod object;
mod environment;
mod evaluator;
mod inbuilt;
use linefeed::{Interface, ReadResult};
use crate::lexer::Lexer;
use crate::evaluator::eval_program;
use std::rc::Rc;
use std::cell::RefCell;
use crate::environment::Environment;
fn main() {
let reader = Interface::new("monkey-rs").unwrap();
let mut env = Rc::new(RefCell::new(Environment::new()));
reader.set_prompt("monkey-rs> ").unwrap();
while let ReadResult::Input(input) = reader.read_line().unwrap() {
if input.eq("exit") {
break;
}
let lexer = Lexer::new(&*input);
let mut parser = parser::Parser::new(lexer);
let program = parser.parse_program().unwrap();
println!("{}", eval_program(program.as_ref(), &mut env));
}
println!("Good bye");
}

View File

@@ -0,0 +1,63 @@
use std;
use crate::ast::BlockStatement;
use crate::environment::Environment;
use std::cell::RefCell;
use std::rc::Rc;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
#[derive(Debug, PartialEq, Clone)]
pub enum Object {
Error(String),
Nil,
Integer(i64),
Boolean(bool),
String(String),
Identifier(String),
FunctionInBuilt(String),
Array(Vec<Object>),
Dict(HashMap<Object, Object>),
FunctionLiteral(Vec<String>, BlockStatement, Rc<RefCell<Environment>>),
}
impl Eq for Object {}
impl Hash for Object {
fn hash<H: Hasher>(&self, state: &mut H) {
match self {
Object::String(s) => s.hash(state),
Object::Integer(i) => i.hash(state),
_ => panic!("Invalid dict key {}, supported types are string and integer", self)
}
}
}
impl std::fmt::Display for Object {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
Object::Error(e) => write!(fmt, "{}", e),
Object::Nil => write!(fmt, "nil"),
Object::Integer(i) => write!(fmt, "{}", i),
Object::Boolean(b) => write!(fmt, "{}", b),
Object::Identifier(s) => write!(fmt, "{}", s),
Object::String(s) => write!(fmt, "\"{}\"", s),
Object::Array(arr) => write!(fmt, "[{}]", arr.iter().map(|a| a.to_string()).collect::<Vec<String>>().join(",")),
Object::Dict(dict) => {
let mut str = String::new();
str.push_str("{");
for (k, v) in dict {
str.push_str(format!("{}:{},", k, v).as_str());
}
if str.ends_with(',') {
str.pop();
}
str.push_str("}");
write!(fmt, "{}", str)
},
Object::FunctionLiteral(parameters, block, _) => write!(fmt, "fn({}){{ {} }}",
parameters.join(","), block.to_string()),
_ => panic!("Invalid object {}", self),
}
}
}

View File

@@ -0,0 +1,825 @@
use crate::lexer::{Lexer, Token};
use crate::ast::*;
use std::fmt;
use std::fmt::Debug;
#[derive(Debug, PartialEq, Eq, Clone, PartialOrd)]
pub enum Precedence {
Lowest,
Equals,
LessGreater,
Sum,
Product,
Prefix,
Call,
Index,
}
#[derive(Debug)]
pub struct ParseError {
token: Token,
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Problem parsing near token {}", self.token)
}
}
pub(crate) struct Parser {
lexer: Box<Lexer>,
curr_token: Token,
next_token: Token,
}
impl Parser {
pub fn new(mut lexer: Box<Lexer>) -> Box<Parser> {
let curr_token = lexer.next();
let next_token = lexer.next();
Box::new(Parser { lexer, curr_token, next_token })
}
pub fn next(&mut self) -> Token {
self.curr_token = self.next_token.clone();
self.next_token = self.lexer.next();
self.curr_token.clone()
}
pub fn peek(&self) -> Token {
self.next_token.clone()
}
pub fn precedence(&self, token: &Token) -> Precedence {
match token {
Token::Eq => Precedence::Equals,
Token::NotEq => Precedence::Equals,
Token::Lt => Precedence::LessGreater,
Token::Gt => Precedence::LessGreater,
Token::Plus => Precedence::Sum,
Token::Minus => Precedence::Sum,
Token::Asterik => Precedence::Product,
Token::Slash => Precedence::Product,
Token::LParen => Precedence::Call,
Token::LBracket => Precedence::Index,
_ => Precedence::Lowest,
}
}
pub fn peek_precedence(&self) -> Precedence {
self.precedence(&self.next_token)
}
pub fn expect_current_token(&mut self, token: Token) {
if self.curr_token == token {
self.next();
} else {
panic!("Did not find expected current token {}, found {}", token, self.curr_token);
}
}
pub fn expect_next_token(&mut self, token: Token) {
if self.peek() == token {
self.next();
} else {
panic!("Did not find expected next token {}, found {}", token, self.peek());
}
}
fn parse_let_statement(&mut self) -> Box<Statement> {
// Get identifier token
let token = self.next();
let identifer = match token {
Token::Ident(s) => Expression::Identifier(s),
_ => panic!("Identifier token not found in let statement {}", token)
};
// Check assignment token
self.expect_next_token(Token::Assign);
self.next();
// Parse expression
let expr = self.parse_expression(Precedence::Lowest);
let let_stmt = Statement::Let(identifer.to_string(), expr);
if self.peek() == Token::Semicolon {
self.next();
}
Box::new(let_stmt)
}
fn parse_return_statement(&mut self) -> Box<Statement> {
self.next();
if self.curr_token == Token::Semicolon {
return Box::new(Statement::Return(None))
}
let expr = self.parse_expression(Precedence::Lowest);
if self.peek() == Token::Semicolon {
self.next();
}
Box::new(Statement::Return(Some(expr)))
}
fn parse_identifier(&mut self) -> Box<Expression> {
let curr_token = &self.curr_token;
match curr_token {
Token::Ident(s) => Box::new(Expression::Identifier(s.to_string())),
_ => panic!("Unable to parse identifier {}", self.curr_token)
}
}
fn parse_string(&mut self) -> Box<Expression> {
let curr_token = &self.curr_token;
match curr_token {
Token::String(s) => Box::new(Expression::String(s.to_string())),
_ => panic!("Unable to parse string {}", self.curr_token)
}
}
fn parse_integer(&mut self) -> Box<Expression> {
let curr_token = &self.curr_token;
match curr_token {
Token::Int(s) => Box::new(Expression::IntegerLiteral(*s)),
_ => panic!("Unable to parse integer {}", self.curr_token)
}
}
fn parse_boolean(&mut self) -> Box<Expression> {
let curr_token = &self.curr_token;
match curr_token {
Token::True => Box::new(Expression::Boolean(true)),
Token::False => Box::new(Expression::Boolean(false)),
_ => panic!("Invalid boolean {}", curr_token)
}
}
fn parse_prefix_expression(&mut self) -> Box<Expression> {
let op = self.curr_token.clone();
let prefix = match op {
Token::Bang => Prefix::Bang,
Token::Minus => Prefix::Minus,
_ => panic!("Invalid token {} prefix expression", op)
};
self.next();
Box::new(Expression::Prefix(prefix, self.parse_expression(Precedence::Prefix)))
}
fn parse_group_expression(&mut self) -> Box<Expression> {
self.expect_current_token(Token::LParen);
let expr = self.parse_expression(Precedence::Lowest);
self.expect_next_token(Token::RParen);
expr
}
fn parse_if_expression(&mut self) -> Box<Expression> {
self.expect_current_token(Token::If);
let condition = self.parse_group_expression();
let true_block = self.parse_block_statement();
self.next();
let mut false_block: Option<Box<BlockStatement>> = None;
if self.curr_token == Token::Else {
false_block = Some(self.parse_block_statement());
self.next();
}
Box::new(Expression::If(condition, true_block, false_block))
}
fn parse_block_statement(&mut self) -> Box<BlockStatement> {
let mut statements: Vec<Statement> = vec![];
self.expect_next_token(Token::LBrace);
self.next();
while self.curr_token != Token::Eof && self.curr_token != Token::RBrace {
let statement = self.parse_statement();
statements.push(*statement);
self.next();
}
Box::new(BlockStatement{stmts: statements})
}
pub fn parse_expression_statement(&mut self) -> Box<Statement> {
let expr = self.parse_expression(Precedence::Lowest);
if self.peek() == Token::Semicolon {
self.next();
}
Box::new(Statement::Expression(expr))
}
///
/// This function has been implemented using the TDOP algorithm mentioned
/// [here](https://eli.thegreenplace.net/2010/01/02/top-down-operator-precedence-parsing)
///
fn parse_expression(&mut self, precedence: Precedence) -> Box<Expression> {
let t = self.curr_token.clone();
// Prefix
let mut expr: Box<Expression> = match t {
Token::Ident(_s) => self.parse_identifier(),
Token::Int(_s) => self.parse_integer(),
Token::String(_s) => self.parse_string(),
Token::True | Token::False => self.parse_boolean(),
Token::Bang | Token::Minus => self.parse_prefix_expression(),
Token::LParen => self.parse_group_expression(),
Token::If => self.parse_if_expression(),
Token::Function => self.parse_function(),
Token::LBracket => self.parse_array_literal(),
Token::LBrace => self.parse_dict_literal(),
_ => panic!("Invalid token in expression {}, next token {}", t, self.next_token.clone())
};
// Infix
while self.peek() != Token::Semicolon && self.peek() != Token::Colon &&
self.peek_precedence() > precedence {
let token = self.next();
expr = match token {
Token::Plus | Token::Minus | Token::Slash | Token::Asterik |
Token::Eq | Token::NotEq | Token::Lt | Token::Gt => {
self.next();
let infix = match token {
Token::Plus => Infix::Plus,
Token::Minus => Infix::Minus,
Token::Slash => Infix::Slash,
Token::Asterik => Infix::Asterisk,
Token::Eq => Infix::Eq,
Token::NotEq => Infix::NotEq,
Token::Lt => Infix::Lt,
Token::Gt => Infix::Gt,
_ => panic!("Invalid infix token {}", token),
};
Box::new(Expression::Infix(infix, expr,
self.parse_expression(self.precedence(&token))))
}
Token::LParen => {
self.parse_function_call(expr)
},
Token::LBracket => {
self.parse_array_index(expr)
}
_ => expr
};
}
expr
}
pub fn parse_call_parameters(&mut self) -> Vec<Expression> {
let mut parameters: Vec<Expression> = vec![];
self.expect_current_token(Token::LParen);
while self.curr_token != Token::RParen {
let expr = self.parse_expression(Precedence::Lowest);
parameters.push(*expr);
if self.peek() == Token::Comma {
self.next();
}
self.next();
}
parameters
}
pub fn parse_function_call(&mut self, left: Box<Expression>) -> Box<Expression> {
let parameters = self.parse_call_parameters();
Box::new(Expression::Call(left, parameters))
}
pub fn parse_array_index(&mut self, left: Box<Expression>) -> Box<Expression> {
self.expect_current_token(Token::LBracket);
let index_expr = self.parse_expression(Precedence::Lowest);
self.expect_next_token(Token::RBracket);
Box::new(Expression::Index(left, index_expr))
}
pub fn parse_array_literal(&mut self) -> Box<Expression> {
let mut members: Vec<Expression> = vec![];
self.expect_current_token(Token::LBracket);
while self.curr_token != Token::RBracket {
let member = self.parse_expression(Precedence::Lowest);
members.push(*member);
if self.peek() == Token::Comma {
self.next();
}
self.next();
}
Box::new(Expression::ArrayLiteral(members))
}
pub fn parse_dict_literal(&mut self) -> Box<Expression> {
let mut key_values= vec![];
self.expect_current_token(Token::LBrace);
while self.curr_token != Token::RBrace {
let key = self.parse_expression(Precedence::Lowest);
self.expect_next_token(Token::Colon);
self.next();
let val = self.parse_expression(Precedence::Lowest);
key_values.push((*key, *val));
if self.peek() == Token::Comma {
self.next();
}
self.next();
}
Box::new(Expression::DictionaryLiteral(key_values))
}
pub fn parse_function_parameters(&mut self) -> Vec<String> {
let mut parameters: Vec<String> = vec![];
self.expect_current_token(Token::LParen);
while self.curr_token != Token::RParen {
let idf = &self.curr_token;
let identifier = match idf {
Token::Ident(i) => i.to_string(),
_ => panic!("Unexpected function parameter {}", idf)
};
parameters.push(identifier);
if self.peek() == Token::Comma {
self.next();
}
self.next();
}
parameters
}
pub fn parse_function(&mut self) -> Box<Expression> {
self.expect_current_token(Token::Function);
let parameters = self.parse_function_parameters();
let body = self.parse_block_statement();
Box::new(Expression::FunctionLiteral(parameters, body))
}
pub fn parse_statement(&mut self) -> Box<Statement> {
let statement = match self.curr_token {
Token::Let => self.parse_let_statement(),
Token::Return => self.parse_return_statement(),
_ => self.parse_expression_statement(),
};
statement
}
pub fn parse_program(&mut self) -> Result<Box<Program>, ParseError> {
let mut program = Box::new(Program { stmts: vec![] });
while self.curr_token != Token::Eof {
let statement = self.parse_statement();
program.stmts.push(*statement);
self.next();
}
Ok(program)
}
}
#[cfg(test)]
mod tests {
use crate::lexer::{Lexer, Token};
use crate::parser::Parser;
use crate::ast::{Statement, Prefix};
use crate::ast::Expression;
const TEST_STR: &str = "
let five = 5;
let ten = 10;
let add = fn(x, y) {
x + y;
};
let result = add(five, ten);
!-/*5;
5 < 10 > 5;
if (5 < 10) {
return true;
} else {
return false;
}
10 == 10;
10 != 9;
let x = \"x\";
let y = \"y\";
";
#[test]
fn test_parser() {
let lexer = Lexer::new(TEST_STR);
let mut parser = Parser::new(lexer);
let mut token = parser.next();
while token != Token::Eof {
token = parser.next();
let peek_token = parser.peek();
println!("{} {}", token, peek_token);
}
}
fn test_case_statements(input: &str) -> Vec<Statement> {
let lexer = Lexer::new(input);
let mut parser = Parser::new(lexer);
let program = parser.parse_program().unwrap();
program.stmts
}
const TEST_LET_STATEMENTS_STR: &str = "
let five = 5;
let is_true = true;
let t = ten;
let twenty = 20 + 20;
let zero = 30 - 30;
let complex = 11 - 22 + 11 * 22;
let x = \"abcd\";
let arr = [1, \"abc\", 3];
let y = [1, 2, 3][1];
let z = arr[2 + 3];
let dict = {1:3};
let dict = {1:3, 2:4, 3: 3 + 2, 4: \"test\"};
";
#[test]
fn test_parser_let_statements() {
let statements = test_case_statements(TEST_LET_STATEMENTS_STR);
assert_eq!(statements.len(), 12);
let mut idx = 0;
for stmt in statements.iter() {
let _let_stmt = match stmt {
Statement::Let(_s, expr) => {
match idx {
0 => {
assert_eq!(stmt.to_string(), "let five = 5;");
match **expr {
Expression::IntegerLiteral(i) => assert_eq!(i, 5),
_ => panic!("Expected integer value of 5"),
}
},
1 => {
assert_eq!(stmt.to_string(), "let is_true = true;");
match **expr {
Expression::Boolean(b) => assert_eq!(b, true),
_ => panic!("Expected a true boolean value"),
}
},
2 => assert_eq!(stmt.to_string(), "let t = ten;"),
3 => assert_eq!(stmt.to_string(), "let twenty = (20 + 20);"),
4 => assert_eq!(stmt.to_string(), "let zero = (30 - 30);"),
5 => assert_eq!(stmt.to_string(), "let complex = ((11 - 22) + (11 * 22));"),
6 => assert_eq!(stmt.to_string(), "let x = \"abcd\";"),
7 => assert_eq!(stmt.to_string(), "let arr = [1,\"abc\",3];"),
8 => assert_eq!(stmt.to_string(), "let y = [1,2,3][1];"),
9 => assert_eq!(stmt.to_string(), "let z = arr[(2 + 3)];"),
10 => assert_eq!(stmt.to_string(), "let dict = {1:3};"),
11 => assert_eq!(stmt.to_string(), "let dict = {1:3,2:4,3:(3 + 2),4:\"test\"};"),
_ => panic!("Unexcepted index {}", idx)
}
},
_ => panic!("Expected let statement but found {}", stmt),
};
idx += 1;
}
}
const TEST_RETURN_STATEMENTS_STR: &str = "
return;
return 5;
return 10 + 4 * 5;
";
#[test]
fn test_parser_return_statements() {
let statements = test_case_statements(TEST_RETURN_STATEMENTS_STR);
assert_eq!(statements.len(), 3);
let mut idx = 0;
for stmt in statements.iter() {
let _ret_stmt = match stmt {
Statement::Return(expr) => {
match idx {
0 => {
assert_eq!(expr.is_none(), true);
assert_eq!(stmt.to_string(), "return;")
},
1 => assert_eq!(stmt.to_string(), "return 5;"),
2 => assert_eq!(stmt.to_string(), "return (10 + (4 * 5));"),
_ => panic!("Unexcepted index {}", idx)
}
},
_ => panic!("{}: Expected return statement but found {}", idx, stmt),
};
idx += 1;
}
}
const TEST_PREFIX_STR: &str = "
!y;
-1;
";
#[test]
fn test_parser_prefix_expressions() {
let statements = test_case_statements(TEST_PREFIX_STR);
assert_eq!(statements.len(), 2);
let mut idx = 0;
for stmt in statements.iter() {
let _prefix_expr= match stmt {
Statement::Expression(expr) => {
match &**expr {
Expression::Prefix(prefix, expr2) => match idx {
0 => {
assert_eq!(*prefix, Prefix::Bang);
assert_eq!(expr2.to_string(), "y");
},
1 => {
assert_eq!(*prefix, Prefix::Minus);
assert_eq!(expr2.to_string(), "1");
},
_ => panic!("Unexpected expression index {}", idx)
},
_ => panic!("Expected prefix expression"),
}
},
_ => panic!("Expected statement with prefix expression but found {}", stmt),
};
idx += 1;
}
}
const TEST_GROUPED_EXPRESSION_STR: &str = "
let x = (x + y);
let x = (x + y) + (l + k);
let x = ((x * 2) + (3 * (2 + 3) + 2));
";
#[test]
fn test_parser_grouped_expressions() {
let statements = test_case_statements(TEST_GROUPED_EXPRESSION_STR);
assert_eq!(statements.len(), 3);
let mut idx = 0;
for stmt in statements.iter() {
match stmt {
Statement::Let(_s, _expr) => {
match idx {
0 => assert_eq!(format!("{}", stmt), "let x = (x + y);"),
1 => assert_eq!(format!("{}", stmt), "let x = ((x + y) + (l + k));"),
2 => assert_eq!(format!("{}", stmt), "let x = ((x * 2) + ((3 * (2 + 3)) + 2));"),
_ => panic!("Unexcepted index {}", idx)
}
}
_ => panic!("Expected let statement found {}", stmt)
}
idx += 1;
}
}
const TEST_IF_NO_ELSE_STR: &str = "
if (x > y) {
let z = x + y;
};
";
#[test]
fn test_parser_if_no_else() {
let statements = test_case_statements(TEST_IF_NO_ELSE_STR);
assert_eq!(statements.len(), 1);
let stmt = &statements[0];
match stmt {
Statement::Expression(expr) => {
match &**expr {
Expression::If(cond, true_block, false_block) => {
assert_eq!(cond.to_string(), "(x > y)");
assert_eq!(true_block.to_string(), "{let z = (x + y);}");
assert_eq!(false_block.is_none(), true);
}
_ => panic!("Expected if expression")
}
}
_ => panic!("Unexpected expression found")
}
}
const TEST_IF_ELSE_STR: &str = "
if (x > y) {
x*2 + 3;
let x = y;
} else {
4 + 5*y;
x + y;
}
";
#[test]
fn test_parser_if_else() {
let statements = test_case_statements(TEST_IF_ELSE_STR);
assert_eq!(statements.len(), 1);
let stmt = &statements[0];
match stmt {
Statement::Expression(expr) => {
match &**expr {
Expression::If(cond, true_block, false_block) => {
assert_eq!(cond.to_string(), "(x > y)");
assert_eq!(true_block.to_string(), "{((x * 2) + 3);let x = y;}");
assert_eq!(false_block.as_ref().unwrap().to_string(), "{(4 + (5 * y));(x + y);}");
}
_ => panic!("Expected if expression")
}
}
_ => panic!("Unexpected expression found")
}
}
const TEST_FUNCTION_STR1: &str = "
fn(x,y,z) {
let z = x + y;
z
};
let fact = fn(x){
if (x > 1) {
return x;
} else {
return 1;
};
};
";
#[test]
fn test_parser_function() {
let statements = test_case_statements(TEST_FUNCTION_STR1);
assert_eq!(statements.len(), 2);
let stmt = &statements[0];
match stmt {
Statement::Expression(expr) => {
match &**expr {
Expression::FunctionLiteral(params, block) => {
assert_eq!(params.iter().as_ref().join(","), "x,y,z");
assert_eq!(block.stmts[0].to_string(), "let z = (x + y);");
assert_eq!(block.stmts[1].to_string(), "z;");
}
_ => panic!("Expected function literal")
}
}
_ => panic!("Unexpected expression found")
}
}
const TEST_FUNCTION_STR2: &str = "
fn() {
10*20;
}
";
#[test]
fn test_parser_function_no_parameters() {
let statements = test_case_statements(TEST_FUNCTION_STR2);
assert_eq!(statements.len(), 1);
let stmt = &statements[0];
match stmt {
Statement::Expression(expr) => {
match &**expr {
Expression::FunctionLiteral(params, block) => {
assert_eq!(params.len(), 0);
assert_eq!(block.stmts[0].to_string(), "(10 * 20);");
}
_ => panic!("Expected function literal")
}
}
_ => panic!("Unexpected expression found")
}
}
const TEST_FUNCTION_STR3: &str = "
fn(x, y) {
x*y;
}(10, 20);
";
#[test]
fn test_parser_function_expression() {
let statements = test_case_statements(TEST_FUNCTION_STR3);
assert_eq!(statements.len(), 1);
let stmt = &statements[0];
match stmt {
Statement::Expression(expr) => {
println!("{}", expr);
}
_ => panic!("Unexpected expression found")
}
}
const TEST_FUNCTION_CALL_STR: &str = "
sum();
sum3(x, y, z);
sum_expr(x, y + w, z);
fn(x, y){x + y;}(2, 3);
";
#[test]
fn test_parser_call_with_parameters() {
let statements = test_case_statements(TEST_FUNCTION_CALL_STR);
assert_eq!(statements.len(), 4);
for idx in 0..statements.len() {
match &statements[idx] {
Statement::Expression(expr) => {
match &**expr {
Expression::Call(func_expr, params) => {
match idx {
0 => {
assert_eq!(func_expr.to_string(), "sum");
assert_eq!(params.len(), 0);
},
1 => {
assert_eq!(func_expr.to_string(), "sum3");
assert_eq!(params.iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","), "x,y,z");
},
2 => {
assert_eq!(func_expr.to_string(), "sum_expr");
assert_eq!(params.iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","),
"x,(y + w),z");
},
3 => {
assert_eq!(func_expr.to_string(), "fn(x,y){(x + y);}");
assert_eq!(params.iter().map(|p| p.to_string()).collect::<Vec<String>>().join(","),
"2,3");
}
_ => panic!("Unexpected idx {}", idx),
}
},
_ => panic!("Expected call expression")
}
},
_ => panic!("Expected a expression statement"),
}
}
}
const TEST_STRINGS_STR: &str = "
\"abcd\";
";
#[test]
fn test_parser_string_statements() {
let statements = test_case_statements(TEST_STRINGS_STR);
assert_eq!(statements.len(), 1);
for stmt in statements.iter() {
match stmt {
Statement::Expression(expr) => {
match &**expr {
Expression::String(s) => println!("{}", s.to_string()),
_ => panic!("Expected string literal in expression found {}", expr)
}
},
_ => panic!("Expected a string expression"),
}
}
}
}