minecraft-datapack-language 15.4.33__py3-none-any.whl → 15.4.35__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.
@@ -3,6 +3,7 @@ from .mdl_lexer import MDLLexer, Token, TokenType
3
3
  from .mdl_parser import MDLParser
4
4
  from .ast_nodes import *
5
5
  from .dir_map import DirMap
6
+ from .python_api import Pack
6
7
 
7
8
  __all__ = [
8
9
  "MDLLexer", "Token", "TokenType",
@@ -13,7 +14,8 @@ __all__ = [
13
14
  "SayCommand", "TellrawCommand", "ExecuteCommand", "ScoreboardCommand",
14
15
  "BinaryExpression", "UnaryExpression", "ParenthesizedExpression", "LiteralExpression",
15
16
  "ScopeSelector",
16
- "DirMap"
17
+ "DirMap",
18
+ "Pack"
17
19
  ]
18
20
 
19
21
  # CLI entry point
@@ -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.33'
32
- __version_tuple__ = version_tuple = (15, 4, 33)
31
+ __version__ = version = '15.4.35'
32
+ __version_tuple__ = version_tuple = (15, 4, 35)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -152,6 +152,12 @@ class MDLCompiler:
152
152
  if hasattr(self, 'temp_commands'):
153
153
  self.temp_commands = []
154
154
 
155
+ # Set current function context and reset per-function counters
156
+ self._current_function_name = func.name
157
+ self.if_counter = 0
158
+ self.else_counter = 0
159
+ self.while_counter = 0
160
+
155
161
  # Generate commands from function body
156
162
  for statement in func.body:
157
163
  cmd = self._statement_to_command(statement)
@@ -231,6 +237,12 @@ class MDLCompiler:
231
237
  load_file = functions_dir / "load.mcfunction"
232
238
  with open(load_file, 'w') as f:
233
239
  f.write(load_content)
240
+ # Ensure minecraft load tag points to namespace:load
241
+ tags_fn_dir = self.output_dir / "data" / "minecraft" / self.dir_map.tags_function
242
+ tags_fn_dir.mkdir(parents=True, exist_ok=True)
243
+ load_tag_file = tags_fn_dir / "load.json"
244
+ with open(load_tag_file, 'w') as f:
245
+ json.dump({"values": [f"{self.current_namespace}:load"]}, f, indent=2)
234
246
 
235
247
  # Create tick function if needed
236
248
  tick_hooks = [h for h in hooks if h.hook_type == "on_tick"]
@@ -239,6 +251,10 @@ class MDLCompiler:
239
251
  tick_file = functions_dir / "tick.mcfunction"
240
252
  with open(tick_file, 'w') as f:
241
253
  f.write(tick_content)
254
+ # Ensure minecraft tick tag points to namespace:tick
255
+ tick_tag_file = tags_fn_dir / "tick.json"
256
+ with open(tick_tag_file, 'w') as f:
257
+ json.dump({"values": [f"{self.current_namespace}:tick"]}, f, indent=2)
242
258
 
243
259
  def _generate_load_function(self, hooks: List[HookDeclaration]) -> str:
244
260
  """Generate the content of load.mcfunction."""
@@ -487,10 +503,8 @@ class MDLCompiler:
487
503
  loop_body_lines.append(cmd)
488
504
 
489
505
  # 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}")
506
+ cond_str, _inv = self._build_condition(while_loop.condition)
507
+ loop_body_lines.append(f"execute if {cond_str} run function {self.current_namespace}:{loop_function_name}")
494
508
 
495
509
  # Store the loop function as its own file
496
510
  self._store_generated_function(loop_function_name, loop_body_lines)
@@ -507,24 +521,21 @@ class MDLCompiler:
507
521
 
508
522
  def _generate_if_function_name(self) -> str:
509
523
  """Generate a unique name for an if function."""
510
- if not hasattr(self, 'if_counter'):
511
- self.if_counter = 0
512
524
  self.if_counter += 1
513
- return f"if_{self.if_counter}"
525
+ prefix = getattr(self, '_current_function_name', 'fn')
526
+ return f"{prefix}__if_{self.if_counter}"
514
527
 
515
528
  def _generate_else_function_name(self) -> str:
516
529
  """Generate a unique name for an else function."""
517
- if not hasattr(self, 'else_counter'):
518
- self.else_counter = 0
519
530
  self.else_counter += 1
520
- return f"else_{self.else_counter}"
531
+ prefix = getattr(self, '_current_function_name', 'fn')
532
+ return f"{prefix}__else_{self.else_counter}"
521
533
 
522
534
  def _generate_while_function_name(self) -> str:
523
535
  """Generate a unique name for a while function."""
524
- if not hasattr(self, 'while_counter'):
525
- self.while_counter = 0
526
536
  self.while_counter += 1
527
- return f"while_{self.while_counter}"
537
+ prefix = getattr(self, '_current_function_name', 'fn')
538
+ return f"{prefix}__while_{self.while_counter}"
528
539
 
529
540
  def _store_generated_function(self, name: str, lines: List[str]):
530
541
  """Store a generated function as a separate file under the same namespace."""
@@ -0,0 +1,207 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Callable, List, Optional, Union
5
+
6
+ from .ast_nodes import (
7
+ Program,
8
+ PackDeclaration,
9
+ NamespaceDeclaration,
10
+ TagDeclaration,
11
+ VariableDeclaration,
12
+ VariableAssignment,
13
+ VariableSubstitution,
14
+ FunctionDeclaration,
15
+ FunctionCall,
16
+ IfStatement,
17
+ WhileLoop,
18
+ RawBlock,
19
+ SayCommand,
20
+ BinaryExpression,
21
+ LiteralExpression,
22
+ )
23
+ from .mdl_compiler import MDLCompiler
24
+
25
+
26
+ # ---------- Expression helpers ----------
27
+
28
+ def num(value: Union[int, float]) -> LiteralExpression:
29
+ return LiteralExpression(value=value, type="number")
30
+
31
+
32
+ def var_read(name: str, scope: str) -> VariableSubstitution:
33
+ # scope must look like <@s>, <@a>, <@e[...]>, or <global>
34
+ return VariableSubstitution(name=name, scope=scope)
35
+
36
+
37
+ def binop(left, operator: str, right) -> BinaryExpression:
38
+ # operator one of: PLUS, MINUS, MULTIPLY, DIVIDE, GREATER, LESS, GREATER_EQUAL, LESS_EQUAL, EQUAL, NOT_EQUAL
39
+ return BinaryExpression(left=left, operator=operator, right=right)
40
+
41
+
42
+ # ---------- Python Bindings ----------
43
+
44
+
45
+ class Pack:
46
+ def __init__(self, name: str, description: str = "", pack_format: int = 82):
47
+ self._pack = PackDeclaration(name=name, description=description, pack_format=pack_format)
48
+ self._namespaces: List[Namespace] = []
49
+ self._variables: List[VariableDeclaration] = []
50
+ self._tags: List[TagDeclaration] = []
51
+ self._hooks: List = [] # HookDeclaration built by Namespace
52
+
53
+ def namespace(self, name: str) -> "Namespace":
54
+ ns = Namespace(self, name)
55
+ self._namespaces.append(ns)
56
+ return ns
57
+
58
+ # Lifecycle hooks reference functions by id "ns:name"
59
+ def on_load(self, function_id: str, scope: Optional[str] = None):
60
+ # defer to Namespace to create HookDeclaration during build
61
+ self._hooks.append(("on_load", function_id, scope))
62
+
63
+ def on_tick(self, function_id: str, scope: Optional[str] = None):
64
+ self._hooks.append(("on_tick", function_id, scope))
65
+
66
+ def tag(self, registry: str, name: str, values: Optional[List[str]] = None, replace: bool = False):
67
+ values = values or []
68
+ # We store TagDeclaration; the compiler writes tag files based on tag_type/name/file_path
69
+ # Here, we map registry + name to a tag reference name; file_path left as name for placeholder.
70
+ self._tags.append(TagDeclaration(tag_type=registry, name=name, file_path=name))
71
+
72
+ def declare_var(self, name: str, scope: str, initial_value: Union[int, float]) -> None:
73
+ self._variables.append(
74
+ VariableDeclaration(var_type="num", name=name, scope=scope, initial_value=num(initial_value))
75
+ )
76
+
77
+ def build(self, output_dir: str):
78
+ # Build Program AST
79
+ namespace_nodes: List[NamespaceDeclaration] = []
80
+ function_nodes: List[FunctionDeclaration] = []
81
+ hook_nodes: List = []
82
+
83
+ # Namespaces and functions
84
+ for ns in self._namespaces:
85
+ namespace_nodes.append(NamespaceDeclaration(name=ns.name))
86
+ function_nodes.extend(ns._functions)
87
+ hook_nodes.extend(ns._hooks)
88
+
89
+ # Hooks added via Pack-level convenience
90
+ for hook_type, function_id, scope in self._hooks:
91
+ ns_name, fn_name = function_id.split(":", 1)
92
+ from .ast_nodes import HookDeclaration
93
+
94
+ hook_nodes.append(
95
+ HookDeclaration(hook_type=hook_type, namespace=ns_name, name=fn_name, scope=scope)
96
+ )
97
+
98
+ program = Program(
99
+ pack=self._pack,
100
+ namespace=namespace_nodes[0] if namespace_nodes else None,
101
+ tags=self._tags,
102
+ variables=self._variables,
103
+ functions=function_nodes,
104
+ hooks=hook_nodes,
105
+ statements=[],
106
+ )
107
+
108
+ MDLCompiler(output_dir).compile(program, output_dir)
109
+
110
+
111
+ class Namespace:
112
+ def __init__(self, pack: Pack, name: str):
113
+ self._pack = pack
114
+ self.name = name
115
+ self._functions: List[FunctionDeclaration] = []
116
+ self._hooks: List = []
117
+
118
+ def function(self, name: str, *commands_or_builder: Union[str, Callable[["FunctionBuilder"], None]]):
119
+ builder = FunctionBuilder(self._pack, self, name)
120
+
121
+ # If a single callable is given, treat it as a builder lambda
122
+ if len(commands_or_builder) == 1 and callable(commands_or_builder[0]):
123
+ commands_or_builder[0](builder)
124
+ else:
125
+ # Interpret simple strings: say "..."; exec ns:name; raw lines fall back to RawBlock
126
+ for cmd in commands_or_builder:
127
+ if not isinstance(cmd, str):
128
+ continue
129
+ stripped = cmd.strip().rstrip(";")
130
+ if stripped.startswith("say "):
131
+ msg = stripped[len("say ") :].strip()
132
+ if msg.startswith("\"") and msg.endswith("\""):
133
+ msg = msg[1:-1]
134
+ builder.say(msg)
135
+ elif stripped.startswith("exec "):
136
+ target = stripped[len("exec ") :].strip()
137
+ # Optional scope in angle brackets e.g., util:helper<@s>
138
+ scope = None
139
+ if "<" in target and target.endswith(">"):
140
+ scope = target[target.index("<") :]
141
+ target = target[: target.index("<")]
142
+ builder.exec(target, scope)
143
+ else:
144
+ builder.raw(cmd)
145
+
146
+ func_node = FunctionDeclaration(namespace=self.name, name=name, scope=None, body=builder._body)
147
+ self._functions.append(func_node)
148
+ return func_node
149
+
150
+ # Convenience forwarding
151
+ def on_load(self, function_id: str, scope: Optional[str] = None):
152
+ from .ast_nodes import HookDeclaration
153
+
154
+ ns_name, fn_name = function_id.split(":", 1)
155
+ self._hooks.append(HookDeclaration(hook_type="on_load", namespace=ns_name, name=fn_name, scope=scope))
156
+
157
+ def on_tick(self, function_id: str, scope: Optional[str] = None):
158
+ from .ast_nodes import HookDeclaration
159
+
160
+ ns_name, fn_name = function_id.split(":", 1)
161
+ self._hooks.append(HookDeclaration(hook_type="on_tick", namespace=ns_name, name=fn_name, scope=scope))
162
+
163
+
164
+ class FunctionBuilder:
165
+ def __init__(self, pack: Pack, namespace: Namespace, function_name: str):
166
+ self._pack = pack
167
+ self._namespace = namespace
168
+ self._name = function_name
169
+ self._body: List = []
170
+
171
+ # Basics
172
+ def say(self, message: str):
173
+ self._body.append(SayCommand(message=message, variables=[]))
174
+
175
+ def raw(self, content: str):
176
+ self._body.append(RawBlock(content=content.rstrip(";")))
177
+
178
+ def exec(self, function_id: str, scope: Optional[str] = None):
179
+ ns, fn = function_id.split(":", 1)
180
+ self._body.append(FunctionCall(namespace=ns, name=fn, scope=scope))
181
+
182
+ # Variables
183
+ def declare_var(self, name: str, scope: str, initial_value: Union[int, float]):
184
+ self._pack.declare_var(name, scope, initial_value)
185
+ # Also set to initial value in this function
186
+ self.set(name, scope, num(initial_value))
187
+
188
+ def set(self, name: str, scope: str, value_expr) -> None:
189
+ self._body.append(VariableAssignment(name=name, scope=scope, value=value_expr))
190
+
191
+ # Control flow
192
+ def if_(self, condition_expr, then_builder: Callable[["FunctionBuilder"], None], else_builder: Optional[Callable[["FunctionBuilder"], None]] = None):
193
+ then_fb = FunctionBuilder(self._pack, self._namespace, self._name)
194
+ then_builder(then_fb)
195
+ else_body = None
196
+ if else_builder:
197
+ else_fb = FunctionBuilder(self._pack, self._namespace, self._name)
198
+ else_builder(else_fb)
199
+ else_body = else_fb._body
200
+ self._body.append(IfStatement(condition=condition_expr, then_body=then_fb._body, else_body=else_body))
201
+
202
+ def while_(self, condition_expr, body_builder: Callable[["FunctionBuilder"], None]):
203
+ body_fb = FunctionBuilder(self._pack, self._namespace, self._name)
204
+ body_builder(body_fb)
205
+ self._body.append(WhileLoop(condition=condition_expr, body=body_fb._body))
206
+
207
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: minecraft-datapack-language
3
- Version: 15.4.33
3
+ Version: 15.4.35
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
@@ -1,17 +1,18 @@
1
- minecraft_datapack_language/__init__.py,sha256=YoTmZWZVH6POAVYMvOTEBKdC-cxQsWi2VomSWZDgYFw,1158
2
- minecraft_datapack_language/_version.py,sha256=oHNM_9N4Gw1WIjw70v38mQJDgIkD6Qbx1TSXwhIvfNI,708
1
+ minecraft_datapack_language/__init__.py,sha256=0KVXBE4ScRaRUrf83aA2tVB-y8A_jplyaxVvtHH6Uw0,1199
2
+ minecraft_datapack_language/_version.py,sha256=W9y3fLZG1wVFwyZO8tkeYzJ_ngoWlfZ8UpHlW0Gsaw4,708
3
3
  minecraft_datapack_language/ast_nodes.py,sha256=nbWrRz137MGMRpmnq8QkXNzrtlaCgyPEknytbkrS_M8,3899
4
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=nS_D2f_G8lcDxUOiADNJWTJQwTnXoQ-W4amyhw6xJoY,34673
6
+ minecraft_datapack_language/mdl_compiler.py,sha256=uw6IcX428HtFD6b_nlcOQQ2V7vx0g4HesQzg22vBnOg,35367
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
+ minecraft_datapack_language/python_api.py,sha256=yU8SIhxRfCWsDW3qBheMQD2Ii0Y6-Tk9VMvVncEPlv4,8183
11
12
  minecraft_datapack_language/utils.py,sha256=Aq0HAGlXqj9BUTEjaEilpvzEW0EtZYYMMwOqG9db6dE,684
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,,
13
+ minecraft_datapack_language-15.4.35.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
14
+ minecraft_datapack_language-15.4.35.dist-info/METADATA,sha256=Np352vNOiaQ91lHGz0TRPKkeYaCye84jo2Kyg7M6tvY,8360
15
+ minecraft_datapack_language-15.4.35.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ minecraft_datapack_language-15.4.35.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
17
+ minecraft_datapack_language-15.4.35.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
18
+ minecraft_datapack_language-15.4.35.dist-info/RECORD,,