11use std:: collections:: HashMap ;
2+ use std:: sync:: OnceLock ;
23
34use anathema_store:: slab:: { Slab , SlabIndex } ;
45
5- use crate :: { error:: ErrorKind , expressions:: Expression } ;
6+ use crate :: error:: ErrorKind ;
7+ use crate :: expressions:: Expression ;
68
79#[ derive( Debug , Default , Clone ) ]
810pub ( crate ) struct Globals ( HashMap < String , Expression > ) ;
@@ -70,6 +72,11 @@ impl Variable {
7072pub struct ScopeId ( Box < [ u16 ] > ) ;
7173
7274impl ScopeId {
75+ fn root ( ) -> & ' static Self {
76+ static ROOT : OnceLock < ScopeId > = OnceLock :: new ( ) ;
77+ ROOT . get_or_init ( || ScopeId ( Box :: new ( [ ] ) ) )
78+ }
79+
7380 // Create the next child id.
7481 fn next ( & self , index : u16 ) -> Self {
7582 let mut scope_id = Vec :: with_capacity ( self . 0 . len ( ) + 1 ) ;
@@ -102,6 +109,16 @@ impl ScopeId {
102109 fn as_slice ( & self ) -> & [ u16 ] {
103110 & self . 0
104111 }
112+
113+ fn contains ( & self , other : impl AsRef < [ u16 ] > ) -> Option < & ScopeId > {
114+ let other = other. as_ref ( ) ;
115+ let len = self . 0 . len ( ) ;
116+
117+ match other. len ( ) >= len {
118+ true => ( * self . 0 == other[ ..len] ) . then_some ( self ) ,
119+ false => None ,
120+ }
121+ }
105122}
106123
107124impl AsRef < [ u16 ] > for ScopeId {
@@ -186,13 +203,14 @@ impl Declarations {
186203 }
187204
188205 // Get the scope id that is closest to the argument
189- fn get ( & self , ident : & str , scope_id : impl AsRef < [ u16 ] > ) -> Option < VarId > {
206+ fn get ( & self , ident : & str , scope_id : impl AsRef < [ u16 ] > , boundary : & ScopeId ) -> Option < VarId > {
190207 self . 0
191208 . get ( ident) ?
192209 . iter ( )
193210 . rev ( )
194- . filter ( |( scope, _) | scope. as_ref ( ) == scope_id. as_ref ( ) )
195- . map ( |( _, var) | * var)
211+ // here we need to look up closest scope that is still within the last boundary
212+ . filter ( |( scope, _) | boundary. contains ( scope) . is_some ( ) )
213+ . filter_map ( |( scope, var) | scope. contains ( & scope_id) . map ( |_| * var) )
196214 . next ( )
197215 }
198216}
@@ -204,6 +222,7 @@ pub struct Variables {
204222 globals : Globals ,
205223 root : RootScope ,
206224 current : ScopeId ,
225+ boundary : Vec < ScopeId > ,
207226 store : Slab < VarId , Variable > ,
208227 declarations : Declarations ,
209228}
@@ -214,6 +233,7 @@ impl Default for Variables {
214233 Self {
215234 globals : Globals :: empty ( ) ,
216235 current : root. 0 . id . clone ( ) ,
236+ boundary : vec ! [ ] ,
217237 root,
218238 store : Slab :: empty ( ) ,
219239 declarations : Declarations :: new ( ) ,
@@ -264,7 +284,21 @@ impl Variables {
264284
265285 /// Fetch a value starting from the current path.
266286 pub fn fetch ( & self , ident : & str ) -> Option < VarId > {
267- self . declarations . get ( ident, & self . current )
287+ self . declarations . get ( ident, & self . current , self . boundary ( ) )
288+ }
289+
290+ /// Create a new scope and set that scope as a boundary.
291+ /// This prevents inner components from accessing values
292+ /// declared outside of the component.
293+ pub ( crate ) fn push_scope_boundary ( & mut self ) {
294+ self . push ( ) ;
295+ self . boundary . push ( self . current . clone ( ) ) ;
296+ }
297+
298+ /// Pop the scope boundary.
299+ pub ( crate ) fn pop_scope_boundary ( & mut self ) {
300+ self . pop ( ) ;
301+ self . boundary . pop ( ) ;
268302 }
269303
270304 /// Create a new child and set the new childs id as the `current` id.
@@ -291,13 +325,17 @@ impl Variables {
291325 // Fetch and load a value from its ident
292326 #[ cfg( test) ]
293327 fn fetch_load ( & self , ident : & str ) -> Option < & Expression > {
294- let id = self . declarations . get ( ident, & self . current ) ?;
328+ let id = self . declarations . get ( ident, & self . current , self . boundary ( ) ) ?;
295329 self . load ( id)
296330 }
297331
298332 pub fn global_lookup ( & self , ident : & str ) -> Option < & Expression > {
299333 self . globals . get ( ident)
300334 }
335+
336+ fn boundary ( & self ) -> & ScopeId {
337+ self . boundary . last ( ) . unwrap_or ( ScopeId :: root ( ) )
338+ }
301339}
302340
303341impl From < Variables > for HashMap < String , Variable > {
@@ -319,6 +357,7 @@ impl From<Variables> for HashMap<String, Variable> {
319357#[ cfg( test) ]
320358mod test {
321359 use super :: * ;
360+ use crate :: expressions:: num;
322361
323362 impl From < usize > for VarId {
324363 fn from ( value : usize ) -> Self {
@@ -403,15 +442,15 @@ mod test {
403442 fn declaration_lookup ( ) {
404443 let mut dec = Declarations :: new ( ) ;
405444 dec. add ( "var" , [ 0 , 0 ] , 0 ) ;
406- let root = dec. get ( "var" , [ 0 , 0 ] ) . unwrap ( ) ;
445+ let root = dec. get ( "var" , [ 0 , 0 ] , ScopeId :: root ( ) ) . unwrap ( ) ;
407446 assert_eq ! ( root, 0 . into( ) ) ;
408447 }
409448
410449 #[ test]
411450 fn declaration_failed_lookup ( ) {
412451 let mut dec = Declarations :: new ( ) ;
413452 dec. add ( "var" , [ 0 ] , 0 ) ;
414- let root = dec. get ( "var" , [ 1 , 0 ] ) ;
453+ let root = dec. get ( "var" , [ 1 , 0 ] , ScopeId :: root ( ) ) ;
415454 assert ! ( root. is_none( ) ) ;
416455 }
417456
@@ -423,15 +462,40 @@ mod test {
423462 dec. add ( ident, [ 0 , 0 ] , 1 ) ;
424463 dec. add ( ident, [ 0 , 0 , 0 ] , 2 ) ;
425464
426- assert_eq ! ( dec. get( ident, [ 0 ] ) . unwrap( ) . 0 , 0 ) ;
427- assert_eq ! ( dec. get( ident, [ 0 , 0 ] ) . unwrap( ) . 0 , 1 ) ;
428- assert ! ( dec. get( ident, [ 0 , 0 , 0 , 1 , 1 ] ) . is_none ( ) ) ;
465+ assert_eq ! ( dec. get( ident, [ 0 ] , ScopeId :: root ( ) ) . unwrap( ) . 0 , 0 ) ;
466+ assert_eq ! ( dec. get( ident, [ 0 , 0 ] , ScopeId :: root ( ) ) . unwrap( ) . 0 , 1 ) ;
467+ assert_eq ! ( dec. get( ident, [ 0 , 0 , 0 , 1 , 1 ] , ScopeId :: root ( ) ) . unwrap ( ) . 0 , 2 ) ;
429468 }
430469
431470 #[ test]
432471 fn unreachable_declaration ( ) {
433472 let mut dec = Declarations :: new ( ) ;
434473 dec. add ( "var" , [ 0 , 1 ] , 0 ) ;
435- assert ! ( dec. get( "var" , [ 0 , 0 , 1 ] ) . is_none( ) ) ;
474+ assert ! ( dec. get( "var" , [ 0 , 0 , 1 ] , ScopeId :: root( ) ) . is_none( ) ) ;
475+ }
476+
477+ #[ test]
478+ fn get_inside_boundary ( ) {
479+ let mut vars = Variables :: new ( ) ;
480+
481+ // Define a varialbe in the root scope
482+ _ = vars. define_local ( "var" , 1 ) ;
483+
484+ // Create a new unique scope and boundary.
485+ // * `var` should be inaccessible from within the new scope boundary
486+ // * `outer_var` should be inaccessible to the root scope
487+ vars. push_scope_boundary ( ) ;
488+ assert ! ( vars. fetch( "var" ) . is_none( ) ) ;
489+ _ = vars. define_local ( "var" , 2 ) ;
490+ _ = vars. define_local ( "other_var" , 3 ) ;
491+ assert_eq ! ( vars. fetch_load( "var" ) . unwrap( ) , & * num( 2 ) ) ;
492+ vars. push ( ) ;
493+ assert_eq ! ( vars. fetch_load( "other_var" ) . unwrap( ) , & * num( 3 ) ) ;
494+ vars. pop ( ) ;
495+
496+ // Return to root scope
497+ vars. pop_scope_boundary ( ) ;
498+ assert_eq ! ( vars. fetch_load( "var" ) . unwrap( ) , & * num( 1 ) ) ;
499+ assert ! ( vars. fetch( "other_var" ) . is_none( ) ) ;
436500 }
437501}
0 commit comments