return result;
}
+/* Minimalistic replacement for std::string_view, which is only available from C++17 onwards */
+class string_view {
+ const std::string & _str;
+ const size_t _start;
+ const size_t _end;
+public:
+ string_view(const std::string & str, size_t start = 0, size_t end = std::string::npos) : _str(str), _start(start), _end(end == std::string::npos ? str.length() : end) {}
+
+ size_t size() const {
+ return _end - _start;
+ }
+
+ size_t length() const {
+ return size();
+ }
+
+ operator std::string() const {
+ return str();
+ }
+
+ std::string str() const {
+ return _str.substr(_start, _end - _start);
+ }
+
+ string_view substr(size_t pos, size_t len = std::string::npos) const {
+ return string_view(_str, _start + pos, len == std::string::npos ? _end : _start + pos + len);
+ }
+
+ char operator[](size_t pos) const {
+ auto index = _start + pos;
+ if (index >= _end) {
+ throw std::out_of_range("string_view index out of range");
+ }
+ return _str[_start + pos];
+ }
+
+ bool operator==(const string_view & other) const {
+ std::string this_str = *this;
+ std::string other_str = other;
+ return this_str == other_str;
+ }
+};
+
+static void _build_min_max_int(int min_value, int max_value, std::stringstream & out, int decimals_left = 16, bool top_level = true) {
+ auto has_min = min_value != std::numeric_limits<int>::min();
+ auto has_max = max_value != std::numeric_limits<int>::max();
+
+ auto digit_range = [&](char from, char to) {
+ out << "[";
+ if (from == to) {
+ out << from;
+ } else {
+ out << from << "-" << to;
+ }
+ out << "]";
+ };
+ auto more_digits = [&](int min_digits, int max_digits) {
+ out << "[0-9]";
+ if (min_digits == max_digits && min_digits == 1) {
+ return;
+ }
+ out << "{";
+ out << min_digits;
+ if (max_digits != min_digits) {
+ out << ",";
+ if (max_digits != std::numeric_limits<int>::max()) {
+ out << max_digits;
+ }
+ }
+ out << "}";
+ };
+ std::function<void(const string_view &, const string_view &)> uniform_range =
+ [&](const string_view & from, const string_view & to) {
+ size_t i = 0;
+ while (i < from.length() && i < to.length() && from[i] == to[i]) {
+ i++;
+ }
+ if (i > 0) {
+ out << "\"" << from.substr(0, i).str() << "\"";
+ }
+ if (i < from.length() && i < to.length()) {
+ if (i > 0) {
+ out << " ";
+ }
+ auto sub_len = from.length() - i - 1;
+ if (sub_len > 0) {
+ auto from_sub = from.substr(i + 1);
+ auto to_sub = to.substr(i + 1);
+ auto sub_zeros = repeat("0", sub_len);
+ auto sub_nines = repeat("9", sub_len);
+
+ auto to_reached = false;
+ out << "(";
+ if (from_sub == sub_zeros) {
+ digit_range(from[i], to[i] - 1);
+ out << " ";
+ more_digits(sub_len, sub_len);
+ } else {
+ out << "[" << from[i] << "] ";
+ out << "(";
+ uniform_range(from_sub, sub_nines);
+ out << ")";
+ if (from[i] < to[i] - 1) {
+ out << " | ";
+ if (to_sub == sub_nines) {
+ digit_range(from[i] + 1, to[i]);
+ to_reached = true;
+ } else {
+ digit_range(from[i] + 1, to[i] - 1);
+ }
+ out << " ";
+ more_digits(sub_len, sub_len);
+ }
+ }
+ if (!to_reached) {
+ out << " | ";
+ digit_range(to[i], to[i]);
+ out << " ";
+ uniform_range(sub_zeros, to_sub);
+ }
+ out << ")";
+ } else {
+ out << "[" << from[i] << "-" << to[i] << "]";
+ }
+ }
+ };
+
+ if (has_min && has_max) {
+ if (min_value < 0 && max_value < 0) {
+ out << "\"-\" (";
+ _build_min_max_int(-max_value, -min_value, out, decimals_left, /* top_level= */ true);
+ out << ")";
+ return;
+ }
+
+ if (min_value < 0) {
+ out << "\"-\" (";
+ _build_min_max_int(0, -min_value, out, decimals_left, /* top_level= */ true);
+ out << ") | ";
+ min_value = 0;
+ }
+
+ auto min_s = std::to_string(min_value);
+ auto max_s = std::to_string(max_value);
+ auto min_digits = min_s.length();
+ auto max_digits = max_s.length();
+
+ for (auto digits = min_digits; digits < max_digits; digits++) {
+ uniform_range(min_s, repeat("9", digits));
+ min_s = "1" + repeat("0", digits);
+ out << " | ";
+ }
+ uniform_range(min_s, max_s);
+ return;
+ }
+
+ auto less_decimals = std::max(decimals_left - 1, 1);
+
+ if (has_min) {
+ if (min_value < 0) {
+ out << "\"-\" (";
+ _build_min_max_int(std::numeric_limits<int>::min(), -min_value, out, decimals_left, /* top_level= */ false);
+ out << ") | [0] | [1-9] ";
+ more_digits(0, decimals_left - 1);
+ } else if (min_value == 0) {
+ if (top_level) {
+ out << "[0] | [1-9] ";
+ more_digits(0, less_decimals);
+ } else {
+ more_digits(1, decimals_left);
+ }
+ } else if (min_value <= 9) {
+ char c = '0' + min_value;
+ auto range_start = top_level ? '1' : '0';
+ if (c > range_start) {
+ digit_range(range_start, c - 1);
+ out << " ";
+ more_digits(1, less_decimals);
+ out << " | ";
+ }
+ digit_range(c, '9');
+ out << " ";
+ more_digits(0, less_decimals);
+ } else {
+ auto min_s = std::to_string(min_value);
+ auto len = min_s.length();
+ auto c = min_s[0];
+
+ if (c > '1') {
+ digit_range(top_level ? '1' : '0', c - 1);
+ out << " ";
+ more_digits(len, less_decimals);
+ out << " | ";
+ }
+ digit_range(c, c);
+ out << " (";
+ _build_min_max_int(std::stoi(min_s.substr(1)), std::numeric_limits<int>::max(), out, less_decimals, /* top_level= */ false);
+ out << ")";
+ if (c < '9') {
+ out << " | ";
+ digit_range(c + 1, '9');
+ out << " ";
+ more_digits(len - 1, less_decimals);
+ }
+ }
+ return;
+ }
+
+ if (has_max) {
+ if (max_value >= 0) {
+ if (top_level) {
+ out << "\"-\" [1-9] ";
+ more_digits(0, less_decimals);
+ out << " | ";
+ }
+ _build_min_max_int(0, max_value, out, decimals_left, /* top_level= */ true);
+ } else {
+ out << "\"-\" (";
+ _build_min_max_int(-max_value, std::numeric_limits<int>::max(), out, decimals_left, /* top_level= */ false);
+ out << ")";
+ }
+ return;
+ }
+
+ throw std::runtime_error("At least one of min_value or max_value must be set");
+}
+
const std::string SPACE_RULE = "| \" \" | \"\\n\" [ \\t]{0,20}";
struct BuiltinRule {
return "\"" + escaped + "\"";
}
-
class SchemaConverter {
private:
std::function<json(const std::string &)> _fetch_json;
int min_len = schema.contains("minLength") ? schema["minLength"].get<int>() : 0;
int max_len = schema.contains("maxLength") ? schema["maxLength"].get<int>() : std::numeric_limits<int>::max();
return _add_rule(rule_name, "\"\\\"\" " + build_repetition(char_rule, min_len, max_len) + " \"\\\"\" space");
+ } else if (schema_type == "integer" && (schema.contains("minimum") || schema.contains("exclusiveMinimum") || schema.contains("maximum") || schema.contains("exclusiveMaximum"))) {
+ int min_value = std::numeric_limits<int>::min();
+ int max_value = std::numeric_limits<int>::max();
+ if (schema.contains("minimum")) {
+ min_value = schema["minimum"].get<int>();
+ } else if (schema.contains("exclusiveMinimum")) {
+ min_value = schema["exclusiveMinimum"].get<int>() + 1;
+ }
+ if (schema.contains("maximum")) {
+ max_value = schema["maximum"].get<int>();
+ } else if (schema.contains("exclusiveMaximum")) {
+ max_value = schema["exclusiveMaximum"].get<int>() - 1;
+ }
+ std::stringstream out;
+ out << "(";
+ _build_min_max_int(min_value, max_value, out);
+ out << ") space";
+ return _add_rule(rule_name, out.str());
} else if (schema.empty() || schema_type == "object") {
return _add_rule(rule_name, _add_primitive("object", PRIMITIVE_RULES.at("object")));
} else {
question: str
concise_answer: str
justification: str
+ stars: Annotated[int, Field(ge=1, le=5)]
class PyramidalSummary(BaseModel):
title: str
import json
import re
import sys
-from typing import Any, Dict, List, Set, Tuple, Union
+from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
def _build_repetition(item_rule, min_items, max_items, separator_rule=None):
result = item_rule + ' ' + _build_repetition(f'({separator_rule} {item_rule})', min_items - 1 if min_items > 0 else 0, max_items - 1 if max_items is not None else None)
return f'({result})?' if min_items == 0 else result
+def _generate_min_max_int(min_value: Optional[int], max_value: Optional[int], out: list, decimals_left: int = 16, top_level: bool = True):
+ has_min = min_value != None
+ has_max = max_value != None
+
+ def digit_range(from_char: str, to_char: str):
+ out.append("[")
+ if from_char == to_char:
+ out.append(from_char)
+ else:
+ out.append(from_char)
+ out.append("-")
+ out.append(to_char)
+ out.append("]")
+
+ def more_digits(min_digits: int, max_digits: int):
+ out.append("[0-9]")
+ if min_digits == max_digits and min_digits == 1:
+ return
+ out.append("{")
+ out.append(str(min_digits))
+ if max_digits != min_digits:
+ out.append(",")
+ if max_digits != sys.maxsize:
+ out.append(str(max_digits))
+ out.append("}")
+
+ def uniform_range(from_str: str, to_str: str):
+ i = 0
+ while i < len(from_str) and from_str[i] == to_str[i]:
+ i += 1
+ if i > 0:
+ out.append("\"")
+ out.append(from_str[:i])
+ out.append("\"")
+ if i < len(from_str):
+ if i > 0:
+ out.append(" ")
+ sub_len = len(from_str) - i - 1
+ if sub_len > 0:
+ from_sub = from_str[i+1:]
+ to_sub = to_str[i+1:]
+ sub_zeros = "0" * sub_len
+ sub_nines = "9" * sub_len
+
+ to_reached = False
+ out.append("(")
+ if from_sub == sub_zeros:
+ digit_range(from_str[i], chr(ord(to_str[i]) - 1))
+ out.append(" ")
+ more_digits(sub_len, sub_len)
+ else:
+ out.append("[")
+ out.append(from_str[i])
+ out.append("] ")
+ out.append("(")
+ uniform_range(from_sub, sub_nines)
+ out.append(")")
+ if ord(from_str[i]) < ord(to_str[i]) - 1:
+ out.append(" | ")
+ if to_sub == sub_nines:
+ digit_range(chr(ord(from_str[i]) + 1), to_str[i])
+ to_reached = True
+ else:
+ digit_range(chr(ord(from_str[i]) + 1), chr(ord(to_str[i]) - 1))
+ out.append(" ")
+ more_digits(sub_len, sub_len)
+ if not to_reached:
+ out.append(" | ")
+ digit_range(to_str[i], to_str[i])
+ out.append(" ")
+ uniform_range(sub_zeros, to_sub)
+ out.append(")")
+ else:
+ out.append("[")
+ out.append(from_str[i])
+ out.append("-")
+ out.append(to_str[i])
+ out.append("]")
+
+ if has_min and has_max:
+ if min_value < 0 and max_value < 0:
+ out.append("\"-\" (")
+ _generate_min_max_int(-max_value, -min_value, out, decimals_left, top_level=True)
+ out.append(")")
+ return
+
+ if min_value < 0:
+ out.append("\"-\" (")
+ _generate_min_max_int(0, -min_value, out, decimals_left, top_level=True)
+ out.append(") | ")
+ min_value = 0
+
+ min_s = str(min_value)
+ max_s = str(max_value)
+ min_digits = len(min_s)
+ max_digits = len(max_s)
+
+ for digits in range(min_digits, max_digits):
+ uniform_range(min_s, "9" * digits)
+ min_s = "1" + "0" * digits
+ out.append(" | ")
+ uniform_range(min_s, max_s)
+ return
+
+ less_decimals = max(decimals_left - 1, 1)
+
+ if has_min:
+ if min_value < 0:
+ out.append("\"-\" (")
+ _generate_min_max_int(None, -min_value, out, decimals_left, top_level=False)
+ out.append(") | [0] | [1-9] ")
+ more_digits(0, decimals_left - 1)
+ elif min_value == 0:
+ if top_level:
+ out.append("[0] | [1-9] ")
+ more_digits(0, less_decimals)
+ else:
+ more_digits(1, decimals_left)
+ elif min_value <= 9:
+ c = str(min_value)
+ range_start = '1' if top_level else '0'
+ if c > range_start:
+ digit_range(range_start, chr(ord(c) - 1))
+ out.append(" ")
+ more_digits(1, less_decimals)
+ out.append(" | ")
+ digit_range(c, "9")
+ out.append(" ")
+ more_digits(0, less_decimals)
+ else:
+ min_s = str(min_value)
+ length = len(min_s)
+ c = min_s[0]
+
+ if c > "1":
+ digit_range("1" if top_level else "0", chr(ord(c) - 1))
+ out.append(" ")
+ more_digits(length, less_decimals)
+ out.append(" | ")
+ digit_range(c, c)
+ out.append(" (")
+ _generate_min_max_int(int(min_s[1:]), None, out, less_decimals, top_level=False)
+ out.append(")")
+ if c < "9":
+ out.append(" | ")
+ digit_range(chr(ord(c) + 1), "9")
+ out.append(" ")
+ more_digits(length - 1, less_decimals)
+ return
+
+ if has_max:
+ if max_value >= 0:
+ if top_level:
+ out.append("\"-\" [1-9] ")
+ more_digits(0, less_decimals)
+ out.append(" | ")
+ _generate_min_max_int(0, max_value, out, decimals_left, top_level=True)
+ else:
+ out.append("\"-\" (")
+ _generate_min_max_int(-max_value, None, out, decimals_left, top_level=False)
+ out.append(")")
+ return
+
+ raise RuntimeError("At least one of min_value or max_value must be set")
class BuiltinRule:
def __init__(self, content: str, deps: list = None):
return self._add_rule(rule_name, r'"\"" ' + _build_repetition(char_rule, min_len, max_len) + r' "\"" space')
+ elif schema_type in (None, 'integer') and \
+ ('minimum' in schema or 'exclusiveMinimum' in schema or 'maximum' in schema or 'exclusiveMaximum' in schema):
+ min_value = None
+ max_value = None
+ if 'minimum' in schema:
+ min_value = schema['minimum']
+ elif 'exclusiveMinimum' in schema:
+ min_value = schema['exclusiveMinimum'] + 1
+ if 'maximum' in schema:
+ max_value = schema['maximum']
+ elif 'exclusiveMaximum' in schema:
+ max_value = schema['exclusiveMaximum'] - 1
+
+ out = ["("]
+ _generate_min_max_int(min_value, max_value, out)
+ out.append(") space")
+ return self._add_rule(rule_name, ''.join(out))
+
elif (schema_type == 'object') or (len(schema) == 0):
return self._add_rule(rule_name, self._add_primitive('object', PRIMITIVE_RULES['object']))
return minItems === 0 ? `(${result})?` : result;
}
+function _generateMinMaxInt(minValue, maxValue, out, decimalsLeft = 16, topLevel = true) {
+ const hasMin = minValue !== null;
+ const hasMax = maxValue !== null;
+
+ function digitRange(fromChar, toChar) {
+ out.push("[");
+ if (fromChar === toChar) {
+ out.push(fromChar);
+ } else {
+ out.push(fromChar);
+ out.push("-");
+ out.push(toChar);
+ }
+ out.push("]");
+ }
+
+ function moreDigits(minDigits, maxDigits) {
+ out.push("[0-9]");
+ if (minDigits === maxDigits && minDigits === 1) {
+ return;
+ }
+ out.push("{");
+ out.push(minDigits.toString());
+ if (maxDigits !== minDigits) {
+ out.push(",");
+ if (maxDigits !== Number.MAX_SAFE_INTEGER) {
+ out.push(maxDigits.toString());
+ }
+ }
+ out.push("}");
+ }
+
+ function uniformRange(fromStr, toStr) {
+ let i = 0;
+ while (i < fromStr.length && fromStr[i] === toStr[i]) {
+ i++;
+ }
+ if (i > 0) {
+ out.push("\"");
+ out.push(fromStr.slice(0, i));
+ out.push("\"");
+ }
+ if (i < fromStr.length) {
+ if (i > 0) {
+ out.push(" ");
+ }
+ const subLen = fromStr.length - i - 1;
+ if (subLen > 0) {
+ const fromSub = fromStr.slice(i + 1);
+ const toSub = toStr.slice(i + 1);
+ const subZeros = "0".repeat(subLen);
+ const subNines = "9".repeat(subLen);
+
+ let toReached = false;
+ out.push("(");
+ if (fromSub === subZeros) {
+ digitRange(fromStr[i], String.fromCharCode(toStr.charCodeAt(i) - 1));
+ out.push(" ");
+ moreDigits(subLen, subLen);
+ } else {
+ out.push("[");
+ out.push(fromStr[i]);
+ out.push("] ");
+ out.push("(");
+ uniformRange(fromSub, subNines);
+ out.push(")");
+ if (fromStr.charCodeAt(i) < toStr.charCodeAt(i) - 1) {
+ out.push(" | ");
+ if (toSub === subNines) {
+ digitRange(String.fromCharCode(fromStr.charCodeAt(i) + 1), toStr[i]);
+ toReached = true;
+ } else {
+ digitRange(String.fromCharCode(fromStr.charCodeAt(i) + 1), String.fromCharCode(toStr.charCodeAt(i) - 1));
+ }
+ out.push(" ");
+ moreDigits(subLen, subLen);
+ }
+ }
+ if (!toReached) {
+ out.push(" | ");
+ digitRange(toStr[i], toStr[i]);
+ out.push(" ");
+ uniformRange(subZeros, toSub);
+ }
+ out.push(")");
+ } else {
+ out.push("[");
+ out.push(fromStr[i]);
+ out.push("-");
+ out.push(toStr[i]);
+ out.push("]");
+ }
+ }
+ }
+
+ if (hasMin && hasMax) {
+ if (minValue < 0 && maxValue < 0) {
+ out.push("\"-\" (");
+ _generateMinMaxInt(-maxValue, -minValue, out, decimalsLeft, true);
+ out.push(")");
+ return;
+ }
+
+ if (minValue < 0) {
+ out.push("\"-\" (");
+ _generateMinMaxInt(0, -minValue, out, decimalsLeft, true);
+ out.push(") | ");
+ minValue = 0;
+ }
+
+ let minS = minValue.toString();
+ const maxS = maxValue.toString();
+ const minDigits = minS.length;
+ const maxDigits = maxS.length;
+
+ for (let digits = minDigits; digits < maxDigits; digits++) {
+ uniformRange(minS, "9".repeat(digits));
+ minS = "1" + "0".repeat(digits);
+ out.push(" | ");
+ }
+ uniformRange(minS, maxS);
+ return;
+ }
+
+ const lessDecimals = Math.max(decimalsLeft - 1, 1);
+
+ if (hasMin) {
+ if (minValue < 0) {
+ out.push("\"-\" (");
+ _generateMinMaxInt(null, -minValue, out, decimalsLeft, false);
+ out.push(") | [0] | [1-9] ");
+ moreDigits(0, decimalsLeft - 1);
+ } else if (minValue === 0) {
+ if (topLevel) {
+ out.push("[0] | [1-9] ");
+ moreDigits(0, lessDecimals);
+ } else {
+ moreDigits(1, decimalsLeft);
+ }
+ } else if (minValue <= 9) {
+ const c = minValue.toString();
+ const range_start = topLevel ? '1' : '0';
+ if (c > range_start) {
+ digitRange(range_start, String.fromCharCode(c.charCodeAt(0) - 1));
+ out.push(" ");
+ moreDigits(1, lessDecimals);
+ out.push(" | ");
+ }
+ digitRange(c, "9");
+ out.push(" ");
+ moreDigits(0, lessDecimals);
+ } else {
+ const minS = minValue.toString();
+ const length = minS.length;
+ const c = minS[0];
+
+ if (c > "1") {
+ digitRange(topLevel ? "1" : "0", String.fromCharCode(c.charCodeAt(0) - 1));
+ out.push(" ");
+ moreDigits(length, lessDecimals);
+ out.push(" | ");
+ }
+ digitRange(c, c);
+ out.push(" (");
+ _generateMinMaxInt(parseInt(minS.slice(1)), null, out, lessDecimals, false);
+ out.push(")");
+ if (c < "9") {
+ out.push(" | ");
+ digitRange(String.fromCharCode(c.charCodeAt(0) + 1), "9");
+ out.push(" ");
+ moreDigits(length - 1, lessDecimals);
+ }
+ }
+ return;
+ }
+
+ if (hasMax) {
+ if (maxValue >= 0) {
+ if (topLevel) {
+ out.push("\"-\" [1-9] ");
+ moreDigits(0, lessDecimals);
+ out.push(" | ");
+ }
+ _generateMinMaxInt(0, maxValue, out, decimalsLeft, true);
+ } else {
+ out.push("\"-\" (");
+ _generateMinMaxInt(-maxValue, null, out, decimalsLeft, false);
+ out.push(")");
+ }
+ return;
+ }
+
+ throw new Error("At least one of minValue or maxValue must be set");
+}
+
class BuiltinRule {
constructor(content, deps) {
this.content = content;
const minLen = schema.minLength || 0;
const maxLen = schema.maxLength;
return this._addRule(ruleName, '"\\\"" ' + _buildRepetition(charRuleName, minLen, maxLen) + ' "\\\"" space');
+ } else if (schemaType === 'integer' && ('minimum' in schema || 'exclusiveMinimum' in schema || 'maximum' in schema || 'exclusiveMaximum' in schema)) {
+ let minValue = null;
+ let maxValue = null;
+ if ('minimum' in schema) {
+ minValue = schema.minimum;
+ } else if ('exclusiveMinimum' in schema) {
+ minValue = schema.exclusiveMinimum + 1;
+ }
+ if ('maximum' in schema) {
+ maxValue = schema.maximum;
+ } else if ('exclusiveMaximum' in schema) {
+ maxValue = schema.exclusiveMaximum - 1;
+ }
+
+ const out = ["("];
+ _generateMinMaxInt(minValue, maxValue, out);
+ out.push(") space");
+ return this._addRule(ruleName, out.join(''));
} else if ((schemaType === 'object') || (Object.keys(schema).length === 0)) {
return this._addRule(ruleName, this._addPrimitive('object', PRIMITIVE_RULES['object']));
} else {
}
static void test_simple_grammar() {
+ test_schema(
+ "min 0",
+ R"""({
+ "type": "integer",
+ "minimum": 0
+ })""",
+ // Passing strings
+ {
+ "0",
+ "10",
+ "12",
+ "10000",
+ },
+ // Failing strings
+ {
+ "-1",
+ "-10",
+ "-10000",
+ "-100000000000000000000000000000000",
+ "100000000000000000000000000000000",
+ "00",
+ "01",
+ "-0",
+ }
+ );
+ test_schema(
+ "min 2",
+ // Schema
+ R"""({
+ "type": "integer",
+ "minimum": 2
+ })""",
+ // Passing strings
+ {
+ "2",
+ "3",
+ "4",
+ "10",
+ "20",
+ "1234567890000000",
+ },
+ // Failing strings
+ {
+ "0",
+ "1",
+ "-1",
+ "-100",
+ "0",
+ "1",
+ "01",
+ "02",
+ "12345678900000000",
+ }
+ );
+ test_schema(
+ "min 456",
+ R"""({
+ "type": "integer",
+ "minimum": 456
+ })""",
+ // Passing strings
+ {
+ "456",
+ "4560",
+ "457",
+ "460",
+ "500",
+ },
+ // Failing strings
+ {
+ "455",
+ "356",
+ "50",
+ "050",
+ "-1",
+ "-456",
+ }
+ );
+ test_schema(
+ "min -123",
+ R"""({
+ "type": "integer",
+ "minimum": -123
+ })""",
+ // Passing strings
+ {
+ "-123",
+ "-122",
+ "-11",
+ "-1",
+ "0",
+ "1",
+ "123",
+ "1234",
+ "2345",
+ },
+ // Failing strings
+ {
+ "-1234",
+ "-124",
+ }
+ );
+
+ test_schema(
+ "max 9999",
+ // Schema
+ R"""({
+ "type": "integer",
+ "maximum": 9999
+ })""",
+ // Passing strings
+ {
+ "-99999",
+ "0",
+ "9999",
+ },
+ // Failing strings
+ {
+ "10000",
+ "99991",
+ }
+ );
+ test_schema(
+ "max -9999",
+ // Schema
+ R"""({
+ "type": "integer",
+ "maximum": -9999
+ })""",
+ // Passing strings
+ {
+ "-10000",
+ "-9999",
+ },
+ // Failing strings
+ {
+ "-9998",
+ "0",
+ "9999",
+ }
+ );
+ test_schema(
+ "min 5 max 30",
+ // Schema
+ R"""({
+ "type": "integer",
+ "minimum": 5,
+ "maximum": 30
+ })""",
+ // Passing strings
+ {
+ "5",
+ "10",
+ "30",
+ },
+ // Failing strings
+ {
+ "05",
+ "4",
+ "-1",
+ "31",
+ "123",
+ "0123",
+ }
+ );
+ test_schema(
+ "min -1 max 1",
+ R"""({
+ "type": "integer",
+ "minimum": -1,
+ "maximum": 1
+ })""",
+ // Passing strings
+ {
+ "-1",
+ "0",
+ "1",
+ },
+ // Failing strings
+ {
+ "-11",
+ "-10",
+ "-2",
+ "2",
+ "10",
+ "11",
+ }
+ );
+ test_schema(
+ "min -123 max 42",
+ R"""({
+ "type": "integer",
+ "minimum": -123,
+ "maximum": 42
+ })""",
+ // Passing strings
+ {
+ "-123",
+ "-122",
+ "-13",
+ "-11",
+ "-2",
+ "-1",
+ "0",
+ "1",
+ "5",
+ "10",
+ "39",
+ "40",
+ "42",
+ },
+ // Failing strings
+ {
+ "-0123",
+ "-124",
+ "-1123",
+ "-200",
+ "43",
+ "123",
+ "0123",
+ }
+ );
+ test_schema(
+ "exclusive min / max",
+ // Schema
+ R"""({
+ "type": "integer",
+ "exclusiveMinimum": 0,
+ "exclusiveMaximum": 10000
+ })""",
+ // Passing strings
+ {
+ "1",
+ "9999",
+ },
+ // Failing strings
+ {
+ "0",
+ "01",
+ "10000",
+ "99999",
+ }
+ );
+
// Test case for a simple grammar
test_grammar(
"simple grammar",
}
);
-
test_schema(
"min+max items",
// Schema
runner(tc);
};
+ test({
+ SUCCESS,
+ "min 0",
+ R"""({
+ "type": "integer",
+ "minimum": 0
+ })""",
+ R"""(
+ root ::= ([0] | [1-9] [0-9]{0,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 1",
+ R"""({
+ "type": "integer",
+ "minimum": 1
+ })""",
+ R"""(
+ root ::= ([1-9] [0-9]{0,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 3",
+ R"""({
+ "type": "integer",
+ "minimum": 3
+ })""",
+ R"""(
+ root ::= ([1-2] [0-9]{1,15} | [3-9] [0-9]{0,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 9",
+ R"""({
+ "type": "integer",
+ "minimum": 9
+ })""",
+ R"""(
+ root ::= ([1-8] [0-9]{1,15} | [9] [0-9]{0,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 10",
+ R"""({
+ "type": "integer",
+ "minimum": 10
+ })""",
+ R"""(
+ root ::= ([1] ([0-9]{1,15}) | [2-9] [0-9]{1,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 25",
+ R"""({
+ "type": "integer",
+ "minimum": 25
+ })""",
+ R"""(
+ root ::= ([1] [0-9]{2,15} | [2] ([0-4] [0-9]{1,14} | [5-9] [0-9]{0,14}) | [3-9] [0-9]{1,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "max 30",
+ R"""({
+ "type": "integer",
+ "maximum": 30
+ })""",
+ R"""(
+ root ::= ("-" [1-9] [0-9]{0,15} | [0-9] | ([1-2] [0-9] | [3] "0")) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min -5",
+ R"""({
+ "type": "integer",
+ "minimum": -5
+ })""",
+ R"""(
+ root ::= ("-" ([0-5]) | [0] | [1-9] [0-9]{0,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min -123",
+ R"""({
+ "type": "integer",
+ "minimum": -123
+ })""",
+ R"""(
+ root ::= ("-" ([0-9] | ([1-8] [0-9] | [9] [0-9]) | "1" ([0-1] [0-9] | [2] [0-3])) | [0] | [1-9] [0-9]{0,15}) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "max -5",
+ R"""({
+ "type": "integer",
+ "maximum": -5
+ })""",
+ R"""(
+ root ::= ("-" ([0-4] [0-9]{1,15} | [5-9] [0-9]{0,15})) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "max 1",
+ R"""({
+ "type": "integer",
+ "maximum": 1
+ })""",
+ R"""(
+ root ::= ("-" [1-9] [0-9]{0,15} | [0-1]) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "max 100",
+ R"""({
+ "type": "integer",
+ "maximum": 100
+ })""",
+ R"""(
+ root ::= ("-" [1-9] [0-9]{0,15} | [0-9] | ([1-8] [0-9] | [9] [0-9]) | "100") space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 0 max 23",
+ R"""({
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 23
+ })""",
+ R"""(
+ root ::= ([0-9] | ([1] [0-9] | [2] [0-3])) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 15 max 300",
+ R"""({
+ "type": "integer",
+ "minimum": 15,
+ "maximum": 300
+ })""",
+ R"""(
+ root ::= (([1] ([5-9]) | [2-9] [0-9]) | ([1-2] [0-9]{2} | [3] "00")) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min 5 max 30",
+ R"""({
+ "type": "integer",
+ "minimum": 5,
+ "maximum": 30
+ })""",
+ R"""(
+ root ::= ([5-9] | ([1-2] [0-9] | [3] "0")) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min -123 max 42",
+ R"""({
+ "type": "integer",
+ "minimum": -123,
+ "maximum": 42
+ })""",
+ R"""(
+ root ::= ("-" ([0-9] | ([1-8] [0-9] | [9] [0-9]) | "1" ([0-1] [0-9] | [2] [0-3])) | [0-9] | ([1-3] [0-9] | [4] [0-2])) space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min -10 max 10",
+ R"""({
+ "type": "integer",
+ "minimum": -10,
+ "maximum": 10
+ })""",
+ R"""(
+ root ::= ("-" ([0-9] | "10") | [0-9] | "10") space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
test({
FAILURE,
"unknown type",
)"""
});
+ test({
+ SUCCESS,
+ "min + max items with min + max values across zero",
+ R"""({
+ "items": {
+ "type": "integer",
+ "minimum": -12,
+ "maximum": 207
+ },
+ "minItems": 3,
+ "maxItems": 5
+ })""",
+ R"""(
+ item ::= ("-" ([0-9] | "1" [0-2]) | [0-9] | ([1-8] [0-9] | [9] [0-9]) | ([1] [0-9]{2} | [2] "0" [0-7])) space
+ root ::= "[" space item ("," space item){2,4} "]" space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
+ test({
+ SUCCESS,
+ "min + max items with min + max values",
+ R"""({
+ "items": {
+ "type": "integer",
+ "minimum": 12,
+ "maximum": 207
+ },
+ "minItems": 3,
+ "maxItems": 5
+ })""",
+ R"""(
+ item ::= (([1] ([2-9]) | [2-9] [0-9]) | ([1] [0-9]{2} | [2] "0" [0-7])) space
+ root ::= "[" space item ("," space item){2,4} "]" space
+ space ::= | " " | "\n" [ \t]{0,20}
+ )"""
+ });
+
test({
SUCCESS,
"simple regexp",