mirror of
				https://github.com/Ed94/gencpp.git
				synced 2025-10-30 14:30:53 -07:00 
			
		
		
		
	Adding the pragma once and includes the files broke compilation, still diagnosing why.
- Some string functions were moved to the cpp, still need to do some more evaluation of it and the containers...
- Added support for forceinline and neverinline to parsing (untested)
- Added support for specifiers in operator cast such as explicit, inline/forceinline/neverinline, etc.
    - Before it only support const.
    - Still need to support volatile.
- Forceinline was not supported at all for tokenization, fixed that.
		
	
		
			
				
	
	
		
			588 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			588 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #pragma once
 | |
| #include "string_ops.cpp"
 | |
| 
 | |
| #pragma region Printing
 | |
| 
 | |
| enum
 | |
| {
 | |
| 	GEN_FMT_MINUS = bit( 0 ),
 | |
| 	GEN_FMT_PLUS  = bit( 1 ),
 | |
| 	GEN_FMT_ALT   = bit( 2 ),
 | |
| 	GEN_FMT_SPACE = bit( 3 ),
 | |
| 	GEN_FMT_ZERO  = bit( 4 ),
 | |
| 
 | |
| 	GEN_FMT_CHAR   = bit( 5 ),
 | |
| 	GEN_FMT_SHORT  = bit( 6 ),
 | |
| 	GEN_FMT_INT    = bit( 7 ),
 | |
| 	GEN_FMT_LONG   = bit( 8 ),
 | |
| 	GEN_FMT_LLONG  = bit( 9 ),
 | |
| 	GEN_FMT_SIZE   = bit( 10 ),
 | |
| 	GEN_FMT_INTPTR = bit( 11 ),
 | |
| 
 | |
| 	GEN_FMT_UNSIGNED = bit( 12 ),
 | |
| 	GEN_FMT_LOWER    = bit( 13 ),
 | |
| 	GEN_FMT_UPPER    = bit( 14 ),
 | |
| 	GEN_FMT_WIDTH    = bit( 15 ),
 | |
| 
 | |
| 	GEN_FMT_DONE = bit( 30 ),
 | |
| 
 | |
| 	GEN_FMT_INTS = GEN_FMT_CHAR | GEN_FMT_SHORT | GEN_FMT_INT | GEN_FMT_LONG | GEN_FMT_LLONG | GEN_FMT_SIZE | GEN_FMT_INTPTR
 | |
| };
 | |
| 
 | |
| struct _format_info
 | |
| {
 | |
| 	s32 base;
 | |
| 	s32 flags;
 | |
| 	s32 width;
 | |
| 	s32 precision;
 | |
| };
 | |
| 
 | |
| internal sw _print_string( char* text, sw max_len, _format_info* info, char const* str )
 | |
| {
 | |
| 	sw    res = 0, len = 0;
 | |
| 	sw    remaining = max_len;
 | |
| 	char* begin     = text;
 | |
| 
 | |
| 	if ( str == NULL && max_len >= 6 )
 | |
| 	{
 | |
| 		res += str_copy_nulpad( text, "(null)", 6 );
 | |
| 		return res;
 | |
| 	}
 | |
| 
 | |
| 	if ( info && info->precision >= 0 )
 | |
| 		// Made the design decision for this library that precision is the length of the string.
 | |
| 		len = info->precision;
 | |
| 	else
 | |
| 		len = str_len( str );
 | |
| 
 | |
| 	if ( info && ( info->width == 0 && info->flags & GEN_FMT_WIDTH ) )
 | |
| 	{
 | |
| 		return res;
 | |
| 	}
 | |
| 
 | |
| 	if ( info && ( info->width == 0 || info->flags & GEN_FMT_MINUS ) )
 | |
| 	{
 | |
| 		if ( info->precision > 0 )
 | |
| 			len = info->precision < len ? info->precision : len;
 | |
| 		if ( res + len > max_len )
 | |
| 			return res;
 | |
| 		res  += str_copy_nulpad( text, str, len );
 | |
| 		text += res;
 | |
| 
 | |
| 		if ( info->width > res )
 | |
| 		{
 | |
| 			sw padding = info->width - len;
 | |
| 
 | |
| 			char pad = ( info->flags & GEN_FMT_ZERO ) ? '0' : ' ';
 | |
| 			while ( padding-- > 0 && remaining-- > 0 )
 | |
| 				*text++ = pad, res++;
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if ( info && ( info->width > res ) )
 | |
| 		{
 | |
| 			sw   padding = info->width - len;
 | |
| 			char pad     = ( info->flags & GEN_FMT_ZERO ) ? '0' : ' ';
 | |
| 			while ( padding-- > 0 && remaining-- > 0 )
 | |
| 				*text++ = pad, res++;
 | |
| 		}
 | |
| 
 | |
| 		if ( res + len > max_len )
 | |
| 			return res;
 | |
| 		res += str_copy_nulpad( text, str, len );
 | |
| 	}
 | |
| 
 | |
| 	if ( info )
 | |
| 	{
 | |
| 		if ( info->flags & GEN_FMT_UPPER )
 | |
| 			str_to_upper( begin );
 | |
| 		else if ( info->flags & GEN_FMT_LOWER )
 | |
| 			str_to_lower( begin );
 | |
| 	}
 | |
| 
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| internal sw _print_char( char* text, sw max_len, _format_info* info, char arg )
 | |
| {
 | |
| 	char str[ 2 ] = "";
 | |
| 	str[ 0 ]      = arg;
 | |
| 	return _print_string( text, max_len, info, str );
 | |
| }
 | |
| 
 | |
| internal sw _print_repeated_char( char* text, sw max_len, _format_info* info, char arg )
 | |
| {
 | |
| 	sw  res = 0;
 | |
| 	s32 rem = ( info ) ? ( info->width > 0 ) ? info->width : 1 : 1;
 | |
| 	res     = rem;
 | |
| 	while ( rem-- > 0 )
 | |
| 		*text++ = arg;
 | |
| 
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| internal sw _print_i64( char* text, sw max_len, _format_info* info, s64 value )
 | |
| {
 | |
| 	char num[ 130 ];
 | |
| 	i64_to_str( value, num, info ? info->base : 10 );
 | |
| 	return _print_string( text, max_len, info, num );
 | |
| }
 | |
| 
 | |
| internal sw _print_u64( char* text, sw max_len, _format_info* info, u64 value )
 | |
| {
 | |
| 	char num[ 130 ];
 | |
| 	u64_to_str( value, num, info ? info->base : 10 );
 | |
| 	return _print_string( text, max_len, info, num );
 | |
| }
 | |
| 
 | |
| internal sw _print_f64( char* text, sw max_len, _format_info* info, b32 is_hexadecimal, f64 arg )
 | |
| {
 | |
| 	// TODO: Handle exponent notation
 | |
| 	sw    width, len, remaining = max_len;
 | |
| 	char* text_begin = text;
 | |
| 
 | |
| 	if ( arg )
 | |
| 	{
 | |
| 		u64 value;
 | |
| 		if ( arg < 0 )
 | |
| 		{
 | |
| 			if ( remaining > 1 )
 | |
| 				*text = '-', remaining--;
 | |
| 			text++;
 | |
| 			arg = -arg;
 | |
| 		}
 | |
| 		else if ( info->flags & GEN_FMT_MINUS )
 | |
| 		{
 | |
| 			if ( remaining > 1 )
 | |
| 				*text = '+', remaining--;
 | |
| 			text++;
 | |
| 		}
 | |
| 
 | |
| 		value  = zpl_cast( u64 ) arg;
 | |
| 		len    = _print_u64( text, remaining, NULL, value );
 | |
| 		text  += len;
 | |
| 
 | |
| 		if ( len >= remaining )
 | |
| 			remaining = min( remaining, 1 );
 | |
| 		else
 | |
| 			remaining -= len;
 | |
| 		arg -= value;
 | |
| 
 | |
| 		if ( info->precision < 0 )
 | |
| 			info->precision = 6;
 | |
| 
 | |
| 		if ( ( info->flags & GEN_FMT_ALT ) || info->precision > 0 )
 | |
| 		{
 | |
| 			s64 mult = 10;
 | |
| 			if ( remaining > 1 )
 | |
| 				*text = '.', remaining--;
 | |
| 			text++;
 | |
| 			while ( info->precision-- > 0 )
 | |
| 			{
 | |
| 				value  = zpl_cast( u64 )( arg * mult );
 | |
| 				len    = _print_u64( text, remaining, NULL, value );
 | |
| 				text  += len;
 | |
| 				if ( len >= remaining )
 | |
| 					remaining = min( remaining, 1 );
 | |
| 				else
 | |
| 					remaining -= len;
 | |
| 				arg  -= zpl_cast( f64 ) value / mult;
 | |
| 				mult *= 10;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if ( remaining > 1 )
 | |
| 			*text = '0', remaining--;
 | |
| 		text++;
 | |
| 		if ( info->flags & GEN_FMT_ALT )
 | |
| 		{
 | |
| 			if ( remaining > 1 )
 | |
| 				*text = '.', remaining--;
 | |
| 			text++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	width = info->width - ( text - text_begin );
 | |
| 	if ( width > 0 )
 | |
| 	{
 | |
| 		char  fill = ( info->flags & GEN_FMT_ZERO ) ? '0' : ' ';
 | |
| 		char* end  = text + remaining - 1;
 | |
| 		len        = ( text - text_begin );
 | |
| 
 | |
| 		for ( len = ( text - text_begin ); len--; )
 | |
| 		{
 | |
| 			if ( ( text_begin + len + width ) < end )
 | |
| 				*( text_begin + len + width ) = *( text_begin + len );
 | |
| 		}
 | |
| 
 | |
| 		len   = width;
 | |
| 		text += len;
 | |
| 		if ( len >= remaining )
 | |
| 			remaining = min( remaining, 1 );
 | |
| 		else
 | |
| 			remaining -= len;
 | |
| 
 | |
| 		while ( len-- )
 | |
| 		{
 | |
| 			if ( text_begin + len < end )
 | |
| 				text_begin[ len ] = fill;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ( text - text_begin );
 | |
| }
 | |
| 
 | |
| neverinline sw str_fmt_va( char* text, sw max_len, char const* fmt, va_list va )
 | |
| {
 | |
| 	char const* text_begin = text;
 | |
| 	sw          remaining  = max_len, res;
 | |
| 
 | |
| 	while ( *fmt )
 | |
| 	{
 | |
| 		_format_info info = { 0 };
 | |
| 		sw           len  = 0;
 | |
| 		info.precision    = -1;
 | |
| 
 | |
| 		while ( *fmt && *fmt != '%' && remaining )
 | |
| 			*text++ = *fmt++;
 | |
| 
 | |
| 		if ( *fmt == '%' )
 | |
| 		{
 | |
| 			do
 | |
| 			{
 | |
| 				switch ( *++fmt )
 | |
| 				{
 | |
| 					case '-' :
 | |
| 						{
 | |
| 							info.flags |= GEN_FMT_MINUS;
 | |
| 							break;
 | |
| 						}
 | |
| 					case '+' :
 | |
| 						{
 | |
| 							info.flags |= GEN_FMT_PLUS;
 | |
| 							break;
 | |
| 						}
 | |
| 					case '#' :
 | |
| 						{
 | |
| 							info.flags |= GEN_FMT_ALT;
 | |
| 							break;
 | |
| 						}
 | |
| 					case ' ' :
 | |
| 						{
 | |
| 							info.flags |= GEN_FMT_SPACE;
 | |
| 							break;
 | |
| 						}
 | |
| 					case '0' :
 | |
| 						{
 | |
| 							info.flags |= ( GEN_FMT_ZERO | GEN_FMT_WIDTH );
 | |
| 							break;
 | |
| 						}
 | |
| 					default :
 | |
| 						{
 | |
| 							info.flags |= GEN_FMT_DONE;
 | |
| 							break;
 | |
| 						}
 | |
| 				}
 | |
| 			} while ( ! ( info.flags & GEN_FMT_DONE ) );
 | |
| 		}
 | |
| 
 | |
| 		// NOTE: Optional Width
 | |
| 		if ( *fmt == '*' )
 | |
| 		{
 | |
| 			int width = va_arg( va, int );
 | |
| 			if ( width < 0 )
 | |
| 			{
 | |
| 				info.flags |= GEN_FMT_MINUS;
 | |
| 				info.width  = -width;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				info.width = width;
 | |
| 			}
 | |
| 			info.flags |= GEN_FMT_WIDTH;
 | |
| 			fmt++;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			info.width = zpl_cast( s32 ) str_to_i64( fmt, zpl_cast( char** ) & fmt, 10 );
 | |
| 			if ( info.width != 0 )
 | |
| 			{
 | |
| 				info.flags |= GEN_FMT_WIDTH;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// NOTE: Optional Precision
 | |
| 		if ( *fmt == '.' )
 | |
| 		{
 | |
| 			fmt++;
 | |
| 			if ( *fmt == '*' )
 | |
| 			{
 | |
| 				info.precision = va_arg( va, int );
 | |
| 				fmt++;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				info.precision = zpl_cast( s32 ) str_to_i64( fmt, zpl_cast( char** ) & fmt, 10 );
 | |
| 			}
 | |
| 			info.flags &= ~GEN_FMT_ZERO;
 | |
| 		}
 | |
| 
 | |
| 		switch ( *fmt++ )
 | |
| 		{
 | |
| 			case 'h' :
 | |
| 				if ( *fmt == 'h' )
 | |
| 				{    // hh => char
 | |
| 					info.flags |= GEN_FMT_CHAR;
 | |
| 					fmt++;
 | |
| 				}
 | |
| 				else
 | |
| 				{    // h => short
 | |
| 					info.flags |= GEN_FMT_SHORT;
 | |
| 				}
 | |
| 				break;
 | |
| 
 | |
| 			case 'l' :
 | |
| 				if ( *fmt == 'l' )
 | |
| 				{    // ll => long long
 | |
| 					info.flags |= GEN_FMT_LLONG;
 | |
| 					fmt++;
 | |
| 				}
 | |
| 				else
 | |
| 				{    // l => long
 | |
| 					info.flags |= GEN_FMT_LONG;
 | |
| 				}
 | |
| 				break;
 | |
| 
 | |
| 				break;
 | |
| 
 | |
| 			case 'z' :    // NOTE: zpl_usize
 | |
| 				info.flags |= GEN_FMT_UNSIGNED;
 | |
| 				// fallthrough
 | |
| 			case 't' :    // NOTE: zpl_isize
 | |
| 				info.flags |= GEN_FMT_SIZE;
 | |
| 				break;
 | |
| 
 | |
| 			default :
 | |
| 				fmt--;
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		switch ( *fmt )
 | |
| 		{
 | |
| 			case 'u' :
 | |
| 				info.flags |= GEN_FMT_UNSIGNED;
 | |
| 				// fallthrough
 | |
| 			case 'd' :
 | |
| 			case 'i' :
 | |
| 				info.base = 10;
 | |
| 				break;
 | |
| 
 | |
| 			case 'o' :
 | |
| 				info.base = 8;
 | |
| 				break;
 | |
| 
 | |
| 			case 'x' :
 | |
| 				info.base   = 16;
 | |
| 				info.flags |= ( GEN_FMT_UNSIGNED | GEN_FMT_LOWER );
 | |
| 				break;
 | |
| 
 | |
| 			case 'X' :
 | |
| 				info.base   = 16;
 | |
| 				info.flags |= ( GEN_FMT_UNSIGNED | GEN_FMT_UPPER );
 | |
| 				break;
 | |
| 
 | |
| 			case 'f' :
 | |
| 			case 'F' :
 | |
| 			case 'g' :
 | |
| 			case 'G' :
 | |
| 				len = _print_f64( text, remaining, &info, 0, va_arg( va, f64 ) );
 | |
| 				break;
 | |
| 
 | |
| 			case 'a' :
 | |
| 			case 'A' :
 | |
| 				len = _print_f64( text, remaining, &info, 1, va_arg( va, f64 ) );
 | |
| 				break;
 | |
| 
 | |
| 			case 'c' :
 | |
| 				len = _print_char( text, remaining, &info, zpl_cast( char ) va_arg( va, int ) );
 | |
| 				break;
 | |
| 
 | |
| 			case 's' :
 | |
| 				len = _print_string( text, remaining, &info, va_arg( va, char* ) );
 | |
| 				break;
 | |
| 
 | |
| 			case 'S':
 | |
| 			{
 | |
| 				String gen_str = String { va_arg( va, char*) };
 | |
| 
 | |
| 				info.precision = gen_str.length();
 | |
| 				len            = _print_string( text, remaining, &info, gen_str );
 | |
| 			}
 | |
| 			break;
 | |
| 
 | |
| 			case 'r' :
 | |
| 				len = _print_repeated_char( text, remaining, &info, va_arg( va, int ) );
 | |
| 				break;
 | |
| 
 | |
| 			case 'p' :
 | |
| 				info.base   = 16;
 | |
| 				info.flags |= ( GEN_FMT_LOWER | GEN_FMT_UNSIGNED | GEN_FMT_ALT | GEN_FMT_INTPTR );
 | |
| 				break;
 | |
| 
 | |
| 			case '%' :
 | |
| 				len = _print_char( text, remaining, &info, '%' );
 | |
| 				break;
 | |
| 
 | |
| 			default :
 | |
| 				fmt--;
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		fmt++;
 | |
| 
 | |
| 		if ( info.base != 0 )
 | |
| 		{
 | |
| 			if ( info.flags & GEN_FMT_UNSIGNED )
 | |
| 			{
 | |
| 				u64 value = 0;
 | |
| 				switch ( info.flags & GEN_FMT_INTS )
 | |
| 				{
 | |
| 					case GEN_FMT_CHAR :
 | |
| 						value = zpl_cast( u64 ) zpl_cast( u8 ) va_arg( va, int );
 | |
| 						break;
 | |
| 					case GEN_FMT_SHORT :
 | |
| 						value = zpl_cast( u64 ) zpl_cast( u16 ) va_arg( va, int );
 | |
| 						break;
 | |
| 					case GEN_FMT_LONG :
 | |
| 						value = zpl_cast( u64 ) va_arg( va, unsigned long );
 | |
| 						break;
 | |
| 					case GEN_FMT_LLONG :
 | |
| 						value = zpl_cast( u64 ) va_arg( va, unsigned long long );
 | |
| 						break;
 | |
| 					case GEN_FMT_SIZE :
 | |
| 						value = zpl_cast( u64 ) va_arg( va, uw );
 | |
| 						break;
 | |
| 					case GEN_FMT_INTPTR :
 | |
| 						value = zpl_cast( u64 ) va_arg( va, uptr );
 | |
| 						break;
 | |
| 					default :
 | |
| 						value = zpl_cast( u64 ) va_arg( va, unsigned int );
 | |
| 						break;
 | |
| 				}
 | |
| 
 | |
| 				len = _print_u64( text, remaining, &info, value );
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				s64 value = 0;
 | |
| 				switch ( info.flags & GEN_FMT_INTS )
 | |
| 				{
 | |
| 					case GEN_FMT_CHAR :
 | |
| 						value = zpl_cast( s64 ) zpl_cast( s8 ) va_arg( va, int );
 | |
| 						break;
 | |
| 					case GEN_FMT_SHORT :
 | |
| 						value = zpl_cast( s64 ) zpl_cast( s16 ) va_arg( va, int );
 | |
| 						break;
 | |
| 					case GEN_FMT_LONG :
 | |
| 						value = zpl_cast( s64 ) va_arg( va, long );
 | |
| 						break;
 | |
| 					case GEN_FMT_LLONG :
 | |
| 						value = zpl_cast( s64 ) va_arg( va, long long );
 | |
| 						break;
 | |
| 					case GEN_FMT_SIZE :
 | |
| 						value = zpl_cast( s64 ) va_arg( va, uw );
 | |
| 						break;
 | |
| 					case GEN_FMT_INTPTR :
 | |
| 						value = zpl_cast( s64 ) va_arg( va, uptr );
 | |
| 						break;
 | |
| 					default :
 | |
| 						value = zpl_cast( s64 ) va_arg( va, int );
 | |
| 						break;
 | |
| 				}
 | |
| 
 | |
| 				len = _print_i64( text, remaining, &info, value );
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		text += len;
 | |
| 		if ( len >= remaining )
 | |
| 			remaining = min( remaining, 1 );
 | |
| 		else
 | |
| 			remaining -= len;
 | |
| 	}
 | |
| 
 | |
| 	*text++ = '\0';
 | |
| 	res     = ( text - text_begin );
 | |
| 	return ( res >= max_len || res < 0 ) ? -1 : res;
 | |
| }
 | |
| 
 | |
| char* str_fmt_buf_va( char const* fmt, va_list va )
 | |
| {
 | |
| 	local_persist thread_local char buffer[ GEN_PRINTF_MAXLEN ];
 | |
| 	str_fmt_va( buffer, size_of( buffer ), fmt, va );
 | |
| 	return buffer;
 | |
| }
 | |
| 
 | |
| char* str_fmt_buf( char const* fmt, ... )
 | |
| {
 | |
| 	va_list va;
 | |
| 	char*   str;
 | |
| 	va_start( va, fmt );
 | |
| 	str = str_fmt_buf_va( fmt, va );
 | |
| 	va_end( va );
 | |
| 	return str;
 | |
| }
 | |
| 
 | |
| sw str_fmt_file_va( struct FileInfo* f, char const* fmt, va_list va )
 | |
| {
 | |
| 	local_persist thread_local char buf[ GEN_PRINTF_MAXLEN ];
 | |
| 	sw                              len = str_fmt_va( buf, size_of( buf ), fmt, va );
 | |
| 	b32                             res = file_write( f, buf, len - 1 );    // NOTE: prevent extra whitespace
 | |
| 	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( char* str, sw n, char const* fmt, ... )
 | |
| {
 | |
| 	sw      res;
 | |
| 	va_list va;
 | |
| 	va_start( va, fmt );
 | |
| 	res = str_fmt_va( str, n, 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 );
 | |
| }
 | |
| 
 | |
| sw str_fmt_out_err_va( char const* fmt, va_list va )
 | |
| {
 | |
| 	return str_fmt_file_va( file_get_standard( EFileStandard_ERROR ), fmt, va );
 | |
| }
 | |
| 
 | |
| sw str_fmt_out_err( char const* fmt, ... )
 | |
| {
 | |
| 	sw      res;
 | |
| 	va_list va;
 | |
| 	va_start( va, fmt );
 | |
| 	res = str_fmt_out_err_va( fmt, va );
 | |
| 	va_end( va );
 | |
| 	return res;
 | |
| }
 | |
| 
 | |
| #pragma endregion Printing
 |