@jhlagado/azm 0.2.8 → 0.2.10

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 (34) hide show
  1. package/README.md +75 -15
  2. package/dist/src/api-compile.js +27 -0
  3. package/dist/src/assembly/assemble-program.js +5 -0
  4. package/dist/src/assembly/import-visibility.d.ts +3 -0
  5. package/dist/src/assembly/import-visibility.js +204 -0
  6. package/dist/src/node/source-host.js +40 -13
  7. package/dist/src/outputs/write-asm80.js +4 -0
  8. package/dist/src/register-contracts/programModel-routines.js +33 -17
  9. package/dist/src/register-contracts/report.js +15 -3
  10. package/dist/src/register-contracts/smartCommentParsing.d.ts +1 -0
  11. package/dist/src/register-contracts/smartCommentParsing.js +42 -7
  12. package/dist/src/register-contracts/smartComments.d.ts +2 -2
  13. package/dist/src/register-contracts/smartComments.js +3 -4
  14. package/dist/src/source/logical-lines.d.ts +3 -0
  15. package/dist/src/source/source-span.d.ts +2 -0
  16. package/dist/src/syntax/parse-directive-statement.d.ts +1 -6
  17. package/dist/src/syntax/parse-directive-statement.js +3 -1
  18. package/dist/src/syntax/parse-layout-declarations.js +11 -2
  19. package/dist/src/syntax/parse-line.js +18 -2
  20. package/dist/src/tooling/api.js +1 -1
  21. package/docs/codebase/01-orientation-and-repository-layout.md +192 -0
  22. package/docs/codebase/02-source-loading-and-parsing.md +263 -0
  23. package/docs/codebase/03-assembly-and-z80-emission.md +251 -0
  24. package/docs/codebase/04-ops-and-register-contracts.md +237 -0
  25. package/docs/codebase/05-interfaces-and-output-artifacts.md +253 -0
  26. package/docs/codebase/06-verification-and-maintenance.md +202 -0
  27. package/docs/codebase/appendices/a-directory-file-reference.md +253 -0
  28. package/docs/codebase/appendices/b-compile-flow-reference.md +103 -0
  29. package/docs/codebase/appendices/c-public-surface-reference.md +106 -0
  30. package/docs/codebase/appendices/index.md +16 -0
  31. package/docs/codebase/index.md +46 -0
  32. package/package.json +2 -3
  33. package/docs/reference/cli.md +0 -158
  34. package/docs/reference/tooling-api.md +0 -320
@@ -2,6 +2,7 @@ function list(units) {
2
2
  return units.length === 0 ? '-' : units.join(',');
3
3
  }
4
4
  const FLAG_UNITS = new Set(['carry', 'zero', 'sign', 'parity', 'halfCarry']);
5
+ const FLAG_UNIT_LIST = ['carry', 'zero', 'sign', 'parity', 'halfCarry'];
5
6
  const CONTRACT_CARRIER_PAIRS = [
6
7
  { label: 'BC', hi: 'B', lo: 'C' },
7
8
  { label: 'DE', hi: 'D', lo: 'E' },
@@ -32,6 +33,14 @@ export function contractCarrierList(units) {
32
33
  }
33
34
  return parts.length === 0 ? '-' : parts.join(',');
34
35
  }
36
+ function sourceContractCarrierList(units) {
37
+ const unique = [...new Set(units)];
38
+ const hasAllFlags = FLAG_UNIT_LIST.every((unit) => unique.includes(unit));
39
+ const compacted = hasAllFlags
40
+ ? unique.filter((unit) => !FLAG_UNITS.has(unit)).concat('F')
41
+ : unique;
42
+ return contractCarrierList(compacted);
43
+ }
35
44
  function relationOutputUnits(relations) {
36
45
  return relations.flatMap((rel) => rel.out);
37
46
  }
@@ -59,9 +68,9 @@ function sourceContractEntries(summary) {
59
68
  const outputUnits = relationOutputUnits(summary.valueRelations);
60
69
  if (outputUnits.length > 0)
61
70
  out.push({ keyword: 'out', carriers: contractCarrierList(outputUnits) });
62
- const clobbers = summary.mayWrite.filter((unit) => !relationOut.has(unit) && !FLAG_UNITS.has(unit));
71
+ const clobbers = summary.mayWrite.filter((unit) => !relationOut.has(unit));
63
72
  if (clobbers.length > 0)
64
- out.push({ keyword: 'clobbers', carriers: contractCarrierList(clobbers) });
73
+ out.push({ keyword: 'clobbers', carriers: sourceContractCarrierList(clobbers) });
65
74
  return out;
66
75
  }
67
76
  function stackStatus(summary) {
@@ -152,5 +161,8 @@ export function renderRegisterContractsInterface(summaries) {
152
161
  return `${lines.join('\n')}\n`;
153
162
  }
154
163
  export function renderRegisterContractsSourceBlock(summary) {
155
- return sourceContractEntries(summary).map((entry) => `;! ${entry.keyword.padEnd(10)}${entry.carriers}`);
164
+ const entries = sourceContractEntries(summary);
165
+ if (entries.length === 0)
166
+ return [];
167
+ return [`;! ${entries.map((entry) => `${entry.keyword} ${entry.carriers}`).join('; ')}`];
156
168
  }
@@ -1,3 +1,4 @@
1
1
  import type { SmartComment } from './types.js';
2
2
  export declare function parseSmartCommentLine(line: string): SmartComment | undefined;
3
+ export declare function parseSmartCommentLines(line: string): SmartComment[];
3
4
  export declare function isCompactSourceCommentLine(line: string): boolean;
@@ -1,5 +1,6 @@
1
1
  import { expandCarrierList } from './carriers.js';
2
2
  const COMPACT_SOURCE_TAG_RE = /^;?\s*!\s*(in|out|clobbers|preserves)(?:\s+(.+))?$/i;
3
+ const COMPACT_SOURCE_CLAUSE_RE = /^(in|out|clobbers|preserves)(?:\s+(.+))?$/i;
3
4
  const COMPACT_SOURCE_LINE_RE = /^\s*;\s*!\s*(?:in|out|maybe-out|clobbers|preserves)(?:\s|$)/i;
4
5
  const CARRIER_RE = /^\{([^}]+)\}(?:\s+(.+))?$/;
5
6
  const CONTRACT_COMMENT_KINDS = new Set(['in', 'out', 'clobbers', 'preserves']);
@@ -35,17 +36,51 @@ function parseCarrierPayload(rest) {
35
36
  return { carriers, ...(name ? { name } : {}) };
36
37
  }
37
38
  export function parseSmartCommentLine(line) {
39
+ return parseSmartCommentLines(line)[0];
40
+ }
41
+ export function parseSmartCommentLines(line) {
38
42
  const trimmed = line.trim();
39
43
  const expectOut = parseExpectOutComment(trimmed);
40
44
  if (expectOut !== undefined)
41
- return expectOut;
45
+ return [expectOut];
46
+ const semicolonSeparated = parseSemicolonSeparatedSourceComments(trimmed);
47
+ if (semicolonSeparated.length > 0)
48
+ return semicolonSeparated;
42
49
  const match = COMPACT_SOURCE_TAG_RE.exec(trimmed);
43
- if (!match)
44
- return undefined;
45
- const tag = match[1].toLowerCase();
46
- if (!CONTRACT_COMMENT_KINDS.has(tag))
47
- return undefined;
48
- return parseCarrierComment(tag, match[2]?.trim());
50
+ if (match) {
51
+ const tag = match[1].toLowerCase();
52
+ if (!CONTRACT_COMMENT_KINDS.has(tag))
53
+ return [];
54
+ const comment = parseCarrierComment(tag, match[2]?.trim());
55
+ return comment === undefined ? [] : [comment];
56
+ }
57
+ return [];
58
+ }
59
+ function parseSemicolonSeparatedSourceComments(trimmed) {
60
+ const sourcePrefix = /^;?\s*!\s*/.exec(trimmed);
61
+ if (sourcePrefix === null)
62
+ return [];
63
+ const content = trimmed.slice(sourcePrefix[0].length);
64
+ const parts = content
65
+ .split(';')
66
+ .map((part) => part.trim())
67
+ .filter(Boolean);
68
+ if (parts.length <= 1)
69
+ return [];
70
+ const comments = [];
71
+ for (const part of parts) {
72
+ const match = COMPACT_SOURCE_CLAUSE_RE.exec(part);
73
+ if (!match)
74
+ return [];
75
+ const tag = match[1].toLowerCase();
76
+ if (!CONTRACT_COMMENT_KINDS.has(tag))
77
+ return [];
78
+ const comment = parseCarrierComment(tag, match[2]?.trim());
79
+ if (comment === undefined)
80
+ return [];
81
+ comments.push(comment);
82
+ }
83
+ return comments;
49
84
  }
50
85
  function parseExpectOutComment(trimmed) {
51
86
  const expectOut = /^;?\s*expects\s+out\s+(.+)$/i.exec(trimmed);
@@ -1,5 +1,5 @@
1
1
  import type { LocatedSmartComment, RegisterContractsRoutine, RoutineContract } from './types.js';
2
- import { parseSmartCommentLine } from './smartCommentParsing.js';
3
- export { parseSmartCommentLine };
2
+ import { parseSmartCommentLine, parseSmartCommentLines } from './smartCommentParsing.js';
3
+ export { parseSmartCommentLine, parseSmartCommentLines };
4
4
  export declare function parseSmartComments(sourceLineComments: ReadonlyMap<string, ReadonlyMap<number, string>>): LocatedSmartComment[];
5
5
  export declare function buildRoutineContracts(comments: LocatedSmartComment[], routines?: RegisterContractsRoutine[], sourceTexts?: ReadonlyMap<string, string>): Map<string, RoutineContract>;
@@ -1,12 +1,11 @@
1
1
  import { collectPrecedingCommentBlock } from './smartCommentBlocks.js';
2
- import { parseSmartCommentLine } from './smartCommentParsing.js';
3
- export { parseSmartCommentLine };
2
+ import { parseSmartCommentLine, parseSmartCommentLines } from './smartCommentParsing.js';
3
+ export { parseSmartCommentLine, parseSmartCommentLines };
4
4
  export function parseSmartComments(sourceLineComments) {
5
5
  const out = [];
6
6
  for (const [file, comments] of sourceLineComments) {
7
7
  for (const [line, text] of comments) {
8
- const parsed = parseSmartCommentLine(`;${text}`);
9
- if (parsed) {
8
+ for (const parsed of parseSmartCommentLines(`;${text}`)) {
10
9
  out.push({ file, line, comment: parsed });
11
10
  }
12
11
  }
@@ -3,5 +3,8 @@ export interface LogicalLine {
3
3
  readonly sourceName: string;
4
4
  readonly line: number;
5
5
  readonly text: string;
6
+ readonly sourceUnit?: string;
7
+ readonly sourceRelation?: SourceRelation;
6
8
  }
9
+ export type SourceRelation = 'entry' | 'include' | 'import';
7
10
  export declare function scanLogicalLines(source: SourceFile): LogicalLine[];
@@ -2,4 +2,6 @@ export interface SourceSpan {
2
2
  readonly sourceName: string;
3
3
  readonly line: number;
4
4
  readonly column: number;
5
+ readonly sourceUnit?: string;
6
+ readonly sourceRelation?: 'entry' | 'include' | 'import';
5
7
  }
@@ -1,14 +1,9 @@
1
1
  import type { LogicalLine } from '../source/logical-lines.js';
2
+ import type { SourceSpan } from '../source/source-span.js';
2
3
  import type { ParseLineResult } from './parse-line.js';
3
- type SourceSpan = {
4
- readonly sourceName: string;
5
- readonly line: number;
6
- readonly column: number;
7
- };
8
4
  export declare function parseDirectiveStatement(line: LogicalLine, text: string, span: SourceSpan): ParseLineResult | undefined;
9
5
  export declare function parseColonDeclaration(line: LogicalLine, name: string, statementText: string, span: {
10
6
  readonly sourceName: string;
11
7
  readonly line: number;
12
8
  readonly column: number;
13
9
  }): ParseLineResult | undefined;
14
- export {};
@@ -144,7 +144,9 @@ function parseDsDirective(line, valueText, span) {
144
144
  };
145
145
  }
146
146
  function validateDsValueList(line, parts) {
147
- return parts.length < 1 || parts.length > 2 ? parseError(line, `invalid .ds value list`) : undefined;
147
+ return parts.length < 1 || parts.length > 2
148
+ ? parseError(line, `invalid .ds value list`)
149
+ : undefined;
148
150
  }
149
151
  function parseDsSize(line, sizeText) {
150
152
  const size = parseTypeSizeExpression(sizeText) ?? parseExpression(sizeText);
@@ -38,7 +38,7 @@ function parseTypeAlias(line, text) {
38
38
  kind: 'type-alias',
39
39
  name: nameLeftTypeAlias[1] ?? '',
40
40
  typeExpr,
41
- span: { sourceName: line.sourceName, line: line.line, column: firstColumn(line.text) },
41
+ span: spanForLine(line),
42
42
  },
43
43
  diagnostics: [],
44
44
  };
@@ -99,10 +99,19 @@ function parseLayoutBlock(lines, index, header) {
99
99
  name: header.name,
100
100
  layoutKind,
101
101
  fields,
102
- span: { sourceName: line.sourceName, line: line.line, column: firstColumn(line.text) },
102
+ span: spanForLine(line),
103
103
  },
104
104
  };
105
105
  }
106
+ function spanForLine(line) {
107
+ return {
108
+ sourceName: line.sourceName,
109
+ line: line.line,
110
+ column: firstColumn(line.text),
111
+ ...(line.sourceUnit !== undefined ? { sourceUnit: line.sourceUnit } : {}),
112
+ ...(line.sourceRelation !== undefined ? { sourceRelation: line.sourceRelation } : {}),
113
+ };
114
+ }
106
115
  function skipToLayoutEnd(lines, index, directive) {
107
116
  const endDirective = directive === 'union' ? '.endunion' : '.endtype';
108
117
  for (let next = index + 1; next < lines.length; next += 1) {
@@ -7,7 +7,7 @@ export function parseLogicalLine(line, options = {}) {
7
7
  if (text.length === 0) {
8
8
  return commentOnlyLine(line);
9
9
  }
10
- const span = { sourceName: line.sourceName, line: line.line, column: firstColumn(line.text) };
10
+ const span = spanForLine(line);
11
11
  const labelWithStatement = /^(@?[A-Za-z_.$?][A-Za-z0-9_.$?]*):\s*(.+)$/.exec(text);
12
12
  if (labelWithStatement) {
13
13
  const rawLabel = labelWithStatement[1] ?? '';
@@ -20,7 +20,10 @@ export function parseLogicalLine(line, options = {}) {
20
20
  }
21
21
  const parsedStatement = parseCanonicalStatement(line, statementText, span);
22
22
  return withLineComment(line, {
23
- items: [{ kind: 'label', name: labelName, ...(isEntry ? { isEntry: true } : {}), span }, ...parsedStatement.items],
23
+ items: [
24
+ { kind: 'label', name: labelName, ...(isEntry ? { isEntry: true } : {}), span },
25
+ ...parsedStatement.items,
26
+ ],
24
27
  diagnostics: parsedStatement.diagnostics,
25
28
  });
26
29
  }
@@ -56,6 +59,8 @@ function commentOnlyLine(line) {
56
59
  sourceName: line.sourceName,
57
60
  line: line.line,
58
61
  column: firstColumn(line.text),
62
+ ...(line.sourceUnit !== undefined ? { sourceUnit: line.sourceUnit } : {}),
63
+ ...(line.sourceRelation !== undefined ? { sourceRelation: line.sourceRelation } : {}),
59
64
  },
60
65
  },
61
66
  ],
@@ -78,6 +83,8 @@ function withLineComment(line, result) {
78
83
  sourceName: line.sourceName,
79
84
  line: line.line,
80
85
  column: firstColumn(line.text),
86
+ ...(line.sourceUnit !== undefined ? { sourceUnit: line.sourceUnit } : {}),
87
+ ...(line.sourceRelation !== undefined ? { sourceRelation: line.sourceRelation } : {}),
81
88
  },
82
89
  },
83
90
  ],
@@ -107,6 +114,15 @@ function parseCanonicalStatement(line, text, span) {
107
114
  }
108
115
  return { items: [], diagnostics: [parseError(line, `unsupported source line: ${text}`)] };
109
116
  }
117
+ function spanForLine(line) {
118
+ return {
119
+ sourceName: line.sourceName,
120
+ line: line.line,
121
+ column: firstColumn(line.text),
122
+ ...(line.sourceUnit !== undefined ? { sourceUnit: line.sourceUnit } : {}),
123
+ ...(line.sourceRelation !== undefined ? { sourceRelation: line.sourceRelation } : {}),
124
+ };
125
+ }
110
126
  function normalizeEntryLabelName(raw) {
111
127
  return raw.startsWith('@') ? raw.slice(1) : raw;
112
128
  }
@@ -34,7 +34,7 @@ export function analyzeProgramNext(loadedProgram, options = {}) {
34
34
  mode: options.caseStyle ?? 'off',
35
35
  });
36
36
  return {
37
- diagnostics: caseStyleDiagnostics,
37
+ diagnostics: [...assembly.diagnostics, ...caseStyleDiagnostics],
38
38
  env: { symbols: assembly.symbols },
39
39
  };
40
40
  }
@@ -0,0 +1,192 @@
1
+ ---
2
+ layout: default
3
+ title: 'Chapter 1 - Orientation and Repository Layout'
4
+ parent: 'AZM Engineering Manual'
5
+ nav_order: 1
6
+ ---
7
+
8
+ [Manual](index.md) | [Source Loading and Parsing ->](02-source-loading-and-parsing.md)
9
+
10
+ # Chapter 1 - Orientation and Repository Layout
11
+
12
+ AZM is a Z80 assembler and tooling package. It turns `.asm` and `.z80` source
13
+ files into bytes, Intel HEX, flat binary output, Debug80 maps, lowered ASM80
14
+ source and register contract metadata. The same implementation serves the command
15
+ line, package consumers, Debug80 integration and the test suite.
16
+
17
+ The codebase follows the same path as an assembly run. A source file is loaded,
18
+ `.include` lines are expanded, source is split into logical lines, logical lines
19
+ become typed source items, visible `op` invocations expand into ordinary
20
+ instructions, assembler-time facts are collected, instructions and data emit
21
+ bytes, symbolic fixups are resolved and output writers serialize the result.
22
+
23
+ AZM's extensions are assembler-time features. Layout types, enums, type
24
+ aliases, AZMDoc comments and register contracts help the assembler
25
+ calculate addresses, check contracts and produce metadata. Runtime behaviour
26
+ still comes from the Z80 instructions and bytes that AZM emits.
27
+
28
+ ## The Compiler Path
29
+
30
+ A small source file shows the main pipeline:
31
+
32
+ ```asm
33
+ .org $0100
34
+
35
+ LIMIT .equ 8
36
+ SpriteArray .typealias Sprite[16]
37
+
38
+ Sprite .type
39
+ x .field byte
40
+ y .field byte
41
+ tile .field byte
42
+ flags .field byte
43
+ .endtype
44
+
45
+ @Start:
46
+ ld b,LIMIT
47
+ Loop:
48
+ djnz Loop
49
+
50
+ Sprites:
51
+ .ds SpriteArray
52
+ ```
53
+
54
+ The loader reads the entry file and expands includes. The logical-line scanner
55
+ records each line with source provenance. The parser emits source items for
56
+ `.org`, `.equ`, `.typealias`, the `Sprite` layout, labels, instructions and
57
+ `.ds`. Address planning assigns `$0100` to `@Start`, assigns the following
58
+ addresses to `Loop` and `Sprites`, records `LIMIT = 8` and records the size of
59
+ `SpriteArray`. The encoder turns `ld b,LIMIT` and `djnz Loop` into fragments.
60
+ Fixup emission resolves `LIMIT` and the relative branch displacement. The
61
+ output writers produce the selected artifacts.
62
+
63
+ The CLI and package consumers use this same path. AZM has one compiler pipeline
64
+ with several entry points.
65
+
66
+ ## Main Layers
67
+
68
+ The implementation has six main layers:
69
+
70
+ 1. **Public entry points** in `src/index.ts`, `src/api-compile.ts`,
71
+ `src/api-artifacts.ts`, `src/api-register-contracts.ts`, `src/api-tooling.ts`
72
+ and `src/cli.ts`.
73
+ 2. **Loading and parsing** in `src/node/`, `src/source/`, `src/syntax/` and
74
+ `src/core/compile.ts`.
75
+ 3. **Assembler-time analysis** in `src/assembly/` and `src/semantics/`.
76
+ 4. **Z80 parsing and encoding** in `src/z80/`.
77
+ 5. **Language services** in `src/expansion/`, `src/register-contracts/` and
78
+ `src/tooling/`.
79
+ 6. **Artifact writers** in `src/outputs/`.
80
+
81
+ Each layer passes structured data to the next. Diagnostics are accumulated as
82
+ data objects and formatted at the CLI edge. Editor tooling, tests and package
83
+ consumers share the same diagnostic model.
84
+
85
+ ## Runtime Boundary
86
+
87
+ AZM computes everything it can at assembly time. `sizeof(Sprite)`,
88
+ `offset(Sprite, flags)` and `<SpriteArray>Sprites[3].tile` fold to numbers while
89
+ the assembler runs. The generated Z80 program receives those numbers in
90
+ instructions and data. At runtime the CPU executes normal Z80 operations:
91
+ loads, stores, branches, calls, returns and port I/O.
92
+
93
+ This boundary explains where major features live. Layout code belongs to the
94
+ assembler because it calculates byte offsets. Register contracts belong to the
95
+ assembler because it analyses visible calls and register effects. Output writers
96
+ belong at the edge because they serialize already-assembled facts.
97
+
98
+ ## Repository Shape
99
+
100
+ The AZM repository has a compact top-level structure:
101
+
102
+ ```text
103
+ AZM/
104
+ src/ TypeScript implementation
105
+ test/ unit, integration, CLI, differential and acceptance tests
106
+ docs/ active contributor references, specs and design notes
107
+ examples/ small runnable source examples
108
+ scripts/ CI, guardrail and developer utility scripts
109
+ dist/ generated package output
110
+ package.json package exports, CLI bin, scripts and dependencies
111
+ ```
112
+
113
+ The repository is a Node package. The source is TypeScript ESM. The published
114
+ package exposes the CLI binary `azm` and stable imports for compile and tooling
115
+ consumers.
116
+
117
+ ## Source Directories
118
+
119
+ `src/` is organised by compiler responsibility:
120
+
121
+ | Directory | Responsibility |
122
+ | --------------------- | ----------------------------------------------------------------------------------------------------------------------- |
123
+ | `assembly/` | Address planning, placement, byte emission and fixups. |
124
+ | `cli/` | Argument parsing, usage text, artifact path calculation and disk artifact writing. |
125
+ | `core/` | In-memory compile helpers, conditional assembly and source-item parsing orchestration. |
126
+ | `diagnostics/` | Diagnostic text formatting. |
127
+ | `expansion/` | Visible `op` collection, operand modelling, overload selection and expansion. |
128
+ | `model/` | Shared data types used across layers. |
129
+ | `node/` | File-backed source loading and include expansion. |
130
+ | `outputs/` | BIN, HEX, D8 map, lowered ASM80 and artifact helper writers. |
131
+ | `register-contracts/` | Register contract routine modelling, instruction shape helpers, liveness, summaries, reports, interfaces and fixes. |
132
+ | `semantics/` | Expression evaluation, constant operators, byte functions and layout evaluation. |
133
+ | `source/` | Source files, spans, logical line scanning, comment scanning and comment stripping. |
134
+ | `syntax/` | Line parsing, directive parsing, expression tokenizing, token expression parsing, layout parsing and directive aliases. |
135
+ | `tooling/` | Editor/tooling APIs and source-style checks. |
136
+ | `z80/` | Z80 instruction model, operand splitting, parser families, encoder families and register effects. |
137
+
138
+ The root files expose public entry points. The subdirectories hold the compiler
139
+ pipeline. A change usually belongs to the directory that owns the data it
140
+ changes.
141
+
142
+ ## Tests, Docs and Scripts
143
+
144
+ The test tree mirrors the implementation boundaries. Unit tests target narrow
145
+ modules. Integration tests cover cross-stage compiler behaviour. CLI tests
146
+ verify argument and artifact contracts. ASM80 and differential tests protect
147
+ compatibility and byte parity. Type tests protect the public TypeScript surface.
148
+
149
+ The repo-local docs are the active working set for implementation detail:
150
+
151
+ ```text
152
+ docs/
153
+ codebase/
154
+ ```
155
+
156
+ `docs/codebase/` is the maintained engineering manual for the source tree,
157
+ compile flow, user-facing CLI, package-facing APIs, AZMDoc/register contract
158
+ metadata and verification lanes. Avoid reintroducing parallel reference,
159
+ planning or design trees unless there is a concrete active need.
160
+
161
+ `scripts/` contains verification and maintenance utilities. The package scripts
162
+ in `package.json` are the normal entry points. Invoke script files directly
163
+ while debugging the script itself.
164
+
165
+ ## Package Exports
166
+
167
+ `package.json` exposes these public paths:
168
+
169
+ ```text
170
+ @jhlagado/azm
171
+ @jhlagado/azm/compile
172
+ @jhlagado/azm/tooling
173
+ @jhlagado/azm/cli
174
+ @jhlagado/azm/package.json
175
+ ```
176
+
177
+ Public consumers import from those paths. Internal files under `src/` and
178
+ compiled files under `dist/src/` are implementation details.
179
+
180
+ ## Reading the Codebase
181
+
182
+ Start with the public entry point that matches your task. For a CLI bug, begin
183
+ in `src/cli/run.ts` and follow the option into `api-compile.ts`. For source
184
+ syntax, begin in `parseNextSourceItems()` and `parse-line.ts`. For an encoding
185
+ bug, begin in `parse-instruction.ts`, `instruction.ts` and `encode.ts`. For a
186
+ D8 map issue, begin in `program-emission.ts`, `outputs/types.ts` and
187
+ `write-d8.ts`.
188
+
189
+ The compiler is small enough that one feature can be followed from front to
190
+ back. `.typealias`, for example, appears in the parser, address planner,
191
+ expression evaluator, tests and manual examples. A feature is complete when
192
+ each boundary that observes it has the right structured fact.