minecraft-datapack-language 15.4.31__py3-none-any.whl → 15.4.32__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.32'
32
+ __version_tuple__ = version_tuple = (15, 4, 32)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -147,12 +147,32 @@ class MDLCompiler:
147
147
  lines.append(f"# Scope: {func.scope}")
148
148
  lines.append("")
149
149
 
150
+ # Reset temporary commands for this function
151
+ if hasattr(self, 'temp_commands'):
152
+ self.temp_commands = []
153
+
150
154
  # Generate commands from function body
151
155
  for statement in func.body:
152
156
  cmd = self._statement_to_command(statement)
153
157
  if cmd:
154
158
  lines.append(cmd)
155
159
 
160
+ # Add any temporary commands that were generated during compilation
161
+ if hasattr(self, 'temp_commands') and self.temp_commands:
162
+ lines.append("")
163
+ lines.append("# Temporary variable operations:")
164
+ for temp_cmd in self.temp_commands:
165
+ lines.append(temp_cmd)
166
+
167
+ # Add any generated functions that were created during compilation
168
+ if hasattr(self, 'generated_functions') and self.generated_functions:
169
+ lines.append("")
170
+ lines.append("# Generated functions:")
171
+ for func_name, func_lines in self.generated_functions.items():
172
+ lines.append(f"# {func_name}:")
173
+ lines.extend(func_lines)
174
+ lines.append("")
175
+
156
176
  return "\n".join(lines)
157
177
 
158
178
  def _compile_hooks(self, hooks: List[HookDeclaration], namespace_dir: Path):
@@ -290,9 +310,20 @@ class MDLCompiler:
290
310
  def _variable_assignment_to_command(self, assignment: VariableAssignment) -> str:
291
311
  """Convert variable assignment to scoreboard command."""
292
312
  objective = self.variables.get(assignment.name, assignment.name)
293
- value = self._expression_to_value(assignment.value)
294
313
  scope = assignment.scope.strip("<>")
295
- return f"scoreboard players set {scope} {objective} {value}"
314
+
315
+ # Check if the value is a complex expression
316
+ if isinstance(assignment.value, BinaryExpression):
317
+ # Complex expression - use temporary variable approach
318
+ temp_var = self._generate_temp_variable_name()
319
+ self._compile_expression_to_temp(assignment.value, temp_var)
320
+
321
+ # Return the command to set the target variable from the temp
322
+ return f"scoreboard players operation {scope} {objective} = @s {temp_var}"
323
+ else:
324
+ # Simple value - use direct assignment
325
+ value = self._expression_to_value(assignment.value)
326
+ return f"scoreboard players set {scope} {objective} {value}"
296
327
 
297
328
  def _say_command_to_command(self, say: SayCommand) -> str:
298
329
  """Convert say command to tellraw command with JSON formatting."""
@@ -348,94 +379,161 @@ class MDLCompiler:
348
379
  return f'tellraw @a {first_part}'
349
380
 
350
381
  def _if_statement_to_command(self, if_stmt: IfStatement) -> str:
351
- """Convert if statement to comment and include actual statements."""
382
+ """Convert if statement to proper Minecraft execute if commands."""
352
383
  condition = self._expression_to_condition(if_stmt.condition)
353
- lines = [f"# if {condition}"]
384
+ lines = []
354
385
 
355
- # Include the actual statements from the if body for visibility
386
+ # Generate the if condition using execute if
387
+ if self._is_scoreboard_condition(if_stmt.condition):
388
+ # For scoreboard conditions, use execute if score
389
+ lines.append(f"execute if score {condition} run function {self.current_namespace}:{self._generate_if_function_name()}")
390
+ else:
391
+ # For other conditions, use execute if
392
+ lines.append(f"execute if {condition} run function {self.current_namespace}:{self._generate_if_function_name()}")
393
+
394
+ # Generate the if body function
395
+ if_body_lines = [f"# Function: {self.current_namespace}:{self._generate_if_function_name()}"]
356
396
  for stmt in if_stmt.then_body:
357
397
  if isinstance(stmt, VariableAssignment):
358
- # Include variable assignments directly
359
398
  cmd = self._variable_assignment_to_command(stmt)
360
- lines.append(cmd)
399
+ if_body_lines.append(cmd)
361
400
  elif isinstance(stmt, SayCommand):
362
- # Include say commands directly
363
401
  cmd = self._say_command_to_command(stmt)
364
- lines.append(cmd)
402
+ if_body_lines.append(cmd)
365
403
  elif isinstance(stmt, RawBlock):
366
- # Include raw blocks directly
367
- lines.append(stmt.content)
404
+ if_body_lines.append(stmt.content)
368
405
  elif isinstance(stmt, IfStatement):
369
- # Recursively handle nested if statements
370
406
  cmd = self._if_statement_to_command(stmt)
371
- lines.append(cmd)
407
+ if_body_lines.append(cmd)
372
408
  elif isinstance(stmt, WhileLoop):
373
- # Handle while loops
374
409
  cmd = self._while_loop_to_command(stmt)
375
- lines.append(cmd)
410
+ if_body_lines.append(cmd)
376
411
  elif isinstance(stmt, FunctionCall):
377
- # Handle function calls
378
412
  cmd = self._function_call_to_command(stmt)
379
- lines.append(cmd)
413
+ if_body_lines.append(cmd)
380
414
 
381
415
  # Handle else body if it exists
382
416
  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)
417
+ if isinstance(if_stmt.else_body, list) and len(if_stmt.else_body) == 1 and isinstance(if_stmt.else_body[0], IfStatement):
418
+ # This is an else if - handle it recursively
419
+ else_if_lines = self._if_statement_to_command(if_stmt.else_body[0])
420
+ lines.extend(else_if_lines.split('\n'))
421
+ else:
422
+ # This is a regular else
423
+ if self._is_scoreboard_condition(if_stmt.condition):
424
+ lines.append(f"execute unless score {condition} run function {self.current_namespace}:{self._generate_else_function_name()}")
425
+ else:
426
+ lines.append(f"execute unless {condition} run function {self.current_namespace}:{self._generate_else_function_name()}")
427
+
428
+ # Generate the else body function
429
+ else_body_lines = [f"# Function: {self.current_namespace}:{self._generate_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
+
449
+ # Store the else function for later generation
450
+ self._store_generated_function(self._generate_else_function_name(), else_body_lines)
451
+
452
+ # Store the if function for later generation
453
+ self._store_generated_function(self._generate_if_function_name(), if_body_lines)
403
454
 
404
455
  return "\n".join(lines)
405
456
 
406
457
  def _while_loop_to_command(self, while_loop: WhileLoop) -> str:
407
- """Convert while loop to comment and include actual statements."""
458
+ """Convert while loop to proper Minecraft loop logic."""
408
459
  condition = self._expression_to_condition(while_loop.condition)
409
- lines = [f"# while {condition}"]
460
+ lines = []
410
461
 
411
- # Include the actual statements from the while body for visibility
462
+ # Generate the while loop using a recursive function approach
463
+ loop_function_name = self._generate_while_function_name()
464
+
465
+ # First, call the loop function
466
+ lines.append(f"function {self.current_namespace}:{loop_function_name}")
467
+
468
+ # Generate the loop function body
469
+ loop_body_lines = [f"# Function: {self.current_namespace}:{loop_function_name}"]
470
+
471
+ # Add the loop body statements
412
472
  for stmt in while_loop.body:
413
473
  if isinstance(stmt, VariableAssignment):
414
- # Include variable assignments directly
415
474
  cmd = self._variable_assignment_to_command(stmt)
416
- lines.append(cmd)
475
+ loop_body_lines.append(cmd)
417
476
  elif isinstance(stmt, SayCommand):
418
- # Include say commands directly
419
477
  cmd = self._say_command_to_command(stmt)
420
- lines.append(cmd)
478
+ loop_body_lines.append(cmd)
421
479
  elif isinstance(stmt, RawBlock):
422
- # Include raw blocks directly
423
- lines.append(stmt.content)
480
+ loop_body_lines.append(stmt.content)
424
481
  elif isinstance(stmt, IfStatement):
425
- # Recursively handle nested if statements
426
482
  cmd = self._if_statement_to_command(stmt)
427
- lines.append(cmd)
483
+ loop_body_lines.append(cmd)
428
484
  elif isinstance(stmt, WhileLoop):
429
- # Handle nested while loops
430
485
  cmd = self._while_loop_to_command(stmt)
431
- lines.append(cmd)
486
+ loop_body_lines.append(cmd)
432
487
  elif isinstance(stmt, FunctionCall):
433
- # Handle function calls
434
488
  cmd = self._function_call_to_command(stmt)
435
- lines.append(cmd)
489
+ loop_body_lines.append(cmd)
490
+
491
+ # Add the recursive call at the end to continue the loop
492
+ if self._is_scoreboard_condition(while_loop.condition):
493
+ loop_body_lines.append(f"execute if score {condition} run function {self.current_namespace}:{loop_function_name}")
494
+ else:
495
+ loop_body_lines.append(f"execute if {condition} run function {self.current_namespace}:{loop_function_name}")
496
+
497
+ # Store the loop function for later generation
498
+ self._store_generated_function(loop_function_name, loop_body_lines)
436
499
 
437
500
  return "\n".join(lines)
438
501
 
502
+ def _is_scoreboard_condition(self, expression: Any) -> bool:
503
+ """Check if an expression is a scoreboard comparison."""
504
+ if isinstance(expression, BinaryExpression):
505
+ # Check if it's comparing a scoreboard value
506
+ if isinstance(expression.left, VariableSubstitution) or isinstance(expression.right, VariableSubstitution):
507
+ return True
508
+ return False
509
+
510
+ def _generate_if_function_name(self) -> str:
511
+ """Generate a unique name for an if function."""
512
+ if not hasattr(self, 'if_counter'):
513
+ self.if_counter = 0
514
+ self.if_counter += 1
515
+ return f"if_{self.if_counter}"
516
+
517
+ def _generate_else_function_name(self) -> str:
518
+ """Generate a unique name for an else function."""
519
+ if not hasattr(self, 'else_counter'):
520
+ self.else_counter = 0
521
+ self.else_counter += 1
522
+ return f"else_{self.else_counter}"
523
+
524
+ def _generate_while_function_name(self) -> str:
525
+ """Generate a unique name for a while function."""
526
+ if not hasattr(self, 'while_counter'):
527
+ self.while_counter = 0
528
+ self.while_counter += 1
529
+ return f"while_{self.while_counter}"
530
+
531
+ def _store_generated_function(self, name: str, lines: List[str]):
532
+ """Store a generated function for later output."""
533
+ if not hasattr(self, 'generated_functions'):
534
+ self.generated_functions = {}
535
+ self.generated_functions[name] = lines
536
+
439
537
  def _function_call_to_command(self, func_call: FunctionCall) -> str:
440
538
  """Convert function call to execute command."""
441
539
  if func_call.scope:
@@ -452,11 +550,12 @@ class MDLCompiler:
452
550
  scope = expression.scope.strip("<>")
453
551
  return f"score {scope} {objective}"
454
552
  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}"
553
+ # For complex expressions, we need to use temporary variables
554
+ temp_var = self._generate_temp_variable_name()
555
+ self._compile_expression_to_temp(expression, temp_var)
556
+ return f"score @s {temp_var}"
458
557
  elif isinstance(expression, ParenthesizedExpression):
459
- return f"({self._expression_to_value(expression.expression)})"
558
+ return self._expression_to_value(expression.expression)
460
559
  else:
461
560
  return str(expression)
462
561
 
@@ -468,3 +567,93 @@ class MDLCompiler:
468
567
  return f"{left} {expression.operator} {right}"
469
568
  else:
470
569
  return self._expression_to_value(expression)
570
+
571
+ def _compile_expression_to_temp(self, expression: BinaryExpression, temp_var: str):
572
+ """Compile a complex expression to a temporary variable using valid Minecraft commands."""
573
+ left_temp = None
574
+ right_temp = None
575
+
576
+ if isinstance(expression.left, BinaryExpression):
577
+ # Left side is complex - compile it first
578
+ left_temp = self._generate_temp_variable_name()
579
+ self._compile_expression_to_temp(expression.left, left_temp)
580
+ left_value = f"score @s {left_temp}"
581
+ else:
582
+ left_value = self._expression_to_value(expression.left)
583
+
584
+ if isinstance(expression.right, BinaryExpression):
585
+ # Right side is complex - compile it first
586
+ right_temp = self._generate_temp_variable_name()
587
+ self._compile_expression_to_temp(expression.right, right_temp)
588
+ right_value = f"score @s {right_temp}"
589
+ else:
590
+ right_value = self._expression_to_value(expression.right)
591
+
592
+ # Generate the operation command
593
+ if expression.operator == "PLUS":
594
+ if isinstance(expression.left, BinaryExpression):
595
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} = @s {left_temp}")
596
+ else:
597
+ self._store_temp_command(f"scoreboard players set @s {temp_var} {left_value}")
598
+
599
+ if isinstance(expression.right, BinaryExpression):
600
+ self._store_temp_command(f"scoreboard players add @s {temp_var} {right_value}")
601
+ else:
602
+ self._store_temp_command(f"scoreboard players add @s {temp_var} {right_value}")
603
+
604
+ elif expression.operator == "MINUS":
605
+ if isinstance(expression.left, BinaryExpression):
606
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} = @s {left_temp}")
607
+ else:
608
+ self._store_temp_command(f"scoreboard players set @s {temp_var} {left_value}")
609
+
610
+ if isinstance(expression.right, BinaryExpression):
611
+ self._store_temp_command(f"scoreboard players remove @s {temp_var} {right_value}")
612
+ else:
613
+ self._store_temp_command(f"scoreboard players remove @s {temp_var} {right_value}")
614
+
615
+ elif expression.operator == "MULTIPLY":
616
+ if isinstance(expression.left, BinaryExpression):
617
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} = @s {left_temp}")
618
+ else:
619
+ self._store_temp_command(f"scoreboard players set @s {temp_var} {left_value}")
620
+
621
+ if isinstance(expression.right, BinaryExpression):
622
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} *= @s {right_temp}")
623
+ else:
624
+ # For literal values, we need to use a different approach
625
+ if isinstance(expression.right, LiteralExpression):
626
+ self._store_temp_command(f"scoreboard players multiply @s {temp_var} {expression.right.value}")
627
+ else:
628
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} *= {right_value}")
629
+
630
+ elif expression.operator == "DIVIDE":
631
+ if isinstance(expression.left, BinaryExpression):
632
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} = @s {left_temp}")
633
+ else:
634
+ self._store_temp_command(f"scoreboard players set @s {temp_var} {left_value}")
635
+
636
+ if isinstance(expression.right, BinaryExpression):
637
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} /= @s {right_temp}")
638
+ else:
639
+ # For literal values, we need to use a different approach
640
+ if isinstance(expression.right, LiteralExpression):
641
+ self._store_temp_command(f"scoreboard players divide @s {temp_var} {expression.right.value}")
642
+ else:
643
+ self._store_temp_command(f"scoreboard players operation @s {temp_var} /= {right_value}")
644
+ else:
645
+ # For other operators, just set the value
646
+ self._store_temp_command(f"scoreboard players set @s {temp_var} 0")
647
+
648
+ def _store_temp_command(self, command: str):
649
+ """Store a temporary command for later execution."""
650
+ if not hasattr(self, 'temp_commands'):
651
+ self.temp_commands = []
652
+ self.temp_commands.append(command)
653
+
654
+ def _generate_temp_variable_name(self) -> str:
655
+ """Generate a unique temporary variable name."""
656
+ if not hasattr(self, 'temp_counter'):
657
+ self.temp_counter = 0
658
+ self.temp_counter += 1
659
+ 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.32
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=42aiYhTCVtqfiLB0RL_MLmflGwWJl3Iw48JjY1hYnoc,708
3
3
  minecraft_datapack_language/ast_nodes.py,sha256=nbWrRz137MGMRpmnq8QkXNzrtlaCgyPEknytbkrS_M8,3899
4
4
  minecraft_datapack_language/cli.py,sha256=dy1KBDULHpaHYYzNfL42EuhOAB5_GTfSVt7_bMImSno,9000
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=SbyguTUwOalz75hQNhGNW-TbcAbxv2LGfGW0EtYT0Ys,30605
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.32.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
13
+ minecraft_datapack_language-15.4.32.dist-info/METADATA,sha256=kzWvTqvwdSuw7UDKCyFkCF-VJMBt-wUCtJ0XVJduNHA,8360
14
+ minecraft_datapack_language-15.4.32.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ minecraft_datapack_language-15.4.32.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
16
+ minecraft_datapack_language-15.4.32.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
17
+ minecraft_datapack_language-15.4.32.dist-info/RECORD,,