fix(journal): tokenize bare dice notation

The tokenizer only recognized dice when the pattern started with a count (3d6), so a bare d20 fell through to the identifier branch and failed as an unknown identifier. Add a dedicated branch for the bare d-form; DICE_RE already accepted it.
This commit is contained in:
hyper
2026-09-12 23:44:24 +08:00
parent 3532be2335
commit e838f9a4b1
2 changed files with 31 additions and 1 deletions
@@ -379,6 +379,20 @@ describe("evaluateExpression", () => {
expect(result.value.value).toBeLessThanOrEqual(23);
});
test("evaluates bare dice notation", () => {
const result = evaluateExpression("d20", { lookup: () => undefined });
expect(result.value.kind).toBe("number");
expect(result.value.value).toBeGreaterThanOrEqual(1);
expect(result.value.value).toBeLessThanOrEqual(20);
});
test("evaluates bare dice alongside modifiers", () => {
const result = evaluateExpression("d6 + 5", { lookup: () => undefined });
expect(result.value.kind).toBe("number");
expect(result.value.value).toBeGreaterThanOrEqual(6);
expect(result.value.value).toBeLessThanOrEqual(11);
});
test("throws on division by zero", () => {
expect(() =>
evaluateExpression("10 / 0", { lookup: () => undefined }),
+17 -1
View File
@@ -6,7 +6,7 @@
* - Number literals (integer or decimal)
* - Tagmap literals: #warrior:1;#druid:2 (bare #warrior → #warrior:1)
* - $var references (resolved via lookup; auto-detects number vs tagmap)
* - Dice patterns: 3d6, 2d8kh1, etc. (delegates to rollFormula)
* - Dice patterns: 3d6, 2d8kh1, d20, etc. (delegates to rollFormula)
* - Arithmetic: + - * / (type-checked via registry)
* - Functions: floor(x), ceil(x), round(x) (numbers only)
* - Parentheses for grouping
@@ -225,6 +225,22 @@ function tokenize(input: string): Token[] {
continue;
}
// Dice pattern without a leading count: e.g. "d20", "d6kh1". The number
// branch above only catches dice that start with a digit ("3d6"), so bare
// forms need their own branch before the generic identifier branch.
if (/[dD]/.test(ch) && i + 1 < input.length && /[0-9]/.test(input[i + 1])) {
let dice = "";
while (i < input.length && /[a-zA-Z0-9]/.test(input[i])) {
dice += input[i];
i++;
}
if (!DICE_RE.test(dice)) {
throw new Error(`Invalid dice notation: "${dice}"`);
}
tokens.push({ kind: "number", value: String(rollDice(dice)), raw: dice });
continue;
}
// Tagmap literal: #warrior:1;#druid:2 or bare #warrior
if (ch === "#") {
let raw = "";