minecraft-datapack-language 16.0.2__py3-none-any.whl → 16.0.4__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 = '16.0.2'
32
- __version_tuple__ = version_tuple = (16, 0, 2)
31
+ __version__ = version = '16.0.4'
32
+ __version_tuple__ = version_tuple = (16, 0, 4)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -94,6 +94,13 @@ class WhileLoop(ASTNode):
94
94
  body: List[ASTNode]
95
95
 
96
96
 
97
+ @dataclass
98
+ class ScheduledWhileLoop(ASTNode):
99
+ """Scheduled-while loop that iterates via Minecraft's schedule command each tick."""
100
+ condition: Any # Expression
101
+ body: List[ASTNode]
102
+
103
+
97
104
  @dataclass
98
105
  class HookDeclaration(ASTNode):
99
106
  """Hook declaration (on_load, on_tick)."""
@@ -11,7 +11,7 @@ from typing import Dict, List, Any, Optional
11
11
  from .ast_nodes import (
12
12
  Program, PackDeclaration, NamespaceDeclaration, TagDeclaration,
13
13
  VariableDeclaration, VariableAssignment, VariableSubstitution, FunctionDeclaration,
14
- FunctionCall, IfStatement, WhileLoop, HookDeclaration, RawBlock, MacroLine,
14
+ FunctionCall, IfStatement, WhileLoop, ScheduledWhileLoop, HookDeclaration, RawBlock, MacroLine,
15
15
  SayCommand, BinaryExpression, LiteralExpression, ParenthesizedExpression
16
16
  )
17
17
  from .dir_map import get_dir_map, DirMap
@@ -270,15 +270,8 @@ class MDLCompiler:
270
270
  tags_fn_dir = self.output_dir / "data" / "minecraft" / self.dir_map.tags_function
271
271
  tags_fn_dir.mkdir(parents=True, exist_ok=True)
272
272
  load_tag_file = tags_fn_dir / "load.json"
273
- # Always include namespace:load (contains scoreboard initialization), plus any explicit on_load hooks
273
+ # Always reference namespace:load (which handles scoreboard init and calls on_load hooks internally)
274
274
  values = [f"{self.current_namespace}:load"]
275
- if has_on_load:
276
- # Add explicit on_load hooks as additional entries
277
- for hook in hooks:
278
- if hook.hook_type == "on_load":
279
- hook_ref = f"{hook.namespace}:{hook.name}"
280
- if hook_ref not in values: # Avoid duplicates
281
- values.append(hook_ref)
282
275
  with open(load_tag_file, 'w') as f:
283
276
  json.dump({"values": values}, f, indent=2)
284
277
 
@@ -352,6 +345,8 @@ class MDLCompiler:
352
345
  return self._if_statement_to_command(statement)
353
346
  elif isinstance(statement, WhileLoop):
354
347
  return self._while_loop_to_command(statement)
348
+ elif isinstance(statement, ScheduledWhileLoop):
349
+ return self._scheduled_while_to_command(statement)
355
350
  elif isinstance(statement, FunctionCall):
356
351
  return self._function_call_to_command(statement)
357
352
  else:
@@ -614,6 +609,62 @@ class MDLCompiler:
614
609
  self._store_generated_function(loop_function_name, loop_body_lines)
615
610
 
616
611
  return "\n".join(lines)
612
+
613
+ def _scheduled_while_to_command(self, while_loop: ScheduledWhileLoop) -> str:
614
+ """Convert scheduledwhile to tick-scheduled loop to avoid recursion limits.
615
+ Strategy:
616
+ - Generate a unique loop function that contains the body, then conditionally schedules itself 1t later.
617
+ - Entry statement schedules the first tick run.
618
+ - Breakout occurs naturally by not scheduling when condition is false.
619
+ """
620
+ loop_function_name = self._generate_while_function_name()
621
+
622
+ # Schedule first iteration for next tick
623
+ lines: List[str] = []
624
+ lines.append(f"schedule function {self.current_namespace}:{loop_function_name} 1t")
625
+
626
+ # Build the loop function body
627
+ loop_body_lines: List[str] = [f"# Function: {self.current_namespace}:{loop_function_name}"]
628
+
629
+ if not hasattr(self, '_temp_sink_stack'):
630
+ self._temp_sink_stack = []
631
+ self._temp_sink_stack.append(loop_body_lines)
632
+ for stmt in while_loop.body:
633
+ if isinstance(stmt, VariableAssignment):
634
+ cmd = self._variable_assignment_to_command(stmt)
635
+ loop_body_lines.append(cmd)
636
+ elif isinstance(stmt, VariableDeclaration):
637
+ cmd = self._variable_declaration_to_command(stmt)
638
+ loop_body_lines.append(cmd)
639
+ elif isinstance(stmt, SayCommand):
640
+ cmd = self._say_command_to_command(stmt)
641
+ loop_body_lines.append(cmd)
642
+ elif isinstance(stmt, RawBlock):
643
+ loop_body_lines.append(stmt.content)
644
+ elif isinstance(stmt, IfStatement):
645
+ cmd = self._if_statement_to_command(stmt)
646
+ loop_body_lines.append(cmd)
647
+ elif isinstance(stmt, WhileLoop):
648
+ cmd = self._while_loop_to_command(stmt)
649
+ loop_body_lines.append(cmd)
650
+ elif isinstance(stmt, ScheduledWhileLoop):
651
+ cmd = self._scheduled_while_to_command(stmt)
652
+ loop_body_lines.append(cmd)
653
+ elif isinstance(stmt, FunctionCall):
654
+ cmd = self._function_call_to_command(stmt)
655
+ loop_body_lines.append(cmd)
656
+ self._temp_sink_stack.pop()
657
+
658
+ cond_str, invert_then = self._build_condition(while_loop.condition)
659
+ if invert_then:
660
+ # Inverted means schedule unless condition (NOT desired). We want continue-when-true.
661
+ # cond_str represents equality in inverted case; continue when not(cond) → use unless.
662
+ loop_body_lines.append(f"execute unless {cond_str} run schedule function {self.current_namespace}:{loop_function_name} 1t")
663
+ else:
664
+ loop_body_lines.append(f"execute if {cond_str} run schedule function {self.current_namespace}:{loop_function_name} 1t")
665
+
666
+ self._store_generated_function(loop_function_name, loop_body_lines)
667
+ return "\n".join(lines)
617
668
 
618
669
  def _is_scoreboard_condition(self, expression: Any) -> bool:
619
670
  """Check if an expression is a scoreboard comparison."""
@@ -33,6 +33,7 @@ class TokenType:
33
33
  IF = "IF"
34
34
  ELSE = "ELSE"
35
35
  WHILE = "WHILE"
36
+ SCHEDULED_WHILE = "SCHEDULED_WHILE"
36
37
  ON_LOAD = "ON_LOAD"
37
38
  ON_TICK = "ON_TICK"
38
39
  EXEC = "EXEC"
@@ -599,6 +600,7 @@ class MDLLexer:
599
600
  'if': TokenType.IF,
600
601
  'else': TokenType.ELSE,
601
602
  'while': TokenType.WHILE,
603
+ 'scheduledwhile': TokenType.SCHEDULED_WHILE,
602
604
  'on_load': TokenType.ON_LOAD,
603
605
  'on_tick': TokenType.ON_TICK,
604
606
  'exec': TokenType.EXEC,
@@ -9,7 +9,7 @@ from .mdl_errors import MDLParserError
9
9
  from .ast_nodes import (
10
10
  ASTNode, Program, PackDeclaration, NamespaceDeclaration, TagDeclaration,
11
11
  VariableDeclaration, VariableAssignment, VariableSubstitution, FunctionDeclaration,
12
- FunctionCall, IfStatement, WhileLoop, HookDeclaration, RawBlock, MacroLine,
12
+ FunctionCall, IfStatement, WhileLoop, ScheduledWhileLoop, HookDeclaration, RawBlock, MacroLine,
13
13
  SayCommand, TellrawCommand, ExecuteCommand, ScoreboardCommand,
14
14
  BinaryExpression, UnaryExpression, ParenthesizedExpression, LiteralExpression,
15
15
  ScopeSelector
@@ -87,6 +87,8 @@ class MDLParser:
87
87
  statements.append(self._parse_if_statement())
88
88
  elif self._peek().type == TokenType.WHILE:
89
89
  statements.append(self._parse_while_loop())
90
+ elif self._peek().type == TokenType.SCHEDULED_WHILE:
91
+ statements.append(self._parse_scheduled_while_loop())
90
92
  elif self._peek().type == TokenType.DOLLAR and self._peek(1).type == TokenType.EXCLAMATION:
91
93
  statements.append(self._parse_raw_block())
92
94
  elif self._peek().type == TokenType.IDENTIFIER:
@@ -348,6 +350,18 @@ class MDLParser:
348
350
  self._expect(TokenType.RBRACE, "Expected '}' to end while body")
349
351
 
350
352
  return WhileLoop(condition=condition, body=body)
353
+
354
+ def _parse_scheduled_while_loop(self) -> ScheduledWhileLoop:
355
+ """Parse scheduledwhile loop: scheduledwhile condition { body }"""
356
+ self._expect(TokenType.SCHEDULED_WHILE, "Expected 'scheduledwhile' keyword")
357
+
358
+ condition = self._parse_expression()
359
+
360
+ self._expect(TokenType.LBRACE, "Expected '{' to start while body")
361
+ body = self._parse_block()
362
+ self._expect(TokenType.RBRACE, "Expected '}' to end while body")
363
+
364
+ return ScheduledWhileLoop(condition=condition, body=body)
351
365
 
352
366
  def _parse_hook_declaration(self) -> HookDeclaration:
353
367
  """Parse hook declaration: on_load/on_tick namespace:name<scope>;"""
@@ -524,6 +538,8 @@ class MDLParser:
524
538
  statements.append(self._parse_if_statement())
525
539
  elif self._peek().type == TokenType.WHILE:
526
540
  statements.append(self._parse_while_loop())
541
+ elif self._peek().type == TokenType.SCHEDULED_WHILE:
542
+ statements.append(self._parse_scheduled_while_loop())
527
543
  elif self._peek().type == TokenType.EXEC:
528
544
  statements.append(self._parse_function_call())
529
545
  elif self._peek().type == TokenType.MACRO_LINE:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: minecraft-datapack-language
3
- Version: 16.0.2
3
+ Version: 16.0.4
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
@@ -0,0 +1,18 @@
1
+ minecraft_datapack_language/__init__.py,sha256=0KVXBE4ScRaRUrf83aA2tVB-y8A_jplyaxVvtHH6Uw0,1199
2
+ minecraft_datapack_language/_version.py,sha256=L1SqJ5yirtp0CEsOA8Rvu0Ugibk5GmwrYFW7YofqfpU,706
3
+ minecraft_datapack_language/ast_nodes.py,sha256=L5izavSeXDr766vsfRvJrcnflXNJyXcy0WSfyJPq2ZA,4484
4
+ minecraft_datapack_language/cli.py,sha256=R4QZYtox-Da9B8pr_kCg_9qc9aI-ORTah7kMkhsI5tw,10373
5
+ minecraft_datapack_language/dir_map.py,sha256=HmxFkuvWGkzHF8o_GFb4BpuMCRc6QMw8UbmcAI8JVdY,1788
6
+ minecraft_datapack_language/mdl_compiler.py,sha256=rYNaxvVRje2ot1RaUHibF4HItxca2MovntH0kjx65Vc,47808
7
+ minecraft_datapack_language/mdl_errors.py,sha256=r0Gu3KhoX1YLPAVW_iO7Q_fPgaf_Dv9tOGSOdKNSzmw,16114
8
+ minecraft_datapack_language/mdl_lexer.py,sha256=rfsW2QNcZxa9HAHpU9HFReshQSmjOrrK6xY_r43mKFk,23485
9
+ minecraft_datapack_language/mdl_linter.py,sha256=z85xoAglENurCh30bR7kEHZ_JeMxcYaLDcGNRAl-RAI,17253
10
+ minecraft_datapack_language/mdl_parser.py,sha256=Krk7Y_E9OKNCcsDOCT7ATvQLbJII951AU2qzEY00GLE,26068
11
+ minecraft_datapack_language/python_api.py,sha256=Iao1jbdeW6ekeA80BZG6gNqHVjxQJEheB3DbpVsuTZQ,12304
12
+ minecraft_datapack_language/utils.py,sha256=Aq0HAGlXqj9BUTEjaEilpvzEW0EtZYYMMwOqG9db6dE,684
13
+ minecraft_datapack_language-16.0.4.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
14
+ minecraft_datapack_language-16.0.4.dist-info/METADATA,sha256=QeiAuNVchg2GoUT3GuculpnG2bCduaghGAALoTcyy9Q,8343
15
+ minecraft_datapack_language-16.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ minecraft_datapack_language-16.0.4.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
17
+ minecraft_datapack_language-16.0.4.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
18
+ minecraft_datapack_language-16.0.4.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- minecraft_datapack_language/__init__.py,sha256=0KVXBE4ScRaRUrf83aA2tVB-y8A_jplyaxVvtHH6Uw0,1199
2
- minecraft_datapack_language/_version.py,sha256=gTTJVB75BNER1i08XUYQNvwb6V_T8h-gkDQrn1Jaars,706
3
- minecraft_datapack_language/ast_nodes.py,sha256=UzUxKLkjBisUd5Gu7sAiNXIIPIjNoRzELq4LfIFcnSY,4290
4
- minecraft_datapack_language/cli.py,sha256=R4QZYtox-Da9B8pr_kCg_9qc9aI-ORTah7kMkhsI5tw,10373
5
- minecraft_datapack_language/dir_map.py,sha256=HmxFkuvWGkzHF8o_GFb4BpuMCRc6QMw8UbmcAI8JVdY,1788
6
- minecraft_datapack_language/mdl_compiler.py,sha256=9o7Q6e-jdZDn3dgitvA6KS_yGR-pQEm3pmHF3trguKs,45102
7
- minecraft_datapack_language/mdl_errors.py,sha256=r0Gu3KhoX1YLPAVW_iO7Q_fPgaf_Dv9tOGSOdKNSzmw,16114
8
- minecraft_datapack_language/mdl_lexer.py,sha256=dVwdgUmdQI7EJaFtoKZpq6rj5156YVjt44mMee-MDIs,23388
9
- minecraft_datapack_language/mdl_linter.py,sha256=z85xoAglENurCh30bR7kEHZ_JeMxcYaLDcGNRAl-RAI,17253
10
- minecraft_datapack_language/mdl_parser.py,sha256=axyjdrcgqeOpbmWolasiIZAjV_RpFaP5QiafLPXAhms,25223
11
- minecraft_datapack_language/python_api.py,sha256=Iao1jbdeW6ekeA80BZG6gNqHVjxQJEheB3DbpVsuTZQ,12304
12
- minecraft_datapack_language/utils.py,sha256=Aq0HAGlXqj9BUTEjaEilpvzEW0EtZYYMMwOqG9db6dE,684
13
- minecraft_datapack_language-16.0.2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
14
- minecraft_datapack_language-16.0.2.dist-info/METADATA,sha256=b9ipiRrA7uorGpPRXtBRwUsA_yQF9_LAEu4KL96yskQ,8343
15
- minecraft_datapack_language-16.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- minecraft_datapack_language-16.0.2.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
17
- minecraft_datapack_language-16.0.2.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
18
- minecraft_datapack_language-16.0.2.dist-info/RECORD,,