minecraft-datapack-language 15.4.21__py3-none-any.whl → 15.4.23__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '15.4.21'
32
- __version_tuple__ = version_tuple = (15, 4, 21)
31
+ __version__ = version = '15.4.23'
32
+ __version_tuple__ = version_tuple = (15, 4, 23)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -176,7 +176,7 @@ def _generate_scoreboard_objectives(ast: Dict[str, Any], output_dir: Path) -> Li
176
176
  if 'body' in func:
177
177
  for statement in func['body']:
178
178
  # Look for variable assignments and usage
179
- if statement['type'] == 'variable_assignment':
179
+ if statement['type'] in ['variable_assignment', 'explicit_scope_assignment']:
180
180
  if statement['name'] not in seen_variables:
181
181
  variables.append(statement['name'])
182
182
  seen_variables.add(statement['name'])
@@ -280,8 +280,7 @@ def _process_say_command_with_variables(content: str, selector: str, variable_sc
280
280
  var_selector = var_parts[1][:-1] # Remove trailing >
281
281
  components.append(f'{{"score":{{"name":"{var_selector}","objective":"{base_var}"}}}}')
282
282
  else:
283
- # Simple variable: $variable$ - determine selector based on declared scope
284
- var_selector = "@e[type=armor_stand,tag=mdl_server,limit=1]" # Default to global
283
+ # Simple variable: $variable$ - use declared scope if available, otherwise default to current selector
285
284
  if variable_scopes and var_name in variable_scopes:
286
285
  declared_scope = variable_scopes[var_name]
287
286
  if declared_scope == 'global':
@@ -290,7 +289,8 @@ def _process_say_command_with_variables(content: str, selector: str, variable_sc
290
289
  var_selector = declared_scope
291
290
  print(f"DEBUG: Variable {var_name} using declared scope {declared_scope} -> selector {var_selector}")
292
291
  else:
293
- print(f"DEBUG: Variable {var_name} has no declared scope, using default global selector")
292
+ var_selector = selector # Use current selector (@s)
293
+ print(f"DEBUG: Variable {var_name} has no declared scope, using default selector {var_selector}")
294
294
 
295
295
  components.append(f'{{"score":{{"name":"{var_selector}","objective":"{var_name}"}}}}')
296
296
 
@@ -336,16 +336,25 @@ def _process_statement(statement: Any, namespace: str, function_name: str, state
336
336
  var_name = statement['name']
337
337
  value = statement['value']
338
338
 
339
- # Determine the correct selector for this variable based on its declared scope
340
- var_selector = selector # Default to current selector
341
- if variable_scopes and var_name in variable_scopes:
342
- declared_scope = variable_scopes[var_name]
343
- if declared_scope == 'global':
344
- var_selector = "@e[type=armor_stand,tag=mdl_server,limit=1]"
345
- else:
346
- var_selector = declared_scope
347
- print(f"DEBUG: Variable {var_name} assignment using selector: {var_selector} (declared scope: {variable_scopes.get(var_name, 'none')})")
339
+ # For implicit assignments, always default to @s (current entity)
340
+ # Declared scopes only affect variable substitutions, not assignments
341
+ var_selector = selector # Default to current selector (@s)
342
+ print(f"DEBUG: Variable {var_name} assignment using selector: {var_selector} (implicit scope, ignoring declared scope: {variable_scopes.get(var_name, 'none')})")
343
+
344
+ elif statement['type'] == 'explicit_scope_assignment':
345
+ var_name = statement['name']
346
+ value = statement['value']
347
+ explicit_scope = statement['scope']
348
348
 
349
+ # For explicit scope assignments, use the specified scope
350
+ if explicit_scope == 'global':
351
+ var_selector = "@e[type=armor_stand,tag=mdl_server,limit=1]"
352
+ else:
353
+ var_selector = explicit_scope
354
+ print(f"DEBUG: Variable {var_name} assignment using explicit selector: {var_selector}")
355
+
356
+ # Handle assignment value processing for both types
357
+ if statement['type'] in ['variable_assignment', 'explicit_scope_assignment']:
349
358
  # Handle different value types
350
359
  if isinstance(value, int):
351
360
  commands.append(f"scoreboard players set {var_selector} {var_name} {value}")
@@ -841,22 +850,22 @@ def _ast_to_pack(ast: Dict[str, Any], mdl_files: List[Path]) -> Pack:
841
850
  else:
842
851
  # Simple function call without scope
843
852
  function.commands.append(f"function {func_namespace}:{func_name}")
844
- elif statement.get('type') == 'variable_assignment':
853
+ elif statement.get('type') in ['variable_assignment', 'explicit_scope_assignment']:
845
854
  # Handle variable assignments
846
855
  var_name = statement['name']
847
856
  value = statement['value']
848
857
 
849
- # Determine selector based on variable scope
850
- var_selector = "@s" # Default
851
- if 'variables' in ast:
852
- for var_decl in ast['variables']:
853
- if var_decl.get('name') == var_name:
854
- var_scope = var_decl.get('scope')
855
- if var_scope == 'global':
856
- var_selector = "@e[type=armor_stand,tag=mdl_server,limit=1]"
857
- elif var_scope:
858
- var_selector = var_scope
859
- break
858
+ # Determine selector based on assignment type
859
+ if statement.get('type') == 'explicit_scope_assignment':
860
+ # Explicit scope assignment - use specified scope
861
+ explicit_scope = statement['scope']
862
+ if explicit_scope == 'global':
863
+ var_selector = "@e[type=armor_stand,tag=mdl_server,limit=1]"
864
+ else:
865
+ var_selector = explicit_scope
866
+ else:
867
+ # Implicit assignment - always default to @s
868
+ var_selector = "@s"
860
869
 
861
870
  if hasattr(value, 'value'):
862
871
  # Simple literal value
@@ -142,6 +142,32 @@ def show_build_help():
142
142
  print(f"5. Build multiple files in a directory:")
143
143
  print(f" {color.code('mdl build --mdl examples/ -o output --verbose')}")
144
144
  print()
145
+
146
+ # Advanced Features
147
+ print_section("Advanced Features")
148
+ print()
149
+ print(color.info("MDL supports advanced features including explicit scopes in conditions:"))
150
+ print()
151
+ print(f" {color.code('// Variables with different scopes')}")
152
+ print(f" {color.code('var num playerScore = 0; // Defaults to @s')}")
153
+ print(f" {color.code('var num globalCounter scope<global> = 0; // Global scope')}")
154
+ print(f" {color.code('var num teamScore scope<@a[team=red]> = 0; // Team scope')}")
155
+ print()
156
+ print(f" {color.code('// Use explicit scopes in conditions')}")
157
+ print(f" {color.code('if \"$playerScore<@s>$ > 10\" {{')}")
158
+ print(f" {color.code(' say \"Current player score is high!\";')}")
159
+ print(f" {color.code('}')}")
160
+ print()
161
+ print(f" {color.code('if \"$globalCounter<global>$ > 100\" {{')}")
162
+ print(f" {color.code(' say \"Global counter reached milestone!\";')}")
163
+ print(f" {color.code('}')}")
164
+ print()
165
+ print(f" {color.code('if \"$teamScore<@a[team=red]>$ > 50\" {{')}")
166
+ print(f" {color.code(' say \"Red team is winning!\";')}")
167
+ print(f" {color.code('}')}")
168
+ print()
169
+ print(color.info("This allows you to check variables across different scopes without changing their declared scope."))
170
+ print()
145
171
 
146
172
  # Output Structure
147
173
  print_section("Output Structure")
@@ -316,10 +316,15 @@ class MDLParser:
316
316
  suggestion="Replace 'for' with 'while' and adjust the loop structure"
317
317
  )
318
318
 
319
- # Check if this is a variable assignment (identifier followed by =)
319
+ # Check if this is a variable assignment
320
+ # Pattern 1: identifier = (simple assignment)
320
321
  if (self.current + 1 < len(self.tokens) and
321
322
  self.tokens[self.current + 1].type == TokenType.ASSIGN):
322
323
  return self._parse_variable_assignment()
324
+
325
+ # Pattern 2: identifier<scope> = (explicit scope assignment)
326
+ elif self._is_explicit_scope_assignment():
327
+ return self._parse_explicit_scope_assignment()
323
328
  else:
324
329
  # Assume it's a command
325
330
  return self._parse_command()
@@ -386,6 +391,72 @@ class MDLParser:
386
391
 
387
392
  return {"type": "variable_assignment", "name": name, "value": value}
388
393
 
394
+ def _is_explicit_scope_assignment(self) -> bool:
395
+ """Check if current position is an explicit scope assignment pattern: var<scope> = value"""
396
+ if self.current >= len(self.tokens):
397
+ return False
398
+
399
+ # Look for pattern: IDENTIFIER LANGLE ... RANGLE ASSIGN
400
+ idx = self.current + 1
401
+
402
+ # Must have LANGLE after identifier
403
+ if idx >= len(self.tokens) or self.tokens[idx].type != TokenType.LANGLE:
404
+ return False
405
+ idx += 1
406
+
407
+ # Skip tokens until we find RANGLE
408
+ while idx < len(self.tokens) and self.tokens[idx].type != TokenType.RANGLE:
409
+ idx += 1
410
+
411
+ # Must find RANGLE
412
+ if idx >= len(self.tokens) or self.tokens[idx].type != TokenType.RANGLE:
413
+ return False
414
+ idx += 1
415
+
416
+ # Must have ASSIGN after RANGLE
417
+ if idx >= len(self.tokens) or self.tokens[idx].type != TokenType.ASSIGN:
418
+ return False
419
+
420
+ return True
421
+
422
+ def _parse_explicit_scope_assignment(self) -> dict:
423
+ """Parse variable assignment with explicit scope: var<scope> = value"""
424
+ # Parse variable name
425
+ name_token = self._match(TokenType.IDENTIFIER)
426
+ name = name_token.value
427
+
428
+ # Parse scope: <scope>
429
+ self._match(TokenType.LANGLE)
430
+
431
+ # Parse scope selector content
432
+ scope_parts = []
433
+ while not self._is_at_end() and self._peek().type != TokenType.RANGLE:
434
+ scope_parts.append(self._peek().value)
435
+ self._advance()
436
+
437
+ if self._is_at_end():
438
+ raise create_parser_error(
439
+ message="Unterminated scope selector in assignment",
440
+ file_path=self.source_file,
441
+ line=self._peek().line,
442
+ column=self._peek().column,
443
+ line_content=self._peek().value,
444
+ suggestion="Add a closing '>' to terminate the scope selector"
445
+ )
446
+
447
+ self._match(TokenType.RANGLE)
448
+ scope = ''.join(scope_parts)
449
+
450
+ # Parse assignment
451
+ self._match(TokenType.ASSIGN)
452
+
453
+ # Parse the value
454
+ value = self._parse_expression()
455
+
456
+ self._match(TokenType.SEMICOLON)
457
+
458
+ return {"type": "explicit_scope_assignment", "name": name, "scope": scope, "value": value}
459
+
389
460
  def _parse_if_statement(self) -> IfStatement:
390
461
  """Parse if statement."""
391
462
  self._match(TokenType.IF)
@@ -836,8 +907,34 @@ class MDLParser:
836
907
  identifier_name = token.value
837
908
  self._advance() # consume the identifier
838
909
 
839
- # Check if the identifier contains a scope selector
840
- if '<' in identifier_name and identifier_name.endswith('>'):
910
+ # Check for explicit scope syntax: identifier<scope>
911
+ if not self._is_at_end() and self._peek().type == TokenType.LANGLE:
912
+ self._advance() # consume <
913
+
914
+ # Parse scope selector content
915
+ scope_parts = []
916
+ while not self._is_at_end() and self._peek().type != TokenType.RANGLE:
917
+ scope_parts.append(self._peek().value)
918
+ self._advance()
919
+
920
+ if self._is_at_end():
921
+ raise create_parser_error(
922
+ message="Unterminated scope selector in expression",
923
+ file_path=self.source_file,
924
+ line=self._peek().line,
925
+ column=self._peek().column,
926
+ line_content=self._peek().value,
927
+ suggestion="Add a closing '>' to terminate the scope selector"
928
+ )
929
+
930
+ self._advance() # consume >
931
+ scope_selector = ''.join(scope_parts)
932
+
933
+ # Return variable expression with explicit scope encoded in name
934
+ return VariableExpression(f"{identifier_name}<{scope_selector}>")
935
+
936
+ # Check if the identifier contains a scope selector (legacy single-token format)
937
+ elif '<' in identifier_name and identifier_name.endswith('>'):
841
938
  # Extract variable name and scope selector
842
939
  parts = identifier_name.split('<', 1)
843
940
  if len(parts) == 2:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: minecraft-datapack-language
3
- Version: 15.4.21
3
+ Version: 15.4.23
4
4
  Summary: Compile JavaScript-style MDL language or Python API 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
@@ -477,6 +477,90 @@ This will create a datapack with:
477
477
 
478
478
  ---
479
479
 
480
+ ## 🎯 **Explicit Scope System**
481
+
482
+ MDL features a powerful **explicit scope system** that makes variable access clear and unambiguous. Each variable access must specify its scope, eliminating hidden state and making code more readable.
483
+
484
+ ### **Variable Declaration Scopes**
485
+
486
+ ```mdl
487
+ // Player-specific variable (defaults to @s in execution context)
488
+ var num playerScore = 0;
489
+
490
+ // Server-wide variable (stored on server armor stand)
491
+ var num globalCounter scope<global> = 0;
492
+
493
+ // Team-specific variable
494
+ var num teamScore scope<@a[team=red]> = 0;
495
+
496
+ // Custom entity variable
497
+ var num entityData scope<@e[type=armor_stand,tag=special,limit=1]> = 0;
498
+ ```
499
+
500
+ ### **Variable Access with Explicit Scopes**
501
+
502
+ ```mdl
503
+ // Each variable access must specify the scope
504
+ playerScore<@s> = 42; // Player scope
505
+ globalCounter<global> = 100; // Global scope
506
+ teamScore<@a[team=red]> = 5; // Team scope
507
+ entityData<@e[type=armor_stand,tag=special,limit=1]> = 10; // Custom entity
508
+ ```
509
+
510
+ ### **Explicit Scopes in Conditions**
511
+
512
+ **NEW!** MDL now supports explicit scope selectors in if/while conditions, allowing you to override declared variable scopes:
513
+
514
+ ```mdl
515
+ function "check_scores" {
516
+ // Check current player's score
517
+ if "$playerScore<@s>$ > 10" {
518
+ say "Your score is high!";
519
+ }
520
+
521
+ // Check global counter
522
+ if "$globalCounter<global>$ > 100" {
523
+ say "Global milestone reached!";
524
+ }
525
+
526
+ // Check another player's score
527
+ if "$playerScore<@p[name=Steve]>$ > 20" {
528
+ say "Steve has a good score!";
529
+ }
530
+
531
+ // Check team score
532
+ if "$teamScore<@a[team=red]>$ > 50" {
533
+ say "Red team is winning!";
534
+ }
535
+
536
+ // Use explicit scopes in while loops too
537
+ while "$globalCounter<global>$ < 10" {
538
+ globalCounter<global> = globalCounter<global> + 1;
539
+ say "Counter: $globalCounter$";
540
+ }
541
+ }
542
+ ```
543
+
544
+ **Benefits:**
545
+ - **Override declared scopes**: Use different scopes than what was declared
546
+ - **Check other entities**: Compare scores across different players/teams
547
+ - **Flexible conditions**: Mix and match scopes as needed
548
+ - **Clear intent**: Explicit scope makes code more readable and debuggable
549
+
550
+ ### **Scope Mapping**
551
+
552
+ MDL maps scopes to Minecraft selectors:
553
+
554
+ | MDL Scope | Minecraft Selector | Description |
555
+ |-----------|-------------------|-------------|
556
+ | `scope<global>` | `@e[type=armor_stand,tag=mdl_server,limit=1]` | Server-wide storage |
557
+ | `scope<@s>` | `@s` | Current player |
558
+ | `scope<@a>` | `@a` | All players |
559
+ | `scope<@a[team=red]>` | `@a[team=red]` | Red team players |
560
+ | `scope<@e[type=armor_stand,tag=something,limit=1]>` | `@e[type=armor_stand,tag=something,limit=1]` | Custom entity |
561
+
562
+ ---
563
+
480
564
  ## 🎯 **Advanced Multi-File Examples with Namespaces**
481
565
 
482
566
  ### **Modern Namespace System**
@@ -1,11 +1,11 @@
1
1
  minecraft_datapack_language/__init__.py,sha256=i-qCchbe5b2Fshgc6yCU9mddOLs2UBt9SAcLqfUIrT0,606
2
- minecraft_datapack_language/_version.py,sha256=UOCaFCMuBKEzZuLZKqrQpMf_Fgz3oXnIRu-he6qYSek,708
2
+ minecraft_datapack_language/_version.py,sha256=2M9DVfO2B0gW3xckA3jnSFVOsLpE1n8q1owBMbxNoIU,708
3
3
  minecraft_datapack_language/ast_nodes.py,sha256=pgjI2Nlap3ixFPgWqGSkqncG9zB91h5BKgRjtcJqMew,2118
4
4
  minecraft_datapack_language/cli.py,sha256=p5A_tEEXugN2NhQFbbgfwi4FxbWYD91RWeKR_A3Vuec,6263
5
- minecraft_datapack_language/cli_build.py,sha256=u0XIOH_zTARPC6dvWf-411OqrF7RjT7Z9Hkp6hTeZsI,47990
5
+ minecraft_datapack_language/cli_build.py,sha256=hid5hdIwd3NVppBoatkij_OeH9HsBjKTT1WseRdSUs0,48510
6
6
  minecraft_datapack_language/cli_check.py,sha256=bPq9gHsxQ1CIiftkrAtRCifWkVAyjp5c8Oay2NNQ1qs,6277
7
7
  minecraft_datapack_language/cli_colors.py,sha256=Hr8awY966bGSnVdXL3WnmRhSP1wH56vTQKGt5z-kIQM,7878
8
- minecraft_datapack_language/cli_help.py,sha256=170MePwG9dV9IowG02SwiRT04fbrEo8FBuREZNwDhXY,20051
8
+ minecraft_datapack_language/cli_help.py,sha256=Rc-v9E2kctsdN_lMunqdKuZ8EZ8rcZIjBCOPrXLBQeE,21363
9
9
  minecraft_datapack_language/cli_new.py,sha256=_pj5EeXESAG00C80_os9jONIXAMcsu2eoR8xVJWDw6g,9347
10
10
  minecraft_datapack_language/cli_utils.py,sha256=qzba7BRHFK9AliSgSLGtxA0p2_Y-Rm20ysDzLLz_KD4,10632
11
11
  minecraft_datapack_language/dir_map.py,sha256=HmxFkuvWGkzHF8o_GFb4BpuMCRc6QMw8UbmcAI8JVdY,1788
@@ -14,12 +14,12 @@ minecraft_datapack_language/linter.py,sha256=7UqbygC5JPCGg-BSOq65NB2xEJBu_OUOYII
14
14
  minecraft_datapack_language/mdl_errors.py,sha256=mz6uyPkeBpbMHj4PiAyVecEVJ9_hdSfR45QAjG6oYf0,15690
15
15
  minecraft_datapack_language/mdl_lexer_js.py,sha256=VvbhKm727khdSAABxa03hoIIA7H3hWi3RLp9BSXbhY0,28277
16
16
  minecraft_datapack_language/mdl_linter.py,sha256=z85xoAglENurCh30bR7kEHZ_JeMxcYaLDcGNRAl-RAI,17253
17
- minecraft_datapack_language/mdl_parser_js.py,sha256=SQzc67pKls3NVnQaT0xIILGqpZYAmcZn78TQ0KIM4TE,40216
17
+ minecraft_datapack_language/mdl_parser_js.py,sha256=ttnxvZCHKwRu51IL9U0w7WIX1q19qXNJJYt0D4f4a8M,44149
18
18
  minecraft_datapack_language/pack.py,sha256=nYiXQ3jgJlDfc4m-65f7C2LFhDRioaUU_XVy6Na4SJI,34625
19
19
  minecraft_datapack_language/utils.py,sha256=Aq0HAGlXqj9BUTEjaEilpvzEW0EtZYYMMwOqG9db6dE,684
20
- minecraft_datapack_language-15.4.21.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
21
- minecraft_datapack_language-15.4.21.dist-info/METADATA,sha256=mgbuo3_xIVmGi54rRh0IGeS1Oh4aLfbAjtM2b87KUEI,35230
22
- minecraft_datapack_language-15.4.21.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- minecraft_datapack_language-15.4.21.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
24
- minecraft_datapack_language-15.4.21.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
25
- minecraft_datapack_language-15.4.21.dist-info/RECORD,,
20
+ minecraft_datapack_language-15.4.23.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
21
+ minecraft_datapack_language-15.4.23.dist-info/METADATA,sha256=dnH_xWsKU808MSG335P3esNgF5C7H8ujM-O-7hs6nb4,37917
22
+ minecraft_datapack_language-15.4.23.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ minecraft_datapack_language-15.4.23.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
24
+ minecraft_datapack_language-15.4.23.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
25
+ minecraft_datapack_language-15.4.23.dist-info/RECORD,,