@@ -18,6 +18,7 @@ use std::{
18
18
use ecow:: { eco_vec, EcoString , EcoVec } ;
19
19
use indexmap:: IndexMap ;
20
20
use instant:: Duration ;
21
+ use serde:: { Deserialize , Serialize } ;
21
22
22
23
use crate :: {
23
24
algorithm:: invert:: { invert_instrs, under_instrs} ,
@@ -55,7 +56,7 @@ pub struct Compiler {
55
56
/// The paths of files currently being imported (used to detect import cycles)
56
57
current_imports : Vec < PathBuf > ,
57
58
/// The bindings of imported files
58
- imports : HashMap < PathBuf , Import > ,
59
+ imports : HashMap < PathBuf , Module > ,
59
60
/// Unexpanded stack macros
60
61
stack_macros : HashMap < usize , StackMacro > ,
61
62
/// Unexpanded array macros
@@ -108,14 +109,14 @@ impl Default for Compiler {
108
109
}
109
110
}
110
111
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 {
114
115
/// The top level comment
115
116
pub comment : Option < EcoString > ,
116
117
/// Map module-local names to global indices
117
118
names : IndexMap < Ident , LocalName > ,
118
- /// Whether the import uses experimental features
119
+ /// Whether the module uses experimental features
119
120
experimental : bool ,
120
121
}
121
122
@@ -173,7 +174,9 @@ pub(crate) struct Scope {
173
174
174
175
#[ derive( Clone , Copy , PartialEq , Eq ) ]
175
176
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
177
180
Module ,
178
181
/// A temporary scope, probably for a macro
179
182
Temp ,
@@ -184,7 +187,7 @@ enum ScopeKind {
184
187
impl Default for Scope {
185
188
fn default ( ) -> Self {
186
189
Self {
187
- kind : ScopeKind :: Module ,
190
+ kind : ScopeKind :: File ,
188
191
file_path : None ,
189
192
comment : None ,
190
193
names : IndexMap :: new ( ) ,
@@ -197,7 +200,7 @@ impl Default for Scope {
197
200
}
198
201
199
202
/// 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 ) ]
201
204
pub ( crate ) struct LocalName {
202
205
pub index : usize ,
203
206
pub public : bool ,
@@ -351,15 +354,15 @@ impl Compiler {
351
354
& mut self ,
352
355
kind : ScopeKind ,
353
356
f : impl FnOnce ( & mut Self ) -> UiuaResult < T > ,
354
- ) -> UiuaResult < Import > {
357
+ ) -> UiuaResult < Module > {
355
358
let experimental = self . scope . experimental ;
356
359
self . higher_scopes . push ( take ( & mut self . scope ) ) ;
357
360
self . scope . kind = kind;
358
361
self . scope . experimental = experimental;
359
362
let res = f ( self ) ;
360
363
let scope = replace ( & mut self . scope , self . higher_scopes . pop ( ) . unwrap ( ) ) ;
361
364
res?;
362
- Ok ( Import {
365
+ Ok ( Module {
363
366
comment : scope. comment ,
364
367
names : scope. names ,
365
368
experimental : scope. experimental ,
@@ -493,17 +496,9 @@ code:
493
496
( words. iter ( ) ) . any ( |w| matches ! ( & w. value, Word :: SemanticComment ( _) ) )
494
497
}
495
498
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) ;
507
502
}
508
503
Item :: Words ( lines) => lines,
509
504
Item :: Binding ( binding) => {
@@ -777,13 +772,13 @@ code:
777
772
format ! ( "Cycle detected importing {}" , path. to_string_lossy( ) ) ,
778
773
) ) ;
779
774
}
780
- let import = self . in_scope ( ScopeKind :: Module , |env| {
775
+ let import = self . in_scope ( ScopeKind :: File , |env| {
781
776
env. load_str_src ( & input, & path) . map ( drop)
782
777
} ) ?;
783
778
self . imports . insert ( path. clone ( ) , import) ;
784
779
}
785
- let import = self . imports . get ( & path) . unwrap ( ) ;
786
- if import . experimental {
780
+ let module = self . imports . get ( & path) . unwrap ( ) ;
781
+ if module . experimental {
787
782
self . experimental_error ( span, || {
788
783
format ! (
789
784
"Module `{path_str}` is experimental. \
@@ -811,6 +806,37 @@ code:
811
806
pathdiff:: diff_paths ( & target, base) . unwrap_or ( target)
812
807
}
813
808
}
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
+ }
814
840
fn compile_words ( & mut self , words : Vec < Sp < Word > > , call : bool ) -> UiuaResult < EcoVec < Instr > > {
815
841
let words = unsplit_words ( split_words ( words) )
816
842
. into_iter ( )
@@ -1482,16 +1508,12 @@ code:
1482
1508
}
1483
1509
fn ref_local ( & self , r : & Ref ) -> UiuaResult < ( Vec < LocalName > , LocalName ) > {
1484
1510
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 ( ) {
1486
1512
Ok ( ( path_locals, local) )
1487
1513
} else {
1488
1514
Err ( self . fatal_error (
1489
1515
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, ) ,
1495
1517
) )
1496
1518
}
1497
1519
} else if let Some ( local) = self . find_name ( & r. name . value , r. in_macro_arg ) {
@@ -1511,8 +1533,8 @@ code:
1511
1533
}
1512
1534
let mut hit_file = false ;
1513
1535
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 {
1516
1538
break ;
1517
1539
}
1518
1540
hit_file = true ;
@@ -1527,7 +1549,7 @@ code:
1527
1549
& self ,
1528
1550
path : & [ RefComponent ] ,
1529
1551
skip_local : bool ,
1530
- ) -> UiuaResult < Option < ( PathBuf , Vec < LocalName > ) > > {
1552
+ ) -> UiuaResult < Option < ( & Module , Vec < LocalName > ) > > {
1531
1553
let Some ( first) = path. first ( ) else {
1532
1554
return Ok ( None ) ;
1533
1555
} ;
@@ -1543,6 +1565,7 @@ code:
1543
1565
path_locals. push ( module_local) ;
1544
1566
let global = & self . asm . bindings [ module_local. index ] . kind ;
1545
1567
let mut module = match global {
1568
+ BindingKind :: Import ( path) => & self . imports [ path] ,
1546
1569
BindingKind :: Module ( module) => module,
1547
1570
BindingKind :: Func ( _) => {
1548
1571
return Err ( self . fatal_error (
@@ -1564,23 +1587,20 @@ code:
1564
1587
}
1565
1588
} ;
1566
1589
for comp in path. iter ( ) . skip ( 1 ) {
1567
- let submod_local = self . imports [ module]
1590
+ let submod_local = module
1568
1591
. names
1569
1592
. get ( & comp. module . value )
1570
1593
. copied ( )
1571
1594
. ok_or_else ( || {
1572
1595
self . fatal_error (
1573
1596
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) ,
1579
1598
)
1580
1599
} ) ?;
1581
1600
path_locals. push ( submod_local) ;
1582
1601
let global = & self . asm . bindings [ submod_local. index ] . kind ;
1583
1602
module = match global {
1603
+ BindingKind :: Import ( path) => & self . imports [ path] ,
1584
1604
BindingKind :: Module ( module) => module,
1585
1605
BindingKind :: Func ( _) => {
1586
1606
return Err ( self . fatal_error (
@@ -1603,7 +1623,7 @@ code:
1603
1623
} ;
1604
1624
}
1605
1625
1606
- Ok ( Some ( ( module. clone ( ) , path_locals) ) )
1626
+ Ok ( Some ( ( module, path_locals) ) )
1607
1627
}
1608
1628
fn reference ( & mut self , r : Ref , call : bool ) -> UiuaResult {
1609
1629
if r. path . is_empty ( ) {
@@ -1708,7 +1728,9 @@ code:
1708
1728
self . push_instr ( Instr :: Call ( span) ) ;
1709
1729
}
1710
1730
}
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
+ }
1712
1734
BindingKind :: Macro => {
1713
1735
// We could error here, but it's easier to handle it higher up
1714
1736
}
0 commit comments