minecraft-datapack-language 15.4.25__py3-none-any.whl → 15.4.27__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.
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '15.4.25'
32
- __version_tuple__ = version_tuple = (15, 4, 25)
31
+ __version__ = version = '15.4.27'
32
+ __version_tuple__ = version_tuple = (15, 4, 27)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -21,6 +21,17 @@ from .cli_colors import (
21
21
  )
22
22
 
23
23
 
24
+ def _extract_scope_selector(var_name: str) -> tuple[str, str]:
25
+ """Extract scope selector from variable name like 'player_score<@s>' -> ('player_score', '@s')"""
26
+ if '<' in var_name and var_name.endswith('>'):
27
+ parts = var_name.split('<', 1)
28
+ if len(parts) == 2:
29
+ base_name = parts[0]
30
+ scope_selector = parts[1][:-1] # Remove trailing >
31
+ return base_name, scope_selector
32
+ return var_name, "@s" # Default to @s if no scope specified
33
+
34
+
24
35
  class BuildContext:
25
36
  """Context for build operations to prevent race conditions."""
26
37
 
@@ -194,7 +205,13 @@ def _generate_scoreboard_objectives(ast: Dict[str, Any], output_dir: Path) -> Li
194
205
 
195
206
  # Create scoreboard objectives in the order they were found
196
207
  for var_name in variables:
197
- scoreboard_commands.append(f"scoreboard objectives add {var_name} dummy")
208
+ # Extract base variable name from scoped variables
209
+ base_var_name, _ = _extract_scope_selector(var_name)
210
+ scoreboard_commands.append(f"scoreboard objectives add {base_var_name} dummy")
211
+
212
+ # Add temporary variables for complex expressions (up to 10 temp variables)
213
+ for i in range(10):
214
+ scoreboard_commands.append(f"scoreboard objectives add temp_{i} dummy")
198
215
 
199
216
  return scoreboard_commands
200
217
 
@@ -299,6 +316,188 @@ def _process_say_command_with_variables(content: str, selector: str, variable_sc
299
316
  return f'tellraw @a [{components_str}]'
300
317
 
301
318
 
319
+ def _process_complex_expression(expression: Any, target_selector: str, target_var: str, variable_scopes: Dict[str, str] = None, temp_var_counter: int = 0) -> tuple[List[str], int]:
320
+ """
321
+ Process complex expressions and break them down into intermediate variables.
322
+ Returns (commands, new_temp_counter)
323
+ """
324
+ if variable_scopes is None:
325
+ variable_scopes = {}
326
+
327
+ commands = []
328
+
329
+ # Handle different expression types
330
+ if hasattr(expression, '__class__'):
331
+ class_name = str(expression.__class__)
332
+
333
+ if 'BinaryExpression' in class_name:
334
+ # Binary expression: left operator right
335
+ if hasattr(expression, 'left') and hasattr(expression, 'right') and hasattr(expression, 'operator'):
336
+ left = expression.left
337
+ right = expression.right
338
+ operator = expression.operator
339
+
340
+ # Process left side
341
+ if hasattr(left, 'name'):
342
+ # Left is a variable
343
+ left_base_name, left_selector = _extract_scope_selector(left.name)
344
+ if left_selector == "@s" and variable_scopes and left_base_name in variable_scopes:
345
+ left_selector = variable_scopes[left_base_name]
346
+ if left_selector == 'global':
347
+ left_selector = "@e[type=armor_stand,tag=mdl_server,limit=1]"
348
+
349
+ # Process right side
350
+ if hasattr(right, 'value') and isinstance(right.value, (int, str)):
351
+ # Right is a literal
352
+ if operator == 'PLUS':
353
+ commands.append(f"scoreboard players add {target_selector} {target_var} {right.value}")
354
+ elif operator == 'MINUS':
355
+ commands.append(f"scoreboard players remove {target_selector} {target_var} {right.value}")
356
+ elif operator == 'MULTIPLY':
357
+ # For multiplication, we need to use operations
358
+ temp_var = f"temp_{temp_var_counter}"
359
+ temp_var_counter += 1
360
+ commands.append(f"scoreboard players set {target_selector} {temp_var} {right.value}")
361
+ commands.append(f"scoreboard players operation {target_selector} {target_var} *= {target_selector} {temp_var}")
362
+ elif operator == 'DIVIDE':
363
+ # For division, we need to use operations
364
+ temp_var = f"temp_{temp_var_counter}"
365
+ temp_var_counter += 1
366
+ commands.append(f"scoreboard players set {target_selector} {temp_var} {right.value}")
367
+ commands.append(f"scoreboard players operation {target_selector} {target_var} /= {target_selector} {temp_var}")
368
+ else:
369
+ # Other operators - use operation
370
+ commands.append(f"# Complex operation: {target_var} = {left.name} {operator} {right.value}")
371
+ elif hasattr(right, 'name'):
372
+ # Right is also a variable - need to use operations
373
+ right_base_name, right_selector = _extract_scope_selector(right.name)
374
+ if right_selector == "@s" and variable_scopes and right_base_name in variable_scopes:
375
+ right_selector = variable_scopes[right_base_name]
376
+ if right_selector == 'global':
377
+ right_selector = "@e[type=armor_stand,tag=mdl_server,limit=1]"
378
+
379
+ if operator == 'PLUS':
380
+ # First set target to left value
381
+ commands.append(f"scoreboard players operation {target_selector} {target_var} = {left_selector} {left_base_name}")
382
+ # Then add right value
383
+ commands.append(f"scoreboard players operation {target_selector} {target_var} += {right_selector} {right_base_name}")
384
+ elif operator == 'MINUS':
385
+ # First set target to left value
386
+ commands.append(f"scoreboard players operation {target_selector} {target_var} = {left_selector} {left_base_name}")
387
+ # Then subtract right value
388
+ commands.append(f"scoreboard players operation {target_selector} {target_var} -= {right_selector} {right_base_name}")
389
+ elif operator == 'MULTIPLY':
390
+ # First set target to left value
391
+ commands.append(f"scoreboard players operation {target_selector} {target_var} = {left_selector} {left_base_name}")
392
+ # Then multiply by right value
393
+ commands.append(f"scoreboard players operation {target_selector} {target_var} *= {right_selector} {right_base_name}")
394
+ elif operator == 'DIVIDE':
395
+ # First set target to left value
396
+ commands.append(f"scoreboard players operation {target_selector} {target_var} = {left_selector} {left_base_name}")
397
+ # Then divide by right value
398
+ commands.append(f"scoreboard players operation {target_selector} {target_var} /= {right_selector} {right_base_name}")
399
+ else:
400
+ # Other operators - use operation
401
+ commands.append(f"# Complex operation: {target_var} = {left.name} {operator} {right.name}")
402
+ else:
403
+ # Complex right side - need to process recursively
404
+ temp_var = f"temp_{temp_var_counter}"
405
+ temp_var_counter += 1
406
+ right_commands, temp_var_counter = _process_complex_expression(right, target_selector, temp_var, variable_scopes, temp_var_counter)
407
+ commands.extend(right_commands)
408
+
409
+ # Now perform the operation
410
+ if operator == 'PLUS':
411
+ commands.append(f"scoreboard players operation {target_selector} {target_var} += {target_selector} {temp_var}")
412
+ elif operator == 'MINUS':
413
+ commands.append(f"scoreboard players operation {target_selector} {target_var} -= {target_selector} {temp_var}")
414
+ elif operator == 'MULTIPLY':
415
+ commands.append(f"scoreboard players operation {target_selector} {target_var} *= {target_selector} {temp_var}")
416
+ elif operator == 'DIVIDE':
417
+ commands.append(f"scoreboard players operation {target_selector} {target_var} /= {target_selector} {temp_var}")
418
+ else:
419
+ commands.append(f"# Complex operation: {target_var} = {left.name} {operator} {temp_var}")
420
+ else:
421
+ # Left is not a simple variable - need to process recursively
422
+ temp_var = f"temp_{temp_var_counter}"
423
+ temp_var_counter += 1
424
+ left_commands, temp_var_counter = _process_complex_expression(left, target_selector, temp_var, variable_scopes, temp_var_counter)
425
+ commands.extend(left_commands)
426
+
427
+ # Now process the right side
428
+ if hasattr(right, 'value') and isinstance(right.value, (int, str)):
429
+ # Right is a literal
430
+ if operator == 'PLUS':
431
+ commands.append(f"scoreboard players add {target_selector} {target_var} {right.value}")
432
+ elif operator == 'MINUS':
433
+ commands.append(f"scoreboard players remove {target_selector} {target_var} {right.value}")
434
+ else:
435
+ commands.append(f"# Complex operation: {target_var} = {temp_var} {operator} {right.value}")
436
+ else:
437
+ # Right is also complex - need to process recursively
438
+ right_temp_var = f"temp_{temp_var_counter}"
439
+ temp_var_counter += 1
440
+ right_commands, temp_var_counter = _process_complex_expression(right, target_selector, right_temp_var, variable_scopes, temp_var_counter)
441
+ commands.extend(right_commands)
442
+
443
+ # Now perform the operation between the two temp variables
444
+ if operator == 'PLUS':
445
+ commands.append(f"scoreboard players operation {target_selector} {target_var} = {target_selector} {temp_var}")
446
+ commands.append(f"scoreboard players operation {target_selector} {target_var} += {target_selector} {right_temp_var}")
447
+ elif operator == 'MINUS':
448
+ commands.append(f"scoreboard players operation {target_selector} {target_var} = {target_selector} {temp_var}")
449
+ commands.append(f"scoreboard players operation {target_selector} {target_var} -= {target_selector} {right_temp_var}")
450
+ elif operator == 'MULTIPLY':
451
+ commands.append(f"scoreboard players operation {target_selector} {target_var} = {target_selector} {temp_var}")
452
+ commands.append(f"scoreboard players operation {target_selector} {target_var} *= {target_selector} {right_temp_var}")
453
+ elif operator == 'DIVIDE':
454
+ commands.append(f"scoreboard players operation {target_selector} {target_var} = {target_selector} {temp_var}")
455
+ commands.append(f"scoreboard players operation {target_selector} {target_var} /= {target_selector} {right_temp_var}")
456
+ else:
457
+ commands.append(f"# Complex operation: {target_var} = {temp_var} {operator} {right_temp_var}")
458
+ else:
459
+ commands.append(f"# Malformed binary expression: {expression}")
460
+
461
+ elif 'VariableExpression' in class_name:
462
+ # Simple variable reference
463
+ if hasattr(expression, 'name'):
464
+ var_name = expression.name
465
+ base_var_name, var_selector = _extract_scope_selector(var_name)
466
+ if var_selector == "@s" and variable_scopes and base_var_name in variable_scopes:
467
+ var_selector = variable_scopes[base_var_name]
468
+ if var_selector == 'global':
469
+ var_selector = "@e[type=armor_stand,tag=mdl_server,limit=1]"
470
+
471
+ commands.append(f"scoreboard players operation {target_selector} {target_var} = {var_selector} {base_var_name}")
472
+ else:
473
+ commands.append(f"# Malformed variable expression: {expression}")
474
+
475
+ elif 'LiteralExpression' in class_name:
476
+ # Literal value
477
+ if hasattr(expression, 'value'):
478
+ try:
479
+ num_value = int(expression.value)
480
+ commands.append(f"scoreboard players set {target_selector} {target_var} {num_value}")
481
+ except (ValueError, TypeError):
482
+ commands.append(f"# Cannot convert literal to number: {expression.value}")
483
+ else:
484
+ commands.append(f"# Malformed literal expression: {expression}")
485
+
486
+ else:
487
+ # Unknown expression type
488
+ commands.append(f"# Unknown expression type: {class_name} - {expression}")
489
+
490
+ else:
491
+ # Direct value
492
+ try:
493
+ num_value = int(expression)
494
+ commands.append(f"scoreboard players set {target_selector} {target_var} {num_value}")
495
+ except (ValueError, TypeError):
496
+ commands.append(f"# Cannot convert expression to number: {expression}")
497
+
498
+ return commands, temp_var_counter
499
+
500
+
302
501
  def _process_statement(statement: Any, namespace: str, function_name: str, statement_index: int = 0, is_tag_function: bool = False, selector: str = "@s", variable_scopes: Dict[str, str] = None, build_context: BuildContext = None, output_dir: Path = None) -> List[str]:
303
502
  """Process a single statement and return Minecraft commands."""
304
503
  if variable_scopes is None:
@@ -336,65 +535,68 @@ def _process_statement(statement: Any, namespace: str, function_name: str, state
336
535
  var_name = statement['name']
337
536
  value = statement['value']
338
537
 
339
- # Determine the correct selector for this variable based on its declared scope
340
- var_selector = selector # Default to current selector
341
- if variable_scopes and var_name in variable_scopes:
342
- declared_scope = variable_scopes[var_name]
538
+ # Extract scope selector from variable name (e.g., 'player_score<@s>' -> 'player_score', '@s')
539
+ base_var_name, var_selector = _extract_scope_selector(var_name)
540
+
541
+ # If no scope selector in name, fall back to declared scope
542
+ if var_selector == "@s" and variable_scopes and base_var_name in variable_scopes:
543
+ declared_scope = variable_scopes[base_var_name]
343
544
  if declared_scope == 'global':
344
545
  var_selector = "@e[type=armor_stand,tag=mdl_server,limit=1]"
345
546
  else:
346
547
  var_selector = declared_scope
347
- print(f"DEBUG: Variable {var_name} assignment using selector: {var_selector} (declared scope: {variable_scopes.get(var_name, 'none')})")
548
+
549
+ print(f"DEBUG: Variable {base_var_name} assignment using selector: {var_selector} (from name: {var_name})")
348
550
 
349
551
  # Handle different value types
350
552
  if isinstance(value, int):
351
- commands.append(f"scoreboard players set {var_selector} {var_name} {value}")
553
+ commands.append(f"scoreboard players set {var_selector} {base_var_name} {value}")
352
554
  elif isinstance(value, str) and value.startswith('$') and value.endswith('$'):
353
555
  # Variable reference
354
556
  ref_var = value[1:-1] # Remove $ symbols
355
- commands.append(f"scoreboard players operation {var_selector} {var_name} = {var_selector} {ref_var}")
356
- elif hasattr(value, '__class__') and 'BinaryExpression' in str(value.__class__):
357
- # Handle complex expressions (BinaryExpression, etc.)
358
- # Convert to proper Minecraft scoreboard commands
359
- if hasattr(value, 'left') and hasattr(value, 'right') and hasattr(value, 'operator'):
360
- left = value.left
361
- right = value.right
362
- operator = value.operator
363
-
364
- # Handle different operators
365
- if operator == 'PLUS':
366
- if hasattr(left, 'name') and hasattr(right, 'value'):
367
- # counter = counter + 1
368
- commands.append(f"scoreboard players add {var_selector} {var_name} {right.value}")
369
- else:
370
- # Complex case - use operation
371
- commands.append(f"# Complex addition: {var_name} = {left} + {right}")
372
- elif operator == 'MINUS':
373
- if hasattr(left, 'name') and hasattr(right, 'value'):
374
- # health = health - 10
375
- commands.append(f"scoreboard players remove {var_selector} {var_name} {right.value}")
376
- else:
377
- # Complex case - use operation
378
- commands.append(f"# Complex operation: {var_name} = {left} - {right}")
557
+ # Extract scope from reference variable if it has one
558
+ ref_base_name, ref_selector = _extract_scope_selector(ref_var)
559
+ if ref_selector == "@s" and variable_scopes and ref_base_name in variable_scopes:
560
+ declared_scope = variable_scopes[ref_base_name]
561
+ if declared_scope == 'global':
562
+ ref_selector = "@e[type=armor_stand,tag=mdl_server,limit=1]"
379
563
  else:
380
- # Other operators - use operation
381
- commands.append(f"# Complex operation: {var_name} = {left} {operator} {right}")
564
+ ref_selector = declared_scope
565
+ commands.append(f"scoreboard players operation {var_selector} {base_var_name} = {ref_selector} {ref_base_name}")
566
+ elif hasattr(value, '__class__') and 'VariableExpression' in str(value.__class__):
567
+ # Variable expression (e.g., playerCounter<@s> = globalCounter<@a>)
568
+ if hasattr(value, 'name'):
569
+ ref_var = value.name
570
+ # Extract scope from reference variable
571
+ ref_base_name, ref_selector = _extract_scope_selector(ref_var)
572
+ if ref_selector == "@s" and variable_scopes and ref_base_name in variable_scopes:
573
+ declared_scope = variable_scopes[ref_base_name]
574
+ if declared_scope == 'global':
575
+ ref_selector = "@e[type=armor_stand,tag=mdl_server,limit=1]"
576
+ else:
577
+ ref_selector = declared_scope
578
+ commands.append(f"scoreboard players operation {var_selector} {base_var_name} = {ref_selector} {ref_base_name}")
382
579
  else:
383
- commands.append(f"# Complex assignment: {var_name} = {value}")
580
+ commands.append(f"# Variable expression assignment: {base_var_name} = {value}")
581
+ elif hasattr(value, '__class__') and 'BinaryExpression' in str(value.__class__):
582
+ # Handle complex expressions using the enhanced expression processor
583
+ # This will break down complex expressions into intermediate variables
584
+ expression_commands, _ = _process_complex_expression(value, var_selector, base_var_name, variable_scopes, 0)
585
+ commands.extend(expression_commands)
384
586
  else:
385
587
  # Handle LiteralExpression and other value types
386
588
  try:
387
589
  if hasattr(value, 'value'):
388
590
  # LiteralExpression case
389
591
  num_value = int(value.value)
390
- commands.append(f"scoreboard players set {var_selector} {var_name} {num_value}")
592
+ commands.append(f"scoreboard players set {var_selector} {base_var_name} {num_value}")
391
593
  else:
392
594
  # Direct value case
393
595
  num_value = int(value)
394
- commands.append(f"scoreboard players set {var_selector} {var_name} {num_value}")
596
+ commands.append(f"scoreboard players set {var_selector} {base_var_name} {num_value}")
395
597
  except (ValueError, TypeError):
396
598
  # If we can't convert to int, add a placeholder
397
- commands.append(f"# Assignment: {var_name} = {value}")
599
+ commands.append(f"# Assignment: {base_var_name} = {value}")
398
600
 
399
601
  elif statement['type'] == 'if_statement':
400
602
  condition = statement['condition']
@@ -507,11 +709,21 @@ def _generate_function_file(ast: Dict[str, Any], output_dir: Path, namespace: st
507
709
  for var_decl in ast['variables']:
508
710
  var_name = var_decl['name']
509
711
  var_scope = var_decl.get('scope')
510
- if var_scope:
511
- variable_scopes[var_name] = var_scope
512
- print(f"DEBUG: Variable {var_name} has scope {var_scope}")
712
+
713
+ # Extract base variable name and scope selector
714
+ base_var_name, scope_selector = _extract_scope_selector(var_name)
715
+
716
+ if scope_selector != "@s":
717
+ # Variable has explicit scope selector
718
+ variable_scopes[base_var_name] = scope_selector
719
+ print(f"DEBUG: Variable {base_var_name} has scope {scope_selector} from name {var_name}")
720
+ elif var_scope:
721
+ # Variable has scope from scope field (legacy support)
722
+ variable_scopes[base_var_name] = var_scope
723
+ print(f"DEBUG: Variable {base_var_name} has scope {var_scope} from scope field")
513
724
  else:
514
- print(f"DEBUG: Variable {var_name} has no scope (defaults to @s)")
725
+ # Variable has no scope (defaults to @s)
726
+ print(f"DEBUG: Variable {base_var_name} has no scope (defaults to @s)")
515
727
 
516
728
  print(f"DEBUG: Collected variable scopes: {variable_scopes}")
517
729
 
@@ -317,9 +317,29 @@ class MDLParser:
317
317
  )
318
318
 
319
319
  # Check if this is a variable assignment (identifier followed by =)
320
+ # Need to handle scope selectors like: identifier<scope> = ...
320
321
  if (self.current + 1 < len(self.tokens) and
321
322
  self.tokens[self.current + 1].type == TokenType.ASSIGN):
322
323
  return self._parse_variable_assignment()
324
+ elif (self.current + 1 < len(self.tokens) and
325
+ self.tokens[self.current + 1].type == TokenType.LANGLE):
326
+ # This might be a scoped variable assignment: identifier<scope> = ...
327
+ # Look ahead to see if there's an assignment after the scope selector
328
+ temp_current = self.current + 1
329
+ while (temp_current < len(self.tokens) and
330
+ self.tokens[temp_current].type != TokenType.RANGLE and
331
+ self.tokens[temp_current].type != TokenType.ASSIGN):
332
+ temp_current += 1
333
+
334
+ if (temp_current < len(self.tokens) and
335
+ self.tokens[temp_current].type == TokenType.RANGLE and
336
+ temp_current + 1 < len(self.tokens) and
337
+ self.tokens[temp_current + 1].type == TokenType.ASSIGN):
338
+ # This is a scoped variable assignment
339
+ return self._parse_variable_assignment()
340
+ else:
341
+ # This is a command with scope selector
342
+ return self._parse_command()
323
343
  else:
324
344
  # Assume it's a command
325
345
  return self._parse_command()
@@ -337,31 +357,29 @@ class MDLParser:
337
357
 
338
358
  # Check for scope selector after variable name
339
359
  scope = None
340
- if not self._is_at_end() and self._peek().type == TokenType.SCOPE:
341
- self._match(TokenType.SCOPE) # consume 'scope'
360
+ if not self._is_at_end() and self._peek().type == TokenType.LANGLE:
361
+ self._match(TokenType.LANGLE) # consume '<'
342
362
 
343
- # Parse scope selector in angle brackets
344
- if not self._is_at_end() and self._peek().type == TokenType.LANGLE:
345
- self._match(TokenType.LANGLE) # consume '<'
346
-
347
- # Parse scope selector content
348
- scope_parts = []
349
- while not self._is_at_end() and self._peek().type != TokenType.RANGLE:
350
- scope_parts.append(self._peek().value)
351
- self._advance()
352
-
353
- if self._is_at_end():
354
- raise create_parser_error(
355
- message="Unterminated scope selector",
356
- file_path=self.source_file,
357
- line=self._peek().line,
358
- column=self._peek().column,
359
- line_content=self._peek().value,
360
- suggestion="Add a closing '>' to terminate the scope selector"
361
- )
362
-
363
- self._match(TokenType.RANGLE) # consume '>'
364
- scope = ''.join(scope_parts)
363
+ # Parse scope selector content
364
+ scope_parts = []
365
+ while not self._is_at_end() and self._peek().type != TokenType.RANGLE:
366
+ scope_parts.append(self._peek().value)
367
+ self._advance()
368
+
369
+ if self._is_at_end():
370
+ raise create_parser_error(
371
+ message="Unterminated scope selector",
372
+ file_path=self.source_file,
373
+ line=self._peek().line,
374
+ column=self._peek().column,
375
+ line_content=self._peek().value,
376
+ suggestion="Add a closing '>' to terminate the scope selector"
377
+ )
378
+
379
+ self._match(TokenType.RANGLE) # consume '>'
380
+ scope = ''.join(scope_parts)
381
+ # Update the name to include the scope selector
382
+ name = f"{name}<{scope}>"
365
383
 
366
384
  self._match(TokenType.ASSIGN)
367
385
 
@@ -901,15 +919,33 @@ class MDLParser:
901
919
  identifier_name = token.value
902
920
  self._advance() # consume the identifier
903
921
 
904
- # Check if the identifier contains a scope selector
905
- if '<' in identifier_name and identifier_name.endswith('>'):
906
- # Extract variable name and scope selector
907
- parts = identifier_name.split('<', 1)
908
- if len(parts) == 2:
909
- var_name = parts[0]
910
- scope_selector = parts[1][:-1] # Remove the closing >
911
- # For variable expressions in assignments, keep the full scoped name
912
- return VariableExpression(identifier_name)
922
+ # Check if this identifier is followed by a scope selector
923
+ if not self._is_at_end() and self._peek().type == TokenType.LANGLE:
924
+ # This is a scoped variable - parse the scope selector
925
+ self._match(TokenType.LANGLE) # consume '<'
926
+
927
+ # Parse scope selector content
928
+ scope_parts = []
929
+ while not self._is_at_end() and self._peek().type != TokenType.RANGLE:
930
+ scope_parts.append(self._peek().value)
931
+ self._advance()
932
+
933
+ if self._is_at_end():
934
+ raise create_parser_error(
935
+ message="Unterminated scope selector in expression",
936
+ file_path=self.source_file,
937
+ line=self._peek().line,
938
+ column=self._peek().column,
939
+ line_content=self._peek().value,
940
+ suggestion="Add a closing '>' to terminate the scope selector"
941
+ )
942
+
943
+ self._match(TokenType.RANGLE) # consume '>'
944
+ scope_selector = ''.join(scope_parts)
945
+
946
+ # Create a scoped variable expression
947
+ full_name = f"{identifier_name}<{scope_selector}>"
948
+ return VariableExpression(full_name)
913
949
 
914
950
  # Regular variable expression without scope
915
951
  return VariableExpression(identifier_name)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: minecraft-datapack-language
3
- Version: 15.4.25
3
+ Version: 15.4.27
4
4
  Summary: Compile JavaScript-style MDL language or Python API into a Minecraft datapack (1.21+ ready). Features variables, control flow, error handling, and VS Code extension.
5
5
  Project-URL: Homepage, https://www.mcmdl.com
6
6
  Project-URL: Documentation, https://www.mcmdl.com/docs
@@ -1,8 +1,8 @@
1
1
  minecraft_datapack_language/__init__.py,sha256=i-qCchbe5b2Fshgc6yCU9mddOLs2UBt9SAcLqfUIrT0,606
2
- minecraft_datapack_language/_version.py,sha256=cpNlMoFPJRzh32B2Q7_IwcsrxLVLvnVsyE7jWlQC1dM,708
2
+ minecraft_datapack_language/_version.py,sha256=LA-jQi7TFEITqcWkyJirDcPThmV9RoX2XbnqzYm2Uh8,708
3
3
  minecraft_datapack_language/ast_nodes.py,sha256=U-CB3R7p7OjaljI47BWsRrQtliw0yAWUrJVoxzOLmQw,2143
4
4
  minecraft_datapack_language/cli.py,sha256=p5A_tEEXugN2NhQFbbgfwi4FxbWYD91RWeKR_A3Vuec,6263
5
- minecraft_datapack_language/cli_build.py,sha256=u0XIOH_zTARPC6dvWf-411OqrF7RjT7Z9Hkp6hTeZsI,47990
5
+ minecraft_datapack_language/cli_build.py,sha256=AfS_fQmLmpUTF_WNNT6SiA7u0vomX_eR1afq6gczQtE,62128
6
6
  minecraft_datapack_language/cli_check.py,sha256=bPq9gHsxQ1CIiftkrAtRCifWkVAyjp5c8Oay2NNQ1qs,6277
7
7
  minecraft_datapack_language/cli_colors.py,sha256=Hr8awY966bGSnVdXL3WnmRhSP1wH56vTQKGt5z-kIQM,7878
8
8
  minecraft_datapack_language/cli_help.py,sha256=Rc-v9E2kctsdN_lMunqdKuZ8EZ8rcZIjBCOPrXLBQeE,21363
@@ -14,12 +14,12 @@ minecraft_datapack_language/linter.py,sha256=7UqbygC5JPCGg-BSOq65NB2xEJBu_OUOYII
14
14
  minecraft_datapack_language/mdl_errors.py,sha256=mz6uyPkeBpbMHj4PiAyVecEVJ9_hdSfR45QAjG6oYf0,15690
15
15
  minecraft_datapack_language/mdl_lexer_js.py,sha256=VvbhKm727khdSAABxa03hoIIA7H3hWi3RLp9BSXbhY0,28277
16
16
  minecraft_datapack_language/mdl_linter.py,sha256=z85xoAglENurCh30bR7kEHZ_JeMxcYaLDcGNRAl-RAI,17253
17
- minecraft_datapack_language/mdl_parser_js.py,sha256=15E_OXXEHYmGAaqS-eCqI-sSIJ4LDk6soSeBbfo_qVg,43350
17
+ minecraft_datapack_language/mdl_parser_js.py,sha256=aY841JdlXSTjR-TYb6KpmPrM87tEyb8lBqoHoB-Meng,45210
18
18
  minecraft_datapack_language/pack.py,sha256=nYiXQ3jgJlDfc4m-65f7C2LFhDRioaUU_XVy6Na4SJI,34625
19
19
  minecraft_datapack_language/utils.py,sha256=Aq0HAGlXqj9BUTEjaEilpvzEW0EtZYYMMwOqG9db6dE,684
20
- minecraft_datapack_language-15.4.25.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
21
- minecraft_datapack_language-15.4.25.dist-info/METADATA,sha256=YGa1EZ268bYICR68n8J-LipaKPObtEORC5jTL4oWXro,37917
22
- minecraft_datapack_language-15.4.25.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- minecraft_datapack_language-15.4.25.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
24
- minecraft_datapack_language-15.4.25.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
25
- minecraft_datapack_language-15.4.25.dist-info/RECORD,,
20
+ minecraft_datapack_language-15.4.27.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
21
+ minecraft_datapack_language-15.4.27.dist-info/METADATA,sha256=cChRD87FAKcP1BFBgNAESn7BKhGn4E565y2DfHQZZws,37917
22
+ minecraft_datapack_language-15.4.27.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ minecraft_datapack_language-15.4.27.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
24
+ minecraft_datapack_language-15.4.27.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
25
+ minecraft_datapack_language-15.4.27.dist-info/RECORD,,