diff --git a/examples/dummy_project/Orchid.toml b/examples/dummy_project/Orchid.toml new file mode 100644 index 0000000..130321a --- /dev/null +++ b/examples/dummy_project/Orchid.toml @@ -0,0 +1,5 @@ +[[target]] +name = 'Dummy Project' +type = 'executable' + +[target.dependencies] diff --git a/examples/dummy_project/main.orc b/examples/dummy_project/main.orc new file mode 100644 index 0000000..935a765 --- /dev/null +++ b/examples/dummy_project/main.orc @@ -0,0 +1,18 @@ +import std::io::(println, out) -- imports + +-- single word substitution (alias) +greet = \name. printf out "Hello {}!\n" [name] + +-- multi-word exported substitution +export (...$pre ;) $a ...$post) =200=> (...$pre (greet $a) ...$post) + +-- single-word exported substitution +export main = ( + print "What is your name? >> + readln >>= \name. + greet name +) + +-- The broadest trait definition in existence +Foo = Bar Baz +default anyFoo = @T. @impl:(T (Bar Baz)). impl:(T Foo) \ No newline at end of file diff --git a/notes.md b/notes.md new file mode 100644 index 0000000..81bc09e --- /dev/null +++ b/notes.md @@ -0,0 +1,19 @@ +# Anatomy of a code file + +```orchid +import std::io::(println, out) -- imports + +-- single word substitution (alias) +greet = \name. printf out "Hello {}!\n" [name] + +-- multi-word exported substitution +export (...$pre ;) $a ...$post) =200=> (...$pre (greet $a) ...$post) + +-- single-word exported substitution +export main = ( + print "What is your name? >> + readln >>= \name. + greet name +) +``` + diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 4867adf..730085c 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -12,5 +12,6 @@ pub use expression::expression_parser; pub use sourcefile::FileEntry; pub use sourcefile::file_parser; pub use sourcefile::imports; +pub use sourcefile::is_op; pub use sourcefile::exported_names; pub use import::Import; \ No newline at end of file diff --git a/src/parse/sourcefile.rs b/src/parse/sourcefile.rs index f891a37..a057c41 100644 --- a/src/parse/sourcefile.rs +++ b/src/parse/sourcefile.rs @@ -61,7 +61,7 @@ pub fn file_parser<'a>( /// Decide if a string can be an operator. Operators can include digits and text, just not at the /// start. -fn is_op(s: &str) -> bool { +pub fn is_op(s: &str) -> bool { return match s.chars().next() { Some(x) => !x.is_alphanumeric(), None => false @@ -99,9 +99,12 @@ pub fn all_ops(src: &Vec) -> Vec<&String> { defined_ops(src, false) } pub fn exported_ops(src: &Vec) -> Vec<&String> { defined_ops(src, true) } /// Summarize all imports from a file in a single list of qualified names -pub fn imports(src: &Vec) -> Vec<&import::Import> { - src.into_iter().filter_map(|ent| match ent { +pub fn imports<'a, 'b, I>( + src: I +) -> impl Iterator + 'a +where I: Iterator + 'a { + src.filter_map(|ent| match ent { FileEntry::Import(impv) => Some(impv.iter()), _ => None - }).flatten().collect() + }).flatten() } \ No newline at end of file diff --git a/src/project/resolve_names.rs b/src/project/resolve_names.rs index ceca618..a0cf94d 100644 --- a/src/project/resolve_names.rs +++ b/src/project/resolve_names.rs @@ -1,4 +1,8 @@ +use std::borrow::Borrow; +use std::cell::RefCell; use std::collections::HashMap; +use std::rc::Rc; +use std::{iter, clone}; use chumsky::{Parser, prelude::Simple}; use thiserror::Error; @@ -36,8 +40,6 @@ impl ParseError { } } - - // Loading a module: // 1. [X] Parse the imports // 2. [ ] Build a mapping of all imported symbols to full paths @@ -45,31 +47,74 @@ impl ParseError { // 3. [ ] Parse everything using the full list of operators // 4. [ ] Traverse and remap elements -pub fn load_project( - mut load_mod: F, prelude: &[&str], entry: &str +type GetLoaded<'a> = dyn FnMut(&'a [&str]) -> &'a Option; +type GetPreparsed<'a> = dyn FnMut(&'a [&str]) -> &'a Option>; + +pub fn load_project<'a, F>( + mut load_mod: F, prelude: &[&'a str], entry: &str ) -> Result where F: FnMut(&[&str]) -> Option { + // TODO: Welcome to Kamino! + let prelude_vec: Vec = prelude.iter().map(|s| s.to_string()).collect(); let preparser = file_parser(prelude, &[]); - let mut loaded = Cache::new(|path: &[&str]| load_mod(path)); - let mut preparsed = Cache::new(|path: &[&str]| { - loaded.get(path).as_ref().map(|loaded| match loaded { + let loaded_cell = RefCell::new(Cache::new(|path: Vec| { + load_mod(&path.iter().map(|s| s.as_str()).collect::>()) + })); + let preparsed_cell = RefCell::new(Cache::new(|path: Vec| { + let mut loaded = loaded_cell.borrow_mut(); + loaded.by_clone(path).as_ref().map(|loaded| match loaded { Loaded::Module(source) => Some(preparser.parse(source.as_str()).ok()?), _ => return None }).flatten() - }); - let exports = Cache::new(|path: &[&str]| loaded.get(path).map(|data| { - match data { - Loaded::Namespace(names) => Some(names), - Loaded::Module(source) => preparsed.get(path).map(|data| { - exported_names(&data).into_iter().map(|n| n[0]).collect() - }) + })); + let exports_cell = RefCell::new(Cache::new(|path: Vec| { + let mut loaded = loaded_cell.borrow_mut(); + loaded.by_clone(path.clone()).as_ref().map(|data| { + let mut preparsed = preparsed_cell.borrow_mut(); + match data { + Loaded::Namespace(names) => Some(names.clone()), + Loaded::Module(source) => preparsed.by_clone(path).as_ref().map(|data| { + parse::exported_names(&data).into_iter() + .map(|n| n[0].clone()) + .collect() + }), + _ => None + } + }).flatten() + })); + let imports_cell = RefCell::new(Cache::new(|path: Vec| { + let mut preparsed = preparsed_cell.borrow_mut(); + let entv = preparsed.by_clone(path).clone()?; + let import_entries = parse::imports(entv.iter()); + let mut imported_symbols: HashMap> = HashMap::new(); + for imp in import_entries { + let mut exports = exports_cell.borrow_mut(); + let export = exports.by_clone(imp.path.clone()).as_ref()?; + if let Some(ref name) = imp.name { + if export.contains(&name) { + imported_symbols.insert(name.clone(), imp.path.clone()); + } + } else { + for exp in export.clone() { + imported_symbols.insert(exp.clone(), imp.path.clone()); + } + } } - }).flatten()); - let imports = Cache::new(|path: &[&str]| preparsed.get(path).map(|data| { - data.iter().filter_map(|ent| match ent { - FileEntry::Import(imp) => Some(imp), - _ => None - }).flatten().collect::>() + Some(imported_symbols) + })); + let parsed = RefCell::new(Cache::new(|path: Vec| { + let mut imports = imports_cell.borrow_mut(); + let mut loaded = loaded_cell.borrow_mut(); + let data = loaded.by_clone(path.clone()).as_ref()?; + let text = match data { Loaded::Module(s) => Some(s), _ => None }?; + let imported_symbols = imports.by_clone(path).as_ref()?; + let imported_ops: Vec<&str> = imported_symbols.keys() + .chain(prelude_vec.iter()) + .map(|s| s.as_str()) + .filter(|s| parse::is_op(s)) + .collect(); + let file_parser = file_parser(prelude, &imported_ops); + file_parser.parse(text.as_str()).ok() })); // let main = preparsed.get(&[entry]); // for imp in parse::imports(main) { @@ -81,7 +126,6 @@ where F: FnMut(&[&str]) -> Option { // modules: HashMap::new() // }; - // Some(project) todo!("Finish this function") } \ No newline at end of file diff --git a/src/utils/cache.rs b/src/utils/cache.rs index 230697d..ddff5e1 100644 --- a/src/utils/cache.rs +++ b/src/utils/cache.rs @@ -1,23 +1,32 @@ -use std::{collections::HashMap, hash::Hash}; +use std::collections::HashMap; +use std::hash::Hash; /// Cache the return values of an effectless closure in a hashmap /// Inspired by the closure_cacher crate. -pub struct Cache where F: FnMut(I) -> O { +pub struct Cache { store: HashMap, closure: F } -impl Cache -where - F: FnMut(I) -> O, - I: Eq + Hash + Copy +impl Cache where + I: Eq + Hash, + F: FnMut(I) -> O { - pub fn new(closure: F) -> Self { Self { store: HashMap::new(), closure } } - pub fn get(&mut self, i: I) -> &O { - // I copied it because I might need `drop` and I prefer `I` to be unconstrained. + pub fn new(closure: F) -> Self { + Self { store: HashMap::new(), closure } + } + pub fn by_copy(&mut self, i: I) -> &O where I: Copy { let closure = &mut self.closure; self.store.entry(i).or_insert_with(|| closure(i)) } + pub fn by_clone(&mut self, i: I) -> &O where I: Clone { + let closure = &mut self.closure; + // Make sure we only clone if necessary + self.store.entry(i).or_insert_with_key(|k| closure(k.clone())) + } + pub fn known(&self, i: &I) -> Option<&O> { + self.store.get(i) + } /// Forget the output for the given input pub fn drop(&mut self, i: &I) -> bool { self.store.remove(i).is_some()