From 823b2c13baf3cace95c4b4f1a30cb43d83895480 Mon Sep 17 00:00:00 2001 From: Vincent Hanquez Date: Sun, 18 Feb 2024 10:35:34 +0800 Subject: [PATCH] resolution changes --- werbolg-compile/src/bindings.rs | 53 ++++---- werbolg-compile/src/compile.rs | 210 ++++++++++++++++++++++++-------- werbolg-compile/src/errors.rs | 30 +++-- werbolg-compile/src/hier.rs | 138 +++++++++++++++++++++ werbolg-compile/src/lib.rs | 151 +---------------------- werbolg-compile/src/prepare.rs | 170 ++++++++++++++++++++++++++ werbolg-compile/src/resolver.rs | 1 + werbolg-compile/src/symbols.rs | 143 +++++++--------------- werbolg-core/src/basic.rs | 43 ++++++- werbolg-tales/src/exec.rs | 12 +- 10 files changed, 605 insertions(+), 346 deletions(-) create mode 100644 werbolg-compile/src/hier.rs create mode 100644 werbolg-compile/src/prepare.rs diff --git a/werbolg-compile/src/bindings.rs b/werbolg-compile/src/bindings.rs index 01fce32..7d8b25f 100644 --- a/werbolg-compile/src/bindings.rs +++ b/werbolg-compile/src/bindings.rs @@ -1,19 +1,23 @@ +use crate::hier::Hier; use alloc::{vec, vec::Vec}; use hashbrown::HashMap; -use werbolg_core::{AbsPath, Ident, Namespace}; +use werbolg_core::{AbsPath, Ident}; #[derive(Clone)] pub struct Bindings(HashMap); -pub struct GlobalBindings { - root: Bindings, - ns: HashMap>, -} +pub struct GlobalBindings(pub(crate) Hier>); pub struct BindingsStack { stack: Vec>, } +impl Default for Bindings { + fn default() -> Self { + Bindings::new() + } +} + impl Bindings { pub fn new() -> Self { Self(HashMap::new()) @@ -40,40 +44,28 @@ impl Bindings { } } -impl GlobalBindings { +impl GlobalBindings { pub fn new() -> Self { - Self { - root: Bindings::new(), - ns: HashMap::new(), - } + Self(Hier::default()) } - pub fn add(&mut self, name: AbsPath, value: T) { + pub fn add(&mut self, name: AbsPath, value: T) -> Result<(), ()> { let (namespace, ident) = name.split(); - if namespace.is_root() { - self.root.add(ident, value) - } else { - if let Some(ns_bindings) = self.ns.get_mut(&namespace) { - ns_bindings.add(ident, value); - } else { - let mut b = Bindings::new(); - b.add(ident, value); - self.ns.insert(namespace, b); - } + + if !self.0.namespace_exist(namespace.clone()) { + self.0.add_ns_hier(namespace.clone()).unwrap() } + + self.0.on_mut(&namespace, |bindings| { + bindings.add(ident.clone(), value.clone()) + }) } + #[allow(unused)] pub fn get(&self, name: &AbsPath) -> Option<&T> { let (namespace, ident) = name.split(); - if namespace.is_root() { - self.root.get(&ident) - } else { - if let Some(ns_bindings) = self.ns.get(&namespace) { - ns_bindings.get(&ident) - } else { - None - } - } + let bindings = self.0.get(&namespace).unwrap(); + bindings.get(&ident) } } @@ -98,7 +90,6 @@ impl BindingsStack { } Some(bindings) => { bindings.add(name.clone(), value); - return; } } } diff --git a/werbolg-compile/src/compile.rs b/werbolg-compile/src/compile.rs index 39cdd9e..38679dc 100644 --- a/werbolg-compile/src/compile.rs +++ b/werbolg-compile/src/compile.rs @@ -9,11 +9,19 @@ use super::CompilationParams; use alloc::{format, vec, vec::Vec}; use werbolg_core as ir; use werbolg_core::{ - AbsPath, ConstrId, FunId, GlobalId, Ident, LitId, Namespace, NifId, Path, PathType, Span, + ConstrId, FunId, GlobalId, Ident, LitId, Namespace, NifId, Path, PathType, Span, }; +pub(crate) struct CompilationSharedState {} +pub(crate) struct CompilationLocalState { + namespace: Namespace, + bindings: LocalBindings, +} + pub(crate) struct CodeBuilder<'a, L: Clone + Eq + core::hash::Hash> { - pub(crate) params: &'a CompilationParams, + #[allow(unused)] + pub(crate) shared: &'a CompilationSharedState, + pub(crate) params: CompilationParams, pub(crate) funs_tbl: SymbolsTable, pub(crate) funs_vec: IdVec, pub(crate) lambdas_vec: IdVecAfter, @@ -89,12 +97,14 @@ pub enum BindingType { impl<'a, L: Clone + Eq + core::hash::Hash> CodeBuilder<'a, L> { pub fn new( - params: &'a CompilationParams, + shared: &'a CompilationSharedState, + params: CompilationParams, funs_tbl: SymbolsTable, lambdas_vec: IdVecAfter, globals: GlobalBindings, ) -> Self { Self { + shared, params, funs_tbl, funs_vec: IdVec::new(), @@ -133,20 +143,24 @@ enum FunPos { pub(crate) fn generate_func_code<'a, L: Clone + Eq + core::hash::Hash>( state: &mut CodeBuilder<'a, L>, + namespace: &Namespace, fundef: Option, funimpl: ir::FunImpl, ) -> Result { let name = fundef.map(|x| x.name.clone()); let ir::FunImpl { vars, body } = funimpl; - let mut local = LocalBindings::new(); - local.scope_enter(); + let mut local = CompilationLocalState { + bindings: LocalBindings::new(), + namespace: namespace.clone(), + }; + local.bindings.scope_enter(); for (var_i, var) in vars.iter().enumerate() { let var_i = var_i.try_into().map_err(|_| { CompilationError::FunctionParamsMoreThanLimit(var.0.span.clone(), vars.len()) })?; - local.add_param(var.0.clone().unspan(), var_i); + local.bindings.add_param(var.0.clone().unspan(), var_i); } let arity = vars.len().try_into().map(|n| CallArity(n)).unwrap(); @@ -156,7 +170,7 @@ pub(crate) fn generate_func_code<'a, L: Clone + Eq + core::hash::Hash>( if !tc { state.write_code().push(Instruction::Ret); } - let stack_size = local.scope_terminate(); + let stack_size = local.bindings.scope_terminate(); // now compute the code for the lambdas. This is in a loop // since it can generate further lambdas @@ -165,10 +179,9 @@ pub(crate) fn generate_func_code<'a, L: Clone + Eq + core::hash::Hash>( core::mem::swap(&mut state.lambdas, &mut lambdas); for (code_ref, fun_impl) in lambdas { - let lirdef = - generate_func_code(state, None, fun_impl).map_err(|e: CompilationError| { - e.context(format!("function lambda code {:?}", name)) - })?; + let lirdef = generate_func_code(state, namespace, None, fun_impl).map_err( + |e: CompilationError| e.context(format!("function lambda code {:?}", name)), + )?; let lambda_funid = state.lambdas_vec.push(lirdef); state @@ -187,7 +200,7 @@ pub(crate) fn generate_func_code<'a, L: Clone + Eq + core::hash::Hash>( fn generate_expression_code<'a, L: Clone + Eq + core::hash::Hash>( state: &mut CodeBuilder<'a, L>, - local: &mut LocalBindings, + local: &mut CompilationLocalState, funpos: FunPos, expr: ir::Expr, ) -> Result { @@ -226,12 +239,12 @@ fn generate_expression_code<'a, L: Clone + Eq + core::hash::Hash>( for e in l { let _: bool = generate_expression_code(state, local, FunPos::NotRoot, e)?; } - match &state.params.sequence_constructor { + match state.params.sequence_constructor { None => return Err(CompilationError::SequenceNotSupported(span)), Some(nifid) => { state .write_code() - .push(Instruction::CallNif(*nifid, call_arity)); + .push(Instruction::CallNif(nifid, call_arity)); Ok(true) } } @@ -242,7 +255,7 @@ fn generate_expression_code<'a, L: Clone + Eq + core::hash::Hash>( .map_err(|e| e.context(alloc::format!("{:?}", *x)))?; match binder { ir::Binder::Ident(ident) => { - let bind = local.add_local(ident.clone()); + let bind = local.bindings.add_local(ident.clone()); state.write_code().push(Instruction::LocalBind(bind)); } ir::Binder::Ignore => { @@ -260,11 +273,36 @@ fn generate_expression_code<'a, L: Clone + Eq + core::hash::Hash>( Ok(tc) } ir::Expr::Field(expr, struct_ident, field_ident) => { - let (struct_path, _) = resolve_path(&state.resolver, &struct_ident.inner); - let (constr_id, constr_def) = + let resolved = resolve_symbol(&state, &local, &struct_ident.inner); + let result = resolved + .into_iter() + .filter_map(|res| match res { + Resolution::Constructor(c, _) => Some(c), + _ => None, + }) + .collect::>(); + + let constr_id = if result.is_empty() { + return Err(CompilationError::MissingSymbol( + struct_ident.span, + struct_ident.inner, + )); + } else if result.len() > 1 { + /* + return Err(CompilationError::DuplicateSymbol( + struct_ident.span, + struct_ident.inner, + )); + */ + todo!() + } else { + &result[0] + }; + + let constr_def = state .constrs - .get(&struct_path) + .get_by_id(*constr_id) .ok_or(CompilationError::MissingConstructor( struct_ident.span.clone(), struct_ident.inner.clone(), @@ -288,7 +326,7 @@ fn generate_expression_code<'a, L: Clone + Eq + core::hash::Hash>( let _: bool = generate_expression_code(state, local, funpos, *expr)?; state .write_code() - .push(Instruction::AccessField(constr_id, index)); + .push(Instruction::AccessField(*constr_id, index)); Ok(false) } ir::Expr::Lambda(_span, funimpl) => { @@ -332,9 +370,9 @@ fn generate_expression_code<'a, L: Clone + Eq + core::hash::Hash>( let cond_jump_ref = state.write_code().push_temp(); let cond_pos = state.get_instruction_address(); - local.scope_enter(); + local.bindings.scope_enter(); let tc_then = generate_expression_code(state, local, funpos, (*then_expr).unspan())?; - local.scope_leave(); + local.bindings.scope_leave(); // if we are at the root, check if we need to ret or not, otherwise // push a temporary for jumping to the end of the block @@ -349,9 +387,9 @@ fn generate_expression_code<'a, L: Clone + Eq + core::hash::Hash>( let else_pos = state.get_instruction_address(); - local.scope_enter(); + local.bindings.scope_enter(); let tc_else = generate_expression_code(state, local, funpos, (*else_expr).unspan())?; - local.scope_leave(); + local.bindings.scope_leave(); let end_pos = state.get_instruction_address(); @@ -379,50 +417,124 @@ fn generate_expression_code<'a, L: Clone + Eq + core::hash::Hash>( fn fetch_ident<'a, L: Clone + Eq + core::hash::Hash>( state: &CodeBuilder<'a, L>, - local: &LocalBindings, + local: &CompilationLocalState, span: Span, path: Path, ) -> Result { - if let Some(local_path) = path.get_local() { - if let Some(bound) = local.bindings.get(local_path) { - return Ok(*bound); + let resolved = resolve_symbol(state, local, &path); + + if resolved.is_empty() { + std::println!("resolution empty"); + Err(CompilationError::MissingSymbol(span, path)) + } else if resolved.len() > 1 { + Err(CompilationError::MultipleSymbol(span, path)) + } else { + let r = &resolved[0]; + match r { + Resolution::Constructor(_, _) => { + // some error + todo!() + } + Resolution::Binding(r) => Ok(*r), } } +} - let resolved = resolve_path(&state.resolver, &path); +pub enum Resolution { + Constructor(ConstrId, Vec), + Binding(BindingType), +} - if let Some(bound) = state.globals.get(&resolved.0) { - Ok(*bound) - } else { - if let Some(resolved) = &resolved.1 { - if let Some(bound) = state.globals.get(resolved) { - Ok(*bound) +pub fn resolve_symbol_at<'a, L: Clone + Eq + core::hash::Hash>( + state: &CodeBuilder<'a, L>, + namespace: Namespace, + path: &Path, +) -> Vec { + let mut out = Vec::new(); + + let mut constr_table = Some(&state.constrs.table.0); + let mut bind_table = Some(&state.globals.0); + + // if namespace is not empty, we need to tweak those symbol table + if !namespace.is_root() { + loop { + let (ident, ns) = namespace.clone().drop_first(); + if let Some(tbl) = constr_table { + constr_table = tbl.get_sub(&ident).ok(); + } else { + panic!("missing module {:?}", namespace) + } + if let Some(tbl) = bind_table { + bind_table = tbl.get_sub(&ident).ok(); } else { - Err(CompilationError::MissingSymbol(span, path)) + panic!("missing module {:?}", namespace) + } + + if ns.is_root() { + break; } - } else { - Err(CompilationError::MissingSymbol(span, path)) } } + + let mut idents = path.components(); + let mut current_ns = namespace; + + while let Some((ident, remaining)) = idents.next() { + let mut namespace_entered = false; + // check in the constructor symbols (struct / enum) + if let Some(tbl) = constr_table { + if let Some(constr) = tbl.current().get(ident) { + out.push(Resolution::Constructor(constr, remaining.to_vec())) + } + constr_table = tbl.get_sub(ident).ok(); + if constr_table.is_some() { + namespace_entered = true; + } + } + // check in the function symbols + if let Some(tbl) = bind_table { + if let Some(bty) = tbl.current().get(ident) { + if remaining.is_empty() { + out.push(Resolution::Binding(*bty)) + } + } + bind_table = tbl.get_sub(ident).ok(); + if bind_table.is_some() { + namespace_entered = true; + } + } + + if !namespace_entered { + // error now ? + break; + } + + current_ns = current_ns.append(ident.clone()) + } + out } -fn resolve_path(resolver: &Option, path: &Path) -> (AbsPath, Option) { +pub fn resolve_symbol<'a, L: Clone + Eq + core::hash::Hash>( + state: &CodeBuilder<'a, L>, + local: &CompilationLocalState, + path: &Path, +) -> Vec { match path.path_type() { PathType::Absolute => { - let (namespace, ident) = path.split(); - (AbsPath::new(&namespace, &ident), None) + // if the path is absolute, we only lookup through the defined symbols, so we never + // look in the local bindings + resolve_symbol_at(state, Namespace::root(), path) } PathType::Relative => { - if let Some(resolver) = resolver { - let (namespace, ident) = path.split(); - let full_namespace = resolver.current.append_namespace(&namespace); - ( - AbsPath::new(&full_namespace, &ident), - Some(AbsPath::new(&Namespace::root(), &ident)), - ) - } else { - panic!("no resolver") + if let Some(local_path) = path.get_local() { + if let Some(bound) = local.bindings.bindings.get(local_path) { + return vec![Resolution::Binding(*bound)]; + } } + let mut result = resolve_symbol_at(state, local.namespace.clone(), path); + let mut root_result = resolve_symbol_at(state, Namespace::root(), path); + result.append(&mut root_result); + result } } } diff --git a/werbolg-compile/src/errors.rs b/werbolg-compile/src/errors.rs index 71ab00f..9a3bbdc 100644 --- a/werbolg-compile/src/errors.rs +++ b/werbolg-compile/src/errors.rs @@ -1,4 +1,4 @@ -use werbolg_core::{Ident, Literal, Path, Span}; +use werbolg_core::{AbsPath, Ident, Literal, Path, Span}; use super::symbols::NamespaceError; use alloc::{boxed::Box, format, string::String}; @@ -6,10 +6,14 @@ use alloc::{boxed::Box, format, string::String}; /// Compilation error #[derive(Debug)] pub enum CompilationError { + /// Duplicate symbol during environment population (e.g. 2 functions with the name) + DuplicateSymbolEnv(String, AbsPath), /// Duplicate symbol during compilation (e.g. 2 functions with the name) DuplicateSymbol(Span, Ident), /// Cannot find the symbol during compilation MissingSymbol(Span, Path), + /// Multiple symbol found for this symbol during compilation + MultipleSymbol(Span, Path), /// Cannot find the constructor symbol during compilation MissingConstructor(Span, Path), /// Number of parameters for a functions is above the limit we chose @@ -32,18 +36,20 @@ pub enum CompilationError { impl CompilationError { /// Get the span of this compilation error - pub fn span(&self) -> Span { + pub fn span(&self) -> Option { match self { - CompilationError::DuplicateSymbol(span, _) => span.clone(), - CompilationError::MissingSymbol(span, _) => span.clone(), - CompilationError::MissingConstructor(span, _) => span.clone(), - CompilationError::FunctionParamsMoreThanLimit(span, _) => span.clone(), - CompilationError::LiteralNotSupported(span, _) => span.clone(), - CompilationError::SequenceNotSupported(span) => span.clone(), - CompilationError::ConstructorNotStructure(span, _) => span.clone(), - CompilationError::StructureFieldNotExistant(span, _, _) => span.clone(), - CompilationError::NamespaceError(_) => todo!(), - CompilationError::CallTooManyArguments(span, _) => span.clone(), + CompilationError::DuplicateSymbolEnv(_, _) => None, + CompilationError::DuplicateSymbol(span, _) => Some(span.clone()), + CompilationError::MissingSymbol(span, _) => Some(span.clone()), + CompilationError::MultipleSymbol(span, _) => Some(span.clone()), + CompilationError::MissingConstructor(span, _) => Some(span.clone()), + CompilationError::FunctionParamsMoreThanLimit(span, _) => Some(span.clone()), + CompilationError::LiteralNotSupported(span, _) => Some(span.clone()), + CompilationError::SequenceNotSupported(span) => Some(span.clone()), + CompilationError::ConstructorNotStructure(span, _) => Some(span.clone()), + CompilationError::StructureFieldNotExistant(span, _, _) => Some(span.clone()), + CompilationError::NamespaceError(_) => None, + CompilationError::CallTooManyArguments(span, _) => Some(span.clone()), CompilationError::Context(_, e) => e.span(), } } diff --git a/werbolg-compile/src/hier.rs b/werbolg-compile/src/hier.rs new file mode 100644 index 0000000..0b6ccbf --- /dev/null +++ b/werbolg-compile/src/hier.rs @@ -0,0 +1,138 @@ +use alloc::vec::Vec; +use hashbrown::HashMap; +use werbolg_core::{Ident, Namespace}; + +pub struct Hier { + current: T, + ns: HashMap>, +} + +impl Default for Hier { + fn default() -> Self { + Self::new(T::default()) + } +} + +impl Hier { + pub fn new(current: T) -> Self { + Self { + current, + ns: HashMap::new(), + } + } + + pub fn namespace_exist(&self, namespace: Namespace) -> bool { + if namespace.is_root() { + true + } else { + let (id, next) = namespace.clone().drop_first(); + if let Some(ns) = self.ns.get(&id) { + ns.namespace_exist(next) + } else { + false + } + } + } + + #[allow(unused)] + pub fn add_ns(&mut self, ident: Ident, t: T) -> Result<(), ()> { + let already_exist = self.ns.insert(ident, Hier::new(t)); + if already_exist.is_some() { + Err(()) + } else { + Ok(()) + } + } + + pub fn add_ns_hier(&mut self, namespace: Namespace) -> Result<(), ()> { + if namespace.is_root() { + Ok(()) + } else { + let (id, next) = namespace.clone().drop_first(); + if let Some(r) = self.ns.get_mut(&id) { + r.add_ns_hier(next) + } else { + let mut h = Hier::new(T::default()); + h.add_ns_hier(next)?; + self.ns.insert(id, h); + Ok(()) + } + } + } + + pub fn current(&self) -> &T { + &self.current + } + + pub fn on_mut(&mut self, namespace: &Namespace, mut f: F) -> Result<(), ()> + where + F: FnMut(&mut T) -> (), + { + if namespace.is_root() { + f(&mut self.current); + Ok(()) + } else { + let (id, next) = namespace.clone().drop_first(); + if let Some(r) = self.ns.get_mut(&id) { + r.on_mut(&next, f) + } else { + Err(()) + } + /* + loop { + let (id, next) = namespace.drop_first(); + + if let Some(mut r) = hier_pointer.ns.get_mut(&id) { + hier_pointer = &mut r; + if next.is_root() { + f(&mut hier_pointer.current); + return Ok(()); + } else { + namespace = next; + } + } else { + return Err(()); + } + } + */ + } + } + + pub fn get(&self, namespace: &Namespace) -> Result<&T, ()> { + if namespace.is_root() { + Ok(&self.current) + } else { + let mut namespace = namespace.clone(); + let mut hier_pointer = self; + loop { + let (id, next) = namespace.drop_first(); + if let Some(r) = hier_pointer.ns.get(&id) { + hier_pointer = &r; + if next.is_root() { + return Ok(&r.current); + } else { + namespace = next; + } + } else { + return Err(()); + } + } + } + } + + pub fn get_sub(&self, id: &Ident) -> Result<&Hier, ()> { + if let Some(hier) = self.ns.get(id) { + Ok(hier) + } else { + Err(()) + } + } + + pub fn dump<'a>(&'a self, current: Namespace, out: &mut Vec<(Namespace, &'a T)>) { + out.push((current.clone(), &self.current)); + for (ns_name, st) in self.ns.iter() { + let child_namespace = current.clone().append(ns_name.clone()); + st.dump(child_namespace, out) + } + } +} diff --git a/werbolg-compile/src/lib.rs b/werbolg-compile/src/lib.rs index ab8ec0e..473281b 100644 --- a/werbolg-compile/src/lib.rs +++ b/werbolg-compile/src/lib.rs @@ -3,6 +3,7 @@ #![deny(missing_docs)] extern crate alloc; +extern crate std; mod bindings; mod code; @@ -10,8 +11,10 @@ mod compile; mod defs; mod environ; mod errors; +mod hier; mod instructions; mod params; +mod prepare; mod resolver; mod symbols; @@ -21,20 +24,17 @@ pub use instructions::{ }; pub use params::CompilationParams; -use compile::*; pub use defs::*; -use resolver::SymbolResolver; use werbolg_core as ir; -use werbolg_core::{AbsPath, ConstrId, FunId, LitId, Namespace}; +use werbolg_core::{ConstrId, FunId, LitId, Namespace}; -use bindings::GlobalBindings; pub use environ::Environment; pub use errors::CompilationError; -use symbols::{IdVec, IdVecAfter, SymbolsTable, SymbolsTableData}; +pub use prepare::CompilationState; +use symbols::{IdVec, SymbolsTable, SymbolsTableData}; use alloc::{format, vec::Vec}; use core::fmt::Write; -use hashbrown::HashMap; /// A compiled unit /// @@ -53,145 +53,6 @@ pub struct CompilationUnit { pub code: IdVec, } -/// State of compilation -pub struct CompilationState { - params: CompilationParams, - funs: SymbolsTableData, - constrs: SymbolsTableData, - namespaces: HashMap, -} - -impl CompilationState { - /// Create a new compilation state - pub fn new(params: CompilationParams) -> Self { - Self { - params, - funs: SymbolsTableData::new(), - constrs: SymbolsTableData::new(), - namespaces: HashMap::new(), - } - } - - /// Add a ir::module to the compilation state - pub fn add_module( - &mut self, - namespace: &Namespace, - module: ir::Module, - ) -> Result<(), CompilationError> { - let mut uses = Vec::new(); - self.funs.create_namespace(namespace.clone())?; - self.constrs.create_namespace(namespace.clone())?; - - for stmt in module.statements.into_iter() { - match stmt { - ir::Statement::Use(u) => { - uses.push(u); - } - ir::Statement::Function(span, fundef, funimpl) => { - let ident = fundef.name.clone(); - let path = AbsPath::new(namespace, &ident); - let _funid = self - .funs - .add(&path, (namespace.clone(), fundef, funimpl)) - .ok_or_else(|| CompilationError::DuplicateSymbol(span, ident))?; - () - } - ir::Statement::Struct(span, structdef) => { - let stru = StructDef { - name: structdef.name.unspan(), - fields: structdef.fields.into_iter().map(|v| v.unspan()).collect(), - }; - let name = stru.name.clone(); - let path = AbsPath::new(namespace, &name); - self.constrs - .add(&path, ConstrDef::Struct(stru)) - .ok_or_else(|| CompilationError::DuplicateSymbol(span, name))?; - } - ir::Statement::Expr(_) => (), - } - } - - if self - .namespaces - .insert( - namespace.clone(), - SymbolResolver::new(namespace.clone(), uses), - ) - .is_some() - { - return Err(CompilationError::NamespaceError( - symbols::NamespaceError::Duplicate(namespace.clone()), - )); - } - - Ok(()) - } - - /// Finalize compilation and return a CompilationUnit containing all the modules compiled in the state - pub fn finalize( - self, - environ: &mut Environment, - ) -> Result, CompilationError> { - let SymbolsTableData { table, vecdata } = self.funs; - - /* - for (p, _id) in table.to_vec(Namespace::root()) { - std::println!("{:?}", p) - } - */ - - let mut root_bindings = GlobalBindings::new(); - for (path, id) in environ.symbols.to_vec(Namespace::root()) { - root_bindings.add(path, BindingType::Nif(id)) - } - - for (path, id) in environ.globals.to_vec(Namespace::root()) { - root_bindings.add(path, BindingType::Global(id)) - } - - for (path, fun_id) in table.to_vec(Namespace::root()) { - root_bindings.add(path, BindingType::Fun(fun_id)) - } - - let mut state = compile::CodeBuilder::new( - &self.params, - table, - IdVecAfter::new(vecdata.next_id()), - root_bindings, - ); - - for (funid, (namespace, fundef, funimpl)) in vecdata.into_iter() { - let Some(uses) = self.namespaces.get(&namespace) else { - panic!("internal error: namespace not defined"); - }; - state.set_module_resolver(uses); - - let fun_name = fundef.name.clone(); - let lirdef = - compile::generate_func_code(&mut state, Some(fundef), funimpl).map_err(|e| { - e.context(format!( - "namespace {:?} function code {:?}", - namespace, fun_name - )) - })?; - let lirid = state.funs_vec.push(lirdef); - assert_eq!(funid, lirid) - } - - // merge the lambdas vec with the main fun vec - state.funs_vec.concat(&mut state.lambdas_vec); - let funs = state.funs_vec; - - Ok(CompilationUnit { - lits: state.lits.finalize(), - constrs: state.constrs, - funs: funs, - funs_tbl: state.funs_tbl, - code: state.main_code.finalize(), - }) - } -} - /// Compile a IR Module into an optimised-for-execution `CompilationUnit` pub fn compile<'a, L: Clone + Eq + core::hash::Hash, N, G>( params: &'a CompilationParams, diff --git a/werbolg-compile/src/prepare.rs b/werbolg-compile/src/prepare.rs new file mode 100644 index 0000000..9e9f6ea --- /dev/null +++ b/werbolg-compile/src/prepare.rs @@ -0,0 +1,170 @@ +pub use crate::params::CompilationParams; + +use crate::compile::{self, *}; +pub use crate::defs::*; +use crate::resolver::SymbolResolver; +use crate::CompilationUnit; +use werbolg_core as ir; +use werbolg_core::{AbsPath, ConstrId, FunId, Namespace}; + +use crate::bindings::GlobalBindings; +pub use crate::environ::Environment; +pub use crate::errors::CompilationError; +use crate::symbols::{self, IdVecAfter, SymbolsTableData}; + +use alloc::{format, string::String, vec::Vec}; +use hashbrown::HashMap; + +/// State of compilation +pub struct CompilationState { + params: CompilationParams, + funs: SymbolsTableData, + constrs: SymbolsTableData, + namespaces: HashMap, +} + +impl CompilationState { + /// Create a new compilation state + pub fn new(params: CompilationParams) -> Self { + Self { + params, + funs: SymbolsTableData::new(), + constrs: SymbolsTableData::new(), + namespaces: HashMap::new(), + } + } + + /// Add a ir::module to the compilation state + pub fn add_module( + &mut self, + namespace: &Namespace, + module: ir::Module, + ) -> Result<(), CompilationError> { + let mut uses = Vec::new(); + self.funs.create_namespace(namespace.clone())?; + self.constrs.create_namespace(namespace.clone())?; + + for stmt in module.statements.into_iter() { + match stmt { + ir::Statement::Use(u) => { + uses.push(u); + } + ir::Statement::Function(span, fundef, funimpl) => { + let ident = fundef.name.clone(); + let path = AbsPath::new(namespace, &ident); + let _funid = self + .funs + .add(&path, (namespace.clone(), fundef, funimpl)) + .ok_or_else(|| CompilationError::DuplicateSymbol(span, ident))?; + () + } + ir::Statement::Struct(span, structdef) => { + let stru = StructDef { + name: structdef.name.unspan(), + fields: structdef.fields.into_iter().map(|v| v.unspan()).collect(), + }; + let name = stru.name.clone(); + let path = AbsPath::new(namespace, &name); + self.constrs + .add(&path, ConstrDef::Struct(stru)) + .ok_or_else(|| CompilationError::DuplicateSymbol(span, name))?; + } + ir::Statement::Expr(_) => (), + } + } + + if self + .namespaces + .insert( + namespace.clone(), + SymbolResolver::new(namespace.clone(), uses), + ) + .is_some() + { + return Err(CompilationError::NamespaceError( + symbols::NamespaceError::Duplicate(namespace.clone()), + )); + } + + Ok(()) + } + + /// Finalize compilation and return a CompilationUnit containing all the modules compiled in the state + pub fn finalize( + self, + environ: &mut Environment, + ) -> Result, CompilationError> { + let SymbolsTableData { table, vecdata } = self.funs; + + for (p, _id) in table.to_vec(Namespace::root()) { + std::println!("{:?}", p) + } + + let mut root_bindings = GlobalBindings::new(); + + for (path, id) in environ.symbols.to_vec(Namespace::root()) { + root_bindings + .add(path.clone(), BindingType::Nif(id)) + .map_err(|()| { + CompilationError::DuplicateSymbolEnv(String::from("NIF"), path.clone()) + .context(format!("adding NIF")) + })? + } + + for (path, id) in environ.globals.to_vec(Namespace::root()) { + root_bindings + .add(path.clone(), BindingType::Global(id)) + .map_err(|()| { + CompilationError::DuplicateSymbolEnv(String::from("global"), path.clone()) + })? + } + + for (path, fun_id) in table.to_vec(Namespace::root()) { + root_bindings + .add(path.clone(), BindingType::Fun(fun_id)) + .map_err(|()| { + CompilationError::DuplicateSymbolEnv(String::from("Fun"), path.clone()) + })? + } + + // all modules share this compilation state + let shared = CompilationSharedState {}; + + let mut state = compile::CodeBuilder::new( + &shared, + self.params, + table, + IdVecAfter::new(vecdata.next_id()), + root_bindings, + ); + + for (funid, (namespace, fundef, funimpl)) in vecdata.into_iter() { + let Some(uses) = self.namespaces.get(&namespace) else { + panic!("internal error: namespace not defined"); + }; + state.set_module_resolver(uses); + + let fun_name = fundef.name.clone(); + let lirdef = compile::generate_func_code(&mut state, &namespace, Some(fundef), funimpl) + .map_err(|e| { + e.context(format!( + "namespace {:?} function code {:?}", + namespace, fun_name + )) + })?; + let lirid = state.funs_vec.push(lirdef); + assert_eq!(funid, lirid) + } + + // merge the lambdas vec with the main fun vec + state.funs_vec.concat(&mut state.lambdas_vec); + + Ok(CompilationUnit { + lits: state.lits.finalize(), + constrs: state.constrs, + funs: state.funs_vec, + funs_tbl: state.funs_tbl, + code: state.main_code.finalize(), + }) + } +} diff --git a/werbolg-compile/src/resolver.rs b/werbolg-compile/src/resolver.rs index daf1840..c4f9a93 100644 --- a/werbolg-compile/src/resolver.rs +++ b/werbolg-compile/src/resolver.rs @@ -4,6 +4,7 @@ use werbolg_core::Namespace; /// Symbol Resolver #[derive(Clone)] pub struct SymbolResolver { + #[allow(unused)] pub(crate) current: Namespace, #[allow(unused)] pub(crate) uses: Vec, diff --git a/werbolg-compile/src/symbols.rs b/werbolg-compile/src/symbols.rs index e953a02..e1a6d26 100644 --- a/werbolg-compile/src/symbols.rs +++ b/werbolg-compile/src/symbols.rs @@ -1,3 +1,4 @@ +use crate::hier::Hier; use alloc::vec::Vec; use core::hash::Hash; use core::marker::PhantomData; @@ -15,6 +16,12 @@ pub struct SymbolsTableFlat { phantom: PhantomData, } +impl Default for SymbolsTableFlat { + fn default() -> Self { + Self::new() + } +} + impl SymbolsTableFlat { pub fn new() -> Self { Self { @@ -36,145 +43,73 @@ impl SymbolsTableFlat { } } -pub struct SymbolsTable { - pub(crate) current: SymbolsTableFlat, - pub(crate) ns: HashMap>, -} +pub struct SymbolsTable(pub(crate) Hier>); #[derive(Clone, Debug)] pub enum NamespaceError { /// Duplicate namespace found Duplicate(Namespace), /// Duplicate namespace found - DuplicateLeaf(Namespace, Ident), + DuplicateLeaf(Namespace), /// Missing namespace Missing(Namespace, Ident), } impl SymbolsTable { pub fn new() -> Self { - Self { - current: SymbolsTableFlat::new(), - ns: HashMap::new(), - } + Self(Hier::default()) } + /* fn create_namespace_here(&mut self, ident: Ident) -> Result<(), ()> { - let already_exist = self.ns.insert(ident, SymbolsTable::new()); - if already_exist.is_some() { - Err(()) - } else { - Ok(()) - } + self.0.add_ns(ident, SymbolsTableFlat::new()) } - fn flat_table(&self, namespace: &Namespace) -> Option<&Self> { - let mut current = self; - for n in namespace.iter() { - if let Some(child) = current.ns.get(n) { - current = child; - } else { - return None; - } - } - Some(current) + fn flat_table(&self, namespace: &Namespace) -> Result<&SymbolsTableFlat, ()> { + self.0.get(namespace) } + */ - fn flat_table_mut(&mut self, namespace: &Namespace) -> Option<&mut SymbolsTableFlat> { - if namespace.is_root() { - return Some(&mut self.current); - } else { - let (id, child_ns) = namespace.clone().drop_first(); - if let Some(x) = self.ns.get_mut(&id) { - x.flat_table_mut(&child_ns) - } else { - panic!("flat-table-mut oops") - } - } - /* - } else { - let mut i = namespace.iter_with_last().collect::>()(); - let (is_last, ns) = i.next(); - if is_last { - self.ns.get_mut(ns).map(|x| &mut x.current) - } else { - } - self.ns[] - for (is_last, n) in namespace.iter_with_last() { - if let Some(child) = current.ns.get(n) { - current = child; - } else { - return None; - } - } - } - */ + fn on_flat_table_mut(&mut self, namespace: &Namespace, f: F) -> Result<(), ()> + where + F: FnMut(&mut SymbolsTableFlat) -> (), + { + self.0.on_mut(namespace, f) } pub fn create_namespace(&mut self, namespace: Namespace) -> Result<(), NamespaceError> { - if namespace.is_root() { - return Ok(()); - } - let mut current = self; - for (is_last, n) in namespace.iter_with_last() { - if is_last { - current - .create_namespace_here(n.clone()) - .map_err(|()| NamespaceError::DuplicateLeaf(namespace.clone(), n.clone()))? - } else { - if let Some(child) = current.ns.get_mut(n) { - current = child; - } else { - return Err(NamespaceError::Missing(namespace.clone(), n.clone())); - } - } - } - Ok(()) + self.0 + .add_ns_hier(namespace.clone()) + .map_err(|()| NamespaceError::DuplicateLeaf(namespace)) } pub fn insert(&mut self, path: &AbsPath, id: ID) { let (namespace, ident) = path.split(); - if let Some(table) = self.flat_table_mut(&namespace) { - table.insert(ident, id) + if let Ok(()) = self.on_flat_table_mut(&namespace, |table| table.insert(ident.clone(), id)) + { + () } else { panic!("unknown namespace {:?}", namespace); } } - pub fn get_in(&self, path: &AbsPath) -> Option { - let mut table = self; - for (is_final, fragment) in path.components() { - if is_final { - return table.current.get(fragment); - } else { - if let Some(child_table) = self.ns.get(fragment) { - table = child_table; - } else { - panic!("unknown namespace {:?} from path {:?}", fragment, path) - } - } - } - return None; - } - pub fn get(&self, path: &AbsPath) -> Option { let (namespace, ident) = path.split(); - if namespace.is_root() { - self.current.get(&ident) + if let Ok(tbl) = self.0.get(&namespace) { + tbl.get(&ident) } else { - let t = self.flat_table(&namespace); - t.and_then(|x| x.current.get(&ident)) + None } } fn dump_path(&self, current: Namespace, vec: &mut Vec<(AbsPath, ID)>) { - for (ident, id) in self.current.iter() { - let path = AbsPath::new(¤t, ident); - vec.push((path, id)) - } - for (ns_name, st) in self.ns.iter() { - let child_namespace = current.clone().append(ns_name.clone()); - st.dump_path(child_namespace, vec) + let mut ts = Vec::new(); + self.0.dump(current, &mut ts); + for (n, t) in ts.iter() { + for (ident, id) in t.iter() { + let path = AbsPath::new(n, ident); + vec.push((path, id)) + } } } @@ -204,7 +139,7 @@ impl SymbolsTableData { } pub fn add(&mut self, path: &AbsPath, v: T) -> Option { - if self.table.get_in(&path).is_some() { + if self.table.get(&path).is_some() { return None; } let id = self.vecdata.push(v); @@ -222,6 +157,10 @@ impl SymbolsTableData { .map(|constr_id| (constr_id, &self.vecdata[constr_id])) } + pub fn get_by_id(&self, id: ID) -> Option<&T> { + Some(&self.vecdata[id]) + } + pub fn to_vec(&self, current: Namespace) -> Vec<(AbsPath, ID)> { self.table.to_vec(current) } diff --git a/werbolg-core/src/basic.rs b/werbolg-core/src/basic.rs index f21fd4b..c9769b1 100644 --- a/werbolg-core/src/basic.rs +++ b/werbolg-core/src/basic.rs @@ -56,9 +56,9 @@ impl core::fmt::Debug for Path { PathType::Absolute => write!(f, "::")?, PathType::Relative => {} } - for (is_final, component) in self.components() { + for (component, remaining) in self.components() { write!(f, "{:?}", component)?; - if !is_final { + if !remaining.is_empty() { write!(f, "::")? } } @@ -67,12 +67,14 @@ impl core::fmt::Debug for Path { } impl Path { + /* /// Split a path to a namespace and an ident pub fn split(&self) -> (Namespace, Ident) { let mut x = self.1.clone(); let ident = x.pop().unwrap(); (Namespace(x), ident) } + */ /// Return the path type of this path pub fn path_type(&self) -> PathType { @@ -132,6 +134,7 @@ impl Path { self } + /* /// Iterate over all components of the path, associate whether the element is final (i.e. a leaf) pub fn components(&self) -> impl Iterator { self.1 @@ -139,6 +142,42 @@ impl Path { .enumerate() .map(|(i, id)| (i + 1 == self.1.len(), id)) } + */ + + /// Iterate over all components of the path, associate whether the element is final (i.e. a leaf) + pub fn components(&self) -> impl Iterator { + PathDecomposer::new(self) + } + + /// Decompose a path into its components in a Vec + pub fn idents(&self) -> Vec { + self.1.clone() + } +} + +/// Path decomposer Iterator +pub struct PathDecomposer<'a> { + dropped: usize, + path: &'a Path, +} + +impl<'a> PathDecomposer<'a> { + /// Create a new path decomposer using the given path + pub fn new(path: &'a Path) -> Self { + Self { dropped: 0, path } + } +} + +impl<'a> Iterator for PathDecomposer<'a> { + type Item = (&'a Ident, &'a [Ident]); + fn next(&mut self) -> Option { + if self.dropped == self.path.1.len() { + return None; + } + let rem = &self.path.1[self.dropped..]; + self.dropped += 1; + Some((&rem[0], &rem[1..])) + } } /// An absolute Path (e.g. namespace::function, or a.g.d) diff --git a/werbolg-tales/src/exec.rs b/werbolg-tales/src/exec.rs index 960aef5..2d531d3 100644 --- a/werbolg-tales/src/exec.rs +++ b/werbolg-tales/src/exec.rs @@ -76,11 +76,13 @@ pub fn run_compile( let exec_module = match compile(&compilation_params, modules, env) { Err(e) => { - let report = Report::new(ReportKind::Error, format!("Compilation Error: {:?}", e)) - .lines_before(1) - .lines_after(1) - .highlight(e.span(), format!("compilation error here")); - report_print(&source, report)?; + if let Some(span) = e.span() { + let report = Report::new(ReportKind::Error, format!("Compilation Error: {:?}", e)) + .lines_before(1) + .lines_after(1) + .highlight(span, format!("compilation error here")); + report_print(&source, report)?; + } return Err(format!("compilation error {:?}", e).into()); } Ok(m) => m,