roll-parser 3.0.0 → 3.2.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +61 -1
  2. package/MIGRATION.md +293 -4
  3. package/README.md +246 -55
  4. package/dist/cli/format.d.ts.map +1 -1
  5. package/dist/cli/format.js +8 -7
  6. package/dist/cli/format.js.map +1 -1
  7. package/dist/evaluator/die.d.ts +2 -1
  8. package/dist/evaluator/die.d.ts.map +1 -1
  9. package/dist/evaluator/die.js.map +1 -1
  10. package/dist/evaluator/env.d.ts +57 -0
  11. package/dist/evaluator/env.d.ts.map +1 -1
  12. package/dist/evaluator/env.js.map +1 -1
  13. package/dist/evaluator/evaluator.d.ts.map +1 -1
  14. package/dist/evaluator/evaluator.js +104 -56
  15. package/dist/evaluator/evaluator.js.map +1 -1
  16. package/dist/evaluator/modifiers/crit-threshold.d.ts +43 -5
  17. package/dist/evaluator/modifiers/crit-threshold.d.ts.map +1 -1
  18. package/dist/evaluator/modifiers/crit-threshold.js +24 -11
  19. package/dist/evaluator/modifiers/crit-threshold.js.map +1 -1
  20. package/dist/evaluator/modifiers/die-bound.d.ts +5 -2
  21. package/dist/evaluator/modifiers/die-bound.d.ts.map +1 -1
  22. package/dist/evaluator/modifiers/die-bound.js +6 -3
  23. package/dist/evaluator/modifiers/die-bound.js.map +1 -1
  24. package/dist/evaluator/modifiers/explode.d.ts.map +1 -1
  25. package/dist/evaluator/modifiers/explode.js +13 -5
  26. package/dist/evaluator/modifiers/explode.js.map +1 -1
  27. package/dist/evaluator/modifiers/flags.d.ts +17 -0
  28. package/dist/evaluator/modifiers/flags.d.ts.map +1 -1
  29. package/dist/evaluator/modifiers/flags.js +4 -4
  30. package/dist/evaluator/modifiers/flags.js.map +1 -1
  31. package/dist/evaluator/modifiers/keep-drop.d.ts +5 -5
  32. package/dist/evaluator/modifiers/keep-drop.d.ts.map +1 -1
  33. package/dist/evaluator/modifiers/keep-drop.js +7 -7
  34. package/dist/evaluator/modifiers/keep-drop.js.map +1 -1
  35. package/dist/evaluator/modifiers/reroll.d.ts.map +1 -1
  36. package/dist/evaluator/modifiers/reroll.js +16 -10
  37. package/dist/evaluator/modifiers/reroll.js.map +1 -1
  38. package/dist/evaluator/modifiers/sort.d.ts +1 -1
  39. package/dist/evaluator/modifiers/sort.d.ts.map +1 -1
  40. package/dist/evaluator/modifiers/sort.js +3 -3
  41. package/dist/evaluator/modifiers/sort.js.map +1 -1
  42. package/dist/evaluator/modifiers/success-count.d.ts +6 -2
  43. package/dist/evaluator/modifiers/success-count.d.ts.map +1 -1
  44. package/dist/evaluator/modifiers/success-count.js +9 -5
  45. package/dist/evaluator/modifiers/success-count.js.map +1 -1
  46. package/dist/notation.d.ts +13 -0
  47. package/dist/notation.d.ts.map +1 -0
  48. package/dist/notation.js +8 -0
  49. package/dist/notation.js.map +1 -0
  50. package/dist/parser/ast.d.ts +12 -7
  51. package/dist/parser/ast.d.ts.map +1 -1
  52. package/dist/parser/ast.js.map +1 -1
  53. package/dist/parser/guards.d.ts +38 -0
  54. package/dist/parser/guards.d.ts.map +1 -1
  55. package/dist/parser/guards.js +51 -0
  56. package/dist/parser/guards.js.map +1 -1
  57. package/dist/parser/parser.d.ts.map +1 -1
  58. package/dist/parser/parser.js +26 -2
  59. package/dist/parser/parser.js.map +1 -1
  60. package/dist/render.d.ts +95 -0
  61. package/dist/render.d.ts.map +1 -0
  62. package/dist/render.js +232 -0
  63. package/dist/render.js.map +1 -0
  64. package/dist/types.d.ts +64 -12
  65. package/dist/types.d.ts.map +1 -1
  66. package/dist/types.js.map +1 -1
  67. package/dist/version.d.ts +1 -1
  68. package/dist/version.js +1 -1
  69. package/package.json +14 -4
  70. package/src/cli/format.ts +15 -23
  71. package/src/evaluator/die.ts +2 -1
  72. package/src/evaluator/env.ts +58 -0
  73. package/src/evaluator/evaluator.ts +192 -70
  74. package/src/evaluator/modifiers/crit-threshold.ts +81 -15
  75. package/src/evaluator/modifiers/die-bound.ts +13 -4
  76. package/src/evaluator/modifiers/explode.ts +21 -6
  77. package/src/evaluator/modifiers/flags.ts +21 -4
  78. package/src/evaluator/modifiers/keep-drop.ts +10 -8
  79. package/src/evaluator/modifiers/reroll.ts +20 -11
  80. package/src/evaluator/modifiers/sort.ts +9 -3
  81. package/src/evaluator/modifiers/success-count.ts +27 -5
  82. package/src/notation.ts +24 -0
  83. package/src/parser/ast.ts +12 -7
  84. package/src/parser/guards.ts +92 -1
  85. package/src/parser/parser.ts +56 -1
  86. package/src/render.ts +394 -0
  87. package/src/types.ts +64 -12
  88. package/src/version.ts +1 -1
package/dist/render.js ADDED
@@ -0,0 +1,232 @@
1
+ import { joinModifierCode } from './notation.js';
2
+ import { DegreeOfSuccess } from './types.js';
3
+ export const MARKDOWN_MARKS = {
4
+ dropped: (_die, text) => `~~${text}~~`,
5
+ success: (_die, text) => `**${text}**`,
6
+ failure: (_die, text) => `__${text}__`,
7
+ droppedGroup: (inner) => `~~${inner}~~`,
8
+ };
9
+ const KEEP_DROP_CODES = {
10
+ keep: { highest: 'kh', lowest: 'kl' },
11
+ drop: { highest: 'dh', lowest: 'dl' },
12
+ };
13
+ const EXPLODE_MARKERS = {
14
+ standard: '!',
15
+ compound: '!!',
16
+ penetrating: '!p',
17
+ };
18
+ const DEGREE_LABELS = {
19
+ [DegreeOfSuccess.CriticalFailure]: 'Critical Failure',
20
+ [DegreeOfSuccess.Failure]: 'Failure',
21
+ [DegreeOfSuccess.Success]: 'Success',
22
+ [DegreeOfSuccess.CriticalSuccess]: 'Critical Success',
23
+ };
24
+ const BARE_VARIABLE = /^[A-Za-z_][A-Za-z0-9_]*$/;
25
+ function comparePointCode(point) {
26
+ return `${point.operator}${point.value}`;
27
+ }
28
+ function critCode(prefix, threshold) {
29
+ return threshold === 'default' ? prefix : `${prefix}${comparePointCode(threshold)}`;
30
+ }
31
+ function keepDropCode(spec) {
32
+ return `${KEEP_DROP_CODES[spec.kind][spec.selector]}${spec.count}`;
33
+ }
34
+ function failCode(point) {
35
+ return point.operator === '=' ? `f${point.value}` : `f${comparePointCode(point)}`;
36
+ }
37
+ function expr(part) {
38
+ switch (part.type) {
39
+ case 'literal':
40
+ case 'variable':
41
+ return String(part.value);
42
+ case 'dice':
43
+ return `${part.count}d${part.sides}`;
44
+ case 'fateDice':
45
+ return `${part.count}dF`;
46
+ case 'grouped':
47
+ return `(${expr(part.inner)})`;
48
+ case 'binaryOp':
49
+ return `${expr(part.left)} ${part.operator} ${expr(part.right)}`;
50
+ case 'unaryOp':
51
+ return `-${expr(part.operand)}`;
52
+ case 'group':
53
+ return `{${part.parts.map(expr).join(', ')}}`;
54
+ case 'functionCall':
55
+ return `${part.name}(${part.args.map(expr).join(', ')})`;
56
+ case 'keepDrop':
57
+ return joinModifierCode(expr(part.target), part.specs.map(keepDropCode).join(''));
58
+ case 'explode':
59
+ return `${expr(part.target)}${explodeCode(part)}`;
60
+ case 'reroll':
61
+ return joinModifierCode(expr(part.target), rerollCode(part));
62
+ case 'dieBound':
63
+ return joinModifierCode(expr(part.target), dieBoundCode(part));
64
+ case 'sort':
65
+ return joinModifierCode(expr(part.target), part.order === 'ascending' ? 's' : 'sd');
66
+ case 'critThreshold':
67
+ return joinModifierCode(expr(part.target), critThresholdCode(part));
68
+ case 'successCount':
69
+ return `${expr(part.target)}${successCountCode(part)}`;
70
+ case 'versus':
71
+ return `${expr(part.roll)} vs ${expr(part.dc)}`;
72
+ }
73
+ }
74
+ function explodeCode(part) {
75
+ const marker = EXPLODE_MARKERS[part.variant];
76
+ return part.threshold == null ? marker : `${marker}${comparePointCode(part.threshold)}`;
77
+ }
78
+ function rerollCode(part) {
79
+ return `${part.once ? 'ro' : 'r'}${comparePointCode(part.condition)}`;
80
+ }
81
+ function dieBoundCode(part) {
82
+ return part.value < 0 ? `${part.bound}(${part.value})` : `${part.bound}${part.value}`;
83
+ }
84
+ function critThresholdCode(part) {
85
+ return [
86
+ ...part.successThresholds.map((threshold) => critCode('cs', threshold)),
87
+ ...part.failThresholds.map((threshold) => critCode('cf', threshold)),
88
+ ].reduce(joinModifierCode, '');
89
+ }
90
+ function successCountCode(part) {
91
+ const fail = part.failThreshold == null ? '' : failCode(part.failThreshold);
92
+ return `${comparePointCode(part.threshold)}${fail}`;
93
+ }
94
+ function collectDice(part, out) {
95
+ switch (part.type) {
96
+ case 'dice':
97
+ case 'fateDice':
98
+ case 'sort':
99
+ case 'explode':
100
+ case 'reroll':
101
+ case 'successCount':
102
+ for (const die of part.rolls)
103
+ out.push(die);
104
+ return;
105
+ case 'grouped':
106
+ collectDice(part.inner, out);
107
+ return;
108
+ case 'unaryOp':
109
+ collectDice(part.operand, out);
110
+ return;
111
+ case 'binaryOp':
112
+ collectDice(part.left, out);
113
+ collectDice(part.right, out);
114
+ return;
115
+ case 'keepDrop':
116
+ case 'dieBound':
117
+ case 'critThreshold':
118
+ collectDice(part.target, out);
119
+ return;
120
+ case 'group':
121
+ for (const sub of part.parts)
122
+ collectDice(sub, out);
123
+ return;
124
+ case 'functionCall':
125
+ for (const arg of part.args)
126
+ collectDice(arg, out);
127
+ return;
128
+ case 'versus':
129
+ collectDice(part.roll, out);
130
+ collectDice(part.dc, out);
131
+ return;
132
+ case 'literal':
133
+ case 'variable':
134
+ return;
135
+ }
136
+ }
137
+ function poolOf(part) {
138
+ const dice = [];
139
+ collectDice(part, dice);
140
+ return dice;
141
+ }
142
+ function markDie(die, marks, plain) {
143
+ let text = String(die.result);
144
+ if (die.critical)
145
+ text = marks.critical?.(die, text) ?? text;
146
+ if (die.fumble)
147
+ text = marks.fumble?.(die, text) ?? text;
148
+ if (plain)
149
+ return text;
150
+ const { modifiers } = die;
151
+ if (modifiers.includes('dropped'))
152
+ return marks.dropped?.(die, text) ?? text;
153
+ if (modifiers.includes('success'))
154
+ return marks.success?.(die, text) ?? text;
155
+ if (modifiers.includes('failure'))
156
+ return marks.failure?.(die, text) ?? text;
157
+ return text;
158
+ }
159
+ function renderPool(dice, marks, plain) {
160
+ const shown = [];
161
+ for (const die of dice) {
162
+ if (die.modifiers.includes('meta'))
163
+ continue;
164
+ shown.push(markDie(die, marks, plain));
165
+ }
166
+ return `[${shown.join(', ')}]`;
167
+ }
168
+ function renderModifier(target, code, dice, marks, plain) {
169
+ const pool = dice.length === 0 ? '' : renderPool(dice, marks, plain);
170
+ return `${joinModifierCode(expr(target), code)}${pool}`;
171
+ }
172
+ function renderPart(part, marks, plain) {
173
+ switch (part.type) {
174
+ case 'literal':
175
+ return String(part.value);
176
+ case 'variable': {
177
+ const display = BARE_VARIABLE.test(part.name) ? `@${part.name}` : `@{${part.name}}`;
178
+ return `${display}[${part.value}]`;
179
+ }
180
+ case 'dice':
181
+ case 'fateDice':
182
+ return `${expr(part)}${renderPool(part.rolls, marks, plain)}`;
183
+ case 'grouped':
184
+ return `(${renderPart(part.inner, marks, plain)})`;
185
+ case 'binaryOp':
186
+ return `${renderPart(part.left, marks, plain)} ${part.operator} ${renderPart(part.right, marks, plain)}`;
187
+ case 'unaryOp':
188
+ return `-${renderPart(part.operand, marks, plain)}`;
189
+ case 'functionCall': {
190
+ const args = part.args.map((arg) => renderPart(arg, marks, plain)).join(', ');
191
+ return `${part.name}(${args})`;
192
+ }
193
+ case 'versus':
194
+ return `${renderPart(part.roll, marks, plain)} vs ${renderPart(part.dc, marks, plain)}`;
195
+ case 'group':
196
+ return renderGroup(part, marks, plain);
197
+ case 'keepDrop':
198
+ return part.target.type === 'group' && part.target.keptIndices != null
199
+ ? renderPart(part.target, marks, plain)
200
+ : `${expr(part.target)}${renderPool(poolOf(part.target), marks, plain)}`;
201
+ case 'explode':
202
+ return renderModifier(part.target, explodeCode(part), part.rolls, marks, plain);
203
+ case 'reroll':
204
+ return renderModifier(part.target, rerollCode(part), part.rolls, marks, plain);
205
+ case 'successCount':
206
+ return part.target.type === 'group' && part.target.parts.length >= 2
207
+ ? `${renderPart(part.target, marks, plain)}${successCountCode(part)}`
208
+ : renderModifier(part.target, successCountCode(part), part.rolls, marks, plain);
209
+ case 'dieBound':
210
+ return `${expr(part)}${renderPool(poolOf(part.target), marks, plain)}`;
211
+ case 'sort':
212
+ return `${expr(part)}${renderPool(part.rolls, marks, plain)}`;
213
+ case 'critThreshold':
214
+ return `${expr(part)}${renderPool(poolOf(part.target), marks, plain)}`;
215
+ }
216
+ }
217
+ function renderGroup(part, marks, plain) {
218
+ const { keptIndices } = part;
219
+ const subRolls = part.parts.map((sub, index) => {
220
+ if (keptIndices == null || keptIndices.includes(index)) {
221
+ return renderPart(sub, marks, plain);
222
+ }
223
+ const inner = renderPart(sub, marks, true);
224
+ return marks.droppedGroup?.(inner) ?? inner;
225
+ });
226
+ return `{${subRolls.join(', ')}}`;
227
+ }
228
+ export function renderBreakdown(result, marks = MARKDOWN_MARKS) {
229
+ const trailing = result.degree == null ? String(result.total) : DEGREE_LABELS[result.degree];
230
+ return `${renderPart(result.parts, marks, false)} = ${trailing}`;
231
+ }
232
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -37,9 +37,12 @@ export type ResolvedComparePoint = {
37
37
  readonly value: number;
38
38
  };
39
39
  /**
40
- * A resolved crit threshold — `'default'` means the per-die default rule
41
- * (`result === sides` for critical, `result === 1` for fumble), which is what
42
- * bare `cs` / `cf` produce.
40
+ * A resolved crit threshold — `'default'` means the per-die default rule,
41
+ * which is what bare `cs` / `cf` produce. It reads the natural face
42
+ * (`initialResult ?? result`): critical when it equals `sides`, fumble when
43
+ * it equals 1, both only for `sides > 1`. An explicit ComparePoint instead
44
+ * reads the die's current `result`, so a preceding modifier that rewrote it
45
+ * is visible to the comparison.
43
46
  *
44
47
  * @category Results
45
48
  */
@@ -64,10 +67,14 @@ export type ResolvedCritThreshold = ResolvedComparePoint | 'default';
64
67
  *
65
68
  * `'meta'` is the one tag with no counterpart in the notation. Dice counts,
66
69
  * sides, modifier counts and computed thresholds may themselves be dice
67
- * (`(1d4)d6`, `4d6kh(1d2)`, `1d6!>(1d2+3)`). Those inner dice are not part of
68
- * any pool, so they never appear in a {@link RollPart}; they are appended to
69
- * `RollResult.rolls` tagged `'meta'` so an audit log can still show what the
70
- * meta-expression rolled. Filter them out when summing or displaying a pool.
70
+ * (`(1d4)d6`, `4d6kh(1d2)`, `1d6!>(1d2+3)`). Those inner dice belong to no
71
+ * pool: they never appear on a `dice` or `fateDice` part, and they are
72
+ * appended to `RollResult.rolls` tagged `'meta'` so an audit log can still
73
+ * show what the meta-expression rolled.
74
+ *
75
+ * The four whole-pool views — `sort`, `explode`, `reroll`, and `successCount`
76
+ * `rolls` — are snapshots of an evaluation context rather than of a pool, so
77
+ * they do carry meta dice. Filter them out when summing or displaying a pool.
71
78
  *
72
79
  * `'dc'` marks the DC side of a `vs` comparison. Unlike `'meta'` these dice do
73
80
  * render — `1d20[3] vs 2d10[5, 6]` shows both sides — but they are not part of
@@ -140,17 +147,28 @@ export type DieResult = {
140
147
  /** The rolled value */
141
148
  result: number;
142
149
  /**
143
- * Raw first roll before any mutation (e.g., compound-explode accumulation).
144
- * Only populated when `result` has been overwritten with a computed value.
150
+ * Raw first roll before any mutation compound-explode accumulation, the
151
+ * `!p` decrement, or a `minN`/`maxN` clamp. Only populated when `result` has
152
+ * been overwritten with a computed value.
145
153
  * Consumers that need the original face (nat-20 / nat-1 detection) should
146
154
  * read `initialResult ?? result`.
147
155
  */
148
156
  initialResult?: number;
149
157
  /** Modifiers applied to this die */
150
158
  modifiers: DieModifier[];
151
- /** True if rolled the maximum value (always false for Fate dice) */
159
+ /**
160
+ * True if the die met its critical criteria — by default, rolling the
161
+ * maximum face on a die with more than one side. That default never fires
162
+ * on `d1` or on Fate dice (`sides = 0` has no maximum face), but an
163
+ * explicit `cs` threshold does: `4dFcs>0` flags every `+1`.
164
+ */
152
165
  critical: boolean;
153
- /** True if rolled 1 (always false for Fate dice) */
166
+ /**
167
+ * True if the die met its fumble criteria — by default, rolling a 1 on a
168
+ * die with more than one side. As with {@link DieResult.critical}, the
169
+ * default never fires on `d1` or Fate dice, while an explicit `cf`
170
+ * threshold does: `4dFcf=-1` flags every `-1`.
171
+ */
154
172
  fumble: boolean;
155
173
  };
156
174
  /**
@@ -196,7 +214,10 @@ export type RollPartBase = {
196
214
  * - `literal.total === value` and `variable.total === value`.
197
215
  * - Each part's `rolls[]` shares `DieResult` references with
198
216
  * `RollResult.rolls[]`; both reflect post-evaluation state (explode
199
- * accumulation, reroll flags, keep/drop flags). No deep clone.
217
+ * accumulation, reroll flags, keep/drop flags, success/failure tags). No deep
218
+ * clone. A part's own numbers record what that part computed, so an inner
219
+ * `successCount` re-scored by an outer one (`{4d6>=5}<=2f5`) keeps its
220
+ * tally while the dice it shares show the outer count's tags.
200
221
  *
201
222
  * Meta-expression sub-trees (`4d6kh(1d2)`, `(1+1)d6` counts/sides, computed
202
223
  * thresholds) are not surfaced as nested parts — their resolved numbers
@@ -296,11 +317,29 @@ export type RollPart = (RollPartBase & {
296
317
  type: 'explode';
297
318
  variant: 'standard' | 'compound' | 'penetrating';
298
319
  threshold?: ResolvedComparePoint;
320
+ /**
321
+ * The expanded pool. Standard and penetrating explosions append dice
322
+ * that exist nowhere under `target`, so this is the only view of the
323
+ * pool the modifier actually produced. Compound explosions accumulate
324
+ * in place, making it the same dice as `target` carries.
325
+ *
326
+ * Like `RollResult.rolls` — and unlike the pool under `target` — this
327
+ * keeps `'meta'` dice. Filter them out before counting or displaying.
328
+ */
329
+ rolls: DieResult[];
299
330
  target: RollPart;
300
331
  }) | (RollPartBase & {
301
332
  type: 'reroll';
302
333
  once: boolean;
303
334
  condition: ResolvedComparePoint;
335
+ /**
336
+ * The post-reroll pool: discarded intermediates (`'rerolled'` +
337
+ * `'dropped'`) alongside their replacements. Both are appended rather
338
+ * than substituted, so neither appears under `target`.
339
+ *
340
+ * Keeps `'meta'` dice, as `RollResult.rolls` does.
341
+ */
342
+ rolls: DieResult[];
304
343
  target: RollPart;
305
344
  }) | (RollPartBase & {
306
345
  type: 'dieBound';
@@ -311,6 +350,11 @@ export type RollPart = (RollPartBase & {
311
350
  type: 'successCount';
312
351
  threshold: ResolvedComparePoint;
313
352
  failThreshold?: ResolvedComparePoint;
353
+ /**
354
+ * The tallied pool, sharing `DieResult` references with `target`.
355
+ * Keeps `'meta'` dice, as `RollResult.rolls` does.
356
+ */
357
+ rolls: DieResult[];
314
358
  target: RollPart;
315
359
  successes: number;
316
360
  failures: number;
@@ -414,6 +458,14 @@ export type RollResult = Readonly<{
414
458
  * arithmetic on top of a success count (e.g. `{5d6>=5}+2`) affects `total`
415
459
  * but not `successes`. Success counts are terminal, so the group braces are
416
460
  * required: `5d6>=5 * 2` is an `INVALID_SUCCESS_COUNT_TARGET` parse error.
461
+ *
462
+ * Braces also let a second count re-count the same pool (`{4d6>=5}<=2f5`).
463
+ * The outermost count owns the outcome: it re-scores the pool against its own
464
+ * thresholds, so no die is ever both a success and a failure, and dice it
465
+ * does not count come out untagged rather than keeping the inner count's
466
+ * tags. The DC side of a `vs` is never tallied here; it keeps whatever tags
467
+ * an inner count gave it, unless the outer count scores subtotals rather
468
+ * than dice, which leaves every die untagged.
417
469
  */
418
470
  successes?: number;
419
471
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AA0HA,MAAM,CAAN,IAAY,eAKX;AALD,WAAY,eAAe;IACzB,2EAAmB,CAAA;IACnB,2DAAW,CAAA;IACX,2DAAW,CAAA;IACX,2EAAmB,CAAA;AACrB,CAAC,EALW,eAAe,KAAf,eAAe,QAK1B"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAiIA,MAAM,CAAN,IAAY,eAKX;AALD,WAAY,eAAe;IACzB,2EAAmB,CAAA;IACnB,2DAAW,CAAA;IACX,2DAAW,CAAA;IACX,2EAAmB,CAAA;AACrB,CAAC,EALW,eAAe,KAAf,eAAe,QAK1B"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const version = "3.0.0";
1
+ export declare const version = "3.2.0";
2
2
  //# sourceMappingURL=version.d.ts.map
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const version = '3.0.0';
1
+ export const version = '3.2.0';
2
2
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roll-parser",
3
- "version": "3.0.0",
3
+ "version": "3.2.0",
4
4
  "description": "High-performance dice notation parser for tabletop RPGs. TypeScript-first, runs on Node, Bun, and browsers with zero dependencies.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -12,6 +12,10 @@
12
12
  "types": "./dist/index.d.ts",
13
13
  "default": "./dist/index.js"
14
14
  },
15
+ "./render": {
16
+ "types": "./dist/render.d.ts",
17
+ "default": "./dist/render.js"
18
+ },
15
19
  "./testing": {
16
20
  "types": "./dist/testing.d.ts",
17
21
  "default": "./dist/testing.js"
@@ -63,7 +67,7 @@
63
67
  "workspaces": ["scripts/browser-smoke", "scripts/docs"],
64
68
  "scripts": {
65
69
  "prepare": "git rev-parse --git-dir > /dev/null 2>&1 && git config core.hooksPath .githooks || exit 0",
66
- "typecheck": "tsc --noEmit && bun run build && tsc --noEmit -p site/tsconfig.json && tsc --noEmit -p scripts/tsconfig.json && tsc --noEmit -p bench/tsconfig.json",
70
+ "typecheck": "tsc --noEmit && bun run build && tsc --noEmit -p site/tsconfig.json && tsc --noEmit -p site/tsconfig.test.json && tsc --noEmit -p scripts/tsconfig.json && tsc --noEmit -p bench/tsconfig.json",
67
71
  "check": "bun typecheck && biome check .",
68
72
  "check:fix": "bun typecheck && biome check --write .",
69
73
  "check:version": "bun scripts/check-version.ts",
@@ -98,7 +102,7 @@
98
102
  {
99
103
  "name": "index.js (full library)",
100
104
  "path": "dist/index.js",
101
- "limit": "12.5 kB"
105
+ "limit": "12.85 kB"
102
106
  },
103
107
  {
104
108
  "name": "index.js — { parse }",
@@ -110,7 +114,13 @@
110
114
  "name": "index.js — { roll }",
111
115
  "path": "dist/index.js",
112
116
  "import": "{ roll }",
113
- "limit": "12 kB"
117
+ "limit": "12.5 kB"
118
+ },
119
+ {
120
+ "name": "render.js — { renderBreakdown }",
121
+ "path": "dist/render.js",
122
+ "import": "{ renderBreakdown }",
123
+ "limit": "2 kB"
114
124
  },
115
125
  {
116
126
  "name": "testing.js — { createMockRng }",
package/src/cli/format.ts CHANGED
@@ -6,8 +6,22 @@
6
6
  * @module cli/format
7
7
  */
8
8
 
9
+ import { type DieMarks, renderBreakdown } from '../render.js';
9
10
  import type { RollResult } from '../types.js';
10
11
 
12
+ /**
13
+ * Terminal stand-ins for the markdown markers `RollResult.rendered` uses:
14
+ * dropped dice become `(value)`, successes `[value]`, failures `{value}`, so
15
+ * the per-die classification stays visible with no markup dependency. A
16
+ * dropped group sub-roll takes the same parentheses as a dropped die.
17
+ */
18
+ const TERMINAL_MARKS: DieMarks = {
19
+ dropped: (_die, text) => `(${text})`,
20
+ success: (_die, text) => `[${text}]`,
21
+ failure: (_die, text) => `{${text}}`,
22
+ droppedGroup: (inner) => `(${inner})`,
23
+ };
24
+
11
25
  /**
12
26
  * Output mode selectors for {@link formatResult}.
13
27
  */
@@ -42,27 +56,5 @@ export function formatResult(result: RollResult, options: FormatOptions = {}): s
42
56
  return String(result.total);
43
57
  }
44
58
 
45
- return formatRendered(result.rendered);
46
- }
47
-
48
- /**
49
- * Converts markdown-style dice markers to terminal-friendly forms.
50
- *
51
- * The evaluator uses markdown syntax in the rendered field:
52
- * `~~value~~` — dropped dice or dropped group sub-rolls
53
- * `**value**` — dice counted as success
54
- * `__value__` — dice counted as failure
55
- *
56
- * For plain terminals these become `(value)`, `[value]`, and `{value}` so
57
- * the per-die classification stays visible without any markup dependency.
58
- * Dropped spans can wrap a whole sub-roll (e.g. `~~1d8[2]~~` from
59
- * `{1d8, 1d10}kh1`), so the strikethrough pattern accepts any tilde-free
60
- * content, not just a single number. The evaluator never nests `~~`
61
- * (see `stripInnerMarkers`), so the tilde-free span match is safe.
62
- */
63
- function formatRendered(rendered: string): string {
64
- return rendered
65
- .replace(/\*\*(-?\d+)\*\*/g, '[$1]')
66
- .replace(/__(-?\d+)__/g, '{$1}')
67
- .replace(/~~([^~]+)~~/g, '($1)');
59
+ return renderBreakdown(result, TERMINAL_MARKS);
68
60
  }
@@ -37,7 +37,8 @@ export function createDieResult(
37
37
 
38
38
  /**
39
39
  * Creates a Fate/Fudge die result. Uses `sides = 0` as a sentinel — Fate dice
40
- * have no max-face concept, so `critical` and `fumble` are always `false`.
40
+ * have no max-face concept, so `critical` and `fumble` start `false` and the
41
+ * default rule never sets them. An explicit `cs`/`cf` threshold still can.
41
42
  */
42
43
  export function createFateDieResult(result: number, modifiers: DieModifier[]): DieResult {
43
44
  return {
@@ -10,6 +10,17 @@
10
10
 
11
11
  import { EvaluatorError } from '../errors.js';
12
12
  import type { ASTNode } from '../parser/ast.js';
13
+ import type { DieResult, ResolvedCritThreshold } from '../types.js';
14
+
15
+ /**
16
+ * A resolved `cs`/`cf` pair, recorded per die so that dice minted later by
17
+ * explode or reroll can be judged by the rule the user declared instead of
18
+ * the built-in default.
19
+ */
20
+ export type CritRule = {
21
+ readonly success: readonly ResolvedCritThreshold[];
22
+ readonly fail: readonly ResolvedCritThreshold[];
23
+ };
13
24
 
14
25
  /**
15
26
  * Per-evaluation shared environment (created once, shared across all branches).
@@ -26,8 +37,20 @@ export type EvalEnv = {
26
37
  * Set to `true` by `evalSuccessCount`. Propagates through the shared env
27
38
  * so `evaluate()` can include `successes`/`failures` fields even when no
28
39
  * die was tagged (impossible threshold).
40
+ *
41
+ * ! Set *after* the target is evaluated, not before. `evalSuccessCount` reads
42
+ * ! the pre-set value to learn whether an inner count already tagged its pool
43
+ * ! — moving the assignment back to the top makes it read its own write.
29
44
  */
30
45
  hasSuccessCount: boolean;
46
+ /**
47
+ * What a multi-sub-roll group count (`{2d6, 2d6}>=10`) scored. Its units are
48
+ * subtotals, which exist only as throwaway synthetic dice, so these verdicts
49
+ * reach the top-level tally here rather than as tags on a die. Every path
50
+ * that releases `TALLY_FLAGS` releases these too.
51
+ */
52
+ subtotalSuccesses: number;
53
+ subtotalFailures: number;
31
54
  /**
32
55
  * `true` while the evaluator is inside a `VersusNode`'s roll or DC
33
56
  * sub-evaluation. `evalVersus` rejects nesting via this flag — catches
@@ -35,6 +58,41 @@ export type EvalEnv = {
35
58
  * left-chain check.
36
59
  */
37
60
  insideVersus: boolean;
61
+ /**
62
+ * `true` once a `vs` has tagged its DC dice `'dc'`. Lets every pool
63
+ * operation skip its per-die `isVersusDc` check outright — the tag exists in
64
+ * a small minority of notation, and the call inside the loop cost 11-38% on
65
+ * keep/drop, success, and sort notation that can never carry one.
66
+ *
67
+ * ! Monotonic: set by `evalVersus`, never cleared. `insideVersus` is not a
68
+ * ! model for it — that flag resets in a `finally`, and it is false exactly
69
+ * ! when the tagged DC dice become visible to the enclosing pool.
70
+ */
71
+ hasVersusDc: boolean;
72
+ /**
73
+ * Crit rule per die, keyed on the `DieResult` object itself. Populated by
74
+ * `applyCritThresholds`; read by explode and reroll so a die they mint
75
+ * inherits the rule of the die it descended from.
76
+ *
77
+ * Out of band rather than a `DieResult` field because `DieResult` is public
78
+ * and serialized. `undefined` until the first `cs`/`cf` node — declared here
79
+ * so the shape stays stable for notation that has none.
80
+ */
81
+ critRules: WeakMap<DieResult, CritRule> | undefined;
82
+ /**
83
+ * Dice minted as explosion continuations by `!` / `!p`, keyed on the
84
+ * `DieResult` object itself. Read by `extractNatural`, which counts them out
85
+ * of the versus primaries — the `'exploded'` tag cannot say it alone, since
86
+ * a compound explode stamps it on the original die it accumulated into.
87
+ *
88
+ * Out of band for the same reason as {@link EvalEnv.critRules}: `DieResult`
89
+ * is public and serialized.
90
+ *
91
+ * ! Only populated while {@link EvalEnv.insideVersus} — nothing outside a
92
+ * ! `vs` reads it, and the `add` per appended die is not free. Any new
93
+ * ! reader must be on a versus path too.
94
+ */
95
+ explosionDice: WeakSet<DieResult> | undefined;
38
96
  /**
39
97
  * User-supplied variable map for `@name` / `@{name}` references. Always
40
98
  * defined — `evaluate()` defaults to an empty object so lookups can be