11use std:: collections:: HashSet ;
22use std:: ops:: { Deref , DerefMut } ;
3- use std:: sync:: LazyLock ;
43use std:: sync:: atomic:: { AtomicI32 , Ordering } ;
54
65use ahash:: AHashMap as HashMap ;
@@ -11,7 +10,6 @@ use cpclib_common::smol_str::ToSmolStr;
1110use cpclib_common:: strsim;
1211use delegate:: delegate;
1312use evalexpr:: { ContextWithMutableVariables , HashMapContext , build_operator_tree} ;
14- use regex:: Regex ;
1513
1614use crate :: symbols:: {
1715 PhysicalAddress , Struct , Symbol , SymbolError , SymbolFor , Value , ValueAndSource , ValueMacro
@@ -186,6 +184,54 @@ struct ModuleSymbolTableIterator<'t> {
186184 current : std:: collections:: hash_map:: Iter < ' t , Symbol , ValueAndSource >
187185}
188186
187+ struct BalancedBracedSegmentsIter < ' a > {
188+ iter : std:: str:: CharIndices < ' a > ,
189+ start : Option < usize > ,
190+ depth : usize
191+ }
192+
193+ impl < ' a > BalancedBracedSegmentsIter < ' a > {
194+ fn new ( text : & ' a str ) -> Self {
195+ Self {
196+ iter : text. char_indices ( ) ,
197+ start : None ,
198+ depth : 0
199+ }
200+ }
201+ }
202+
203+ impl Iterator for BalancedBracedSegmentsIter < ' _ > {
204+ type Item = ( usize , usize ) ;
205+
206+ fn next ( & mut self ) -> Option < Self :: Item > {
207+ for ( idx, ch) in & mut self . iter {
208+ match ch {
209+ '{' => {
210+ if self . depth == 0 {
211+ self . start = Some ( idx) ;
212+ }
213+ self . depth += 1 ;
214+ } ,
215+ '}' => {
216+ if self . depth == 0 {
217+ continue ;
218+ }
219+
220+ self . depth -= 1 ;
221+ if self . depth == 0 {
222+ if let Some ( begin) = self . start . take ( ) {
223+ return Some ( ( begin, idx + 1 ) ) ;
224+ }
225+ }
226+ } ,
227+ _ => { }
228+ }
229+ }
230+
231+ None
232+ }
233+ }
234+
189235impl < ' t > ModuleSymbolTableIterator < ' t > {
190236 fn new ( table : & ' t ModuleSymbolTable ) -> Self {
191237 Self {
@@ -288,6 +334,14 @@ impl Clone for SymbolsTable {
288334/// Local/global label handling code
289335#[ allow( dead_code) ]
290336impl SymbolsTable {
337+ /// Return top-level balanced `{...}` byte ranges found in `text` as an iterator.
338+ ///
339+ /// Returned tuples are `(start, end)` where `end` is exclusive.
340+ /// Example: `A{{x} + 1}B{y}` => ranges for `{{x} + 1}` and `{y}`.
341+ fn collect_balanced_braced_segments ( text : & str ) -> BalancedBracedSegmentsIter < ' _ > {
342+ BalancedBracedSegmentsIter :: new ( text)
343+ }
344+
291345 pub fn enter_function ( & mut self ) {
292346 self . functions_stack . enter_function ( ) ;
293347 }
@@ -349,47 +403,65 @@ impl SymbolsTable {
349403 // dbg!("Input", &symbol);
350404
351405 // handle the labels build with patterns
352- // Get the replacement strings
353- static RE : LazyLock < Regex > = LazyLock :: new ( || Regex :: new ( r"\{+[^\}]+\}+" ) . unwrap ( ) ) ;
354- let mut replace = HashSet :: new ( ) ;
355- for cap in RE . captures_iter ( & symbol) {
356- if cap[ 0 ] != symbol {
357- replace. insert ( cap[ 0 ] . to_owned ( ) ) ;
358- }
359- }
406+ let input_symbol = symbol. clone ( ) ;
407+ let mut output_symbol = String :: with_capacity ( input_symbol. len ( ) ) ;
408+ let mut cursor = 0usize ;
409+ let mut has_replacement = false ;
360410
361- // make the replacement
362- for model in replace. iter ( ) {
363- let local_expr = & model[ 1 ..model. len ( ) - 1 ] ; // remove {}
411+ for ( start, end) in Self :: collect_balanced_braced_segments ( & input_symbol)
412+ . filter ( |( start, end) | !( * start == 0 && * end == input_symbol. len ( ) ) )
413+ {
414+ has_replacement = true ;
415+ output_symbol. push_str ( & input_symbol[ cursor..start] ) ;
416+ let local_expr = & input_symbol[ start + 1 ..end - 1 ] ; // remove {}
364417
365418 let local_value = match self . any_value :: < & str > ( local_expr) ?. map ( |vl| vl. value ( ) ) {
366419 Some ( Value :: String ( s) ) => s. to_string ( ) ,
367420 Some ( Value :: Expr ( e) ) => e. to_string ( ) ,
368421 Some ( Value :: Counter ( e) ) => e. to_string ( ) ,
369422 _ => {
370- let tree = build_operator_tree ( local_expr)
423+ // Expand nested `{symbol}` placeholders before evaluating arithmetic.
424+ let local_expr_for_eval = self
425+ . extend_local_and_patterns_for_symbol :: < & str > ( local_expr) ?
426+ . value ( )
427+ . to_owned ( ) ;
428+
429+ let tree = build_operator_tree ( & local_expr_for_eval)
371430 . expect ( "Expression should be valid here. There is a bug in the assembler" ) ;
372431
373432 // Fill the variable values to allow an evaluation
374433 let mut context = HashMapContext :: new ( ) ;
375434 for variable in tree. iter_variable_identifiers ( ) {
376- let variable_value = self
377- . any_value :: < & str > ( variable) ?
378- . ok_or_else ( || SymbolError :: WrongSymbol ( variable. into ( ) ) ) ?;
435+ let variable_value = if let Some ( value) = self . any_value :: < & str > ( variable) ? {
436+ value
437+ }
438+ else {
439+ let braced = format ! ( "{{{variable}}}" ) ;
440+ self . any_value :: < & str > ( & braced) ?
441+ . ok_or_else ( || SymbolError :: WrongSymbol ( variable. into ( ) ) ) ?
442+ } ;
379443 context
380- . set_value ( variable. to_owned ( ) , variable_value. clone ( ) . into ( ) )
444+ . set_value ( variable. to_owned ( ) , variable_value. into ( ) )
381445 . unwrap ( ) ;
382446 }
383447
384448 // evaluate the expression
385449 let res = tree
386450 . eval_with_context ( & context)
387- . map_err ( |_e| SymbolError :: CannotModify ( local_expr . into ( ) ) ) ?;
451+ . map_err ( |_e| SymbolError :: CannotModify ( local_expr_for_eval . into ( ) ) ) ?;
388452
389453 res. to_string ( )
390454 }
391455 } ;
392- symbol = symbol. replace ( model, & local_value) ;
456+
457+ output_symbol. push_str ( & local_value) ;
458+ cursor = end;
459+ }
460+
461+ // make the replacement
462+ if has_replacement {
463+ output_symbol. push_str ( & input_symbol[ cursor..] ) ;
464+ symbol = output_symbol;
393465 }
394466
395467 // Local symbols are expensed with their global symbol
0 commit comments