2024-03-21 11:50:43 +00:00
#ifdef NDEBUG
#undef NDEBUG
#endif
2024-05-08 21:53:08 +02:00
#include <cassert>
2024-03-21 11:50:43 +00:00
#include <fstream>
#include <sstream>
#include <regex>
#include "json-schema-to-grammar.h"
#include "grammar-parser.h"
static std :: string trim ( const std :: string & source ) {
std :: string s ( source );
s . erase ( 0 , s . find_first_not_of ( " \n\r\t " ));
s . erase ( s . find_last_not_of ( " \n\r\t " ) + 1 );
return std :: regex_replace ( s , std :: regex ( "(^| \n )[ \t ]+" ), "$1" );
}
enum TestCaseStatus {
SUCCESS ,
FAILURE
};
struct TestCase {
TestCaseStatus expected_status ;
std :: string name ;
std :: string schema ;
std :: string expected_grammar ;
void _print_failure_header () const {
fprintf ( stderr , "# \n # Test '%s' failed. \n # \n %s \n " , name . c_str (), schema . c_str ());
}
void verify ( const std :: string & actual_grammar ) const {
if ( trim ( actual_grammar ) != trim ( expected_grammar )) {
_print_failure_header ();
fprintf ( stderr , "# EXPECTED: \n %s \n # ACTUAL: \n %s \n " , expected_grammar . c_str (), actual_grammar . c_str ());
assert ( false );
}
}
void verify_expectation_parseable () const {
try {
auto state = grammar_parser :: parse ( expected_grammar . c_str ());
if ( state . symbol_ids . find ( "root" ) == state . symbol_ids . end ()) {
throw std :: runtime_error ( "Grammar failed to parse: \n " + expected_grammar );
}
} catch ( const std :: runtime_error & ex ) {
_print_failure_header ();
fprintf ( stderr , "# GRAMMAR ERROR: %s \n " , ex . what ());
assert ( false );
}
}
void verify_status ( TestCaseStatus status ) const {
if ( status != expected_status ) {
_print_failure_header ();
fprintf ( stderr , "# EXPECTED STATUS: %s \n " , expected_status == SUCCESS ? "SUCCESS" : "FAILURE" );
fprintf ( stderr , "# ACTUAL STATUS: %s \n " , status == SUCCESS ? "SUCCESS" : "FAILURE" );
assert ( false );
}
}
};
static void write ( const std :: string & file , const std :: string & content ) {
std :: ofstream f ;
f . open ( file . c_str ());
f << content . c_str ();
f . close ();
}
static std :: string read ( const std :: string & file ) {
std :: ostringstream actuals ;
actuals << std :: ifstream ( file . c_str ()). rdbuf ();
return actuals . str ();
}
static void test_all ( const std :: string & lang , std :: function < void ( const TestCase & ) > runner ) {
fprintf ( stderr , "# \n # Testing JSON schema conversion (%s) \n # \n " , lang . c_str ());
auto test = [ & ]( const TestCase & tc ) {
fprintf ( stderr , "- %s%s \n " , tc . name . c_str (), tc . expected_status == FAILURE ? " (failure expected)" : "" );
runner ( tc );
};
2024-06-25 20:06:20 +01:00
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 }
) """
});
2024-03-21 11:50:43 +00:00
test ({
FAILURE ,
"unknown type" ,
R """({
"type" : "kaboom"
}) """,
""
});
test ({
FAILURE ,
2024-03-22 13:07:44 +00:00
"invalid type" ,
2024-03-21 11:50:43 +00:00
R """({
"type" : 123
}) """,
""
});
test ({
SUCCESS ,
"empty schema (object)" ,
"{}" ,
R """(
array ::= "[" space ( value ( "," space value ) * ) ? "]" space
boolean ::= ( "true" | "false" ) space
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-06-06 10:07:06 +01:00
decimal - part ::= [ 0 - 9 ]{ 1 , 16 }
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-03-21 11:50:43 +00:00
null ::= "null" space
2024-04-12 19:43:38 +01:00
number ::= ( "-" ? integral - part ) ( "." decimal - part ) ? ([ eE ] [ -+ ] ? integral - part ) ? space
2024-03-21 11:50:43 +00:00
object ::= "{" space ( string ":" space value ( "," space string ":" space value ) * ) ? "}" space
root ::= object
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
string ::= " \" " char * " \" " space
value ::= object | array | string | number | boolean | null
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"exotic formats" ,
R """({
"items" : [
{ "format" : "date" },
{ "format" : "uuid" },
{ "format" : "time" },
{ "format" : "date-time" }
]
}) """,
R """(
2024-06-06 10:07:06 +01:00
date ::= [ 0 - 9 ]{ 4 } "-" ( "0" [ 1 - 9 ] | "1" [ 0 - 2 ] ) "-" ( "0" [ 1 - 9 ] | [ 1 - 2 ] [ 0 - 9 ] | "3" [ 0 - 1 ] )
2024-03-21 11:50:43 +00:00
date - string ::= " \" " date " \" " space
date - time ::= date "T" time
date - time - string ::= " \" " date - time " \" " space
2024-04-12 19:43:38 +01:00
root ::= "[" space tuple - 0 "," space uuid "," space tuple - 2 "," space tuple - 3 "]" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-06-06 10:07:06 +01:00
time ::= ([ 01 ] [ 0 - 9 ] | "2" [ 0 - 3 ]) ":" [ 0 - 5 ] [ 0 - 9 ] ":" [ 0 - 5 ] [ 0 - 9 ] ( "." [ 0 - 9 ]{ 3 } ) ? ( "Z" | ( "+" | "-" ) ( [ 01 ] [ 0 - 9 ] | "2" [ 0 - 3 ] ) ":" [ 0 - 5 ] [ 0 - 9 ] )
2024-03-21 11:50:43 +00:00
time - string ::= " \" " time " \" " space
2024-04-12 19:43:38 +01:00
tuple - 0 ::= date - string
tuple - 2 ::= time - string
tuple - 3 ::= date - time - string
2024-06-06 10:07:06 +01:00
uuid ::= " \" " [ 0 - 9 a - fA - F ]{ 8 } "-" [ 0 - 9 a - fA - F ]{ 4 } "-" [ 0 - 9 a - fA - F ]{ 4 } "-" [ 0 - 9 a - fA - F ]{ 4 } "-" [ 0 - 9 a - fA - F ]{ 12 } " \" " space
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"string" ,
R """({
"type" : "string"
}) """,
R """(
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-04-12 19:43:38 +01:00
root ::= " \" " char * " \" " space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
) """
});
test ({
SUCCESS ,
"string w/ min length 1" ,
R """({
"type" : "string" ,
"minLength" : 1
}) """,
R """(
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-04-12 19:43:38 +01:00
root ::= " \" " char + " \" " space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
) """
});
test ({
SUCCESS ,
"string w/ min length 3" ,
R """({
"type" : "string" ,
"minLength" : 3
}) """,
R """(
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-06-06 10:07:06 +01:00
root ::= " \" " char { 3 ,} " \" " space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
) """
});
test ({
SUCCESS ,
"string w/ max length" ,
R """({
"type" : "string" ,
"maxLength" : 3
}) """,
R """(
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-06-06 10:07:06 +01:00
root ::= " \" " char { 0 , 3 } " \" " space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
) """
});
test ({
SUCCESS ,
"string w/ min & max length" ,
R """({
"type" : "string" ,
"minLength" : 1 ,
"maxLength" : 4
}) """,
R """(
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-06-06 10:07:06 +01:00
root ::= " \" " char { 1 , 4 } " \" " space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"boolean" ,
R """({
"type" : "boolean"
}) """,
R """(
root ::= ( "true" | "false" ) space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"integer" ,
R """({
"type" : "integer"
}) """,
R """(
2024-06-06 10:07:06 +01:00
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-04-12 19:43:38 +01:00
root ::= ( "-" ? integral - part ) space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"string const" ,
R """({
"const" : "foo"
}) """,
R """(
2024-06-26 01:45:58 +01:00
root ::= " \" foo \" " space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
2024-03-22 13:07:44 +00:00
SUCCESS ,
2024-03-21 11:50:43 +00:00
"non-string const" ,
R """({
"const" : 123
}) """,
2024-03-22 13:07:44 +00:00
R """(
2024-06-26 01:45:58 +01:00
root ::= "123" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-22 13:07:44 +00:00
) """
2024-03-21 11:50:43 +00:00
});
test ({
2024-03-22 13:07:44 +00:00
SUCCESS ,
2024-03-21 11:50:43 +00:00
"non-string enum" ,
R """({
2024-03-22 13:07:44 +00:00
"enum" : [ "red" , "amber" , "green" , null , 42 , [ "foo" ]]
2024-03-21 11:50:43 +00:00
}) """,
2024-03-22 13:07:44 +00:00
R """(
2024-06-26 01:45:58 +01:00
root ::= ( " \" red \" " | " \" amber \" " | " \" green \" " | "null" | "42" | "[ \" foo \" ]" ) space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-22 13:07:44 +00:00
) """
2024-03-21 11:50:43 +00:00
});
2024-06-26 01:46:35 +01:00
test ({
SUCCESS ,
"string array" ,
R """({
"type" : "array" ,
"prefixItems" : { "type" : "string" }
}) """,
R """(
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
root ::= "[" space ( string ( "," space string ) * ) ? "]" space
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
string ::= " \" " char * " \" " space
) """
});
test ({
SUCCESS ,
"nullable string array" ,
R """({
"type" : [ "array" , "null" ],
"prefixItems" : { "type" : "string" }
}) """,
R """(
alternative - 0 ::= "[" space ( string ( "," space string ) * ) ? "]" space
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
null ::= "null" space
root ::= alternative - 0 | null
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
string ::= " \" " char * " \" " space
) """
});
2024-03-21 11:50:43 +00:00
test ({
SUCCESS ,
"tuple1" ,
R """({
"prefixItems" : [{ "type" : "string" }]
}) """,
R """(
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-03-21 11:50:43 +00:00
root ::= "[" space string "]" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
string ::= " \" " char * " \" " space
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"tuple2" ,
R """({
"prefixItems" : [{ "type" : "string" }, { "type" : "number" }]
}) """,
R """(
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-06-06 10:07:06 +01:00
decimal - part ::= [ 0 - 9 ]{ 1 , 16 }
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-04-12 19:43:38 +01:00
number ::= ( "-" ? integral - part ) ( "." decimal - part ) ? ([ eE ] [ -+ ] ? integral - part ) ? space
2024-03-21 11:50:43 +00:00
root ::= "[" space string "," space number "]" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
string ::= " \" " char * " \" " space
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"number" ,
R """({
"type" : "number"
}) """,
R """(
2024-06-06 10:07:06 +01:00
decimal - part ::= [ 0 - 9 ]{ 1 , 16 }
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-04-12 19:43:38 +01:00
root ::= ( "-" ? integral - part ) ( "." decimal - part ) ? ([ eE ] [ -+ ] ? integral - part ) ? space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"minItems" ,
R """({
"items" : {
"type" : "boolean"
},
"minItems" : 2
}) """,
R """(
boolean ::= ( "true" | "false" ) space
2024-06-06 10:07:06 +01:00
root ::= "[" space boolean ( "," space boolean ) + "]" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"maxItems 1" ,
R """({
"items" : {
"type" : "boolean"
},
"maxItems" : 1
}) """,
R """(
boolean ::= ( "true" | "false" ) space
2024-06-06 10:07:06 +01:00
root ::= "[" space boolean ? "]" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"maxItems 2" ,
R """({
"items" : {
"type" : "boolean"
},
"maxItems" : 2
}) """,
R """(
boolean ::= ( "true" | "false" ) space
2024-04-12 19:43:38 +01:00
root ::= "[" space ( boolean ( "," space boolean ) ? ) ? "]" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"min + maxItems" ,
R """({
"items" : {
"type" : [ "number" , "integer" ]
},
"minItems" : 3 ,
"maxItems" : 5
}) """,
R """(
2024-06-06 10:07:06 +01:00
decimal - part ::= [ 0 - 9 ]{ 1 , 16 }
2024-04-12 19:43:38 +01:00
integer ::= ( "-" ? integral - part ) space
2024-06-06 10:07:06 +01:00
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-03-21 11:50:43 +00:00
item ::= number | integer
2024-04-12 19:43:38 +01:00
number ::= ( "-" ? integral - part ) ( "." decimal - part ) ? ([ eE ] [ -+ ] ? integral - part ) ? space
2024-06-06 10:07:06 +01:00
root ::= "[" space item ( "," space item ){ 2 , 4 } "]" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
2024-06-25 20:06:20 +01:00
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 }
) """
});
2024-03-21 11:50:43 +00:00
test ({
SUCCESS ,
"simple regexp" ,
R """({
"type" : "string" ,
"pattern" : "^abc?d*efg+(hij)?kl$"
}) """,
R """(
root ::= " \" " "ab" "c" ? "d" * "ef" "g" + ( "hij" ) ? "kl" " \" " space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"regexp escapes" ,
R """({
"type" : "string" ,
"pattern" : "^ \\ [ \\ ] \\ { \\ } \\ ( \\ ) \\ | \\ + \\ * \\ ?$"
}) """,
R """(
root ::= " \" " "[]{}()|+*?" " \" " space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"regexp quote" ,
R """({
"type" : "string" ,
"pattern" : "^ \" $"
}) """,
R """(
root ::= " \" " " \" " " \" " space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"regexp" ,
R """({
"type" : "string" ,
2024-04-12 19:43:38 +01:00
"pattern" : "^( \\ ([0-9]{1,3} \\ ))?[0-9]{3}-[0-9]{4} a{3,5}nd...$"
2024-03-21 11:50:43 +00:00
}) """,
R """(
2024-04-12 19:43:38 +01:00
dot ::= [ ^ \ x0A \ x0D ]
2024-06-06 10:07:06 +01:00
root ::= " \" " ( "(" root - 1 { 1 , 3 } ")" ) ? root - 1 { 3 , 3 } "-" root - 1 { 4 , 4 } " " "a" { 3 , 5 } "nd" dot dot dot " \" " space
2024-03-21 11:50:43 +00:00
root - 1 ::= [ 0 - 9 ]
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
2024-03-22 13:07:44 +00:00
"required props in original order" ,
2024-03-21 11:50:43 +00:00
R """({
"type" : "object" ,
"properties" : {
2024-03-22 13:07:44 +00:00
"b" : { "type" : "string" },
"c" : { "type" : "string" },
"a" : { "type" : "string" }
2024-03-21 11:50:43 +00:00
},
"required" : [
"a" ,
2024-03-22 13:07:44 +00:00
"b" ,
"c"
2024-03-21 11:50:43 +00:00
],
"additionalProperties" : false ,
"definitions" : {}
}) """,
R """(
a - kv ::= " \" a \" " space ":" space string
b - kv ::= " \" b \" " space ":" space string
2024-03-22 13:07:44 +00:00
c - kv ::= " \" c \" " space ":" space string
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-03-22 13:07:44 +00:00
root ::= "{" space b - kv "," space c - kv "," space a - kv "}" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
string ::= " \" " char * " \" " space
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"1 optional prop" ,
R """({
"properties" : {
"a" : {
"type" : "string"
}
},
"additionalProperties" : false
}) """,
R """(
a - kv ::= " \" a \" " space ":" space string
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-03-21 11:50:43 +00:00
root ::= "{" space ( a - kv ) ? "}" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
string ::= " \" " char * " \" " space
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"N optional props" ,
R """({
"properties" : {
"a" : { "type" : "string" },
"b" : { "type" : "string" },
"c" : { "type" : "string" }
},
"additionalProperties" : false
}) """,
R """(
a - kv ::= " \" a \" " space ":" space string
a - rest ::= ( "," space b - kv ) ? b - rest
b - kv ::= " \" b \" " space ":" space string
b - rest ::= ( "," space c - kv ) ?
c - kv ::= " \" c \" " space ":" space string
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-03-21 11:50:43 +00:00
root ::= "{" space ( a - kv a - rest | b - kv b - rest | c - kv ) ? "}" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
string ::= " \" " char * " \" " space
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
2024-03-22 13:07:44 +00:00
"required + optional props each in original order" ,
2024-03-21 11:50:43 +00:00
R """({
"properties" : {
"b" : { "type" : "string" },
2024-03-22 13:07:44 +00:00
"a" : { "type" : "string" },
"d" : { "type" : "string" },
"c" : { "type" : "string" }
2024-03-21 11:50:43 +00:00
},
"required" : [ "a" , "b" ],
"additionalProperties" : false
}) """,
R """(
a - kv ::= " \" a \" " space ":" space string
b - kv ::= " \" b \" " space ":" space string
c - kv ::= " \" c \" " space ":" space string
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-03-21 11:50:43 +00:00
d - kv ::= " \" d \" " space ":" space string
2024-03-22 13:07:44 +00:00
d - rest ::= ( "," space c - kv ) ?
root ::= "{" space b - kv "," space a - kv ( "," space ( d - kv d - rest | c - kv ) ) ? "}" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
string ::= " \" " char * " \" " space
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"additional props" ,
R """({
"type" : "object" ,
"additionalProperties" : { "type" : "array" , "items" : { "type" : "number" }}
}) """,
R """(
additional - kv ::= string ":" space additional - value
2024-04-12 19:43:38 +01:00
additional - value ::= "[" space ( number ( "," space number ) * ) ? "]" space
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-06-06 10:07:06 +01:00
decimal - part ::= [ 0 - 9 ]{ 1 , 16 }
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-04-12 19:43:38 +01:00
number ::= ( "-" ? integral - part ) ( "." decimal - part ) ? ([ eE ] [ -+ ] ? integral - part ) ? space
2024-06-26 01:45:58 +01:00
root ::= "{" space ( additional - kv ( "," space additional - kv ) * ) ? "}" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
string ::= " \" " char * " \" " space
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"additional props (true)" ,
R """({
"type" : "object" ,
"additionalProperties" : true
}) """,
R """(
array ::= "[" space ( value ( "," space value ) * ) ? "]" space
boolean ::= ( "true" | "false" ) space
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-06-06 10:07:06 +01:00
decimal - part ::= [ 0 - 9 ]{ 1 , 16 }
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-03-21 11:50:43 +00:00
null ::= "null" space
2024-04-12 19:43:38 +01:00
number ::= ( "-" ? integral - part ) ( "." decimal - part ) ? ([ eE ] [ -+ ] ? integral - part ) ? space
2024-03-21 11:50:43 +00:00
object ::= "{" space ( string ":" space value ( "," space string ":" space value ) * ) ? "}" space
root ::= object
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
string ::= " \" " char * " \" " space
value ::= object | array | string | number | boolean | null
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"additional props (implicit)" ,
R """({
"type" : "object"
}) """,
R """(
array ::= "[" space ( value ( "," space value ) * ) ? "]" space
boolean ::= ( "true" | "false" ) space
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-06-06 10:07:06 +01:00
decimal - part ::= [ 0 - 9 ]{ 1 , 16 }
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-03-21 11:50:43 +00:00
null ::= "null" space
2024-04-12 19:43:38 +01:00
number ::= ( "-" ? integral - part ) ( "." decimal - part ) ? ([ eE ] [ -+ ] ? integral - part ) ? space
2024-03-21 11:50:43 +00:00
object ::= "{" space ( string ":" space value ( "," space string ":" space value ) * ) ? "}" space
root ::= object
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
string ::= " \" " char * " \" " space
value ::= object | array | string | number | boolean | null
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"empty w/o additional props" ,
R """({
"type" : "object" ,
"additionalProperties" : false
}) """,
R """(
root ::= "{" space "}" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"required + additional props" ,
R """({
"type" : "object" ,
"properties" : {
"a" : { "type" : "number" }
},
"required" : [ "a" ],
"additionalProperties" : { "type" : "string" }
}) """,
R """(
a - kv ::= " \" a \" " space ":" space number
2024-06-26 01:45:58 +01:00
additional - k ::= [ "] ( [a] char+ | [^" a ] char * ) ? [ "] space
additional - kv ::= additional - k ":" space string
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-06-06 10:07:06 +01:00
decimal - part ::= [ 0 - 9 ]{ 1 , 16 }
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-04-12 19:43:38 +01:00
number ::= ( "-" ? integral - part ) ( "." decimal - part ) ? ([ eE ] [ -+ ] ? integral - part ) ? space
2024-06-26 01:45:58 +01:00
root ::= "{" space a - kv ( "," space ( additional - kv ( "," space additional - kv ) * ) ) ? "}" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
string ::= " \" " char * " \" " space
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"optional + additional props" ,
R """({
"type" : "object" ,
"properties" : {
"a" : { "type" : "number" }
},
"additionalProperties" : { "type" : "number" }
}) """,
R """(
a - kv ::= " \" a \" " space ":" space number
2024-06-26 01:45:58 +01:00
a - rest ::= ( "," space additional - kv ) *
additional - k ::= [ "] ( [a] char+ | [^" a ] char * ) ? [ "] space
additional - kv ::= additional - k ":" space number
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-06-06 10:07:06 +01:00
decimal - part ::= [ 0 - 9 ]{ 1 , 16 }
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-04-12 19:43:38 +01:00
number ::= ( "-" ? integral - part ) ( "." decimal - part ) ? ([ eE ] [ -+ ] ? integral - part ) ? space
2024-06-26 01:45:58 +01:00
root ::= "{" space ( a - kv a - rest | additional - kv ( "," space additional - kv ) * ) ? "}" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"required + optional + additional props" ,
R """({
"type" : "object" ,
"properties" : {
2024-06-26 01:45:58 +01:00
"and" : { "type" : "number" },
"also" : { "type" : "number" }
2024-03-21 11:50:43 +00:00
},
2024-06-26 01:45:58 +01:00
"required" : [ "and" ],
2024-03-21 11:50:43 +00:00
"additionalProperties" : { "type" : "number" }
}) """,
R """(
2024-06-26 01:45:58 +01:00
additional - k ::= [ "] ( [a] ([l] ([s] ([o] char+ | [^" o ] char * ) | [ ^ "s] char*) | [n] ([d] char+ | [^" d ] char * ) | [ ^ "ln] char*) | [^" a ] char * ) ? [ "] space
additional - kv ::= additional - k ":" space number
also - kv ::= " \" also \" " space ":" space number
also - rest ::= ( "," space additional - kv ) *
and - kv ::= " \" and \" " space ":" space number
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-06-06 10:07:06 +01:00
decimal - part ::= [ 0 - 9 ]{ 1 , 16 }
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-04-12 19:43:38 +01:00
number ::= ( "-" ? integral - part ) ( "." decimal - part ) ? ([ eE ] [ -+ ] ? integral - part ) ? space
2024-06-26 01:45:58 +01:00
root ::= "{" space and - kv ( "," space ( also - kv also - rest | additional - kv ( "," space additional - kv ) * ) ) ? "}" space
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
) """
});
test ({
SUCCESS ,
"optional props with empty name" ,
R """({
"properties" : {
"" : { "type" : "integer" },
"a" : { "type" : "integer" }
},
"additionalProperties" : { "type" : "integer" }
}) """,
R """(
- kv ::= " \"\" " space ":" space root
- rest ::= ( "," space a - kv ) ? a - rest
a - kv ::= " \" a \" " space ":" space integer
a - rest ::= ( "," space additional - kv ) *
additional - k ::= [ "] ( [a] char+ | [^" a ] char * ) [ "] space
additional - kv ::= additional - k ":" space integer
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
integer ::= ( "-" ? integral - part ) space
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
root ::= ( "-" ? integral - part ) space
root0 ::= "{" space ( - kv - rest | a - kv a - rest | additional - kv ( "," space additional - kv ) * ) ? "}" space
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
) """
});
test ({
SUCCESS ,
"optional props with nested names" ,
R """({
"properties" : {
"a" : { "type" : "integer" },
"aa" : { "type" : "integer" }
},
"additionalProperties" : { "type" : "integer" }
}) """,
R """(
a - kv ::= " \" a \" " space ":" space integer
a - rest ::= ( "," space aa - kv ) ? aa - rest
aa - kv ::= " \" aa \" " space ":" space integer
aa - rest ::= ( "," space additional - kv ) *
additional - k ::= [ "] ( [a] ([a] char+ | [^" a ] char * ) | [ ^ "a] char* )? [" ] space
additional - kv ::= additional - k ":" space integer
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
integer ::= ( "-" ? integral - part ) space
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
root ::= "{" space ( a - kv a - rest | aa - kv aa - rest | additional - kv ( "," space additional - kv ) * ) ? "}" space
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
) """
});
test ({
SUCCESS ,
"optional props with common prefix" ,
R """({
"properties" : {
"ab" : { "type" : "integer" },
"ac" : { "type" : "integer" }
},
"additionalProperties" : { "type" : "integer" }
}) """,
R """(
ab - kv ::= " \" ab \" " space ":" space integer
ab - rest ::= ( "," space ac - kv ) ? ac - rest
ac - kv ::= " \" ac \" " space ":" space integer
ac - rest ::= ( "," space additional - kv ) *
additional - k ::= [ "] ( [a] ([b] char+ | [c] char+ | [^" bc ] char * ) | [ ^ "a] char* )? [" ] space
additional - kv ::= additional - k ":" space integer
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
integer ::= ( "-" ? integral - part ) space
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
root ::= "{" space ( ab - kv ab - rest | ac - kv ac - rest | additional - kv ( "," space additional - kv ) * ) ? "}" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"top-level $ref" ,
R """({
2024-04-12 19:43:38 +01:00
"$ref" : "#/definitions/foo" ,
2024-03-21 11:50:43 +00:00
"definitions" : {
2024-04-12 19:43:38 +01:00
"foo" : {
2024-03-22 13:07:44 +00:00
"type" : "object" ,
"properties" : {
"a" : {
"type" : "string"
}
},
"required" : [
"a"
],
"additionalProperties" : false
2024-03-21 11:50:43 +00:00
}
}
}) """,
R """(
2024-06-11 01:00:30 +01:00
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-04-12 19:43:38 +01:00
foo ::= "{" space foo - a - kv "}" space
foo - a - kv ::= " \" a \" " space ":" space string
root ::= foo
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-04-12 19:43:38 +01:00
string ::= " \" " char * " \" " space
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"anyOf" ,
R """({
"anyOf" : [
{ "$ref" : "#/definitions/foo" },
{ "$ref" : "#/definitions/bar" }
],
"definitions" : {
"foo" : {
2024-03-22 13:07:44 +00:00
"properties" : { "a" : { "type" : "number" }}
2024-03-21 11:50:43 +00:00
},
"bar" : {
2024-03-22 13:07:44 +00:00
"properties" : { "b" : { "type" : "number" }}
2024-03-21 11:50:43 +00:00
}
},
"type" : "object"
}) """,
R """(
alternative - 0 ::= foo
alternative - 1 ::= bar
2024-06-26 01:45:58 +01:00
array ::= "[" space ( value ( "," space value ) * ) ? "]" space
bar ::= "{" space ( bar - b - kv bar - b - rest | bar - additional - kv ( "," space bar - additional - kv ) * ) ? "}" space
bar - additional - k ::= [ "] ( [b] char+ | [^" b ] char * ) ? [ "] space
bar - additional - kv ::= bar - additional - k ":" space value
2024-03-21 11:50:43 +00:00
bar - b - kv ::= " \" b \" " space ":" space number
2024-06-26 01:45:58 +01:00
bar - b - rest ::= ( "," space bar - additional - kv ) *
boolean ::= ( "true" | "false" ) space
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-06-06 10:07:06 +01:00
decimal - part ::= [ 0 - 9 ]{ 1 , 16 }
2024-06-26 01:45:58 +01:00
foo ::= "{" space ( foo - a - kv foo - a - rest | foo - additional - kv ( "," space foo - additional - kv ) * ) ? "}" space
2024-03-21 11:50:43 +00:00
foo - a - kv ::= " \" a \" " space ":" space number
2024-06-26 01:45:58 +01:00
foo - a - rest ::= ( "," space foo - additional - kv ) *
foo - additional - k ::= [ "] ( [a] char+ | [^" a ] char * ) ? [ "] space
foo - additional - kv ::= foo - additional - k ":" space value
2024-06-06 10:07:06 +01:00
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-06-26 01:45:58 +01:00
null ::= "null" space
2024-04-12 19:43:38 +01:00
number ::= ( "-" ? integral - part ) ( "." decimal - part ) ? ([ eE ] [ -+ ] ? integral - part ) ? space
2024-06-26 01:45:58 +01:00
object ::= "{" space ( string ":" space value ( "," space string ":" space value ) * ) ? "}" space
2024-03-21 11:50:43 +00:00
root ::= alternative - 0 | alternative - 1
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-06-26 01:45:58 +01:00
string ::= " \" " char * " \" " space
value ::= object | array | string | number | boolean | null
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"mix of allOf, anyOf and $ref (similar to https://json.schemastore.org/tsconfig.json)" ,
R """({
"allOf" : [
{ "$ref" : "#/definitions/foo" },
{ "$ref" : "#/definitions/bar" },
{
"anyOf" : [
{ "$ref" : "#/definitions/baz" },
{ "$ref" : "#/definitions/bam" }
]
}
],
"definitions" : {
"foo" : {
2024-03-22 13:07:44 +00:00
"properties" : { "a" : { "type" : "number" }}
2024-03-21 11:50:43 +00:00
},
"bar" : {
2024-03-22 13:07:44 +00:00
"properties" : { "b" : { "type" : "number" }}
2024-03-21 11:50:43 +00:00
},
"bam" : {
2024-03-22 13:07:44 +00:00
"properties" : { "c" : { "type" : "number" }}
2024-03-21 11:50:43 +00:00
},
"baz" : {
2024-03-22 13:07:44 +00:00
"properties" : { "d" : { "type" : "number" }}
2024-03-21 11:50:43 +00:00
}
},
"type" : "object"
}) """,
R """(
a - kv ::= " \" a \" " space ":" space number
2024-06-26 01:45:58 +01:00
additional - k ::= [ "] ( [a] char+ | [b] char+ | [c] char+ | [d] char+ | [^" abcd ] char * ) ? [ "] space
additional - kv ::= additional - k ":" space value
array ::= "[" space ( value ( "," space value ) * ) ? "]" space
2024-03-21 11:50:43 +00:00
b - kv ::= " \" b \" " space ":" space number
2024-06-26 01:45:58 +01:00
boolean ::= ( "true" | "false" ) space
2024-03-21 11:50:43 +00:00
c - kv ::= " \" c \" " space ":" space number
2024-06-26 01:45:58 +01:00
c - rest ::= ( "," space additional - kv ) *
char ::= [ ^ " \\\x7F\x00 - \x1F ] | [ \\ ] ([" \\ bfnrt ] | "u" [ 0 - 9 a - fA - F ]{ 4 })
2024-03-21 11:50:43 +00:00
d - kv ::= " \" d \" " space ":" space number
2024-06-26 01:45:58 +01:00
d - rest ::= ( "," space c - kv ) ? c - rest
2024-06-06 10:07:06 +01:00
decimal - part ::= [ 0 - 9 ]{ 1 , 16 }
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-06-26 01:45:58 +01:00
null ::= "null" space
2024-04-12 19:43:38 +01:00
number ::= ( "-" ? integral - part ) ( "." decimal - part ) ? ([ eE ] [ -+ ] ? integral - part ) ? space
2024-06-26 01:45:58 +01:00
object ::= "{" space ( string ":" space value ( "," space string ":" space value ) * ) ? "}" space
root ::= "{" space a - kv "," space b - kv ( "," space ( d - kv d - rest | c - kv c - rest | additional - kv ( "," space additional - kv ) * ) ) ? "}" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-06-26 01:45:58 +01:00
string ::= " \" " char * " \" " space
value ::= object | array | string | number | boolean | null
2024-03-21 11:50:43 +00:00
) """
});
test ({
SUCCESS ,
"conflicting names" ,
R """({
"type" : "object" ,
"properties" : {
"number" : {
"type" : "object" ,
"properties" : {
"number" : {
"type" : "object" ,
2024-03-22 13:07:44 +00:00
"properties" : {
"root" : {
"type" : "number"
}
},
"required" : [
"root"
],
"additionalProperties" : false
2024-03-21 11:50:43 +00:00
}
},
"required" : [
"number"
],
"additionalProperties" : false
}
},
"required" : [
"number"
],
"additionalProperties" : false ,
"definitions" : {}
}) """,
R """(
2024-06-06 10:07:06 +01:00
decimal - part ::= [ 0 - 9 ]{ 1 , 16 }
integral - part ::= [ 0 ] | [ 1 - 9 ] [ 0 - 9 ]{ 0 , 15 }
2024-04-12 19:43:38 +01:00
number ::= ( "-" ? integral - part ) ( "." decimal - part ) ? ([ eE ] [ -+ ] ? integral - part ) ? space
2024-03-21 11:50:43 +00:00
number - ::= "{" space number - number - kv "}" space
number - kv ::= " \" number \" " space ":" space number -
number - number ::= "{" space number - number - root - kv "}" space
number - number - kv ::= " \" number \" " space ":" space number - number
number - number - root - kv ::= " \" root \" " space ":" space number
root ::= "{" space number - kv "}" space
2024-06-11 02:22:57 +01:00
space ::= | " " | " \n " [ \ t ]{ 0 , 20 }
2024-03-21 11:50:43 +00:00
) """
});
}
int main () {
2024-03-22 13:09:07 +00:00
fprintf ( stderr , "LLAMA_NODE_AVAILABLE = %s \n " , getenv ( "LLAMA_NODE_AVAILABLE" ) ? "true" : "false" );
fprintf ( stderr , "LLAMA_PYTHON_AVAILABLE = %s \n " , getenv ( "LLAMA_PYTHON_AVAILABLE" ) ? "true" : "false" );
2024-03-21 11:50:43 +00:00
test_all ( "C++" , []( const TestCase & tc ) {
try {
2024-03-22 13:07:44 +00:00
tc . verify ( json_schema_to_grammar ( nlohmann :: ordered_json :: parse ( tc . schema )));
2024-03-21 11:50:43 +00:00
tc . verify_status ( SUCCESS );
} catch ( const std :: runtime_error & ex ) {
fprintf ( stderr , "Error: %s \n " , ex . what ());
tc . verify_status ( FAILURE );
}
});
2024-03-22 13:09:07 +00:00
2024-06-11 10:10:20 +03:00
if ( getenv ( "LLAMA_PYTHON_AVAILABLE" ) || ( std :: system ( "python -c \" import sys; exit(1) if sys.version_info < (3, 8) else print('Python version is sufficient') \" " ) == 0 )) {
2024-03-22 13:09:07 +00:00
test_all ( "Python" , []( const TestCase & tc ) {
write ( "test-json-schema-input.tmp" , tc . schema );
tc . verify_status ( std :: system (
2024-04-12 19:43:38 +01:00
"python ./examples/json_schema_to_grammar.py test-json-schema-input.tmp > test-grammar-output.tmp" ) == 0 ? SUCCESS : FAILURE );
2024-03-22 13:09:07 +00:00
tc . verify ( read ( "test-grammar-output.tmp" ));
});
} else {
2024-06-11 10:10:20 +03:00
fprintf ( stderr , " \033 [33mWARNING: Python not found (min version required is 3.8), skipping Python JSON schema -> grammar tests. \n\033 [0m" );
2024-03-22 13:09:07 +00:00
}
if ( getenv ( "LLAMA_NODE_AVAILABLE" ) || ( std :: system ( "node --version" ) == 0 )) {
test_all ( "JavaScript" , []( const TestCase & tc ) {
write ( "test-json-schema-input.tmp" , tc . schema );
tc . verify_status ( std :: system (
"node ./tests/run-json-schema-to-grammar.mjs test-json-schema-input.tmp > test-grammar-output.tmp" ) == 0 ? SUCCESS : FAILURE );
tc . verify ( read ( "test-grammar-output.tmp" ));
});
} else {
fprintf ( stderr , " \033 [33mWARNING: Node not found, skipping JavaScript JSON schema -> grammar tests. \n\033 [0m" );
}
2024-03-21 11:50:43 +00:00
test_all ( "Check Expectations Validity" , []( const TestCase & tc ) {
if ( tc . expected_status == SUCCESS ) {
tc . verify_expectation_parseable ();
}
});
}