use std::cell::RefCell;
use crate::{
currencies::currency::Currency,
scripting::{
nodes::node::*,
parsing::lexer::{Token, Tokenize},
utils::errors::{Result, ScriptingError},
},
time::{date::Date, daycounter::DayCounter},
};
#[cfg(test)]
use crate::scripting::parsing::lexer::Lexer;
pub struct Parser {
tokens: RefCell<Vec<Token>>,
position: RefCell<usize>,
line: RefCell<usize>,
column: RefCell<usize>,
reserved_keywords: Vec<String>,
}
impl Parser {
pub fn new(tokens: Vec<Token>) -> Self {
Self {
tokens: RefCell::new(tokens),
position: RefCell::new(0),
line: RefCell::new(1),
column: RefCell::new(1),
reserved_keywords: vec![
"if".to_string(),
"else".to_string(),
"and".to_string(),
"or".to_string(),
"true".to_string(),
"false".to_string(),
"Spot".to_string(),
"RateIndex".to_string(),
"Df".to_string(),
"pays".to_string(),
"exp".to_string(),
"ln".to_string(),
"pow".to_string(),
"min".to_string(),
"max".to_string(),
"cvg".to_string(),
"fif".to_string(),
"in".to_string(),
"range".to_string(),
],
}
}
pub fn parse(&self) -> Result<Node> {
let mut expressions = Vec::new();
while self.current_token() != Token::EOF {
if self.current_token() == Token::Newline {
self.advance();
continue;
}
let expr = self.parse_expression()?;
expressions.push(expr);
}
Ok(Node::Base(NodeData {
children: expressions,
}))
}
}
impl Parser {
fn expect_not_reserved(&self, word: &str) -> Result<()> {
if self.reserved_keywords.contains(&word.to_string()) {
Err(self.invalid_syntax_err("Reserved keyword"))
} else {
Ok(())
}
}
fn current_token(&self) -> Token {
self.tokens
.borrow()
.get(*self.position.borrow())
.cloned()
.unwrap_or(Token::EOF)
}
fn peek_token(&self) -> Token {
self.tokens
.borrow()
.get(*self.position.borrow() + 1)
.cloned()
.unwrap_or(Token::EOF)
}
fn advance(&self) {
let mut pos = self.position.borrow_mut();
let mut line = self.line.borrow_mut();
let mut column = self.column.borrow_mut();
let tokens = self.tokens.borrow();
loop {
let current_token = tokens.get(*pos + 1).cloned().unwrap_or(Token::EOF);
match current_token {
Token::Newline => {
*line += 1;
*column = 1;
*pos += 1;
}
_ => {
*column += {
let token_str = format!("{:?}", current_token);
token_str.len()
};
*pos += 1;
break;
}
}
}
}
fn invalid_syntax_err(&self, msg: &str) -> ScriptingError {
let line = *self.line.borrow();
let column = *self.column.borrow();
ScriptingError::InvalidSyntax(format!(
"Error at line {}, column {}: {}",
line, column, msg
))
}
fn unexpected_token_err(&self, expected: Token, received: Token) -> ScriptingError {
let line = *self.line.borrow();
let column = *self.column.borrow();
ScriptingError::UnexpectedToken(format!(
"Error at line {}, column {}: Expected token {:?}, found {:?}",
line, column, expected, received
))
}
fn expect_token(&self, expected: Token) -> Result<()> {
if self.current_token() == expected {
Ok(())
} else {
Err(self.unexpected_token_err(expected, self.current_token()))
}
}
fn parse_expression(&self) -> Result<Node> {
match self.current_token() {
Token::If => self.parse_if(),
Token::For => self.parse_for(),
Token::Pays => {
let expr = self.parse_pays()?;
if self.current_token() == Token::Semicolon {
self.advance();
}
Ok(expr)
}
Token::EOF => Err(self.invalid_syntax_err("Unexpected end of expression")),
_ => {
if let Token::Identifier(_) = self.current_token() {
match self.peek_token() {
Token::Assign => {
let lhs = self.parse_variable()?;
self.parse_assign(lhs)
}
Token::PlusAssign
| Token::MinusAssign
| Token::MultiplyAssign
| Token::DivideAssign => {
let lhs = self.parse_variable()?;
self.parse_compound_assign(lhs)
}
Token::Pays => {
let lhs = self.parse_variable()?;
let pays_expr = self.parse_pays()?;
if self.current_token() == Token::Semicolon {
self.advance();
}
let rhs = Node::Add(NodeData {
children: vec![lhs.clone(), pays_expr],
});
Ok(Node::Assign(NodeData {
children: vec![lhs, rhs],
}))
}
_ => {
let expr = self.parse_expr()?;
self.expect_token(Token::Semicolon)?;
self.advance();
Ok(expr)
}
}
} else {
let expr = self.parse_expr()?;
self.expect_token(Token::Semicolon)?;
self.advance();
Ok(expr)
}
}
}
}
fn parse_pays(&self) -> Result<Node> {
self.expect_token(Token::Pays)?;
self.advance();
let mut pays = Vec::new();
while self.current_token() != Token::EOF
&& self.current_token() != Token::Semicolon
&& self.current_token() != Token::CloseParen
&& self.current_token() != Token::On
&& self.current_token() != Token::In
{
let expr = self.parse_expr()?;
pays.push(expr);
}
let mut pay_date = None;
if self.current_token() == Token::On {
self.advance();
let date_str = match self.parse_string()? {
Node::String(s) => s,
_ => return Err(self.invalid_syntax_err("Invalid argument, expected string")),
};
pay_date = Some(
Date::from_str(&date_str, "%Y-%m-%d")
.map_err(|_| self.invalid_syntax_err("Invalid date"))?,
);
}
let mut currency = None;
if self.current_token() == Token::In {
self.advance();
let ccy_str = match self.parse_string()? {
Node::String(s) => s,
_ => return Err(self.invalid_syntax_err("Invalid argument, expected string")),
};
currency = Some(
Currency::try_from(ccy_str)
.map_err(|_| self.invalid_syntax_err("Invalid currency"))?,
);
}
Ok(Node::Pays(PaysData {
children: pays,
date: pay_date,
currency,
id: None,
df_id: None,
spot_id: None,
}))
}
fn parse_if(&self) -> Result<Node> {
self.expect_token(Token::If)?;
self.advance();
let condition = self.parse_conditions()?;
self.expect_token(Token::OpenCurlyParen)?;
self.advance();
let mut if_body = Vec::new();
while self.current_token() != Token::CloseCurlyParen {
if self.current_token() == Token::EOF {
return Err(self.invalid_syntax_err("Unexpected end of input in if body"));
}
let expr = self.parse_expression()?;
if_body.push(expr);
}
self.advance();
let mut else_index = None;
if self.current_token() == Token::Else {
self.advance();
self.expect_token(Token::OpenCurlyParen)?;
self.advance();
else_index = Some(if_body.len() + 1);
while self.current_token() != Token::CloseCurlyParen {
if self.current_token() == Token::EOF {
return Err(self.invalid_syntax_err("Unexpected end of input in else body"));
}
let expr = self.parse_expression()?;
if_body.push(expr);
}
self.advance();
}
let mut nodes = condition;
nodes.append(&mut if_body);
Ok(Node::If(IfData {
children: nodes,
first_else: else_index,
affected_vars: Vec::new(),
}))
}
fn parse_for(&self) -> Result<Node> {
self.expect_token(Token::For)?;
self.advance();
let var_name = match self.current_token() {
Token::Identifier(name) => {
self.expect_not_reserved(&name)?;
self.advance();
name
}
_ => return Err(self.invalid_syntax_err("Expected variable name")),
};
match self.current_token() {
Token::In => self.advance(),
Token::Identifier(ref s) if s == "in" => self.advance(),
_ => return Err(self.invalid_syntax_err("Expected 'in' after variable")),
}
let iter_expr = Box::new(self.parse_expr()?);
self.expect_token(Token::OpenCurlyParen)?;
self.advance();
let mut body = Vec::new();
while self.current_token() != Token::CloseCurlyParen {
if self.current_token() == Token::EOF {
return Err(self.invalid_syntax_err("Unexpected end of input in for body"));
}
let expr = self.parse_expression()?;
body.push(expr);
}
self.advance();
Ok(Node::new_for_each(var_name, iter_expr, body))
}
fn parse_variable(&self) -> Result<Node> {
match self.current_token() {
Token::Identifier(name) => {
self.expect_not_reserved(&name)?;
self.advance();
Ok(Node::new_variable(name))
}
_ => Err(self
.unexpected_token_err(Token::Identifier("Any".to_string()), self.current_token())),
}
}
fn parse_string(&self) -> Result<Node> {
match self.current_token() {
Token::String(string) => {
self.advance();
Ok(Node::new_string(string))
}
_ => Err(self.invalid_syntax_err("Invalid string, expected string literal")),
}
}
fn parse_assign(&self, lhs: Node) -> Result<Node> {
self.expect_token(Token::Assign)?;
self.advance();
let rhs = self.parse_expr()?;
self.expect_token(Token::Semicolon)?;
self.advance();
Ok(Node::new_asign_with_values(lhs, rhs))
}
fn parse_compound_assign(&self, lhs: Node) -> Result<Node> {
let op = self.current_token();
self.advance();
let rhs = self.parse_expr()?;
self.expect_token(Token::Semicolon)?;
self.advance();
let rhs_expr = match op {
Token::PlusAssign => Node::new_add_with_values(lhs.clone(), rhs),
Token::MinusAssign => Node::new_subtract_with_values(lhs.clone(), rhs),
Token::MultiplyAssign => Node::new_multiply_with_values(lhs.clone(), rhs),
Token::DivideAssign => Node::new_divide_with_values(lhs.clone(), rhs),
_ => unreachable!(),
};
Ok(Node::new_asign_with_values(lhs, rhs_expr))
}
fn parse_constant(&self) -> Result<Node> {
match self.current_token() {
Token::Value(value, boolean) => {
self.advance();
match boolean {
Some(true) => Ok(Node::True),
Some(false) => Ok(Node::False),
None => match value {
Some(v) => Ok(Node::new_constant(v)),
None => Err(self.invalid_syntax_err("Invalid constant")),
},
}
}
_ => Err(self.invalid_syntax_err("Invalid constant")),
}
}
fn parse_conditions(&self) -> Result<Vec<Node>> {
let mut conditions = Vec::new();
let mut condition = self.parse_condition_element()?;
while matches!(self.current_token(), Token::And | Token::Or) {
let operator = self.current_token();
self.advance();
let rhs = self.parse_condition_element()?;
condition = match operator {
Token::And => Node::new_and_with_values(condition, rhs),
Token::Or => Node::new_or_with_values(condition, rhs),
_ => return Err(self.invalid_syntax_err("Invalid operator")),
};
}
conditions.push(condition);
Ok(conditions)
}
fn parse_condition_element(&self) -> Result<Node> {
let lhs = self.parse_arith_expr()?;
let comparator = self.current_token();
match comparator {
Token::Equal
| Token::NotEqual
| Token::Superior
| Token::Inferior
| Token::SuperiorOrEqual
| Token::InferiorOrEqual => {
self.advance();
let rhs = self.parse_arith_expr()?;
let comparison_node = match comparator {
Token::Equal => Node::new_equal_with_values(lhs, rhs),
Token::NotEqual => Node::new_not_equal_with_values(lhs, rhs),
Token::Superior => Node::new_superior_with_values(lhs, rhs),
Token::Inferior => Node::new_inferior_with_values(lhs, rhs),
Token::SuperiorOrEqual => Node::new_superior_or_equal_with_values(lhs, rhs),
Token::InferiorOrEqual => Node::new_inferior_or_equal_with_values(lhs, rhs),
_ => return Err(self.invalid_syntax_err("Invalid comparison operator")),
};
Ok(comparison_node)
}
_ => {
Ok(lhs)
}
}
}
fn parse_function_args(&self) -> Result<Vec<Node>> {
self.expect_token(Token::OpenParen)?;
self.advance();
let mut args = Vec::new();
while self.current_token() != Token::CloseParen {
let arg = self.parse_expr()?;
args.push(arg);
match self.current_token() {
Token::Comma => self.advance(),
Token::CloseParen => (),
_ => return Err(self.invalid_syntax_err("Expected comma or closing parenthesis")),
};
}
Ok(args)
}
fn parse_list(&self) -> Result<Node> {
self.expect_token(Token::OpenBracket)?;
self.advance();
let mut elements = Vec::new();
while self.current_token() != Token::CloseBracket {
let elem = self.parse_expr()?;
elements.push(elem);
match self.current_token() {
Token::Comma => self.advance(),
Token::CloseBracket => (),
_ => return Err(self.invalid_syntax_err("Expected comma or closing bracket")),
};
}
self.expect_token(Token::CloseBracket)?;
self.advance();
Ok(Node::new_list_with_values(elements))
}
fn parse_var_const_func(&self) -> Result<Node> {
if self.current_token() == Token::OpenParen {
self.advance();
let expr = self.parse_expr()?;
self.expect_token(Token::CloseParen)?;
self.advance();
return Ok(expr);
}
if self.current_token() == Token::Plus {
self.advance();
return self.parse_var_const_func();
}
if self.current_token() == Token::Minus {
self.advance();
match self.current_token() {
Token::Value(Some(v), None) => {
self.advance();
return Ok(Node::new_constant(-v));
}
_ => {
let expr = self.parse_var_const_func()?;
let mut node = Node::new_unary_minus();
node.add_child(expr);
return Ok(node);
}
}
}
if self.current_token() == Token::OpenBracket {
let list = self.parse_list()?;
return self.parse_postfix(list);
}
let try_const = self.parse_constant();
if try_const.is_ok() {
return try_const;
}
let try_string = self.parse_string();
if try_string.is_ok() {
return try_string;
}
let mut min_args = 0;
let mut max_args = 0;
let mut expr = None;
match self.current_token() {
Token::Pays => {
return self.parse_pays();
}
Token::Identifier(name) => match name.as_str() {
"ln" => {
min_args = 1;
max_args = 1;
expr = Some(Node::new_ln());
}
"exp" => {
min_args = 1;
max_args = 1;
expr = Some(Node::new_exp());
}
"pow" => {
min_args = 2;
max_args = 2;
expr = Some(Node::new_pow());
}
"min" => {
min_args = 2;
max_args = 100;
expr = Some(Node::new_min());
}
"max" => {
min_args = 2;
max_args = 100;
expr = Some(Node::new_max());
}
"cvg" => {
min_args = 3;
max_args = 3;
expr = Some(Node::new_cvg());
}
"fif" => {
min_args = 4;
max_args = 4;
expr = Some(Node::new_fif());
}
"range" => {
min_args = 2;
max_args = 2;
expr = Some(Node::new_range());
}
"Spot" => {
return self.parse_spot();
}
"Df" => {
return self.parse_df();
}
"RateIndex" => {
return self.parse_rate_index();
}
_ => (),
},
_ => {
return Err(ScriptingError::UnexpectedToken(format!(
"{:?}",
self.current_token()
)))
}
}
if let Some(mut expr) = expr {
self.advance();
let args = self.parse_function_args()?;
self.expect_token(Token::CloseParen)?;
self.advance();
if args.len() < min_args || args.len() > max_args {
return Err(self.invalid_syntax_err("Invalid number of arguments"));
}
if matches!(expr, Node::Cvg(_)) {
if let [a, b, c] = &args[..] {
let get_str = |n: Node| match n {
Node::String(s) => Ok(s.clone()),
_ => Err(self.invalid_syntax_err("Invalid argument, expected string")),
};
let s1 = get_str((*a).clone())?;
let s2 = get_str((*b).clone())?;
let s3 = get_str((*c).clone())?;
Date::from_str(&s1, "%Y-%m-%d")
.map_err(|_| self.invalid_syntax_err("Invalid date"))?;
Date::from_str(&s2, "%Y-%m-%d")
.map_err(|_| self.invalid_syntax_err("Invalid date"))?;
DayCounter::try_from(s3)
.map_err(|_| self.invalid_syntax_err("Invalid day counter"))?;
} else {
return Err(self.invalid_syntax_err("Invalid number of arguments"));
}
}
args.into_iter().for_each(|arg| expr.add_child(arg));
return Ok(expr);
}
let var = self.parse_variable()?;
self.parse_postfix(var)
}
fn parse_postfix(&self, mut expr: Node) -> Result<Node> {
loop {
if self.current_token() == Token::Dot {
self.advance();
let method = match self.current_token() {
Token::Identifier(name) => {
self.advance();
name
}
_ => return Err(self.invalid_syntax_err("Expected method name")),
};
let args = self.parse_function_args()?;
self.expect_token(Token::CloseParen)?;
self.advance();
expr = match method.as_str() {
"append" => {
if args.len() != 1 {
return Err(self.invalid_syntax_err("append expects one argument"));
}
Node::new_append_with_values(expr, args[0].clone())
}
"mean" => {
if !args.is_empty() {
return Err(self.invalid_syntax_err("mean expects no arguments"));
}
Node::new_mean_with_values(expr)
}
"std" => {
if !args.is_empty() {
return Err(self.invalid_syntax_err("std expects no arguments"));
}
Node::new_std_with_values(expr)
}
_ => return Err(self.invalid_syntax_err("Unknown method")),
};
} else if self.current_token() == Token::OpenBracket {
self.advance();
let idx = self.parse_expr()?;
self.expect_token(Token::CloseBracket)?;
self.advance();
expr = Node::new_index_with_values(vec![expr], idx);
} else {
break;
}
}
Ok(expr)
}
fn parse_spot(&self) -> Result<Node> {
self.expect_token(Token::Identifier("Spot".to_string()))?;
self.advance();
self.expect_token(Token::OpenParen)?;
self.advance();
let first_str = match self.parse_string()? {
Node::String(s) => s,
_ => return Err(self.invalid_syntax_err("Invalid argument, expected string")),
};
let Ok(first) = Currency::try_from(first_str.clone()) else {
let mut date: Option<Date> = None;
if self.current_token() == Token::Comma {
self.advance();
let date_str = match self.parse_string()? {
Node::String(s) => s,
_ => return Err(self.invalid_syntax_err("Invalid argument, expected string")),
};
date = Some(
Date::from_str(&date_str, "%Y-%m-%d")
.map_err(|_| self.invalid_syntax_err("Invalid date"))?,
);
}
self.expect_token(Token::CloseParen)?;
self.advance();
return Ok(Node::new_equity_spot(first_str, date));
};
let mut second: Currency = Currency::USD;
let mut date: Option<Date> = None;
if self.current_token() == Token::Comma {
self.advance();
match self.parse_string()? {
Node::String(s) => {
second = Currency::try_from(s)
.map_err(|_| self.invalid_syntax_err("Invalid currency"))?;
}
_ => return Err(self.invalid_syntax_err("Invalid argument, expected string")),
}
if self.current_token() == Token::Comma {
self.advance();
let date_str = match self.parse_string()? {
Node::String(s) => s,
_ => return Err(self.invalid_syntax_err("Invalid argument, expected string")),
};
date = Some(
Date::from_str(&date_str, "%Y-%m-%d")
.map_err(|_| self.invalid_syntax_err("Invalid date"))?,
);
}
}
self.expect_token(Token::CloseParen)?;
self.advance();
Ok(Node::new_spot(first, second, date))
}
fn parse_df(&self) -> Result<Node> {
self.expect_token(Token::Identifier("Df".to_string()))?;
self.advance();
self.expect_token(Token::OpenParen)?;
self.advance();
let date_str = match self.parse_string()? {
Node::String(s) => s,
_ => return Err(self.invalid_syntax_err("Invalid argument, expected string")),
};
let date = Date::from_str(&date_str, "%Y-%m-%d")
.map_err(|_| self.invalid_syntax_err("Invalid date"))?;
let mut curve: Option<String> = None;
if self.current_token() == Token::Comma {
self.advance();
match self.parse_string()? {
Node::String(s) => curve = Some(s),
_ => return Err(self.invalid_syntax_err("Invalid argument, expected string")),
}
}
self.expect_token(Token::CloseParen)?;
self.advance();
Ok(Node::new_df(date, curve))
}
fn parse_rate_index(&self) -> Result<Node> {
self.expect_token(Token::Identifier("RateIndex".to_string()))?;
self.advance();
self.expect_token(Token::OpenParen)?;
self.advance();
let name = match self.parse_string()? {
Node::String(s) => s,
_ => return Err(self.invalid_syntax_err("Invalid argument, expected string")),
};
self.expect_token(Token::Comma)?;
self.advance();
let start_str = match self.parse_string()? {
Node::String(s) => s,
_ => return Err(self.invalid_syntax_err("Invalid argument, expected string")),
};
self.expect_token(Token::Comma)?;
self.advance();
let end_str = match self.parse_string()? {
Node::String(s) => s,
_ => return Err(self.invalid_syntax_err("Invalid argument, expected string")),
};
self.expect_token(Token::CloseParen)?;
self.advance();
let start = Date::from_str(&start_str, "%Y-%m-%d")
.map_err(|_| self.invalid_syntax_err("Invalid date"))?;
let end = Date::from_str(&end_str, "%Y-%m-%d")
.map_err(|_| self.invalid_syntax_err("Invalid date"))?;
Ok(Node::new_rate_index(name, start, end))
}
fn parse_expr(&self) -> Result<Node> {
let mut lhs = self.parse_condition_element()?;
while self.current_token() == Token::Plus
|| self.current_token() == Token::Minus
|| self.current_token() == Token::And
|| self.current_token() == Token::Or && self.current_token() != Token::EOF
{
let token = self.current_token();
self.advance();
match self.current_token() {
Token::EOF => return Err(self.invalid_syntax_err("Unexpected end of expression")),
_ => {
let rhs = self.parse_condition_element()?;
lhs = match token {
Token::Plus => Node::new_add_with_values(lhs, rhs),
Token::Minus => Node::new_subtract_with_values(lhs, rhs),
Token::And => Node::new_and_with_values(lhs, rhs),
Token::Or => Node::new_or_with_values(lhs, rhs),
_ => {
return Err(self.invalid_syntax_err("Invalid operator"));
}
};
}
}
}
Ok(lhs)
}
fn parse_arith_expr(&self) -> Result<Node> {
let mut lhs = self.parse_expr_l2()?;
while matches!(self.current_token(), Token::Plus | Token::Minus) {
let op = self.current_token();
self.advance();
match self.current_token() {
Token::EOF => return Err(self.invalid_syntax_err("Unexpected end of expression")),
_ => {
let rhs = self.parse_expr_l2()?;
lhs = match op {
Token::Plus => Node::new_add_with_values(lhs, rhs),
Token::Minus => Node::new_subtract_with_values(lhs, rhs),
_ => unreachable!(),
};
}
}
}
Ok(lhs)
}
fn parse_expr_l2(&self) -> Result<Node> {
let mut lhs = self.parse_expr_l3()?;
while self.current_token() == Token::Multiply
|| self.current_token() == Token::Divide && self.current_token() != Token::EOF
{
let token = self.current_token();
self.advance();
match self.current_token() {
Token::EOF => return Err(self.invalid_syntax_err("Unexpected end of expression")),
_ => {
let rhs = self.parse_expr_l3()?;
lhs = match token {
Token::Multiply => Node::new_multiply_with_values(lhs, rhs),
Token::Divide => Node::new_divide_with_values(lhs, rhs),
_ => {
return Err(self.invalid_syntax_err("Invalid operator"));
}
};
}
}
}
Ok(lhs)
}
fn parse_expr_l3(&self) -> Result<Node> {
let mut lhs = self.parse_var_const_func()?;
while self.current_token() == Token::Power && self.current_token() != Token::EOF {
self.advance();
match self.current_token() {
Token::EOF => return Err(self.invalid_syntax_err("Unexpected end of expression")),
_ => {
let rhs = self.parse_var_const_func()?;
lhs = Node::new_pow_with_values(lhs, rhs);
}
}
}
Ok(lhs)
}
}
impl TryFrom<String> for Node {
type Error = ScriptingError;
fn try_from(script: String) -> Result<Node> {
let tokens = script.tokenize()?;
let parser = Parser::new(tokens);
parser.parse()
}
}
impl TryFrom<&str> for Node {
type Error = ScriptingError;
fn try_from(script: &str) -> Result<Node> {
let tokens = script.to_string().tokenize()?;
let parser = Parser::new(tokens);
parser.parse()
}
}
#[cfg(test)]
mod other_tests {
use super::*;
use crate::scripting::parsing::lexer::Lexer;
#[test]
fn test_negative_expression() {
let script = "-5 + 3;".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let parser = Parser::new(tokens);
let ast = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_add_with_values(
Node::new_constant(-5.0),
Node::new_constant(3.0),
)],
});
assert_eq!(ast, expected);
}
#[test]
fn test_advance_token() {
let tokens = Lexer::new("a = 1;".to_string()).tokenize().unwrap();
let parser = Parser::new(tokens);
assert_eq!(parser.current_token(), Token::Identifier("a".to_string()));
parser.advance();
assert_eq!(parser.current_token(), Token::Assign);
parser.advance();
assert_eq!(parser.current_token(), Token::Value(Some(1.0), None));
parser.advance();
assert_eq!(parser.current_token(), Token::Semicolon);
parser.advance();
assert_eq!(parser.current_token(), Token::EOF);
}
#[test]
fn test_advance_token_with_newlines() {
let tokens = Lexer::new("a = 1;\n\n".to_string()).tokenize().unwrap();
let parser = Parser::new(tokens);
assert_eq!(parser.current_token(), Token::Identifier("a".to_string()));
parser.advance();
assert_eq!(parser.current_token(), Token::Assign);
parser.advance();
assert_eq!(parser.current_token(), Token::Value(Some(1.0), None));
parser.advance();
assert_eq!(parser.current_token(), Token::Semicolon);
parser.advance();
assert_eq!(parser.current_token(), Token::EOF);
}
#[test]
fn test_paren_expr() {
let script = "avg = (s1 + s2) / 2.0;".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let parser = Parser::new(tokens);
let ast = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("avg".to_string()),
Node::new_divide_with_values(
Node::new_add_with_values(
Node::new_variable("s1".to_string()),
Node::new_variable("s2".to_string()),
),
Node::new_constant(2.0),
),
)],
});
assert_eq!(ast, expected);
}
#[test]
fn test_list_expression() {
let script = "a = [1,2,3];".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let parser = Parser::new(tokens);
let ast = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("a".to_string()),
Node::new_list_with_values(vec![
Node::new_constant(1.0),
Node::new_constant(2.0),
Node::new_constant(3.0),
]),
)],
});
assert_eq!(ast, expected);
}
#[test]
fn test_list_with_variables() {
let script = "x = 1; y = 2; a = [x, y];".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let parser = Parser::new(tokens);
let ast = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![
Node::new_asign_with_values(
Node::new_variable("x".to_string()),
Node::new_constant(1.0),
),
Node::new_asign_with_values(
Node::new_variable("y".to_string()),
Node::new_constant(2.0),
),
Node::new_asign_with_values(
Node::new_variable("a".to_string()),
Node::new_list_with_values(vec![
Node::new_variable("x".to_string()),
Node::new_variable("y".to_string()),
]),
),
],
});
assert_eq!(ast, expected);
}
#[test]
fn test_list_with_spot() {
let script = "a = [Spot(\"USD\", \"EUR\")];".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let parser = Parser::new(tokens);
let ast = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("a".to_string()),
Node::new_list_with_values(vec![Node::new_spot(
Currency::try_from("USD".to_string()).unwrap(),
Currency::try_from("EUR".to_string()).unwrap(),
None,
)]),
)],
});
assert_eq!(ast, expected);
}
#[test]
fn test_list_method_call() {
let script = "spots = [Spot(\"USD\",\"CLP\"), Spot(\"USD\",\"EUR\")].mean();";
let tokens = Lexer::new(script.to_string()).tokenize().unwrap();
let parser = Parser::new(tokens);
let ast = parser.parse().unwrap();
let list = Node::new_list_with_values(vec![
Node::new_spot(
Currency::try_from("USD".to_string()).unwrap(),
Currency::try_from("CLP".to_string()).unwrap(),
None,
),
Node::new_spot(
Currency::try_from("USD".to_string()).unwrap(),
Currency::try_from("EUR".to_string()).unwrap(),
None,
),
]);
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("spots".to_string()),
Node::new_mean_with_values(list),
)],
});
assert_eq!(ast, expected);
}
#[test]
fn test_variable_method_call() {
let script = "vals = [1,2]; avg = vals.mean();".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let parser = Parser::new(tokens);
let ast = parser.parse().unwrap();
let list =
Node::new_list_with_values(vec![Node::new_constant(1.0), Node::new_constant(2.0)]);
let expected = Node::Base(NodeData {
children: vec![
Node::new_asign_with_values(Node::new_variable("vals".to_string()), list.clone()),
Node::new_asign_with_values(
Node::new_variable("avg".to_string()),
Node::new_mean_with_values(Node::new_variable("vals".to_string())),
),
],
});
assert_eq!(ast, expected);
}
#[test]
fn test_parse_valid_script() {
let script = "a = 1; b = 2; c = a + b;".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let parser = Parser::new(tokens);
let ast = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![
Node::new_asign_with_values(
Node::new_variable("a".to_string()),
Node::new_constant(1.0),
),
Node::new_asign_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(2.0),
),
Node::new_asign_with_values(
Node::new_variable("c".to_string()),
Node::new_add_with_values(
Node::new_variable("a".to_string()),
Node::new_variable("b".to_string()),
),
),
],
});
assert_eq!(ast, expected);
}
#[test]
fn test_parse_invalid_script() {
let script = "a = ; b = 2;".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let parser = Parser::new(tokens);
assert!(parser.parse().is_err());
}
#[test]
fn test_parse_empty() {
let tokens = Lexer::new("".to_string()).tokenize().unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
match result {
Node::Base(data) => assert!(data.children.is_empty()),
_ => panic!("expected base node"),
}
}
#[test]
fn test_handle_newline() {
let tokens = Lexer::new("\n\n\n".to_string()).tokenize().unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
match result {
Node::Base(data) => assert!(data.children.is_empty()),
_ => panic!("expected base node"),
}
}
#[test]
fn test_variable_assignment() {
let tokens = Lexer::new("a = 1;".to_string()).tokenize().unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
match result {
Node::Base(data) => assert_eq!(data.children.len(), 1),
_ => panic!("expected base node"),
}
}
#[test]
fn test_variable_assignment_with_new_lines() {
let tokens = Lexer::new("a = 1;".to_string()).tokenize().unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
match result {
Node::Base(data) => assert_eq!(data.children.len(), 1),
_ => panic!("expected base node"),
}
}
#[test]
fn test_boolean_expression_assignment() {
let tokens = Lexer::new("a = 1 < 2;".to_string()).tokenize().unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("a".to_string()),
Node::new_inferior_with_values(Node::new_constant(1.0), Node::new_constant(2.0)),
)],
});
assert_eq!(result, expected);
}
}
#[cfg(test)]
mod tests_for {
use super::*;
use crate::scripting::parsing::lexer::Lexer;
#[test]
fn test_range_expression() {
let script = "vals = range(1,3);".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let parser = Parser::new(tokens);
let ast = parser.parse().unwrap();
let mut range_node = Node::new_range();
range_node.add_child(Node::new_constant(1.0));
range_node.add_child(Node::new_constant(3.0));
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("vals".to_string()),
range_node,
)],
});
assert_eq!(ast, expected);
}
#[test]
fn test_for_each_range_loop() {
let script = "total = 0; for i in range(1,3) { total = total + i; }";
let tokens = Lexer::new(script.to_string()).tokenize().unwrap();
let parser = Parser::new(tokens);
let ast = parser.parse().unwrap();
let mut range_node = Node::new_range();
range_node.add_child(Node::new_constant(1.0));
range_node.add_child(Node::new_constant(3.0));
let body = vec![Node::new_asign_with_values(
Node::new_variable("total".to_string()),
Node::new_add_with_values(
Node::new_variable("total".to_string()),
Node::new_variable("i".to_string()),
),
)];
let expected = Node::Base(NodeData {
children: vec![
Node::new_asign_with_values(
Node::new_variable("total".to_string()),
Node::new_constant(0.0),
),
Node::new_for_each("i".to_string(), Box::new(range_node), body),
],
});
assert_eq!(ast, expected);
}
#[test]
fn test_for_each_list_loop() {
let script = "sum = 0; for x in [1,2,3] { sum = sum + x; }";
let tokens = Lexer::new(script.to_string()).tokenize().unwrap();
let parser = Parser::new(tokens);
let ast = parser.parse().unwrap();
let list_node = Node::new_list_with_values(vec![
Node::new_constant(1.0),
Node::new_constant(2.0),
Node::new_constant(3.0),
]);
let body = vec![Node::new_asign_with_values(
Node::new_variable("sum".to_string()),
Node::new_add_with_values(
Node::new_variable("sum".to_string()),
Node::new_variable("x".to_string()),
),
)];
let expected = Node::Base(NodeData {
children: vec![
Node::new_asign_with_values(
Node::new_variable("sum".to_string()),
Node::new_constant(0.0),
),
Node::new_for_each("x".to_string(), Box::new(list_node), body),
],
});
assert_eq!(ast, expected);
}
}
#[cfg(test)]
mod tests_if {
use super::*;
use crate::scripting::parsing::lexer::Lexer;
#[test]
fn test_if_statement() {
let tokens = Lexer::new(
"
if a == 1 {
b = 2;
}"
.to_string(),
)
.tokenize()
.unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::If(IfData {
children: vec![
Node::new_equal_with_values(
Node::new_variable("a".to_string()),
Node::new_constant(1.0),
),
Node::new_asign_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(2.0),
),
],
first_else: None,
affected_vars: Vec::new(),
})],
});
assert_eq!(result, expected);
}
#[test]
fn test_if_else_statement() {
let tokens = Lexer::new(
"
if a == 1 {
b = 2;
} else {
b = 3;
}
"
.to_string(),
)
.tokenize()
.unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::If(IfData {
children: vec![
Node::new_equal_with_values(
Node::new_variable("a".to_string()),
Node::new_constant(1.0),
),
Node::new_asign_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(2.0),
),
Node::new_asign_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(3.0),
),
],
first_else: Some(2),
affected_vars: Vec::new(),
})],
});
assert_eq!(result, expected);
}
#[test]
fn test_nested_if_else_statement() {
let tokens = Lexer::new(
"
if a == 1 {
if b == 2 {
c = 3;
}else {
c = 4;
}
} else {
c = 5;
}"
.to_string(),
)
.tokenize()
.unwrap();
let result = Parser::new(tokens).parse().unwrap();
let inner_if = Node::If(IfData {
children: vec![
Node::new_equal_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(2.0),
),
Node::new_asign_with_values(
Node::new_variable("c".to_string()),
Node::new_constant(3.0),
),
Node::new_asign_with_values(
Node::new_variable("c".to_string()),
Node::new_constant(4.0),
),
],
first_else: Some(2),
affected_vars: Vec::new(),
});
let expected = Node::Base(NodeData {
children: vec![Node::If(IfData {
children: vec![
Node::new_equal_with_values(
Node::new_variable("a".to_string()),
Node::new_constant(1.0),
),
inner_if,
Node::new_asign_with_values(
Node::new_variable("c".to_string()),
Node::new_constant(5.0),
),
],
first_else: Some(2),
affected_vars: Vec::new(),
})],
});
assert_eq!(result, expected);
}
#[test]
fn test_nested_if_else_statement_with_multiple_statements() {
let tokens = Lexer::new(
"
if a == 1 {
if b == 2 {
c = 3;
d = 4;
} else {
c = 5;
d = 6;
}
} else {
c = 7;
d = 8;
}"
.to_string(),
)
.tokenize()
.unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
let inner_if = Node::If(IfData {
children: vec![
Node::new_equal_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(2.0),
),
Node::new_asign_with_values(
Node::new_variable("c".to_string()),
Node::new_constant(3.0),
),
Node::new_asign_with_values(
Node::new_variable("d".to_string()),
Node::new_constant(4.0),
),
Node::new_asign_with_values(
Node::new_variable("c".to_string()),
Node::new_constant(5.0),
),
Node::new_asign_with_values(
Node::new_variable("d".to_string()),
Node::new_constant(6.0),
),
],
first_else: Some(3),
affected_vars: Vec::new(),
});
let expected = Node::Base(NodeData {
children: vec![Node::If(IfData {
children: vec![
Node::new_equal_with_values(
Node::new_variable("a".to_string()),
Node::new_constant(1.0),
),
inner_if,
Node::new_asign_with_values(
Node::new_variable("c".to_string()),
Node::new_constant(7.0),
),
Node::new_asign_with_values(
Node::new_variable("d".to_string()),
Node::new_constant(8.0),
),
],
first_else: Some(2),
affected_vars: Vec::new(),
})],
});
assert_eq!(result, expected);
}
#[test]
fn test_if_multiple_conditions() {
let tokens = Lexer::new(
"
if a == 1 and b == 2 {
c = 3;
}"
.to_string(),
)
.tokenize()
.unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::If(IfData {
children: vec![
Node::new_and_with_values(
Node::new_equal_with_values(
Node::new_variable("a".to_string()),
Node::new_constant(1.0),
),
Node::new_equal_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(2.0),
),
),
Node::new_asign_with_values(
Node::new_variable("c".to_string()),
Node::new_constant(3.0),
),
],
first_else: None,
affected_vars: Vec::new(),
})],
});
assert_eq!(result, expected);
let tokens = Lexer::new(
"
if a == 1 or b == 2 {
c = 3;
}"
.to_string(),
)
.tokenize();
let parser = Parser::new(tokens.unwrap());
let result = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::If(IfData {
children: vec![
Node::new_or_with_values(
Node::new_equal_with_values(
Node::new_variable("a".to_string()),
Node::new_constant(1.0),
),
Node::new_equal_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(2.0),
),
),
Node::new_asign_with_values(
Node::new_variable("c".to_string()),
Node::new_constant(3.0),
),
],
first_else: None,
affected_vars: Vec::new(),
})],
});
assert_eq!(result, expected);
}
#[test]
fn test_func_in_if_statement() {
let tokens = Lexer::new(
"
if a-1 == 1 {
b = 2;
}"
.to_string(),
)
.tokenize()
.unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::If(IfData {
children: vec![
Node::new_equal_with_values(
Node::new_subtract_with_values(
Node::new_variable("a".to_string()),
Node::new_constant(1.0),
),
Node::new_constant(1.0),
),
Node::new_asign_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(2.0),
),
],
first_else: None,
affected_vars: Vec::new(),
})],
});
assert_eq!(result, expected);
}
#[test]
fn test_if_new_variable() {
let tokens = Lexer::new(
"
x = 2;
if x == 1 {
z = 3;
w = 4;
}
"
.to_string(),
)
.tokenize()
.unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![
Node::new_asign_with_values(
Node::new_variable("x".to_string()),
Node::new_constant(2.0),
),
Node::If(IfData {
children: vec![
Node::new_equal_with_values(
Node::new_variable("x".to_string()),
Node::new_constant(1.0),
),
Node::new_asign_with_values(
Node::new_variable("z".to_string()),
Node::new_constant(3.0),
),
Node::new_asign_with_values(
Node::new_variable("w".to_string()),
Node::new_constant(4.0),
),
],
first_else: None,
affected_vars: Vec::new(),
}),
],
});
assert_eq!(result, expected);
}
#[test]
fn test_bool_variables_with_if() {
let tokens = Lexer::new(
"
x = true;
y = false;
if x == true {
z = 3;
}
"
.to_string(),
)
.tokenize()
.unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![
Node::new_asign_with_values(Node::new_variable("x".to_string()), Node::True),
Node::new_asign_with_values(Node::new_variable("y".to_string()), Node::False),
Node::If(IfData {
children: vec![
Node::new_equal_with_values(
Node::new_variable("x".to_string()),
Node::True,
),
Node::new_asign_with_values(
Node::new_variable("z".to_string()),
Node::new_constant(3.0),
),
],
first_else: None,
affected_vars: Vec::new(),
}),
],
});
assert_eq!(result, expected);
}
#[test]
fn test_if_sup() {
let s1 = "
if a > 1 {
b = 2;
}
"
.to_string();
let tokens = Lexer::new(s1).tokenize().unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::If(IfData {
children: vec![
Node::new_superior_with_values(
Node::new_variable("a".to_string()),
Node::new_constant(1.0),
),
Node::new_asign_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(2.0),
),
],
first_else: None,
affected_vars: Vec::new(),
})],
});
assert_eq!(result, expected);
}
#[test]
fn test_single_if() {
let script = "
x = true;
if x {
b = 2;
}
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![
Node::new_asign_with_values(Node::new_variable("x".to_string()), Node::True),
Node::If(IfData {
children: vec![
Node::new_variable("x".to_string()),
Node::new_asign_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(2.0),
),
],
first_else: None,
affected_vars: Vec::new(),
}),
],
});
assert_eq!(result, expected);
}
#[test]
fn test_if_inf() {
let s1 = "
if a < 1 {
b = 2;
}
"
.to_string();
let tokens = Lexer::new(s1).tokenize().unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::If(IfData {
children: vec![
Node::new_inferior_with_values(
Node::new_variable("a".to_string()),
Node::new_constant(1.0),
),
Node::new_asign_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(2.0),
),
],
first_else: None,
affected_vars: Vec::new(),
})],
});
assert_eq!(result, expected);
}
#[test]
fn test_if_inf_eq() {
let s1 = "
if a <= 1 {
b = 2;
}
"
.to_string();
let tokens = Lexer::new(s1).tokenize().unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::If(IfData {
children: vec![
Node::new_inferior_or_equal_with_values(
Node::new_variable("a".to_string()),
Node::new_constant(1.0),
),
Node::new_asign_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(2.0),
),
],
first_else: None,
affected_vars: Vec::new(),
})],
});
assert_eq!(result, expected);
}
#[test]
fn test_if_sup_eq() {
let s1 = "
if a >= 1 {
b = 2;
}
"
.to_string();
let tokens = Lexer::new(s1).tokenize().unwrap();
let parser = Parser::new(tokens);
let result = parser.parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::If(IfData {
children: vec![
Node::new_superior_or_equal_with_values(
Node::new_variable("a".to_string()),
Node::new_constant(1.0),
),
Node::new_asign_with_values(
Node::new_variable("b".to_string()),
Node::new_constant(2.0),
),
],
first_else: None,
affected_vars: Vec::new(),
})],
});
assert_eq!(result, expected);
}
#[test]
fn test_multiple_bool_vars() {
let script = "
x = true;
y = false;
z = x and y;
w = x or y;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![
Node::new_asign_with_values(Node::new_variable("x".to_string()), Node::True),
Node::new_asign_with_values(Node::new_variable("y".to_string()), Node::False),
Node::new_asign_with_values(
Node::new_variable("z".to_string()),
Node::new_and_with_values(
Node::new_variable("x".to_string()),
Node::new_variable("y".to_string()),
),
),
Node::new_asign_with_values(
Node::new_variable("w".to_string()),
Node::new_or_with_values(
Node::new_variable("x".to_string()),
Node::new_variable("y".to_string()),
),
),
],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_cvg_function() {
let script = "
x = cvg(\"2020-01-01\", \"2020-06-01\", \"Actual360\");
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let mut cvg_node = Node::new_cvg();
cvg_node.add_child(Node::new_string("2020-01-01".to_string()));
cvg_node.add_child(Node::new_string("2020-06-01".to_string()));
cvg_node.add_child(Node::new_string("Actual360".to_string()));
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("x".to_string()),
cvg_node,
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_max_function() {
let script = "
z = max(1, 2);
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let mut max_node = Node::new_max();
max_node.add_child(Node::new_constant(1.0));
max_node.add_child(Node::new_constant(2.0));
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("z".to_string()),
max_node,
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_fif_function() {
let script = "
z = fif(0.0, 1, 0, 1);
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let mut fif_node = Node::new_fif();
fif_node.add_child(Node::new_constant(0.0));
fif_node.add_child(Node::new_constant(1.0));
fif_node.add_child(Node::new_constant(0.0));
fif_node.add_child(Node::new_constant(1.0));
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("z".to_string()),
fif_node,
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_string_variable() {
let script = "
x = \"hello\";
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("x".to_string()),
Node::new_string("hello".to_string()),
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_spot_function() {
let script = "
x = Spot(\"USD\", \"EUR\");
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("x".to_string()),
Node::new_spot(
Currency::try_from("USD".to_string()).unwrap(),
Currency::try_from("EUR".to_string()).unwrap(),
None,
),
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_spot_function_with_date() {
let script = "
x = Spot(\"USD\", \"EUR\", \"2025-06-01\");
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("x".to_string()),
Node::new_spot(
Currency::try_from("USD".to_string()).unwrap(),
Currency::try_from("EUR".to_string()).unwrap(),
Some(Date::from_str("2025-06-01", "%Y-%m-%d").unwrap()),
),
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_df_function() {
let script = "x = Df(\"2025-06-01\");".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("x".to_string()),
Node::new_df(Date::from_str("2025-06-01", "%Y-%m-%d").unwrap(), None),
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_df_function_with_curve() {
let script = "x = Df(\"2025-06-01\", \"curve\");".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("x".to_string()),
Node::new_df(
Date::from_str("2025-06-01", "%Y-%m-%d").unwrap(),
Some("curve".to_string()),
),
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_rate_index_function() {
let script = "
x = RateIndex(\"0\", \"2024-01-01\", \"2024-02-01\");
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("x".to_string()),
Node::new_rate_index(
"0".to_string(),
Date::from_str("2024-01-01", "%Y-%m-%d").unwrap(),
Date::from_str("2024-02-01", "%Y-%m-%d").unwrap(),
),
)],
});
assert_eq!(nodes, expected);
}
}
#[cfg(test)]
mod test_reserved_keywords {
use super::*;
#[test]
fn test_if_reserved() {
let script = "
if = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_else_reserved() {
let script = "
else = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_and_reserved() {
let script = "
and = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_or_reserved() {
let script = "
or = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_true_reserved() {
let script = "
true = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_false_reserved() {
let script = "
false = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_max_reserved() {
let script = "
max = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_min_reserved() {
let script = "
min = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_pow_reserved() {
let script = "
pow = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_ln_reserved() {
let script = "
ln = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_exp_reserved() {
let script = "
exp = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_spot_reserved() {
let script = "
Spot = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_rate_index_reserved() {
let script = "
RateIndex = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_pays_reserved() {
let script = "
pays = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_cvg_reserved() {
let script = "
cvg = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
#[test]
fn test_fif_reserved() {
let script = "
fif = 1;
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_err());
}
}
#[cfg(test)]
mod test_function_args {
use super::*;
use crate::scripting::nodes::node::Node;
#[test]
fn test_function_args() {
let script = "
x = max(1, 2);
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let mut max = Node::new_max();
max.add_child(Node::new_constant(1.0));
max.add_child(Node::new_constant(2.0));
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("x".to_string()),
max,
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_function_args_multiple() {
let script = "
x = max(1, 2, 3, 4);
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let mut max = Node::new_max();
max.add_child(Node::new_constant(1.0));
max.add_child(Node::new_constant(2.0));
max.add_child(Node::new_constant(3.0));
max.add_child(Node::new_constant(4.0));
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("x".to_string()),
max,
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_function_args_nested() {
let script = "
x = max(max(1, 2), 3);
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let mut inner_max = Node::new_max();
inner_max.add_child(Node::new_constant(1.0));
inner_max.add_child(Node::new_constant(2.0));
let mut outer_max = Node::new_max();
outer_max.add_child(inner_max);
outer_max.add_child(Node::new_constant(3.0));
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("x".to_string()),
outer_max,
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_function_args_nested_multiple() {
let script = "
x = max(max(1, 2), max(3, 4));
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let mut inner_max1 = Node::new_max();
inner_max1.add_child(Node::new_constant(1.0));
inner_max1.add_child(Node::new_constant(2.0));
let mut inner_max2 = Node::new_max();
inner_max2.add_child(Node::new_constant(3.0));
inner_max2.add_child(Node::new_constant(4.0));
let mut outer_max = Node::new_max();
outer_max.add_child(inner_max1);
outer_max.add_child(inner_max2);
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("x".to_string()),
outer_max,
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_function_args_nested_multiple_with_variables() {
let script = "
x = max(max(a, b), max(c, d));
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let mut inner_max1 = Node::new_max();
inner_max1.add_child(Node::new_variable("a".to_string()));
inner_max1.add_child(Node::new_variable("b".to_string()));
let mut inner_max2 = Node::new_max();
inner_max2.add_child(Node::new_variable("c".to_string()));
inner_max2.add_child(Node::new_variable("d".to_string()));
let mut outer_max = Node::new_max();
outer_max.add_child(inner_max1);
outer_max.add_child(inner_max2);
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("x".to_string()),
outer_max,
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_no_assigmnet() {
let script = "
max(1, 2);
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse();
assert!(nodes.is_ok());
}
}
#[cfg(test)]
mod test_pays_expression {
use super::*;
use crate::currencies::currency::Currency;
#[test]
fn test_pays_as_expression() {
let script = "
call = pays max(Spot(\"CLP\", \"USD\") - 900.0, 0);
"
.to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let mut max_node = Node::new_max();
max_node.add_child(Node::new_subtract_with_values(
Node::new_spot(
Currency::try_from("CLP".to_string()).unwrap(),
Currency::try_from("USD".to_string()).unwrap(),
None,
),
Node::new_constant(900.0),
));
max_node.add_child(Node::new_constant(0.0));
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("call".to_string()),
Node::Pays(PaysData {
children: vec![max_node],
date: None,
currency: None,
id: None,
df_id: None,
spot_id: None,
}),
)],
});
assert_eq!(nodes, expected);
}
}
#[cfg(test)]
mod test_pays_assignment {
use super::*;
#[test]
fn test_variable_pays_assignment() {
let script = "prd pays 100;".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("prd".to_string()),
Node::new_add_with_values(
Node::new_variable("prd".to_string()),
Node::Pays(PaysData {
children: vec![Node::new_constant(100.0)],
date: None,
currency: None,
id: None,
df_id: None,
spot_id: None,
}),
),
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_variable_pays_assignment_with_date() {
let script = "prd pays 100 on \"2025-06-30\";".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("prd".to_string()),
Node::new_add_with_values(
Node::new_variable("prd".to_string()),
Node::Pays(PaysData {
children: vec![Node::new_constant(100.0)],
date: Some(Date::from_str("2025-06-30", "%Y-%m-%d").unwrap()),
currency: None,
id: None,
df_id: None,
spot_id: None,
}),
),
)],
});
assert_eq!(nodes, expected);
}
#[test]
fn test_variable_pays_assignment_with_date_and_currency() {
let script = "prd pays 100 on \"2025-06-30\" in \"EUR\";".to_string();
let tokens = Lexer::new(script).tokenize().unwrap();
let nodes = Parser::new(tokens).parse().unwrap();
let expected = Node::Base(NodeData {
children: vec![Node::new_asign_with_values(
Node::new_variable("prd".to_string()),
Node::new_add_with_values(
Node::new_variable("prd".to_string()),
Node::Pays(PaysData {
children: vec![Node::new_constant(100.0)],
date: Some(Date::from_str("2025-06-30", "%Y-%m-%d").unwrap()),
currency: Some(Currency::try_from("EUR".to_string()).unwrap()),
id: None,
df_id: None,
spot_id: None,
}),
),
)],
});
assert_eq!(nodes, expected);
}
}