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 | import type IGeneratorOutput from "../generators/IGeneratorOutput";
/**
* One level of the binary precedence ladder, reduced to what renders it.
*
* #1445 box 3: `BinaryExprGenerator` had ten generators, one per grammar
* precedence level, recursing into each other. They reduce to FOUR renderings
* plus a leaf -- the ten levels differ in which separator, which default
* operator and which extra rule applies, not in what they do.
*
* ## There is no nested IR, and that is deliberate
*
* Operands are THUNKS that re-enter the planner one level down and render the
* result. So this record is one level deep and the nesting lives in the closure
* chain, which matters for two reasons:
*
* - Nine of the ten levels are single-child pass-through levels for almost every
* expression, and the planner collapses them by returning the child's plan
* directly. A plan is only as deep as the expression's real operator
* nesting.
* - Laziness has to be in the PLANNER, not just in a top-level thunk. See
* below.
*
* ## Why every operand is a thunk
*
* `CodeGenState.withoutExpectedType` is a dynamic scope over the whole operand
* SUBTREE at unbounded depth: it clears `expectedType` and
* `suppressBareEnumResolution` for the duration of a callback, and the consumer
* reads them live at the instant a leaf renders. So an operand of a bitwise,
* shift or arithmetic level -- nested anywhere under a comparison -- must
* render inside that window too, and a planner that renders eagerly renders it
* outside.
*
* Issue #1032 is what that costs: `i32 < 0` emitting `signedIdx < 0U`, which C's
* usual arithmetic conversions then promote to unsigned, making the test dead.
* `tests/comparison-expected-type/subscript-operand-window` pins it for
* subscript operands specifically, because every other fixture that compares
* one sits in an `if` condition where `expectedType` is already null and the
* window is a no-op.
*/
type TPlannedBinaryExpr =
/** A single operand: the ladder bottomed out. */
| { readonly kind: "leaf"; readonly render: () => string }
/**
* Operands joined by one fixed separator: `||`, `&&`, `|`, `^`, `&`.
*
* These five levels take no operators from the tree because the grammar
* admits only one at each.
*/
| {
readonly kind: "join";
readonly separator: string;
readonly renderOperands: readonly (() => IGeneratorOutput)[];
}
/**
* An equality or relational comparison.
*
* The renderer opens the `withoutExpectedType` window around the operands --
* see above. `adrLine` is present only when ADR-001's `=`-means-equality rule
* actually fired: an expression using only `!=` is unchanged from C and must
* not claim a matrix cell (#1241).
*/
| {
readonly kind: "comparison";
readonly defaultOperator: string;
readonly operators: readonly string[];
/** ADR-001: maps `=` to `==`. Absent on the relational arm. */
readonly mapOperator: ((operator: string) => string) | null;
readonly adrLine: number | undefined;
/**
* ADR-045: the operands are strings, so this is a `strcmp`.
*
* Decided at plan time because `isStringExpression` is a type-registry
* predicate that generates nothing. The RENDERER raises the
* `<string.h>` include, on the branch that emits the call -- a plan that
* pre-registered it would drag the header into every file with a
* comparison in it.
*/
readonly strcmp: { readonly isNotEqual: boolean } | null;
readonly renderOperands: readonly (() => IGeneratorOutput)[];
}
/** `<<` / `>>`, joined with operators read from the tree and no window. */
| {
readonly kind: "shift";
readonly operators: readonly string[];
readonly renderOperands: readonly (() => IGeneratorOutput)[];
}
/**
* `+ - * / %` -- constant-folded, or lowered to ADR-044 saturating helpers,
* or joined.
*
* `clampType` and `clampBehavior` are thunks asked AFTER the operands render,
* which is where they are asked today. Both read the typer over 1.4's settled
* declarations (#1668), so the order is not load-bearing.
*/
| {
readonly kind: "arithmetic";
/**
* #1175: the chain's value when it is a constant expression, decided in
* 2.2 by the one evaluator (ADR-044: none where it would clamp or wrap);
* null otherwise
*/
readonly constantValue: string | null;
readonly defaultOperator: string;
readonly operators: readonly string[];
readonly clampType: () => string | null;
readonly clampBehavior: () => string | null;
readonly adrLine: number | undefined;
readonly renderOperands: readonly (() => IGeneratorOutput)[];
};
export default TPlannedBinaryExpr;
|