48 static const TokenValue TokenValue_Zero = { .s = NULL };
55 static const Token Token_Zero = { .type = TokenType_Zero, .value = TokenValue_Zero };
67 static const Context Context_Zero = {
77 static const char EndOfFile = 26;
79 static bool get_next_char( Context * context,
char * c ) {
80 if( context->look_ahead ) {
81 *c = context->look_ahead;
82 context->look_ahead =
'\0';
85 if( context->pos >= context->limit ) {
86 context->limit = fread( context->buffer, 1,
sizeof( context->buffer ), context->stream );
87 if(( context->limit == 0 )&& feof( context->stream )) {
93 *c = context->buffer[context->pos++];
97 static bool remaining_chars_are_only_whitespaces( Context * context ) {
98 while( context->pos < context->limit ) {
100 if( get_next_char( context, &c )&&( NULL == strchr(
" \t\r\n", c ))) {
108 static bool check_separator( Context * context,
JST_Error error ) {
110 if( ! get_next_char( context, &c )) {
111 context->error = error;
114 if( ( c ==
' ' )||( c ==
'\t' )
115 ||( c ==
'\r' )||( c ==
'\n' )
116 ||( c ==
',' )||( c ==
':' )
117 ||( c ==
'}' )||( c ==
']' )
118 ||( c ==
'\0' )||( c == EndOfFile ))
120 context->look_ahead = c;
123 context->error = error;
127 static bool is_separator_after_number(
char c ) {
129 || ( c ==
' ' )||( c ==
'\t' )
130 || ( c ==
'\r' )||( c ==
'\n' )
131 || ( c ==
',' )||( c ==
'}' )||( c ==
']' );
134 static bool get_number( Context * context,
char c, Token * token ) {
137 while( get_next_char( context, &c )
138 &&( ! is_separator_after_number( c ))
141 context->look_ahead = c;
149 token->value.i = strtoll( s.
buffer, &err, 10 );
150 if( is_separator_after_number( *err )) {
153 fprintf( stderr,
"TOKEN_INTEGER: %ld\n", token->value.i );
158 token->value.d = strtod( s.
buffer, &err );
159 if( is_separator_after_number( *err )) {
162 fprintf( stderr,
"TOKEN_DOUBLE: %G\n", token->value.d );
172 static bool get_string( Context * context, Token * token ) {
183 if( strchr(
"\b\f\n\r\t", c )) {
189 if( NULL == strchr(
"\"\\/bfnrtu", c )) {
194 else if( c ==
'u' ) {
195 for(
int i = 0; i < 4; ++i ) {
196 if( ! get_next_char( context, &c )) {
200 if( ! isxdigit( c )) {
212 if( ( ! get_next_char( context, &c ) )
224 token->value.s = strdup( s.
buffer );
227 fprintf( stderr,
"TOKEN_STRING: '%s'\n", token->value.s );
240 static bool get_true( Context * context, Token * token ) {
242 if( get_next_char( context, &c )&&( c ==
'r' )
243 && get_next_char( context, &c )&&( c ==
'u' )
244 && get_next_char( context, &c )&&( c ==
'e' ))
248 fprintf( stderr,
"TOKEN_TRUE\n" );
256 static bool get_false( Context * context, Token * token ) {
258 if( get_next_char( context, &c )&&( c ==
'a' )
259 && get_next_char( context, &c )&&( c ==
'l' )
260 && get_next_char( context, &c )&&( c ==
's' )
261 && get_next_char( context, &c )&&( c ==
'e' ))
265 fprintf( stderr,
"TOKEN_FALSE\n" );
273 static bool get_null( Context * context, Token * token ) {
275 if( get_next_char( context, &c )&&( c ==
'u' )
276 && get_next_char( context, &c )&&( c ==
'l' )
277 && get_next_char( context, &c )&&( c ==
'l' ))
281 fprintf( stderr,
"TOKEN_NULL\n" );
289 static bool get_next_token( Context * context, Token * token ) {
292 while(( c ==
' ' )||( c ==
'\t' )||( c ==
'\r' )||( c ==
'\n' )) {
293 if(( c ==
'\r' )||( c ==
'\n' )) {
296 if( ! get_next_char( context, &c )) {
303 fprintf( stderr,
"TOKEN_OPEN_OBJECT\n" );
309 fprintf( stderr,
"TOKEN_COLON\n" );
315 fprintf( stderr,
"TOKEN_COMMA\n" );
321 fprintf( stderr,
"TOKEN_CLOSE_OBJECT\n" );
327 fprintf( stderr,
"TOKEN_OPEN_ARRAY\n" );
333 fprintf( stderr,
"TOKEN_CLOSE_ARRAY\n" );
337 case '"':
return get_string( context, token );
338 case 't':
return get_true ( context, token );
339 case 'f':
return get_false ( context, token );
340 case 'n':
return get_null ( context, token );
341 case '-':
case '0':
case '1':
case '2':
case '3':
342 case '4':
case '5':
case '6':
case '7':
case '8':
343 case '9':
return get_number( context, c, token );
354 static bool set_value( Context * context,
JST_Element * node,
JST_Element * parent,
const Token * token ) {
355 switch( token->type ) {
358 return add_object( context, &(node->
value.
object), parent );
361 return add_array( context, &(node->
value.
array), parent );
392 static JST_Pair * add_property( Context * context,
char * property_name,
JST_Object * parent ) {
398 item->
name = property_name;
401 if( get_next_token( context, &token )) {
403 if( get_next_token( context, &token ) && set_value( context, &(item->
element), (
JST_Element *)item, &token )) {
411 free( property_name );
430 static void set_pair(
void * dest,
unsigned index,
void * src ) {
436 JST_Pair * prev =
object->items[index-1];
439 object->items[index] = src;
442 static void set_item(
void * dest,
unsigned index,
void * src ) {
447 object->parent = parent;
451 bool item_expected =
true;
453 if( get_next_token( context, &token )) {
454 if( item_expected ) {
459 JST_Pair * pair = add_property( context, token.value.s,
object );
463 if( get_next_token( context, &token )) {
499 bool item_expected =
true;
501 if( get_next_token( context, &token )) {
502 if( item_expected ) {
510 if( get_next_token( context, &token )) {
537 if(( stream == NULL )||( root == NULL )) {
541 Context context = Context_Zero;
542 Token token = Token_Zero;
543 context.stream = stream;
545 && get_next_token( &context, &token )
546 && set_value( &context, root, NULL, &token )
547 && remaining_chars_are_only_whitespaces( &context );
550 syntax_error->
line = (unsigned)( 1 + context.line );
551 size_t pos = (( context.pos < context.limit )||(context.limit == 0 )) ? context.pos : ( context.limit - 1 );
553 && (( context.pos - pos ) < (
sizeof( syntax_error->
context ) / 2 ))
554 && ( context.buffer[pos] !=
'\r' )
555 && ( context.buffer[pos] !=
'\n' ))
559 if(( context.buffer[pos] ==
'\r' )||( context.buffer[pos] ==
'\n' )) {
562 strncpy( syntax_error->
context, context.buffer + pos,
sizeof( syntax_error->
context ) - 1 );
564 syntax_error->
pos = (unsigned)( context.pos - pos );
565 if( syntax_error->
pos > 0 ) {
566 --(syntax_error->
pos);
570 return context.error;
574 if(( path == NULL )) {
577 FILE * stream = fopen( path,
"rt" );
578 if( stream == NULL ) {
struct JST_Pair_ ** items
Array of JST_Pair, ordered by JST_Pair.name to ease search with bsearch()
char * name
This property's name.
int JST_pairs_compare(const void *left, const void *right)
const JST_Object JST_Object_Zero
Constant defined to initialize safely a variable of type JST_Object.
A JSON Value may be an object, an array, a boolean, a number or a string.
const JST_Element JST_Element_Zero
Constant defined to initialize safely a variable of type JST_Element.
A object attribute item has a parent and is a named-typed-value pair.
An array item has a parent and a typed value.
JST_Error JST_load_from_file(const char *path, JST_Element *root, JST_SyntaxError *syntax_error)
Reads the input file, allocates the node of the corresponding tree, than frees the input buffer...
const JST_Value JST_Value_Zero
Constant defined to initialize safely a variable of type JST_Value.
A JSON object, a sorted set of named-value pairs.
JST_Error JST_load_from_stream(FILE *stream, JST_Element *root, JST_SyntaxError *syntax_error)
Reads the input stream, allocates the node of the corresponding tree, than frees the input buffer...
JST_Error JST_List_push_back(JST_List **first, JST_List **current, void *data)
const JST_ArrayItem JST_ArrayItem_Zero
bool JST_DEBUG_SHOW_TOKENS
char context[80]
Characters around the error.
struct JST_Pair_ * first
A linked list is mandatory to preserve the properties's order.
An element is a typed value.
Information returned by JST_load() in case of error.
bool JST_String_append_char(JST_String *string, char c)
const JST_SyntaxError JST_SyntaxError_Zero
Constant defined to initialize safely a variable of type JST_SyntaxError.
JST_Error JST_List_move_to(JST_List *from, void *dest, LST_assign_t assign)
For each item in list, call assign to transfert ownership of items and free the list, node by node.
JST_Object * parent
This property's owner.
JST_Error JST_String_delete(JST_String *string)
struct JST_Pair_ * next
A linked list is mandatory to preserve the properties's order.
unsigned line
Last line's index (from 1)
struct JST_ArrayItem_ ** items
Array of JST_ArrayItem.
const JST_Pair JST_Pair_Zero
Constant defined to initialize safely a variable of type JST_Pair.
unsigned count
Cardinality of the previous array.
struct JST_Element_ * parent
of type JST_ArrayItem or JST_Pair, case selector is parent->type
const JST_Array JST_Array_Zero
Constant defined to initialize safely a variable of type JST_Array.
JST_Element element
The value associated with the name.
const JST_String JST_String_Zero
unsigned count
Cardinality of the previous array.
unsigned pos
Position of error in context.