diff --git a/Cargo.lock b/Cargo.lock index a4969a2..7589a66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -212,6 +212,16 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "duplicate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de78e66ac9061e030587b2a2e75cc88f22304913c907b11307bca737141230cb" +dependencies = [ + "heck", + "proc-macro-error", +] + [[package]] name = "dyn-clone" version = "1.0.11" @@ -395,6 +405,7 @@ version = "0.2.2" dependencies = [ "chumsky", "clap", + "duplicate", "dyn-clone", "hashbrown 0.13.2", "itertools", @@ -420,6 +431,30 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.56" diff --git a/Cargo.toml b/Cargo.toml index 7e05313..9498940 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,3 +32,4 @@ clap = { version = "4.3", features = ["derive"] } trait-set = "0.3" paste = "1.0" rust-embed = { version = "6.6", features = ["include-exclude"] } +duplicate = "1.0.0" diff --git a/src/bin/main.rs b/src/bin/main.rs index 68a94f9..05be6bf 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -2,13 +2,15 @@ mod cli; use std::fs::File; use std::path::{Path, PathBuf}; -use std::process; +use std::{iter, process}; use clap::Parser; use hashbrown::HashMap; use itertools::Itertools; -use orchidlang::interner::{InternedDisplay, Interner, Sym}; -use orchidlang::{ast, ast_to_interpreted, interpreter, pipeline, rule, stl}; +use orchidlang::interner::{InternedDisplay, Interner}; +use orchidlang::{ + ast, ast_to_interpreted, interpreter, pipeline, rule, stl, Stok, Sym, VName, +}; use crate::cli::cmd_prompt; @@ -66,12 +68,16 @@ impl Args { /// Load and parse all source related to the symbol `target` or all symbols /// in the namespace `target` in the context of the STL. All sourcefiles must /// reside within `dir`. -fn load_dir(dir: &Path, target: Sym, i: &Interner) -> pipeline::ProjectTree { +fn load_dir( + dir: &Path, + target: &[Stok], + i: &Interner, +) -> pipeline::ProjectTree { let file_cache = pipeline::file_loader::mk_dir_cache(dir.to_path_buf(), i); let library = stl::mk_stl(i, stl::StlOptions::default()); pipeline::parse_layer( - &[target], - &|path| file_cache.find(&path), + iter::once(target), + &|path| file_cache.find(path), &library, &stl::mk_prelude(i), i, @@ -79,12 +85,12 @@ fn load_dir(dir: &Path, target: Sym, i: &Interner) -> pipeline::ProjectTree { .expect("Failed to load source code") } -pub fn to_sym(data: &str, i: &Interner) -> Sym { - i.i(&data.split("::").map(|s| i.i(s)).collect::>()[..]) +pub fn to_vname(data: &str, i: &Interner) -> VName { + data.split("::").map(|s| i.i(s)).collect::>() } /// A little utility to step through the resolution of a macro set -pub fn macro_debug(repo: rule::Repo, mut code: ast::Expr, i: &Interner) { +pub fn macro_debug(repo: rule::Repo, mut code: ast::Expr, i: &Interner) { let mut idx = 0; println!("Macro debugger working on {}", code.bundle(i)); loop { @@ -119,8 +125,8 @@ pub fn main() { args.chk_proj().unwrap_or_else(|e| panic!("{e}")); let dir = PathBuf::try_from(args.dir).unwrap(); let i = Interner::new(); - let main = to_sym(&args.main, &i); - let project = load_dir(&dir, main, &i); + let main = to_vname(&args.main, &i); + let project = pipeline::vname_to_sym_tree(load_dir(&dir, &main, &i), &i); let rules = pipeline::collect_rules(&project); let consts = pipeline::collect_consts(&project, &i); let repo = rule::Repo::new(rules, &i).unwrap_or_else(|(rule, error)| { @@ -135,7 +141,7 @@ pub fn main() { println!("Parsed rules: {}", repo.bundle(&i)); return; } else if !args.macro_debug.is_empty() { - let name = to_sym(&args.macro_debug, &i); + let name = i.i(&to_vname(&args.macro_debug, &i)); let code = consts .get(&name) .unwrap_or_else(|| panic!("Constant {} not found", args.macro_debug)); @@ -153,7 +159,7 @@ pub fn main() { } let ctx = interpreter::Context { symbols: &exec_table, interner: &i, gas: None }; - let entrypoint = exec_table.get(&main).unwrap_or_else(|| { + let entrypoint = exec_table.get(&i.i(&main)).unwrap_or_else(|| { let main = args.main; let symbols = exec_table.keys().map(|t| i.extern_vec(*t).join("::")).join(", "); diff --git a/src/interner/mod.rs b/src/interner/mod.rs index a81a612..5b86a87 100644 --- a/src/interner/mod.rs +++ b/src/interner/mod.rs @@ -2,24 +2,12 @@ //! //! Can be used to deduplicate various structures for fast equality comparisons. //! The parser uses it to intern strings. -mod display; mod monotype; mod multitype; mod token; +mod traits; -pub use display::{DisplayBundle, InternedDisplay}; pub use monotype::TypedInterner; pub use multitype::Interner; pub use token::Tok; - -/// A symbol, nsname, nname or namespaced name is a sequence of namespaces -/// and an identifier. The [Vec] can never be empty. -/// -/// Throughout different stages of processing, these names can be -/// -/// - local names to be prefixed with the current module -/// - imported names starting with a segment -/// - ending a single import or -/// - defined in one of the glob imported modules -/// - absolute names -pub type Sym = Tok>>; +pub use traits::{DisplayBundle, InternedDisplay, InternedInto}; diff --git a/src/interner/display.rs b/src/interner/traits.rs similarity index 79% rename from src/interner/display.rs rename to src/interner/traits.rs index fbecef0..e365802 100644 --- a/src/interner/display.rs +++ b/src/interner/traits.rs @@ -54,3 +54,18 @@ impl<'a, T: InternedDisplay + ?Sized> Display for DisplayBundle<'a, T> { self.data.fmt_i(f, self.interner) } } + +/// Conversions that are possible in the presence of an interner +/// +/// Essentially, this allows to define abstractions over interned and +/// non-interned versions of a type and convert between them +pub trait InternedInto { + /// Execute the conversion + fn into_i(self, i: &Interner) -> U; +} + +impl, U> InternedInto for T { + fn into_i(self, _i: &Interner) -> U { + self.into() + } +} diff --git a/src/interpreter/context.rs b/src/interpreter/context.rs index 8439182..b5f95d7 100644 --- a/src/interpreter/context.rs +++ b/src/interpreter/context.rs @@ -1,7 +1,8 @@ use hashbrown::HashMap; -use crate::interner::{Interner, Sym}; +use crate::interner::Interner; use crate::representations::interpreted::ExprInst; +use crate::Sym; /// All the data associated with an interpreter run #[derive(Clone)] diff --git a/src/lib.rs b/src/lib.rs index 4dd8e12..9aaa55b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,7 +18,10 @@ pub mod rule; pub mod stl; mod utils; -pub use interner::Sym; +use interner::Tok; +pub use representations::{NameLike, Sym, VName}; +/// Element of VName and a common occurrence in the API +pub type Stok = Tok; pub use representations::ast_to_interpreted::ast_to_interpreted; pub use representations::{ ast, interpreted, sourcefile, tree, Literal, Location, PathSet, Primitive, diff --git a/src/parse/expression.rs b/src/parse/expression.rs index 4020f2f..0a9507d 100644 --- a/src/parse/expression.rs +++ b/src/parse/expression.rs @@ -8,15 +8,14 @@ use super::context::Context; use super::decls::SimpleParser; use super::enum_filter::enum_filter; use super::lexer::{filter_map_lex, Entry, Lexeme}; -use crate::interner::Sym; use crate::representations::ast::{Clause, Expr}; use crate::representations::location::Location; -use crate::representations::Primitive; +use crate::representations::{Primitive, VName}; /// Parses any number of expr wrapped in (), [] or {} fn sexpr_parser( - expr: impl SimpleParser + Clone, -) -> impl SimpleParser)> + Clone { + expr: impl SimpleParser> + Clone, +) -> impl SimpleParser, Range)> + Clone { let body = expr.repeated(); choice(( Lexeme::LP('(').parser().then(body.clone()).then(Lexeme::RP('(').parser()), @@ -40,9 +39,9 @@ fn sexpr_parser( /// and type and body are both expressions. Comments are allowed /// and ignored everywhere in between the tokens fn lambda_parser<'a>( - expr: impl SimpleParser + Clone + 'a, + expr: impl SimpleParser> + Clone + 'a, ctx: impl Context + 'a, -) -> impl SimpleParser)> + Clone + 'a { +) -> impl SimpleParser, Range)> + Clone + 'a { Lexeme::BS .parser() .ignore_then(expr.clone()) @@ -56,9 +55,8 @@ fn lambda_parser<'a>( /// Parses a sequence of names separated by ::
/// Comments and line breaks are allowed and ignored in between -pub fn ns_name_parser<'a>( - ctx: impl Context + 'a, -) -> impl SimpleParser)> + Clone + 'a { +pub fn ns_name_parser<'a>() +-> impl SimpleParser)> + Clone + 'a { filter_map_lex(enum_filter!(Lexeme::Name)) .separated_by(Lexeme::NS.parser()) .at_least(1) @@ -66,32 +64,31 @@ pub fn ns_name_parser<'a>( let start = elements.first().expect("can never be empty").1.start; let end = elements.last().expect("can never be empty").1.end; let tokens = (elements.iter().map(|(t, _)| *t)).collect::>(); - (ctx.interner().i(&tokens), start..end) + (tokens, start..end) }) .labelled("Namespaced name") } -pub fn namelike_parser<'a>( - ctx: impl Context + 'a, -) -> impl SimpleParser)> + Clone + 'a { +pub fn namelike_parser<'a>() +-> impl SimpleParser, Range)> + Clone + 'a { choice(( filter_map_lex(enum_filter!(Lexeme::PH)) .map(|(ph, range)| (Clause::Placeh(ph), range)), - ns_name_parser(ctx).map(|(token, range)| (Clause::Name(token), range)), + ns_name_parser().map(|(token, range)| (Clause::Name(token), range)), )) } pub fn clause_parser<'a>( - expr: impl SimpleParser + Clone + 'a, + expr: impl SimpleParser> + Clone + 'a, ctx: impl Context + 'a, -) -> impl SimpleParser)> + Clone + 'a { +) -> impl SimpleParser, Range)> + Clone + 'a { choice(( filter_map_lex(enum_filter!(Lexeme >> Primitive; Literal)) .map(|(p, s)| (Clause::P(p), s)) .labelled("Literal"), sexpr_parser(expr.clone()), - lambda_parser(expr, ctx.clone()), - namelike_parser(ctx), + lambda_parser(expr, ctx), + namelike_parser(), )) .labelled("Clause") } @@ -99,7 +96,7 @@ pub fn clause_parser<'a>( /// Parse an expression pub fn xpr_parser<'a>( ctx: impl Context + 'a, -) -> impl SimpleParser + 'a { +) -> impl SimpleParser> + 'a { recursive(move |expr| { clause_parser(expr, ctx.clone()).map(move |(value, range)| Expr { value, diff --git a/src/parse/sourcefile.rs b/src/parse/sourcefile.rs index 4384d6c..1271e03 100644 --- a/src/parse/sourcefile.rs +++ b/src/parse/sourcefile.rs @@ -15,20 +15,17 @@ use super::Entry; use crate::ast::{Clause, Constant, Expr, Rule}; use crate::representations::location::Location; use crate::representations::sourcefile::{FileEntry, Member, Namespace}; +use crate::representations::VName; fn rule_parser<'a>( ctx: impl Context + 'a, -) -> impl SimpleParser + 'a { +) -> impl SimpleParser> + 'a { xpr_parser(ctx.clone()) .repeated() .at_least(1) .then(filter_map_lex(enum_filter!(Lexeme::Rule))) .then(xpr_parser(ctx).repeated().at_least(1)) - .map(|((p, (prio, _)), t)| Rule { - pattern: Rc::new(p), - prio, - template: Rc::new(t), - }) + .map(|((p, (prio, _)), t)| Rule { pattern: p, prio, template: t }) .labelled("Rule") } diff --git a/src/pipeline/file_loader.rs b/src/pipeline/file_loader.rs index a964be2..676d6de 100644 --- a/src/pipeline/file_loader.rs +++ b/src/pipeline/file_loader.rs @@ -6,12 +6,11 @@ use std::{fs, io}; use chumsky::text::Character; use rust_embed::RustEmbed; -use crate::interner::{Interner, Sym}; -use crate::pipeline::error::{ - ErrorPosition, ProjectError, UnexpectedDirectory, -}; +use crate::interner::Interner; +use crate::pipeline::error::{ErrorPosition, ProjectError}; use crate::utils::iter::box_once; use crate::utils::{BoxedIter, Cache}; +use crate::{Stok, VName}; /// All the data available about a failed source load call #[derive(Debug)] @@ -89,9 +88,9 @@ pub fn load_file(root: &Path, path: &[impl AsRef]) -> IOResult { } /// Generates a cached file loader for a directory -pub fn mk_dir_cache(root: PathBuf, i: &Interner) -> Cache { - Cache::new(move |token: Sym, _this| -> IOResult { - let path = i.r(token).iter().map(|t| i.r(*t).as_str()).collect::>(); +pub fn mk_dir_cache(root: PathBuf, i: &Interner) -> Cache { + Cache::new(move |vname: VName, _this| -> IOResult { + let path = vname.iter().map(|t| i.r(*t).as_str()).collect::>(); load_file(&root, &path) }) } @@ -128,28 +127,9 @@ pub fn load_embed(path: &str, ext: &str) -> IOResult { pub fn mk_embed_cache<'a, T: 'static + RustEmbed>( ext: &'a str, i: &'a Interner, -) -> Cache<'a, Sym, IOResult> { - Cache::new(move |token: Sym, _this| -> IOResult { - let path = i.extern_vec(token).join("/"); +) -> Cache<'a, Vec, IOResult> { + Cache::new(move |vname: VName, _this| -> IOResult { + let path = i.extern_all(&vname).join("/"); load_embed::(&path, ext) }) } - -/// Loads the string contents of a file at the given location. -/// If the path points to a directory, raises an error. -pub fn load_text( - path: Sym, - load_file: &impl Fn(Sym) -> IOResult, - i: &Interner, -) -> Result, Rc> { - if let Loaded::Code(s) = load_file(path)? { - Ok(s) - } else { - Err( - UnexpectedDirectory { - path: i.r(path).iter().map(|t| i.r(*t)).cloned().collect(), - } - .rc(), - ) - } -} diff --git a/src/pipeline/import_resolution/alias_map.rs b/src/pipeline/import_resolution/alias_map.rs index 63ab3fc..751a999 100644 --- a/src/pipeline/import_resolution/alias_map.rs +++ b/src/pipeline/import_resolution/alias_map.rs @@ -2,22 +2,22 @@ use std::hash::Hash; use hashbrown::{HashMap, HashSet}; -use crate::interner::Sym; +use crate::interner::Tok; #[derive(Clone, Debug, Default)] pub struct AliasMap { - pub targets: HashMap, - pub aliases: HashMap>, + pub targets: HashMap>, Vec>>, + pub aliases: HashMap>, HashSet>>>, } impl AliasMap { pub fn new() -> Self { Self::default() } - pub fn link(&mut self, alias: Sym, target: Sym) { - let prev = self.targets.insert(alias, target); + pub fn link(&mut self, alias: Vec>, target: Vec>) { + let prev = self.targets.insert(alias.clone(), target.clone()); debug_assert!(prev.is_none(), "Alias already has a target"); - multimap_entry(&mut self.aliases, &target).insert(alias); + multimap_entry(&mut self.aliases, &target).insert(alias.clone()); // Remove aliases of the alias if let Some(alts) = self.aliases.remove(&alias) { for alt in alts { @@ -26,17 +26,18 @@ impl AliasMap { self.aliases.get(&alt).map(HashSet::is_empty).unwrap_or(true), "Alias set of alias not empty" ); + let alt_target = self.targets.insert(alt.clone(), target.clone()); debug_assert!( - self.targets.insert(alt, target) == Some(alias), + alt_target.as_ref() == Some(&alias), "Name not target of its own alias" ); - multimap_entry(&mut self.aliases, &target).insert(alt); + multimap_entry(&mut self.aliases, &alias).insert(alt); } } } - pub fn resolve(&self, alias: Sym) -> Option { - self.targets.get(&alias).copied() + pub fn resolve(&self, alias: &[Tok]) -> Option<&Vec>> { + self.targets.get(alias) } } diff --git a/src/pipeline/import_resolution/apply_aliases.rs b/src/pipeline/import_resolution/apply_aliases.rs index 15e5e78..cf24b30 100644 --- a/src/pipeline/import_resolution/apply_aliases.rs +++ b/src/pipeline/import_resolution/apply_aliases.rs @@ -1,24 +1,22 @@ -use std::rc::Rc; - use hashbrown::HashMap; use super::alias_map::AliasMap; use super::decls::{InjectedAsFn, UpdatedFn}; use crate::ast::{Expr, Rule}; -use crate::interner::{Interner, Sym, Tok}; +use crate::interner::Tok; use crate::pipeline::{ProjectExt, ProjectModule}; use crate::representations::tree::{ModEntry, ModMember}; +use crate::representations::VName; use crate::utils::Substack; fn resolve_rec( - token: Sym, + namespace: &[Tok], alias_map: &AliasMap, - i: &Interner, ) -> Option>> { - if let Some(alias) = alias_map.resolve(token) { - Some(i.r(alias).clone()) - } else if let Some((foot, body)) = i.r(token).split_last() { - let mut new_beginning = resolve_rec(i.i(body), alias_map, i)?; + if let Some(alias) = alias_map.resolve(namespace) { + Some(alias.clone()) + } else if let Some((foot, body)) = namespace.split_last() { + let mut new_beginning = resolve_rec(body, alias_map)?; new_beginning.push(*foot); Some(new_beginning) } else { @@ -27,25 +25,23 @@ fn resolve_rec( } fn resolve( - token: Sym, + namespace: &[Tok], alias_map: &AliasMap, injected_as: &impl InjectedAsFn, - i: &Interner, -) -> Option { - injected_as(&i.r(token)[..]).or_else(|| { - let next_v = resolve_rec(token, alias_map, i)?; - Some(injected_as(&next_v).unwrap_or_else(|| i.i(&next_v))) +) -> Option>> { + injected_as(namespace).or_else(|| { + let next_v = resolve_rec(namespace, alias_map)?; + Some(injected_as(&next_v).unwrap_or(next_v)) }) } fn process_expr( - expr: &Expr, + expr: &Expr, alias_map: &AliasMap, injected_as: &impl InjectedAsFn, - i: &Interner, -) -> Expr { +) -> Expr { expr - .map_names(&|n| resolve(n, alias_map, injected_as, i)) + .map_names(&|n| resolve(n, alias_map, injected_as)) .unwrap_or_else(|| expr.clone()) } @@ -53,32 +49,23 @@ fn process_expr( /// Replace all aliases with the name they're originally defined as fn apply_aliases_rec( path: Substack>, - module: &ProjectModule, + module: &ProjectModule, alias_map: &AliasMap, - i: &Interner, injected_as: &impl InjectedAsFn, updated: &impl UpdatedFn, -) -> ProjectModule { +) -> ProjectModule { let items = (module.items.iter()) .map(|(name, ent)| { let ModEntry { exported, member } = ent; let member = match member { ModMember::Item(expr) => - ModMember::Item(process_expr(expr, alias_map, injected_as, i)), + ModMember::Item(process_expr(expr, alias_map, injected_as)), ModMember::Sub(module) => { let subpath = path.push(*name); let new_mod = if !updated(&subpath.iter().rev_vec_clone()) { module.clone() } else { - let module = module.as_ref(); - Rc::new(apply_aliases_rec( - subpath, - module, - alias_map, - i, - injected_as, - updated, - )) + apply_aliases_rec(subpath, module, alias_map, injected_as, updated) }; ModMember::Sub(new_mod) }, @@ -91,16 +78,12 @@ fn apply_aliases_rec( let Rule { pattern, prio, template } = rule; Rule { prio: *prio, - pattern: Rc::new( - (pattern.iter()) - .map(|expr| process_expr(expr, alias_map, injected_as, i)) - .collect::>(), - ), - template: Rc::new( - (template.iter()) - .map(|expr| process_expr(expr, alias_map, injected_as, i)) - .collect::>(), - ), + pattern: (pattern.iter()) + .map(|expr| process_expr(expr, alias_map, injected_as)) + .collect::>(), + template: (template.iter()) + .map(|expr| process_expr(expr, alias_map, injected_as)) + .collect::>(), } }) .collect::>(); @@ -111,7 +94,7 @@ fn apply_aliases_rec( rules, exports: (module.extra.exports.iter()) .map(|(k, v)| { - (*k, resolve(*v, alias_map, injected_as, i).unwrap_or(*v)) + (*k, resolve(v, alias_map, injected_as).unwrap_or(v.clone())) }) .collect(), file: module.extra.file.clone(), @@ -121,18 +104,10 @@ fn apply_aliases_rec( } pub fn apply_aliases( - module: &ProjectModule, + module: &ProjectModule, alias_map: &AliasMap, - i: &Interner, injected_as: &impl InjectedAsFn, updated: &impl UpdatedFn, -) -> ProjectModule { - apply_aliases_rec( - Substack::Bottom, - module, - alias_map, - i, - injected_as, - updated, - ) +) -> ProjectModule { + apply_aliases_rec(Substack::Bottom, module, alias_map, injected_as, updated) } diff --git a/src/pipeline/import_resolution/collect_aliases.rs b/src/pipeline/import_resolution/collect_aliases.rs index d9e554c..45930a2 100644 --- a/src/pipeline/import_resolution/collect_aliases.rs +++ b/src/pipeline/import_resolution/collect_aliases.rs @@ -7,21 +7,23 @@ use crate::interner::{Interner, Tok}; use crate::pipeline::error::{NotExported, ProjectError}; use crate::pipeline::project_tree::{split_path, ProjectModule, ProjectTree}; use crate::representations::tree::{ModMember, WalkErrorKind}; +use crate::representations::VName; use crate::utils::{pushed, unwrap_or, Substack}; /// Assert that a module identified by a path can see a given symbol fn assert_visible( source: &[Tok], // must point to a file or submodule target: &[Tok], // may point to a symbol or module of any kind - project: &ProjectTree, + project: &ProjectTree, i: &Interner, ) -> Result<(), Rc> { let (tgt_item, tgt_path) = unwrap_or!(target.split_last(); return Ok(())); let shared_len = source.iter().zip(tgt_path.iter()).take_while(|(a, b)| a == b).count(); let vis_ignored_len = usize::min(tgt_path.len(), shared_len + 1); - let private_root = - (project.0).walk(&tgt_path[..vis_ignored_len], false).unwrap_or_else(|e| { + let private_root = (project.0) + .walk_ref(&tgt_path[..vis_ignored_len], false) + .unwrap_or_else(|e| { let path_slc = &tgt_path[..vis_ignored_len]; let bad_path = i.extern_all(path_slc).join("::"); eprintln!( @@ -32,7 +34,7 @@ fn assert_visible( panic!("") }); let direct_parent = private_root - .walk(&tgt_path[vis_ignored_len..], true) + .walk_ref(&tgt_path[vis_ignored_len..], true) .map_err(|e| match e.kind { WalkErrorKind::Missing => panic!("checked in parsing"), WalkErrorKind::Private => { @@ -70,8 +72,8 @@ fn assert_visible( /// Populate target and alias maps from the module tree recursively fn collect_aliases_rec( path: Substack>, - module: &ProjectModule, - project: &ProjectTree, + module: &ProjectModule, + project: &ProjectTree, alias_map: &mut AliasMap, i: &Interner, updated: &impl UpdatedFn, @@ -81,31 +83,29 @@ fn collect_aliases_rec( if !updated(&mod_path_v) { return Ok(()); }; - for (&name, &target_mod_name) in module.extra.imports_from.iter() { - let target_mod_v = i.r(target_mod_name); - let target_sym_v = pushed(target_mod_v, name); + for (&name, target_mod_name) in module.extra.imports_from.iter() { + let target_sym_v = pushed(target_mod_name, name); assert_visible(&mod_path_v, &target_sym_v, project, i)?; let sym_path_v = pushed(&mod_path_v, name); - let sym_path = i.i(&sym_path_v); - let target_mod = (project.0.walk(target_mod_v, false)) + let target_mod = (project.0.walk_ref(target_mod_name, false)) .expect("checked above in assert_visible"); - let target_sym = - *target_mod.extra.exports.get(&name).unwrap_or_else(|| { + let target_sym = target_mod + .extra + .exports + .get(&name) + .unwrap_or_else(|| { panic!( "error in {}, {} has no member {}", i.extern_all(&mod_path_v).join("::"), - i.extern_all(target_mod_v).join("::"), + i.extern_all(target_mod_name).join("::"), i.r(name) ) - }); - alias_map.link(sym_path, target_sym); + }) + .clone(); + alias_map.link(sym_path_v, target_sym); } for (&name, entry) in module.items.iter() { - let submodule = if let ModMember::Sub(s) = &entry.member { - s.as_ref() - } else { - continue; - }; + let submodule = unwrap_or!(&entry.member => ModMember::Sub; continue); collect_aliases_rec( path.push(name), submodule, @@ -120,8 +120,8 @@ fn collect_aliases_rec( /// Populate target and alias maps from the module tree pub fn collect_aliases( - module: &ProjectModule, - project: &ProjectTree, + module: &ProjectModule, + project: &ProjectTree, alias_map: &mut AliasMap, i: &Interner, updated: &impl UpdatedFn, diff --git a/src/pipeline/import_resolution/decls.rs b/src/pipeline/import_resolution/decls.rs index bd735b1..f464131 100644 --- a/src/pipeline/import_resolution/decls.rs +++ b/src/pipeline/import_resolution/decls.rs @@ -1,8 +1,8 @@ use trait_set::trait_set; -use crate::interner::{Sym, Tok}; +use crate::interner::Tok; trait_set! { - pub trait InjectedAsFn = Fn(&[Tok]) -> Option; + pub trait InjectedAsFn = Fn(&[Tok]) -> Option>>; pub trait UpdatedFn = Fn(&[Tok]) -> bool; } diff --git a/src/pipeline/import_resolution/resolve_imports.rs b/src/pipeline/import_resolution/resolve_imports.rs index c9bcd14..f33d259 100644 --- a/src/pipeline/import_resolution/resolve_imports.rs +++ b/src/pipeline/import_resolution/resolve_imports.rs @@ -7,18 +7,18 @@ use super::decls::{InjectedAsFn, UpdatedFn}; use crate::interner::Interner; use crate::pipeline::error::ProjectError; use crate::pipeline::project_tree::ProjectTree; +use crate::representations::VName; /// Follow import chains to locate the original name of all tokens, then /// replace these aliases with the original names throughout the tree pub fn resolve_imports( - project: ProjectTree, + project: ProjectTree, i: &Interner, injected_as: &impl InjectedAsFn, updated: &impl UpdatedFn, -) -> Result> { +) -> Result, Rc> { let mut map = AliasMap::new(); - collect_aliases(project.0.as_ref(), &project, &mut map, i, updated)?; - let new_mod = - apply_aliases(project.0.as_ref(), &map, i, injected_as, updated); - Ok(ProjectTree(Rc::new(new_mod))) + collect_aliases(&project.0, &project, &mut map, i, updated)?; + let new_mod = apply_aliases(&project.0, &map, injected_as, updated); + Ok(ProjectTree(new_mod)) } diff --git a/src/pipeline/mod.rs b/src/pipeline/mod.rs index 3b06847..0fa5532 100644 --- a/src/pipeline/mod.rs +++ b/src/pipeline/mod.rs @@ -9,6 +9,6 @@ mod source_loader; pub use parse_layer::parse_layer; pub use project_tree::{ - collect_consts, collect_rules, from_const_tree, ConstTree, ProjectExt, - ProjectModule, ProjectTree, + collect_consts, collect_rules, from_const_tree, vname_to_sym_tree, ConstTree, + ProjectExt, ProjectModule, ProjectTree, }; diff --git a/src/pipeline/parse_layer.rs b/src/pipeline/parse_layer.rs index 23efedb..ac1707f 100644 --- a/src/pipeline/parse_layer.rs +++ b/src/pipeline/parse_layer.rs @@ -3,8 +3,9 @@ use std::rc::Rc; use super::error::ProjectError; use super::file_loader::IOResult; use super::{import_resolution, project_tree, source_loader, ProjectTree}; -use crate::interner::{Interner, Sym, Tok}; +use crate::interner::{Interner, Tok}; use crate::representations::sourcefile::FileEntry; +use crate::representations::VName; /// Using an IO callback, produce a project tree that includes the given /// target symbols or files if they're defined. @@ -14,35 +15,32 @@ use crate::representations::sourcefile::FileEntry; /// prelude which will be prepended to each individual file. Since the /// prelude gets compiled with each file, normally it should be a glob /// import pointing to a module in the environment. -pub fn parse_layer( - targets: &[Sym], - loader: &impl Fn(Sym) -> IOResult, - environment: &ProjectTree, +pub fn parse_layer<'a>( + targets: impl Iterator]>, + loader: &impl Fn(&[Tok]) -> IOResult, + environment: &'a ProjectTree, prelude: &[FileEntry], i: &Interner, -) -> Result> { +) -> Result, Rc> { // A path is injected if it is walkable in the injected tree let injected_as = |path: &[Tok]| { let (item, modpath) = path.split_last()?; - let module = environment.0.walk(modpath, false).ok()?; - let inj = module.extra.exports.get(item).copied()?; - Some(inj) + let module = environment.0.walk_ref(modpath, false).ok()?; + module.extra.exports.get(item).cloned() }; let injected_names = |path: Tok>>| { - let module = environment.0.walk(&i.r(path)[..], false).ok()?; + let module = environment.0.walk_ref(&i.r(path)[..], false).ok()?; Some(Rc::new(module.extra.exports.keys().copied().collect())) }; let source = source_loader::load_source(targets, prelude, i, loader, &|path| { - environment.0.walk(&i.r(path)[..], false).is_ok() + environment.0.walk_ref(path, false).is_ok() })?; let tree = project_tree::build_tree(source, i, prelude, &injected_names)?; - let sum = ProjectTree(Rc::new( - environment.0.as_ref().clone().overlay(tree.0.as_ref().clone()), - )); + let sum = ProjectTree(environment.0.clone().overlay(tree.0.clone())); let resolvd = import_resolution::resolve_imports(sum, i, &injected_as, &|path| { - tree.0.walk(path, false).is_ok() + tree.0.walk_ref(path, false).is_ok() })?; // Addition among modules favours the left hand side. Ok(resolvd) diff --git a/src/pipeline/project_tree/build_tree.rs b/src/pipeline/project_tree/build_tree.rs index 62e722e..29af43a 100644 --- a/src/pipeline/project_tree/build_tree.rs +++ b/src/pipeline/project_tree/build_tree.rs @@ -12,8 +12,9 @@ use crate::pipeline::error::ProjectError; use crate::pipeline::source_loader::{LoadedSource, LoadedSourceTable}; use crate::representations::sourcefile::{absolute_path, FileEntry, Member}; use crate::representations::tree::{ModEntry, ModMember, Module}; +use crate::representations::{NameLike, VName}; use crate::utils::iter::{box_empty, box_once}; -use crate::utils::{pushed, Substack}; +use crate::utils::{pushed, unwrap_or, Substack}; #[derive(Debug)] struct ParsedSource<'a> { @@ -24,7 +25,7 @@ struct ParsedSource<'a> { pub fn split_path<'a>( path: &'a [Tok], - proj: &'a ProjectTree, + proj: &'a ProjectTree, ) -> (&'a [Tok], &'a [Tok]) { let (end, body) = if let Some(s) = path.split_last() { s @@ -32,9 +33,9 @@ pub fn split_path<'a>( return (&[], &[]); }; let mut module = - proj.0.walk(body, false).expect("invalid path cannot be split"); + proj.0.walk_ref(body, false).expect("invalid path cannot be split"); if let ModMember::Sub(m) = &module.items[end].member { - module = m.clone(); + module = m; } let file = module.extra.file.as_ref().map(|s| &path[..s.len()]).unwrap_or(path); @@ -52,7 +53,7 @@ fn source_to_module( // context i: &Interner, filepath_len: usize, -) -> Rc> { +) -> Module, ProjectExt> { let path_v = path.iter().rev_vec_clone(); let imports = data .iter() @@ -70,13 +71,13 @@ fn source_to_module( let mut abs_path = absolute_path(&path_v, &imp_path_v, i).expect("tested in preparsing"); let name = abs_path.pop().expect("importing the global context"); - (name, i.i(&abs_path)) + (name, abs_path) }) .collect::>(); let exports = data .iter() .flat_map(|ent| { - let mk_ent = |name| (name, i.i(&pushed(&path_v, name))); + let mk_ent = |name| (name, pushed(&path_v, name)); match ent { FileEntry::Export(names) => Box::new(names.iter().copied().map(mk_ent)), FileEntry::Exported(mem) => match mem { @@ -86,8 +87,8 @@ fn source_to_module( let mut names = Vec::new(); for e in rule.pattern.iter() { e.visit_names(Substack::Bottom, &mut |n| { - if let Some([name]) = i.r(n).strip_prefix(&path_v[..]) { - names.push((*name, n)) + if let Some([name]) = n.strip_prefix(&path_v[..]) { + names.push((*name, n.clone())) } }) } @@ -109,58 +110,37 @@ fn source_to_module( .collect::>(); let items = data .into_iter() - .filter_map(|ent| match ent { - FileEntry::Exported(Member::Namespace(ns)) => { - let prep_member = &preparsed.items[&ns.name].member; - let new_prep = if let ModMember::Sub(s) = prep_member { - s.as_ref() - } else { - panic!("preparsed missing a submodule") - }; - let module = source_to_module( - path.push(ns.name), - new_prep, - ns.body, - i, - filepath_len, - ); - let member = ModMember::Sub(module); - Some((ns.name, ModEntry { exported: true, member })) - }, - FileEntry::Internal(Member::Namespace(ns)) => { - let prep_member = &preparsed.items[&ns.name].member; - let new_prep = if let ModMember::Sub(s) = prep_member { - s.as_ref() - } else { - panic!("preparsed missing a submodule") - }; - let module = source_to_module( - path.push(ns.name), - new_prep, - ns.body, - i, - filepath_len, - ); - let member = ModMember::Sub(module); - Some((ns.name, ModEntry { exported: false, member })) - }, - FileEntry::Exported(Member::Constant(Constant { name, value })) => { - let member = ModMember::Item(value); - Some((name, ModEntry { exported: true, member })) - }, - FileEntry::Internal(Member::Constant(Constant { name, value })) => { - let member = ModMember::Item(value); - Some((name, ModEntry { exported: false, member })) - }, - _ => None, + .filter_map(|ent| { + let member_to_item = |exported, member| match member { + Member::Namespace(ns) => { + let new_prep = unwrap_or!( + &preparsed.items[&ns.name].member => ModMember::Sub; + panic!("preparsed missing a submodule") + ); + let module = source_to_module( + path.push(ns.name), + new_prep, + ns.body, + i, + filepath_len, + ); + let member = ModMember::Sub(module); + Some((ns.name, ModEntry { exported, member })) + }, + Member::Constant(Constant { name, value }) => { + let member = ModMember::Item(value); + Some((name, ModEntry { exported, member })) + }, + _ => None, + }; + match ent { + FileEntry::Exported(member) => member_to_item(true, member), + FileEntry::Internal(member) => member_to_item(false, member), + _ => None, + } }) .collect::>(); - // println!( - // "Constructing file-module {} with members ({})", - // i.extern_all(&path_v[..]).join("::"), - // exports.keys().map(|t| i.r(*t)).join(", ") - // ); - Rc::new(Module { + Module { imports, items, extra: ProjectExt { @@ -169,14 +149,14 @@ fn source_to_module( rules, file: Some(path_v[..filepath_len].to_vec()), }, - }) + } } fn files_to_module( path: Substack>, files: Vec, i: &Interner, -) -> Rc> { +) -> Module, ProjectExt> { let lvl = path.len(); debug_assert!( files.iter().map(|f| f.path.len()).max().unwrap() >= lvl, @@ -186,7 +166,7 @@ fn files_to_module( if files.len() == 1 && files[0].path.len() == lvl { return source_to_module( path, - files[0].loaded.preparsed.0.as_ref(), + &files[0].loaded.preparsed.0, files[0].parsed.clone(), i, path.len(), @@ -204,12 +184,9 @@ fn files_to_module( (namespace, ModEntry { exported: true, member }) }) .collect::>(); - let exports: HashMap<_, _> = items - .keys() - .copied() - .map(|name| (name, i.i(&pushed(&path_v, name)))) - .collect(); - Rc::new(Module { + let exports: HashMap<_, _> = + items.keys().copied().map(|name| (name, pushed(&path_v, name))).collect(); + Module { items, imports: vec![], extra: ProjectExt { @@ -218,7 +195,7 @@ fn files_to_module( rules: vec![], file: None, }, - }) + } } pub fn build_tree( @@ -226,17 +203,13 @@ pub fn build_tree( i: &Interner, prelude: &[FileEntry], injected: &impl InjectedOperatorsFn, -) -> Result> { +) -> Result, Rc> { assert!(!files.is_empty(), "A tree requires at least one module"); let ops_cache = collect_ops::mk_cache(&files, i, injected); let mut entries = files .iter() .map(|(path, loaded)| { - Ok(( - i.r(*path), - loaded, - parse_file(*path, &files, &ops_cache, i, prelude)?, - )) + Ok((path, loaded, parse_file(path, &files, &ops_cache, i, prelude)?)) }) .collect::, Rc>>()?; // sort by similarity, then longest-first diff --git a/src/pipeline/project_tree/collect_ops/exported_ops.rs b/src/pipeline/project_tree/collect_ops/exported_ops.rs index 827dcb7..35122f2 100644 --- a/src/pipeline/project_tree/collect_ops/exported_ops.rs +++ b/src/pipeline/project_tree/collect_ops/exported_ops.rs @@ -3,11 +3,12 @@ use std::rc::Rc; use hashbrown::HashSet; use trait_set::trait_set; -use crate::interner::{Interner, Sym, Tok}; +use crate::interner::{Interner, Tok}; use crate::pipeline::error::{ModuleNotFound, ProjectError}; use crate::pipeline::source_loader::LoadedSourceTable; use crate::representations::tree::WalkErrorKind; use crate::utils::{split_max_prefix, unwrap_or, Cache}; +use crate::Sym; pub type OpsResult = Result>>, Rc>; pub type ExportedOpsCache<'a> = Cache<'a, Sym, OpsResult>; @@ -31,16 +32,13 @@ pub fn collect_exported_ops( injected: &impl InjectedOperatorsFn, ) -> OpsResult { let injected = injected(path).unwrap_or_else(|| Rc::new(HashSet::new())); - let is_file = |n: &[Tok]| loaded.contains_key(&i.i(n)); let path_s = &i.r(path)[..]; - let name_split = split_max_prefix(path_s, &is_file); - let (fpath_v, subpath_v) = unwrap_or!(name_split; return Ok(Rc::new( + let name_split = split_max_prefix(path_s, &|n| loaded.contains_key(n)); + let (fpath, subpath) = unwrap_or!(name_split; return Ok(Rc::new( (loaded.keys()) - .copied() .filter_map(|modname| { - let modname_s = i.r(modname); - if path_s.len() == coprefix(path_s.iter(), modname_s.iter()) { - Some(modname_s[path_s.len()]) + if path_s.len() == coprefix(path_s.iter(), modname.iter()) { + Some(modname[path_s.len()]) } else { None } @@ -48,24 +46,24 @@ pub fn collect_exported_ops( .chain(injected.iter().copied()) .collect::>(), ))); - let fpath = i.i(fpath_v); - let preparsed = &loaded[&fpath].preparsed; - let module = preparsed.0.walk(subpath_v, false).map_err(|walk_err| { - match walk_err.kind { - WalkErrorKind::Private => { - unreachable!("visibility is not being checked here") + let preparsed = &loaded[fpath].preparsed; + let module = + preparsed.0.walk_ref(subpath, false).map_err( + |walk_err| match walk_err.kind { + WalkErrorKind::Private => { + unreachable!("visibility is not being checked here") + }, + WalkErrorKind::Missing => ModuleNotFound { + file: i.extern_all(fpath), + subpath: (subpath.iter()) + .take(walk_err.pos) + .map(|t| i.r(*t)) + .cloned() + .collect(), + } + .rc(), }, - WalkErrorKind::Missing => ModuleNotFound { - file: i.extern_vec(fpath), - subpath: (subpath_v.iter()) - .take(walk_err.pos) - .map(|t| i.r(*t)) - .cloned() - .collect(), - } - .rc(), - } - })?; + )?; let out = (module.items.iter()) .filter(|(_, v)| v.exported) .map(|(k, _)| *k) diff --git a/src/pipeline/project_tree/collect_ops/ops_for.rs b/src/pipeline/project_tree/collect_ops/ops_for.rs index 8b97c8f..89ba9c1 100644 --- a/src/pipeline/project_tree/collect_ops/ops_for.rs +++ b/src/pipeline/project_tree/collect_ops/ops_for.rs @@ -19,7 +19,7 @@ fn tree_all_ops( ops.extend(module.items.keys().copied()); for ent in module.items.values() { if let ModMember::Sub(m) = &ent.member { - tree_all_ops(m.as_ref(), ops); + tree_all_ops(m, ops); } } } @@ -31,9 +31,9 @@ pub fn collect_ops_for( ops_cache: &ExportedOpsCache, i: &Interner, ) -> OpsResult { - let tree = &loaded[&i.i(file)].preparsed.0; + let tree = &loaded[file].preparsed.0; let mut ret = HashSet::new(); - tree_all_ops(tree.as_ref(), &mut ret); + tree_all_ops(tree, &mut ret); tree.visit_all_imports(&mut |modpath, _module, import| { if let Some(n) = import.name { ret.insert(n); diff --git a/src/pipeline/project_tree/const_tree.rs b/src/pipeline/project_tree/const_tree.rs index c25cead..49cdf94 100644 --- a/src/pipeline/project_tree/const_tree.rs +++ b/src/pipeline/project_tree/const_tree.rs @@ -1,15 +1,14 @@ use std::ops::Add; -use std::rc::Rc; use hashbrown::HashMap; use super::{ProjectExt, ProjectModule, ProjectTree}; use crate::ast::{Clause, Expr}; use crate::foreign::{Atom, Atomic, ExternFn}; -use crate::interner::{Interner, Tok}; +use crate::interner::Tok; use crate::representations::location::Location; use crate::representations::tree::{ModEntry, ModMember, Module}; -use crate::representations::Primitive; +use crate::representations::{Primitive, VName}; use crate::utils::{pushed, Substack}; /// A lightweight module tree that can be built declaratively by hand to @@ -17,7 +16,7 @@ use crate::utils::{pushed, Substack}; /// added convenience pub enum ConstTree { /// A function or constant - Const(Expr), + Const(Expr), /// A submodule Tree(HashMap, ConstTree>), } @@ -67,8 +66,7 @@ fn from_const_tree_rec( path: Substack>, consts: HashMap, ConstTree>, file: &[Tok], - i: &Interner, -) -> ProjectModule { +) -> ProjectModule { let mut items = HashMap::new(); let path_v = path.iter().rev_vec_clone(); for (name, item) in consts { @@ -76,17 +74,13 @@ fn from_const_tree_rec( exported: true, member: match item { ConstTree::Const(c) => ModMember::Item(c), - ConstTree::Tree(t) => ModMember::Sub(Rc::new(from_const_tree_rec( - path.push(name), - t, - file, - i, - ))), + ConstTree::Tree(t) => + ModMember::Sub(from_const_tree_rec(path.push(name), t, file)), }, }); } let exports = - items.keys().map(|name| (*name, i.i(&pushed(&path_v, *name)))).collect(); + items.keys().map(|name| (*name, pushed(&path_v, *name))).collect(); Module { items, imports: vec![], @@ -103,8 +97,7 @@ fn from_const_tree_rec( pub fn from_const_tree( consts: HashMap, ConstTree>, file: &[Tok], - i: &Interner, -) -> ProjectTree { - let module = from_const_tree_rec(Substack::Bottom, consts, file, i); - ProjectTree(Rc::new(module)) +) -> ProjectTree { + let module = from_const_tree_rec(Substack::Bottom, consts, file); + ProjectTree(module) } diff --git a/src/pipeline/project_tree/mod.rs b/src/pipeline/project_tree/mod.rs index cc70436..7bc4459 100644 --- a/src/pipeline/project_tree/mod.rs +++ b/src/pipeline/project_tree/mod.rs @@ -26,5 +26,6 @@ pub use build_tree::{build_tree, split_path}; pub use collect_ops::InjectedOperatorsFn; pub use const_tree::{from_const_tree, ConstTree}; pub use tree::{ - collect_consts, collect_rules, ProjectExt, ProjectModule, ProjectTree, + collect_consts, collect_rules, vname_to_sym_tree, ProjectExt, ProjectModule, + ProjectTree, }; diff --git a/src/pipeline/project_tree/normalize_imports.rs b/src/pipeline/project_tree/normalize_imports.rs index e7465cf..7618215 100644 --- a/src/pipeline/project_tree/normalize_imports.rs +++ b/src/pipeline/project_tree/normalize_imports.rs @@ -6,7 +6,7 @@ use crate::representations::sourcefile::{ }; use crate::representations::tree::{ModMember, Module}; use crate::utils::iter::box_once; -use crate::utils::{BoxedIter, Substack}; +use crate::utils::{unwrap_or, BoxedIter, Substack}; fn member_rec( // level @@ -21,20 +21,12 @@ fn member_rec( ) -> Member { match member { Member::Namespace(Namespace { name, body }) => { - let prepmember = &preparsed.items[&name].member; - let subprep = if let ModMember::Sub(m) = prepmember { - m.clone() - } else { + let subprep = unwrap_or!( + &preparsed.items[&name].member => ModMember::Sub; unreachable!("This name must point to a namespace") - }; - let new_body = entv_rec( - mod_stack.push(name), - subprep.as_ref(), - body, - path, - ops_cache, - i, ); + let new_body = + entv_rec(mod_stack.push(name), subprep, body, path, ops_cache, i); Member::Namespace(Namespace { name, body: new_body }) }, any => any, diff --git a/src/pipeline/project_tree/parse_file.rs b/src/pipeline/project_tree/parse_file.rs index f483891..6916a77 100644 --- a/src/pipeline/project_tree/parse_file.rs +++ b/src/pipeline/project_tree/parse_file.rs @@ -4,40 +4,35 @@ use super::add_prelude::add_prelude; use super::collect_ops::{collect_ops_for, ExportedOpsCache}; use super::normalize_imports::normalize_imports; use super::prefix::prefix; -use crate::interner::{Interner, Sym}; +use crate::interner::{Interner, Tok}; use crate::parse; use crate::pipeline::error::ProjectError; use crate::pipeline::source_loader::LoadedSourceTable; use crate::representations::sourcefile::{normalize_namespaces, FileEntry}; pub fn parse_file( - path: Sym, + path: &[Tok], loaded: &LoadedSourceTable, ops_cache: &ExportedOpsCache, i: &Interner, prelude: &[FileEntry], ) -> Result, Rc> { - let ld = &loaded[&path]; + let ld = &loaded[path]; // let ops_cache = collect_ops::mk_cache(loaded, i); - let ops = collect_ops_for(&i.r(path)[..], loaded, ops_cache, i)?; + let ops = collect_ops_for(path, loaded, ops_cache, i)?; let ops_vec = ops.iter().map(|t| i.r(*t)).cloned().collect::>(); let ctx = parse::ParsingContext { interner: i, ops: &ops_vec, - file: Rc::new(i.extern_vec(path)), + file: Rc::new(i.extern_all(path)), }; let entries = parse::parse(ld.text.as_str(), ctx) .expect("This error should have been caught during loading"); - let with_prelude = add_prelude(entries, &i.r(path)[..], prelude); - let impnormalized = normalize_imports( - &ld.preparsed.0, - with_prelude, - &i.r(path)[..], - ops_cache, - i, - ); + let with_prelude = add_prelude(entries, path, prelude); + let impnormalized = + normalize_imports(&ld.preparsed.0, with_prelude, path, ops_cache, i); let nsnormalized = normalize_namespaces(Box::new(impnormalized.into_iter())) .expect("This error should have been caught during preparsing"); - let prefixed = prefix(nsnormalized, &i.r(path)[..], ops_cache, i); + let prefixed = prefix(nsnormalized, path, ops_cache, i); Ok(prefixed) } diff --git a/src/pipeline/project_tree/prefix.rs b/src/pipeline/project_tree/prefix.rs index 4192dfb..2c04d18 100644 --- a/src/pipeline/project_tree/prefix.rs +++ b/src/pipeline/project_tree/prefix.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - use super::collect_ops::ExportedOpsCache; use crate::ast::{Constant, Rule}; use crate::interner::{Interner, Tok}; @@ -16,14 +14,10 @@ fn member_rec( ops_cache: &ExportedOpsCache, i: &Interner, ) -> Member { - // let except = |op| imported.contains(&op); - let except = |_| false; - let prefix_v = path - .iter() + let prefix = (path.iter()) .copied() .chain(mod_stack.iter().rev_vec_clone().into_iter()) .collect::>(); - let prefix = i.i(&prefix_v); match data { Member::Namespace(Namespace { name, body }) => { let new_body = entv_rec(mod_stack.push(name), body, path, ops_cache, i); @@ -31,16 +25,16 @@ fn member_rec( }, Member::Constant(constant) => Member::Constant(Constant { name: constant.name, - value: constant.value.prefix(prefix, i, &except), + value: constant.value.prefix(&prefix, &|_| false), }), Member::Rule(rule) => Member::Rule(Rule { prio: rule.prio, - pattern: Rc::new( - rule.pattern.iter().map(|e| e.prefix(prefix, i, &except)).collect(), - ), - template: Rc::new( - rule.template.iter().map(|e| e.prefix(prefix, i, &except)).collect(), - ), + pattern: (rule.pattern.into_iter()) + .map(|e| e.prefix(&prefix, &|_| false)) + .collect(), + template: (rule.template.into_iter()) + .map(|e| e.prefix(&prefix, &|_| false)) + .collect(), }), } } diff --git a/src/pipeline/project_tree/tree.rs b/src/pipeline/project_tree/tree.rs index 98b2c3e..c2de388 100644 --- a/src/pipeline/project_tree/tree.rs +++ b/src/pipeline/project_tree/tree.rs @@ -1,28 +1,30 @@ use std::ops::Add; -use std::rc::Rc; use hashbrown::HashMap; use crate::ast::{Expr, Rule}; -use crate::interner::{Interner, Sym, Tok}; +use crate::interner::{Interner, Tok}; use crate::representations::tree::{ModMember, Module}; +use crate::representations::NameLike; +use crate::tree::ModEntry; use crate::utils::Substack; +use crate::{Sym, VName}; /// Additional data about a loaded module beyond the list of constants and /// submodules #[derive(Clone, Debug, Default)] -pub struct ProjectExt { +pub struct ProjectExt { /// Pairs each foreign token to the module it was imported from - pub imports_from: HashMap, Sym>, + pub imports_from: HashMap, N>, /// Pairs each exported token to its original full name - pub exports: HashMap, Sym>, + pub exports: HashMap, N>, /// All rules defined in this module, exported or not - pub rules: Vec, + pub rules: Vec>, /// Filename, if known, for error reporting pub file: Option>>, } -impl Add for ProjectExt { +impl Add for ProjectExt { type Output = Self; fn add(mut self, rhs: Self) -> Self::Output { @@ -38,33 +40,36 @@ impl Add for ProjectExt { } /// A node in the tree describing the project -pub type ProjectModule = Module; +pub type ProjectModule = Module, ProjectExt>; /// Module corresponding to the root of a project #[derive(Debug, Clone)] -pub struct ProjectTree(pub Rc); +pub struct ProjectTree(pub ProjectModule); -fn collect_rules_rec(bag: &mut Vec, module: &ProjectModule) { +fn collect_rules_rec( + bag: &mut Vec>, + module: &ProjectModule, +) { bag.extend(module.extra.rules.iter().cloned()); for item in module.items.values() { if let ModMember::Sub(module) = &item.member { - collect_rules_rec(bag, module.as_ref()); + collect_rules_rec(bag, module); } } } /// Collect the complete list of rules to be used by the rule repository from /// the [ProjectTree] -pub fn collect_rules(project: &ProjectTree) -> Vec { +pub fn collect_rules(project: &ProjectTree) -> Vec> { let mut rules = Vec::new(); - collect_rules_rec(&mut rules, project.0.as_ref()); + collect_rules_rec(&mut rules, &project.0); rules } -fn collect_consts_rec( +fn collect_consts_rec( path: Substack>, - bag: &mut HashMap, - module: &ProjectModule, + bag: &mut HashMap>, + module: &ProjectModule, i: &Interner, ) { for (key, entry) in module.items.iter() { @@ -81,11 +86,59 @@ fn collect_consts_rec( } /// Extract the symbol table from a [ProjectTree] -pub fn collect_consts( - project: &ProjectTree, +pub fn collect_consts( + project: &ProjectTree, i: &Interner, -) -> HashMap { +) -> HashMap> { let mut consts = HashMap::new(); - collect_consts_rec(Substack::Bottom, &mut consts, project.0.as_ref(), i); + collect_consts_rec(Substack::Bottom, &mut consts, &project.0, i); consts } + +fn vname_to_sym_tree_rec( + tree: ProjectModule, + i: &Interner, +) -> ProjectModule { + let process_expr = |ex: Expr| ex.transform_names(&|n| i.i(&n)); + ProjectModule { + imports: tree.imports, + items: (tree.items.into_iter()) + .map(|(k, ModEntry { exported, member })| { + (k, ModEntry { + exported, + member: match member { + ModMember::Sub(module) => + ModMember::Sub(vname_to_sym_tree_rec(module, i)), + ModMember::Item(ex) => ModMember::Item(process_expr(ex)), + }, + }) + }) + .collect(), + extra: ProjectExt { + imports_from: (tree.extra.imports_from.into_iter()) + .map(|(k, v)| (k, i.i(&v))) + .collect(), + exports: (tree.extra.exports.into_iter()) + .map(|(k, v)| (k, i.i(&v))) + .collect(), + rules: (tree.extra.rules.into_iter()) + .map(|Rule { pattern, prio, template }| Rule { + pattern: pattern.into_iter().map(process_expr).collect(), + prio, + template: template.into_iter().map(process_expr).collect(), + }) + .collect(), + file: tree.extra.file, + }, + } +} + +/// Convert a flexible vname-based tree to a more rigid but faster symbol-based +/// tree. The pipeline works with vnames, but the macro executor works with +/// symbols. +pub fn vname_to_sym_tree( + tree: ProjectTree, + i: &Interner, +) -> ProjectTree { + ProjectTree(vname_to_sym_tree_rec(tree.0, i)) +} diff --git a/src/pipeline/source_loader/load_source.rs b/src/pipeline/source_loader/load_source.rs index 115ee10..d1e4c24 100644 --- a/src/pipeline/source_loader/load_source.rs +++ b/src/pipeline/source_loader/load_source.rs @@ -3,22 +3,22 @@ use std::rc::Rc; use super::loaded_source::{LoadedSource, LoadedSourceTable}; use super::preparse::preparse; -use crate::interner::{Interner, Sym}; -use crate::pipeline::error::ProjectError; -use crate::pipeline::file_loader::{load_text, IOResult, Loaded}; +use crate::interner::{Interner, Tok}; +use crate::pipeline::error::{ProjectError, UnexpectedDirectory}; +use crate::pipeline::file_loader::{IOResult, Loaded}; use crate::pipeline::import_abs_path::import_abs_path; use crate::representations::sourcefile::FileEntry; -use crate::utils::split_max_prefix; +use crate::utils::{split_max_prefix, unwrap_or}; /// Load the source at the given path or all within if it's a collection, /// and all sources imported from these. fn load_abs_path_rec( - abs_path: Sym, + abs_path: &[Tok], table: &mut LoadedSourceTable, prelude: &[FileEntry], i: &Interner, - get_source: &impl Fn(Sym) -> IOResult, - is_injected_module: &impl Fn(Sym) -> bool, + get_source: &impl Fn(&[Tok]) -> IOResult, + is_injected_module: &impl Fn(&[Tok]) -> bool, ) -> Result<(), Rc> { // # Termination // @@ -29,23 +29,26 @@ fn load_abs_path_rec( // contains no cycles. // Termination: exit if entry already visited - if table.contains_key(&abs_path) { + if table.contains_key(abs_path) { return Ok(()); } // try splitting the path to file, swallowing any IO errors - let is_file = |sym| get_source(sym).map(|l| l.is_code()).unwrap_or(false); - let name_split = split_max_prefix(&i.r(abs_path)[..], &|p| is_file(i.i(p))); + let name_split = split_max_prefix(abs_path, &|p| { + get_source(p).map(|l| l.is_code()).unwrap_or(false) + }); if let Some((filename, _)) = name_split { // if the filename is valid, load, preparse and record this file - let text = load_text(i.i(filename), &get_source, i)?; + let text = unwrap_or!(get_source(filename)? => Loaded::Code; { + return Err(UnexpectedDirectory { path: i.extern_all(filename) }.rc()) + }); let preparsed = preparse( filename.iter().map(|t| i.r(*t)).cloned().collect(), text.as_str(), prelude, i, )?; - table.insert(i.i(filename), LoadedSource { + table.insert(filename.to_vec(), LoadedSource { text, preparsed: preparsed.clone(), }); @@ -55,7 +58,7 @@ fn load_abs_path_rec( import_abs_path(filename, modpath, &import.nonglob_path(i), i)?; // recurse on imported module load_abs_path_rec( - i.i(&abs_pathv), + &abs_pathv, table, prelude, i, @@ -70,20 +73,20 @@ fn load_abs_path_rec( Ok(Loaded::Code(_)) => unreachable!("split_name returned None but the path is a file"), Err(e) => { - let parent = i.r(abs_path).split_last().expect("import path nonzero").1; + let parent = abs_path.split_last().expect("import path nonzero").1; // exit without error if it was injected, or raise any IO error that was // previously swallowed - return if is_injected_module(i.i(parent)) { Ok(()) } else { Err(e) }; + return if is_injected_module(parent) { Ok(()) } else { Err(e) }; }, }; // recurse on all files and folders within for item in coll.iter() { - let abs_subpath = (i.r(abs_path).iter()) + let abs_subpath = (abs_path.iter()) .copied() .chain(iter::once(i.i(item))) .collect::>(); load_abs_path_rec( - i.i(&abs_subpath), + &abs_subpath, table, prelude, i, @@ -101,17 +104,17 @@ fn load_abs_path_rec( /// is_injected_module must return false for injected symbols, but may return /// true for parents of injected modules that are not directly part of the /// injected data (the ProjectTree doesn't make a distinction between the two) -pub fn load_source( - targets: &[Sym], +pub fn load_source<'a>( + targets: impl Iterator]>, prelude: &[FileEntry], i: &Interner, - get_source: &impl Fn(Sym) -> IOResult, - is_injected_module: &impl Fn(Sym) -> bool, + get_source: &impl Fn(&[Tok]) -> IOResult, + is_injected_module: &impl Fn(&[Tok]) -> bool, ) -> Result> { let mut table = LoadedSourceTable::new(); for target in targets { load_abs_path_rec( - *target, + target, &mut table, prelude, i, diff --git a/src/pipeline/source_loader/loaded_source.rs b/src/pipeline/source_loader/loaded_source.rs index ae9fd43..9b72764 100644 --- a/src/pipeline/source_loader/loaded_source.rs +++ b/src/pipeline/source_loader/loaded_source.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::rc::Rc; use super::preparse::Preparsed; -use crate::interner::Sym; +use crate::representations::VName; #[derive(Debug)] pub struct LoadedSource { @@ -10,4 +10,4 @@ pub struct LoadedSource { pub preparsed: Preparsed, } -pub type LoadedSourceTable = HashMap; +pub type LoadedSourceTable = HashMap; diff --git a/src/pipeline/source_loader/preparse.rs b/src/pipeline/source_loader/preparse.rs index a622483..e9e826f 100644 --- a/src/pipeline/source_loader/preparse.rs +++ b/src/pipeline/source_loader/preparse.rs @@ -15,7 +15,7 @@ use crate::representations::sourcefile::{ use crate::representations::tree::{ModEntry, ModMember, Module}; #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Preparsed(pub Rc>); +pub struct Preparsed(pub Module<(), ()>); /// Add an internal flat name if it does not exist yet fn add_intern(map: &mut HashMap>, k: K) { @@ -33,22 +33,18 @@ fn add_export(map: &mut HashMap>, k: K) { } /// Convert source lines into a module -fn to_module( - src: &[FileEntry], - prelude: &[FileEntry], - i: &Interner, -) -> Rc> { +fn to_module(src: &[FileEntry], prelude: &[FileEntry]) -> Module<(), ()> { let all_src = || src.iter().chain(prelude.iter()); let imports = imports(all_src()).cloned().collect::>(); let mut items = all_src() .filter_map(|ent| match ent { FileEntry::Internal(Member::Namespace(ns)) => { - let member = ModMember::Sub(to_module(&ns.body, prelude, i)); + let member = ModMember::Sub(to_module(&ns.body, prelude)); let entry = ModEntry { exported: false, member }; Some((ns.name, entry)) }, FileEntry::Exported(Member::Namespace(ns)) => { - let member = ModMember::Sub(to_module(&ns.body, prelude, i)); + let member = ModMember::Sub(to_module(&ns.body, prelude)); let entry = ModEntry { exported: true, member }; Some((ns.name, entry)) }, @@ -70,20 +66,20 @@ fn to_module( FileEntry::Exported(Member::Constant(Constant { name, .. })) => add_export(&mut items, *name), FileEntry::Internal(Member::Rule(rule)) => { - let names = rule.collect_single_names(i); + let names = rule.collect_single_names(); for name in names { add_intern(&mut items, name) } }, FileEntry::Exported(Member::Rule(rule)) => { - let names = rule.collect_single_names(i); + let names = rule.collect_single_names(); for name in names { add_export(&mut items, name) } }, } } - Rc::new(Module { imports, items, extra: () }) + Module { imports, items, extra: () } } /// Preparse the module. At this stage, only the imports and @@ -112,5 +108,5 @@ pub fn preparse( } .rc() })?; - Ok(Preparsed(to_module(&normalized, prelude, i))) + Ok(Preparsed(to_module(&normalized, prelude))) } diff --git a/src/representations/ast.rs b/src/representations/ast.rs index 451bc58..e5e1507 100644 --- a/src/representations/ast.rs +++ b/src/representations/ast.rs @@ -1,9 +1,7 @@ -//! Datastructures representing syntax as written +//! Datastructures representing the units of macro execution //! -//! These structures are produced by the parser, processed by the macro -//! executor, and then converted to other usable formats. This module is public -//! in order to allow users to define injected libraries programmatically, -//! although going through the parser is usually preferable. +//! These structures are produced by the pipeline, processed by the macro +//! executor, and then converted to other usable formats. use std::hash::Hash; use std::rc::Rc; @@ -12,55 +10,62 @@ use itertools::Itertools; use ordered_float::NotNan; use super::location::Location; +use super::namelike::{NameLike, VName}; use super::primitive::Primitive; -use crate::interner::{InternedDisplay, Interner, Sym, Tok}; -use crate::utils::Substack; +use crate::interner::{InternedDisplay, Interner, Tok}; +use crate::utils::{map_rc, Substack}; /// A [Clause] with associated metadata #[derive(Clone, Debug, PartialEq)] -pub struct Expr { +pub struct Expr { /// The actual value - pub value: Clause, + pub value: Clause, /// Information about the code that produced this value pub location: Location, } -impl Expr { +impl Expr { /// Obtain the contained clause - pub fn into_clause(self) -> Clause { + pub fn into_clause(self) -> Clause { self.value } /// Call the function on every name in this expression - pub fn visit_names(&self, binds: Substack, cb: &mut impl FnMut(Sym)) { + pub fn visit_names(&self, binds: Substack<&N>, cb: &mut impl FnMut(&N)) { let Expr { value, .. } = self; value.visit_names(binds, cb); } /// Process all names with the given mapper. /// Return a new object if anything was processed - pub fn map_names(&self, pred: &impl Fn(Sym) -> Option) -> Option { + pub fn map_names(&self, pred: &impl Fn(&N) -> Option) -> Option { Some(Self { value: self.value.map_names(pred)?, location: self.location.clone(), }) } + /// Transform from one name system to another + pub fn transform_names(self, pred: &impl Fn(N) -> O) -> Expr { + Expr { value: self.value.transform_names(pred), location: self.location } + } +} + +impl Expr { /// Add the specified prefix to every Name pub fn prefix( &self, - prefix: Sym, - i: &Interner, + prefix: &[Tok], except: &impl Fn(Tok) -> bool, ) -> Self { Self { - value: self.value.prefix(prefix, i, except), + value: self.value.prefix(prefix, except), location: self.location.clone(), } } } -impl InternedDisplay for Expr { +impl InternedDisplay for Expr { fn fmt_i( &self, f: &mut std::fmt::Formatter<'_>, @@ -116,23 +121,23 @@ impl InternedDisplay for Placeholder { /// An S-expression as read from a source file #[derive(Debug, PartialEq, Clone)] -pub enum Clause { +pub enum Clause { /// A primitive P(Primitive), /// A c-style name or an operator, eg. `+`, `i`, `foo::bar` - Name(Sym), - /// A parenthesized exmrc_empty_slice()pression + Name(N), + /// A parenthesized expression /// eg. `(print out "hello")`, `[1, 2, 3]`, `{Some(t) => t}` - S(char, Rc>), + S(char, Rc>>), /// A function expression, eg. `\x. x + 1` - Lambda(Rc, Rc>), + Lambda(Rc>, Rc>>), /// A placeholder for macros, eg. `$name`, `...$body`, `...$lhs:1` Placeh(Placeholder), } -impl Clause { +impl Clause { /// Extract the expressions from an auto, lambda or S - pub fn body(&self) -> Option>> { + pub fn body(&self) -> Option>>> { match self { Self::Lambda(_, body) | Self::S(_, body) => Some(body.clone()), _ => None, @@ -140,7 +145,7 @@ impl Clause { } /// Convert with identical meaning - pub fn into_expr(self) -> Expr { + pub fn into_expr(self) -> Expr { if let Self::S('(', body) = &self { if body.len() == 1 { body[0].clone() @@ -153,7 +158,7 @@ impl Clause { } /// Convert with identical meaning - pub fn from_exprs(exprs: &[Expr]) -> Option { + pub fn from_exprs(exprs: &[Expr]) -> Option { if exprs.is_empty() { None } else if exprs.len() == 1 { @@ -163,7 +168,7 @@ impl Clause { } } /// Convert with identical meaning - pub fn from_exprv(exprv: &Rc>) -> Option { + pub fn from_exprv(exprv: &Rc>>) -> Option> { if exprv.len() < 2 { Self::from_exprs(exprv) } else { @@ -175,12 +180,12 @@ impl Clause { /// It also finds a lot of things that aren't names, such as all /// bound parameters. Generally speaking, this is not a very /// sophisticated search. - pub fn visit_names(&self, binds: Substack, cb: &mut impl FnMut(Sym)) { + pub fn visit_names(&self, binds: Substack<&N>, cb: &mut impl FnMut(&N)) { match self { Clause::Lambda(arg, body) => { arg.visit_names(binds, cb); let new_binds = - if let Clause::Name(n) = arg.value { binds.push(n) } else { binds }; + if let Clause::Name(n) = &arg.value { binds.push(n) } else { binds }; for x in body.iter() { x.visit_names(new_binds, cb) } @@ -190,8 +195,8 @@ impl Clause { x.visit_names(binds, cb) }, Clause::Name(name) => - if binds.iter().all(|x| x != name) { - cb(*name) + if binds.iter().all(|x| x != &name) { + cb(name) }, _ => (), } @@ -199,10 +204,10 @@ impl Clause { /// Process all names with the given mapper. /// Return a new object if anything was processed - pub fn map_names(&self, pred: &impl Fn(Sym) -> Option) -> Option { + pub fn map_names(&self, pred: &impl Fn(&N) -> Option) -> Option { match self { Clause::P(_) | Clause::Placeh(_) => None, - Clause::Name(name) => pred(*name).map(Clause::Name), + Clause::Name(name) => pred(name).map(Clause::Name), Clause::S(c, body) => { let mut any_some = false; let new_body = body @@ -238,29 +243,49 @@ impl Clause { } } + /// Transform from one name representation to another + pub fn transform_names( + self, + pred: &impl Fn(N) -> O, + ) -> Clause { + match self { + Self::Name(n) => Clause::Name(pred(n)), + Self::Placeh(p) => Clause::Placeh(p), + Self::P(p) => Clause::P(p), + Self::Lambda(n, b) => Clause::Lambda( + map_rc(n, |n| n.transform_names(pred)), + map_rc(b, |b| b.into_iter().map(|e| e.transform_names(pred)).collect()), + ), + Self::S(c, b) => Clause::S( + c, + map_rc(b, |b| b.into_iter().map(|e| e.transform_names(pred)).collect()), + ), + } + } +} + +impl Clause { /// Add the specified prefix to every Name pub fn prefix( &self, - prefix: Sym, - i: &Interner, + prefix: &[Tok], except: &impl Fn(Tok) -> bool, ) -> Self { self .map_names(&|name| { - let old = i.r(name); - if except(old[0]) { + if except(name[0]) { return None; } - let mut new = i.r(prefix).clone(); - new.extend_from_slice(old); - Some(i.i(&new)) + let mut new = prefix.to_vec(); + new.extend_from_slice(name); + Some(new) }) .unwrap_or_else(|| self.clone()) } } -fn fmt_expr_seq<'a>( - it: &mut impl Iterator, +fn fmt_expr_seq<'a, N: NameLike>( + it: &mut impl Iterator>, f: &mut std::fmt::Formatter<'_>, i: &Interner, ) -> std::fmt::Result { @@ -273,19 +298,7 @@ fn fmt_expr_seq<'a>( Ok(()) } -pub(crate) fn fmt_name( - name: Sym, - f: &mut std::fmt::Formatter, - i: &Interner, -) -> std::fmt::Result { - let strings = i.r(name).iter().map(|t| i.r(*t).as_str()); - for el in itertools::intersperse(strings, "::") { - write!(f, "{}", el)? - } - Ok(()) -} - -impl InternedDisplay for Clause { +impl InternedDisplay for Clause { fn fmt_i( &self, f: &mut std::fmt::Formatter<'_>, @@ -293,7 +306,7 @@ impl InternedDisplay for Clause { ) -> std::fmt::Result { match self { Self::P(p) => write!(f, "{:?}", p), - Self::Name(name) => fmt_name(*name, f, i), + Self::Name(name) => write!(f, "{}", name.to_strv(i).join("::")), Self::S(del, items) => { f.write_str(&del.to_string())?; fmt_expr_seq(&mut items.iter(), f, i)?; @@ -317,54 +330,47 @@ impl InternedDisplay for Clause { /// A substitution rule as read from the source #[derive(Debug, Clone, PartialEq)] -pub struct Rule { +pub struct Rule { /// Tree fragment in the source code that activates this rule - pub pattern: Rc>, + pub pattern: Vec>, /// Influences the order in which rules are checked pub prio: NotNan, /// Tree fragment generated by this rule - pub template: Rc>, + pub template: Vec>, } -impl Rule { - /// Return a list of all names that don't contain a namespace separator `::`. - /// These are exported when the rule is exported - pub fn collect_single_names(&self, i: &Interner) -> Vec> { - let mut names = Vec::new(); - for e in self.pattern.iter() { - e.visit_names(Substack::Bottom, &mut |tok| { - let ns_name = i.r(tok); - let (name, excess) = - ns_name.split_first().expect("Namespaced name must not be empty"); - if !excess.is_empty() { - return; - } - names.push(*name) - }); - } - names - } - +impl Rule { /// Namespace all tokens in the rule pub fn prefix( &self, - prefix: Sym, - i: &Interner, + prefix: &[Tok], except: &impl Fn(Tok) -> bool, ) -> Self { Self { prio: self.prio, - pattern: Rc::new( - self.pattern.iter().map(|e| e.prefix(prefix, i, except)).collect(), - ), - template: Rc::new( - self.template.iter().map(|e| e.prefix(prefix, i, except)).collect(), - ), + pattern: self.pattern.iter().map(|e| e.prefix(prefix, except)).collect(), + template: (self.template.iter()) + .map(|e| e.prefix(prefix, except)) + .collect(), } } + + /// Return a list of all names that don't contain a namespace separator `::`. + /// These are exported when the rule is exported + pub fn collect_single_names(&self) -> Vec> { + let mut names = Vec::new(); + for e in self.pattern.iter() { + e.visit_names(Substack::Bottom, &mut |ns_name| { + if ns_name.len() == 1 { + names.push(ns_name[0]) + } + }); + } + names + } } -impl InternedDisplay for Rule { +impl InternedDisplay for Rule { fn fmt_i( &self, f: &mut std::fmt::Formatter<'_>, @@ -389,7 +395,7 @@ pub struct Constant { /// Used to reference the constant pub name: Tok, /// The constant value inserted where the name is found - pub value: Expr, + pub value: Expr, } impl InternedDisplay for Constant { diff --git a/src/representations/ast_to_interpreted.rs b/src/representations/ast_to_interpreted.rs index 6e208a8..49fbc06 100644 --- a/src/representations/ast_to_interpreted.rs +++ b/src/representations/ast_to_interpreted.rs @@ -1,4 +1,5 @@ use super::{ast, ast_to_postmacro, interpreted, postmacro_to_interpreted}; +use crate::Sym; #[allow(unused)] pub type AstError = ast_to_postmacro::Error; @@ -6,7 +7,7 @@ pub type AstError = ast_to_postmacro::Error; /// Attempt to convert the AST processed by macros into an executable format #[allow(unused)] pub fn ast_to_interpreted( - ast: &ast::Expr, + ast: &ast::Expr, ) -> Result { let pmtree = ast_to_postmacro::expr(ast)?; Ok(postmacro_to_interpreted::expr(&pmtree)) diff --git a/src/representations/ast_to_postmacro.rs b/src/representations/ast_to_postmacro.rs index 152d484..d47b97a 100644 --- a/src/representations/ast_to_postmacro.rs +++ b/src/representations/ast_to_postmacro.rs @@ -3,8 +3,8 @@ use std::rc::Rc; use super::location::Location; use super::{ast, postmacro}; -use crate::interner::Sym; use crate::utils::Substack; +use crate::Sym; #[derive(Clone)] pub enum Error { @@ -42,7 +42,7 @@ impl Display for Error { } /// Try to convert an expression from AST format to typed lambda -pub fn expr(expr: &ast::Expr) -> Result { +pub fn expr(expr: &ast::Expr) -> Result { expr_rec(expr, Context::new()) } @@ -66,7 +66,7 @@ impl<'a> Context<'a> { /// Process an expression sequence fn exprv_rec<'a>( - v: &'a [ast::Expr], + v: &'a [ast::Expr], ctx: Context<'a>, ) -> Result { let (last, rest) = v.split_last().ok_or(Error::EmptyS)?; @@ -81,7 +81,7 @@ fn exprv_rec<'a>( /// Process an expression fn expr_rec<'a>( - ast::Expr { value, location }: &'a ast::Expr, + ast::Expr { value, location }: &'a ast::Expr, ctx: Context<'a>, ) -> Result { if let ast::Clause::S(paren, body) = value { @@ -98,7 +98,7 @@ fn expr_rec<'a>( /// Process a clause fn clause_rec<'a>( - cls: &'a ast::Clause, + cls: &'a ast::Clause, ctx: Context<'a>, ) -> Result { match cls { diff --git a/src/representations/interpreted.rs b/src/representations/interpreted.rs index b75db9b..3a0a220 100644 --- a/src/representations/interpreted.rs +++ b/src/representations/interpreted.rs @@ -11,8 +11,9 @@ use super::location::Location; use super::path_set::PathSet; use super::primitive::Primitive; use super::Literal; -use crate::interner::{InternedDisplay, Sym}; +use crate::interner::InternedDisplay; use crate::utils::sym2string; +use crate::Sym; // TODO: implement Debug, Eq and Hash with cycle detection diff --git a/src/representations/mod.rs b/src/representations/mod.rs index f17e123..f4c7eb8 100644 --- a/src/representations/mod.rs +++ b/src/representations/mod.rs @@ -4,6 +4,7 @@ pub mod ast_to_postmacro; pub mod interpreted; pub mod literal; pub mod location; +mod namelike; pub mod path_set; pub mod postmacro; pub mod postmacro_to_interpreted; @@ -13,5 +14,6 @@ pub mod tree; pub use literal::Literal; pub use location::Location; +pub use namelike::{NameLike, Sym, VName}; pub use path_set::PathSet; pub use primitive::Primitive; diff --git a/src/representations/namelike.rs b/src/representations/namelike.rs new file mode 100644 index 0000000..c3aec57 --- /dev/null +++ b/src/representations/namelike.rs @@ -0,0 +1,34 @@ +use crate::interner::{Interner, Tok}; + +/// A mutable representation of a namespaced identifier. +/// +/// These names may be relative or otherwise partially processed. +/// +/// See also [Sym] +pub type VName = Vec>; + +/// An interned representation of a namespaced identifier. +/// +/// These names are always absolute. +/// +/// See also [VName] +pub type Sym = Tok>>; + +/// An abstraction over tokenized vs non-tokenized names so that they can be +/// handled together in datastructures +pub trait NameLike: 'static + Clone + Eq { + /// Fully resolve the name for printing + fn to_strv(&self, i: &Interner) -> Vec; +} + +impl NameLike for Sym { + fn to_strv(&self, i: &Interner) -> Vec { + i.extern_vec(*self) + } +} + +impl NameLike for VName { + fn to_strv(&self, i: &Interner) -> Vec { + i.extern_all(self) + } +} diff --git a/src/representations/path_set.rs b/src/representations/path_set.rs index ea5a278..5edfcb5 100644 --- a/src/representations/path_set.rs +++ b/src/representations/path_set.rs @@ -2,7 +2,7 @@ use std::fmt::Debug; use std::ops::Add; use std::rc::Rc; -use crate::utils::Side; +use crate::utils::{rc_to_owned, Side}; /// A branching path selecting some placeholders (but at least one) in a Lambda /// expression @@ -41,8 +41,7 @@ impl Add for PathSet { type Output = Self; fn add(self, rhs: Side) -> Self::Output { let PathSet { steps, next } = self; - let mut new_steps = - Rc::try_unwrap(steps).unwrap_or_else(|rc| rc.as_ref().clone()); + let mut new_steps = rc_to_owned(steps); new_steps.insert(0, rhs); Self { steps: Rc::new(new_steps), next } } diff --git a/src/representations/postmacro.rs b/src/representations/postmacro.rs index 5329360..498aa2d 100644 --- a/src/representations/postmacro.rs +++ b/src/representations/postmacro.rs @@ -3,8 +3,8 @@ use std::rc::Rc; use super::location::Location; use super::primitive::Primitive; -use crate::interner::Sym; use crate::utils::string_from_charset; +use crate::Sym; /// Indicates whether either side needs to be wrapped. Syntax whose end is /// ambiguous on that side must use parentheses, or forward the flag diff --git a/src/representations/sourcefile.rs b/src/representations/sourcefile.rs index 451bb55..0d82a50 100644 --- a/src/representations/sourcefile.rs +++ b/src/representations/sourcefile.rs @@ -3,9 +3,11 @@ use std::iter; use itertools::{Either, Itertools}; +use super::namelike::VName; use crate::ast::{Constant, Rule}; -use crate::interner::{Interner, Sym, Tok}; +use crate::interner::{Interner, Tok}; use crate::utils::{unwrap_or, BoxedIter}; +use crate::Sym; /// An import pointing at another module, either specifying the symbol to be /// imported or importing all available symbols with a globstar (*) @@ -50,7 +52,7 @@ pub struct Namespace { pub enum Member { /// A substitution rule. Rules apply even when they're not in scope, if the /// absolute names are present eg. because they're produced by other rules - Rule(Rule), + Rule(Rule), /// A constant (or function) associated with a name Constant(Constant), /// A prefixed set of other entries diff --git a/src/representations/tree.rs b/src/representations/tree.rs index b59665d..860a9d4 100644 --- a/src/representations/tree.rs +++ b/src/representations/tree.rs @@ -2,8 +2,8 @@ //! //! Used by various stages of the pipeline with different parameters use std::ops::Add; -use std::rc::Rc; +use duplicate::duplicate_item; use hashbrown::HashMap; use super::sourcefile::Import; @@ -16,7 +16,7 @@ pub enum ModMember { /// Arbitrary data Item(TItem), /// A child module - Sub(Rc>), + Sub(Module), } /// Data about a name in a [Module] @@ -61,18 +61,25 @@ pub struct WalkError { pub type ModPath<'a> = Substack<'a, Tok>; impl Module { - /// Return the module at the end of the given path. - pub fn walk( - self: &Rc, + /// Return the module at the end of the given path + #[allow(clippy::needless_arbitrary_self_type)] // duplicate + #[duplicate_item( + method reference(type) dereference(expr) map_method; + [walk] [type] [expr] [remove]; + [walk_ref] [&type] [*expr] [get]; + [walk_mut] [&mut type] [*expr] [get_mut]; + )] + pub fn method( + self: reference([Self]), path: &[Tok], require_exported: bool, - ) -> Result, WalkError> { + ) -> Result { let mut cur = self; for (pos, step) in path.iter().enumerate() { if let Some(ModEntry { member: ModMember::Sub(next), exported }) = - cur.items.get(step) + cur.items.map_method(step) { - if require_exported && !exported { + if require_exported && !dereference([exported]) { return Err(WalkError { pos, kind: WalkErrorKind::Private }); } cur = next @@ -80,7 +87,7 @@ impl Module { return Err(WalkError { pos, kind: WalkErrorKind::Missing }); } } - Ok(cur.clone()) + Ok(cur) } fn visit_all_imports_rec( @@ -118,21 +125,16 @@ impl Module { let mut new_items = HashMap::new(); for (key, right) in items { // if both contain a submodule - if let Some(left) = self.items.remove(&key) { - if let ModMember::Sub(rsub) = &right.member { - if let ModMember::Sub(lsub) = &left.member { - // merge them with rhs exportedness - let new_mod = lsub.as_ref().clone().overlay(rsub.as_ref().clone()); - new_items.insert(key, ModEntry { - exported: right.exported, - member: ModMember::Sub(Rc::new(new_mod)), - }); - continue; - } - } - } - // otherwise right shadows left - new_items.insert(key, right); + match (self.items.remove(&key), right) { + ( + Some(ModEntry { member: ModMember::Sub(lsub), .. }), + ModEntry { member: ModMember::Sub(rsub), exported }, + ) => new_items.insert(key, ModEntry { + exported, + member: ModMember::Sub(lsub.overlay(rsub)), + }), + (_, right) => new_items.insert(key, right), + }; } new_items.extend(self.items.into_iter()); self.imports.extend(imports.into_iter()); diff --git a/src/rule/matcher.rs b/src/rule/matcher.rs index ec45a22..c57ce3d 100644 --- a/src/rule/matcher.rs +++ b/src/rule/matcher.rs @@ -2,12 +2,15 @@ use std::rc::Rc; use super::state::State; use crate::ast::Expr; +use crate::Sym; + +pub type RuleExpr = Expr; /// Cacheable optimized structures for matching patterns on slices. This is /// injected to allow experimentation in the matcher implementation. pub trait Matcher { /// Build matcher for a pattern - fn new(pattern: Rc>) -> Self; + fn new(pattern: Rc>) -> Self; /// Apply matcher to a token sequence - fn apply<'a>(&self, source: &'a [Expr]) -> Option>; + fn apply<'a>(&self, source: &'a [RuleExpr]) -> Option>; } diff --git a/src/rule/matcher_vectree/any_match.rs b/src/rule/matcher_vectree/any_match.rs index 812469d..cfb86b2 100644 --- a/src/rule/matcher_vectree/any_match.rs +++ b/src/rule/matcher_vectree/any_match.rs @@ -1,12 +1,12 @@ use super::scal_match::scalv_match; use super::shared::AnyMatcher; use super::vec_match::vec_match; -use crate::ast::Expr; +use crate::rule::matcher::RuleExpr; use crate::rule::state::State; pub fn any_match<'a>( matcher: &AnyMatcher, - seq: &'a [Expr], + seq: &'a [RuleExpr], ) -> Option> { match matcher { AnyMatcher::Scalar(scalv) => scalv_match(scalv, seq), diff --git a/src/rule/matcher_vectree/build.rs b/src/rule/matcher_vectree/build.rs index 39b8adf..a19ba92 100644 --- a/src/rule/matcher_vectree/build.rs +++ b/src/rule/matcher_vectree/build.rs @@ -1,16 +1,18 @@ use itertools::Itertools; use super::shared::{AnyMatcher, ScalMatcher, VecMatcher}; -use crate::ast::{Clause, Expr, PHClass, Placeholder}; +use crate::ast::{Clause, PHClass, Placeholder}; use crate::interner::Tok; +use crate::rule::matcher::RuleExpr; use crate::rule::vec_attrs::vec_attrs; use crate::utils::Side; -pub type MaxVecSplit<'a> = (&'a [Expr], (Tok, u64, bool), &'a [Expr]); +pub type MaxVecSplit<'a> = + (&'a [RuleExpr], (Tok, u64, bool), &'a [RuleExpr]); /// Derive the details of the central vectorial and the two sides from a /// slice of Expr's -fn split_at_max_vec(pattern: &[Expr]) -> Option { +fn split_at_max_vec(pattern: &[RuleExpr]) -> Option { let rngidx = pattern.iter().position_max_by_key(|expr| { vec_attrs(expr).map(|attrs| attrs.1 as i64).unwrap_or(-1) })?; @@ -21,11 +23,11 @@ fn split_at_max_vec(pattern: &[Expr]) -> Option { vec_attrs(placeh).map(|attrs| (left, attrs, right)) } -fn scal_cnt<'a>(iter: impl Iterator) -> usize { +fn scal_cnt<'a>(iter: impl Iterator) -> usize { iter.take_while(|expr| vec_attrs(expr).is_none()).count() } -pub fn mk_any(pattern: &[Expr]) -> AnyMatcher { +pub fn mk_any(pattern: &[RuleExpr]) -> AnyMatcher { let left_split = scal_cnt(pattern.iter()); if pattern.len() <= left_split { return AnyMatcher::Scalar(mk_scalv(pattern)); @@ -41,12 +43,12 @@ pub fn mk_any(pattern: &[Expr]) -> AnyMatcher { } /// Pattern MUST NOT contain vectorial placeholders -fn mk_scalv(pattern: &[Expr]) -> Vec { +fn mk_scalv(pattern: &[RuleExpr]) -> Vec { pattern.iter().map(mk_scalar).collect() } /// Pattern MUST start and end with a vectorial placeholder -fn mk_vec(pattern: &[Expr]) -> VecMatcher { +fn mk_vec(pattern: &[RuleExpr]) -> VecMatcher { debug_assert!(!pattern.is_empty(), "pattern cannot be empty"); debug_assert!( pattern.first().map(vec_attrs).is_some(), @@ -97,7 +99,7 @@ fn mk_vec(pattern: &[Expr]) -> VecMatcher { } /// Pattern MUST NOT be a vectorial placeholder -fn mk_scalar(pattern: &Expr) -> ScalMatcher { +fn mk_scalar(pattern: &RuleExpr) -> ScalMatcher { match &pattern.value { Clause::P(p) => ScalMatcher::P(p.clone()), Clause::Name(n) => ScalMatcher::Name(*n), diff --git a/src/rule/matcher_vectree/scal_match.rs b/src/rule/matcher_vectree/scal_match.rs index 8e19539..2b6c499 100644 --- a/src/rule/matcher_vectree/scal_match.rs +++ b/src/rule/matcher_vectree/scal_match.rs @@ -1,11 +1,12 @@ use super::any_match::any_match; use super::shared::ScalMatcher; -use crate::ast::{Clause, Expr}; +use crate::ast::Clause; +use crate::rule::matcher::RuleExpr; use crate::rule::state::{State, StateEntry}; pub fn scal_match<'a>( matcher: &ScalMatcher, - expr: &'a Expr, + expr: &'a RuleExpr, ) -> Option> { match (matcher, &expr.value) { (ScalMatcher::P(p1), Clause::P(p2)) if p1 == p2 => Some(State::new()), @@ -25,7 +26,7 @@ pub fn scal_match<'a>( pub fn scalv_match<'a>( matchers: &[ScalMatcher], - seq: &'a [Expr], + seq: &'a [RuleExpr], ) -> Option> { if seq.len() != matchers.len() { return None; diff --git a/src/rule/matcher_vectree/shared.rs b/src/rule/matcher_vectree/shared.rs index ac1d339..bc67a4d 100644 --- a/src/rule/matcher_vectree/shared.rs +++ b/src/rule/matcher_vectree/shared.rs @@ -3,12 +3,12 @@ use std::rc::Rc; use super::any_match::any_match; use super::build::mk_any; -use crate::ast::Expr; -use crate::interner::{InternedDisplay, Interner, Sym, Tok}; +use crate::interner::{InternedDisplay, Interner, Tok}; use crate::representations::Primitive; -use crate::rule::matcher::Matcher; +use crate::rule::matcher::{Matcher, RuleExpr}; use crate::rule::state::State; use crate::utils::{sym2string, unwrap_or, Side}; +use crate::Sym; pub enum ScalMatcher { P(Primitive), @@ -54,11 +54,11 @@ pub enum AnyMatcher { Vec { left: Vec, mid: VecMatcher, right: Vec }, } impl Matcher for AnyMatcher { - fn new(pattern: Rc>) -> Self { + fn new(pattern: Rc>) -> Self { mk_any(&pattern) } - fn apply<'a>(&self, source: &'a [Expr]) -> Option> { + fn apply<'a>(&self, source: &'a [RuleExpr]) -> Option> { any_match(self, source) } } @@ -183,11 +183,11 @@ impl InternedDisplay for AnyMatcher { /// vectorial placeholders and handles the scalars on leaves. pub struct VectreeMatcher(AnyMatcher); impl Matcher for VectreeMatcher { - fn new(pattern: Rc>) -> Self { + fn new(pattern: Rc>) -> Self { Self(AnyMatcher::new(pattern)) } - fn apply<'a>(&self, source: &'a [Expr]) -> Option> { + fn apply<'a>(&self, source: &'a [RuleExpr]) -> Option> { self.0.apply(source) } } diff --git a/src/rule/matcher_vectree/vec_match.rs b/src/rule/matcher_vectree/vec_match.rs index 0b155f9..e8b5ced 100644 --- a/src/rule/matcher_vectree/vec_match.rs +++ b/src/rule/matcher_vectree/vec_match.rs @@ -4,13 +4,13 @@ use itertools::Itertools; use super::scal_match::scalv_match; use super::shared::VecMatcher; -use crate::ast::Expr; +use crate::rule::matcher::RuleExpr; use crate::rule::state::{State, StateEntry}; use crate::utils::unwrap_or; pub fn vec_match<'a>( matcher: &VecMatcher, - seq: &'a [Expr], + seq: &'a [RuleExpr], ) -> Option> { match matcher { VecMatcher::Placeh { key, nonzero } => { diff --git a/src/rule/prepare_rule.rs b/src/rule/prepare_rule.rs index e466f57..47b8f70 100644 --- a/src/rule/prepare_rule.rs +++ b/src/rule/prepare_rule.rs @@ -1,24 +1,24 @@ -use std::rc::Rc; - use hashbrown::HashMap; use itertools::Itertools; +use super::matcher::RuleExpr; use super::vec_attrs::vec_attrs; use super::RuleError; use crate::ast::{Clause, Expr, PHClass, Placeholder, Rule}; use crate::interner::{Interner, Tok}; use crate::representations::location::Location; +use crate::Sym; /// Ensure that the rule's source begins and ends with a vectorial without /// changing its meaning -fn pad(mut rule: Rule, i: &Interner) -> Rule { +fn pad(mut rule: Rule, i: &Interner) -> Rule { let class: PHClass = PHClass::Vec { nonzero: false, prio: 0 }; - let empty: &[Expr] = &[]; - let prefix: &[Expr] = &[Expr { + let empty: &[Expr] = &[]; + let prefix: &[Expr] = &[Expr { location: Location::Unknown, value: Clause::Placeh(Placeholder { name: i.i("::prefix"), class }), }]; - let suffix: &[Expr] = &[Expr { + let suffix: &[Expr] = &[Expr { location: Location::Unknown, value: Clause::Placeh(Placeholder { name: i.i("::suffix"), class }), }]; @@ -28,22 +28,14 @@ fn pad(mut rule: Rule, i: &Interner) -> Rule { let suffix_explicit = vec_attrs(rule_tail).is_some(); let prefix_v = if prefix_explicit { empty } else { prefix }; let suffix_v = if suffix_explicit { empty } else { suffix }; - rule.pattern = Rc::new( - prefix_v - .iter() - .chain(rule.pattern.iter()) - .chain(suffix_v.iter()) - .cloned() - .collect(), - ); - rule.template = Rc::new( - prefix_v - .iter() - .chain(rule.template.iter()) - .chain(suffix_v.iter()) - .cloned() - .collect(), - ); + rule.pattern = (prefix_v.iter().cloned()) + .chain(rule.pattern.into_iter()) + .chain(suffix_v.iter().cloned()) + .collect(); + rule.template = (prefix_v.iter().cloned()) + .chain(rule.template.into_iter()) + .chain(suffix_v.iter().cloned()) + .collect(); rule } @@ -62,7 +54,7 @@ impl From for PHType { } fn check_rec_expr( - expr: &Expr, + expr: &RuleExpr, types: &mut HashMap, PHType>, in_template: bool, ) -> Result<(), RuleError> { @@ -95,7 +87,7 @@ fn check_rec_expr( } fn check_rec_exprv( - exprv: &[Expr], + exprv: &[RuleExpr], types: &mut HashMap, PHType>, in_template: bool, ) -> Result<(), RuleError> { @@ -115,7 +107,10 @@ fn check_rec_exprv( } } -pub fn prepare_rule(rule: Rule, i: &Interner) -> Result { +pub fn prepare_rule( + rule: Rule, + i: &Interner, +) -> Result, RuleError> { // Dimension check let mut types = HashMap::new(); check_rec_exprv(&rule.pattern, &mut types, false)?; diff --git a/src/rule/repository.rs b/src/rule/repository.rs index 5a3aed5..c2f46df 100644 --- a/src/rule/repository.rs +++ b/src/rule/repository.rs @@ -5,19 +5,20 @@ use std::rc::Rc; use hashbrown::HashSet; use ordered_float::NotNan; -use super::matcher::Matcher; +use super::matcher::{Matcher, RuleExpr}; use super::prepare_rule::prepare_rule; use super::state::apply_exprv; use super::{update_first_seq, RuleError, VectreeMatcher}; -use crate::ast::{Expr, Rule}; -use crate::interner::{InternedDisplay, Interner, Sym}; +use crate::ast::Rule; +use crate::interner::{InternedDisplay, Interner}; use crate::utils::Substack; +use crate::Sym; #[derive(Debug)] pub struct CachedRule { matcher: M, - pattern: Rc>, - template: Rc>, + pattern: Vec, + template: Vec, } impl InternedDisplay for CachedRule { @@ -48,9 +49,9 @@ pub struct Repository { impl Repository { /// Build a new repository to hold the given set of rules pub fn new( - mut rules: Vec, + mut rules: Vec>, i: &Interner, - ) -> Result { + ) -> Result, RuleError)> { rules.sort_by_key(|r| -r.prio); let cache = rules .into_iter() @@ -60,10 +61,10 @@ impl Repository { let mut glossary = HashSet::new(); for e in rule.pattern.iter() { e.visit_names(Substack::Bottom, &mut |op| { - glossary.insert(op); + glossary.insert(*op); }) } - let matcher = M::new(rule.pattern.clone()); + let matcher = M::new(Rc::new(rule.pattern.clone())); let prep = CachedRule { matcher, pattern: rule.pattern, @@ -76,10 +77,10 @@ impl Repository { } /// Attempt to run each rule in priority order once - pub fn step(&self, code: &Expr) -> Option { + pub fn step(&self, code: &RuleExpr) -> Option { let mut glossary = HashSet::new(); code.visit_names(Substack::Bottom, &mut |op| { - glossary.insert(op); + glossary.insert(*op); }); for (rule, deps, _) in self.cache.iter() { if !deps.is_subset(&glossary) { @@ -100,7 +101,7 @@ impl Repository { /// Keep running the matching rule with the highest priority until no /// rules match. WARNING: this function might not terminate #[allow(unused)] - pub fn pass(&self, code: &Expr) -> Option { + pub fn pass(&self, code: &RuleExpr) -> Option { if let Some(mut processed) = self.step(code) { while let Some(out) = self.step(&processed) { processed = out @@ -114,7 +115,11 @@ impl Repository { /// Attempt to run each rule in priority order `limit` times. Returns /// the final tree and the number of iterations left to the limit. #[allow(unused)] - pub fn long_step(&self, code: &Expr, mut limit: usize) -> (Expr, usize) { + pub fn long_step( + &self, + code: &RuleExpr, + mut limit: usize, + ) -> (RuleExpr, usize) { if limit == 0 { return (code.clone(), 0); } diff --git a/src/rule/state.rs b/src/rule/state.rs index f04e413..ca85211 100644 --- a/src/rule/state.rs +++ b/src/rule/state.rs @@ -3,18 +3,19 @@ use std::rc::Rc; use hashbrown::HashMap; use itertools::Itertools; +use super::matcher::RuleExpr; use crate::ast::{Clause, Expr, PHClass, Placeholder}; use crate::interner::Tok; use crate::utils::unwrap_or; #[derive(Clone, Copy, Debug, PartialEq)] pub enum StateEntry<'a> { - Vec(&'a [Expr]), - Scalar(&'a Expr), + Vec(&'a [RuleExpr]), + Scalar(&'a RuleExpr), } pub type State<'a> = HashMap, StateEntry<'a>>; -pub fn apply_exprv(template: &[Expr], state: &State) -> Vec { +pub fn apply_exprv(template: &[RuleExpr], state: &State) -> Vec { template .iter() .map(|e| apply_expr(e, state)) @@ -22,7 +23,7 @@ pub fn apply_exprv(template: &[Expr], state: &State) -> Vec { .collect() } -pub fn apply_expr(template: &Expr, state: &State) -> Vec { +pub fn apply_expr(template: &RuleExpr, state: &State) -> Vec { let Expr { location, value } = template; match value { Clause::P(_) | Clause::Name(_) => vec![template.clone()], diff --git a/src/rule/update_first_seq.rs b/src/rule/update_first_seq.rs index 5044440..fd8625a 100644 --- a/src/rule/update_first_seq.rs +++ b/src/rule/update_first_seq.rs @@ -1,15 +1,17 @@ use std::rc::Rc; +use super::matcher::RuleExpr; use crate::ast::{Clause, Expr}; use crate::utils::replace_first; +use crate::Sym; /// Traverse the tree, calling pred on every sibling list until it returns /// some vec then replace the sibling list with that vec and return true /// return false if pred never returned some -pub fn exprv>) -> Option>>>( - input: Rc>, +pub fn exprv>) -> Option>>>( + input: Rc>, pred: &mut F, -) -> Option>> { +) -> Option>> { if let Some(v) = pred(input.clone()) { return Some(v); } @@ -17,18 +19,18 @@ pub fn exprv>) -> Option>>>( .map(|i| Rc::new(i.collect())) } -pub fn expr>) -> Option>>>( - input: &Expr, +pub fn expr>) -> Option>>>( + input: &RuleExpr, pred: &mut F, -) -> Option { +) -> Option { clause(&input.value, pred) .map(|value| Expr { value, location: input.location.clone() }) } -pub fn clause>) -> Option>>>( - c: &Clause, +pub fn clause>) -> Option>>>( + c: &Clause, pred: &mut F, -) -> Option { +) -> Option> { match c { Clause::P(_) | Clause::Placeh { .. } | Clause::Name { .. } => None, Clause::Lambda(arg, body) => diff --git a/src/rule/vec_attrs.rs b/src/rule/vec_attrs.rs index 3f7582b..aee13ba 100644 --- a/src/rule/vec_attrs.rs +++ b/src/rule/vec_attrs.rs @@ -1,9 +1,10 @@ -use crate::ast::{Clause, Expr, PHClass, Placeholder}; +use super::matcher::RuleExpr; +use crate::ast::{Clause, PHClass, Placeholder}; use crate::interner::Tok; /// Returns the name, priority and nonzero of the expression if it is /// a vectorial placeholder -pub fn vec_attrs(expr: &Expr) -> Option<(Tok, u64, bool)> { +pub fn vec_attrs(expr: &RuleExpr) -> Option<(Tok, u64, bool)> { if let Clause::Placeh(Placeholder { class: PHClass::Vec { prio, nonzero }, name, diff --git a/src/stl/mk_stl.rs b/src/stl/mk_stl.rs index 7eb630f..796b0ec 100644 --- a/src/stl/mk_stl.rs +++ b/src/stl/mk_stl.rs @@ -6,9 +6,10 @@ use super::conv::conv; use super::io::io; use super::num::num; use super::str::str; -use crate::interner::{Interner, Sym}; +use crate::interner::Interner; use crate::pipeline::file_loader::mk_embed_cache; use crate::pipeline::{from_const_tree, parse_layer, ProjectTree}; +use crate::representations::VName; use crate::sourcefile::{FileEntry, Import}; /// Feature flags for the STL. @@ -29,29 +30,28 @@ struct StlEmbed; /// Build the standard library used by the interpreter by combining the other /// libraries -pub fn mk_stl(i: &Interner, options: StlOptions) -> ProjectTree { +pub fn mk_stl(i: &Interner, options: StlOptions) -> ProjectTree { let const_tree = from_const_tree( HashMap::from([( i.i("std"), io(i, options.impure) + conv(i) + bool(i) + str(i) + num(i), )]), &[i.i("std")], - i, ); let ld_cache = mk_embed_cache::(".orc", i); + let targets = StlEmbed::iter() + .map(|path| { + path + .strip_suffix(".orc") + .expect("the embed is filtered for suffix") + .split('/') + .map(|segment| i.i(segment)) + .collect::>() + }) + .collect::>(); parse_layer( - &StlEmbed::iter() - .map(|path| -> Sym { - let segtoks = path - .strip_suffix(".orc") - .expect("the embed is filtered for suffix") - .split('/') - .map(|segment| i.i(segment)) - .collect::>(); - i.i(&segtoks[..]) - }) - .collect::>()[..], - &|p| ld_cache.find(&p), + targets.iter().map(|v| &v[..]), + &|p| ld_cache.find(p), &const_tree, &[], i, diff --git a/src/utils/cache.rs b/src/utils/cache.rs index 0afcd7e..2695eaa 100644 --- a/src/utils/cache.rs +++ b/src/utils/cache.rs @@ -1,3 +1,4 @@ +use std::borrow::Borrow; use std::cell::RefCell; use std::hash::Hash; @@ -23,19 +24,23 @@ impl<'a, I: Eq + Hash + Clone, O: Clone> Cache<'a, I, O> { } /// Produce and cache a result by cloning I if necessary - pub fn find(&self, i: &I) -> O { + pub fn find(&self, q: &Q) -> O + where + Q: Eq + Hash + ToOwned, + I: Borrow, + { let closure = &self.closure; - if let Some(v) = self.store.borrow().get(i) { + if let Some(v) = self.store.borrow().get(q) { return v.clone(); } // In the moment of invocation the refcell is on immutable // this is important for recursive calculations - let result = closure(i.clone(), self); + let result = closure(q.to_owned(), self); let mut store = self.store.borrow_mut(); store .raw_entry_mut() - .from_key(i) - .or_insert_with(|| (i.clone(), result)) + .from_key(q) + .or_insert_with(|| (q.to_owned(), result)) .1 .clone() } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 1c01525..bae24df 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,6 +1,7 @@ mod cache; mod print_nname; mod pushed; +mod rc_to_owned; mod replace_first; mod side; mod split_max_prefix; @@ -11,6 +12,7 @@ mod unwrap_or; pub use cache::Cache; pub use print_nname::sym2string; pub use pushed::pushed; +pub use rc_to_owned::{map_rc, rc_to_owned}; pub use replace_first::replace_first; pub use side::Side; pub use split_max_prefix::split_max_prefix; diff --git a/src/utils/print_nname.rs b/src/utils/print_nname.rs index 27c2203..d3301ca 100644 --- a/src/utils/print_nname.rs +++ b/src/utils/print_nname.rs @@ -1,6 +1,7 @@ use itertools::Itertools; -use crate::interner::{Interner, Sym}; +use crate::interner::Interner; +use crate::Sym; /// Print symbols to :: delimited strings pub fn sym2string(t: Sym, i: &Interner) -> String { diff --git a/src/utils/rc_to_owned.rs b/src/utils/rc_to_owned.rs new file mode 100644 index 0000000..db031cd --- /dev/null +++ b/src/utils/rc_to_owned.rs @@ -0,0 +1,9 @@ +use std::rc::Rc; + +pub fn rc_to_owned(rc: Rc) -> T { + Rc::try_unwrap(rc).unwrap_or_else(|rc| rc.as_ref().clone()) +} + +pub fn map_rc(rc: Rc, pred: impl FnOnce(T) -> U) -> Rc { + Rc::new(pred(rc_to_owned(rc))) +} diff --git a/src/utils/unwrap_or.rs b/src/utils/unwrap_or.rs index 62b0c06..944835f 100644 --- a/src/utils/unwrap_or.rs +++ b/src/utils/unwrap_or.rs @@ -1,6 +1,17 @@ /// A macro version of [Option::unwrap_or_else] which supports flow /// control statements such as `return` and `break` in the "else" branch. +/// +/// ```ignore +/// crate::unwrap_or!(Some(1); return) +/// ``` +/// /// It also supports unwrapping concrete variants of other enums +/// +/// ```ignore +/// use crate::representations::Literal; +/// +/// crate::unwrap_or!(Literal::Usize(2) => Literal::Number; return) +/// ``` macro_rules! unwrap_or { ($m:expr; $fail:expr) => {{ if let Some(res) = ($m) { res } else { $fail }