feat: add monkey-rs
This commit is contained in:
825
__lang/monkey-rs/src/parser.rs
Normal file
825
__lang/monkey-rs/src/parser.rs
Normal 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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user