11use std:: collections:: HashMap ;
2+ use std:: sync:: OnceLock ;
23
34use anathema_store:: slab:: { Slab , SlabIndex } ;
45
6+ use crate :: error:: ErrorKind ;
57use crate :: expressions:: Expression ;
68
79#[ derive( Debug , Default , Clone ) ]
@@ -12,6 +14,10 @@ impl Globals {
1214 Self ( HashMap :: new ( ) )
1315 }
1416
17+ pub fn contains ( & self , ident : & str ) -> bool {
18+ self . 0 . contains_key ( ident)
19+ }
20+
1521 pub fn get ( & self , ident : & str ) -> Option < & Expression > {
1622 self . 0 . get ( ident)
1723 }
@@ -66,6 +72,11 @@ impl Variable {
6672pub struct ScopeId ( Box < [ u16 ] > ) ;
6773
6874impl ScopeId {
75+ fn root ( ) -> & ' static Self {
76+ static ROOT : OnceLock < ScopeId > = OnceLock :: new ( ) ;
77+ ROOT . get_or_init ( || ScopeId ( Box :: new ( [ ] ) ) )
78+ }
79+
6980 // Create the next child id.
7081 fn next ( & self , index : u16 ) -> Self {
7182 let mut scope_id = Vec :: with_capacity ( self . 0 . len ( ) + 1 ) ;
@@ -98,6 +109,16 @@ impl ScopeId {
98109 fn as_slice ( & self ) -> & [ u16 ] {
99110 & self . 0
100111 }
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+ }
101122}
102123
103124impl AsRef < [ u16 ] > for ScopeId {
@@ -182,13 +203,14 @@ impl Declarations {
182203 }
183204
184205 // Get the scope id that is closest to the argument
185- 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 > {
186207 self . 0
187208 . get ( ident) ?
188209 . iter ( )
189210 . rev ( )
190- . filter ( |( scope, _) | scope. as_ref ( ) == scope_id. as_ref ( ) )
191- . 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) )
192214 . next ( )
193215 }
194216}
@@ -200,6 +222,7 @@ pub struct Variables {
200222 globals : Globals ,
201223 root : RootScope ,
202224 current : ScopeId ,
225+ boundary : Vec < ScopeId > ,
203226 store : Slab < VarId , Variable > ,
204227 declarations : Declarations ,
205228}
@@ -210,6 +233,7 @@ impl Default for Variables {
210233 Self {
211234 globals : Globals :: empty ( ) ,
212235 current : root. 0 . id . clone ( ) ,
236+ boundary : vec ! [ ] ,
213237 root,
214238 store : Slab :: empty ( ) ,
215239 declarations : Declarations :: new ( ) ,
@@ -232,9 +256,15 @@ impl Variables {
232256 var_id
233257 }
234258
235- pub fn define_global ( & mut self , ident : impl Into < String > , value : impl Into < Expression > ) {
259+ pub fn define_global ( & mut self , ident : impl Into < String > , value : impl Into < Expression > ) -> Result < ( ) , ErrorKind > {
260+ let ident = ident. into ( ) ;
261+ if self . globals . contains ( & ident) {
262+ return Err ( ErrorKind :: GlobalAlreadyAssigned ( ident) ) ;
263+ }
264+
236265 let value = value. into ( ) ;
237- self . globals . set ( ident. into ( ) , value)
266+ self . globals . set ( ident, value) ;
267+ Ok ( ( ) )
238268 }
239269
240270 pub fn define_local ( & mut self , ident : impl Into < String > , value : impl Into < Expression > ) -> VarId {
@@ -254,7 +284,21 @@ impl Variables {
254284
255285 /// Fetch a value starting from the current path.
256286 pub fn fetch ( & self , ident : & str ) -> Option < VarId > {
257- 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 ( ) ;
258302 }
259303
260304 /// Create a new child and set the new childs id as the `current` id.
@@ -281,13 +325,17 @@ impl Variables {
281325 // Fetch and load a value from its ident
282326 #[ cfg( test) ]
283327 fn fetch_load ( & self , ident : & str ) -> Option < & Expression > {
284- let id = self . declarations . get ( ident, & self . current ) ?;
328+ let id = self . declarations . get ( ident, & self . current , self . boundary ( ) ) ?;
285329 self . load ( id)
286330 }
287331
288332 pub fn global_lookup ( & self , ident : & str ) -> Option < & Expression > {
289333 self . globals . get ( ident)
290334 }
335+
336+ fn boundary ( & self ) -> & ScopeId {
337+ self . boundary . last ( ) . unwrap_or ( ScopeId :: root ( ) )
338+ }
291339}
292340
293341impl From < Variables > for HashMap < String , Variable > {
@@ -309,6 +357,7 @@ impl From<Variables> for HashMap<String, Variable> {
309357#[ cfg( test) ]
310358mod test {
311359 use super :: * ;
360+ use crate :: expressions:: num;
312361
313362 impl From < usize > for VarId {
314363 fn from ( value : usize ) -> Self {
@@ -393,15 +442,15 @@ mod test {
393442 fn declaration_lookup ( ) {
394443 let mut dec = Declarations :: new ( ) ;
395444 dec. add ( "var" , [ 0 , 0 ] , 0 ) ;
396- let root = dec. get ( "var" , [ 0 , 0 ] ) . unwrap ( ) ;
445+ let root = dec. get ( "var" , [ 0 , 0 ] , ScopeId :: root ( ) ) . unwrap ( ) ;
397446 assert_eq ! ( root, 0 . into( ) ) ;
398447 }
399448
400449 #[ test]
401450 fn declaration_failed_lookup ( ) {
402451 let mut dec = Declarations :: new ( ) ;
403452 dec. add ( "var" , [ 0 ] , 0 ) ;
404- let root = dec. get ( "var" , [ 1 , 0 ] ) ;
453+ let root = dec. get ( "var" , [ 1 , 0 ] , ScopeId :: root ( ) ) ;
405454 assert ! ( root. is_none( ) ) ;
406455 }
407456
@@ -413,15 +462,40 @@ mod test {
413462 dec. add ( ident, [ 0 , 0 ] , 1 ) ;
414463 dec. add ( ident, [ 0 , 0 , 0 ] , 2 ) ;
415464
416- assert_eq ! ( dec. get( ident, [ 0 ] ) . unwrap( ) . 0 , 0 ) ;
417- assert_eq ! ( dec. get( ident, [ 0 , 0 ] ) . unwrap( ) . 0 , 1 ) ;
418- 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 ) ;
419468 }
420469
421470 #[ test]
422471 fn unreachable_declaration ( ) {
423472 let mut dec = Declarations :: new ( ) ;
424473 dec. add ( "var" , [ 0 , 1 ] , 0 ) ;
425- 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( ) ) ;
426500 }
427501}
0 commit comments