minecraft-datapack-language 15.4.28__py3-none-any.whl → 15.4.30__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 (27) hide show
  1. minecraft_datapack_language/__init__.py +23 -2
  2. minecraft_datapack_language/_version.py +2 -2
  3. minecraft_datapack_language/ast_nodes.py +87 -59
  4. minecraft_datapack_language/cli.py +276 -139
  5. minecraft_datapack_language/mdl_compiler.py +470 -0
  6. minecraft_datapack_language/mdl_errors.py +14 -0
  7. minecraft_datapack_language/mdl_lexer.py +624 -0
  8. minecraft_datapack_language/mdl_parser.py +573 -0
  9. minecraft_datapack_language-15.4.30.dist-info/METADATA +266 -0
  10. minecraft_datapack_language-15.4.30.dist-info/RECORD +17 -0
  11. minecraft_datapack_language/cli_build.py +0 -1292
  12. minecraft_datapack_language/cli_check.py +0 -155
  13. minecraft_datapack_language/cli_colors.py +0 -264
  14. minecraft_datapack_language/cli_help.py +0 -508
  15. minecraft_datapack_language/cli_new.py +0 -300
  16. minecraft_datapack_language/cli_utils.py +0 -276
  17. minecraft_datapack_language/expression_processor.py +0 -352
  18. minecraft_datapack_language/linter.py +0 -409
  19. minecraft_datapack_language/mdl_lexer_js.py +0 -754
  20. minecraft_datapack_language/mdl_parser_js.py +0 -1049
  21. minecraft_datapack_language/pack.py +0 -758
  22. minecraft_datapack_language-15.4.28.dist-info/METADATA +0 -1274
  23. minecraft_datapack_language-15.4.28.dist-info/RECORD +0 -25
  24. {minecraft_datapack_language-15.4.28.dist-info → minecraft_datapack_language-15.4.30.dist-info}/WHEEL +0 -0
  25. {minecraft_datapack_language-15.4.28.dist-info → minecraft_datapack_language-15.4.30.dist-info}/entry_points.txt +0 -0
  26. {minecraft_datapack_language-15.4.28.dist-info → minecraft_datapack_language-15.4.30.dist-info}/licenses/LICENSE +0 -0
  27. {minecraft_datapack_language-15.4.28.dist-info → minecraft_datapack_language-15.4.30.dist-info}/top_level.txt +0 -0
@@ -1,159 +1,296 @@
1
+ #!/usr/bin/env python3
1
2
  """
2
- MDL CLI - Simplified Minecraft Datapack Language Compiler
3
- Handles basic control structures and number variables only
3
+ MDL CLI - Command Line Interface for Minecraft Datapack Language
4
4
  """
5
5
 
6
6
  import argparse
7
7
  import sys
8
+ import os
8
9
  from pathlib import Path
9
- from typing import Optional
10
-
11
- from .cli_build import build_mdl
12
- from .cli_check import lint_mdl_file_wrapper, lint_mdl_directory_wrapper
13
- from .cli_new import create_new_project
14
- from .cli_help import show_main_help, show_build_help, show_check_help, show_new_help
15
- from .mdl_errors import MDLErrorCollector, create_error, MDLConfigurationError
10
+ from .mdl_lexer import MDLLexer
11
+ from .mdl_parser import MDLParser
12
+ from .mdl_compiler import MDLCompiler
13
+ from .mdl_errors import MDLLexerError, MDLParserError, MDLCompilerError
16
14
 
17
15
 
18
16
  def main():
19
17
  """Main CLI entry point."""
20
- error_collector = MDLErrorCollector()
18
+ parser = argparse.ArgumentParser(
19
+ description="MDL (Minecraft Datapack Language) - Compile MDL files to Minecraft datapacks",
20
+ formatter_class=argparse.RawDescriptionHelpFormatter,
21
+ epilog="""
22
+ Examples:
23
+ mdl build --mdl main.mdl -o dist # Build a single MDL file
24
+ mdl build --mdl . -o dist # Build all MDL files in current directory
25
+ mdl check main.mdl # Check syntax without building
26
+ mdl new my_project # Create a new project
27
+ """
28
+ )
29
+
30
+ subparsers = parser.add_subparsers(dest='command', help='Available commands')
31
+
32
+ # Build command
33
+ build_parser = subparsers.add_parser('build', help='Build MDL files into a datapack')
34
+ build_parser.add_argument('--mdl', required=True, help='MDL file(s) or directory to build')
35
+ build_parser.add_argument('-o', '--output', required=True, help='Output directory for the datapack')
36
+ build_parser.add_argument('--verbose', action='store_true', help='Show detailed output')
37
+
38
+ # Check command
39
+ check_parser = subparsers.add_parser('check', help='Check MDL files for syntax errors')
40
+ check_parser.add_argument('files', nargs='+', help='MDL files to check')
41
+ check_parser.add_argument('--verbose', action='store_true', help='Show detailed output')
42
+
43
+ # New command
44
+ new_parser = subparsers.add_parser('new', help='Create a new MDL project')
45
+ new_parser.add_argument('project_name', help='Name of the new project')
46
+ new_parser.add_argument('--pack-name', help='Custom name for the datapack')
47
+ new_parser.add_argument('--pack-format', type=int, default=82, help='Pack format number (default: 82)')
48
+
49
+ args = parser.parse_args()
50
+
51
+ if not args.command:
52
+ parser.print_help()
53
+ return 1
21
54
 
22
55
  try:
23
- # Check for help requests and determine command first
24
- has_help = '--help' in sys.argv or '-h' in sys.argv
25
-
26
- # Determine which command is being used for help
27
- help_command = None
28
- if has_help:
29
- for i, arg in enumerate(sys.argv[1:], 1):
30
- if arg in ['build', 'check', 'new']:
31
- help_command = arg
32
- break
33
-
34
- # Handle command-specific help before any parsing
35
- if has_help and help_command == 'build':
36
- show_build_help()
37
- return
38
- elif has_help and help_command == 'check':
39
- show_check_help()
40
- return
41
- elif has_help and help_command == 'new':
42
- show_new_help()
43
- return
44
- elif has_help:
45
- # General help request
46
- show_main_help()
47
- return
48
-
49
- # Create argument parser without built-in help
50
- parser = argparse.ArgumentParser(
51
- description="MDL (Minecraft Datapack Language) CLI",
52
- add_help=False
53
- )
54
-
55
- # Add subcommands
56
- subparsers = parser.add_subparsers(dest='command', help='Available commands')
57
-
58
- # Build command
59
- build_parser = subparsers.add_parser('build', add_help=False)
60
- build_parser.add_argument('--mdl', '-m', required=True, help='Input MDL file or directory')
61
- build_parser.add_argument('-o', '--output', required=True, help='Output directory')
62
- build_parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output')
63
- build_parser.add_argument('--pack-format', type=int, help='Pack format override')
64
- build_parser.add_argument('--wrapper', help='Create zip file with specified name')
65
- build_parser.add_argument('--ignore-warnings', action='store_true', help='Suppress warning messages during build')
66
-
67
- # Check command
68
- check_parser = subparsers.add_parser('check', add_help=False)
69
- check_parser.add_argument('input', help='Input MDL file or directory')
70
- check_parser.add_argument('--verbose', '-v', action='store_true', help='Verbose output')
71
- check_parser.add_argument('--ignore-warnings', action='store_true', help='Suppress warning messages during check')
72
-
73
- # New command
74
- new_parser = subparsers.add_parser('new', add_help=False)
75
- new_parser.add_argument('project_name', help='Name for the new project')
76
- new_parser.add_argument('--pack-name', help='Custom pack name')
77
- new_parser.add_argument('--pack-format', type=int, default=82, help='Pack format number')
78
-
56
+ if args.command == 'build':
57
+ return build_command(args)
58
+ elif args.command == 'check':
59
+ return check_command(args)
60
+ elif args.command == 'new':
61
+ return new_command(args)
62
+ else:
63
+ print(f"Unknown command: {args.command}")
64
+ return 1
65
+ except Exception as e:
66
+ print(f"Error: {e}")
67
+ if args.verbose:
68
+ import traceback
69
+ traceback.print_exc()
70
+ return 1
71
+
72
+
73
+ def build_command(args):
74
+ """Build MDL files into a datapack."""
75
+ mdl_path = Path(args.mdl)
76
+ output_dir = Path(args.output)
77
+
78
+ if not mdl_path.exists():
79
+ print(f"Error: MDL path '{mdl_path}' does not exist")
80
+ return 1
81
+
82
+ # Determine what to build
83
+ if mdl_path.is_file():
84
+ mdl_files = [mdl_path]
85
+ elif mdl_path.is_dir():
86
+ mdl_files = list(mdl_path.glob("**/*.mdl"))
87
+ if not mdl_files:
88
+ print(f"Error: No .mdl files found in directory '{mdl_path}'")
89
+ return 1
90
+ else:
91
+ print(f"Error: Invalid MDL path '{mdl_path}'")
92
+ return 1
93
+
94
+ if args.verbose:
95
+ print(f"Building {len(mdl_files)} MDL file(s)...")
96
+ for f in mdl_files:
97
+ print(f" {f}")
98
+
99
+ # Parse and compile each file
100
+ all_asts = []
101
+ for mdl_file in mdl_files:
79
102
  try:
80
- args = parser.parse_args()
81
- except SystemExit:
82
- # Invalid arguments - show help
83
- show_main_help()
84
- return
85
-
86
- # Process commands
87
- if args.command == "build":
88
- try:
89
- build_mdl(
90
- args.mdl,
91
- args.output,
92
- verbose=args.verbose,
93
- pack_format_override=args.pack_format,
94
- wrapper=args.wrapper,
95
- ignore_warnings=args.ignore_warnings
96
- )
97
- except Exception as e:
98
- error_collector.add_error(create_error(
99
- MDLConfigurationError,
100
- f"Build command failed: {str(e)}",
101
- suggestion="Check your arguments and try again."
102
- ))
103
+ with open(mdl_file, 'r', encoding='utf-8') as f:
104
+ source = f.read()
105
+
106
+ if args.verbose:
107
+ print(f"Parsing {mdl_file}...")
108
+
109
+ parser = MDLParser(str(mdl_file))
110
+ ast = parser.parse(source)
111
+ all_asts.append(ast)
112
+
113
+ except (MDLLexerError, MDLParserError) as e:
114
+ print(f"Error in {mdl_file}: {e}")
115
+ return 1
116
+
117
+ # Merge all ASTs if multiple files
118
+ if len(all_asts) == 1:
119
+ final_ast = all_asts[0]
120
+ else:
121
+ # Merge multiple ASTs
122
+ final_ast = all_asts[0]
123
+ for ast in all_asts[1:]:
124
+ final_ast.variables.extend(ast.variables)
125
+ final_ast.functions.extend(ast.functions)
126
+ final_ast.tags.extend(ast.tags)
127
+ final_ast.hooks.extend(ast.hooks)
128
+ final_ast.statements.extend(ast.statements)
129
+
130
+ # Compile
131
+ try:
132
+ if args.verbose:
133
+ print(f"Compiling to {output_dir}...")
103
134
 
104
- elif args.command == "check":
105
- try:
106
- input_path = Path(args.input)
107
- if input_path.is_file():
108
- lint_mdl_file_wrapper(args.input, args.verbose, args.ignore_warnings)
109
- else:
110
- lint_mdl_directory_wrapper(args.input, args.verbose, args.ignore_warnings)
111
- except Exception as e:
112
- error_collector.add_error(create_error(
113
- MDLConfigurationError,
114
- f"Check command failed: {str(e)}",
115
- suggestion="Check your arguments and try again."
116
- ))
135
+ compiler = MDLCompiler()
136
+ output_path = compiler.compile(final_ast, str(output_dir))
117
137
 
118
- elif args.command == "new":
119
- try:
120
- create_new_project(
121
- args.project_name,
122
- pack_name=args.pack_name,
123
- pack_format=args.pack_format
124
- )
125
- except Exception as e:
126
- error_collector.add_error(create_error(
127
- MDLConfigurationError,
128
- f"New command failed: {str(e)}",
129
- suggestion="Check your arguments and try again."
130
- ))
138
+ print(f"Successfully built datapack: {output_path}")
139
+ return 0
131
140
 
132
- elif args.command is None:
133
- # No command specified - show help
134
- show_main_help()
135
- return
136
- else:
137
- # Unknown command
138
- error_collector.add_error(create_error(
139
- MDLConfigurationError,
140
- f"Unknown command: {args.command}",
141
- suggestion="Use 'mdl --help' to see available commands."
142
- ))
141
+ except MDLCompilerError as e:
142
+ print(f"Compilation error: {e}")
143
+ return 1
144
+
145
+
146
+ def check_command(args):
147
+ """Check MDL files for syntax errors."""
148
+ all_errors = []
149
+
150
+ for file_path in args.files:
151
+ file_path = Path(file_path)
152
+ if not file_path.exists():
153
+ print(f"Error: File '{file_path}' does not exist")
154
+ continue
143
155
 
144
- # Print any errors and exit
145
- error_collector.print_errors(verbose=True, ignore_warnings=False)
146
- error_collector.raise_if_errors()
156
+ try:
157
+ with open(file_path, 'r', encoding='utf-8') as f:
158
+ source = f.read()
159
+
160
+ if args.verbose:
161
+ print(f"Checking {file_path}...")
162
+
163
+ # Lex and parse to check for errors
164
+ lexer = MDLLexer(str(file_path))
165
+ tokens = list(lexer.lex(source))
166
+
167
+ parser = MDLParser(str(file_path))
168
+ ast = parser.parse(source)
169
+
170
+ if args.verbose:
171
+ print(f" ✓ {file_path} - {len(ast.functions)} functions, {len(ast.variables)} variables")
172
+
173
+ except MDLLexerError as e:
174
+ print(f"Lexer error in {file_path}: {e}")
175
+ all_errors.append(e)
176
+ except MDLParserError as e:
177
+ print(f"Parser error in {file_path}: {e}")
178
+ all_errors.append(e)
179
+ except Exception as e:
180
+ print(f"Unexpected error in {file_path}: {e}")
181
+ all_errors.append(e)
147
182
 
148
- except Exception as e:
149
- error_collector.add_error(create_error(
150
- MDLConfigurationError,
151
- f"Unexpected error: {str(e)}",
152
- suggestion="If this error persists, please report it as a bug."
153
- ))
154
- error_collector.print_errors(verbose=True, ignore_warnings=False)
155
- error_collector.raise_if_errors()
183
+ if all_errors:
184
+ print(f"\nFound {len(all_errors)} error(s)")
185
+ return 1
186
+ else:
187
+ print("All files passed syntax check!")
188
+ return 0
189
+
190
+
191
+ def new_command(args):
192
+ """Create a new MDL project."""
193
+ project_name = args.project_name
194
+ pack_name = args.pack_name or project_name
195
+ pack_format = args.pack_format
196
+
197
+ # Create project directory
198
+ project_dir = Path(project_name)
199
+ if project_dir.exists():
200
+ print(f"Error: Project directory '{project_name}' already exists")
201
+ return 1
202
+
203
+ project_dir.mkdir(parents=True)
204
+
205
+ # Create main MDL file
206
+ mdl_file = project_dir / f"{project_name}.mdl"
207
+
208
+ template_content = f'''pack "{pack_name}" "Generated by MDL CLI" {pack_format};
209
+ namespace "{project_name}";
210
+
211
+ // Variables
212
+ var num counter<@s> = 0;
213
+ var num global_timer<@a> = 0;
214
+
215
+ // Main function
216
+ function {project_name}:main<@s> {{
217
+ say "Hello from {project_name}!";
218
+
219
+ // Variable example
220
+ counter<@s> = 10;
221
+ say "Counter: $counter<@s>$";
222
+
223
+ // Conditional example
224
+ if $counter<@s>$ > 5 {{
225
+ say "High counter!";
226
+ }} else {{
227
+ say "Try again!";
228
+ }}
229
+ }}
230
+
231
+ // Load function
232
+ function {project_name}:load<@s> {{
233
+ say "Datapack loaded successfully!";
234
+ }}
235
+
236
+ // Hook to run on load
237
+ on_load {project_name}:load<@s>;
238
+ '''
239
+
240
+ with open(mdl_file, 'w', encoding='utf-8') as f:
241
+ f.write(template_content)
242
+
243
+ # Create README
244
+ readme_file = project_dir / "README.md"
245
+ readme_content = f'''# {project_name}
246
+
247
+ A Minecraft datapack created with MDL (Minecraft Datapack Language).
248
+
249
+ ## Getting Started
250
+
251
+ 1. **Build the datapack:**
252
+ ```bash
253
+ mdl build --mdl {project_name}.mdl -o dist
254
+ ```
255
+
256
+ 2. **Install in Minecraft:**
257
+ - Copy `dist/{project_name}/` to your world's `datapacks/` folder
258
+ - Run `/reload` in-game
259
+
260
+ 3. **Run the main function:**
261
+ ```bash
262
+ /function {project_name}:main
263
+ ```
264
+
265
+ ## Features
266
+
267
+ - **Variables**: Player-scoped counter and global timer
268
+ - **Control Flow**: If/else statements
269
+ - **Functions**: Main function and load hook
270
+ - **Automatic Execution**: Runs on datapack load
271
+
272
+ ## Development
273
+
274
+ - Edit `{project_name}.mdl` to modify the datapack
275
+ - Use `mdl check {project_name}.mdl` to validate syntax
276
+ - Use `mdl build --mdl {project_name}.mdl -o dist` to rebuild
277
+
278
+ For more information, visit: https://www.mcmdl.com
279
+ '''
280
+
281
+ with open(readme_file, 'w', encoding='utf-8') as f:
282
+ f.write(readme_content)
283
+
284
+ print(f"Created new MDL project: {project_name}/")
285
+ print(f" - {mdl_file}")
286
+ print(f" - {readme_file}")
287
+ print(f"\nNext steps:")
288
+ print(f" 1. cd {project_name}")
289
+ print(f" 2. mdl build --mdl {project_name}.mdl -o dist")
290
+ print(f" 3. Copy dist/{project_name}/ to your Minecraft world's datapacks folder")
291
+
292
+ return 0
156
293
 
157
294
 
158
- if __name__ == "__main__":
159
- main()
295
+ if __name__ == '__main__':
296
+ sys.exit(main())