diff --git a/Readme.md b/Readme.md index 390d27d..bc5cf72 100644 --- a/Readme.md +++ b/Readme.md @@ -699,7 +699,8 @@ This project came about for a few reasons: * Not a fan of pure C, maybe I'll succumb to the drawbacks. * All alternatives to C/C++ are too opionionated instead of providing a lax frontend, or a proper compiler backend with a frontend api to quickly roll your own forntend. * One of the core issues I've always had with programming is there has always been a need for metaprogramming, but every single tool has horrible error deduction for the user (backend blackbox from codebase size or closed-source, error log nightmare). -* I spend an obnoxious amount of time trying to express code that cannot be expressed well in templates or macros and still have an adequate editor experience, even with full blown IDEs. -* I wanted to be able to easily refactor interated with projects with some form of curation, and still have the ability to not maintain a separate fork (IF the scanner gets implemetned, that is possible). +* I spend an obnoxious amount of time trying to express code that cannot be expressed well in templates or macros. The experience is inadequate; even with full blown IDEs. +* I wanted to be able to easily refactor libraries interated in projects with some form of curation. While still having the ability to not maintain a separate fork (IF the scanner gets implemetned, that is possible). * I did not use Metadesk as it was an esoteric library for me to use as a dependency when I didn't fully grasp the vision for how this library would end up. (Not much practice doing metaprogramming or code gen/transform development) * I have no issue rewritting the library to use it as a backend if its worth while but its most likely better to just make an extension for it. +* This project showed me rewwriting code isn't as expensive as people make it out to be (vs using a bloated toolchain) diff --git a/project/gen_dep.cpp b/project/gen_dep.cpp index 5724284..26f933e 100644 --- a/project/gen_dep.cpp +++ b/project/gen_dep.cpp @@ -223,6 +223,93 @@ namespace gen str_reverse( string ); } + + f64 str_to_f64( const char* str, char** end_ptr ) + { + f64 result, value, sign, scale; + s32 frac; + + while ( char_is_space( *str ) ) + { + str++; + } + + sign = 1.0; + if ( *str == '-' ) + { + sign = -1.0; + str++; + } + else if ( *str == '+' ) + { + str++; + } + + for ( value = 0.0; char_is_digit( *str ); str++ ) + { + value = value * 10.0 + ( *str - '0' ); + } + + if ( *str == '.' ) + { + f64 pow10 = 10.0; + str++; + while ( char_is_digit( *str ) ) + { + value += ( *str - '0' ) / pow10; + pow10 *= 10.0; + str++; + } + } + + frac = 0; + scale = 1.0; + if ( ( *str == 'e' ) || ( *str == 'E' ) ) + { + u32 exp; + + str++; + if ( *str == '-' ) + { + frac = 1; + str++; + } + else if ( *str == '+' ) + { + str++; + } + + for ( exp = 0; char_is_digit( *str ); str++ ) + { + exp = exp * 10 + ( *str - '0' ); + } + if ( exp > 308 ) + exp = 308; + + while ( exp >= 50 ) + { + scale *= 1e50; + exp -= 50; + } + while ( exp >= 8 ) + { + scale *= 1e8; + exp -= 8; + } + while ( exp > 0 ) + { + scale *= 10.0; + exp -= 1; + } + } + + result = sign * ( frac ? ( value / scale ) : ( value * scale ) ); + + if ( end_ptr ) + *end_ptr = zpl_cast( char* ) str; + + return result; + } #pragma endregion String Ops #pragma region Printing @@ -757,6 +844,16 @@ namespace gen return res ? len : -1; } + sw str_fmt_file( struct FileInfo* f, char const* fmt, ... ) + { + sw res; + va_list va; + va_start( va, fmt ); + res = str_fmt_file_va( f, fmt, va ); + va_end( va ); + return res; + } + sw str_fmt_out_va( char const* fmt, va_list va ) { return str_fmt_file_va( file_get_standard( EFileStandard_OUTPUT ), fmt, va ); @@ -1167,7 +1264,787 @@ namespace gen #pragma endregion Memory #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::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; + } + 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 @@ -1546,8 +2423,8 @@ namespace gen if ( ! _std_file_set ) { # define GEN__SET_STD_FILE( type, v ) \ - _std_files[ type ].FD.p = v; \ - _std_files[ type ].Ops = default_file_operations + _std_files[ type ].fd.p = v; \ + _std_files[ type ].ops = default_file_operations GEN__SET_STD_FILE( EFileStandard_INPUT, GetStdHandle( STD_INPUT_HANDLE ) ); GEN__SET_STD_FILE( EFileStandard_OUTPUT, GetStdHandle( STD_OUTPUT_HANDLE ) ); GEN__SET_STD_FILE( EFileStandard_ERROR, GetStdHandle( STD_ERROR_HANDLE ) ); @@ -1582,26 +2459,26 @@ namespace gen if ( ! f ) return EFileError_INVALID; - if ( f->Filename ) - free( heap(), zpl_cast( char* ) f->Filename ); + if ( f->filename ) + free( heap(), zpl_cast( char* ) f->filename ); #if defined( GEN_SYSTEM_WINDOWS ) - if ( f->FD.p == INVALID_HANDLE_VALUE ) + if ( f->fd.p == INVALID_HANDLE_VALUE ) return EFileError_INVALID; #else if ( f->fd.i < 0 ) return EFileError_INVALID; #endif - if ( f->IsTemp ) + if ( f->is_temp ) { - f->Ops.close( f->FD ); + f->ops.close( f->fd ); return EFileError_NONE; } - if ( ! f->Ops.read_at ) - f->Ops = default_file_operations; - f->Ops.close( f->FD ); + if ( ! f->ops.read_at ) + f->ops = default_file_operations; + f->ops.close( f->fd ); #if 0 if ( f->Dir ) @@ -1620,12 +2497,12 @@ namespace gen FileError err = EFileError_NONE; sw len = str_len( filename ); - f->Ops = ops; - f->FD = fd; - f->Dir = nullptr; - f->LastWriteTime = 0; - f->Filename = alloc_array( heap(), char, len + 1 ); - mem_copy( zpl_cast( char* ) f->Filename, zpl_cast( char* ) filename, len + 1 ); + f->ops = ops; + f->fd = fd; + f->dir = nullptr; + f->last_write_time = 0; + f->filename = alloc_array( heap(), char, len + 1 ); + mem_copy( zpl_cast( char* ) f->filename, zpl_cast( char* ) filename, len + 1 ); return err; } @@ -1646,13 +2523,13 @@ namespace gen FileError err; #if defined( GEN_SYSTEM_WINDOWS ) || defined( GEN_SYSTEM_CYGWIN ) - err = _win32_file_open( &f->FD, &f->Ops, mode, filename ); + err = _win32_file_open( &f->fd, &f->ops, mode, filename ); #else err = _posix_file_open( &f->fd, &f->ops, mode, filename ); #endif if ( err == EFileError_NONE ) - return file_new( f, f->FD, f->Ops, filename ); + return file_new( f, f->fd, f->ops, filename ); return err; } @@ -1669,6 +2546,196 @@ namespace gen return size; } + + struct _memory_fd + { + u8 magic; + u8* buf; //< zpl_array OR plain buffer if we can't write + sw cursor; + AllocatorInfo allocator; + + FileStreamFlags flags; + sw cap; + }; + + #define GEN__FILE_STREAM_FD_MAGIC 37 + + GEN_DEF_INLINE FileDescriptor _file_stream_fd_make( _memory_fd* d ); + GEN_DEF_INLINE _memory_fd* _file_stream_from_fd( FileDescriptor fd ); + + GEN_IMPL_INLINE FileDescriptor _file_stream_fd_make( _memory_fd* d ) + { + FileDescriptor fd = { 0 }; + fd.p = ( void* )d; + return fd; + } + + GEN_IMPL_INLINE _memory_fd* _file_stream_from_fd( FileDescriptor fd ) + { + _memory_fd* d = ( _memory_fd* )fd.p; + GEN_ASSERT( d->magic == GEN__FILE_STREAM_FD_MAGIC ); + return d; + } + + b8 file_stream_new( FileInfo* file, AllocatorInfo allocator ) + { + GEN_ASSERT_NOT_NULL( file ); + + _memory_fd* d = ( _memory_fd* )alloc( allocator, size_of( _memory_fd ) ); + + if ( ! d ) + return false; + + zero_item( file ); + d->magic = GEN__FILE_STREAM_FD_MAGIC; + d->allocator = allocator; + d->flags = EFileStream_CLONE_WRITABLE; + d->cap = 0; + d->buf = Array::init( allocator ); + + if ( ! d->buf ) + return false; + + file->ops = memory_file_operations; + file->fd = _file_stream_fd_make( d ); + file->dir = NULL; + file->last_write_time = 0; + file->filename = NULL; + file->is_temp = true; + return true; + } + + b8 file_stream_open( FileInfo* file, AllocatorInfo allocator, u8* buffer, sw size, FileStreamFlags flags ) + { + GEN_ASSERT_NOT_NULL( file ); + _memory_fd* d = ( _memory_fd* )alloc( allocator, size_of( _memory_fd ) ); + if ( ! d ) + return false; + zero_item( file ); + d->magic = GEN__FILE_STREAM_FD_MAGIC; + d->allocator = allocator; + d->flags = flags; + if ( d->flags & EFileStream_CLONE_WRITABLE ) + { + Array arr = Array::init_reserve( allocator, size ); + d->buf = arr; + + if ( ! d->buf ) + return false; + + mem_copy( d->buf, buffer, size ); + d->cap = size; + + arr.get_header()->Num = size; + } + else + { + d->buf = buffer; + d->cap = size; + } + file->ops = memory_file_operations; + file->fd = _file_stream_fd_make( d ); + file->dir = NULL; + file->last_write_time = 0; + file->filename = NULL; + file->is_temp = true; + return true; + } + + u8* file_stream_buf( FileInfo* file, sw* size ) + { + GEN_ASSERT_NOT_NULL( file ); + _memory_fd* d = _file_stream_from_fd( file->fd ); + if ( size ) + *size = d->cap; + return d->buf; + } + + internal GEN_FILE_SEEK_PROC( _memory_file_seek ) + { + _memory_fd* d = _file_stream_from_fd( fd ); + sw buflen = d->cap; + + if ( whence == ESeekWhence_BEGIN ) + d->cursor = 0; + else if ( whence == ESeekWhence_END ) + d->cursor = buflen; + + d->cursor = max( 0, clamp( d->cursor + offset, 0, buflen ) ); + if ( new_offset ) + *new_offset = d->cursor; + return true; + } + + internal GEN_FILE_READ_AT_PROC( _memory_file_read ) + { + // unused( stop_at_newline ); + _memory_fd* d = _file_stream_from_fd( fd ); + mem_copy( buffer, d->buf + offset, size ); + if ( bytes_read ) + *bytes_read = size; + return true; + } + + internal GEN_FILE_WRITE_AT_PROC( _memory_file_write ) + { + _memory_fd* d = _file_stream_from_fd( fd ); + + if ( ! ( d->flags & ( EFileStream_CLONE_WRITABLE | EFileStream_WRITABLE ) ) ) + return false; + + sw buflen = d->cap; + sw extralen = max( 0, size - ( buflen - offset ) ); + sw rwlen = size - extralen; + sw new_cap = buflen + extralen; + + if ( d->flags & EFileStream_CLONE_WRITABLE ) + { + Array arr = { d->buf }; + + if ( arr.get_header()->Capacity < new_cap ) + { + if ( ! arr.grow( ( s64 )( new_cap ) ) ) + return false; + d->buf = arr; + } + } + + mem_copy( d->buf + offset, buffer, rwlen ); + + if ( ( d->flags & EFileStream_CLONE_WRITABLE ) && extralen > 0 ) + { + Array arr = { d->buf }; + + mem_copy( d->buf + offset + rwlen, pointer_add_const( buffer, rwlen ), extralen ); + d->cap = new_cap; + arr.get_header()->Capacity = new_cap; + } + else + { + extralen = 0; + } + + if ( bytes_written ) + *bytes_written = ( rwlen + extralen ); + return true; + } + + internal GEN_FILE_CLOSE_PROC( _memory_file_close ) + { + _memory_fd* d = _file_stream_from_fd( fd ); + AllocatorInfo allocator = d->allocator; + + if ( d->flags & EFileStream_CLONE_WRITABLE ) + { + Array arr = { d->buf }; + arr.free(); + } + + free( allocator, d ); + } + + FileOperations const memory_file_operations = { _memory_file_read, _memory_file_write, _memory_file_seek, _memory_file_close }; #pragma endregion File Handling #pragma region String diff --git a/project/gen_dep.hpp b/project/gen_dep.hpp index 72978fc..497c791 100644 --- a/project/gen_dep.hpp +++ b/project/gen_dep.hpp @@ -223,17 +223,12 @@ namespace gen } \ while(0); + #define clamp( x, lower, upper ) min( max( ( x ), ( lower ) ), ( upper ) ) #define count_of( x ) ( ( size_of( x ) / size_of( 0 [ x ] ) ) / ( ( sw )( ! ( size_of( x ) % size_of( 0 [ x ] ) ) ) ) ) #define is_between( x, lower, upper ) ( ( ( lower ) <= ( x ) ) && ( ( x ) <= ( upper ) ) ) + #define max( a, b ) ( ( a ) > ( b ) ? ( a ) : ( b ) ) #define min( a, b ) ( ( a ) < ( b ) ? ( a ) : ( b ) ) #define size_of( x ) ( sw )( sizeof( x ) ) - // #define swap( Type, a, b ) \ - // do \ - // { \ - // Type tmp = ( a ); \ - // ( a ) = ( b ); \ - // ( b ) = tmp; \ - // } while ( 0 ) template< class Type > void swap( Type a, Type b ) @@ -420,6 +415,12 @@ namespace gen //! Moves pointer forward by bytes. GEN_DEF_INLINE void* pointer_add( void* ptr, sw bytes ); + //! Moves pointer forward by bytes. + GEN_DEF_INLINE void const* pointer_add_const( void const* ptr, sw bytes ); + + //! Calculates difference between two addresses. + GEN_DEF_INLINE sw pointer_diff( void const* begin, void const* end ); + //! Copy non-overlapping memory from source to destination. void* mem_copy( void* dest, void const* source, sw size ); @@ -550,6 +551,16 @@ namespace gen return zpl_cast( void* )( zpl_cast( u8* ) ptr + bytes ); } + GEN_IMPL_INLINE void const* pointer_add_const( void const* ptr, sw bytes ) + { + return zpl_cast( void const* )( zpl_cast( u8 const* ) ptr + bytes ); + } + + GEN_IMPL_INLINE sw pointer_diff( void const* begin, void const* end ) + { + return zpl_cast( sw )( zpl_cast( u8 const* ) end - zpl_cast( u8 const* ) begin ); + } + GEN_IMPL_INLINE void* mem_move( void* dest, void const* source, sw n ) { if ( dest == NULL ) @@ -869,6 +880,7 @@ namespace gen #pragma region String Ops GEN_DEF_INLINE const char* char_first_occurence( const char* str, char c ); + constexpr auto str_find = &char_first_occurence; GEN_DEF_INLINE b32 char_is_alpha( char c ); GEN_DEF_INLINE b32 char_is_alphanumeric( char c ); @@ -881,21 +893,24 @@ namespace gen GEN_DEF_INLINE s32 digit_to_int( char c ); GEN_DEF_INLINE s32 hex_digit_to_int( char c ); - GEN_DEF_INLINE s32 str_compare( const char* s1, const char* s2 ); - GEN_DEF_INLINE s32 str_compare( const char* s1, const char* s2, sw len ); - GEN_DEF_INLINE char* str_copy( char* dest, const char* source, sw len ); - GEN_DEF_INLINE sw str_copy_nulpad( char* dest, const char* source, sw len ); - GEN_DEF_INLINE sw str_len( const char* str ); - GEN_DEF_INLINE sw str_len( const char* str, sw max_len ); - GEN_DEF_INLINE char* str_reverse( char* str ); // NOTE: ASCII only + GEN_DEF_INLINE s32 str_compare( const char* s1, const char* s2 ); + GEN_DEF_INLINE s32 str_compare( const char* s1, const char* s2, sw len ); + GEN_DEF_INLINE char* str_copy( char* dest, const char* source, sw len ); + GEN_DEF_INLINE sw str_copy_nulpad( char* dest, const char* source, sw len ); + GEN_DEF_INLINE sw str_len( const char* str ); + GEN_DEF_INLINE sw str_len( const char* str, sw max_len ); + GEN_DEF_INLINE char* str_reverse( char* str ); // NOTE: ASCII only + GEN_DEF_INLINE char const* str_skip( char const* str, char c ); + GEN_DEF_INLINE char const* str_skip_any( char const* str, char const* char_list ); // NOTE: ASCII only GEN_DEF_INLINE void str_to_lower( char* str ); GEN_DEF_INLINE void str_to_upper( char* str ); - s64 str_to_i64( const char* str, char** end_ptr, s32 base ); // TODO : Support more than just decimal and hexadecimal + s64 str_to_i64( const char* str, char** end_ptr, s32 base ); void i64_to_str( s64 value, char* string, s32 base ); void u64_to_str( u64 value, char* string, s32 base ); + f64 str_to_f64( const char* str, char** end_ptr ); GEN_IMPL_INLINE const char* char_first_occurence( const char* s, char c ) { @@ -1070,6 +1085,27 @@ namespace gen return str; } + GEN_IMPL_INLINE char const* str_skip( char const* str, char c ) + { + while ( *str && *str != c ) + { + ++str; + } + return str; + } + + GEN_IMPL_INLINE char const* str_skip_any( char const* str, char const* char_list ) + { + char const* closest_ptr = zpl_cast( char const* ) pointer_add( ( void* )str, str_len( str ) ); + sw char_list_count = str_len( char_list ); + for ( sw i = 0; i < char_list_count; i++ ) + { + char const* p = str_skip( str, char_list[ i ] ); + closest_ptr = min( closest_ptr, p ); + } + return closest_ptr; + } + GEN_IMPL_INLINE void str_to_lower( char* str ) { if ( ! str ) @@ -1101,13 +1137,14 @@ namespace gen #endif // NOTE: A locally persisting buffer is used internally - char* str_fmt_buf( char const* fmt, ... ); - char* str_fmt_buf_va( char const* fmt, va_list va ); - sw str_fmt_va( char* str, sw n, char const* fmt, va_list va ); - sw str_fmt_out_va( char const* fmt, va_list va ); - sw str_fmt_out_err( char const* fmt, ... ); + char* str_fmt_buf ( char const* fmt, ... ); + char* str_fmt_buf_va ( char const* fmt, va_list va ); + sw str_fmt_va ( char* str, sw n, char const* fmt, va_list va ); + sw str_fmt_file ( FileInfo* f, char const* fmt, ... ); + sw str_fmt_file_va ( FileInfo* f, char const* fmt, va_list va ); + sw str_fmt_out_va ( char const* fmt, va_list va ); + sw str_fmt_out_err ( char const* fmt, ... ); sw str_fmt_out_err_va( char const* fmt, va_list va ); - sw str_fmt_file_va( FileInfo* f, char const* fmt, va_list va ); constexpr char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; @@ -1207,6 +1244,77 @@ namespace gen return true; } + bool append( Type* items, uw item_num ) + { + Header* header = get_header(); + + if ( header->Num + item_num > header->Capacity ) + { + if ( ! grow( header->Capacity + item_num )) + return false; + + header = get_header(); + } + + mem_copy( Data + header->Num, items, item_num * sizeof(Type) ); + header->Num += item_num; + + return true; + } + + bool append_at( Type item, sw idx ) + { + Header* header = get_header(); + + if ( idx >= header->Num ) + idx = header->Num - 1; + + if ( idx < 0 ) + idx = 0; + + if ( header->Capacity < header->Num + 1 ) + { + if ( ! grow( header->Capacity + 1 )) + return false; + + header = get_header(); + } + + Type* target = Data + idx; + + mem_move( target + 1, target, (header->Num - idx) * sizeof(Type) ); + + return true; + } + + bool append_at( Type* items, uw item_num, sw idx ) + { + Header* header = get_header(); + + if ( idx >= header->Num ) + { + return append( items, item_num ); + } + + if ( item_num > header->Capacity ) + { + if ( ! grow( header->Capacity + item_num ) ) + return false; + + header = get_header(); + } + + Type* target = Data + idx + item_num; + Type* src = Data + idx; + + mem_move( target, src, (header->Num - idx) * sizeof(Type) ); + mem_copy( src, items, item_num * sizeof(Type) ); + + header->Num += item_num; + + return true; + } + Type& back( void ) { Header& header = * get_header(); @@ -1238,6 +1346,7 @@ namespace gen { Header& header = * get_header(); gen::free( header.Allocator, &header ); + Data = nullptr; } Header* get_header( void ) @@ -2074,30 +2183,30 @@ namespace gen struct DirEntry { - char const* FileName; - DirInfo* Info; - u8 Type; + char const* filename; + struct DirInfo* dir_info; + u8 type; }; struct DirInfo { - char const* FullPath; - DirEntry* Entries; // zpl_array + char const* fullpath; + DirEntry* entries; // zpl_array // Internals - char** Filenames; // zpl_array - char* Buffer; // zpl_string + char** filenames; // zpl_array + String buf; }; struct FileInfo { - FileOperations Ops; - FileDescriptor FD; - b32 IsTemp; + FileOperations ops; + FileDescriptor fd; + b32 is_temp; - char const* Filename; - FileTime LastWriteTime; - DirEntry* Dir; + char const* filename; + FileTime last_write_time; + DirEntry* dir; }; enum FileStandardType @@ -2129,7 +2238,7 @@ namespace gen inline char const* file_name( FileInfo* file ) { - return file->Filename ? file->Filename : ""; + return file->filename ? file->filename : ""; } /** @@ -2227,10 +2336,10 @@ namespace gen { s64 new_offset = 0; - if ( ! f->Ops.read_at ) - f->Ops = default_file_operations; + if ( ! f->ops.read_at ) + f->ops = default_file_operations; - f->Ops.seek( f->FD, offset, ESeekWhence_BEGIN, &new_offset ); + f->ops.seek( f->fd, offset, ESeekWhence_BEGIN, &new_offset ); return new_offset; } @@ -2239,10 +2348,10 @@ namespace gen { s64 new_offset = 0; - if ( ! f->Ops.read_at ) - f->Ops = default_file_operations; + if ( ! f->ops.read_at ) + f->ops = default_file_operations; - f->Ops.seek( f->FD, 0, ESeekWhence_END, &new_offset ); + f->ops.seek( f->fd, 0, ESeekWhence_END, &new_offset ); return new_offset; } @@ -2251,10 +2360,10 @@ namespace gen { s64 new_offset = 0; - if ( ! f->Ops.read_at ) - f->Ops = default_file_operations; + if ( ! f->ops.read_at ) + f->ops = default_file_operations; - f->Ops.seek( f->FD, 0, ESeekWhence_CURRENT, &new_offset ); + f->ops.seek( f->fd, 0, ESeekWhence_CURRENT, &new_offset ); return new_offset; } @@ -2274,9 +2383,9 @@ namespace gen GEN_IMPL_INLINE b32 file_read_at_check( FileInfo* f, void* buffer, sw size, s64 offset, sw* bytes_read ) { - if ( ! f->Ops.read_at ) - f->Ops = default_file_operations; - return f->Ops.read_at( f->FD, buffer, size, offset, bytes_read, false ); + if ( ! f->ops.read_at ) + f->ops = default_file_operations; + return f->ops.read_at( f->fd, buffer, size, offset, bytes_read, false ); } GEN_IMPL_INLINE b32 file_write( FileInfo* f, void const* buffer, sw size ) @@ -2296,120 +2405,116 @@ namespace gen GEN_IMPL_INLINE b32 file_write_at_check( FileInfo* f, void const* buffer, sw size, s64 offset, sw* bytes_written ) { - if ( ! f->Ops.read_at ) - f->Ops = default_file_operations; + if ( ! f->ops.read_at ) + f->ops = default_file_operations; - return f->Ops.write_at( f->FD, buffer, size, offset, bytes_written ); + return f->ops.write_at( f->fd, buffer, size, offset, bytes_written ); } + + enum FileStreamFlags : u32 + { + /* Allows us to write to the buffer directly. Beware: you can not append a new data! */ + EFileStream_WRITABLE = bit( 0 ), + + /* Clones the input buffer so you can write (zpl_file_write*) data into it. */ + /* Since we work with a clone, the buffer size can dynamically grow as well. */ + EFileStream_CLONE_WRITABLE = bit( 1 ), + }; + + /** + * Opens a new memory stream + * @param file + * @param allocator + */ + b8 file_stream_new( FileInfo* file, AllocatorInfo allocator ); + + /** + * Opens a memory stream over an existing buffer + * @param file + * @param allocator + * @param buffer Memory to create stream from + * @param size Buffer's size + * @param flags + */ + b8 file_stream_open( FileInfo* file, AllocatorInfo allocator, u8* buffer, sw size, FileStreamFlags flags ); + + /** + * Retrieves the stream's underlying buffer and buffer size. + * @param file memory stream + * @param size (Optional) buffer size + */ + u8* file_stream_buf( FileInfo* file, sw* size ); + + extern FileOperations const memory_file_operations; #pragma endregion File Handling #pragma region ADT enum ADT_Type : u32 { - EADTTYPE_UNINITIALISED, /* node was not initialised, this is a programming error! */ - EADTTYPE_ARRAY, - EADTTYPE_OBJECT, - EADTTYPE_STRING, - EADTTYPE_MULTISTRING, - EADTTYPE_INTEGER, - EADTTYPE_REAL, + EADT_TYPE_UNINITIALISED, /* node was not initialised, this is a programming error! */ + EADT_TYPE_ARRAY, + EADT_TYPE_OBJECT, + EADT_TYPE_STRING, + EADT_TYPE_MULTISTRING, + EADT_TYPE_INTEGER, + EADT_TYPE_REAL, }; enum ADT_Props : u32 { - EADTPROPS_NONE, - EADTPROPS_NAN, - EADTPROPS_NAN_NEG, - EADTPROPS_INFINITY, - EADTPROPS_INFINITY_NEG, - EADTPROPS_FALSE, - EADTPROPS_TRUE, - EADTPROPS_NULL, - EADTPROPS_IS_EXP, - EADTPROPS_IS_HEX, + EADT_PROPS_NONE, + EADT_PROPS_NAN, + EADT_PROPS_NAN_NEG, + EADT_PROPS_INFINITY, + EADT_PROPS_INFINITY_NEG, + EADT_PROPS_FALSE, + EADT_PROPS_TRUE, + EADT_PROPS_NULL, + EADT_PROPS_IS_EXP, + EADT_PROPS_IS_HEX, // Used internally so that people can fill in real numbers they plan to write. - EADTPROPS_IS_PARSED_REAL, + EADT_PROPS_IS_PARSED_REAL, }; enum ADT_NamingStyle : u32 { - EADTNAME_STYLE_DOUBLE_QUOTE, - EADTNAME_STYLE_SINGLE_QUOTE, - EADTNAME_STYLE_NO_QUOTES, + EADT_NAME_STYLE_DOUBLE_QUOTE, + EADT_NAME_STYLE_SINGLE_QUOTE, + EADT_NAME_STYLE_NO_QUOTES, }; enum ADT_AssignStyle : u32 { - EADTASSIGN_STYLE_COLON, - EADTASSIGN_STYLE_EQUALS, - EADTASSIGN_STYLE_LINE, + EADT_ASSIGN_STYLE_COLON, + EADT_ASSIGN_STYLE_EQUALS, + EADT_ASSIGN_STYLE_LINE, }; enum ADT_DelimStyle : u32 { - EADTDELIM_STYLE_COMMA, - EADTDELIM_STYLE_LINE, - EADTDELIM_STYLE_NEWLINE, + EADT_DELIM_STYLE_COMMA, + EADT_DELIM_STYLE_LINE, + EADT_DELIM_STYLE_NEWLINE, }; enum ADT_Error : u32 { - EADTERROR_NONE, - EADTERROR_INTERNAL, - EADTERROR_ALREADY_CONVERTED, - EADTERROR_INVALID_TYPE, - EADTERROR_OUT_OF_MEMORY, + EADT_ERROR_NONE, + EADT_ERROR_INTERNAL, + EADT_ERROR_ALREADY_CONVERTED, + EADT_ERROR_INVALID_TYPE, + EADT_ERROR_OUT_OF_MEMORY, }; struct ADT_Node { - static ADT_Node* make_branch( AllocatorInfo backing, char const* name, b32 is_array ); - static ADT_Node* make_leaf( AllocatorInfo backing, char const* name, u8 type ); - - static ADT_Node* set_arr( char const* name, AllocatorInfo backing ); - static ADT_Node* set_flt( char const* name, f64 value ); - static ADT_Node* set_int( char const* name, s64 value ); - static ADT_Node* set_obj( char const* name, AllocatorInfo backing ); - static ADT_Node* set_str( char const* name, char const* value ); - - static void swap( ADT_Node* node, ADT_Node* other ); - - ADT_Node* append_arr( char const* name ); - ADT_Node* append_flt( char const* name, f64 value ); - ADT_Node* append_int( char const* name, s64 value ); - ADT_Node* append_obj( char const* name ); - ADT_Node* append_str( char const* name, char const* value ); - - ADT_Node* destroy(); - - ADT_Node* query( char const* uri ); - - ADT_Node* find( char const* name, b32 deep_search ); - - ADT_Node* alloc(); - ADT_Node* alloc_at( sw index ); - - ADT_Node* move_node( ADT_Node* new_parent ); - - ADT_Node* move_node_at( ADT_Node* new_parent, sw index ); - - char* parse_number( char* base ); - - void remove( ADT_Node* node ); - - ADT_Error str_to_number(); - - ADT_Error print_number( FileInfo* file ); - ADT_Error print_string( FileInfo* file, char const* escapsed_chars, char const* escape_symbol ); - - #pragma region Layout - char const* name; - ADT_Node* parent; + char const* name; + struct ADT_Node* parent; /* properties */ - ADT_Type type; - ADT_Props props; + ADT_Type type : 4; + u8 props : 4; #ifndef ZPL_PARSER_DISABLE_ANALYSIS u8 cfg_mode : 1; u8 name_style : 2; @@ -2422,8 +2527,8 @@ namespace gen /* adt data */ union { - char const* string; - struct ADT_Node* nodes; ///< zpl_array + char const* string; + Array nodes; ///< zpl_array struct { @@ -2433,7 +2538,7 @@ namespace gen s64 integer; }; - #ifndef ZPL_PARSER_DISABLE_ANALYSIS + #ifndef ZPL_PARSER_DISABLE_ANALYSIS /* number analysis */ s32 base; s32 base2; @@ -2441,12 +2546,290 @@ namespace gen s8 exp : 4; u8 neg_zero : 1; u8 lead_digit : 1; - #endif + #endif }; }; - #pragma endregion Layout }; + /* ADT NODE LIMITS + * delimiter and assignment segment width is limited to 128 whitespace symbols each. + * real number limits decimal position to 128 places. + * real number exponent is limited to 64 digits. + */ + + /** + * @brief Initialise an ADT object or array + * + * @param node + * @param backing Memory allocator used for descendants + * @param name Node's name + * @param is_array + * @return error code + */ + u8 adt_make_branch( ADT_Node* node, AllocatorInfo backing, char const* name, b32 is_array ); + + /** + * @brief Destroy an ADT branch and its descendants + * + * @param node + * @return error code + */ + u8 adt_destroy_branch( ADT_Node* node ); + + /** + * @brief Initialise an ADT leaf + * + * @param node + * @param name Node's name + * @param type Node's type (use zpl_adt_make_branch for container nodes) + * @return error code + */ + u8 adt_make_leaf( ADT_Node* node, char const* name, ADT_Type type ); + + + /** + * @brief Fetch a node using provided URI string. + * + * This method uses a basic syntax to fetch a node from the ADT. The following features are available + * to retrieve the data: + * + * - "a/b/c" navigates through objects "a" and "b" to get to "c" + * - "arr/[foo=123]/bar" iterates over "arr" to find any object with param "foo" that matches the value "123", then gets its field called "bar" + * - "arr/3" retrieves the 4th element in "arr" + * - "arr/[apple]" retrieves the first element of value "apple" in "arr" + * + * @param node ADT node + * @param uri Locator string as described above + * @return zpl_adt_node* + * + * @see code/apps/examples/json_get.c + */ + ADT_Node* adt_query( ADT_Node* node, char const* uri ); + + /** + * @brief Find a field node within an object by the given name. + * + * @param node + * @param name + * @param deep_search Perform search recursively + * @return zpl_adt_node * node + */ + ADT_Node* adt_find( ADT_Node* node, char const* name, b32 deep_search ); + + /** + * @brief Allocate an unitialised node within a container at a specified index. + * + * @param parent + * @param index + * @return zpl_adt_node * node + */ + ADT_Node* adt_alloc_at( ADT_Node* parent, sw index ); + + /** + * @brief Allocate an unitialised node within a container. + * + * @param parent + * @return zpl_adt_node * node + */ + ADT_Node* adt_alloc( ADT_Node* parent ); + + /** + * @brief Move an existing node to a new container at a specified index. + * + * @param node + * @param new_parent + * @param index + * @return zpl_adt_node * node + */ + ADT_Node* adt_move_node_at( ADT_Node* node, ADT_Node* new_parent, sw index ); + + /** + * @brief Move an existing node to a new container. + * + * @param node + * @param new_parent + * @return zpl_adt_node * node + */ + ADT_Node* adt_move_node( ADT_Node* node, ADT_Node* new_parent ); + + /** + * @brief Swap two nodes. + * + * @param node + * @param other_node + * @return + */ + void adt_swap_nodes( ADT_Node* node, ADT_Node* other_node ); + + /** + * @brief Remove node from container. + * + * @param node + * @return + */ + void adt_remove_node( ADT_Node* node ); + + /** + * @brief Initialise a node as an object + * + * @param obj + * @param name + * @param backing + * @return + */ + b8 adt_set_obj( ADT_Node* obj, char const* name, AllocatorInfo backing ); + + /** + * @brief Initialise a node as an array + * + * @param obj + * @param name + * @param backing + * @return + */ + b8 adt_set_arr( ADT_Node* obj, char const* name, AllocatorInfo backing ); + + /** + * @brief Initialise a node as a string + * + * @param obj + * @param name + * @param value + * @return + */ + b8 adt_set_str( ADT_Node* obj, char const* name, char const* value ); + + /** + * @brief Initialise a node as a float + * + * @param obj + * @param name + * @param value + * @return + */ + b8 adt_set_flt( ADT_Node* obj, char const* name, f64 value ); + + /** + * @brief Initialise a node as a signed integer + * + * @param obj + * @param name + * @param value + * @return + */ + b8 adt_set_int( ADT_Node* obj, char const* name, s64 value ); + + /** + * @brief Append a new node to a container as an object + * + * @param parent + * @param name + * @return* + */ + ADT_Node* adt_append_obj( ADT_Node* parent, char const* name ); + + /** + * @brief Append a new node to a container as an array + * + * @param parent + * @param name + * @return* + */ + ADT_Node* adt_append_arr( ADT_Node* parent, char const* name ); + + /** + * @brief Append a new node to a container as a string + * + * @param parent + * @param name + * @param value + * @return* + */ + ADT_Node* adt_append_str( ADT_Node* parent, char const* name, char const* value ); + + /** + * @brief Append a new node to a container as a float + * + * @param parent + * @param name + * @param value + * @return* + */ + ADT_Node* adt_append_flt( ADT_Node* parent, char const* name, f64 value ); + + /** + * @brief Append a new node to a container as a signed integer + * + * @param parent + * @param name + * @param value + * @return* + */ + ADT_Node* adt_append_int( ADT_Node* parent, char const* name, s64 value ); + + /* parser helpers */ + + /** + * @brief Parses a text and stores the result into an unitialised node. + * + * @param node + * @param base + * @return* + */ + char* adt_parse_number( ADT_Node* node, char* base ); + + /** + * @brief Parses a text and stores the result into an unitialised node. + * This function expects the entire input to be a number. + * + * @param node + * @param base + * @return* + */ + char* adt_parse_number_strict( ADT_Node* node, char* base_str ); + + /** + * @brief Parses and converts an existing string node into a number. + * + * @param node + * @return + */ + ADT_Error adt_str_to_number( ADT_Node* node ); + + /** + * @brief Parses and converts an existing string node into a number. + * This function expects the entire input to be a number. + * + * @param node + * @return + */ + ADT_Error adt_str_to_number_strict( ADT_Node* node ); + + /** + * @brief Prints a number into a file stream. + * + * The provided file handle can also be a memory mapped stream. + * + * @see zpl_file_stream_new + * @param file + * @param node + * @return + */ + ADT_Error adt_print_number( FileInfo* file, ADT_Node* node ); + + /** + * @brief Prints a string into a file stream. + * + * The provided file handle can also be a memory mapped stream. + * + * @see zpl_file_stream_new + * @param file + * @param node + * @param escaped_chars + * @param escape_symbol + * @return + */ + ADT_Error adt_print_string( FileInfo* file, ADT_Node* node, char const* escaped_chars, char const* escape_symbol ); #pragma endregion ADT #pragma region CSV diff --git a/test/SOA.hpp b/test/SOA.cpp similarity index 81% rename from test/SOA.hpp rename to test/SOA.cpp index 3da92f3..bb9b992 100644 --- a/test/SOA.hpp +++ b/test/SOA.cpp @@ -1,9 +1,9 @@ -#pragma once - -#ifdef gen_time +#if gen_time #define GEN_FEATURE_PARSING #define GEN_DEFINE_LIBRARY_CODE_CONSTANTS #define GEN_ENFORCE_STRONG_CODE_TYPES +#define GEN_EXPOSE_BACKEND +#define GEN_BENCHMARK #include "gen.hpp" using namespace gen; @@ -91,7 +91,7 @@ Code gen_SOA( CodeStruct struct_def, s32 num_entries = 0 ) } )); - String content = String::make( Memory::GlobalAllocator, "return\n{\n" ); + String content = String::make( GlobalAllocator, "return\n{\n" ); for ( CodeVar member : vars ) { @@ -114,4 +114,31 @@ Code gen_SOA( CodeStruct struct_def, s32 num_entries = 0 ) return soa; } + +void check_SOA() +{ + gen::init(); + Builder soa_test; soa_test.open( "SOA.gen.hpp" ); + + soa_test.print( parse_using( code( + using u16 = unsigned short; + ))); + soa_test.print( def_include( txt_StrC("gen.hpp"))); + soa_test.print( def_using_namespace( name(gen) ) ); + + soa_test.print( gen_SOA( + parse_struct( code( + struct TestStruct + { + u8 A; + u16 B; + u32 C; + u64 D; + }; + )) + )); + + soa_test.write(); + gen::deinit(); +} #endif diff --git a/test/test.cpp b/test/test.cpp index 11b8d4c..f79938e 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -1,5 +1,11 @@ +#define GEN_FEATURE_PARSING +#define GEN_DEFINE_LIBRARY_CODE_CONSTANTS +#define GEN_ENFORCE_STRONG_CODE_TYPES +#define GEN_EXPOSE_BACKEND +#define GEN_BENCHMARK #include "gen.cpp" #include "sanity.cpp" +#include "SOA.cpp" #if gen_time int gen_main() @@ -9,6 +15,8 @@ int gen_main() check_sanity(); + check_SOA(); + return 0; } #endif