minecraft-datapack-language 16.0.16__py3-none-any.whl → 16.0.18__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '16.0.16'
32
- __version_tuple__ = version_tuple = (16, 0, 16)
31
+ __version__ = version = '16.0.18'
32
+ __version_tuple__ = version_tuple = (16, 0, 18)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -824,7 +824,7 @@ class MDLCompiler:
824
824
  Returns one of: '>', '>=', '<', '<=', '==', '!=' or None if unknown.
825
825
  """
826
826
  # Direct symbol passthrough
827
- if op_in in ('>', '>=', '<', '<=', '==', '!='):
827
+ if op_in in ('>', '>=', '<', '<=', '==', '!=', '&&', '||', '!'):
828
828
  return op_in
829
829
  # TokenType instances
830
830
  try:
@@ -840,6 +840,13 @@ class MDLCompiler:
840
840
  return '=='
841
841
  if op_in == TokenType.NOT_EQUAL:
842
842
  return '!='
843
+ # Logical
844
+ if getattr(TokenType, 'AND', None) and op_in == TokenType.AND:
845
+ return '&&'
846
+ if getattr(TokenType, 'OR', None) and op_in == TokenType.OR:
847
+ return '||'
848
+ if getattr(TokenType, 'NOT', None) and op_in == TokenType.NOT:
849
+ return '!'
843
850
  except Exception:
844
851
  pass
845
852
  # String names from bindings or parser
@@ -857,6 +864,12 @@ class MDLCompiler:
857
864
  return '=='
858
865
  if upper == 'NOT_EQUAL' or upper == 'NE':
859
866
  return '!='
867
+ if upper in ('AND', '&&'):
868
+ return '&&'
869
+ if upper in ('OR', '||'):
870
+ return '||'
871
+ if upper in ('NOT', '!'):
872
+ return '!'
860
873
  return None
861
874
 
862
875
  def _expression_to_condition(self, expression: Any) -> str:
@@ -887,10 +900,38 @@ class MDLCompiler:
887
900
  e = e.expression
888
901
  return e
889
902
 
903
+ # Handle logical operators by compiling to a boolean temp scoreboard var
904
+ unwrapped = unwrap(expression)
905
+ op_sym_unwrapped = None
906
+ # Import here to avoid top-level cycle
907
+ from .ast_nodes import UnaryExpression as _UnaryExpr
908
+ if isinstance(unwrapped, BinaryExpression) or isinstance(unwrapped, _UnaryExpr):
909
+ op_sym_unwrapped = self._normalize_operator(getattr(unwrapped, 'operator', None))
910
+ if op_sym_unwrapped in ('&&', '||', '!'):
911
+ bool_var = self._compile_boolean_expression(unwrapped)
912
+ return (f"score @s {bool_var} matches 1..", False)
913
+
890
914
  if isinstance(expression, BinaryExpression):
891
915
  left = unwrap(expression.left)
892
916
  right = unwrap(expression.right)
893
917
  op_sym = self._normalize_operator(expression.operator)
918
+ # Treat a leading logical NOT on a comparator operand as negating the entire comparison
919
+ # Example: !$a > 0 => NOT ( $a > 0 )
920
+ if op_sym in (">", ">=", "<", "<=", "==", "!="):
921
+ try:
922
+ from .ast_nodes import UnaryExpression as _UnaryExpr
923
+ except Exception:
924
+ _UnaryExpr = None
925
+ if _UnaryExpr is not None and isinstance(left, _UnaryExpr) and self._normalize_operator(getattr(left, 'operator', None)) == '!':
926
+ # Build condition for (left.operand op right) and invert
927
+ inner = BinaryExpression(left=left.operand, operator=expression.operator, right=right)
928
+ cond_str, inv = self._build_condition(inner)
929
+ return (cond_str, not inv)
930
+ if _UnaryExpr is not None and isinstance(right, _UnaryExpr) and self._normalize_operator(getattr(right, 'operator', None)) == '!':
931
+ # Build condition for (left op right.operand) and invert
932
+ inner = BinaryExpression(left=left, operator=expression.operator, right=right.operand)
933
+ cond_str, inv = self._build_condition(inner)
934
+ return (cond_str, not inv)
894
935
  # Variable vs literal
895
936
  if op_sym and isinstance(left, VariableSubstitution) and isinstance(right, LiteralExpression) and isinstance(right.value, (int, float)):
896
937
  objective = self.variables.get(left.name, left.name)
@@ -967,6 +1008,53 @@ class MDLCompiler:
967
1008
 
968
1009
  # Fallback: treat as generic condition string
969
1010
  return (self._expression_to_condition(expression), False)
1011
+
1012
+ def _compile_boolean_expression(self, expression: Any, out_var: Optional[str] = None) -> str:
1013
+ """Compile a logical expression into a temporary boolean scoreboard variable (1 true, 0 false).
1014
+ Returns the objective name for the boolean temp variable.
1015
+ """
1016
+ from .ast_nodes import BinaryExpression as Bin, UnaryExpression as Un, ParenthesizedExpression as Par
1017
+ if out_var is None:
1018
+ out_var = self._generate_temp_variable_name()
1019
+ # Ensure initialized to 0
1020
+ self._store_temp_command(f"scoreboard players set @s {out_var} 0")
1021
+
1022
+ def emit_set_true_when(cond_expr: Any):
1023
+ cond_str, inv = self._build_condition(cond_expr)
1024
+ prefix = "unless" if inv else "if"
1025
+ self._store_temp_command(f"execute {prefix} {cond_str} run scoreboard players set @s {out_var} 1")
1026
+
1027
+ expr = expression
1028
+ # Parentheses
1029
+ if isinstance(expr, Par):
1030
+ return self._compile_boolean_expression(expr.expression, out_var)
1031
+ # Unary NOT
1032
+ if isinstance(expr, Un):
1033
+ op = self._normalize_operator(expr.operator)
1034
+ if op == '!':
1035
+ inner_var = self._compile_boolean_expression(expr.operand)
1036
+ # out = NOT inner
1037
+ self._store_temp_command(f"execute unless score @s {inner_var} matches 1.. run scoreboard players set @s {out_var} 1")
1038
+ return out_var
1039
+ # Binary logical
1040
+ if isinstance(expr, Bin):
1041
+ op = self._normalize_operator(expr.operator)
1042
+ if op == '&&':
1043
+ left_var = self._compile_boolean_expression(expr.left)
1044
+ right_var = self._compile_boolean_expression(expr.right)
1045
+ # Set true only when both true
1046
+ self._store_temp_command(f"execute if score @s {left_var} matches 1.. if score @s {right_var} matches 1.. run scoreboard players set @s {out_var} 1")
1047
+ return out_var
1048
+ if op == '||':
1049
+ left_var = self._compile_boolean_expression(expr.left)
1050
+ right_var = self._compile_boolean_expression(expr.right)
1051
+ # Set true when either true
1052
+ self._store_temp_command(f"execute if score @s {left_var} matches 1.. run scoreboard players set @s {out_var} 1")
1053
+ self._store_temp_command(f"execute if score @s {right_var} matches 1.. run scoreboard players set @s {out_var} 1")
1054
+ return out_var
1055
+ # Base comparator or non-logical: set true if base condition holds
1056
+ emit_set_true_when(expr)
1057
+ return out_var
970
1058
 
971
1059
  def _compile_expression_to_temp(self, expression: BinaryExpression, temp_var: str):
972
1060
  """Compile a complex expression to a temporary variable using valid Minecraft commands."""
@@ -59,6 +59,9 @@ class TokenType:
59
59
  LESS = "LESS" # <
60
60
  GREATER_EQUAL = "GREATER_EQUAL" # >=
61
61
  LESS_EQUAL = "LESS_EQUAL" # <=
62
+ AND = "AND" # &&
63
+ OR = "OR" # ||
64
+ NOT = "NOT" # ! (logical not)
62
65
 
63
66
  # Delimiters
64
67
  SEMICOLON = "SEMICOLON" # ;
@@ -552,7 +555,7 @@ class MDLLexer:
552
555
  if self.current + 1 < len(self.source):
553
556
  two_char = self.source[self.current:self.current + 2]
554
557
 
555
- if two_char in ['==', '!=', '>=', '<=', '..']:
558
+ if two_char in ['==', '!=', '>=', '<=', '..', '&&', '||']:
556
559
  self.current += 2
557
560
  self.column += 2
558
561
 
@@ -561,7 +564,9 @@ class MDLLexer:
561
564
  '!=': TokenType.NOT_EQUAL,
562
565
  '>=': TokenType.GREATER_EQUAL,
563
566
  '<=': TokenType.LESS_EQUAL,
564
- '..': TokenType.RANGE
567
+ '..': TokenType.RANGE,
568
+ '&&': TokenType.AND,
569
+ '||': TokenType.OR
565
570
  }[two_char]
566
571
 
567
572
  self.tokens.append(Token(token_type, two_char, self.line, self.column - 2))
@@ -576,6 +581,7 @@ class MDLLexer:
576
581
  '=': TokenType.ASSIGN,
577
582
  '>': TokenType.GREATER,
578
583
  '<': TokenType.LESS,
584
+ '!': TokenType.NOT,
579
585
  ';': TokenType.SEMICOLON,
580
586
  ',': TokenType.COMMA,
581
587
  ':': TokenType.COLON,
@@ -465,7 +465,27 @@ class MDLParser:
465
465
 
466
466
  def _parse_expression(self) -> Any:
467
467
  """Parse an expression with operator precedence."""
468
- return self._parse_comparison()
468
+ return self._parse_or()
469
+
470
+ def _parse_or(self) -> Any:
471
+ """Parse logical OR (||) with left associativity."""
472
+ expr = self._parse_and()
473
+ while not self._is_at_end() and self._peek().type == TokenType.OR:
474
+ operator = self._peek().type
475
+ self._advance()
476
+ right = self._parse_and()
477
+ expr = BinaryExpression(left=expr, operator=operator, right=right)
478
+ return expr
479
+
480
+ def _parse_and(self) -> Any:
481
+ """Parse logical AND (&&) with left associativity."""
482
+ expr = self._parse_comparison()
483
+ while not self._is_at_end() and self._peek().type == TokenType.AND:
484
+ operator = self._peek().type
485
+ self._advance()
486
+ right = self._parse_comparison()
487
+ expr = BinaryExpression(left=expr, operator=operator, right=right)
488
+ return expr
469
489
 
470
490
  def _parse_comparison(self) -> Any:
471
491
  """Parse comparison expressions (>, <, >=, <=, ==, !=)."""
@@ -496,15 +516,24 @@ class MDLParser:
496
516
 
497
517
  def _parse_factor(self) -> Any:
498
518
  """Parse multiplication and division factors."""
499
- expr = self._parse_primary()
519
+ expr = self._parse_unary()
500
520
 
501
521
  while not self._is_at_end() and self._peek().type in [TokenType.MULTIPLY, TokenType.DIVIDE]:
502
522
  operator = self._peek().type
503
523
  self._advance()
504
- right = self._parse_primary()
524
+ right = self._parse_unary()
505
525
  expr = BinaryExpression(left=expr, operator=operator, right=right)
506
526
 
507
527
  return expr
528
+
529
+ def _parse_unary(self) -> Any:
530
+ """Parse unary expressions (logical NOT)."""
531
+ if not self._is_at_end() and self._peek().type in [TokenType.NOT]:
532
+ operator = self._peek().type
533
+ self._advance()
534
+ operand = self._parse_unary()
535
+ return UnaryExpression(operator=operator, operand=operand)
536
+ return self._parse_primary()
508
537
 
509
538
  def _parse_primary(self) -> Any:
510
539
  """Parse primary expressions (literals, variables, parenthesized expressions)."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: minecraft-datapack-language
3
- Version: 16.0.16
3
+ Version: 16.0.18
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,18 +1,18 @@
1
1
  minecraft_datapack_language/__init__.py,sha256=0KVXBE4ScRaRUrf83aA2tVB-y8A_jplyaxVvtHH6Uw0,1199
2
- minecraft_datapack_language/_version.py,sha256=d04fOD3hne3CIblJtZJsJY9-QzwsZ1sOB7PzgWLaDWM,708
2
+ minecraft_datapack_language/_version.py,sha256=mdkSHoP-7a-SjOHB7GFAkWYMURefj-puFhnmsSxEKLE,708
3
3
  minecraft_datapack_language/ast_nodes.py,sha256=L5izavSeXDr766vsfRvJrcnflXNJyXcy0WSfyJPq2ZA,4484
4
4
  minecraft_datapack_language/cli.py,sha256=R4QZYtox-Da9B8pr_kCg_9qc9aI-ORTah7kMkhsI5tw,10373
5
5
  minecraft_datapack_language/dir_map.py,sha256=HmxFkuvWGkzHF8o_GFb4BpuMCRc6QMw8UbmcAI8JVdY,1788
6
- minecraft_datapack_language/mdl_compiler.py,sha256=5lBvLpBbB90SVg1i10oAkeRVYhVDfeBvbJXONoSgAUE,55612
6
+ minecraft_datapack_language/mdl_compiler.py,sha256=3oF0XjXozma8UugNYSd3BbRADIWDAKYXYkcn_mveGHI,60636
7
7
  minecraft_datapack_language/mdl_errors.py,sha256=r0Gu3KhoX1YLPAVW_iO7Q_fPgaf_Dv9tOGSOdKNSzmw,16114
8
- minecraft_datapack_language/mdl_lexer.py,sha256=bDzAj39-kfnX0uVlYQg1oQ7YDb-JO9UZx8jbODQqg6E,23902
8
+ minecraft_datapack_language/mdl_lexer.py,sha256=YuRflOkoMOcjECPAZzoAkJciMks5amWMtGbcTIVKmAs,24166
9
9
  minecraft_datapack_language/mdl_linter.py,sha256=z85xoAglENurCh30bR7kEHZ_JeMxcYaLDcGNRAl-RAI,17253
10
- minecraft_datapack_language/mdl_parser.py,sha256=Krk7Y_E9OKNCcsDOCT7ATvQLbJII951AU2qzEY00GLE,26068
10
+ minecraft_datapack_language/mdl_parser.py,sha256=Bwfk1i6PFsMB3Zs7Z6a3fgngPaWPv-KKqQRQUkFRquM,27272
11
11
  minecraft_datapack_language/python_api.py,sha256=Iao1jbdeW6ekeA80BZG6gNqHVjxQJEheB3DbpVsuTZQ,12304
12
12
  minecraft_datapack_language/utils.py,sha256=Aq0HAGlXqj9BUTEjaEilpvzEW0EtZYYMMwOqG9db6dE,684
13
- minecraft_datapack_language-16.0.16.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
14
- minecraft_datapack_language-16.0.16.dist-info/METADATA,sha256=LLHp-JLyIvV2l7sVIhwgKk_i4NX1S9PRxR-trcER4Zc,8344
15
- minecraft_datapack_language-16.0.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- minecraft_datapack_language-16.0.16.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
17
- minecraft_datapack_language-16.0.16.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
18
- minecraft_datapack_language-16.0.16.dist-info/RECORD,,
13
+ minecraft_datapack_language-16.0.18.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
14
+ minecraft_datapack_language-16.0.18.dist-info/METADATA,sha256=h_rvudGiJ3j0gffl-lpx0BmXkKs_ycMpjzHeBxnIb_c,8344
15
+ minecraft_datapack_language-16.0.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ minecraft_datapack_language-16.0.18.dist-info/entry_points.txt,sha256=c6vjBeCiyQflvPHBRyBk2nJCSfYt3Oc7Sc9V87ySi_U,108
17
+ minecraft_datapack_language-16.0.18.dist-info/top_level.txt,sha256=ADtFI476tbKLLxEAA-aJQAfg53MA3k_DOb0KTFiggfw,28
18
+ minecraft_datapack_language-16.0.18.dist-info/RECORD,,