@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,226 @@
|
|
|
1
|
+
import { DiagnosticIds } from '../diagnosticTypes.js';
|
|
2
|
+
function scalarSize(name) {
|
|
3
|
+
switch (name) {
|
|
4
|
+
case 'byte':
|
|
5
|
+
return 1;
|
|
6
|
+
case 'word':
|
|
7
|
+
case 'addr':
|
|
8
|
+
return 2;
|
|
9
|
+
default:
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function reportTypeError(diagnostics) {
|
|
14
|
+
return (file, message) => {
|
|
15
|
+
diagnostics?.push({ id: DiagnosticIds.TypeError, severity: 'error', message, file });
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function reportMissingArrayLength(te, diag) {
|
|
19
|
+
diag(te.span.file, `Array length is required here; write T[N].`);
|
|
20
|
+
}
|
|
21
|
+
function resolveNamedType(te, env, visiting, diag, onResolve) {
|
|
22
|
+
const s = scalarSize(te.name);
|
|
23
|
+
if (s !== undefined)
|
|
24
|
+
return onResolve({ kind: 'Scalar', name: te.name, size: s });
|
|
25
|
+
if (visiting.has(te.name)) {
|
|
26
|
+
diag(te.span.file, `Recursive type definition detected for "${te.name}".`);
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
const decl = env.types.get(te.name);
|
|
30
|
+
if (!decl) {
|
|
31
|
+
diag(te.span.file, `Unknown type "${te.name}".`);
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
visiting.add(te.name);
|
|
35
|
+
try {
|
|
36
|
+
return onResolve({ kind: 'Decl', decl });
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
visiting.delete(te.name);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function sumFieldSizes(fields, resolveTypeExpr) {
|
|
43
|
+
let sum = 0;
|
|
44
|
+
for (const f of fields) {
|
|
45
|
+
const fs = resolveTypeExpr(f.typeExpr);
|
|
46
|
+
if (!fs)
|
|
47
|
+
return undefined;
|
|
48
|
+
sum += fs.size;
|
|
49
|
+
}
|
|
50
|
+
return { size: sum };
|
|
51
|
+
}
|
|
52
|
+
function maxFieldSize(fields, resolveTypeExpr) {
|
|
53
|
+
let maxLayout = 0;
|
|
54
|
+
for (const f of fields) {
|
|
55
|
+
const fs = resolveTypeExpr(f.typeExpr);
|
|
56
|
+
if (!fs)
|
|
57
|
+
return undefined;
|
|
58
|
+
if (fs.size > maxLayout)
|
|
59
|
+
maxLayout = fs.size;
|
|
60
|
+
}
|
|
61
|
+
return { size: maxLayout };
|
|
62
|
+
}
|
|
63
|
+
function typeLayoutInfoForDecl(decl, resolveTypeExpr) {
|
|
64
|
+
if (decl.kind === 'UnionDecl') {
|
|
65
|
+
return maxFieldSize(decl.fields, resolveTypeExpr);
|
|
66
|
+
}
|
|
67
|
+
const te = decl.typeExpr;
|
|
68
|
+
if (te.kind === 'RecordType') {
|
|
69
|
+
return sumFieldSizes(te.fields, resolveTypeExpr);
|
|
70
|
+
}
|
|
71
|
+
return resolveTypeExpr(te);
|
|
72
|
+
}
|
|
73
|
+
export function layoutInfoForTypeExpr(typeExpr, env, diagnostics) {
|
|
74
|
+
const visiting = new Set();
|
|
75
|
+
const memo = new Map();
|
|
76
|
+
const diag = reportTypeError(diagnostics);
|
|
77
|
+
const sizeOf = (te) => {
|
|
78
|
+
switch (te.kind) {
|
|
79
|
+
case 'TypeName': {
|
|
80
|
+
const cached = memo.get(te.name);
|
|
81
|
+
if (cached !== undefined)
|
|
82
|
+
return cached;
|
|
83
|
+
return resolveNamedType(te, env, visiting, diag, (resolved) => {
|
|
84
|
+
if (resolved.kind === 'Scalar') {
|
|
85
|
+
return { size: resolved.size };
|
|
86
|
+
}
|
|
87
|
+
const info = typeLayoutInfoForDecl(resolved.decl, sizeOf);
|
|
88
|
+
if (info)
|
|
89
|
+
memo.set(te.name, info);
|
|
90
|
+
return info;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
case 'ArrayType': {
|
|
94
|
+
const es = sizeOf(te.element);
|
|
95
|
+
if (!es)
|
|
96
|
+
return undefined;
|
|
97
|
+
if (te.length === undefined) {
|
|
98
|
+
reportMissingArrayLength(te, diag);
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return { size: es.size * te.length };
|
|
102
|
+
}
|
|
103
|
+
case 'RecordType':
|
|
104
|
+
return sumFieldSizes(te.fields, sizeOf);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
return sizeOf(typeExpr);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Compute the exact semantic size in bytes of a type expression.
|
|
111
|
+
*/
|
|
112
|
+
export function sizeOfTypeExpr(typeExpr, env, diagnostics) {
|
|
113
|
+
const info = layoutInfoForTypeExpr(typeExpr, env, diagnostics);
|
|
114
|
+
return info?.size;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Compute the byte offset of a field path inside a type expression.
|
|
118
|
+
*
|
|
119
|
+
* Rules:
|
|
120
|
+
* - Record fields contribute exact sizes of preceding fields.
|
|
121
|
+
* - Union field offsets are always 0.
|
|
122
|
+
* - Array indices must be compile-time constants and contribute index * element exact size.
|
|
123
|
+
*/
|
|
124
|
+
export function offsetPathInTypeExpr(typeExpr, path, env, evalImm, diagnostics) {
|
|
125
|
+
const diag = reportTypeError(diagnostics);
|
|
126
|
+
const resolveType = (te, visiting = new Set()) => {
|
|
127
|
+
switch (te.kind) {
|
|
128
|
+
case 'TypeName':
|
|
129
|
+
return resolveNamedType(te, env, visiting, diag, (resolved) => {
|
|
130
|
+
if (resolved.kind === 'Scalar')
|
|
131
|
+
return { kind: 'Scalar', name: resolved.name };
|
|
132
|
+
if (resolved.decl.kind === 'UnionDecl')
|
|
133
|
+
return { kind: 'Union', fields: resolved.decl.fields };
|
|
134
|
+
return resolveType(resolved.decl.typeExpr, visiting);
|
|
135
|
+
});
|
|
136
|
+
case 'ArrayType': {
|
|
137
|
+
if (te.length === undefined) {
|
|
138
|
+
reportMissingArrayLength(te, diag);
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
return { kind: 'Array', element: te.element, length: te.length };
|
|
142
|
+
}
|
|
143
|
+
case 'RecordType':
|
|
144
|
+
return { kind: 'Record', fields: te.fields };
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
const findField = (fields, fieldName, file) => {
|
|
148
|
+
let offsetBefore = 0;
|
|
149
|
+
for (const f of fields) {
|
|
150
|
+
if (f.name === fieldName)
|
|
151
|
+
return { field: f, offsetBefore };
|
|
152
|
+
const fs = sizeOfTypeExpr(f.typeExpr, env, diagnostics);
|
|
153
|
+
if (fs === undefined)
|
|
154
|
+
return undefined;
|
|
155
|
+
offsetBefore += fs;
|
|
156
|
+
}
|
|
157
|
+
diag(file, `Unknown field "${fieldName}".`);
|
|
158
|
+
return undefined;
|
|
159
|
+
};
|
|
160
|
+
const initial = resolveType(typeExpr);
|
|
161
|
+
if (!initial)
|
|
162
|
+
return undefined;
|
|
163
|
+
let cur = initial;
|
|
164
|
+
let total = 0;
|
|
165
|
+
const file = path.span.file;
|
|
166
|
+
const selectField = (name) => {
|
|
167
|
+
if (cur.kind === 'Record') {
|
|
168
|
+
const found = findField(cur.fields, name, file);
|
|
169
|
+
if (!found)
|
|
170
|
+
return false;
|
|
171
|
+
total += found.offsetBefore;
|
|
172
|
+
const next = resolveType(found.field.typeExpr);
|
|
173
|
+
if (!next)
|
|
174
|
+
return false;
|
|
175
|
+
cur = next;
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
if (cur.kind === 'Union') {
|
|
179
|
+
const found = findField(cur.fields, name, file);
|
|
180
|
+
if (!found)
|
|
181
|
+
return false;
|
|
182
|
+
const next = resolveType(found.field.typeExpr);
|
|
183
|
+
if (!next)
|
|
184
|
+
return false;
|
|
185
|
+
cur = next;
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
diag(file, `Cannot select field "${name}" from non-record/union type.`);
|
|
189
|
+
return false;
|
|
190
|
+
};
|
|
191
|
+
if (path.base !== undefined && !selectField(path.base))
|
|
192
|
+
return undefined;
|
|
193
|
+
for (const step of path.steps) {
|
|
194
|
+
if (step.kind === 'OffsetField') {
|
|
195
|
+
if (!selectField(step.name))
|
|
196
|
+
return undefined;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (cur.kind !== 'Array') {
|
|
200
|
+
diag(file, `Cannot index into non-array type in offset path.`);
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
const idx = evalImm(step.expr);
|
|
204
|
+
if (idx === undefined) {
|
|
205
|
+
diag(file, `Failed to evaluate offset index expression.`);
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
if (!Number.isInteger(idx)) {
|
|
209
|
+
diag(file, `offset index must evaluate to an integer.`);
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
if (idx < 0 || idx >= cur.length) {
|
|
213
|
+
diag(file, `offset index ${idx} out of bounds for length ${cur.length}.`);
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
const elemSize = sizeOfTypeExpr(cur.element, env, diagnostics);
|
|
217
|
+
if (elemSize === undefined)
|
|
218
|
+
return undefined;
|
|
219
|
+
total += idx * elemSize;
|
|
220
|
+
const next = resolveType(cur.element);
|
|
221
|
+
if (!next)
|
|
222
|
+
return undefined;
|
|
223
|
+
cur = next;
|
|
224
|
+
}
|
|
225
|
+
return total;
|
|
226
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Diagnostic } from '../diagnosticTypes.js';
|
|
2
|
+
import type { EaExprNode, ImmExprNode, SourceSpan } from '../frontend/ast.js';
|
|
3
|
+
import type { CompileEnv } from './env.js';
|
|
4
|
+
type LayoutCastAbsFold = {
|
|
5
|
+
baseLower: string;
|
|
6
|
+
addend: number;
|
|
7
|
+
};
|
|
8
|
+
export declare function isConstantLayoutCastEa(ea: EaExprNode): boolean;
|
|
9
|
+
/** Label or label +/- imm base for `<Type>base[path]`. */
|
|
10
|
+
export declare function isLayoutCastLabelBase(ea: EaExprNode): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Fold `<Type>label[path]` to a label fixup addend when the cast uses a label base
|
|
13
|
+
* and compile-time indexes only.
|
|
14
|
+
*/
|
|
15
|
+
export declare function foldLayoutCastAbsEa(ea: EaExprNode, params: {
|
|
16
|
+
env: CompileEnv;
|
|
17
|
+
evalImm: (expr: ImmExprNode) => number | undefined;
|
|
18
|
+
resolveAbsBase: (baseEa: EaExprNode) => LayoutCastAbsFold | undefined;
|
|
19
|
+
diagnostics?: Diagnostic[];
|
|
20
|
+
}): LayoutCastAbsFold | undefined;
|
|
21
|
+
export declare function diagLayoutCastRuntimeIndex(diagnostics: Diagnostic[], span: SourceSpan, ea: EaExprNode): boolean;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { DiagnosticIds } from '../diagnosticTypes.js';
|
|
2
|
+
import { ALL_REGISTER_NAMES } from '../frontend/grammarData.js';
|
|
3
|
+
import { offsetPathInTypeExpr } from './layout.js';
|
|
4
|
+
function containsLayoutCast(ea) {
|
|
5
|
+
switch (ea.kind) {
|
|
6
|
+
case 'EaName':
|
|
7
|
+
case 'EaImm':
|
|
8
|
+
return false;
|
|
9
|
+
case 'EaLayoutCast':
|
|
10
|
+
return true;
|
|
11
|
+
case 'EaField':
|
|
12
|
+
case 'EaAdd':
|
|
13
|
+
case 'EaSub':
|
|
14
|
+
case 'EaIndex':
|
|
15
|
+
return containsLayoutCast(ea.base);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function hasRuntimeIndexInLayoutCast(ea) {
|
|
19
|
+
switch (ea.kind) {
|
|
20
|
+
case 'EaName':
|
|
21
|
+
case 'EaImm':
|
|
22
|
+
return false;
|
|
23
|
+
case 'EaLayoutCast':
|
|
24
|
+
return hasRuntimeIndexInLayoutCast(ea.base);
|
|
25
|
+
case 'EaField':
|
|
26
|
+
case 'EaAdd':
|
|
27
|
+
case 'EaSub':
|
|
28
|
+
return hasRuntimeIndexInLayoutCast(ea.base);
|
|
29
|
+
case 'EaIndex':
|
|
30
|
+
if (containsLayoutCast(ea.base) &&
|
|
31
|
+
(ea.index.kind === 'IndexReg8' ||
|
|
32
|
+
ea.index.kind === 'IndexReg16' ||
|
|
33
|
+
ea.index.kind === 'IndexMemHL' ||
|
|
34
|
+
ea.index.kind === 'IndexMemIxIy' ||
|
|
35
|
+
ea.index.kind === 'IndexEa')) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return hasRuntimeIndexInLayoutCast(ea.base);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function isConstantLayoutCastEa(ea) {
|
|
42
|
+
return containsLayoutCast(ea) && !hasRuntimeIndexInLayoutCast(ea);
|
|
43
|
+
}
|
|
44
|
+
/** Label or label +/- imm base for `<Type>base[path]`. */
|
|
45
|
+
export function isLayoutCastLabelBase(ea) {
|
|
46
|
+
switch (ea.kind) {
|
|
47
|
+
case 'EaName': {
|
|
48
|
+
if (ALL_REGISTER_NAMES.has(ea.name.toUpperCase()))
|
|
49
|
+
return false;
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
case 'EaAdd':
|
|
53
|
+
case 'EaSub':
|
|
54
|
+
return isLayoutCastLabelBase(ea.base);
|
|
55
|
+
default:
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function decomposeLayoutCastEa(ea) {
|
|
60
|
+
const steps = [];
|
|
61
|
+
let cur = ea;
|
|
62
|
+
while (cur.kind === 'EaField' || cur.kind === 'EaIndex') {
|
|
63
|
+
if (cur.kind === 'EaField') {
|
|
64
|
+
steps.unshift({ kind: 'OffsetField', span: cur.span, name: cur.field });
|
|
65
|
+
cur = cur.base;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (cur.index.kind !== 'IndexImm')
|
|
69
|
+
return undefined;
|
|
70
|
+
steps.unshift({ kind: 'OffsetIndex', span: cur.span, expr: cur.index.value });
|
|
71
|
+
cur = cur.base;
|
|
72
|
+
}
|
|
73
|
+
if (cur.kind !== 'EaLayoutCast')
|
|
74
|
+
return undefined;
|
|
75
|
+
return {
|
|
76
|
+
cast: cur,
|
|
77
|
+
path: { kind: 'OffsetPath', span: cur.span, steps },
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Fold `<Type>label[path]` to a label fixup addend when the cast uses a label base
|
|
82
|
+
* and compile-time indexes only.
|
|
83
|
+
*/
|
|
84
|
+
export function foldLayoutCastAbsEa(ea, params) {
|
|
85
|
+
if (!isConstantLayoutCastEa(ea))
|
|
86
|
+
return undefined;
|
|
87
|
+
const decomposed = decomposeLayoutCastEa(ea);
|
|
88
|
+
if (!decomposed)
|
|
89
|
+
return undefined;
|
|
90
|
+
if (!isLayoutCastLabelBase(decomposed.cast.base))
|
|
91
|
+
return undefined;
|
|
92
|
+
const pathOffset = offsetPathInTypeExpr(decomposed.cast.typeExpr, decomposed.path, params.env, params.evalImm, params.diagnostics);
|
|
93
|
+
if (pathOffset === undefined)
|
|
94
|
+
return undefined;
|
|
95
|
+
const base = params.resolveAbsBase(decomposed.cast.base);
|
|
96
|
+
if (!base)
|
|
97
|
+
return undefined;
|
|
98
|
+
return {
|
|
99
|
+
baseLower: base.baseLower,
|
|
100
|
+
addend: (base.addend + pathOffset) & 0xffff,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function layoutCastRuntimeIndexMessage() {
|
|
104
|
+
return `Layout-cast address expressions require compile-time constant indexes; runtime index registers require explicit address arithmetic with sizeof/offset constants.`;
|
|
105
|
+
}
|
|
106
|
+
export function diagLayoutCastRuntimeIndex(diagnostics, span, ea) {
|
|
107
|
+
if (!hasRuntimeIndexInLayoutCast(ea))
|
|
108
|
+
return false;
|
|
109
|
+
diagnostics.push({
|
|
110
|
+
id: DiagnosticIds.TypeError,
|
|
111
|
+
severity: 'error',
|
|
112
|
+
message: layoutCastRuntimeIndexMessage(),
|
|
113
|
+
file: span.file,
|
|
114
|
+
line: span.start.line,
|
|
115
|
+
column: span.start.column,
|
|
116
|
+
});
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* typeQueries.ts — semantic-layer type-resolution helpers.
|
|
3
|
+
*
|
|
4
|
+
* These helpers are purely semantic: they walk type expressions and EA paths
|
|
5
|
+
* using the compile environment and explicit layout casts, but they know
|
|
6
|
+
* nothing about placed/lowered program state or code-generation concerns.
|
|
7
|
+
*
|
|
8
|
+
* The lowering layer imports these helpers directly.
|
|
9
|
+
*/
|
|
10
|
+
import type { EaExprNode, RecordFieldNode, TypeExprNode } from '../frontend/ast.js';
|
|
11
|
+
import type { CompileEnv } from './env.js';
|
|
12
|
+
export type ScalarKind = 'byte' | 'word' | 'addr';
|
|
13
|
+
export type AggregateType = {
|
|
14
|
+
kind: 'record' | 'union';
|
|
15
|
+
fields: RecordFieldNode[];
|
|
16
|
+
};
|
|
17
|
+
type TypeResolutionContext = {
|
|
18
|
+
env: CompileEnv;
|
|
19
|
+
};
|
|
20
|
+
export declare function createTypeResolutionHelpers(ctx: TypeResolutionContext): {
|
|
21
|
+
resolveScalarKind: (typeExpr: TypeExprNode) => ScalarKind | undefined;
|
|
22
|
+
resolveAggregateType: (te: TypeExprNode) => AggregateType | undefined;
|
|
23
|
+
resolveArrayType: (te: TypeExprNode) => {
|
|
24
|
+
element: TypeExprNode;
|
|
25
|
+
length?: number;
|
|
26
|
+
} | undefined;
|
|
27
|
+
resolveEaTypeExpr: (ea: EaExprNode) => TypeExprNode | undefined;
|
|
28
|
+
sameTypeShape: (left: TypeExprNode, right: TypeExprNode) => boolean;
|
|
29
|
+
typeDisplay: (te: TypeExprNode) => string;
|
|
30
|
+
};
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* typeQueries.ts — semantic-layer type-resolution helpers.
|
|
3
|
+
*
|
|
4
|
+
* These helpers are purely semantic: they walk type expressions and EA paths
|
|
5
|
+
* using the compile environment and explicit layout casts, but they know
|
|
6
|
+
* nothing about placed/lowered program state or code-generation concerns.
|
|
7
|
+
*
|
|
8
|
+
* The lowering layer imports these helpers directly.
|
|
9
|
+
*/
|
|
10
|
+
export function createTypeResolutionHelpers(ctx) {
|
|
11
|
+
const resolveScalarKind = (typeExpr) => {
|
|
12
|
+
if (typeExpr.kind !== 'TypeName')
|
|
13
|
+
return undefined;
|
|
14
|
+
const lower = typeExpr.name.toLowerCase();
|
|
15
|
+
if (lower === 'byte' || lower === 'word' || lower === 'addr')
|
|
16
|
+
return lower;
|
|
17
|
+
return undefined;
|
|
18
|
+
};
|
|
19
|
+
const resolveAggregateType = (te) => {
|
|
20
|
+
if (te.kind === 'RecordType')
|
|
21
|
+
return { kind: 'record', fields: te.fields };
|
|
22
|
+
if (te.kind === 'TypeName') {
|
|
23
|
+
const decl = ctx.env.types.get(te.name);
|
|
24
|
+
if (!decl)
|
|
25
|
+
return undefined;
|
|
26
|
+
if (decl.kind === 'UnionDecl')
|
|
27
|
+
return { kind: 'union', fields: decl.fields };
|
|
28
|
+
if (decl.typeExpr.kind === 'RecordType') {
|
|
29
|
+
return { kind: 'record', fields: decl.typeExpr.fields };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return undefined;
|
|
33
|
+
};
|
|
34
|
+
const resolveArrayType = (te) => {
|
|
35
|
+
if (te.kind !== 'ArrayType')
|
|
36
|
+
return undefined;
|
|
37
|
+
return te.length === undefined
|
|
38
|
+
? { element: te.element }
|
|
39
|
+
: { element: te.element, length: te.length };
|
|
40
|
+
};
|
|
41
|
+
const typeDisplay = (te) => {
|
|
42
|
+
const render = (x) => {
|
|
43
|
+
if (x.kind === 'TypeName')
|
|
44
|
+
return x.name;
|
|
45
|
+
if (x.kind === 'ArrayType') {
|
|
46
|
+
const inner = render(x.element);
|
|
47
|
+
return `${inner}[${x.length === undefined ? '' : x.length}]`;
|
|
48
|
+
}
|
|
49
|
+
if (x.kind === 'RecordType') {
|
|
50
|
+
return `record{${x.fields.map((f) => `${f.name}:${render(f.typeExpr)}`).join(',')}}`;
|
|
51
|
+
}
|
|
52
|
+
return 'type';
|
|
53
|
+
};
|
|
54
|
+
return render(te);
|
|
55
|
+
};
|
|
56
|
+
const sameTypeShape = (left, right) => {
|
|
57
|
+
if (left.kind !== right.kind)
|
|
58
|
+
return false;
|
|
59
|
+
switch (left.kind) {
|
|
60
|
+
case 'TypeName':
|
|
61
|
+
return right.kind === 'TypeName' && left.name.toLowerCase() === right.name.toLowerCase();
|
|
62
|
+
case 'ArrayType':
|
|
63
|
+
if (right.kind !== 'ArrayType')
|
|
64
|
+
return false;
|
|
65
|
+
if (left.length !== right.length)
|
|
66
|
+
return false;
|
|
67
|
+
return sameTypeShape(left.element, right.element);
|
|
68
|
+
case 'RecordType':
|
|
69
|
+
if (right.kind !== 'RecordType')
|
|
70
|
+
return false;
|
|
71
|
+
if (left.fields.length !== right.fields.length)
|
|
72
|
+
return false;
|
|
73
|
+
for (let i = 0; i < left.fields.length; i++) {
|
|
74
|
+
const lf = left.fields[i];
|
|
75
|
+
const rf = right.fields[i];
|
|
76
|
+
if (lf.name !== rf.name || !sameTypeShape(lf.typeExpr, rf.typeExpr))
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const resolveEaTypeExprInternal = (ea) => {
|
|
83
|
+
switch (ea.kind) {
|
|
84
|
+
case 'EaName':
|
|
85
|
+
return undefined;
|
|
86
|
+
case 'EaAdd':
|
|
87
|
+
case 'EaSub':
|
|
88
|
+
return resolveEaTypeExprInternal(ea.base);
|
|
89
|
+
case 'EaLayoutCast': {
|
|
90
|
+
return ea.typeExpr;
|
|
91
|
+
}
|
|
92
|
+
case 'EaField': {
|
|
93
|
+
const baseType = resolveEaTypeExprInternal(ea.base);
|
|
94
|
+
if (!baseType)
|
|
95
|
+
return undefined;
|
|
96
|
+
const agg = resolveAggregateType(baseType);
|
|
97
|
+
if (!agg)
|
|
98
|
+
return undefined;
|
|
99
|
+
for (const f of agg.fields) {
|
|
100
|
+
if (f.name === ea.field)
|
|
101
|
+
return f.typeExpr;
|
|
102
|
+
}
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
case 'EaIndex': {
|
|
106
|
+
const baseType = resolveEaTypeExprInternal(ea.base);
|
|
107
|
+
if (!baseType)
|
|
108
|
+
return undefined;
|
|
109
|
+
return resolveArrayType(baseType)?.element;
|
|
110
|
+
}
|
|
111
|
+
case 'EaImm':
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const resolveEaTypeExpr = (ea) => resolveEaTypeExprInternal(ea);
|
|
116
|
+
return {
|
|
117
|
+
resolveScalarKind,
|
|
118
|
+
resolveAggregateType,
|
|
119
|
+
resolveArrayType,
|
|
120
|
+
resolveEaTypeExpr,
|
|
121
|
+
sameTypeShape,
|
|
122
|
+
typeDisplay,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Diagnostic } from './diagnosticTypes.js';
|
|
2
|
+
import type { DirectiveAliasPolicy } from './frontend/directiveAliases.js';
|
|
3
|
+
export type ExpandedSource = {
|
|
4
|
+
text: string;
|
|
5
|
+
lineFiles: string[];
|
|
6
|
+
lineBaseLines: number[];
|
|
7
|
+
};
|
|
8
|
+
export declare function expandTextIncludesForFile(args: {
|
|
9
|
+
sourcePath: string;
|
|
10
|
+
sourceText: string;
|
|
11
|
+
includeDirs: string[];
|
|
12
|
+
diagnostics: Diagnostic[];
|
|
13
|
+
sourceTexts: Map<string, string>;
|
|
14
|
+
includeStack: string[];
|
|
15
|
+
aliasPolicy?: DirectiveAliasPolicy;
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
}): Promise<ExpandedSource | undefined>;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { normalizePath } from './compileShared.js';
|
|
3
|
+
import { DiagnosticIds } from './diagnosticTypes.js';
|
|
4
|
+
import { resolveDirectiveAlias } from './frontend/directiveAliases.js';
|
|
5
|
+
import { stripLineComment } from './frontend/parseParserShared.js';
|
|
6
|
+
import { resolveIncludeCandidates } from './sourceIncludePaths.js';
|
|
7
|
+
const INCLUDE_DIRECTIVE_RE = /^\s*([.]?[A-Za-z][A-Za-z0-9_]*)\b\s+"([^"]+)"\s*$/i;
|
|
8
|
+
function throwIfAborted(signal) {
|
|
9
|
+
signal?.throwIfAborted();
|
|
10
|
+
}
|
|
11
|
+
function isIgnorableIncludeProbeError(err) {
|
|
12
|
+
if (!err || typeof err !== 'object')
|
|
13
|
+
return false;
|
|
14
|
+
const code = err.code;
|
|
15
|
+
return code === 'ENOENT' || code === 'ENOTDIR';
|
|
16
|
+
}
|
|
17
|
+
function includeDirectiveForLine(raw, aliasPolicy) {
|
|
18
|
+
const stripped = stripLineComment(raw).trim();
|
|
19
|
+
const match = INCLUDE_DIRECTIVE_RE.exec(stripped);
|
|
20
|
+
if (!match)
|
|
21
|
+
return undefined;
|
|
22
|
+
return resolveDirectiveAlias(match[1], aliasPolicy) === '.include' ? match[2] : undefined;
|
|
23
|
+
}
|
|
24
|
+
async function resolveIncludeSource(sourcePath, rawLine, spec, lineNo, includeDirs, diagnostics, sourceTexts, signal) {
|
|
25
|
+
const candidates = resolveIncludeCandidates(sourcePath, spec, includeDirs);
|
|
26
|
+
for (const c of candidates) {
|
|
27
|
+
throwIfAborted(signal);
|
|
28
|
+
try {
|
|
29
|
+
const resolvedText = await readFile(c, 'utf8');
|
|
30
|
+
const resolvedKey = normalizePath(c);
|
|
31
|
+
if (!sourceTexts.has(resolvedKey))
|
|
32
|
+
sourceTexts.set(resolvedKey, resolvedText);
|
|
33
|
+
return { resolved: c, resolvedText };
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
if (isIgnorableIncludeProbeError(err))
|
|
37
|
+
continue;
|
|
38
|
+
diagnostics.push({
|
|
39
|
+
id: DiagnosticIds.IoReadFailed,
|
|
40
|
+
severity: 'error',
|
|
41
|
+
message: `Failed to read include candidate "${c}" while resolving includes for "${sourcePath}": ${String(err)}`,
|
|
42
|
+
file: sourcePath,
|
|
43
|
+
line: lineNo,
|
|
44
|
+
column: rawLine.indexOf('include') + 1 || 1,
|
|
45
|
+
});
|
|
46
|
+
return 'hard-failure';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
diagnostics.push({
|
|
50
|
+
id: DiagnosticIds.IncludeNotFound,
|
|
51
|
+
severity: 'error',
|
|
52
|
+
message: `Failed to resolve include "${spec}" from "${sourcePath}". Tried:\n${candidates
|
|
53
|
+
.map((c) => `- ${c}`)
|
|
54
|
+
.join('\n')}`,
|
|
55
|
+
file: sourcePath,
|
|
56
|
+
line: lineNo,
|
|
57
|
+
column: rawLine.indexOf('include') + 1 || 1,
|
|
58
|
+
});
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
export async function expandTextIncludesForFile(args) {
|
|
62
|
+
const { sourcePath, sourceText, includeDirs, diagnostics, sourceTexts, includeStack, aliasPolicy, signal, } = args;
|
|
63
|
+
const sourceKey = normalizePath(sourcePath);
|
|
64
|
+
if (!sourceTexts.has(sourceKey))
|
|
65
|
+
sourceTexts.set(sourceKey, sourceText);
|
|
66
|
+
const lines = sourceText.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
|
|
67
|
+
const out = [];
|
|
68
|
+
const lineFiles = [];
|
|
69
|
+
const lineBaseLines = [];
|
|
70
|
+
for (let i = 0; i < lines.length; i++) {
|
|
71
|
+
throwIfAborted(signal);
|
|
72
|
+
const raw = lines[i] ?? '';
|
|
73
|
+
const lineNo = i + 1;
|
|
74
|
+
const spec = includeDirectiveForLine(raw, aliasPolicy);
|
|
75
|
+
if (!spec) {
|
|
76
|
+
out.push(raw);
|
|
77
|
+
lineFiles.push(sourcePath);
|
|
78
|
+
lineBaseLines.push(lineNo);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const resolvedInclude = await resolveIncludeSource(sourcePath, raw, spec, lineNo, includeDirs, diagnostics, sourceTexts, signal);
|
|
82
|
+
if (resolvedInclude === 'hard-failure')
|
|
83
|
+
return undefined;
|
|
84
|
+
if (!resolvedInclude) {
|
|
85
|
+
out.push(raw);
|
|
86
|
+
lineFiles.push(sourcePath);
|
|
87
|
+
lineBaseLines.push(lineNo);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (includeStack.includes(resolvedInclude.resolved)) {
|
|
91
|
+
diagnostics.push({
|
|
92
|
+
id: DiagnosticIds.SemanticsError,
|
|
93
|
+
severity: 'error',
|
|
94
|
+
message: `Include cycle detected: "${resolvedInclude.resolved}" is already active in the include stack.`,
|
|
95
|
+
file: sourcePath,
|
|
96
|
+
line: lineNo,
|
|
97
|
+
column: raw.indexOf('include') + 1 || 1,
|
|
98
|
+
});
|
|
99
|
+
out.push(raw);
|
|
100
|
+
lineFiles.push(sourcePath);
|
|
101
|
+
lineBaseLines.push(lineNo);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const expanded = await expandTextIncludesForFile({
|
|
105
|
+
sourcePath: resolvedInclude.resolved,
|
|
106
|
+
sourceText: resolvedInclude.resolvedText,
|
|
107
|
+
includeDirs,
|
|
108
|
+
diagnostics,
|
|
109
|
+
sourceTexts,
|
|
110
|
+
includeStack: [...includeStack, resolvedInclude.resolved],
|
|
111
|
+
...(aliasPolicy ? { aliasPolicy } : {}),
|
|
112
|
+
...(signal ? { signal } : {}),
|
|
113
|
+
});
|
|
114
|
+
if (expanded === undefined)
|
|
115
|
+
return undefined;
|
|
116
|
+
const expandedLines = expanded.text.split('\n');
|
|
117
|
+
for (let j = 0; j < expandedLines.length; j++) {
|
|
118
|
+
out.push(expandedLines[j]);
|
|
119
|
+
lineFiles.push(expanded.lineFiles[j] ?? resolvedInclude.resolved);
|
|
120
|
+
lineBaseLines.push(expanded.lineBaseLines[j] ?? j + 1);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { text: out.join('\n'), lineFiles, lineBaseLines };
|
|
124
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function resolveIncludeCandidates(fromSourcePath: string, specifier: string, includeDirs: string[]): string[];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { dirname, resolve } from 'node:path';
|
|
2
|
+
import { normalizePath } from './compileShared.js';
|
|
3
|
+
export function resolveIncludeCandidates(fromSourcePath, specifier, includeDirs) {
|
|
4
|
+
const fromDir = dirname(fromSourcePath);
|
|
5
|
+
const out = [];
|
|
6
|
+
out.push(normalizePath(resolve(fromDir, specifier)));
|
|
7
|
+
for (const inc of includeDirs) {
|
|
8
|
+
out.push(normalizePath(resolve(inc, specifier)));
|
|
9
|
+
}
|
|
10
|
+
const seen = new Set();
|
|
11
|
+
return out.filter((p) => (seen.has(p) ? false : (seen.add(p), true)));
|
|
12
|
+
}
|