refactor: support string literals without quotes

This commit is contained in:
hypercross 2026-04-13 10:45:54 +08:00
parent 51a11a26bf
commit 97c8b1966c
2 changed files with 84 additions and 36 deletions

View File

@ -26,6 +26,15 @@ describe('Type generation for string literals and unions', () => {
expect(schema.schema.type).toBe('stringLiteral'); expect(schema.schema.type).toBe('stringLiteral');
expect(schema.parse('"hello-world"')).toBe('hello-world'); expect(schema.parse('"hello-world"')).toBe('hello-world');
}); });
it('should parse string literal without quotes', () => {
const schema = defineSchema('"on"');
expect(schema.schema.type).toBe('stringLiteral');
// 不带引号的值应该像普通字符串一样解析
expect(schema.parse('on')).toBe('on');
expect(schema.validator('on')).toBe(true);
expect(schema.validator('off')).toBe(false);
});
}); });
describe('Union schema', () => { describe('Union schema', () => {
@ -42,10 +51,12 @@ describe('Type generation for string literals and unions', () => {
it('should generate correct type for union of string literals', () => { it('should generate correct type for union of string literals', () => {
const schema = defineSchema('"on" | "off"'); const schema = defineSchema('"on" | "off"');
expect(schema.schema.type).toBe('union'); expect(schema.schema.type).toBe('union');
// Both values should be valid // 带引号和不带引号的值都应该有效
expect(schema.parse('"on"')).toBe('on'); expect(schema.parse('"on"')).toBe('on');
expect(schema.parse('"off"')).toBe('off'); expect(schema.parse('"off"')).toBe('off');
expect(schema.parse('on')).toBe('on');
expect(schema.parse('off')).toBe('off');
expect(schema.validator('on')).toBe(true); expect(schema.validator('on')).toBe(true);
expect(schema.validator('off')).toBe(true); expect(schema.validator('off')).toBe(true);
expect(schema.validator('maybe')).toBe(false); expect(schema.validator('maybe')).toBe(false);
@ -70,19 +81,29 @@ describe('Type generation for string literals and unions', () => {
it('should handle tuple with union field', () => { it('should handle tuple with union field', () => {
const schema = defineSchema('[name: string; status: "active" | "inactive"]'); const schema = defineSchema('[name: string; status: "active" | "inactive"]');
expect(schema.schema.type).toBe('tuple'); expect(schema.schema.type).toBe('tuple');
const value = schema.parse('[john; "active"]'); // 带引号和不带引号的值都应该有效
expect(value).toEqual(['john', 'active']); const value1 = schema.parse('[john; "active"]');
expect(value1).toEqual(['john', 'active']);
expect(schema.validator(['john', 'active'])).toBe(true); expect(schema.validator(['john', 'active'])).toBe(true);
const value2 = schema.parse('[john; active]');
expect(value2).toEqual(['john', 'active']);
expect(schema.validator(['john', 'unknown'])).toBe(false); expect(schema.validator(['john', 'unknown'])).toBe(false);
}); });
it('should handle array of unions', () => { it('should handle array of unions', () => {
const schema = defineSchema('("pending" | "approved" | "rejected")[]'); const schema = defineSchema('("pending" | "approved" | "rejected")[]');
expect(schema.schema.type).toBe('array'); expect(schema.schema.type).toBe('array');
// 带引号和不带引号的值都应该有效
const value1 = schema.parse('["pending"; "approved"]');
expect(value1).toEqual(['pending', 'approved']);
const value2 = schema.parse('[pending; approved]');
expect(value2).toEqual(['pending', 'approved']);
const value = schema.parse('["pending"; "approved"]');
expect(value).toEqual(['pending', 'approved']);
expect(schema.validator(['pending', 'approved'])).toBe(true); expect(schema.validator(['pending', 'approved'])).toBe(true);
expect(schema.validator(['pending', 'unknown'])).toBe(false); expect(schema.validator(['pending', 'unknown'])).toBe(false);
}); });
@ -90,9 +111,14 @@ describe('Type generation for string literals and unions', () => {
it('should handle array of string literals', () => { it('should handle array of string literals', () => {
const schema = defineSchema('"item"[]'); const schema = defineSchema('"item"[]');
expect(schema.schema.type).toBe('array'); expect(schema.schema.type).toBe('array');
// 带引号和不带引号的值都应该有效
const value1 = schema.parse('["item"; "item"; "item"]');
expect(value1).toEqual(['item', 'item', 'item']);
const value2 = schema.parse('[item; item; item]');
expect(value2).toEqual(['item', 'item', 'item']);
const value = schema.parse('["item"; "item"; "item"]');
expect(value).toEqual(['item', 'item', 'item']);
expect(schema.validator(['item', 'item'])).toBe(true); expect(schema.validator(['item', 'item'])).toBe(true);
expect(schema.validator(['item', 'other'])).toBe(false); expect(schema.validator(['item', 'other'])).toBe(false);
}); });

View File

@ -126,40 +126,62 @@ class ValueParser {
private parseStringLiteralValue(schema: StringLiteralSchema): string { private parseStringLiteralValue(schema: StringLiteralSchema): string {
const quote = this.peek(); const quote = this.peek();
if (quote !== '"' && quote !== "'") {
throw new ParseError(`Expected string literal with quotes for value "${schema.value}"`, this.pos);
}
this.consume(); // Consume opening quote
let value = ''; // 支持带引号或不带引号的字符串值
while (this.pos < this.input.length) { if (quote === '"' || quote === "'") {
const char = this.peek(); this.consume(); // Consume opening quote
if (char === '\\') { let value = '';
this.consume(); while (this.pos < this.input.length) {
const nextChar = this.consume(); const char = this.peek();
if (nextChar === '"' || nextChar === "'" || nextChar === '\\' || nextChar === ';') {
value += nextChar; if (char === '\\') {
this.consume();
const nextChar = this.consume();
if (nextChar === '"' || nextChar === "'" || nextChar === '\\' || nextChar === ';') {
value += nextChar;
} else {
value += '\\' + nextChar;
}
} else if (char === quote) {
this.consume(); // Consume closing quote
if (value !== schema.value) {
throw new ParseError(
`Invalid value '"${value}"'. Expected '"${schema.value}"'`,
this.pos
);
}
return value;
} else { } else {
value += '\\' + nextChar; value += this.consume();
} }
} else if (char === quote) { }
this.consume(); // Consume closing quote
throw new ParseError('Unterminated string literal', this.pos);
if (value !== schema.value) { } else {
throw new ParseError( // 不带引号的字符串,像普通字符串一样解析
`Invalid value '"${value}"'. Expected '"${schema.value}"'`, let value = '';
this.pos while (this.pos < this.input.length) {
); const char = this.peek();
if (char === ';' || char === ']' || char === ')') {
break;
} }
return value;
} else {
value += this.consume(); value += this.consume();
} }
value = value.trim();
if (value !== schema.value) {
throw new ParseError(
`Invalid value '${value}'. Expected '${schema.value}'`,
this.pos - value.length
);
}
return value;
} }
throw new ParseError('Unterminated string literal', this.pos);
} }
private parseUnionValue(schema: UnionSchema): unknown { private parseUnionValue(schema: UnionSchema): unknown {