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

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"),
}
}
}
}