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

Namespace refactor #22

Merged
merged 4 commits into from
Mar 19, 2021
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
1 change: 1 addition & 0 deletions forc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ line-col = "0.2"
toml = "0.5"
serde = { version = "1.0", features = ["derive"] }
whoami = "1.1"
pest = "2.1"
65 changes: 25 additions & 40 deletions forc/src/cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,23 @@ use source_span::{
fmt::{Color, Formatter, Style},
Position, Span,
};
use std::collections::HashMap;
use std::io::{self, Write};
use std::{collections::HashMap, fs::File};
use termcolor::{BufferWriter, Color as TermColor, ColorChoice, ColorSpec, WriteColor};

use crate::manifest::{Dependency, DependencyDetails, Manifest};
use parser::{Ident, LibraryExports, TypeInfo, TypedDeclaration, TypedFunctionDeclaration};
use parser::{
Ident, LibraryExports, Namespace, TypeInfo, TypedDeclaration, TypedFunctionDeclaration,
};
use std::{fs, path::PathBuf};

pub(crate) fn build() -> Result<(), String> {
pub(crate) fn build(path: Option<String>) -> Result<(), String> {
// find manifest directory, even if in subdirectory
let this_dir = std::env::current_dir().unwrap();
let this_dir = if let Some(path) = path {
PathBuf::from(path)
} else {
std::env::current_dir().unwrap()
};
let manifest_dir = match find_manifest_dir(&this_dir) {
Some(dir) => dir,
None => {
Expand All @@ -25,27 +31,21 @@ pub(crate) fn build() -> Result<(), String> {
};
let manifest = read_manifest(&manifest_dir)?;

let mut namespaces = Default::default();
let mut method_namespaces = Default::default();
let mut namespace: Namespace = Default::default();
if let Some(ref deps) = manifest.dependencies {
for (dependency_name, dependency_details) in deps.iter() {
compile_dependency_lib(
&this_dir,
&dependency_name,
&dependency_details,
&mut namespaces,
&mut method_namespaces,
&mut namespace,
)?;
}
}

// now, compile this program with all of its dependencies
let main_file = get_main_file(&manifest, &manifest_dir)?;
let main = compile(
main_file,
&manifest.project.name,
&namespaces,
&method_namespaces,
)?;
let _main = compile(main_file, &manifest.project.name, &namespace)?;

Ok(())
}
Expand All @@ -70,16 +70,10 @@ fn find_manifest_dir(starter_path: &PathBuf) -> Option<PathBuf> {
/// Takes a dependency and returns a namespace of exported things from that dependency
/// trait implementations are included as well
fn compile_dependency_lib<'source, 'manifest>(
project_path: &PathBuf,
dependency_name: &'manifest str,
dependency_lib: &Dependency,
namespaces: &mut HashMap<
&'manifest str,
HashMap<Ident<'source>, HashMap<Ident<'source>, TypedDeclaration<'source>>>,
>,
method_namespaces: &mut HashMap<
&'manifest str,
HashMap<Ident<'source>, HashMap<TypeInfo<'source>, Vec<TypedFunctionDeclaration<'source>>>>,
>,
namespace: &mut Namespace<'source>,
) -> Result<(), String> {
//todo!("For tomorrow: This needs to accumulate dependencies over time and build up the dependency namespace. Then, colon delineated paths in the compiler
// need to look in the imports namespace.");
Expand All @@ -95,9 +89,13 @@ fn compile_dependency_lib<'source, 'manifest>(
None => return Err("Only simple path imports are supported right now. Please supply a path relative to the manifest file.".into())
};

// dependency paths are relative to the path of the project being compiled
let mut project_path = project_path.clone();
project_path.push(dep_path);

// compile the dependencies of this dependency
//this should detect circular dependencies
let manifest_dir = match find_manifest_dir(&PathBuf::from(dep_path)) {
let manifest_dir = match find_manifest_dir(&project_path) {
Some(o) => o,
None => return Err("Manifest not found for dependency.".into()),
};
Expand All @@ -115,15 +113,9 @@ fn compile_dependency_lib<'source, 'manifest>(

let main_file = get_main_file(&manifest_of_dep, &manifest_dir)?;

let compiled = compile(
main_file,
&manifest_of_dep.project.name,
namespaces,
method_namespaces,
)?;
let compiled = compile(main_file, &manifest_of_dep.project.name, namespace)?;

namespaces.insert(&dependency_name, compiled.namespaces);
method_namespaces.insert(&dependency_name, compiled.methods_namespaces);
namespace.insert_module(dependency_name.to_string(), compiled.namespace);

// nothing is returned from this method since it mutates the hashmaps it was given
Ok(())
Expand Down Expand Up @@ -154,16 +146,9 @@ fn read_manifest(manifest_dir: &PathBuf) -> Result<Manifest, String> {
fn compile<'source, 'manifest>(
source: &'source str,
proj_name: &str,
namespaces: &HashMap<
&'manifest str,
HashMap<Ident<'source>, HashMap<Ident<'source>, TypedDeclaration<'source>>>,
>,
method_namespaces: &HashMap<
&'manifest str,
HashMap<Ident<'source>, HashMap<TypeInfo<'source>, Vec<TypedFunctionDeclaration<'source>>>>,
>,
namespace: &Namespace<'source>,
) -> Result<LibraryExports<'source>, String> {
let res = parser::compile(&source, namespaces, method_namespaces);
let res = parser::compile(&source, namespace);
match res {
Ok((compiled, warnings)) => {
for ref warning in warnings.iter() {
Expand Down
7 changes: 5 additions & 2 deletions forc/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ enum Command {
#[structopt(name = "init")]
Init { project_name: String },
#[structopt(name = "build")]
Build,
Build {
#[structopt(short = "p")]
path: Option<String>,
},
}

pub(crate) fn run_cli() -> Result<(), String> {
Expand All @@ -25,7 +28,7 @@ pub(crate) fn run_cli() -> Result<(), String> {
Command::Init { project_name } => {
init::init_new_project(project_name).map_err(|e| e.to_string())
}
Command::Build => build::build(),
Command::Build { path } => build::build(path),
}?;
/*
let content = fs::read_to_string(opt.input.clone())?;
Expand Down
2 changes: 1 addition & 1 deletion forc/src/manifest.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use serde::{Deserialize, Serialize};
use serde::Deserialize;
use std::collections::BTreeMap;

// using https://github.com/rust-lang/cargo/blob/master/src/cargo/util/toml/mod.rs as the source of
Expand Down
8 changes: 7 additions & 1 deletion parser/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ pub enum Warning<'sc> {
module: String,
name: String,
},
OverridesOtherSymbol {
name: &'sc str,
},
OverridingTraitImplementation,
}

impl<'sc> Warning<'sc> {
Expand All @@ -177,7 +181,9 @@ impl<'sc> Warning<'sc> {
NonSnakeCaseFunctionName { name } => format!("Function name \"{}\" is not idiomatic. Function names should be snake_case, like \"{}\".", name, to_snake_case(name)),
LossOfPrecision { initial_type, cast_to } => format!("This cast, from type {} to type {}, will lose precision.", initial_type.friendly_type_str(), cast_to.friendly_type_str()),
UnusedReturnValue { r#type } => format!("This returns a value of type {}, which is not assigned to anything and is ignored.", r#type.friendly_type_str()),
SimilarMethodFound { lib, module, name } => format!("A method with the same name was found for type {} in dependency \"{}::{}\". Traits must be in scope in order to access their methods. ", name, lib, module)
SimilarMethodFound { lib, module, name } => format!("A method with the same name was found for type {} in dependency \"{}::{}\". Traits must be in scope in order to access their methods. ", name, lib, module),
OverridesOtherSymbol { name } => format!("This import would override another symbol with the same name \"{}\" in this namespace.", name),
OverridingTraitImplementation => format!("This trait implementation overrides another one that was previously defined.")
}
}
}
Expand Down
7 changes: 3 additions & 4 deletions parser/src/hll.pest
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,9 @@ impl_trait = { impl_keyword ~ trait_name ~ type_params? ~ "for" ~ type_name ~ ty

// imports

// for now, use statements are just strings to the file you import. this will be changed to the commented out version later
use_statement = { use_keyword ~ string ~ ";" }
//use_statement = { use_keyword ~ import_path ~ ";"}
import_path = { ident ~ (path_separator ~ ident)* }
use_statement = { use_keyword ~ import_path ~ ";"}
import_path = { ident ~ (path_separator ~ ident)* ~ path_separator ~ (ident|star) }
star = { "*" }

// loops
while_loop = { while_keyword ~ expr ~ code_block }
Expand Down
58 changes: 12 additions & 46 deletions parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ use crate::parser::{HllParser, Rule};
use either::{Either, Left, Right};
use pest::iterators::Pair;
use pest::Parser;
pub use semantics::{Namespace, TypedDeclaration, TypedFunctionDeclaration};
use semantics::{TreeType, TypedParseTree};
pub use semantics::{TypedDeclaration, TypedFunctionDeclaration};
use std::collections::HashMap;
pub use types::TypeInfo;

Expand All @@ -46,11 +46,7 @@ pub struct HllTypedParseTree<'sc> {

#[derive(Debug)]
pub struct LibraryExports<'sc> {
// a map from export_name => (symbol_name => decl)
pub namespaces: HashMap<Ident<'sc>, HashMap<Ident<'sc>, semantics::TypedDeclaration<'sc>>>,
// a map from export_name => (type => methods for that type)
pub methods_namespaces:
HashMap<Ident<'sc>, HashMap<TypeInfo<'sc>, Vec<semantics::TypedFunctionDeclaration<'sc>>>>,
pub namespace: Namespace<'sc>,
}

#[derive(Debug)]
Expand All @@ -70,7 +66,7 @@ struct AstNode<'sc> {

#[derive(Debug, Clone)]
pub(crate) enum AstNodeContent<'sc> {
UseStatement(UseStatement),
UseStatement(UseStatement<'sc>),
CodeBlock(CodeBlock<'sc>),
ReturnStatement(ReturnStatement<'sc>),
Declaration(Declaration<'sc>),
Expand Down Expand Up @@ -146,14 +142,7 @@ pub fn parse<'sc>(input: &'sc str) -> CompileResult<'sc, HllParseTree<'sc>> {

pub fn compile<'sc, 'manifest>(
input: &'sc str,
imported_namespace: &HashMap<
&'manifest str,
HashMap<Ident<'sc>, HashMap<Ident<'sc>, semantics::TypedDeclaration<'sc>>>,
>,
imported_method_namespace: &HashMap<
&'manifest str,
HashMap<Ident<'sc>, HashMap<TypeInfo<'sc>, Vec<semantics::TypedFunctionDeclaration<'sc>>>>,
>,
initial_namespace: &Namespace<'sc>,
) -> Result<
(HllTypedParseTree<'sc>, Vec<CompileWarning<'sc>>),
(Vec<CompileError<'sc>>, Vec<CompileWarning<'sc>>),
Expand All @@ -169,12 +158,7 @@ pub fn compile<'sc, 'manifest>(
);

let contract_ast: Option<_> = if let Some(tree) = parse_tree.contract_ast {
match TypedParseTree::type_check(
tree,
imported_namespace,
imported_method_namespace,
TreeType::Contract,
) {
match TypedParseTree::type_check(tree, initial_namespace.clone(), TreeType::Contract) {
CompileResult::Ok {
warnings: mut l_w,
errors: mut l_e,
Expand All @@ -197,12 +181,7 @@ pub fn compile<'sc, 'manifest>(
None
};
let predicate_ast: Option<_> = if let Some(tree) = parse_tree.predicate_ast {
match TypedParseTree::type_check(
tree,
imported_namespace,
imported_method_namespace,
TreeType::Predicate,
) {
match TypedParseTree::type_check(tree, initial_namespace.clone(), TreeType::Predicate) {
CompileResult::Ok {
warnings: mut l_w,
errors: mut l_e,
Expand All @@ -225,12 +204,7 @@ pub fn compile<'sc, 'manifest>(
None
};
let script_ast: Option<_> = if let Some(tree) = parse_tree.script_ast {
match TypedParseTree::type_check(
tree,
imported_namespace,
imported_method_namespace,
TreeType::Script,
) {
match TypedParseTree::type_check(tree, initial_namespace.clone(), TreeType::Script) {
CompileResult::Ok {
warnings: mut l_w,
errors: mut l_e,
Expand All @@ -257,12 +231,8 @@ pub fn compile<'sc, 'manifest>(
.library_exports
.into_iter()
.filter_map(|(name, tree)| {
match TypedParseTree::type_check(
tree,
imported_namespace,
imported_method_namespace,
TreeType::Library,
) {
match TypedParseTree::type_check(tree, initial_namespace.clone(), TreeType::Library)
{
CompileResult::Ok {
warnings: mut l_w,
errors: mut l_e,
Expand All @@ -284,16 +254,12 @@ pub fn compile<'sc, 'manifest>(
})
.collect();
let mut exports = LibraryExports {
methods_namespaces: Default::default(),
namespaces: Default::default(),
namespace: Default::default(),
};
for (ref name, parse_tree) in res {
exports
.methods_namespaces
.insert(name.clone(), parse_tree.methods_namespace);
exports
.namespaces
.insert(name.clone(), parse_tree.namespace);
.namespace
.insert_module(name.primary_name.to_string(), parse_tree.namespace);
}
exports
};
Expand Down
2 changes: 1 addition & 1 deletion parser/src/parse_tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@ pub(crate) use expression::{
AsmExpression, Expression, MatchBranch, StructExpressionField, UnaryOp,
};
pub(crate) use literal::Literal;
pub(crate) use use_statement::UseStatement;
pub(crate) use use_statement::{ImportType, UseStatement};
pub(crate) use while_loop::WhileLoop;
Loading