mirror of
https://github.com/Ed94/gencpp.git
synced 2024-11-13 20:24:52 -08:00
1111 lines
23 KiB
C++
1111 lines
23 KiB
C++
#ifdef GEN_INTELLISENSE_DIRECTIVES
|
|
# pragma once
|
|
#endif
|
|
|
|
#pragma region ADT
|
|
|
|
#define _adt_fprintf( s_, fmt_, ... ) \
|
|
do \
|
|
{ \
|
|
if ( str_fmt_file( s_, fmt_, ##__VA_ARGS__ ) < 0 ) \
|
|
return EADT_ERROR_OUT_OF_MEMORY; \
|
|
} while ( 0 )
|
|
|
|
u8 adt_make_branch( ADT_Node* node, AllocatorInfo backing, char const* name, b32 is_array )
|
|
{
|
|
ADT_Type type = EADT_TYPE_OBJECT;
|
|
if ( is_array )
|
|
type = EADT_TYPE_ARRAY;
|
|
|
|
ADT_Node* parent = node->parent;
|
|
zero_item( node );
|
|
|
|
node->type = type;
|
|
node->name = name;
|
|
node->parent = parent;
|
|
node->nodes = Array<ADT_Node>::init( backing );
|
|
|
|
if ( ! node->nodes )
|
|
return EADT_ERROR_OUT_OF_MEMORY;
|
|
|
|
return 0;
|
|
}
|
|
|
|
u8 adt_destroy_branch( ADT_Node* node )
|
|
{
|
|
GEN_ASSERT_NOT_NULL( node );
|
|
if ( ( node->type == EADT_TYPE_OBJECT || node->type == EADT_TYPE_ARRAY ) && node->nodes )
|
|
{
|
|
for ( sw i = 0; i < node->nodes.num(); ++i )
|
|
{
|
|
adt_destroy_branch( node->nodes + i );
|
|
}
|
|
|
|
node->nodes.free();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
u8 adt_make_leaf( ADT_Node* node, char const* name, ADT_Type type )
|
|
{
|
|
GEN_ASSERT( type != EADT_TYPE_OBJECT && type != EADT_TYPE_ARRAY );
|
|
|
|
ADT_Node* parent = node->parent;
|
|
zero_item( node );
|
|
|
|
node->type = type;
|
|
node->name = name;
|
|
node->parent = parent;
|
|
return 0;
|
|
}
|
|
|
|
ADT_Node* adt_find( ADT_Node* node, char const* name, b32 deep_search )
|
|
{
|
|
if ( node->type != EADT_TYPE_OBJECT )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for ( sw i = 0; i < node->nodes.num(); i++ )
|
|
{
|
|
if ( ! str_compare( node->nodes[ i ].name, name ) )
|
|
{
|
|
return ( node->nodes + i );
|
|
}
|
|
}
|
|
|
|
if ( deep_search )
|
|
{
|
|
for ( sw i = 0; i < node->nodes.num(); i++ )
|
|
{
|
|
ADT_Node* res = adt_find( node->nodes + i, name, deep_search );
|
|
|
|
if ( res != NULL )
|
|
return res;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
internal ADT_Node* _adt_get_value( ADT_Node* node, char const* value )
|
|
{
|
|
switch ( node->type )
|
|
{
|
|
case EADT_TYPE_MULTISTRING :
|
|
case EADT_TYPE_STRING :
|
|
{
|
|
if ( node->string && ! str_compare( node->string, value ) )
|
|
{
|
|
return node;
|
|
}
|
|
}
|
|
break;
|
|
case EADT_TYPE_INTEGER :
|
|
case EADT_TYPE_REAL :
|
|
{
|
|
char back[ 4096 ] = { 0 };
|
|
FileInfo tmp;
|
|
|
|
/* allocate a file descriptor for a memory-mapped number to string conversion, input source buffer is not cloned, however. */
|
|
file_stream_open( &tmp, heap(), ( u8* )back, size_of( back ), EFileStream_WRITABLE );
|
|
adt_print_number( &tmp, node );
|
|
|
|
sw fsize = 0;
|
|
u8* buf = file_stream_buf( &tmp, &fsize );
|
|
|
|
if ( ! str_compare( ( char const* )buf, value ) )
|
|
{
|
|
file_close( &tmp );
|
|
return node;
|
|
}
|
|
|
|
file_close( &tmp );
|
|
}
|
|
break;
|
|
default :
|
|
break; /* node doesn't support value based lookup */
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
internal ADT_Node* _adt_get_field( ADT_Node* node, char* name, char* value )
|
|
{
|
|
for ( sw i = 0; i < node->nodes.num(); i++ )
|
|
{
|
|
if ( ! str_compare( node->nodes[ i ].name, name ) )
|
|
{
|
|
ADT_Node* child = &node->nodes[ i ];
|
|
if ( _adt_get_value( child, value ) )
|
|
{
|
|
return node; /* this object does contain a field of a specified value! */
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
ADT_Node* adt_query( ADT_Node* node, char const* uri )
|
|
{
|
|
GEN_ASSERT_NOT_NULL( uri );
|
|
|
|
if ( *uri == '/' )
|
|
{
|
|
uri++;
|
|
}
|
|
|
|
if ( *uri == 0 )
|
|
{
|
|
return node;
|
|
}
|
|
|
|
if ( ! node || ( node->type != EADT_TYPE_OBJECT && node->type != EADT_TYPE_ARRAY ) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
#if defined EADT_URI_DEBUG || 0
|
|
str_fmt_out( "uri: %s\n", uri );
|
|
#endif
|
|
|
|
char * p = ( char* )uri, *b = p, *e = p;
|
|
ADT_Node* found_node = NULL;
|
|
|
|
b = p;
|
|
p = e = ( char* )str_skip( p, '/' );
|
|
char* buf = str_fmt_buf( "%.*s", ( int )( e - b ), b );
|
|
|
|
/* handle field value lookup */
|
|
if ( *b == '[' )
|
|
{
|
|
char *l_p = buf + 1, *l_b = l_p, *l_e = l_p, *l_b2 = l_p, *l_e2 = l_p;
|
|
l_e = ( char* )str_skip( l_p, '=' );
|
|
l_e2 = ( char* )str_skip( l_p, ']' );
|
|
|
|
if ( ( ! *l_e && node->type != EADT_TYPE_ARRAY ) || ! *l_e2 )
|
|
{
|
|
GEN_ASSERT_MSG( 0, "Invalid field value lookup" );
|
|
return NULL;
|
|
}
|
|
|
|
*l_e2 = 0;
|
|
|
|
/* [field=value] */
|
|
if ( *l_e )
|
|
{
|
|
*l_e = 0;
|
|
l_b2 = l_e + 1;
|
|
|
|
/* run a value comparison against our own fields */
|
|
if ( node->type == EADT_TYPE_OBJECT )
|
|
{
|
|
found_node = _adt_get_field( node, l_b, l_b2 );
|
|
}
|
|
|
|
/* run a value comparison against any child that is an object node */
|
|
else if ( node->type == EADT_TYPE_ARRAY )
|
|
{
|
|
for ( sw i = 0; i < node->nodes.num(); i++ )
|
|
{
|
|
ADT_Node* child = &node->nodes[ i ];
|
|
if ( child->type != EADT_TYPE_OBJECT )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
found_node = _adt_get_field( child, l_b, l_b2 );
|
|
|
|
if ( found_node )
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* [value] */
|
|
else
|
|
{
|
|
for ( sw i = 0; i < node->nodes.num(); i++ )
|
|
{
|
|
ADT_Node* child = &node->nodes[ i ];
|
|
if ( _adt_get_value( child, l_b2 ) )
|
|
{
|
|
found_node = child;
|
|
break; /* we found a matching value in array, ignore the rest of it */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* go deeper if uri continues */
|
|
if ( *e )
|
|
{
|
|
return adt_query( found_node, e + 1 );
|
|
}
|
|
}
|
|
/* handle field name lookup */
|
|
else if ( node->type == EADT_TYPE_OBJECT )
|
|
{
|
|
found_node = adt_find( node, buf, false );
|
|
|
|
/* go deeper if uri continues */
|
|
if ( *e )
|
|
{
|
|
return adt_query( found_node, e + 1 );
|
|
}
|
|
}
|
|
/* handle array index lookup */
|
|
else
|
|
{
|
|
sw idx = ( sw )str_to_i64( buf, NULL, 10 );
|
|
if ( idx >= 0 && idx < node->nodes.num() )
|
|
{
|
|
found_node = &node->nodes[ idx ];
|
|
|
|
/* go deeper if uri continues */
|
|
if ( *e )
|
|
{
|
|
return adt_query( found_node, e + 1 );
|
|
}
|
|
}
|
|
}
|
|
|
|
return found_node;
|
|
}
|
|
|
|
ADT_Node* adt_alloc_at( ADT_Node* parent, sw index )
|
|
{
|
|
if ( ! parent || ( parent->type != EADT_TYPE_OBJECT && parent->type != EADT_TYPE_ARRAY ) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if ( ! parent->nodes )
|
|
return NULL;
|
|
|
|
if ( index < 0 || index > parent->nodes.num() )
|
|
return NULL;
|
|
|
|
ADT_Node o = { 0 };
|
|
o.parent = parent;
|
|
if ( ! parent->nodes.append_at( o, index ) )
|
|
return NULL;
|
|
|
|
return parent->nodes + index;
|
|
}
|
|
|
|
ADT_Node* adt_alloc( ADT_Node* parent )
|
|
{
|
|
if ( ! parent || ( parent->type != EADT_TYPE_OBJECT && parent->type != EADT_TYPE_ARRAY ) )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
if ( ! parent->nodes )
|
|
return NULL;
|
|
|
|
return adt_alloc_at( parent, parent->nodes.num() );
|
|
}
|
|
|
|
b8 adt_set_obj( ADT_Node* obj, char const* name, AllocatorInfo backing )
|
|
{
|
|
return adt_make_branch( obj, backing, name, 0 );
|
|
}
|
|
|
|
b8 adt_set_arr( ADT_Node* obj, char const* name, AllocatorInfo backing )
|
|
{
|
|
return adt_make_branch( obj, backing, name, 1 );
|
|
}
|
|
|
|
b8 adt_set_str( ADT_Node* obj, char const* name, char const* value )
|
|
{
|
|
adt_make_leaf( obj, name, EADT_TYPE_STRING );
|
|
obj->string = value;
|
|
return true;
|
|
}
|
|
|
|
b8 adt_set_flt( ADT_Node* obj, char const* name, f64 value )
|
|
{
|
|
adt_make_leaf( obj, name, EADT_TYPE_REAL );
|
|
obj->real = value;
|
|
return true;
|
|
}
|
|
|
|
b8 adt_set_int( ADT_Node* obj, char const* name, s64 value )
|
|
{
|
|
adt_make_leaf( obj, name, EADT_TYPE_INTEGER );
|
|
obj->integer = value;
|
|
return true;
|
|
}
|
|
|
|
ADT_Node* adt_move_node_at( ADT_Node* node, ADT_Node* new_parent, sw index )
|
|
{
|
|
GEN_ASSERT_NOT_NULL( node );
|
|
GEN_ASSERT_NOT_NULL( new_parent );
|
|
ADT_Node* old_parent = node->parent;
|
|
ADT_Node* new_node = adt_alloc_at( new_parent, index );
|
|
*new_node = *node;
|
|
new_node->parent = new_parent;
|
|
if ( old_parent )
|
|
{
|
|
adt_remove_node( node );
|
|
}
|
|
return new_node;
|
|
}
|
|
|
|
ADT_Node* adt_move_node( ADT_Node* node, ADT_Node* new_parent )
|
|
{
|
|
GEN_ASSERT_NOT_NULL( node );
|
|
GEN_ASSERT_NOT_NULL( new_parent );
|
|
GEN_ASSERT( new_parent->type == EADT_TYPE_ARRAY || new_parent->type == EADT_TYPE_OBJECT );
|
|
return adt_move_node_at( node, new_parent, new_parent->nodes.num() );
|
|
}
|
|
|
|
void adt_swap_nodes( ADT_Node* node, ADT_Node* other_node )
|
|
{
|
|
GEN_ASSERT_NOT_NULL( node );
|
|
GEN_ASSERT_NOT_NULL( other_node );
|
|
ADT_Node* parent = node->parent;
|
|
ADT_Node* other_parent = other_node->parent;
|
|
sw index = ( pointer_diff( parent->nodes, node ) / size_of( ADT_Node ) );
|
|
sw index2 = ( pointer_diff( other_parent->nodes, other_node ) / size_of( ADT_Node ) );
|
|
ADT_Node temp = parent->nodes[ index ];
|
|
temp.parent = other_parent;
|
|
other_parent->nodes[ index2 ].parent = parent;
|
|
parent->nodes[ index ] = other_parent->nodes[ index2 ];
|
|
other_parent->nodes[ index2 ] = temp;
|
|
}
|
|
|
|
void adt_remove_node( ADT_Node* node )
|
|
{
|
|
GEN_ASSERT_NOT_NULL( node );
|
|
GEN_ASSERT_NOT_NULL( node->parent );
|
|
ADT_Node* parent = node->parent;
|
|
sw index = ( pointer_diff( parent->nodes, node ) / size_of( ADT_Node ) );
|
|
parent->nodes.remove_at( index );
|
|
}
|
|
|
|
ADT_Node* adt_append_obj( ADT_Node* parent, char const* name )
|
|
{
|
|
ADT_Node* o = adt_alloc( parent );
|
|
if ( ! o )
|
|
return NULL;
|
|
if ( adt_set_obj( o, name, parent->nodes.get_header()->Allocator ) )
|
|
{
|
|
adt_remove_node( o );
|
|
return NULL;
|
|
}
|
|
return o;
|
|
}
|
|
|
|
ADT_Node* adt_append_arr( ADT_Node* parent, char const* name )
|
|
{
|
|
ADT_Node* o = adt_alloc( parent );
|
|
if ( ! o )
|
|
return NULL;
|
|
if ( adt_set_arr( o, name, parent->nodes.get_header()->Allocator ) )
|
|
{
|
|
adt_remove_node( o );
|
|
return NULL;
|
|
}
|
|
return o;
|
|
}
|
|
|
|
ADT_Node* adt_append_str( ADT_Node* parent, char const* name, char const* value )
|
|
{
|
|
ADT_Node* o = adt_alloc( parent );
|
|
if ( ! o )
|
|
return NULL;
|
|
adt_set_str( o, name, value );
|
|
return o;
|
|
}
|
|
|
|
ADT_Node* adt_append_flt( ADT_Node* parent, char const* name, f64 value )
|
|
{
|
|
ADT_Node* o = adt_alloc( parent );
|
|
if ( ! o )
|
|
return NULL;
|
|
adt_set_flt( o, name, value );
|
|
return o;
|
|
}
|
|
|
|
ADT_Node* adt_append_int( ADT_Node* parent, char const* name, s64 value )
|
|
{
|
|
ADT_Node* o = adt_alloc( parent );
|
|
if ( ! o )
|
|
return NULL;
|
|
adt_set_int( o, name, value );
|
|
return o;
|
|
}
|
|
|
|
/* parser helpers */
|
|
char* adt_parse_number_strict( ADT_Node* node, char* base_str )
|
|
{
|
|
GEN_ASSERT_NOT_NULL( node );
|
|
GEN_ASSERT_NOT_NULL( base_str );
|
|
char *p = base_str, *e = p;
|
|
|
|
while ( *e )
|
|
++e;
|
|
|
|
while ( *p && ( str_find( "eE.+-", *p ) || char_is_hex_digit( *p ) ) )
|
|
{
|
|
++p;
|
|
}
|
|
|
|
if ( p >= e )
|
|
{
|
|
return adt_parse_number( node, base_str );
|
|
}
|
|
|
|
return base_str;
|
|
}
|
|
|
|
char* adt_parse_number( ADT_Node* node, char* base_str )
|
|
{
|
|
GEN_ASSERT_NOT_NULL( node );
|
|
GEN_ASSERT_NOT_NULL( base_str );
|
|
char *p = base_str, *e = p;
|
|
|
|
s32 base = 0;
|
|
s32 base2 = 0;
|
|
u8 base2_offset = 0;
|
|
s8 exp = 0, orig_exp = 0;
|
|
u8 neg_zero = 0;
|
|
u8 lead_digit = 0;
|
|
ADT_Type node_type = EADT_TYPE_UNINITIALISED;
|
|
u8 node_props = 0;
|
|
|
|
/* skip false positives and special cases */
|
|
if ( ! ! str_find( "eE", *p ) || ( ! ! str_find( ".+-", *p ) && ! char_is_hex_digit( *( p + 1 ) ) && *( p + 1 ) != '.' ) )
|
|
{
|
|
return ++base_str;
|
|
}
|
|
|
|
node_type = EADT_TYPE_INTEGER;
|
|
neg_zero = false;
|
|
|
|
sw ib = 0;
|
|
char buf[ 48 ] = { 0 };
|
|
|
|
if ( *e == '+' )
|
|
++e;
|
|
else if ( *e == '-' )
|
|
{
|
|
buf[ ib++ ] = *e++;
|
|
}
|
|
|
|
if ( *e == '.' )
|
|
{
|
|
node_type = EADT_TYPE_REAL;
|
|
node_props = EADT_PROPS_IS_PARSED_REAL;
|
|
lead_digit = false;
|
|
buf[ ib++ ] = '0';
|
|
do
|
|
{
|
|
buf[ ib++ ] = *e;
|
|
} while ( char_is_digit( *++e ) );
|
|
}
|
|
else
|
|
{
|
|
if ( ! str_compare( e, "0x", 2 ) || ! str_compare( e, "0X", 2 ) )
|
|
{
|
|
node_props = EADT_PROPS_IS_HEX;
|
|
}
|
|
|
|
/* bail if ZPL_ADT_PROPS_IS_HEX is unset but we get 'x' on input */
|
|
if ( char_to_lower( *e ) == 'x' && ( node_props != EADT_PROPS_IS_HEX ) )
|
|
{
|
|
return ++base_str;
|
|
}
|
|
|
|
while ( char_is_hex_digit( *e ) || char_to_lower( *e ) == 'x' )
|
|
{
|
|
buf[ ib++ ] = *e++;
|
|
}
|
|
|
|
if ( *e == '.' )
|
|
{
|
|
node_type = EADT_TYPE_REAL;
|
|
lead_digit = true;
|
|
u32 step = 0;
|
|
|
|
do
|
|
{
|
|
buf[ ib++ ] = *e;
|
|
++step;
|
|
} while ( char_is_digit( *++e ) );
|
|
|
|
if ( step < 2 )
|
|
{
|
|
buf[ ib++ ] = '0';
|
|
}
|
|
}
|
|
}
|
|
|
|
/* check if we have a dot here, this is a false positive (IP address, ...) */
|
|
if ( *e == '.' )
|
|
{
|
|
return ++base_str;
|
|
}
|
|
|
|
f32 eb = 10;
|
|
char expbuf[ 6 ] = { 0 };
|
|
sw expi = 0;
|
|
|
|
if ( *e && ! ! str_find( "eE", *e ) )
|
|
{
|
|
++e;
|
|
if ( *e == '+' || *e == '-' || char_is_digit( *e ) )
|
|
{
|
|
if ( *e == '-' )
|
|
{
|
|
eb = 0.1f;
|
|
}
|
|
if ( ! char_is_digit( *e ) )
|
|
{
|
|
++e;
|
|
}
|
|
while ( char_is_digit( *e ) )
|
|
{
|
|
expbuf[ expi++ ] = *e++;
|
|
}
|
|
}
|
|
|
|
orig_exp = exp = ( u8 )str_to_i64( expbuf, NULL, 10 );
|
|
}
|
|
|
|
if ( node_type == EADT_TYPE_INTEGER )
|
|
{
|
|
node->integer = str_to_i64( buf, 0, 0 );
|
|
#ifndef GEN_PARSER_DISABLE_ANALYSIS
|
|
/* special case: negative zero */
|
|
if ( node->integer == 0 && buf[ 0 ] == '-' )
|
|
{
|
|
neg_zero = true;
|
|
}
|
|
#endif
|
|
while ( orig_exp-- > 0 )
|
|
{
|
|
node->integer *= ( s64 )eb;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
node->real = str_to_f64( buf, 0 );
|
|
|
|
#ifndef GEN_PARSER_DISABLE_ANALYSIS
|
|
char *q = buf, *base_string = q, *base_string2 = q;
|
|
base_string = zpl_cast( char* ) str_skip( base_string, '.' );
|
|
*base_string = '\0';
|
|
base_string2 = base_string + 1;
|
|
char* base_string_off = base_string2;
|
|
while ( *base_string_off++ == '0' )
|
|
base2_offset++;
|
|
|
|
base = ( s32 )str_to_i64( q, 0, 0 );
|
|
base2 = ( s32 )str_to_i64( base_string2, 0, 0 );
|
|
if ( exp )
|
|
{
|
|
exp = exp * ( ! ( eb == 10.0f ) ? -1 : 1 );
|
|
node_props = EADT_PROPS_IS_EXP;
|
|
}
|
|
|
|
/* special case: negative zero */
|
|
if ( base == 0 && buf[ 0 ] == '-' )
|
|
{
|
|
neg_zero = true;
|
|
}
|
|
#endif
|
|
while ( orig_exp-- > 0 )
|
|
{
|
|
node->real *= eb;
|
|
}
|
|
}
|
|
|
|
node->type = node_type;
|
|
node->props = node_props;
|
|
|
|
#ifndef GEN_PARSER_DISABLE_ANALYSIS
|
|
node->base = base;
|
|
node->base2 = base2;
|
|
node->base2_offset = base2_offset;
|
|
node->exp = exp;
|
|
node->neg_zero = neg_zero;
|
|
node->lead_digit = lead_digit;
|
|
#else
|
|
unused( base );
|
|
unused( base2 );
|
|
unused( base2_offset );
|
|
unused( exp );
|
|
unused( neg_zero );
|
|
unused( lead_digit );
|
|
#endif
|
|
return e;
|
|
}
|
|
|
|
ADT_Error adt_print_number( FileInfo* file, ADT_Node* node )
|
|
{
|
|
GEN_ASSERT_NOT_NULL( file );
|
|
GEN_ASSERT_NOT_NULL( node );
|
|
if ( node->type != EADT_TYPE_INTEGER && node->type != EADT_TYPE_REAL )
|
|
{
|
|
return EADT_ERROR_INVALID_TYPE;
|
|
}
|
|
|
|
#ifndef GEN_PARSER_DISABLE_ANALYSIS
|
|
if ( node->neg_zero )
|
|
{
|
|
_adt_fprintf( file, "-" );
|
|
}
|
|
#endif
|
|
|
|
switch ( node->type )
|
|
{
|
|
case EADT_TYPE_INTEGER :
|
|
{
|
|
if ( node->props == EADT_PROPS_IS_HEX )
|
|
{
|
|
_adt_fprintf( file, "0x%llx", ( long long )node->integer );
|
|
}
|
|
else
|
|
{
|
|
_adt_fprintf( file, "%lld", ( long long )node->integer );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EADT_TYPE_REAL :
|
|
{
|
|
if ( node->props == EADT_PROPS_NAN )
|
|
{
|
|
_adt_fprintf( file, "NaN" );
|
|
}
|
|
else if ( node->props == EADT_PROPS_NAN_NEG )
|
|
{
|
|
_adt_fprintf( file, "-NaN" );
|
|
}
|
|
else if ( node->props == EADT_PROPS_INFINITY )
|
|
{
|
|
_adt_fprintf( file, "Infinity" );
|
|
}
|
|
else if ( node->props == EADT_PROPS_INFINITY_NEG )
|
|
{
|
|
_adt_fprintf( file, "-Infinity" );
|
|
}
|
|
else if ( node->props == EADT_PROPS_TRUE )
|
|
{
|
|
_adt_fprintf( file, "true" );
|
|
}
|
|
else if ( node->props == EADT_PROPS_FALSE )
|
|
{
|
|
_adt_fprintf( file, "false" );
|
|
}
|
|
else if ( node->props == EADT_PROPS_NULL )
|
|
{
|
|
_adt_fprintf( file, "null" );
|
|
#ifndef GEN_PARSER_DISABLE_ANALYSIS
|
|
}
|
|
else if ( node->props == EADT_PROPS_IS_EXP )
|
|
{
|
|
_adt_fprintf( file, "%lld.%0*d%llde%lld", ( long long )node->base, node->base2_offset, 0, ( long long )node->base2, ( long long )node->exp );
|
|
}
|
|
else if ( node->props == EADT_PROPS_IS_PARSED_REAL )
|
|
{
|
|
if ( ! node->lead_digit )
|
|
_adt_fprintf( file, ".%0*d%lld", node->base2_offset, 0, ( long long )node->base2 );
|
|
else
|
|
_adt_fprintf( file, "%lld.%0*d%lld", ( long long int )node->base2_offset, 0, ( int )node->base, ( long long )node->base2 );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
_adt_fprintf( file, "%f", node->real );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return EADT_ERROR_NONE;
|
|
}
|
|
|
|
ADT_Error adt_print_string( FileInfo* file, ADT_Node* node, char const* escaped_chars, char const* escape_symbol )
|
|
{
|
|
GEN_ASSERT_NOT_NULL( file );
|
|
GEN_ASSERT_NOT_NULL( node );
|
|
GEN_ASSERT_NOT_NULL( escaped_chars );
|
|
if ( node->type != EADT_TYPE_STRING && node->type != EADT_TYPE_MULTISTRING )
|
|
{
|
|
return EADT_ERROR_INVALID_TYPE;
|
|
}
|
|
|
|
/* escape string */
|
|
char const *p = node->string, *b = p;
|
|
|
|
if ( ! p )
|
|
return EADT_ERROR_NONE;
|
|
|
|
do
|
|
{
|
|
p = str_skip_any( p, escaped_chars );
|
|
_adt_fprintf( file, "%.*s", pointer_diff( b, p ), b );
|
|
if ( *p && ! ! str_find( escaped_chars, *p ) )
|
|
{
|
|
_adt_fprintf( file, "%s%c", escape_symbol, *p );
|
|
p++;
|
|
}
|
|
b = p;
|
|
} while ( *p );
|
|
|
|
return EADT_ERROR_NONE;
|
|
}
|
|
|
|
ADT_Error adt_str_to_number( ADT_Node* node )
|
|
{
|
|
GEN_ASSERT( node );
|
|
|
|
if ( node->type == EADT_TYPE_REAL || node->type == EADT_TYPE_INTEGER )
|
|
return EADT_ERROR_ALREADY_CONVERTED; /* this is already converted/parsed */
|
|
if ( node->type != EADT_TYPE_STRING && node->type != EADT_TYPE_MULTISTRING )
|
|
{
|
|
return EADT_ERROR_INVALID_TYPE;
|
|
}
|
|
|
|
adt_parse_number( node, ( char* )node->string );
|
|
|
|
return EADT_ERROR_NONE;
|
|
}
|
|
|
|
ADT_Error adt_str_to_number_strict( ADT_Node* node )
|
|
{
|
|
GEN_ASSERT( node );
|
|
|
|
if ( node->type == EADT_TYPE_REAL || node->type == EADT_TYPE_INTEGER )
|
|
return EADT_ERROR_ALREADY_CONVERTED; /* this is already converted/parsed */
|
|
if ( node->type != EADT_TYPE_STRING && node->type != EADT_TYPE_MULTISTRING )
|
|
{
|
|
return EADT_ERROR_INVALID_TYPE;
|
|
}
|
|
|
|
adt_parse_number_strict( node, ( char* )node->string );
|
|
|
|
return EADT_ERROR_NONE;
|
|
}
|
|
|
|
#undef _adt_fprintf
|
|
|
|
#pragma endregion ADT
|
|
|
|
#pragma region CSV
|
|
|
|
#ifdef GEN_CSV_DEBUG
|
|
# define GEN_CSV_ASSERT( msg ) GEN_PANIC( msg )
|
|
#else
|
|
# define GEN_CSV_ASSERT( msg )
|
|
#endif
|
|
|
|
u8 csv_parse_delimiter( CSV_Object* root, char* text, AllocatorInfo allocator, b32 has_header, char delim )
|
|
{
|
|
CSV_Error error = ECSV_Error__NONE;
|
|
GEN_ASSERT_NOT_NULL( root );
|
|
GEN_ASSERT_NOT_NULL( text );
|
|
zero_item( root );
|
|
|
|
adt_make_branch( root, allocator, NULL, has_header ? false : true );
|
|
|
|
char* currentChar = text;
|
|
char* beginChar;
|
|
char* endChar;
|
|
|
|
sw columnIndex = 0;
|
|
sw totalColumnIndex = 0;
|
|
|
|
do
|
|
{
|
|
char delimiter = 0;
|
|
currentChar = zpl_cast( char* ) str_trim( currentChar, false );
|
|
|
|
if ( *currentChar == 0 )
|
|
break;
|
|
|
|
ADT_Node rowItem = { 0 };
|
|
rowItem.type = EADT_TYPE_STRING;
|
|
|
|
#ifndef GEN_PARSER_DISABLE_ANALYSIS
|
|
rowItem.name_style = EADT_NAME_STYLE_NO_QUOTES;
|
|
#endif
|
|
|
|
/* handle string literals */
|
|
if ( *currentChar == '"' )
|
|
{
|
|
currentChar += 1;
|
|
beginChar = currentChar;
|
|
endChar = currentChar;
|
|
rowItem.string = beginChar;
|
|
#ifndef GEN_PARSER_DISABLE_ANALYSIS
|
|
rowItem.name_style = EADT_NAME_STYLE_DOUBLE_QUOTE;
|
|
#endif
|
|
do
|
|
{
|
|
endChar = zpl_cast( char* ) str_skip( endChar, '"' );
|
|
|
|
if ( *endChar && *( endChar + 1 ) == '"' )
|
|
{
|
|
endChar += 2;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
while ( *endChar );
|
|
|
|
if ( *endChar == 0 )
|
|
{
|
|
GEN_CSV_ASSERT( "unmatched quoted string" );
|
|
error = ECSV_Error__UNEXPECTED_END_OF_INPUT;
|
|
return error;
|
|
}
|
|
|
|
*endChar = 0;
|
|
currentChar = zpl_cast( char* ) str_trim( endChar + 1, true );
|
|
delimiter = * currentChar;
|
|
|
|
/* unescape escaped quotes (so that unescaped text escapes :) */
|
|
{
|
|
char* escapedChar = beginChar;
|
|
do
|
|
{
|
|
if ( *escapedChar == '"' && *( escapedChar + 1 ) == '"' )
|
|
{
|
|
mem_move( escapedChar, escapedChar + 1, str_len( escapedChar ) );
|
|
}
|
|
escapedChar++;
|
|
}
|
|
while ( *escapedChar );
|
|
}
|
|
}
|
|
else if ( *currentChar == delim )
|
|
{
|
|
delimiter = * currentChar;
|
|
rowItem.string = "";
|
|
}
|
|
else if ( *currentChar )
|
|
{
|
|
/* regular data */
|
|
beginChar = currentChar;
|
|
endChar = currentChar;
|
|
rowItem.string = beginChar;
|
|
|
|
do
|
|
{
|
|
endChar++;
|
|
}
|
|
while ( * endChar && * endChar != delim && * endChar != '\n' );
|
|
|
|
if ( * endChar )
|
|
{
|
|
currentChar = zpl_cast( char* ) str_trim( endChar, true );
|
|
|
|
while ( char_is_space( *( endChar - 1 ) ) )
|
|
{
|
|
endChar--;
|
|
}
|
|
|
|
delimiter = * currentChar;
|
|
* endChar = 0;
|
|
}
|
|
else
|
|
{
|
|
delimiter = 0;
|
|
currentChar = endChar;
|
|
}
|
|
|
|
/* check if number and process if so */
|
|
b32 skip_number = false;
|
|
char* num_p = beginChar;
|
|
|
|
// We only consider hexadecimal values if they start with 0x
|
|
if ( str_len(num_p) > 2 && num_p[0] == '0' && (num_p[1] == 'x' || num_p[1] == 'X') )
|
|
{
|
|
num_p += 2; // skip '0x' prefix
|
|
do
|
|
{
|
|
if (!char_is_hex_digit(*num_p))
|
|
{
|
|
skip_number = true;
|
|
break;
|
|
}
|
|
} while (*num_p++);
|
|
}
|
|
else
|
|
{
|
|
skip_number = true;
|
|
}
|
|
|
|
if (!skip_number)
|
|
{
|
|
adt_str_to_number(&rowItem);
|
|
}
|
|
}
|
|
|
|
if ( columnIndex >= root->nodes.num() )
|
|
{
|
|
adt_append_arr( root, NULL );
|
|
}
|
|
|
|
root->nodes[ columnIndex ].nodes.append( rowItem );
|
|
|
|
if ( delimiter == delim )
|
|
{
|
|
columnIndex++;
|
|
currentChar++;
|
|
}
|
|
else if ( delimiter == '\n' || delimiter == 0 )
|
|
{
|
|
/* check if number of rows is not mismatched */
|
|
if ( totalColumnIndex < columnIndex )
|
|
totalColumnIndex = columnIndex;
|
|
|
|
else if ( totalColumnIndex != columnIndex )
|
|
{
|
|
GEN_CSV_ASSERT( "mismatched rows" );
|
|
error = ECSV_Error__MISMATCHED_ROWS;
|
|
return error;
|
|
}
|
|
|
|
columnIndex = 0;
|
|
|
|
if ( delimiter != 0 )
|
|
currentChar++;
|
|
}
|
|
}
|
|
while ( *currentChar );
|
|
|
|
if ( root->nodes.num() == 0 )
|
|
{
|
|
GEN_CSV_ASSERT( "unexpected end of input. stream is empty." );
|
|
error = ECSV_Error__UNEXPECTED_END_OF_INPUT;
|
|
return error;
|
|
}
|
|
|
|
/* consider first row as a header. */
|
|
if ( has_header )
|
|
{
|
|
for ( sw i = 0; i < root->nodes.num(); i++ )
|
|
{
|
|
CSV_Object* col = root->nodes + i;
|
|
CSV_Object* hdr = col->nodes;
|
|
col->name = hdr->string;
|
|
col->nodes.remove_at( 0 );
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
void csv_free( CSV_Object* obj )
|
|
{
|
|
adt_destroy_branch( obj );
|
|
}
|
|
|
|
void _csv_write_record( FileInfo* file, CSV_Object* node )
|
|
{
|
|
switch ( node->type )
|
|
{
|
|
case EADT_TYPE_STRING :
|
|
{
|
|
#ifndef GEN_PARSER_DISABLE_ANALYSIS
|
|
switch ( node->name_style )
|
|
{
|
|
case EADT_NAME_STYLE_DOUBLE_QUOTE :
|
|
{
|
|
str_fmt_file( file, "\"" );
|
|
adt_print_string( file, node, "\"", "\"" );
|
|
str_fmt_file( file, "\"" );
|
|
}
|
|
break;
|
|
|
|
case EADT_NAME_STYLE_NO_QUOTES :
|
|
{
|
|
#endif
|
|
str_fmt_file( file, "%s", node->string );
|
|
#ifndef GEN_PARSER_DISABLE_ANALYSIS
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case EADT_TYPE_REAL :
|
|
case EADT_TYPE_INTEGER :
|
|
{
|
|
adt_print_number( file, node );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void _csv_write_header( FileInfo* file, CSV_Object* header )
|
|
{
|
|
CSV_Object temp = *header;
|
|
temp.string = temp.name;
|
|
temp.type = EADT_TYPE_STRING;
|
|
_csv_write_record( file, &temp );
|
|
}
|
|
|
|
void csv_write_delimiter( FileInfo* file, CSV_Object* obj, char delimiter )
|
|
{
|
|
GEN_ASSERT_NOT_NULL( file );
|
|
GEN_ASSERT_NOT_NULL( obj );
|
|
GEN_ASSERT( obj->nodes );
|
|
sw cols = obj->nodes.num();
|
|
if ( cols == 0 )
|
|
return;
|
|
|
|
sw rows = obj->nodes[ 0 ].nodes.num();
|
|
if ( rows == 0 )
|
|
return;
|
|
|
|
b32 has_headers = obj->nodes[ 0 ].name != NULL;
|
|
|
|
if ( has_headers )
|
|
{
|
|
for ( sw i = 0; i < cols; i++ )
|
|
{
|
|
_csv_write_header( file, &obj->nodes[ i ] );
|
|
if ( i + 1 != cols )
|
|
{
|
|
str_fmt_file( file, "%c", delimiter );
|
|
}
|
|
}
|
|
str_fmt_file( file, "\n" );
|
|
}
|
|
|
|
for ( sw r = 0; r < rows; r++ )
|
|
{
|
|
for ( sw i = 0; i < cols; i++ )
|
|
{
|
|
_csv_write_record( file, &obj->nodes[ i ].nodes[ r ] );
|
|
if ( i + 1 != cols )
|
|
{
|
|
str_fmt_file( file, "%c", delimiter );
|
|
}
|
|
}
|
|
str_fmt_file( file, "\n" );
|
|
}
|
|
}
|
|
|
|
String csv_write_string_delimiter( AllocatorInfo a, CSV_Object* obj, char delimiter )
|
|
{
|
|
FileInfo tmp;
|
|
file_stream_new( &tmp, a );
|
|
csv_write_delimiter( &tmp, obj, delimiter );
|
|
sw fsize;
|
|
u8* buf = file_stream_buf( &tmp, &fsize );
|
|
String output = String::make_length( a, ( char* )buf, fsize );
|
|
file_close( &tmp );
|
|
return output;
|
|
}
|
|
|
|
#pragma endregion CSV
|
|
|