From 669c99e7e2cad491a369d19b5cb8a1a8694913ff Mon Sep 17 00:00:00 2001 From: Ph0enixKM Date: Wed, 28 Sep 2022 18:00:13 +0200 Subject: [PATCH] feat: add std library --- setup/install.sh | 30 +++++++++++++------------- setup/uninstall.sh | 8 +++---- src/cli/tests.rs | 26 +++++++++++----------- src/compiler.rs | 16 +++++++++----- src/modules/builtin/echo.rs | 32 ++++++++++++++++++++++++++++ src/modules/builtin/mod.rs | 1 + src/modules/function/invocation.rs | 2 +- src/modules/imports/import.rs | 8 +++++-- src/modules/imports/import_string.rs | 9 ++++++++ src/modules/main.rs | 1 - src/modules/mod.rs | 1 + src/modules/statement/stmt.rs | 10 +++++---- src/modules/types.rs | 3 +++ src/std/main.ab | 4 ++++ tests/io/print.ab | 7 ------ tests/is_even.ab | 4 ++-- tests/str/trim.ab | 7 ++++++ 17 files changed, 115 insertions(+), 54 deletions(-) create mode 100644 src/modules/builtin/echo.rs create mode 100644 src/modules/builtin/mod.rs create mode 100644 src/std/main.ab delete mode 100644 tests/io/print.ab create mode 100644 tests/str/trim.ab diff --git a/setup/install.sh b/setup/install.sh index cb6dc57a..a7c01871 100755 --- a/setup/install.sh +++ b/setup/install.sh @@ -7,27 +7,27 @@ arch=$(if [ $([ "_$(uname -m)" != "_arm64" ]; echo $?) != 0 ]; then echo "aarch6 url="https://github.com/Ph0enixKM/${name}/releases/download/${tag}/amber_${os}_${arch}"; test -d "${place}" > /dev/null; if [ $([ "_$(echo $?)" != "_0" ]; echo $?) != 0 ]; then -echo "Amber already installed" ; -echo "It seems that Amber is already installed on your system."; -echo "If you want to reinstall Amber - try to uninstall it first."; -echo "(Find out more at https://amber.marbl.cc)"; -exit 0 + echo "Amber already installed" ; + echo "It seems that Amber is already installed on your system."; + echo "If you want to reinstall Amber - try to uninstall it first."; + echo "(Find out more at https://amber.marbl.cc)"; + exit 0 fi; echo "Installing Amber" ; if [ $([ "_$(ruby -v > /dev/null; echo $?)" != "_0" ]; echo $?) != 0 ]; then -code="require \"open-uri\"; open(\"${target}\", \"wb\") do |file|; file << open(\"${url}\").read; end"; -echo "Using ruby as a download method..."; -ruby -e "${code}" + code="require \"open-uri\"; open(\"${target}\", \"wb\") do |file|; file << open(\"${url}\").read; end"; + echo "Using ruby as a download method..."; + ruby -e "${code}" elif [ $([ "_$(curl -v > /dev/null; echo $?)" != "_0" ]; echo $?) != 0 ]; then -echo "Using curl as a download method..."; -curl -o "${target}" "${url}" + echo "Using curl as a download method..."; + curl -o "${target}" "${url}" elif [ $([ "_$(wget -v > /dev/null; echo $?)" != "_0" ]; echo $?) != 0 ]; then -echo "Using wget as a download method..."; -wget -O "${target}" "${url}" + echo "Using wget as a download method..."; + wget -O "${target}" "${url}" else -echo "Neither ruby, curl or wget are installed on your system."; -echo "Please install one of them and try again."; -exit 1 + echo "Neither ruby, curl or wget are installed on your system."; + echo "Please install one of them and try again."; + exit 1 fi; sudo mkdir ${place} > /dev/null; sudo mv ${target} ${place}/${target}; diff --git a/setup/uninstall.sh b/setup/uninstall.sh index 57938fda..a15729a3 100755 --- a/setup/uninstall.sh +++ b/setup/uninstall.sh @@ -1,9 +1,9 @@ place="/opt/amber"; test -d "${place}" > /dev/null; if [ $([ "_$(echo $?)" != "_0" ]; echo $?) != 0 ]; then -sudo rm -rf "${place}"; -sudo rm '/usr/local/bin/amber'; -echo 'Uninstalled Amber successfully 🎉' + sudo rm -rf "${place}"; + sudo rm '/usr/local/bin/amber'; + echo 'Uninstalled Amber successfully 🎉' else -echo 'Amber is not installed' + echo 'Amber is not installed' fi \ No newline at end of file diff --git a/src/cli/tests.rs b/src/cli/tests.rs index 2d3e62e7..15beed4f 100644 --- a/src/cli/tests.rs +++ b/src/cli/tests.rs @@ -30,7 +30,7 @@ fn hello_world() { #[test] fn hello_world_error() { - test_amber_err!("echo Hello World", "Variable 'echo' does not exist"); + test_amber_err!("echo Hello World", "Variable 'Hello' does not exist"); } #[test] @@ -388,7 +388,7 @@ fn function() { fun test() { $echo Hello World$ } - test() + echo test() "; test_amber!(code, "Hello World"); } @@ -400,9 +400,9 @@ fn function_with_args() { $echo {a}$ $echo {b}$ } - test('Hello', 'World') + echo test('Hello', 'World') "; - test_amber!(code, "Hello\nWorld"); + test_amber!(code, "Hello World"); } #[test] @@ -411,8 +411,8 @@ fn function_with_args_different_types() { fun test(a, b) { $echo {a + b}$ } - test('Hello', 'World') - test(11, 42) + echo test('Hello', 'World') + echo test(11, 42) "; test_amber!(code, "HelloWorld\n53"); } @@ -423,7 +423,7 @@ fn function_with_typed_args() { fun test(a: Num, b: Num) { $echo {a + b}$ } - test(11, 42) + echo test(11, 42) "; test_amber!(code, "53"); } @@ -434,9 +434,9 @@ fn function_with_typed_different_args() { $echo {a}$ $echo {b}$ } - test(11, 'Hello') + echo test(11, 'Hello') "; - test_amber!(code, "11\nHello"); + test_amber!(code, "11 Hello"); } #[test] @@ -445,7 +445,7 @@ fn function_with_typed_args_text() { fun test(a: Text, b: Text) { $echo {a + b}$ } - test('Hello', 'World') + echo test('Hello', 'World') "; test_amber!(code, "HelloWorld"); } @@ -453,8 +453,8 @@ fn function_with_typed_args_text() { #[test] fn import_existing_file() { let code = " - import * from 'tests/io/print.ab' - print('Hello World') + import * from 'tests/str/trim.ab' + echo trim(' Hello World ') "; test_amber!(code, "Hello World"); } @@ -463,7 +463,7 @@ fn import_existing_file() { fn import_existing_nested_file() { let code = " import * from 'tests/is_even.ab' - is_even(10) + echo is_even(10) "; test_amber!(code, "even"); } \ No newline at end of file diff --git a/src/compiler.rs b/src/compiler.rs index 90816f3a..8ae417be 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,5 +1,5 @@ use heraclitus_compiler::prelude::*; -use crate::modules::block; +use crate::modules::block::Block; use crate::translate::check_all_blocks; use crate::utils::{ParserMetadata, TranslateMetadata}; use crate::translate::module::TranslateModule; @@ -48,13 +48,13 @@ impl AmberCompiler { } } - pub fn parse(&self, tokens: Vec) -> Result<(block::Block, ParserMetadata), Message> { + pub fn parse(&self, tokens: Vec) -> Result<(Block, ParserMetadata), Message> { let code = self.cc.code.as_ref().expect(NO_CODE_PROVIDED).clone(); let mut meta = ParserMetadata::new(tokens, self.path.clone(), Some(code)); if let Err(Failure::Loud(err)) = check_all_blocks(&mut meta) { return Err(err); } - let mut block = block::Block::new(); + let mut block = Block::new(); // Parse with debug or not let result = if let Ok(value) = env::var("AMBER_DEBUG_PARSER") { if value == "true" { @@ -72,7 +72,7 @@ impl AmberCompiler { } } - pub fn translate(&self, block: block::Block, meta: ParserMetadata) -> String { + pub fn translate(&self, block: Block, meta: ParserMetadata) -> String { let mut meta = TranslateMetadata::new(&meta); block.translate(&mut meta) } @@ -89,7 +89,7 @@ impl AmberCompiler { } #[allow(dead_code)] - pub fn test_eval(&self) -> Result { + pub fn test_eval(&mut self) -> Result { self.compile().map_or_else(Err, |(code, _)| { let child = Command::new("/bin/bash") .arg("-c").arg::<&str>(code.as_ref()) @@ -97,4 +97,10 @@ impl AmberCompiler { Ok(String::from_utf8_lossy(&child.stdout).to_string()) }) } + + pub fn import_std() -> String { + [ + include_str!("std/main.ab") + ].join("\n") + } } \ No newline at end of file diff --git a/src/modules/builtin/echo.rs b/src/modules/builtin/echo.rs new file mode 100644 index 00000000..86918550 --- /dev/null +++ b/src/modules/builtin/echo.rs @@ -0,0 +1,32 @@ +use heraclitus_compiler::prelude::*; +use crate::modules::expression::expr::Expr; +use crate::translate::module::TranslateModule; +use crate::utils::{ParserMetadata, TranslateMetadata}; + +#[derive(Debug, Clone)] +pub struct Echo { + value: Box +} + +impl SyntaxModule for Echo { + syntax_name!("Log"); + + fn new() -> Self { + Echo { + value: Box::new(Expr::new()) + } + } + + fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult { + token(meta, "echo")?; + syntax(meta, &mut *self.value)?; + Ok(()) + } +} + +impl TranslateModule for Echo { + fn translate(&self, meta: &mut TranslateMetadata) -> String { + let value = self.value.translate(meta); + format!("echo {}", value) + } +} diff --git a/src/modules/builtin/mod.rs b/src/modules/builtin/mod.rs new file mode 100644 index 00000000..a1b829e5 --- /dev/null +++ b/src/modules/builtin/mod.rs @@ -0,0 +1 @@ +pub mod echo; diff --git a/src/modules/function/invocation.rs b/src/modules/function/invocation.rs index e05d7ef6..9a00dc40 100644 --- a/src/modules/function/invocation.rs +++ b/src/modules/function/invocation.rs @@ -62,6 +62,6 @@ impl TranslateModule for FunctionInvocation { fn translate(&self, meta: &mut TranslateMetadata) -> String { let name = if self.variant_id != 0 { format!("__{}_{}", self.variant_id, self.name) } else { self.name.clone() }; let args = self.args.iter().map(|arg| arg.translate(meta)).collect::>().join(" "); - format!("{name} {args}") + format!("$({name} {args})") } } \ No newline at end of file diff --git a/src/modules/imports/import.rs b/src/modules/imports/import.rs index 9ea59838..a60ba713 100644 --- a/src/modules/imports/import.rs +++ b/src/modules/imports/import.rs @@ -62,7 +62,7 @@ impl Import { } fn handle_import(&mut self, meta: &mut ParserMetadata, tok: Option, imported_code: String) -> SyntaxResult { - match AmberCompiler::new(imported_code.clone(), meta.path.clone()).tokenize() { + match AmberCompiler::new(imported_code.clone(), Some(self.path.value.clone())).tokenize() { Ok(tokens) => { // Save snapshot of current file let code = meta.code.clone(); @@ -114,7 +114,11 @@ impl SyntaxModule for Import { token(meta, "from")?; let tok_str = meta.get_current_token(); syntax(meta, &mut self.path)?; - let imported_code = self.resolve_import(meta, tok_str)?; + let imported_code = if self.path.value == "[standard library]" { + AmberCompiler::import_std() + } else { + self.resolve_import(meta, tok_str)? + }; self.handle_import(meta, tok, imported_code)?; Ok(()) } diff --git a/src/modules/imports/import_string.rs b/src/modules/imports/import_string.rs index 5597b05f..6932fc48 100644 --- a/src/modules/imports/import_string.rs +++ b/src/modules/imports/import_string.rs @@ -6,6 +6,14 @@ pub struct ImportString { pub value: String } +impl ImportString { + pub fn std_lib_path(&mut self) { + if self.value == "std" { + self.value = "[standard library]".to_string(); + } + } +} + impl SyntaxModule for ImportString { syntax_name!("Import String"); @@ -19,6 +27,7 @@ impl SyntaxModule for ImportString { let value = token_by(meta, |word| word.starts_with('\''))?; if value.ends_with('\'') { self.value = value[1..value.len() - 1].to_string(); + self.std_lib_path(); } else { return error!(meta, meta.get_current_token(), "Import string cannot interpolate expressions") diff --git a/src/modules/main.rs b/src/modules/main.rs index ee99fd66..b82b6dd6 100644 --- a/src/modules/main.rs +++ b/src/modules/main.rs @@ -29,7 +29,6 @@ impl SyntaxModule for Main { 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 diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 4969d345..e1ee646a 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -10,6 +10,7 @@ pub mod function; pub mod types; pub mod imports; pub mod main; +pub mod builtin; #[macro_export] macro_rules! handle_types { diff --git a/src/modules/statement/stmt.rs b/src/modules/statement/stmt.rs index 74fa4f44..98cb3c77 100644 --- a/src/modules/statement/stmt.rs +++ b/src/modules/statement/stmt.rs @@ -31,6 +31,7 @@ use crate::modules::imports::{ import::Import }; use crate::modules::main::Main; +use crate::modules::builtin::echo::Echo; #[derive(Debug, Clone)] pub enum StatementType { @@ -50,7 +51,8 @@ pub enum StatementType { Continue(Continue), FunctionDeclaration(FunctionDeclaration), Import(Import), - Main(Main) + Main(Main), + Echo(Echo) } #[derive(Debug, Clone)] @@ -75,7 +77,7 @@ impl Statement { ShorthandMul, ShorthandDiv, ShorthandModulo, // Command - CommandStatement, + CommandStatement, Echo, // Expression Expr ]); @@ -128,8 +130,8 @@ impl TranslateModule for Statement { fn translate(&self, meta: &mut TranslateMetadata) -> String { let translated = self.translate_match(meta, self.value.as_ref().unwrap()); // This is a workaround that handles $(...) which cannot be used as a statement - if translated.starts_with("$(") { + if translated.starts_with('$') { format!("echo {} > /dev/null 2>&1", translated) } else { translated } } -} \ No newline at end of file +} diff --git a/src/modules/types.rs b/src/modules/types.rs index 59df4405..475993ae 100644 --- a/src/modules/types.rs +++ b/src/modules/types.rs @@ -26,6 +26,9 @@ impl Display for Type { pub trait Typed { fn get_type(&self) -> Type; + fn has_echo(&self) -> bool { + false + } } pub fn parse_type(meta: &mut ParserMetadata) -> Result { diff --git a/src/std/main.ab b/src/std/main.ab new file mode 100644 index 00000000..2de94089 --- /dev/null +++ b/src/std/main.ab @@ -0,0 +1,4 @@ +pub fun input() { + $read$ + echo '$REPLY' +} diff --git a/tests/io/print.ab b/tests/io/print.ab deleted file mode 100644 index 606137f9..00000000 --- a/tests/io/print.ab +++ /dev/null @@ -1,7 +0,0 @@ -pub fun print(text) { - $echo {text}$ -} - -main { - print('Hello, World!') -} \ No newline at end of file diff --git a/tests/is_even.ab b/tests/is_even.ab index 4c390583..b3c00892 100644 --- a/tests/is_even.ab +++ b/tests/is_even.ab @@ -1,5 +1,5 @@ -import * from 'io/print.ab'; +import * from 'str/trim.ab'; pub fun is_even(n: Num) { - print(n % 2 == 0 then 'even' else 'odd') + echo n % 2 == 0 then trim(' even ') else 'odd' } \ No newline at end of file diff --git a/tests/str/trim.ab b/tests/str/trim.ab new file mode 100644 index 00000000..eac4a194 --- /dev/null +++ b/tests/str/trim.ab @@ -0,0 +1,7 @@ +pub fun trim(text) { + echo '$\{text##*( )}' +} + +main { + trim('long text') +} \ No newline at end of file