Skip to content

Commit 41667e5

Browse files
committed
initial scoped module implementation
1 parent 5595139 commit 41667e5

File tree

8 files changed

+97
-59
lines changed

8 files changed

+97
-59
lines changed

src/assembly.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use serde::*;
66

77
use crate::{
88
is_ident_char, CodeSpan, DynamicFunction, FuncSlice, Function, Ident, ImplPrimitive, InputSrc,
9-
Instr, IntoInputSrc, LocalName, Primitive, Signature, Span, StackSwizzle, TempStack, Uiua,
10-
UiuaResult, Value,
9+
Instr, IntoInputSrc, LocalName, Module, Primitive, Signature, Span, StackSwizzle, TempStack,
10+
Uiua, UiuaResult, Value,
1111
};
1212

1313
/// A compiled Uiua assembly
@@ -325,8 +325,10 @@ pub enum BindingKind {
325325
Const(Option<Value>),
326326
/// A function
327327
Func(Function),
328-
/// A module
329-
Module(PathBuf),
328+
/// An imported module
329+
Import(PathBuf),
330+
/// A scoped module
331+
Module(Module),
330332
/// A macro
331333
Macro,
332334
}
@@ -337,7 +339,8 @@ impl BindingKind {
337339
match self {
338340
Self::Const(_) => Some(Signature::new(0, 1)),
339341
Self::Func(func) => Some(func.signature()),
340-
Self::Module { .. } => None,
342+
Self::Import { .. } => None,
343+
Self::Module(_) => None,
341344
Self::Macro => None,
342345
}
343346
}

src/ast.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,21 @@ impl Binding {
5454
/// A scoped module
5555
#[derive(Debug, Clone)]
5656
pub struct ScopedModule {
57-
/// The name of the scope
58-
pub name: Option<Sp<Ident>>,
57+
/// The module kind
58+
pub kind: ModuleKind,
5959
/// The items
6060
pub items: Vec<Item>,
6161
}
6262

63+
/// The kind of a module
64+
#[derive(Debug, Clone)]
65+
pub enum ModuleKind {
66+
/// A named module
67+
Named(Sp<Ident>),
68+
/// A test scope
69+
Test,
70+
}
71+
6372
/// An import
6473
#[derive(Debug, Clone)]
6574
pub struct Import {

src/compile/binding.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ impl Compiler {
412412
};
413413
self.asm.add_global_at(
414414
local,
415-
BindingKind::Module(module_path.clone()),
415+
BindingKind::Import(module_path.clone()),
416416
Some(name.span.clone()),
417417
prev_com
418418
.or_else(|| imported.comment.clone())

src/compile/mod.rs

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use std::{
1818
use ecow::{eco_vec, EcoString, EcoVec};
1919
use indexmap::IndexMap;
2020
use instant::Duration;
21+
use serde::{Deserialize, Serialize};
2122

2223
use crate::{
2324
algorithm::invert::{invert_instrs, under_instrs},
@@ -55,7 +56,7 @@ pub struct Compiler {
5556
/// The paths of files currently being imported (used to detect import cycles)
5657
current_imports: Vec<PathBuf>,
5758
/// The bindings of imported files
58-
imports: HashMap<PathBuf, Import>,
59+
imports: HashMap<PathBuf, Module>,
5960
/// Unexpanded stack macros
6061
stack_macros: HashMap<usize, StackMacro>,
6162
/// Unexpanded array macros
@@ -108,14 +109,14 @@ impl Default for Compiler {
108109
}
109110
}
110111

111-
/// An imported module
112-
#[derive(Clone)]
113-
pub struct Import {
112+
/// A Uiua module
113+
#[derive(Debug, Clone, Serialize, Deserialize)]
114+
pub struct Module {
114115
/// The top level comment
115116
pub comment: Option<EcoString>,
116117
/// Map module-local names to global indices
117118
names: IndexMap<Ident, LocalName>,
118-
/// Whether the import uses experimental features
119+
/// Whether the module uses experimental features
119120
experimental: bool,
120121
}
121122

@@ -173,7 +174,9 @@ pub(crate) struct Scope {
173174

174175
#[derive(Clone, Copy, PartialEq, Eq)]
175176
enum ScopeKind {
176-
/// A scope at the top level of a file or in a named module
177+
/// A scope at the top level of a file
178+
File,
179+
/// A scope in a named module
177180
Module,
178181
/// A temporary scope, probably for a macro
179182
Temp,
@@ -184,7 +187,7 @@ enum ScopeKind {
184187
impl Default for Scope {
185188
fn default() -> Self {
186189
Self {
187-
kind: ScopeKind::Module,
190+
kind: ScopeKind::File,
188191
file_path: None,
189192
comment: None,
190193
names: IndexMap::new(),
@@ -197,7 +200,7 @@ impl Default for Scope {
197200
}
198201

199202
/// The index of a named local in the bindings, and whether it is public
200-
#[derive(Clone, Copy)]
203+
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
201204
pub(crate) struct LocalName {
202205
pub index: usize,
203206
pub public: bool,
@@ -351,15 +354,15 @@ impl Compiler {
351354
&mut self,
352355
kind: ScopeKind,
353356
f: impl FnOnce(&mut Self) -> UiuaResult<T>,
354-
) -> UiuaResult<Import> {
357+
) -> UiuaResult<Module> {
355358
let experimental = self.scope.experimental;
356359
self.higher_scopes.push(take(&mut self.scope));
357360
self.scope.kind = kind;
358361
self.scope.experimental = experimental;
359362
let res = f(self);
360363
let scope = replace(&mut self.scope, self.higher_scopes.pop().unwrap());
361364
res?;
362-
Ok(Import {
365+
Ok(Module {
363366
comment: scope.comment,
364367
names: scope.names,
365368
experimental: scope.experimental,
@@ -493,17 +496,9 @@ code:
493496
(words.iter()).any(|w| matches!(&w.value, Word::SemanticComment(_)))
494497
}
495498
let mut lines = match item {
496-
Item::Module(module) => {
497-
prev_comment.take();
498-
let is_test =
499-
(module.value.name.as_ref()).map_or(true, |name| name.value == "test");
500-
let scope_kind = if is_test {
501-
ScopeKind::Test
502-
} else {
503-
ScopeKind::Module
504-
};
505-
self.in_scope(scope_kind, |env| env.items(module.value.items, true))?;
506-
return Ok(());
499+
Item::Module(m) => {
500+
let prev_com = prev_comment.take();
501+
return self.module(m, prev_com);
507502
}
508503
Item::Words(lines) => lines,
509504
Item::Binding(binding) => {
@@ -777,13 +772,13 @@ code:
777772
format!("Cycle detected importing {}", path.to_string_lossy()),
778773
));
779774
}
780-
let import = self.in_scope(ScopeKind::Module, |env| {
775+
let import = self.in_scope(ScopeKind::File, |env| {
781776
env.load_str_src(&input, &path).map(drop)
782777
})?;
783778
self.imports.insert(path.clone(), import);
784779
}
785-
let import = self.imports.get(&path).unwrap();
786-
if import.experimental {
780+
let module = self.imports.get(&path).unwrap();
781+
if module.experimental {
787782
self.experimental_error(span, || {
788783
format!(
789784
"Module `{path_str}` is experimental. \
@@ -811,6 +806,37 @@ code:
811806
pathdiff::diff_paths(&target, base).unwrap_or(target)
812807
}
813808
}
809+
fn module(&mut self, m: Sp<ScopedModule>, prev_com: Option<EcoString>) -> UiuaResult {
810+
let m = m.value;
811+
let scope_kind = match m.kind {
812+
ModuleKind::Named(_) => ScopeKind::Module,
813+
ModuleKind::Test => ScopeKind::Test,
814+
};
815+
let in_test = matches!(m.kind, ModuleKind::Test);
816+
let module = self.in_scope(scope_kind, |env| env.items(m.items, in_test))?;
817+
match m.kind {
818+
ModuleKind::Named(name) => {
819+
let global_index = self.next_global;
820+
self.next_global += 1;
821+
let local = LocalName {
822+
index: global_index,
823+
public: true,
824+
};
825+
let comment = prev_com
826+
.or_else(|| module.comment.clone())
827+
.map(|text| DocComment::from(text.as_str()));
828+
self.asm.add_global_at(
829+
local,
830+
BindingKind::Module(module),
831+
Some(name.span.clone()),
832+
comment,
833+
);
834+
self.scope.names.insert(name.value.clone(), local);
835+
}
836+
ModuleKind::Test => {}
837+
}
838+
Ok(())
839+
}
814840
fn compile_words(&mut self, words: Vec<Sp<Word>>, call: bool) -> UiuaResult<EcoVec<Instr>> {
815841
let words = unsplit_words(split_words(words))
816842
.into_iter()
@@ -1482,16 +1508,12 @@ code:
14821508
}
14831509
fn ref_local(&self, r: &Ref) -> UiuaResult<(Vec<LocalName>, LocalName)> {
14841510
if let Some((module, path_locals)) = self.ref_path(&r.path, r.in_macro_arg)? {
1485-
if let Some(local) = self.imports[&module].names.get(&r.name.value).copied() {
1511+
if let Some(local) = module.names.get(&r.name.value).copied() {
14861512
Ok((path_locals, local))
14871513
} else {
14881514
Err(self.fatal_error(
14891515
r.name.span.clone(),
1490-
format!(
1491-
"Item `{}` not found in module `{}`",
1492-
r.name.value,
1493-
module.display()
1494-
),
1516+
format!("Item `{}` not found", r.name.value,),
14951517
))
14961518
}
14971519
} else if let Some(local) = self.find_name(&r.name.value, r.in_macro_arg) {
@@ -1511,8 +1533,8 @@ code:
15111533
}
15121534
let mut hit_file = false;
15131535
for scope in self.higher_scopes.iter().rev() {
1514-
if scope.kind == ScopeKind::Module {
1515-
if hit_file || self.scope.kind == ScopeKind::Module {
1536+
if scope.kind == ScopeKind::File {
1537+
if hit_file || self.scope.kind == ScopeKind::File {
15161538
break;
15171539
}
15181540
hit_file = true;
@@ -1527,7 +1549,7 @@ code:
15271549
&self,
15281550
path: &[RefComponent],
15291551
skip_local: bool,
1530-
) -> UiuaResult<Option<(PathBuf, Vec<LocalName>)>> {
1552+
) -> UiuaResult<Option<(&Module, Vec<LocalName>)>> {
15311553
let Some(first) = path.first() else {
15321554
return Ok(None);
15331555
};
@@ -1543,6 +1565,7 @@ code:
15431565
path_locals.push(module_local);
15441566
let global = &self.asm.bindings[module_local.index].kind;
15451567
let mut module = match global {
1568+
BindingKind::Import(path) => &self.imports[path],
15461569
BindingKind::Module(module) => module,
15471570
BindingKind::Func(_) => {
15481571
return Err(self.fatal_error(
@@ -1564,23 +1587,20 @@ code:
15641587
}
15651588
};
15661589
for comp in path.iter().skip(1) {
1567-
let submod_local = self.imports[module]
1590+
let submod_local = module
15681591
.names
15691592
.get(&comp.module.value)
15701593
.copied()
15711594
.ok_or_else(|| {
15721595
self.fatal_error(
15731596
comp.module.span.clone(),
1574-
format!(
1575-
"Module `{}` not found in module `{}`",
1576-
comp.module.value,
1577-
module.display()
1578-
),
1597+
format!("Module `{}` not found", comp.module.value),
15791598
)
15801599
})?;
15811600
path_locals.push(submod_local);
15821601
let global = &self.asm.bindings[submod_local.index].kind;
15831602
module = match global {
1603+
BindingKind::Import(path) => &self.imports[path],
15841604
BindingKind::Module(module) => module,
15851605
BindingKind::Func(_) => {
15861606
return Err(self.fatal_error(
@@ -1603,7 +1623,7 @@ code:
16031623
};
16041624
}
16051625

1606-
Ok(Some((module.clone(), path_locals)))
1626+
Ok(Some((module, path_locals)))
16071627
}
16081628
fn reference(&mut self, r: Ref, call: bool) -> UiuaResult {
16091629
if r.path.is_empty() {
@@ -1708,7 +1728,9 @@ code:
17081728
self.push_instr(Instr::Call(span));
17091729
}
17101730
}
1711-
BindingKind::Module { .. } => self.add_error(span, "Cannot import module item here."),
1731+
BindingKind::Import { .. } | BindingKind::Module(_) => {
1732+
self.add_error(span, "Cannot import module item here.")
1733+
}
17121734
BindingKind::Macro => {
17131735
// We could error here, but it's easier to handle it higher up
17141736
}

src/format.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ impl<'a> Formatter<'a> {
589589
Item::Module(m) => {
590590
self.prev_import_function = None;
591591
self.output.push_str("---");
592-
if let Some(name) = &m.value.name {
592+
if let ModuleKind::Named(name) = &m.value.kind {
593593
self.push(&name.span, &name.value);
594594
}
595595
self.format_items(&m.value.items, depth + 1);

src/lsp.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ impl Spanner {
265265
if comment.is_none() {
266266
match &binfo.kind {
267267
BindingKind::Const(None) => comment = Some("constant".into()),
268-
BindingKind::Module { .. } => comment = Some("module".into()),
268+
BindingKind::Import(_) => comment = Some("module".into()),
269269
BindingKind::Macro => comment = Some("macro".into()),
270270
_ => {}
271271
}
@@ -289,7 +289,7 @@ impl Spanner {
289289
BindingKind::Macro => {
290290
BindingDocsKind::Modifier(binfo.span.as_str(self.inputs(), ident_modifier_args))
291291
}
292-
BindingKind::Module { .. } => BindingDocsKind::Module,
292+
BindingKind::Import(_) | BindingKind::Module(_) => BindingDocsKind::Module,
293293
};
294294
BindingDocs {
295295
src_span: binfo.span.clone(),
@@ -894,7 +894,7 @@ mod server {
894894
BindingKind::Const(_) => CompletionItemKind::CONSTANT,
895895
BindingKind::Func(_) => CompletionItemKind::FUNCTION,
896896
BindingKind::Macro => CompletionItemKind::FUNCTION,
897-
BindingKind::Module { .. } => CompletionItemKind::MODULE,
897+
BindingKind::Import(_) | BindingKind::Module(_) => CompletionItemKind::MODULE,
898898
};
899899
CompletionItem {
900900
label: name.clone(),
@@ -932,7 +932,7 @@ mod server {
932932
span.end.line as usize == line && span.end.col as usize == col
933933
})
934934
{
935-
if let BindingKind::Module(module) = &doc.asm.bindings[*index].kind {
935+
if let BindingKind::Import(module) = &doc.asm.bindings[*index].kind {
936936
let mut completions = Vec::new();
937937
let mut span = span.clone();
938938
span.start = span.end;
@@ -1018,7 +1018,7 @@ mod server {
10181018
continue;
10191019
};
10201020

1021-
if let BindingKind::Module(module) = &binding.kind {
1021+
if let BindingKind::Import(module) = &binding.kind {
10221022
for binding in self.bindings_in_file(doc_uri, module) {
10231023
if !binding.public {
10241024
continue;

src/parse.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,12 @@ impl<'i> Parser<'i> {
323323
let backup = self.index;
324324
let start = self.try_exact(TripleMinus.into())?;
325325
self.try_spaces();
326-
let name = self.try_ident();
327-
if in_scope && name.is_none() {
326+
let kind = match self.try_ident() {
327+
Some(name) if name.value == "test" => ModuleKind::Test,
328+
Some(name) => ModuleKind::Named(name),
329+
None => ModuleKind::Test,
330+
};
331+
if in_scope && matches!(kind, ModuleKind::Test) {
328332
self.index = backup;
329333
return None;
330334
}
@@ -335,7 +339,7 @@ impl<'i> Parser<'i> {
335339
self.errors.push(self.expected([TripleMinus]));
336340
start
337341
};
338-
let module = ScopedModule { name, items };
342+
let module = ScopedModule { kind, items };
339343
Item::Module(span.sp(module))
340344
}
341345
})

0 commit comments

Comments
 (0)