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/src/types.ts CHANGED
@@ -42,9 +42,12 @@ export type ResolvedComparePoint = {
42
42
  };
43
43
 
44
44
  /**
45
- * A resolved crit threshold — `'default'` means the per-die default rule
46
- * (`result === sides` for critical, `result === 1` for fumble), which is what
47
- * bare `cs` / `cf` produce.
45
+ * A resolved crit threshold — `'default'` means the per-die default rule,
46
+ * which is what bare `cs` / `cf` produce. It reads the natural face
47
+ * (`initialResult ?? result`): critical when it equals `sides`, fumble when
48
+ * it equals 1, both only for `sides > 1`. An explicit ComparePoint instead
49
+ * reads the die's current `result`, so a preceding modifier that rewrote it
50
+ * is visible to the comparison.
48
51
  *
49
52
  * @category Results
50
53
  */
@@ -70,10 +73,14 @@ export type ResolvedCritThreshold = ResolvedComparePoint | 'default';
70
73
  *
71
74
  * `'meta'` is the one tag with no counterpart in the notation. Dice counts,
72
75
  * sides, modifier counts and computed thresholds may themselves be dice
73
- * (`(1d4)d6`, `4d6kh(1d2)`, `1d6!>(1d2+3)`). Those inner dice are not part of
74
- * any pool, so they never appear in a {@link RollPart}; they are appended to
75
- * `RollResult.rolls` tagged `'meta'` so an audit log can still show what the
76
- * meta-expression rolled. Filter them out when summing or displaying a pool.
76
+ * (`(1d4)d6`, `4d6kh(1d2)`, `1d6!>(1d2+3)`). Those inner dice belong to no
77
+ * pool: they never appear on a `dice` or `fateDice` part, and they are
78
+ * appended to `RollResult.rolls` tagged `'meta'` so an audit log can still
79
+ * show what the meta-expression rolled.
80
+ *
81
+ * The four whole-pool views — `sort`, `explode`, `reroll`, and `successCount`
82
+ * `rolls` — are snapshots of an evaluation context rather than of a pool, so
83
+ * they do carry meta dice. Filter them out when summing or displaying a pool.
77
84
  *
78
85
  * `'dc'` marks the DC side of a `vs` comparison. Unlike `'meta'` these dice do
79
86
  * render — `1d20[3] vs 2d10[5, 6]` shows both sides — but they are not part of
@@ -158,17 +165,28 @@ export type DieResult = {
158
165
  /** The rolled value */
159
166
  result: number;
160
167
  /**
161
- * Raw first roll before any mutation (e.g., compound-explode accumulation).
162
- * Only populated when `result` has been overwritten with a computed value.
168
+ * Raw first roll before any mutation compound-explode accumulation, the
169
+ * `!p` decrement, or a `minN`/`maxN` clamp. Only populated when `result` has
170
+ * been overwritten with a computed value.
163
171
  * Consumers that need the original face (nat-20 / nat-1 detection) should
164
172
  * read `initialResult ?? result`.
165
173
  */
166
174
  initialResult?: number;
167
175
  /** Modifiers applied to this die */
168
176
  modifiers: DieModifier[];
169
- /** True if rolled the maximum value (always false for Fate dice) */
177
+ /**
178
+ * True if the die met its critical criteria — by default, rolling the
179
+ * maximum face on a die with more than one side. That default never fires
180
+ * on `d1` or on Fate dice (`sides = 0` has no maximum face), but an
181
+ * explicit `cs` threshold does: `4dFcs>0` flags every `+1`.
182
+ */
170
183
  critical: boolean;
171
- /** True if rolled 1 (always false for Fate dice) */
184
+ /**
185
+ * True if the die met its fumble criteria — by default, rolling a 1 on a
186
+ * die with more than one side. As with {@link DieResult.critical}, the
187
+ * default never fires on `d1` or Fate dice, while an explicit `cf`
188
+ * threshold does: `4dFcf=-1` flags every `-1`.
189
+ */
172
190
  fumble: boolean;
173
191
  };
174
192
 
@@ -217,7 +235,10 @@ export type RollPartBase = {
217
235
  * - `literal.total === value` and `variable.total === value`.
218
236
  * - Each part's `rolls[]` shares `DieResult` references with
219
237
  * `RollResult.rolls[]`; both reflect post-evaluation state (explode
220
- * accumulation, reroll flags, keep/drop flags). No deep clone.
238
+ * accumulation, reroll flags, keep/drop flags, success/failure tags). No deep
239
+ * clone. A part's own numbers record what that part computed, so an inner
240
+ * `successCount` re-scored by an outer one (`{4d6>=5}<=2f5`) keeps its
241
+ * tally while the dice it shares show the outer count's tags.
221
242
  *
222
243
  * Meta-expression sub-trees (`4d6kh(1d2)`, `(1+1)d6` counts/sides, computed
223
244
  * thresholds) are not surfaced as nested parts — their resolved numbers
@@ -299,12 +320,30 @@ export type RollPart =
299
320
  type: 'explode';
300
321
  variant: 'standard' | 'compound' | 'penetrating';
301
322
  threshold?: ResolvedComparePoint;
323
+ /**
324
+ * The expanded pool. Standard and penetrating explosions append dice
325
+ * that exist nowhere under `target`, so this is the only view of the
326
+ * pool the modifier actually produced. Compound explosions accumulate
327
+ * in place, making it the same dice as `target` carries.
328
+ *
329
+ * Like `RollResult.rolls` — and unlike the pool under `target` — this
330
+ * keeps `'meta'` dice. Filter them out before counting or displaying.
331
+ */
332
+ rolls: DieResult[];
302
333
  target: RollPart;
303
334
  })
304
335
  | (RollPartBase & {
305
336
  type: 'reroll';
306
337
  once: boolean;
307
338
  condition: ResolvedComparePoint;
339
+ /**
340
+ * The post-reroll pool: discarded intermediates (`'rerolled'` +
341
+ * `'dropped'`) alongside their replacements. Both are appended rather
342
+ * than substituted, so neither appears under `target`.
343
+ *
344
+ * Keeps `'meta'` dice, as `RollResult.rolls` does.
345
+ */
346
+ rolls: DieResult[];
308
347
  target: RollPart;
309
348
  })
310
349
  | (RollPartBase & { type: 'dieBound'; bound: 'min' | 'max'; value: number; target: RollPart })
@@ -312,6 +351,11 @@ export type RollPart =
312
351
  type: 'successCount';
313
352
  threshold: ResolvedComparePoint;
314
353
  failThreshold?: ResolvedComparePoint;
354
+ /**
355
+ * The tallied pool, sharing `DieResult` references with `target`.
356
+ * Keeps `'meta'` dice, as `RollResult.rolls` does.
357
+ */
358
+ rolls: DieResult[];
315
359
  target: RollPart;
316
360
  successes: number;
317
361
  failures: number;
@@ -409,6 +453,14 @@ export type RollResult = Readonly<{
409
453
  * arithmetic on top of a success count (e.g. `{5d6>=5}+2`) affects `total`
410
454
  * but not `successes`. Success counts are terminal, so the group braces are
411
455
  * required: `5d6>=5 * 2` is an `INVALID_SUCCESS_COUNT_TARGET` parse error.
456
+ *
457
+ * Braces also let a second count re-count the same pool (`{4d6>=5}<=2f5`).
458
+ * The outermost count owns the outcome: it re-scores the pool against its own
459
+ * thresholds, so no die is ever both a success and a failure, and dice it
460
+ * does not count come out untagged rather than keeping the inner count's
461
+ * tags. The DC side of a `vs` is never tallied here; it keeps whatever tags
462
+ * an inner count gave it, unless the outer count scores subtotals rather
463
+ * than dice, which leaves every die untagged.
412
464
  */
413
465
  successes?: number;
414
466
  /**
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by `bun run generate:version` from package.json — do not edit.
2
- export const version = '3.0.0';
2
+ export const version = '3.2.0';