refactor: enforce mandatory brackets for composite values
Implements Phase 1 and 2 of the syntax rework plan: - Brackets `[]` are now mandatory for all tuple and array values. - Removed the `[Type][]` array syntax; arrays now only use `Type[]`. - `[single]` is now strictly a 1-tuple rather than an array. - Updated documentation and test fixtures to reflect these breaking changes.
This commit is contained in:
+44
-85
@@ -41,7 +41,7 @@ class ValueParser {
|
||||
return false;
|
||||
}
|
||||
|
||||
parseValue(schema: Schema, allowOmitBrackets: boolean = false): unknown {
|
||||
parseValue(schema: Schema): unknown {
|
||||
this.skipWhitespace();
|
||||
|
||||
switch (schema.type) {
|
||||
@@ -60,9 +60,9 @@ class ValueParser {
|
||||
case "union":
|
||||
return this.parseUnionValue(schema);
|
||||
case "tuple":
|
||||
return this.parseTupleValue(schema, allowOmitBrackets);
|
||||
return this.parseTupleValue(schema);
|
||||
case "array":
|
||||
return this.parseArrayValue(schema, allowOmitBrackets);
|
||||
return this.parseArrayValue(schema);
|
||||
case "reference":
|
||||
// Reference values are parsed as strings (IDs) initially, resolved later
|
||||
return this.parseReferenceValue(schema);
|
||||
@@ -250,7 +250,7 @@ class ValueParser {
|
||||
for (let i = 0; i < schema.members.length; i++) {
|
||||
this.pos = savedPos;
|
||||
try {
|
||||
return this.parseValue(schema.members[i], false);
|
||||
return this.parseValue(schema.members[i]);
|
||||
} catch (e) {
|
||||
errors.push(e as Error);
|
||||
// Continue to next member
|
||||
@@ -266,16 +266,8 @@ class ValueParser {
|
||||
);
|
||||
}
|
||||
|
||||
private parseTupleValue(
|
||||
schema: TupleSchema,
|
||||
allowOmitBrackets: boolean,
|
||||
): unknown[] {
|
||||
let hasOpenBracket = false;
|
||||
|
||||
if (this.peek() === "[") {
|
||||
this.consume();
|
||||
hasOpenBracket = true;
|
||||
} else if (!allowOmitBrackets) {
|
||||
private parseTupleValue(schema: TupleSchema): unknown[] {
|
||||
if (!this.consumeStr("[")) {
|
||||
throw new ParseError(
|
||||
"Expected [",
|
||||
this.pos,
|
||||
@@ -286,7 +278,7 @@ class ValueParser {
|
||||
|
||||
this.skipWhitespace();
|
||||
|
||||
if (this.peek() === "]" && hasOpenBracket) {
|
||||
if (this.peek() === "]") {
|
||||
this.consume();
|
||||
return [];
|
||||
}
|
||||
@@ -308,7 +300,7 @@ class ValueParser {
|
||||
}
|
||||
}
|
||||
|
||||
result.push(this.parseValue(elementSchema.schema, false));
|
||||
result.push(this.parseValue(elementSchema.schema));
|
||||
this.skipWhitespace();
|
||||
|
||||
if (i < schema.elements.length - 1) {
|
||||
@@ -325,57 +317,24 @@ class ValueParser {
|
||||
|
||||
this.skipWhitespace();
|
||||
|
||||
if (hasOpenBracket) {
|
||||
if (!this.consumeStr("]")) {
|
||||
throw new ParseError(
|
||||
"Expected ]",
|
||||
this.pos,
|
||||
this.schemaString,
|
||||
this.input,
|
||||
);
|
||||
}
|
||||
if (!this.consumeStr("]")) {
|
||||
throw new ParseError(
|
||||
"Expected ]",
|
||||
this.pos,
|
||||
this.schemaString,
|
||||
this.input,
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private parseArrayValue(
|
||||
schema: ArraySchema,
|
||||
allowOmitBrackets: boolean,
|
||||
): unknown[] {
|
||||
let hasOpenBracket = false;
|
||||
const elementIsTupleOrArray =
|
||||
schema.element.type === "tuple" || schema.element.type === "array";
|
||||
|
||||
private parseArrayValue(schema: ArraySchema): unknown[] {
|
||||
if (this.pos >= this.input.length || !this.input.trim()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (this.peek() === "[") {
|
||||
if (!elementIsTupleOrArray) {
|
||||
this.consume();
|
||||
hasOpenBracket = true;
|
||||
} 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.skipWhitespace();
|
||||
if (this.peek() === "]") {
|
||||
// Empty array []
|
||||
this.consume();
|
||||
return [];
|
||||
} else if (this.peek() === "[") {
|
||||
// Nested brackets [[ - this is the array opener
|
||||
hasOpenBracket = true;
|
||||
} else {
|
||||
// [ belongs to the first element, restore position
|
||||
this.pos = savedPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasOpenBracket && !allowOmitBrackets && !elementIsTupleOrArray) {
|
||||
if (!this.consumeStr("[")) {
|
||||
throw new ParseError(
|
||||
"Expected [",
|
||||
this.pos,
|
||||
@@ -386,7 +345,7 @@ class ValueParser {
|
||||
|
||||
this.skipWhitespace();
|
||||
|
||||
if (this.peek() === "]" && hasOpenBracket) {
|
||||
if (this.peek() === "]") {
|
||||
this.consume();
|
||||
return [];
|
||||
}
|
||||
@@ -395,7 +354,7 @@ class ValueParser {
|
||||
while (true) {
|
||||
this.skipWhitespace();
|
||||
|
||||
result.push(this.parseValue(schema.element, elementIsTupleOrArray));
|
||||
result.push(this.parseValue(schema.element));
|
||||
this.skipWhitespace();
|
||||
|
||||
if (!this.consumeStr(";")) {
|
||||
@@ -405,15 +364,13 @@ class ValueParser {
|
||||
|
||||
this.skipWhitespace();
|
||||
|
||||
if (hasOpenBracket) {
|
||||
if (!this.consumeStr("]")) {
|
||||
throw new ParseError(
|
||||
"Expected ]",
|
||||
this.pos,
|
||||
this.schemaString,
|
||||
this.input,
|
||||
);
|
||||
}
|
||||
if (!this.consumeStr("]")) {
|
||||
throw new ParseError(
|
||||
"Expected ]",
|
||||
this.pos,
|
||||
this.schemaString,
|
||||
this.input,
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -431,15 +388,18 @@ class ValueParser {
|
||||
|
||||
if (schema.isArray) {
|
||||
// Parse array of IDs: [id1; id2; id3]
|
||||
let hasOpenBracket = false;
|
||||
if (this.peek() === "[") {
|
||||
this.consume();
|
||||
hasOpenBracket = true;
|
||||
if (!this.consumeStr("[")) {
|
||||
throw new ParseError(
|
||||
"Expected [",
|
||||
this.pos,
|
||||
this.schemaString,
|
||||
this.input,
|
||||
);
|
||||
}
|
||||
|
||||
this.skipWhitespace();
|
||||
|
||||
if (this.peek() === "]" && hasOpenBracket) {
|
||||
if (this.peek() === "]") {
|
||||
this.consume();
|
||||
return [];
|
||||
}
|
||||
@@ -464,15 +424,15 @@ class ValueParser {
|
||||
}
|
||||
}
|
||||
|
||||
if (hasOpenBracket) {
|
||||
if (!this.consumeStr("]")) {
|
||||
throw new ParseError(
|
||||
"Expected ]",
|
||||
this.pos,
|
||||
this.schemaString,
|
||||
this.input,
|
||||
);
|
||||
}
|
||||
this.skipWhitespace();
|
||||
|
||||
if (!this.consumeStr("]")) {
|
||||
throw new ParseError(
|
||||
"Expected ]",
|
||||
this.pos,
|
||||
this.schemaString,
|
||||
this.input,
|
||||
);
|
||||
}
|
||||
|
||||
return ids;
|
||||
@@ -506,8 +466,7 @@ export function parseValue(
|
||||
): unknown {
|
||||
const sStr = schemaString || schemaToTypeString(schema);
|
||||
const parser = new ValueParser(valueString.trim(), sStr);
|
||||
const allowOmitBrackets = schema.type === "tuple" || schema.type === "array";
|
||||
const value = parser.parseValue(schema, allowOmitBrackets);
|
||||
const value = parser.parseValue(schema);
|
||||
|
||||
if (parser.getPosition() < parser.getInputLength()) {
|
||||
throw new ParseError(
|
||||
|
||||
Reference in New Issue
Block a user