roll-parser 2.3.2 → 3.0.0-alpha.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 (101) hide show
  1. package/README.md +47 -178
  2. package/dist/cli/args.d.ts +35 -0
  3. package/dist/cli/args.d.ts.map +1 -0
  4. package/dist/cli/format.d.ts +20 -0
  5. package/dist/cli/format.d.ts.map +1 -0
  6. package/dist/cli/index.d.ts +8 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli.js +1775 -0
  9. package/dist/errors.d.ts +39 -0
  10. package/dist/errors.d.ts.map +1 -0
  11. package/dist/evaluator/evaluator.d.ts +65 -0
  12. package/dist/evaluator/evaluator.d.ts.map +1 -0
  13. package/dist/evaluator/index.d.ts +8 -0
  14. package/dist/evaluator/index.d.ts.map +1 -0
  15. package/dist/evaluator/modifiers/compare.d.ts +15 -0
  16. package/dist/evaluator/modifiers/compare.d.ts.map +1 -0
  17. package/dist/evaluator/modifiers/explode.d.ts +44 -0
  18. package/dist/evaluator/modifiers/explode.d.ts.map +1 -0
  19. package/dist/evaluator/modifiers/keep-drop.d.ts +44 -0
  20. package/dist/evaluator/modifiers/keep-drop.d.ts.map +1 -0
  21. package/dist/evaluator/modifiers/reroll.d.ts +33 -0
  22. package/dist/evaluator/modifiers/reroll.d.ts.map +1 -0
  23. package/dist/evaluator/modifiers/success-count.d.ts +28 -0
  24. package/dist/evaluator/modifiers/success-count.d.ts.map +1 -0
  25. package/dist/index.d.ts +20 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +1723 -0
  28. package/dist/index.mjs +1724 -0
  29. package/dist/lexer/lexer.d.ts +75 -0
  30. package/dist/lexer/lexer.d.ts.map +1 -0
  31. package/dist/lexer/tokens.d.ts +87 -0
  32. package/dist/lexer/tokens.d.ts.map +1 -0
  33. package/dist/parser/ast.d.ts +183 -0
  34. package/dist/parser/ast.d.ts.map +1 -0
  35. package/dist/parser/parser.d.ts +102 -0
  36. package/dist/parser/parser.d.ts.map +1 -0
  37. package/dist/rng/index.d.ts +8 -0
  38. package/dist/rng/index.d.ts.map +1 -0
  39. package/dist/rng/mock.d.ts +36 -0
  40. package/dist/rng/mock.d.ts.map +1 -0
  41. package/dist/rng/seeded.d.ts +33 -0
  42. package/dist/rng/seeded.d.ts.map +1 -0
  43. package/dist/rng/types.d.ts +24 -0
  44. package/dist/rng/types.d.ts.map +1 -0
  45. package/dist/roll.d.ts +47 -0
  46. package/dist/roll.d.ts.map +1 -0
  47. package/dist/testing.d.ts +11 -0
  48. package/dist/testing.d.ts.map +1 -0
  49. package/dist/testing.js +38 -0
  50. package/dist/testing.mjs +39 -0
  51. package/dist/types.d.ts +105 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/package.json +62 -43
  54. package/src/cli/args.ts +72 -0
  55. package/src/cli/format.ts +45 -0
  56. package/src/cli/index.ts +75 -0
  57. package/src/errors.ts +78 -0
  58. package/src/evaluator/evaluator.ts +837 -0
  59. package/src/evaluator/index.ts +14 -0
  60. package/src/evaluator/modifiers/compare.ts +29 -0
  61. package/src/evaluator/modifiers/explode.ts +215 -0
  62. package/src/evaluator/modifiers/keep-drop.ts +151 -0
  63. package/src/evaluator/modifiers/reroll.ts +172 -0
  64. package/src/evaluator/modifiers/success-count.ts +59 -0
  65. package/src/index.ts +72 -0
  66. package/src/lexer/lexer.ts +281 -0
  67. package/src/lexer/tokens.ts +147 -0
  68. package/src/parser/ast.ts +261 -0
  69. package/src/parser/parser.ts +716 -0
  70. package/src/rng/index.ts +8 -0
  71. package/src/rng/mock.ts +65 -0
  72. package/src/rng/seeded.ts +127 -0
  73. package/src/rng/types.ts +25 -0
  74. package/src/roll.ts +66 -0
  75. package/src/testing.ts +16 -0
  76. package/src/types.ts +112 -0
  77. package/bin/help.txt +0 -19
  78. package/bin/roll-parser.js +0 -23
  79. package/dist/roll-parser.js +0 -697
  80. package/dist/roll-parser.min.js +0 -1
  81. package/index.js +0 -24
  82. package/src/complex/parse.js +0 -37
  83. package/src/complex/parseAndRoll.js +0 -21
  84. package/src/complex/parseAndRollClassic.js +0 -20
  85. package/src/complex/parseAndRollSimple.js +0 -19
  86. package/src/complex/parseAndRollWod.js +0 -20
  87. package/src/complex/parseClassicRoll.js +0 -22
  88. package/src/complex/parseSimpleRoll.js +0 -21
  89. package/src/complex/parseWodRoll.js +0 -21
  90. package/src/converter.js +0 -45
  91. package/src/grammar.js +0 -14
  92. package/src/mapper.js +0 -50
  93. package/src/normalizer.js +0 -51
  94. package/src/object/Result.js +0 -23
  95. package/src/object/Roll.js +0 -26
  96. package/src/object/Type.js +0 -5
  97. package/src/object/WodRoll.js +0 -29
  98. package/src/parser.js +0 -43
  99. package/src/random.js +0 -22
  100. package/src/roller.js +0 -106
  101. package/src/stringifier.js +0 -33
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Dice notation lexer with character-by-character scanning.
3
+ *
4
+ * @module lexer/lexer
5
+ */
6
+ import type { RollParserErrorCode } from '../errors';
7
+ import { RollParserError } from '../errors';
8
+ import { type Token } from './tokens';
9
+ /**
10
+ * Error thrown when the lexer encounters an invalid character.
11
+ */
12
+ export declare class LexerError extends RollParserError {
13
+ readonly position: number;
14
+ readonly character: string;
15
+ constructor(message: string, code: RollParserErrorCode, position: number, character: string);
16
+ }
17
+ /**
18
+ * Lexer for dice notation.
19
+ *
20
+ * Produces a stream of tokens from an input string using character-by-character
21
+ * scanning with maximal munch for multi-character tokens.
22
+ */
23
+ export declare class Lexer {
24
+ private pos;
25
+ private readonly input;
26
+ constructor(input: string);
27
+ /**
28
+ * Tokenize the entire input and return all tokens.
29
+ */
30
+ tokenize(): Token[];
31
+ /**
32
+ * Get the next token from the input.
33
+ */
34
+ nextToken(): Token;
35
+ private skipWhitespace;
36
+ private scanNumber;
37
+ /**
38
+ * Scans an identifier using full-accumulation: collects all consecutive
39
+ * alpha characters, then classifies the result against known keywords.
40
+ *
41
+ * Special cases run before/after the accumulation loop:
42
+ * - `dF` / `Df` / `dF` / `DF` produces DICE_FATE. Must be handled BEFORE
43
+ * the loop because `F` is alpha and would otherwise be greedily merged
44
+ * into identifiers like `dfkh` (from `4dFkh2`) or `dfdf` (from `dFdF`).
45
+ * Reserves the `d[fF]` prefix namespace for Fate dice.
46
+ * - Bare `d` followed by `%` produces DICE_PERCENT. `%` is not alpha so the
47
+ * accumulation loop stops naturally and the post-loop check handles it.
48
+ */
49
+ private scanIdentifier;
50
+ private peek;
51
+ private peekNext;
52
+ private advance;
53
+ private match;
54
+ private isAtEnd;
55
+ private isDigit;
56
+ private isAlpha;
57
+ private isWhitespace;
58
+ private createToken;
59
+ private createTokenAt;
60
+ }
61
+ /**
62
+ * Tokenize a dice notation string.
63
+ *
64
+ * @param input - The dice notation to tokenize
65
+ * @returns Array of tokens including EOF
66
+ * @throws {LexerError} If an invalid character is encountered
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * const tokens = lex('2d20+5');
71
+ * // [NUMBER(2), DICE, NUMBER(20), PLUS, NUMBER(5), EOF]
72
+ * ```
73
+ */
74
+ export declare function lex(input: string): Token[];
75
+ //# sourceMappingURL=lexer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../../src/lexer/lexer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,KAAK,KAAK,EAAa,MAAM,UAAU,CAAC;AAEjD;;GAEG;AACH,qBAAa,UAAW,SAAQ,eAAe;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAM5F;AAsBD;;;;;GAKG;AACH,qBAAa,KAAK;IAChB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,KAAK,EAAE,MAAM;IAIzB;;OAEG;IACH,QAAQ,IAAI,KAAK,EAAE;IAYnB;;OAEG;IACH,SAAS,IAAI,KAAK;IAyElB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,UAAU;IAoBlB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,cAAc;IAgCtB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,KAAK;IAOb,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,aAAa;CAGtB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE,CAE1C"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Token type definitions for the dice notation lexer.
3
+ *
4
+ * @module lexer/tokens
5
+ */
6
+ /**
7
+ * Token types for dice notation.
8
+ *
9
+ * Grouped semantically. Numeric values are stable identifiers — the specific
10
+ * numbers don't matter, but they must be unique.
11
+ */
12
+ export declare enum TokenType {
13
+ /** Numeric literal: integer or decimal */
14
+ NUMBER = 0,
15
+ /** Dice operator: 'd' or 'D' */
16
+ DICE = 1,
17
+ /** Percentile dice operator: 'd%' (alias for d100) */
18
+ DICE_PERCENT = 2,
19
+ /** Fate/Fudge dice operator: 'dF' */
20
+ DICE_FATE = 3,
21
+ /** Addition operator: '+' */
22
+ PLUS = 4,
23
+ /** Subtraction operator: '-' */
24
+ MINUS = 5,
25
+ /** Multiplication operator: '*' */
26
+ MULTIPLY = 6,
27
+ /** Division operator: '/' */
28
+ DIVIDE = 7,
29
+ /** Modulo operator: '%' */
30
+ MODULO = 8,
31
+ /** Power operator: '**' or '^' */
32
+ POWER = 9,
33
+ /** Greater than: '>' */
34
+ GREATER = 10,
35
+ /** Greater than or equal: '>=' */
36
+ GREATER_EQUAL = 11,
37
+ /** Less than: '<' */
38
+ LESS = 12,
39
+ /** Less than or equal: '<=' */
40
+ LESS_EQUAL = 13,
41
+ /** Equal: '=' */
42
+ EQUAL = 14,
43
+ /** Left parenthesis: '(' */
44
+ LPAREN = 15,
45
+ /** Right parenthesis: ')' */
46
+ RPAREN = 16,
47
+ /** Argument separator: ',' */
48
+ COMMA = 17,
49
+ /** Keep highest modifier: 'kh' or 'k' */
50
+ KEEP_HIGH = 18,
51
+ /** Keep lowest modifier: 'kl' */
52
+ KEEP_LOW = 19,
53
+ /** Drop highest modifier: 'dh' */
54
+ DROP_HIGH = 20,
55
+ /** Drop lowest modifier: 'dl' */
56
+ DROP_LOW = 21,
57
+ /** Standard explode: '!' */
58
+ EXPLODE = 22,
59
+ /** Compounding explode: '!!' */
60
+ EXPLODE_COMPOUND = 23,
61
+ /** Penetrating explode: '!p' */
62
+ EXPLODE_PENETRATING = 24,
63
+ /** Recursive reroll: 'r' */
64
+ REROLL = 25,
65
+ /** Reroll once: 'ro' */
66
+ REROLL_ONCE = 26,
67
+ /** Fail marker: 'f' */
68
+ FAIL = 27,
69
+ /** Math function: 'floor', 'ceil', 'round', 'abs', 'max', 'min' */
70
+ FUNCTION = 28,
71
+ /** Versus operator: 'vs' */
72
+ VS = 29,
73
+ /** End of input marker */
74
+ EOF = 30
75
+ }
76
+ /**
77
+ * A token produced by the lexer.
78
+ */
79
+ export type Token = {
80
+ /** The type of this token */
81
+ type: TokenType;
82
+ /** The raw string value from input (lowercased for identifiers) */
83
+ value: string;
84
+ /** Zero-based position in the input string */
85
+ position: number;
86
+ };
87
+ //# sourceMappingURL=tokens.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/lexer/tokens.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;GAKG;AACH,oBAAY,SAAS;IAKnB,0CAA0C;IAC1C,MAAM,IAAI;IAMV,gCAAgC;IAChC,IAAI,IAAI;IACR,sDAAsD;IACtD,YAAY,IAAI;IAChB,qCAAqC;IACrC,SAAS,IAAI;IAMb,6BAA6B;IAC7B,IAAI,IAAI;IACR,gCAAgC;IAChC,KAAK,IAAI;IACT,mCAAmC;IACnC,QAAQ,IAAI;IACZ,6BAA6B;IAC7B,MAAM,IAAI;IACV,2BAA2B;IAC3B,MAAM,IAAI;IACV,kCAAkC;IAClC,KAAK,IAAI;IAMT,wBAAwB;IACxB,OAAO,KAAK;IACZ,kCAAkC;IAClC,aAAa,KAAK;IAClB,qBAAqB;IACrB,IAAI,KAAK;IACT,+BAA+B;IAC/B,UAAU,KAAK;IACf,iBAAiB;IACjB,KAAK,KAAK;IAMV,4BAA4B;IAC5B,MAAM,KAAK;IACX,6BAA6B;IAC7B,MAAM,KAAK;IACX,8BAA8B;IAC9B,KAAK,KAAK;IAMV,yCAAyC;IACzC,SAAS,KAAK;IACd,iCAAiC;IACjC,QAAQ,KAAK;IACb,kCAAkC;IAClC,SAAS,KAAK;IACd,iCAAiC;IACjC,QAAQ,KAAK;IAMb,4BAA4B;IAC5B,OAAO,KAAK;IACZ,gCAAgC;IAChC,gBAAgB,KAAK;IACrB,gCAAgC;IAChC,mBAAmB,KAAK;IAMxB,4BAA4B;IAC5B,MAAM,KAAK;IACX,wBAAwB;IACxB,WAAW,KAAK;IAMhB,uBAAuB;IACvB,IAAI,KAAK;IAMT,mEAAmE;IACnE,QAAQ,KAAK;IAMb,4BAA4B;IAC5B,EAAE,KAAK;IAMP,0BAA0B;IAC1B,GAAG,KAAK;CACT;AAED;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG;IAClB,6BAA6B;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC"}
@@ -0,0 +1,183 @@
1
+ /**
2
+ * AST node type definitions for the dice notation parser.
3
+ *
4
+ * @module parser/ast
5
+ */
6
+ import type { ComparePoint } from '../types';
7
+ /**
8
+ * Numeric literal node.
9
+ */
10
+ export type LiteralNode = {
11
+ type: 'Literal';
12
+ value: number;
13
+ };
14
+ /**
15
+ * Dice roll node.
16
+ * Count and sides can be expressions to support computed dice like (1+1)d(3*2).
17
+ */
18
+ export type DiceNode = {
19
+ type: 'Dice';
20
+ count: ASTNode;
21
+ sides: ASTNode;
22
+ };
23
+ /**
24
+ * Fate/Fudge dice node (`dF`).
25
+ * Each die produces a result in {-1, 0, +1}. No configurable sides.
26
+ */
27
+ export type FateDiceNode = {
28
+ type: 'FateDice';
29
+ count: ASTNode;
30
+ };
31
+ /**
32
+ * Binary operation node.
33
+ */
34
+ export type BinaryOpNode = {
35
+ type: 'BinaryOp';
36
+ operator: '+' | '-' | '*' | '/' | '%' | '**';
37
+ left: ASTNode;
38
+ right: ASTNode;
39
+ };
40
+ /**
41
+ * Unary operation node.
42
+ */
43
+ export type UnaryOpNode = {
44
+ type: 'UnaryOp';
45
+ operator: '-';
46
+ operand: ASTNode;
47
+ };
48
+ /**
49
+ * Keep/drop modifier node.
50
+ * Wraps a dice expression with keep highest/lowest or drop highest/lowest.
51
+ */
52
+ export type ModifierNode = {
53
+ type: 'Modifier';
54
+ modifier: 'keep' | 'drop';
55
+ selector: 'highest' | 'lowest';
56
+ count: ASTNode;
57
+ target: ASTNode;
58
+ };
59
+ /**
60
+ * Exploding dice node (`!`, `!!`, `!p`, `!>Y`).
61
+ * Wraps a dice expression with a standard, compounding, or penetrating
62
+ * explosion. An absent `threshold` means "explode on the die's maximum face".
63
+ */
64
+ export type ExplodeNode = {
65
+ type: 'Explode';
66
+ variant: 'standard' | 'compound' | 'penetrating';
67
+ threshold?: ComparePoint;
68
+ target: ASTNode;
69
+ };
70
+ /**
71
+ * Reroll node (`r<COND>`, `ro<COND>`).
72
+ * Re-rolls dice that match a comparison condition. `once: true` for `ro`
73
+ * keeps the second result regardless of match; `once: false` for `r`
74
+ * re-rolls recursively until the condition no longer matches.
75
+ */
76
+ export type RerollNode = {
77
+ type: 'Reroll';
78
+ once: boolean;
79
+ condition: ComparePoint;
80
+ target: ASTNode;
81
+ };
82
+ /**
83
+ * Success counting node (`>=T`, `>T`, `<T`, `<=T`, `=T`, with optional `f=F`).
84
+ *
85
+ * Transforms a dice pool into a success count: each die meeting `threshold`
86
+ * adds +1, each die meeting `failThreshold` subtracts 1. Terminal — no further
87
+ * postfix modifiers may wrap a `SuccessCountNode`. The `failThreshold`
88
+ * operator is always `=` (fail on an exact value).
89
+ */
90
+ export type SuccessCountNode = {
91
+ type: 'SuccessCount';
92
+ target: ASTNode;
93
+ threshold: ComparePoint;
94
+ failThreshold?: ComparePoint;
95
+ };
96
+ /**
97
+ * Versus node (`<roll> vs <dc>`) — PF2e Degrees of Success.
98
+ *
99
+ * Both sides are full expressions. The `roll` side is evaluated and compared
100
+ * against the `dc` side total, producing a `DegreeOfSuccess` with natural
101
+ * d20 upgrade/downgrade applied when exactly one kept d20 appears on the
102
+ * roll side. Lowest-precedence operator — chaining (`a vs b vs c`) is
103
+ * rejected at parse time; nesting via parens (`a vs (b vs c)`) is rejected
104
+ * at evaluation time.
105
+ */
106
+ export type VersusNode = {
107
+ type: 'Versus';
108
+ roll: ASTNode;
109
+ dc: ASTNode;
110
+ };
111
+ /**
112
+ * Math function call node (`floor(expr)`, `max(a, b, ...)`, etc.).
113
+ *
114
+ * Supports the fixed-arity functions `floor`, `ceil`, `round`, `abs`, and the
115
+ * variadic functions `max`, `min` (minimum 2 args). Arity is validated at
116
+ * parse time against a static table; by the time the evaluator sees a
117
+ * `FunctionCallNode`, `args.length` is guaranteed to match the function.
118
+ */
119
+ export type FunctionCallNode = {
120
+ type: 'FunctionCall';
121
+ name: string;
122
+ args: ASTNode[];
123
+ };
124
+ /**
125
+ * Union type of all AST nodes.
126
+ */
127
+ export type ASTNode = LiteralNode | DiceNode | FateDiceNode | BinaryOpNode | UnaryOpNode | ModifierNode | ExplodeNode | RerollNode | SuccessCountNode | VersusNode | FunctionCallNode;
128
+ /**
129
+ * Type guard for LiteralNode.
130
+ */
131
+ export declare function isLiteral(node: ASTNode): node is LiteralNode;
132
+ /**
133
+ * Type guard for DiceNode.
134
+ */
135
+ export declare function isDice(node: ASTNode): node is DiceNode;
136
+ /**
137
+ * Type guard for FateDiceNode.
138
+ */
139
+ export declare function isFateDice(node: ASTNode): node is FateDiceNode;
140
+ /**
141
+ * Type guard for BinaryOpNode.
142
+ */
143
+ export declare function isBinaryOp(node: ASTNode): node is BinaryOpNode;
144
+ /**
145
+ * Type guard for UnaryOpNode.
146
+ */
147
+ export declare function isUnaryOp(node: ASTNode): node is UnaryOpNode;
148
+ /**
149
+ * Type guard for ModifierNode.
150
+ */
151
+ export declare function isModifier(node: ASTNode): node is ModifierNode;
152
+ /**
153
+ * Type guard for ExplodeNode.
154
+ */
155
+ export declare function isExplode(node: ASTNode): node is ExplodeNode;
156
+ /**
157
+ * Type guard for RerollNode.
158
+ */
159
+ export declare function isReroll(node: ASTNode): node is RerollNode;
160
+ /**
161
+ * Type guard for SuccessCountNode.
162
+ */
163
+ export declare function isSuccessCount(node: ASTNode): node is SuccessCountNode;
164
+ /**
165
+ * Type guard for VersusNode.
166
+ */
167
+ export declare function isVersus(node: ASTNode): node is VersusNode;
168
+ /**
169
+ * Type guard for FunctionCallNode.
170
+ */
171
+ export declare function isFunctionCall(node: ASTNode): node is FunctionCallNode;
172
+ /**
173
+ * Returns `true` if the AST contains a `Dice` or `FateDice` node reachable
174
+ * through structural composition (BinaryOp, UnaryOp, keep/drop, explode,
175
+ * reroll, success-count wrappers). Meta-expressions — dice count/sides,
176
+ * modifier counts, and ComparePoint values — are treated as leaves and
177
+ * never recursed into.
178
+ *
179
+ * Used by the parser to reject success-counting targets that don't actually
180
+ * roll any dice (e.g. `1>=3`, `(1+2)>=3`).
181
+ */
182
+ export declare function containsDice(node: ASTNode): boolean;
183
+ //# sourceMappingURL=ast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/parser/ast.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;IAC7C,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,QAAQ,EAAE,GAAG,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,QAAQ,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC/B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,UAAU,GAAG,UAAU,GAAG,aAAa,CAAC;IACjD,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,YAAY,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,cAAc,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,YAAY,CAAC;IACxB,aAAa,CAAC,EAAE,YAAY,CAAC;CAC9B,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,EAAE,EAAE,OAAO,CAAC;CACb,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,OAAO,GACf,WAAW,GACX,QAAQ,GACR,YAAY,GACZ,YAAY,GACZ,WAAW,GACX,YAAY,GACZ,WAAW,GACX,UAAU,GACV,gBAAgB,GAChB,UAAU,GACV,gBAAgB,CAAC;AAErB;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,WAAW,CAE5D;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,QAAQ,CAEtD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,YAAY,CAE9D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,YAAY,CAE9D;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,WAAW,CAE5D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,YAAY,CAE9D;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,WAAW,CAE5D;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,UAAU,CAE1D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,gBAAgB,CAEtE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,UAAU,CAE1D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,gBAAgB,CAEtE;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAqBnD"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Pratt parser for dice notation.
3
+ *
4
+ * @module parser/parser
5
+ */
6
+ import type { RollParserErrorCode } from '../errors';
7
+ import { RollParserError } from '../errors';
8
+ import { type Token } from '../lexer/tokens';
9
+ import type { ComparePoint } from '../types';
10
+ import type { ASTNode } from './ast';
11
+ /**
12
+ * Error thrown when the parser encounters invalid syntax.
13
+ */
14
+ export declare class ParseError extends RollParserError {
15
+ readonly position: number;
16
+ readonly token: Token | undefined;
17
+ constructor(message: string, code: RollParserErrorCode, position: number, token?: Token);
18
+ }
19
+ /**
20
+ * Pratt parser for dice notation.
21
+ *
22
+ * Uses binding power (precedence) to handle operator associativity and
23
+ * precedence without left recursion issues.
24
+ */
25
+ export declare class Parser {
26
+ private readonly tokens;
27
+ private pos;
28
+ constructor(tokens: Token[]);
29
+ /**
30
+ * Parse the token stream into an AST.
31
+ */
32
+ parse(): ASTNode;
33
+ /**
34
+ * Parse an expression with minimum binding power.
35
+ */
36
+ private parseExpression;
37
+ /**
38
+ * NUD - Null Denotation.
39
+ * Handles tokens that appear at the start of an expression (prefix position).
40
+ */
41
+ private parseNud;
42
+ /**
43
+ * LED - Left Denotation.
44
+ * Handles tokens that appear between expressions (infix/postfix position).
45
+ */
46
+ private parseLed;
47
+ private parseLiteral;
48
+ private parseUnaryMinus;
49
+ private parsePrefixDice;
50
+ private parseInfixDice;
51
+ private parsePrefixDicePercent;
52
+ private parseInfixDicePercent;
53
+ private parsePrefixFateDice;
54
+ private parseInfixFateDice;
55
+ private parseGrouped;
56
+ private parseFunctionCall;
57
+ private parseBinaryOp;
58
+ private rejectSuccessCountTarget;
59
+ private parseModifier;
60
+ private parseExplode;
61
+ private parseReroll;
62
+ private parseSuccessCount;
63
+ private parseVersus;
64
+ /**
65
+ * Checks whether the next token is a comparison operator.
66
+ */
67
+ isComparePointAhead(): boolean;
68
+ /**
69
+ * Parses a comparison operator followed by a value expression.
70
+ * Called by modifier parsers (explode, reroll, success counting).
71
+ *
72
+ * @returns A ComparePoint with the operator and value AST node
73
+ * @throws {ParseError} If the next token is not a comparison operator
74
+ */
75
+ parseComparePoint(): ComparePoint;
76
+ private getCompareOp;
77
+ private getOperatorSymbol;
78
+ private getLeftBp;
79
+ private getRightBp;
80
+ private peek;
81
+ private advance;
82
+ private expect;
83
+ private hasTokens;
84
+ }
85
+ /**
86
+ * Parse a dice notation string into an AST.
87
+ *
88
+ * @param notation - The dice notation to parse
89
+ * @returns The root AST node
90
+ * @throws {LexerError} If the input contains invalid characters
91
+ * @throws {ParseError} If the input has invalid syntax
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * const ast = parse('2d6+3');
96
+ * // { type: 'BinaryOp', operator: '+',
97
+ * // left: { type: 'Dice', count: 2, sides: 6 },
98
+ * // right: { type: 'Literal', value: 3 } }
99
+ * ```
100
+ */
101
+ export declare function parse(notation: string): ASTNode;
102
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parser/parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,EAAE,KAAK,KAAK,EAAa,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAa,YAAY,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,KAAK,EACV,OAAO,EAYR,MAAM,OAAO,CAAC;AAGf;;GAEG;AACH,qBAAa,UAAW,SAAQ,eAAe;IAC7C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,CAAC;gBAEtB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK;CAMxF;AAkDD;;;;;GAKG;AACH,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAU;IACjC,OAAO,CAAC,GAAG,CAAK;gBAEJ,MAAM,EAAE,KAAK,EAAE;IAI3B;;OAEG;IACH,KAAK,IAAI,OAAO;IAqBhB;;OAEG;IACH,OAAO,CAAC,eAAe;IAgBvB;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAsChB;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAwDhB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,cAAc;IAUtB,OAAO,CAAC,sBAAsB;IAS9B,OAAO,CAAC,qBAAqB;IAS7B,OAAO,CAAC,mBAAmB;IAQ3B,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,iBAAiB;IAgDzB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,wBAAwB;IAWhC,OAAO,CAAC,aAAa;IA2BrB,OAAO,CAAC,YAAY;IA4BpB,OAAO,CAAC,WAAW;IAmBnB,OAAO,CAAC,iBAAiB;IAgCzB,OAAO,CAAC,WAAW;IAenB;;OAEG;IACH,mBAAmB,IAAI,OAAO;IAW9B;;;;;;OAMG;IACH,iBAAiB,IAAI,YAAY;IAWjC,OAAO,CAAC,YAAY;IAwBpB,OAAO,CAAC,iBAAiB;IAwBzB,OAAO,CAAC,SAAS;IA8CjB,OAAO,CAAC,UAAU;IAoBlB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,MAAM;IAcd,OAAO,CAAC,SAAS;CAGlB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAI/C"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * RNG module - Seedable random number generation.
3
+ *
4
+ * @module rng
5
+ */
6
+ export type { RNG } from './types';
7
+ export { SeededRNG } from './seeded';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rng/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Mock RNG for deterministic testing.
3
+ *
4
+ * @module rng/mock
5
+ */
6
+ import type { RNG } from './types';
7
+ /**
8
+ * Error thrown when MockRNG exhausts its predefined values.
9
+ *
10
+ * This is intentional behavior to catch incorrect roll counts in tests.
11
+ * If you see this error, your test is consuming more random values than expected.
12
+ */
13
+ export declare class MockRNGExhaustedError extends Error {
14
+ readonly consumed: number;
15
+ constructor(consumed: number);
16
+ }
17
+ /**
18
+ * Creates a mock RNG that returns predefined values in sequence.
19
+ *
20
+ * IMPORTANT: Throws MockRNGExhaustedError when all values are consumed.
21
+ * This behavior catches incorrect roll counts in tests - it never wraps around.
22
+ *
23
+ * @param values - Array of values to return (dice results for nextInt, floats for next)
24
+ * @returns RNG instance returning predefined values
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const rng = createMockRng([4, 2, 6]);
29
+ * rng.nextInt(1, 6); // Returns 4
30
+ * rng.nextInt(1, 6); // Returns 2
31
+ * rng.nextInt(1, 6); // Returns 6
32
+ * rng.nextInt(1, 6); // Throws MockRNGExhaustedError
33
+ * ```
34
+ */
35
+ export declare function createMockRng(values: number[]): RNG;
36
+ //# sourceMappingURL=mock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../src/rng/mock.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AAEnC;;;;;GAKG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,QAAQ,EAAE,MAAM;CAK7B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,CAsBnD"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Seedable RNG using xorshift128 algorithm.
3
+ *
4
+ * @module rng/seeded
5
+ */
6
+ import type { RNG } from './types';
7
+ /**
8
+ * Seedable pseudo-random number generator using xorshift128.
9
+ *
10
+ * Produces reproducible sequences from identical seeds.
11
+ * Period: 2^128 - 1
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * // Same seed = same sequence
16
+ * const rng1 = new SeededRNG('test-seed');
17
+ * const rng2 = new SeededRNG('test-seed');
18
+ * rng1.nextInt(1, 6) === rng2.nextInt(1, 6); // true
19
+ * ```
20
+ */
21
+ export declare class SeededRNG implements RNG {
22
+ private s0;
23
+ private s1;
24
+ private s2;
25
+ private s3;
26
+ constructor(seed?: string | number);
27
+ private initState;
28
+ private hashString;
29
+ private nextUint32;
30
+ next(): number;
31
+ nextInt(min: number, max: number): number;
32
+ }
33
+ //# sourceMappingURL=seeded.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seeded.d.ts","sourceRoot":"","sources":["../../src/rng/seeded.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AAEnC;;;;;;;;;;;;;GAaG;AACH,qBAAa,SAAU,YAAW,GAAG;IACnC,OAAO,CAAC,EAAE,CAAS;IACnB,OAAO,CAAC,EAAE,CAAS;IACnB,OAAO,CAAC,EAAE,CAAS;IACnB,OAAO,CAAC,EAAE,CAAS;gBAEP,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAelC,OAAO,CAAC,SAAS;IA+BjB,OAAO,CAAC,UAAU;IASlB,OAAO,CAAC,UAAU;IAgBlB,IAAI,IAAI,MAAM;IAKd,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;CAsB1C"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Random Number Generator interface.
3
+ *
4
+ * All dice rolling MUST use this interface - never use Math.random() directly.
5
+ *
6
+ * @module rng/types
7
+ */
8
+ /**
9
+ * Random Number Generator interface for dice rolling.
10
+ */
11
+ export type RNG = {
12
+ /**
13
+ * Returns a random floating-point number in the range [0, 1).
14
+ */
15
+ next(): number;
16
+ /**
17
+ * Returns a random integer in the inclusive range [min, max].
18
+ *
19
+ * @param min - Minimum value (inclusive)
20
+ * @param max - Maximum value (inclusive)
21
+ */
22
+ nextInt(min: number, max: number): number;
23
+ };
24
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/rng/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,MAAM,MAAM,GAAG,GAAG;IAChB;;OAEG;IACH,IAAI,IAAI,MAAM,CAAC;IAEf;;;;;OAKG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3C,CAAC"}
package/dist/roll.d.ts ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Main public API for rolling dice expressions.
3
+ *
4
+ * @module roll
5
+ */
6
+ import type { RNG } from './rng/types';
7
+ import type { RollResult } from './types';
8
+ /**
9
+ * Options for the roll function.
10
+ */
11
+ export type RollOptions = {
12
+ /** Custom RNG instance (takes precedence over seed) */
13
+ rng?: RNG;
14
+ /** Seed for deterministic rolls (ignored if rng provided) */
15
+ seed?: string | number;
16
+ /** Maximum total dice allowed per evaluation (default: 10,000) */
17
+ maxDice?: number;
18
+ /** Maximum explosion iterations allowed per die (default: 1,000) */
19
+ maxExplodeIterations?: number;
20
+ /** Maximum reroll iterations allowed per die (default: 1,000) */
21
+ maxRerollIterations?: number;
22
+ };
23
+ /**
24
+ * Parses and evaluates a dice notation string.
25
+ *
26
+ * @param notation - Dice notation (e.g., "2d6+3", "4d6kh3")
27
+ * @param options - Optional configuration (RNG or seed)
28
+ * @returns Complete roll result with total and metadata
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * // Random roll
33
+ * const result = roll('2d6+3');
34
+ * console.log(result.total); // 5-15
35
+ *
36
+ * // Seeded for reproducibility
37
+ * const r1 = roll('4d6', { seed: 'test' });
38
+ * const r2 = roll('4d6', { seed: 'test' });
39
+ * r1.total === r2.total; // true
40
+ *
41
+ * // Custom RNG for testing
42
+ * const result = roll('1d20', { rng: createMockRng([15]) });
43
+ * result.total; // 15
44
+ * ```
45
+ */
46
+ export declare function roll(notation: string, options?: RollOptions): RollResult;
47
+ //# sourceMappingURL=roll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roll.d.ts","sourceRoot":"","sources":["../src/roll.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EAAmB,UAAU,EAAE,MAAM,SAAS,CAAC;AAM3D;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,uDAAuD;IACvD,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,6DAA6D;IAC7D,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,iEAAiE;IACjE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,UAAU,CAa5E"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Test utilities for roll-parser consumers.
3
+ *
4
+ * Import from `roll-parser/testing` for deterministic dice testing.
5
+ *
6
+ * @module testing
7
+ */
8
+ import { MockRNGExhaustedError as _MockRNGExhaustedError, createMockRng as _createMockRng } from './rng/mock';
9
+ export declare const createMockRng: typeof _createMockRng;
10
+ export declare const MockRNGExhaustedError: typeof _MockRNGExhaustedError;
11
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EACL,qBAAqB,IAAI,sBAAsB,EAC/C,aAAa,IAAI,cAAc,EAChC,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,aAAa,uBAAiB,CAAC;AAC5C,eAAO,MAAM,qBAAqB,+BAAyB,CAAC"}