diff --git a/src/cli/cli_interface.rs b/src/cli/cli_interface.rs index ab9ea66a..92fd7dca 100644 --- a/src/cli/cli_interface.rs +++ b/src/cli/cli_interface.rs @@ -1,6 +1,7 @@ use heraclitus_compiler::prelude::*; use colored::Colorize; use crate::modules::block; +use crate::translate::check_all_blocks; use crate::utils::{ParserMetadata, TranslateMetadata}; use crate::translate::module::TranslateModule; use crate::rules; @@ -125,15 +126,32 @@ impl CLI { let mut cc = Compiler::new("Amber", rules); let mut block = block::Block::new(); cc.load(code.clone()); - if let Ok(tokens) = cc.tokenize() { - let mut meta = ParserMetadata::new(tokens, path, Some(code)); - if let Ok(()) = block.parse(&mut meta) { - let mut meta = TranslateMetadata::new(); - return block.translate(&mut meta); + match cc.tokenize() { + Ok(tokens) => { + let mut meta = ParserMetadata::new(tokens, path, Some(code)); + check_all_blocks(&mut meta); + if let Ok(()) = block.parse(&mut meta) { + let mut meta = TranslateMetadata::new(&meta); + return block.translate(&mut meta); + } + "[parsing err]".to_string() + }, + Err((err_type, details)) => { + let error_message = match err_type { + LexerErrorType::Singleline => { + format!("Singleline {} not closed", details.data.as_ref().unwrap()) + }, + LexerErrorType::Unclosed => { + format!("Unclosed {}", details.data.as_ref().unwrap()) + } + }; + let pos = details.get_pos_by_code(code.clone()); + Logger::new_err_at_position(path, Some(code), pos) + .attach_message(error_message) + .show().exit(); + "[tokenizing err]".to_string() } - return "[parsing err]".to_string() } - "[lexing err]".to_string() } fn execute(&self, code: String) { diff --git a/src/cli/tests.rs b/src/cli/tests.rs index 855db091..f3046749 100644 --- a/src/cli/tests.rs +++ b/src/cli/tests.rs @@ -388,4 +388,42 @@ fn modulo_shorthand() { $echo {a}$ "; assert_eq!(cli.test_eval(code).trim(), "1"); +} + +#[test] +fn function() { + let cli = CLI::new(); + let code = " + fun test() { + $echo Hello World$ + } + test() + "; + assert_eq!(cli.test_eval(code).trim(), "Hello World"); +} + +#[test] +fn function_with_args() { + let cli = CLI::new(); + let code = " + fun test(a, b) { + $echo {a}$ + $echo {b}$ + } + test('Hello', 'World') + "; + assert_eq!(cli.test_eval(code).trim(), "Hello\nWorld"); +} + +#[test] +fn function_with_args_different_types() { + let cli = CLI::new(); + let code = " + fun test(a, b) { + $echo {a + b}$ + } + test('Hello', 'World') + test(11, 42) + "; + assert_eq!(cli.test_eval(code).trim(), "HelloWorld\n53"); } \ No newline at end of file diff --git a/src/modules/block.rs b/src/modules/block.rs index 022d0371..1adcf8fc 100644 --- a/src/modules/block.rs +++ b/src/modules/block.rs @@ -1,9 +1,9 @@ use heraclitus_compiler::prelude::*; use crate::{utils::{metadata::ParserMetadata, error::get_error_logger, TranslateMetadata}}; use crate::translate::module::TranslateModule; -use super::statement::st::Statement; +use super::statement::stmt::Statement; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Block { statements: Vec } @@ -35,7 +35,7 @@ impl SyntaxModule for Block { } fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { - meta.var_mem.push_scope(); + meta.mem.push_scope(); while let Some(token) = meta.get_current_token() { // Handle the end of line or command if ["\n", ";"].contains(&token.word.as_str()) { @@ -57,15 +57,23 @@ impl SyntaxModule for Block { } self.statements.push(statemant); } - meta.var_mem.pop_scope(); + meta.mem.pop_scope(); Ok(()) } } impl TranslateModule for Block { fn translate(&self, meta: &mut TranslateMetadata) -> String { - self.statements.iter() - .map(|module| module.translate(meta)) - .collect::>().join(";\n") + meta.increase_indent(); + let result = if self.is_empty() { + ":".to_string() + } + else { + self.statements.iter() + .map(|module| meta.gen_indent() + &module.translate(meta)) + .collect::>().join(";\n") + }; + meta.decrease_indent(); + result } } \ No newline at end of file diff --git a/src/modules/command/expr.rs b/src/modules/command/expr.rs index 7eb648f5..03420a0e 100644 --- a/src/modules/command/expr.rs +++ b/src/modules/command/expr.rs @@ -5,7 +5,7 @@ use crate::translate::module::TranslateModule; use crate::modules::expression::literal::{parse_interpolated_region, translate_interpolated_region}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CommandExpr { strings: Vec, interps: Vec diff --git a/src/modules/command/statement.rs b/src/modules/command/statement.rs index 979acb5e..440ab691 100644 --- a/src/modules/command/statement.rs +++ b/src/modules/command/statement.rs @@ -5,7 +5,7 @@ use crate::translate::module::TranslateModule; use crate::modules::expression::literal::{parse_interpolated_region, translate_interpolated_region}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct CommandStatement { strings: Vec, interps: Vec diff --git a/src/modules/conditions/ifchain.rs b/src/modules/condition/ifchain.rs similarity index 97% rename from src/modules/conditions/ifchain.rs rename to src/modules/condition/ifchain.rs index e4ec99d3..ad5a2ec9 100644 --- a/src/modules/conditions/ifchain.rs +++ b/src/modules/condition/ifchain.rs @@ -3,9 +3,9 @@ use crate::modules::expression::expr::Expr; use crate::translate::module::TranslateModule; use crate::utils::metadata::{ParserMetadata, TranslateMetadata}; use crate::modules::block::Block; -use crate::modules::statement::st::Statement; +use crate::modules::statement::stmt::Statement; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct IfChain { cond_blocks: Vec<(Expr, Block)>, false_block: Option> diff --git a/src/modules/conditions/ifcond.rs b/src/modules/condition/ifcond.rs similarity index 88% rename from src/modules/conditions/ifcond.rs rename to src/modules/condition/ifcond.rs index 8376a9bf..bcca07ef 100644 --- a/src/modules/conditions/ifcond.rs +++ b/src/modules/condition/ifcond.rs @@ -4,9 +4,9 @@ use crate::translate::module::TranslateModule; use crate::utils::error::get_warn_logger; use crate::utils::metadata::{ParserMetadata, TranslateMetadata}; use crate::modules::block::Block; -use crate::modules::statement::st::{Statement, StatementType}; +use crate::modules::statement::stmt::{Statement, StatementType}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct IfCondition { expr: Box, true_block: Box, @@ -14,14 +14,6 @@ pub struct IfCondition { } impl IfCondition { - fn translate_block(&self, meta: &mut TranslateMetadata, block: &Block) -> String { - if block.is_empty() { - ":\n".to_string() - } else { - block.translate(meta) - } - } - fn prevent_not_using_if_chain(&self, meta: &mut ParserMetadata, statement: &Statement, tok: Option) { let is_not_if_chain = matches!(statement.value.as_ref().unwrap(), StatementType::IfCondition(_) | StatementType::IfChain(_)); if is_not_if_chain { @@ -90,10 +82,10 @@ impl TranslateModule for IfCondition { fn translate(&self, meta: &mut TranslateMetadata) -> String { let mut result = vec![]; result.push(format!("if [ {} != 0 ]; then", self.expr.translate(meta))); - result.push(self.translate_block(meta, &self.true_block)); + result.push(self.true_block.translate(meta)); if let Some(false_block) = &self.false_block { result.push("else".to_string()); - result.push(self.translate_block(meta, false_block)); + result.push(false_block.translate(meta)); } result.push("fi".to_string()); result.join("\n") diff --git a/src/modules/conditions/mod.rs b/src/modules/condition/mod.rs similarity index 100% rename from src/modules/conditions/mod.rs rename to src/modules/condition/mod.rs diff --git a/src/modules/conditions/ternary.rs b/src/modules/condition/ternary.rs similarity index 98% rename from src/modules/conditions/ternary.rs rename to src/modules/condition/ternary.rs index 2137c0a7..9c425ccf 100644 --- a/src/modules/conditions/ternary.rs +++ b/src/modules/condition/ternary.rs @@ -6,7 +6,7 @@ use crate::translate::module::TranslateModule; use crate::utils::error::get_error_logger; use crate::utils::metadata::{ParserMetadata, TranslateMetadata}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Ternary { cond: Box, true_expr: Box, diff --git a/src/modules/expression/binop/add.rs b/src/modules/expression/binop/add.rs index 75c21a14..d998e411 100644 --- a/src/modules/expression/binop/add.rs +++ b/src/modules/expression/binop/add.rs @@ -5,7 +5,7 @@ use crate::translate::module::TranslateModule; use super::{super::expr::Expr, parse_left_expr, expression_arms_of_type}; use crate::modules::{Type, Typed}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Add { left: Box, right: Box, @@ -36,7 +36,9 @@ impl SyntaxModule for Add { syntax(meta, &mut *self.right)?; // If left and right are not of type Number let error = "Add operation can only add numbers or text"; - self.kind = expression_arms_of_type(meta, &*self.left, &*self.right, &[Type::Num, Type::Text], tok, error); + let l_type = self.left.get_type(); + let r_type = self.right.get_type(); + self.kind = expression_arms_of_type(meta, &l_type, &r_type, &[Type::Num, Type::Text], tok, error); Ok(()) } } diff --git a/src/modules/expression/binop/and.rs b/src/modules/expression/binop/and.rs index ece06cb3..c16b5c04 100644 --- a/src/modules/expression/binop/and.rs +++ b/src/modules/expression/binop/and.rs @@ -6,7 +6,7 @@ use super::{super::expr::Expr, parse_left_expr, expression_arms_of_same_type}; use crate::modules::{Type, Typed}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct And { left: Box, right: Box diff --git a/src/modules/expression/binop/div.rs b/src/modules/expression/binop/div.rs index cbd098d7..368b4239 100644 --- a/src/modules/expression/binop/div.rs +++ b/src/modules/expression/binop/div.rs @@ -4,7 +4,7 @@ use super::{super::expr::Expr, parse_left_expr, expression_arms_of_type}; use crate::modules::{Type, Typed}; use crate::translate::module::TranslateModule; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Div { left: Box, right: Box @@ -32,7 +32,9 @@ impl SyntaxModule for Div { token(meta, "/")?; syntax(meta, &mut *self.right)?; let error = "Divide operation can only divide numbers"; - expression_arms_of_type(meta, &*self.left, &*self.right, &[Type::Num], tok, error); + let l_type = self.left.get_type(); + let r_type = self.right.get_type(); + expression_arms_of_type(meta, &l_type, &r_type, &[Type::Num], tok, error); Ok(()) } } diff --git a/src/modules/expression/binop/eq.rs b/src/modules/expression/binop/eq.rs index f7b06ae7..1a6387af 100644 --- a/src/modules/expression/binop/eq.rs +++ b/src/modules/expression/binop/eq.rs @@ -6,7 +6,7 @@ use super::strip_text_quotes; use super::{super::expr::Expr, parse_left_expr, expression_arms_of_same_type}; use crate::modules::{Type, Typed}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Eq { left: Box, right: Box diff --git a/src/modules/expression/binop/ge.rs b/src/modules/expression/binop/ge.rs index 75e6b3c9..d62de498 100644 --- a/src/modules/expression/binop/ge.rs +++ b/src/modules/expression/binop/ge.rs @@ -5,7 +5,7 @@ use crate::translate::module::TranslateModule; use super::{super::expr::Expr, parse_left_expr, expression_arms_of_type}; use crate::modules::{Type, Typed}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Ge { left: Box, right: Box @@ -33,7 +33,9 @@ impl SyntaxModule for Ge { token(meta, ">=")?; syntax(meta, &mut *self.right)?; let error = "Cannot compare two values of different types"; - expression_arms_of_type(meta, &*self.left, &*self.right, &[Type::Num], tok, error); + let l_type = self.left.get_type(); + let r_type = self.right.get_type(); + expression_arms_of_type(meta, &l_type, &r_type, &[Type::Num], tok, error); Ok(()) } } diff --git a/src/modules/expression/binop/gt.rs b/src/modules/expression/binop/gt.rs index 80d5b00e..a1c65ea2 100644 --- a/src/modules/expression/binop/gt.rs +++ b/src/modules/expression/binop/gt.rs @@ -5,7 +5,7 @@ use crate::translate::module::TranslateModule; use super::{super::expr::Expr, parse_left_expr, expression_arms_of_type}; use crate::modules::{Type, Typed}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Gt { left: Box, right: Box @@ -33,7 +33,9 @@ impl SyntaxModule for Gt { token(meta, ">")?; syntax(meta, &mut *self.right)?; let error = "Cannot compare two values of different types"; - expression_arms_of_type(meta, &*self.left, &*self.right, &[Type::Num], tok, error); + let l_type = self.left.get_type(); + let r_type = self.right.get_type(); + expression_arms_of_type(meta, &l_type, &r_type, &[Type::Num], tok, error); Ok(()) } } diff --git a/src/modules/expression/binop/le.rs b/src/modules/expression/binop/le.rs index 575baa13..3ccaeaf8 100644 --- a/src/modules/expression/binop/le.rs +++ b/src/modules/expression/binop/le.rs @@ -5,7 +5,7 @@ use crate::translate::module::TranslateModule; use super::{super::expr::Expr, parse_left_expr, expression_arms_of_type}; use crate::modules::{Type, Typed}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Le { left: Box, right: Box @@ -33,7 +33,9 @@ impl SyntaxModule for Le { token(meta, "<=")?; syntax(meta, &mut *self.right)?; let error = "Cannot compare two values of different types"; - expression_arms_of_type(meta, &*self.left, &*self.right, &[Type::Num], tok, error); + let l_type = self.left.get_type(); + let r_type = self.right.get_type(); + expression_arms_of_type(meta, &l_type, &r_type, &[Type::Num], tok, error); Ok(()) } } diff --git a/src/modules/expression/binop/lt.rs b/src/modules/expression/binop/lt.rs index 66182489..f0c33326 100644 --- a/src/modules/expression/binop/lt.rs +++ b/src/modules/expression/binop/lt.rs @@ -5,7 +5,7 @@ use crate::translate::module::TranslateModule; use super::{super::expr::Expr, parse_left_expr, expression_arms_of_type}; use crate::modules::{Type, Typed}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Lt { left: Box, right: Box @@ -33,7 +33,9 @@ impl SyntaxModule for Lt { token(meta, "<")?; syntax(meta, &mut *self.right)?; let error = "Cannot compare two values of different types"; - expression_arms_of_type(meta, &*self.left, &*self.right, &[Type::Num], tok, error); + let l_type = self.left.get_type(); + let r_type = self.right.get_type(); + expression_arms_of_type(meta, &l_type, &r_type, &[Type::Num], tok, error); Ok(()) } } diff --git a/src/modules/expression/binop/mod.rs b/src/modules/expression/binop/mod.rs index ea7b764f..9d7924d6 100644 --- a/src/modules/expression/binop/mod.rs +++ b/src/modules/expression/binop/mod.rs @@ -16,18 +16,14 @@ pub mod le; pub mod eq; pub mod neq; -pub fn expression_arms_of_type(meta: &mut ParserMetadata, left: &U, right: &V, kinds: &[Type], tok_pos: Option, message: &str) -> Type -where - U: Typed, - V: Typed, -{ - if kinds.iter().all(|kind | ![left.get_type(), right.get_type()].iter().all(|item| item == kind)) { +pub fn expression_arms_of_type(meta: &mut ParserMetadata, left: &Type, right: &Type, kinds: &[Type], tok_pos: Option, message: &str) -> Type { + if kinds.iter().all(|kind | ![left, right].iter().all(|item| **item == *kind)) { get_error_logger(meta, ErrorDetails::from_token_option(tok_pos)) .attach_message(message) .show() .exit() } - left.get_type() + left.clone() } pub fn expression_arms_of_same_type(meta: &mut ParserMetadata, left: &Expr, right: &Expr, tok_pos: Option, message: &str) { diff --git a/src/modules/expression/binop/modulo.rs b/src/modules/expression/binop/modulo.rs index ac1317f3..f0f9e725 100644 --- a/src/modules/expression/binop/modulo.rs +++ b/src/modules/expression/binop/modulo.rs @@ -5,7 +5,7 @@ use crate::translate::module::TranslateModule; use super::{super::expr::Expr, parse_left_expr, expression_arms_of_type}; use crate::modules::{Type, Typed}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Modulo { left: Box, right: Box @@ -33,7 +33,9 @@ impl SyntaxModule for Modulo { token(meta, "%")?; syntax(meta, &mut *self.right)?; let error = "Modulo operation can only be applied to numbers"; - expression_arms_of_type(meta, &*self.left, &*self.right, &[Type::Num], tok, error); + let l_type = self.left.get_type(); + let r_type = self.right.get_type(); + expression_arms_of_type(meta, &l_type, &r_type, &[Type::Num], tok, error); Ok(()) } } diff --git a/src/modules/expression/binop/mul.rs b/src/modules/expression/binop/mul.rs index 565ea94d..43a68671 100644 --- a/src/modules/expression/binop/mul.rs +++ b/src/modules/expression/binop/mul.rs @@ -5,7 +5,7 @@ use crate::translate::module::TranslateModule; use super::{super::expr::Expr, parse_left_expr, expression_arms_of_type}; use crate::modules::{Type, Typed}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Mul { left: Box, right: Box @@ -33,7 +33,9 @@ impl SyntaxModule for Mul { token(meta, "*")?; syntax(meta, &mut *self.right)?; let error = "Multiply operation can only multiply numbers"; - expression_arms_of_type(meta, &*self.left, &*self.right, &[Type::Num], tok, error); + let l_type = self.left.get_type(); + let r_type = self.right.get_type(); + expression_arms_of_type(meta, &l_type, &r_type, &[Type::Num], tok, error); Ok(()) } } diff --git a/src/modules/expression/binop/neq.rs b/src/modules/expression/binop/neq.rs index 275950cb..56c59139 100644 --- a/src/modules/expression/binop/neq.rs +++ b/src/modules/expression/binop/neq.rs @@ -6,7 +6,7 @@ use super::strip_text_quotes; use super::{super::expr::Expr, parse_left_expr, expression_arms_of_same_type}; use crate::modules::{Type, Typed}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Neq { left: Box, right: Box diff --git a/src/modules/expression/binop/or.rs b/src/modules/expression/binop/or.rs index 4ed73953..618246dc 100644 --- a/src/modules/expression/binop/or.rs +++ b/src/modules/expression/binop/or.rs @@ -5,7 +5,7 @@ use crate::translate::module::TranslateModule; use super::{super::expr::Expr, parse_left_expr, expression_arms_of_same_type}; use crate::modules::{Type, Typed}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Or { left: Box, right: Box diff --git a/src/modules/expression/binop/sub.rs b/src/modules/expression/binop/sub.rs index 27fa7d47..62e5eea1 100644 --- a/src/modules/expression/binop/sub.rs +++ b/src/modules/expression/binop/sub.rs @@ -5,7 +5,7 @@ use crate::translate::module::TranslateModule; use super::{super::expr::Expr, parse_left_expr, expression_arms_of_type}; use crate::modules::{Type, Typed}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Sub { left: Box, right: Box @@ -33,7 +33,9 @@ impl SyntaxModule for Sub { token(meta, "-")?; syntax(meta, &mut *self.right)?; let error = "Substract operation can only substract numbers"; - expression_arms_of_type(meta, &*self.left, &*self.right, &[Type::Num], tok, error); + let l_type = self.left.get_type(); + let r_type = self.right.get_type(); + expression_arms_of_type(meta, &l_type, &r_type, &[Type::Num], tok, error); Ok(()) } } diff --git a/src/modules/expression/expr.rs b/src/modules/expression/expr.rs index 663fd75a..1ead960a 100644 --- a/src/modules/expression/expr.rs +++ b/src/modules/expression/expr.rs @@ -29,10 +29,11 @@ use super::unop::{ use super::parenthesis::Parenthesis; use crate::modules::variable::get::VariableGet; use crate::modules::command::expr::CommandExpr; -use crate::modules::conditions::ternary::Ternary; +use crate::modules::condition::ternary::Ternary; +use crate::modules::function::invocation::FunctionInvocation; use crate::handle_types; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ExprType { Bool(Bool), Number(Number), @@ -54,10 +55,11 @@ pub enum ExprType { Eq(Eq), Neq(Neq), Not(Not), - Ternary(Ternary) + Ternary(Ternary), + FunctionInvocation(FunctionInvocation) } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Expr { value: Option, can_fail: bool, @@ -82,6 +84,9 @@ impl Expr { Add, Sub, Mul, Div, Modulo, // Literals Parenthesis, CommandExpr, Bool, Number, Text, + // Function invocation + FunctionInvocation, + // Variable access VariableGet ]); diff --git a/src/modules/expression/literal/bool.rs b/src/modules/expression/literal/bool.rs index 0e424780..8a9ea333 100644 --- a/src/modules/expression/literal/bool.rs +++ b/src/modules/expression/literal/bool.rs @@ -3,7 +3,7 @@ use crate::{utils::metadata::ParserMetadata, modules::{Type, Typed}}; use crate::translate::module::TranslateModule; use crate::utils::TranslateMetadata; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Bool { value: bool } diff --git a/src/modules/expression/literal/number.rs b/src/modules/expression/literal/number.rs index 380e5f47..93ec3f3e 100644 --- a/src/modules/expression/literal/number.rs +++ b/src/modules/expression/literal/number.rs @@ -2,7 +2,7 @@ use heraclitus_compiler::prelude::*; use crate::{utils::metadata::{ParserMetadata, TranslateMetadata}, modules::{Typed, Type}}; use crate::translate::module::TranslateModule; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Number { value: String } diff --git a/src/modules/expression/literal/text.rs b/src/modules/expression/literal/text.rs index c723ce7e..f414a886 100644 --- a/src/modules/expression/literal/text.rs +++ b/src/modules/expression/literal/text.rs @@ -5,7 +5,7 @@ use crate::modules::expression::expr::Expr; use super::{parse_interpolated_region, translate_interpolated_region}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Text { strings: Vec, interps: Vec diff --git a/src/modules/expression/parenthesis.rs b/src/modules/expression/parenthesis.rs index c8420fbb..679c06d6 100644 --- a/src/modules/expression/parenthesis.rs +++ b/src/modules/expression/parenthesis.rs @@ -3,7 +3,7 @@ use crate::{utils::metadata::ParserMetadata, modules::{Type, Typed}}; use crate::translate::module::TranslateModule; use super::expr::Expr; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Parenthesis { value: Box, kind: Type diff --git a/src/modules/expression/unop/not.rs b/src/modules/expression/unop/not.rs index 83dfcaba..11b76c24 100644 --- a/src/modules/expression/unop/not.rs +++ b/src/modules/expression/unop/not.rs @@ -2,7 +2,7 @@ use heraclitus_compiler::prelude::*; use crate::{utils::{metadata::ParserMetadata, TranslateMetadata}, modules::{Type, Typed}, translate::{module::TranslateModule, compute::{translate_computation, ArithOp}}}; use super::super::expr::Expr; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Not { expr: Box, kind: Type diff --git a/src/modules/function/declaration.rs b/src/modules/function/declaration.rs new file mode 100644 index 00000000..b195e895 --- /dev/null +++ b/src/modules/function/declaration.rs @@ -0,0 +1,107 @@ +use heraclitus_compiler::prelude::*; +use crate::modules::Type; +use crate::modules::variable::variable_name_extensions; +use crate::utils::error::get_error_logger; +use crate::utils::metadata::{ParserMetadata, TranslateMetadata}; +use crate::translate::module::TranslateModule; +use crate::modules::block::Block; +use crate::context; + +use super::declaration_utils::*; + +#[derive(Debug, Clone)] +pub struct FunctionDeclaration { + pub name: String, + pub args: Vec<(String, Type)>, + pub returns: Type, + pub body: Block, + pub id: usize +} + +impl FunctionDeclaration { + fn set_args_as_variables(&self, meta: &mut TranslateMetadata) -> String { + meta.increase_indent(); + let mut result = vec![]; + for (index, (name, _kind)) in self.args.clone().iter().enumerate() { + let indent = meta.gen_indent(); + result.push(format!("{indent}{name}=${}", index + 1)); + } + meta.decrease_indent(); + result.join("\n") + } +} + +impl SyntaxModule for FunctionDeclaration { + syntax_name!("Function Declaration"); + + fn new() -> Self { + FunctionDeclaration { + name: String::new(), + args: vec![], + returns: Type::Generic, + body: Block::new(), + id: 0 + } + } + + fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { + token(meta, "fun")?; + // Get the function name + let tok = meta.get_current_token(); + self.name = variable(meta, variable_name_extensions())?; + handle_existing_function(meta, tok.clone()); + context!({ + // Get the arguments + token(meta, "(")?; + loop { + if token(meta, ")").is_ok() { + break + } + let name = variable(meta, variable_name_extensions())?; + self.args.push((name, Type::Generic)); + match token(meta, ")") { + Ok(_) => break, + Err(_) => token(meta, ",")? + }; + } + // Parse the body + token(meta, "{")?; + let index_begin = meta.get_index(); + skip_function_body(meta); + let index_end = meta.get_index(); + token(meta, "}")?; + // Add the function to the memory + let body = meta.expr[index_begin..index_end].to_vec(); + self.id = handle_add_function(meta, &self.name, &self.args, self.returns.clone(), tok, body); + Ok(()) + }, |err| { + let message = format!("Failed to parse function declaration '{}'", self.name); + get_error_logger(meta, err) + .attach_message(message) + .show() + .exit(); + }); + Ok(()) + } +} + +impl TranslateModule for FunctionDeclaration { + fn translate(&self, meta: &mut TranslateMetadata) -> String { + let mut result = vec![]; + let blocks = meta.mem.get_function_instances(self.id).unwrap().to_vec(); + // Translate each one of them + for (index, function) in blocks.iter().enumerate() { + let mut name = self.name.clone(); + if index != 0 { + name = format!("__{}_{}", index, name); + } + // Parse the function body + result.push(format!("function {} {{", name)); + result.push(self.set_args_as_variables(meta)); + result.push(function.body.translate(meta)); + result.push("}".to_string()); + } + // Return the translation + result.join("\n") + } +} \ No newline at end of file diff --git a/src/modules/function/declaration_utils.rs b/src/modules/function/declaration_utils.rs new file mode 100644 index 00000000..5c56aa7f --- /dev/null +++ b/src/modules/function/declaration_utils.rs @@ -0,0 +1,57 @@ +use heraclitus_compiler::prelude::*; +use crate::modules::Type; +use crate::utils::{ParserMetadata, error::get_error_logger}; +use crate::modules::variable::{handle_identifier_name}; + +pub fn skip_function_body(meta: &mut ParserMetadata) { + let mut scope = 1; + while let Some(tok) = meta.get_current_token() { + match tok.word.as_str() { + "{" => scope += 1, + "}" => scope -= 1, + _ => {} + } + if scope == 0 { break } + meta.increment_index(); + } +} + +pub fn handle_existing_function(meta: &mut ParserMetadata, tok: Option) { + let name = tok.as_ref().unwrap().word.clone(); + handle_identifier_name(meta, &name, tok.clone()); + if meta.mem.get_function(&name).is_some() { + let message = format!("Function '{}' already exists", name); + let details = ErrorDetails::from_token_option(tok); + get_error_logger(meta, details) + .attach_message(message) + .show() + .exit(); + } +} + +pub fn handle_add_function(meta: &mut ParserMetadata, name: &str, args: &[(String, Type)], returns: Type, tok: Option, body: Vec) -> usize { + handle_identifier_name(meta, name, tok.clone()); + let any_generic = args.iter().any(|(_, kind)| kind == &Type::Generic); + let any_typed = args.iter().any(|(_, kind)| kind != &Type::Generic); + // Either all arguments are generic or typed + if any_typed && (any_generic || returns == Type::Generic) { + get_error_logger(meta, ErrorDetails::from_token_option(tok.clone())) + .attach_message(format!("Function '{}' has a mix of generic and typed arguments", name)) + .attach_comment("Please decide whether to use generics or types for all arguments") + .show() + .exit(); + } + // Try to add the function to the memory + match meta.mem.add_function(name, args, returns, body) { + // Return the id of the function + Some(id) => id, + // If the function already exists, show an error + None => { + get_error_logger(meta, ErrorDetails::from_token_option(tok)) + .attach_message(format!("Function '{}' already exists", name)) + .show() + .exit(); + 0 + } + } +} \ No newline at end of file diff --git a/src/modules/function/invocation.rs b/src/modules/function/invocation.rs new file mode 100644 index 00000000..2e6c3ce7 --- /dev/null +++ b/src/modules/function/invocation.rs @@ -0,0 +1,68 @@ +use heraclitus_compiler::prelude::*; +use crate::modules::Type; +use crate::modules::variable::variable_name_extensions; +use crate::utils::metadata::{ParserMetadata, TranslateMetadata}; +use crate::translate::module::TranslateModule; +use crate::modules::expression::expr::Expr; +use crate::modules::Typed; + +use super::invocation_utils::*; + +#[derive(Debug, Clone)] +pub struct FunctionInvocation { + name: String, + args: Vec, + kind: Type, + id: usize +} + +impl Typed for FunctionInvocation { + fn get_type(&self) -> Type { + self.kind.clone() + } +} + +impl SyntaxModule for FunctionInvocation { + syntax_name!("Function Invocation"); + + fn new() -> Self { + FunctionInvocation { + name: String::new(), + args: vec![], + kind: Type::Null, + id: 0 + } + } + + fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { + // Get the function name + let tok = meta.get_current_token(); + self.name = variable(meta, variable_name_extensions())?; + // Get the arguments + token(meta, "(")?; + handle_function_reference(meta, tok, &self.name); + loop { + if token(meta, ")").is_ok() { + break + } + let mut expr = Expr::new(); + syntax(meta, &mut expr)?; + self.args.push(expr); + match token(meta, ")") { + Ok(_) => break, + Err(_) => token(meta, ",")? + }; + } + let types = self.args.iter().map(|e| e.get_type()).collect::>(); + (self.kind, self.id) = handle_function_parameters(meta, &self.name, &types); + Ok(()) + } +} + +impl TranslateModule for FunctionInvocation { + fn translate(&self, meta: &mut TranslateMetadata) -> String { + let name = if self.id != 0 { format!("__{}_{}", self.id, self.name) } else { self.name.clone() }; + let args = self.args.iter().map(|arg| arg.translate(meta)).collect::>().join(" "); + format!("{name} {args}") + } +} \ No newline at end of file diff --git a/src/modules/function/invocation_utils.rs b/src/modules/function/invocation_utils.rs new file mode 100644 index 00000000..a07a31c3 --- /dev/null +++ b/src/modules/function/invocation_utils.rs @@ -0,0 +1,55 @@ +use heraclitus_compiler::prelude::*; +use similar_string::find_best_similarity; +use crate::modules::Type; +use crate::utils::{ParserMetadata, error::get_error_logger}; +use crate::modules::block::Block; + +fn run_function_with_args(meta: &mut ParserMetadata, name: &str, args: &[Type]) -> usize { + let function = meta.mem.get_function(name).unwrap().clone(); + let mut block = Block::new(); + // Create a new parser metadata specific for the function parsing context + let mut new_meta = meta.clone(); + new_meta.expr = function.body.clone(); + new_meta.set_index(0); + // Create a sub context for new variables + new_meta.mem.push_scope(); + for (kind, (name, _generic)) in args.iter().zip(function.args.iter()) { + new_meta.mem.add_variable(name, kind.clone()); + } + // Parse the function body + if let Ok(()) = syntax(&mut new_meta, &mut block) { + // Pop function body + new_meta.mem.pop_scope(); + // Persist the new function instance + meta.mem.add_function_instance(function.id, args, Type::Text, block) + } else { 0 } +} + +pub fn handle_function_reference(meta: &mut ParserMetadata, tok: Option, name: &str) { + if meta.mem.get_function(name).is_none() { + let message = format!("Function '{}' does not exist", name); + let details = ErrorDetails::from_token_option(tok); + let mut error = get_error_logger(meta, details).attach_message(message); + // Find other similar variable if exists + if let Some(comment) = handle_similar_function(meta, name) { + error = error.attach_comment(comment); + } + error.show().exit(); + } +} + +pub fn handle_function_parameters(meta: &mut ParserMetadata, name: &str, args: &[Type]) -> (Type, usize) { + let function_unit = meta.mem.get_function(name).unwrap().clone(); + // TODO: Here is a good place to insert trace + (function_unit.returns, run_function_with_args(meta, name, args)) +} + +fn handle_similar_function(meta: &mut ParserMetadata, name: &str) -> Option { + let vars = Vec::from_iter(meta.mem.get_available_functions()); + if let Some((match_name, score)) = find_best_similarity(name, &vars) { + match score >= 0.75 { + true => Some(format!("Did you mean '{match_name}'?")), + false => None + } + } else { None } +} \ No newline at end of file diff --git a/src/modules/function/mod.rs b/src/modules/function/mod.rs new file mode 100644 index 00000000..3eb6bad6 --- /dev/null +++ b/src/modules/function/mod.rs @@ -0,0 +1,4 @@ +pub mod declaration; +pub mod declaration_utils; +pub mod invocation; +pub mod invocation_utils; diff --git a/src/modules/loops/break_stmt.rs b/src/modules/loops/break_stmt.rs index ed22cbb4..e988b41c 100644 --- a/src/modules/loops/break_stmt.rs +++ b/src/modules/loops/break_stmt.rs @@ -3,7 +3,7 @@ use crate::translate::module::TranslateModule; use crate::utils::error::get_error_logger; use crate::utils::metadata::{ParserMetadata, TranslateMetadata}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Break; impl SyntaxModule for Break { diff --git a/src/modules/loops/continue_stmt.rs b/src/modules/loops/continue_stmt.rs index a40d7651..4ec27615 100644 --- a/src/modules/loops/continue_stmt.rs +++ b/src/modules/loops/continue_stmt.rs @@ -3,7 +3,7 @@ use crate::translate::module::TranslateModule; use crate::utils::error::get_error_logger; use crate::utils::metadata::{ParserMetadata, TranslateMetadata}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Continue; impl SyntaxModule for Continue { diff --git a/src/modules/loops/infinite_loop.rs b/src/modules/loops/infinite_loop.rs index eb1de5c2..335b2180 100644 --- a/src/modules/loops/infinite_loop.rs +++ b/src/modules/loops/infinite_loop.rs @@ -3,7 +3,7 @@ use crate::translate::module::TranslateModule; use crate::utils::metadata::{ParserMetadata, TranslateMetadata}; use crate::modules::block::Block; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct InfiniteLoop { block: Block, } diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 13242e44..fb6168f9 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -3,9 +3,10 @@ pub mod expression; pub mod block; pub mod variable; pub mod command; -pub mod conditions; +pub mod condition; pub mod shorthand; pub mod loops; +pub mod function; #[macro_export] macro_rules! handle_types { @@ -41,7 +42,8 @@ pub enum Type { Text, Bool, Num, - Null + Null, + Generic } pub trait Typed { diff --git a/src/modules/shorthand/add.rs b/src/modules/shorthand/add.rs index e012b216..077bb31a 100644 --- a/src/modules/shorthand/add.rs +++ b/src/modules/shorthand/add.rs @@ -1,9 +1,10 @@ use heraclitus_compiler::prelude::*; -use crate::{modules::{variable::get::VariableGet, expression::{expr::Expr, binop::expression_arms_of_type}, Type}, utils::ParserMetadata, translate::{module::TranslateModule, compute::{ArithOp, translate_computation}}}; +use crate::{modules::{variable::{variable_name_extensions, handle_variable_reference}, expression::{expr::Expr, binop::expression_arms_of_type}, Type}, utils::ParserMetadata, translate::{module::TranslateModule, compute::{ArithOp, translate_computation}}}; +use crate::modules::Typed; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ShorthandAdd { - var: VariableGet, + var: String, expr: Box, kind: Type } @@ -13,28 +14,30 @@ impl SyntaxModule for ShorthandAdd { fn new() -> Self { Self { - var: VariableGet::new(), + var: String::new(), expr: Box::new(Expr::new()), kind: Type::Null } } fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { - self.var.parse(meta)?; + let var_tok = meta.get_current_token(); + self.var = variable(meta, variable_name_extensions())?; let tok = meta.get_current_token(); token(meta, "+=")?; + self.kind = handle_variable_reference(meta, var_tok, &self.var); self.expr.parse(meta)?; let message = "Add operation can only add numbers or text"; - self.kind = expression_arms_of_type(meta, &self.var, &*self.expr, &[Type::Num, Type::Text], tok, message); + expression_arms_of_type(meta, &self.kind, &self.expr.get_type(), &[Type::Num, Type::Text], tok, message); Ok(()) } } impl TranslateModule for ShorthandAdd { fn translate(&self, meta: &mut crate::utils::TranslateMetadata) -> String { - let var = self.var.translate(meta); let expr = self.expr.translate(meta); - let name = self.var.name.clone(); + let name = self.var.clone(); + let var = format!("${{{name}}}"); if self.kind == Type::Text { format!("{}+={}", name, expr) } diff --git a/src/modules/shorthand/div.rs b/src/modules/shorthand/div.rs index e5e0a1c8..a11e3f38 100644 --- a/src/modules/shorthand/div.rs +++ b/src/modules/shorthand/div.rs @@ -1,9 +1,10 @@ use heraclitus_compiler::prelude::*; -use crate::{modules::{variable::get::VariableGet, expression::{expr::Expr, binop::expression_arms_of_type}, Type}, utils::ParserMetadata, translate::{module::TranslateModule, compute::{ArithOp, translate_computation}}}; +use crate::{modules::{variable::{variable_name_extensions, handle_variable_reference}, expression::{expr::Expr, binop::expression_arms_of_type}, Type}, utils::ParserMetadata, translate::{module::TranslateModule, compute::{ArithOp, translate_computation}}}; +use crate::modules::Typed; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ShorthandDiv { - var: VariableGet, + var: String, expr: Box, kind: Type } @@ -13,28 +14,30 @@ impl SyntaxModule for ShorthandDiv { fn new() -> Self { Self { - var: VariableGet::new(), + var: String::new(), expr: Box::new(Expr::new()), kind: Type::Null } } fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { - self.var.parse(meta)?; + let var_tok = meta.get_current_token(); + self.var = variable(meta, variable_name_extensions())?; let tok = meta.get_current_token(); token(meta, "/=")?; + self.kind = handle_variable_reference(meta, var_tok, &self.var); self.expr.parse(meta)?; let message = "Division operation can only divide numbers"; - self.kind = expression_arms_of_type(meta, &self.var, &*self.expr, &[Type::Num], tok, message); + expression_arms_of_type(meta, &self.kind, &self.expr.get_type(), &[Type::Num], tok, message); Ok(()) } } impl TranslateModule for ShorthandDiv { fn translate(&self, meta: &mut crate::utils::TranslateMetadata) -> String { - let var = self.var.translate(meta); let expr = self.expr.translate(meta); - let name = self.var.name.clone(); + let name = self.var.clone(); + let var = format!("${{{name}}}"); format!("{}={}", name, translate_computation(meta, ArithOp::Div, Some(var), Some(expr))) } } \ No newline at end of file diff --git a/src/modules/shorthand/modulo.rs b/src/modules/shorthand/modulo.rs index 99003091..6cc8b43a 100644 --- a/src/modules/shorthand/modulo.rs +++ b/src/modules/shorthand/modulo.rs @@ -1,9 +1,10 @@ use heraclitus_compiler::prelude::*; -use crate::{modules::{variable::get::VariableGet, expression::{expr::Expr, binop::expression_arms_of_type}, Type}, utils::ParserMetadata, translate::{module::TranslateModule, compute::{ArithOp, translate_computation}}}; +use crate::{modules::{variable::{variable_name_extensions, handle_variable_reference}, expression::{expr::Expr, binop::expression_arms_of_type}, Type}, utils::ParserMetadata, translate::{module::TranslateModule, compute::{ArithOp, translate_computation}}}; +use crate::modules::Typed; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ShorthandModulo { - var: VariableGet, + var: String, expr: Box, kind: Type } @@ -13,28 +14,30 @@ impl SyntaxModule for ShorthandModulo { fn new() -> Self { Self { - var: VariableGet::new(), + var: String::new(), expr: Box::new(Expr::new()), kind: Type::Null } } fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { - self.var.parse(meta)?; + let var_tok = meta.get_current_token(); + self.var = variable(meta, variable_name_extensions())?; let tok = meta.get_current_token(); token(meta, "%=")?; + self.kind = handle_variable_reference(meta, var_tok, &self.var); self.expr.parse(meta)?; let message = "Modulo operation can only be applied to numbers"; - self.kind = expression_arms_of_type(meta, &self.var, &*self.expr, &[Type::Num], tok, message); + expression_arms_of_type(meta, &self.kind, &self.expr.get_type(), &[Type::Num], tok, message); Ok(()) } } impl TranslateModule for ShorthandModulo { fn translate(&self, meta: &mut crate::utils::TranslateMetadata) -> String { - let var = self.var.translate(meta); let expr = self.expr.translate(meta); - let name = self.var.name.clone(); + let name = self.var.clone(); + let var = format!("${{{name}}}"); format!("{}={}", name, translate_computation(meta, ArithOp::Modulo, Some(var), Some(expr))) } } \ No newline at end of file diff --git a/src/modules/shorthand/mul.rs b/src/modules/shorthand/mul.rs index dd48e14b..9ed691b2 100644 --- a/src/modules/shorthand/mul.rs +++ b/src/modules/shorthand/mul.rs @@ -1,9 +1,10 @@ use heraclitus_compiler::prelude::*; -use crate::{modules::{variable::get::VariableGet, expression::{expr::Expr, binop::expression_arms_of_type}, Type}, utils::ParserMetadata, translate::{module::TranslateModule, compute::{ArithOp, translate_computation}}}; +use crate::{modules::{variable::{variable_name_extensions, handle_variable_reference}, expression::{expr::Expr, binop::expression_arms_of_type}, Type}, utils::ParserMetadata, translate::{module::TranslateModule, compute::{ArithOp, translate_computation}}}; +use crate::modules::Typed; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ShorthandMul { - var: VariableGet, + var: String, expr: Box, kind: Type } @@ -13,28 +14,30 @@ impl SyntaxModule for ShorthandMul { fn new() -> Self { Self { - var: VariableGet::new(), + var: String::new(), expr: Box::new(Expr::new()), kind: Type::Null } } fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { - self.var.parse(meta)?; + let var_tok = meta.get_current_token(); + self.var = variable(meta, variable_name_extensions())?; let tok = meta.get_current_token(); token(meta, "*=")?; + self.kind = handle_variable_reference(meta, var_tok, &self.var); self.expr.parse(meta)?; let message = "Multiplication operation can only multiply numbers"; - self.kind = expression_arms_of_type(meta, &self.var, &*self.expr, &[Type::Num], tok, message); + expression_arms_of_type(meta, &self.kind, &self.expr.get_type(), &[Type::Num, Type::Text], tok, message); Ok(()) } } impl TranslateModule for ShorthandMul { fn translate(&self, meta: &mut crate::utils::TranslateMetadata) -> String { - let var = self.var.translate(meta); let expr = self.expr.translate(meta); - let name = self.var.name.clone(); + let name = self.var.clone(); + let var = format!("${{{name}}}"); format!("{}={}", name, translate_computation(meta, ArithOp::Mul, Some(var), Some(expr))) } } \ No newline at end of file diff --git a/src/modules/shorthand/sub.rs b/src/modules/shorthand/sub.rs index 9198797d..5bb87640 100644 --- a/src/modules/shorthand/sub.rs +++ b/src/modules/shorthand/sub.rs @@ -1,9 +1,10 @@ use heraclitus_compiler::prelude::*; -use crate::{modules::{variable::get::VariableGet, expression::{expr::Expr, binop::expression_arms_of_type}, Type}, utils::ParserMetadata, translate::{module::TranslateModule, compute::{ArithOp, translate_computation}}}; +use crate::{modules::{variable::{variable_name_extensions, handle_variable_reference}, expression::{expr::Expr, binop::expression_arms_of_type}, Type}, utils::ParserMetadata, translate::{module::TranslateModule, compute::{ArithOp, translate_computation}}}; +use crate::modules::Typed; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ShorthandSub { - var: VariableGet, + var: String, expr: Box, kind: Type } @@ -13,28 +14,30 @@ impl SyntaxModule for ShorthandSub { fn new() -> Self { Self { - var: VariableGet::new(), + var: String::new(), expr: Box::new(Expr::new()), kind: Type::Null } } fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { - self.var.parse(meta)?; + let var_tok = meta.get_current_token(); + self.var = variable(meta, variable_name_extensions())?; let tok = meta.get_current_token(); token(meta, "-=")?; + self.kind = handle_variable_reference(meta, var_tok, &self.var); self.expr.parse(meta)?; let message = "Substract operation can only substract numbers"; - self.kind = expression_arms_of_type(meta, &self.var, &*self.expr, &[Type::Num], tok, message); + expression_arms_of_type(meta, &self.kind, &self.expr.get_type(), &[Type::Num, Type::Text], tok, message); Ok(()) } } impl TranslateModule for ShorthandSub { fn translate(&self, meta: &mut crate::utils::TranslateMetadata) -> String { - let var = self.var.translate(meta); let expr = self.expr.translate(meta); - let name = self.var.name.clone(); + let name = self.var.clone(); + let var = format!("${{{name}}}"); format!("{}={}", name, translate_computation(meta, ArithOp::Sub, Some(var), Some(expr))) } } \ No newline at end of file diff --git a/src/modules/statement/mod.rs b/src/modules/statement/mod.rs index f44230d2..2bf27aa8 100644 --- a/src/modules/statement/mod.rs +++ b/src/modules/statement/mod.rs @@ -1 +1 @@ -pub mod st; \ No newline at end of file +pub mod stmt; \ No newline at end of file diff --git a/src/modules/statement/st.rs b/src/modules/statement/stmt.rs similarity index 83% rename from src/modules/statement/st.rs rename to src/modules/statement/stmt.rs index 0d143e10..9f887ccc 100644 --- a/src/modules/statement/st.rs +++ b/src/modules/statement/stmt.rs @@ -8,7 +8,7 @@ use crate::modules::variable::{ }; use crate::modules::command::statement::CommandStatement; use crate::handle_types; -use crate::modules::conditions::{ +use crate::modules::condition::{ ifchain::IfChain, ifcond::IfCondition }; @@ -24,8 +24,11 @@ use crate::modules::loops::{ break_stmt::Break, continue_stmt::Continue }; +use crate::modules::function::{ + declaration::FunctionDeclaration +}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum StatementType { Expr(Expr), VariableInit(VariableInit), @@ -40,16 +43,19 @@ pub enum StatementType { ShorthandModulo(ShorthandModulo), InfiniteLoop(InfiniteLoop), Break(Break), - Continue(Continue) + Continue(Continue), + FunctionDeclaration(FunctionDeclaration) } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Statement { pub value: Option } impl Statement { handle_types!(StatementType, [ + // Functions + FunctionDeclaration, // Loops InfiniteLoop, Break, Continue, // Conditions @@ -95,6 +101,12 @@ impl SyntaxModule for Statement { let mut error = None; let statements = self.get_modules(); for statement in statements { + // If the statement is an expression, we want it to not fire it's own error + let statement = if let StatementType::Expr(mut expr) = statement { + expr.cannot_fail(); + StatementType::Expr(expr) + } else { statement }; + // Try to parse the statement match self.parse_match(meta, statement) { Ok(()) => return Ok(()), Err(details) => error = Some(details) diff --git a/src/modules/variable/get.rs b/src/modules/variable/get.rs index f9bb32f9..248b957e 100644 --- a/src/modules/variable/get.rs +++ b/src/modules/variable/get.rs @@ -3,7 +3,7 @@ use crate::{utils::{ParserMetadata, TranslateMetadata}, modules::{Type, Typed}}; use crate::translate::module::TranslateModule; use super::{variable_name_extensions, handle_variable_reference}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct VariableGet { pub name: String, kind: Type @@ -28,7 +28,7 @@ impl SyntaxModule for VariableGet { fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { let tok = meta.get_current_token(); self.name = variable(meta, variable_name_extensions())?; - self.kind = handle_variable_reference(meta, tok, self.name.clone()); + self.kind = handle_variable_reference(meta, tok, &self.name); Ok(()) } } diff --git a/src/modules/variable/init.rs b/src/modules/variable/init.rs index 87f36a0e..19dfd665 100644 --- a/src/modules/variable/init.rs +++ b/src/modules/variable/init.rs @@ -1,16 +1,25 @@ use heraclitus_compiler::prelude::*; -use crate::modules::{Typed}; +use crate::context; +use crate::modules::{Typed, Type}; use crate::modules::expression::expr::Expr; use crate::translate::module::TranslateModule; +use crate::utils::error::get_error_logger; use crate::utils::metadata::{ParserMetadata, TranslateMetadata}; -use super::variable_name_extensions; +use super::{variable_name_extensions, handle_identifier_name}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct VariableInit { name: String, expr: Box } +impl VariableInit { + fn handle_add_variable(&self, meta: &mut ParserMetadata, name: &str, kind: Type, tok: Option) { + handle_identifier_name(meta, name, tok); + meta.mem.add_variable(name, kind); + } +} + impl SyntaxModule for VariableInit { syntax_name!("Variable Initialize"); @@ -24,11 +33,21 @@ impl SyntaxModule for VariableInit { fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { token(meta, "let")?; // Get the variable name + let tok = meta.get_current_token(); self.name = variable(meta, variable_name_extensions())?; - token(meta, "=")?; - syntax(meta, &mut *self.expr)?; - // Add a variable to the memory - meta.var_mem.add_variable(self.name.clone(), self.expr.get_type()); + context!({ + token(meta, "=")?; + syntax(meta, &mut *self.expr)?; + // Add a variable to the memory + self.handle_add_variable(meta, &self.name, self.expr.get_type(), tok); + Ok(()) + }, |details| { + let message = format!("Expected '=' after variable name '{}'", self.name); + get_error_logger(meta, details) + .attach_message(message) + .show() + .exit(); + }); Ok(()) } } diff --git a/src/modules/variable/mod.rs b/src/modules/variable/mod.rs index 800aece8..8eb5ed26 100644 --- a/src/modules/variable/mod.rs +++ b/src/modules/variable/mod.rs @@ -10,12 +10,18 @@ pub fn variable_name_extensions() -> Vec { vec!['_'] } -pub fn handle_variable_reference(meta: &mut ParserMetadata, token: Option, name: String) -> Type { - match meta.var_mem.get_variable(name.clone()) { +pub fn variable_name_keywords() -> Vec<&'static str> { + vec!["true", "false", "null", "if", "loop", "break", "continue", "fun", "else", "let"] +} + + +pub fn handle_variable_reference(meta: &mut ParserMetadata, tok: Option, name: &str) -> Type { + handle_identifier_name(meta, name, tok.clone()); + match meta.mem.get_variable(name) { Some(variable_unit) => variable_unit.kind.clone(), None => { let message = format!("Variable '{}' does not exist", name); - let details = ErrorDetails::from_token_option(token); + let details = ErrorDetails::from_token_option(tok); let mut error = get_error_logger(meta, details).attach_message(message); // Find other similar variable if exists if let Some(comment) = handle_similar_variable(meta, name) { @@ -27,8 +33,8 @@ pub fn handle_variable_reference(meta: &mut ParserMetadata, token: Option } } -fn handle_similar_variable(meta: &mut ParserMetadata, name: String) -> Option { - let vars = Vec::from_iter(meta.var_mem.get_available_variables()); +fn handle_similar_variable(meta: &mut ParserMetadata, name: &str) -> Option { + let vars = Vec::from_iter(meta.mem.get_available_variables()); if let Some((match_name, score)) = find_best_similarity(name, &vars) { match score >= 0.75 { true => Some(format!("Did you mean '{match_name}'?")), @@ -36,3 +42,25 @@ fn handle_similar_variable(meta: &mut ParserMetadata, name: String) -> Option) { + // Validate if the variable name uses the reserved prefix + if name.chars().take(2).all(|chr| chr == '_') { + let new_name = name.get(1..).unwrap(); + let message = format!("Indentifier '{name}' is not allowed"); + let comment = format!("Identifiers with double underscores are reserved for the compiler.\nConsider using '{new_name}' instead."); + let details = ErrorDetails::from_token_option(tok.clone()); + get_error_logger(meta, details) + .attach_message(message) + .attach_comment(comment) + .show().exit(); + } + // Validate if the variable name is a keyword + if variable_name_keywords().contains(&name) { + let message = format!("Indentifier '{name}' is a reserved keyword"); + let details = ErrorDetails::from_token_option(tok); + get_error_logger(meta, details) + .attach_message(message) + .show().exit(); + } +} diff --git a/src/modules/variable/set.rs b/src/modules/variable/set.rs index c2f39fe8..3ad5c422 100644 --- a/src/modules/variable/set.rs +++ b/src/modules/variable/set.rs @@ -3,7 +3,7 @@ use crate::{modules::expression::expr::Expr, translate::module::TranslateModule} use crate::utils::{ParserMetadata, TranslateMetadata}; use super::{variable_name_extensions, handle_variable_reference}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct VariableSet { name: String, value: Box @@ -24,7 +24,7 @@ impl SyntaxModule for VariableSet { self.name = variable(meta, variable_name_extensions())?; token(meta, "=")?; syntax(meta, &mut *self.value)?; - handle_variable_reference(meta, tok, self.name.clone()); + handle_variable_reference(meta, tok, &self.name); Ok(()) } } diff --git a/src/rules.rs b/src/rules.rs index 1c733f84..b3c8c8f0 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -3,7 +3,7 @@ use heraclitus_compiler::prelude::*; pub fn get_rules() -> Rules { let symbols = vec![ '+', '-', '*', '/', '%', '\n', ';', - '(', ')', '[', ']', '{', '}' + '(', ')', '[', ']', '{', '}', ',' ]; let compounds = vec![ ('<', '='), diff --git a/src/translate/mod.rs b/src/translate/mod.rs index 524f06af..c1992f9a 100644 --- a/src/translate/mod.rs +++ b/src/translate/mod.rs @@ -1,2 +1,22 @@ +use heraclitus_compiler::prelude::*; + +use crate::utils::{error::get_error_logger, ParserMetadata}; + pub mod module; -pub mod compute; \ No newline at end of file +pub mod compute; + +pub fn check_all_blocks(meta: &mut ParserMetadata) { + let mut stack = 0; + for token in meta.expr.iter() { + match token.word.as_str() { + "{" => stack += 1, + "}" => stack -= 1, + _ => () + } + if stack < 0 { + get_error_logger(meta, ErrorDetails::from_token_option(Some(token.clone()))) + .attach_comment("There are too many closing brackets") + .show().exit(); + } + } +} \ No newline at end of file diff --git a/src/utils/error.rs b/src/utils/error.rs index 6752931b..a5e2db5f 100644 --- a/src/utils/error.rs +++ b/src/utils/error.rs @@ -1,13 +1,13 @@ use heraclitus_compiler::prelude::*; use crate::utils::metadata::ParserMetadata; -pub fn get_error_logger(meta: &mut ParserMetadata, details: ErrorDetails) -> Logger { +pub fn get_error_logger(meta: &ParserMetadata, details: ErrorDetails) -> Logger { let path = meta.path.clone(); let code = meta.code.clone(); Logger::new_err_with_details(path, code, details) } -pub fn get_warn_logger(meta: &mut ParserMetadata, details: ErrorDetails) -> Logger { +pub fn get_warn_logger(meta: &ParserMetadata, details: ErrorDetails) -> Logger { let path = meta.path.clone(); let code = meta.code.clone(); Logger::new_warn_with_details(path, code, details) diff --git a/src/utils/function_map.rs b/src/utils/function_map.rs new file mode 100644 index 00000000..aa53ce15 --- /dev/null +++ b/src/utils/function_map.rs @@ -0,0 +1,44 @@ +use std::collections::HashMap; +use crate::modules::{Type, block::Block}; + +#[derive(Clone, Debug)] +pub struct FunctionInstance { + pub args: Vec, + pub returns: Type, + pub body: Block, +} + +#[derive(Clone, Debug)] +// This is a map of all generated functions based on their invocations +pub struct FunctionMap { + map: HashMap>, + current_id: usize +} + +impl FunctionMap { + pub fn new() -> FunctionMap { + FunctionMap { + map: HashMap::new(), + current_id: 0 + } + } + + pub fn add_declaration(&mut self) -> usize { + let id = self.current_id; + self.map.insert(id, vec![]); + self.current_id += 1; + id + } + + pub fn add_instance(&mut self, id: usize, function: FunctionInstance) -> usize { + if let Some(functions) = self.map.get_mut(&id) { + let length = functions.len(); + functions.push(function); + length + } else { 0 } + } + + pub fn get(&self, id: usize) -> Option<&Vec> { + self.map.get(&id) + } +} \ No newline at end of file diff --git a/src/utils/memory.rs b/src/utils/memory.rs new file mode 100644 index 00000000..c25701a3 --- /dev/null +++ b/src/utils/memory.rs @@ -0,0 +1,147 @@ +use heraclitus_compiler::prelude::*; +use std::collections::{HashMap, BTreeSet}; +use crate::modules::{Type, block::Block}; + +use super::function_map::{FunctionMap, FunctionInstance}; + +// TODO: Change (args, returns) to function descriptor + +#[derive(Clone, Debug)] +pub struct FunctionDecl { + pub name: String, + pub args: Vec<(String, Type)>, + pub returns: Type, + pub body: Vec, + pub typed: bool, + pub id: usize +} + +#[derive(Clone, Debug)] +pub struct VariableDecl { + pub name: String, + pub kind: Type +} + +#[derive(Clone, Debug)] +pub struct ScopeUnit { + pub vars: HashMap, + pub funs: HashMap +} + +impl ScopeUnit { + fn new() -> ScopeUnit { + ScopeUnit { + vars: HashMap::new(), + funs: HashMap::new() + } + } +} + +#[derive(Clone, Debug)] +pub struct Memory { + scopes: Vec, + // Map of all generated functions based on their invocations + function_map: FunctionMap +} + +impl Memory { + pub fn new() -> Memory { + Memory { + scopes: vec![], + function_map: FunctionMap::new() + } + } + + pub fn push_scope(&mut self) { + self.scopes.push(ScopeUnit::new()) + } + + pub fn pop_scope(&mut self) -> Option { + self.scopes.pop() + } + + pub fn add_variable(&mut self, name: &str, kind: Type) -> bool { + if self.get_function(name).is_some() { + return false; + } + let scope = self.scopes.last_mut().unwrap(); + scope.vars.insert(name.to_string(), VariableDecl { name: name.to_string(), kind }).is_none() + } + + pub fn get_variable(&self, name: &str) -> Option<&VariableDecl> { + for scope in self.scopes.iter().rev() { + if let Some(var) = scope.vars.get(name) { + return Some(var); + } + } + None + } + + pub fn get_available_variables(&self) -> BTreeSet<&String> { + let mut set = BTreeSet::new(); + for scope in self.scopes.iter().rev() { + for name in scope.vars.keys() { + set.insert(name); + } + } + set + } + + pub fn add_function(&mut self, name: &str, args: &[(String, Type)], returns: Type, body: Vec) -> Option { + // Make sure that there is no variable with the same name + if self.get_variable(name).is_some() { + return None; + } + let typed = !args.iter().any(|(_, kind)| kind == &Type::Generic); + let scope = self.scopes.last_mut().unwrap(); + // Add function declaration + let id = self.function_map.add_declaration(); + let success = scope.funs.insert(name.to_string(), FunctionDecl { + name: name.to_string(), + args: args.to_vec(), + returns, + body, + typed, + id + }); + // If this is a new function, return its id + if success.is_none() { + Some(id) + } + // If we are having a conflict + else { + None + } + } + + pub fn add_function_instance(&mut self, id: usize, args: &[Type], returns: Type, body: Block) -> usize { + self.function_map.add_instance(id, FunctionInstance { + args: args.to_vec(), + returns, + body + }) + } + + pub fn get_function(&self, name: &str) -> Option<&FunctionDecl> { + for scope in self.scopes.iter().rev() { + if let Some(fun) = scope.funs.get(name) { + return Some(fun); + } + } + None + } + + pub fn get_function_instances(&self, id: usize) -> Option<&Vec> { + self.function_map.get(id) + } + + pub fn get_available_functions(&self) -> BTreeSet<&String> { + let mut set = BTreeSet::new(); + for scope in self.scopes.iter().rev() { + for name in scope.funs.keys() { + set.insert(name); + } + } + set + } +} \ No newline at end of file diff --git a/src/utils/metadata/parser.rs b/src/utils/metadata/parser.rs index 9b72e827..e30435aa 100644 --- a/src/utils/metadata/parser.rs +++ b/src/utils/metadata/parser.rs @@ -1,13 +1,14 @@ use heraclitus_compiler::prelude::*; -use crate::utils::variable_memory::VariableMemory; +use crate::utils::memory::Memory; +#[derive(Clone)] pub struct ParserMetadata { pub expr: Vec, index: usize, pub path: Option, pub code: Option, pub binop_border: Option, - pub var_mem: VariableMemory, + pub mem: Memory, debug: Option, pub loop_ctx: bool } @@ -20,7 +21,7 @@ impl Metadata for ParserMetadata { path, code, binop_border: None, - var_mem: VariableMemory::new(), + mem: Memory::new(), debug: None, loop_ctx: false } @@ -37,9 +38,11 @@ impl Metadata for ParserMetadata { fn get_token_at(&self, index: usize) -> Option { self.expr.get(index).cloned() } + fn get_debug(&mut self) -> Option { self.debug } + fn set_debug(&mut self, indent: usize) { self.debug = Some(indent) } diff --git a/src/utils/metadata/translate.rs b/src/utils/metadata/translate.rs index 5d0fb11c..6462d3e7 100644 --- a/src/utils/metadata/translate.rs +++ b/src/utils/metadata/translate.rs @@ -1,13 +1,30 @@ -use crate::translate::compute::ArithType; +use crate::{translate::compute::ArithType, utils::memory::Memory}; +use super::ParserMetadata; pub struct TranslateMetadata { - pub arith_module: ArithType + pub arith_module: ArithType, + pub mem: Memory, + pub indent: i64 } impl TranslateMetadata { - pub fn new() -> Self { + pub fn new(meta: &ParserMetadata) -> Self { TranslateMetadata { - arith_module: ArithType::BcSed + arith_module: ArithType::BcSed, + mem: meta.mem.clone(), + indent: -1 } } + + pub fn gen_indent(&self) -> String { + " ".repeat(self.indent as usize) + } + + pub fn increase_indent(&mut self) { + self.indent += 1; + } + + pub fn decrease_indent(&mut self) { + self.indent -= 1; + } } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 7b3d11d6..e9fb7910 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,4 +1,17 @@ pub mod metadata; -pub mod variable_memory; +pub mod memory; pub mod error; -pub use metadata::*; \ No newline at end of file +pub mod function_map; +pub use metadata::*; + +#[macro_export] +macro_rules! context { + ($body:block, |$name:ident| $error:block) => { + { + let ctx: SyntaxResult = (|| { $body })(); + if let Err($name) = ctx { + $error + } + } + }; +} \ No newline at end of file diff --git a/src/utils/variable_memory.rs b/src/utils/variable_memory.rs deleted file mode 100644 index 1b021a40..00000000 --- a/src/utils/variable_memory.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::collections::{HashMap, BTreeSet}; -use crate::modules::Type; - -pub struct VariableUnit { - pub name: String, - pub kind: Type -} - -pub struct ScopeUnit { - pub vars: HashMap -} - -impl ScopeUnit { - fn new() -> ScopeUnit { - ScopeUnit { - vars: HashMap::new() - } - } -} - - -pub struct VariableMemory { - mem: Vec -} - -impl VariableMemory { - pub fn new() -> VariableMemory { - VariableMemory { - mem: vec![] - } - } - - pub fn push_scope(&mut self) { - self.mem.push(ScopeUnit::new()) - } - - pub fn pop_scope(&mut self) -> Option { - self.mem.pop() - } - - pub fn add_variable(&mut self, name: String, kind: Type) -> bool { - let scope = self.mem.last_mut().unwrap(); - scope.vars.insert(name.clone(), VariableUnit { name, kind }).is_none() - } - - pub fn get_variable(&mut self, name: impl AsRef) -> Option<&VariableUnit> { - for scope in self.mem.iter().rev() { - if let Some(var) = scope.vars.get(name.as_ref()) { - return Some(var); - } - } - None - } - - pub fn get_available_variables(&mut self) -> BTreeSet<&String> { - let mut set = BTreeSet::new(); - for scope in self.mem.iter().rev() { - for name in scope.vars.keys() { - set.insert(name); - } - } - set - } -} \ No newline at end of file