Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | 2x 2x 2x 4x 4x 4x 4x 58x 13x 3x 35x 4x 2x 1x 64x 37x 36x 64x 56x 56x 58x 58x 58x 56x 56x 47x 40x 40x 56x 40x 8x 8x 32x 40x 40x | /**
* Switch Statement Generator (ADR-025)
*
* Generates C switch statements from C-Next switch syntax, expanding `||`
* labels into consecutive C case labels and guaranteeing a `default`.
*
* #1445 box 3: takes `IPlannedSwitch`, not the node. The six-way case-label
* discrimination is a question about which grammar alternative matched, so it
* is `TPlannedCaseLabel` and the planner answers it. What is left here is the
* rendering -- which label opens the brace, how deep each line indents, and
* Issue #855's Rule 16.4 default.
*
* Three exported functions went with the nodes. `generateSwitchCase`,
* `generateDefaultCase` and `generateCaseLabel` were `TGeneratorFn`-shaped
* only so they could take a context, and each returned an `effects` array
* that was ALWAYS empty -- the label path pushed nothing and the two case
* paths only forwarded what the labels did not produce. Label rendering is a
* pure function of the plan now, so the plumbing is gone rather than carried.
*/
import IGeneratorInput from "../IGeneratorInput";
import IGeneratorState from "../IGeneratorState";
import IGeneratorOutput from "../IGeneratorOutput";
import IOrchestrator from "../IOrchestrator";
import TGeneratorFn from "../TGeneratorFn";
import QualifiedCName from "../../../../../utils/QualifiedCName";
import type IPlannedSwitch from "../../types/IPlannedSwitch";
import type IPlannedSwitchCase from "../../types/IPlannedSwitchCase";
import type TPlannedCaseLabel from "../../types/TPlannedCaseLabel";
/**
* Issue #471: resolve an unqualified identifier as a member of the switch's
* enum. Returns the prefixed member, or null when it is not one.
*/
function tryResolveEnumMember(
id: string,
switchEnumType: string,
symbols: IGeneratorInput["symbols"],
): string | null {
Iif (!symbols) return null;
const members = symbols.enumMembers.get(switchEnumType);
return members?.has(id)
? QualifiedCName.fromParts([switchEnumType, id])
: null;
}
/**
* Render a binary literal as hex, for cleaner C output.
*
* Issue #114: through `BigInt`, which handles the `0b` prefix natively and
* keeps precision above 2^53. A value beyond 32 bits takes a `ULL` suffix.
*/
function renderBinaryLiteral(text: string, negative: boolean): string {
const value = BigInt(text);
const hex = value.toString(16).toUpperCase();
const suffix = value > 0xffffffffn ? "ULL" : "";
return `${negative ? "-" : ""}0x${hex}${suffix}`;
}
/**
* Render one case label.
*
* A pure function of the plan and the symbols -- no orchestrator, no effects.
*/
function renderCaseLabel(
label: TPlannedCaseLabel,
subjectEnumType: string | undefined,
input: IGeneratorInput,
): string {
switch (label.kind) {
case "qualified":
return QualifiedCName.fromParts(label.parts);
case "identifier":
// Issue #471: an unqualified member of the switch's enum takes its
// type prefix. #1322: a bare member the enum does NOT declare is E0424
// in pass 2.1 (ADR-017); what reaches here otherwise is a const label.
return subjectEnumType
? (tryResolveEnumMember(label.name, subjectEnumType, input.symbols) ??
label.name)
: label.name;
case "numeric":
return label.negative ? `-${label.text}` : label.text;
case "binary":
return renderBinaryLiteral(label.text, label.negative);
case "char":
return label.text;
case "none":
return "";
}
}
/**
* Render a case or default body: its statements, then `break;` and the brace.
*
* A statement that renders to nothing contributes no line.
*/
function renderBlockBody(
renderBody: () => readonly string[],
lines: string[],
orchestrator: IOrchestrator,
): void {
for (const statement of renderBody()) {
if (statement) {
lines.push(orchestrator.indent(orchestrator.indent(statement)));
}
}
lines.push(
orchestrator.indent(orchestrator.indent("break;")),
orchestrator.indent("}"),
);
}
/**
* Render one case, expanding `||` into consecutive C labels.
*
* Only the LAST label opens the brace; the ones before it fall through.
*/
function renderCase(
planned: IPlannedSwitchCase,
subjectEnumType: string | undefined,
input: IGeneratorInput,
orchestrator: IOrchestrator,
): string {
const lines: string[] = [];
planned.labels.forEach((label, index) => {
const code = renderCaseLabel(label, subjectEnumType, input);
const isLast = index === planned.labels.length - 1;
lines.push(orchestrator.indent(`case ${code}:${isLast ? " {" : ""}`));
});
renderBlockBody(planned.renderBody, lines, orchestrator);
return lines.join("\n");
}
/**
* Generate C code for a switch statement (ADR-025).
*
* #1322: ADR-025's semantic validation is E0711-E0714 in pass 2.1.
*/
const generateSwitch: TGeneratorFn<IPlannedSwitch> = (
planned: IPlannedSwitch,
input: IGeneratorInput,
_state: IGeneratorState,
orchestrator: IOrchestrator,
): IGeneratorOutput => {
const lines: string[] = [`switch (${planned.subject}) {`];
for (const switchCase of planned.cases) {
lines.push(
renderCase(switchCase, planned.subjectEnumType, input, orchestrator),
);
}
if (planned.renderDefaultBody) {
// Note: default(n) count is for compile-time validation only,
// not included in generated C
lines.push(orchestrator.indent("default: {"));
renderBlockBody(planned.renderDefaultBody, lines, orchestrator);
} else {
// Issue #855: MISRA C:2012 Rule 16.4 -- every switch shall have a default
lines.push(
orchestrator.indent("default: {"),
orchestrator.indent(orchestrator.indent("break;")),
orchestrator.indent("}"),
);
}
lines.push("}");
return { code: lines.join("\n"), effects: [] };
};
export default generateSwitch;
|