Skip to content

Commit

Permalink
initial scoped module implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
kaikalii committed Jun 28, 2024
1 parent 5595139 commit 41667e5
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 59 deletions.
13 changes: 8 additions & 5 deletions src/assembly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use serde::*;

use crate::{
is_ident_char, CodeSpan, DynamicFunction, FuncSlice, Function, Ident, ImplPrimitive, InputSrc,
Instr, IntoInputSrc, LocalName, Primitive, Signature, Span, StackSwizzle, TempStack, Uiua,
UiuaResult, Value,
Instr, IntoInputSrc, LocalName, Module, Primitive, Signature, Span, StackSwizzle, TempStack,
Uiua, UiuaResult, Value,
};

/// A compiled Uiua assembly
Expand Down Expand Up @@ -325,8 +325,10 @@ pub enum BindingKind {
Const(Option<Value>),
/// A function
Func(Function),
/// A module
Module(PathBuf),
/// An imported module
Import(PathBuf),
/// A scoped module
Module(Module),
/// A macro
Macro,
}
Expand All @@ -337,7 +339,8 @@ impl BindingKind {
match self {
Self::Const(_) => Some(Signature::new(0, 1)),
Self::Func(func) => Some(func.signature()),
Self::Module { .. } => None,
Self::Import { .. } => None,
Self::Module(_) => None,
Self::Macro => None,
}
}
Expand Down
13 changes: 11 additions & 2 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,21 @@ impl Binding {
/// A scoped module
#[derive(Debug, Clone)]
pub struct ScopedModule {
/// The name of the scope
pub name: Option<Sp<Ident>>,
/// The module kind
pub kind: ModuleKind,
/// The items
pub items: Vec<Item>,
}

/// The kind of a module
#[derive(Debug, Clone)]
pub enum ModuleKind {
/// A named module
Named(Sp<Ident>),
/// A test scope
Test,
}

/// An import
#[derive(Debug, Clone)]
pub struct Import {
Expand Down
2 changes: 1 addition & 1 deletion src/compile/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ impl Compiler {
};
self.asm.add_global_at(
local,
BindingKind::Module(module_path.clone()),
BindingKind::Import(module_path.clone()),
Some(name.span.clone()),
prev_com
.or_else(|| imported.comment.clone())
Expand Down
104 changes: 63 additions & 41 deletions src/compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use std::{
use ecow::{eco_vec, EcoString, EcoVec};
use indexmap::IndexMap;
use instant::Duration;
use serde::{Deserialize, Serialize};

use crate::{
algorithm::invert::{invert_instrs, under_instrs},
Expand Down Expand Up @@ -55,7 +56,7 @@ pub struct Compiler {
/// The paths of files currently being imported (used to detect import cycles)
current_imports: Vec<PathBuf>,
/// The bindings of imported files
imports: HashMap<PathBuf, Import>,
imports: HashMap<PathBuf, Module>,
/// Unexpanded stack macros
stack_macros: HashMap<usize, StackMacro>,
/// Unexpanded array macros
Expand Down Expand Up @@ -108,14 +109,14 @@ impl Default for Compiler {
}
}

/// An imported module
#[derive(Clone)]
pub struct Import {
/// A Uiua module
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Module {
/// The top level comment
pub comment: Option<EcoString>,
/// Map module-local names to global indices
names: IndexMap<Ident, LocalName>,
/// Whether the import uses experimental features
/// Whether the module uses experimental features
experimental: bool,
}

Expand Down Expand Up @@ -173,7 +174,9 @@ pub(crate) struct Scope {

#[derive(Clone, Copy, PartialEq, Eq)]
enum ScopeKind {
/// A scope at the top level of a file or in a named module
/// A scope at the top level of a file
File,
/// A scope in a named module
Module,
/// A temporary scope, probably for a macro
Temp,
Expand All @@ -184,7 +187,7 @@ enum ScopeKind {
impl Default for Scope {
fn default() -> Self {
Self {
kind: ScopeKind::Module,
kind: ScopeKind::File,
file_path: None,
comment: None,
names: IndexMap::new(),
Expand All @@ -197,7 +200,7 @@ impl Default for Scope {
}

/// The index of a named local in the bindings, and whether it is public
#[derive(Clone, Copy)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub(crate) struct LocalName {
pub index: usize,
pub public: bool,
Expand Down Expand Up @@ -351,15 +354,15 @@ impl Compiler {
&mut self,
kind: ScopeKind,
f: impl FnOnce(&mut Self) -> UiuaResult<T>,
) -> UiuaResult<Import> {
) -> UiuaResult<Module> {
let experimental = self.scope.experimental;
self.higher_scopes.push(take(&mut self.scope));
self.scope.kind = kind;
self.scope.experimental = experimental;
let res = f(self);
let scope = replace(&mut self.scope, self.higher_scopes.pop().unwrap());
res?;
Ok(Import {
Ok(Module {
comment: scope.comment,
names: scope.names,
experimental: scope.experimental,
Expand Down Expand Up @@ -493,17 +496,9 @@ code:
(words.iter()).any(|w| matches!(&w.value, Word::SemanticComment(_)))
}
let mut lines = match item {
Item::Module(module) => {
prev_comment.take();
let is_test =
(module.value.name.as_ref()).map_or(true, |name| name.value == "test");
let scope_kind = if is_test {
ScopeKind::Test
} else {
ScopeKind::Module
};
self.in_scope(scope_kind, |env| env.items(module.value.items, true))?;
return Ok(());
Item::Module(m) => {
let prev_com = prev_comment.take();
return self.module(m, prev_com);
}
Item::Words(lines) => lines,
Item::Binding(binding) => {
Expand Down Expand Up @@ -777,13 +772,13 @@ code:
format!("Cycle detected importing {}", path.to_string_lossy()),
));
}
let import = self.in_scope(ScopeKind::Module, |env| {
let import = self.in_scope(ScopeKind::File, |env| {
env.load_str_src(&input, &path).map(drop)
})?;
self.imports.insert(path.clone(), import);
}
let import = self.imports.get(&path).unwrap();
if import.experimental {
let module = self.imports.get(&path).unwrap();
if module.experimental {
self.experimental_error(span, || {
format!(
"Module `{path_str}` is experimental. \
Expand Down Expand Up @@ -811,6 +806,37 @@ code:
pathdiff::diff_paths(&target, base).unwrap_or(target)
}
}
fn module(&mut self, m: Sp<ScopedModule>, prev_com: Option<EcoString>) -> UiuaResult {
let m = m.value;
let scope_kind = match m.kind {
ModuleKind::Named(_) => ScopeKind::Module,
ModuleKind::Test => ScopeKind::Test,
};
let in_test = matches!(m.kind, ModuleKind::Test);
let module = self.in_scope(scope_kind, |env| env.items(m.items, in_test))?;
match m.kind {
ModuleKind::Named(name) => {
let global_index = self.next_global;
self.next_global += 1;
let local = LocalName {
index: global_index,
public: true,
};
let comment = prev_com
.or_else(|| module.comment.clone())
.map(|text| DocComment::from(text.as_str()));
self.asm.add_global_at(
local,
BindingKind::Module(module),
Some(name.span.clone()),
comment,
);
self.scope.names.insert(name.value.clone(), local);
}
ModuleKind::Test => {}
}
Ok(())
}
fn compile_words(&mut self, words: Vec<Sp<Word>>, call: bool) -> UiuaResult<EcoVec<Instr>> {
let words = unsplit_words(split_words(words))
.into_iter()
Expand Down Expand Up @@ -1482,16 +1508,12 @@ code:
}
fn ref_local(&self, r: &Ref) -> UiuaResult<(Vec<LocalName>, LocalName)> {
if let Some((module, path_locals)) = self.ref_path(&r.path, r.in_macro_arg)? {
if let Some(local) = self.imports[&module].names.get(&r.name.value).copied() {
if let Some(local) = module.names.get(&r.name.value).copied() {
Ok((path_locals, local))
} else {
Err(self.fatal_error(
r.name.span.clone(),
format!(
"Item `{}` not found in module `{}`",
r.name.value,
module.display()
),
format!("Item `{}` not found", r.name.value,),
))
}
} else if let Some(local) = self.find_name(&r.name.value, r.in_macro_arg) {
Expand All @@ -1511,8 +1533,8 @@ code:
}
let mut hit_file = false;
for scope in self.higher_scopes.iter().rev() {
if scope.kind == ScopeKind::Module {
if hit_file || self.scope.kind == ScopeKind::Module {
if scope.kind == ScopeKind::File {
if hit_file || self.scope.kind == ScopeKind::File {
break;
}
hit_file = true;
Expand All @@ -1527,7 +1549,7 @@ code:
&self,
path: &[RefComponent],
skip_local: bool,
) -> UiuaResult<Option<(PathBuf, Vec<LocalName>)>> {
) -> UiuaResult<Option<(&Module, Vec<LocalName>)>> {
let Some(first) = path.first() else {
return Ok(None);
};
Expand All @@ -1543,6 +1565,7 @@ code:
path_locals.push(module_local);
let global = &self.asm.bindings[module_local.index].kind;
let mut module = match global {
BindingKind::Import(path) => &self.imports[path],
BindingKind::Module(module) => module,
BindingKind::Func(_) => {
return Err(self.fatal_error(
Expand All @@ -1564,23 +1587,20 @@ code:
}
};
for comp in path.iter().skip(1) {
let submod_local = self.imports[module]
let submod_local = module
.names
.get(&comp.module.value)
.copied()
.ok_or_else(|| {
self.fatal_error(
comp.module.span.clone(),
format!(
"Module `{}` not found in module `{}`",
comp.module.value,
module.display()
),
format!("Module `{}` not found", comp.module.value),
)
})?;
path_locals.push(submod_local);
let global = &self.asm.bindings[submod_local.index].kind;
module = match global {
BindingKind::Import(path) => &self.imports[path],
BindingKind::Module(module) => module,
BindingKind::Func(_) => {
return Err(self.fatal_error(
Expand All @@ -1603,7 +1623,7 @@ code:
};
}

Ok(Some((module.clone(), path_locals)))
Ok(Some((module, path_locals)))
}
fn reference(&mut self, r: Ref, call: bool) -> UiuaResult {
if r.path.is_empty() {
Expand Down Expand Up @@ -1708,7 +1728,9 @@ code:
self.push_instr(Instr::Call(span));
}
}
BindingKind::Module { .. } => self.add_error(span, "Cannot import module item here."),
BindingKind::Import { .. } | BindingKind::Module(_) => {
self.add_error(span, "Cannot import module item here.")
}
BindingKind::Macro => {
// We could error here, but it's easier to handle it higher up
}
Expand Down
2 changes: 1 addition & 1 deletion src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ impl<'a> Formatter<'a> {
Item::Module(m) => {
self.prev_import_function = None;
self.output.push_str("---");
if let Some(name) = &m.value.name {
if let ModuleKind::Named(name) = &m.value.kind {
self.push(&name.span, &name.value);
}
self.format_items(&m.value.items, depth + 1);
Expand Down
10 changes: 5 additions & 5 deletions src/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ impl Spanner {
if comment.is_none() {
match &binfo.kind {
BindingKind::Const(None) => comment = Some("constant".into()),
BindingKind::Module { .. } => comment = Some("module".into()),
BindingKind::Import(_) => comment = Some("module".into()),
BindingKind::Macro => comment = Some("macro".into()),
_ => {}
}
Expand All @@ -289,7 +289,7 @@ impl Spanner {
BindingKind::Macro => {
BindingDocsKind::Modifier(binfo.span.as_str(self.inputs(), ident_modifier_args))
}
BindingKind::Module { .. } => BindingDocsKind::Module,
BindingKind::Import(_) | BindingKind::Module(_) => BindingDocsKind::Module,
};
BindingDocs {
src_span: binfo.span.clone(),
Expand Down Expand Up @@ -894,7 +894,7 @@ mod server {
BindingKind::Const(_) => CompletionItemKind::CONSTANT,
BindingKind::Func(_) => CompletionItemKind::FUNCTION,
BindingKind::Macro => CompletionItemKind::FUNCTION,
BindingKind::Module { .. } => CompletionItemKind::MODULE,
BindingKind::Import(_) | BindingKind::Module(_) => CompletionItemKind::MODULE,
};
CompletionItem {
label: name.clone(),
Expand Down Expand Up @@ -932,7 +932,7 @@ mod server {
span.end.line as usize == line && span.end.col as usize == col
})
{
if let BindingKind::Module(module) = &doc.asm.bindings[*index].kind {
if let BindingKind::Import(module) = &doc.asm.bindings[*index].kind {
let mut completions = Vec::new();
let mut span = span.clone();
span.start = span.end;
Expand Down Expand Up @@ -1018,7 +1018,7 @@ mod server {
continue;
};

if let BindingKind::Module(module) = &binding.kind {
if let BindingKind::Import(module) = &binding.kind {
for binding in self.bindings_in_file(doc_uri, module) {
if !binding.public {
continue;
Expand Down
10 changes: 7 additions & 3 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,12 @@ impl<'i> Parser<'i> {
let backup = self.index;
let start = self.try_exact(TripleMinus.into())?;
self.try_spaces();
let name = self.try_ident();
if in_scope && name.is_none() {
let kind = match self.try_ident() {
Some(name) if name.value == "test" => ModuleKind::Test,
Some(name) => ModuleKind::Named(name),
None => ModuleKind::Test,
};
if in_scope && matches!(kind, ModuleKind::Test) {
self.index = backup;
return None;
}
Expand All @@ -335,7 +339,7 @@ impl<'i> Parser<'i> {
self.errors.push(self.expected([TripleMinus]));
start
};
let module = ScopedModule { name, items };
let module = ScopedModule { kind, items };
Item::Module(span.sp(module))
}
})
Expand Down
Loading

0 comments on commit 41667e5

Please sign in to comment.