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 | 104x 374x 374x 836x 374x 8x 8x 4x 8x 8x 6x 4x 4x 759x 421x 421x 836x 825x 825x | /**
* One enum's member values, in declaration order (#1669, ADR-017 "Member
* Values"). The one place the auto-increment, the `i32` range and the
* "members above it only" rule are applied, so 1.4 Resolve settling the
* values and 2.1 Analyze reporting them cannot disagree.
*
* Pure: what a NAME is worth is the caller's environment's to answer, as for
* every constant expression. `ownMember` is how that environment answers a
* member of the enum being computed.
*/
import ConstantEvaluator from "./ConstantEvaluator";
import TypeCheckUtils from "./TypeCheckUtils";
import type IConstantEnvironment from "./types/IConstantEnvironment";
import type ISourcePosition from "./types/ISourcePosition";
import type TConstExpr from "../types/TConstExpr";
import type TConstResult from "../types/TConstResult";
import type TEnumMemberValue from "../types/TEnumMemberValue";
/** ADR-017: a member's value is an i32 */
const MEMBER_TYPE = "i32";
interface IEnumMemberInput {
readonly name: string;
readonly valueExpr: TConstExpr | null;
}
class EnumMemberValues {
/**
* Every member's value. `envAt(index, settled)` is the environment member
* `index`'s value is written in, with the members above it already settled.
*/
static compute(
members: ReadonlyArray<IEnumMemberInput>,
envAt: (
index: number,
settled: ReadonlyArray<TEnumMemberValue>,
) => IConstantEnvironment,
): TEnumMemberValue[] {
const settled: TEnumMemberValue[] = [];
members.forEach((member, index) => {
settled.push(
EnumMemberValues.ranged(
member.valueExpr === null
? EnumMemberValues.continued(members, settled, index)
: ConstantEvaluator.evaluate(
member.valueExpr,
envAt(index, settled),
),
),
);
});
return settled;
}
/**
* What `member`, a member of the enum being computed, is worth to member
* `index`'s value: only a member declared above it has a value yet.
*/
static ownMember(
names: ReadonlyArray<string>,
index: number,
settled: ReadonlyArray<TEnumMemberValue>,
member: string,
spelling: string,
at: ISourcePosition,
): TConstResult {
const position = names.indexOf(member);
const without = (
reason: "undeclaredMember" | "selfMember" | "laterMember" | "unfolded",
) => ({ kind: "notConstant", reason, spelling, at }) as const;
Iif (position < 0) return without("undeclaredMember");
if (position === index) return without("selfMember");
if (position > index) return without("laterMember");
const above = settled[position];
return above.kind === "value"
? { kind: "value", value: above.value, typeName: null }
: without("unfolded");
}
/** ADR-017: a member written without a value is the one above it, plus one */
private static continued(
members: ReadonlyArray<IEnumMemberInput>,
settled: ReadonlyArray<TEnumMemberValue>,
index: number,
): TEnumMemberValue {
if (index === 0) return { kind: "value", value: 0n, typeName: null };
const above = settled[index - 1];
return above.kind === "value"
? { kind: "value", value: above.value + 1n, typeName: null }
: { kind: "follows", member: members[index - 1].name };
}
private static ranged(result: TEnumMemberValue): TEnumMemberValue {
if (result.kind !== "value") return result;
const range = TypeCheckUtils.integerRange(MEMBER_TYPE)!;
return result.value < range[0] || result.value > range[1]
? { kind: "outOfRange", value: result.value }
: result;
}
}
export default EnumMemberValues;
|