@@ -24,26 +24,37 @@ pub(super) enum AccessComponent {
2424 Expression ( ExprRef ) ,
2525}
2626
27+ /// Root of a reference chain - either a named variable or another arbitrary expression
28+ #[ derive( Debug , Clone ) ]
29+ pub ( super ) enum ReferenceRoot {
30+ Variable ( String ) ,
31+ Expression ( ExprRef ) ,
32+ }
33+
2734/// Represents a chained reference like data.a.b[expr].c[expr]
2835#[ derive( Debug , Clone ) ]
2936pub ( super ) struct ReferenceChain {
30- /// The root variable (e.g., "data", "input", "local_var" )
31- pub ( super ) root : String ,
37+ /// The root of the chain (variable or arbitrary expression )
38+ pub ( super ) root : ReferenceRoot ,
3239 /// Chain of field accesses - either literal field names or dynamic expressions
3340 pub ( super ) components : Vec < AccessComponent > ,
3441}
3542
3643impl ReferenceChain {
3744 /// Get the static prefix path (all literal components from the start)
38- pub ( super ) fn get_static_prefix ( & self ) -> Vec < & str > {
39- let mut prefix = vec ! [ self . root. as_str( ) ] ;
45+ pub ( super ) fn get_static_prefix ( & self ) -> Option < Vec < & str > > {
46+ let ReferenceRoot :: Variable ( root) = & self . root else {
47+ return None ;
48+ } ;
49+
50+ let mut prefix = vec ! [ root. as_str( ) ] ;
4051 for component in & self . components {
4152 match component {
4253 AccessComponent :: Field ( field) => prefix. push ( field. as_str ( ) ) ,
4354 AccessComponent :: Expression ( _) => break ,
4455 }
4556 }
46- prefix
57+ Some ( prefix)
4758 }
4859}
4960
@@ -57,7 +68,7 @@ pub(super) fn parse_reference_chain(expr: &ExprRef) -> Result<ReferenceChain> {
5768 match current_expr. as_ref ( ) {
5869 Expr :: Var { span, .. } => {
5970 // Found the root variable
60- let root = span. text ( ) . to_string ( ) ;
71+ let root = ReferenceRoot :: Variable ( span. text ( ) . to_string ( ) ) ;
6172 components. reverse ( ) ; // We built backwards, so reverse
6273 return Ok ( ReferenceChain { root, components } ) ;
6374 }
@@ -81,7 +92,12 @@ pub(super) fn parse_reference_chain(expr: &ExprRef) -> Result<ReferenceChain> {
8192 current_expr = refr;
8293 }
8394 _ => {
84- return Err ( CompilerError :: NotSimpleReferenceChain . at ( current_expr. span ( ) ) ) ;
95+ // Fallback root expression (e.g., array literal, function call)
96+ components. reverse ( ) ;
97+ return Ok ( ReferenceChain {
98+ root : ReferenceRoot :: Expression ( current_expr. clone ( ) ) ,
99+ components,
100+ } ) ;
85101 }
86102 }
87103 }
@@ -94,10 +110,17 @@ impl<'a> Compiler<'a> {
94110 // Parse the expression into a reference chain
95111 let chain = parse_reference_chain ( expr) ?;
96112
97- match chain. root . as_str ( ) {
98- "input" => self . compile_input_chain ( & chain, span) ,
99- "data" => self . compile_data_chain ( & chain, span) ,
100- _ => self . compile_local_var_chain ( & chain, span) ,
113+ match chain. root . clone ( ) {
114+ ReferenceRoot :: Variable ( name) => match name. as_str ( ) {
115+ "input" => self . compile_input_chain ( & chain, span) ,
116+ "data" => self . compile_data_chain ( & chain, span) ,
117+ _ => self . compile_local_var_chain ( & name, & chain, span) ,
118+ } ,
119+ ReferenceRoot :: Expression ( root_expr) => {
120+ let root_reg =
121+ self . compile_rego_expr_with_span ( & root_expr, root_expr. span ( ) , false ) ?;
122+ self . compile_chain_access ( root_reg, & chain. components , span)
123+ }
101124 }
102125 }
103126
@@ -121,7 +144,9 @@ impl<'a> Compiler<'a> {
121144 }
122145
123146 // Build the static prefix path components for rule matching
124- let static_prefix = chain. get_static_prefix ( ) ;
147+ let static_prefix = chain
148+ . get_static_prefix ( )
149+ . expect ( "data references must have variable roots" ) ;
125150
126151 // Try to find the longest matching rule prefix
127152 // Start from the full path and work backwards
@@ -252,17 +277,22 @@ impl<'a> Compiler<'a> {
252277 }
253278
254279 /// Compile local variable access chain
255- fn compile_local_var_chain ( & mut self , chain : & ReferenceChain , span : & Span ) -> Result < Register > {
280+ fn compile_local_var_chain (
281+ & mut self ,
282+ root : & str ,
283+ chain : & ReferenceChain ,
284+ span : & Span ,
285+ ) -> Result < Register > {
256286 // Check if it's a local variable first (precedence over rules)
257- if let Some ( var_reg) = self . lookup_variable ( & chain . root ) {
287+ if let Some ( var_reg) = self . lookup_variable ( root) {
258288 if chain. components . is_empty ( ) {
259289 return Ok ( var_reg) ;
260290 }
261291 return self . compile_chain_access ( var_reg, & chain. components , span) ;
262292 }
263293
264294 // Check if there's a rule in the current package that matches
265- let current_pkg_prefix = format ! ( "{}.{}" , & self . current_package, & chain . root) ;
295+ let current_pkg_prefix = format ! ( "{}.{}" , & self . current_package, root) ;
266296
267297 // Build static path for rule matching
268298 let mut rule_path_parts = vec ! [ current_pkg_prefix. as_str( ) ] ;
@@ -300,7 +330,7 @@ impl<'a> Compiler<'a> {
300330
301331 // No rule found - undefined variable
302332 Err ( CompilerError :: UndefinedVariable {
303- name : chain . root . clone ( ) ,
333+ name : root. to_string ( ) ,
304334 }
305335 . at ( span) )
306336 }
0 commit comments