IncludeCPP 4.0.3__py3-none-any.whl → 4.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -113,6 +113,8 @@ class TokenType(Enum):
113
113
  # Append operator for constructor/function extension
114
114
  PLUS_PLUS = auto() # ++ for constructor/function append (keeps old + adds new)
115
115
  MINUS_MINUS = auto() # -- for potential future use
116
+ # Multi-language support (v4.1.0)
117
+ LANG_INSTANCE_REF = auto() # cpp$InstanceName, py$Object - cross-language instance access
116
118
 
117
119
 
118
120
  KEYWORDS = {
@@ -155,6 +157,8 @@ KEYWORDS = {
155
157
  'static', # Static method/function
156
158
  # CSSL Include Keywords
157
159
  'include', 'get',
160
+ # Multi-language support (v4.1.0)
161
+ 'supports', 'libinclude',
158
162
  }
159
163
 
160
164
  # Function modifiers that can appear in any order before function name
@@ -182,6 +186,12 @@ INJECTION_HELPERS = {
182
186
  'string', 'integer', 'json', 'array', 'vector', 'combo', 'dynamic', 'sql'
183
187
  }
184
188
 
189
+ # Language identifiers for multi-language support (v4.1.0)
190
+ # Used in lang$instance patterns like cpp$MyClass, py$Object
191
+ LANGUAGE_IDS = {
192
+ 'cpp', 'py', 'python', 'java', 'csharp', 'js', 'javascript'
193
+ }
194
+
185
195
 
186
196
  @dataclass
187
197
  class Token:
@@ -457,6 +467,25 @@ class CSSLLexer:
457
467
  self._advance()
458
468
  value = self.source[start:self.pos]
459
469
 
470
+ # Check for language$instance pattern (v4.1.0)
471
+ # e.g., cpp$MyClass, py$Object, java$Service
472
+ if value.lower() in LANGUAGE_IDS and self.pos < len(self.source) and self.source[self.pos] == '$':
473
+ lang_id = value
474
+ self._advance() # skip '$'
475
+ # Read instance name
476
+ instance_start = self.pos
477
+ while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
478
+ self._advance()
479
+ instance_name = self.source[instance_start:self.pos]
480
+ if instance_name:
481
+ self._add_token(TokenType.LANG_INSTANCE_REF, {'lang': lang_id, 'instance': instance_name})
482
+ return
483
+ # If no instance name, revert and treat as normal identifier
484
+ self.pos = start
485
+ while self.pos < len(self.source) and (self.source[self.pos].isalnum() or self.source[self.pos] == '_'):
486
+ self._advance()
487
+ value = self.source[start:self.pos]
488
+
460
489
  if value in ('True', 'true'):
461
490
  self._add_token(TokenType.BOOLEAN, True)
462
491
  elif value in ('False', 'false'):
@@ -1350,13 +1379,7 @@ class CSSLParser:
1350
1379
  # Wrap in global_assignment to mark as global variable (same as 'global' keyword)
1351
1380
  global_stmt = ASTNode('global_assignment', value=stmt)
1352
1381
  root.children.append(global_stmt)
1353
- # Handle statements - keywords like 'instance', 'list', 'map' can be variable names
1354
- elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
1355
- self._check(TokenType.SELF_REF) or self._check(TokenType.SHARED_REF) or
1356
- self._check(TokenType.KEYWORD)):
1357
- stmt = self._parse_expression_statement()
1358
- if stmt:
1359
- root.children.append(stmt)
1382
+ # Control flow keywords must be checked BEFORE generic KEYWORD handling
1360
1383
  elif self._match_keyword('if'):
1361
1384
  root.children.append(self._parse_if())
1362
1385
  elif self._match_keyword('while'):
@@ -1365,6 +1388,13 @@ class CSSLParser:
1365
1388
  root.children.append(self._parse_for())
1366
1389
  elif self._match_keyword('foreach'):
1367
1390
  root.children.append(self._parse_foreach())
1391
+ # Handle statements - keywords like 'instance', 'list', 'map' can be variable names
1392
+ elif (self._check(TokenType.IDENTIFIER) or self._check(TokenType.AT) or
1393
+ self._check(TokenType.SELF_REF) or self._check(TokenType.SHARED_REF) or
1394
+ self._check(TokenType.KEYWORD)):
1395
+ stmt = self._parse_expression_statement()
1396
+ if stmt:
1397
+ root.children.append(stmt)
1368
1398
  # Skip comments and newlines
1369
1399
  elif self._check(TokenType.COMMENT) or self._check(TokenType.NEWLINE):
1370
1400
  self._advance()
@@ -1627,15 +1657,22 @@ class CSSLParser:
1627
1657
  # class Child : extends Parent (param1, param2) { ... } <- constructor args for parent
1628
1658
  extends_class = None
1629
1659
  extends_is_python = False
1660
+ extends_lang_ref = None # v4.1.0: Cross-language inheritance (cpp$ClassName)
1630
1661
  extends_args = []
1631
1662
  overwrites_class = None
1632
1663
  overwrites_is_python = False
1664
+ supports_language = None # v4.1.0: Multi-language syntax support
1633
1665
 
1634
1666
  if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
1635
1667
  # Parse extends and/or overwrites (can be chained with : or ::)
1636
1668
  while True:
1637
1669
  if self._match_keyword('extends'):
1638
- if self._check(TokenType.IDENTIFIER):
1670
+ # v4.1.0: Check for cross-language inheritance: extends cpp$ClassName
1671
+ if self._check(TokenType.LANG_INSTANCE_REF):
1672
+ ref = self._advance().value
1673
+ extends_lang_ref = ref # {'lang': 'cpp', 'instance': 'ClassName'}
1674
+ extends_class = ref['instance']
1675
+ elif self._check(TokenType.IDENTIFIER):
1639
1676
  extends_class = self._advance().value
1640
1677
  elif self._check(TokenType.SHARED_REF):
1641
1678
  extends_class = self._advance().value
@@ -1660,8 +1697,20 @@ class CSSLParser:
1660
1697
  # Skip optional () after class name
1661
1698
  if self._match(TokenType.PAREN_START):
1662
1699
  self._expect(TokenType.PAREN_END)
1700
+ # v4.1.0: Parse 'supports' keyword for multi-language syntax
1701
+ elif self._match_keyword('supports'):
1702
+ if self._check(TokenType.AT):
1703
+ self._advance() # consume @
1704
+ if self._check(TokenType.IDENTIFIER):
1705
+ supports_language = '@' + self._advance().value
1706
+ else:
1707
+ raise CSSLSyntaxError("Expected language identifier after '@' in 'supports'")
1708
+ elif self._check(TokenType.IDENTIFIER):
1709
+ supports_language = self._advance().value
1710
+ else:
1711
+ raise CSSLSyntaxError("Expected language identifier after 'supports'")
1663
1712
  else:
1664
- raise CSSLSyntaxError("Expected 'extends' or 'overwrites' after ':' or '::' in class declaration")
1713
+ raise CSSLSyntaxError("Expected 'extends', 'overwrites', or 'supports' after ':' or '::' in class declaration")
1665
1714
  # Check for another : or :: for chaining
1666
1715
  if not (self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON)):
1667
1716
  break
@@ -1673,9 +1722,11 @@ class CSSLParser:
1673
1722
  'class_params': class_params,
1674
1723
  'extends': extends_class,
1675
1724
  'extends_is_python': extends_is_python,
1725
+ 'extends_lang_ref': extends_lang_ref, # v4.1.0
1676
1726
  'extends_args': extends_args,
1677
1727
  'overwrites': overwrites_class,
1678
- 'overwrites_is_python': overwrites_is_python
1728
+ 'overwrites_is_python': overwrites_is_python,
1729
+ 'supports_language': supports_language # v4.1.0
1679
1730
  }, children=[])
1680
1731
  self._expect(TokenType.BLOCK_START)
1681
1732
 
@@ -1945,6 +1996,7 @@ class CSSLParser:
1945
1996
  extends_method_ref = None
1946
1997
  overwrites_class_ref = None
1947
1998
  overwrites_method_ref = None
1999
+ supports_language = None # v4.1.0: Multi-language syntax support
1948
2000
 
1949
2001
  if self._match(TokenType.DOUBLE_COLON) or self._match(TokenType.COLON):
1950
2002
  # Parse extends and/or overwrites (supports :: method-level syntax)
@@ -2005,6 +2057,18 @@ class CSSLParser:
2005
2057
  # Skip optional () after function/method name
2006
2058
  if self._match(TokenType.PAREN_START):
2007
2059
  self._expect(TokenType.PAREN_END)
2060
+ # v4.1.0: Parse 'supports' keyword for multi-language syntax
2061
+ elif self._match_keyword('supports'):
2062
+ if self._check(TokenType.AT):
2063
+ self._advance() # consume @
2064
+ if self._check(TokenType.IDENTIFIER):
2065
+ supports_language = '@' + self._advance().value
2066
+ else:
2067
+ raise CSSLSyntaxError("Expected language identifier after '@' in 'supports'")
2068
+ elif self._check(TokenType.IDENTIFIER):
2069
+ supports_language = self._advance().value
2070
+ else:
2071
+ raise CSSLSyntaxError("Expected language identifier after 'supports'")
2008
2072
  else:
2009
2073
  break
2010
2074
  # Check for another :: or : for chaining extends/overwrites
@@ -2092,7 +2156,9 @@ class CSSLParser:
2092
2156
  # New append mode fields
2093
2157
  'append_mode': append_mode,
2094
2158
  'append_ref_class': append_ref_class,
2095
- 'append_ref_member': append_ref_member
2159
+ 'append_ref_member': append_ref_member,
2160
+ # v4.1.0: Multi-language support
2161
+ 'supports_language': supports_language
2096
2162
  }, children=[])
2097
2163
  self._expect(TokenType.BLOCK_START)
2098
2164
 
@@ -2352,42 +2418,40 @@ class CSSLParser:
2352
2418
 
2353
2419
  Supports: i = i + 1, i++, ++i, i += 1, i -= 1
2354
2420
  """
2355
- # Check for prefix increment/decrement: ++i or --i
2356
- if self._check(TokenType.PLUS) or self._check(TokenType.MINUS):
2357
- op_token = self._advance()
2358
- # Check for double operator (++ or --)
2359
- if self._check(op_token.type):
2360
- self._advance()
2361
- var_name = self._advance().value
2362
- op = 'increment' if op_token.type == TokenType.PLUS else 'decrement'
2363
- return ASTNode('c_for_update', value={'var': var_name, 'op': op})
2421
+ # Check for prefix increment/decrement: ++i or --i (as single PLUS_PLUS/MINUS_MINUS token)
2422
+ if self._check(TokenType.PLUS_PLUS):
2423
+ self._advance()
2424
+ var_name = self._advance().value
2425
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'increment'})
2426
+ elif self._check(TokenType.MINUS_MINUS):
2427
+ self._advance()
2428
+ var_name = self._advance().value
2429
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'decrement'})
2364
2430
 
2365
2431
  # Regular variable assignment or postfix
2366
2432
  var_name = self._advance().value
2367
2433
 
2368
- # Check for postfix increment/decrement: i++ or i--
2369
- if self._check(TokenType.PLUS):
2434
+ # Check for postfix increment/decrement: i++ or i-- (as single PLUS_PLUS/MINUS_MINUS token)
2435
+ if self._check(TokenType.PLUS_PLUS):
2436
+ self._advance()
2437
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'increment'})
2438
+ elif self._check(TokenType.MINUS_MINUS):
2439
+ self._advance()
2440
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'decrement'})
2441
+ # i += value
2442
+ elif self._check(TokenType.PLUS):
2370
2443
  self._advance()
2371
- if self._check(TokenType.PLUS):
2444
+ if self._check(TokenType.EQUALS):
2372
2445
  self._advance()
2373
- return ASTNode('c_for_update', value={'var': var_name, 'op': 'increment'})
2374
- else:
2375
- # i += value
2376
- if self._check(TokenType.EQUALS):
2377
- self._advance()
2378
- value = self._parse_expression()
2379
- return ASTNode('c_for_update', value={'var': var_name, 'op': 'add', 'value': value})
2446
+ value = self._parse_expression()
2447
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'add', 'value': value})
2448
+ # i -= value
2380
2449
  elif self._check(TokenType.MINUS):
2381
2450
  self._advance()
2382
- if self._check(TokenType.MINUS):
2451
+ if self._check(TokenType.EQUALS):
2383
2452
  self._advance()
2384
- return ASTNode('c_for_update', value={'var': var_name, 'op': 'decrement'})
2385
- else:
2386
- # i -= value
2387
- if self._check(TokenType.EQUALS):
2388
- self._advance()
2389
- value = self._parse_expression()
2390
- return ASTNode('c_for_update', value={'var': var_name, 'op': 'subtract', 'value': value})
2453
+ value = self._parse_expression()
2454
+ return ASTNode('c_for_update', value={'var': var_name, 'op': 'subtract', 'value': value})
2391
2455
 
2392
2456
  # Regular assignment: i = expression
2393
2457
  if self._check(TokenType.EQUALS):
@@ -2948,6 +3012,14 @@ class CSSLParser:
2948
3012
  if self._match(TokenType.MINUS):
2949
3013
  operand = self._parse_unary()
2950
3014
  return ASTNode('unary', value={'op': '-', 'operand': operand})
3015
+ # Prefix increment: ++i
3016
+ if self._match(TokenType.PLUS_PLUS):
3017
+ operand = self._parse_unary()
3018
+ return ASTNode('increment', value={'op': 'prefix', 'operand': operand})
3019
+ # Prefix decrement: --i
3020
+ if self._match(TokenType.MINUS_MINUS):
3021
+ operand = self._parse_unary()
3022
+ return ASTNode('decrement', value={'op': 'prefix', 'operand': operand})
2951
3023
  if self._match(TokenType.AMPERSAND):
2952
3024
  # Reference operator: &variable or &@module
2953
3025
  operand = self._parse_unary()
@@ -3148,6 +3220,28 @@ class CSSLParser:
3148
3220
  break
3149
3221
  return node
3150
3222
 
3223
+ # v4.1.0: Cross-language instance reference: cpp$ClassName, py$Object
3224
+ if self._check(TokenType.LANG_INSTANCE_REF):
3225
+ token = self._advance()
3226
+ ref = token.value # {'lang': 'cpp', 'instance': 'ClassName'}
3227
+ node = ASTNode('lang_instance_ref', value=ref, line=token.line, column=token.column)
3228
+ # Check for member access, calls, indexing
3229
+ while True:
3230
+ if self._match(TokenType.PAREN_START):
3231
+ args, kwargs = self._parse_call_arguments()
3232
+ self._expect(TokenType.PAREN_END)
3233
+ node = ASTNode('call', value={'callee': node, 'args': args, 'kwargs': kwargs})
3234
+ elif self._match(TokenType.DOT):
3235
+ member = self._advance().value
3236
+ node = ASTNode('member_access', value={'object': node, 'member': member})
3237
+ elif self._match(TokenType.BRACKET_START):
3238
+ index = self._parse_expression()
3239
+ self._expect(TokenType.BRACKET_END)
3240
+ node = ASTNode('index_access', value={'object': node, 'index': index})
3241
+ else:
3242
+ break
3243
+ return node
3244
+
3151
3245
  if self._check(TokenType.NUMBER):
3152
3246
  return ASTNode('literal', value=self._advance().value)
3153
3247
 
@@ -3398,6 +3492,12 @@ class CSSLParser:
3398
3492
  index = self._parse_expression()
3399
3493
  self._expect(TokenType.BRACKET_END)
3400
3494
  node = ASTNode('index_access', value={'object': node, 'index': index})
3495
+ # Postfix increment: i++
3496
+ elif self._match(TokenType.PLUS_PLUS):
3497
+ node = ASTNode('increment', value={'op': 'postfix', 'operand': node})
3498
+ # Postfix decrement: i--
3499
+ elif self._match(TokenType.MINUS_MINUS):
3500
+ node = ASTNode('decrement', value={'op': 'postfix', 'operand': node})
3401
3501
  else:
3402
3502
  break
3403
3503
 
@@ -2549,24 +2549,60 @@ class CSSLRuntime:
2549
2549
  if filter_info:
2550
2550
  source = self._apply_injection_filter(source, filter_info)
2551
2551
 
2552
- # Get current target value for add/move modes
2552
+ # Get current target value for add/move/replace modes (needed for UniversalInstance handling)
2553
2553
  current_value = None
2554
- if mode in ('add', 'move'):
2555
- try:
2556
- current_value = self._evaluate(target)
2557
- except Exception:
2558
- # Target might not exist yet, that's okay for add mode
2559
- current_value = None
2554
+ try:
2555
+ current_value = self._evaluate(target)
2556
+ except Exception:
2557
+ # Target might not exist yet, that's okay for add mode
2558
+ current_value = None
2560
2559
 
2561
2560
  # Determine final value based on mode
2562
2561
  if mode == 'replace':
2563
- final_value = source
2562
+ from .cssl_types import CSSLInstance, UniversalInstance, CSSLClass
2563
+ # Special handling for UniversalInstance targets - inject instead of replace
2564
+ if isinstance(current_value, UniversalInstance):
2565
+ if isinstance(source, CSSLClass):
2566
+ current_value.set_member(source.name, source)
2567
+ final_value = current_value
2568
+ elif isinstance(source, ASTNode) and source.type == 'function':
2569
+ func_info = source.value
2570
+ func_name = func_info.get('name') if isinstance(func_info, dict) else None
2571
+ if func_name:
2572
+ current_value.set_method(func_name, source, self)
2573
+ final_value = current_value
2574
+ elif isinstance(source, CSSLInstance):
2575
+ current_value.set_member(source._class.name, source)
2576
+ final_value = current_value
2577
+ else:
2578
+ # For other types, store as member with source type name
2579
+ final_value = source
2580
+ else:
2581
+ final_value = source
2564
2582
  elif mode == 'add':
2565
2583
  # Copy & add - preserve target and add source
2566
- from .cssl_types import CSSLInstance
2584
+ from .cssl_types import CSSLInstance, UniversalInstance, CSSLClass
2567
2585
 
2586
+ # Special handling for UniversalInstance + CSSLClass
2587
+ if isinstance(current_value, UniversalInstance) and isinstance(source, CSSLClass):
2588
+ # Inject class definition into universal instance
2589
+ current_value.set_member(source.name, source)
2590
+ final_value = current_value
2591
+ # Special handling for UniversalInstance + Function (AST node)
2592
+ elif isinstance(current_value, UniversalInstance) and isinstance(source, ASTNode) and source.type == 'function':
2593
+ # Inject function as a method into universal instance
2594
+ func_info = source.value
2595
+ func_name = func_info.get('name') if isinstance(func_info, dict) else None
2596
+ if func_name:
2597
+ current_value.set_method(func_name, source, self)
2598
+ final_value = current_value
2599
+ # Special handling for UniversalInstance + CSSLInstance
2600
+ elif isinstance(current_value, UniversalInstance) and isinstance(source, CSSLInstance):
2601
+ class_name = source._class.name
2602
+ current_value.set_member(class_name, source)
2603
+ final_value = current_value
2568
2604
  # Special handling for CSSLInstance - merge classes
2569
- if isinstance(current_value, CSSLInstance) and isinstance(source, CSSLInstance):
2605
+ elif isinstance(current_value, CSSLInstance) and isinstance(source, CSSLInstance):
2570
2606
  # Add the new class instance as a member with class name as key
2571
2607
  class_name = source._class.name
2572
2608
  current_value._members[class_name] = source
@@ -2873,7 +2909,7 @@ class CSSLRuntime:
2873
2909
  func_info = child.value
2874
2910
  func_name = func_info.get('name') if isinstance(func_info, dict) else None
2875
2911
  if func_name:
2876
- instance.set_method(func_name, child)
2912
+ instance.set_method(func_name, child, self)
2877
2913
  elif child.type == 'var_declaration':
2878
2914
  # Extract variable and value
2879
2915
  var_info = child.value
@@ -3214,6 +3250,45 @@ class CSSLRuntime:
3214
3250
  # Return None if instance doesn't exist (can be created via ==>)
3215
3251
  return None
3216
3252
 
3253
+ # v4.1.0: Cross-language instance reference: cpp$ClassName, py$Object
3254
+ if node.type == 'lang_instance_ref':
3255
+ ref = node.value # {'lang': 'cpp', 'instance': 'ClassName'}
3256
+ lang_id = ref['lang']
3257
+ instance_name = ref['instance']
3258
+
3259
+ # First, try to get the language support object from scope
3260
+ lang_support = self.scope.get(lang_id)
3261
+ if lang_support is None:
3262
+ lang_support = self.global_scope.get(lang_id)
3263
+
3264
+ # If not found in scope, try to get from modules
3265
+ if lang_support is None:
3266
+ lang_support = self._modules.get(lang_id)
3267
+
3268
+ # If still not found, try getting default language support
3269
+ if lang_support is None:
3270
+ from .cssl_languages import get_language
3271
+ lang_support = get_language(lang_id)
3272
+
3273
+ if lang_support is not None:
3274
+ # Check if it's a LanguageSupport object with get_instance method
3275
+ if hasattr(lang_support, 'get_instance'):
3276
+ instance = lang_support.get_instance(instance_name)
3277
+ if instance is not None:
3278
+ return instance
3279
+ # Check _instances dict directly
3280
+ if hasattr(lang_support, '_instances'):
3281
+ if instance_name in lang_support._instances:
3282
+ return lang_support._instances[instance_name]
3283
+
3284
+ # Build helpful error message
3285
+ hint = f"Use '{lang_id}.share(\"{instance_name}\", instance)' to register the instance first."
3286
+ raise self._format_error(
3287
+ node.line if hasattr(node, 'line') else 0,
3288
+ f"Cross-language instance '{lang_id}${instance_name}' not found",
3289
+ hint
3290
+ )
3291
+
3217
3292
  if node.type == 'new':
3218
3293
  # Create new instance of a class: new ClassName(args)
3219
3294
  return self._eval_new(node)
@@ -3287,6 +3362,14 @@ class CSSLRuntime:
3287
3362
  if node.type == 'unary':
3288
3363
  return self._eval_unary(node)
3289
3364
 
3365
+ # Increment: ++i or i++
3366
+ if node.type == 'increment':
3367
+ return self._eval_increment(node)
3368
+
3369
+ # Decrement: --i or i--
3370
+ if node.type == 'decrement':
3371
+ return self._eval_decrement(node)
3372
+
3290
3373
  if node.type == 'non_null_assert':
3291
3374
  # *$var, *@module, *identifier - assert value is not null/None
3292
3375
  operand = node.value.get('operand')
@@ -3572,6 +3655,74 @@ class CSSLRuntime:
3572
3655
 
3573
3656
  return None
3574
3657
 
3658
+ def _eval_increment(self, node: ASTNode) -> Any:
3659
+ """Evaluate increment operation (++i or i++)"""
3660
+ op_type = node.value.get('op') # 'prefix' or 'postfix'
3661
+ operand = node.value.get('operand')
3662
+
3663
+ # Get variable name
3664
+ var_name = None
3665
+ if isinstance(operand, ASTNode):
3666
+ if operand.type == 'identifier':
3667
+ var_name = operand.value
3668
+ elif operand.type == 'shared_ref':
3669
+ # Handle $var++
3670
+ var_name = operand.value
3671
+ current = self.shared_vars.get(var_name, 0)
3672
+ if op_type == 'prefix':
3673
+ self.shared_vars[var_name] = current + 1
3674
+ return current + 1
3675
+ else: # postfix
3676
+ self.shared_vars[var_name] = current + 1
3677
+ return current
3678
+
3679
+ if var_name:
3680
+ current = self.scope.get(var_name, 0)
3681
+ if op_type == 'prefix':
3682
+ # ++i: increment then return
3683
+ self.scope.set(var_name, current + 1)
3684
+ return current + 1
3685
+ else:
3686
+ # i++: return then increment
3687
+ self.scope.set(var_name, current + 1)
3688
+ return current
3689
+
3690
+ return None
3691
+
3692
+ def _eval_decrement(self, node: ASTNode) -> Any:
3693
+ """Evaluate decrement operation (--i or i--)"""
3694
+ op_type = node.value.get('op') # 'prefix' or 'postfix'
3695
+ operand = node.value.get('operand')
3696
+
3697
+ # Get variable name
3698
+ var_name = None
3699
+ if isinstance(operand, ASTNode):
3700
+ if operand.type == 'identifier':
3701
+ var_name = operand.value
3702
+ elif operand.type == 'shared_ref':
3703
+ # Handle $var--
3704
+ var_name = operand.value
3705
+ current = self.shared_vars.get(var_name, 0)
3706
+ if op_type == 'prefix':
3707
+ self.shared_vars[var_name] = current - 1
3708
+ return current - 1
3709
+ else: # postfix
3710
+ self.shared_vars[var_name] = current - 1
3711
+ return current
3712
+
3713
+ if var_name:
3714
+ current = self.scope.get(var_name, 0)
3715
+ if op_type == 'prefix':
3716
+ # --i: decrement then return
3717
+ self.scope.set(var_name, current - 1)
3718
+ return current - 1
3719
+ else:
3720
+ # i--: return then decrement
3721
+ self.scope.set(var_name, current - 1)
3722
+ return current
3723
+
3724
+ return None
3725
+
3575
3726
  def _eval_call(self, node: ASTNode) -> Any:
3576
3727
  """Evaluate function call with optional named arguments"""
3577
3728
  callee_node = node.value.get('callee')
@@ -3783,6 +3934,22 @@ class CSSLRuntime:
3783
3934
  hint
3784
3935
  )
3785
3936
 
3937
+ # If we got a variable that holds a class reference, use that class
3938
+ if not isinstance(class_def, CSSLClass):
3939
+ # Check if it's a variable holding a class (dynamic class instantiation)
3940
+ if hasattr(class_def, '__class__') and isinstance(class_def, CSSLClass):
3941
+ pass # Already a CSSLClass, continue
3942
+ elif isinstance(class_def, dict) and 'class_def' in class_def:
3943
+ # Injected class reference from +<== operator
3944
+ class_def = class_def['class_def']
3945
+ else:
3946
+ # Not a class - show error
3947
+ raise CSSLRuntimeError(
3948
+ f"'{class_name}' is not a class",
3949
+ node.line,
3950
+ hint=f"'{class_name}' is of type {type(class_def).__name__}"
3951
+ )
3952
+
3786
3953
  if not isinstance(class_def, CSSLClass):
3787
3954
  raise CSSLRuntimeError(
3788
3955
  f"'{class_name}' is not a class",