json: support non-string const / enums
This commit is contained in:
parent
62d0b3d194
commit
49c99c5bac
5 changed files with 1421 additions and 1449 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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
|
@ -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));
|
||||||
|
|
|
@ -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({
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue