minecraft-datapack-language 17.0.10__py3-none-any.whl → 17.0.11__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.
Files changed (43) hide show
  1. minecraft_datapack_language/_embedded/docs/404.html +42 -0
  2. minecraft_datapack_language/_embedded/docs/Gemfile +26 -0
  3. minecraft_datapack_language/_embedded/docs/README.md +84 -0
  4. minecraft_datapack_language/_embedded/docs/_config.yml +74 -0
  5. minecraft_datapack_language/_embedded/docs/_data/version.yml +3 -0
  6. minecraft_datapack_language/_embedded/docs/_docs/cli-reference.md +458 -0
  7. minecraft_datapack_language/_embedded/docs/_docs/contributing.md +352 -0
  8. minecraft_datapack_language/_embedded/docs/_docs/docs-hub.md +266 -0
  9. minecraft_datapack_language/_embedded/docs/_docs/documentation.md +135 -0
  10. minecraft_datapack_language/_embedded/docs/_docs/examples.md +194 -0
  11. minecraft_datapack_language/_embedded/docs/_docs/getting-started.md +230 -0
  12. minecraft_datapack_language/_embedded/docs/_docs/language-reference.md +1637 -0
  13. minecraft_datapack_language/_embedded/docs/_docs/multi-file-projects.md +221 -0
  14. minecraft_datapack_language/_embedded/docs/_docs/python-bindings.md +446 -0
  15. minecraft_datapack_language/_embedded/docs/_docs/vscode-extension.md +381 -0
  16. minecraft_datapack_language/_embedded/docs/_includes/head-custom.html +983 -0
  17. minecraft_datapack_language/_embedded/docs/_includes/navigation.html +362 -0
  18. minecraft_datapack_language/_embedded/docs/_layouts/default.html +27 -0
  19. minecraft_datapack_language/_embedded/docs/_layouts/page.html +281 -0
  20. minecraft_datapack_language/_embedded/docs/_plugins/test_version.rb +13 -0
  21. minecraft_datapack_language/_embedded/docs/_plugins/version_reader.rb +37 -0
  22. minecraft_datapack_language/_embedded/docs/assets/css/style.css +211 -0
  23. minecraft_datapack_language/_embedded/docs/docs.md +134 -0
  24. minecraft_datapack_language/_embedded/docs/downloads.md +444 -0
  25. minecraft_datapack_language/_embedded/docs/icons/favicon-16.png +0 -0
  26. minecraft_datapack_language/_embedded/docs/icons/favicon-32.png +0 -0
  27. minecraft_datapack_language/_embedded/docs/icons/favicon-48.png +0 -0
  28. minecraft_datapack_language/_embedded/docs/icons/favicon-64.png +0 -0
  29. minecraft_datapack_language/_embedded/docs/icons/icon-1024.png +0 -0
  30. minecraft_datapack_language/_embedded/docs/icons/icon-128.png +0 -0
  31. minecraft_datapack_language/_embedded/docs/icons/icon-256.png +0 -0
  32. minecraft_datapack_language/_embedded/docs/icons/icon-512.png +0 -0
  33. minecraft_datapack_language/_embedded/docs/icons/icon-64.png +0 -0
  34. minecraft_datapack_language/_embedded/docs/index.md +378 -0
  35. minecraft_datapack_language/_version.py +2 -2
  36. minecraft_datapack_language/cli.py +5 -1
  37. {minecraft_datapack_language-17.0.10.dist-info → minecraft_datapack_language-17.0.11.dist-info}/METADATA +1 -1
  38. minecraft_datapack_language-17.0.11.dist-info/RECORD +56 -0
  39. minecraft_datapack_language-17.0.10.dist-info/RECORD +0 -22
  40. {minecraft_datapack_language-17.0.10.dist-info → minecraft_datapack_language-17.0.11.dist-info}/WHEEL +0 -0
  41. {minecraft_datapack_language-17.0.10.dist-info → minecraft_datapack_language-17.0.11.dist-info}/entry_points.txt +0 -0
  42. {minecraft_datapack_language-17.0.10.dist-info → minecraft_datapack_language-17.0.11.dist-info}/licenses/LICENSE +0 -0
  43. {minecraft_datapack_language-17.0.10.dist-info → minecraft_datapack_language-17.0.11.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1637 @@
1
+ ---
2
+ layout: page
3
+ title: Language Reference
4
+ permalink: /docs/language-reference/
5
+ ---
6
+
7
+ # MDL (Minecraft Datapack Language) - Complete Language Reference
8
+
9
+ MDL is a simple, scope-aware language that compiles to Minecraft datapack `.mcfunction` files. This document defines the complete language specification.
10
+
11
+ ## Core Language Design
12
+
13
+ ### Philosophy
14
+ - **Explicit scoping**: Variables support explicit `<scope>`; if omitted, `@s` (current entity) is assumed
15
+ - **Clear reading vs writing**: Use `$variable<scope>$` or `$variable$` for reading, and `variable<scope>` or `variable` for writing
16
+ - **No scope inheritance**: Each operation uses its own explicitly defined scope (or defaults to `@s` when omitted)
17
+ - **Default scope**: When no scope specified, always use `@s` (current entity)
18
+ - **No return values**: All functions are void - they execute commands and modify state
19
+ - **No quotes needed**: Use `$variable<scope>$` syntax directly instead of string literals
20
+ - **Function execution**: Use `exec` keyword to execute all functions
21
+ - **Tag-based resources**: Use tag syntax to reference datapack resources like recipes, loot tables, etc.
22
+ - **User-friendly communication**: `say` commands automatically convert to `tellraw` with proper JSON formatting
23
+ - **Real control flow**: If/else if/else statements and while loops that actually work and generate proper Minecraft conditional logic
24
+
25
+ ## Basic Syntax
26
+
27
+ ### Pack Declaration
28
+ ```mdl
29
+ pack "pack_name" "description" pack_format;
30
+ ```
31
+
32
+ ### Namespace Declaration
33
+ ```mdl
34
+ namespace "namespace_name";
35
+ ```
36
+
37
+ ### Tag Declarations
38
+ ```mdl
39
+ // Recipe tags
40
+ tag recipe "RecipeName" "path/to/recipe.json";
41
+ tag recipe "diamond_sword" "recipes/diamond_sword.json";
42
+
43
+ // Loot table tags
44
+ tag loot_table "LootTableName" "path/to/loot_table.json";
45
+ tag loot_table "epic_loot" "loot_tables/epic_loot.json";
46
+
47
+ // Advancement tags
48
+ tag advancement "AdvancementName" "path/to/advancement.json";
49
+ tag advancement "first_spell" "advancements/first_spell.json";
50
+
51
+ // Item modifier tags
52
+ tag item_modifier "ItemModifierName" "path/to/item_modifier.json";
53
+ tag item_modifier "enchant_tool" "item_modifiers/enchant_tool.json";
54
+
55
+ // Predicate tags
56
+ tag predicate "PredicateName" "path/to/predicate.json";
57
+ tag predicate "has_mana" "predicates/has_mana.json";
58
+
59
+ // Structure tags
60
+ tag structure "StructureName" "path/to/structure.json";
61
+ tag structure "custom_house" "structures/custom_house.json";
62
+ ```
63
+
64
+ ### Variable Declaration
65
+ ```mdl
66
+ // Declare variables (scope optional; defaults to @s)
67
+ var num player_score<@a> = 0; // Global scope - accessible by all players
68
+ var num player_health<@s> = 20; // Player-specific scope
69
+ var num player_health = 20; // Same as player_health<@s> = 20
70
+ var num team_score<@a[team=red]> = 0; // Team scope
71
+ var num entity_data<@e[type=armor_stand,tag=mdl_global,limit=1]> = 0; // Custom entity scope
72
+ ```
73
+
74
+ ### Variable Assignment
75
+ ```mdl
76
+ // Scope optional; defaults to @s for both reads and writes when omitted
77
+ player_score<@s> = $player_score<@s>$ + 1; // Add 1 to current player's score
78
+ player_health<@a> = $player_health<@s>$; // Read from @s, write to @a
79
+ team_score<@a[team=red]> = 5; // Set red team score to 5
80
+
81
+ // Default scope is @s when not specified
82
+ player_score = 0; // Same as player_score<@s> = 0;
83
+ ```
84
+
85
+ ### Variable Substitution
86
+ ```mdl
87
+ // Use $variable<scope>$ or $variable$ anywhere in the code
88
+ // $variable$ defaults to <@s>
89
+ tellraw @s {"text":"You have ","extra":[{"score":{"name":"@s","objective":"player_score"}}," points"]};
90
+ tellraw @s {"text":"You have ","extra":[{"score":{"name":"@s","objective":"player_score"}}," points"]}; // $player_score$
91
+ execute if score @s player_score matches 10.. run game:celebrate;
92
+
93
+ // In conditions
94
+ if $player_score$ > 10 {
95
+ player_score = 0; // defaults to <@s>
96
+ }
97
+ ```
98
+
99
+ ### Say Commands (Auto-converted to tellraw)
100
+ ```mdl
101
+ // Simple say commands automatically convert to tellraw with JSON formatting
102
+ say "Welcome to the game!";
103
+ say "You have $player_score<@s>$ points!";
104
+ say "Team score: $team_score<@a[team=red]>$";
105
+
106
+ // These get converted to:
107
+ // tellraw @a {"text":"Welcome to the game!"};
108
+ // tellraw @a {"text":"You have ","extra":[{"score":{"name":"@s","objective":"player_score"}}," points!"]};
109
+ // tellraw @a {"text":"Team score: ","extra":[{"score":{"name":"@s","objective":"team_score"}}]};
110
+ ```
111
+
112
+ ### Functions
113
+
114
+ #### Function Declaration
115
+ ```mdl
116
+ // Basic function
117
+ function game:start_game {
118
+ player_score<@s> = 0;
119
+ player_health<@s> = 20;
120
+ }
121
+
122
+ // Function declaration (no scope on definition)
123
+ function game:reset_player {
124
+ player_score<@s> = 0;
125
+ player_health<@s> = 20;
126
+ }
127
+ ```
128
+
129
+ #### Function Calls
130
+ ```mdl
131
+ // Execute function with exec keyword (runs any function, with or without scope)
132
+ exec game:reset_player; // Execute function
133
+ exec game:start_game; // Execute any function
134
+ exec utils:calculator; // Execute from different namespace
135
+ exec game:reset_player<@s>; // Execute function with scope
136
+ exec game:reset_player<@a>; // Execute function with different scope
137
+
138
+ // Function Macros (Minecraft snapshot): pass macro arguments
139
+ // Inline JSON compound as a single-quoted string to minimize escapes
140
+ exec game:spawn_mob '{id:"minecraft:cow",name:"Betsy"}';
141
+ // With-clause to pull a compound from a data source
142
+ exec game:spawn_mob with storage mymod:ctx path.to.compound;
143
+ ```
144
+
145
+ ### Exec and Scope Execution Rules
146
+ - `exec ns:name` runs `function ns:name` in the current executor context.
147
+ - `exec ns:name<selector>` compiles to `execute as <selector> run function ns:name`.
148
+ - Macro args compile to `function ns:name {json}` form; with-clause compiles to `function ns:name with <data source and path>`.
149
+
150
+ ### Control Structures
151
+
152
+ #### If Statements
153
+ ```mdl
154
+ if $player_score<@s>$ > 10 {
155
+ exec game:celebrate;
156
+ player_score<@s> = 0;
157
+ }
158
+
159
+ if $player_health<@s>$ < 5 {
160
+ exec game:heal;
161
+ } else {
162
+ exec game:check_health;
163
+ }
164
+ ```
165
+
166
+ #### Else If Statements
167
+ ```mdl
168
+ if $player_score<@s>$ > 100 {
169
+ exec game:celebrate;
170
+ player_score<@s> = 0;
171
+ } else if $player_score<@s>$ > 50 {
172
+ exec game:reward;
173
+ player_score<@s> = $player_score<@s>$ + 10;
174
+ } else {
175
+ exec game:encourage;
176
+ player_score<@s>$ = $player_score<@s>$ + 5;
177
+ }
178
+ ```
179
+
180
+ #### While Loops
181
+ ```mdl
182
+ while $counter<@s>$ > 0 {
183
+ counter<@s> = $counter<@s>$ - 1;
184
+ exec game:countdown;
185
+ }
186
+ ```
187
+
188
+ **Note:** The standard `while` loop uses recursive function calls internally. This is simple and fast but can hit Minecraft's function call depth limit for very long-running loops.
189
+
190
+ #### Scheduled While Loops
191
+
192
+ Use `scheduledwhile` to iterate via the scheduler instead of recursion. This avoids recursion limits by running one iteration per game tick and scheduling the next iteration only if the condition remains true.
193
+
194
+ ```mdl
195
+ scheduledwhile $counter<@s>$ > 0 {
196
+ counter<@s> = $counter<@s>$ - 1;
197
+ exec game:countdown;
198
+ }
199
+ ```
200
+
201
+ Compilation strategy:
202
+ - Generates a helper function containing the loop body
203
+ - At the end of the helper, emits `execute if <condition> run schedule function <helper> 1t`
204
+ - Entry point schedules the first iteration with `schedule function <helper> 1t`
205
+ - Breakout occurs naturally when the condition becomes false (no re-schedule)
206
+
207
+ When to use:
208
+ - Prefer `while` for short/medium loops
209
+ - Prefer `scheduledwhile` for long-running loops, per-tick processes, or when avoiding recursion depth limits
210
+
211
+ ### Hooks
212
+ ```mdl
213
+ on_load game:start_game; // Runs when datapack loads
214
+ on_tick game:update_timer; // Runs every tick
215
+ ```
216
+
217
+ **Note:** Hooks use the same function reference syntax as regular function calls, but they are processed at datapack load time, not during execution.
218
+
219
+ ### Raw Blocks
220
+ ```mdl
221
+ // Raw blocks pass through unchanged - no MDL processing
222
+ $!raw
223
+ scoreboard players set @s player_timer_enabled 1
224
+ execute as @a run function game:increase_tick_per_player
225
+ say "Raw commands bypass MDL syntax checking"
226
+ raw!$
227
+
228
+ // Single-line raw commands
229
+ $!raw scoreboard players add @s player_tick_counter 1 raw!$
230
+
231
+ // Raw blocks can contain any Minecraft commands, including complex execute chains
232
+ $!raw
233
+ execute as @a[team=red] at @s run particle minecraft:explosion ~ ~ ~ 1 1 1 0 10
234
+ execute as @a[team=blue] at @s run playsound minecraft:entity.player.levelup player @s ~ ~ ~ 1 1
235
+ raw!$
236
+ ```
237
+
238
+ ### Macro Lines
239
+ ```mdl
240
+ // Lines starting with $ are emitted as-is into the generated .mcfunction and
241
+ // can contain $(variable) placeholders that Minecraft will substitute when the
242
+ // function is called with a macro compound.
243
+ $summon minecraft:cow ~ ~ ~ {CustomName:'{"text":"$(name)"}'}
244
+ ```
245
+
246
+ **Important:** Raw blocks are completely ignored by the MDL parser. They get copied directly to the output `.mcfunction` files without any processing. This means you can use any valid Minecraft command syntax inside raw blocks.
247
+
248
+ ## Scope System
249
+
250
+ ### Core Scope Rules
251
+
252
+ 1. **Variable Writing**: Use `variable<scope>` for assignments and declarations; `variable` defaults to `<@s>`
253
+ 2. **Variable Reading**: Use `$variable<scope>$` for reading values; `$variable$` defaults to `<@s>`
254
+ 3. **Function Execution**: Use `exec` keyword to run any function (with or without scope)
255
+ 4. **No Inheritance**: Functions do not inherit scope from their caller
256
+ 5. **Default Scope**: When no scope specified, always use `@s` (current entity)
257
+ 6. **No Memory**: The system does not remember a variable's declared scope for subsequent operations
258
+
259
+ ### Scope Usage Examples
260
+
261
+ ```mdl
262
+ // VARIABLES: Clear distinction between reading and writing
263
+ var num score<@a> = 0; // Declare with scope
264
+ score<@s> = 5; // Write with scope
265
+ if $score<@a>$ > 10 { ... } // Read with scope
266
+
267
+ // FUNCTIONS: Use exec keyword to run any function (with or without scope)
268
+ exec game:start; // Execute function
269
+ exec utils:helper; // Execute from different namespace
270
+ exec game:start<@a>; // Execute function with scope
271
+ ```
272
+
273
+ ### Scope Examples
274
+
275
+ ```mdl
276
+ // Declare variable with global scope
277
+ var num global_counter<@a> = 0;
278
+
279
+ // Later operations - each specifies its own scope
280
+ global_counter<@s> = 5; // Set current player's counter to 5
281
+ global_counter<@a> = $global_counter<@a>$ + 1; // Increment global counter
282
+ global_counter = 10; // Same as global_counter<@s> = 10 (defaults to @s)
283
+ say "Player has $global_counter$ points"; // $global_counter$ defaults to <@s>
284
+
285
+ // Function calls
286
+ exec game:increment; // Execute function
287
+ exec game:increment<@s>; // Execute function with scope
288
+ exec utils:helper; // Execute from different namespace
289
+ ```
290
+
291
+ ### Valid Scope Selectors
292
+
293
+ ```mdl
294
+ // Basic selectors
295
+ <@s> // Current player
296
+ <@a> // All players
297
+ <@p> // Nearest player
298
+ <@r> // Random player
299
+
300
+ // Complex selectors
301
+ <@a[team=red]> // Red team players
302
+ <@e[type=armor_stand,tag=mdl_global,limit=1]> // Specific entity
303
+ <@s[distance=..5]> // Current player within 5 blocks
304
+
305
+ // Global scope (special case)
306
+ <global> // Maps to @e[type=armor_stand,tag=mdl_global,limit=1]
307
+ // A single invisible armor stand with tag 'mdl_global' is ensured on load
308
+ ```
309
+
310
+ ## Mathematical Expressions
311
+
312
+ ### Operators
313
+ ```mdl
314
+ // Arithmetic
315
+ + (addition)
316
+ - (subtraction)
317
+ * (multiplication)
318
+ / (division)
319
+
320
+ // Comparison
321
+ == (equal)
322
+ != (not equal)
323
+ > (greater than)
324
+ < (less than)
325
+ >= (greater than or equal)
326
+ <= (less than or equal)
327
+
328
+ // Logical
329
+ && (logical AND)
330
+ || (logical OR)
331
+ ! (logical NOT)
332
+
333
+ // Range (for matches)
334
+ .. (range operator)
335
+ ```
336
+
337
+ ### Unary Operators and Precedence
338
+ - Unary minus: `-x` applies before multiplication/division and addition/subtraction. Literals are constant-folded; non-literals are compiled as `0 - x` via a temp score.
339
+ - Logical NOT: `!expr` negates a boolean expression. For comparisons like `!$a$ > 0`, the comparison is compiled first, then inverted using `execute unless`.
340
+ - Precedence (lowest to highest):
341
+ 1) `||`
342
+ 2) `&&`
343
+ 3) Comparisons (`>`, `>=`, `<`, `<=`, `==`, `!=`)
344
+ 4) `+`, `-`
345
+ 5) `*`, `/`
346
+ 6) Unary (`!`, unary `-`)
347
+ 7) Parentheses `(...)`
348
+
349
+ ### Expression Examples
350
+ ```mdl
351
+ // Complex expressions with different scopes
352
+ player_score<@s> = $x<@a>$ + $y<@p>$ * $z<@r>$;
353
+
354
+ // Parentheses for precedence
355
+ player_score<@s> = ($x<@s>$ + $y<@s>$) * 2;
356
+
357
+ // Comparisons
358
+ if $score<@s>$ > 10 {
359
+ exec game:reward;
360
+ }
361
+
362
+ // Logical operators
363
+ if $a<@s>$ > 0 && $b<@s>$ > 0 {
364
+ say "Both are greater than 0";
365
+ }
366
+
367
+ if $a<@s>$ > 0 || $b<@s>$ > 0 {
368
+ say "At least one is greater than 0";
369
+ }
370
+
371
+ // NOT negates the entire comparison when used like: !$a$ > 0
372
+ if !$a$ > 0 {
373
+ say "a is not greater than 0";
374
+ }
375
+
376
+ // Complex logical expression with parentheses
377
+ if ($a$ > 0 && $b$ > 0) || $c$ > 0 {
378
+ say "Condition satisfied";
379
+ }
380
+ // Unary minus with literals and variables
381
+ var num t = -2;
382
+ if ($x$ + -($y$ * 3)) >= -5 { say "ok"; }
383
+ ```
384
+
385
+ ## Reserved Names
386
+
387
+ ### Function Names to Avoid
388
+ - `load` - Conflicts with Minecraft's built-in load function
389
+ - `tick` - Conflicts with Minecraft's built-in tick function
390
+ - Any other names that might conflict with Minecraft's internal functions
391
+
392
+ ### Alternative Naming
393
+ ```mdl
394
+ // Instead of 'load', use:
395
+ function game:initialize { ... }
396
+ function game:setup { ... }
397
+ function game:start { ... }
398
+
399
+ // Instead of 'tick', use:
400
+ function game:update { ... }
401
+ function game:loop { ... }
402
+ function game:process { ... }
403
+ ```
404
+
405
+ ## Complete Examples
406
+
407
+ ### Basic Counter with Tags
408
+ ```mdl
409
+ pack "counter" "Counter example" 82;
410
+ namespace "counter";
411
+
412
+ // Tag declarations
413
+ tag recipe "diamond_sword" "recipes/diamond_sword.json";
414
+ tag loot_table "sword_loot" "loot_tables/sword_loot.json";
415
+ tag advancement "first_sword" "advancements/first_sword.json";
416
+
417
+ var num global_counter<@a> = 0;
418
+ var num player_counter<@s> = 0;
419
+
420
+ function "increment" {
421
+ global_counter<@a> = $global_counter<@a>$ + 1;
422
+ player_counter<@s> = $player_counter<@s>$ + 1;
423
+
424
+ // Using tellraw for player-specific messages
425
+ tellraw @s {"text":"Global: ","extra":[{"score":{"name":"@s","objective":"global_counter"}}," Player: ",{"score":{"name":"@s","objective":"player_counter"}}]};
426
+
427
+ // Using say for broadcast messages (auto-converts to tellraw)
428
+ say "Player $player_counter<@s>$ just incremented the counter!";
429
+ }
430
+
431
+ function "reset_player" {
432
+ player_counter<@s> = 0;
433
+ tellraw @s {"text":"Counter reset!"};
434
+ }
435
+
436
+ on_load "counter:increment";
437
+ ```
438
+
439
+ ### Team Game with Resources
440
+ ```mdl
441
+ pack "teamgame" "Team game example" 82;
442
+ namespace "teamgame";
443
+
444
+ // Tag declarations
445
+ tag recipe "team_banner" "recipes/team_banner.json";
446
+ tag loot_table "team_reward" "loot_tables/team_reward.json";
447
+ tag advancement "team_win" "advancements/team_win.json";
448
+ tag item_modifier "team_boost" "item_modifiers/team_boost.json";
449
+
450
+ var num red_score<@a[team=red]> = 0;
451
+ var num blue_score<@a[team=blue]> = 0;
452
+ var num player_score<@s> = 0;
453
+
454
+ function "award_points" {
455
+ player_score<@s> = $player_score<@s>$ + 10;
456
+
457
+ if $player_score<@s>$ > 100 {
458
+ red_score<@a[team=red]> = $red_score<@a[team=red]>$ + 10;
459
+ tellraw @s {"text":"High score bonus! Red team score: ","extra":[{"score":{"name":"@s","objective":"red_score"}}]};
460
+ } else if $player_score<@s>$ > 50 {
461
+ red_score<@a[team=red]> = $red_score<@a[team=red]>$ + 5;
462
+ tellraw @s {"text":"Medium score bonus! Red team score: ","extra":[{"score":{"name":"@s","objective":"red_score"}}]};
463
+ } else {
464
+ red_score<@a[team=red]> = $red_score<@a[team=red]>$ + 1;
465
+ tellraw @s {"text":"Standard bonus! Red team score: ","extra":[{"score":{"name":"@s","objective":"red_score"}}]};
466
+ }
467
+
468
+ tellraw @s {"text":"Your score: ","extra":[{"score":{"name":"@s","objective":"player_score"}}]};
469
+ }
470
+
471
+ function "show_leaderboard" {
472
+ tellraw @s {"text":"=== LEADERBOARD ==="};
473
+ tellraw @s {"text":"Red Team: ","extra":[{"score":{"name":"@s","objective":"red_score"}}]};
474
+ tellraw @s {"text":"Blue Team: ","extra":[{"score":{"name":"@s","objective":"blue_score"}}]};
475
+ tellraw @s {"text":"Your Score: ","extra":[{"score":{"name":"@s","objective":"player_score"}}]};
476
+ }
477
+
478
+ function "countdown_timer" {
479
+ var num timer<@s> = 10;
480
+
481
+ while $timer<@s>$ > 0 {
482
+ tellraw @s {"text":"Time remaining: ","extra":[{"score":{"name":"@s","objective":"timer"}}]};
483
+ timer<@s> = $timer<@s>$ - 1;
484
+ exec game:wait_one_second;
485
+ }
486
+
487
+ tellraw @s {"text":"Time's up!"};
488
+ }
489
+ ```
490
+
491
+ ### Complex Game Logic
492
+ ```mdl
493
+ pack "game" "Complex game example" 82;
494
+ namespace "game";
495
+
496
+ // Tag declarations
497
+ tag recipe "magic_wand" "recipes/magic_wand.json";
498
+ tag loot_table "magic_loot" "loot_tables/magic_loot.json";
499
+ tag advancement "magic_master" "advancements/magic_master.json";
500
+ tag predicate "has_mana" "predicates/has_mana.json";
501
+ tag structure "magic_tower" "structures/magic_tower.json";
502
+
503
+ var num player_level<@s> = 1;
504
+ var num player_exp<@s> = 0;
505
+ var num global_high_score<@a> = 0;
506
+ var num game_timer<@a> = 0;
507
+
508
+ function "gain_experience" {
509
+ player_exp<@s> = $player_exp<@s>$ + 10;
510
+
511
+ if $player_exp<@s>$ >= 100 {
512
+ player_level<@s> = $player_level<@s>$ + 1;
513
+ player_exp<@s> = 0;
514
+ tellraw @s {"text":"Level up! New level: ","extra":[{"score":{"name":"@s","objective":"player_level"}}]};
515
+
516
+ if $player_level<@s>$ > $global_high_score<@a>$ {
517
+ global_high_score<@a> = $player_level<@s>$;
518
+ tellraw @a {"text":"New high level achieved: ","extra":[{"score":{"name":"@s","objective":"global_high_score"}}]};
519
+ }
520
+ }
521
+ }
522
+
523
+ function "update_timer" {
524
+ game_timer<@a> = $game_timer<@a>$ + 1;
525
+
526
+ if $game_timer<@a>$ >= 1200 {
527
+ game_timer<@a> = 0;
528
+ tellraw @s {"text":"Time's up! Final level: ","extra":[{"score":{"name":"@s","objective":"player_level"}}]};
529
+ }
530
+ }
531
+
532
+ on_tick "game:update_timer";
533
+ ```
534
+
535
+ ## Compilation Rules
536
+
537
+ ### Variable Resolution
538
+ 1. **Declaration**: Variables declare their storage scope when defined
539
+ 2. **Reading**: `$variable<scope>$` gets converted to appropriate Minecraft scoreboard commands
540
+ 3. **Writing**: `variable<scope>` specifies the target scope for assignments
541
+ 4. **Access**: Variables can be accessed at any scope, regardless of where they were declared
542
+
543
+ ### Function Compilation
544
+ 1. **Exec Calls**: `exec function` becomes `execute as @s run function namespace:function`
545
+ 2. **Exec Calls with Scope**: `exec function<@s>` becomes `execute as @s run function namespace:function`
546
+ 3. **No Return Values**: Functions compile to a series of Minecraft commands
547
+
548
+ ### Control Structure Compilation
549
+ 1. **If Statements**: Comparisons compile to scoreboard comparisons. `!=` uses equality with inversion. Boolean expressions (`&&`, `||`, `!`) compile via temporary boolean scores and `execute` chaining.
550
+ 2. **Else If Statements**: Handled as nested `if` with separate generated helper functions; chains are preserved.
551
+ 3. **Else Blocks**: Compiled using inverted conditions with `execute unless` to run the else helper function.
552
+ 4. **While Loops**: Generate recursive function calls that continue while the condition is true.
553
+ 5. **Scheduled While Loops**: Generate a per-tick scheduled helper, tagging entities to iterate; schedule continues while the condition remains true.
554
+ 6. **Nested Structures**: Automatically handle complex nested if/else and while loop combinations.
555
+
556
+ ### Say Command Compilation
557
+ 1. **Simple Text**: `say "message"` becomes `tellraw @a {"text":"message"}`
558
+ 2. **With Variables**: `say "Score: $score<@s>$"` or `say "Score: $score$"` compiles to `tellraw` with a `score` component; `$var$` defaults to `<@s>`.
559
+ 3. **Multiple Variables**: Complex variable substitutions are automatically formatted into proper JSON structure.
560
+ 4. **Default Target**: All say commands target `@a` (all players) for maximum visibility.
561
+
562
+ ### Tag Compilation
563
+ 1. **Recipe Tags**: `tag recipe "name" "path"` generates appropriate tag files
564
+ 2. **Loot Table Tags**: `tag loot_table "name" "path"` generates loot table tag files
565
+ 3. **Advancement Tags**: `tag advancement "name" "path"` generates advancement tag files
566
+ 4. **Item Modifier Tags**: `tag item_modifier "name" "path"` generates item modifier tag files
567
+ 5. **Predicate Tags**: `tag predicate "name" "path"` generates predicate tag files
568
+ 6. **Structure Tags**: `tag structure "name" "path"` generates structure tag files
569
+
570
+ ### Error Handling
571
+ - **Undefined Variables**: Compilation error if variable not declared
572
+ - **Invalid Scopes**: Compilation error if scope selector is malformed
573
+ - **Missing Semicolons**: Compilation error for incomplete statements
574
+ - **Unterminated Blocks**: Compilation error for missing braces
575
+ - **Invalid Tag Paths**: Compilation error if tag file path is malformed
576
+
577
+ ## Best Practices
578
+
579
+ 1. **Always specify scopes explicitly** - Makes code clear and prevents bugs
580
+ 2. **Use consistent syntax** - `$variable<scope>$` for reading, `variable<scope>` for writing
581
+ 3. **Use meaningful variable names** - `player_score<@s>` is clearer than `score<@s>`
582
+ 4. **Group related variables** - Keep variables with similar purposes together
583
+ 5. **Comment complex scopes** - Explain non-standard selectors
584
+ 6. **Avoid reserved names** - Don't use `load`, `tick`, or other Minecraft keywords
585
+ 7. **Use consistent naming** - Pick a convention and stick to it
586
+ 8. **Test scope combinations** - Verify that your scope logic works as expected
587
+ 9. **Organize tag declarations** - Group related tags together at the top of files
588
+ 10. **Use descriptive tag names** - Make tag names clear and meaningful
589
+
590
+ ## Tokenization Specification
591
+
592
+ This section defines exactly how MDL source code is broken down into tokens. This specification is critical for maintaining consistency between the lexer, parser, and compiler.
593
+
594
+ ### Core Token Types
595
+
596
+ #### **Keywords** (Reserved Words)
597
+ ```
598
+ pack, namespace, function, var, num, if, else, while, scheduledwhile, on_load, on_tick, exec, tag
599
+ ```
600
+
601
+ #### **Tag Types** (Resource Categories)
602
+ ```
603
+ recipe, loot_table, advancement, item_modifier, predicate, structure
604
+ ```
605
+
606
+ #### **Identifiers**
607
+ ```
608
+ [a-zA-Z_][a-zA-Z0-9_]*
609
+ ```
610
+ Examples: `player_score`, `game`, `start_game`, `_internal_var`
611
+
612
+ #### **Numbers**
613
+ ```
614
+ [0-9]+(\.[0-9]+)?
615
+ ```
616
+ Examples: `0`, `42`, `3.14`, `1000`
617
+
618
+ #### **Operators**
619
+ ```
620
+ // Arithmetic
621
+ + (PLUS), - (MINUS), * (MULTIPLY), / (DIVIDE)
622
+
623
+ // Comparison
624
+ == (EQUAL), != (NOT_EQUAL), > (GREATER), < (LESS), >= (GREATER_EQUAL), <= (LESS_EQUAL)
625
+
626
+ // Logical
627
+ && (AND), || (OR), ! (NOT)
628
+
629
+ // Assignment
630
+ = (ASSIGN)
631
+
632
+ // Range
633
+ .. (RANGE)
634
+
635
+ // Execution
636
+ exec (EXEC)
637
+ ```
638
+
639
+ #### **Delimiters**
640
+ ```
641
+ ; (SEMICOLON) - Statement terminator
642
+ , (COMMA) - Parameter separator
643
+ : (COLON) - Namespace separator
644
+ ```
645
+
646
+ #### **Brackets and Braces**
647
+ ```
648
+ ( (LPAREN), ) (RPAREN) - Parentheses for expressions and function calls
649
+ { (LBRACE), } (RBRACE) - Braces for code blocks
650
+ [ (LBRACKET), ] (RBRACKET) - Brackets for selectors and arrays
651
+ < (LANGLE), > (RANGLE) - Angle brackets for scope syntax
652
+ ```
653
+
654
+ #### **Special Tokens**
655
+ ```
656
+ $ (DOLLAR) - Variable substitution delimiter; line-start $... as MACRO_LINE
657
+ ! (EXCLAMATION) - Used in $!raw markers
658
+ RAW_CONTENT - Entire content of a raw block
659
+ " (QUOTE) - String literal delimiter (supports both " and ' in lexer)
660
+ ```
661
+
662
+ ### Tag Declaration Tokenization
663
+
664
+ #### **Basic Tag Declaration**
665
+ ```
666
+ tag recipe "RecipeName" "path/to/recipe.json";
667
+ ```
668
+ Tokenized as:
669
+ 1. `TAG` (`tag`)
670
+ 2. `RECIPE` (`recipe`)
671
+ 3. `QUOTE` (`"`)
672
+ 4. `IDENTIFIER` (`RecipeName`)
673
+ 5. `QUOTE` (`"`)
674
+ 6. `QUOTE` (`"`)
675
+ 7. `IDENTIFIER` (`path/to/recipe.json`)
676
+ 8. `QUOTE` (`"`)
677
+ 9. `SEMICOLON` (`;`)
678
+
679
+ #### **Tag Declaration with Complex Path**
680
+ ```
681
+ tag loot_table "EpicLoot" "loot_tables/epic_loot.json";
682
+ ```
683
+ Tokenized as:
684
+ 1. `TAG` (`tag`)
685
+ 2. `LOOT_TABLE` (`loot_table`)
686
+ 3. `QUOTE` (`"`)
687
+ 4. `IDENTIFIER` (`EpicLoot`)
688
+ 5. `QUOTE` (`"`)
689
+ 6. `QUOTE` (`"`)
690
+ 7. `IDENTIFIER` (`loot_tables/epic_loot.json`)
691
+ 8. `QUOTE` (`"`)
692
+ 9. `SEMICOLON` (`;`)
693
+ ```
694
+
695
+ ### Scope Selector Tokenization
696
+
697
+ #### **Basic Selectors**
698
+ ```
699
+ @s, @a, @p, @r
700
+ ```
701
+ These are tokenized as single `IDENTIFIER` tokens.
702
+
703
+ #### **Complex Selectors**
704
+ ```
705
+ @e[type=armor_stand,tag=mdl_global,limit=1]
706
+ ```
707
+ This entire selector is tokenized as a single `IDENTIFIER` token.
708
+
709
+ #### **Scope Syntax**
710
+ ```
711
+ <@s>, <@a[team=red]>, <global>
712
+ ```
713
+ These are tokenized as:
714
+ 1. `LANGLE` (`<`)
715
+ 2. `IDENTIFIER` (the selector content)
716
+ 3. `RANGLE` (`>`)
717
+
718
+ ### Variable Substitution Tokenization
719
+
720
+ #### **Basic Substitution**
721
+ ```
722
+ $player_score<@s>$
723
+ ```
724
+ Tokenized as:
725
+ 1. `DOLLAR` (`$`)
726
+ 2. `IDENTIFIER` (`player_score`)
727
+ 3. `LANGLE` (`<`)
728
+ 4. `IDENTIFIER` (`@s`)
729
+ 5. `RANGLE` (`>`)
730
+ 6. `DOLLAR` (`$`)
731
+
732
+ #### **Shorthand (Default Scope)**
733
+ ```
734
+ $player_score$
735
+ ```
736
+ Tokenized as:
737
+ 1. `DOLLAR` (`$`)
738
+ 2. `IDENTIFIER` (`player_score`)
739
+ 3. `DOLLAR` (`$`)
740
+
741
+ Note: When the scope is omitted, the parser defaults it to `<@s>` during AST construction.
742
+
743
+ #### **Complex Substitution**
744
+ ```
745
+ $team_score<@a[team=red]>$
746
+ ```
747
+ Tokenized as:
748
+ 1. `DOLLAR` (`$`)
749
+ 2. `IDENTIFIER` (`team_score`)
750
+ 3. `LANGLE` (`<`)
751
+ 4. `IDENTIFIER` (`@a[team=red]`)
752
+ 5. `RANGLE` (`>`)
753
+ 6. `DOLLAR` (`$`)
754
+
755
+ ### Function Declaration Tokenization
756
+
757
+ #### **Basic Function**
758
+ ```
759
+ function game:start_game {
760
+ ```
761
+ Tokenized as:
762
+ 1. `FUNCTION` (`function`)
763
+ 2. `IDENTIFIER` (`game`)
764
+ 3. `COLON` (`:`)
765
+ 4. `IDENTIFIER` (`start_game`)
766
+ 5. `LBRACE` (`{`)
767
+
768
+ #### **Function with Scope**
769
+ ```
770
+ function game:reset_player {
771
+ ```
772
+ Tokenized as:
773
+ 1. `FUNCTION` (`function`)
774
+ 2. `IDENTIFIER` (`game`)
775
+ 3. `COLON` (`:`)
776
+ 4. `IDENTIFIER` (`reset_player`)
777
+ 5. `LANGLE` (`<`)
778
+ 6. `IDENTIFIER` (`@s`)
779
+ 7. `RANGLE` (`>`)
780
+ 8. `LBRACE` (`{`)
781
+
782
+ ### Function Call Tokenization
783
+
784
+ #### **Call with Scope**
785
+ ```
786
+ exec game:reset_player<@s>;
787
+ ```
788
+ Tokenized as:
789
+ 1. `EXEC` (`exec`)
790
+ 2. `IDENTIFIER` (`game`)
791
+ 3. `COLON` (`:`)
792
+ 4. `IDENTIFIER` (`reset_player`)
793
+ 5. `LANGLE` (`<`)
794
+ 6. `IDENTIFIER` (`@s`)
795
+ 7. `RANGLE` (`>`)
796
+ 8. `SEMICOLON` (`;`)
797
+
798
+ #### **Exec Call without Scope**
799
+ ```
800
+ exec game:reset_player;
801
+ ```
802
+ Tokenized as:
803
+ 1. `EXEC` (`exec`)
804
+ 2. `IDENTIFIER` (`game`)
805
+ 3. `COLON` (`:`)
806
+ 4. `IDENTIFIER` (`reset_player`)
807
+ 5. `SEMICOLON` (`;`)
808
+ ```
809
+
810
+ ### Variable Declaration Tokenization
811
+
812
+ #### **Basic Declaration**
813
+ ```
814
+ var num player_score<@s> = 0;
815
+ ```
816
+ Tokenized as:
817
+ 1. `VAR` (`var`)
818
+ 2. `NUM` (`num`)
819
+ 3. `IDENTIFIER` (`player_score`)
820
+ 4. `LANGLE` (`<`)
821
+ 5. `IDENTIFIER` (`@s`)
822
+ 6. `RANGLE` (`>`)
823
+ 7. `ASSIGN` (`=`)
824
+ 8. `NUMBER` (`0`)
825
+ 9. `SEMICOLON` (`;`)
826
+
827
+ ### Variable Assignment Tokenization
828
+
829
+ #### **Simple Assignment**
830
+ ```
831
+ player_score<@s> = 42;
832
+ ```
833
+ Tokenized as:
834
+ 1. `IDENTIFIER` (`player_score`)
835
+ 2. `LANGLE` (`<`)
836
+ 3. `IDENTIFIER` (`@s`)
837
+ 4. `RANGLE` (`>`)
838
+ 5. `ASSIGN` (`=`)
839
+ 6. `NUMBER` (`42`)
840
+ 7. `SEMICOLON` (`;`)
841
+
842
+ #### **Expression Assignment**
843
+ ```
844
+ player_score<@s> = $player_score<@s>$ + 1;
845
+ ```
846
+ Tokenized as:
847
+ 1. `IDENTIFIER` (`player_score`)
848
+ 2. `LANGLE` (`<`)
849
+ 3. `IDENTIFIER` (`@s`)
850
+ 4. `RANGLE` (`>`)
851
+ 5. `ASSIGN` (`=`)
852
+ 6. `DOLLAR` (`$`)
853
+ 7. `IDENTIFIER` (`player_score`)
854
+ 8. `LANGLE` (`<`)
855
+ 9. `IDENTIFIER` (`@s`)
856
+ 10. `RANGLE` (`>`)
857
+ 11. `DOLLAR` (`$`)
858
+ 12. `PLUS` (`+`)
859
+ 13. `NUMBER` (`1`)
860
+ 14. `SEMICOLON` (`;`)
861
+
862
+ ### Control Structure Tokenization
863
+
864
+ #### **If Statement**
865
+ ```
866
+ if $player_score<@s>$ > 10 {
867
+ ```
868
+ Tokenized as:
869
+ 1. `IF` (`if`)
870
+ 2. `DOLLAR` (`$`)
871
+ 3. `IDENTIFIER` (`player_score`)
872
+ 4. `LANGLE` (`<`)
873
+ 5. `IDENTIFIER` (`@s`)
874
+ 6. `RANGLE` (`>`)
875
+ 7. `DOLLAR` (`$`)
876
+ 8. `GREATER` (`>`)
877
+ 9. `NUMBER` (`10`)
878
+ 10. `LBRACE` (`{`)
879
+
880
+ #### **While Loop**
881
+ ```
882
+ while $counter<@s>$ > 0 {
883
+ ```
884
+ Tokenized as:
885
+ 1. `WHILE` (`while`)
886
+ 2. `DOLLAR` (`$`)
887
+ 3. `IDENTIFIER` (`counter`)
888
+ 4. `LANGLE` (`<`)
889
+ 5. `IDENTIFIER` (`@s`)
890
+ 6. `RANGLE` (`>`)
891
+ 7. `DOLLAR` (`$`)
892
+ 8. `GREATER` (`>`)
893
+ 9. `NUMBER` (`0`)
894
+ 10. `LBRACE` (`{`)
895
+
896
+ ### Raw Block Tokenization
897
+
898
+ #### **Raw Block Start**
899
+ ```
900
+ $!raw
901
+ ```
902
+ Tokenized as:
903
+ 1. `DOLLAR` (`$`)
904
+ 2. `EXCLAMATION` (`!`)
905
+ 3. `IDENTIFIER` (`raw`)
906
+
907
+ #### **Raw Block End**
908
+ ```
909
+ raw!$
910
+ ```
911
+ Tokenized as:
912
+ 1. `IDENTIFIER` (`raw`)
913
+ 2. `EXCLAMATION` (`!`)
914
+ 3. `DOLLAR` (`$`)
915
+
916
+ ### Whitespace and Comments
917
+
918
+ #### **Whitespace**
919
+ - Spaces, tabs, and newlines are ignored during tokenization
920
+ - They serve only to separate tokens
921
+ - Multiple consecutive whitespace characters are treated as a single separator
922
+
923
+ #### **Comments**
924
+ ```
925
+ // Single line comment
926
+ /* Multi-line comment */
927
+ ```
928
+ Comments are completely ignored during tokenization and do not generate any tokens.
929
+
930
+ **Comment Rules:**
931
+ - Single-line comments start with `//` and continue to the end of the line
932
+ - Multi-line comments start with `/*` and end with `*/`
933
+ - Comments can appear anywhere in the code
934
+ - Comments are stripped out before processing - they don't affect the generated `.mcfunction` files
935
+
936
+ ### Tokenization Rules
937
+
938
+ 1. **Longest Match**: Always consume the longest possible token (e.g., `>=` not `>` then `=`)
939
+ 2. **No Ambiguity**: Each character sequence maps to exactly one token type
940
+ 3. **Scope Priority**: Scope selectors are always tokenized as complete `IDENTIFIER` tokens
941
+ 4. **No Context**: Tokenization is context-free - the same character sequence always produces the same tokens
942
+ 5. **Error Handling**: Invalid characters or unterminated sequences generate appropriate error tokens
943
+ 6. **String Handling**: Quoted strings are tokenized as complete units with their delimiters
944
+
945
+ ### Example Complete Tokenization
946
+
947
+ ```mdl
948
+ tag recipe "diamond_sword" "recipes/diamond_sword.json";
949
+ var num player_score<@s> = 0;
950
+ ```
951
+
952
+ **Tokens Generated:**
953
+ 1. `TAG` (`tag`)
954
+ 2. `RECIPE` (`recipe`)
955
+ 3. `QUOTE` (`"`)
956
+ 4. `IDENTIFIER` (`diamond_sword`)
957
+ 5. `QUOTE` (`"`)
958
+ 6. `QUOTE` (`"`)
959
+ 7. `IDENTIFIER` (`recipes/diamond_sword.json`)
960
+ 8. `QUOTE` (`"`)
961
+ 9. `SEMICOLON` (`;`)
962
+ 10. `VAR` (`var`)
963
+ 11. `NUM` (`num`)
964
+ 12. `IDENTIFIER` (`player_score`)
965
+ 13. `LANGLE` (`<`)
966
+ 14. `IDENTIFIER` (`@s`)
967
+ 15. `RANGLE` (`>`)
968
+ 16. `ASSIGN` (`=`)
969
+ 17. `NUMBER` (`0`)
970
+ 18. `SEMICOLON` (`;`)
971
+ 19. `EOF`
972
+
973
+ This tokenization specification ensures that the lexer, parser, and compiler all work with the same understanding of how MDL source code is structured.
974
+
975
+ ## Edge Cases and Error Handling
976
+
977
+ ### Common Error Scenarios
978
+
979
+ #### **Unterminated Scope Selectors**
980
+ ```mdl
981
+ // ❌ Error: Missing closing >
982
+ var num score<@s = 0;
983
+
984
+ // ✅ Correct
985
+ var num score<@s> = 0;
986
+ ```
987
+
988
+ #### **Invalid Scope Selectors**
989
+ ```mdl
990
+ // ❌ Error: Invalid selector syntax
991
+ var num score<@invalid[type=armor_stand]> = 0;
992
+
993
+ // ✅ Correct
994
+ var num score<@e[type=armor_stand,tag=mdl_global,limit=1]> = 0;
995
+ ```
996
+
997
+ #### **Missing Semicolons**
998
+ ```mdl
999
+ // ❌ Error: Missing semicolon
1000
+ var num score<@s> = 0
1001
+ player_score<@s> = 5
1002
+
1003
+ // ✅ Correct
1004
+ var num score<@s> = 0;
1005
+ player_score<@s> = 5;
1006
+ ```
1007
+
1008
+ #### **Unterminated Blocks**
1009
+ ```mdl
1010
+ // ❌ Error: Missing closing brace
1011
+ function game:test {
1012
+ player_score<@s> = 0;
1013
+ // Missing }
1014
+
1015
+ // ✅ Correct
1016
+ function game:test {
1017
+ player_score<@s> = 0;
1018
+ }
1019
+ ```
1020
+
1021
+ #### **Invalid Variable References**
1022
+ ```mdl
1023
+ // ❌ Error: Variable not declared
1024
+ player_score<@s> = 0;
1025
+ score<@s> = 5; // 'score' was never declared
1026
+
1027
+ // ✅ Correct
1028
+ var num score<@s> = 0;
1029
+ player_score<@s> = 0;
1030
+ score<@s> = 5;
1031
+ ```
1032
+
1033
+ #### **Invalid Tag Declarations**
1034
+ ```mdl
1035
+ // ❌ Error: Missing quotes
1036
+ tag recipe RecipeName "path/to/recipe.json";
1037
+
1038
+ // ❌ Error: Missing semicolon
1039
+ tag recipe "RecipeName" "path/to/recipe.json"
1040
+
1041
+ // ✅ Correct
1042
+ tag recipe "RecipeName" "path/to/recipe.json";
1043
+ ```
1044
+
1045
+ ### Complex Edge Cases
1046
+
1047
+ #### **Nested Scope Selectors in Raw Blocks**
1048
+ ```mdl
1049
+ // This is valid - raw blocks pass through unchanged
1050
+ $!raw
1051
+ execute if score @s player_score<@s> matches 10.. run function game:celebrate
1052
+ raw!$
1053
+ ```
1054
+
1055
+ #### **Scope Selectors with Special Characters**
1056
+ ```mdl
1057
+ // Valid - selector with complex parameters
1058
+ var num data<@e[type=armor_stand,tag=mdl_global,limit=1,nbt={CustomName:'{"text":"Server"}'}]> = 0;
1059
+ ```
1060
+
1061
+ #### **Variable Names with Underscores**
1062
+ ```mdl
1063
+ // Valid - underscores are allowed in variable names
1064
+ var num player_score_red_team<@a[team=red]> = 0;
1065
+ var num _internal_counter<@s> = 0;
1066
+ ```
1067
+
1068
+ #### **Function Names with Numbers**
1069
+ ```mdl
1070
+ // Valid - numbers are allowed in function names
1071
+ function game:level_1_complete {
1072
+ player_score<@s> = player_score<@s> + 100;
1073
+ }
1074
+ ```
1075
+
1076
+ #### **Tag Paths with Special Characters**
1077
+ ```mdl
1078
+ // Valid - paths can contain various characters
1079
+ tag recipe "complex_recipe" "recipes/complex/recipe_v1.2.json";
1080
+ tag loot_table "special_loot" "loot_tables/special/loot_#1.json";
1081
+ ```
1082
+
1083
+ ### Error Recovery
1084
+
1085
+ The MDL compiler attempts to provide helpful error messages:
1086
+
1087
+ 1. **Line and Column Information** - Shows exactly where the error occurred
1088
+ 2. **Context** - Displays the problematic line with surrounding context
1089
+ 3. **Suggestions** - Provides specific guidance on how to fix the error
1090
+ 4. **Error Categories** - Groups errors by type (syntax, scope, undefined variables, invalid tags, etc.)
1091
+
1092
+ ### Performance Considerations
1093
+
1094
+ - **Large Selectors**: Very long scope selectors may impact compilation time
1095
+ - **Deep Nesting**: Excessive nesting of control structures may affect parsing performance
1096
+ - **Raw Block Size**: Large raw blocks are processed efficiently as they're copied without parsing
1097
+ - **Tag Processing**: Tag declarations are processed efficiently as they're simple string operations
1098
+
1099
+ ## Abstract Syntax Tree (AST) Implementation
1100
+
1101
+ The MDL language is implemented using a comprehensive Abstract Syntax Tree (AST) system that represents the parsed code structure. This section explains how the AST works and how it represents all language constructs.
1102
+
1103
+ ### AST Node Hierarchy
1104
+
1105
+ The AST is built using a hierarchy of node classes, each representing a specific language construct:
1106
+
1107
+ #### **Root Node: Program**
1108
+ ```python
1109
+ class Program:
1110
+ pack: Optional[PackDeclaration] # Pack metadata
1111
+ namespace: Optional[NamespaceDeclaration] # Default namespace
1112
+ tags: List[TagDeclaration] # Resource tag declarations
1113
+ variables: List[VariableDeclaration] # Variable declarations
1114
+ functions: List[FunctionDeclaration] # Function definitions
1115
+ hooks: List[HookDeclaration] # Event hooks (on_load, on_tick)
1116
+ statements: List[ASTNode] # Top-level statements
1117
+ ```
1118
+
1119
+ The `Program` node serves as the root of the AST, containing all top-level declarations and statements. This structure allows the compiler to:
1120
+ - Generate the `pack.mcmeta` file from pack declarations
1121
+ - Create the proper directory structure based on namespace
1122
+ - Process all tag references for resource generation
1123
+ - Manage variable scoping across the entire program
1124
+ - Generate function files with proper namespacing
1125
+ - Set up event hooks for automatic execution
1126
+
1127
+ #### **Declaration Nodes**
1128
+
1129
+ **PackDeclaration**
1130
+ ```python
1131
+ class PackDeclaration:
1132
+ name: str # Pack name (e.g., "MyGame")
1133
+ description: str # Pack description
1134
+ pack_format: int # Minecraft pack format version
1135
+ ```
1136
+
1137
+ **NamespaceDeclaration**
1138
+ ```python
1139
+ class NamespaceDeclaration:
1140
+ name: str # Namespace name (e.g., "game")
1141
+ ```
1142
+
1143
+ **TagDeclaration**
1144
+ ```python
1145
+ class TagDeclaration:
1146
+ tag_type: str # Resource type (recipe, loot_table, etc.)
1147
+ name: str # Tag name
1148
+ file_path: str # Path to the JSON file
1149
+ ```
1150
+
1151
+ **VariableDeclaration**
1152
+ ```python
1153
+ class VariableDeclaration:
1154
+ var_type: str # Variable type (currently only "num")
1155
+ name: str # Variable name
1156
+ scope: str # Scope selector (e.g., "<@s>", "<@a[team=red]>")
1157
+ initial_value: Any # Initial value expression
1158
+ ```
1159
+
1160
+ **FunctionDeclaration**
1161
+ ```python
1162
+ class FunctionDeclaration:
1163
+ namespace: str # Function namespace
1164
+ name: str # Function name
1165
+ scope: Optional[str] # Optional scope for function execution
1166
+ body: List[ASTNode] # Function body statements
1167
+ ```
1168
+
1169
+ **HookDeclaration**
1170
+ ```python
1171
+ class HookDeclaration:
1172
+ hook_type: str # Hook type ("on_load" or "on_tick")
1173
+ namespace: str # Function namespace to call
1174
+ name: str # Function name to call
1175
+ scope: Optional[str] # Optional scope for hook execution
1176
+ ```
1177
+
1178
+ #### **Statement Nodes**
1179
+
1180
+ **VariableAssignment**
1181
+ ```python
1182
+ class VariableAssignment:
1183
+ name: str # Variable name
1184
+ scope: str # Scope selector
1185
+ value: Any # Value expression
1186
+ ```
1187
+
1188
+ **VariableSubstitution**
1189
+ ```python
1190
+ class VariableSubstitution:
1191
+ name: str # Variable name
1192
+ scope: str # Scope selector
1193
+ ```
1194
+
1195
+ **FunctionCall**
1196
+ ```python
1197
+ class FunctionCall:
1198
+ namespace: str # Function namespace
1199
+ name: str # Function name
1200
+ scope: Optional[str] # Optional scope
1201
+ ```
1202
+
1203
+ **Control Structures**
1204
+ ```python
1205
+ class IfStatement:
1206
+ condition: Any # Condition expression
1207
+ then_body: List[ASTNode] # Then block statements
1208
+ else_body: Optional[List[ASTNode]] # Optional else block
1209
+
1210
+ class WhileLoop:
1211
+ condition: Any # Loop condition
1212
+ body: List[ASTNode] # Loop body statements
1213
+ ```
1214
+
1215
+ **Commands**
1216
+ ```python
1217
+ class SayCommand:
1218
+ message: str # Message text with variable placeholders
1219
+ variables: List[VariableSubstitution] # Extracted variables
1220
+
1221
+ class RawBlock:
1222
+ content: str # Raw content (passed through unchanged)
1223
+ ```
1224
+
1225
+ #### **Expression Nodes**
1226
+
1227
+ **BinaryExpression**
1228
+ ```python
1229
+ class BinaryExpression:
1230
+ left: Any # Left operand
1231
+ operator: str # Operator (+, -, *, /, >, <, >=, <=, ==, !=)
1232
+ right: Any # Right operand
1233
+ ```
1234
+
1235
+ **LiteralExpression**
1236
+ ```python
1237
+ class LiteralExpression:
1238
+ value: Any # Literal value
1239
+ type: str # Value type ("number", "string", "identifier")
1240
+ ```
1241
+
1242
+ **ParenthesizedExpression**
1243
+ ```python
1244
+ class ParenthesizedExpression:
1245
+ expression: Any # Expression inside parentheses
1246
+ ```
1247
+
1248
+ ### AST Construction Process
1249
+
1250
+ The AST is constructed through a multi-stage process:
1251
+
1252
+ 1. **Lexical Analysis**: Source code is converted to tokens
1253
+ 2. **Parsing**: Tokens are parsed into AST nodes
1254
+ 3. **Validation**: AST structure is validated for correctness
1255
+ 4. **Compilation**: AST is traversed to generate output
1256
+
1257
+ #### **Lexical Analysis (Tokenization)**
1258
+
1259
+ The lexer converts source code into a stream of tokens:
1260
+
1261
+ ```python
1262
+ # Source: var num score<@s> = 0;
1263
+ # Tokens: [VAR, NUM, IDENTIFIER('score'), LESS, IDENTIFIER('@s'),
1264
+ # GREATER, ASSIGN, NUMBER('0'), SEMICOLON]
1265
+ ```
1266
+
1267
+ **Token Types**
1268
+ - **Keywords**: `PACK`, `NAMESPACE`, `FUNCTION`, `VAR`, `IF`, `WHILE`, etc.
1269
+ - **Operators**: `PLUS`, `MINUS`, `MULTIPLY`, `DIVIDE`, `ASSIGN`, `GREATER`, `LESS`, etc.
1270
+ - **Delimiters**: `SEMICOLON`, `COMMA`, `COLON`, `LPAREN`, `RPAREN`, etc.
1271
+ - **Literals**: `IDENTIFIER`, `NUMBER`, `QUOTE`
1272
+ - **Special**: `DOLLAR`, `EXCLAMATION`, `RAW_CONTENT`
1273
+
1274
+ #### **Parsing Strategy**
1275
+
1276
+ The parser uses a recursive descent approach with operator precedence:
1277
+
1278
+ ```python
1279
+ def _parse_expression(self) -> Any:
1280
+ """Parse expressions with proper operator precedence."""
1281
+ return self._parse_comparison()
1282
+
1283
+ def _parse_comparison(self) -> Any:
1284
+ """Parse comparison expressions (>, <, >=, <=, ==, !=)."""
1285
+ expr = self._parse_term()
1286
+ while self._peek().type in [GREATER, LESS, GREATER_EQUAL, LESS_EQUAL, EQUAL, NOT_EQUAL]:
1287
+ operator = self._advance().type
1288
+ right = self._parse_term()
1289
+ expr = BinaryExpression(left=expr, operator=operator, right=right)
1290
+ return expr
1291
+ ```
1292
+
1293
+ **Operator Precedence** (highest to lowest):
1294
+ 1. **Primary**: Variables, literals, parenthesized expressions
1295
+ 2. **Factors**: Multiplication, division
1296
+ 3. **Terms**: Addition, subtraction
1297
+ 4. **Comparisons**: Greater, less, equal, not equal
1298
+
1299
+ ### Logical Operators - Compilation Notes
1300
+
1301
+ - Logical expressions compile into temporary boolean scoreboard values (1 true, 0 false) checked via `execute if/unless`.
1302
+ - `&&` is compiled as a chain of `execute if` conditions; `||` sets the result true if either operand is true.
1303
+ - `!` negates the entire operand. For comparisons like `!$a<@s>$ > 0`, the comparison is evaluated first, then negated.
1304
+ - `!=` is compiled using equality with inversion (`unless score ... = ...`) because Minecraft lacks a direct not-equal comparator.
1305
+
1306
+ ### Integer-Only Arithmetic and Literal Handling
1307
+
1308
+ - Scoreboard arithmetic is integer-only. MDL normalizes integer-like literals: `2.0` -> `2`. Non-integer literals (e.g., `2.5`) cause a compile-time error when used in scoreboard math.
1309
+ - Literal addition/subtraction uses `scoreboard players add/remove`; elides `+0`/`-0`.
1310
+ - Literal multiplication/division uses `scoreboard players multiply/divide`; elides `*1`/`/1`. `*0` sets the temp to zero. Division by zero is a compile-time error.
1311
+ - Mixed expressions are lowered via temporary scores to preserve precedence. Score-to-score operations use `scoreboard players operation`.
1312
+
1313
+ ### AST Traversal and Code Generation
1314
+
1315
+ The AST is designed to support efficient code generation:
1316
+
1317
+ #### **Visitor Pattern Support**
1318
+ ```python
1319
+ class ASTVisitor:
1320
+ def visit_program(self, node: Program): pass
1321
+ def visit_variable_declaration(self, node: VariableDeclaration): pass
1322
+ def visit_function_declaration(self, node: FunctionDeclaration): pass
1323
+ # ... other visit methods
1324
+ ```
1325
+
1326
+ #### **Code Generation Strategy**
1327
+ 1. **Pack Generation**: Create `pack.mcmeta` from pack declarations
1328
+ 2. **Namespace Setup**: Establish directory structure
1329
+ 3. **Tag Processing**: Generate resource references
1330
+ 4. **Variable Management**: Set up scoreboard objectives
1331
+ 5. **Function Generation**: Create `.mcfunction` files
1332
+ 6. **Hook Integration**: Set up automatic execution
1333
+
1334
+ ### Error Handling and Recovery
1335
+
1336
+ The AST system provides comprehensive error handling:
1337
+
1338
+ #### **Parser Error Context**
1339
+ ```python
1340
+ class MDLParserError:
1341
+ message: str # Error description
1342
+ file_path: str # Source file path
1343
+ line: int # Error line number
1344
+ column: int # Error column number
1345
+ line_content: str # Problematic line content
1346
+ suggestion: str # How to fix the error
1347
+ ```
1348
+
1349
+ #### **Error Recovery Strategies**
1350
+ 1. **Graceful Degradation**: Continue parsing when possible
1351
+ 2. **Context Preservation**: Maintain line/column information
1352
+ 3. **Helpful Messages**: Provide specific fix suggestions
1353
+ 4. **Error Aggregation**: Collect multiple errors when possible
1354
+
1355
+ ### Extensibility Features
1356
+
1357
+ The AST system is designed for easy extension:
1358
+
1359
+ #### **Adding New Node Types**
1360
+ ```python
1361
+ class NewNode(ASTNode):
1362
+ def __init__(self, new_field: str):
1363
+ self.new_field = new_field
1364
+ ```
1365
+
1366
+ #### **Adding New Parsers**
1367
+ ```python
1368
+ def _parse_new_construct(self) -> NewNode:
1369
+ # Parse new language construct
1370
+ pass
1371
+ ```
1372
+
1373
+ #### **Adding New Token Types**
1374
+ ```python
1375
+ class TokenType:
1376
+ # ... existing types ...
1377
+ NEW_TYPE = "NEW_TYPE"
1378
+ ```
1379
+
1380
+ ## Parsing System Implementation
1381
+
1382
+ The MDL parser implements a robust, extensible parsing system that handles all language constructs defined in the specification. This section explains how the parsing works and how it processes the language.
1383
+
1384
+ ### Parser Architecture
1385
+
1386
+ The parser uses a **recursive descent** approach with **lookahead** capabilities:
1387
+
1388
+ ```python
1389
+ class MDLParser:
1390
+ def __init__(self, source_file: str = None):
1391
+ self.source_file = source_file
1392
+ self.tokens: List[Token] = []
1393
+ self.current = 0
1394
+ self.current_namespace = "mdl"
1395
+ ```
1396
+
1397
+ #### **Core Parsing Methods**
1398
+
1399
+ **Program Parsing**
1400
+ ```python
1401
+ def _parse_program(self) -> Program:
1402
+ """Parse the complete program structure."""
1403
+ pack = None
1404
+ namespace = None
1405
+ tags = []
1406
+ variables = []
1407
+ functions = []
1408
+ hooks = []
1409
+ statements = []
1410
+
1411
+ while not self._is_at_end():
1412
+ # Parse top-level constructs based on token type
1413
+ if self._peek().type == TokenType.PACK:
1414
+ pack = self._parse_pack_declaration()
1415
+ elif self._peek().type == TokenType.NAMESPACE:
1416
+ namespace = self._parse_namespace_declaration()
1417
+ # ... continue with other constructs
1418
+ ```
1419
+
1420
+ **Declaration Parsing**
1421
+ ```python
1422
+ def _parse_pack_declaration(self) -> PackDeclaration:
1423
+ """Parse: pack "name" "description" format;"""
1424
+ self._expect(TokenType.PACK, "Expected 'pack' keyword")
1425
+ self._expect(TokenType.QUOTE, "Expected opening quote for pack name")
1426
+ name = self._expect_identifier("Expected pack name")
1427
+ # ... continue parsing
1428
+ ```
1429
+
1430
+ **Statement Parsing**
1431
+ ```python
1432
+ def _parse_if_statement(self) -> IfStatement:
1433
+ """Parse: if condition { then_body } else { else_body }"""
1434
+ self._expect(TokenType.IF, "Expected 'if' keyword")
1435
+ condition = self._parse_expression()
1436
+ self._expect(TokenType.LBRACE, "Expected '{' to start if body")
1437
+ then_body = self._parse_block()
1438
+ # ... handle optional else clause
1439
+ ```
1440
+
1441
+ ### Expression Parsing with Operator Precedence
1442
+
1443
+ The parser implements a **Pratt parser** approach for expressions:
1444
+
1445
+ ```python
1446
+ def _parse_expression(self) -> Any:
1447
+ """Entry point for expression parsing."""
1448
+ return self._parse_comparison()
1449
+
1450
+ def _parse_comparison(self) -> Any:
1451
+ """Parse comparison expressions with left associativity."""
1452
+ expr = self._parse_term()
1453
+
1454
+ while not self._is_at_end() and self._peek().type in [
1455
+ TokenType.GREATER, TokenType.LESS, TokenType.GREATER_EQUAL,
1456
+ TokenType.LESS_EQUAL, TokenType.EQUAL, TokenType.NOT_EQUAL
1457
+ ]:
1458
+ operator = self._peek().type
1459
+ self._advance()
1460
+ right = self._parse_term()
1461
+ expr = BinaryExpression(left=expr, operator=operator, right=right)
1462
+
1463
+ return expr
1464
+ ```
1465
+
1466
+ **Precedence Levels**:
1467
+ 1. **Primary**: Variables, literals, parentheses
1468
+ 2. **Factors**: `*`, `/`
1469
+ 3. **Terms**: `+`, `-`
1470
+ 4. **Comparisons**: `>`, `<`, `>=`, `<=`, `==`, `!=`
1471
+
1472
+ ### Scope Selector Parsing
1473
+
1474
+ Scope selectors are parsed differently based on context:
1475
+
1476
+ ```python
1477
+ def _parse_scope_selector(self) -> str:
1478
+ """Parse scope selector: <@s>, <@a[team=red]>, etc."""
1479
+ self._expect(TokenType.LESS, "Expected '<' for scope selector")
1480
+
1481
+ selector_content = ""
1482
+ while not self._is_at_end() and self._peek().type != TokenType.GREATER:
1483
+ selector_content += self._peek().value
1484
+ self._advance()
1485
+
1486
+ self._expect(TokenType.GREATER, "Expected '>' to close scope selector")
1487
+ return f"<{selector_content}>"
1488
+ ```
1489
+
1490
+ **Context-Sensitive Parsing**:
1491
+ - **Variable Declarations**: Use `LESS`/`GREATER` tokens
1492
+ - **Variable Substitutions**: Use `LANGLE`/`RANGLE` tokens
1493
+ - **Function Parameters**: Use `LESS`/`GREATER` tokens
1494
+
1495
+ ### Raw Block Processing
1496
+
1497
+ Raw blocks are handled specially to preserve exact content:
1498
+
1499
+ ```python
1500
+ def _parse_raw_block(self) -> RawBlock:
1501
+ """Parse: $!raw ... raw!$"""
1502
+ # Consume $!raw
1503
+ self._expect(TokenType.DOLLAR, "Expected '$' to start raw block")
1504
+ self._expect(TokenType.EXCLAMATION, "Expected '!' after '$' in raw block")
1505
+ self._expect(TokenType.IDENTIFIER, "Expected 'raw' keyword")
1506
+
1507
+ # Look for RAW_CONTENT token (generated by lexer)
1508
+ if self._peek().type == TokenType.RAW_CONTENT:
1509
+ content = self._peek().value
1510
+ self._advance()
1511
+ else:
1512
+ content = ""
1513
+
1514
+ # Consume raw!$ end marker
1515
+ self._expect(TokenType.IDENTIFIER, "Expected 'raw' to end raw block")
1516
+ self._expect(TokenType.EXCLAMATION, "Expected '!' to end raw block")
1517
+ self._expect(TokenType.DOLLAR, "Expected '$' to end raw block")
1518
+
1519
+ return RawBlock(content=content)
1520
+ ```
1521
+
1522
+ ### Variable Substitution in Strings
1523
+
1524
+ Variable substitutions within strings are handled through regex extraction:
1525
+
1526
+ ```python
1527
+ def _parse_say_command(self) -> SayCommand:
1528
+ """Parse: say "message with $variable<scope>$";"""
1529
+ self._expect(TokenType.IDENTIFIER, "Expected 'say' keyword")
1530
+ self._expect(TokenType.QUOTE, "Expected opening quote for say message")
1531
+
1532
+ # Get string content (includes variable substitutions)
1533
+ if self._peek().type == TokenType.IDENTIFIER:
1534
+ message = self._peek().value
1535
+ self._advance()
1536
+ else:
1537
+ message = ""
1538
+
1539
+ # Extract variables using regex pattern
1540
+ variables = []
1541
+ import re
1542
+ var_pattern = r'\$([a-zA-Z_][a-zA-Z0-9_]*<[^>]+>)\$'
1543
+ matches = re.findall(var_pattern, message)
1544
+
1545
+ for match in matches:
1546
+ if '<' in match and '>' in match:
1547
+ name = match[:match.index('<')]
1548
+ scope = match[match.index('<'):match.index('>')+1]
1549
+ variables.append(VariableSubstitution(name=name, scope=scope))
1550
+
1551
+ return SayCommand(message=message, variables=variables)
1552
+ ```
1553
+
1554
+ ### Error Recovery and Context
1555
+
1556
+ The parser provides detailed error information:
1557
+
1558
+ ```python
1559
+ def _error(self, message: str, suggestion: str):
1560
+ """Raise a parser error with full context."""
1561
+ if self._is_at_end():
1562
+ line = 1
1563
+ column = 1
1564
+ line_content = "end of file"
1565
+ else:
1566
+ token = self._peek()
1567
+ line = token.line
1568
+ column = token.column
1569
+ line_content = token.value
1570
+
1571
+ raise MDLParserError(
1572
+ message=message,
1573
+ file_path=self.source_file,
1574
+ line=line,
1575
+ column=column,
1576
+ line_content=line_content,
1577
+ suggestion=suggestion
1578
+ )
1579
+ ```
1580
+
1581
+ ### Parser Extensibility
1582
+
1583
+ The parser is designed for easy extension:
1584
+
1585
+ #### **Adding New Constructs**
1586
+ ```python
1587
+ def _parse_new_construct(self) -> NewNode:
1588
+ """Parse new language construct."""
1589
+ # Implementation here
1590
+ pass
1591
+
1592
+ # Add to _parse_program method:
1593
+ elif self._peek().type == TokenType.NEW_KEYWORD:
1594
+ statements.append(self._parse_new_construct())
1595
+ ```
1596
+
1597
+ #### **Adding New Expression Types**
1598
+ ```python
1599
+ def _parse_primary(self) -> Any:
1600
+ """Parse primary expressions."""
1601
+ if self._peek().type == TokenType.NEW_TYPE:
1602
+ return self._parse_new_expression()
1603
+ # ... existing cases
1604
+ ```
1605
+
1606
+ ### Performance Optimizations
1607
+
1608
+ The parser includes several performance optimizations:
1609
+
1610
+ 1. **Token Lookahead**: Efficient `_peek()` method for lookahead
1611
+ 2. **Early Exit**: Quick checks for common token types
1612
+ 3. **Memory Efficiency**: Minimal object creation during parsing
1613
+ 4. **Error Recovery**: Fast error detection and reporting
1614
+
1615
+ ### Integration with Lexer
1616
+
1617
+ The parser works seamlessly with the lexer:
1618
+
1619
+ ```python
1620
+ def parse(self, source: str) -> Program:
1621
+ """Parse MDL source code into an AST."""
1622
+ # Lex the source into tokens
1623
+ lexer = MDLLexer(self.source_file)
1624
+ self.tokens = lexer.lex(source)
1625
+ self.current = 0
1626
+
1627
+ # Parse the program
1628
+ return self._parse_program()
1629
+ ```
1630
+
1631
+ This architecture ensures that:
1632
+ - **Lexical errors** are caught early with detailed context
1633
+ - **Parsing errors** provide helpful recovery suggestions
1634
+ - **AST construction** is robust and handles edge cases
1635
+ - **Error reporting** is consistent across the entire pipeline
1636
+
1637
+ The parsing system provides a solid foundation for the MDL compiler, ensuring that all language constructs are properly understood and can be translated into Minecraft datapack commands.