roll-parser - v3.4.0
    Preparing search index...

    Type Alias RollPart

    RollPart:
        | RollPartBase & { type: "literal"; value: number }
        | RollPartBase & { name: string; type: "variable"; value: number }
        | RollPartBase & {
            count: number;
            rolls: DieResult[];
            sides: number;
            type: "dice";
        }
        | RollPartBase & { count: number; rolls: DieResult[]; type: "fateDice" }
        | RollPartBase & { inner: RollPart; type: "grouped" }
        | RollPartBase & {
            left: RollPart;
            operator: "+" | "-" | "*" | "/" | "%" | "**";
            right: RollPart;
            type: "binaryOp";
        }
        | RollPartBase & { operand: RollPart; operator: "-"; type: "unaryOp" }
        | RollPartBase & {
            specs: KeepDropSpec[];
            target: RollPart;
            type: "keepDrop";
        }
        | RollPartBase & {
            rolls: DieResult[];
            target: RollPart;
            threshold?: ResolvedComparePoint;
            type: "explode";
            variant: "standard"
            | "compound"
            | "penetrating";
        }
        | RollPartBase & {
            condition: ResolvedComparePoint;
            once: boolean;
            rolls: DieResult[];
            target: RollPart;
            type: "reroll";
        }
        | RollPartBase & {
            bound: "min"
            | "max";
            target: RollPart;
            type: "dieBound";
            value: number;
        }
        | RollPartBase & {
            failThreshold?: ResolvedComparePoint;
            failures: number;
            rolls: DieResult[];
            successes: number;
            target: RollPart;
            threshold: ResolvedComparePoint;
            type: "successCount";
        }
        | RollPartBase & {
            dc: RollPart;
            degree: DegreeOfSuccess;
            roll: RollPart;
            type: "versus";
        }
        | RollPartBase & { args: RollPart[]; name: string; type: "functionCall" }
        | RollPartBase & {
            keptIndices?: number[];
            parts: RollPart[];
            type: "group";
        }
        | RollPartBase & {
            order: "ascending"
            | "descending";
            rolls: DieResult[];
            target: RollPart;
            type: "sort";
        }
        | RollPartBase & {
            failThresholds: ResolvedCritThreshold[];
            successThresholds: ResolvedCritThreshold[];
            target: RollPart;
            type: "critThreshold";
        }

    Structured breakdown of an evaluated expression, mirroring the AST 1:1 — every ASTNode produces exactly one RollPart. Discriminants are lowercase camelCase to distinguish evaluation-tree types from ASTNode.type (PascalCase) at a glance.

    Invariants:

    • RollResult.parts.total === RollResult.total.
    • successCount.total === successes - failures.
    • literal.total === value and variable.total === value.
    • Each part's rolls[] shares DieResult references with RollResult.rolls[]; both reflect post-evaluation state (explode accumulation, reroll flags, keep/drop flags, success/failure tags). No deep clone. A part's own numbers record what that part computed, so an inner successCount re-scored by an outer one ({4d6>=5}<=2f5) keeps its tally while the dice it shares show the outer count's tags.

    Meta-expression sub-trees (4d6kh(1d2), (1+1)d6 counts/sides, computed thresholds) are not surfaced as nested parts — their resolved numbers appear in the owning part, and their dice are inspectable in RollResult.rolls via the 'meta' modifier tag.

    Type Declaration

    import type { RollPart } from 'roll-parser';

    function describe(part: RollPart): string {
    switch (part.type) {
    case 'literal':
    return String(part.value);
    case 'variable':
    return `@${part.name}`;
    case 'dice':
    return `${part.count}d${part.sides}[${part.rolls.map((d) => d.result).join(', ')}]`;
    case 'fateDice':
    return `${part.count}dF`;
    case 'grouped':
    return `(${describe(part.inner)})`;
    case 'binaryOp':
    return `${describe(part.left)} ${part.operator} ${describe(part.right)}`;
    case 'unaryOp':
    return `-${describe(part.operand)}`;
    case 'keepDrop':
    return `${describe(part.target)} [${part.specs.length} keep/drop]`;
    case 'explode':
    return `${describe(part.target)} (${part.variant} explode)`;
    case 'reroll':
    return `${describe(part.target)} (reroll${part.once ? ' once' : ''})`;
    case 'dieBound':
    return `${describe(part.target)} (${part.bound} ${part.value})`;
    case 'successCount':
    return `${describe(part.target)} => ${part.successes}-${part.failures}`;
    case 'versus':
    return `${describe(part.roll)} vs ${describe(part.dc)}`;
    case 'functionCall':
    return `${part.name}(${part.args.map(describe).join(', ')})`;
    case 'group':
    return `{${part.parts.map(describe).join(', ')}}`;
    case 'sort':
    return `${describe(part.target)} (${part.order})`;
    case 'critThreshold':
    return `${describe(part.target)} (crit override)`;
    }
    }
    import { roll } from 'roll-parser';
    import { createMockRng } from 'roll-parser/testing';

    const result = roll('4d6kh3 + 2', { rng: createMockRng([3, 6, 2, 5]) });
    result.parts.type; // 'binaryOp'
    result.parts.total; // 16 — always equal to result.total
    describe(result.parts); // '4d6[3, 6, 2, 5] [1 keep/drop] + 2'