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.
- minecraft_datapack_language/__init__.py +3 -1
- minecraft_datapack_language/_version.py +2 -2
- minecraft_datapack_language/mdl_compiler.py +24 -13
- minecraft_datapack_language/python_api.py +207 -0
- {minecraft_datapack_language-15.4.33.dist-info → minecraft_datapack_language-15.4.35.dist-info}/METADATA +1 -1
- {minecraft_datapack_language-15.4.33.dist-info → minecraft_datapack_language-15.4.35.dist-info}/RECORD +10 -9
- {minecraft_datapack_language-15.4.33.dist-info → minecraft_datapack_language-15.4.35.dist-info}/WHEEL +0 -0
- {minecraft_datapack_language-15.4.33.dist-info → minecraft_datapack_language-15.4.35.dist-info}/entry_points.txt +0 -0
- {minecraft_datapack_language-15.4.33.dist-info → minecraft_datapack_language-15.4.35.dist-info}/licenses/LICENSE +0 -0
- {minecraft_datapack_language-15.4.33.dist-info → minecraft_datapack_language-15.4.35.dist-info}/top_level.txt +0 -0
@@ -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.
|
32
|
-
__version_tuple__ = version_tuple = (15, 4,
|
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
|
-
|
491
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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=
|
2
|
-
minecraft_datapack_language/_version.py,sha256=
|
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=
|
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.
|
13
|
-
minecraft_datapack_language-15.4.
|
14
|
-
minecraft_datapack_language-15.4.
|
15
|
-
minecraft_datapack_language-15.4.
|
16
|
-
minecraft_datapack_language-15.4.
|
17
|
-
minecraft_datapack_language-15.4.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|