minecraft-datapack-language 15.4.31__py3-none-any.whl → 15.4.33__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.31'
32
- __version_tuple__ = version_tuple = (15, 4, 31)
31
+ __version__ = version = '15.4.33'
32
+ __version_tuple__ = version_tuple = (15, 4, 33)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -26,6 +26,8 @@ Examples:
26
26
  mdl new my_project # Create a new project
27
27
  """
28
28
  )
29
+ # Global options
30
+ parser.add_argument('--version', action='store_true', help='Show version and exit')
29
31
 
30
32
  subparsers = parser.add_subparsers(dest='command', help='Available commands')
31
33
 
@@ -34,6 +36,7 @@ Examples:
34
36
  build_parser.add_argument('--mdl', required=True, help='MDL file(s) or directory to build')
35
37
  build_parser.add_argument('-o', '--output', required=True, help='Output directory for the datapack')
36
38
  build_parser.add_argument('--verbose', action='store_true', help='Show detailed output')
39
+ build_parser.add_argument('--wrapper', help='Optional wrapper directory name for the datapack output')
37
40
 
38
41
  # Check command
39
42
  check_parser = subparsers.add_parser('check', help='Check MDL files for syntax errors')
@@ -45,9 +48,19 @@ Examples:
45
48
  new_parser.add_argument('project_name', help='Name of the new project')
46
49
  new_parser.add_argument('--pack-name', help='Custom name for the datapack')
47
50
  new_parser.add_argument('--pack-format', type=int, default=82, help='Pack format number (default: 82)')
51
+ new_parser.add_argument('--output', help='Directory to create the project in (defaults to current directory)')
48
52
 
49
53
  args = parser.parse_args()
50
54
 
55
+ if args.version and not args.command:
56
+ # Print version and exit
57
+ try:
58
+ from . import __version__
59
+ except Exception:
60
+ __version__ = "0.0.0"
61
+ print(__version__)
62
+ return 0
63
+
51
64
  if not args.command:
52
65
  parser.print_help()
53
66
  return 1
@@ -133,6 +146,7 @@ def build_command(args):
133
146
  print(f"Compiling to {output_dir}...")
134
147
 
135
148
  compiler = MDLCompiler()
149
+ # Note: --wrapper is currently accepted for compatibility but not required by compiler
136
150
  output_path = compiler.compile(final_ast, str(output_dir))
137
151
 
138
152
  print(f"Successfully built datapack: {output_path}")
@@ -193,9 +207,10 @@ def new_command(args):
193
207
  project_name = args.project_name
194
208
  pack_name = args.pack_name or project_name
195
209
  pack_format = args.pack_format
210
+ base_dir = Path(args.output) if getattr(args, 'output', None) else Path('.')
211
+ project_dir = base_dir / project_name
196
212
 
197
213
  # Create project directory
198
- project_dir = Path(project_name)
199
214
  if project_dir.exists():
200
215
  print(f"Error: Project directory '{project_name}' already exists")
201
216
  return 1
@@ -213,7 +228,7 @@ var num counter<@s> = 0;
213
228
  var num global_timer<@a> = 0;
214
229
 
215
230
  // Main function
216
- function {project_name}:main<@s> {{
231
+ function {project_name}:main {{
217
232
  say "Hello from {project_name}!";
218
233
 
219
234
  // Variable example
@@ -228,13 +243,13 @@ function {project_name}:main<@s> {{
228
243
  }}
229
244
  }}
230
245
 
231
- // Load function
232
- function {project_name}:load<@s> {{
233
- say "Datapack loaded successfully!";
246
+ // Init function (avoid reserved names like 'load' or 'tick')
247
+ function {project_name}:init {{
248
+ say "Datapack initialized successfully!";
234
249
  }}
235
250
 
236
251
  // Hook to run on load
237
- on_load {project_name}:load<@s>;
252
+ on_load {project_name}:init;
238
253
  '''
239
254
 
240
255
  with open(mdl_file, 'w', encoding='utf-8') as f:
@@ -281,7 +296,7 @@ For more information, visit: https://www.mcmdl.com
281
296
  with open(readme_file, 'w', encoding='utf-8') as f:
282
297
  f.write(readme_content)
283
298
 
284
- print(f"Created new MDL project: {project_name}/")
299
+ print(f"Created new MDL project: {project_dir}/")
285
300
  print(f" - {mdl_file}")
286
301
  print(f" - {readme_file}")
287
302
  print(f"\nNext steps:")
@@ -16,6 +16,7 @@ from .ast_nodes import (
16
16
  )
17
17
  from .dir_map import get_dir_map, DirMap
18
18
  from .mdl_errors import MDLCompilerError
19
+ from .mdl_lexer import TokenType
19
20
 
20
21
 
21
22
  class MDLCompiler:
@@ -147,12 +148,23 @@ class MDLCompiler:
147
148
  lines.append(f"# Scope: {func.scope}")
148
149
  lines.append("")
149
150
 
151
+ # Reset temporary commands for this function
152
+ if hasattr(self, 'temp_commands'):
153
+ self.temp_commands = []
154
+
150
155
  # Generate commands from function body
151
156
  for statement in func.body:
152
157
  cmd = self._statement_to_command(statement)
153
158
  if cmd:
154
159
  lines.append(cmd)
155
160
 
161
+ # Add any temporary commands that were generated during compilation
162
+ if hasattr(self, 'temp_commands') and self.temp_commands:
163
+ lines.append("")
164
+ lines.append("# Temporary variable operations:")
165
+ for temp_cmd in self.temp_commands:
166
+ lines.append(temp_cmd)
167
+
156
168
  return "\n".join(lines)
157
169
 
158
170
  def _compile_hooks(self, hooks: List[HookDeclaration], namespace_dir: Path):
@@ -290,9 +302,20 @@ class MDLCompiler:
290
302
  def _variable_assignment_to_command(self, assignment: VariableAssignment) -> str:
291
303
  """Convert variable assignment to scoreboard command."""
292
304
  objective = self.variables.get(assignment.name, assignment.name)
293
- value = self._expression_to_value(assignment.value)
294
305
  scope = assignment.scope.strip("<>")
295
- return f"scoreboard players set {scope} {objective} {value}"
306
+
307
+ # Check if the value is a complex expression
308
+ if isinstance(assignment.value, BinaryExpression):
309
+ # Complex expression - use temporary variable approach
310
+ temp_var = self._generate_temp_variable_name()
311
+ self._compile_expression_to_temp(assignment.value, temp_var)
312
+
313
+ # Return the command to set the target variable from the temp
314
+ return f"scoreboard players operation {scope} {objective} = @s {temp_var}"
315
+ else:
316
+ # Simple value - use direct assignment
317
+ value = self._expression_to_value(assignment.value)
318
+ return f"scoreboard players set {scope} {objective} {value}"
296
319
 
297
320
  def _say_command_to_command(self, say: SayCommand) -> str:
298
321
  """Convert say command to tellraw command with JSON formatting."""
@@ -348,94 +371,172 @@ class MDLCompiler:
348
371
  return f'tellraw @a {first_part}'
349
372
 
350
373
  def _if_statement_to_command(self, if_stmt: IfStatement) -> str:
351
- """Convert if statement to comment and include actual statements."""
352
- condition = self._expression_to_condition(if_stmt.condition)
353
- lines = [f"# if {condition}"]
374
+ """Convert if statement to proper Minecraft execute if commands."""
375
+ condition, invert_then = self._build_condition(if_stmt.condition)
376
+ lines = []
354
377
 
355
- # Include the actual statements from the if body for visibility
378
+ # Prepare function name for the then branch
379
+ if_function_name = self._generate_if_function_name()
380
+ # Generate condition command
381
+ if invert_then:
382
+ lines.append(f"execute unless {condition} run function {self.current_namespace}:{if_function_name}")
383
+ else:
384
+ lines.append(f"execute if {condition} run function {self.current_namespace}:{if_function_name}")
385
+
386
+ # Generate the if body function content
387
+ if_body_lines = [f"# Function: {self.current_namespace}:{if_function_name}"]
356
388
  for stmt in if_stmt.then_body:
357
389
  if isinstance(stmt, VariableAssignment):
358
- # Include variable assignments directly
359
390
  cmd = self._variable_assignment_to_command(stmt)
360
- lines.append(cmd)
391
+ if_body_lines.append(cmd)
361
392
  elif isinstance(stmt, SayCommand):
362
- # Include say commands directly
363
393
  cmd = self._say_command_to_command(stmt)
364
- lines.append(cmd)
394
+ if_body_lines.append(cmd)
365
395
  elif isinstance(stmt, RawBlock):
366
- # Include raw blocks directly
367
- lines.append(stmt.content)
396
+ if_body_lines.append(stmt.content)
368
397
  elif isinstance(stmt, IfStatement):
369
- # Recursively handle nested if statements
370
398
  cmd = self._if_statement_to_command(stmt)
371
- lines.append(cmd)
399
+ if_body_lines.append(cmd)
372
400
  elif isinstance(stmt, WhileLoop):
373
- # Handle while loops
374
401
  cmd = self._while_loop_to_command(stmt)
375
- lines.append(cmd)
402
+ if_body_lines.append(cmd)
376
403
  elif isinstance(stmt, FunctionCall):
377
- # Handle function calls
378
404
  cmd = self._function_call_to_command(stmt)
379
- lines.append(cmd)
405
+ if_body_lines.append(cmd)
380
406
 
381
407
  # Handle else body if it exists
382
408
  if if_stmt.else_body:
383
- lines.append("")
384
- lines.append("# else:")
385
- for stmt in if_stmt.else_body:
386
- if isinstance(stmt, VariableAssignment):
387
- cmd = self._variable_assignment_to_command(stmt)
388
- lines.append(cmd)
389
- elif isinstance(stmt, SayCommand):
390
- cmd = self._say_command_to_command(stmt)
391
- lines.append(cmd)
392
- elif isinstance(stmt, RawBlock):
393
- lines.append(stmt.content)
394
- elif isinstance(stmt, IfStatement):
395
- cmd = self._if_statement_to_command(stmt)
396
- lines.append(cmd)
397
- elif isinstance(stmt, WhileLoop):
398
- cmd = self._while_loop_to_command(stmt)
399
- lines.append(cmd)
400
- elif isinstance(stmt, FunctionCall):
401
- cmd = self._function_call_to_command(stmt)
402
- lines.append(cmd)
409
+ if isinstance(if_stmt.else_body, list) and len(if_stmt.else_body) == 1 and isinstance(if_stmt.else_body[0], IfStatement):
410
+ # Else-if: create an else function wrapper that contains the nested if
411
+ else_function_name = self._generate_else_function_name()
412
+ if invert_then:
413
+ lines.append(f"execute if {condition} run function {self.current_namespace}:{else_function_name}")
414
+ else:
415
+ lines.append(f"execute unless {condition} run function {self.current_namespace}:{else_function_name}")
416
+ else_body_lines = [f"# Function: {self.current_namespace}:{else_function_name}"]
417
+ nested_cmd = self._if_statement_to_command(if_stmt.else_body[0])
418
+ for nested_line in nested_cmd.split('\n'):
419
+ if nested_line:
420
+ else_body_lines.append(nested_line)
421
+ self._store_generated_function(else_function_name, else_body_lines)
422
+ else:
423
+ # Regular else: compile its body into its own function
424
+ else_function_name = self._generate_else_function_name()
425
+ if invert_then:
426
+ lines.append(f"execute if {condition} run function {self.current_namespace}:{else_function_name}")
427
+ else:
428
+ lines.append(f"execute unless {condition} run function {self.current_namespace}:{else_function_name}")
429
+ else_body_lines = [f"# Function: {self.current_namespace}:{else_function_name}"]
430
+ for stmt in if_stmt.else_body:
431
+ if isinstance(stmt, VariableAssignment):
432
+ cmd = self._variable_assignment_to_command(stmt)
433
+ else_body_lines.append(cmd)
434
+ elif isinstance(stmt, SayCommand):
435
+ cmd = self._say_command_to_command(stmt)
436
+ else_body_lines.append(cmd)
437
+ elif isinstance(stmt, RawBlock):
438
+ else_body_lines.append(stmt.content)
439
+ elif isinstance(stmt, IfStatement):
440
+ cmd = self._if_statement_to_command(stmt)
441
+ else_body_lines.append(cmd)
442
+ elif isinstance(stmt, WhileLoop):
443
+ cmd = self._while_loop_to_command(stmt)
444
+ else_body_lines.append(cmd)
445
+ elif isinstance(stmt, FunctionCall):
446
+ cmd = self._function_call_to_command(stmt)
447
+ else_body_lines.append(cmd)
448
+ self._store_generated_function(else_function_name, else_body_lines)
449
+
450
+ # Store the if function as its own file
451
+ self._store_generated_function(if_function_name, if_body_lines)
403
452
 
404
453
  return "\n".join(lines)
405
454
 
406
455
  def _while_loop_to_command(self, while_loop: WhileLoop) -> str:
407
- """Convert while loop to comment and include actual statements."""
456
+ """Convert while loop to proper Minecraft loop logic."""
408
457
  condition = self._expression_to_condition(while_loop.condition)
409
- lines = [f"# while {condition}"]
458
+ lines = []
459
+
460
+ # Generate the while loop using a recursive function approach
461
+ loop_function_name = self._generate_while_function_name()
462
+
463
+ # First, call the loop function
464
+ lines.append(f"function {self.current_namespace}:{loop_function_name}")
410
465
 
411
- # Include the actual statements from the while body for visibility
466
+ # Generate the loop function body
467
+ loop_body_lines = [f"# Function: {self.current_namespace}:{loop_function_name}"]
468
+
469
+ # Add the loop body statements
412
470
  for stmt in while_loop.body:
413
471
  if isinstance(stmt, VariableAssignment):
414
- # Include variable assignments directly
415
472
  cmd = self._variable_assignment_to_command(stmt)
416
- lines.append(cmd)
473
+ loop_body_lines.append(cmd)
417
474
  elif isinstance(stmt, SayCommand):
418
- # Include say commands directly
419
475
  cmd = self._say_command_to_command(stmt)
420
- lines.append(cmd)
476
+ loop_body_lines.append(cmd)
421
477
  elif isinstance(stmt, RawBlock):
422
- # Include raw blocks directly
423
- lines.append(stmt.content)
478
+ loop_body_lines.append(stmt.content)
424
479
  elif isinstance(stmt, IfStatement):
425
- # Recursively handle nested if statements
426
480
  cmd = self._if_statement_to_command(stmt)
427
- lines.append(cmd)
481
+ loop_body_lines.append(cmd)
428
482
  elif isinstance(stmt, WhileLoop):
429
- # Handle nested while loops
430
483
  cmd = self._while_loop_to_command(stmt)
431
- lines.append(cmd)
484
+ loop_body_lines.append(cmd)
432
485
  elif isinstance(stmt, FunctionCall):
433
- # Handle function calls
434
486
  cmd = self._function_call_to_command(stmt)
435
- lines.append(cmd)
487
+ loop_body_lines.append(cmd)
488
+
489
+ # Add the recursive call at the end to continue the loop
490
+ if self._is_scoreboard_condition(while_loop.condition):
491
+ loop_body_lines.append(f"execute if score {condition} run function {self.current_namespace}:{loop_function_name}")
492
+ else:
493
+ loop_body_lines.append(f"execute if {condition} run function {self.current_namespace}:{loop_function_name}")
494
+
495
+ # Store the loop function as its own file
496
+ self._store_generated_function(loop_function_name, loop_body_lines)
436
497
 
437
498
  return "\n".join(lines)
438
499
 
500
+ def _is_scoreboard_condition(self, expression: Any) -> bool:
501
+ """Check if an expression is a scoreboard comparison."""
502
+ if isinstance(expression, BinaryExpression):
503
+ # Check if it's comparing a scoreboard value
504
+ if isinstance(expression.left, VariableSubstitution) or isinstance(expression.right, VariableSubstitution):
505
+ return True
506
+ return False
507
+
508
+ def _generate_if_function_name(self) -> str:
509
+ """Generate a unique name for an if function."""
510
+ if not hasattr(self, 'if_counter'):
511
+ self.if_counter = 0
512
+ self.if_counter += 1
513
+ return f"if_{self.if_counter}"
514
+
515
+ def _generate_else_function_name(self) -> str:
516
+ """Generate a unique name for an else function."""
517
+ if not hasattr(self, 'else_counter'):
518
+ self.else_counter = 0
519
+ self.else_counter += 1
520
+ return f"else_{self.else_counter}"
521
+
522
+ def _generate_while_function_name(self) -> str:
523
+ """Generate a unique name for a while function."""
524
+ if not hasattr(self, 'while_counter'):
525
+ self.while_counter = 0
526
+ self.while_counter += 1
527
+ return f"while_{self.while_counter}"
528
+
529
+ def _store_generated_function(self, name: str, lines: List[str]):
530
+ """Store a generated function as a separate file under the same namespace."""
531
+ if self.dir_map:
532
+ functions_dir = self.output_dir / "data" / self.current_namespace / self.dir_map.function
533
+ else:
534
+ functions_dir = self.output_dir / "data" / self.current_namespace / "functions"
535
+ functions_dir.mkdir(parents=True, exist_ok=True)
536
+ func_file = functions_dir / f"{name}.mcfunction"
537
+ with open(func_file, 'w') as f:
538
+ f.write("\n".join(lines) + "\n")
539
+
439
540
  def _function_call_to_command(self, func_call: FunctionCall) -> str:
440
541
  """Convert function call to execute command."""
441
542
  if func_call.scope:
@@ -446,25 +547,188 @@ class MDLCompiler:
446
547
  def _expression_to_value(self, expression: Any) -> str:
447
548
  """Convert expression to a value string."""
448
549
  if isinstance(expression, LiteralExpression):
550
+ # Format numbers as integers if possible
551
+ if isinstance(expression.value, (int, float)):
552
+ try:
553
+ v = float(expression.value)
554
+ if v.is_integer():
555
+ return str(int(v))
556
+ return str(v)
557
+ except Exception:
558
+ return str(expression.value)
449
559
  return str(expression.value)
450
560
  elif isinstance(expression, VariableSubstitution):
451
561
  objective = self.variables.get(expression.name, expression.name)
452
562
  scope = expression.scope.strip("<>")
453
563
  return f"score {scope} {objective}"
454
564
  elif isinstance(expression, BinaryExpression):
455
- left = self._expression_to_value(expression.left)
456
- right = self._expression_to_value(expression.right)
457
- return f"{left} {expression.operator} {right}"
565
+ # For complex expressions, we need to use temporary variables
566
+ temp_var = self._generate_temp_variable_name()
567
+ self._compile_expression_to_temp(expression, temp_var)
568
+ return f"score @s {temp_var}"
458
569
  elif isinstance(expression, ParenthesizedExpression):
459
- return f"({self._expression_to_value(expression.expression)})"
570
+ return self._expression_to_value(expression.expression)
460
571
  else:
461
572
  return str(expression)
462
573
 
463
574
  def _expression_to_condition(self, expression: Any) -> str:
464
- """Convert expression to a condition string."""
575
+ """Legacy: Convert expression to a naive condition string (internal use)."""
465
576
  if isinstance(expression, BinaryExpression):
466
577
  left = self._expression_to_value(expression.left)
467
578
  right = self._expression_to_value(expression.right)
468
579
  return f"{left} {expression.operator} {right}"
469
580
  else:
470
581
  return self._expression_to_value(expression)
582
+
583
+ def _build_condition(self, expression: Any) -> (str, bool):
584
+ """Build a valid Minecraft execute condition.
585
+ Returns (condition_string, invert_then) where invert_then True means the THEN branch should use 'unless'.
586
+ """
587
+ # Default: generic expression string, no inversion
588
+ invert_then = False
589
+
590
+ if isinstance(expression, BinaryExpression):
591
+ left = expression.left
592
+ right = expression.right
593
+ op = expression.operator
594
+ # Variable vs literal
595
+ if isinstance(left, VariableSubstitution) and isinstance(right, LiteralExpression) and isinstance(right.value, (int, float)):
596
+ objective = self.variables.get(left.name, left.name)
597
+ scope = left.scope.strip("<>")
598
+ # Normalize number
599
+ try:
600
+ v = float(right.value)
601
+ except Exception:
602
+ v = None
603
+ if v is not None:
604
+ n = int(v) if float(v).is_integer() else v
605
+ if op == TokenType.GREATER:
606
+ rng = f"{int(n)+1}.." if isinstance(n, int) else f"{v+1}.."
607
+ return (f"score {scope} {objective} matches {rng}", False)
608
+ if op == TokenType.GREATER_EQUAL:
609
+ rng = f"{int(n)}.."
610
+ return (f"score {scope} {objective} matches {rng}", False)
611
+ if op == TokenType.LESS:
612
+ rng = f"..{int(n)-1}"
613
+ return (f"score {scope} {objective} matches {rng}", False)
614
+ if op == TokenType.LESS_EQUAL:
615
+ rng = f"..{int(n)}"
616
+ return (f"score {scope} {objective} matches {rng}", False)
617
+ if op == TokenType.EQUAL:
618
+ rng = f"{int(n)}"
619
+ return (f"score {scope} {objective} matches {rng}", False)
620
+ if op == TokenType.NOT_EQUAL:
621
+ rng = f"{int(n)}"
622
+ return (f"score {scope} {objective} matches {rng}", True)
623
+ # Variable vs variable
624
+ if isinstance(left, VariableSubstitution) and isinstance(right, VariableSubstitution):
625
+ lobj = self.variables.get(left.name, left.name)
626
+ lscope = left.scope.strip("<>")
627
+ robj = self.variables.get(right.name, right.name)
628
+ rscope = right.scope.strip("<>")
629
+ if op in (TokenType.GREATER, TokenType.GREATER_EQUAL, TokenType.LESS, TokenType.LESS_EQUAL, TokenType.EQUAL):
630
+ comp_map = {
631
+ TokenType.GREATER: ">",
632
+ TokenType.GREATER_EQUAL: ">=",
633
+ TokenType.LESS: "<",
634
+ TokenType.LESS_EQUAL: "<=",
635
+ TokenType.EQUAL: "="
636
+ }
637
+ comp = comp_map[op]
638
+ return (f"score {lscope} {lobj} {comp} {rscope} {robj}", False)
639
+ if op == TokenType.NOT_EQUAL:
640
+ # Use equals with inversion
641
+ return (f"score {lscope} {lobj} = {rscope} {robj}", True)
642
+
643
+ # Fallback: treat as generic condition string
644
+ return (self._expression_to_condition(expression), False)
645
+
646
+ def _compile_expression_to_temp(self, expression: BinaryExpression, temp_var: str):
647
+ """Compile a complex expression to a temporary variable using valid Minecraft commands."""
648
+ left_temp = None
649
+ right_temp = None
650
+
651
+ if isinstance(expression.left, BinaryExpression):
652
+ # Left side is complex - compile it first
653
+ left_temp = self._generate_temp_variable_name()
654
+ self._compile_expression_to_temp(expression.left, left_temp)
655
+ left_value = f"score @s {left_temp}"
656
+ else:
657
+ left_value = self._expression_to_value(expression.left)
658
+
659
+ if isinstance(expression.right, BinaryExpression):
660
+ # Right side is complex - compile it first
661
+ right_temp = self._generate_temp_variable_name()
662
+ self._compile_expression_to_temp(expression.right, right_temp)
663
+ right_value = f"score @s {right_temp}"
664
+ else:
665
+ right_value = self._expression_to_value(expression.right)
666
+
667
+ # Generate the operation command
668
+ if expression.operator == "PLUS":
669
+ if isinstance(expression.left, BinaryExpression):
670
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} = @s {left_temp}")
671
+ else:
672
+ self._store_temp_command(f"scoreboard players set @s {temp_var} {left_value}")
673
+
674
+ if isinstance(expression.right, BinaryExpression):
675
+ self._store_temp_command(f"scoreboard players add @s {temp_var} {right_value}")
676
+ else:
677
+ self._store_temp_command(f"scoreboard players add @s {temp_var} {right_value}")
678
+
679
+ elif expression.operator == "MINUS":
680
+ if isinstance(expression.left, BinaryExpression):
681
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} = @s {left_temp}")
682
+ else:
683
+ self._store_temp_command(f"scoreboard players set @s {temp_var} {left_value}")
684
+
685
+ if isinstance(expression.right, BinaryExpression):
686
+ self._store_temp_command(f"scoreboard players remove @s {temp_var} {right_value}")
687
+ else:
688
+ self._store_temp_command(f"scoreboard players remove @s {temp_var} {right_value}")
689
+
690
+ elif expression.operator == "MULTIPLY":
691
+ if isinstance(expression.left, BinaryExpression):
692
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} = @s {left_temp}")
693
+ else:
694
+ self._store_temp_command(f"scoreboard players set @s {temp_var} {left_value}")
695
+
696
+ if isinstance(expression.right, BinaryExpression):
697
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} *= @s {right_temp}")
698
+ else:
699
+ # For literal values, we need to use a different approach
700
+ if isinstance(expression.right, LiteralExpression):
701
+ self._store_temp_command(f"scoreboard players multiply @s {temp_var} {expression.right.value}")
702
+ else:
703
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} *= {right_value}")
704
+
705
+ elif expression.operator == "DIVIDE":
706
+ if isinstance(expression.left, BinaryExpression):
707
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} = @s {left_temp}")
708
+ else:
709
+ self._store_temp_command(f"scoreboard players set @s {temp_var} {left_value}")
710
+
711
+ if isinstance(expression.right, BinaryExpression):
712
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} /= @s {right_temp}")
713
+ else:
714
+ # For literal values, we need to use a different approach
715
+ if isinstance(expression.right, LiteralExpression):
716
+ self._store_temp_command(f"scoreboard players divide @s {temp_var} {expression.right.value}")
717
+ else:
718
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} /= {right_value}")
719
+ else:
720
+ # For other operators, just set the value
721
+ self._store_temp_command(f"scoreboard players set @s {temp_var} 0")
722
+
723
+ def _store_temp_command(self, command: str):
724
+ """Store a temporary command for later execution."""
725
+ if not hasattr(self, 'temp_commands'):
726
+ self.temp_commands = []
727
+ self.temp_commands.append(command)
728
+
729
+ def _generate_temp_variable_name(self) -> str:
730
+ """Generate a unique temporary variable name."""
731
+ if not hasattr(self, 'temp_counter'):
732
+ self.temp_counter = 0
733
+ self.temp_counter += 1
734
+ return f"temp_{self.temp_counter}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: minecraft-datapack-language
3
- Version: 15.4.31
3
+ Version: 15.4.33
4
4
  Summary: Compile MDL language with explicit scoping 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
@@ -28,7 +28,6 @@ A **modern, scope-aware language** that lets you write Minecraft datapacks with
28
28
  🔧 **[VS Code Extension](https://marketplace.visualstudio.com/items?itemName=mdl.minecraft-datapack-language)** - Syntax highlighting, IntelliSense, and snippets
29
29
 
30
30
  ![CI](https://github.com/aaron777collins/MinecraftDatapackLanguage/workflows/CI/badge.svg)
31
- ![Test Examples](https://github.com/aaron777collins/MinecraftDatapackLanguage/workflows/Test%20Examples/badge.svg)
32
31
  ![Documentation](https://github.com/aaron777collins/MinecraftDatapackLanguage/workflows/Build%20and%20Deploy%20Documentation/badge.svg)
33
32
  ![PyPI](https://img.shields.io/pypi/v/minecraft-datapack-language?style=flat-square)
34
33
  ![Release](https://github.com/aaron777collins/MinecraftDatapackLanguage/workflows/Release/badge.svg)
@@ -1,17 +1,17 @@
1
1
  minecraft_datapack_language/__init__.py,sha256=YoTmZWZVH6POAVYMvOTEBKdC-cxQsWi2VomSWZDgYFw,1158
2
- minecraft_datapack_language/_version.py,sha256=ybDeUo_pBGup1BE9UTgdEc140_gEuC_6hkQJEnPjtlE,708
2
+ minecraft_datapack_language/_version.py,sha256=oHNM_9N4Gw1WIjw70v38mQJDgIkD6Qbx1TSXwhIvfNI,708
3
3
  minecraft_datapack_language/ast_nodes.py,sha256=nbWrRz137MGMRpmnq8QkXNzrtlaCgyPEknytbkrS_M8,3899
4
- minecraft_datapack_language/cli.py,sha256=dy1KBDULHpaHYYzNfL42EuhOAB5_GTfSVt7_bMImSno,9000
4
+ minecraft_datapack_language/cli.py,sha256=-TMAL1HCCtwf0aG46x88MVBsYUswPRCVhy854li7X9c,9780
5
5
  minecraft_datapack_language/dir_map.py,sha256=HmxFkuvWGkzHF8o_GFb4BpuMCRc6QMw8UbmcAI8JVdY,1788
6
- minecraft_datapack_language/mdl_compiler.py,sha256=GQv2CD29aj8vxhqRczTn_2JjNn6hiHQ4XoU2qh-s1n4,20429
6
+ minecraft_datapack_language/mdl_compiler.py,sha256=nS_D2f_G8lcDxUOiADNJWTJQwTnXoQ-W4amyhw6xJoY,34673
7
7
  minecraft_datapack_language/mdl_errors.py,sha256=r0Gu3KhoX1YLPAVW_iO7Q_fPgaf_Dv9tOGSOdKNSzmw,16114
8
8
  minecraft_datapack_language/mdl_lexer.py,sha256=CjbEUpuuF4eU_ucA_WIhw6wSMcHGk2BchtQ0bLAGvwg,22033
9
9
  minecraft_datapack_language/mdl_linter.py,sha256=z85xoAglENurCh30bR7kEHZ_JeMxcYaLDcGNRAl-RAI,17253
10
10
  minecraft_datapack_language/mdl_parser.py,sha256=aQPKcmATM9BOMzO7vCXmMdxU1qjOJNLCSAKJopu5h3g,23429
11
11
  minecraft_datapack_language/utils.py,sha256=Aq0HAGlXqj9BUTEjaEilpvzEW0EtZYYMMwOqG9db6dE,684
12
- minecraft_datapack_language-15.4.31.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
13
- minecraft_datapack_language-15.4.31.dist-info/METADATA,sha256=MhDDSgOGSon07gtuGAXAgtGrQrLdMtpZ8A55adIrQFU,8475
14
- minecraft_datapack_language-15.4.31.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
- minecraft_datapack_language-15.4.31.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
16
- minecraft_datapack_language-15.4.31.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
17
- minecraft_datapack_language-15.4.31.dist-info/RECORD,,
12
+ minecraft_datapack_language-15.4.33.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
13
+ minecraft_datapack_language-15.4.33.dist-info/METADATA,sha256=-vx5gJu0ZmkHmVVnlBIaa4rtBEhhfoi32BgXl1NnAqg,8360
14
+ minecraft_datapack_language-15.4.33.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ minecraft_datapack_language-15.4.33.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
16
+ minecraft_datapack_language-15.4.33.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
17
+ minecraft_datapack_language-15.4.33.dist-info/RECORD,,