diff --git a/install.amber b/install.amber new file mode 100644 index 00000000..2c8295c8 --- /dev/null +++ b/install.amber @@ -0,0 +1,5 @@ +let tag = '1.0.0' +let place = '/opt/amber' +let url = 'https://github.com/Ph0enixKM/Amber/releases/download/{tag}/amber.zip' + +$test -d "{place}"$ diff --git a/out.sh b/out.sh index 449cb90f..6fba8046 100755 --- a/out.sh +++ b/out.sh @@ -1,4 +1,6 @@ -a=12; -b=2; -a=$(echo $a '+' 12 | bc -l | sed '/\./ s/\.\{0,1\}0\{1,\}$//'); -echo ($(echo $a '+' $b | bc -l | sed '/\./ s/\.\{0,1\}0\{1,\}$//')) \ No newline at end of file +age=22; +if [ $(echo $age '>=' 18 | bc -l | sed '/\./ s/\.\{0,1\}0\{1,\}$//') != 0 ]; then +echo "You can drive" +else +echo "You cannot drive" +fi \ No newline at end of file diff --git a/src/cli/cli.rs b/src/cli/cli.rs index b7b47607..7b30ed57 100644 --- a/src/cli/cli.rs +++ b/src/cli/cli.rs @@ -114,8 +114,9 @@ impl CLI { let mut meta = TranslateMetadata::new(); return format!("{}", block.translate(&mut meta)); } + return format!("[parsing err]") } - format!("[err]") + format!("[lexing err]") } fn execute(&self, code: String) { diff --git a/src/cli/mod.rs b/src/cli/mod.rs index e6f8e33c..7c902809 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -132,4 +132,86 @@ mod tests { "; assert_eq!(cli.test_eval(code).trim(), ""); } + + #[test] + fn compare_eq_texts() { + let cli = CLI::new(); + let code = " + let x = 'Hello World' + let y = 'Hello World' + $echo {x == y}$ + "; + assert_eq!(cli.test_eval(code).trim(), "1"); + } + + #[test] + fn compare_eq_numbers() { + let cli = CLI::new(); + let code = " + let x = 42 + let y = 42 + $echo {x == y}$ + "; + assert_eq!(cli.test_eval(code).trim(), "1"); + } + + #[test] + fn compare_neq_numbers() { + let cli = CLI::new(); + let code = " + let x = 42 + let y = 24 + $echo {x != y}$ + "; + assert_eq!(cli.test_eval(code).trim(), "1"); + } + + #[test] + fn if_statements() { + let cli = CLI::new(); + let code = " + let x = 42 + let y = 24 + if x == y { + $echo {x}$ + } else { + $echo {y}$ + } + "; + assert_eq!(cli.test_eval(code).trim(), "24"); + } + + #[test] + fn if_statements_else() { + let cli = CLI::new(); + let code = " + let x = 42 + let y = 24 + if x == y { + $echo {x}$ + } + else { + $echo {y}$ + } + "; + assert_eq!(cli.test_eval(code).trim(), "24"); + } + + #[test] + fn if_statement_chain() { + let cli = CLI::new(); + let code = " + let x = 42 + let y = 24 + if { + x == y { + $echo {x}$ + } + else { + $echo {y}$ + } + } + "; + assert_eq!(cli.test_eval(code).trim(), "24"); + } } \ No newline at end of file diff --git a/src/modules/block.rs b/src/modules/block.rs index 01242ac4..b877343c 100644 --- a/src/modules/block.rs +++ b/src/modules/block.rs @@ -37,11 +37,13 @@ impl SyntaxModule for Block { continue; } // Handle comments - else if let Some(token) = meta.get_current_token() { - if token.word.starts_with("#") { - meta.increment_index(); - continue - } + if token.word.starts_with("#") { + meta.increment_index(); + continue + } + // Handle block end + else if token.word == "}" { + break; } } None => break diff --git a/src/modules/conditions/ifchain.rs b/src/modules/conditions/ifchain.rs new file mode 100644 index 00000000..d1a750fb --- /dev/null +++ b/src/modules/conditions/ifchain.rs @@ -0,0 +1,81 @@ +use heraclitus_compiler::prelude::*; +use crate::modules::expression::expr::Expr; +use crate::translate::module::TranslateModule; +use crate::utils::metadata::{ParserMetadata, TranslateMetadata}; +use crate::modules::block::Block; + +#[derive(Debug)] +pub struct IfChain { + cond_blocks: Vec<(Expr, Block)>, + false_block: Option> +} + +impl SyntaxModule for IfChain { + syntax_name!("If Condition"); + + fn new() -> Self { + IfChain { + cond_blocks: vec![], + false_block: None + } + } + + fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { + let mut is_else = false; + token(meta, "if")?; + // Parse true block + token(meta, "{")?; + loop { + let mut cond = Expr::new(); + let mut block = Block::new(); + cond.cannot_fail(); + // Handle else keyword + if let Ok(_) = token(meta, "else") { + is_else = true; + break; + } + // Handle end of the if chain + if let Err(_) = syntax(meta, &mut cond) { + println!("{:?}", meta.get_current_token()); + break + } + token(meta, "{")?; + syntax(meta, &mut block)?; + token(meta, "}")?; + self.cond_blocks.push((cond, block)); + } + // Parse false block + if is_else { + token(meta, "{")?; + let mut false_block = Box::new(Block::new()); + syntax(meta, &mut *false_block)?; + self.false_block = Some(false_block); + token(meta, "}")?; + } + token(meta, "}")?; + Ok(()) + } +} + +impl TranslateModule for IfChain { + fn translate(&self, meta: &mut TranslateMetadata) -> String { + let mut result = vec![]; + let mut is_first = true; + for (cond, block) in self.cond_blocks.iter() { + if is_first { + result.push(format!("if [ {} != 0 ]; then", cond.translate(meta))); + result.push(block.translate(meta)); + is_first = false; + } else { + result.push(format!("elif [ {} != 0 ]; then", cond.translate(meta))); + result.push(block.translate(meta)); + } + } + if let Some(false_block) = &self.false_block { + result.push(format!("else")); + result.push(false_block.translate(meta)); + } + result.push(format!("fi")); + result.join("\n") + } +} \ No newline at end of file diff --git a/src/modules/conditions/ifcond.rs b/src/modules/conditions/ifcond.rs new file mode 100644 index 00000000..a0633b41 --- /dev/null +++ b/src/modules/conditions/ifcond.rs @@ -0,0 +1,57 @@ +use heraclitus_compiler::prelude::*; +use crate::modules::expression::expr::Expr; +use crate::translate::module::TranslateModule; +use crate::utils::metadata::{ParserMetadata, TranslateMetadata}; +use crate::modules::block::Block; + +#[derive(Debug)] +pub struct IfCondition { + expr: Box, + true_block: Box, + false_block: Option> +} + +impl SyntaxModule for IfCondition { + syntax_name!("If Condition"); + + fn new() -> Self { + IfCondition { + expr: Box::new(Expr::new()), + true_block: Box::new(Block::new()), + false_block: None + } + } + + fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { + token(meta, "if")?; + // Parse expression + syntax(meta, &mut *self.expr)?; + // Parse true block + token(meta, "{")?; + syntax(meta, &mut *self.true_block)?; + token(meta, "}")?; + // Parse false block + if let Ok(_) = token(meta, "else") { + token(meta, "{")?; + let mut false_block = Box::new(Block::new()); + syntax(meta, &mut *false_block)?; + self.false_block = Some(false_block); + token(meta, "}")?; + } + Ok(()) + } +} + +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.true_block.translate(meta)); + if let Some(false_block) = &self.false_block { + result.push(format!("else")); + result.push(false_block.translate(meta)); + } + result.push(format!("fi")); + result.join("\n") + } +} \ No newline at end of file diff --git a/src/modules/conditions/mod.rs b/src/modules/conditions/mod.rs new file mode 100644 index 00000000..7c1fa13a --- /dev/null +++ b/src/modules/conditions/mod.rs @@ -0,0 +1,2 @@ +pub mod ifcond; +pub mod ifchain; \ No newline at end of file diff --git a/src/modules/expression/binop/eq.rs b/src/modules/expression/binop/eq.rs index 7dd4eecc..d9b9faeb 100644 --- a/src/modules/expression/binop/eq.rs +++ b/src/modules/expression/binop/eq.rs @@ -42,6 +42,11 @@ impl TranslateModule for Eq { fn translate(&self, meta: &mut TranslateMetadata) -> String { let left = self.left.translate(meta); let right = self.right.translate(meta); - translate_computation(meta, ArithOp::Eq, Some(left), Some(right)) + // Handle text comparison + if self.left.get_type() == Type::Text && self.right.get_type() == Type::Text { + format!("$([ \"_{left}\" != \"_{right}\" ]; echo $?)") + } else { + 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 97e6ec17..02bb4742 100644 --- a/src/modules/expression/binop/ge.rs +++ b/src/modules/expression/binop/ge.rs @@ -2,7 +2,7 @@ use heraclitus_compiler::prelude::*; use crate::translate::compute::{ArithOp, translate_computation}; use crate::utils::{ParserMetadata, TranslateMetadata}; use crate::translate::module::TranslateModule; -use super::{super::expr::Expr, parse_left_expr, expression_arms_of_same_type}; +use super::{super::expr::Expr, parse_left_expr, expression_arms_of_type}; use crate::modules::{Type, Typed}; #[derive(Debug)] @@ -33,7 +33,7 @@ impl SyntaxModule for Ge { token(meta, ">=")?; syntax(meta, &mut *self.right)?; let error = "Cannot compare two values of different types"; - expression_arms_of_same_type(meta, &self.left, &self.right, tok, error); + expression_arms_of_type(meta, &self.left, &self.right, Type::Num, tok, error); Ok(()) } } diff --git a/src/modules/expression/binop/gt.rs b/src/modules/expression/binop/gt.rs index e21950a7..3eb28ee2 100644 --- a/src/modules/expression/binop/gt.rs +++ b/src/modules/expression/binop/gt.rs @@ -2,7 +2,7 @@ use heraclitus_compiler::prelude::*; use crate::translate::compute::{ArithOp, translate_computation}; use crate::utils::{ParserMetadata, TranslateMetadata}; use crate::translate::module::TranslateModule; -use super::{super::expr::Expr, parse_left_expr, expression_arms_of_same_type}; +use super::{super::expr::Expr, parse_left_expr, expression_arms_of_type}; use crate::modules::{Type, Typed}; #[derive(Debug)] @@ -33,7 +33,7 @@ impl SyntaxModule for Gt { token(meta, ">")?; syntax(meta, &mut *self.right)?; let error = "Cannot compare two values of different types"; - expression_arms_of_same_type(meta, &self.left, &self.right, tok, error); + expression_arms_of_type(meta, &self.left, &self.right, Type::Num, tok, error); Ok(()) } } diff --git a/src/modules/expression/binop/le.rs b/src/modules/expression/binop/le.rs index 3a0b9986..bae94aa5 100644 --- a/src/modules/expression/binop/le.rs +++ b/src/modules/expression/binop/le.rs @@ -2,7 +2,7 @@ use heraclitus_compiler::prelude::*; use crate::translate::compute::{ArithOp, translate_computation}; use crate::utils::{ParserMetadata, TranslateMetadata}; use crate::translate::module::TranslateModule; -use super::{super::expr::Expr, parse_left_expr, expression_arms_of_same_type}; +use super::{super::expr::Expr, parse_left_expr, expression_arms_of_type}; use crate::modules::{Type, Typed}; #[derive(Debug)] @@ -33,7 +33,7 @@ impl SyntaxModule for Le { token(meta, "<=")?; syntax(meta, &mut *self.right)?; let error = "Cannot compare two values of different types"; - expression_arms_of_same_type(meta, &self.left, &self.right, tok, error); + expression_arms_of_type(meta, &self.left, &self.right, Type::Num, tok, error); Ok(()) } } diff --git a/src/modules/expression/binop/lt.rs b/src/modules/expression/binop/lt.rs index 656a8b6a..c5b64411 100644 --- a/src/modules/expression/binop/lt.rs +++ b/src/modules/expression/binop/lt.rs @@ -2,7 +2,7 @@ use heraclitus_compiler::prelude::*; use crate::translate::compute::{translate_computation, ArithOp}; use crate::utils::{ParserMetadata, TranslateMetadata}; use crate::translate::module::TranslateModule; -use super::{super::expr::Expr, parse_left_expr, expression_arms_of_same_type}; +use super::{super::expr::Expr, parse_left_expr, expression_arms_of_type}; use crate::modules::{Type, Typed}; #[derive(Debug)] @@ -33,7 +33,7 @@ impl SyntaxModule for Lt { token(meta, "<")?; syntax(meta, &mut *self.right)?; let error = "Cannot compare two values of different types"; - expression_arms_of_same_type(meta, &self.left, &self.right, tok, error); + expression_arms_of_type(meta, &self.left, &self.right, Type::Num, tok, error); Ok(()) } } diff --git a/src/modules/expression/binop/neq.rs b/src/modules/expression/binop/neq.rs index 77c7fbc7..0b9ad904 100644 --- a/src/modules/expression/binop/neq.rs +++ b/src/modules/expression/binop/neq.rs @@ -42,6 +42,10 @@ impl TranslateModule for Neq { fn translate(&self, meta: &mut TranslateMetadata) -> String { let left = self.left.translate(meta); let right = self.right.translate(meta); - translate_computation(meta, ArithOp::Neq, Some(left), Some(right)) + if self.left.get_type() == Type::Text && self.right.get_type() == Type::Text { + format!("$([ \"_{left}\" == \"_{right}\" ]; echo $?)") + } else { + translate_computation(meta, ArithOp::Neq, 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 c89d973d..6fdb99ed 100644 --- a/src/modules/expression/expr.rs +++ b/src/modules/expression/expr.rs @@ -56,6 +56,7 @@ pub enum ExprType { #[derive(Debug)] pub struct Expr { value: Option, + can_fail: bool, kind: Type } @@ -78,6 +79,10 @@ impl Expr { VariableGet ]); + pub fn cannot_fail(&mut self) { + self.can_fail = false; + } + // Get result out of the provided module and save it in the internal state fn get(&mut self, meta: &mut ParserMetadata, mut module: S, cb: impl Fn(S) -> ExprType) -> SyntaxResult where @@ -107,7 +112,8 @@ impl SyntaxModule for Expr { fn new() -> Self { Expr { value: None, - kind: Type::Null + kind: Type::Null, + can_fail: true } } @@ -127,7 +133,9 @@ impl SyntaxModule for Expr { Err(details) => error = Some(details) } } - self.error(meta); + if self.can_fail { + self.error(meta); + } Err(error.unwrap()) } } diff --git a/src/modules/mod.rs b/src/modules/mod.rs index dde99a68..7703b3c5 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -3,6 +3,7 @@ pub mod expression; pub mod block; pub mod variable; pub mod command; +pub mod conditions; #[macro_export] macro_rules! handle_types { diff --git a/src/modules/statement/statement.rs b/src/modules/statement/statement.rs index 31a466bc..daaf055c 100644 --- a/src/modules/statement/statement.rs +++ b/src/modules/statement/statement.rs @@ -8,13 +8,19 @@ use crate::modules::variable::{ }; use crate::modules::command::statement::CommandStatement; use crate::handle_types; +use crate::modules::conditions::{ + ifchain::IfChain, + ifcond::IfCondition +}; #[derive(Debug)] enum StatementType { Expr(Expr), VariableInit(VariableInit), VariableSet(VariableSet), - CommandStatement(CommandStatement) + CommandStatement(CommandStatement), + IfCondition(IfCondition), + IfChain(IfChain) } #[derive(Debug)] @@ -24,6 +30,7 @@ pub struct Statement { impl Statement { handle_types!(StatementType, [ + IfChain, IfCondition, // Variables VariableInit, VariableSet, // Command diff --git a/src/rules.rs b/src/rules.rs index 19136e68..1f4198e0 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -34,7 +34,8 @@ pub fn get_rules() -> Rules { ]), reg!(comment as "comment" => { begin: "#", - end: "\n" + end: "\n", + allow_left_open: true }) ]; Rules::new(symbols, compounds, region) diff --git a/test.amber b/test.amber index e1c56c7f..d78d7353 100644 --- a/test.amber +++ b/test.amber @@ -1,5 +1,16 @@ -let a = 12 -let b = 2 # test -let c = 3 # test -$echo {(a + b) * c}$ -# test +let soda = 'other' + +if { + soda == 'coke' { + $echo "I like coke"$ + } + soda == 'sprite' { + $echo "I like sprite"$ + } + soda == 'fanta' { + $echo "I like fanta"$ + } + else { + $echo "I don't like soda"$ + } +}