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.
- package/README.md +47 -178
- package/dist/cli/args.d.ts +35 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/format.d.ts +20 -0
- package/dist/cli/format.d.ts.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli.js +1775 -0
- package/dist/errors.d.ts +39 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/evaluator/evaluator.d.ts +65 -0
- package/dist/evaluator/evaluator.d.ts.map +1 -0
- package/dist/evaluator/index.d.ts +8 -0
- package/dist/evaluator/index.d.ts.map +1 -0
- package/dist/evaluator/modifiers/compare.d.ts +15 -0
- package/dist/evaluator/modifiers/compare.d.ts.map +1 -0
- package/dist/evaluator/modifiers/explode.d.ts +44 -0
- package/dist/evaluator/modifiers/explode.d.ts.map +1 -0
- package/dist/evaluator/modifiers/keep-drop.d.ts +44 -0
- package/dist/evaluator/modifiers/keep-drop.d.ts.map +1 -0
- package/dist/evaluator/modifiers/reroll.d.ts +33 -0
- package/dist/evaluator/modifiers/reroll.d.ts.map +1 -0
- package/dist/evaluator/modifiers/success-count.d.ts +28 -0
- package/dist/evaluator/modifiers/success-count.d.ts.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1723 -0
- package/dist/index.mjs +1724 -0
- package/dist/lexer/lexer.d.ts +75 -0
- package/dist/lexer/lexer.d.ts.map +1 -0
- package/dist/lexer/tokens.d.ts +87 -0
- package/dist/lexer/tokens.d.ts.map +1 -0
- package/dist/parser/ast.d.ts +183 -0
- package/dist/parser/ast.d.ts.map +1 -0
- package/dist/parser/parser.d.ts +102 -0
- package/dist/parser/parser.d.ts.map +1 -0
- package/dist/rng/index.d.ts +8 -0
- package/dist/rng/index.d.ts.map +1 -0
- package/dist/rng/mock.d.ts +36 -0
- package/dist/rng/mock.d.ts.map +1 -0
- package/dist/rng/seeded.d.ts +33 -0
- package/dist/rng/seeded.d.ts.map +1 -0
- package/dist/rng/types.d.ts +24 -0
- package/dist/rng/types.d.ts.map +1 -0
- package/dist/roll.d.ts +47 -0
- package/dist/roll.d.ts.map +1 -0
- package/dist/testing.d.ts +11 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +38 -0
- package/dist/testing.mjs +39 -0
- package/dist/types.d.ts +105 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +62 -43
- package/src/cli/args.ts +72 -0
- package/src/cli/format.ts +45 -0
- package/src/cli/index.ts +75 -0
- package/src/errors.ts +78 -0
- package/src/evaluator/evaluator.ts +837 -0
- package/src/evaluator/index.ts +14 -0
- package/src/evaluator/modifiers/compare.ts +29 -0
- package/src/evaluator/modifiers/explode.ts +215 -0
- package/src/evaluator/modifiers/keep-drop.ts +151 -0
- package/src/evaluator/modifiers/reroll.ts +172 -0
- package/src/evaluator/modifiers/success-count.ts +59 -0
- package/src/index.ts +72 -0
- package/src/lexer/lexer.ts +281 -0
- package/src/lexer/tokens.ts +147 -0
- package/src/parser/ast.ts +261 -0
- package/src/parser/parser.ts +716 -0
- package/src/rng/index.ts +8 -0
- package/src/rng/mock.ts +65 -0
- package/src/rng/seeded.ts +127 -0
- package/src/rng/types.ts +25 -0
- package/src/roll.ts +66 -0
- package/src/testing.ts +16 -0
- package/src/types.ts +112 -0
- package/bin/help.txt +0 -19
- package/bin/roll-parser.js +0 -23
- package/dist/roll-parser.js +0 -697
- package/dist/roll-parser.min.js +0 -1
- package/index.js +0 -24
- package/src/complex/parse.js +0 -37
- package/src/complex/parseAndRoll.js +0 -21
- package/src/complex/parseAndRollClassic.js +0 -20
- package/src/complex/parseAndRollSimple.js +0 -19
- package/src/complex/parseAndRollWod.js +0 -20
- package/src/complex/parseClassicRoll.js +0 -22
- package/src/complex/parseSimpleRoll.js +0 -21
- package/src/complex/parseWodRoll.js +0 -21
- package/src/converter.js +0 -45
- package/src/grammar.js +0 -14
- package/src/mapper.js +0 -50
- package/src/normalizer.js +0 -51
- package/src/object/Result.js +0 -23
- package/src/object/Roll.js +0 -26
- package/src/object/Type.js +0 -5
- package/src/object/WodRoll.js +0 -29
- package/src/parser.js +0 -43
- package/src/random.js +0 -22
- package/src/roller.js +0 -106
- package/src/stringifier.js +0 -33
package/src/index.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Roll Parser - Dice notation parser for tabletop RPGs.
|
|
3
|
+
*
|
|
4
|
+
* @module roll-parser
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// * Error hierarchy
|
|
8
|
+
export { RollParserError, isRollParserError } from './errors';
|
|
9
|
+
export type { RollParserErrorCode } from './errors';
|
|
10
|
+
|
|
11
|
+
// * Lexer exports
|
|
12
|
+
export { LexerError } from './lexer/lexer';
|
|
13
|
+
|
|
14
|
+
// * Parser exports
|
|
15
|
+
export { parse, ParseError } from './parser/parser';
|
|
16
|
+
export type {
|
|
17
|
+
ASTNode,
|
|
18
|
+
BinaryOpNode,
|
|
19
|
+
DiceNode,
|
|
20
|
+
ExplodeNode,
|
|
21
|
+
FateDiceNode,
|
|
22
|
+
FunctionCallNode,
|
|
23
|
+
LiteralNode,
|
|
24
|
+
ModifierNode,
|
|
25
|
+
RerollNode,
|
|
26
|
+
SuccessCountNode,
|
|
27
|
+
UnaryOpNode,
|
|
28
|
+
VersusNode,
|
|
29
|
+
} from './parser/ast';
|
|
30
|
+
export {
|
|
31
|
+
isBinaryOp,
|
|
32
|
+
isDice,
|
|
33
|
+
isExplode,
|
|
34
|
+
isFateDice,
|
|
35
|
+
isFunctionCall,
|
|
36
|
+
isLiteral,
|
|
37
|
+
isModifier,
|
|
38
|
+
isReroll,
|
|
39
|
+
isSuccessCount,
|
|
40
|
+
isUnaryOp,
|
|
41
|
+
isVersus,
|
|
42
|
+
} from './parser/ast';
|
|
43
|
+
|
|
44
|
+
// * RNG exports
|
|
45
|
+
export type { RNG } from './rng/types';
|
|
46
|
+
export { SeededRNG } from './rng/seeded';
|
|
47
|
+
|
|
48
|
+
// * Evaluator exports
|
|
49
|
+
export {
|
|
50
|
+
DEFAULT_MAX_DICE,
|
|
51
|
+
DEFAULT_MAX_EXPLODE_ITERATIONS,
|
|
52
|
+
DEFAULT_MAX_REROLL_ITERATIONS,
|
|
53
|
+
evaluate,
|
|
54
|
+
EvaluatorError,
|
|
55
|
+
} from './evaluator/evaluator';
|
|
56
|
+
export type {
|
|
57
|
+
CompareOp,
|
|
58
|
+
ComparePoint,
|
|
59
|
+
DieModifier,
|
|
60
|
+
DieResult,
|
|
61
|
+
EvaluateOptions,
|
|
62
|
+
RollResult,
|
|
63
|
+
} from './types';
|
|
64
|
+
export { DegreeOfSuccess } from './types';
|
|
65
|
+
|
|
66
|
+
// * Public API
|
|
67
|
+
export { roll } from './roll';
|
|
68
|
+
export type { RollOptions } from './roll';
|
|
69
|
+
|
|
70
|
+
import pkg from '../package.json';
|
|
71
|
+
|
|
72
|
+
export const VERSION: string = pkg.version;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dice notation lexer with character-by-character scanning.
|
|
3
|
+
*
|
|
4
|
+
* @module lexer/lexer
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { RollParserErrorCode } from '../errors';
|
|
8
|
+
import { RollParserError } from '../errors';
|
|
9
|
+
import { type Token, TokenType } from './tokens';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown when the lexer encounters an invalid character.
|
|
13
|
+
*/
|
|
14
|
+
export class LexerError extends RollParserError {
|
|
15
|
+
readonly position: number;
|
|
16
|
+
readonly character: string;
|
|
17
|
+
|
|
18
|
+
constructor(message: string, code: RollParserErrorCode, position: number, character: string) {
|
|
19
|
+
super(`${message} at position ${position}: '${character}'`, code);
|
|
20
|
+
this.name = 'LexerError';
|
|
21
|
+
this.position = position;
|
|
22
|
+
this.character = character;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Known identifier keywords mapped to their token types. */
|
|
27
|
+
const IDENTIFIER_KEYWORDS: Record<string, TokenType> = {
|
|
28
|
+
kh: TokenType.KEEP_HIGH,
|
|
29
|
+
kl: TokenType.KEEP_LOW,
|
|
30
|
+
k: TokenType.KEEP_HIGH,
|
|
31
|
+
dh: TokenType.DROP_HIGH,
|
|
32
|
+
dl: TokenType.DROP_LOW,
|
|
33
|
+
d: TokenType.DICE,
|
|
34
|
+
r: TokenType.REROLL,
|
|
35
|
+
ro: TokenType.REROLL_ONCE,
|
|
36
|
+
f: TokenType.FAIL,
|
|
37
|
+
vs: TokenType.VS,
|
|
38
|
+
floor: TokenType.FUNCTION,
|
|
39
|
+
ceil: TokenType.FUNCTION,
|
|
40
|
+
round: TokenType.FUNCTION,
|
|
41
|
+
abs: TokenType.FUNCTION,
|
|
42
|
+
max: TokenType.FUNCTION,
|
|
43
|
+
min: TokenType.FUNCTION,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Lexer for dice notation.
|
|
48
|
+
*
|
|
49
|
+
* Produces a stream of tokens from an input string using character-by-character
|
|
50
|
+
* scanning with maximal munch for multi-character tokens.
|
|
51
|
+
*/
|
|
52
|
+
export class Lexer {
|
|
53
|
+
private pos = 0;
|
|
54
|
+
private readonly input: string;
|
|
55
|
+
|
|
56
|
+
constructor(input: string) {
|
|
57
|
+
this.input = input;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Tokenize the entire input and return all tokens.
|
|
62
|
+
*/
|
|
63
|
+
tokenize(): Token[] {
|
|
64
|
+
const tokens: Token[] = [];
|
|
65
|
+
|
|
66
|
+
while (true) {
|
|
67
|
+
const token = this.nextToken();
|
|
68
|
+
tokens.push(token);
|
|
69
|
+
if (token.type === TokenType.EOF) break;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return tokens;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get the next token from the input.
|
|
77
|
+
*/
|
|
78
|
+
nextToken(): Token {
|
|
79
|
+
this.skipWhitespace();
|
|
80
|
+
|
|
81
|
+
if (this.isAtEnd()) {
|
|
82
|
+
return this.createToken(TokenType.EOF, '');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const startPos = this.pos;
|
|
86
|
+
const char = this.peek();
|
|
87
|
+
|
|
88
|
+
// * Numbers
|
|
89
|
+
if (this.isDigit(char)) {
|
|
90
|
+
return this.scanNumber();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// * Identifiers (d, kh, kl, dh, dl, r, ro, f, vs, floor, ceil, ...)
|
|
94
|
+
if (this.isAlpha(char)) {
|
|
95
|
+
return this.scanIdentifier();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// * Operators and punctuation
|
|
99
|
+
this.advance();
|
|
100
|
+
|
|
101
|
+
switch (char) {
|
|
102
|
+
case '+':
|
|
103
|
+
return this.createTokenAt(TokenType.PLUS, char, startPos);
|
|
104
|
+
case '-':
|
|
105
|
+
return this.createTokenAt(TokenType.MINUS, char, startPos);
|
|
106
|
+
case '*':
|
|
107
|
+
if (this.match('*')) {
|
|
108
|
+
return this.createTokenAt(TokenType.POWER, '**', startPos);
|
|
109
|
+
}
|
|
110
|
+
return this.createTokenAt(TokenType.MULTIPLY, char, startPos);
|
|
111
|
+
case '/':
|
|
112
|
+
return this.createTokenAt(TokenType.DIVIDE, char, startPos);
|
|
113
|
+
case '%':
|
|
114
|
+
return this.createTokenAt(TokenType.MODULO, char, startPos);
|
|
115
|
+
case '^':
|
|
116
|
+
return this.createTokenAt(TokenType.POWER, char, startPos);
|
|
117
|
+
case '(':
|
|
118
|
+
return this.createTokenAt(TokenType.LPAREN, char, startPos);
|
|
119
|
+
case ')':
|
|
120
|
+
return this.createTokenAt(TokenType.RPAREN, char, startPos);
|
|
121
|
+
case ',':
|
|
122
|
+
return this.createTokenAt(TokenType.COMMA, char, startPos);
|
|
123
|
+
case '>':
|
|
124
|
+
if (this.match('=')) {
|
|
125
|
+
return this.createTokenAt(TokenType.GREATER_EQUAL, '>=', startPos);
|
|
126
|
+
}
|
|
127
|
+
return this.createTokenAt(TokenType.GREATER, char, startPos);
|
|
128
|
+
case '<':
|
|
129
|
+
if (this.match('=')) {
|
|
130
|
+
return this.createTokenAt(TokenType.LESS_EQUAL, '<=', startPos);
|
|
131
|
+
}
|
|
132
|
+
return this.createTokenAt(TokenType.LESS, char, startPos);
|
|
133
|
+
case '=':
|
|
134
|
+
return this.createTokenAt(TokenType.EQUAL, char, startPos);
|
|
135
|
+
case '!':
|
|
136
|
+
if (this.match('!')) {
|
|
137
|
+
return this.createTokenAt(TokenType.EXPLODE_COMPOUND, '!!', startPos);
|
|
138
|
+
}
|
|
139
|
+
if (!this.isAtEnd() && this.peek().toLowerCase() === 'p') {
|
|
140
|
+
this.advance();
|
|
141
|
+
return this.createTokenAt(TokenType.EXPLODE_PENETRATING, '!p', startPos);
|
|
142
|
+
}
|
|
143
|
+
return this.createTokenAt(TokenType.EXPLODE, char, startPos);
|
|
144
|
+
default:
|
|
145
|
+
throw new LexerError('Unexpected character', 'UNEXPECTED_CHARACTER', startPos, char);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// * Private helpers
|
|
150
|
+
|
|
151
|
+
private skipWhitespace(): void {
|
|
152
|
+
while (!this.isAtEnd() && this.isWhitespace(this.peek())) {
|
|
153
|
+
this.advance();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private scanNumber(): Token {
|
|
158
|
+
const startPos = this.pos;
|
|
159
|
+
let value = '';
|
|
160
|
+
|
|
161
|
+
// Integer part
|
|
162
|
+
while (!this.isAtEnd() && this.isDigit(this.peek())) {
|
|
163
|
+
value += this.advance();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Decimal part
|
|
167
|
+
if (!this.isAtEnd() && this.peek() === '.' && this.isDigit(this.peekNext())) {
|
|
168
|
+
value += this.advance(); // consume '.'
|
|
169
|
+
while (!this.isAtEnd() && this.isDigit(this.peek())) {
|
|
170
|
+
value += this.advance();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return this.createTokenAt(TokenType.NUMBER, value, startPos);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Scans an identifier using full-accumulation: collects all consecutive
|
|
179
|
+
* alpha characters, then classifies the result against known keywords.
|
|
180
|
+
*
|
|
181
|
+
* Special cases run before/after the accumulation loop:
|
|
182
|
+
* - `dF` / `Df` / `dF` / `DF` produces DICE_FATE. Must be handled BEFORE
|
|
183
|
+
* the loop because `F` is alpha and would otherwise be greedily merged
|
|
184
|
+
* into identifiers like `dfkh` (from `4dFkh2`) or `dfdf` (from `dFdF`).
|
|
185
|
+
* Reserves the `d[fF]` prefix namespace for Fate dice.
|
|
186
|
+
* - Bare `d` followed by `%` produces DICE_PERCENT. `%` is not alpha so the
|
|
187
|
+
* accumulation loop stops naturally and the post-loop check handles it.
|
|
188
|
+
*/
|
|
189
|
+
private scanIdentifier(): Token {
|
|
190
|
+
const startPos = this.pos;
|
|
191
|
+
|
|
192
|
+
const first = this.peek();
|
|
193
|
+
const second = this.peekNext();
|
|
194
|
+
if ((first === 'd' || first === 'D') && (second === 'f' || second === 'F')) {
|
|
195
|
+
this.advance();
|
|
196
|
+
this.advance();
|
|
197
|
+
return this.createTokenAt(TokenType.DICE_FATE, 'df', startPos);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
let value = '';
|
|
201
|
+
|
|
202
|
+
while (!this.isAtEnd() && this.isAlpha(this.peek())) {
|
|
203
|
+
value += this.advance();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const lower = value.toLowerCase();
|
|
207
|
+
|
|
208
|
+
if (lower === 'd' && !this.isAtEnd() && this.peek() === '%') {
|
|
209
|
+
this.advance();
|
|
210
|
+
return this.createTokenAt(TokenType.DICE_PERCENT, 'd%', startPos);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const tokenType = IDENTIFIER_KEYWORDS[lower];
|
|
214
|
+
if (tokenType != null) {
|
|
215
|
+
return this.createTokenAt(tokenType, lower, startPos);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
throw new LexerError('Unexpected identifier', 'UNEXPECTED_IDENTIFIER', startPos, lower);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private peek(): string {
|
|
222
|
+
return this.input[this.pos] ?? '';
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private peekNext(): string {
|
|
226
|
+
return this.input[this.pos + 1] ?? '';
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
private advance(): string {
|
|
230
|
+
return this.input[this.pos++] ?? '';
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private match(expected: string): boolean {
|
|
234
|
+
if (this.isAtEnd()) return false;
|
|
235
|
+
if (this.input[this.pos] !== expected) return false;
|
|
236
|
+
this.pos++;
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private isAtEnd(): boolean {
|
|
241
|
+
return this.pos >= this.input.length;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private isDigit(char: string): boolean {
|
|
245
|
+
return char >= '0' && char <= '9';
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private isAlpha(char: string): boolean {
|
|
249
|
+
const c = char.toLowerCase();
|
|
250
|
+
return c >= 'a' && c <= 'z';
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private isWhitespace(char: string): boolean {
|
|
254
|
+
return char === ' ' || char === '\t' || char === '\n' || char === '\r';
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private createToken(type: TokenType, value: string): Token {
|
|
258
|
+
return { type, value, position: this.pos };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
private createTokenAt(type: TokenType, value: string, position: number): Token {
|
|
262
|
+
return { type, value, position };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Tokenize a dice notation string.
|
|
268
|
+
*
|
|
269
|
+
* @param input - The dice notation to tokenize
|
|
270
|
+
* @returns Array of tokens including EOF
|
|
271
|
+
* @throws {LexerError} If an invalid character is encountered
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```typescript
|
|
275
|
+
* const tokens = lex('2d20+5');
|
|
276
|
+
* // [NUMBER(2), DICE, NUMBER(20), PLUS, NUMBER(5), EOF]
|
|
277
|
+
* ```
|
|
278
|
+
*/
|
|
279
|
+
export function lex(input: string): Token[] {
|
|
280
|
+
return new Lexer(input).tokenize();
|
|
281
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token type definitions for the dice notation lexer.
|
|
3
|
+
*
|
|
4
|
+
* @module lexer/tokens
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Token types for dice notation.
|
|
9
|
+
*
|
|
10
|
+
* Grouped semantically. Numeric values are stable identifiers — the specific
|
|
11
|
+
* numbers don't matter, but they must be unique.
|
|
12
|
+
*/
|
|
13
|
+
export enum TokenType {
|
|
14
|
+
//
|
|
15
|
+
// * Literals
|
|
16
|
+
//
|
|
17
|
+
|
|
18
|
+
/** Numeric literal: integer or decimal */
|
|
19
|
+
NUMBER = 0,
|
|
20
|
+
|
|
21
|
+
//
|
|
22
|
+
// * Dice operators
|
|
23
|
+
//
|
|
24
|
+
|
|
25
|
+
/** Dice operator: 'd' or 'D' */
|
|
26
|
+
DICE = 1,
|
|
27
|
+
/** Percentile dice operator: 'd%' (alias for d100) */
|
|
28
|
+
DICE_PERCENT = 2,
|
|
29
|
+
/** Fate/Fudge dice operator: 'dF' */
|
|
30
|
+
DICE_FATE = 3,
|
|
31
|
+
|
|
32
|
+
//
|
|
33
|
+
// * Arithmetic operators
|
|
34
|
+
//
|
|
35
|
+
|
|
36
|
+
/** Addition operator: '+' */
|
|
37
|
+
PLUS = 4,
|
|
38
|
+
/** Subtraction operator: '-' */
|
|
39
|
+
MINUS = 5,
|
|
40
|
+
/** Multiplication operator: '*' */
|
|
41
|
+
MULTIPLY = 6,
|
|
42
|
+
/** Division operator: '/' */
|
|
43
|
+
DIVIDE = 7,
|
|
44
|
+
/** Modulo operator: '%' */
|
|
45
|
+
MODULO = 8,
|
|
46
|
+
/** Power operator: '**' or '^' */
|
|
47
|
+
POWER = 9,
|
|
48
|
+
|
|
49
|
+
//
|
|
50
|
+
// * Comparison operators
|
|
51
|
+
//
|
|
52
|
+
|
|
53
|
+
/** Greater than: '>' */
|
|
54
|
+
GREATER = 10,
|
|
55
|
+
/** Greater than or equal: '>=' */
|
|
56
|
+
GREATER_EQUAL = 11,
|
|
57
|
+
/** Less than: '<' */
|
|
58
|
+
LESS = 12,
|
|
59
|
+
/** Less than or equal: '<=' */
|
|
60
|
+
LESS_EQUAL = 13,
|
|
61
|
+
/** Equal: '=' */
|
|
62
|
+
EQUAL = 14,
|
|
63
|
+
|
|
64
|
+
//
|
|
65
|
+
// * Grouping and punctuation
|
|
66
|
+
//
|
|
67
|
+
|
|
68
|
+
/** Left parenthesis: '(' */
|
|
69
|
+
LPAREN = 15,
|
|
70
|
+
/** Right parenthesis: ')' */
|
|
71
|
+
RPAREN = 16,
|
|
72
|
+
/** Argument separator: ',' */
|
|
73
|
+
COMMA = 17,
|
|
74
|
+
|
|
75
|
+
//
|
|
76
|
+
// * Keep/drop modifiers
|
|
77
|
+
//
|
|
78
|
+
|
|
79
|
+
/** Keep highest modifier: 'kh' or 'k' */
|
|
80
|
+
KEEP_HIGH = 18,
|
|
81
|
+
/** Keep lowest modifier: 'kl' */
|
|
82
|
+
KEEP_LOW = 19,
|
|
83
|
+
/** Drop highest modifier: 'dh' */
|
|
84
|
+
DROP_HIGH = 20,
|
|
85
|
+
/** Drop lowest modifier: 'dl' */
|
|
86
|
+
DROP_LOW = 21,
|
|
87
|
+
|
|
88
|
+
//
|
|
89
|
+
// * Explode modifiers
|
|
90
|
+
//
|
|
91
|
+
|
|
92
|
+
/** Standard explode: '!' */
|
|
93
|
+
EXPLODE = 22,
|
|
94
|
+
/** Compounding explode: '!!' */
|
|
95
|
+
EXPLODE_COMPOUND = 23,
|
|
96
|
+
/** Penetrating explode: '!p' */
|
|
97
|
+
EXPLODE_PENETRATING = 24,
|
|
98
|
+
|
|
99
|
+
//
|
|
100
|
+
// * Reroll modifiers
|
|
101
|
+
//
|
|
102
|
+
|
|
103
|
+
/** Recursive reroll: 'r' */
|
|
104
|
+
REROLL = 25,
|
|
105
|
+
/** Reroll once: 'ro' */
|
|
106
|
+
REROLL_ONCE = 26,
|
|
107
|
+
|
|
108
|
+
//
|
|
109
|
+
// * Success counting
|
|
110
|
+
//
|
|
111
|
+
|
|
112
|
+
/** Fail marker: 'f' */
|
|
113
|
+
FAIL = 27,
|
|
114
|
+
|
|
115
|
+
//
|
|
116
|
+
// * Functions
|
|
117
|
+
//
|
|
118
|
+
|
|
119
|
+
/** Math function: 'floor', 'ceil', 'round', 'abs', 'max', 'min' */
|
|
120
|
+
FUNCTION = 28,
|
|
121
|
+
|
|
122
|
+
//
|
|
123
|
+
// * Keywords
|
|
124
|
+
//
|
|
125
|
+
|
|
126
|
+
/** Versus operator: 'vs' */
|
|
127
|
+
VS = 29,
|
|
128
|
+
|
|
129
|
+
//
|
|
130
|
+
// * End of input
|
|
131
|
+
//
|
|
132
|
+
|
|
133
|
+
/** End of input marker */
|
|
134
|
+
EOF = 30,
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* A token produced by the lexer.
|
|
139
|
+
*/
|
|
140
|
+
export type Token = {
|
|
141
|
+
/** The type of this token */
|
|
142
|
+
type: TokenType;
|
|
143
|
+
/** The raw string value from input (lowercased for identifiers) */
|
|
144
|
+
value: string;
|
|
145
|
+
/** Zero-based position in the input string */
|
|
146
|
+
position: number;
|
|
147
|
+
};
|