archml 0.1.0__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.
archml/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ # Copyright 2026 ArchML Contributors
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """ArchML — a text-based DSL for defining software architecture alongside code."""
5
+
6
+ __version__ = "0.1.0"
archml/cli/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ # Copyright 2026 ArchML Contributors
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Command-line interface for ArchML."""
archml/cli/main.py ADDED
@@ -0,0 +1,614 @@
1
+ # Copyright 2026 ArchML Contributors
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Entry point for the ArchML command-line interface."""
5
+
6
+ import argparse
7
+ import re
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ from archml.compiler.build import CompilerError, SourceImportKey, compile_files
12
+ from archml.validation.checks import validate
13
+ from archml.workspace.config import WorkspaceConfigError, load_workspace_config
14
+
15
+ # ###############
16
+ # Public Interface
17
+ # ###############
18
+
19
+
20
+ def main() -> None:
21
+ """Run the ArchML CLI."""
22
+ parser = argparse.ArgumentParser(
23
+ prog="archml",
24
+ description="ArchML — architecture modeling tool",
25
+ )
26
+ subparsers = parser.add_subparsers(dest="command", metavar="COMMAND")
27
+
28
+ # Shared parent parser for the workspace directory option, used by all
29
+ # subcommands that operate on an existing workspace.
30
+ _workspace_parent = argparse.ArgumentParser(add_help=False)
31
+ _workspace_parent.add_argument(
32
+ "--workspace",
33
+ "-C",
34
+ default=".",
35
+ metavar="DIR",
36
+ help="Directory containing the ArchML workspace (default: current directory)",
37
+ )
38
+
39
+ # init subcommand
40
+ init_parser = subparsers.add_parser(
41
+ "init",
42
+ help="Initialize a new ArchML workspace",
43
+ description="Create a new ArchML workspace in a repository.",
44
+ )
45
+ init_parser.add_argument(
46
+ "name",
47
+ help="Mnemonic name for the workspace source import",
48
+ )
49
+ init_parser.add_argument(
50
+ "workspace_dir",
51
+ help="Directory to initialize the workspace in",
52
+ )
53
+
54
+ # check subcommand
55
+ subparsers.add_parser(
56
+ "check",
57
+ parents=[_workspace_parent],
58
+ help="Check the consistency of the architecture",
59
+ description="Validate architecture files for consistency errors.",
60
+ )
61
+
62
+ # visualize subcommand
63
+ visualize_parser = subparsers.add_parser(
64
+ "visualize",
65
+ parents=[_workspace_parent],
66
+ help="Generate a diagram for a system or component",
67
+ description="Render a box diagram for the specified architecture entity.",
68
+ )
69
+ visualize_parser.add_argument(
70
+ "entity",
71
+ help="Entity path (e.g. 'SystemA' or 'SystemA::ComponentB'), or 'all' to visualize every top-level entity",
72
+ )
73
+ visualize_parser.add_argument(
74
+ "output",
75
+ help="Output file path for the rendered diagram (e.g. 'diagram.png')",
76
+ )
77
+ visualize_parser.add_argument(
78
+ "--depth",
79
+ type=int,
80
+ default=None,
81
+ metavar="N",
82
+ help=(
83
+ "Maximum nesting depth to expand. "
84
+ "For a single entity: 0 = root only, 1 = direct children, etc. "
85
+ "For 'all': 0 = top-level entities as opaque boxes, 1 = expanded one level, etc. "
86
+ "Omit for full depth (default)."
87
+ ),
88
+ )
89
+
90
+ # export subcommand
91
+ export_parser = subparsers.add_parser(
92
+ "export",
93
+ parents=[_workspace_parent],
94
+ help="Export the architecture as a standalone HTML viewer",
95
+ description="Generate a self-contained HTML file with the interactive architecture viewer.",
96
+ )
97
+ export_parser.add_argument(
98
+ "--output",
99
+ "-o",
100
+ default="architecture.html",
101
+ metavar="FILE",
102
+ help="Output file path (default: architecture.html)",
103
+ )
104
+ export_parser.add_argument(
105
+ "--width-optimized",
106
+ action="store_true",
107
+ default=False,
108
+ help=(
109
+ "Combine left and right sidebars into a single left sidebar and add a top bar "
110
+ "with a hamburger toggle. Saves horizontal space for narrow viewports."
111
+ ),
112
+ )
113
+
114
+ # sync-remote subcommand
115
+ subparsers.add_parser(
116
+ "sync-remote",
117
+ parents=[_workspace_parent],
118
+ help="Download configured remote git repositories",
119
+ description=(
120
+ "Download remote git repositories listed in the workspace configuration "
121
+ "to the configured sync directory at the commits pinned in the lockfile."
122
+ ),
123
+ )
124
+
125
+ # update-remote subcommand
126
+ subparsers.add_parser(
127
+ "update-remote",
128
+ parents=[_workspace_parent],
129
+ help="Update remote git repository commits in the lockfile",
130
+ description=(
131
+ "Resolve branch or tag references to their latest commit SHAs and "
132
+ "write the results to the lockfile. Commit-hash revisions are pinned as-is."
133
+ ),
134
+ )
135
+
136
+ args = parser.parse_args()
137
+ if args.command is None:
138
+ parser.print_help()
139
+ sys.exit(0)
140
+
141
+ sys.exit(_dispatch(args))
142
+
143
+
144
+ # ################
145
+ # Implementation
146
+ # ################
147
+
148
+ _DEFAULT_BUILD_DIR = ".archml-build"
149
+
150
+
151
+ def _template_path() -> Path:
152
+ """Return the path to the bundled viewer HTML template."""
153
+ return Path(__file__).parent.parent / "static" / "archml-viewer-template.html"
154
+
155
+
156
+ def _dispatch(args: argparse.Namespace) -> int:
157
+ """Dispatch to the appropriate subcommand handler."""
158
+ if args.command == "init":
159
+ return _cmd_init(args)
160
+ if args.command == "check":
161
+ return _cmd_check(args)
162
+ if args.command == "visualize":
163
+ return _cmd_visualize(args)
164
+ if args.command == "export":
165
+ return _cmd_export(args)
166
+ if args.command == "sync-remote":
167
+ return _cmd_sync_remote(args)
168
+ if args.command == "update-remote":
169
+ return _cmd_update_remote(args)
170
+ return 0
171
+
172
+
173
+ def _cmd_init(args: argparse.Namespace) -> int:
174
+ """Handle the init subcommand."""
175
+ name = args.name
176
+ if not name:
177
+ print("Error: mnemonic name cannot be empty.", file=sys.stderr)
178
+ return 1
179
+ if not re.match(r"^[a-z][a-z0-9_-]*$", name):
180
+ print(
181
+ f"Error: invalid mnemonic name '{name}': must start with a lowercase letter "
182
+ "followed by lowercase letters, digits, hyphens, or underscores.",
183
+ file=sys.stderr,
184
+ )
185
+ return 1
186
+
187
+ workspace_dir = Path(args.workspace_dir).resolve()
188
+ workspace_dir.mkdir(parents=True, exist_ok=True)
189
+
190
+ workspace_yaml = workspace_dir / ".archml-workspace.yaml"
191
+ if workspace_yaml.exists():
192
+ print(
193
+ f"Error: workspace already exists at '{workspace_yaml}'.",
194
+ file=sys.stderr,
195
+ )
196
+ return 1
197
+
198
+ workspace_yaml.write_text(
199
+ f"name: {name}\nbuild-directory: {_DEFAULT_BUILD_DIR}\nsource-imports:\n - name: {name}\n local-path: .\n",
200
+ encoding="utf-8",
201
+ )
202
+
203
+ print(f"Initialized ArchML workspace '{name}' at '{workspace_dir}'.")
204
+ return 0
205
+
206
+
207
+ def _cmd_check(args: argparse.Namespace) -> int:
208
+ """Handle the check subcommand."""
209
+ from archml.workspace.config import GitPathImport, LocalPathImport, find_workspace_root
210
+
211
+ directory = Path(args.workspace).resolve()
212
+
213
+ if not directory.exists():
214
+ print(f"Error: directory '{directory}' does not exist.", file=sys.stderr)
215
+ return 1
216
+
217
+ workspace_yaml = directory / ".archml-workspace.yaml"
218
+
219
+ if not workspace_yaml.exists():
220
+ root = find_workspace_root(directory)
221
+ if root is None:
222
+ print(
223
+ f"Error: no ArchML workspace found at '{directory}' or any parent directory."
224
+ "Run 'archml init' to initialize a workspace.",
225
+ file=sys.stderr,
226
+ )
227
+ return 1
228
+ directory = root
229
+ workspace_yaml = directory / ".archml-workspace.yaml"
230
+
231
+ try:
232
+ config = load_workspace_config(workspace_yaml)
233
+ except WorkspaceConfigError as exc:
234
+ print(f"Error: {exc}", file=sys.stderr)
235
+ return 1
236
+
237
+ build_dir = directory / config.build_directory
238
+ sync_dir = directory / config.remote_sync_directory
239
+
240
+ # Build the source import map: SourceImportKey(repo, mnemonic) -> absolute base path.
241
+ # Local mnemonics use config.name as repo; remote repos use "@name".
242
+ source_import_map: dict[SourceImportKey, Path] = {}
243
+
244
+ for imp in config.source_imports:
245
+ if isinstance(imp, LocalPathImport):
246
+ source_import_map[SourceImportKey(config.name, imp.name)] = (directory / imp.local_path).resolve()
247
+ elif isinstance(imp, GitPathImport):
248
+ repo_dir = (sync_dir / imp.name).resolve()
249
+ if repo_dir.exists():
250
+ remote_workspace_yaml = repo_dir / ".archml-workspace.yaml"
251
+ if remote_workspace_yaml.exists():
252
+ try:
253
+ remote_config = load_workspace_config(remote_workspace_yaml)
254
+ for remote_imp in remote_config.source_imports:
255
+ if isinstance(remote_imp, LocalPathImport):
256
+ mnemonic_path = (repo_dir / remote_imp.local_path).resolve()
257
+ source_import_map[SourceImportKey(f"@{imp.name}", remote_imp.name)] = mnemonic_path
258
+ except WorkspaceConfigError as exc:
259
+ print(f"Warning: could not load workspace config from remote '{imp.name}': {exc}")
260
+
261
+ # Scan only files under local mnemonic paths (repo == config.name, i.e. not remote).
262
+ local_mnemonic_paths = {base_path for key, base_path in source_import_map.items() if key.repo == config.name}
263
+ seen_files: set[Path] = set()
264
+ archml_files: list[Path] = []
265
+ for base_path in sorted(local_mnemonic_paths):
266
+ for f in base_path.rglob("*.archml"):
267
+ if f not in seen_files and build_dir not in f.parents and sync_dir not in f.parents:
268
+ seen_files.add(f)
269
+ archml_files.append(f)
270
+
271
+ if not archml_files:
272
+ print("No .archml files found in the workspace.")
273
+ return 0
274
+
275
+ print(f"Checking {len(archml_files)} architecture file(s)...")
276
+ try:
277
+ compiled = compile_files(archml_files, build_dir, source_import_map)
278
+ except CompilerError as exc:
279
+ print(f"Error: {exc}", file=sys.stderr)
280
+ return 1
281
+
282
+ has_errors = False
283
+ for arch_file in compiled.values():
284
+ result = validate(arch_file)
285
+ for warning in result.warnings:
286
+ print(f"Warning: {warning.message}")
287
+ for error in result.errors:
288
+ print(f"Error: {error.message}", file=sys.stderr)
289
+ has_errors = True
290
+
291
+ if has_errors:
292
+ return 1
293
+
294
+ print("No issues found.")
295
+ return 0
296
+
297
+
298
+ def _cmd_visualize(args: argparse.Namespace) -> int:
299
+ """Handle the visualize subcommand."""
300
+ from archml.views.layout import compute_layout
301
+ from archml.views.resolver import EntityNotFoundError, resolve_entity
302
+ from archml.views.topology import build_viz_diagram, build_viz_diagram_all
303
+ from archml.workspace.config import LocalPathImport
304
+
305
+ directory = Path(args.workspace).resolve()
306
+
307
+ if not directory.exists():
308
+ print(f"Error: directory '{directory}' does not exist.", file=sys.stderr)
309
+ return 1
310
+
311
+ workspace_yaml = directory / ".archml-workspace.yaml"
312
+
313
+ if not workspace_yaml.exists():
314
+ print(
315
+ f"Error: no ArchML workspace found at '{directory}'. Run 'archml init' to initialize a workspace.",
316
+ file=sys.stderr,
317
+ )
318
+ return 1
319
+
320
+ try:
321
+ config = load_workspace_config(workspace_yaml)
322
+ except WorkspaceConfigError as exc:
323
+ print(f"Error: {exc}", file=sys.stderr)
324
+ return 1
325
+
326
+ build_dir = directory / config.build_directory
327
+
328
+ source_import_map: dict[SourceImportKey, Path] = {}
329
+ for imp in config.source_imports:
330
+ if isinstance(imp, LocalPathImport):
331
+ source_import_map[SourceImportKey(config.name, imp.name)] = (directory / imp.local_path).resolve()
332
+
333
+ archml_files = [f for f in directory.rglob("*.archml") if build_dir not in f.parents]
334
+ if not archml_files:
335
+ print("No .archml files found in the workspace.", file=sys.stderr)
336
+ return 1
337
+
338
+ try:
339
+ compiled = compile_files(archml_files, build_dir, source_import_map)
340
+ except CompilerError as exc:
341
+ print(f"Error: {exc}", file=sys.stderr)
342
+ return 1
343
+
344
+ output_path = Path(args.output)
345
+
346
+ depth: int | None = args.depth
347
+
348
+ if args.entity == "all":
349
+ viz_diagram = build_viz_diagram_all(compiled, depth=depth)
350
+ else:
351
+ try:
352
+ entity = resolve_entity(compiled, args.entity)
353
+ except EntityNotFoundError as exc:
354
+ print(f"Error: {exc}", file=sys.stderr)
355
+ return 1
356
+ global_connects = [c for af in compiled.values() for c in af.connects]
357
+ viz_diagram = build_viz_diagram(entity, depth=depth, global_connects=global_connects)
358
+ try:
359
+ layout_plan = compute_layout(viz_diagram)
360
+ except RuntimeError as exc:
361
+ print(f"Error: {exc}", file=sys.stderr)
362
+ return 1
363
+
364
+ from archml.views.diagram import render_diagram
365
+
366
+ render_diagram(viz_diagram, layout_plan, output_path)
367
+
368
+ print(f"Diagram written to '{output_path}'.")
369
+ return 0
370
+
371
+
372
+ def _cmd_export(args: argparse.Namespace) -> int:
373
+ """Handle the export subcommand."""
374
+ from archml.export import build_viewer_payload
375
+ from archml.workspace.config import LocalPathImport
376
+
377
+ template_path = _template_path()
378
+ if not template_path.exists():
379
+ print(
380
+ "Warning: JS viewer not built. Run 'python tools/build_js.py' first.",
381
+ file=sys.stderr,
382
+ )
383
+ return 1
384
+
385
+ directory = Path(args.workspace).resolve()
386
+
387
+ if not directory.exists():
388
+ print(f"Error: directory '{directory}' does not exist.", file=sys.stderr)
389
+ return 1
390
+
391
+ workspace_yaml = directory / ".archml-workspace.yaml"
392
+
393
+ if not workspace_yaml.exists():
394
+ print(
395
+ f"Error: no ArchML workspace found at '{directory}'. Run 'archml init' to initialize a workspace.",
396
+ file=sys.stderr,
397
+ )
398
+ return 1
399
+
400
+ try:
401
+ config = load_workspace_config(workspace_yaml)
402
+ except WorkspaceConfigError as exc:
403
+ print(f"Error: {exc}", file=sys.stderr)
404
+ return 1
405
+
406
+ build_dir = directory / config.build_directory
407
+
408
+ source_import_map: dict[SourceImportKey, Path] = {}
409
+ for imp in config.source_imports:
410
+ if isinstance(imp, LocalPathImport):
411
+ source_import_map[SourceImportKey(config.name, imp.name)] = (directory / imp.local_path).resolve()
412
+
413
+ archml_files = [f for f in directory.rglob("*.archml") if build_dir not in f.parents]
414
+ if not archml_files:
415
+ print("No .archml files found in the workspace.", file=sys.stderr)
416
+ return 1
417
+
418
+ try:
419
+ compiled = compile_files(archml_files, build_dir, source_import_map)
420
+ except CompilerError as exc:
421
+ print(f"Error: {exc}", file=sys.stderr)
422
+ return 1
423
+
424
+ payload_json = build_viewer_payload(compiled, width_optimized=args.width_optimized)
425
+
426
+ template = template_path.read_text(encoding="utf-8")
427
+ data_tag = f'<script id="archml-data" type="application/json">{payload_json}</script>'
428
+ html = template.replace("<!-- ARCHML_DATA_PLACEHOLDER -->", data_tag)
429
+
430
+ output_path = Path(args.output)
431
+ output_path.write_text(html, encoding="utf-8")
432
+ print(f"Architecture viewer written to '{output_path}'.")
433
+ return 0
434
+
435
+
436
+ def _cmd_sync_remote(args: argparse.Namespace) -> int:
437
+ """Handle the sync-remote subcommand."""
438
+ from archml.workspace.config import GitPathImport, find_workspace_root
439
+ from archml.workspace.git_ops import GitError, clone_at_commit, get_current_commit
440
+ from archml.workspace.lockfile import LOCKFILE_NAME, LockfileError, load_lockfile
441
+
442
+ directory = Path(args.workspace).resolve()
443
+
444
+ if not directory.exists():
445
+ print(f"Error: directory '{directory}' does not exist.", file=sys.stderr)
446
+ return 1
447
+
448
+ workspace_yaml = directory / ".archml-workspace.yaml"
449
+ if not workspace_yaml.exists():
450
+ root = find_workspace_root(directory)
451
+ if root is None:
452
+ print(
453
+ f"Error: no ArchML workspace found at '{directory}' or any parent directory."
454
+ "Run 'archml init' to initialize a workspace.",
455
+ file=sys.stderr,
456
+ )
457
+ return 1
458
+ directory = root
459
+ workspace_yaml = directory / ".archml-workspace.yaml"
460
+
461
+ try:
462
+ config = load_workspace_config(workspace_yaml)
463
+ except WorkspaceConfigError as exc:
464
+ print(f"Error: {exc}", file=sys.stderr)
465
+ return 1
466
+
467
+ git_imports = [imp for imp in config.source_imports if isinstance(imp, GitPathImport)]
468
+ if not git_imports:
469
+ print("No remote git repositories configured. Nothing to sync.")
470
+ return 0
471
+
472
+ lockfile_path = directory / LOCKFILE_NAME
473
+ if not lockfile_path.exists():
474
+ print(
475
+ "Error: lockfile not found. Run 'archml update-remote' to create the lockfile.",
476
+ file=sys.stderr,
477
+ )
478
+ return 1
479
+
480
+ try:
481
+ lockfile = load_lockfile(lockfile_path)
482
+ except LockfileError as exc:
483
+ print(f"Error: {exc}", file=sys.stderr)
484
+ return 1
485
+
486
+ locked_by_name = {entry.name: entry for entry in lockfile.locked_revisions}
487
+ sync_dir = directory / config.remote_sync_directory
488
+
489
+ has_errors = False
490
+ for imp in git_imports:
491
+ if imp.name not in locked_by_name:
492
+ print(
493
+ f"Error: '{imp.name}' is not in the lockfile. Run 'archml update-remote' first.",
494
+ file=sys.stderr,
495
+ )
496
+ has_errors = True
497
+ continue
498
+
499
+ pinned_commit = locked_by_name[imp.name].commit
500
+ target_dir = sync_dir / imp.name
501
+
502
+ try:
503
+ current = get_current_commit(target_dir)
504
+ except GitError as exc:
505
+ print(f"Error: cannot check current state of '{imp.name}': {exc}", file=sys.stderr)
506
+ has_errors = True
507
+ continue
508
+
509
+ if current == pinned_commit:
510
+ print(f" {imp.name}: already at {pinned_commit[:8]}")
511
+ continue
512
+
513
+ print(f" {imp.name}: syncing to {pinned_commit[:8]}...")
514
+ try:
515
+ clone_at_commit(imp.git_repository, pinned_commit, target_dir)
516
+ print(f" {imp.name}: done.")
517
+ except GitError as exc:
518
+ print(f"Error: failed to sync '{imp.name}': {exc}", file=sys.stderr)
519
+ has_errors = True
520
+
521
+ return 1 if has_errors else 0
522
+
523
+
524
+ def _cmd_update_remote(args: argparse.Namespace) -> int:
525
+ """Handle the update-remote subcommand."""
526
+ from archml.workspace.config import GitPathImport, find_workspace_root
527
+ from archml.workspace.git_ops import GitError, is_commit_hash, resolve_commit
528
+ from archml.workspace.lockfile import (
529
+ LOCKFILE_NAME,
530
+ LockedRevision,
531
+ Lockfile,
532
+ LockfileError,
533
+ load_lockfile,
534
+ save_lockfile,
535
+ )
536
+
537
+ directory = Path(args.workspace).resolve()
538
+
539
+ if not directory.exists():
540
+ print(f"Error: directory '{directory}' does not exist.", file=sys.stderr)
541
+ return 1
542
+
543
+ workspace_yaml = directory / ".archml-workspace.yaml"
544
+ if not workspace_yaml.exists():
545
+ root = find_workspace_root(directory)
546
+ if root is None:
547
+ print(
548
+ f"Error: no ArchML workspace found at '{directory}' or any parent directory."
549
+ "Run 'archml init' to initialize a workspace.",
550
+ file=sys.stderr,
551
+ )
552
+ return 1
553
+ directory = root
554
+ workspace_yaml = directory / ".archml-workspace.yaml"
555
+
556
+ try:
557
+ config = load_workspace_config(workspace_yaml)
558
+ except WorkspaceConfigError as exc:
559
+ print(f"Error: {exc}", file=sys.stderr)
560
+ return 1
561
+
562
+ git_imports = [imp for imp in config.source_imports if isinstance(imp, GitPathImport)]
563
+ if not git_imports:
564
+ print("No remote git repositories configured. Nothing to update.")
565
+ return 0
566
+
567
+ lockfile_path = directory / LOCKFILE_NAME
568
+ if lockfile_path.exists():
569
+ try:
570
+ lockfile = load_lockfile(lockfile_path)
571
+ except LockfileError as exc:
572
+ print(f"Error: {exc}", file=sys.stderr)
573
+ return 1
574
+ else:
575
+ lockfile = Lockfile()
576
+
577
+ locked_by_name = {entry.name: entry for entry in lockfile.locked_revisions}
578
+
579
+ has_errors = False
580
+ for imp in git_imports:
581
+ if is_commit_hash(imp.revision):
582
+ commit = imp.revision
583
+ print(f" {imp.name}: pinned at {commit[:8]} (commit hash, no update needed)")
584
+ else:
585
+ print(f" {imp.name}: resolving '{imp.revision}'...")
586
+ try:
587
+ commit = resolve_commit(imp.git_repository, imp.revision)
588
+ print(f" {imp.name}: resolved to {commit[:8]}")
589
+ except GitError as exc:
590
+ print(f"Error: failed to resolve '{imp.name}': {exc}", file=sys.stderr)
591
+ has_errors = True
592
+ continue
593
+
594
+ locked_by_name[imp.name] = LockedRevision.model_validate(
595
+ {
596
+ "name": imp.name,
597
+ "git-repository": imp.git_repository,
598
+ "revision": imp.revision,
599
+ "commit": commit,
600
+ }
601
+ )
602
+
603
+ if has_errors:
604
+ return 1
605
+
606
+ lockfile.locked_revisions = list(locked_by_name.values())
607
+ try:
608
+ save_lockfile(lockfile, lockfile_path)
609
+ except LockfileError as exc:
610
+ print(f"Error: {exc}", file=sys.stderr)
611
+ return 1
612
+
613
+ print(f"Lockfile updated: {lockfile_path}")
614
+ return 0
@@ -0,0 +1,23 @@
1
+ # Copyright 2026 ArchML Contributors
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Compiler pipeline for .archml files: scanning, parsing, and semantic analysis."""
5
+
6
+ from archml.compiler.artifact import ARTIFACT_SUFFIX, deserialize, read_artifact, serialize, write_artifact
7
+ from archml.compiler.build import CompilerError, compile_files
8
+ from archml.compiler.parser import ParseError, parse
9
+ from archml.compiler.semantic_analysis import SemanticError, analyze
10
+
11
+ __all__ = [
12
+ "parse",
13
+ "ParseError",
14
+ "analyze",
15
+ "SemanticError",
16
+ "serialize",
17
+ "deserialize",
18
+ "write_artifact",
19
+ "read_artifact",
20
+ "ARTIFACT_SUFFIX",
21
+ "compile_files",
22
+ "CompilerError",
23
+ ]