json: support non-string const / enums

This commit is contained in:
ochafik 2024-03-22 09:27:03 +00:00
parent 62d0b3d194
commit 49c99c5bac
5 changed files with 1421 additions and 1449 deletions

View file

@ -124,7 +124,7 @@ static std::string replacePattern(const std::string & input, const std::regex &
} }
static std::string format_literal(const std::string & literal) { static std::string format_literal(const std::string & literal) {
std::string escaped = replacePattern(json(literal).dump(), GRAMMAR_LITERAL_ESCAPE_RE, [&](const std::smatch & match) { std::string escaped = replacePattern(literal, GRAMMAR_LITERAL_ESCAPE_RE, [&](const std::smatch & match) {
char c = match.str()[0]; char c = match.str()[0];
return GRAMMAR_LITERAL_ESCAPES.at(c); return GRAMMAR_LITERAL_ESCAPES.at(c);
}); });
@ -413,7 +413,7 @@ private:
std::string prop_rule_name = visit(prop_schema, name + (name.empty() ? "" : "-") + prop_name); std::string prop_rule_name = visit(prop_schema, name + (name.empty() ? "" : "-") + prop_name);
prop_kv_rule_names[prop_name] = _add_rule( prop_kv_rule_names[prop_name] = _add_rule(
name + (name.empty() ? "" : "-") + prop_name + "-kv", name + (name.empty() ? "" : "-") + prop_name + "-kv",
format_literal(prop_name) + " space \":\" space " + prop_rule_name format_literal(json(prop_name).dump()) + " space \":\" space " + prop_rule_name
); );
if (required.find(prop_name) != required.end()) { if (required.find(prop_name) != required.end()) {
required_props.push_back(prop_name); required_props.push_back(prop_name);
@ -557,11 +557,7 @@ public:
} }
std::string _generate_constant_rule(const json & value) { std::string _generate_constant_rule(const json & value) {
if (!value.is_string()) { return format_literal(value.dump());
_errors.push_back("Only std::string constants are supported, got " + value.dump());
return "";
}
return format_literal(value.get<std::string>());
} }
std::string visit(const json & schema, const std::string & name) { std::string visit(const json & schema, const std::string & name) {

View file

@ -61,7 +61,7 @@ class SchemaConverter:
def _format_literal(self, literal): def _format_literal(self, literal):
escaped = GRAMMAR_LITERAL_ESCAPE_RE.sub( escaped = GRAMMAR_LITERAL_ESCAPE_RE.sub(
lambda m: GRAMMAR_LITERAL_ESCAPES.get(m.group(0)), json.dumps(literal) lambda m: GRAMMAR_LITERAL_ESCAPES.get(m.group(0)), literal
) )
return f'"{escaped}"' return f'"{escaped}"'
@ -308,8 +308,7 @@ class SchemaConverter:
return ref_name return ref_name
def _generate_constant_rule(self, value): def _generate_constant_rule(self, value):
assert isinstance(value, str), f'Only string constants are supported, got {value}' return self._format_literal(json.dumps(value))
return self._format_literal(value)
def visit(self, schema, name): def visit(self, schema, name):
schema_type = schema.get('type') schema_type = schema.get('type')
@ -428,7 +427,7 @@ class SchemaConverter:
prop_rule_name = self.visit(prop_schema, f'{name}{"-" if name else ""}{prop_name}') prop_rule_name = self.visit(prop_schema, f'{name}{"-" if name else ""}{prop_name}')
prop_kv_rule_names[prop_name] = self._add_rule( prop_kv_rule_names[prop_name] = self._add_rule(
f'{name}{"-" if name else ""}{prop_name}-kv', f'{name}{"-" if name else ""}{prop_name}-kv',
fr'{self._format_literal(prop_name)} space ":" space {prop_rule_name}' fr'{self._format_literal(json.dumps(prop_name))} space ":" space {prop_rule_name}'
) )
required_props = [k for k in sorted_props if k in required] required_props = [k for k in sorted_props if k in required]
optional_props = [k for k in sorted_props if k not in required] optional_props = [k for k in sorted_props if k not in required]

File diff suppressed because it is too large Load diff

View file

@ -48,7 +48,7 @@ export class SchemaConverter {
} }
_formatLiteral(literal) { _formatLiteral(literal) {
const escaped = JSON.stringify(literal).replace( const escaped = literal.replace(
GRAMMAR_LITERAL_ESCAPE_RE, GRAMMAR_LITERAL_ESCAPE_RE,
m => GRAMMAR_LITERAL_ESCAPES[m] m => GRAMMAR_LITERAL_ESCAPES[m]
); );
@ -327,10 +327,7 @@ export class SchemaConverter {
} }
_generateConstantRule(value) { _generateConstantRule(value) {
if (typeof value !== 'string') { return this._formatLiteral(JSON.stringify(value));
throw new Error('Only string constants are supported, got ' + JSON.stringify(value));
}
return this._formatLiteral(value);
} }
visit(schema, name) { visit(schema, name) {
@ -346,9 +343,6 @@ export class SchemaConverter {
} else if (Array.isArray(schemaType)) { } else if (Array.isArray(schemaType)) {
return this._addRule(ruleName, this._generateUnionRule(name, schemaType.map(t => ({ type: t })))); return this._addRule(ruleName, this._generateUnionRule(name, schemaType.map(t => ({ type: t }))));
} else if ('const' in schema) { } else if ('const' in schema) {
if (typeof schema.const !== 'string') {
throw new Error('Only string constants are supported, got ' + JSON.stringify(schema.const));
}
return this._addRule(ruleName, this._generateConstantRule(schema.const)); return this._addRule(ruleName, this._generateConstantRule(schema.const));
} else if ('enum' in schema) { } else if ('enum' in schema) {
const rule = schema.enum.map(v => this._generateConstantRule(v)).join(' | '); const rule = schema.enum.map(v => this._generateConstantRule(v)).join(' | ');
@ -457,7 +451,7 @@ export class SchemaConverter {
const propRuleName = this.visit(propSchema, `${name ?? ''}${name ? '-' : ''}${propName}`); const propRuleName = this.visit(propSchema, `${name ?? ''}${name ? '-' : ''}${propName}`);
propKvRuleNames[propName] = this._addRule( propKvRuleNames[propName] = this._addRule(
`${name ?? ''}${name ? '-' : ''}${propName}-kv`, `${name ?? ''}${name ? '-' : ''}${propName}-kv`,
`${this._formatLiteral(propName)} space ":" space ${propRuleName}` `${this._formatLiteral(JSON.stringify(propName))} space ":" space ${propRuleName}`
); );
} }
const requiredProps = sortedProps.filter(k => required.has(k)); const requiredProps = sortedProps.filter(k => required.has(k));

View file

@ -193,21 +193,27 @@ static void test_all(const std::string & lang, std::function<void(const TestCase
}); });
test({ test({
FAILURE, SUCCESS,
"non-string const", "non-string const",
R"""({ R"""({
"const": 123 "const": 123
})""", })""",
"" R"""(
root ::= "123"
space ::= " "?
)"""
}); });
test({ test({
FAILURE, SUCCESS,
"non-string enum", "non-string enum",
R"""({ R"""({
"enum": [123] "enum": ["red", "amber", "green", null, 42, ["foo"]]
})""", })""",
"" R"""(
root ::= "\"red\"" | "\"amber\"" | "\"green\"" | "null" | "42" | "[\"foo\"]"
space ::= " "?
)"""
}); });
test({ test({