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