@jhlagado/azm 0.1.0
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.
- package/LICENSE +649 -0
- package/README.md +142 -0
- package/dist/src/analysis.d.ts +11 -0
- package/dist/src/analysis.js +41 -0
- package/dist/src/api-compile.d.ts +8 -0
- package/dist/src/api-compile.js +3 -0
- package/dist/src/api-tooling.d.ts +25 -0
- package/dist/src/api-tooling.js +21 -0
- package/dist/src/cli.d.ts +30 -0
- package/dist/src/cli.js +523 -0
- package/dist/src/compile.d.ts +10 -0
- package/dist/src/compile.js +175 -0
- package/dist/src/compileShared.d.ts +3 -0
- package/dist/src/compileShared.js +7 -0
- package/dist/src/diagnosticTypes.d.ts +77 -0
- package/dist/src/diagnosticTypes.js +53 -0
- package/dist/src/formats/index.d.ts +7 -0
- package/dist/src/formats/index.js +17 -0
- package/dist/src/formats/range.d.ts +17 -0
- package/dist/src/formats/range.js +45 -0
- package/dist/src/formats/types.d.ts +208 -0
- package/dist/src/formats/types.js +1 -0
- package/dist/src/formats/writeAsm80.d.ts +6 -0
- package/dist/src/formats/writeAsm80.js +86 -0
- package/dist/src/formats/writeBin.d.ts +7 -0
- package/dist/src/formats/writeBin.js +23 -0
- package/dist/src/formats/writeD8m.d.ts +9 -0
- package/dist/src/formats/writeD8m.js +239 -0
- package/dist/src/formats/writeHex.d.ts +9 -0
- package/dist/src/formats/writeHex.js +39 -0
- package/dist/src/formats/writeListing.d.ts +8 -0
- package/dist/src/formats/writeListing.js +83 -0
- package/dist/src/frontend/asm80/asmLine.d.ts +39 -0
- package/dist/src/frontend/asm80/asmLine.js +89 -0
- package/dist/src/frontend/asm80/parseAsmRawValues.d.ts +4 -0
- package/dist/src/frontend/asm80/parseAsmRawValues.js +94 -0
- package/dist/src/frontend/asm80/quoteScan.d.ts +10 -0
- package/dist/src/frontend/asm80/quoteScan.js +25 -0
- package/dist/src/frontend/ast.d.ts +376 -0
- package/dist/src/frontend/ast.js +1 -0
- package/dist/src/frontend/directiveAliases.d.ts +14 -0
- package/dist/src/frontend/directiveAliases.js +189 -0
- package/dist/src/frontend/grammarData.d.ts +14 -0
- package/dist/src/frontend/grammarData.js +65 -0
- package/dist/src/frontend/immExprUtils.d.ts +2 -0
- package/dist/src/frontend/immExprUtils.js +12 -0
- package/dist/src/frontend/parseAsmFlatDirectiveLine.d.ts +17 -0
- package/dist/src/frontend/parseAsmFlatDirectiveLine.js +187 -0
- package/dist/src/frontend/parseAsmInstruction.d.ts +3 -0
- package/dist/src/frontend/parseAsmInstruction.js +73 -0
- package/dist/src/frontend/parseAsmStatements.d.ts +6 -0
- package/dist/src/frontend/parseAsmStatements.js +16 -0
- package/dist/src/frontend/parseAsmStream.d.ts +10 -0
- package/dist/src/frontend/parseAsmStream.js +33 -0
- package/dist/src/frontend/parseAsmTopLevel.d.ts +18 -0
- package/dist/src/frontend/parseAsmTopLevel.js +34 -0
- package/dist/src/frontend/parseDiagnostics.d.ts +9 -0
- package/dist/src/frontend/parseDiagnostics.js +16 -0
- package/dist/src/frontend/parseEnum.d.ts +12 -0
- package/dist/src/frontend/parseEnum.js +70 -0
- package/dist/src/frontend/parseImm.d.ts +10 -0
- package/dist/src/frontend/parseImm.js +397 -0
- package/dist/src/frontend/parseLogicalLines.d.ts +11 -0
- package/dist/src/frontend/parseLogicalLines.js +94 -0
- package/dist/src/frontend/parseOp.d.ts +25 -0
- package/dist/src/frontend/parseOp.js +120 -0
- package/dist/src/frontend/parseOpHeader.d.ts +20 -0
- package/dist/src/frontend/parseOpHeader.js +32 -0
- package/dist/src/frontend/parseOperands.d.ts +4 -0
- package/dist/src/frontend/parseOperands.js +290 -0
- package/dist/src/frontend/parseParams.d.ts +6 -0
- package/dist/src/frontend/parseParams.js +62 -0
- package/dist/src/frontend/parseParserRecovery.d.ts +12 -0
- package/dist/src/frontend/parseParserRecovery.js +17 -0
- package/dist/src/frontend/parseParserShared.d.ts +2 -0
- package/dist/src/frontend/parseParserShared.js +8 -0
- package/dist/src/frontend/parseRawDataDirectiveStart.d.ts +1 -0
- package/dist/src/frontend/parseRawDataDirectiveStart.js +3 -0
- package/dist/src/frontend/parseRawDataDirectives.d.ts +7 -0
- package/dist/src/frontend/parseRawDataDirectives.js +142 -0
- package/dist/src/frontend/parseRecordFieldDecl.d.ts +34 -0
- package/dist/src/frontend/parseRecordFieldDecl.js +177 -0
- package/dist/src/frontend/parseSourceItemDispatch.d.ts +46 -0
- package/dist/src/frontend/parseSourceItemDispatch.js +53 -0
- package/dist/src/frontend/parseSourceItemTable.d.ts +16 -0
- package/dist/src/frontend/parseSourceItemTable.js +68 -0
- package/dist/src/frontend/parseTopLevelCommon.d.ts +11 -0
- package/dist/src/frontend/parseTopLevelCommon.js +50 -0
- package/dist/src/frontend/parseTypes.d.ts +24 -0
- package/dist/src/frontend/parseTypes.js +77 -0
- package/dist/src/frontend/parser.d.ts +11 -0
- package/dist/src/frontend/parser.js +88 -0
- package/dist/src/frontend/source.d.ts +28 -0
- package/dist/src/frontend/source.js +58 -0
- package/dist/src/frontend/sourceExtensions.d.ts +2 -0
- package/dist/src/frontend/sourceExtensions.js +6 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +2 -0
- package/dist/src/lintCaseStyle.d.ts +4 -0
- package/dist/src/lintCaseStyle.js +129 -0
- package/dist/src/lowering/asmDirectiveLowering.d.ts +4 -0
- package/dist/src/lowering/asmDirectiveLowering.js +229 -0
- package/dist/src/lowering/asmDirectiveTraversal.d.ts +47 -0
- package/dist/src/lowering/asmDirectiveTraversal.js +52 -0
- package/dist/src/lowering/asmEquResolution.d.ts +8 -0
- package/dist/src/lowering/asmEquResolution.js +69 -0
- package/dist/src/lowering/asmInstructionLdHelpers.d.ts +15 -0
- package/dist/src/lowering/asmInstructionLdHelpers.js +102 -0
- package/dist/src/lowering/asmInstructionLowering.d.ts +5 -0
- package/dist/src/lowering/asmInstructionLowering.js +54 -0
- package/dist/src/lowering/asmInstructionStream.d.ts +46 -0
- package/dist/src/lowering/asmInstructionStream.js +51 -0
- package/dist/src/lowering/asmLoweringBranchCall.d.ts +43 -0
- package/dist/src/lowering/asmLoweringBranchCall.js +254 -0
- package/dist/src/lowering/asmLoweringHost.d.ts +23 -0
- package/dist/src/lowering/asmLoweringHost.js +1 -0
- package/dist/src/lowering/asmLoweringLd.d.ts +28 -0
- package/dist/src/lowering/asmLoweringLd.js +144 -0
- package/dist/src/lowering/asmRangeLowering.d.ts +17 -0
- package/dist/src/lowering/asmRangeLowering.js +39 -0
- package/dist/src/lowering/asmRawDataLowering.d.ts +16 -0
- package/dist/src/lowering/asmRawDataLowering.js +209 -0
- package/dist/src/lowering/asmSourceEmitter.d.ts +4 -0
- package/dist/src/lowering/asmSourceEmitter.js +9 -0
- package/dist/src/lowering/asmSourceInstructionLowering.d.ts +4 -0
- package/dist/src/lowering/asmSourceInstructionLowering.js +14 -0
- package/dist/src/lowering/asmUtils.d.ts +13 -0
- package/dist/src/lowering/asmUtils.js +105 -0
- package/dist/src/lowering/assemblerFlowSetup.d.ts +54 -0
- package/dist/src/lowering/assemblerFlowSetup.js +128 -0
- package/dist/src/lowering/assemblerLoweringContext.d.ts +151 -0
- package/dist/src/lowering/assemblerLoweringContext.js +16 -0
- package/dist/src/lowering/assemblerLoweringContextSplit.d.ts +7 -0
- package/dist/src/lowering/assemblerLoweringContextSplit.js +75 -0
- package/dist/src/lowering/assemblerLoweringPhases.d.ts +66 -0
- package/dist/src/lowering/assemblerLoweringPhases.js +166 -0
- package/dist/src/lowering/bytePlacement.d.ts +7 -0
- package/dist/src/lowering/bytePlacement.js +37 -0
- package/dist/src/lowering/capabilities.d.ts +67 -0
- package/dist/src/lowering/capabilities.js +1 -0
- package/dist/src/lowering/eaResolution.d.ts +58 -0
- package/dist/src/lowering/eaResolution.js +159 -0
- package/dist/src/lowering/emissionCore.d.ts +17 -0
- package/dist/src/lowering/emissionCore.js +21 -0
- package/dist/src/lowering/emit.d.ts +17 -0
- package/dist/src/lowering/emit.js +46 -0
- package/dist/src/lowering/emitContextBuilder.d.ts +63 -0
- package/dist/src/lowering/emitContextBuilder.js +41 -0
- package/dist/src/lowering/emitFinalization.d.ts +61 -0
- package/dist/src/lowering/emitFinalization.js +66 -0
- package/dist/src/lowering/emitFinalizationSetup.d.ts +19 -0
- package/dist/src/lowering/emitFinalizationSetup.js +26 -0
- package/dist/src/lowering/emitPhase1BuildProgramLoweringContext.d.ts +7 -0
- package/dist/src/lowering/emitPhase1BuildProgramLoweringContext.js +119 -0
- package/dist/src/lowering/emitPhase1Helpers.d.ts +4 -0
- package/dist/src/lowering/emitPhase1Helpers.js +12 -0
- package/dist/src/lowering/emitPhase1Types.d.ts +21 -0
- package/dist/src/lowering/emitPhase1Types.js +1 -0
- package/dist/src/lowering/emitPhase1WirePipeline.d.ts +70 -0
- package/dist/src/lowering/emitPhase1WirePipeline.js +203 -0
- package/dist/src/lowering/emitPhase1Workspace.d.ts +82 -0
- package/dist/src/lowering/emitPhase1Workspace.js +55 -0
- package/dist/src/lowering/emitPipeline.d.ts +121 -0
- package/dist/src/lowering/emitPipeline.js +57 -0
- package/dist/src/lowering/emitProgramContext.d.ts +39 -0
- package/dist/src/lowering/emitProgramContext.js +29 -0
- package/dist/src/lowering/emitState.d.ts +90 -0
- package/dist/src/lowering/emitState.js +124 -0
- package/dist/src/lowering/fixupBaseResolution.d.ts +7 -0
- package/dist/src/lowering/fixupBaseResolution.js +23 -0
- package/dist/src/lowering/fixupEmission.d.ts +64 -0
- package/dist/src/lowering/fixupEmission.js +199 -0
- package/dist/src/lowering/immMath.d.ts +2 -0
- package/dist/src/lowering/immMath.js +34 -0
- package/dist/src/lowering/inputAssets.d.ts +7 -0
- package/dist/src/lowering/inputAssets.js +106 -0
- package/dist/src/lowering/ldEncoding.d.ts +15 -0
- package/dist/src/lowering/ldEncoding.js +12 -0
- package/dist/src/lowering/ldEncodingRegMemHelpers.d.ts +5 -0
- package/dist/src/lowering/ldEncodingRegMemHelpers.js +124 -0
- package/dist/src/lowering/ldFormSelection.d.ts +26 -0
- package/dist/src/lowering/ldFormSelection.js +92 -0
- package/dist/src/lowering/ldLowering.d.ts +7 -0
- package/dist/src/lowering/ldLowering.js +13 -0
- package/dist/src/lowering/loweredAsmByteEmission.d.ts +23 -0
- package/dist/src/lowering/loweredAsmByteEmission.js +185 -0
- package/dist/src/lowering/loweredAsmPlacement.d.ts +13 -0
- package/dist/src/lowering/loweredAsmPlacement.js +86 -0
- package/dist/src/lowering/loweredAsmStreamRecording.d.ts +27 -0
- package/dist/src/lowering/loweredAsmStreamRecording.js +215 -0
- package/dist/src/lowering/loweredAsmTypes.d.ts +202 -0
- package/dist/src/lowering/loweredAsmTypes.js +1 -0
- package/dist/src/lowering/loweredFormat.d.ts +3 -0
- package/dist/src/lowering/loweredFormat.js +26 -0
- package/dist/src/lowering/loweredItemSize.d.ts +4 -0
- package/dist/src/lowering/loweredItemSize.js +17 -0
- package/dist/src/lowering/loweringDiagnostics.d.ts +9 -0
- package/dist/src/lowering/loweringDiagnostics.js +55 -0
- package/dist/src/lowering/loweringTypes.d.ts +27 -0
- package/dist/src/lowering/loweringTypes.js +1 -0
- package/dist/src/lowering/opCandidateRegistry.d.ts +9 -0
- package/dist/src/lowering/opCandidateRegistry.js +9 -0
- package/dist/src/lowering/opExpansionExecution.d.ts +15 -0
- package/dist/src/lowering/opExpansionExecution.js +45 -0
- package/dist/src/lowering/opExpansionOrchestration.d.ts +15 -0
- package/dist/src/lowering/opExpansionOrchestration.js +88 -0
- package/dist/src/lowering/opExpansionStream.d.ts +12 -0
- package/dist/src/lowering/opExpansionStream.js +176 -0
- package/dist/src/lowering/opMatching.d.ts +52 -0
- package/dist/src/lowering/opMatching.js +355 -0
- package/dist/src/lowering/opSubstitution.d.ts +13 -0
- package/dist/src/lowering/opSubstitution.js +175 -0
- package/dist/src/lowering/prescanTypes.d.ts +7 -0
- package/dist/src/lowering/prescanTypes.js +1 -0
- package/dist/src/lowering/programLowering.d.ts +144 -0
- package/dist/src/lowering/programLowering.js +2 -0
- package/dist/src/lowering/programLoweringDeclarations.d.ts +5 -0
- package/dist/src/lowering/programLoweringDeclarations.js +5 -0
- package/dist/src/lowering/programLoweringFinalize.d.ts +18 -0
- package/dist/src/lowering/programLoweringFinalize.js +115 -0
- package/dist/src/lowering/programLoweringTraversal.d.ts +2 -0
- package/dist/src/lowering/programLoweringTraversal.js +93 -0
- package/dist/src/lowering/programPrescan.d.ts +3 -0
- package/dist/src/lowering/programPrescan.js +37 -0
- package/dist/src/lowering/traceFormat.d.ts +13 -0
- package/dist/src/lowering/traceFormat.js +211 -0
- package/dist/src/pathCompare.d.ts +3 -0
- package/dist/src/pathCompare.js +26 -0
- package/dist/src/pipeline.d.ts +78 -0
- package/dist/src/pipeline.js +1 -0
- package/dist/src/registerCare/analyze.d.ts +24 -0
- package/dist/src/registerCare/analyze.js +327 -0
- package/dist/src/registerCare/annotate.d.ts +11 -0
- package/dist/src/registerCare/annotate.js +76 -0
- package/dist/src/registerCare/boundaryHints.d.ts +2 -0
- package/dist/src/registerCare/boundaryHints.js +10 -0
- package/dist/src/registerCare/carriers.d.ts +4 -0
- package/dist/src/registerCare/carriers.js +78 -0
- package/dist/src/registerCare/controlFlow.d.ts +5 -0
- package/dist/src/registerCare/controlFlow.js +35 -0
- package/dist/src/registerCare/fix.d.ts +11 -0
- package/dist/src/registerCare/fix.js +119 -0
- package/dist/src/registerCare/liveness.d.ts +7 -0
- package/dist/src/registerCare/liveness.js +227 -0
- package/dist/src/registerCare/profiles.d.ts +11 -0
- package/dist/src/registerCare/profiles.js +45 -0
- package/dist/src/registerCare/programModel.d.ts +3 -0
- package/dist/src/registerCare/programModel.js +181 -0
- package/dist/src/registerCare/report.d.ts +5 -0
- package/dist/src/registerCare/report.js +139 -0
- package/dist/src/registerCare/smartComments.d.ts +5 -0
- package/dist/src/registerCare/smartComments.js +247 -0
- package/dist/src/registerCare/sourceText.d.ts +8 -0
- package/dist/src/registerCare/sourceText.js +15 -0
- package/dist/src/registerCare/summary.d.ts +3 -0
- package/dist/src/registerCare/summary.js +492 -0
- package/dist/src/registerCare/tooling.d.ts +42 -0
- package/dist/src/registerCare/tooling.js +50 -0
- package/dist/src/registerCare/types.d.ts +154 -0
- package/dist/src/registerCare/types.js +1 -0
- package/dist/src/semantics/declVisitor.d.ts +5 -0
- package/dist/src/semantics/declVisitor.js +11 -0
- package/dist/src/semantics/env.d.ts +28 -0
- package/dist/src/semantics/env.js +432 -0
- package/dist/src/semantics/layout.d.ts +21 -0
- package/dist/src/semantics/layout.js +226 -0
- package/dist/src/semantics/layoutCastFold.d.ts +22 -0
- package/dist/src/semantics/layoutCastFold.js +118 -0
- package/dist/src/semantics/semanticsDiagnostics.d.ts +2 -0
- package/dist/src/semantics/semanticsDiagnostics.js +4 -0
- package/dist/src/semantics/typeQueries.d.ts +31 -0
- package/dist/src/semantics/typeQueries.js +124 -0
- package/dist/src/sourceIncludeExpansion.d.ts +17 -0
- package/dist/src/sourceIncludeExpansion.js +124 -0
- package/dist/src/sourceIncludePaths.d.ts +1 -0
- package/dist/src/sourceIncludePaths.js +12 -0
- package/dist/src/sourceLoader.d.ts +15 -0
- package/dist/src/sourceLoader.js +118 -0
- package/dist/src/z80/effects.d.ts +3 -0
- package/dist/src/z80/effects.js +516 -0
- package/dist/src/z80/encode.d.ts +10 -0
- package/dist/src/z80/encode.js +412 -0
- package/dist/src/z80/encodeAlu.d.ts +7 -0
- package/dist/src/z80/encodeAlu.js +219 -0
- package/dist/src/z80/encodeBitOps.d.ts +7 -0
- package/dist/src/z80/encodeBitOps.js +123 -0
- package/dist/src/z80/encodeContext.d.ts +29 -0
- package/dist/src/z80/encodeContext.js +1 -0
- package/dist/src/z80/encodeControl.d.ts +26 -0
- package/dist/src/z80/encodeControl.js +180 -0
- package/dist/src/z80/encodeCoreOps.d.ts +7 -0
- package/dist/src/z80/encodeCoreOps.js +131 -0
- package/dist/src/z80/encodeIo.d.ts +9 -0
- package/dist/src/z80/encodeIo.js +128 -0
- package/dist/src/z80/encodeLd.d.ts +13 -0
- package/dist/src/z80/encodeLd.js +273 -0
- package/dist/src/z80/encoderRegistry.d.ts +13 -0
- package/dist/src/z80/encoderRegistry.js +169 -0
- package/docs/reference/cli.md +134 -0
- package/docs/reference/tooling-api.md +248 -0
- package/package.json +98 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { createInlineOpInstructionStreamExpander } from '../lowering/opExpansionStream.js';
|
|
2
|
+
function flattenItems(items, out, expandInstruction) {
|
|
3
|
+
for (const item of items) {
|
|
4
|
+
if (item.kind === 'AsmLabel') {
|
|
5
|
+
out.push({ kind: 'label', label: item });
|
|
6
|
+
continue;
|
|
7
|
+
}
|
|
8
|
+
if (item.kind === 'AsmInstruction') {
|
|
9
|
+
out.push(...expandInstruction(item));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function directCallTarget(inst) {
|
|
14
|
+
if (inst.head.toLowerCase() !== 'call')
|
|
15
|
+
return undefined;
|
|
16
|
+
if (inst.operands.length !== 1 && inst.operands.length !== 2)
|
|
17
|
+
return undefined;
|
|
18
|
+
const op = inst.operands[inst.operands.length - 1];
|
|
19
|
+
if (op?.kind !== 'Imm' || op.expr.kind !== 'ImmName')
|
|
20
|
+
return undefined;
|
|
21
|
+
return op.expr.name;
|
|
22
|
+
}
|
|
23
|
+
function directTailJumpTarget(inst, entryNames) {
|
|
24
|
+
if (inst.head.toLowerCase() !== 'jp')
|
|
25
|
+
return undefined;
|
|
26
|
+
if (inst.operands.length !== 1 && inst.operands.length !== 2)
|
|
27
|
+
return undefined;
|
|
28
|
+
if (inst.operands.length === 2 && entryNames === undefined)
|
|
29
|
+
return undefined;
|
|
30
|
+
const op = inst.operands[inst.operands.length - 1];
|
|
31
|
+
if (op?.kind !== 'Imm' || op.expr.kind !== 'ImmName')
|
|
32
|
+
return undefined;
|
|
33
|
+
if (op.expr.name.startsWith('.'))
|
|
34
|
+
return undefined;
|
|
35
|
+
if (entryNames !== undefined && !entryNames.has(op.expr.name))
|
|
36
|
+
return undefined;
|
|
37
|
+
return op.expr.name;
|
|
38
|
+
}
|
|
39
|
+
function toInstruction(inst, labels = []) {
|
|
40
|
+
return {
|
|
41
|
+
instruction: inst,
|
|
42
|
+
head: inst.head.toLowerCase(),
|
|
43
|
+
file: inst.span.file,
|
|
44
|
+
line: inst.span.start.line,
|
|
45
|
+
column: inst.span.start.column,
|
|
46
|
+
labels,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function spanFrom(start, end) {
|
|
50
|
+
if (start.file !== end.file)
|
|
51
|
+
return start;
|
|
52
|
+
return {
|
|
53
|
+
file: start.file,
|
|
54
|
+
start: start.start,
|
|
55
|
+
end: end.end,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function isLocalLabel(name) {
|
|
59
|
+
return name.startsWith('.');
|
|
60
|
+
}
|
|
61
|
+
function isEntryLabel(label) {
|
|
62
|
+
return label.isEntry === true;
|
|
63
|
+
}
|
|
64
|
+
function flatItemFile(item) {
|
|
65
|
+
return item.kind === 'label' ? item.label.span.file : item.instruction.span.file;
|
|
66
|
+
}
|
|
67
|
+
function isTerminalReturn(inst) {
|
|
68
|
+
const head = inst.head.toLowerCase();
|
|
69
|
+
if (head === 'ret')
|
|
70
|
+
return inst.operands.length === 0;
|
|
71
|
+
return head === 'retn' || head === 'reti';
|
|
72
|
+
}
|
|
73
|
+
export function buildRegisterCareProgramModel(program) {
|
|
74
|
+
const flat = [];
|
|
75
|
+
const { expandInstruction } = createInlineOpInstructionStreamExpander(program);
|
|
76
|
+
for (const file of program.files) {
|
|
77
|
+
flattenItems(file.items, flat, expandInstruction);
|
|
78
|
+
}
|
|
79
|
+
const labelItems = flat.filter((item) => item.kind === 'label');
|
|
80
|
+
const filesWithEntryLabels = new Set(labelItems.filter((item) => isEntryLabel(item.label)).map((item) => item.label.span.file));
|
|
81
|
+
const entryLabelNames = new Set(labelItems
|
|
82
|
+
.filter((item) => isEntryLabel(item.label) ||
|
|
83
|
+
(!filesWithEntryLabels.has(item.label.span.file) && !isLocalLabel(item.label.name)))
|
|
84
|
+
.map((item) => item.label.name));
|
|
85
|
+
const directCalls = flat.flatMap((item) => {
|
|
86
|
+
if (item.kind !== 'instruction')
|
|
87
|
+
return [];
|
|
88
|
+
const target = directCallTarget(item.instruction);
|
|
89
|
+
if (target === undefined)
|
|
90
|
+
return [];
|
|
91
|
+
return [
|
|
92
|
+
{
|
|
93
|
+
target,
|
|
94
|
+
subject: `CALL ${target}`,
|
|
95
|
+
file: item.instruction.span.file,
|
|
96
|
+
line: item.instruction.span.start.line,
|
|
97
|
+
column: item.instruction.span.start.column,
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
});
|
|
101
|
+
const directTailJumps = flat.flatMap((item) => {
|
|
102
|
+
if (item.kind !== 'instruction')
|
|
103
|
+
return [];
|
|
104
|
+
const sourceFileUsesEntryLabels = filesWithEntryLabels.has(item.instruction.span.file);
|
|
105
|
+
const target = directTailJumpTarget(item.instruction, sourceFileUsesEntryLabels ? entryLabelNames : undefined);
|
|
106
|
+
if (target === undefined)
|
|
107
|
+
return [];
|
|
108
|
+
return [
|
|
109
|
+
{
|
|
110
|
+
target,
|
|
111
|
+
subject: `JP ${target}`,
|
|
112
|
+
file: item.instruction.span.file,
|
|
113
|
+
line: item.instruction.span.start.line,
|
|
114
|
+
column: item.instruction.span.start.column,
|
|
115
|
+
},
|
|
116
|
+
];
|
|
117
|
+
});
|
|
118
|
+
const directBoundaries = [...directCalls, ...directTailJumps];
|
|
119
|
+
const directCallTargets = Array.from(new Set(directCalls.map((call) => call.target))).sort();
|
|
120
|
+
const routines = [];
|
|
121
|
+
const coalescedGlobalLabelIndexes = new Set();
|
|
122
|
+
for (let index = 0; index < flat.length; index += 1) {
|
|
123
|
+
const item = flat[index];
|
|
124
|
+
if (coalescedGlobalLabelIndexes.has(index))
|
|
125
|
+
continue;
|
|
126
|
+
if (item?.kind !== 'label' || isLocalLabel(item.label.name))
|
|
127
|
+
continue;
|
|
128
|
+
const routineFile = item.label.span.file;
|
|
129
|
+
const fileUsesEntryLabels = filesWithEntryLabels.has(routineFile);
|
|
130
|
+
if (fileUsesEntryLabels && !isEntryLabel(item.label))
|
|
131
|
+
continue;
|
|
132
|
+
const labels = [item.label.name];
|
|
133
|
+
const entryLabels = isEntryLabel(item.label) ? [item.label.name] : undefined;
|
|
134
|
+
const instructions = [];
|
|
135
|
+
let pendingInstructionLabels = [item.label.name];
|
|
136
|
+
let endSpan = item.label.span;
|
|
137
|
+
for (let rangeIndex = index + 1; rangeIndex < flat.length; rangeIndex += 1) {
|
|
138
|
+
const rangeItem = flat[rangeIndex];
|
|
139
|
+
if (!rangeItem)
|
|
140
|
+
break;
|
|
141
|
+
if (flatItemFile(rangeItem) !== routineFile)
|
|
142
|
+
break;
|
|
143
|
+
if (rangeItem.kind === 'label') {
|
|
144
|
+
if (fileUsesEntryLabels && isEntryLabel(rangeItem.label)) {
|
|
145
|
+
if (instructions.length > 0)
|
|
146
|
+
break;
|
|
147
|
+
labels.push(rangeItem.label.name);
|
|
148
|
+
entryLabels?.push(rangeItem.label.name);
|
|
149
|
+
coalescedGlobalLabelIndexes.add(rangeIndex);
|
|
150
|
+
endSpan = rangeItem.label.span;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (!fileUsesEntryLabels && !isLocalLabel(rangeItem.label.name)) {
|
|
154
|
+
if (instructions.length > 0)
|
|
155
|
+
break;
|
|
156
|
+
labels.push(rangeItem.label.name);
|
|
157
|
+
coalescedGlobalLabelIndexes.add(rangeIndex);
|
|
158
|
+
endSpan = rangeItem.label.span;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
labels.push(rangeItem.label.name);
|
|
162
|
+
pendingInstructionLabels.push(rangeItem.label.name);
|
|
163
|
+
endSpan = rangeItem.label.span;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
instructions.push(toInstruction(rangeItem.instruction, pendingInstructionLabels));
|
|
167
|
+
pendingInstructionLabels = [];
|
|
168
|
+
endSpan = rangeItem.instruction.span;
|
|
169
|
+
if (isTerminalReturn(rangeItem.instruction))
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
routines.push({
|
|
173
|
+
name: item.label.name,
|
|
174
|
+
span: spanFrom(item.label.span, endSpan),
|
|
175
|
+
labels,
|
|
176
|
+
...(entryLabels ? { entryLabels } : {}),
|
|
177
|
+
instructions,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
return { routines, directCallTargets, directCalls, directBoundaries };
|
|
181
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { RegisterCareReportModel, RegisterCareUnit, RoutineSummary } from './types.js';
|
|
2
|
+
export declare function contractCarrierList(units: RegisterCareUnit[]): string;
|
|
3
|
+
export declare function renderRegisterCareReport(model: RegisterCareReportModel): string;
|
|
4
|
+
export declare function renderRegisterCareInterface(summaries: RoutineSummary[]): string;
|
|
5
|
+
export declare function renderRegisterCareSourceBlock(summary: RoutineSummary): string[];
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
function list(units) {
|
|
2
|
+
return units.length === 0 ? '-' : units.join(',');
|
|
3
|
+
}
|
|
4
|
+
const FLAG_UNITS = new Set(['carry', 'zero', 'sign', 'parity', 'halfCarry']);
|
|
5
|
+
const CONTRACT_CARRIER_PAIRS = [
|
|
6
|
+
{ label: 'BC', hi: 'B', lo: 'C' },
|
|
7
|
+
{ label: 'DE', hi: 'D', lo: 'E' },
|
|
8
|
+
{ label: 'HL', hi: 'H', lo: 'L' },
|
|
9
|
+
{ label: 'IX', hi: 'IXH', lo: 'IXL' },
|
|
10
|
+
{ label: 'IY', hi: 'IYH', lo: 'IYL' },
|
|
11
|
+
{ label: 'SP', hi: 'SPH', lo: 'SPL' },
|
|
12
|
+
];
|
|
13
|
+
export function contractCarrierList(units) {
|
|
14
|
+
const unique = [...new Set(units)];
|
|
15
|
+
const unitSet = new Set(unique);
|
|
16
|
+
const emitted = new Set();
|
|
17
|
+
const parts = [];
|
|
18
|
+
for (const unit of unique) {
|
|
19
|
+
if (emitted.has(unit))
|
|
20
|
+
continue;
|
|
21
|
+
const pair = CONTRACT_CARRIER_PAIRS.find((candidate) => (candidate.hi === unit || candidate.lo === unit) &&
|
|
22
|
+
unitSet.has(candidate.hi) &&
|
|
23
|
+
unitSet.has(candidate.lo));
|
|
24
|
+
if (pair) {
|
|
25
|
+
parts.push(pair.label);
|
|
26
|
+
emitted.add(pair.hi);
|
|
27
|
+
emitted.add(pair.lo);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
parts.push(unit);
|
|
31
|
+
emitted.add(unit);
|
|
32
|
+
}
|
|
33
|
+
return parts.length === 0 ? '-' : parts.join(',');
|
|
34
|
+
}
|
|
35
|
+
function relationOutputUnits(relations) {
|
|
36
|
+
return relations.flatMap((rel) => rel.out);
|
|
37
|
+
}
|
|
38
|
+
function contractEntries(summary) {
|
|
39
|
+
const out = [];
|
|
40
|
+
if (summary.mayRead.length > 0)
|
|
41
|
+
out.push({ keyword: 'in', carriers: contractCarrierList(summary.mayRead) });
|
|
42
|
+
const outputUnits = relationOutputUnits(summary.valueRelations);
|
|
43
|
+
if (outputUnits.length > 0)
|
|
44
|
+
out.push({ keyword: 'out', carriers: contractCarrierList(outputUnits) });
|
|
45
|
+
const relationOut = relationOutUnits(summary);
|
|
46
|
+
const clobbers = summary.mayWrite.filter((unit) => !relationOut.has(unit));
|
|
47
|
+
if (clobbers.length > 0)
|
|
48
|
+
out.push({ keyword: 'clobbers', carriers: contractCarrierList(clobbers) });
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
function sourceContractEntries(summary) {
|
|
52
|
+
const out = [];
|
|
53
|
+
if (summary.mayRead.length > 0)
|
|
54
|
+
out.push({ keyword: 'in', carriers: contractCarrierList(summary.mayRead) });
|
|
55
|
+
const relationOut = relationOutUnits(summary);
|
|
56
|
+
const candidates = (summary.outputCandidates ?? []).filter((unit) => !relationOut.has(unit));
|
|
57
|
+
if (candidates.length > 0)
|
|
58
|
+
out.push({ keyword: 'maybe-out', carriers: contractCarrierList(candidates) });
|
|
59
|
+
const outputUnits = relationOutputUnits(summary.valueRelations);
|
|
60
|
+
if (outputUnits.length > 0)
|
|
61
|
+
out.push({ keyword: 'out', carriers: contractCarrierList(outputUnits) });
|
|
62
|
+
const clobbers = summary.mayWrite.filter((unit) => !relationOut.has(unit) && !FLAG_UNITS.has(unit));
|
|
63
|
+
if (clobbers.length > 0)
|
|
64
|
+
out.push({ keyword: 'clobbers', carriers: contractCarrierList(clobbers) });
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
function stackStatus(summary) {
|
|
68
|
+
const balance = summary.stackBalanced ? 'balanced' : 'unbalanced';
|
|
69
|
+
return summary.hasUnknownStackEffect ? `${balance}, unknown effect` : balance;
|
|
70
|
+
}
|
|
71
|
+
function relationOutUnits(summary) {
|
|
72
|
+
return new Set(summary.valueRelations.flatMap((rel) => rel.out));
|
|
73
|
+
}
|
|
74
|
+
export function renderRegisterCareReport(model) {
|
|
75
|
+
const lines = ['AZM Register-Care Report', `Entry: ${model.entryFile}`, `Mode: ${model.mode}`];
|
|
76
|
+
if (model.profile)
|
|
77
|
+
lines.push(`Profile: ${model.profile}`);
|
|
78
|
+
lines.push('');
|
|
79
|
+
if (model.summaries.length === 0) {
|
|
80
|
+
lines.push('Routines: none', '');
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
for (const summary of model.summaries) {
|
|
84
|
+
lines.push(`Routine: ${summary.name}`);
|
|
85
|
+
lines.push(` reads: ${list(summary.mayRead)}`);
|
|
86
|
+
lines.push(` writes: ${list(summary.mayWrite)}`);
|
|
87
|
+
lines.push(` preserves: ${list(summary.preserved)}`);
|
|
88
|
+
lines.push(` stack: ${stackStatus(summary)}`);
|
|
89
|
+
for (const rel of summary.valueRelations) {
|
|
90
|
+
lines.push(` relation: ${list(rel.out)} <= ${list(rel.from)}`);
|
|
91
|
+
}
|
|
92
|
+
lines.push('');
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
lines.push('Conflicts:');
|
|
96
|
+
if (model.conflicts.length === 0) {
|
|
97
|
+
lines.push(' none');
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
for (const conflict of model.conflicts) {
|
|
101
|
+
lines.push(` ${conflict.file}:${conflict.line}:${conflict.column}: ${conflict.callTarget}: ${list(conflict.carriers)}: ${conflict.message}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
lines.push('');
|
|
105
|
+
lines.push('Output candidates:');
|
|
106
|
+
if (!model.outputCandidates || model.outputCandidates.length === 0) {
|
|
107
|
+
lines.push(' none');
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
for (const candidate of model.outputCandidates) {
|
|
111
|
+
lines.push(` ${candidate.file}:${candidate.line}:${candidate.column}: ${candidate.routine}: ${list(candidate.carriers)}: ${candidate.message}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
lines.push('');
|
|
115
|
+
lines.push('Unknown calls:');
|
|
116
|
+
if (model.unknownCalls.length === 0) {
|
|
117
|
+
lines.push(' none');
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
for (const call of model.unknownCalls)
|
|
121
|
+
lines.push(` ${call}`);
|
|
122
|
+
}
|
|
123
|
+
lines.push('');
|
|
124
|
+
return `${lines.join('\n')}\n`;
|
|
125
|
+
}
|
|
126
|
+
export function renderRegisterCareInterface(summaries) {
|
|
127
|
+
const lines = [];
|
|
128
|
+
for (const summary of summaries) {
|
|
129
|
+
lines.push(`extern ${summary.name}`);
|
|
130
|
+
for (const entry of contractEntries(summary)) {
|
|
131
|
+
lines.push(`${entry.keyword} ${entry.carriers}`);
|
|
132
|
+
}
|
|
133
|
+
lines.push('end', '');
|
|
134
|
+
}
|
|
135
|
+
return `${lines.join('\n')}\n`;
|
|
136
|
+
}
|
|
137
|
+
export function renderRegisterCareSourceBlock(summary) {
|
|
138
|
+
return sourceContractEntries(summary).map((entry) => `;! ${entry.keyword.padEnd(10)}${entry.carriers}`);
|
|
139
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { LocatedSmartComment, RegisterCareRoutine, RoutineContract, SmartComment } from './types.js';
|
|
2
|
+
export declare function parseSmartCommentLine(line: string): SmartComment | undefined;
|
|
3
|
+
export declare function parseSmartComments(sourceLineComments: Map<string, Map<number, string>>): LocatedSmartComment[];
|
|
4
|
+
export declare function buildRoutineContracts(comments: LocatedSmartComment[], routines?: RegisterCareRoutine[], sourceTexts?: Map<string, string>): Map<string, RoutineContract>;
|
|
5
|
+
export declare function parseInterfaceContracts(text: string, file?: string): Map<string, RoutineContract>;
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { expandCarrierList } from './carriers.js';
|
|
2
|
+
const COMPACT_SOURCE_TAG_RE = /^;?\s*!\s*(in|out|clobbers|preserves)(?:\s+(.+))?$/i;
|
|
3
|
+
const COMPACT_SOURCE_LINE_RE = /^\s*;\s*!\s*(?:in|out|maybe-out|clobbers|preserves)(?:\s|$)/i;
|
|
4
|
+
const INTERFACE_TAG_RE = /^\s*(in|out|clobbers|preserves)(?:\s+(.+))?$/i;
|
|
5
|
+
const CARRIER_RE = /^\{([^}]+)\}(?:\s+(.+))?$/;
|
|
6
|
+
function parseCarrierPayload(rest) {
|
|
7
|
+
if (!rest)
|
|
8
|
+
return undefined;
|
|
9
|
+
const match = CARRIER_RE.exec(rest.trim());
|
|
10
|
+
if (match) {
|
|
11
|
+
const carriers = match[1]
|
|
12
|
+
.split(',')
|
|
13
|
+
.map((s) => s.trim())
|
|
14
|
+
.filter(Boolean);
|
|
15
|
+
const name = match[2]?.trim();
|
|
16
|
+
return { carriers, ...(name ? { name } : {}) };
|
|
17
|
+
}
|
|
18
|
+
const tokens = rest.trim().split(/\s+/);
|
|
19
|
+
const carriers = [];
|
|
20
|
+
let consumed = 0;
|
|
21
|
+
for (const token of tokens) {
|
|
22
|
+
const cleaned = token.replace(/[.:;]+$/u, '');
|
|
23
|
+
const parts = cleaned
|
|
24
|
+
.split(',')
|
|
25
|
+
.map((s) => s.trim())
|
|
26
|
+
.filter(Boolean);
|
|
27
|
+
if (parts.length === 0 || !expandCarrierList(parts))
|
|
28
|
+
break;
|
|
29
|
+
carriers.push(...parts);
|
|
30
|
+
consumed += 1;
|
|
31
|
+
}
|
|
32
|
+
if (carriers.length === 0)
|
|
33
|
+
return undefined;
|
|
34
|
+
const name = tokens.slice(consumed).join(' ').trim();
|
|
35
|
+
return { carriers, ...(name ? { name } : {}) };
|
|
36
|
+
}
|
|
37
|
+
export function parseSmartCommentLine(line) {
|
|
38
|
+
const trimmed = line.trim();
|
|
39
|
+
const expectOut = /^;?\s*expects\s+out\s+(.+)$/i.exec(trimmed);
|
|
40
|
+
if (expectOut) {
|
|
41
|
+
const payload = parseCarrierPayload(expectOut[1]?.trim());
|
|
42
|
+
if (!payload)
|
|
43
|
+
return undefined;
|
|
44
|
+
const carriers = expandCarrierList(payload.carriers);
|
|
45
|
+
if (!carriers || carriers.length === 0)
|
|
46
|
+
return undefined;
|
|
47
|
+
return { kind: 'expectOut', carriers, ...(payload.name ? { name: payload.name } : {}) };
|
|
48
|
+
}
|
|
49
|
+
const match = COMPACT_SOURCE_TAG_RE.exec(trimmed);
|
|
50
|
+
if (!match)
|
|
51
|
+
return undefined;
|
|
52
|
+
const tag = match[1].toLowerCase();
|
|
53
|
+
const rest = match[2]?.trim();
|
|
54
|
+
const payload = parseCarrierPayload(rest);
|
|
55
|
+
if (!payload)
|
|
56
|
+
return undefined;
|
|
57
|
+
const carriers = expandCarrierList(payload.carriers);
|
|
58
|
+
if (!carriers || carriers.length === 0)
|
|
59
|
+
return undefined;
|
|
60
|
+
if (tag === 'in')
|
|
61
|
+
return { kind: 'in', carriers, ...(payload.name ? { name: payload.name } : {}) };
|
|
62
|
+
if (tag === 'out')
|
|
63
|
+
return { kind: 'out', carriers, ...(payload.name ? { name: payload.name } : {}) };
|
|
64
|
+
if (tag === 'clobbers')
|
|
65
|
+
return { kind: 'clobbers', carriers };
|
|
66
|
+
if (tag === 'preserves')
|
|
67
|
+
return { kind: 'preserves', carriers };
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
function parseInterfaceContractLine(line) {
|
|
71
|
+
const trimmed = line.trim();
|
|
72
|
+
if (trimmed.length === 0 || trimmed.startsWith(';'))
|
|
73
|
+
return undefined;
|
|
74
|
+
const extern = /^extern\s+(\S+)\s*$/i.exec(trimmed);
|
|
75
|
+
if (extern)
|
|
76
|
+
return { kind: 'extern', name: extern[1] };
|
|
77
|
+
if (/^end\s*$/i.test(trimmed))
|
|
78
|
+
return { kind: 'end' };
|
|
79
|
+
const match = INTERFACE_TAG_RE.exec(trimmed);
|
|
80
|
+
if (!match)
|
|
81
|
+
return undefined;
|
|
82
|
+
const tag = match[1].toLowerCase();
|
|
83
|
+
const rest = match[2]?.trim();
|
|
84
|
+
if (!rest)
|
|
85
|
+
return undefined;
|
|
86
|
+
const rawCarriers = rest.split(',').map((part) => part.trim());
|
|
87
|
+
if (rawCarriers.length === 0 || rawCarriers.some((part) => part.length === 0)) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
const carriers = expandCarrierList(rawCarriers);
|
|
91
|
+
if (!carriers || carriers.length === 0)
|
|
92
|
+
return undefined;
|
|
93
|
+
if (tag === 'in')
|
|
94
|
+
return { kind: 'in', carriers };
|
|
95
|
+
if (tag === 'out')
|
|
96
|
+
return { kind: 'out', carriers };
|
|
97
|
+
if (tag === 'clobbers')
|
|
98
|
+
return { kind: 'clobbers', carriers };
|
|
99
|
+
if (tag === 'preserves')
|
|
100
|
+
return { kind: 'preserves', carriers };
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
function isCompactSourceContractLine(line) {
|
|
104
|
+
return COMPACT_SOURCE_LINE_RE.test(line);
|
|
105
|
+
}
|
|
106
|
+
export function parseSmartComments(sourceLineComments) {
|
|
107
|
+
const out = [];
|
|
108
|
+
for (const [file, comments] of sourceLineComments) {
|
|
109
|
+
for (const [line, text] of comments) {
|
|
110
|
+
const parsed = parseSmartCommentLine(`;${text}`);
|
|
111
|
+
if (parsed)
|
|
112
|
+
out.push({ file, line, comment: parsed });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return out.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
|
|
116
|
+
}
|
|
117
|
+
function appendUnique(out, items) {
|
|
118
|
+
for (const item of items) {
|
|
119
|
+
if (!out.includes(item))
|
|
120
|
+
out.push(item);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function applyContractComment(contract, comment) {
|
|
124
|
+
if (comment.kind === 'in')
|
|
125
|
+
appendUnique(contract.in, comment.carriers);
|
|
126
|
+
if (comment.kind === 'out')
|
|
127
|
+
appendUnique(contract.out, comment.carriers);
|
|
128
|
+
if (comment.kind === 'clobbers')
|
|
129
|
+
appendUnique(contract.clobbers, comment.carriers);
|
|
130
|
+
if (comment.kind === 'preserves')
|
|
131
|
+
appendUnique(contract.preserves, comment.carriers);
|
|
132
|
+
}
|
|
133
|
+
function hasContractContent(contract) {
|
|
134
|
+
return (contract.in.length > 0 ||
|
|
135
|
+
contract.out.length > 0 ||
|
|
136
|
+
contract.clobbers.length > 0 ||
|
|
137
|
+
contract.preserves.length > 0);
|
|
138
|
+
}
|
|
139
|
+
function isCommentOnlyLine(line) {
|
|
140
|
+
return /^\s*;/.test(line);
|
|
141
|
+
}
|
|
142
|
+
function collectPrecedingCommentBlock(routine, sourceTexts) {
|
|
143
|
+
const source = sourceTexts.get(routine.span.file);
|
|
144
|
+
if (!source)
|
|
145
|
+
return { comments: [], complete: false };
|
|
146
|
+
const lines = source.split(/\r?\n/);
|
|
147
|
+
const rawBlock = [];
|
|
148
|
+
for (let index = routine.span.start.line - 2; index >= 0; index -= 1) {
|
|
149
|
+
const raw = lines[index] ?? '';
|
|
150
|
+
if (!isCommentOnlyLine(raw))
|
|
151
|
+
break;
|
|
152
|
+
rawBlock.push({ line: index + 1, text: raw });
|
|
153
|
+
}
|
|
154
|
+
rawBlock.reverse();
|
|
155
|
+
let compactStart = rawBlock.length;
|
|
156
|
+
while (compactStart > 0 && isCompactSourceContractLine(rawBlock[compactStart - 1]?.text ?? '')) {
|
|
157
|
+
compactStart -= 1;
|
|
158
|
+
}
|
|
159
|
+
if (compactStart < rawBlock.length) {
|
|
160
|
+
return {
|
|
161
|
+
complete: true,
|
|
162
|
+
comments: rawBlock.slice(compactStart).flatMap((item) => {
|
|
163
|
+
const parsed = parseSmartCommentLine(item.text);
|
|
164
|
+
return parsed ? [{ file: routine.span.file, line: item.line, comment: parsed }] : [];
|
|
165
|
+
}),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
complete: false,
|
|
170
|
+
comments: rawBlock.flatMap((item) => {
|
|
171
|
+
const parsed = parseSmartCommentLine(item.text);
|
|
172
|
+
return parsed ? [{ file: routine.span.file, line: item.line, comment: parsed }] : [];
|
|
173
|
+
}),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function buildImplicitRoutineContracts(routines, sourceTexts) {
|
|
177
|
+
const contracts = new Map();
|
|
178
|
+
for (const routine of routines) {
|
|
179
|
+
const docBlock = collectPrecedingCommentBlock(routine, sourceTexts);
|
|
180
|
+
if (docBlock.comments.some((item) => item.comment.kind === 'extern' || item.comment.kind === 'end')) {
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const contract = {
|
|
184
|
+
name: routine.name,
|
|
185
|
+
in: [],
|
|
186
|
+
out: [],
|
|
187
|
+
clobbers: [],
|
|
188
|
+
preserves: [],
|
|
189
|
+
...(docBlock.complete ? { complete: true } : {}),
|
|
190
|
+
};
|
|
191
|
+
for (const item of docBlock.comments) {
|
|
192
|
+
applyContractComment(contract, item.comment);
|
|
193
|
+
}
|
|
194
|
+
if (hasContractContent(contract))
|
|
195
|
+
contracts.set(routine.name, contract);
|
|
196
|
+
}
|
|
197
|
+
return contracts;
|
|
198
|
+
}
|
|
199
|
+
export function buildRoutineContracts(comments, routines = [], sourceTexts = new Map()) {
|
|
200
|
+
const contracts = new Map();
|
|
201
|
+
let current;
|
|
202
|
+
for (const item of comments) {
|
|
203
|
+
const comment = item.comment;
|
|
204
|
+
if (comment.kind === 'extern') {
|
|
205
|
+
current = {
|
|
206
|
+
name: comment.name,
|
|
207
|
+
in: [],
|
|
208
|
+
out: [],
|
|
209
|
+
clobbers: [],
|
|
210
|
+
preserves: [],
|
|
211
|
+
complete: true,
|
|
212
|
+
};
|
|
213
|
+
contracts.set(comment.name, current);
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
if (comment.kind === 'end') {
|
|
217
|
+
current = undefined;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (!current)
|
|
221
|
+
continue;
|
|
222
|
+
applyContractComment(current, comment);
|
|
223
|
+
}
|
|
224
|
+
for (const [name, contract] of buildImplicitRoutineContracts(routines, sourceTexts)) {
|
|
225
|
+
if (!contracts.has(name))
|
|
226
|
+
contracts.set(name, contract);
|
|
227
|
+
}
|
|
228
|
+
return contracts;
|
|
229
|
+
}
|
|
230
|
+
export function parseInterfaceContracts(text, file = '<register-care-interface>') {
|
|
231
|
+
const comments = [];
|
|
232
|
+
const lines = text.split(/\r?\n/);
|
|
233
|
+
lines.forEach((line, index) => {
|
|
234
|
+
const trimmed = line.trim();
|
|
235
|
+
if (trimmed.length === 0)
|
|
236
|
+
return;
|
|
237
|
+
if (trimmed.startsWith(';')) {
|
|
238
|
+
throw new Error(`${file}:${index + 1}: .asmi files do not permit comments`);
|
|
239
|
+
}
|
|
240
|
+
const comment = parseInterfaceContractLine(line);
|
|
241
|
+
if (!comment) {
|
|
242
|
+
throw new Error(`${file}:${index + 1}: invalid register-care interface line "${trimmed}"`);
|
|
243
|
+
}
|
|
244
|
+
comments.push({ file, line: index + 1, comment });
|
|
245
|
+
});
|
|
246
|
+
return buildRoutineContracts(comments);
|
|
247
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type SourceLines = {
|
|
2
|
+
lines: string[];
|
|
3
|
+
trailingNewline: boolean;
|
|
4
|
+
eol: '\n' | '\r\n';
|
|
5
|
+
};
|
|
6
|
+
export declare function splitSourceLines(text: string): SourceLines;
|
|
7
|
+
export declare function joinSourceLines({ lines, trailingNewline, eol }: SourceLines): string;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
function lineEnding(text) {
|
|
2
|
+
return text.includes('\r\n') ? '\r\n' : '\n';
|
|
3
|
+
}
|
|
4
|
+
export function splitSourceLines(text) {
|
|
5
|
+
const eol = lineEnding(text);
|
|
6
|
+
const trailingNewline = text.endsWith('\n');
|
|
7
|
+
const lines = text.split(/\r?\n/);
|
|
8
|
+
if (trailingNewline)
|
|
9
|
+
lines.pop();
|
|
10
|
+
return { lines, trailingNewline, eol };
|
|
11
|
+
}
|
|
12
|
+
export function joinSourceLines({ lines, trailingNewline, eol }) {
|
|
13
|
+
const text = lines.join(eol);
|
|
14
|
+
return trailingNewline ? `${text}${eol}` : text;
|
|
15
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { RegisterCareRoutine, RoutineContract, RoutineSummary } from './types.js';
|
|
2
|
+
export declare function inferRoutineSummary(routine: RegisterCareRoutine, boundarySummaries?: ReadonlyMap<string, RoutineSummary>): RoutineSummary;
|
|
3
|
+
export declare function applyRoutineContract(summary: RoutineSummary, contract: RoutineContract): RoutineSummary;
|