diff --git a/src/modules/block.rs b/src/modules/block.rs index 02ec1e70..19955907 100644 --- a/src/modules/block.rs +++ b/src/modules/block.rs @@ -82,4 +82,4 @@ impl TranslateModule for Block { std::mem::swap(&mut meta.stmt_queue, &mut new_queue); result } -} \ No newline at end of file +} diff --git a/src/modules/command/command.rs b/src/modules/command/command.rs new file mode 100644 index 00000000..953894fa --- /dev/null +++ b/src/modules/command/command.rs @@ -0,0 +1,93 @@ +use std::mem::swap; + +use heraclitus_compiler::prelude::*; +use crate::{modules::{condition::failed::Failed, expression::literal::bool, types::{Type, Typed}}, utils::{ParserMetadata, TranslateMetadata}}; +use crate::modules::expression::expr::Expr; +use crate::translate::module::TranslateModule; +use crate::modules::expression::literal::{parse_interpolated_region, translate_interpolated_region}; +use super::modifier::CommandModifier; + +#[derive(Debug, Clone)] +pub struct Command { + strings: Vec, + interps: Vec, + modifier: CommandModifier, + failed: Failed +} + +impl Typed for Command { + fn get_type(&self) -> Type { + Type::Text + } +} + +impl SyntaxModule for Command { + syntax_name!("Command"); + + fn new() -> Self { + Command { + strings: vec![], + interps: vec![], + modifier: CommandModifier::new().parse_expr(), + failed: Failed::new() + } + } + + fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { + syntax(meta, &mut self.modifier)?; + self.modifier.use_modifiers(meta, |_this, meta| { + let tok = meta.get_current_token(); + (self.strings, self.interps) = parse_interpolated_region(meta, '$')?; + match syntax(meta, &mut self.failed) { + Ok(_) => Ok(()), + Err(Failure::Quiet(_)) => error!(meta, tok => { + message: "Every command statement must handle failed execution", + comment: "You can use '?' in the end to propagate the failure" + }), + Err(err) => Err(err) + } + }) + } +} + +impl Command { + fn translate_command(&self, meta: &mut TranslateMetadata, is_statement: bool) -> String { + // Translate all interpolations + let interps = self.interps.iter() + .map(|item| item.translate(meta)) + .collect::>(); + let failed = self.failed.translate(meta); + let mut is_silent = self.modifier.is_silent || meta.silenced; + swap(&mut is_silent, &mut meta.silenced); + let silent = meta.gen_silent(); + let translation = translate_interpolated_region(self.strings.clone(), interps, false); + swap(&mut is_silent, &mut meta.silenced); + let translation = format!("{translation}{silent}"); + if is_statement { + return if failed.is_empty() { translation } else { + meta.stmt_queue.push_back(translation); + failed + } + } + if failed.is_empty() { + meta.gen_subprocess(&translation) + } else { + let id = meta.gen_value_id(); + let quote = meta.gen_quote(); + let dollar = meta.gen_dollar(); + meta.stmt_queue.push_back(format!("__AMBER_VAL_{id}=$({translation})")); + meta.stmt_queue.push_back(failed); + format!("{quote}{dollar}{{__AMBER_VAL_{id}}}{quote}") + } + } + + pub fn translate_command_statement(&self, meta: &mut TranslateMetadata) -> String { + self.translate_command(meta, true) + } +} + +impl TranslateModule for Command { + fn translate(&self, meta: &mut TranslateMetadata) -> String { + self.translate_command(meta, false) + } +} diff --git a/src/modules/command/expr.rs b/src/modules/command/expr.rs deleted file mode 100644 index 950d433c..00000000 --- a/src/modules/command/expr.rs +++ /dev/null @@ -1,88 +0,0 @@ -use std::mem::swap; - -use heraclitus_compiler::prelude::*; -use crate::{utils::{ParserMetadata, TranslateMetadata}, modules::{types::{Type, Typed}, condition::failed::Failed, expression::expr::ExprType}}; -use crate::modules::expression::expr::Expr; -use crate::translate::module::TranslateModule; -use crate::modules::expression::literal::{parse_interpolated_region, translate_interpolated_region}; - -use super::modifier::CommandModifier; - -#[derive(Debug, Clone)] -pub struct CommandExpr { - strings: Vec, - interps: Vec, - modifier: CommandModifier, - failed: Failed, - is_silent_expr: bool -} - -impl Typed for CommandExpr { - fn get_type(&self) -> Type { - Type::Text - } -} - -impl SyntaxModule for CommandExpr { - syntax_name!("CommandExpr"); - - fn new() -> Self { - CommandExpr { - strings: vec![], - interps: vec![], - modifier: CommandModifier::new().parse_expr(), - failed: Failed::new(), - is_silent_expr: false - } - } - - fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { - match syntax(meta, &mut self.modifier) { - // If the command modifier was parsed successfully, then we swap the result - Ok(_) => { - if let Some(ExprType::CommandExpr(command)) = &mut self.modifier.expr.value.clone() { - swap(command, self); - // Retrieve the silent modifier from the command modifier - self.is_silent_expr = command.modifier.is_silent; - } - Ok(()) - }, - Err(Failure::Loud(err)) => Err(Failure::Loud(err)), - Err(Failure::Quiet(_)) => { - let tok = meta.get_current_token(); - (self.strings, self.interps) = parse_interpolated_region(meta, '$')?; - match syntax(meta, &mut self.failed) { - Ok(_) => Ok(()), - Err(Failure::Quiet(_)) => error!(meta, tok => { - message: "Every command statement must handle failed execution", - comment: "You can use '?' in the end to fail the exit code of the command" - }), - Err(err) => Err(err) - } - } - } - } -} - -impl TranslateModule for CommandExpr { - fn translate(&self, meta: &mut TranslateMetadata) -> String { - // Translate all interpolations - let interps = self.interps.iter() - .map(|item| item.translate(meta)) - .collect::>(); - let failed = self.failed.translate(meta); - let silent = if self.is_silent_expr { " 2>/dev/null" } else { "" }; - if failed.is_empty() { - let translation = translate_interpolated_region(self.strings.clone(), interps, false); - meta.gen_subprocess(&(translation + silent)) - } else { - let id = meta.gen_value_id(); - let quote = meta.gen_quote(); - let dollar = meta.gen_dollar(); - let translation = translate_interpolated_region(self.strings.clone(), interps, false); - meta.stmt_queue.push_back(format!("__AMBER_VAL_{id}=$({translation}{silent})")); - meta.stmt_queue.push_back(failed); - format!("{quote}{dollar}{{__AMBER_VAL_{id}}}{quote}") - } - } -} \ No newline at end of file diff --git a/src/modules/command/mod.rs b/src/modules/command/mod.rs index 67fd73db..eb6ae2ff 100644 --- a/src/modules/command/mod.rs +++ b/src/modules/command/mod.rs @@ -1,3 +1,2 @@ -pub mod expr; -pub mod statement; -pub mod modifier; \ No newline at end of file +pub mod command; +pub mod modifier; diff --git a/src/modules/command/modifier.rs b/src/modules/command/modifier.rs index ada9fbc5..1e51e6c3 100644 --- a/src/modules/command/modifier.rs +++ b/src/modules/command/modifier.rs @@ -2,134 +2,102 @@ use std::mem::swap; use heraclitus_compiler::prelude::*; use crate::modules::block::Block; -use crate::modules::expression::expr::{Expr, ExprType}; -use crate::modules::statement::stmt::Statement; -use crate::modules::types::{Typed, Type}; use crate::translate::module::TranslateModule; use crate::utils::metadata::{ParserMetadata, TranslateMetadata}; #[derive(Debug, Clone)] pub struct CommandModifier { pub block: Box, - pub expr: Box, - pub is_expr: bool, + pub is_block: bool, pub is_unsafe: bool, pub is_silent: bool } -pub struct CommandModifierExpr { - pub modifier: CommandModifier -} - -impl Typed for CommandModifierExpr { - fn get_type(&self) -> Type { - self.modifier.expr.get_type() - } -} - impl CommandModifier { pub fn parse_expr(mut self) -> Self { - self.is_expr = true; + self.is_block = false; self } - fn flip_unsafe(&mut self, meta: &mut ParserMetadata, is_unsafe: bool) { - if is_unsafe { - swap(&mut self.is_unsafe, &mut meta.context.is_unsafe_ctx); + pub fn use_modifiers( + &mut self, meta: &mut ParserMetadata, context: F + ) -> SyntaxResult where F: FnOnce(&mut Self, &mut ParserMetadata) -> SyntaxResult { + let mut is_unsafe_holder = self.is_unsafe; + if self.is_unsafe { + swap(&mut is_unsafe_holder, &mut meta.context.is_unsafe_ctx); } - } -} - -impl SyntaxModule for CommandModifier { - syntax_name!("Command Modifier"); - - fn new() -> Self { - CommandModifier { - block: Box::new(Block::new()), - expr: Box::new(Expr::new()), - is_expr: false, - is_unsafe: false, - is_silent: false + let result = context(self, meta); + // Swap back the value + if self.is_unsafe { + swap(&mut is_unsafe_holder, &mut meta.context.is_unsafe_ctx); } + result } - fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { - let mut is_matched = false; - let mut sequence = String::new(); - let tok = meta.get_current_token(); + fn parse_modifier_sequence(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { loop { match meta.get_current_token() { Some(tok) => { match tok.word.as_str() { "unsafe" => { + if self.is_unsafe { + return error!(meta, Some(tok.clone()), "You already declared `unsafe` modifier before"); + } self.is_unsafe = true; - is_matched = true; meta.increment_index(); }, "silent" => { + if self.is_silent { + return error!(meta, Some(tok.clone()), "You already declared `silent` modifier before"); + } self.is_silent = true; - is_matched = true; meta.increment_index(); }, - _ => if is_matched { - break; - } else { - return Err(Failure::Quiet(PositionInfo::from_metadata(meta))) - } + _ => break } - sequence.push_str(tok.word.as_str()); - sequence.push(' '); }, None => return Err(Failure::Quiet(PositionInfo::from_metadata(meta))) } } - let is_unsafe = self.is_unsafe; - self.flip_unsafe(meta, is_unsafe); - if self.is_expr { - if let Err(err) = syntax(meta, &mut *self.expr) { - self.flip_unsafe(meta, is_unsafe); - return Err(err) - } - if !matches!(self.expr.value, Some(ExprType::CommandExpr(_) | ExprType::FunctionInvocation(_))) { - sequence = sequence.trim().to_string(); - let count = sequence.split_whitespace().count(); - let plural = if count > 1 { "s" } else { "" }; - self.flip_unsafe(meta, is_unsafe); - return error!(meta, tok, format!("Expected command or function call, after '{sequence}' command modifier{plural}.")); - } - } else { - match token(meta, "{") { - Ok(_) => { - if let Err(err) = syntax(meta, &mut *self.block) { - self.flip_unsafe(meta, is_unsafe); - return Err(err) - } - token(meta, "}")?; - }, - Err(_) => { - let mut statement = Statement::new(); - if let Err(err) = syntax(meta, &mut statement) { - self.flip_unsafe(meta, is_unsafe); - return Err(err) - } - self.block.push_statement(statement); - } - } + Ok(()) + } +} + +impl SyntaxModule for CommandModifier { + syntax_name!("Command Modifier"); + + fn new() -> Self { + CommandModifier { + block: Box::new(Block::new()), + is_block: true, + is_unsafe: false, + is_silent: false + } + } + + fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { + self.parse_modifier_sequence(meta)?; + if self.is_block { + return self.use_modifiers(meta, |this, meta| { + token(meta, "{")?; + syntax(meta, &mut *this.block)?; + token(meta, "}")?; + Ok(()) + }) } - self.flip_unsafe(meta, is_unsafe); Ok(()) } } impl TranslateModule for CommandModifier { fn translate(&self, meta: &mut TranslateMetadata) -> String { - meta.silenced = self.is_silent; - let result = if self.is_expr { - return self.expr.translate(meta) + if self.is_block { + meta.silenced = self.is_silent; + let result = self.block.translate(meta); + meta.silenced = false; + result } else { - self.block.translate(meta) - }; - meta.silenced = false; - result + String::new() + } } -} \ No newline at end of file +} diff --git a/src/modules/command/statement.rs b/src/modules/command/statement.rs deleted file mode 100644 index 61cfc262..00000000 --- a/src/modules/command/statement.rs +++ /dev/null @@ -1,62 +0,0 @@ -use heraclitus_compiler::prelude::*; -use crate::{utils::{ParserMetadata, TranslateMetadata}, modules::{types::{Type, Typed}, condition::failed::Failed}}; -use crate::modules::expression::expr::Expr; -use crate::translate::module::TranslateModule; - -use crate::modules::expression::literal::{parse_interpolated_region, translate_interpolated_region}; - -#[derive(Debug, Clone)] -pub struct CommandStatement { - strings: Vec, - interps: Vec, - failed: Failed -} - -impl Typed for CommandStatement { - fn get_type(&self) -> Type { - Type::Text - } -} - -impl SyntaxModule for CommandStatement { - syntax_name!("CommandStatement"); - - fn new() -> Self { - CommandStatement { - strings: vec![], - interps: vec![], - failed: Failed::new() - } - } - - fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { - let tok = meta.get_current_token(); - (self.strings, self.interps) = parse_interpolated_region(meta, '$')?; - match syntax(meta, &mut self.failed) { - Ok(_) => Ok(()), - Err(Failure::Quiet(_)) => error!(meta, tok => { - message: "Every command statement must handle failed execution", - comment: "You can use '?' in the end to propagate the failure" - }), - Err(err) => Err(err) - } - } -} - -impl TranslateModule for CommandStatement { - fn translate(&self, meta: &mut TranslateMetadata) -> String { - // Translate all interpolations - let interps = self.interps.iter() - .map(|item| item.translate(meta)) - .collect::>(); - let failed = self.failed.translate(meta); - let mut translation = translate_interpolated_region(self.strings.clone(), interps, false); - let silent = meta.gen_silent(); - // Strip down all the inner command interpolations [A32] - while translation.starts_with("$(") { - let end = translation.len() - 1; - translation = translation.get(2..end).unwrap().to_string(); - } - format!("{translation}{silent}\n{failed}").trim_end().to_string() - } -} \ No newline at end of file diff --git a/src/modules/condition/failed.rs b/src/modules/condition/failed.rs index f5317c48..43b9d06b 100644 --- a/src/modules/condition/failed.rs +++ b/src/modules/condition/failed.rs @@ -61,7 +61,6 @@ impl SyntaxModule for Failed { self.is_parsed = true; return Ok(()); } else { - dbg!("FAILED"); return error!(meta, tok, "Failed expression must be followed by a block or statement") } } @@ -105,4 +104,4 @@ impl TranslateModule for Failed { String::new() } } -} \ No newline at end of file +} diff --git a/src/modules/condition/ifchain.rs b/src/modules/condition/ifchain.rs index b9fad677..7f5f6cad 100644 --- a/src/modules/condition/ifchain.rs +++ b/src/modules/condition/ifchain.rs @@ -96,4 +96,4 @@ impl TranslateModule for IfChain { result.push("fi".to_string()); result.join("\n") } -} \ No newline at end of file +} diff --git a/src/modules/condition/ifcond.rs b/src/modules/condition/ifcond.rs index 511bca13..c82881b1 100644 --- a/src/modules/condition/ifcond.rs +++ b/src/modules/condition/ifcond.rs @@ -99,4 +99,4 @@ impl TranslateModule for IfCondition { result.push("fi".to_string()); result.join("\n") } -} \ No newline at end of file +} diff --git a/src/modules/condition/ternary.rs b/src/modules/condition/ternary.rs index 1f7f8268..a55e5063 100644 --- a/src/modules/condition/ternary.rs +++ b/src/modules/condition/ternary.rs @@ -55,4 +55,4 @@ impl TranslateModule for Ternary { let false_expr = self.false_expr.translate(meta); meta.gen_subprocess(&format!("if [ {} != 0 ]; then echo {}; else echo {}; fi", cond, true_expr, false_expr)) } -} \ No newline at end of file +} diff --git a/src/modules/expression/binop/add.rs b/src/modules/expression/binop/add.rs index a42b499a..a91d5171 100644 --- a/src/modules/expression/binop/add.rs +++ b/src/modules/expression/binop/add.rs @@ -58,4 +58,4 @@ impl TranslateModule for Add { _ => translate_computation(meta, ArithOp::Add, Some(left), Some(right)) } } -} \ No newline at end of file +} diff --git a/src/modules/expression/binop/and.rs b/src/modules/expression/binop/and.rs index d17368b5..d0b84115 100644 --- a/src/modules/expression/binop/and.rs +++ b/src/modules/expression/binop/and.rs @@ -44,4 +44,4 @@ impl TranslateModule for And { let right = self.right.translate(meta); translate_computation(meta, ArithOp::And, Some(left), Some(right)) } -} \ No newline at end of file +} diff --git a/src/modules/expression/binop/div.rs b/src/modules/expression/binop/div.rs index ccf4e457..3570079b 100644 --- a/src/modules/expression/binop/div.rs +++ b/src/modules/expression/binop/div.rs @@ -46,4 +46,4 @@ impl TranslateModule for Div { let right = self.right.translate(meta); translate_computation(meta, ArithOp::Div, Some(left), Some(right)) } -} \ No newline at end of file +} diff --git a/src/modules/expression/binop/eq.rs b/src/modules/expression/binop/eq.rs index fdf7471c..dbc16726 100644 --- a/src/modules/expression/binop/eq.rs +++ b/src/modules/expression/binop/eq.rs @@ -54,4 +54,4 @@ impl TranslateModule for Eq { translate_computation(meta, ArithOp::Eq, Some(left), Some(right)) } } -} \ No newline at end of file +} diff --git a/src/modules/expression/binop/ge.rs b/src/modules/expression/binop/ge.rs index e12ed16c..02437481 100644 --- a/src/modules/expression/binop/ge.rs +++ b/src/modules/expression/binop/ge.rs @@ -47,4 +47,4 @@ impl TranslateModule for Ge { let right = self.right.translate(meta); translate_computation(meta, ArithOp::Ge, Some(left), Some(right)) } -} \ No newline at end of file +} diff --git a/src/modules/expression/binop/gt.rs b/src/modules/expression/binop/gt.rs index 834f5abe..0fe53c3d 100644 --- a/src/modules/expression/binop/gt.rs +++ b/src/modules/expression/binop/gt.rs @@ -47,4 +47,4 @@ impl TranslateModule for Gt { let right = self.right.translate(meta); translate_computation(meta, ArithOp::Gt, Some(left), Some(right)) } -} \ No newline at end of file +} diff --git a/src/modules/expression/binop/le.rs b/src/modules/expression/binop/le.rs index bc53ed08..460ea943 100644 --- a/src/modules/expression/binop/le.rs +++ b/src/modules/expression/binop/le.rs @@ -47,4 +47,4 @@ impl TranslateModule for Le { let right = self.right.translate(meta); translate_computation(meta, ArithOp::Le, Some(left), Some(right)) } -} \ No newline at end of file +} diff --git a/src/modules/expression/binop/lt.rs b/src/modules/expression/binop/lt.rs index 37ff7d2e..4757ca38 100644 --- a/src/modules/expression/binop/lt.rs +++ b/src/modules/expression/binop/lt.rs @@ -47,4 +47,4 @@ impl TranslateModule for Lt { let right = self.right.translate(meta); translate_computation(meta, ArithOp::Lt, Some(left), Some(right)) } -} \ No newline at end of file +} diff --git a/src/modules/expression/binop/modulo.rs b/src/modules/expression/binop/modulo.rs index 4c45d428..226b925e 100644 --- a/src/modules/expression/binop/modulo.rs +++ b/src/modules/expression/binop/modulo.rs @@ -47,4 +47,4 @@ impl TranslateModule for Modulo { let right = self.right.translate(meta); translate_computation(meta, ArithOp::Modulo, Some(left), Some(right)) } -} \ No newline at end of file +} diff --git a/src/modules/expression/binop/mul.rs b/src/modules/expression/binop/mul.rs index d94c6fd7..8e1ebb3b 100644 --- a/src/modules/expression/binop/mul.rs +++ b/src/modules/expression/binop/mul.rs @@ -47,4 +47,4 @@ impl TranslateModule for Mul { let right = self.right.translate(meta); translate_computation(meta, ArithOp::Mul, Some(left), Some(right)) } -} \ No newline at end of file +} diff --git a/src/modules/expression/binop/neq.rs b/src/modules/expression/binop/neq.rs index 214d28a3..8458f9cc 100644 --- a/src/modules/expression/binop/neq.rs +++ b/src/modules/expression/binop/neq.rs @@ -53,4 +53,4 @@ impl TranslateModule for Neq { translate_computation(meta, ArithOp::Neq, Some(left), Some(right)) } } -} \ No newline at end of file +} diff --git a/src/modules/expression/binop/or.rs b/src/modules/expression/binop/or.rs index 4ede0200..9c48d772 100644 --- a/src/modules/expression/binop/or.rs +++ b/src/modules/expression/binop/or.rs @@ -44,4 +44,4 @@ impl TranslateModule for Or { let right = self.right.translate(meta); translate_computation(meta, ArithOp::Or, Some(left), Some(right)) } -} \ No newline at end of file +} diff --git a/src/modules/expression/binop/sub.rs b/src/modules/expression/binop/sub.rs index 0e099076..0fbbcb89 100644 --- a/src/modules/expression/binop/sub.rs +++ b/src/modules/expression/binop/sub.rs @@ -47,4 +47,4 @@ impl TranslateModule for Sub { let right = self.right.translate(meta); translate_computation(meta, ArithOp::Sub, Some(left), Some(right)) } -} \ No newline at end of file +} diff --git a/src/modules/expression/expr.rs b/src/modules/expression/expr.rs index 01b8919f..16d8461c 100644 --- a/src/modules/expression/expr.rs +++ b/src/modules/expression/expr.rs @@ -1,4 +1,5 @@ use heraclitus_compiler::prelude::*; +use crate::modules::command::command::Command; use crate::modules::types::{Typed, Type}; use crate::translate::module::TranslateModule; use crate::utils::{ParserMetadata, TranslateMetadata}; @@ -34,7 +35,6 @@ use super::unop::{ }; use super::parenthesis::Parenthesis; use crate::modules::variable::get::VariableGet; -use crate::modules::command::expr::CommandExpr; use crate::modules::condition::ternary::Ternary; use crate::modules::function::invocation::FunctionInvocation; use crate::modules::builtin::nameof::Nameof; @@ -45,7 +45,6 @@ pub enum ExprType { Bool(Bool), Number(Number), Text(Text), - CommandExpr(CommandExpr), Parenthesis(Parenthesis), VariableGet(VariableGet), Add(Add), @@ -65,6 +64,7 @@ pub enum ExprType { Not(Not), Ternary(Ternary), FunctionInvocation(FunctionInvocation), + Command(Command), Array(Array), Range(Range), Null(Null), @@ -111,9 +111,9 @@ impl Expr { // Unary operators Cast, Not, Neg, Nameof, Is, // Literals - Range, Parenthesis, CommandExpr, Bool, Number, Text, Array, Null, Status, + Range, Parenthesis, Bool, Number, Text, Array, Null, Status, // Function invocation - FunctionInvocation, + FunctionInvocation, Command, // Variable access VariableGet ]); diff --git a/src/modules/expression/literal/array.rs b/src/modules/expression/literal/array.rs index bbdd3374..45be7d3c 100644 --- a/src/modules/expression/literal/array.rs +++ b/src/modules/expression/literal/array.rs @@ -91,4 +91,4 @@ impl TranslateModule for Array { meta.stmt_queue.push_back(format!("{name}=({args})")); format!("{quote}{dollar}{{{name}[@]}}{quote}") } -} \ No newline at end of file +} diff --git a/src/modules/expression/literal/bool.rs b/src/modules/expression/literal/bool.rs index 12fab70a..c73af016 100644 --- a/src/modules/expression/literal/bool.rs +++ b/src/modules/expression/literal/bool.rs @@ -26,7 +26,7 @@ impl SyntaxModule for Bool { fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { let value = token_by(meta, |value| ["true", "false"].contains(&value.as_str()))?; self.value = value == "true"; - Ok(()) + Ok(()) } } @@ -34,4 +34,4 @@ impl TranslateModule for Bool { fn translate(&self, _meta: &mut TranslateMetadata) -> String { format!("{}", if self.value { 1 } else { 0 }) } -} \ No newline at end of file +} diff --git a/src/modules/expression/literal/null.rs b/src/modules/expression/literal/null.rs index fd5a624e..c2cbc7a2 100644 --- a/src/modules/expression/literal/null.rs +++ b/src/modules/expression/literal/null.rs @@ -20,7 +20,7 @@ impl SyntaxModule for Null { fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { token(meta, "null")?; - Ok(()) + Ok(()) } } @@ -28,4 +28,4 @@ impl TranslateModule for Null { fn translate(&self, _meta: &mut TranslateMetadata) -> String { "''".to_string() } -} \ No newline at end of file +} diff --git a/src/modules/expression/literal/range.rs b/src/modules/expression/literal/range.rs index adc29e9d..03ce9405 100644 --- a/src/modules/expression/literal/range.rs +++ b/src/modules/expression/literal/range.rs @@ -53,4 +53,4 @@ impl TranslateModule for Range { meta.gen_subprocess(&format!("seq {} {}", from, to)) } } -} \ No newline at end of file +} diff --git a/src/modules/expression/literal/status.rs b/src/modules/expression/literal/status.rs index d53eaddc..7b513c2c 100644 --- a/src/modules/expression/literal/status.rs +++ b/src/modules/expression/literal/status.rs @@ -21,7 +21,7 @@ impl SyntaxModule for Status { fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { token(meta, "status")?; - Ok(()) + Ok(()) } } @@ -29,4 +29,4 @@ impl TranslateModule for Status { fn translate(&self, _meta: &mut TranslateMetadata) -> String { "$__AS".to_string() } -} \ No newline at end of file +} diff --git a/src/modules/expression/literal/text.rs b/src/modules/expression/literal/text.rs index 40a0278c..e04a8b7e 100644 --- a/src/modules/expression/literal/text.rs +++ b/src/modules/expression/literal/text.rs @@ -42,4 +42,4 @@ impl TranslateModule for Text { let quote = meta.gen_quote(); format!("{quote}{}{quote}", translate_interpolated_region(self.strings.clone(), interps, true)) } -} \ No newline at end of file +} diff --git a/src/modules/expression/parenthesis.rs b/src/modules/expression/parenthesis.rs index b6a8d00e..4543d5d4 100644 --- a/src/modules/expression/parenthesis.rs +++ b/src/modules/expression/parenthesis.rs @@ -38,4 +38,4 @@ impl TranslateModule for Parenthesis { fn translate(&self, meta: &mut crate::utils::TranslateMetadata) -> String { self.value.translate(meta) } -} \ No newline at end of file +} diff --git a/src/modules/expression/unop/cast.rs b/src/modules/expression/unop/cast.rs index 2fdbaa9c..8b65e03c 100644 --- a/src/modules/expression/unop/cast.rs +++ b/src/modules/expression/unop/cast.rs @@ -55,4 +55,4 @@ impl TranslateModule for Cast { fn translate(&self, meta: &mut TranslateMetadata) -> String { self.expr.translate(meta) } -} \ No newline at end of file +} diff --git a/src/modules/expression/unop/is.rs b/src/modules/expression/unop/is.rs index bb2ff7a4..0efa3720 100644 --- a/src/modules/expression/unop/is.rs +++ b/src/modules/expression/unop/is.rs @@ -43,4 +43,4 @@ impl TranslateModule for Is { "0".to_string() } } -} \ No newline at end of file +} diff --git a/src/modules/function/declaration.rs b/src/modules/function/declaration.rs index e511fab1..bc75d924 100644 --- a/src/modules/function/declaration.rs +++ b/src/modules/function/declaration.rs @@ -165,7 +165,7 @@ impl TranslateModule for FunctionDeclaration { // Parse the function body result.push(format!("function {name} {{")); if let Some(args) = self.set_args_as_variables(meta, function, &self.arg_refs) { - result.push(args); + result.push(args); } result.push(function.block.translate(meta)); result.push(meta.gen_indent() + "}"); @@ -175,4 +175,4 @@ impl TranslateModule for FunctionDeclaration { // Return the translation result.join("\n") } -} \ No newline at end of file +} diff --git a/src/modules/function/fail.rs b/src/modules/function/fail.rs index 29033855..b87c6972 100644 --- a/src/modules/function/fail.rs +++ b/src/modules/function/fail.rs @@ -82,4 +82,4 @@ impl TranslateModule for Fail { format!("return {translate}") } } -} \ No newline at end of file +} diff --git a/src/modules/function/invocation.rs b/src/modules/function/invocation.rs index fd0e4521..19c1efd6 100644 --- a/src/modules/function/invocation.rs +++ b/src/modules/function/invocation.rs @@ -1,5 +1,8 @@ +use std::mem::swap; + use heraclitus_compiler::prelude::*; use itertools::izip; +use crate::modules::command::modifier::CommandModifier; use crate::modules::condition::failed::Failed; use crate::modules::types::{Type, Typed}; use crate::modules::variable::variable_name_extensions; @@ -18,6 +21,7 @@ pub struct FunctionInvocation { id: usize, line: usize, failed: Failed, + modifier: CommandModifier, is_failable: bool } @@ -40,57 +44,61 @@ impl SyntaxModule for FunctionInvocation { id: 0, line: 0, failed: Failed::new(), + modifier: CommandModifier::new().parse_expr(), is_failable: false } } fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { - // Get the function name - let tok = meta.get_current_token(); - if let Some(ref tok) = tok { - self.line = tok.pos.0; - } - self.name = variable(meta, variable_name_extensions())?; - // Get the arguments - token(meta, "(")?; - self.id = handle_function_reference(meta, tok.clone(), &self.name)?; - loop { - if token(meta, ")").is_ok() { - break + syntax(meta, &mut self.modifier)?; + self.modifier.use_modifiers(meta, |_this, meta| { + // Get the function name + let tok = meta.get_current_token(); + if let Some(ref tok) = tok { + self.line = tok.pos.0; } - let mut expr = Expr::new(); - syntax(meta, &mut expr)?; - self.args.push(expr); - match token(meta, ")") { - Ok(_) => break, - Err(_) => token(meta, ",")? - }; - } - let function_unit = meta.get_fun_declaration(&self.name).unwrap().clone(); - self.is_failable = function_unit.is_failable; - if self.is_failable { - match syntax(meta, &mut self.failed) { - Ok(_) => {}, - Err(Failure::Quiet(_)) => return error!(meta, tok => { - message: "This function can fail. Please handle the failure", - comment: "You can use '?' in the end to propagate the failure" - }), - Err(err) => return Err(err) + self.name = variable(meta, variable_name_extensions())?; + // Get the arguments + token(meta, "(")?; + self.id = handle_function_reference(meta, tok.clone(), &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, ",")? + }; } - } else { - let tok = meta.get_current_token(); - if let Ok(symbol) = token_by(meta, |word| ["?", "failed"].contains(&word.as_str())) { - let message = Message::new_warn_at_token(meta, tok) - .message("This function cannot fail") - .comment(format!("You can remove the '{symbol}' in the end")); - meta.add_message(message); + let function_unit = meta.get_fun_declaration(&self.name).unwrap().clone(); + self.is_failable = function_unit.is_failable; + if self.is_failable { + match syntax(meta, &mut self.failed) { + Ok(_) => {}, + Err(Failure::Quiet(_)) => return error!(meta, tok => { + message: "This function can fail. Please handle the failure", + comment: "You can use '?' in the end to propagate the failure" + }), + Err(err) => return Err(err) + } + } else { + let tok = meta.get_current_token(); + if let Ok(symbol) = token_by(meta, |word| ["?", "failed"].contains(&word.as_str())) { + let message = Message::new_warn_at_token(meta, tok) + .message("This function cannot fail") + .comment(format!("You can remove the '{symbol}' in the end")); + meta.add_message(message); + } } - } - let types = self.args.iter().map(|e| e.get_type()).collect::>(); - let var_names = self.args.iter().map(|e| e.is_var()).collect::>(); - self.refs = function_unit.arg_refs.clone(); - (self.kind, self.variant_id) = handle_function_parameters(meta, self.id, function_unit, &types, &var_names, tok)?; - Ok(()) + let types = self.args.iter().map(|e| e.get_type()).collect::>(); + let var_names = self.args.iter().map(|e| e.is_var()).collect::>(); + self.refs = function_unit.arg_refs.clone(); + (self.kind, self.variant_id) = handle_function_parameters(meta, self.id, function_unit, &types, &var_names, tok)?; + Ok(()) + }) } } @@ -111,6 +119,8 @@ impl FunctionInvocation { impl TranslateModule for FunctionInvocation { fn translate(&self, meta: &mut TranslateMetadata) -> String { let name = format!("{}__{}_v{}", self.name, self.id, self.variant_id); + let mut is_silent = self.modifier.is_silent || meta.silenced; + swap(&mut is_silent, &mut meta.silenced); let silent = meta.gen_silent(); let args = izip!(self.args.iter(), self.refs.iter()).map(| (arg, is_ref) | { if *is_ref { @@ -127,6 +137,7 @@ impl TranslateModule for FunctionInvocation { let invocation_return = &format!("__AF_{}{}_v{}", self.name, self.id, self.variant_id); let invocation_instance = &format!("__AF_{}{}_v{}__{}", self.name, self.id, self.variant_id, self.line); let parsed_invocation_return = self.get_variable(meta, invocation_return, true); + swap(&mut is_silent, &mut meta.silenced); if self.is_failable { let failed = self.failed.translate(meta); meta.stmt_queue.push_back(failed); @@ -141,4 +152,4 @@ impl TranslateModule for FunctionInvocation { ); self.get_variable(meta, invocation_instance, false) } -} \ No newline at end of file +} diff --git a/src/modules/function/ret.rs b/src/modules/function/ret.rs index cf6f883d..cd5f15f0 100644 --- a/src/modules/function/ret.rs +++ b/src/modules/function/ret.rs @@ -59,4 +59,4 @@ impl TranslateModule for Return { meta.stmt_queue.push_back(format!("__AF_{name}{id}_v{variant}={result}")); "return 0".to_string() } -} \ No newline at end of file +} diff --git a/src/modules/imports/import.rs b/src/modules/imports/import.rs index 22640a80..c2c7b9ce 100644 --- a/src/modules/imports/import.rs +++ b/src/modules/imports/import.rs @@ -174,4 +174,4 @@ impl TranslateModule for Import { fn translate(&self, _meta: &mut TranslateMetadata) -> String { "".to_string() } -} \ No newline at end of file +} diff --git a/src/modules/loops/infinite_loop.rs b/src/modules/loops/infinite_loop.rs index c92ad9a7..e3a4bf1a 100644 --- a/src/modules/loops/infinite_loop.rs +++ b/src/modules/loops/infinite_loop.rs @@ -41,4 +41,4 @@ impl TranslateModule for InfiniteLoop { self.block.translate(meta), "done".to_string()].join("\n") } -} \ No newline at end of file +} diff --git a/src/modules/main.rs b/src/modules/main.rs index 63e974fe..45802fad 100644 --- a/src/modules/main.rs +++ b/src/modules/main.rs @@ -75,4 +75,4 @@ impl TranslateModule for Main { format!("{args}\n{}", self.block.translate(meta)) } } -} \ No newline at end of file +} diff --git a/src/modules/shorthand/add.rs b/src/modules/shorthand/add.rs index 40cbbe23..aeb451a4 100644 --- a/src/modules/shorthand/add.rs +++ b/src/modules/shorthand/add.rs @@ -73,4 +73,4 @@ impl TranslateModule for ShorthandAdd { stmt } } -} \ No newline at end of file +} diff --git a/src/modules/shorthand/div.rs b/src/modules/shorthand/div.rs index 76d70eca..5f44970c 100644 --- a/src/modules/shorthand/div.rs +++ b/src/modules/shorthand/div.rs @@ -63,4 +63,4 @@ impl TranslateModule for ShorthandDiv { format!("{}={}", name, eval) } } -} \ No newline at end of file +} diff --git a/src/modules/shorthand/modulo.rs b/src/modules/shorthand/modulo.rs index 0ab0800b..18b1c56c 100644 --- a/src/modules/shorthand/modulo.rs +++ b/src/modules/shorthand/modulo.rs @@ -63,4 +63,4 @@ impl TranslateModule for ShorthandModulo { format!("{}={}", name, expr) } } -} \ No newline at end of file +} diff --git a/src/modules/shorthand/mul.rs b/src/modules/shorthand/mul.rs index c9bfaeaa..10ea112d 100644 --- a/src/modules/shorthand/mul.rs +++ b/src/modules/shorthand/mul.rs @@ -63,4 +63,4 @@ impl TranslateModule for ShorthandMul { format!("{}={}", name, expr) } } -} \ No newline at end of file +} diff --git a/src/modules/shorthand/sub.rs b/src/modules/shorthand/sub.rs index b920217d..8fa33afc 100644 --- a/src/modules/shorthand/sub.rs +++ b/src/modules/shorthand/sub.rs @@ -63,4 +63,4 @@ impl TranslateModule for ShorthandSub { format!("{}={}", name, expr) } } -} \ No newline at end of file +} diff --git a/src/modules/statement/stmt.rs b/src/modules/statement/stmt.rs index 3f95e044..35da270e 100644 --- a/src/modules/statement/stmt.rs +++ b/src/modules/statement/stmt.rs @@ -1,16 +1,13 @@ use heraclitus_compiler::prelude::*; use itertools::Itertools; use crate::utils::metadata::{ParserMetadata, TranslateMetadata}; -use crate::modules::expression::expr::Expr; +use crate::modules::expression::expr::{Expr, ExprType}; use crate::translate::module::TranslateModule; use crate::modules::variable::{ init::VariableInit, set::VariableSet }; -use crate::modules::command::{ - statement::CommandStatement, - modifier::CommandModifier -}; +use crate::modules::command::modifier::CommandModifier; use crate::handle_types; use crate::modules::condition::{ ifchain::IfChain, @@ -47,7 +44,6 @@ pub enum StatementType { Expr(Expr), VariableInit(VariableInit), VariableSet(VariableSet), - CommandStatement(CommandStatement), IfCondition(IfCondition), IfChain(IfChain), ShorthandAdd(ShorthandAdd), @@ -90,7 +86,7 @@ impl Statement { ShorthandMul, ShorthandDiv, ShorthandModulo, // Command - CommandModifier, CommandStatement, Echo, + CommandModifier, Echo, // Expression Expr ]); @@ -104,7 +100,7 @@ impl Statement { match syntax(meta, &mut module) { Ok(()) => { self.value = Some(cb(module)); - Ok(()) + Ok(()) } Err(details) => Err(details) } @@ -148,15 +144,19 @@ impl SyntaxModule for Statement { impl TranslateModule for Statement { fn translate(&self, meta: &mut TranslateMetadata) -> String { - // Translate the statement - let translated = self.translate_match(meta, self.value.as_ref().unwrap()); + // Translate the staxtement + let statement = self.value.as_ref().unwrap(); // This is a workaround that handles $(...) which cannot be used as a statement - let translated = (matches!(self.value, Some(StatementType::Expr(_))) || translated.starts_with("$(") || translated.starts_with("\"$(")) - .then(|| format!("echo {} > /dev/null 2>&1", translated)) - .unwrap_or(translated); + let translated = match statement { + StatementType::Expr(expr) => match &expr.value { + Some(ExprType::Command(cmd)) => cmd.translate_command_statement(meta), + _ => format!("echo {} > /dev/null 2>&1", self.translate_match(meta, statement)) + }, + _ => self.translate_match(meta, statement) + }; // Get all the required supplemental statements let indentation = meta.gen_indent(); - let statements = meta.stmt_queue.drain(..).map(|st| indentation.clone() + &st + ";\n").join(""); + let statements = meta.stmt_queue.drain(..).map(|st| indentation.clone() + &st.trim_end_matches(';') + ";\n").join(""); // Return all the statements statements + &indentation + &translated } diff --git a/src/modules/variable/get.rs b/src/modules/variable/get.rs index 3ea2217b..8bb517df 100644 --- a/src/modules/variable/get.rs +++ b/src/modules/variable/get.rs @@ -86,4 +86,4 @@ impl TranslateModule for VariableGet { _ => res } } -} \ No newline at end of file +} diff --git a/src/modules/variable/init.rs b/src/modules/variable/init.rs index f1df0ef2..63ed6494 100644 --- a/src/modules/variable/init.rs +++ b/src/modules/variable/init.rs @@ -64,4 +64,4 @@ impl TranslateModule for VariableInit { None => format!("{local}{name}={expr}") } } -} \ No newline at end of file +} diff --git a/src/modules/variable/set.rs b/src/modules/variable/set.rs index d9abb8ac..e2c29dd0 100644 --- a/src/modules/variable/set.rs +++ b/src/modules/variable/set.rs @@ -25,7 +25,7 @@ impl SyntaxModule for VariableSet { is_ref: false } } - + fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { let tok = meta.get_current_token(); self.name = variable(meta, variable_name_extensions())?; @@ -83,4 +83,4 @@ impl TranslateModule for VariableSet { } } } -} \ No newline at end of file +} diff --git a/src/tests/validity.rs b/src/tests/validity.rs index 80e3df4a..00067fda 100644 --- a/src/tests/validity.rs +++ b/src/tests/validity.rs @@ -1084,3 +1084,18 @@ fn main_args() { "; test_amber!(code, "ok") } + +#[test] +fn unsafe_function_call() { + let code = " + fun safe_division(a: Num, b: Num): Num { + if b == 0: + fail 1 + return a / b + } + + let result = unsafe safe_division(24, 4) + echo \"{result}, {status}\" + "; + test_amber!(code, "6, 0") +} diff --git a/src/translate/module.rs b/src/translate/module.rs index 9e958d23..fc7a6e1c 100644 --- a/src/translate/module.rs +++ b/src/translate/module.rs @@ -10,4 +10,4 @@ pub trait TranslateModule { meta.eval_ctx = prev; expr } -} \ No newline at end of file +}