Skip to content

Commit

Permalink
Merge pull request #41 from Ph0enixKM/A65
Browse files Browse the repository at this point in the history
A65 Feature: Add named function Imports
  • Loading branch information
Ph0enixKM authored Oct 5, 2022
2 parents 40a82f0 + 9874eb5 commit b1ee744
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 32 deletions.
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.

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

[dependencies]
heraclitus-compiler = "1.5.2"
heraclitus-compiler = "1.5.3"
similar-string = "1.4.2"
colored = "2.0.0"
122 changes: 93 additions & 29 deletions src/modules/imports/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,95 @@ use std::fs;
use heraclitus_compiler::prelude::*;
use crate::compiler::AmberCompiler;
use crate::modules::block::Block;
use crate::modules::variable::variable_name_extensions;
use crate::utils::exports::{Exports, ExportUnit};
use crate::utils::{ParserMetadata, TranslateMetadata};
use crate::translate::module::TranslateModule;
use super::import_string::ImportString;

#[derive(Debug, Clone)]
pub struct Import {
path: ImportString
path: ImportString,
token_import: Option<Token>,
token_path: Option<Token>,
is_all: bool,
export_defs: Vec<(String, Option<String>, Option<Token>)>
}

impl Import {
fn handle_export(&mut self, meta: &mut ParserMetadata, exports: Exports) -> SyntaxResult {
for export in exports.get_exports().iter().cloned() {
match export {
ExportUnit::Function(mut func_decl) => {
func_decl.is_public = false;
if !meta.mem.add_existing_function_declaration(func_decl) {
unimplemented!("Function redefinition");
let exports = exports.get_exports().iter().cloned();
for (name, alias, tok) in self.export_defs.drain(..) {
let mut found = false;
for export_unit in exports.clone() {
match export_unit {
ExportUnit::Function(mut func) => {
if func.name == name {
found = true;
func.name = alias.unwrap_or_else(|| name.clone());
if !meta.mem.add_existing_function_declaration(func) {
return error!(meta, tok => {
message: format!("Function '{}' is already defined", name)
})
}
break
}
}
}
}
if !found {
return error!(meta, tok => {
message: format!("Export '{}' not found in module '{}'", &name, self.path.value),
comment: "Exports are case-sensitive"
})
}
}
if self.is_all {
for export in exports {
match export {
ExportUnit::Function(mut func_decl) => {
let name = func_decl.name.clone();
func_decl.is_public = false;
if !meta.mem.add_existing_function_declaration(func_decl) {
return error!(meta, self.token_import.clone() => {
message: format!("Function '{}' is already defined", name)
})
}
}
}
}
}
Ok(())
}

fn add_import(&mut self, meta: &mut ParserMetadata, tok: Option<Token>, path: &str) -> SyntaxResult {
fn add_import(&mut self, meta: &mut ParserMetadata, path: &str) -> SyntaxResult {
if meta.import_history.add_import(meta.get_path(), path.to_string()).is_none() {
return error!(meta, tok => {
return error!(meta, self.token_path.clone() => {
message: "Circular import detected",
comment: "Please remove the circular import"
})
}
Ok(())
}

fn resolve_import(&mut self, meta: &mut ParserMetadata, tok: Option<Token>) -> Result<String, Failure> {
fn resolve_import(&mut self, meta: &mut ParserMetadata) -> Result<String, Failure> {
match fs::read_to_string(self.path.value.clone()) {
Ok(content) => Ok(content),
Err(err) => error!(meta, tok => {
Err(err) => error!(meta, self.token_path.clone() => {
message: format!("Could not read file '{}'", self.path.value),
comment: err.to_string()
})
}
}

fn handle_import(&mut self, meta: &mut ParserMetadata, tok: Option<Token>, imported_code: String) -> SyntaxResult {
fn handle_import(&mut self, meta: &mut ParserMetadata, imported_code: String) -> SyntaxResult {
match meta.import_history.get_export(Some(self.path.value.clone())) {
Some(exports) => self.handle_export(meta, exports),
None => self.handle_compile_code(meta, tok, imported_code)
None => self.handle_compile_code(meta, imported_code)
}
}

fn handle_compile_code(&mut self, meta: &mut ParserMetadata, tok: Option<Token>, imported_code: String) -> SyntaxResult {
fn handle_compile_code(&mut self, meta: &mut ParserMetadata, imported_code: String) -> SyntaxResult {
match AmberCompiler::new(imported_code.clone(), Some(self.path.value.clone())).tokenize() {
Ok(tokens) => {
let mut block = Block::new();
Expand All @@ -66,24 +102,26 @@ impl Import {
let index = meta.get_index();
let scopes = meta.mem.scopes.clone();
// Parse the imported file
meta.push_trace(PositionInfo::from_token(meta, tok));
meta.push_trace(PositionInfo::from_token(meta, self.token_import.clone()));
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 block)?;
meta.mem.scopes = scopes;
meta.import_history.add_import_block(Some(self.path.value.clone()), block);
meta.import_history.add_export(Some(self.path.value.clone()), meta.mem.exports.clone());
self.handle_export(meta, meta.mem.exports.clone())?;
// Restore snapshot of current file
meta.mem.scopes = scopes;
meta.code = code;
meta.path = path;
meta.expr = expr;
meta.mem.exports = exports;
meta.set_index(index);
meta.pop_trace();
// Finalize importing phase
meta.import_history.add_import_block(Some(self.path.value.clone()), block);
meta.import_history.add_export(Some(self.path.value.clone()), meta.mem.exports.clone());
self.handle_export(meta, meta.mem.exports.clone())?;
// Restore exports
meta.mem.exports = exports;
Ok(())
}
Err(err) => {
Expand All @@ -98,28 +136,54 @@ impl SyntaxModule<ParserMetadata> for Import {

fn new() -> Self {
Self {
path: ImportString::new()
path: ImportString::new(),
token_import: None,
token_path: None,
is_all: false,
export_defs: vec![]
}
}

fn parse(&mut self, meta: &mut ParserMetadata) -> SyntaxResult {
self.token_import = meta.get_current_token();
token(meta, "import")?;
let tok = meta.get_current_token();
if meta.mem.get_depth() > 1 {
return error!(meta, tok, "Imports must be in the global scope")
return error!(meta, self.token_import.clone(), "Imports must be in the global scope")
}
match token(meta, "*") {
Ok(_) => self.is_all = true,
Err(_) => {
token(meta, "{")?;
let mut exports = vec![];
loop {
let tok = meta.get_current_token();
let name = variable(meta, variable_name_extensions())?;
let alias = match token(meta, "as") {
Ok(_) => Some(variable(meta, variable_name_extensions())?),
Err(_) => None
};
exports.push((name, alias, tok));
match token(meta, ",") {
Ok(_) => {},
Err(_) => break
}
}
self.export_defs = exports;
token(meta, "}")?;
}
}
token(meta, "*")?;
token(meta, "from")?;
let tok_str = meta.get_current_token();
self.token_path = meta.get_current_token();
syntax(meta, &mut self.path)?;
// Import code from file or standard library
let imported_code = if self.path.value == "[standard library]" {
self.add_import(meta, tok_str, "[standard library]")?;
self.add_import(meta, "[standard library]")?;
AmberCompiler::import_std()
} else {
self.add_import(meta, tok_str.clone(), &self.path.value.clone())?;
self.resolve_import(meta, tok_str)?
self.add_import(meta, &self.path.value.clone())?;
self.resolve_import(meta)?
};
self.handle_import(meta, tok, imported_code)?;
self.handle_import(meta, imported_code)?;
Ok(())
}
}
Expand Down

0 comments on commit b1ee744

Please sign in to comment.