Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A51 Optimize the way we store function body #44

Merged
merged 1 commit into from
Dec 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions --release
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

function __15_v0 {
echo "foo
bar"
};
function __16_v0 {
echo "bar"
}
function __19_v0 {
abc=$1
test=$2
echo "$(__16_v0 )";
echo "${test}"
};
echo "$(__19_v0 "test" "this")"
echo "$(__19_v0 "hello world" "this")";
echo "$(__15_v0 )"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
test*.ab
test*.sh

# Flamegraph files
flamegraph.svg

# MacOS
**/.DS_Store
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
heraclitus-compiler = "1.5.4"
heraclitus-compiler = "1.5.6"
similar-string = "1.4.2"
colored = "2.0.0"

[profile.release]
debug = true
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@ Finally in order to build
amber build.ab
```

In order to parse AST with a debug trace run cargo with the following environment variable:
Debugging Amber:
```bash
// Shows the AST
AMBER_DEBUG_PARSER=true cargo run <file.ab>
// Shows the time it took to compile each phase
AMBER_DEBUG_TIME=true cargo run <file.ab>

// Flamegraph is a profiling tool that is used to visualize the time each function took to execute
sudo cargo flamegraph -- <file.ab> <file.sh>
```
2 changes: 2 additions & 0 deletions meta.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ParserMetadata { expr: [Tok[pub 1:1], Tok[fun 1:5], Tok[input 1:9], Tok[<symbol: ( > 1:14], Tok[<symbol: ) > 1:15], Tok[<symbol: { > 1:17], Tok[$read$ 2:5], Tok[echo 3:5], Tok['$REPLY' 3:10], Tok[<symbol: } > 4:1], Tok[pub 6:1], Tok[fun 6:5], Tok[replace_once 6:9], Tok[<symbol: ( > 6:21], Tok[source 6:22], Tok[, 6:28], Tok[pattern 6:30], Tok[, 6:37], Tok[replacement 6:39], Tok[<symbol: ) > 6:50], Tok[<symbol: { > 6:52], Tok[echo 7:5], Tok[$echo "\$\{source/ 7:10], Tok[<symbol: { > 7:28], Tok[pattern 7:29], Tok[<symbol: } > 7:36], Tok[/ 7:37], Tok[<symbol: { > 7:38], Tok[replacement 7:39], Tok[<symbol: } > 7:50], Tok[}"$ 7:51], Tok[<symbol: } > 8:1], Tok[pub 10:1], Tok[fun 10:5], Tok[replace 10:9], Tok[<symbol: ( > 10:16], Tok[source 10:17], Tok[, 10:23], Tok[pattern 10:25], Tok[, 10:32], Tok[replacement 10:34], Tok[<symbol: ) > 10:45], Tok[<symbol: { > 10:47], Tok[echo 11:5], Tok[$echo "\$\{source// 11:10], Tok[<symbol: { > 11:29], Tok[pattern 11:30], Tok[<symbol: } > 11:37], Tok[/ 11:38], Tok[<symbol: { > 11:39], Tok[replacement 11:40], Tok[<symbol: } > 11:51], Tok[}"$ 11:52], Tok[<symbol: } > 12:1], Tok[pub 14:1], Tok[fun 14:5], Tok[replace_regex 14:9], Tok[<symbol: ( > 14:22], Tok[source 14:23], Tok[<symbol: : > 14:29], Tok[Text 14:31], Tok[, 14:35], Tok[pattern 14:37], Tok[<symbol: : > 14:44], Tok[Text 14:46], Tok[, 14:50], Tok[replacement 14:52], Tok[<symbol: : > 14:63], Tok[Text 14:65], Tok[<symbol: ) > 14:69], Tok[<symbol: : > 14:70], Tok[Text 14:72], Tok[<symbol: { > 14:77], Tok[echo 15:5], Tok[$echo " 15:10], Tok[<symbol: { > 15:17], Tok[source 15:18], Tok[<symbol: } > 15:24], Tok[" | sed -e "s/ 15:25], Tok[<symbol: { > 15:39], Tok[pattern 15:40], Tok[<symbol: } > 15:47], Tok[/ 15:48], Tok[<symbol: { > 15:49], Tok[replacement 15:50], Tok[<symbol: } > 15:61], Tok[/g"$ 15:62], Tok[<symbol: } > 16:1], Tok[pub 18:1], Tok[fun 18:5], Tok[file_read 18:9], Tok[<symbol: ( > 18:18], Tok[path 18:19], Tok[<symbol: ) > 18:23], Tok[<symbol: { > 18:25], Tok[echo 19:5], Tok[$cat " 19:10], Tok[<symbol: { > 19:16], Tok[path 19:17], Tok[<symbol: } > 19:21], Tok["$ 19:22], Tok[<symbol: } > 20:1], Tok[pub 22:1], Tok[fun 22:5], Tok[file_write 22:9], Tok[<symbol: ( > 22:19], Tok[path 22:20], Tok[, 22:24], Tok[content 22:26], Tok[<symbol: ) > 22:33], Tok[<symbol: { > 22:35], Tok[$echo " 23:5], Tok[<symbol: { > 23:12], Tok[content 23:13], Tok[<symbol: } > 23:20], Tok[" > " 23:21], Tok[<symbol: { > 23:26], Tok[path 23:27], Tok[<symbol: } > 23:31], Tok["$ 23:32], Tok[<symbol: } > 24:1], Tok[pub 26:1], Tok[fun 26:5], Tok[file_append 26:9], Tok[<symbol: ( > 26:20], Tok[path 26:21], Tok[, 26:25], Tok[content 26:27], Tok[<symbol: ) > 26:34], Tok[<symbol: { > 26:36], Tok[$echo " 27:5], Tok[<symbol: { > 27:12], Tok[content 27:13], Tok[<symbol: } > 27:20], Tok[" >> " 27:21], Tok[<symbol: { > 27:27], Tok[path 27:28], Tok[<symbol: } > 27:32], Tok["$ 27:33], Tok[<symbol: } > 28:1]], index: 10, path: Some("[standard library]"), code: Some("pub fun input() {\n $read$\n echo '$REPLY'\n}\n\npub fun replace_once(source, pattern, replacement) {\n echo $echo \"\\$\\{source/{pattern}/{replacement}}\"$\n}\n\npub fun replace(source, pattern, replacement) {\n echo $echo \"\\$\\{source//{pattern}/{replacement}}\"$\n}\n\npub fun replace_regex(source: Text, pattern: Text, replacement: Text): Text {\n echo $echo \"{source}\" | sed -e \"s/{pattern}/{replacement}/g\"$\n}\n\npub fun file_read(path) {\n echo $cat \"{path}\"$\n}\n\npub fun file_write(path, content) {\n $echo \"{content}\" > \"{path}\"$\n}\n\npub fun file_append(path, content) {\n $echo \"{content}\" >> \"{path}\"$\n}"), binop_border: None, mem: Memory { scopes: [ScopeUnit { vars: {}, funs: {} }], function_map: FunctionMap { map: {}, current_id: 0 }, variable_id: 0, exports: Exports { values: [] } }, debug: None, trace: [PositionInfo { path: Some("test.ab"), position: Pos(2, 1), row: 2, col: 1, len: 6, data: None }, PositionInfo { path: Some("test4.ab"), position: Pos(1, 1), row: 1, col: 1, len: 6, data: None }], import_history: ImportHistory { imports: ["test.ab", "test4.ab", "[standard library]"], import_graph: [[1], [2], []], import_blocks: [None, None, None], exports: [None, None, None] }, loop_ctx: false, function_ctx: false, messages: [] }
false
4 changes: 2 additions & 2 deletions src/cli/cli_interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, messages)) => {
Ok((messages, code)) => {
messages.iter().for_each(|m| m.show());
AmberCompiler::execute(code, self.flags.get_args())
},
Expand All @@ -67,7 +67,7 @@ impl CLI {
match self.read_file(input.clone()) {
Ok(code) => {
match AmberCompiler::new(code, Some(input)).compile() {
Ok((code, messages)) => {
Ok((messages, code)) => {
messages.iter().for_each(|m| m.show());
// Save to the output file
if self.args.len() >= 3 {
Expand Down
3 changes: 0 additions & 3 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
pub mod flag_registry;
pub mod cli_interface;

#[cfg(test)]
pub mod tests;
15 changes: 8 additions & 7 deletions src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,11 @@ impl AmberCompiler {
}

pub fn translate(&self, block: Block, meta: ParserMetadata) -> String {
let imports_sorted = meta.import_history.topological_sort();
let imports_blocks = meta.import_history.import_blocks.clone();
let _imports = meta.import_history.imports.clone();
let mut meta = TranslateMetadata::new(&meta);
let imports_sorted = meta.import_cache.topological_sort();
let imports_blocks = meta.import_cache.files.iter()
.map(|file| file.metadata.as_ref().map(|meta| meta.block.clone()))
.collect::<Vec<Option<Block>>>();
let mut meta = TranslateMetadata::new(meta);
let mut result = vec![];
let time = Instant::now();
for index in imports_sorted.iter() {
Expand All @@ -110,10 +111,10 @@ impl AmberCompiler {
result.join("\n")
}

pub fn compile(&self) -> Result<(String, Vec<Message>), Message> {
pub fn compile(&self) -> Result<(Vec<Message>, String), Message> {
self.tokenize()
.and_then(|tokens| self.parse(tokens))
.map(|(block, meta)| (self.translate(block, meta.clone()), meta.messages))
.map(|(block, meta)| (meta.messages.clone(), self.translate(block, meta)))
}

pub fn execute(code: String, flags: &[String]) {
Expand All @@ -123,7 +124,7 @@ impl AmberCompiler {

#[allow(dead_code)]
pub fn test_eval(&mut self) -> Result<String, Message> {
self.compile().map_or_else(Err, |(code, _)| {
self.compile().map_or_else(Err, |(_, code)| {
let child = Command::new("/bin/bash")
.arg("-c").arg::<&str>(code.as_ref())
.output().unwrap();
Expand Down
3 changes: 3 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ mod compiler;
pub mod cli;
use cli::cli_interface::CLI;

#[cfg(test)]
pub mod tests;

fn main() {
let mut cli = CLI::new();
cli.run();
Expand Down
4 changes: 2 additions & 2 deletions src/modules/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl SyntaxModule<ParserMetadata> for Block {
}

fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult {
meta.mem.push_scope();
meta.push_scope();
while let Some(token) = meta.get_current_token() {
// Handle the end of line or command
if ["\n", ";"].contains(&token.word.as_str()) {
Expand All @@ -55,7 +55,7 @@ impl SyntaxModule<ParserMetadata> for Block {
}
self.statements.push(statemant);
}
meta.mem.pop_scope();
meta.pop_scope();
Ok(())
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/modules/expression/binop/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub fn parse_left_expr(meta: &mut ParserMetadata, module: &mut Expr, op: &str) -
}

// Check if this binop can actually take place and return a new boundary for the left hand expression
fn binop_left_cut(meta: &mut ParserMetadata, op: &str) -> Result<usize, Failure> {
pub fn binop_left_cut(meta: &mut ParserMetadata, op: &str) -> Result<usize, Failure> {
let old_index = meta.get_index();
let mut parenthesis = 0;
while let Some(token) = meta.get_current_token() {
Expand Down
13 changes: 3 additions & 10 deletions src/modules/expression/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,9 @@ impl SyntaxModule<ParserMetadata> for Expr {
}

fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult {
let statements = self.get_modules();
for statement in statements {
// Handle comments
if let Some(token) = meta.get_current_token() {
if token.word.starts_with('#') {
meta.increment_index();
continue
}
}
match self.parse_match(meta, statement) {
let exprs = self.get_modules();
for expr in exprs {
match self.parse_match(meta, expr) {
Ok(()) => return Ok(()),
Err(failure) => {
if let Failure::Loud(err) = failure {
Expand Down
37 changes: 25 additions & 12 deletions src/modules/function/declaration.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use heraclitus_compiler::prelude::*;
use crate::modules::types::Type;
use crate::modules::variable::variable_name_extensions;
use crate::utils::function_interface::FunctionInterface;
use crate::utils::metadata::{ParserMetadata, TranslateMetadata};
use crate::translate::module::TranslateModule;
use crate::modules::block::Block;
Expand All @@ -11,7 +12,8 @@ use super::declaration_utils::*;
#[derive(Debug, Clone)]
pub struct FunctionDeclaration {
pub name: String,
pub args: Vec<(String, Type)>,
pub arg_names: Vec<String>,
pub arg_types: Vec<Type>,
pub returns: Type,
pub body: Block,
pub id: usize,
Expand All @@ -20,10 +22,10 @@ pub struct FunctionDeclaration {

impl FunctionDeclaration {
fn set_args_as_variables(&self, meta: &mut TranslateMetadata) -> Option<String> {
if !self.args.is_empty() {
if !self.arg_names.is_empty() {
meta.increase_indent();
let mut result = vec![];
for (index, (name, _kind)) in self.args.clone().iter().enumerate() {
for (index, name) in self.arg_names.clone().iter().enumerate() {
let indent = meta.gen_indent();
result.push(format!("{indent}{name}=${}", index + 1));
}
Expand All @@ -39,7 +41,8 @@ impl SyntaxModule<ParserMetadata> for FunctionDeclaration {
fn new() -> Self {
FunctionDeclaration {
name: String::new(),
args: vec![],
arg_names: vec![],
arg_types: vec![],
returns: Type::Generic,
body: Block::new(),
id: 0,
Expand Down Expand Up @@ -67,8 +70,14 @@ impl SyntaxModule<ParserMetadata> for FunctionDeclaration {
let name = variable(meta, variable_name_extensions())?;
// Optionally parse the argument type
match token(meta, ":") {
Ok(_) => self.args.push((name.clone(), parse_type(meta)?)),
Err(_) => self.args.push((name, Type::Generic))
Ok(_) => {
self.arg_names.push(name.clone());
self.arg_types.push(parse_type(meta)?);
},
Err(_) => {
self.arg_names.push(name.clone());
self.arg_types.push(Type::Generic);
}
}
match token(meta, ")") {
Ok(_) => break,
Expand All @@ -83,15 +92,19 @@ impl SyntaxModule<ParserMetadata> for FunctionDeclaration {
// Parse the body
token(meta, "{")?;
let (index_begin, index_end) = skip_function_body(meta);
// Create a new context with the function body
let expr = meta.context.expr[index_begin..index_end].to_vec();
let ctx = meta.context.clone().function_invocation(expr);
token(meta, "}")?;
// Add the function to the memory
self.id = handle_add_function(meta, tok, FunctionDeclSyntax {
self.id = handle_add_function(meta, tok, FunctionInterface {
id: None,
name: self.name.clone(),
args: self.args.clone(),
arg_names: self.arg_names.clone(),
arg_types: self.arg_types.clone(),
returns: self.returns.clone(),
body: meta.expr[index_begin..index_end].to_vec(),
is_public: self.is_public
})?;
}, ctx)?;
Ok(())
}, |pos| {
error_pos!(meta, pos, format!("Failed to parse function declaration '{}'", self.name))
Expand All @@ -102,7 +115,7 @@ impl SyntaxModule<ParserMetadata> for FunctionDeclaration {
impl TranslateModule for FunctionDeclaration {
fn translate(&self, meta: &mut TranslateMetadata) -> String {
let mut result = vec![];
let blocks = meta.mem.get_function_instances(self.id).unwrap().to_vec();
let blocks = meta.fun_cache.get_instances_cloned(self.id).unwrap();
// Translate each one of them
for (index, function) in blocks.iter().enumerate() {
let name = format!("__{}_v{}", self.id, index);
Expand All @@ -111,7 +124,7 @@ impl TranslateModule for FunctionDeclaration {
if let Some(args) = self.set_args_as_variables(meta) {
result.push(args);
}
result.push(function.body.translate(meta));
result.push(function.block.translate(meta));
result.push(meta.gen_indent() + "}");
}
// Return the translation
Expand Down
25 changes: 9 additions & 16 deletions src/modules/function/declaration_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,8 @@ use heraclitus_compiler::prelude::*;
use crate::modules::types::Type;
use crate::utils::ParserMetadata;
use crate::modules::variable::{handle_identifier_name};

#[derive(Debug, Clone)]
pub struct FunctionDeclSyntax {
pub name: String,
pub args: Vec<(String, Type)>,
pub returns: Type,
pub body: Vec<Token>,
pub is_public: bool
}
use crate::utils::context::Context;
use crate::utils::function_interface::FunctionInterface;

pub fn skip_function_body(meta: &mut ParserMetadata) -> (usize, usize) {
let index_begin = meta.get_index();
Expand All @@ -31,26 +24,26 @@ pub fn skip_function_body(meta: &mut ParserMetadata) -> (usize, usize) {
pub fn handle_existing_function(meta: &mut ParserMetadata, tok: Option<Token>) -> Result<(), Failure> {
let name = tok.as_ref().unwrap().word.clone();
handle_identifier_name(meta, &name, tok.clone())?;
if meta.mem.get_function(&name).is_some() {
if meta.get_fun_declaration(&name).is_some() {
return error!(meta, tok, format!("Function '{}' already exists", name))
}
Ok(())
}

pub fn handle_add_function(meta: &mut ParserMetadata, tok: Option<Token>, decl: FunctionDeclSyntax) -> Result<usize, Failure> {
let name = decl.name.clone();
pub fn handle_add_function(meta: &mut ParserMetadata, tok: Option<Token>, fun: FunctionInterface, ctx: Context) -> Result<usize, Failure> {
let name = fun.name.clone();
handle_identifier_name(meta, &name, tok.clone())?;
let any_generic = decl.args.iter().any(|(_, kind)| kind == &Type::Generic);
let any_typed = decl.args.iter().any(|(_, kind)| kind != &Type::Generic);
let any_generic = fun.arg_types.iter().any(|kind| kind == &Type::Generic);
let any_typed = fun.arg_types.iter().any(|kind| kind != &Type::Generic);
// Either all arguments are generic or typed
if any_typed && (any_generic || decl.returns == Type::Generic) {
if any_typed && (any_generic || fun.returns == Type::Generic) {
return error!(meta, tok => {
message: format!("Function '{}' has a mix of generic and typed arguments", name),
comment: "Please decide whether to use generics or types for all arguments"
})
}
// Try to add the function to the memory
match meta.mem.add_function_declaration(meta.clone(), decl) {
match meta.add_fun_declaration(fun, ctx) {
// Return the id of the function
Some(id) => Ok(id),
// If the function already exists, show an error
Expand Down
2 changes: 1 addition & 1 deletion src/modules/function/invocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl SyntaxModule<ParserMetadata> for FunctionInvocation {
};
}
let types = self.args.iter().map(|e| e.get_type()).collect::<Vec<Type>>();
(self.kind, self.variant_id) = handle_function_parameters(meta, &self.name, &types, tok)?;
(self.kind, self.variant_id) = handle_function_parameters(meta, self.id, &self.name, &types, tok)?;
Ok(())
}
}
Expand Down
Loading