From 1bb77036e0a3126d898e8ccedce0a3b993ef0f27 Mon Sep 17 00:00:00 2001
From: Ph0enixKM <pkaras.it@gmail.com>
Date: Tue, 27 Sep 2022 15:46:05 +0200
Subject: [PATCH] feat: add main block

---
 src/cli/cli_interface.rs         |  4 +-
 src/cli/flag_registry.rs         | 20 +++++++-
 src/compiler.rs                  |  3 +-
 src/modules/block.rs             | 17 +++----
 src/modules/condition/ifchain.rs |  1 -
 src/modules/expression/expr.rs   |  8 +---
 src/modules/imports/import.rs    |  4 +-
 src/modules/main.rs              | 82 ++++++++++++++++++++++++++++++++
 src/modules/mod.rs               |  1 +
 src/modules/statement/stmt.rs    | 20 ++++----
 src/utils/memory.rs              |  8 +++-
 tests/io/print.ab                |  4 ++
 12 files changed, 136 insertions(+), 36 deletions(-)
 create mode 100644 src/modules/main.rs

diff --git a/src/cli/cli_interface.rs b/src/cli/cli_interface.rs
index 5f64e746..2e191390 100644
--- a/src/cli/cli_interface.rs
+++ b/src/cli/cli_interface.rs
@@ -43,7 +43,7 @@ impl CLI {
             match self.flags.get_flag("-e").unwrap().value.clone() {
                 Some(code) => {
                     match AmberCompiler::new(code, None).compile() {
-                        Ok(code) => AmberCompiler::execute(code),
+                        Ok(code) => AmberCompiler::execute(code, self.flags.get_args()),
                         Err(err) => {
                             err.show();
                             std::process::exit(1);
@@ -71,7 +71,7 @@ impl CLI {
                             }
                             // Execute the code
                             else {
-                                AmberCompiler::execute(code);
+                                AmberCompiler::execute(code, self.flags.get_args());
                             }
                         },
                         Err(err) => {
diff --git a/src/cli/flag_registry.rs b/src/cli/flag_registry.rs
index 95e6207b..82f393c0 100644
--- a/src/cli/flag_registry.rs
+++ b/src/cli/flag_registry.rs
@@ -17,7 +17,8 @@ impl Flag {
 }
 
 pub struct FlagRegistry {
-    flags: HashMap<String, Flag>
+    flags: HashMap<String, Flag>,
+    args: Vec<String>
 }
 
 impl Default for FlagRegistry {
@@ -30,7 +31,8 @@ impl FlagRegistry {
     #[inline]
     pub fn new() -> Self {
         FlagRegistry {
-            flags: HashMap::new()
+            flags: HashMap::new(),
+            args: vec![]
         }
     }
 
@@ -39,6 +41,11 @@ impl FlagRegistry {
         self.flags.get(name.as_ref())
     }
 
+    #[inline]
+    pub fn get_args(&self) -> &Vec<String> {
+        &self.args
+    }
+
     #[inline]
     pub fn flag_triggered(&self, name: impl AsRef<str>) -> bool {
         match self.flags.get(name.as_ref()) {
@@ -55,7 +62,12 @@ impl FlagRegistry {
     pub fn parse(&mut self, args: Vec<String>) -> Vec<String> {
         let mut result = vec![];
         let mut is_value_flag = None;
+        let mut is_args = false;
         for arg in args.iter() {
+            if is_args {
+                self.args.push(arg.clone());
+                continue;
+            }
             if let Some(name) = &is_value_flag {
                 let flag = self.flags.get_mut(name).unwrap();
                 flag.value = Some(arg.clone());
@@ -68,6 +80,10 @@ impl FlagRegistry {
                 }
                 continue
             }
+            if arg == "--" {
+                is_args = true;
+                continue
+            }
             result.push(arg.to_string())
         }
         result
diff --git a/src/compiler.rs b/src/compiler.rs
index 2dde5cb1..b4a4e67a 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -83,7 +83,8 @@ impl AmberCompiler {
             .map(|(block, meta)| self.translate(block, meta))
     }
 
-    pub fn execute(code: String) {
+    pub fn execute(code: String, flags: &[String]) {
+        let code = format!("set -- {};\n\n{}", flags.join(" "), code);
         Command::new("/bin/bash").arg("-c").arg(code).spawn().unwrap().wait().unwrap();
     }
 
diff --git a/src/modules/block.rs b/src/modules/block.rs
index d992cd85..69284fa7 100644
--- a/src/modules/block.rs
+++ b/src/modules/block.rs
@@ -5,8 +5,7 @@ use super::statement::stmt::Statement;
 
 #[derive(Debug, Clone)]
 pub struct Block {
-    statements: Vec<Statement>,
-    is_scope: bool
+    statements: Vec<Statement>
 }
 
 impl Block {
@@ -15,10 +14,6 @@ impl Block {
         self.statements.is_empty()
     }
 
-    pub fn set_scopeless(&mut self) {
-        self.is_scope = false;
-    }
-
     // Push a parsed statement into the block
     pub fn push_statement(&mut self, statement: Statement) {
         self.statements.push(statement);
@@ -30,8 +25,7 @@ impl SyntaxModule<ParserMetadata> for Block {
 
     fn new() -> Self {
         Block {
-            statements: vec![],
-            is_scope: true
+            statements: vec![]
         }
     }
 
@@ -68,16 +62,17 @@ impl SyntaxModule<ParserMetadata> for Block {
 
 impl TranslateModule for Block {
     fn translate(&self, meta: &mut TranslateMetadata) -> String {
-        if self.is_scope { meta.increase_indent(); }
+        meta.increase_indent();
         let result = if self.is_empty() {
             ":".to_string()
         }
         else {
             self.statements.iter()
-                .map(|module| meta.gen_indent() + &module.translate(meta))
+                .map(|statement| meta.gen_indent() + &statement.translate(meta))
+                .filter(|translation| !translation.trim().is_empty())
                 .collect::<Vec<_>>().join(";\n")
         };
-        if self.is_scope { meta.decrease_indent(); }
+        meta.decrease_indent();
         result
     }
 }
\ No newline at end of file
diff --git a/src/modules/condition/ifchain.rs b/src/modules/condition/ifchain.rs
index ad5a2ec9..124315b2 100644
--- a/src/modules/condition/ifchain.rs
+++ b/src/modules/condition/ifchain.rs
@@ -29,7 +29,6 @@ impl SyntaxModule<ParserMetadata> for IfChain {
         loop {
             let mut cond = Expr::new();
             let mut block = Block::new();
-            cond.cannot_fail();
             // Handle comments and empty lines
             if token_by(meta, |token| token.starts_with('#') || token.starts_with('\n')).is_ok() {
                 continue
diff --git a/src/modules/expression/expr.rs b/src/modules/expression/expr.rs
index c4d63d3a..01670ee1 100644
--- a/src/modules/expression/expr.rs
+++ b/src/modules/expression/expr.rs
@@ -61,7 +61,6 @@ pub enum ExprType {
 #[derive(Debug, Clone)]
 pub struct Expr {
     value: Option<ExprType>,
-    can_fail: bool,
     kind: Type
 }
 
@@ -89,10 +88,6 @@ 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<S>(&mut self, meta: &mut ParserMetadata, mut module: S, cb: impl Fn(S) -> ExprType) -> SyntaxResult
     where
@@ -116,8 +111,7 @@ impl SyntaxModule<ParserMetadata> for Expr {
     fn new() -> Self {
         Expr {
             value: None,
-            kind: Type::Null,
-            can_fail: true
+            kind: Type::Null
         }
     }
 
diff --git a/src/modules/imports/import.rs b/src/modules/imports/import.rs
index b051b254..9ea59838 100644
--- a/src/modules/imports/import.rs
+++ b/src/modules/imports/import.rs
@@ -64,20 +64,22 @@ impl Import {
     fn handle_import(&mut self, meta: &mut ParserMetadata, tok: Option<Token>, imported_code: String) -> SyntaxResult {
         match AmberCompiler::new(imported_code.clone(), meta.path.clone()).tokenize() {
             Ok(tokens) => {
-                self.block.set_scopeless();
                 // Save snapshot of current file
                 let code = meta.code.clone();
                 let path = meta.path.clone();
                 let expr = meta.expr.clone();
                 let exports = meta.mem.exports.clone();
                 let index = meta.get_index();
+                let scopes = meta.mem.scopes.clone();
                 // Parse the imported file
                 meta.push_trace(PositionInfo::from_token(meta, tok));
                 meta.path = Some(self.path.value.clone());
                 meta.code = Some(imported_code);
                 meta.expr = tokens;
                 meta.set_index(0);
+                meta.mem.scopes = vec![];
                 syntax(meta, &mut self.block)?;
+                meta.mem.scopes = scopes;
                 self.handle_export(meta, meta.mem.exports.clone());
                 // Restore snapshot of current file
                 meta.code = code;
diff --git a/src/modules/main.rs b/src/modules/main.rs
new file mode 100644
index 00000000..ee99fd66
--- /dev/null
+++ b/src/modules/main.rs
@@ -0,0 +1,82 @@
+use heraclitus_compiler::prelude::*;
+use crate::translate::module::TranslateModule;
+use crate::utils::{ParserMetadata, TranslateMetadata};
+use crate::modules::types::Type;
+use crate::modules::block::Block;
+
+use super::variable::variable_name_extensions;
+
+#[derive(Debug, Clone)]
+pub struct Main {
+    pub args: Vec<String>,
+    pub block: Block,
+    pub is_skipped: bool
+}
+
+impl SyntaxModule<ParserMetadata> for Main {
+    syntax_name!("Main");
+
+    fn new() -> Self {
+        Self {
+            args: vec![],
+            block: Block::new(),
+            is_skipped: false
+        }
+    }
+
+    fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult {
+        let tok = meta.get_current_token();
+        token(meta, "main")?;
+        // Main cannot be parsed inside of a block
+        if meta.mem.get_depth() > 1 {
+            dbg!(meta.mem.get_depth());
+            return error!(meta, tok, "Main module must be in the global scope")
+        }
+        // If this main is included in other file, skip it
+        if !meta.trace.is_empty() {
+            self.is_skipped = true;
+        }
+        context!({
+            if token(meta, "(").is_ok() {
+                loop {
+                    if token(meta, ")").is_ok() {
+                        break;
+                    }
+                    self.args.push(variable(meta, variable_name_extensions())?);
+                    match token(meta, ")") {
+                        Ok(_) => break,
+                        Err(_) => token(meta, ",")?
+                    };
+                }
+            }
+            token(meta, "{")?;
+            // Create a new scope for variables
+            meta.mem.push_scope();
+            // Create variables
+            for arg in self.args.iter() {
+                meta.mem.add_variable(arg, Type::Text);
+            }
+            // Parse the block
+            syntax(meta, &mut self.block)?;
+            // Remove the scope made for variables
+            meta.mem.pop_scope();
+            token(meta, "}")?;
+            Ok(())
+        }, |pos| {
+            error_pos!(meta, pos, "Undefined syntax in main block")
+        })
+    }
+}
+
+impl TranslateModule for Main {
+    fn translate(&self, meta: &mut TranslateMetadata) -> String {
+        let variables = self.args.iter().enumerate()
+            .map(|(index, name)| format!("{name}=${}", index + 1))
+            .collect::<Vec<_>>().join("\n");
+        if self.is_skipped {
+            String::new()
+        } else {
+            format!("{variables}\n{}", self.block.translate(meta))
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/modules/mod.rs b/src/modules/mod.rs
index 7f7e6455..4969d345 100644
--- a/src/modules/mod.rs
+++ b/src/modules/mod.rs
@@ -9,6 +9,7 @@ pub mod loops;
 pub mod function;
 pub mod types;
 pub mod imports;
+pub mod main;
 
 #[macro_export]
 macro_rules! handle_types {
diff --git a/src/modules/statement/stmt.rs b/src/modules/statement/stmt.rs
index 6a090854..74fa4f44 100644
--- a/src/modules/statement/stmt.rs
+++ b/src/modules/statement/stmt.rs
@@ -30,6 +30,7 @@ use crate::modules::function::{
 use crate::modules::imports::{
     import::Import
 };
+use crate::modules::main::Main;
 
 #[derive(Debug, Clone)]
 pub enum StatementType {
@@ -48,7 +49,8 @@ pub enum StatementType {
     Break(Break),
     Continue(Continue),
     FunctionDeclaration(FunctionDeclaration),
-    Import(Import)
+    Import(Import),
+    Main(Main)
 }
 
 #[derive(Debug, Clone)]
@@ -61,7 +63,7 @@ impl Statement {
         // Imports
         Import,
         // Functions
-        FunctionDeclaration,
+        FunctionDeclaration, Main,
         // Loops
         InfiniteLoop, Break, Continue,
         // Conditions
@@ -107,18 +109,18 @@ impl SyntaxModule<ParserMetadata> 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)
+                Err(failure) => {
+                    match failure {
+                        Failure::Loud(err) => return Err(Failure::Loud(err)),
+                        Failure::Quiet(err) => error = Some(err)
+                    }
+                }
             }
         }
-        Err(error.unwrap())
+        Err(Failure::Quiet(error.unwrap()))
     }
 }
 
diff --git a/src/utils/memory.rs b/src/utils/memory.rs
index 15a2c866..7789639c 100644
--- a/src/utils/memory.rs
+++ b/src/utils/memory.rs
@@ -38,9 +38,9 @@ impl ScopeUnit {
 
 #[derive(Clone, Debug)]
 pub struct Memory {
-    scopes: Vec<ScopeUnit>,
+    pub scopes: Vec<ScopeUnit>,
     // Map of all generated functions based on their invocations
-    function_map: FunctionMap,
+    pub function_map: FunctionMap,
     pub exports: Exports
 }
 
@@ -52,6 +52,10 @@ impl Memory {
             exports: Exports::new()
         }
     }
+    
+    pub fn get_depth(&self) -> usize {
+        self.scopes.len()
+    }
 
     pub fn push_scope(&mut self) {
         self.scopes.push(ScopeUnit::new())
diff --git a/tests/io/print.ab b/tests/io/print.ab
index fcb6a331..606137f9 100644
--- a/tests/io/print.ab
+++ b/tests/io/print.ab
@@ -1,3 +1,7 @@
 pub fun print(text) {
     $echo {text}$
+}
+
+main {
+    print('Hello, World!')
 }
\ No newline at end of file