fix(parser): fix empty array and nested bracket parsing

Improve the `ValueParser` to correctly disambiguate between empty
arrays `[]` and the start of nested structures. This ensures that
the parser can distinguish between an array containing a single
element that starts with a bracket and an actual empty array.

Update tests to reflect that parsed tuples are returned as arrays
rather than objects.
This commit is contained in:
hypercross 2026-04-22 00:28:10 +08:00
parent 585f33b856
commit b5be558b57
2 changed files with 27 additions and 20 deletions

View File

@ -183,16 +183,8 @@ describe("parseCsv - references in combinatory schemas", () => {
const cactusEnemies = result.data[0].enemies as unknown[]; const cactusEnemies = result.data[0].enemies as unknown[];
expect(cactusEnemies).toHaveLength(2); expect(cactusEnemies).toHaveLength(2);
expect(cactusEnemies[0]).toEqual({ expect(cactusEnemies[0]).toEqual(["仙人掌怪", 12, []]);
name: "仙人掌怪", expect(cactusEnemies[1]).toEqual(["仙人掌怪", 12, []]);
hp: 12,
effects: [],
});
expect(cactusEnemies[1]).toEqual({
name: "仙人掌怪",
hp: 12,
effects: [],
});
// Second row: snake_pair // Second row: snake_pair
expect(result.data[1]).toMatchObject({ expect(result.data[1]).toMatchObject({
@ -206,13 +198,13 @@ describe("parseCsv - references in combinatory schemas", () => {
const snakeEnemies = result.data[1].enemies as unknown[]; const snakeEnemies = result.data[1].enemies as unknown[];
expect(snakeEnemies).toHaveLength(1); expect(snakeEnemies).toHaveLength(1);
expect(snakeEnemies[0]).toEqual({ expect(snakeEnemies[0]).toEqual([
name: "蛇", "蛇",
hp: 10, 10,
effects: [ [
{ effect: "poison", stacks: 2 }, ["poison", 2],
{ effect: "quick", stacks: 1 }, ["quick", 1],
], ],
}); ]);
}); });
}); });

View File

@ -304,9 +304,23 @@ class ValueParser {
if (!elementIsTupleOrArray) { if (!elementIsTupleOrArray) {
this.consume(); this.consume();
hasOpenBracket = true; hasOpenBracket = true;
} else if (this.input[this.pos + 1] === "[") { } else {
// Element is tuple or array - need to disambiguate
// Save position to check if this is an empty array
const savedPos = this.pos;
this.consume(); this.consume();
this.skipWhitespace();
if (this.peek() === "]") {
// Empty array []
this.consume();
return [];
} else if (this.peek() === "[") {
// Nested brackets [[ - this is the array opener
hasOpenBracket = true; hasOpenBracket = true;
} else {
// [ belongs to the first element, restore position
this.pos = savedPos;
}
} }
} }
@ -324,7 +338,8 @@ class ValueParser {
const result: unknown[] = []; const result: unknown[] = [];
while (true) { while (true) {
this.skipWhitespace(); this.skipWhitespace();
result.push(this.parseValue(schema.element, false));
result.push(this.parseValue(schema.element, elementIsTupleOrArray));
this.skipWhitespace(); this.skipWhitespace();
if (!this.consumeStr(";")) { if (!this.consumeStr(";")) {