From 80c830b59d6e690b4351aa7109483b822621b2d1 Mon Sep 17 00:00:00 2001 From: Ed_ Date: Fri, 29 Nov 2024 14:31:38 -0500 Subject: [PATCH] Starting to build a codebase for messing with assembly generation compatible with yasm --- .editorconfig | 2 +- .vscode/c_cpp_propertiees.json | 37 + .vscode/settings.json | 9 + scripts/build.ps1 | 3 + scripts/update_deps.ps1 | 15 + source/base/basic_types.h | 128 + source/base/debug.h | 63 + source/base/macros.h | 183 + source/base/memory.h | 25 + source/base/platform.h | 115 + source/base/pop_ignores.inline.h | 7 + source/base/push_ignores.inline.h | 18 + source/{copy_hello.asm => execution/launch.c} | 0 source/gen_asm.c | 9 + source/hello.asm => source_asm/copy_hello.asm | 0 source_asm/hello.asm | 43 + toolchain/gencpp/gen.hpp | 26283 ++++++++++++++++ toolchain/yasm | 1 + 18 files changed, 26940 insertions(+), 1 deletion(-) create mode 100644 .vscode/c_cpp_propertiees.json create mode 100644 .vscode/settings.json create mode 100644 scripts/update_deps.ps1 create mode 100644 source/base/basic_types.h create mode 100644 source/base/debug.h create mode 100644 source/base/macros.h create mode 100644 source/base/memory.h create mode 100644 source/base/platform.h create mode 100644 source/base/pop_ignores.inline.h create mode 100644 source/base/push_ignores.inline.h rename source/{copy_hello.asm => execution/launch.c} (100%) create mode 100644 source/gen_asm.c rename source/hello.asm => source_asm/copy_hello.asm (100%) create mode 100644 source_asm/hello.asm create mode 100644 toolchain/gencpp/gen.hpp create mode 160000 toolchain/yasm diff --git a/.editorconfig b/.editorconfig index b1fbaf4..51723fa 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,7 +14,7 @@ indent_size = 4 [*.{h, c, hpp, cpp}] indent_style = tab -indent_size = 4 +indent_size = 2 [*.{ps1, psm1}] indent_style = tab diff --git a/.vscode/c_cpp_propertiees.json b/.vscode/c_cpp_propertiees.json new file mode 100644 index 0000000..f3a6900 --- /dev/null +++ b/.vscode/c_cpp_propertiees.json @@ -0,0 +1,37 @@ +{ + "configurations": [ + { + "name": "Win32 msvc", + "includePath": [ + "${workspaceFolder}/source/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE", + "INTELLISENSE_DIRECTIVES" + ], + "windowsSdkVersion": "10.0.19041.0", + "compilerPath": "C:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.29.30133/bin/HostX64/x64/cl.exe", + "intelliSenseMode": "msvc-x64", + "compileCommands": "${workspaceFolder}/project/build/compile_commands.json" + }, + { + "name": "Win32 clang", + "includePath": [ + "${workspaceFolder}/source/**" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE", + "INTELLISENSE_DIRECTIVES" + ], + "windowsSdkVersion": "10.0.19041.0", + "compilerPath": "C:/Users/Ed/scoop/apps/llvm/current/bin/clang++.exe", + "intelliSenseMode": "windows-clang-x64", + "compileCommands": "${workspaceFolder}/project/build/compile_commands.json" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a90d1ad --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "files.associations": { + "*.rmd": "markdown", + "pop_ignores.inline.h": "c", + "basic_types.h": "c", + "push_ignores.inline.h": "c", + "memory.h": "c" + } +} \ No newline at end of file diff --git a/scripts/build.ps1 b/scripts/build.ps1 index a5ffb7a..02a650a 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -1,6 +1,9 @@ $misc = join-path $PSScriptRoot 'helpers/misc.ps1' . $misc +$update_deps = join-path $PSScriptRoot 'update_deps.ps1' +. $update_deps + $path_root = git rev-parse --show-toplevel $path_build = join-path $path_root 'build' $path_scripts = join-path $path_root 'scripts' diff --git a/scripts/update_deps.ps1 b/scripts/update_deps.ps1 new file mode 100644 index 0000000..77b9aed --- /dev/null +++ b/scripts/update_deps.ps1 @@ -0,0 +1,15 @@ +$misc = join-path $PSScriptRoot 'helpers/misc.ps1' +. $misc + +$path_root = git rev-parse --show-toplevel +$path_build = join-path $path_root 'build' +$path_scripts = join-path $path_root 'scripts' +$path_source = join-path $path_root 'source' +$path_toolchain = join-path $path_root 'toolchain' + +$url_yasm = 'https://github.com/yasm/yasm.git' + +$path_yasm = join-path $path_toolchain 'yasm' +$path_libyasm = join-path $path_yasm 'libyasm' + +clone-gitrepo $path_yasm $url_yasm diff --git a/source/base/basic_types.h b/source/base/basic_types.h new file mode 100644 index 0000000..baef191 --- /dev/null +++ b/source/base/basic_types.h @@ -0,0 +1,128 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "macros.hpp" +#endif + +#pragma region Basic Types + +#define U8_MIN 0u +#define U8_MAX 0xffu +#define I8_MIN ( -0x7f - 1 ) +#define I8_MAX 0x7f + +#define U16_MIN 0u +#define U16_MAX 0xffffu +#define I16_MIN ( -0x7fff - 1 ) +#define I16_MAX 0x7fff + +#define U32_MIN 0u +#define U32_MAX 0xffffffffu +#define I32_MIN ( -0x7fffffff - 1 ) +#define I32_MAX 0x7fffffff + +#define U64_MIN 0ull +#define U64_MAX 0xffffffffffffffffull +#define I64_MIN ( -0x7fffffffffffffffll - 1 ) +#define I64_MAX 0x7fffffffffffffffll + +#if defined( ARCH_32_BIT ) +# define USIZE_MIN GEN_U32_MIN +# define USIZE_MAX GEN_U32_MAX +# define ISIZE_MIN GEN_S32_MIN +# define ISIZE_MAX GEN_S32_MAX +#elif defined( ARCH_64_BIT ) +# define USIZE_MIN GEN_U64_MIN +# define USIZE_MAX GEN_U64_MAX +# define ISIZE_MIN GEN_I64_MIN +# define ISIZE_MAX GEN_I64_MAX +#else +# error Unknown architecture size. This library only supports 32 bit and 64 bit architectures. +#endif + +#define F32_MIN 1.17549435e-38f +#define F32_MAX 3.40282347e+38f +#define F64_MIN 2.2250738585072014e-308 +#define F64_MAX 1.7976931348623157e+308 + +#if defined( GEN_COMPILER_MSVC ) +# if _MSC_VER < 1300 +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; +# else +typedef unsigned __int8 u8; +typedef signed __int8 s8; +typedef unsigned __int16 u16; +typedef signed __int16 s16; +typedef unsigned __int32 u32; +typedef signed __int32 s32; +# endif +typedef unsigned __int64 u64; +typedef signed __int64 s64; +#else +# include + +typedef uint8_t u8; +typedef int8_t s8; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint64_t u64; +typedef int64_t s64; +#endif + +static_assert( sizeof( u8 ) == sizeof( s8 ), "sizeof(u8) != sizeof(s8)" ); +static_assert( sizeof( u16 ) == sizeof( s16 ), "sizeof(u16) != sizeof(s16)" ); +static_assert( sizeof( u32 ) == sizeof( s32 ), "sizeof(u32) != sizeof(s32)" ); +static_assert( sizeof( u64 ) == sizeof( s64 ), "sizeof(u64) != sizeof(s64)" ); + +static_assert( sizeof( u8 ) == 1, "sizeof(u8) != 1" ); +static_assert( sizeof( u16 ) == 2, "sizeof(u16) != 2" ); +static_assert( sizeof( u32 ) == 4, "sizeof(u32) != 4" ); +static_assert( sizeof( u64 ) == 8, "sizeof(u64) != 8" ); + +typedef size_t usize; +typedef ptrdiff_t ssize; + +static_assert( sizeof( usize ) == sizeof( ssize ), "sizeof(usize) != sizeof(ssize)" ); + +// NOTE: (u)zpl_intptr is only here for semantic reasons really as this library will only support 32/64 bit OSes. +#if defined( _WIN64 ) +typedef signed __int64 sptr; +typedef unsigned __int64 uptr; +#elif defined( _WIN32 ) +// NOTE; To mark types changing their size, e.g. zpl_intptr +# ifndef _W64 +# if ! defined( __midl ) && ( defined( _X86_ ) || defined( _M_IX86 ) ) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +# endif +typedef _W64 signed int sptr; +typedef _W64 unsigned int uptr; +#else +typedef uintptr_t uptr; +typedef intptr_t sptr; +#endif + +static_assert( sizeof( uptr ) == sizeof( sptr ), "sizeof(uptr) != sizeof(sptr)" ); + +typedef float f32; +typedef double f64; + +static_assert( sizeof( f32 ) == 4, "sizeof(f32) != 4" ); +static_assert( sizeof( f64 ) == 8, "sizeof(f64) != 8" ); + +typedef s8 b8; +typedef s16 b16; +typedef s32 b32; + +typedef void* memptr; +typedef void const* memptr_const; + +#pragma endregion Basic Types diff --git a/source/base/debug.h b/source/base/debug.h new file mode 100644 index 0000000..857ba90 --- /dev/null +++ b/source/base/debug.h @@ -0,0 +1,63 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "basic_types.hpp" +#endif + +#pragma region Debug + +#if defined( _MSC_VER ) +# if _MSC_VER < 1300 +# define DEBUG_TRAP() __asm int 3 /* Trap to debugger! */ +# else +# define debug_trap() __debugbreak() +# endif +#elif defined( GEN_COMPILER_TINYC ) +# define DEBUG_TRAP() process_exit( 1 ) +#else +# define DEBUG_TRAP() __builtin_trap() +#endif + +#define ASSERT( cond ) ASSERT( cond, NULL ) + +#define ASSERT_MSG( cond, msg, ... ) \ + do \ + { \ + if ( ! ( cond ) ) \ + { \ + assert_handler( #cond, __FILE__, scast( s64, __LINE__ ), msg, ##__VA_ARGS__ ); \ + GEN_DEBUG_TRAP(); \ + } \ + } while ( 0 ) + +#define ASSERT_NOT_NULL( ptr ) ASSERT_MSG( ( ptr ) != NULL, #ptr " must not be NULL" ) + +// NOTE: Things that shouldn't happen with a message! +#define PANIC( msg, ... ) ASSERT_MSG( 0, msg, ##__VA_ARGS__ ) + +#if Build_Debug + #define FATAL( ... ) \ + do \ + { \ + local_persist thread_local \ + char buf[GEN_PRINTF_MAXLEN] = { 0 }; \ + \ + str_fmt(buf, PRINTF_MAXLEN, __VA_ARGS__); \ + PANIC(buf); \ + } \ + while (0) +#else + +# define FATAL( ... ) \ + do \ + { \ + str_fmt_out_err( __VA_ARGS__ ); \ + process_exit(1); \ + } \ + while (0) +#endif + +void assert_handler( char const* condition, char const* file, s32 line, char const* msg, ... ); +s32 assert_crash( char const* condition ); +void process_exit( u32 code ); + +#pragma endregion Debug diff --git a/source/base/macros.h b/source/base/macros.h new file mode 100644 index 0000000..fcd1a45 --- /dev/null +++ b/source/base/macros.h @@ -0,0 +1,183 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +#endif + +#pragma region Macros + +#ifndef global +#define global static // Global variables +#endif +#ifndef internal +#define internal static // Internal linkage +#endif +#ifndef local_persist +#define local_persist static // Local Persisting variables +#endif + +#ifndef api_c +#define api_c extern "C" +#endif + +#ifndef bit +#define bit( Value ) ( 1 << Value ) +#define bitfield_is_equal( Type, Field, Mask ) ( (Type(Mask) & Type(Field)) == Type(Mask) ) +#endif + +#ifndef cast +#define cast( type, value ) ( (type)(value) ) +#endif + +#ifndef ccast +#define ccast( type, value ) ( const_cast< type >( (value) ) ) +#endif +#ifndef pcast +#define pcast( type, value ) ( * reinterpret_cast< type* >( & ( value ) ) ) +#endif +#ifndef rcast +#define rcast( type, value ) reinterpret_cast< type >( value ) +#endif +#ifndef scast +#define scast( type, value ) static_cast< type >( value ) +#endif + +#ifndef stringize +#define stringize_va( ... ) #__VA_ARGS__ +#define stringize( ... ) stringize_va( __VA_ARGS__ ) +#endif + +#ifndef do_once +#define do_once( statement ) for ( local_persist b32 once = true; once; once = false, (statement) ) + +#define do_once_start \ + do \ + { \ + local_persist \ + bool done = false; \ + if ( done ) \ + break; \ + done = true; + +#define do_once_end \ + } \ + while(0); +#endif + +#ifndef labeled_scope_start +#define labeled_scope_start if ( false ) { +#define labeled_scope_end } +#endif + +#ifndef compiler_decorated_func_name +# ifdef COMPILER_CLANG +# define compiler_decorated_func_name __PRETTY_NAME__ +# elif defined(COMPILER_MSVC) +# define compiler_decorated_func_name __FUNCDNAME__ +# endif +#endif + +#ifndef num_args_impl +#define num_args_impl( _0, \ + _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ + _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ + _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \ + _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \ + _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, \ + _71, _72, _73, _74, _75, _76, _77, _78, _79, _80, \ + _81, _82, _83, _84, _85, _86, _87, _88, _89, _90, \ + _91, _92, _93, _94, _95, _96, _97, _98, _99, _100, \ + N, ... \ + ) N + +// ## deletes preceding comma if _VA_ARGS__ is empty (GCC, Clang) +#define num_args(...) \ + num_args_impl(_, ## __VA_ARGS__, \ + 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, \ + 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, \ + 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, \ + 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, \ + 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, \ + 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \ + 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, \ + 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \ + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \ + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, \ + 0 \ + ) +#endif + +#ifndef clamp +#define clamp( x, lower, upper ) min( max( ( x ), ( lower ) ), ( upper ) ) +#endif +#ifndef count_of +#define count_of( x ) ( ( size_of( x ) / size_of( 0 [ x ] ) ) / ( ( ssize )( ! ( size_of( x ) % size_of( 0 [ x ] ) ) ) ) ) +#endif +#ifndef is_between +#define is_between( x, lower, upper ) ( ( ( lower ) <= ( x ) ) && ( ( x ) <= ( upper ) ) ) +#endif +#ifndef size_of +#define size_of( x ) ( ssize )( sizeof( x ) ) +#endif + +#ifndef max +#define max( a, b ) ( (a > b) ? (a) : (b) ) +#endif +#ifndef min +#define min( a, b ) ( (a < b) ? (a) : (b) ) +#endif + +#if defined( _MSC_VER ) || defined( GEN_COMPILER_TINYC ) +# define offset_of( Type, element ) ( ( GEN_NS( ssize ) ) & ( ( ( Type* )0 )->element ) ) +#else +# define offset_of( Type, element ) __builtin_offsetof( Type, element ) +#endif + +#ifndef forceinline +# ifdef GEN_COMPILER_MSVC +# define forceinline __forceinline +# define neverinline __declspec( noinline ) +# elif defined(GEN_COMPILER_GCC) +# define forceinline inline __attribute__((__always_inline__)) +# define neverinline __attribute__( ( __noinline__ ) ) +# elif defined(GEN_COMPILER_CLANG) +# if __has_attribute(__always_inline__) +# define forceinline inline __attribute__((__always_inline__)) +# define neverinline __attribute__( ( __noinline__ ) ) +# else +# define forceinline +# define neverinline +# endif +# else +# define forceinline +# define neverinline +# endif +#endif + +#ifndef neverinline +# ifdef GEN_COMPILER_MSVC +# define neverinline __declspec( noinline ) +# elif defined(GEN_COMPILER_GCC) +# define neverinline __attribute__( ( __noinline__ ) ) +# elif defined(GEN_COMPILER_CLANG) +# if __has_attribute(__always_inline__) +# define neverinline __attribute__( ( __noinline__ ) ) +# else +# define neverinline +# endif +# else +# define neverinline +# endif +#endif + +#if !defined(typeof) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 202311L) +# if defined(_MSC_VER) +# define typeof(x) __typeof(x) +# elif defined(__GNUC__) || defined(__clang__) +# define typeof(x) __typeof__(x) +# else +# error "Compiler not supported" +# endif +#endif + +#pragma endregion Macros diff --git a/source/base/memory.h b/source/base/memory.h new file mode 100644 index 0000000..bc140b1 --- /dev/null +++ b/source/base/memory.h @@ -0,0 +1,25 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +# include "debug.h" +#endif + +#pragma region Memory + +#define kilobytes( x ) ( ( x ) * ( s64 )( 1024 ) ) +#define megabytes( x ) ( kilobytes( x ) * ( s64 )( 1024 ) ) +#define gigabytes( x ) ( megabytes( x ) * ( s64 )( 1024 ) ) +#define terabytes( x ) ( gigabytes( x ) * ( s64 )( 1024 ) ) + +#define _ONES ( cast( usize, - 1) / U8_MAX ) +#define _HIGHS ( GEN__ONES * ( U8_MAX / 2 + 1 ) ) +#define _HAS_ZERO( x ) ( ( ( x ) - _ONES ) & ~( x ) & _HIGHS ) + +#define swap( a, b ) \ +do { \ + typeof(a) \ + temp = (a); \ + (a) = (b); \ + (b) = temp; \ +} while(0) + +#pragma endregion Memory diff --git a/source/base/platform.h b/source/base/platform.h new file mode 100644 index 0000000..fbf480b --- /dev/null +++ b/source/base/platform.h @@ -0,0 +1,115 @@ +#ifdef INTELLISENSE_DIRECTIVES +# pragma once +#endif + +#pragma region Platform Detection + +/* Platform architecture */ + +#if defined( _WIN64 ) || defined( __x86_64__ ) || defined( _M_X64 ) || defined( __64BIT__ ) || defined( __powerpc64__ ) || defined( __ppc64__ ) || defined( __aarch64__ ) +# ifndef ARCH_64_BIT +# define ARCH_64_BIT 1 +# endif +#else +# ifndef ARCH_32_BItxt_StrCaT +# define ARCH_32_BIT 1 +# endif +#endif + +/* Platform OS */ + +#if defined( _WIN32 ) || defined( _WIN64 ) +# ifndef SYSTEM_WINDOWS +# define SYSTEM_WINDOWS 1 +# endif +#elif defined( __APPLE__ ) && defined( __MACH__ ) +# ifndef SYSTEM_OSX +# define SYSTEM_OSX 1 +# endif +# ifndef SYSTEM_MACOS +# define SYSTEM_MACOS 1 +# endif +# include +# if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1 +# ifndef SYSTEM_IOS +# define SYSTEM_IOS 1 +# endif +# endif +#elif defined( __unix__ ) +# ifndef SYSTEM_UNIX +# define SYSTEM_UNIX 1 +# endif +# if defined( ANDROID ) || defined( __ANDROID__ ) +# ifndef SYSTEM_ANDROID +# define SYSTEM_ANDROID 1 +# endif +# ifndef SYSTEM_LINUX +# define SYSTEM_LINUX 1 +# endif +# elif defined( __linux__ ) +# ifndef SYSTEM_LINUX +# define SYSTEM_LINUX 1 +# endif +# elif defined( __FreeBSD__ ) || defined( __FreeBSD_kernel__ ) +# ifndef SYSTEM_FREEBSD +# define SYSTEM_FREEBSD 1 +# endif +# elif defined( __OpenBSD__ ) +# ifndef SYSTEM_OPENBSD +# define SYSTEM_OPENBSD 1 +# endif +# elif defined( __EMSCRIPTEN__ ) +# ifndef SYSTEM_EMSCRIPTEN +# define SYSTEM_EMSCRIPTEN 1 +# endif +# elif defined( __CYGWIN__ ) +# ifndef SYSTEM_CYGWIN +# define SYSTEM_CYGWIN 1 +# endif +# else +# error This UNIX operating system is not supported +# endif +#else +# error This operating system is not supported +#endif + +/* Platform compiler */ + +#if defined( _MSC_VER ) +# define COMPILER_MSVC 1 +#elif defined( __GNUC__ ) +# define COMPILER_GCC 1 +#elif defined( __clang__ ) +# define COMPILER_CLANG 1 +#elif defined( __MINGW32__ ) +# define COMPILER_MINGW 1 +# error Unknown compiler +#endif + +#if defined( __has_attribute ) +# define HAS_ATTRIBUTE( attribute ) __has_attribute( attribute ) +#else +# define HAS_ATTRIBUTE( attribute ) ( 0 ) +#endif + +#if defined(GCC_VERSION_CHECK) +# undef GCC_VERSION_CHECK +#endif +#if defined(GCC_VERSION) +# define GCC_VERSION_CHECK(major,minor,patch) (GCC_VERSION >= VERSION_ENCODE(major, minor, patch)) +#else +# define GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#pragma endregion Platform Detection + +#pragma region Mandatory Includes + +# include +# include + +# if defined( SYSTEM_WINDOWS ) +# include +# endif + +#pragma endregion Mandatory Includes diff --git a/source/base/pop_ignores.inline.h b/source/base/pop_ignores.inline.h new file mode 100644 index 0000000..ab6a57f --- /dev/null +++ b/source/base/pop_ignores.inline.h @@ -0,0 +1,7 @@ +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif diff --git a/source/base/push_ignores.inline.h b/source/base/push_ignores.inline.h new file mode 100644 index 0000000..a6b2df8 --- /dev/null +++ b/source/base/push_ignores.inline.h @@ -0,0 +1,18 @@ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-const-variable" +# pragma clang diagnostic ignored "-Wunused-but-set-variable" +# pragma clang diagnostic ignored "-Wswitch" +# pragma clang diagnostic ignored "-Wunused-variable" +# pragma clang diagnostic ignored "-Wunknown-pragmas" +# pragma clang diagnostic ignored "-Wvarargs" +# pragma clang diagnostic ignored "-Wunused-function" +#endif + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunknown-pragmas" +# pragma GCC diagnostic ignored "-Wcomment" +# pragma GCC diagnostic ignored "-Wswitch" +# pragma GCC diagnostic ignored "-Wunused-variable" +#endif diff --git a/source/copy_hello.asm b/source/execution/launch.c similarity index 100% rename from source/copy_hello.asm rename to source/execution/launch.c diff --git a/source/gen_asm.c b/source/gen_asm.c new file mode 100644 index 0000000..564ea76 --- /dev/null +++ b/source/gen_asm.c @@ -0,0 +1,9 @@ +#include "base/push_ignores.inline.h" + +#include "base/platform.h" +#include "base/macros.h" +#include "base/basic_types.h" +#include "base/debug.h" +#include "base/memory.h" + +#include "base/pop_ignores.inline.h" diff --git a/source/hello.asm b/source_asm/copy_hello.asm similarity index 100% rename from source/hello.asm rename to source_asm/copy_hello.asm diff --git a/source_asm/hello.asm b/source_asm/hello.asm new file mode 100644 index 0000000..b6cebb5 --- /dev/null +++ b/source_asm/hello.asm @@ -0,0 +1,43 @@ +; hello.asm - Hello World with debug symbols for NASM +BITS 64 ; Explicitly specify 64-bit mode +DEFAULT REL ; Use RIP-relative addressing by default + +; Data section +section .data + message db "Hello, World!", 13, 10, 0 ; String with CRLF and null terminator + message_len equ $ - message ; Calculate string length + +; Code section +section .text +global main ; Export main symbol for linker + +extern ExitProcess ; Import Windows API functions +extern GetStdHandle +extern WriteConsoleA + +main: + ; Function prologue + push rbp + mov rbp, rsp + sub rsp, 32 ; Shadow space for Windows API calls + + ; Get stdout handle + mov ecx, -11 ; STD_OUTPUT_HANDLE + call GetStdHandle + mov rbx, rax ; Save handle for WriteConsole + + ; Write message + mov rcx, rbx ; Console handle + lea rdx, [message] ; Message buffer + mov r8d, message_len ; Message length + lea r9, [rsp+28] ; Written chars (unused) + mov qword [rsp+20], 0 ; Reserved (must be 0) + call WriteConsoleA + + ; Exit program + xor ecx, ecx ; Exit code 0 + call ExitProcess + + ; Function epilogue (not reached due to ExitProcess) + leave + ret \ No newline at end of file diff --git a/toolchain/gencpp/gen.hpp b/toolchain/gencpp/gen.hpp new file mode 100644 index 0000000..9656dc8 --- /dev/null +++ b/toolchain/gencpp/gen.hpp @@ -0,0 +1,26283 @@ +// This file was generated automatially by gencpp's singleheader.cpp(See: https://github.com/Ed94/gencpp) + +#pragma once + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-const-variable" +# pragma clang diagnostic ignored "-Wunused-but-set-variable" +# pragma clang diagnostic ignored "-Wswitch" +# pragma clang diagnostic ignored "-Wunused-variable" +# pragma clang diagnostic ignored "-Wunknown-pragmas" +# pragma clang diagnostic ignored "-Wvarargs" +# pragma clang diagnostic ignored "-Wunused-function" +#endif + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunknown-pragmas" +# pragma GCC diagnostic ignored "-Wcomment" +# pragma GCC diagnostic ignored "-Wswitch" +# pragma GCC diagnostic ignored "-Wunused-variable" +#endif +/* + gencpp: An attempt at "simple" staged metaprogramming for c/c++. + + See Readme.md for more information from the project repository. + + Public Address: + https://github.com/Ed94/gencpp + + This is a single header variant of the library. + Define GEN_IMPLEMENTATION before including this file in a single compilation unit. +*/ +#if ! defined(GEN_DONT_ENFORCE_GEN_TIME_GUARD) && ! defined(GEN_TIME) +# error Gen.hpp : GEN_TIME not defined +#endif + +#ifdef GEN_DONT_USE_NAMESPACE +# define GEN_NS_BEGIN +# define GEN_NS_END +#else +# define GEN_NS_BEGIN namespace gen { +# define GEN_NS_END } +#endif + + +//! If its desired to roll your own dependencies, define GEN_ROLL_OWN_DEPENDENCIES before including this file. +// Dependencies are derived from the c-zpl library: https://github.com/zpl-c/zpl +#ifndef GEN_ROLL_OWN_DEPENDENCIES + +#pragma region Platform Detection + +/* Platform architecture */ + +#if defined( _WIN64 ) || defined( __x86_64__ ) || defined( _M_X64 ) || defined( __64BIT__ ) || defined( __powerpc64__ ) || defined( __ppc64__ ) || defined( __aarch64__ ) +# ifndef GEN_ARCH_64_BIT +# define GEN_ARCH_64_BIT 1 +# endif +#else +# ifndef GEN_ARCH_32_BItxt_StrCaT +# define GEN_ARCH_32_BIT 1 +# endif +#endif + +/* Platform OS */ + +#if defined( _WIN32 ) || defined( _WIN64 ) +# ifndef GEN_SYSTEM_WINDOWS +# define GEN_SYSTEM_WINDOWS 1 +# endif +#elif defined( __APPLE__ ) && defined( __MACH__ ) +# ifndef GEN_SYSTEM_OSX +# define GEN_SYSTEM_OSX 1 +# endif +# ifndef GEN_SYSTEM_MACOS +# define GEN_SYSTEM_MACOS 1 +# endif +# include +# if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1 +# ifndef GEN_SYSTEM_IOS +# define GEN_SYSTEM_IOS 1 +# endif +# endif +#elif defined( __unix__ ) +# ifndef GEN_SYSTEM_UNIX +# define GEN_SYSTEM_UNIX 1 +# endif +# if defined( ANDROID ) || defined( __ANDROID__ ) +# ifndef GEN_SYSTEM_ANDROID +# define GEN_SYSTEM_ANDROID 1 +# endif +# ifndef GEN_SYSTEM_LINUX +# define GEN_SYSTEM_LINUX 1 +# endif +# elif defined( __linux__ ) +# ifndef GEN_SYSTEM_LINUX +# define GEN_SYSTEM_LINUX 1 +# endif +# elif defined( __FreeBSD__ ) || defined( __FreeBSD_kernel__ ) +# ifndef GEN_SYSTEM_FREEBSD +# define GEN_SYSTEM_FREEBSD 1 +# endif +# elif defined( __OpenBSD__ ) +# ifndef GEN_SYSTEM_OPENBSD +# define GEN_SYSTEM_OPENBSD 1 +# endif +# elif defined( __EMSCRIPTEN__ ) +# ifndef GEN_SYSTEM_EMSCRIPTEN +# define GEN_SYSTEM_EMSCRIPTEN 1 +# endif +# elif defined( __CYGWIN__ ) +# ifndef GEN_SYSTEM_CYGWIN +# define GEN_SYSTEM_CYGWIN 1 +# endif +# else +# error This UNIX operating system is not supported +# endif +#else +# error This operating system is not supported +#endif + +/* Platform compiler */ + +#if defined( _MSC_VER ) +# define GEN_COMPILER_MSVC 1 +#elif defined( __GNUC__ ) +# define GEN_COMPILER_GCC 1 +#elif defined( __clang__ ) +# define GEN_COMPILER_CLANG 1 +#elif defined( __MINGW32__ ) +# define GEN_COMPILER_MINGW 1 +# error Unknown compiler +#endif + +#if defined( __has_attribute ) +# define GEN_HAS_ATTRIBUTE( attribute ) __has_attribute( attribute ) +#else +# define GEN_HAS_ATTRIBUTE( attribute ) ( 0 ) +#endif + +#if defined(GEN_GCC_VERSION_CHECK) +# undef GEN_GCC_VERSION_CHECK +#endif +#if defined(GEN_GCC_VERSION) +# define GEN_GCC_VERSION_CHECK(major,minor,patch) (GEN_GCC_VERSION >= GEN_VERSION_ENCODE(major, minor, patch)) +#else +# define GEN_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#pragma endregion Platform Detection + +#pragma region Mandatory Includes + +# include +# include + +# if defined( GEN_SYSTEM_WINDOWS ) +# include +# endif + +#pragma endregion Mandatory Includes + +#ifdef GEN_DONT_USE_NAMESPACE +# define GEN_NS +# define GEN_NS_BEGIN +# define GEN_NS_END +#else +# define GEN_NS gen:: +# define GEN_NS_BEGIN namespace gen { +# define GEN_NS_END } +#endif + +GEN_NS_BEGIN + +#pragma region Macros + +#ifndef global +#define global static // Global variables +#endif +#ifndef internal +#define internal static // Internal linkage +#endif +#ifndef local_persist +#define local_persist static // Local Persisting variables +#endif + +#ifndef api_c +#define api_c extern "C" +#endif + +#ifndef bit +#define bit( Value ) ( 1 << Value ) +#define bitfield_is_equal( Type, Field, Mask ) ( (Type(Mask) & Type(Field)) == Type(Mask) ) +#endif + +#ifndef ccast +#define ccast( type, value ) ( const_cast< type >( (value) ) ) +#endif +#ifndef pcast +#define pcast( type, value ) ( * reinterpret_cast< type* >( & ( value ) ) ) +#endif +#ifndef rcast +#define rcast( type, value ) reinterpret_cast< type >( value ) +#endif +#ifndef scast +#define scast( type, value ) static_cast< type >( value ) +#endif + +#ifndef stringize +#define stringize_va( ... ) #__VA_ARGS__ +#define stringize( ... ) stringize_va( __VA_ARGS__ ) +#endif + +#ifndef do_once +#define do_once( statement ) for ( local_persist b32 once = true; once; once = false, (statement) ) + +#define do_once_start \ + do \ + { \ + local_persist \ + bool done = false; \ + if ( done ) \ + break; \ + done = true; + +#define do_once_end \ + } \ + while(0); +#endif + +#ifndef labeled_scope_start +#define labeled_scope_start if ( false ) { +#define labeled_scope_end } +#endif + +#ifndef compiler_decorated_func_name +# ifdef COMPILER_CLANG +# define compiler_decorated_func_name __PRETTY_NAME__ +# elif defined(COMPILER_MSVC) +# define compiler_decorated_func_name __FUNCDNAME__ +# endif +#endif + +#ifndef num_args_impl +#define num_args_impl( _0, \ + _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ + _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, \ + _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \ + _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, \ + _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, \ + _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, \ + _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, \ + _71, _72, _73, _74, _75, _76, _77, _78, _79, _80, \ + _81, _82, _83, _84, _85, _86, _87, _88, _89, _90, \ + _91, _92, _93, _94, _95, _96, _97, _98, _99, _100, \ + N, ... \ + ) N + +// ## deletes preceding comma if _VA_ARGS__ is empty (GCC, Clang) +#define num_args(...) \ + num_args_impl(_, ## __VA_ARGS__, \ + 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, \ + 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, \ + 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, \ + 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, \ + 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, \ + 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, \ + 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, \ + 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, \ + 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, \ + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, \ + 0 \ + ) +#endif + +#ifndef clamp +#define clamp( x, lower, upper ) min( max( ( x ), ( lower ) ), ( upper ) ) +#endif +#ifndef count_of +#define count_of( x ) ( ( size_of( x ) / size_of( 0 [ x ] ) ) / ( ( ssize )( ! ( size_of( x ) % size_of( 0 [ x ] ) ) ) ) ) +#endif +#ifndef is_between +#define is_between( x, lower, upper ) ( ( ( lower ) <= ( x ) ) && ( ( x ) <= ( upper ) ) ) +#endif +#ifndef size_of +#define size_of( x ) ( ssize )( sizeof( x ) ) +#endif + +#ifndef max +#define max( a, b ) ( (a > b) ? (a) : (b) ) +#endif +#ifndef min +#define min( a, b ) ( (a < b) ? (a) : (b) ) +#endif + +#if defined( _MSC_VER ) || defined( GEN_COMPILER_TINYC ) +# define offset_of( Type, element ) ( ( GEN_NS( ssize ) ) & ( ( ( Type* )0 )->element ) ) +#else +# define offset_of( Type, element ) __builtin_offsetof( Type, element ) +#endif + +#ifndef forceinline +# ifdef GEN_COMPILER_MSVC +# define forceinline __forceinline +# define neverinline __declspec( noinline ) +# elif defined(GEN_COMPILER_GCC) +# define forceinline inline __attribute__((__always_inline__)) +# define neverinline __attribute__( ( __noinline__ ) ) +# elif defined(GEN_COMPILER_CLANG) +# if __has_attribute(__always_inline__) +# define forceinline inline __attribute__((__always_inline__)) +# define neverinline __attribute__( ( __noinline__ ) ) +# else +# define forceinline +# define neverinline +# endif +# else +# define forceinline +# define neverinline +# endif +#endif + +#ifndef neverinline +# ifdef GEN_COMPILER_MSVC +# define neverinline __declspec( noinline ) +# elif defined(GEN_COMPILER_GCC) +# define neverinline __attribute__( ( __noinline__ ) ) +# elif defined(GEN_COMPILER_CLANG) +# if __has_attribute(__always_inline__) +# define neverinline __attribute__( ( __noinline__ ) ) +# else +# define neverinline +# endif +# else +# define neverinline +# endif +#endif + +#pragma endregion Macros + +#pragma region Basic Types + +#define GEN_U8_MIN 0u +#define GEN_U8_MAX 0xffu +#define GEN_I8_MIN ( -0x7f - 1 ) +#define GEN_I8_MAX 0x7f + +#define GEN_U16_MIN 0u +#define GEN_U16_MAX 0xffffu +#define GEN_I16_MIN ( -0x7fff - 1 ) +#define GEN_I16_MAX 0x7fff + +#define GEN_U32_MIN 0u +#define GEN_U32_MAX 0xffffffffu +#define GEN_I32_MIN ( -0x7fffffff - 1 ) +#define GEN_I32_MAX 0x7fffffff + +#define GEN_U64_MIN 0ull +#define GEN_U64_MAX 0xffffffffffffffffull +#define GEN_I64_MIN ( -0x7fffffffffffffffll - 1 ) +#define GEN_I64_MAX 0x7fffffffffffffffll + +#if defined( GEN_ARCH_32_BIT ) +# define GEN_USIZE_MIN GEN_U32_MIN +# define GEN_USIZE_MAX GEN_U32_MAX +# define GEN_ISIZE_MIN GEN_S32_MIN +# define GEN_ISIZE_MAX GEN_S32_MAX +#elif defined( GEN_ARCH_64_BIT ) +# define GEN_USIZE_MIN GEN_U64_MIN +# define GEN_USIZE_MAX GEN_U64_MAX +# define GEN_ISIZE_MIN GEN_I64_MIN +# define GEN_ISIZE_MAX GEN_I64_MAX +#else +# error Unknown architecture size. This library only supports 32 bit and 64 bit architectures. +#endif + +#define GEN_F32_MIN 1.17549435e-38f +#define GEN_F32_MAX 3.40282347e+38f +#define GEN_F64_MIN 2.2250738585072014e-308 +#define GEN_F64_MAX 1.7976931348623157e+308 + +#if defined( GEN_COMPILER_MSVC ) +# if _MSC_VER < 1300 +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned int u32; +typedef signed int s32; +# else +typedef unsigned __int8 u8; +typedef signed __int8 s8; +typedef unsigned __int16 u16; +typedef signed __int16 s16; +typedef unsigned __int32 u32; +typedef signed __int32 s32; +# endif +typedef unsigned __int64 u64; +typedef signed __int64 s64; +#else +# include + +typedef uint8_t u8; +typedef int8_t s8; +typedef uint16_t u16; +typedef int16_t s16; +typedef uint32_t u32; +typedef int32_t s32; +typedef uint64_t u64; +typedef int64_t s64; +#endif + +static_assert( sizeof( u8 ) == sizeof( s8 ), "sizeof(u8) != sizeof(s8)" ); +static_assert( sizeof( u16 ) == sizeof( s16 ), "sizeof(u16) != sizeof(s16)" ); +static_assert( sizeof( u32 ) == sizeof( s32 ), "sizeof(u32) != sizeof(s32)" ); +static_assert( sizeof( u64 ) == sizeof( s64 ), "sizeof(u64) != sizeof(s64)" ); + +static_assert( sizeof( u8 ) == 1, "sizeof(u8) != 1" ); +static_assert( sizeof( u16 ) == 2, "sizeof(u16) != 2" ); +static_assert( sizeof( u32 ) == 4, "sizeof(u32) != 4" ); +static_assert( sizeof( u64 ) == 8, "sizeof(u64) != 8" ); + +typedef size_t usize; +typedef ptrdiff_t ssize; + +static_assert( sizeof( usize ) == sizeof( ssize ), "sizeof(usize) != sizeof(ssize)" ); + +// NOTE: (u)zpl_intptr is only here for semantic reasons really as this library will only support 32/64 bit OSes. +#if defined( _WIN64 ) +typedef signed __int64 sptr; +typedef unsigned __int64 uptr; +#elif defined( _WIN32 ) +// NOTE; To mark types changing their size, e.g. zpl_intptr +# ifndef _W64 +# if ! defined( __midl ) && ( defined( _X86_ ) || defined( _M_IX86 ) ) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +# endif +typedef _W64 signed int sptr; +typedef _W64 unsigned int uptr; +#else +typedef uintptr_t uptr; +typedef intptr_t sptr; +#endif + +static_assert( sizeof( uptr ) == sizeof( sptr ), "sizeof(uptr) != sizeof(sptr)" ); + +typedef float f32; +typedef double f64; + +static_assert( sizeof( f32 ) == 4, "sizeof(f32) != 4" ); +static_assert( sizeof( f64 ) == 8, "sizeof(f64) != 8" ); + +typedef s8 b8; +typedef s16 b16; +typedef s32 b32; + +using mem_ptr = void*; +using mem_ptr_const = void const*; + +template uptr to_uptr( Type* ptr ) { return (uptr)ptr; } +template sptr to_sptr( Type* ptr ) { return (sptr)ptr; } + +template mem_ptr to_mem_ptr ( Type ptr ) { return (mem_ptr) ptr; } +template mem_ptr_const to_mem_ptr_const( Type ptr ) { return (mem_ptr_const)ptr; } + +#pragma endregion Basic Types + +#pragma region Debug + +#if defined( _MSC_VER ) +# if _MSC_VER < 1300 +# define GEN_DEBUG_TRAP() __asm int 3 /* Trap to debugger! */ +# else +# define GEN_DEBUG_TRAP() __debugbreak() +# endif +#elif defined( GEN_COMPILER_TINYC ) +# define GEN_DEBUG_TRAP() process_exit( 1 ) +#else +# define GEN_DEBUG_TRAP() __builtin_trap() +#endif + +#define GEN_ASSERT( cond ) GEN_ASSERT_MSG( cond, NULL ) + +#define GEN_ASSERT_MSG( cond, msg, ... ) \ + do \ + { \ + if ( ! ( cond ) ) \ + { \ + assert_handler( #cond, __FILE__, scast( s64, __LINE__ ), msg, ##__VA_ARGS__ ); \ + GEN_DEBUG_TRAP(); \ + } \ + } while ( 0 ) + +#define GEN_ASSERT_NOT_NULL( ptr ) GEN_ASSERT_MSG( ( ptr ) != NULL, #ptr " must not be NULL" ) + +// NOTE: Things that shouldn't happen with a message! +#define GEN_PANIC( msg, ... ) GEN_ASSERT_MSG( 0, msg, ##__VA_ARGS__ ) + +#if Build_Debug + #define GEN_FATAL( ... ) \ + do \ + { \ + local_persist thread_local \ + char buf[GEN_PRINTF_MAXLEN] = { 0 }; \ + \ + str_fmt(buf, GEN_PRINTF_MAXLEN, __VA_ARGS__); \ + GEN_PANIC(buf); \ + } \ + while (0) +#else + +# define GEN_FATAL( ... ) \ + do \ + { \ + str_fmt_out_err( __VA_ARGS__ ); \ + process_exit(1); \ + } \ + while (0) +#endif + +void assert_handler( char const* condition, char const* file, s32 line, char const* msg, ... ); +s32 assert_crash( char const* condition ); +void process_exit( u32 code ); + +#pragma endregion Debug + +#pragma region Memory + +#define kilobytes( x ) ( ( x ) * ( s64 )( 1024 ) ) +#define megabytes( x ) ( kilobytes( x ) * ( s64 )( 1024 ) ) +#define gigabytes( x ) ( megabytes( x ) * ( s64 )( 1024 ) ) +#define terabytes( x ) ( gigabytes( x ) * ( s64 )( 1024 ) ) + +#define GEN__ONES ( scast( GEN_NS usize, - 1) / GEN_U8_MAX ) +#define GEN__HIGHS ( GEN__ONES * ( GEN_U8_MAX / 2 + 1 ) ) +#define GEN__HAS_ZERO( x ) ( ( ( x ) - GEN__ONES ) & ~( x ) & GEN__HIGHS ) + +template< class Type > +void swap( Type& a, Type& b ) +{ + Type tmp = a; + a = b; + b = tmp; +} + +//! Checks if value is power of 2. +b32 is_power_of_two( ssize x ); + +//! Aligns address to specified alignment. +void* align_forward( void* ptr, ssize alignment ); + +//! Aligns value to a specified alignment. +s64 align_forward_i64( s64 value, ssize alignment ); + +//! Moves pointer forward by bytes. +void* pointer_add( void* ptr, ssize bytes ); + +//! Moves pointer forward by bytes. +void const* pointer_add_const( void const* ptr, ssize bytes ); + +//! Calculates difference between two addresses. +ssize pointer_diff( void const* begin, void const* end ); + +//! Copy non-overlapping memory from source to destination. +void* mem_copy( void* dest, void const* source, ssize size ); + +//! Search for a constant value within the size limit at memory location. +void const* mem_find( void const* data, u8 byte_value, ssize size ); + +//! Copy memory from source to destination. +void* mem_move( void* dest, void const* source, ssize size ); + +//! Set constant value at memory location with specified size. +void* mem_set( void* data, u8 byte_value, ssize size ); + +//! @param ptr Memory location to clear up. +//! @param size The size to clear up with. +void zero_size( void* ptr, ssize size ); + +//! Clears up an item. +#define zero_item( t ) zero_size( ( t ), size_of( *( t ) ) ) // NOTE: Pass pointer of struct + +//! Clears up an array. +#define zero_array( a, count ) zero_size( ( a ), size_of( *( a ) ) * count ) + +enum AllocType : u8 +{ + EAllocation_ALLOC, + EAllocation_FREE, + EAllocation_FREE_ALL, + EAllocation_RESIZE, +}; + +using AllocatorProc = void* ( void* allocator_data, AllocType type + , ssize size, ssize alignment + , void* old_memory, ssize old_size + , u64 flags ); + +struct AllocatorInfo +{ + AllocatorProc* Proc; + void* Data; +}; + +enum AllocFlag +{ + ALLOCATOR_FLAG_CLEAR_TO_ZERO = bit( 0 ), +}; + +#ifndef GEN_DEFAULT_MEMORY_ALIGNMENT +# define GEN_DEFAULT_MEMORY_ALIGNMENT ( 2 * size_of( void* ) ) +#endif + +#ifndef GEN_DEFAULT_ALLOCATOR_FLAGS +# define GEN_DEFAULT_ALLOCATOR_FLAGS ( ALLOCATOR_FLAG_CLEAR_TO_ZERO ) +#endif + +//! Allocate memory with default alignment. +void* alloc( AllocatorInfo a, ssize size ); + +//! Allocate memory with specified alignment. +void* alloc_align( AllocatorInfo a, ssize size, ssize alignment ); + +//! Free allocated memory. +void free( AllocatorInfo a, void* ptr ); + +//! Free all memory allocated by an allocator. +void free_all( AllocatorInfo a ); + +//! Resize an allocated memory. +void* resize( AllocatorInfo a, void* ptr, ssize old_size, ssize new_size ); + +//! Resize an allocated memory with specified alignment. +void* resize_align( AllocatorInfo a, void* ptr, ssize old_size, ssize new_size, ssize alignment ); + +//! Allocate memory for an item. +#define alloc_item( allocator_, Type ) ( Type* )alloc( allocator_, size_of( Type ) ) + +//! Allocate memory for an array of items. +#define alloc_array( allocator_, Type, count ) ( Type* )alloc( allocator_, size_of( Type ) * ( count ) ) + +/* heap memory analysis tools */ +/* define GEN_HEAP_ANALYSIS to enable this feature */ +/* call zpl_heap_stats_init at the beginning of the entry point */ +/* you can call zpl_heap_stats_check near the end of the execution to validate any possible leaks */ +void heap_stats_init( void ); +ssize heap_stats_used_memory( void ); +ssize heap_stats_alloc_count( void ); +void heap_stats_check( void ); + +//! Allocate/Resize memory using default options. + +//! Use this if you don't need a "fancy" resize allocation +void* default_resize_align( AllocatorInfo a, void* ptr, ssize old_size, ssize new_size, ssize alignment ); + +void* heap_allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ); + +//! The heap allocator backed by operating system's memory manager. +constexpr AllocatorInfo heap( void ) { return { heap_allocator_proc, nullptr }; } + +//! Helper to allocate memory using heap allocator. +#define malloc( sz ) alloc( heap(), sz ) + +//! Helper to free memory allocated by heap allocator. +#define mfree( ptr ) free( heap(), ptr ) + +struct VirtualMemory +{ + void* data; + ssize size; +}; + +//! Initialize virtual memory from existing data. +VirtualMemory vm_from_memory( void* data, ssize size ); + +//! Allocate virtual memory at address with size. + +//! @param addr The starting address of the region to reserve. If NULL, it lets operating system to decide where to allocate it. +//! @param size The size to serve. +VirtualMemory vm_alloc( void* addr, ssize size ); + +//! Release the virtual memory. +b32 vm_free( VirtualMemory vm ); + +//! Trim virtual memory. +VirtualMemory vm_trim( VirtualMemory vm, ssize lead_size, ssize size ); + +//! Purge virtual memory. +b32 gen_vm_purge( VirtualMemory vm ); + +//! Retrieve VM's page size and alignment. +ssize gen_virtual_memory_page_size( ssize* alignment_out ); + +struct Arena +{ + static + void* allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ); + + static + Arena init_from_memory( void* start, ssize size ) + { + return + { + { nullptr, nullptr }, + start, + size, + 0, + 0 + }; + } + + static + Arena init_from_allocator( AllocatorInfo backing, ssize size ) + { + Arena result = + { + backing, + alloc( backing, size), + size, + 0, + 0 + }; + return result; + } + + static + Arena init_sub( Arena& parent, ssize size ) + { + return init_from_allocator( parent.Backing, size ); + } + + ssize alignment_of( ssize alignment ) + { + ssize alignment_offset, result_pointer, mask; + GEN_ASSERT( is_power_of_two( alignment ) ); + + alignment_offset = 0; + result_pointer = (ssize) PhysicalStart + TotalUsed; + mask = alignment - 1; + + if ( result_pointer & mask ) + alignment_offset = alignment - ( result_pointer & mask ); + + return alignment_offset; + } + +// This id is defined by Unreal for asserts +#pragma push_macro("check") +#undef check + void check() + { + GEN_ASSERT( TempCount == 0 ); + } +#pragma pop_macro("check") + + void free() + { + if ( Backing.Proc ) + { + gen::free( Backing, PhysicalStart ); + PhysicalStart = nullptr; + } + } + + ssize size_remaining( ssize alignment ) + { + ssize result = TotalSize - ( TotalUsed + alignment_of( alignment ) ); + return result; + } + + AllocatorInfo Backing; + void* PhysicalStart; + ssize TotalSize; + ssize TotalUsed; + ssize TempCount; + + operator AllocatorInfo() + { + return { allocator_proc, this }; + } +}; + +// Just a wrapper around using an arena with memory associated with its scope instead of from an allocator. +// Used for static segment or stack allocations. +template< s32 Size > +struct FixedArena +{ + static + FixedArena init() + { + FixedArena result = { Arena::init_from_memory( result.memory, Size ), {0} }; + return result; + } + + ssize size_remaining( ssize alignment ) + { + return arena.size_remaining( alignment ); + } + + operator AllocatorInfo() + { + return { Arena::allocator_proc, &arena }; + } + + Arena arena; + char memory[ Size ]; +}; + +using Arena_1KB = FixedArena< kilobytes( 1 ) >; +using Arena_4KB = FixedArena< kilobytes( 4 ) >; +using Arena_8KB = FixedArena< kilobytes( 8 ) >; +using Arena_16KB = FixedArena< kilobytes( 16 ) >; +using Arena_32KB = FixedArena< kilobytes( 32 ) >; +using Arena_64KB = FixedArena< kilobytes( 64 ) >; +using Arena_128KB = FixedArena< kilobytes( 128 ) >; +using Arena_256KB = FixedArena< kilobytes( 256 ) >; +using Arena_512KB = FixedArena< kilobytes( 512 ) >; +using Arena_1MB = FixedArena< megabytes( 1 ) >; +using Arena_2MB = FixedArena< megabytes( 2 ) >; +using Arena_4MB = FixedArena< megabytes( 4 ) >; + +struct Pool +{ + static + void* allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ); + + static + Pool init( AllocatorInfo backing, ssize num_blocks, ssize block_size ) + { + return init_align( backing, num_blocks, block_size, GEN_DEFAULT_MEMORY_ALIGNMENT ); + } + + static + Pool init_align( AllocatorInfo backing, ssize num_blocks, ssize block_size, ssize block_align ); + + void clear(); + + void free() + { + if ( Backing.Proc ) + { + gen::free( Backing, PhysicalStart ); + } + } + + AllocatorInfo Backing; + void* PhysicalStart; + void* FreeList; + ssize BlockSize; + ssize BlockAlign; + ssize TotalSize; + ssize NumBlocks; + + operator AllocatorInfo() + { + return { allocator_proc, this }; + } +}; + + +inline +b32 is_power_of_two( ssize x ) { + if ( x <= 0 ) + return false; + return ! ( x & ( x - 1 ) ); +} + +inline +mem_ptr align_forward( void* ptr, ssize alignment ) +{ + GEN_ASSERT( is_power_of_two( alignment ) ); + uptr p = to_uptr(ptr); + uptr forward = (p + ( alignment - 1 ) ) & ~( alignment - 1 ); + + return to_mem_ptr(forward); +} + +inline s64 align_forward_i64( s64 value, ssize alignment ) { return value + ( alignment - value % alignment ) % alignment; } + +inline void* pointer_add ( void* ptr, ssize bytes ) { return rcast(void*, rcast( u8*, ptr) + bytes ); } +inline void const* pointer_add_const( void const* ptr, ssize bytes ) { return rcast(void const*, rcast( u8 const*, ptr) + bytes ); } + +inline sptr pointer_diff( mem_ptr_const begin, mem_ptr_const end ) { + return scast( ssize, rcast( u8 const*, end) - rcast(u8 const*, begin) ); +} + +inline +void* mem_move( void* destination, void const* source, ssize byte_count ) +{ + if ( destination == NULL ) + { + return NULL; + } + + u8* dest_ptr = rcast( u8*, destination); + u8 const* src_ptr = rcast( u8 const*, source); + + if ( dest_ptr == src_ptr ) + return dest_ptr; + + if ( src_ptr + byte_count <= dest_ptr || dest_ptr + byte_count <= src_ptr ) // NOTE: Non-overlapping + return mem_copy( dest_ptr, src_ptr, byte_count ); + + if ( dest_ptr < src_ptr ) + { + if ( to_uptr(src_ptr) % size_of( ssize ) == to_uptr(dest_ptr) % size_of( ssize ) ) + { + while ( pcast( uptr, dest_ptr) % size_of( ssize ) ) + { + if ( ! byte_count-- ) + return destination; + + *dest_ptr++ = *src_ptr++; + } + while ( byte_count >= size_of( ssize ) ) + { + * rcast(ssize*, dest_ptr) = * rcast(ssize const*, src_ptr); + byte_count -= size_of( ssize ); + dest_ptr += size_of( ssize ); + src_ptr += size_of( ssize ); + } + } + for ( ; byte_count; byte_count-- ) + *dest_ptr++ = *src_ptr++; + } + else + { + if ( ( to_uptr(src_ptr) % size_of( ssize ) ) == ( to_uptr(dest_ptr) % size_of( ssize ) ) ) + { + while ( to_uptr( dest_ptr + byte_count ) % size_of( ssize ) ) + { + if ( ! byte_count-- ) + return destination; + + dest_ptr[ byte_count ] = src_ptr[ byte_count ]; + } + while ( byte_count >= size_of( ssize ) ) + { + byte_count -= size_of( ssize ); + * rcast(ssize*, dest_ptr + byte_count ) = * rcast( ssize const*, src_ptr + byte_count ); + } + } + while ( byte_count ) + byte_count--, dest_ptr[ byte_count ] = src_ptr[ byte_count ]; + } + + return destination; +} + +inline +void* mem_set( void* destination, u8 fill_byte, ssize byte_count ) +{ + if ( destination == NULL ) + { + return NULL; + } + + ssize align_offset; + u8* dest_ptr = rcast( u8*, destination); + u32 fill_word = ( ( u32 )-1 ) / 255 * fill_byte; + + if ( byte_count == 0 ) + return destination; + + dest_ptr[ 0 ] = dest_ptr[ byte_count - 1 ] = fill_byte; + if ( byte_count < 3 ) + return destination; + + dest_ptr[ 1 ] = dest_ptr[ byte_count - 2 ] = fill_byte; + dest_ptr[ 2 ] = dest_ptr[ byte_count - 3 ] = fill_byte; + if ( byte_count < 7 ) + return destination; + + dest_ptr[ 3 ] = dest_ptr[ byte_count - 4 ] = fill_byte; + if ( byte_count < 9 ) + return destination; + + align_offset = -to_sptr( dest_ptr ) & 3; + dest_ptr += align_offset; + byte_count -= align_offset; + byte_count &= -4; + + * rcast( u32*, ( dest_ptr + 0 ) ) = fill_word; + * rcast( u32*, ( dest_ptr + byte_count - 4 ) ) = fill_word; + if ( byte_count < 9 ) + return destination; + + * rcast( u32*, dest_ptr + 4 ) = fill_word; + * rcast( u32*, dest_ptr + 8 ) = fill_word; + * rcast( u32*, dest_ptr + byte_count - 12 ) = fill_word; + * rcast( u32*, dest_ptr + byte_count - 8 ) = fill_word; + if ( byte_count < 25 ) + return destination; + + * rcast( u32*, dest_ptr + 12 ) = fill_word; + * rcast( u32*, dest_ptr + 16 ) = fill_word; + * rcast( u32*, dest_ptr + 20 ) = fill_word; + * rcast( u32*, dest_ptr + 24 ) = fill_word; + * rcast( u32*, dest_ptr + byte_count - 28 ) = fill_word; + * rcast( u32*, dest_ptr + byte_count - 24 ) = fill_word; + * rcast( u32*, dest_ptr + byte_count - 20 ) = fill_word; + * rcast( u32*, dest_ptr + byte_count - 16 ) = fill_word; + + align_offset = 24 + to_uptr( dest_ptr ) & 4; + dest_ptr += align_offset; + byte_count -= align_offset; + + { + u64 fill_doubleword = ( scast( u64, fill_word) << 32 ) | fill_word; + while ( byte_count > 31 ) + { + * rcast( u64*, dest_ptr + 0 ) = fill_doubleword; + * rcast( u64*, dest_ptr + 8 ) = fill_doubleword; + * rcast( u64*, dest_ptr + 16 ) = fill_doubleword; + * rcast( u64*, dest_ptr + 24 ) = fill_doubleword; + + byte_count -= 32; + dest_ptr += 32; + } + } + + return destination; +} + +inline +void* alloc_align( AllocatorInfo a, ssize size, ssize alignment ) { + return a.Proc( a.Data, EAllocation_ALLOC, size, alignment, nullptr, 0, GEN_DEFAULT_ALLOCATOR_FLAGS ); +} + +inline +void* alloc( AllocatorInfo a, ssize size ) { + return alloc_align( a, size, GEN_DEFAULT_MEMORY_ALIGNMENT ); +} + +inline +void free( AllocatorInfo a, void* ptr ) { + if ( ptr != nullptr ) + a.Proc( a.Data, EAllocation_FREE, 0, 0, ptr, 0, GEN_DEFAULT_ALLOCATOR_FLAGS ); +} + +inline +void free_all( AllocatorInfo a ) { + a.Proc( a.Data, EAllocation_FREE_ALL, 0, 0, nullptr, 0, GEN_DEFAULT_ALLOCATOR_FLAGS ); +} + +inline +void* resize( AllocatorInfo a, void* ptr, ssize old_size, ssize new_size ) { + return resize_align( a, ptr, old_size, new_size, GEN_DEFAULT_MEMORY_ALIGNMENT ); +} + +inline +void* resize_align( AllocatorInfo a, void* ptr, ssize old_size, ssize new_size, ssize alignment ) { + return a.Proc( a.Data, EAllocation_RESIZE, new_size, alignment, ptr, old_size, GEN_DEFAULT_ALLOCATOR_FLAGS ); +} + +inline +void* default_resize_align( AllocatorInfo a, void* old_memory, ssize old_size, ssize new_size, ssize alignment ) +{ + if ( ! old_memory ) + return alloc_align( a, new_size, alignment ); + + if ( new_size == 0 ) + { + free( a, old_memory ); + return nullptr; + } + + if ( new_size < old_size ) + new_size = old_size; + + if ( old_size == new_size ) + { + return old_memory; + } + else + { + void* new_memory = alloc_align( a, new_size, alignment ); + if ( ! new_memory ) + return nullptr; + + mem_move( new_memory, old_memory, min( new_size, old_size ) ); + free( a, old_memory ); + return new_memory; + } +} + +inline +void zero_size( void* ptr, ssize size ) { + mem_set( ptr, 0, size ); +} + +#pragma endregion Memory + +#pragma region String Ops + +const char* char_first_occurence( const char* str, char c ); +constexpr auto str_find = &char_first_occurence; + +b32 char_is_alpha( char c ); +b32 char_is_alphanumeric( char c ); +b32 char_is_digit( char c ); +b32 char_is_hex_digit( char c ); +b32 char_is_space( char c ); +char char_to_lower( char c ); +char char_to_upper( char c ); + +s32 digit_to_int( char c ); +s32 hex_digit_to_int( char c ); + +s32 str_compare( const char* s1, const char* s2 ); +s32 str_compare( const char* s1, const char* s2, ssize len ); +char* str_copy( char* dest, const char* source, ssize len ); +ssize str_copy_nulpad( char* dest, const char* source, ssize len ); +ssize str_len( const char* str ); +ssize str_len( const char* str, ssize max_len ); +char* str_reverse( char* str ); // NOTE: ASCII only +char const* str_skip( char const* str, char c ); +char const* str_skip_any( char const* str, char const* char_list ); +char const* str_trim( char const* str, b32 catch_newline ); + +// NOTE: ASCII only +void str_to_lower( char* str ); +void str_to_upper( char* str ); + +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 ); + +inline +const char* char_first_occurence( const char* s, char c ) +{ + char ch = c; + for ( ; *s != ch; s++ ) + { + if ( *s == '\0' ) + return NULL; + } + return s; +} + +inline +b32 char_is_alpha( char c ) +{ + if ( ( c >= 'A' && c <= 'Z' ) || ( c >= 'a' && c <= 'z' ) ) + return true; + return false; +} + +inline +b32 char_is_alphanumeric( char c ) +{ + return char_is_alpha( c ) || char_is_digit( c ); +} + +inline +b32 char_is_digit( char c ) +{ + if ( c >= '0' && c <= '9' ) + return true; + return false; +} + +inline +b32 char_is_hex_digit( char c ) +{ + if ( char_is_digit( c ) || ( c >= 'a' && c <= 'f' ) || ( c >= 'A' && c <= 'F' ) ) + return true; + return false; +} + +inline +b32 char_is_space( char c ) +{ + if ( c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v' ) + return true; + return false; +} + +inline +char char_to_lower( char c ) +{ + if ( c >= 'A' && c <= 'Z' ) + return 'a' + ( c - 'A' ); + return c; +} + +inline char char_to_upper( char c ) +{ + if ( c >= 'a' && c <= 'z' ) + return 'A' + ( c - 'a' ); + return c; +} + +inline +s32 digit_to_int( char c ) +{ + return char_is_digit( c ) ? c - '0' : c - 'W'; +} + +inline +s32 hex_digit_to_int( char c ) +{ + if ( char_is_digit( c ) ) + return digit_to_int( c ); + else if ( is_between( c, 'a', 'f' ) ) + return c - 'a' + 10; + else if ( is_between( c, 'A', 'F' ) ) + return c - 'A' + 10; + return -1; +} + +inline +s32 str_compare( const char* s1, const char* s2 ) +{ + while ( *s1 && ( *s1 == *s2 ) ) + { + s1++, s2++; + } + return *( u8* )s1 - *( u8* )s2; +} + +inline +s32 str_compare( const char* s1, const char* s2, ssize len ) +{ + for ( ; len > 0; s1++, s2++, len-- ) + { + if ( *s1 != *s2 ) + return ( ( s1 < s2 ) ? -1 : +1 ); + else if ( *s1 == '\0' ) + return 0; + } + return 0; +} + +inline +char* str_copy( char* dest, const char* source, ssize len ) +{ + GEN_ASSERT_NOT_NULL( dest ); + if ( source ) + { + char* str = dest; + while ( len > 0 && *source ) + { + *str++ = *source++; + len--; + } + while ( len > 0 ) + { + *str++ = '\0'; + len--; + } + } + return dest; +} + +inline +ssize str_copy_nulpad( char* dest, const char* source, ssize len ) +{ + ssize result = 0; + GEN_ASSERT_NOT_NULL( dest ); + if ( source ) + { + const char* source_start = source; + char* str = dest; + while ( len > 0 && *source ) + { + *str++ = *source++; + len--; + } + while ( len > 0 ) + { + *str++ = '\0'; + len--; + } + + result = source - source_start; + } + return result; +} + +inline +ssize str_len( const char* str ) +{ + if ( str == NULL ) + { + return 0; + } + const char* p = str; + while ( *str ) + str++; + return str - p; +} + +inline +ssize str_len( const char* str, ssize max_len ) +{ + const char* end = rcast(const char*, mem_find( str, 0, max_len )); + if ( end ) + return end - str; + return max_len; +} + +inline +char* str_reverse( char* str ) +{ + ssize len = str_len( str ); + char* a = str + 0; + char* b = str + len - 1; + len /= 2; + while ( len-- ) + { + swap( *a, *b ); + a++, b--; + } + return str; +} + +inline +char const* str_skip( char const* str, char c ) +{ + while ( *str && *str != c ) + { + ++str; + } + return str; +} + +inline +char const* str_skip_any( char const* str, char const* char_list ) +{ + char const* closest_ptr = rcast( char const*, pointer_add_const( rcast(void const*, str), str_len( str ) )); + ssize char_list_count = str_len( char_list ); + for ( ssize 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; +} + +inline +char const* str_trim( char const* str, b32 catch_newline ) +{ + while ( *str && char_is_space( *str ) && ( ! catch_newline || ( catch_newline && *str != '\n' ) ) ) + { + ++str; + } + return str; +} + +inline +void str_to_lower( char* str ) +{ + if ( ! str ) + return; + while ( *str ) + { + *str = char_to_lower( *str ); + str++; + } +} + +inline +void str_to_upper( char* str ) +{ + if ( ! str ) + return; + while ( *str ) + { + *str = char_to_upper( *str ); + str++; + } +} + +#pragma endregion String Ops + +#pragma region Printing + +struct FileInfo; + +#ifndef GEN_PRINTF_MAXLEN +# define GEN_PRINTF_MAXLEN kilobytes(128) +#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 ); +ssize str_fmt ( char* str, ssize n, char const* fmt, ... ); +ssize str_fmt_va ( char* str, ssize n, char const* fmt, va_list va ); +ssize str_fmt_out_va ( char const* fmt, va_list va ); +ssize str_fmt_out_err ( char const* fmt, ... ); +ssize str_fmt_out_err_va( char const* fmt, va_list va ); +ssize str_fmt_file ( FileInfo* f, char const* fmt, ... ); +ssize str_fmt_file_va ( FileInfo* f, char const* fmt, va_list va ); + +constexpr +char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED"; + +inline +ssize log_fmt(char const* fmt, ...) +{ + ssize res; + va_list va; + + va_start(va, fmt); + res = str_fmt_out_va(fmt, va); + va_end(va); + + return res; +} + +#pragma endregion Printing + +#pragma region Containers + +template struct RemoveConst { typedef TType Type; }; +template struct RemoveConst { typedef TType Type; }; +template struct RemoveConst { typedef TType Type[]; }; +template struct RemoveConst { typedef TType Type[Size]; }; + +template +using TRemoveConst = typename RemoveConst::Type; + +template +struct Array +{ + struct Header + { + AllocatorInfo Allocator; + usize Capacity; + usize Num; + }; + + static + Array init( AllocatorInfo allocator ) + { + return init_reserve( allocator, grow_formula(0) ); + } + + static + Array init_reserve( AllocatorInfo allocator, ssize capacity ) + { + Header* header = rcast( Header*, alloc( allocator, sizeof(Header) + sizeof(Type) * capacity )); + + if ( header == nullptr ) + return { nullptr }; + + header->Allocator = allocator; + header->Capacity = capacity; + header->Num = 0; + + return { rcast( Type*, header + 1) }; + } + + static + usize grow_formula( usize value ) + { + return 2 * value + 8; + } + + bool append( Array other ) + { + return append( other, other.num() ); + } + + bool append( Type value ) + { + Header* header = get_header(); + + if ( header->Num == header->Capacity ) + { + if ( ! grow( header->Capacity )) + return false; + + header = get_header(); + } + + Data[ header->Num ] = value; + header->Num++; + + return true; + } + + bool append( Type* items, usize 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, usize 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) ); + header->Num++; + + return true; + } + + bool append_at( Type* items, usize item_num, usize 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(); + return Data[ header.Num - 1 ]; + } + + void clear( void ) + { + Header& header = * get_header(); + header.Num = 0; + } + + bool fill( usize begin, usize end, Type value ) + { + Header& header = * get_header(); + + if ( begin < 0 || end > header.Num ) + return false; + + for ( ssize idx = ssize(begin); idx < ssize(end); idx++ ) + { + Data[ idx ] = value; + } + + return true; + } + + void free( void ) + { + Header& header = * get_header(); + gen::free( header.Allocator, &header ); + Data = nullptr; + } + + Header* get_header( void ) + { + using NonConstType = TRemoveConst< Type >; + return rcast( Header*, const_cast(Data) ) - 1 ; + } + + bool grow( usize min_capacity ) + { + Header& header = * get_header(); + usize new_capacity = grow_formula( header.Capacity ); + + if ( new_capacity < min_capacity ) + new_capacity = min_capacity; + + return set_capacity( new_capacity ); + } + + usize num( void ) + { + return get_header()->Num; + } + + void pop( void ) + { + Header& header = * get_header(); + + GEN_ASSERT( header.Num > 0 ); + header.Num--; + } + + void remove_at( usize idx ) + { + Header* header = get_header(); + GEN_ASSERT( idx < header->Num ); + + mem_move( header + idx, header + idx + 1, sizeof( Type ) * ( header->Num - idx - 1 ) ); + header->Num--; + } + + bool reserve( usize new_capacity ) + { + Header& header = * get_header(); + + if ( header.Capacity < new_capacity ) + return set_capacity( new_capacity ); + + return true; + } + + bool resize( usize num ) + { + Header* header = get_header(); + + if ( header->Capacity < num ) + { + if ( ! grow( num ) ) + return false; + + header = get_header(); + } + + header->Num = num; + return true; + } + + bool set_capacity( usize new_capacity ) + { + Header& header = * get_header(); + + if ( new_capacity == header.Capacity ) + return true; + + if ( new_capacity < header.Num ) + { + // Already have the memory, mine as well keep it. + header.Num = new_capacity; + return true; + } + + ssize size = sizeof( Header ) + sizeof( Type ) * new_capacity; + Header* new_header = rcast( Header*, alloc( header.Allocator, size ) ); + + if ( new_header == nullptr ) + return false; + + mem_move( new_header, &header, sizeof( Header ) + sizeof( Type ) * header.Num ); + + new_header->Capacity = new_capacity; + + gen::free( header.Allocator, &header ); + + Data = rcast( Type*, new_header + 1); + return true; + } + + Type* Data; + + operator Type*() + { + return Data; + } + + operator Type const*() const + { + return Data; + } + + // For-range based support + + Type* begin() + { + return Data; + } + + Type* end() + { + return Data + get_header()->Num; + } +}; + +// TODO(Ed) : This thing needs ALOT of work. + +template +struct HashTable +{ + struct FindResult + { + ssize HashIndex; + ssize PrevIndex; + ssize EntryIndex; + }; + + struct Entry + { + u64 Key; + ssize Next; + Type Value; + }; + + static constexpr f32 CriticalLoadScale = 0.7f; + + static + HashTable init( AllocatorInfo allocator ) + { + HashTable result = init_reserve(allocator, 8); + return result; + } + + static + HashTable init_reserve( AllocatorInfo allocator, usize num ) + { + HashTable result = { { nullptr }, { nullptr } }; + + result.Hashes = Array::init_reserve( allocator, num ); + result.Hashes.get_header()->Num = num; + result.Hashes.resize( num ); + result.Hashes.fill( 0, num, -1); + + result.Entries = Array::init_reserve( allocator, num ); + return result; + } + + void clear( void ) + { + Entries.clear(); + Hashes.fill( 0, Hashes.num(), -1); + } + + void destroy( void ) + { + if ( Hashes && Hashes.get_header()->Capacity ) + { + Hashes.free(); + Entries.free(); + } + } + + Type* get( u64 key ) + { + ssize idx = find( key ).EntryIndex; + if ( idx >= 0 ) + return & Entries[ idx ].Value; + + return nullptr; + } + + using MapProc = void (*)( u64 key, Type value ); + + void map( MapProc map_proc ) + { + GEN_ASSERT_NOT_NULL( map_proc ); + + for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx ) + { + map_proc( Entries[ idx ].Key, Entries[ idx ].Value ); + } + } + + using MapMutProc = void (*)( u64 key, Type* value ); + + void map_mut( MapMutProc map_proc ) + { + GEN_ASSERT_NOT_NULL( map_proc ); + + for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx ) + { + map_proc( Entries[ idx ].Key, & Entries[ idx ].Value ); + } + } + + void grow() + { + ssize new_num = Array::grow_formula( Entries.num() ); + rehash( new_num ); + } + + void rehash( ssize new_num ) + { + ssize last_added_index; + + HashTable new_ht = init_reserve( Hashes.get_header()->Allocator, new_num ); + for ( ssize idx = 0; idx < ssize(Entries.num()); ++idx ) + { + FindResult find_result; + + Entry& entry = Entries[ idx ]; + find_result = new_ht.find( entry.Key ); + last_added_index = new_ht.add_entry( entry.Key ); + + if ( find_result.PrevIndex < 0 ) + new_ht.Hashes[ find_result.HashIndex ] = last_added_index; + else + new_ht.Entries[ find_result.PrevIndex ].Next = last_added_index; + + new_ht.Entries[ last_added_index ].Next = find_result.EntryIndex; + new_ht.Entries[ last_added_index ].Value = entry.Value; + } + + destroy(); + *this = new_ht; + } + + void rehash_fast() + { + ssize idx; + + for ( idx = 0; idx < ssize(Entries.num()); idx++ ) + Entries[ idx ].Next = -1; + + for ( idx = 0; idx < ssize(Hashes.num()); idx++ ) + Hashes[ idx ] = -1; + + for ( idx = 0; idx < ssize(Entries.num()); idx++ ) + { + Entry* entry; + FindResult find_result; + + entry = & Entries[ idx ]; + find_result = find( entry->Key ); + + if ( find_result.PrevIndex < 0 ) + Hashes[ find_result.HashIndex ] = idx; + else + Entries[ find_result.PrevIndex ].Next = idx; + } + } + + void remove( u64 key ) + { + FindResult find_result = find( key); + + if ( find_result.EntryIndex >= 0 ) + { + Entries.remove_at( find_result.EntryIndex ); + rehash_fast(); + } + } + + void remove_entry( ssize idx ) + { + Entries.remove_at( idx ); + } + + void set( u64 key, Type value ) + { + ssize idx; + FindResult find_result; + + if ( full() ) + grow(); + + find_result = find( key ); + if ( find_result.EntryIndex >= 0 ) + { + idx = find_result.EntryIndex; + } + else + { + idx = add_entry( key ); + + if ( find_result.PrevIndex >= 0 ) + { + Entries[ find_result.PrevIndex ].Next = idx; + } + else + { + Hashes[ find_result.HashIndex ] = idx; + } + } + + Entries[ idx ].Value = value; + + if ( full() ) + grow(); + } + + ssize slot( u64 key ) + { + for ( ssize idx = 0; idx < ssize(Hashes.num()); ++idx ) + if ( Hashes[ idx ] == key ) + return idx; + + return -1; + } + + Array< ssize> Hashes; + Array< Entry> Entries; + +protected: + + ssize add_entry( u64 key ) + { + ssize idx; + Entry entry = { key, -1 }; + + idx = Entries.num(); + Entries.append( entry ); + return idx; + } + + FindResult find( u64 key ) + { + FindResult result = { -1, -1, -1 }; + + if ( Hashes.num() > 0 ) + { + result.HashIndex = key % Hashes.num(); + result.EntryIndex = Hashes[ result.HashIndex ]; + + while ( result.EntryIndex >= 0 ) + { + if ( Entries[ result.EntryIndex ].Key == key ) + break; + + result.PrevIndex = result.EntryIndex; + result.EntryIndex = Entries[ result.EntryIndex ].Next; + } + } + + return result; + } + + b32 full() + { + usize critical_load = usize( CriticalLoadScale * f32(Hashes.num()) ); + b32 result = Entries.num() > critical_load; + return result; + } +}; + +#pragma endregion Containers + +#pragma region Hashing + +u32 crc32( void const* data, ssize len ); +u64 crc64( void const* data, ssize len ); + +#pragma endregion Hashing + +#pragma region Strings + +// Constant string with length. +struct StrC +{ + ssize Len; + char const* Ptr; + + operator char const* () const { return Ptr; } + char const& operator[]( ssize index ) const { return Ptr[index]; } +}; + +#define cast_to_strc( str ) * rcast( StrC*, (str) - sizeof(ssize) ) +#define txt( text ) StrC { sizeof( text ) - 1, ( text ) } + +inline +StrC to_str( char const* str ) +{ + return { str_len( str ), str }; +} + +// Dynamic String +// This is directly based off the ZPL string api. +// They used a header pattern +// I kept it for simplicty of porting but its not necessary to keep it that way. +struct String +{ + struct Header + { + AllocatorInfo Allocator; + ssize Capacity; + ssize Length; + }; + + static + usize grow_formula( usize value ) + { + // Using a very aggressive growth formula to reduce time mem_copying with recursive calls to append in this library. + return 4 * value + 8; + } + + static + String make( AllocatorInfo allocator, char const* str ) + { + ssize length = str ? str_len( str ) : 0; + return make_length( allocator, str, length ); + } + + static + String make( AllocatorInfo allocator, StrC str ) + { + return make_length( allocator, str.Ptr, str.Len ); + } + + static + String make_reserve( AllocatorInfo allocator, ssize capacity ); + + static + String make_length( AllocatorInfo allocator, char const* str, ssize length ); + + static + String fmt( AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ... ); + + static + String fmt_buf( AllocatorInfo allocator, char const* fmt, ... ); + + static + String join( AllocatorInfo allocator, char const** parts, ssize num_parts, char const* glue ) + { + String result = make( allocator, "" ); + + for ( ssize idx = 0; idx < num_parts; ++idx ) + { + result.append( parts[ idx ] ); + + if ( idx < num_parts - 1 ) + result.append( glue ); + } + + return result; + } + + static + bool are_equal( String lhs, String rhs ) + { + if ( lhs.length() != rhs.length() ) + return false; + + for ( ssize idx = 0; idx < lhs.length(); ++idx ) + if ( lhs[ idx ] != rhs[ idx ] ) + return false; + + return true; + } + + static + bool are_equal( String lhs, StrC rhs ) + { + if ( lhs.length() != (rhs.Len) ) + return false; + + for ( ssize idx = 0; idx < lhs.length(); ++idx ) + if ( lhs[idx] != rhs[idx] ) + return false; + + return true; + } + + bool make_space_for( char const* str, ssize add_len ); + + bool append( char c ) + { + return append( & c, 1 ); + } + + bool append( char const* str ) + { + return append( str, str_len( str ) ); + } + + bool append( char const* str, ssize length ) + { + if ( sptr(str) > 0 ) + { + ssize curr_len = this->length(); + + if ( ! make_space_for( str, length ) ) + return false; + + Header& header = get_header(); + + mem_copy( Data + curr_len, str, length ); + + Data[ curr_len + length ] = '\0'; + + header.Length = curr_len + length; + } + return str != nullptr; + } + + bool append( StrC str) + { + return append( str.Ptr, str.Len ); + } + + bool append( const String other ) + { + return append( other.Data, other.length() ); + } + + bool append_fmt( char const* fmt, ... ); + + ssize avail_space() const + { + Header const& + header = * rcast( Header const*, Data - sizeof( Header )); + + return header.Capacity - header.Length; + } + + char& back() + { + return Data[ length() - 1 ]; + } + + bool contains(StrC substring) const + { + Header const& header = * rcast( Header const*, Data - sizeof( Header )); + + if (substring.Len > header.Length) + return false; + + ssize main_len = header.Length; + ssize sub_len = substring.Len; + + for (ssize i = 0; i <= main_len - sub_len; ++i) + { + if (str_compare(Data + i, substring.Ptr, sub_len) == 0) + return true; + } + + return false; + } + + bool contains(String const& substring) const + { + Header const& header = * rcast( Header const*, Data - sizeof( Header )); + + if (substring.length() > header.Length) + return false; + + ssize main_len = header.Length; + ssize sub_len = substring.length(); + + for (ssize i = 0; i <= main_len - sub_len; ++i) + { + if (str_compare(Data + i, substring.Data, sub_len) == 0) + return true; + } + + return false; + } + + ssize capacity() const + { + Header const& + header = * rcast( Header const*, Data - sizeof( Header )); + + return header.Capacity; + } + + void clear() + { + get_header().Length = 0; + } + + String duplicate( AllocatorInfo allocator ) const + { + return make_length( allocator, Data, length() ); + } + + void free() + { + if ( ! Data ) + return; + + Header& header = get_header(); + + gen::free( header.Allocator, & header ); + } + + Header& get_header() + { + return *(Header*)(Data - sizeof(Header)); + } + + ssize length() const + { + Header const& + header = * rcast( Header const*, Data - sizeof( Header )); + + return header.Length; + } + + b32 starts_with( StrC substring ) const + { + if (substring.Len > length()) + return false; + + b32 result = str_compare(Data, substring.Ptr, substring.Len ) == 0; + return result; + } + + b32 starts_with( String substring ) const + { + if (substring.length() > length()) + return false; + + b32 result = str_compare(Data, substring, substring.length() - 1 ) == 0; + return result; + } + + void skip_line() + { + #define current (*scanner) + char* scanner = Data; + while ( current != '\r' && current != '\n' ) + { + ++ scanner; + } + + s32 new_length = scanner - Data; + + if ( current == '\r' ) + { + new_length += 1; + } + + mem_move( Data, scanner, new_length ); + + Header* header = & get_header(); + header->Length = new_length; + #undef current + } + + void strip_space() + { + char* write_pos = Data; + char* read_pos = Data; + + while ( * read_pos) + { + if ( ! char_is_space( *read_pos )) + { + *write_pos = *read_pos; + write_pos++; + } + read_pos++; + } + + write_pos[0] = '\0'; // Null-terminate the modified string + + // Update the length if needed + get_header().Length = write_pos - Data; + } + + void trim( char const* cut_set ) + { + ssize len = 0; + + char* start_pos = Data; + char* end_pos = Data + length() - 1; + + while ( start_pos <= end_pos && char_first_occurence( cut_set, *start_pos ) ) + start_pos++; + + while ( end_pos > start_pos && char_first_occurence( cut_set, *end_pos ) ) + end_pos--; + + len = scast( ssize, ( start_pos > end_pos ) ? 0 : ( ( end_pos - start_pos ) + 1 ) ); + + if ( Data != start_pos ) + mem_move( Data, start_pos, len ); + + Data[ len ] = '\0'; + + get_header().Length = len; + } + + void trim_space() + { + return trim( " \t\r\n\v\f" ); + } + + // Debug function that provides a copy of the string with whitespace characters visualized. + String visualize_whitespace() const + { + Header* header = (Header*)(Data - sizeof(Header)); + + String result = make_reserve(header->Allocator, length() * 2); // Assume worst case for space requirements. + + for ( char c : *this ) + { + switch ( c ) + { + case ' ': + result.append( txt("·") ); + break; + case '\t': + result.append( txt("→") ); + break; + case '\n': + result.append( txt("↵") ); + break; + case '\r': + result.append( txt("⏎") ); + break; + case '\v': + result.append( txt("⇕") ); + break; + case '\f': + result.append( txt("⌂") ); + break; + default: + result.append(c); + break; + } + } + + return result; + } + + // For-range support + + char* begin() const + { + return Data; + } + + char* end() const + { + Header const& + header = * rcast( Header const*, Data - sizeof( Header )); + + return Data + header.Length; + } + + operator bool() + { + return Data != nullptr; + } + + operator char* () + { + return Data; + } + + operator char const* () const + { + return Data; + } + + operator StrC() const + { + return { length(), Data }; + } + + // Used with cached strings + // Essentially makes the string a string view. + String const& operator = ( String const& other ) const + { + if ( this == & other ) + return *this; + + String* + this_ = ccast(String*, this); + this_->Data = other.Data; + + return *this; + } + + char& operator [] ( ssize index ) + { + return Data[ index ]; + } + + char const& operator [] ( ssize index ) const + { + return Data[ index ]; + } + + char* Data; +}; + +struct String_POD +{ + char* Data; +}; +static_assert( sizeof( String_POD ) == sizeof( String ), "String is not a POD" ); + +// Implements basic string interning. Data structure is based off the ZPL Hashtable. +using StringTable = HashTable; + +// Represents strings cached with the string table. +// Should never be modified, if changed string is desired, cache_string( str ) another. +using StringCached = String const; + +#pragma endregion Strings + +#pragma region File Handling + +typedef u32 FileMode; + +enum FileModeFlag +{ + EFileMode_READ = bit( 0 ), + EFileMode_WRITE = bit( 1 ), + EFileMode_APPEND = bit( 2 ), + EFileMode_RW = bit( 3 ), + GEN_FILE_MODES = EFileMode_READ | EFileMode_WRITE | EFileMode_APPEND | EFileMode_RW, +}; + +// NOTE: Only used internally and for the file operations +enum SeekWhenceType +{ + ESeekWhence_BEGIN = 0, + ESeekWhence_CURRENT = 1, + ESeekWhence_END = 2, +}; + +enum FileError +{ + EFileError_NONE, + EFileError_INVALID, + EFileError_INVALID_FILENAME, + EFileError_EXISTS, + EFileError_NOT_EXISTS, + EFileError_PERMISSION, + EFileError_TRUNCATION_FAILURE, + EFileError_NOT_EMPTY, + EFileError_NAME_TOO_LONG, + EFileError_UNKNOWN, +}; + +union FileDescriptor +{ + void* p; + sptr i; + uptr u; +}; + +typedef struct FileOperations FileOperations; + +#define GEN_FILE_OPEN_PROC( name ) FileError name( FileDescriptor* fd, FileOperations* ops, FileMode mode, char const* filename ) +#define GEN_FILE_READ_AT_PROC( name ) b32 name( FileDescriptor fd, void* buffer, ssize size, s64 offset, ssize* bytes_read, b32 stop_at_newline ) +#define GEN_FILE_WRITE_AT_PROC( name ) b32 name( FileDescriptor fd, void const* buffer, ssize size, s64 offset, ssize* bytes_written ) +#define GEN_FILE_SEEK_PROC( name ) b32 name( FileDescriptor fd, s64 offset, SeekWhenceType whence, s64* new_offset ) +#define GEN_FILE_CLOSE_PROC( name ) void name( FileDescriptor fd ) + +typedef GEN_FILE_OPEN_PROC( file_open_proc ); +typedef GEN_FILE_READ_AT_PROC( FileReadProc ); +typedef GEN_FILE_WRITE_AT_PROC( FileWriteProc ); +typedef GEN_FILE_SEEK_PROC( FileSeekProc ); +typedef GEN_FILE_CLOSE_PROC( FileCloseProc ); + +struct FileOperations +{ + FileReadProc* read_at; + FileWriteProc* write_at; + FileSeekProc* seek; + FileCloseProc* close; +}; + +extern FileOperations const default_file_operations; + +typedef u64 FileTime; + +enum DirType +{ + GEN_DIR_TYPE_FILE, + GEN_DIR_TYPE_FOLDER, + GEN_DIR_TYPE_UNKNOWN, +}; + +struct DirInfo; + +struct DirEntry +{ + char const* filename; + struct DirInfo* dir_info; + u8 type; +}; + +struct DirInfo +{ + char const* fullpath; + DirEntry* entries; // zpl_array + + // Internals + char** filenames; // zpl_array + String buf; +}; + +struct FileInfo +{ + FileOperations ops; + FileDescriptor fd; + b32 is_temp; + + char const* filename; + FileTime last_write_time; + DirEntry* dir; +}; + +enum FileStandardType +{ + EFileStandard_INPUT, + EFileStandard_OUTPUT, + EFileStandard_ERROR, + + EFileStandard_COUNT, +}; + +/** + * Get standard file I/O. + * @param std Check zpl_file_standard_type + * @return File handle to standard I/O + */ +FileInfo* file_get_standard( FileStandardType std ); + +/** + * Closes the file + * @param file + */ +FileError file_close( FileInfo* file ); + +/** + * Returns the currently opened file's name + * @param file + */ +inline + char const* file_name( FileInfo* file ) +{ + return file->filename ? file->filename : ""; +} + +/** + * Opens a file + * @param file + * @param filename + */ +FileError file_open( FileInfo* file, char const* filename ); + +/** + * Opens a file using a specified mode + * @param file + * @param mode Access mode to use + * @param filename + */ +FileError file_open_mode( FileInfo* file, FileMode mode, char const* filename ); + +/** + * Reads from a file + * @param file + * @param buffer Buffer to read to + * @param size Size to read + */ +b32 file_read( FileInfo* file, void* buffer, ssize size ); + +/** + * Reads file at a specific offset + * @param file + * @param buffer Buffer to read to + * @param size Size to read + * @param offset Offset to read from + * @param bytes_read How much data we've actually read + */ +b32 file_read_at( FileInfo* file, void* buffer, ssize size, s64 offset ); + +/** + * Reads file safely + * @param file + * @param buffer Buffer to read to + * @param size Size to read + * @param offset Offset to read from + * @param bytes_read How much data we've actually read + */ +b32 file_read_at_check( FileInfo* file, void* buffer, ssize size, s64 offset, ssize* bytes_read ); + +struct FileContents +{ + AllocatorInfo allocator; + void* data; + ssize size; +}; + +constexpr b32 zero_terminate = true; +constexpr b32 no_zero_terminate = false; + +/** + * Reads the whole file contents + * @param a Allocator to use + * @param zero_terminate End the read data with null terminator + * @param filepath Path to the file + * @return File contents data + */ +FileContents file_read_contents( AllocatorInfo a, b32 zero_terminate, char const* filepath ); + +/** + * Returns a size of the file + * @param file + * @return File size + */ +s64 file_size( FileInfo* file ); + +/** + * Seeks the file cursor from the beginning of file to a specific position + * @param file + * @param offset Offset to seek to + */ +s64 file_seek( FileInfo* file, s64 offset ); + +/** + * Seeks the file cursor to the end of the file + * @param file + */ +s64 file_seek_to_end( FileInfo* file ); + +/** + * Returns the length from the beginning of the file we've read so far + * @param file + * @return Our current position in file + */ +s64 file_tell( FileInfo* file ); + +/** + * Writes to a file + * @param file + * @param buffer Buffer to read from + * @param size Size to read + */ +b32 file_write( FileInfo* file, void const* buffer, ssize size ); + +/** + * Writes to file at a specific offset + * @param file + * @param buffer Buffer to read from + * @param size Size to write + * @param offset Offset to write to + * @param bytes_written How much data we've actually written + */ +b32 file_write_at( FileInfo* file, void const* buffer, ssize size, s64 offset ); + +/** + * Writes to file safely + * @param file + * @param buffer Buffer to read from + * @param size Size to write + * @param offset Offset to write to + * @param bytes_written How much data we've actually written + */ +b32 file_write_at_check( FileInfo* file, void const* buffer, ssize size, s64 offset, ssize* 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, ssize 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, ssize* size ); + +extern FileOperations const memory_file_operations; + +inline +s64 file_seek( FileInfo* f, s64 offset ) +{ + s64 new_offset = 0; + + if ( ! f->ops.read_at ) + f->ops = default_file_operations; + + f->ops.seek( f->fd, offset, ESeekWhence_BEGIN, &new_offset ); + + return new_offset; +} + +inline +s64 file_seek_to_end( FileInfo* f ) +{ + s64 new_offset = 0; + + if ( ! f->ops.read_at ) + f->ops = default_file_operations; + + f->ops.seek( f->fd, 0, ESeekWhence_END, &new_offset ); + + return new_offset; +} + +inline +s64 file_tell( FileInfo* f ) +{ + s64 new_offset = 0; + + if ( ! f->ops.read_at ) + f->ops = default_file_operations; + + f->ops.seek( f->fd, 0, ESeekWhence_CURRENT, &new_offset ); + + return new_offset; +} + +inline +b32 file_read( FileInfo* f, void* buffer, ssize size ) +{ + s64 cur_offset = file_tell( f ); + b32 result = file_read_at( f, buffer, size, file_tell( f ) ); + file_seek( f, cur_offset + size ); + return result; +} + +inline +b32 file_read_at( FileInfo* f, void* buffer, ssize size, s64 offset ) +{ + return file_read_at_check( f, buffer, size, offset, NULL ); +} + +inline +b32 file_read_at_check( FileInfo* f, void* buffer, ssize size, s64 offset, ssize* 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 ); +} + +inline +b32 file_write( FileInfo* f, void const* buffer, ssize size ) +{ + s64 cur_offset = file_tell( f ); + b32 result = file_write_at( f, buffer, size, file_tell( f ) ); + + file_seek( f, cur_offset + size ); + + return result; +} + +inline +b32 file_write_at( FileInfo* f, void const* buffer, ssize size, s64 offset ) +{ + return file_write_at_check( f, buffer, size, offset, NULL ); +} + +inline +b32 file_write_at_check( FileInfo* f, void const* buffer, ssize size, s64 offset, ssize* bytes_written ) +{ + if ( ! f->ops.read_at ) + f->ops = default_file_operations; + + return f->ops.write_at( f->fd, buffer, size, offset, bytes_written ); +} + +#pragma endregion File Handling + +#pragma region Timing + +#ifdef GEN_BENCHMARK +//! Return CPU timestamp. +u64 read_cpu_time_stamp_counter( void ); + +//! Return relative time (in seconds) since the application start. +f64 time_rel( void ); + +//! Return relative time since the application start. +u64 time_rel_ms( void ); +#endif + +#pragma endregion Timing + +#pragma region Parsing + +#pragma region ADT + +enum ADT_Type : u32 +{ + 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 +{ + 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. + EADT_PROPS_IS_PARSED_REAL, +}; + +enum ADT_NamingStyle : u32 +{ + EADT_NAME_STYLE_DOUBLE_QUOTE, + EADT_NAME_STYLE_SINGLE_QUOTE, + EADT_NAME_STYLE_NO_QUOTES, +}; + +enum ADT_AssignStyle : u32 +{ + EADT_ASSIGN_STYLE_COLON, + EADT_ASSIGN_STYLE_EQUALS, + EADT_ASSIGN_STYLE_LINE, +}; + +enum ADT_DelimStyle : u32 +{ + EADT_DELIM_STYLE_COMMA, + EADT_DELIM_STYLE_LINE, + EADT_DELIM_STYLE_NEWLINE, +}; + +enum ADT_Error : u32 +{ + EADT_ERROR_NONE, + EADT_ERROR_INTERNAL, + EADT_ERROR_ALREADY_CONVERTED, + EADT_ERROR_INVALID_TYPE, + EADT_ERROR_OUT_OF_MEMORY, +}; + +struct ADT_Node +{ + char const* name; + struct ADT_Node* parent; + + /* properties */ + ADT_Type type : 4; + u8 props : 4; +#ifndef GEN_PARSER_DISABLE_ANALYSIS + u8 cfg_mode : 1; + u8 name_style : 2; + u8 assign_style : 2; + u8 delim_style : 2; + u8 delim_line_width : 4; + u8 assign_line_width : 4; +#endif + + /* adt data */ + union + { + char const* string; + Array nodes; ///< zpl_array + + struct + { + union + { + f64 real; + s64 integer; + }; + +#ifndef GEN_PARSER_DISABLE_ANALYSIS + /* number analysis */ + s32 base; + s32 base2; + u8 base2_offset : 4; + s8 exp : 4; + u8 neg_zero : 1; + u8 lead_digit : 1; +#endif + }; + }; +}; + +/* 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, ssize 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, ssize 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 + +enum CSV_Error : u32 +{ + ECSV_Error__NONE, + ECSV_Error__INTERNAL, + ECSV_Error__UNEXPECTED_END_OF_INPUT, + ECSV_Error__MISMATCHED_ROWS, +}; + +typedef ADT_Node CSV_Object; + +u8 csv_parse( CSV_Object* root, char* text, AllocatorInfo allocator, b32 has_header ); +u8 csv_parse_delimiter( CSV_Object* root, char* text, AllocatorInfo allocator, b32 has_header, char delim ); +void csv_free( CSV_Object* obj ); + +void csv_write( FileInfo* file, CSV_Object* obj ); +String csv_write_string( AllocatorInfo a, CSV_Object* obj ); +void csv_write_delimiter( FileInfo* file, CSV_Object* obj, char delim ); +String csv_write_string_delimiter( AllocatorInfo a, CSV_Object* obj, char delim ); + +/* inline */ + +inline +u8 csv_parse( CSV_Object* root, char* text, AllocatorInfo allocator, b32 has_header ) +{ + return csv_parse_delimiter( root, text, allocator, has_header, ',' ); +} + +inline +void csv_write( FileInfo* file, CSV_Object* obj ) +{ + csv_write_delimiter( file, obj, ',' ); +} + +inline +String csv_write_string( AllocatorInfo a, CSV_Object* obj ) +{ + return csv_write_string_delimiter( a, obj, ',' ); +} + +#pragma endregion CSV +#pragma endregion Parsing + +GEN_NS_END + +// GEN_ROLL_OWN_DEPENDENCIES +#endif + +GEN_NS_BEGIN + +#pragma region Types + +using LogFailType = ssize(*)(char const*, ...); + +// By default this library will either crash or exit if an error is detected while generating codes. +// Even if set to not use GEN_FATAL, GEN_FATAL will still be used for memory failures as the library is unusable when they occur. +#ifdef GEN_DONT_USE_FATAL + #define log_failure log_fmt +#else + #define log_failure GEN_FATAL +#endif + +enum class AccessSpec : u32 +{ + Default, + Private, + Protected, + Public, + + Num_AccessSpec, + Invalid, +}; + +inline +char const* to_str( AccessSpec type ) +{ + local_persist + char const* lookup[ (u32)AccessSpec::Num_AccessSpec ] = { + "", + "private", + "protected", + "public", + }; + + if ( type > AccessSpec::Public ) + return "Invalid"; + + return lookup[ (u32)type ]; +} + + +enum CodeFlag : u32 +{ + None = 0, + FunctionType = bit(0), + ParamPack = bit(1), + Module_Export = bit(2), + Module_Import = bit(3), +}; + +// Used to indicate if enum definitoin is an enum class or regular enum. +enum class EnumT : u8 +{ + Regular, + Class +}; + +constexpr EnumT EnumClass = EnumT::Class; +constexpr EnumT EnumRegular = EnumT::Regular; + +enum class ModuleFlag : u32 +{ + None = 0, + Export = bit(0), + Import = bit(1), + + Num_ModuleFlags, + Invalid, +}; + +inline +StrC to_str( ModuleFlag flag ) +{ + local_persist + StrC lookup[ (u32)ModuleFlag::Num_ModuleFlags ] = { + { sizeof("__none__"), "__none__" }, + { sizeof("export"), "export" }, + { sizeof("import"), "import" }, + }; + + if ( flag > ModuleFlag::Import ) + return { sizeof("invalid"), "invalid" }; + + return lookup[ (u32)flag ]; +} + +inline +ModuleFlag operator|( ModuleFlag A, ModuleFlag B) +{ + return (ModuleFlag)( (u32)A | (u32)B ); +} + +enum class EPreprocessCond : u32 +{ + If, + IfDef, + IfNotDef, + ElIf +}; + +constexpr EPreprocessCond PreprocessCond_If = EPreprocessCond::If; +constexpr EPreprocessCond PreprocessCond_IfDef = EPreprocessCond::IfDef; +constexpr EPreprocessCond PreprocessCond_IfNotDef = EPreprocessCond::IfNotDef; +constexpr EPreprocessCond PreprocessCond_ElIf = EPreprocessCond::ElIf; + +namespace ECode +{ + enum Type : u32 + { + Invalid, + Untyped, + NewLine, + Comment, + Access_Private, + Access_Protected, + Access_Public, + PlatformAttributes, + Class, + Class_Fwd, + Class_Body, + Constructor, + Constructor_Fwd, + Destructor, + Destructor_Fwd, + Enum, + Enum_Fwd, + Enum_Body, + Enum_Class, + Enum_Class_Fwd, + Execution, + Export_Body, + Extern_Linkage, + Extern_Linkage_Body, + Friend, + Function, + Function_Fwd, + Function_Body, + Global_Body, + Module, + Namespace, + Namespace_Body, + Operator, + Operator_Fwd, + Operator_Member, + Operator_Member_Fwd, + Operator_Cast, + Operator_Cast_Fwd, + Parameters, + Preprocess_Define, + Preprocess_Include, + Preprocess_If, + Preprocess_IfDef, + Preprocess_IfNotDef, + Preprocess_ElIf, + Preprocess_Else, + Preprocess_EndIf, + Preprocess_Pragma, + Specifiers, + Struct, + Struct_Fwd, + Struct_Body, + Template, + Typedef, + Typename, + Union, + Union_Body, + Using, + Using_Namespace, + Variable, + NumTypes + }; + + inline StrC to_str( Type type ) + { + local_persist StrC lookup[] { + { sizeof( "Invalid" ), "Invalid" }, + { sizeof( "Untyped" ), "Untyped" }, + { sizeof( "NewLine" ), "NewLine" }, + { sizeof( "Comment" ), "Comment" }, + { sizeof( "Access_Private" ), "Access_Private" }, + { sizeof( "Access_Protected" ), "Access_Protected" }, + { sizeof( "Access_Public" ), "Access_Public" }, + { sizeof( "PlatformAttributes" ), "PlatformAttributes" }, + { sizeof( "Class" ), "Class" }, + { sizeof( "Class_Fwd" ), "Class_Fwd" }, + { sizeof( "Class_Body" ), "Class_Body" }, + { sizeof( "Constructor" ), "Constructor" }, + { sizeof( "Constructor_Fwd" ), "Constructor_Fwd" }, + { sizeof( "Destructor" ), "Destructor" }, + { sizeof( "Destructor_Fwd" ), "Destructor_Fwd" }, + { sizeof( "Enum" ), "Enum" }, + { sizeof( "Enum_Fwd" ), "Enum_Fwd" }, + { sizeof( "Enum_Body" ), "Enum_Body" }, + { sizeof( "Enum_Class" ), "Enum_Class" }, + { sizeof( "Enum_Class_Fwd" ), "Enum_Class_Fwd" }, + { sizeof( "Execution" ), "Execution" }, + { sizeof( "Export_Body" ), "Export_Body" }, + { sizeof( "Extern_Linkage" ), "Extern_Linkage" }, + { sizeof( "Extern_Linkage_Body" ), "Extern_Linkage_Body" }, + { sizeof( "Friend" ), "Friend" }, + { sizeof( "Function" ), "Function" }, + { sizeof( "Function_Fwd" ), "Function_Fwd" }, + { sizeof( "Function_Body" ), "Function_Body" }, + { sizeof( "Global_Body" ), "Global_Body" }, + { sizeof( "Module" ), "Module" }, + { sizeof( "Namespace" ), "Namespace" }, + { sizeof( "Namespace_Body" ), "Namespace_Body" }, + { sizeof( "Operator" ), "Operator" }, + { sizeof( "Operator_Fwd" ), "Operator_Fwd" }, + { sizeof( "Operator_Member" ), "Operator_Member" }, + { sizeof( "Operator_Member_Fwd" ), "Operator_Member_Fwd" }, + { sizeof( "Operator_Cast" ), "Operator_Cast" }, + { sizeof( "Operator_Cast_Fwd" ), "Operator_Cast_Fwd" }, + { sizeof( "Parameters" ), "Parameters" }, + { sizeof( "Preprocess_Define" ), "Preprocess_Define" }, + { sizeof( "Preprocess_Include" ), "Preprocess_Include" }, + { sizeof( "Preprocess_If" ), "Preprocess_If" }, + { sizeof( "Preprocess_IfDef" ), "Preprocess_IfDef" }, + { sizeof( "Preprocess_IfNotDef" ), "Preprocess_IfNotDef" }, + { sizeof( "Preprocess_ElIf" ), "Preprocess_ElIf" }, + { sizeof( "Preprocess_Else" ), "Preprocess_Else" }, + { sizeof( "Preprocess_EndIf" ), "Preprocess_EndIf" }, + { sizeof( "Preprocess_Pragma" ), "Preprocess_Pragma" }, + { sizeof( "Specifiers" ), "Specifiers" }, + { sizeof( "Struct" ), "Struct" }, + { sizeof( "Struct_Fwd" ), "Struct_Fwd" }, + { sizeof( "Struct_Body" ), "Struct_Body" }, + { sizeof( "Template" ), "Template" }, + { sizeof( "Typedef" ), "Typedef" }, + { sizeof( "Typename" ), "Typename" }, + { sizeof( "Union" ), "Union" }, + { sizeof( "Union_Body" ), "Union_Body" }, + { sizeof( "Using" ), "Using" }, + { sizeof( "Using_Namespace" ), "Using_Namespace" }, + { sizeof( "Variable" ), "Variable" }, + }; + return lookup[type]; + } + +} // namespace ECode + +using CodeT = ECode::Type; + +namespace EOperator +{ + enum Type : u32 + { + Invalid, + Assign, + Assign_Add, + Assign_Subtract, + Assign_Multiply, + Assign_Divide, + Assign_Modulo, + Assign_BAnd, + Assign_BOr, + Assign_BXOr, + Assign_LShift, + Assign_RShift, + Increment, + Decrement, + Unary_Plus, + Unary_Minus, + UnaryNot, + Add, + Subtract, + Multiply, + Divide, + Modulo, + BNot, + BAnd, + BOr, + BXOr, + LShift, + RShift, + LAnd, + LOr, + LEqual, + LNot, + Lesser, + Greater, + LesserEqual, + GreaterEqual, + Subscript, + Indirection, + AddressOf, + MemberOfPointer, + PtrToMemOfPtr, + FunctionCall, + Comma, + New, + NewArray, + Delete, + DeleteArray, + NumOps + }; + + inline StrC to_str( Type op ) + { + local_persist StrC lookup[] { + { sizeof( "INVALID" ), "INVALID" }, + { sizeof( "=" ), "=" }, + { sizeof( "+=" ), "+=" }, + { sizeof( "-=" ), "-=" }, + { sizeof( "*=" ), "*=" }, + { sizeof( "/=" ), "/=" }, + { sizeof( "%=" ), "%=" }, + { sizeof( "&=" ), "&=" }, + { sizeof( "|=" ), "|=" }, + { sizeof( "^=" ), "^=" }, + { sizeof( "<<=" ), "<<=" }, + { sizeof( ">>=" ), ">>=" }, + { sizeof( "++" ), "++" }, + { sizeof( "--" ), "--" }, + { sizeof( "+" ), "+" }, + { sizeof( "-" ), "-" }, + { sizeof( "!" ), "!" }, + { sizeof( "+" ), "+" }, + { sizeof( "-" ), "-" }, + { sizeof( "*" ), "*" }, + { sizeof( "/" ), "/" }, + { sizeof( "%" ), "%" }, + { sizeof( "~" ), "~" }, + { sizeof( "&" ), "&" }, + { sizeof( "|" ), "|" }, + { sizeof( "^" ), "^" }, + { sizeof( "<<" ), "<<" }, + { sizeof( ">>" ), ">>" }, + { sizeof( "&&" ), "&&" }, + { sizeof( "||" ), "||" }, + { sizeof( "==" ), "==" }, + { sizeof( "!=" ), "!=" }, + { sizeof( "<" ), "<" }, + { sizeof( ">" ), ">" }, + { sizeof( "<=" ), "<=" }, + { sizeof( ">=" ), ">=" }, + { sizeof( "[]" ), "[]" }, + { sizeof( "*" ), "*" }, + { sizeof( "&" ), "&" }, + { sizeof( "->" ), "->" }, + { sizeof( "->*" ), "->*" }, + { sizeof( "()" ), "()" }, + { sizeof( "," ), "," }, + { sizeof( "new" ), "new" }, + { sizeof( "new[]" ), "new[]" }, + { sizeof( "delete" ), "delete" }, + { sizeof( "delete[]" ), "delete[]" }, + }; + return lookup[op]; + } + +} // namespace EOperator + +using OperatorT = EOperator::Type; + +namespace ESpecifier +{ + enum Type : u32 + { + Invalid, + Consteval, + Constexpr, + Constinit, + Explicit, + External_Linkage, + ForceInline, + Global, + Inline, + Internal_Linkage, + Local_Persist, + Mutable, + NeverInline, + Ptr, + Ref, + Register, + RValue, + Static, + Thread_Local, + Virtual, + Const, + Final, + NoExceptions, + Override, + Pure, + Volatile, + NumSpecifiers + }; + + inline bool is_trailing( Type specifier ) + { + return specifier > Virtual; + } + + inline StrC to_str( Type type ) + { + local_persist StrC lookup[] { + { sizeof( "INVALID" ), "INVALID" }, + { sizeof( "consteval" ), "consteval" }, + { sizeof( "constexpr" ), "constexpr" }, + { sizeof( "constinit" ), "constinit" }, + { sizeof( "explicit" ), "explicit" }, + { sizeof( "extern" ), "extern" }, + { sizeof( "forceinline" ), "forceinline" }, + { sizeof( "global" ), "global" }, + { sizeof( "inline" ), "inline" }, + { sizeof( "internal" ), "internal" }, + { sizeof( "local_persist" ), "local_persist" }, + { sizeof( "mutable" ), "mutable" }, + { sizeof( "neverinline" ), "neverinline" }, + { sizeof( "*" ), "*" }, + { sizeof( "&" ), "&" }, + { sizeof( "register" ), "register" }, + { sizeof( "&&" ), "&&" }, + { sizeof( "static" ), "static" }, + { sizeof( "thread_local" ), "thread_local" }, + { sizeof( "virtual" ), "virtual" }, + { sizeof( "const" ), "const" }, + { sizeof( "final" ), "final" }, + { sizeof( "noexcept" ), "noexcept" }, + { sizeof( "override" ), "override" }, + { sizeof( "= 0" ), "= 0" }, + { sizeof( "volatile" ), "volatile" }, + }; + return lookup[type]; + } + + inline Type to_type( StrC str ) + { + local_persist u32 keymap[NumSpecifiers]; + do_once_start for ( u32 index = 0; index < NumSpecifiers; index++ ) + { + StrC enum_str = to_str( (Type)index ); + keymap[index] = crc32( enum_str.Ptr, enum_str.Len - 1 ); + } + do_once_end u32 hash = crc32( str.Ptr, str.Len ); + for ( u32 index = 0; index < NumSpecifiers; index++ ) + { + if ( keymap[index] == hash ) + return (Type)index; + } + return Invalid; + } + +} // namespace ESpecifier + +using SpecifierT = ESpecifier::Type; + +#pragma endregion Types + +#pragma region AST + +struct AST; +struct AST_Body; +struct AST_Attributes; +struct AST_Comment; +struct AST_Constructor; +// struct AST_BaseClass; +struct AST_Class; +struct AST_Define; +struct AST_Destructor; +struct AST_Enum; +struct AST_Exec; +struct AST_Extern; +struct AST_Include; +struct AST_Friend; +struct AST_Fn; +struct AST_Module; +struct AST_NS; +struct AST_Operator; +struct AST_OpCast; +struct AST_Param; +struct AST_Pragma; +struct AST_PreprocessCond; +struct AST_Specifiers; + +#if GEN_EXECUTION_EXPRESSION_SUPPORT +struct AST_Expr; +struct AST_Expr_Assign; +struct AST_Expr_Alignof; +struct AST_Expr_Binary; +struct AST_Expr_CStyleCast; +struct AST_Expr_FunctionalCast; +struct AST_Expr_CppCast; +struct AST_Expr_ProcCall; +struct AST_Expr_Decltype; +struct AST_Expr_Comma; // TODO(Ed) : This is a binary op not sure if it needs its own AST... +struct AST_Expr_AMS; // Access Member Symbol +struct AST_Expr_Sizeof; +struct AST_Expr_Subscript; +struct AST_Expr_Ternary; +struct AST_Expr_UnaryPrefix; +struct AST_Expr_UnaryPostfix; +struct AST_Expr_Element; + +struct AST_Stmt; +struct AST_Stmt_Break; +struct AST_Stmt_Case; +struct AST_Stmt_Continue; +struct AST_Stmt_Decl; +struct AST_Stmt_Do; +struct AST_Stmt_Expr; // TODO(Ed) : Is this distinction needed? (Should it be a flag instead?) +struct AST_Stmt_Else; +struct AST_Stmt_If; +struct AST_Stmt_For; +struct AST_Stmt_Goto; +struct AST_Stmt_Label; +struct AST_Stmt_Switch; +struct AST_Stmt_While; +#endif + +struct AST_Struct; +struct AST_Template; +struct AST_Type; +struct AST_Typedef; +struct AST_Union; +struct AST_Using; +struct AST_Var; + +struct Code; +struct CodeBody; +// These are to offer ease of use and optionally strong type safety for the AST. +struct CodeAttributes; +// struct CodeBaseClass; +struct CodeComment; +struct CodeClass; +struct CodeConstructor; +struct CodeDefine; +struct CodeDestructor; +struct CodeEnum; +struct CodeExec; +struct CodeExtern; +struct CodeInclude; +struct CodeFriend; +struct CodeFn; +struct CodeModule; +struct CodeNS; +struct CodeOperator; +struct CodeOpCast; +struct CodeParam; +struct CodePreprocessCond; +struct CodePragma; +struct CodeSpecifiers; + +#if GEN_EXECUTION_EXPRESSION_SUPPORT +struct CodeExpr; +struct CodeExpr_Assign; +struct CodeExpr_Alignof; +struct CodeExpr_Binary; +struct CodeExpr_CStyleCast; +struct CodeExpr_FunctionalCast; +struct CodeExpr_CppCast; +struct CodeExpr_Element; +struct CodeExpr_ProcCall; +struct CodeExpr_Decltype; +struct CodeExpr_Comma; +struct CodeExpr_AMS; // Access Member Symbol +struct CodeExpr_Sizeof; +struct CodeExpr_Subscript; +struct CodeExpr_Ternary; +struct CodeExpr_UnaryPrefix; +struct CodeExpr_UnaryPostfix; + +struct CodeStmt; +struct CodeStmt_Break; +struct CodeStmt_Case; +struct CodeStmt_Continue; +struct CodeStmt_Decl; +struct CodeStmt_Do; +struct CodeStmt_Expr; +struct CodeStmt_Else; +struct CodeStmt_If; +struct CodeStmt_For; +struct CodeStmt_Goto; +struct CodeStmt_Label; +struct CodeStmt_Switch; +struct CodeStmt_While; +#endif + +struct CodeStruct; +struct CodeTemplate; +struct CodeType; +struct CodeTypedef; +struct CodeUnion; +struct CodeUsing; +struct CodeVar; + +namespace parser +{ + struct Token; +} + +/* + AST* wrapper + - Not constantly have to append the '*' as this is written often.. + - Allows for implicit conversion to any of the ASTs (raw or filtered). +*/ +struct Code +{ +# pragma region Statics + // Used to identify ASTs that should always be duplicated. (Global constant ASTs) + static Code Global; + + // Used to identify invalid generated code. + static Code Invalid; +# pragma endregion Statics + +# define Using_Code( Typename ) \ + char const* debug_str(); \ + Code duplicate(); \ + bool is_equal( Code other ); \ + bool is_valid(); \ + void set_global(); \ + String to_string(); \ + Typename& operator = ( AST* other ); \ + Typename& operator = ( Code other ); \ + bool operator ==( Code other ); \ + bool operator !=( Code other ); \ + operator bool(); + + Using_Code( Code ); + + template< class Type > + forceinline Type cast() + { + return * rcast( Type*, this ); + } + + AST* operator ->() + { + return ast; + } + Code& operator ++(); + + // TODO(Ed) : Remove this overload. + auto& operator*() + { + local_persist thread_local + Code NullRef = { nullptr }; + + if ( ast == nullptr ) + return NullRef; + + return *this; + } + + AST* ast; + +#ifdef GEN_ENFORCE_STRONG_CODE_TYPES +# define operator explicit operator +#endif + operator CodeBody() const; + operator CodeAttributes() const; + // operator CodeBaseClass() const; + operator CodeComment() const; + operator CodeClass() const; + operator CodeConstructor() const; + operator CodeDefine() const; + operator CodeDestructor() const; + operator CodeExec() const; + operator CodeEnum() const; + operator CodeExtern() const; + operator CodeInclude() const; + operator CodeFriend() const; + operator CodeFn() const; + operator CodeModule() const; + operator CodeNS() const; + operator CodeOperator() const; + operator CodeOpCast() const; + operator CodeParam() const; + operator CodePragma() const; + operator CodePreprocessCond() const; + operator CodeSpecifiers() const; + operator CodeStruct() const; + operator CodeTemplate() const; + operator CodeType() const; + operator CodeTypedef() const; + operator CodeUnion() const; + operator CodeUsing() const; + operator CodeVar() const; + #undef operator +}; + +struct Code_POD +{ + AST* ast; +}; + +static_assert( sizeof(Code) == sizeof(Code_POD), "ERROR: Code is not POD" ); + +// Desired width of the AST data structure. +constexpr int const AST_POD_Size = 128; + +/* + Simple AST POD with functionality to seralize into C++ syntax. +*/ +struct AST +{ +# pragma region Member Functions + void append ( AST* other ); + char const* debug_str (); + AST* duplicate (); + Code& entry ( u32 idx ); + bool has_entries(); + bool is_equal ( AST* other ); + char const* type_str(); + bool validate_body(); + + String to_string(); + + neverinline + void to_string( String& result ); + + template< class Type > + forceinline Type cast() + { + return * this; + } + + operator Code(); + operator CodeBody(); + operator CodeAttributes(); + // operator CodeBaseClass(); + operator CodeComment(); + operator CodeConstructor(); + operator CodeDestructor(); + operator CodeClass(); + operator CodeDefine(); + operator CodeEnum(); + operator CodeExec(); + operator CodeExtern(); + operator CodeInclude(); + operator CodeFriend(); + operator CodeFn(); + operator CodeModule(); + operator CodeNS(); + operator CodeOperator(); + operator CodeOpCast(); + operator CodeParam(); + operator CodePragma(); + operator CodePreprocessCond(); + operator CodeSpecifiers(); + operator CodeStruct(); + operator CodeTemplate(); + operator CodeType(); + operator CodeTypedef(); + operator CodeUnion(); + operator CodeUsing(); + operator CodeVar(); +# pragma endregion Member Functions + + constexpr static + int ArrSpecs_Cap = + ( + AST_POD_Size + - sizeof(AST*) * 3 + - sizeof(parser::Token*) + - sizeof(AST*) + - sizeof(StringCached) + - sizeof(CodeT) + - sizeof(ModuleFlag) + - sizeof(int) + ) + / sizeof(int) - 1; // -1 for 4 extra bytes + + union { + struct + { + AST* InlineCmt; // Class, Constructor, Destructor, Enum, Friend, Functon, Operator, OpCast, Struct, Typedef, Using, Variable + AST* Attributes; // Class, Enum, Function, Struct, Typedef, Union, Using, Variable + AST* Specs; // Destructor, Function, Operator, Typename, Variable + union { + AST* InitializerList; // Constructor + AST* ParentType; // Class, Struct, ParentType->Next has a possible list of interfaces. + AST* ReturnType; // Function, Operator, Typename + AST* UnderlyingType; // Enum, Typedef + AST* ValueType; // Parameter, Variable + }; + union { + AST* Macro; // Parameter + AST* BitfieldSize; // Variable (Class/Struct Data Member) + AST* Params; // Constructor, Function, Operator, Template, Typename + }; + union { + AST* ArrExpr; // Typename + AST* Body; // Class, Constructor, Destructor, Enum, Friend, Function, Namespace, Struct, Union + AST* Declaration; // Friend, Template + AST* Value; // Parameter, Variable + }; + union { + AST* NextVar; // Variable; Possible way to handle comma separated variables declarations. ( , NextVar->Specs NextVar->Name NextVar->ArrExpr = NextVar->Value ) + AST* SuffixSpecs; // Only used with typenames, to store the function suffix if typename is function signature. ( May not be needed ) + AST* PostNameMacro; // Only used with parameters for specifically UE_REQUIRES (Thanks Unreal) + }; + }; + StringCached Content; // Attributes, Comment, Execution, Include + struct { + SpecifierT ArrSpecs[ArrSpecs_Cap]; // Specifiers + AST* NextSpecs; // Specifiers; If ArrSpecs is full, then NextSpecs is used. + }; + }; + union { + AST* Prev; + AST* Front; + AST* Last; + }; + union { + AST* Next; + AST* Back; + }; + parser::Token* Token; // Reference to starting token, only avaialble if it was derived from parsing. + AST* Parent; + StringCached Name; + CodeT Type; +// CodeFlag CodeFlags; + ModuleFlag ModuleFlags; + union { + b32 IsFunction; // Used by typedef to not serialize the name field. + b32 IsParamPack; // Used by typename to know if type should be considered a parameter pack. + OperatorT Op; + AccessSpec ParentAccess; + s32 NumEntries; + s32 VarConstructorInit; // Used by variables to know that initialization is using a constructor expression instead of an assignment expression. + }; +}; + +struct AST_POD +{ + union { + struct + { + AST* InlineCmt; // Class, Constructor, Destructor, Enum, Friend, Functon, Operator, OpCast, Struct, Typedef, Using, Variable + AST* Attributes; // Class, Enum, Function, Struct, Typedef, Union, Using, Variable + AST* Specs; // Destructor, Function, Operator, Typename, Variable + union { + AST* InitializerList; // Constructor + AST* ParentType; // Class, Struct, ParentType->Next has a possible list of interfaces. + AST* ReturnType; // Function, Operator, Typename + AST* UnderlyingType; // Enum, Typedef + AST* ValueType; // Parameter, Variable + }; + union { + AST* Macro; // Parameter + AST* BitfieldSize; // Variable (Class/Struct Data Member) + AST* Params; // Constructor, Function, Operator, Template, Typename + }; + union { + AST* ArrExpr; // Typename + AST* Body; // Class, Constructr, Destructor, Enum, Friend, Function, Namespace, Struct, Union + AST* Declaration; // Friend, Template + AST* Value; // Parameter, Variable + }; + union { + AST* NextVar; // Variable; Possible way to handle comma separated variables declarations. ( , NextVar->Specs NextVar->Name NextVar->ArrExpr = NextVar->Value ) + AST* SuffixSpecs; // Only used with typenames, to store the function suffix if typename is function signature. ( May not be needed ) + AST* PostNameMacro; // Only used with parameters for specifically UE_REQUIRES (Thanks Unreal) + }; + }; + StringCached Content; // Attributes, Comment, Execution, Include + struct { + SpecifierT ArrSpecs[AST::ArrSpecs_Cap]; // Specifiers + AST* NextSpecs; // Specifiers; If ArrSpecs is full, then NextSpecs is used. + }; + }; + union { + AST* Prev; + AST* Front; + AST* Last; + }; + union { + AST* Next; + AST* Back; + }; + parser::Token* Token; // Reference to starting token, only avaialble if it was derived from parsing. + AST* Parent; + StringCached Name; + CodeT Type; + CodeFlag CodeFlags; + ModuleFlag ModuleFlags; + union { + b32 IsFunction; // Used by typedef to not serialize the name field. + b32 IsParamPack; // Used by typename to know if type should be considered a parameter pack. + OperatorT Op; + AccessSpec ParentAccess; + s32 NumEntries; + s32 VarConstructorInit; // Used by variables to know that initialization is using a constructor expression instead of an assignment expression. + }; +}; + +struct test { + SpecifierT ArrSpecs[AST::ArrSpecs_Cap]; // Specifiers + AST* NextSpecs; // Specifiers; If ArrSpecs is full, then NextSpecs is used. +}; + +constexpr int pls = sizeof(test); + +// Its intended for the AST to have equivalent size to its POD. +// All extra functionality within the AST namespace should just be syntatic sugar. +static_assert( sizeof(AST) == sizeof(AST_POD), "ERROR: AST IS NOT POD" ); +static_assert( sizeof(AST_POD) == AST_POD_Size, "ERROR: AST POD is not size of AST_POD_Size" ); + +// Used when the its desired when omission is allowed in a definition. +#define NoCode { nullptr } +#define CodeInvalid (* Code::Invalid.ast) // Uses an implicitly overloaded cast from the AST to the desired code type. + +#pragma region Code Types + +struct CodeBody +{ + Using_Code( CodeBody ); + + void append( Code other ) + { + raw()->append( other.ast ); + } + void append( CodeBody body ) + { + for ( Code entry : body ) + { + append( entry ); + } + } + bool has_entries() + { + return rcast( AST*, ast )->has_entries(); + } + void to_string( String& result ); + void to_string_export( String& result ); + AST* raw() + { + return rcast( AST*, ast ); + } + AST_Body* operator->() + { + return ast; + } + operator Code() + { + return * rcast( Code*, this ); + } +#pragma region Iterator + Code begin() + { + if ( ast ) + return { rcast( AST*, ast)->Front }; + + return { nullptr }; + } + Code end() + { + return { rcast(AST*, ast)->Back->Next }; + } +#pragma endregion Iterator + + AST_Body* ast; +}; + +struct CodeClass +{ + Using_Code( CodeClass ); + + void add_interface( CodeType interface ); + + void to_string_def( String& result ); + void to_string_fwd( String& result ); + + AST* raw() + { + return rcast( AST*, ast ); + } + operator Code() + { + return * rcast( Code*, this ); + } + AST_Class* operator->() + { + if ( ast == nullptr ) + { + log_failure("Attempt to dereference a nullptr"); + return nullptr; + } + return ast; + } + AST_Class* ast; +}; + +struct CodeParam +{ + Using_Code( CodeParam ); + + void append( CodeParam other ); + + CodeParam get( s32 idx ); + bool has_entries(); + void to_string( String& result ); + AST* raw() + { + return rcast( AST*, ast ); + } + AST_Param* operator->() + { + if ( ast == nullptr ) + { + log_failure("Attempt to dereference a nullptr!"); + return nullptr; + } + return ast; + } + operator Code() + { + return { (AST*)ast }; + } +#pragma region Iterator + CodeParam begin() + { + if ( ast ) + return { ast }; + + return { nullptr }; + } + CodeParam end() + { + // return { (AST_Param*) rcast( AST*, ast)->Last }; + return { nullptr }; + } + CodeParam& operator++(); + CodeParam operator*() + { + return * this; + } +#pragma endregion Iterator + + AST_Param* ast; +}; + +struct CodeSpecifiers +{ + Using_Code( CodeSpecifiers ); + + bool append( SpecifierT spec ) + { + if ( ast == nullptr ) + { + log_failure("CodeSpecifiers: Attempted to append to a null specifiers AST!"); + return false; + } + + if ( raw()->NumEntries == AST::ArrSpecs_Cap ) + { + log_failure("CodeSpecifiers: Attempted to append over %d specifiers to a specifiers AST!", AST::ArrSpecs_Cap ); + return false; + } + + raw()->ArrSpecs[ raw()->NumEntries ] = spec; + raw()->NumEntries++; + return true; + } + s32 has( SpecifierT spec ) + { + for ( s32 idx = 0; idx < raw()->NumEntries; idx++ ) + { + if ( raw()->ArrSpecs[ idx ] == spec ) + return idx; + } + + return -1; + } + void to_string( String& result ); + AST* raw() + { + return rcast( AST*, ast ); + } + AST_Specifiers* operator->() + { + if ( ast == nullptr ) + { + log_failure("Attempt to dereference a nullptr!"); + return nullptr; + } + return ast; + } + operator Code() + { + return { (AST*) ast }; + } +#pragma region Iterator + SpecifierT* begin() + { + if ( ast ) + return & raw()->ArrSpecs[0]; + + return nullptr; + } + SpecifierT* end() + { + return raw()->ArrSpecs + raw()->NumEntries; + } +#pragma endregion Iterator + + AST_Specifiers* ast; +}; + +struct CodeStruct +{ + Using_Code( CodeStruct ); + + void add_interface( CodeType interface ); + + void to_string_def( String& result ); + void to_string_fwd( String& result ); + + AST* raw() + { + return rcast( AST*, ast ); + } + operator Code() + { + return * rcast( Code*, this ); + } + AST_Struct* operator->() + { + if ( ast == nullptr ) + { + log_failure("Attempt to dereference a nullptr"); + return nullptr; + } + return ast; + } + AST_Struct* ast; +}; + +#define Define_CodeType( Typename ) \ + struct Code##Typename \ + { \ + Using_Code( Code ## Typename ); \ + AST* raw(); \ + operator Code(); \ + AST_##Typename* operator->(); \ + AST_##Typename* ast; \ + } + +Define_CodeType( Attributes ); +// Define_CodeType( BaseClass ); +Define_CodeType( Comment ); + +struct CodeConstructor +{ + Using_Code( CodeConstructor ); + + void to_string_def( String& result ); + void to_string_fwd( String& result ); + + AST* raw(); + operator Code(); + AST_Constructor* operator->(); + AST_Constructor* ast; +}; + +struct CodeDefine +{ + Using_Code( CodeDefine ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Define* operator->(); + AST_Define* ast; +}; + +struct CodeDestructor +{ + Using_Code( CodeDestructor ); + + void to_string_def( String& result ); + void to_string_fwd( String& result ); + + AST* raw(); + operator Code(); + AST_Destructor* operator->(); + AST_Destructor* ast; +}; + +struct CodeEnum +{ + Using_Code( CodeEnum ); + + void to_string_def( String& result ); + void to_string_fwd( String& result ); + void to_string_class_def( String& result ); + void to_string_class_fwd( String& result ); + + AST* raw(); + operator Code(); + AST_Enum* operator->(); + AST_Enum* ast; +}; + +Define_CodeType( Exec ); + +#if GEN_EXECUTION_EXPRESSION_SUPPORT +struct CodeExpr +{ + Using_Code( CodeExpr ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr* operator->(); + AST_Expr* ast; +}; + +struct CodeExpr_Assign +{ + Using_Code( CodeExpr_Assign ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_Assign* operator->(); + AST_Expr_Assign* ast; +}; + +struct CodeExpr_Alignof +{ + Using_Code( CodeExpr_Alignof ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_Alignof* operator->(); + AST_Expr_Alignof* ast; +}; + +struct CodeExpr_Binary +{ + Using_Code( CodeExpr_Binary ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_Binary* operator->(); + AST_Expr_Binary* ast; +}; + +struct CodeExpr_CStyleCast +{ + Using_Code( CodeExpr_CStyleCast ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_CStyleCast* operator->(); + AST_Expr_CStyleCast* ast; +}; + +struct CodeExpr_FunctionalCast +{ + Using_Code( CodeExpr_FunctionalCast ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_FunctionalCast* operator->(); + AST_Expr_FunctionalCast* ast; +}; + +struct CodeExpr_CppCast +{ + Using_Code( CodeExpr_CppCast ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_CppCast* operator->(); + AST_Expr_CppCast* ast; +}; + +struct CodeExpr_Element +{ + Using_Code( CodeExpr_Element ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_Element* operator->(); + AST_Expr_Element* ast; +}; + +struct CodeExpr_ProcCall +{ + Using_Code( CodeExpr_ProcCall ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_ProcCall* operator->(); + AST_Expr_ProcCall* ast; +}; + +struct CodeExpr_Decltype +{ + Using_Code( CodeExpr_Decltype ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_Decltype* operator->(); + AST_Expr_Decltype* ast; +}; + +struct CodeExpr_Comma +{ + Using_Code( CodeExpr_Comma ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_Comma* operator->(); + AST_Expr_Comma* ast; +}; + +struct CodeExpr_AMS +{ + Using_Code( CodeExpr_AMS ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_AMS* operator->(); + AST_Expr_AMS* ast; +}; + +struct CodeExpr_Sizeof +{ + Using_Code( CodeExpr_Sizeof ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_Sizeof* operator->(); + AST_Expr_Sizeof* ast; +}; + +struct CodeExpr_Subscript +{ + Using_Code( CodeExpr_Subscript ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_Subscript* operator->(); + AST_Expr_Subscript* ast; +}; + +struct CodeExpr_Ternary +{ + Using_Code( CodeExpr_Ternary ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_Ternary* operator->(); + AST_Expr_Ternary* ast; +}; + +struct CodeExpr_UnaryPrefix +{ + Using_Code( CodeExpr_UnaryPrefix ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_UnaryPrefix* operator->(); + AST_Expr_UnaryPrefix* ast; +}; + +struct CodeExpr_UnaryPostfix +{ + Using_Code( CodeExpr_UnaryPostfix ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Expr_UnaryPostfix* operator->(); + AST_Expr_UnaryPostfix* ast; +}; +#endif + +struct CodeExtern +{ + Using_Code( CodeExtern ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Extern* operator->(); + AST_Extern* ast; +}; + +struct CodeInclude +{ + Using_Code( CodeInclude ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Include* operator->(); + AST_Include* ast; +}; + +struct CodeFriend +{ + Using_Code( CodeFriend ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Friend* operator->(); + AST_Friend* ast; +}; + +struct CodeFn +{ + Using_Code( CodeFn ); + + void to_string_def( String& result ); + void to_string_fwd( String& result ); + + AST* raw(); + operator Code(); + AST_Fn* operator->(); + AST_Fn* ast; +}; + +struct CodeModule +{ + Using_Code( CodeModule ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Module* operator->(); + AST_Module* ast; +}; + +struct CodeNS +{ + Using_Code( CodeNS ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_NS* operator->(); + AST_NS* ast; +}; + +struct CodeOperator +{ + Using_Code( CodeOperator ); + + void to_string_def( String& result ); + void to_string_fwd( String& result ); + + AST* raw(); + operator Code(); + AST_Operator* operator->(); + AST_Operator* ast; +}; + +struct CodeOpCast +{ + Using_Code( CodeOpCast ); + + void to_string_def( String& result ); + void to_string_fwd( String& result ); + + AST* raw(); + operator Code(); + AST_OpCast* operator->(); + AST_OpCast* ast; +}; + +struct CodePragma +{ + Using_Code( CodePragma ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Pragma* operator->(); + AST_Pragma* ast; +}; + +struct CodePreprocessCond +{ + Using_Code( CodePreprocessCond ); + + void to_string_if( String& result ); + void to_string_ifdef( String& result ); + void to_string_ifndef( String& result ); + void to_string_elif( String& result ); + void to_string_else( String& result ); + void to_string_endif( String& result ); + + AST* raw(); + operator Code(); + AST_PreprocessCond* operator->(); + AST_PreprocessCond* ast; +}; + +#if GEN_EXECUTION_EXPRESSION_SUPPORT +struct CodeStmt +{ + Using_Code( CodeStmt ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt* operator->(); + AST_Stmt* ast; +}; + +struct CodeStmt_Break +{ + Using_Code( CodeStmt_Break ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt_Break* operator->(); + AST_Stmt_Break* ast; +}; + +struct CodeStmt_Case +{ + Using_Code( CodeStmt_Case ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt_Case* operator->(); + AST_Stmt_Case* ast; +}; + +struct CodeStmt_Continue +{ + Using_Code( CodeStmt_Continue ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt_Continue* operator->(); + AST_Stmt_Continue* ast; +}; + +struct CodeStmt_Decl +{ + Using_Code( CodeStmt_Decl ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt_Decl* operator->(); + AST_Stmt_Decl* ast; +}; + +struct CodeStmt_Do +{ + Using_Code( CodeStmt_Do ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt_Do* operator->(); + AST_Stmt_Do* ast; +}; + +struct CodeStmt_Expr +{ + Using_Code( CodeStmt_Expr ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt_Expr* operator->(); + AST_Stmt_Expr* ast; +}; + +struct CodeStmt_Else +{ + Using_Code( CodeStmt_Else ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt_Else* operator->(); + AST_Stmt_Else* ast; +}; + +struct CodeStmt_If +{ + Using_Code( CodeStmt_If ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt_If* operator->(); + AST_Stmt_If* ast; +}; + +struct CodeStmt_For +{ + Using_Code( CodeStmt_For ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt_For* operator->(); + AST_Stmt_For* ast; +}; + +struct CodeStmt_Goto +{ + Using_Code( CodeStmt_Goto ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt_Goto* operator->(); + AST_Stmt_Goto* ast; +}; + +struct CodeStmt_Label +{ + Using_Code( CodeStmt_Label ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt_Label* operator->(); + AST_Stmt_Label* ast; +}; + +struct CodeStmt_Switch +{ + Using_Code( CodeStmt_Switch ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt_Switch* operator->(); + AST_Stmt_Switch* ast; +}; + +struct CodeStmt_While +{ + Using_Code( CodeStmt_While ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Stmt_While* operator->(); + AST_Stmt_While* ast; +}; +#endif + +struct CodeTemplate +{ + Using_Code( CodeTemplate ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Template* operator->(); + AST_Template* ast; +}; + +struct CodeType +{ + Using_Code( CodeType ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Type* operator->(); + AST_Type* ast; +}; + +struct CodeTypedef +{ + Using_Code( CodeTypedef ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Typedef* operator->(); + AST_Typedef* ast; +}; + +struct CodeUnion +{ + Using_Code( CodeUnion ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Union* operator->(); + AST_Union* ast; +}; + +struct CodeUsing +{ + Using_Code( CodeUsing ); + + void to_string( String& result ); + void to_string_ns( String& result ); + + AST* raw(); + operator Code(); + AST_Using* operator->(); + AST_Using* ast; +}; + +struct CodeVar +{ + Using_Code( CodeVar ); + + void to_string( String& result ); + + AST* raw(); + operator Code(); + AST_Var* operator->(); + AST_Var* ast; +}; + +#undef Define_CodeType +#undef Using_Code + +#pragma endregion Code Types + +#pragma region AST Types +/* + Show only relevant members of the AST for its type. + AST* fields are replaced with Code types. + - Guards assignemnts to AST* fields to ensure the AST is duplicated if assigned to another parent. +*/ + +struct AST_Body +{ + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + Code Front; + Code Back; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + s32 NumEntries; +}; +static_assert( sizeof(AST_Body) == sizeof(AST), "ERROR: AST_Body is not the same size as AST"); + +struct AST_Attributes +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + StringCached Content; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Attributes) == sizeof(AST), "ERROR: AST_Attributes is not the same size as AST"); + +#if 0 +struct AST_BaseClass +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_BaseClass) == sizeof(AST), "ERROR: AST_BaseClass is not the same size as AST"); +#endif + +struct AST_Comment +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + StringCached Content; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Comment) == sizeof(AST), "ERROR: AST_Comment is not the same size as AST"); + +struct AST_Class +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; // Only supported by forward declarations + CodeAttributes Attributes; + char _PAD_SPECS_ [ sizeof(AST*) ]; + CodeType ParentType; + char _PAD_PARAMS_[ sizeof(AST*) ]; + CodeBody Body; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + CodeType Prev; + CodeType Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + AccessSpec ParentAccess; +}; +static_assert( sizeof(AST_Class) == sizeof(AST), "ERROR: AST_Class is not the same size as AST"); + +struct AST_Constructor +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; // Only supported by forward declarations + char _PAD_PROPERTIES_ [ sizeof(AST*) * 1 ]; + CodeSpecifiers Specs; + Code InitializerList; + CodeParam Params; + Code Body; + char _PAD_PROPERTIES_2_ [ sizeof(AST*) * 2 ]; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Constructor) == sizeof(AST), "ERROR: AST_Constructor is not the same size as AST"); + +struct AST_Define +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + StringCached Content; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Define) == sizeof(AST), "ERROR: AST_Define is not the same size as AST"); + +struct AST_Destructor +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + char _PAD_PROPERTIES_ [ sizeof(AST*) * 1 ]; + CodeSpecifiers Specs; + char _PAD_PROPERTIES_2_ [ sizeof(AST*) * 2 ]; + Code Body; + char _PAD_PROPERTIES_3_ [ sizeof(AST*) ]; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Destructor) == sizeof(AST), "ERROR: AST_Destructor is not the same size as AST"); + +struct AST_Enum +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + CodeAttributes Attributes; + char _PAD_SPEC_ [ sizeof(AST*) ]; + CodeType UnderlyingType; + char _PAD_PARAMS_[ sizeof(AST*) ]; + CodeBody Body; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Enum) == sizeof(AST), "ERROR: AST_Enum is not the same size as AST"); + +struct AST_Exec +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + StringCached Content; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Exec) == sizeof(AST), "ERROR: AST_Exec is not the same size as AST"); + +#if GEN_EXECUTION_EXPRESSION_SUPPORT +struct AST_Expr +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr) == sizeof(AST), "ERROR: AST_Expr is not the same size as AST"); + +struct AST_Expr_Assign +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Assign) == sizeof(AST), "ERROR: AST_Expr_Assign is not the same size as AST"); + +struct AST_Expr_Alignof +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Alignof) == sizeof(AST), "ERROR: AST_Expr_Alignof is not the same size as AST"); + +struct AST_Expr_Binary +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Binary) == sizeof(AST), "ERROR: AST_Expr_Binary is not the same size as AST"); + +struct AST_Expr_CStyleCast +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_CStyleCast) == sizeof(AST), "ERROR: AST_Expr_CStyleCast is not the same size as AST"); + +struct AST_Expr_FunctionalCast +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_FunctionalCast) == sizeof(AST), "ERROR: AST_Expr_FunctionalCast is not the same size as AST"); + +struct AST_Expr_CppCast +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_CppCast) == sizeof(AST), "ERROR: AST_Expr_CppCast is not the same size as AST"); + +struct AST_Expr_ProcCall +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_ProcCall) == sizeof(AST), "ERROR: AST_Expr_Identifier is not the same size as AST"); + +struct AST_Expr_Decltype +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Decltype) == sizeof(AST), "ERROR: AST_Expr_Decltype is not the same size as AST"); + +struct AST_Expr_Comma +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Comma) == sizeof(AST), "ERROR: AST_Expr_Comma is not the same size as AST"); + +struct AST_Expr_AMS +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_AMS) == sizeof(AST), "ERROR: AST_Expr_AMS is not the same size as AST"); + +struct AST_Expr_Sizeof +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Sizeof) == sizeof(AST), "ERROR: AST_Expr_Sizeof is not the same size as AST"); + +struct AST_Expr_Subscript +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Subscript) == sizeof(AST), "ERROR: AST_Expr_Subscript is not the same size as AST"); + +struct AST_Expr_Ternary +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Ternary) == sizeof(AST), "ERROR: AST_Expr_Ternary is not the same size as AST"); + +struct AST_Expr_UnaryPrefix +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_UnaryPrefix) == sizeof(AST), "ERROR: AST_Expr_UnaryPrefix is not the same size as AST"); + +struct AST_Expr_UnaryPostfix +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_UnaryPostfix) == sizeof(AST), "ERROR: AST_Expr_UnaryPostfix is not the same size as AST"); + +struct AST_Expr_Element +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Expr_Element) == sizeof(AST), "ERROR: AST_Expr_Element is not the same size as AST"); +#endif + +struct AST_Extern +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + char _PAD_PROPERTIES_[ sizeof(AST*) * 5 ]; + CodeBody Body; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Extern) == sizeof(AST), "ERROR: AST_Extern is not the same size as AST"); + +struct AST_Include +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + StringCached Content; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Include) == sizeof(AST), "ERROR: AST_Include is not the same size as AST"); + +struct AST_Friend +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + char _PAD_PROPERTIES_[ sizeof(AST*) * 4 ]; + Code Declaration; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Friend) == sizeof(AST), "ERROR: AST_Friend is not the same size as AST"); + +struct AST_Fn +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + CodeAttributes Attributes; + CodeSpecifiers Specs; + CodeType ReturnType; + CodeParam Params; + CodeBody Body; + char _PAD_PROPERTIES_ [ sizeof(AST*) ]; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Fn) == sizeof(AST), "ERROR: AST_Fn is not the same size as AST"); + +struct AST_Module +{ + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Module) == sizeof(AST), "ERROR: AST_Module is not the same size as AST"); + +struct AST_NS +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct { + char _PAD_PROPERTIES_[ sizeof(AST*) * 5 ]; + CodeBody Body; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_NS) == sizeof(AST), "ERROR: AST_NS is not the same size as AST"); + +struct AST_Operator +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + CodeAttributes Attributes; + CodeSpecifiers Specs; + CodeType ReturnType; + CodeParam Params; + CodeBody Body; + char _PAD_PROPERTIES_ [ sizeof(AST*) ]; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + OperatorT Op; +}; +static_assert( sizeof(AST_Operator) == sizeof(AST), "ERROR: AST_Operator is not the same size as AST"); + +struct AST_OpCast +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + char _PAD_PROPERTIES_[ sizeof(AST*) ]; + CodeSpecifiers Specs; + CodeType ValueType; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + CodeBody Body; + char _PAD_PROPERTIES_3_[ sizeof(AST*) ]; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_OpCast) == sizeof(AST), "ERROR: AST_OpCast is not the same size as AST"); + +struct AST_Param +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + char _PAD_PROPERTIES_2_[ sizeof(AST*) * 3 ]; + CodeType ValueType; + Code Macro; + Code Value; + Code PostNameMacro; // Thanks Unreal + // char _PAD_PROPERTIES_3_[sizeof( AST* )]; + }; + }; + CodeParam Last; + CodeParam Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + s32 NumEntries; +}; +static_assert( sizeof(AST_Param) == sizeof(AST), "ERROR: AST_Param is not the same size as AST"); + +struct AST_Pragma +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + StringCached Content; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Pragma) == sizeof(AST), "ERROR: AST_Pragma is not the same size as AST"); + +struct AST_PreprocessCond +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + StringCached Content; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_PreprocessCond) == sizeof(AST), "ERROR: AST_PreprocessCond is not the same size as AST"); + +struct AST_Specifiers +{ + SpecifierT ArrSpecs[ AST::ArrSpecs_Cap ]; + CodeSpecifiers NextSpecs; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + s32 NumEntries; +}; +static_assert( sizeof(AST_Specifiers) == sizeof(AST), "ERROR: AST_Specifier is not the same size as AST"); + +#if GEN_EXECUTION_EXPRESSION_SUPPORT +struct AST_Stmt +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt) == sizeof(AST), "ERROR: AST_Stmt is not the same size as AST"); + +struct AST_Stmt_Break +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Break) == sizeof(AST), "ERROR: AST_Stmt_Break is not the same size as AST"); + +struct AST_Stmt_Case +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Case) == sizeof(AST), "ERROR: AST_Stmt_Case is not the same size as AST"); + +struct AST_Stmt_Continue +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Continue) == sizeof(AST), "ERROR: AST_Stmt_Continue is not the same size as AST"); + +struct AST_Stmt_Decl +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Decl) == sizeof(AST), "ERROR: AST_Stmt_Decl is not the same size as AST"); + +struct AST_Stmt_Do +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Do) == sizeof(AST), "ERROR: AST_Stmt_Do is not the same size as AST"); + +struct AST_Stmt_Expr +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Expr) == sizeof(AST), "ERROR: AST_Stmt_Expr is not the same size as AST"); + +struct AST_Stmt_Else +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Else) == sizeof(AST), "ERROR: AST_Stmt_Else is not the same size as AST"); + +struct AST_Stmt_If +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_If) == sizeof(AST), "ERROR: AST_Stmt_If is not the same size as AST"); + +struct AST_Stmt_For +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_For) == sizeof(AST), "ERROR: AST_Stmt_For is not the same size as AST"); + +struct AST_Stmt_Goto +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Goto) == sizeof(AST), "ERROR: AST_Stmt_Goto is not the same size as AST"); + +struct AST_Stmt_Label +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Label) == sizeof(AST), "ERROR: AST_Stmt_Label is not the same size as AST"); + +struct AST_Stmt_Switch +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_Switch) == sizeof(AST), "ERROR: AST_Stmt_Switch is not the same size as AST"); + +struct AST_Stmt_While +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + }; + CodeExpr Prev; + CodeExpr Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) + sizeof(u32) ]; +}; +static_assert( sizeof(AST_Stmt_While) == sizeof(AST), "ERROR: AST_Stmt_While is not the same size as AST"); +#endif + +struct AST_Struct +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + CodeAttributes Attributes; + char _PAD_SPECS_ [ sizeof(AST*) ]; + CodeType ParentType; + char _PAD_PARAMS_[ sizeof(AST*) ]; + CodeBody Body; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + CodeType Prev; + CodeType Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + AccessSpec ParentAccess; +}; +static_assert( sizeof(AST_Struct) == sizeof(AST), "ERROR: AST_Struct is not the same size as AST"); + +struct AST_Template +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + char _PAD_PROPERTIES_[ sizeof(AST*) * 4 ]; + CodeParam Params; + Code Declaration; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Template) == sizeof(AST), "ERROR: AST_Template is not the same size as AST"); + +#if 0 +// WIP... The type ast is going to become more advanced and lead to a major change to AST design. +struct AST_Type +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + char _PAD_INLINE_CMT_[ sizeof(AST*) ]; + CodeAttributes Attributes; + CodeSpecifiers Specs; + Code QualifierID; + // CodeType ReturnType; // Only used for function signatures + // CodeParam Params; // Only used for function signatures + Code ArrExpr; + // CodeSpecifiers SpecsFuncSuffix; // Only used for function signatures + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + b32 IsParamPack; +}; +static_assert( sizeof(AST_Type) == sizeof(AST), "ERROR: AST_Type is not the same size as AST"); +#endif + +struct AST_Type +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + char _PAD_INLINE_CMT_[ sizeof(AST*) ]; + CodeAttributes Attributes; + CodeSpecifiers Specs; + CodeType ReturnType; // Only used for function signatures + CodeParam Params; // Only used for function signatures + Code ArrExpr; + CodeSpecifiers SpecsFuncSuffix; // Only used for function signatures + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + char _PAD_UNUSED_[ sizeof(ModuleFlag) ]; + b32 IsParamPack; +}; +static_assert( sizeof(AST_Type) == sizeof(AST), "ERROR: AST_Type is not the same size as AST"); + +struct AST_Typedef +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + char _PAD_PROPERTIES_[ sizeof(AST*) * 2 ]; + Code UnderlyingType; + char _PAD_PROPERTIES_2_[ sizeof(AST*) * 3 ]; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + b32 IsFunction; +}; +static_assert( sizeof(AST_Typedef) == sizeof(AST), "ERROR: AST_Typedef is not the same size as AST"); + +struct AST_Union +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + char _PAD_INLINE_CMT_[ sizeof(AST*) ]; + CodeAttributes Attributes; + char _PAD_PROPERTIES_[ sizeof(AST*) * 3 ]; + CodeBody Body; + char _PAD_PROPERTIES_2_[ sizeof(AST*) ]; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Union) == sizeof(AST), "ERROR: AST_Union is not the same size as AST"); + +struct AST_Using +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + CodeAttributes Attributes; + char _PAD_SPECS_ [ sizeof(AST*) ]; + CodeType UnderlyingType; + char _PAD_PROPERTIES_[ sizeof(AST*) * 3 ]; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + char _PAD_UNUSED_[ sizeof(u32) ]; +}; +static_assert( sizeof(AST_Using) == sizeof(AST), "ERROR: AST_Using is not the same size as AST"); + +struct AST_Var +{ + union { + char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap + sizeof(AST*) ]; + struct + { + CodeComment InlineCmt; + CodeAttributes Attributes; + CodeSpecifiers Specs; + CodeType ValueType; + Code BitfieldSize; + Code Value; + CodeVar NextVar; + }; + }; + Code Prev; + Code Next; + parser::Token* Tok; + Code Parent; + StringCached Name; + CodeT Type; + ModuleFlag ModuleFlags; + s32 VarConstructorInit; +}; +static_assert( sizeof(AST_Var) == sizeof(AST), "ERROR: AST_Var is not the same size as AST"); + +#pragma endregion AST Types + +#pragma endregion AST + +#pragma region Gen Interface + +// Initialize the library. +// This currently just initializes the CodePool. +void init(); + +// Currently manually free's the arenas, code for checking for leaks. +// However on Windows at least, it doesn't need to occur as the OS will clean up after the process. +void deinit(); + +// Clears the allocations, but doesn't return to the heap, the calls init() again. +// Ease of use. +void reset(); + +// Used internally to retrive or make string allocations. +// Strings are stored in a series of string arenas of fixed size (SizePer_StringArena) +StringCached get_cached_string( StrC str ); + +/* + This provides a fresh Code AST. + The gen interface use this as their method from getting a new AST object from the CodePool. + Use this if you want to make your own API for formatting the supported Code Types. +*/ +Code make_code(); + +// Set these before calling gen's init() procedure. + +void set_allocator_data_arrays ( AllocatorInfo data_array_allocator ); +void set_allocator_code_pool ( AllocatorInfo pool_allocator ); +void set_allocator_lexer ( AllocatorInfo lex_allocator ); +void set_allocator_string_arena( AllocatorInfo string_allocator ); +void set_allocator_string_table( AllocatorInfo string_allocator ); +void set_allocator_type_table ( AllocatorInfo type_reg_allocator ); + +#pragma region Upfront + +CodeAttributes def_attributes( StrC content ); +CodeComment def_comment ( StrC content ); + +CodeClass def_class( StrC name + , Code body = NoCode + , CodeType parent = NoCode, AccessSpec access = AccessSpec::Default + , CodeAttributes attributes = NoCode + , ModuleFlag mflags = ModuleFlag::None + , CodeType* interfaces = nullptr, s32 num_interfaces = 0 ); + +CodeConstructor def_constructor( CodeParam params = NoCode, Code initializer_list = NoCode, Code body = NoCode ); + +CodeDefine def_define( StrC name, StrC content ); + +CodeDestructor def_destructor( Code body = NoCode, CodeSpecifiers specifiers = NoCode ); + +CodeEnum def_enum( StrC name + , Code body = NoCode, CodeType type = NoCode + , EnumT specifier = EnumRegular, CodeAttributes attributes = NoCode + , ModuleFlag mflags = ModuleFlag::None ); + +CodeExec def_execution ( StrC content ); +CodeExtern def_extern_link( StrC name, Code body ); +CodeFriend def_friend ( Code symbol ); + +CodeFn def_function( StrC name + , CodeParam params = NoCode, CodeType ret_type = NoCode, Code body = NoCode + , CodeSpecifiers specifiers = NoCode, CodeAttributes attributes = NoCode + , ModuleFlag mflags = ModuleFlag::None ); + +CodeInclude def_include ( StrC content, bool foreign = false ); +CodeModule def_module ( StrC name, ModuleFlag mflags = ModuleFlag::None ); +CodeNS def_namespace( StrC name, Code body, ModuleFlag mflags = ModuleFlag::None ); + +CodeOperator def_operator( OperatorT op, StrC nspace + , CodeParam params = NoCode, CodeType ret_type = NoCode, Code body = NoCode + , CodeSpecifiers specifiers = NoCode, CodeAttributes attributes = NoCode + , ModuleFlag mflags = ModuleFlag::None ); + +CodeOpCast def_operator_cast( CodeType type, Code body = NoCode, CodeSpecifiers specs = NoCode ); + +CodeParam def_param ( CodeType type, StrC name, Code value = NoCode ); +CodePragma def_pragma( StrC directive ); + +CodePreprocessCond def_preprocess_cond( EPreprocessCond type, StrC content ); + +CodeSpecifiers def_specifier( SpecifierT specifier ); + +CodeStruct def_struct( StrC name + , Code body = NoCode + , CodeType parent = NoCode, AccessSpec access = AccessSpec::Default + , CodeAttributes attributes = NoCode + , ModuleFlag mflags = ModuleFlag::None + , CodeType* interfaces = nullptr, s32 num_interfaces = 0 ); + +CodeTemplate def_template( CodeParam params, Code definition, ModuleFlag mflags = ModuleFlag::None ); + +CodeType def_type ( StrC name, Code arrayexpr = NoCode, CodeSpecifiers specifiers = NoCode, CodeAttributes attributes = NoCode ); +CodeTypedef def_typedef( StrC name, Code type, CodeAttributes attributes = NoCode, ModuleFlag mflags = ModuleFlag::None ); + +CodeUnion def_union( StrC name, Code body, CodeAttributes attributes = NoCode, ModuleFlag mflags = ModuleFlag::None ); + +CodeUsing def_using( StrC name, CodeType type = NoCode + , CodeAttributes attributess = NoCode + , ModuleFlag mflags = ModuleFlag::None ); + +CodeUsing def_using_namespace( StrC name ); + +CodeVar def_variable( CodeType type, StrC name, Code value = NoCode + , CodeSpecifiers specifiers = NoCode, CodeAttributes attributes = NoCode + , ModuleFlag mflags = ModuleFlag::None ); + +// Constructs an empty body. Use AST::validate_body() to check if the body is was has valid entries. +CodeBody def_body( CodeT type ); + +// There are two options for defining a struct body, either varadically provided with the args macro to auto-deduce the arg num, +/// or provide as an array of Code objects. + +CodeBody def_class_body ( s32 num, ... ); +CodeBody def_class_body ( s32 num, Code* codes ); +CodeBody def_enum_body ( s32 num, ... ); +CodeBody def_enum_body ( s32 num, Code* codes ); +CodeBody def_export_body ( s32 num, ... ); +CodeBody def_export_body ( s32 num, Code* codes); +CodeBody def_extern_link_body( s32 num, ... ); +CodeBody def_extern_link_body( s32 num, Code* codes ); +CodeBody def_function_body ( s32 num, ... ); +CodeBody def_function_body ( s32 num, Code* codes ); +CodeBody def_global_body ( s32 num, ... ); +CodeBody def_global_body ( s32 num, Code* codes ); +CodeBody def_namespace_body ( s32 num, ... ); +CodeBody def_namespace_body ( s32 num, Code* codes ); +CodeParam def_params ( s32 num, ... ); +CodeParam def_params ( s32 num, CodeParam* params ); +CodeSpecifiers def_specifiers ( s32 num, ... ); +CodeSpecifiers def_specifiers ( s32 num, SpecifierT* specs ); +CodeBody def_struct_body ( s32 num, ... ); +CodeBody def_struct_body ( s32 num, Code* codes ); +CodeBody def_union_body ( s32 num, ... ); +CodeBody def_union_body ( s32 num, Code* codes ); + +#pragma endregion Upfront + +#pragma region Parsing + +// TODO(Ed) : Implmeent the new parser API design. + +#if 0 +namespace parser { + struct StackNode + { + StackNode* Prev; + + Token Start; + Token Name; // The name of the AST node (if parsed) + StrC FailedProc; // The name of the procedure that failed + }; + // Stack nodes are allocated the error's allocator + + struct Error + { + String message; + StackNode* context_stack; + }; +} + +struct ParseInfo +{ + Arena FileMem; + Arena TokMem; + Arena CodeMem; + + FileContents FileContent; + Array Tokens; + Array Errors; + // Errors are allocated to a dedicated general arena. +}; + +CodeBody parse_file( StrC path ); +#endif + +CodeClass parse_class ( StrC class_def ); +CodeConstructor parse_constructor ( StrC constructor_def ); +CodeDestructor parse_destructor ( StrC destructor_def ); +CodeEnum parse_enum ( StrC enum_def ); +CodeBody parse_export_body ( StrC export_def ); +CodeExtern parse_extern_link ( StrC exten_link_def ); +CodeFriend parse_friend ( StrC friend_def ); +CodeFn parse_function ( StrC fn_def ); +CodeBody parse_global_body ( StrC body_def ); +CodeNS parse_namespace ( StrC namespace_def ); +CodeOperator parse_operator ( StrC operator_def ); +CodeOpCast parse_operator_cast( StrC operator_def ); +CodeStruct parse_struct ( StrC struct_def ); +CodeTemplate parse_template ( StrC template_def ); +CodeType parse_type ( StrC type_def ); +CodeTypedef parse_typedef ( StrC typedef_def ); +CodeUnion parse_union ( StrC union_def ); +CodeUsing parse_using ( StrC using_def ); +CodeVar parse_variable ( StrC var_def ); + +#pragma endregion Parsing + +#pragma region Untyped text + +ssize token_fmt_va( char* buf, usize buf_size, s32 num_tokens, va_list va ); +//! Do not use directly. Use the token_fmt macro instead. +StrC token_fmt_impl( ssize, ... ); + +Code untyped_str ( StrC content); +Code untyped_fmt ( char const* fmt, ... ); +Code untyped_token_fmt( char const* fmt, s32 num_tokens, ... ); + +#pragma endregion Untyped text + +#pragma endregion Gen Interface + +#pragma region Inlines + +inline +void AST::append( AST* other ) +{ + if ( other->Parent ) + other = other->duplicate(); + + other->Parent = this; + + if ( Front == nullptr ) + { + Front = other; + Back = other; + + NumEntries++; + return; + } + + AST* + Current = Back; + Current->Next = other; + other->Prev = Current; + Back = other; + NumEntries++; +} + +inline +Code& AST::entry( u32 idx ) +{ + AST** current = & Front; + while ( idx >= 0 && current != nullptr ) + { + if ( idx == 0 ) + return * rcast( Code*, current); + + current = & ( * current )->Next; + idx--; + } + + return * rcast( Code*, current); +} + +inline +bool AST::has_entries() +{ + return NumEntries > 0; +} + +inline +char const* AST::type_str() +{ + return ECode::to_str( Type ); +} + +inline +AST::operator Code() +{ + return { this }; +} + +inline +Code& Code::operator ++() +{ + if ( ast ) + ast = ast->Next; + + return *this; +} + +inline +void CodeClass::add_interface( CodeType type ) +{ + CodeType possible_slot = ast->ParentType; + if ( possible_slot.ast ) + { + // Were adding an interface to parent type, so we need to make sure the parent type is public. + ast->ParentAccess = AccessSpec::Public; + // If your planning on adding a proper parent, + // then you'll need to move this over to ParentType->next and update ParentAccess accordingly. + } + + while ( possible_slot.ast != nullptr ) + { + possible_slot.ast = (AST_Type*) possible_slot->Next.ast; + } + + possible_slot.ast = type.ast; +} + +inline +void CodeParam::append( CodeParam other ) +{ + AST* self = (AST*) ast; + AST* entry = (AST*) other.ast; + + if ( entry->Parent ) + entry = entry->duplicate(); + + entry->Parent = self; + + if ( self->Last == nullptr ) + { + self->Last = entry; + self->Next = entry; + self->NumEntries++; + return; + } + + self->Last->Next = entry; + self->Last = entry; + self->NumEntries++; +} + +inline +CodeParam CodeParam::get( s32 idx ) +{ + CodeParam param = *this; + do + { + if ( ! ++ param ) + return { nullptr }; + + param = { (AST_Param*) param.raw()->Next }; + } + while ( --idx ); + + return param; +} + +inline +bool CodeParam::has_entries() +{ + return ast->NumEntries > 0; +} + +inline +CodeParam& CodeParam::operator ++() +{ + ast = ast->Next.ast; + return * this; +} + +inline +void CodeStruct::add_interface( CodeType type ) +{ + CodeType possible_slot = ast->ParentType; + if ( possible_slot.ast ) + { + // Were adding an interface to parent type, so we need to make sure the parent type is public. + ast->ParentAccess = AccessSpec::Public; + // If your planning on adding a proper parent, + // then you'll need to move this over to ParentType->next and update ParentAccess accordingly. + } + + while ( possible_slot.ast != nullptr ) + { + possible_slot.ast = (AST_Type*) possible_slot->Next.ast; + } + + possible_slot.ast = type.ast; +} + +inline +CodeBody def_body( CodeT type ) +{ + switch ( type ) + { + using namespace ECode; + case Class_Body: + case Enum_Body: + case Export_Body: + case Extern_Linkage: + case Function_Body: + case Global_Body: + case Namespace_Body: + case Struct_Body: + case Union_Body: + break; + + default: + log_failure( "def_body: Invalid type %s", (char const*)ECode::to_str(type) ); + return (CodeBody)Code::Invalid; + } + + Code + result = make_code(); + result->Type = type; + return (CodeBody)result; +} + +inline +StrC token_fmt_impl( ssize num, ... ) +{ + local_persist thread_local + char buf[GEN_PRINTF_MAXLEN] = { 0 }; + mem_set( buf, 0, GEN_PRINTF_MAXLEN ); + + va_list va; + va_start(va, num ); + ssize result = token_fmt_va(buf, GEN_PRINTF_MAXLEN, num, va); + va_end(va); + + return { result, buf }; +} +#pragma region generated code inline implementation + +inline char const* Code::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code Code::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool Code::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool Code::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void Code::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline Code& Code::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool Code::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool Code::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline Code::operator bool() +{ + return ast != nullptr; +} + +inline char const* CodeBody::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeBody::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeBody::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeBody::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeBody::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeBody& CodeBody::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeBody::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeBody::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeBody::operator bool() +{ + return ast != nullptr; +} + +inline char const* CodeAttributes::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeAttributes::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeAttributes::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeAttributes::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeAttributes::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeAttributes& CodeAttributes::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeAttributes::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeAttributes::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeAttributes::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeAttributes::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeAttributes::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Attributes* CodeAttributes::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeComment::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeComment::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeComment::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeComment::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeComment::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeComment& CodeComment::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeComment::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeComment::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeComment::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeComment::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeComment::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Comment* CodeComment::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeConstructor::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeConstructor::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeConstructor::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeConstructor::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeConstructor::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeConstructor& CodeConstructor::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeConstructor::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeConstructor::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeConstructor::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeConstructor::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeConstructor::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Constructor* CodeConstructor::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeClass::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeClass::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeClass::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeClass::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeClass::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeClass& CodeClass::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeClass::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeClass::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeClass::operator bool() +{ + return ast != nullptr; +} + +inline char const* CodeDefine::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeDefine::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeDefine::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeDefine::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeDefine::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeDefine& CodeDefine::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeDefine::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeDefine::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeDefine::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeDefine::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeDefine::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Define* CodeDefine::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeDestructor::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeDestructor::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeDestructor::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeDestructor::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeDestructor::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeDestructor& CodeDestructor::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeDestructor::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeDestructor::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeDestructor::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeDestructor::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeDestructor::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Destructor* CodeDestructor::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeEnum::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeEnum::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeEnum::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeEnum::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeEnum::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeEnum& CodeEnum::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeEnum::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeEnum::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeEnum::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeEnum::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeEnum::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Enum* CodeEnum::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeExec::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeExec::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeExec::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeExec::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeExec::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeExec& CodeExec::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeExec::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeExec::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeExec::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeExec::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeExec::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Exec* CodeExec::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeExtern::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeExtern::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeExtern::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeExtern::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeExtern::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeExtern& CodeExtern::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeExtern::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeExtern::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeExtern::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeExtern::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeExtern::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Extern* CodeExtern::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeFriend::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeFriend::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeFriend::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeFriend::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeFriend::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeFriend& CodeFriend::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeFriend::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeFriend::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeFriend::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeFriend::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeFriend::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Friend* CodeFriend::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeFn::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeFn::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeFn::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeFn::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeFn::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeFn& CodeFn::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeFn::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeFn::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeFn::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeFn::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeFn::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Fn* CodeFn::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeInclude::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeInclude::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeInclude::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeInclude::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeInclude::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeInclude& CodeInclude::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeInclude::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeInclude::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeInclude::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeInclude::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeInclude::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Include* CodeInclude::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeModule::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeModule::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeModule::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeModule::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeModule::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeModule& CodeModule::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeModule::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeModule::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeModule::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeModule::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeModule::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Module* CodeModule::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeNS::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeNS::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeNS::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeNS::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeNS::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeNS& CodeNS::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeNS::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeNS::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeNS::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeNS::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeNS::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_NS* CodeNS::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeOperator::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeOperator::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeOperator::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeOperator::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeOperator::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeOperator& CodeOperator::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeOperator::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeOperator::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeOperator::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeOperator::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeOperator::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Operator* CodeOperator::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeOpCast::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeOpCast::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeOpCast::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeOpCast::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeOpCast::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeOpCast& CodeOpCast::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeOpCast::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeOpCast::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeOpCast::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeOpCast::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeOpCast::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_OpCast* CodeOpCast::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeParam::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeParam::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeParam::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeParam::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeParam::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeParam& CodeParam::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeParam::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeParam::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeParam::operator bool() +{ + return ast != nullptr; +} + +inline char const* CodePragma::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodePragma::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodePragma::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodePragma::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodePragma::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodePragma& CodePragma::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodePragma::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodePragma::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodePragma::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodePragma::raw() +{ + return rcast( AST*, ast ); +} + +inline CodePragma::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Pragma* CodePragma::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodePreprocessCond::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodePreprocessCond::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodePreprocessCond::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodePreprocessCond::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodePreprocessCond::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodePreprocessCond& CodePreprocessCond::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodePreprocessCond::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodePreprocessCond::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodePreprocessCond::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodePreprocessCond::raw() +{ + return rcast( AST*, ast ); +} + +inline CodePreprocessCond::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_PreprocessCond* CodePreprocessCond::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeSpecifiers::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeSpecifiers::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeSpecifiers::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeSpecifiers::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeSpecifiers::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeSpecifiers& CodeSpecifiers::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeSpecifiers::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeSpecifiers::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeSpecifiers::operator bool() +{ + return ast != nullptr; +} + +inline char const* CodeStruct::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeStruct::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeStruct::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeStruct::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeStruct::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeStruct& CodeStruct::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeStruct::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeStruct::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeStruct::operator bool() +{ + return ast != nullptr; +} + +inline char const* CodeTemplate::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeTemplate::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeTemplate::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeTemplate::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeTemplate::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeTemplate& CodeTemplate::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeTemplate::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeTemplate::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeTemplate::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeTemplate::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeTemplate::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Template* CodeTemplate::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeType::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeType::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeType::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeType::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeType::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeType& CodeType::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeType::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeType::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeType::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeType::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeType::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Type* CodeType::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeTypedef::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeTypedef::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeTypedef::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeTypedef::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeTypedef::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeTypedef& CodeTypedef::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeTypedef::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeTypedef::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeTypedef::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeTypedef::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeTypedef::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Typedef* CodeTypedef::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeUnion::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeUnion::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeUnion::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeUnion::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeUnion::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeUnion& CodeUnion::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeUnion::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeUnion::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeUnion::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeUnion::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeUnion::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Union* CodeUnion::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeUsing::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeUsing::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeUsing::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeUsing::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeUsing::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeUsing& CodeUsing::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeUsing::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeUsing::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeUsing::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeUsing::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeUsing::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Using* CodeUsing::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +inline char const* CodeVar::debug_str() +{ + if ( ast == nullptr ) + return "Code::debug_str: AST is null!"; + return rcast( AST*, ast )->debug_str(); +} + +inline Code CodeVar::duplicate() +{ + if ( ast == nullptr ) + { + log_failure( "Code::duplicate: Cannot duplicate code, AST is null!" ); + return Code::Invalid; + } + return { rcast( AST*, ast )->duplicate() }; +} + +inline bool CodeVar::is_equal( Code other ) +{ + if ( ast == nullptr || other.ast == nullptr ) + { + return ast == nullptr && other.ast == nullptr; + } + return rcast( AST*, ast )->is_equal( other.ast ); +} + +inline bool CodeVar::is_valid() +{ + return (AST*)ast != nullptr && rcast( AST*, ast )->Type != CodeT::Invalid; +} + +inline void CodeVar::set_global() +{ + if ( ast == nullptr ) + { + log_failure( "Code::set_global: Cannot set code as global, AST is null!" ); + return; + } + rcast( AST*, ast )->Parent = Code::Global.ast; +} + +inline CodeVar& CodeVar::operator=( Code other ) +{ + if ( other.ast && other->Parent ) + { + ast = rcast( decltype( ast ), other.ast->duplicate() ); + rcast( AST*, ast )->Parent = nullptr; + } + ast = rcast( decltype( ast ), other.ast ); + return *this; +} + +inline bool CodeVar::operator==( Code other ) +{ + return (AST*)ast == other.ast; +} + +inline bool CodeVar::operator!=( Code other ) +{ + return (AST*)ast != other.ast; +} + +inline CodeVar::operator bool() +{ + return ast != nullptr; +} + +inline AST* CodeVar::raw() +{ + return rcast( AST*, ast ); +} + +inline CodeVar::operator Code() +{ + return *rcast( Code*, this ); +} + +inline AST_Var* CodeVar::operator->() +{ + if ( ast == nullptr ) + { + log_failure( "Attempt to dereference a nullptr!" ); + return nullptr; + } + return ast; +} + +#pragma endregion generated code inline implementation + +#pragma region generated AST/Code cast implementation + +inline AST::operator CodeBody() +{ + return { rcast( AST_Body*, this ) }; +} + +inline Code::operator CodeBody() const +{ + return { (AST_Body*)ast }; +} + +inline AST::operator CodeAttributes() +{ + return { rcast( AST_Attributes*, this ) }; +} + +inline Code::operator CodeAttributes() const +{ + return { (AST_Attributes*)ast }; +} + +inline AST::operator CodeComment() +{ + return { rcast( AST_Comment*, this ) }; +} + +inline Code::operator CodeComment() const +{ + return { (AST_Comment*)ast }; +} + +inline AST::operator CodeConstructor() +{ + return { rcast( AST_Constructor*, this ) }; +} + +inline Code::operator CodeConstructor() const +{ + return { (AST_Constructor*)ast }; +} + +inline AST::operator CodeClass() +{ + return { rcast( AST_Class*, this ) }; +} + +inline Code::operator CodeClass() const +{ + return { (AST_Class*)ast }; +} + +inline AST::operator CodeDefine() +{ + return { rcast( AST_Define*, this ) }; +} + +inline Code::operator CodeDefine() const +{ + return { (AST_Define*)ast }; +} + +inline AST::operator CodeDestructor() +{ + return { rcast( AST_Destructor*, this ) }; +} + +inline Code::operator CodeDestructor() const +{ + return { (AST_Destructor*)ast }; +} + +inline AST::operator CodeEnum() +{ + return { rcast( AST_Enum*, this ) }; +} + +inline Code::operator CodeEnum() const +{ + return { (AST_Enum*)ast }; +} + +inline AST::operator CodeExec() +{ + return { rcast( AST_Exec*, this ) }; +} + +inline Code::operator CodeExec() const +{ + return { (AST_Exec*)ast }; +} + +inline AST::operator CodeExtern() +{ + return { rcast( AST_Extern*, this ) }; +} + +inline Code::operator CodeExtern() const +{ + return { (AST_Extern*)ast }; +} + +inline AST::operator CodeFriend() +{ + return { rcast( AST_Friend*, this ) }; +} + +inline Code::operator CodeFriend() const +{ + return { (AST_Friend*)ast }; +} + +inline AST::operator CodeFn() +{ + return { rcast( AST_Fn*, this ) }; +} + +inline Code::operator CodeFn() const +{ + return { (AST_Fn*)ast }; +} + +inline AST::operator CodeInclude() +{ + return { rcast( AST_Include*, this ) }; +} + +inline Code::operator CodeInclude() const +{ + return { (AST_Include*)ast }; +} + +inline AST::operator CodeModule() +{ + return { rcast( AST_Module*, this ) }; +} + +inline Code::operator CodeModule() const +{ + return { (AST_Module*)ast }; +} + +inline AST::operator CodeNS() +{ + return { rcast( AST_NS*, this ) }; +} + +inline Code::operator CodeNS() const +{ + return { (AST_NS*)ast }; +} + +inline AST::operator CodeOperator() +{ + return { rcast( AST_Operator*, this ) }; +} + +inline Code::operator CodeOperator() const +{ + return { (AST_Operator*)ast }; +} + +inline AST::operator CodeOpCast() +{ + return { rcast( AST_OpCast*, this ) }; +} + +inline Code::operator CodeOpCast() const +{ + return { (AST_OpCast*)ast }; +} + +inline AST::operator CodeParam() +{ + return { rcast( AST_Param*, this ) }; +} + +inline Code::operator CodeParam() const +{ + return { (AST_Param*)ast }; +} + +inline AST::operator CodePragma() +{ + return { rcast( AST_Pragma*, this ) }; +} + +inline Code::operator CodePragma() const +{ + return { (AST_Pragma*)ast }; +} + +inline AST::operator CodePreprocessCond() +{ + return { rcast( AST_PreprocessCond*, this ) }; +} + +inline Code::operator CodePreprocessCond() const +{ + return { (AST_PreprocessCond*)ast }; +} + +inline AST::operator CodeSpecifiers() +{ + return { rcast( AST_Specifiers*, this ) }; +} + +inline Code::operator CodeSpecifiers() const +{ + return { (AST_Specifiers*)ast }; +} + +inline AST::operator CodeStruct() +{ + return { rcast( AST_Struct*, this ) }; +} + +inline Code::operator CodeStruct() const +{ + return { (AST_Struct*)ast }; +} + +inline AST::operator CodeTemplate() +{ + return { rcast( AST_Template*, this ) }; +} + +inline Code::operator CodeTemplate() const +{ + return { (AST_Template*)ast }; +} + +inline AST::operator CodeType() +{ + return { rcast( AST_Type*, this ) }; +} + +inline Code::operator CodeType() const +{ + return { (AST_Type*)ast }; +} + +inline AST::operator CodeTypedef() +{ + return { rcast( AST_Typedef*, this ) }; +} + +inline Code::operator CodeTypedef() const +{ + return { (AST_Typedef*)ast }; +} + +inline AST::operator CodeUnion() +{ + return { rcast( AST_Union*, this ) }; +} + +inline Code::operator CodeUnion() const +{ + return { (AST_Union*)ast }; +} + +inline AST::operator CodeUsing() +{ + return { rcast( AST_Using*, this ) }; +} + +inline Code::operator CodeUsing() const +{ + return { (AST_Using*)ast }; +} + +inline AST::operator CodeVar() +{ + return { rcast( AST_Var*, this ) }; +} + +inline Code::operator CodeVar() const +{ + return { (AST_Var*)ast }; +} + +#pragma endregion generated AST / Code cast implementation + +#pragma endregion Inlines + +#pragma region Constants + +#ifndef GEN_GLOBAL_BUCKET_SIZE +# define GEN_GLOBAL_BUCKET_SIZE megabytes(8) +#endif +#ifndef GEN_CODEPOOL_NUM_BLOCKS +# define GEN_CODEPOOL_NUM_BLOCKS kilobytes(16) +#endif +#ifndef GEN_SIZE_PER_STRING_ARENA +# define GEN_SIZE_PER_STRING_ARENA megabytes(1) +#endif +#ifndef GEN_MAX_COMMENT_LINE_LENGTH +# define GEN_MAX_COMMENT_LINE_LENGTH 1024 +#endif +#ifndef GEN_MAX_NAME_LENGTH +# define GEN_MAX_NAME_LENGTH 128 +#endif +#ifndef GEN_MAX_UNTYPED_STR_LENGTH +# define GEN_MAX_UNTYPED_STR_LENGTH megabytes(1) +#endif +#ifndef GEN_TOKEN_FMT_TOKEN_MAP_MEM_SIZE +# define GEN_TOKEN_FMT_TOKEN_MAP_MEM_SIZE kilobytes(4) +#endif +#ifndef GEN_LEX_ALLOCATOR_SIZE +# define GEN_LEX_ALLOCATOR_SIZE megabytes(4) +#endif +#ifndef GEN_BUILDER_STR_BUFFER_RESERVE +# define GEN_BUILDER_STR_BUFFER_RESERVE megabytes(2) +#endif + +// These constexprs are used for allocation behavior of data structures +// or string handling while constructing or serializing. +// Change them to suit your needs. + +constexpr s32 InitSize_DataArrays = 16; + +// NOTE: This limits the maximum size of an allocation +// If you are generating a string larger than this, increase the size of the bucket here. +constexpr usize Global_BucketSize = GEN_GLOBAL_BUCKET_SIZE; +constexpr s32 CodePool_NumBlocks = GEN_CODEPOOL_NUM_BLOCKS; +constexpr s32 SizePer_StringArena = GEN_SIZE_PER_STRING_ARENA; + +constexpr s32 MaxCommentLineLength = GEN_MAX_COMMENT_LINE_LENGTH; +constexpr s32 MaxNameLength = GEN_MAX_NAME_LENGTH; +constexpr s32 MaxUntypedStrLength = GEN_MAX_UNTYPED_STR_LENGTH; +constexpr s32 TokenFmt_TokenMap_MemSize = GEN_TOKEN_FMT_TOKEN_MAP_MEM_SIZE; +constexpr s32 LexAllocator_Size = GEN_LEX_ALLOCATOR_SIZE; +constexpr s32 Builder_StrBufferReserve = GEN_BUILDER_STR_BUFFER_RESERVE; + +extern Code access_public; +extern Code access_protected; +extern Code access_private; + +extern CodeAttributes attrib_api_export; +extern CodeAttributes attrib_api_import; + +extern Code module_global_fragment; +extern Code module_private_fragment; + +extern Code fmt_newline; + +extern CodePragma pragma_once; + +extern CodeParam param_varadic; + +extern CodePreprocessCond preprocess_else; +extern CodePreprocessCond preprocess_endif; + +extern CodeSpecifiers spec_const; +extern CodeSpecifiers spec_consteval; +extern CodeSpecifiers spec_constexpr; +extern CodeSpecifiers spec_constinit; +extern CodeSpecifiers spec_extern_linkage; +extern CodeSpecifiers spec_final; +extern CodeSpecifiers spec_forceinline; +extern CodeSpecifiers spec_global; +extern CodeSpecifiers spec_inline; +extern CodeSpecifiers spec_internal_linkage; +extern CodeSpecifiers spec_local_persist; +extern CodeSpecifiers spec_mutable; +extern CodeSpecifiers spec_neverinline; +extern CodeSpecifiers spec_noexcept; +extern CodeSpecifiers spec_override; +extern CodeSpecifiers spec_ptr; +extern CodeSpecifiers spec_pure; +extern CodeSpecifiers spec_ref; +extern CodeSpecifiers spec_register; +extern CodeSpecifiers spec_rvalue; +extern CodeSpecifiers spec_static_member; +extern CodeSpecifiers spec_thread_local; +extern CodeSpecifiers spec_virtual; +extern CodeSpecifiers spec_volatile; + +extern CodeType t_empty; // Used with varaidc parameters. (Exposing just in case its useful for another circumstance) +extern CodeType t_auto; +extern CodeType t_void; +extern CodeType t_int; +extern CodeType t_bool; +extern CodeType t_char; +extern CodeType t_wchar_t; +extern CodeType t_class; +extern CodeType t_typename; + +#ifdef GEN_DEFINE_LIBRARY_CODE_CONSTANTS + // Predefined typename codes. Are set to readonly and are setup during gen::init() + + extern CodeType t_b32; + + extern CodeType t_s8; + extern CodeType t_s16; + extern CodeType t_s32; + extern CodeType t_s64; + + extern CodeType t_u8; + extern CodeType t_u16; + extern CodeType t_u32; + extern CodeType t_u64; + + extern CodeType t_ssize; + extern CodeType t_usize; + + extern CodeType t_f32; + extern CodeType t_f64; +#endif + +#pragma endregion Constants + +#pragma region Macros + +# define gen_main main + +# define __ NoCode + + // Convienence for defining any name used with the gen api. + // Lets you provide the length and string literal to the functions without the need for the DSL. +# define name( Id_ ) { sizeof(stringize( Id_ )) - 1, stringize(Id_) } + + // Same as name just used to indicate intention of literal for code instead of names. +# define code( ... ) { sizeof(stringize(__VA_ARGS__)) - 1, stringize( __VA_ARGS__ ) } + +# define args( ... ) num_args( __VA_ARGS__ ), __VA_ARGS__ + +# define code_str( ... ) GEN_NS untyped_str( code( __VA_ARGS__ ) ) +# define code_fmt( ... ) GEN_NS untyped_str( token_fmt( __VA_ARGS__ ) ) + + // Takes a format string (char const*) and a list of tokens (StrC) and returns a StrC of the formatted string. +# define token_fmt( ... ) GEN_NS token_fmt_impl( (num_args( __VA_ARGS__ ) + 1) / 2, __VA_ARGS__ ) + +#pragma endregion Macros + +// Used by the lexer to persistently treat all these identifiers as preprocessor defines. +// Populate with strings via gen::get_cached_string. +// Functional defines must have format: id( ;at minimum to indicate that the define is only valid with arguments. +extern Array< StringCached > PreprocessorDefines; + +#ifdef GEN_EXPOSE_BACKEND + + // Global allocator used for data with process lifetime. + extern AllocatorInfo GlobalAllocator; + extern Array< Arena > Global_AllocatorBuckets; + + extern Array< Pool > CodePools; + extern Array< Arena > StringArenas; + + extern StringTable StringCache; + + extern Arena LexArena; + + extern AllocatorInfo Allocator_DataArrays; + extern AllocatorInfo Allocator_CodePool; + extern AllocatorInfo Allocator_Lexer; + extern AllocatorInfo Allocator_StringArena; + extern AllocatorInfo Allocator_StringTable; + extern AllocatorInfo Allocator_TypeTable; + +#endif + +#pragma region Builder + +struct Builder +{ + FileInfo File; + String Buffer; + + static Builder open( char const* path ); + + void pad_lines( s32 num ); + + void print( Code ); + void print_fmt( char const* fmt, ... ); + + void write(); +}; + +#pragma endregion Builder +GEN_NS_END + +#pragma region GENCPP IMPLEMENTATION GUARD +#if defined(GEN_IMPLEMENTATION) && ! defined(GEN_IMPLEMENTED) +# define GEN_IMPLEMENTED + + +//! If its desired to roll your own dependencies, define GEN_ROLL_OWN_DEPENDENCIES before including this file. +// Dependencies are derived from the c-zpl library: https://github.com/zpl-c/zpl +#ifndef GEN_ROLL_OWN_DEPENDENCIES +GEN_NS_BEGIN + + +#pragma region Macros and Includes + +# include +// NOTE: Ensure we use standard methods for these calls if we use GEN_PICO +# if ! defined( GEN_PICO_CUSTOM_ROUTINES ) +# if ! defined( GEN_MODULE_CORE ) +# define _strlen strlen +# define _printf_err( fmt, ... ) fprintf( stderr, fmt, __VA_ARGS__ ) +# define _printf_err_va( fmt, va ) vfprintf( stderr, fmt, va ) +# else +# define _strlen str_len +# define _printf_err( fmt, ... ) str_fmt_out_err( fmt, __VA_ARGS__ ) +# define _printf_err_va( fmt, va ) str_fmt_out_err_va( fmt, va ) +# endif +# endif +# +# include +# +# if defined( GEN_SYSTEM_UNIX ) || defined( GEN_SYSTEM_MACOS ) +# include +# elif defined( GEN_SYSTEM_WINDOWS ) +# if ! defined( GEN_NO_WINDOWS_H ) +# ifndef WIN32_LEAN_AND_MEAN +# ifndef NOMINMAX +# define NOMINMAX +# endif +# +# define WIN32_LEAN_AND_MEAN +# define WIN32_MEAN_AND_LEAN +# define VC_EXTRALEAN +# endif +# include +# undef NOMINMAX +# undef WIN32_LEAN_AND_MEAN +# undef WIN32_MEAN_AND_LEAN +# undef VC_EXTRALEAN +# endif +# endif + +#include + +#ifdef GEN_SYSTEM_MACOS +# include +#endif + +#ifdef GEN_SYSTEM_CYGWIN +# include +#endif + +#if defined( GEN_SYSTEM_WINDOWS ) && ! defined( GEN_COMPILER_GCC ) +# include +#endif + +#if defined( GEN_SYSTEM_LINUX ) +# include +#endif + +#ifdef GEN_BENCHMARK +// Timing includes +#if defined( GEN_SYSTEM_MACOS ) || GEN_SYSTEM_UNIX +# include +# include +#endif + +#if defined( GEN_SYSTEM_MACOS ) +# include +# include +# include +#endif + +#if defined( GEN_SYSTEM_EMSCRIPTEN ) +# include +#endif + +#if defined( GEN_SYSTEM_WINDOWS ) +# include +#endif +#endif + +#pragma endregion Macros and Includes + +#pragma region Debug + +void assert_handler( char const* condition, char const* file, s32 line, char const* msg, ... ) +{ + _printf_err( "%s:(%d): Assert Failure: ", file, line ); + + if ( condition ) + _printf_err( "`%s` \n", condition ); + + if ( msg ) + { + va_list va; + va_start( va, msg ); + _printf_err_va( msg, va ); + va_end( va ); + } + + _printf_err( "%s", "\n" ); +} + +s32 assert_crash( char const* condition ) +{ + GEN_PANIC( condition ); + return 0; +} + +#if defined( GEN_SYSTEM_WINDOWS ) + void process_exit( u32 code ) + { + ExitProcess( code ); + } +#else +# include + + void process_exit( u32 code ) + { + exit( code ); + } +#endif + +#pragma endregion Debug + +#pragma region String Ops + +internal +ssize _scan_zpl_i64( const char* text, s32 base, s64* value ) +{ + const char* text_begin = text; + s64 result = 0; + b32 negative = false; + + if ( *text == '-' ) + { + negative = true; + text++; + } + + if ( base == 16 && str_compare( text, "0x", 2 ) == 0 ) + text += 2; + + for ( ;; ) + { + s64 v; + if ( char_is_digit( *text ) ) + v = *text - '0'; + else if ( base == 16 && char_is_hex_digit( *text ) ) + v = hex_digit_to_int( *text ); + else + break; + + result *= base; + result += v; + text++; + } + + if ( value ) + { + if ( negative ) + result = -result; + *value = result; + } + + return ( text - text_begin ); +} + +// TODO : Are these good enough for characters? +global const char _num_to_char_table[] = + "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "@$"; + +s64 str_to_i64( const char* str, char** end_ptr, s32 base ) +{ + ssize len; + s64 value; + + if ( ! base ) + { + if ( ( str_len( str ) > 2 ) && ( str_compare( str, "0x", 2 ) == 0 ) ) + base = 16; + else + base = 10; + } + + len = _scan_zpl_i64( str, base, &value ); + if ( end_ptr ) + *end_ptr = ( char* )str + len; + return value; +} + +void i64_to_str( s64 value, char* string, s32 base ) +{ + char* buf = string; + b32 negative = false; + u64 v; + + if ( value < 0 ) + { + negative = true; + value = -value; + } + + v = scast( u64, value); + if ( v != 0 ) + { + while ( v > 0 ) + { + *buf++ = _num_to_char_table[ v % base ]; + v /= base; + } + } + else + { + *buf++ = '0'; + } + if ( negative ) + *buf++ = '-'; + *buf = '\0'; + str_reverse( string ); +} + +void u64_to_str( u64 value, char* string, s32 base ) +{ + char* buf = string; + + if ( value ) + { + while ( value > 0 ) + { + *buf++ = _num_to_char_table[ value % base ]; + value /= base; + } + } + else + { + *buf++ = '0'; + } + *buf = '\0'; + + 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 = rcast( char*, ccast(char*, str) ); + + return result; +} + +#pragma endregion String Ops + +#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 ssize _print_string( char* text, ssize max_len, _format_info* info, char const* str ) +{ + ssize res = 0, len = 0; + ssize 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 ) + { + ssize 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 ) ) + { + ssize 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 ssize _print_char( char* text, ssize max_len, _format_info* info, char arg ) +{ + char str[ 2 ] = ""; + str[ 0 ] = arg; + return _print_string( text, max_len, info, str ); +} + +internal ssize _print_repeated_char( char* text, ssize max_len, _format_info* info, char arg ) +{ + ssize res = 0; + s32 rem = ( info ) ? ( info->width > 0 ) ? info->width : 1 : 1; + res = rem; + while ( rem-- > 0 ) + *text++ = arg; + + return res; +} + +internal ssize _print_i64( char* text, ssize 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 ssize _print_u64( char* text, ssize 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 ssize _print_f64( char* text, ssize max_len, _format_info* info, b32 is_hexadecimal, f64 arg ) +{ + // TODO: Handle exponent notation + ssize 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 = scast( 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 = scast( u64, arg * mult ); + len = _print_u64( text, remaining, NULL, value ); + text += len; + if ( len >= remaining ) + remaining = min( remaining, 1 ); + else + remaining -= len; + arg -= scast( 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 ssize str_fmt_va( char* text, ssize max_len, char const* fmt, va_list va ) +{ + char const* text_begin = text; + ssize remaining = max_len, res; + + while ( *fmt ) + { + _format_info info = { 0 }; + ssize 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 = scast( s32, str_to_i64( fmt, ccast( 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 = scast( s32, str_to_i64( fmt, ccast( 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, scast( 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 = scast( u64, scast( u8, va_arg( va, int ))); + break; + case GEN_FMT_SHORT : + value = scast( u64, scast( u16, va_arg( va, int ))); + break; + case GEN_FMT_LONG: + value = scast( u64, va_arg( va, unsigned long )); + break; + case GEN_FMT_LLONG : + value = scast( u64, va_arg( va, unsigned long long )); + break; + case GEN_FMT_SIZE : + value = scast( u64, va_arg( va, usize )); + break; + case GEN_FMT_INTPTR : + value = scast( u64, va_arg( va, uptr )); + break; + default : + value = scast( 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 = scast( s64, scast( s8, va_arg( va, int ))); + break; + case GEN_FMT_SHORT : + value = scast( s64, scast( s16, va_arg( va, int ))); + break; + case GEN_FMT_LONG : + value = scast( s64, va_arg( va, long )); + break; + case GEN_FMT_LLONG : + value = scast( s64, va_arg( va, long long )); + break; + case GEN_FMT_SIZE : + value = scast( s64, va_arg( va, usize )); + break; + case GEN_FMT_INTPTR : + value = scast( s64, va_arg( va, uptr )); + break; + default : + value = scast( 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; +} + +ssize str_fmt_file_va( struct FileInfo* f, char const* fmt, va_list va ) +{ + local_persist thread_local char buf[ GEN_PRINTF_MAXLEN ]; + ssize 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; +} + +ssize str_fmt_file( struct FileInfo* f, char const* fmt, ... ) +{ + ssize res; + va_list va; + va_start( va, fmt ); + res = str_fmt_file_va( f, fmt, va ); + va_end( va ); + return res; +} + +ssize str_fmt( char* str, ssize n, char const* fmt, ... ) +{ + ssize res; + va_list va; + va_start( va, fmt ); + res = str_fmt_va( str, n, fmt, va ); + va_end( va ); + return res; +} + +ssize str_fmt_out_va( char const* fmt, va_list va ) +{ + return str_fmt_file_va( file_get_standard( EFileStandard_OUTPUT ), fmt, va ); +} + +ssize str_fmt_out_err_va( char const* fmt, va_list va ) +{ + return str_fmt_file_va( file_get_standard( EFileStandard_ERROR ), fmt, va ); +} + +ssize str_fmt_out_err( char const* fmt, ... ) +{ + ssize res; + va_list va; + va_start( va, fmt ); + res = str_fmt_out_err_va( fmt, va ); + va_end( va ); + return res; +} + +#pragma endregion Printing + +#pragma region Memory + +void* mem_copy( void* dest, void const* source, ssize n ) +{ + if ( dest == NULL ) + { + return NULL; + } + + return memcpy( dest, source, n ); +} + +void const* mem_find( void const* data, u8 c, ssize n ) +{ + u8 const* s = rcast( u8 const*, data); + while ( ( rcast( uptr, s) & ( sizeof( usize ) - 1 ) ) && n && *s != c ) + { + s++; + n--; + } + if ( n && *s != c ) + { + ssize const* w; + ssize k = GEN__ONES * c; + w = rcast( ssize const*, s); + while ( n >= size_of( ssize ) && ! GEN__HAS_ZERO( *w ^ k ) ) + { + w++; + n -= size_of( ssize ); + } + s = rcast( u8 const*, w); + while ( n && *s != c ) + { + s++; + n--; + } + } + + return n ? rcast( void const*, s ) : NULL; +} + +#define GEN_HEAP_STATS_MAGIC 0xDEADC0DE + +struct _heap_stats +{ + u32 magic; + ssize used_memory; + ssize alloc_count; +}; + +global _heap_stats _heap_stats_info; + +void heap_stats_init( void ) +{ + zero_item( &_heap_stats_info ); + _heap_stats_info.magic = GEN_HEAP_STATS_MAGIC; +} + +ssize heap_stats_used_memory( void ) +{ + GEN_ASSERT_MSG( _heap_stats_info.magic == GEN_HEAP_STATS_MAGIC, "heap_stats is not initialised yet, call heap_stats_init first!" ); + return _heap_stats_info.used_memory; +} + +ssize heap_stats_alloc_count( void ) +{ + GEN_ASSERT_MSG( _heap_stats_info.magic == GEN_HEAP_STATS_MAGIC, "heap_stats is not initialised yet, call heap_stats_init first!" ); + return _heap_stats_info.alloc_count; +} + +void heap_stats_check( void ) +{ + GEN_ASSERT_MSG( _heap_stats_info.magic == GEN_HEAP_STATS_MAGIC, "heap_stats is not initialised yet, call heap_stats_init first!" ); + GEN_ASSERT( _heap_stats_info.used_memory == 0 ); + GEN_ASSERT( _heap_stats_info.alloc_count == 0 ); +} + +struct _heap_alloc_info +{ + ssize size; + void* physical_start; +}; + +void* heap_allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ) +{ + void* ptr = NULL; + // unused( allocator_data ); + // unused( old_size ); + if ( ! alignment ) + alignment = GEN_DEFAULT_MEMORY_ALIGNMENT; + +#ifdef GEN_HEAP_ANALYSIS + ssize alloc_info_size = size_of( _heap_alloc_info ); + ssize alloc_info_remainder = ( alloc_info_size % alignment ); + ssize track_size = max( alloc_info_size, alignment ) + alloc_info_remainder; + switch ( type ) + { + case EAllocation_FREE : + { + if ( ! old_memory ) + break; + _heap_alloc_info* alloc_info = rcast( _heap_alloc_info*, old_memory) - 1; + _heap_stats_info.used_memory -= alloc_info->size; + _heap_stats_info.alloc_count--; + old_memory = alloc_info->physical_start; + } + break; + case EAllocation_ALLOC : + { + size += track_size; + } + break; + default : + break; + } +#endif + + switch ( type ) + { +#if defined( GEN_COMPILER_MSVC ) || ( defined( GEN_COMPILER_GCC ) && defined( GEN_SYSTEM_WINDOWS ) ) || ( defined( GEN_COMPILER_TINYC ) && defined( GEN_SYSTEM_WINDOWS ) ) + case EAllocation_ALLOC : + ptr = _aligned_malloc( size, alignment ); + if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO ) + zero_size( ptr, size ); + break; + case EAllocation_FREE : + _aligned_free( old_memory ); + break; + case EAllocation_RESIZE : + { + AllocatorInfo a = heap(); + ptr = default_resize_align( a, old_memory, old_size, size, alignment ); + } + break; + +#elif defined( GEN_SYSTEM_LINUX ) && ! defined( GEN_CPU_ARM ) && ! defined( GEN_COMPILER_TINYC ) + case EAllocation_ALLOC : + { + ptr = aligned_alloc( alignment, ( size + alignment - 1 ) & ~( alignment - 1 ) ); + + if ( flags & GEN_ALLOCATOR_FLAG_CLEAR_TO_ZERO ) + { + zero_size( ptr, size ); + } + } + break; + + case EAllocation_FREE : + { + free( old_memory ); + } + break; + + case EAllocation_RESIZE : + { + AllocatorInfo a = heap(); + ptr = default_resize_align( a, old_memory, old_size, size, alignment ); + } + break; +#else + case EAllocation_ALLOC : + { + posix_memalign( &ptr, alignment, size ); + + if ( flags & GEN_ALLOCATOR_FLAG_CLEAR_TO_ZERO ) + { + zero_size( ptr, size ); + } + } + break; + + case EAllocation_FREE : + { + free( old_memory ); + } + break; + + case EAllocation_RESIZE : + { + AllocatorInfo a = heap(); + ptr = default_resize_align( a, old_memory, old_size, size, alignment ); + } + break; +#endif + + case EAllocation_FREE_ALL : + break; + } + +#ifdef GEN_HEAP_ANALYSIS + if ( type == EAllocation_ALLOC ) + { + _heap_alloc_info* alloc_info = rcast( _heap_alloc_info*, rcast( char*, ptr) + alloc_info_remainder ); + zero_item( alloc_info ); + alloc_info->size = size - track_size; + alloc_info->physical_start = ptr; + ptr = rcast( void*, alloc_info + 1 ); + _heap_stats_info.used_memory += alloc_info->size; + _heap_stats_info.alloc_count++; + } +#endif + + return ptr; +} + +#pragma region VirtualMemory +VirtualMemory vm_from_memory( void* data, ssize size ) +{ + VirtualMemory vm; + vm.data = data; + vm.size = size; + return vm; +} + +#if defined( GEN_SYSTEM_WINDOWS ) +VirtualMemory vm_alloc( void* addr, ssize size ) +{ + VirtualMemory vm; + GEN_ASSERT( size > 0 ); + vm.data = VirtualAlloc( addr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE ); + vm.size = size; + return vm; +} + +b32 vm_free( VirtualMemory vm ) +{ + MEMORY_BASIC_INFORMATION info; + while ( vm.size > 0 ) + { + if ( VirtualQuery( vm.data, &info, size_of( info ) ) == 0 ) + return false; + if ( info.BaseAddress != vm.data || info.AllocationBase != vm.data || info.State != MEM_COMMIT || info.RegionSize > scast( usize, vm.size) ) + { + return false; + } + if ( VirtualFree( vm.data, 0, MEM_RELEASE ) == 0 ) + return false; + vm.data = pointer_add( vm.data, info.RegionSize ); + vm.size -= info.RegionSize; + } + return true; +} + +VirtualMemory vm_trim( VirtualMemory vm, ssize lead_size, ssize size ) +{ + VirtualMemory new_vm = { 0 }; + void* ptr; + GEN_ASSERT( vm.size >= lead_size + size ); + + ptr = pointer_add( vm.data, lead_size ); + + vm_free( vm ); + new_vm = vm_alloc( ptr, size ); + if ( new_vm.data == ptr ) + return new_vm; + if ( new_vm.data ) + vm_free( new_vm ); + return new_vm; +} + +b32 vm_purge( VirtualMemory vm ) +{ + VirtualAlloc( vm.data, vm.size, MEM_RESET, PAGE_READWRITE ); + // NOTE: Can this really fail? + return true; +} + +ssize virtual_memory_page_size( ssize* alignment_out ) +{ + SYSTEM_INFO info; + GetSystemInfo( &info ); + if ( alignment_out ) + *alignment_out = info.dwAllocationGranularity; + return info.dwPageSize; +} + +#else +# include + +# ifndef MAP_ANONYMOUS +# define MAP_ANONYMOUS MAP_ANON +# endif +VirtualMemory vm_alloc( void* addr, ssize size ) +{ + VirtualMemory vm; + GEN_ASSERT( size > 0 ); + vm.data = mmap( addr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0 ); + vm.size = size; + return vm; +} + +b32 vm_free( VirtualMemory vm ) +{ + munmap( vm.data, vm.size ); + return true; +} + +VirtualMemory vm_trim( VirtualMemory vm, ssize lead_size, ssize size ) +{ + void* ptr; + ssize trail_size; + GEN_ASSERT( vm.size >= lead_size + size ); + + ptr = pointer_add( vm.data, lead_size ); + trail_size = vm.size - lead_size - size; + + if ( lead_size != 0 ) + vm_free( vm_from_memory(( vm.data, lead_size ) ); + if ( trail_size != 0 ) + vm_free( vm_from_memory( ptr, trail_size ) ); + return vm_from_memory( ptr, size ); +} + +b32 vm_purge( VirtualMemory vm ) +{ + int err = madvise( vm.data, vm.size, MADV_DONTNEED ); + return err != 0; +} + +ssize virtual_memory_page_size( ssize* alignment_out ) +{ + // TODO: Is this always true? + ssize result = scast( ssize, sysconf( _SC_PAGE_SIZE )); + if ( alignment_out ) + *alignment_out = result; + return result; +} +#endif + +#pragma endregion VirtualMemory + +void* Arena::allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ) +{ + Arena* arena = rcast(Arena*, allocator_data); + void* ptr = NULL; + + // unused( old_size ); + + switch ( type ) + { + case EAllocation_ALLOC : + { + void* end = pointer_add( arena->PhysicalStart, arena->TotalUsed ); + ssize total_size = align_forward_i64( size, alignment ); + + // NOTE: Out of memory + if ( arena->TotalUsed + total_size > (ssize) arena->TotalSize ) + { + // zpl__printf_err("%s", "Arena out of memory\n"); + GEN_FATAL("Arena out of memory! (Possibly could not fit for the largest size Arena!!)"); + return nullptr; + } + + ptr = align_forward( end, alignment ); + arena->TotalUsed += total_size; + + if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO ) + zero_size( ptr, size ); + } + break; + + case EAllocation_FREE : + // NOTE: Free all at once + // Use Temp_Arena_Memory if you want to free a block + break; + + case EAllocation_FREE_ALL : + arena->TotalUsed = 0; + break; + + case EAllocation_RESIZE : + { + // TODO : Check if ptr is on top of stack and just extend + AllocatorInfo a = arena->Backing; + ptr = default_resize_align( a, old_memory, old_size, size, alignment ); + } + break; + } + return ptr; +} + +void* Pool::allocator_proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ) +{ + Pool* pool = rcast( Pool*, allocator_data); + void* ptr = NULL; + + // unused( old_size ); + + switch ( type ) + { + case EAllocation_ALLOC : + { + uptr next_free; + + GEN_ASSERT( size == pool->BlockSize ); + GEN_ASSERT( alignment == pool->BlockAlign ); + GEN_ASSERT( pool->FreeList != NULL ); + + next_free = * rcast( uptr*, pool->FreeList); + ptr = pool->FreeList; + pool->FreeList = rcast( void*, next_free); + pool->TotalSize += pool->BlockSize; + + if ( flags & ALLOCATOR_FLAG_CLEAR_TO_ZERO ) + zero_size( ptr, size ); + } + break; + + case EAllocation_FREE : + { + uptr* next; + if ( old_memory == NULL ) + return NULL; + + next = rcast( uptr*, old_memory); + *next = rcast( uptr, pool->FreeList); + pool->FreeList = old_memory; + pool->TotalSize -= pool->BlockSize; + } + break; + + case EAllocation_FREE_ALL : + { + ssize actual_block_size, block_index; + void* curr; + uptr* end; + + actual_block_size = pool->BlockSize + pool->BlockAlign; + pool->TotalSize = 0; + + // NOTE: Init intrusive freelist + curr = pool->PhysicalStart; + for ( block_index = 0; block_index < pool->NumBlocks - 1; block_index++ ) + { + uptr* next = rcast( uptr*, curr); + * next = rcast( uptr, curr) + actual_block_size; + curr = pointer_add( curr, actual_block_size ); + } + + end = rcast( uptr*, curr); + * end = scast( uptr, NULL); + pool->FreeList = pool->PhysicalStart; + } + break; + + case EAllocation_RESIZE : + // NOTE: Cannot resize + GEN_PANIC( "You cannot resize something allocated by with a pool." ); + break; + } + + return ptr; +} + +Pool Pool::init_align( AllocatorInfo backing, ssize num_blocks, ssize block_size, ssize block_align ) +{ + Pool pool = {}; + + ssize actual_block_size, pool_size, block_index; + void *data, *curr; + uptr* end; + + zero_item( &pool ); + + pool.Backing = backing; + pool.BlockSize = block_size; + pool.BlockAlign = block_align; + pool.NumBlocks = num_blocks; + + actual_block_size = block_size + block_align; + pool_size = num_blocks * actual_block_size; + + data = alloc_align( backing, pool_size, block_align ); + + // NOTE: Init intrusive freelist + curr = data; + for ( block_index = 0; block_index < num_blocks - 1; block_index++ ) + { + uptr* next = ( uptr* ) curr; + *next = ( uptr ) curr + actual_block_size; + curr = pointer_add( curr, actual_block_size ); + } + + end = ( uptr* ) curr; + *end = ( uptr ) NULL; + + pool.PhysicalStart = data; + pool.FreeList = data; + + return pool; +} + +void Pool::clear() +{ + ssize actual_block_size, block_index; + void* curr; + uptr* end; + + actual_block_size = BlockSize + BlockAlign; + + curr = PhysicalStart; + for ( block_index = 0; block_index < NumBlocks - 1; block_index++ ) + { + uptr* next = ( uptr* ) curr; + *next = ( uptr ) curr + actual_block_size; + curr = pointer_add( curr, actual_block_size ); + } + + end = ( uptr* ) curr; + *end = ( uptr ) NULL; + + FreeList = PhysicalStart; +} + +#pragma endregion Memory + +#pragma region Hashing + +global u32 const _crc32_table[ 256 ] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, + 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, + 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, + 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, + 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, + 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, + 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +u32 crc32( void const* data, ssize len ) +{ + ssize remaining; + u32 result = ~( scast( u32, 0) ); + u8 const* c = rcast( u8 const*, data); + for ( remaining = len; remaining--; c++ ) + result = ( result >> 8 ) ^ ( _crc32_table[ ( result ^ *c ) & 0xff ] ); + return ~result; +} + +global u64 const _crc64_table[ 256 ] = { + 0x0000000000000000ull, 0x7ad870c830358979ull, 0xf5b0e190606b12f2ull, 0x8f689158505e9b8bull, 0xc038e5739841b68full, 0xbae095bba8743ff6ull, 0x358804e3f82aa47dull, + 0x4f50742bc81f2d04ull, 0xab28ecb46814fe75ull, 0xd1f09c7c5821770cull, 0x5e980d24087fec87ull, 0x24407dec384a65feull, 0x6b1009c7f05548faull, 0x11c8790fc060c183ull, + 0x9ea0e857903e5a08ull, 0xe478989fa00bd371ull, 0x7d08ff3b88be6f81ull, 0x07d08ff3b88be6f8ull, 0x88b81eabe8d57d73ull, 0xf2606e63d8e0f40aull, 0xbd301a4810ffd90eull, + 0xc7e86a8020ca5077ull, 0x4880fbd87094cbfcull, 0x32588b1040a14285ull, 0xd620138fe0aa91f4ull, 0xacf86347d09f188dull, 0x2390f21f80c18306ull, 0x594882d7b0f40a7full, + 0x1618f6fc78eb277bull, 0x6cc0863448deae02ull, 0xe3a8176c18803589ull, 0x997067a428b5bcf0ull, 0xfa11fe77117cdf02ull, 0x80c98ebf2149567bull, 0x0fa11fe77117cdf0ull, + 0x75796f2f41224489ull, 0x3a291b04893d698dull, 0x40f16bccb908e0f4ull, 0xcf99fa94e9567b7full, 0xb5418a5cd963f206ull, 0x513912c379682177ull, 0x2be1620b495da80eull, + 0xa489f35319033385ull, 0xde51839b2936bafcull, 0x9101f7b0e12997f8ull, 0xebd98778d11c1e81ull, 0x64b116208142850aull, 0x1e6966e8b1770c73ull, 0x8719014c99c2b083ull, + 0xfdc17184a9f739faull, 0x72a9e0dcf9a9a271ull, 0x08719014c99c2b08ull, 0x4721e43f0183060cull, 0x3df994f731b68f75ull, 0xb29105af61e814feull, 0xc849756751dd9d87ull, + 0x2c31edf8f1d64ef6ull, 0x56e99d30c1e3c78full, 0xd9810c6891bd5c04ull, 0xa3597ca0a188d57dull, 0xec09088b6997f879ull, 0x96d1784359a27100ull, 0x19b9e91b09fcea8bull, + 0x636199d339c963f2ull, 0xdf7adabd7a6e2d6full, 0xa5a2aa754a5ba416ull, 0x2aca3b2d1a053f9dull, 0x50124be52a30b6e4ull, 0x1f423fcee22f9be0ull, 0x659a4f06d21a1299ull, + 0xeaf2de5e82448912ull, 0x902aae96b271006bull, 0x74523609127ad31aull, 0x0e8a46c1224f5a63ull, 0x81e2d7997211c1e8ull, 0xfb3aa75142244891ull, 0xb46ad37a8a3b6595ull, + 0xceb2a3b2ba0eececull, 0x41da32eaea507767ull, 0x3b024222da65fe1eull, 0xa2722586f2d042eeull, 0xd8aa554ec2e5cb97ull, 0x57c2c41692bb501cull, 0x2d1ab4dea28ed965ull, + 0x624ac0f56a91f461ull, 0x1892b03d5aa47d18ull, 0x97fa21650afae693ull, 0xed2251ad3acf6feaull, 0x095ac9329ac4bc9bull, 0x7382b9faaaf135e2ull, 0xfcea28a2faafae69ull, + 0x8632586aca9a2710ull, 0xc9622c4102850a14ull, 0xb3ba5c8932b0836dull, 0x3cd2cdd162ee18e6ull, 0x460abd1952db919full, 0x256b24ca6b12f26dull, 0x5fb354025b277b14ull, + 0xd0dbc55a0b79e09full, 0xaa03b5923b4c69e6ull, 0xe553c1b9f35344e2ull, 0x9f8bb171c366cd9bull, 0x10e3202993385610ull, 0x6a3b50e1a30ddf69ull, 0x8e43c87e03060c18ull, + 0xf49bb8b633338561ull, 0x7bf329ee636d1eeaull, 0x012b592653589793ull, 0x4e7b2d0d9b47ba97ull, 0x34a35dc5ab7233eeull, 0xbbcbcc9dfb2ca865ull, 0xc113bc55cb19211cull, + 0x5863dbf1e3ac9decull, 0x22bbab39d3991495ull, 0xadd33a6183c78f1eull, 0xd70b4aa9b3f20667ull, 0x985b3e827bed2b63ull, 0xe2834e4a4bd8a21aull, 0x6debdf121b863991ull, + 0x1733afda2bb3b0e8ull, 0xf34b37458bb86399ull, 0x8993478dbb8deae0ull, 0x06fbd6d5ebd3716bull, 0x7c23a61ddbe6f812ull, 0x3373d23613f9d516ull, 0x49aba2fe23cc5c6full, + 0xc6c333a67392c7e4ull, 0xbc1b436e43a74e9dull, 0x95ac9329ac4bc9b5ull, 0xef74e3e19c7e40ccull, 0x601c72b9cc20db47ull, 0x1ac40271fc15523eull, 0x5594765a340a7f3aull, + 0x2f4c0692043ff643ull, 0xa02497ca54616dc8ull, 0xdafce7026454e4b1ull, 0x3e847f9dc45f37c0ull, 0x445c0f55f46abeb9ull, 0xcb349e0da4342532ull, 0xb1eceec59401ac4bull, + 0xfebc9aee5c1e814full, 0x8464ea266c2b0836ull, 0x0b0c7b7e3c7593bdull, 0x71d40bb60c401ac4ull, 0xe8a46c1224f5a634ull, 0x927c1cda14c02f4dull, 0x1d148d82449eb4c6ull, + 0x67ccfd4a74ab3dbfull, 0x289c8961bcb410bbull, 0x5244f9a98c8199c2ull, 0xdd2c68f1dcdf0249ull, 0xa7f41839ecea8b30ull, 0x438c80a64ce15841ull, 0x3954f06e7cd4d138ull, + 0xb63c61362c8a4ab3ull, 0xcce411fe1cbfc3caull, 0x83b465d5d4a0eeceull, 0xf96c151de49567b7ull, 0x76048445b4cbfc3cull, 0x0cdcf48d84fe7545ull, 0x6fbd6d5ebd3716b7ull, + 0x15651d968d029fceull, 0x9a0d8ccedd5c0445ull, 0xe0d5fc06ed698d3cull, 0xaf85882d2576a038ull, 0xd55df8e515432941ull, 0x5a3569bd451db2caull, 0x20ed197575283bb3ull, + 0xc49581ead523e8c2ull, 0xbe4df122e51661bbull, 0x3125607ab548fa30ull, 0x4bfd10b2857d7349ull, 0x04ad64994d625e4dull, 0x7e7514517d57d734ull, 0xf11d85092d094cbfull, + 0x8bc5f5c11d3cc5c6ull, 0x12b5926535897936ull, 0x686de2ad05bcf04full, 0xe70573f555e26bc4ull, 0x9ddd033d65d7e2bdull, 0xd28d7716adc8cfb9ull, 0xa85507de9dfd46c0ull, + 0x273d9686cda3dd4bull, 0x5de5e64efd965432ull, 0xb99d7ed15d9d8743ull, 0xc3450e196da80e3aull, 0x4c2d9f413df695b1ull, 0x36f5ef890dc31cc8ull, 0x79a59ba2c5dc31ccull, + 0x037deb6af5e9b8b5ull, 0x8c157a32a5b7233eull, 0xf6cd0afa9582aa47ull, 0x4ad64994d625e4daull, 0x300e395ce6106da3ull, 0xbf66a804b64ef628ull, 0xc5bed8cc867b7f51ull, + 0x8aeeace74e645255ull, 0xf036dc2f7e51db2cull, 0x7f5e4d772e0f40a7ull, 0x05863dbf1e3ac9deull, 0xe1fea520be311aafull, 0x9b26d5e88e0493d6ull, 0x144e44b0de5a085dull, + 0x6e963478ee6f8124ull, 0x21c640532670ac20ull, 0x5b1e309b16452559ull, 0xd476a1c3461bbed2ull, 0xaeaed10b762e37abull, 0x37deb6af5e9b8b5bull, 0x4d06c6676eae0222ull, + 0xc26e573f3ef099a9ull, 0xb8b627f70ec510d0ull, 0xf7e653dcc6da3dd4ull, 0x8d3e2314f6efb4adull, 0x0256b24ca6b12f26ull, 0x788ec2849684a65full, 0x9cf65a1b368f752eull, + 0xe62e2ad306bafc57ull, 0x6946bb8b56e467dcull, 0x139ecb4366d1eea5ull, 0x5ccebf68aecec3a1ull, 0x2616cfa09efb4ad8ull, 0xa97e5ef8cea5d153ull, 0xd3a62e30fe90582aull, + 0xb0c7b7e3c7593bd8ull, 0xca1fc72bf76cb2a1ull, 0x45775673a732292aull, 0x3faf26bb9707a053ull, 0x70ff52905f188d57ull, 0x0a2722586f2d042eull, 0x854fb3003f739fa5ull, + 0xff97c3c80f4616dcull, 0x1bef5b57af4dc5adull, 0x61372b9f9f784cd4ull, 0xee5fbac7cf26d75full, 0x9487ca0fff135e26ull, 0xdbd7be24370c7322ull, 0xa10fceec0739fa5bull, + 0x2e675fb4576761d0ull, 0x54bf2f7c6752e8a9ull, 0xcdcf48d84fe75459ull, 0xb71738107fd2dd20ull, 0x387fa9482f8c46abull, 0x42a7d9801fb9cfd2ull, 0x0df7adabd7a6e2d6ull, + 0x772fdd63e7936bafull, 0xf8474c3bb7cdf024ull, 0x829f3cf387f8795dull, 0x66e7a46c27f3aa2cull, 0x1c3fd4a417c62355ull, 0x935745fc4798b8deull, 0xe98f353477ad31a7ull, + 0xa6df411fbfb21ca3ull, 0xdc0731d78f8795daull, 0x536fa08fdfd90e51ull, 0x29b7d047efec8728ull, +}; + +u64 crc64( void const* data, ssize len ) +{ + ssize remaining; + u64 result = ( scast( u64, 0) ); + u8 const* c = rcast( u8 const*, data); + for ( remaining = len; remaining--; c++ ) + result = ( result >> 8 ) ^ ( _crc64_table[ ( result ^ *c ) & 0xff ] ); + return result; +} + +#pragma endregion Hashing + +#pragma region String + +String String::fmt( AllocatorInfo allocator, char* buf, ssize buf_size, char const* fmt, ... ) +{ + va_list va; + va_start( va, fmt ); + str_fmt_va( buf, buf_size, fmt, va ); + va_end( va ); + + return make( allocator, buf ); +} + +String String::make_length( AllocatorInfo allocator, char const* str, ssize length ) +{ + constexpr ssize header_size = sizeof( Header ); + + s32 alloc_size = header_size + length + 1; + void* allocation = alloc( allocator, alloc_size ); + + if ( allocation == nullptr ) + return { nullptr }; + + Header& + header = * rcast(Header*, allocation); + header = { allocator, length, length }; + + String result = { rcast( char*, allocation) + header_size }; + + if ( length && str ) + mem_copy( result, str, length ); + else + mem_set( result, 0, alloc_size - header_size ); + + result[ length ] = '\0'; + + return result; +} + +String String::make_reserve( AllocatorInfo allocator, ssize capacity ) +{ + constexpr ssize header_size = sizeof( Header ); + + s32 alloc_size = header_size + capacity + 1; + void* allocation = alloc( allocator, alloc_size ); + + if ( allocation == nullptr ) + return { nullptr }; + + mem_set( allocation, 0, alloc_size ); + + Header* + header = rcast(Header*, allocation); + header->Allocator = allocator; + header->Capacity = capacity; + header->Length = 0; + + String result = { rcast(char*, allocation) + header_size }; + return result; +} + +String String::fmt_buf( AllocatorInfo allocator, char const* fmt, ... ) +{ + local_persist thread_local + char buf[ GEN_PRINTF_MAXLEN ] = { 0 }; + + va_list va; + va_start( va, fmt ); + str_fmt_va( buf, GEN_PRINTF_MAXLEN, fmt, va ); + va_end( va ); + + return make( allocator, buf ); +} + +bool String::append_fmt( char const* fmt, ... ) +{ + ssize res; + char buf[ GEN_PRINTF_MAXLEN ] = { 0 }; + + va_list va; + va_start( va, fmt ); + res = str_fmt_va( buf, count_of( buf ) - 1, fmt, va ) - 1; + va_end( va ); + + return append( buf, res ); +} + +bool String::make_space_for( char const* str, ssize add_len ) +{ + ssize available = avail_space(); + + // NOTE: Return if there is enough space left + if ( available >= add_len ) + { + return true; + } + else + { + ssize new_len, old_size, new_size; + + void* ptr; + void* new_ptr; + + AllocatorInfo allocator = get_header().Allocator; + Header* header = nullptr; + + new_len = grow_formula( length() + add_len ); + ptr = & get_header(); + old_size = size_of( Header ) + length() + 1; + new_size = size_of( Header ) + new_len + 1; + + new_ptr = resize( allocator, ptr, old_size, new_size ); + + if ( new_ptr == nullptr ) + return false; + + header = rcast( Header*, new_ptr); + header->Allocator = allocator; + header->Capacity = new_len; + + Data = rcast( char*, header + 1 ); + + return true; + } +} +#pragma endregion String + +#pragma region File Handling + +#if defined( GEN_SYSTEM_WINDOWS ) || defined( GEN_SYSTEM_CYGWIN ) + +internal +wchar_t* _alloc_utf8_to_ucs2( AllocatorInfo a, char const* text, ssize* w_len_ ) +{ + wchar_t* w_text = NULL; + ssize len = 0, w_len = 0, w_len1 = 0; + if ( text == NULL ) + { + if ( w_len_ ) + *w_len_ = w_len; + return NULL; + } + len = str_len( text ); + if ( len == 0 ) + { + if ( w_len_ ) + *w_len_ = w_len; + return NULL; + } + w_len = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, text, scast( int, len), NULL, 0 ); + if ( w_len == 0 ) + { + if ( w_len_ ) + *w_len_ = w_len; + return NULL; + } + w_text = alloc_array( a, wchar_t, w_len + 1 ); + w_len1 = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, text, scast( int, len), w_text, scast( int, w_len) ); + if ( w_len1 == 0 ) + { + free( a, w_text ); + if ( w_len_ ) + *w_len_ = 0; + return NULL; + } + w_text[ w_len ] = 0; + if ( w_len_ ) + *w_len_ = w_len; + return w_text; +} + +internal +GEN_FILE_SEEK_PROC( _win32_file_seek ) +{ + LARGE_INTEGER li_offset; + li_offset.QuadPart = offset; + if ( ! SetFilePointerEx( fd.p, li_offset, &li_offset, whence ) ) + { + return false; + } + + if ( new_offset ) + *new_offset = li_offset.QuadPart; + return true; +} + +internal +GEN_FILE_READ_AT_PROC( _win32_file_read ) +{ + // unused( stop_at_newline ); + b32 result = false; + _win32_file_seek( fd, offset, ESeekWhence_BEGIN, NULL ); + DWORD size_ = scast( DWORD, ( size > GEN_I32_MAX ? GEN_I32_MAX : size )); + DWORD bytes_read_; + if ( ReadFile( fd.p, buffer, size_, &bytes_read_, NULL ) ) + { + if ( bytes_read ) + *bytes_read = bytes_read_; + result = true; + } + + return result; +} + +internal +GEN_FILE_WRITE_AT_PROC( _win32_file_write ) +{ + DWORD size_ = scast( DWORD, ( size > GEN_I32_MAX ? GEN_I32_MAX : size )); + DWORD bytes_written_; + _win32_file_seek( fd, offset, ESeekWhence_BEGIN, NULL ); + if ( WriteFile( fd.p, buffer, size_, &bytes_written_, NULL ) ) + { + if ( bytes_written ) + *bytes_written = bytes_written_; + return true; + } + return false; +} + +internal +GEN_FILE_CLOSE_PROC( _win32_file_close ) +{ + CloseHandle( fd.p ); +} + +FileOperations const default_file_operations = { _win32_file_read, _win32_file_write, _win32_file_seek, _win32_file_close }; + +neverinline +GEN_FILE_OPEN_PROC( _win32_file_open ) +{ + DWORD desired_access; + DWORD creation_disposition; + void* handle; + wchar_t* w_text; + + switch ( mode & GEN_FILE_MODES ) + { + case EFileMode_READ : + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + break; + case EFileMode_WRITE : + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + break; + case EFileMode_APPEND : + desired_access = GENERIC_WRITE; + creation_disposition = OPEN_ALWAYS; + break; + case EFileMode_READ | EFileMode_RW : + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + break; + case EFileMode_WRITE | EFileMode_RW : + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + break; + case EFileMode_APPEND | EFileMode_RW : + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_ALWAYS; + break; + default : + GEN_PANIC( "Invalid file mode" ); + return EFileError_INVALID; + } + + w_text = _alloc_utf8_to_ucs2( heap(), filename, NULL ); + handle = CreateFileW( w_text, desired_access, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL ); + + free( heap(), w_text ); + + if ( handle == INVALID_HANDLE_VALUE ) + { + DWORD err = GetLastError(); + switch ( err ) + { + case ERROR_FILE_NOT_FOUND : + return EFileError_NOT_EXISTS; + case ERROR_FILE_EXISTS : + return EFileError_EXISTS; + case ERROR_ALREADY_EXISTS : + return EFileError_EXISTS; + case ERROR_ACCESS_DENIED : + return EFileError_PERMISSION; + } + return EFileError_INVALID; + } + + if ( mode & EFileMode_APPEND ) + { + LARGE_INTEGER offset = { { 0 } }; + if ( ! SetFilePointerEx( handle, offset, NULL, ESeekWhence_END ) ) + { + CloseHandle( handle ); + return EFileError_INVALID; + } + } + + fd->p = handle; + *ops = default_file_operations; + return EFileError_NONE; +} + +#else // POSIX +# include + +internal +GEN_FILE_SEEK_PROC( _posix_file_seek ) +{ +# if defined( GEN_SYSTEM_OSX ) + s64 res = lseek( fd.i, offset, whence ); +# else // TODO(ZaKlaus): @fixme lseek64 + s64 res = lseek( fd.i, offset, whence ); +# endif + if ( res < 0 ) + return false; + if ( new_offset ) + *new_offset = res; + return true; +} + +internal +GEN_FILE_READ_AT_PROC( _posix_file_read ) +{ + unused( stop_at_newline ); + ssize res = pread( fd.i, buffer, size, offset ); + if ( res < 0 ) + return false; + if ( bytes_read ) + *bytes_read = res; + return true; +} + +internal +GEN_FILE_WRITE_AT_PROC( _posix_file_write ) +{ + ssize res; + s64 curr_offset = 0; + _posix_file_seek( fd, 0, ESeekWhence_CURRENT, &curr_offset ); + if ( curr_offset == offset ) + { + // NOTE: Writing to stdout et al. doesn't like pwrite for numerous reasons + res = write( scast( int, fd.i), buffer, size ); + } + else + { + res = pwrite( scast( int, fd.i), buffer, size, offset ); + } + if ( res < 0 ) + return false; + if ( bytes_written ) + *bytes_written = res; + return true; +} + +internal +GEN_FILE_CLOSE_PROC( _posix_file_close ) +{ + close( fd.i ); +} + +FileOperations const default_file_operations = { _posix_file_read, _posix_file_write, _posix_file_seek, _posix_file_close }; + +neverinline +GEN_FILE_OPEN_PROC( _posix_file_open ) +{ + s32 os_mode; + switch ( mode & GEN_FILE_MODES ) + { + case EFileMode_READ : + os_mode = O_RDONLY; + break; + case EFileMode_WRITE : + os_mode = O_WRONLY | O_CREAT | O_TRUNC; + break; + case EFileMode_APPEND : + os_mode = O_WRONLY | O_APPEND | O_CREAT; + break; + case EFileMode_READ | EFileMode_RW : + os_mode = O_RDWR; + break; + case EFileMode_WRITE | EFileMode_RW : + os_mode = O_RDWR | O_CREAT | O_TRUNC; + break; + case EFileMode_APPEND | EFileMode_RW : + os_mode = O_RDWR | O_APPEND | O_CREAT; + break; + default : + GEN_PANIC( "Invalid file mode" ); + return EFileError_INVALID; + } + + fd->i = open( filename, os_mode, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ); + if ( fd->i < 0 ) + { + // TODO : More file errors + return EFileError_INVALID; + } + + *ops = default_file_operations; + return EFileError_NONE; +} + +// POSIX +#endif + +internal void _dirinfo_free_entry( DirEntry* entry ); + +// TODO : Is this a bad idea? +global b32 _std_file_set = false; +global FileInfo _std_files[ EFileStandard_COUNT ] = { +{ + { nullptr, nullptr, nullptr, nullptr }, + { nullptr }, + 0, + nullptr, + 0, + nullptr +} }; + +#if defined( GEN_SYSTEM_WINDOWS ) || defined( GEN_SYSTEM_CYGWIN ) + +FileInfo* file_get_standard( FileStandardType std ) +{ + if ( ! _std_file_set ) + { +# define GEN__SET_STD_FILE( type, v ) \ + _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 ) ); +# undef GEN__SET_STD_FILE + _std_file_set = true; + } + return &_std_files[ std ]; +} + +#else // POSIX + +FileInfo* file_get_standard( FileStandardType std ) +{ + if ( ! _std_file_set ) + { +# define GEN__SET_STD_FILE( type, v ) \ + _std_files[ type ].fd.i = v; \ + _std_files[ type ].ops = default_file_operations + GEN__SET_STD_FILE( EFileStandard_INPUT, 0 ); + GEN__SET_STD_FILE( EFileStandard_OUTPUT, 1 ); + GEN__SET_STD_FILE( EFileStandard_ERROR, 2 ); +# undef GEN__SET_STD_FILE + _std_file_set = true; + } + return &_std_files[ std ]; +} + +#endif + +FileError file_close( FileInfo* f ) +{ + if ( ! f ) + return EFileError_INVALID; + + if ( f->filename ) + free( heap(), ccast( char*, f->filename )); + +#if defined( GEN_SYSTEM_WINDOWS ) + if ( f->fd.p == INVALID_HANDLE_VALUE ) + return EFileError_INVALID; +#else + if ( f->fd.i < 0 ) + return EFileError_INVALID; +#endif + + if ( f->is_temp ) + { + f->ops.close( f->fd ); + return EFileError_NONE; + } + + if ( ! f->ops.read_at ) + f->ops = default_file_operations; + f->ops.close( f->fd ); + +#if 0 + if ( f->Dir ) + { + _dirinfo_free_entry( f->Dir ); + mfree( f->Dir ); + f->Dir = NULL; + } +#endif + + return EFileError_NONE; +} + +FileError file_new( FileInfo* f, FileDescriptor fd, FileOperations ops, char const* filename ) +{ + FileError err = EFileError_NONE; + ssize len = str_len( filename ); + + f->ops = ops; + f->fd = fd; + f->dir = nullptr; + f->last_write_time = 0; + f->filename = alloc_array( heap(), char, len + 1 ); + mem_copy( ccast( char*, f->filename), ccast( char*, filename), len + 1 ); + + return err; +} + +FileError file_open( FileInfo* f, char const* filename ) +{ + return file_open_mode( f, EFileMode_READ, filename ); +} + +FileError file_open_mode( FileInfo* f, FileMode mode, char const* filename ) +{ + FileInfo file_ = + { + { nullptr, nullptr, nullptr, nullptr }, + { nullptr }, + 0, + nullptr, + 0, + nullptr + }; + + *f = file_; + FileError err; + +#if defined( GEN_SYSTEM_WINDOWS ) || defined( GEN_SYSTEM_CYGWIN ) + 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 err; +} + +s64 file_size( FileInfo* f ) +{ + s64 size = 0; + s64 prev_offset = file_tell( f ); + + file_seek_to_end( f ); + size = file_tell( f ); + + file_seek( f, prev_offset ); + + return size; +} + +FileContents file_read_contents( AllocatorInfo a, b32 zero_terminate, char const* filepath ) +{ + FileContents result; + FileInfo file ; + + result.allocator = a; + + if ( file_open( &file, filepath ) == EFileError_NONE ) + { + ssize fsize = scast( ssize , file_size( &file )); + if ( fsize > 0 ) + { + result.data = alloc( a, zero_terminate ? fsize + 1 : fsize ); + result.size = fsize; + file_read_at( &file, result.data, result.size, 0 ); + if ( zero_terminate ) + { + u8* str = rcast( u8*, result.data); + str[ fsize ] = '\0'; + } + } + file_close( &file ); + } + + return result; +} + +struct _memory_fd +{ + u8 magic; + u8* buf; //< zpl_array OR plain buffer if we can't write + ssize cursor; + AllocatorInfo allocator; + + FileStreamFlags flags; + ssize cap; +}; + +#define GEN__FILE_STREAM_FD_MAGIC 37 + +FileDescriptor _file_stream_fd_make( _memory_fd* d ); +_memory_fd* _file_stream_from_fd( FileDescriptor fd ); + +inline +FileDescriptor _file_stream_fd_make( _memory_fd* d ) +{ + FileDescriptor fd = { 0 }; + fd.p = ( void* )d; + return fd; +} + +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, ssize 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, ssize* 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 ); + ssize 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; + + ssize buflen = d->cap; + ssize extralen = max( 0, size - ( buflen - offset ) ); + ssize rwlen = size - extralen; + ssize new_cap = buflen + extralen; + + if ( d->flags & EFileStream_CLONE_WRITABLE ) + { + Array arr = { d->buf }; + + if ( arr.get_header()->Capacity < usize(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 Timing + +#ifdef GEN_BENCHMARK + #if defined( GEN_COMPILER_MSVC ) && ! defined( __clang__ ) + u64 read_cpu_time_stamp_counter( void ) + { + return __rdtsc(); + } + #elif defined( __i386__ ) + u64 read_cpu_time_stamp_counter( void ) + { + u64 x; + __asm__ volatile( ".byte 0x0f, 0x31" : "=A"( x ) ); + return x; + } + #elif defined( __x86_64__ ) + u64 read_cpu_time_stamp_counter( void ) + { + u32 hi, lo; + __asm__ __volatile__( "rdtsc" : "=a"( lo ), "=d"( hi ) ); + return scast( u64, lo ) | ( scast( u64, hi ) << 32 ); + } + #elif defined( __powerpc__ ) + u64 read_cpu_time_stamp_counter( void ) + { + u64 result = 0; + u32 upper, lower, tmp; + __asm__ volatile( + "0: \n" + "\tmftbu %0 \n" + "\tmftb %1 \n" + "\tmftbu %2 \n" + "\tcmpw %2,%0 \n" + "\tbne 0b \n" + : "=r"( upper ), "=r"( lower ), "=r"( tmp ) + ); + result = upper; + result = result << 32; + result = result | lower; + + return result; + } + #elif defined( GEN_SYSTEM_EMSCRIPTEN ) + u64 read_cpu_time_stamp_counter( void ) + { + return ( u64 )( emscripten_get_now() * 1e+6 ); + } + #elif defined( GEN_CPU_ARM ) && ! defined( GEN_COMPILER_TINYC ) + u64 read_cpu_time_stamp_counter( void ) + { + # if defined( __aarch64__ ) + int64_t r = 0; + asm volatile( "mrs %0, cntvct_el0" : "=r"( r ) ); + # elif ( __ARM_ARCH >= 6 ) + uint32_t r = 0; + uint32_t pmccntr; + uint32_t pmuseren; + uint32_t pmcntenset; + + // Read the user mode perf monitor counter access permissions. + asm volatile( "mrc p15, 0, %0, c9, c14, 0" : "=r"( pmuseren ) ); + if ( pmuseren & 1 ) + { // Allows reading perfmon counters for user mode code. + asm volatile( "mrc p15, 0, %0, c9, c12, 1" : "=r"( pmcntenset ) ); + if ( pmcntenset & 0x80000000ul ) + { // Is it counting? + asm volatile( "mrc p15, 0, %0, c9, c13, 0" : "=r"( pmccntr ) ); + // The counter is set up to count every 64th cycle + return ( ( int64_t )pmccntr ) * 64; // Should optimize to << 6 + } + } + # else + # error "No suitable method for read_cpu_time_stamp_counter for this cpu type" + # endif + + return r; + } + #else + u64 read_cpu_time_stamp_counter( void ) + { + GEN_PANIC( "read_cpu_time_stamp_counter is not supported on this particular setup" ); + return -0; + } + #endif + + #if defined( GEN_SYSTEM_WINDOWS ) || defined( GEN_SYSTEM_CYGWIN ) + + u64 time_rel_ms( void ) + { + local_persist LARGE_INTEGER win32_perf_count_freq = {}; + u64 result; + LARGE_INTEGER counter; + local_persist LARGE_INTEGER win32_perf_counter = {}; + if ( ! win32_perf_count_freq.QuadPart ) + { + QueryPerformanceFrequency( &win32_perf_count_freq ); + GEN_ASSERT( win32_perf_count_freq.QuadPart != 0 ); + QueryPerformanceCounter( &win32_perf_counter ); + } + + QueryPerformanceCounter( &counter ); + + result = ( counter.QuadPart - win32_perf_counter.QuadPart ) * 1000 / ( win32_perf_count_freq.QuadPart ); + return result; + } + + #else + + # if defined( GEN_SYSTEM_LINUX ) || defined( GEN_SYSTEM_FREEBSD ) || defined( GEN_SYSTEM_OPENBSD ) || defined( GEN_SYSTEM_EMSCRIPTEN ) + u64 _unix_gettime( void ) + { + struct timespec t; + u64 result; + + clock_gettime( 1 /*CLOCK_MONOTONIC*/, &t ); + result = 1000 * t.tv_sec + 1.0e-6 * t.tv_nsec; + return result; + } + # endif + + u64 time_rel_ms( void ) + { + # if defined( GEN_SYSTEM_OSX ) + u64 result; + + local_persist u64 timebase = 0; + local_persist u64 timestart = 0; + + if ( ! timestart ) + { + mach_timebase_info_data_t tb = { 0 }; + mach_timebase_info( &tb ); + timebase = tb.numer; + timebase /= tb.denom; + timestart = mach_absolute_time(); + } + + // NOTE: mach_absolute_time() returns things in nanoseconds + result = 1.0e-6 * ( mach_absolute_time() - timestart ) * timebase; + return result; + # else + local_persist u64 unix_timestart = 0.0; + + if ( ! unix_timestart ) + { + unix_timestart = _unix_gettime(); + } + + u64 now = _unix_gettime(); + + return ( now - unix_timestart ); + # endif + } + #endif + + f64 time_rel( void ) + { + return ( f64 )( time_rel_ms() * 1e-3 ); + } +#endif + +#pragma endregion Timing + +#pragma region Parsing + +#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 ( ssize i = 0; i < scast(ssize, 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 ( ssize i = 0; i < scast(ssize, node->nodes.num()); i++ ) + { + if ( ! str_compare( node->nodes[ i ].name, name ) ) + { + return ( node->nodes + i ); + } + } + + if ( deep_search ) + { + for ( ssize i = 0; i < scast(ssize, 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 ); + + ssize 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 ( ssize i = 0; i < scast(ssize, 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 ( ssize i = 0; i < scast(ssize, 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 ( ssize i = 0; i < scast(ssize, 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 + { + ssize idx = ( ssize )str_to_i64( buf, NULL, 10 ); + if ( idx >= 0 && idx < scast(ssize, 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, ssize index ) +{ + if ( ! parent || ( parent->type != EADT_TYPE_OBJECT && parent->type != EADT_TYPE_ARRAY ) ) + { + return NULL; + } + + if ( ! parent->nodes ) + return NULL; + + if ( index < 0 || index > scast(ssize, 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, ssize 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; + ssize index = ( pointer_diff( parent->nodes, node ) / size_of( ADT_Node ) ); + ssize 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; + ssize 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; + + ssize 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 }; + ssize 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 = ccast( 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; + + ssize columnIndex = 0; + ssize totalColumnIndex = 0; + + do + { + char delimiter = 0; + currentChar = ccast( 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 = ccast( 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 = ccast( 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 = ccast( 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 >= scast(ssize, 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 ( ssize i = 0; i < scast(ssize, 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 ); + ssize cols = obj->nodes.num(); + if ( cols == 0 ) + return; + + ssize rows = obj->nodes[ 0 ].nodes.num(); + if ( rows == 0 ) + return; + + b32 has_headers = obj->nodes[ 0 ].name != NULL; + + if ( has_headers ) + { + for ( ssize 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 ( ssize r = 0; r < rows; r++ ) + { + for ( ssize 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 ); + + ssize 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 + +#pragma endregion Parsing + +GEN_NS_END + +// GEN_ROLL_OWN_DEPENDENCIES +#endif + +GEN_NS_BEGIN + +#pragma region StaticData + +// TODO : Convert global allocation strategy to use a slab allocation strategy. +global AllocatorInfo GlobalAllocator; +global Array Global_AllocatorBuckets; + +// TODO(Ed) : Make the code pool a dynamic arena +global Array< Pool > CodePools = { nullptr }; +global Array< Arena > StringArenas = { nullptr }; + +global StringTable StringCache; + +global Arena LexArena; + +global AllocatorInfo Allocator_DataArrays = heap(); +global AllocatorInfo Allocator_CodePool = heap(); +global AllocatorInfo Allocator_Lexer = heap(); +global AllocatorInfo Allocator_StringArena = heap(); +global AllocatorInfo Allocator_StringTable = heap(); +global AllocatorInfo Allocator_TypeTable = heap(); + +#pragma endregion StaticData + +#pragma region Constants + +global Code access_public; +global Code access_protected; +global Code access_private; + +global CodeAttributes attrib_api_export; +global CodeAttributes attrib_api_import; + +global Code module_global_fragment; +global Code module_private_fragment; + +global Code fmt_newline; + +global CodeParam param_varadic; + +global CodePragma pragma_once; + +global CodePreprocessCond preprocess_else; +global CodePreprocessCond preprocess_endif; + +global CodeSpecifiers spec_const; +global CodeSpecifiers spec_consteval; +global CodeSpecifiers spec_constexpr; +global CodeSpecifiers spec_constinit; +global CodeSpecifiers spec_extern_linkage; +global CodeSpecifiers spec_final; +global CodeSpecifiers spec_forceinline; +global CodeSpecifiers spec_global; +global CodeSpecifiers spec_inline; +global CodeSpecifiers spec_internal_linkage; +global CodeSpecifiers spec_local_persist; +global CodeSpecifiers spec_mutable; +global CodeSpecifiers spec_noexcept; +global CodeSpecifiers spec_neverinline; +global CodeSpecifiers spec_override; +global CodeSpecifiers spec_ptr; +global CodeSpecifiers spec_pure; +global CodeSpecifiers spec_ref; +global CodeSpecifiers spec_register; +global CodeSpecifiers spec_rvalue; +global CodeSpecifiers spec_static_member; +global CodeSpecifiers spec_thread_local; +global CodeSpecifiers spec_virtual; +global CodeSpecifiers spec_volatile; + +global CodeType t_empty; +global CodeType t_auto; +global CodeType t_void; +global CodeType t_int; +global CodeType t_bool; +global CodeType t_char; +global CodeType t_wchar_t; +global CodeType t_class; +global CodeType t_typename; + +global Array< StringCached > PreprocessorDefines; + +#ifdef GEN_DEFINE_LIBRARY_CODE_CONSTANTS +global CodeType t_b32; + +global CodeType t_s8; +global CodeType t_s16; +global CodeType t_s32; +global CodeType t_s64; + +global CodeType t_u8; +global CodeType t_u16; +global CodeType t_u32; +global CodeType t_u64; + +global CodeType t_ssize; +global CodeType t_usize; + +global CodeType t_f32; +global CodeType t_f64; +#endif + +#pragma endregion Constants +#pragma region AST + +# define GEN_AST_BODY_CLASS_UNALLOWED_TYPES \ + case PlatformAttributes: \ + case Class_Body: \ + case Enum_Body: \ + case Extern_Linkage: \ + case Function_Body: \ + case Function_Fwd: \ + case Global_Body: \ + case Namespace: \ + case Namespace_Body: \ + case Operator: \ + case Operator_Fwd: \ + case Parameters: \ + case Specifiers: \ + case Struct_Body: \ + case Typename: +# define GEN_AST_BODY_STRUCT_UNALLOWED_TYPES GEN_AST_BODY_CLASS_UNALLOWED_TYPES + +# define GEN_AST_BODY_FUNCTION_UNALLOWED_TYPES \ + case Access_Public: \ + case Access_Protected: \ + case Access_Private: \ + case PlatformAttributes: \ + case Class_Body: \ + case Enum_Body: \ + case Extern_Linkage: \ + case Friend: \ + case Function_Body: \ + case Function_Fwd: \ + case Global_Body: \ + case Namespace: \ + case Namespace_Body: \ + case Operator: \ + case Operator_Fwd: \ + case Operator_Member: \ + case Operator_Member_Fwd: \ + case Parameters: \ + case Specifiers: \ + case Struct_Body: \ + case Typename: + +# define GEN_AST_BODY_GLOBAL_UNALLOWED_TYPES \ + case Access_Public: \ + case Access_Protected: \ + case Access_Private: \ + case PlatformAttributes: \ + case Class_Body: \ + case Enum_Body: \ + case Execution: \ + case Friend: \ + case Function_Body: \ + case Namespace_Body: \ + case Operator_Member: \ + case Operator_Member_Fwd: \ + case Parameters: \ + case Specifiers: \ + case Struct_Body: \ + case Typename: +# define GEN_AST_BODY_EXPORT_UNALLOWED_TYPES GEN_AST_BODY_GLOBAL_UNALLOWED_TYPES +# define GEN_AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES GEN_AST_BODY_GLOBAL_UNALLOWED_TYPES + +# define GEN_AST_BODY_NAMESPACE_UNALLOWED_TYPES \ + case Access_Public: \ + case Access_Protected: \ + case Access_Private: \ + case PlatformAttributes: \ + case Class_Body: \ + case Enum_Body: \ + case Execution: \ + case Friend: \ + case Function_Body: \ + case Namespace_Body: \ + case Operator_Member: \ + case Operator_Member_Fwd: \ + case Parameters: \ + case Specifiers: \ + case Struct_Body: \ + case Typename: + +Code Code::Global; +Code Code::Invalid; + +// This serializes all the data-members in a "debug" format, where each member is printed with its associated value. +char const* AST::debug_str() +{ + String result = String::make_reserve( GlobalAllocator, kilobytes(1) ); + + if ( Parent ) + result.append_fmt( "\n\tParent : %S %S", Parent->type_str(), Name ? Name : "" ); + else + result.append_fmt( "\n\tParent : %S", "Null" ); + + result.append_fmt( "\n\tName : %S", Name ? Name : "Null" ); + result.append_fmt( "\n\tType : %S", type_str() ); + result.append_fmt( "\n\tModule Flags : %S", to_str( ModuleFlags ) ); + + switch ( Type ) + { + using namespace ECode; + + case Invalid: + case NewLine: + case Access_Private: + case Access_Protected: + case Access_Public: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + break; + + case Untyped: + case Execution: + case Comment: + case PlatformAttributes: + case Preprocess_Define: + case Preprocess_Include: + case Preprocess_Pragma: + case Preprocess_If: + case Preprocess_ElIf: + case Preprocess_Else: + case Preprocess_IfDef: + case Preprocess_IfNotDef: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tContent: %S", Content ); + break; + + case Class: + case Struct: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmd : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tAttributes : %S", Attributes ? Attributes->to_string() : "Null" ); + result.append_fmt( "\n\tParentAccess: %s", ParentType ? to_str( ParentAccess ) : "No Parent" ); + result.append_fmt( "\n\tParentType : %s", ParentType ? ParentType->type_str() : "Null" ); + result.append_fmt( "\n\tBody : %S", Body ? Body->debug_str() : "Null" ); + break; + + case Class_Fwd: + case Struct_Fwd: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmd : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tAttributes : %S", Attributes ? Attributes->to_string() : "Null" ); + result.append_fmt( "\n\tParentAccess: %s", ParentType ? to_str( ParentAccess ) : "No Parent" ); + result.append_fmt( "\n\tParentType : %s", ParentType ? ParentType->type_str() : "Null" ); + break; + + case Constructor: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tSpecs : %S", Specs ? Specs->to_string() : "Null" ); + result.append_fmt( "\n\tInitializerList: %S", InitializerList ? InitializerList->to_string() : "Null" ); + result.append_fmt( "\n\tParams : %S", Params ? Params->to_string() : "Null" ); + result.append_fmt( "\n\tBody : %S", Body ? Body->debug_str() : "Null" ); + break; + + case Constructor_Fwd: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tSpecs : %S", Specs ? Specs->to_string() : "Null" ); + result.append_fmt( "\n\tInitializerList: %S", InitializerList ? InitializerList->to_string() : "Null" ); + result.append_fmt( "\n\tParams : %S", Params ? Params->to_string() : "Null" ); + break; + + case Destructor: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tSpecs : %S", Specs ? Specs->to_string() : "Null" ); + result.append_fmt( "\n\tBody : %S", Body ? Body->debug_str() : "Null" ); + break; + + case Destructor_Fwd: + break; + + case Enum: + case Enum_Class: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tAttributes : %S", Attributes ? Attributes->to_string() : "Null" ); + result.append_fmt( "\n\tUnderlying Type : %S", UnderlyingType ? UnderlyingType->to_string() : "Null" ); + result.append_fmt( "\n\tBody : %S", Body ? Body->debug_str() : "Null" ); + break; + + case Enum_Fwd: + case Enum_Class_Fwd: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tAttributes : %S", Attributes ? Attributes->to_string() : "Null" ); + result.append_fmt( "\n\tUnderlying Type : %S", UnderlyingType ? UnderlyingType->to_string() : "Null" ); + break; + + case Extern_Linkage: + case Namespace: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tBody: %S", Body ? Body->debug_str() : "Null" ); + break; + + case Friend: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tDeclaration: %S", Declaration ? Declaration->to_string() : "Null" ); + break; + + case Function: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tAttributes: %S", Attributes ? Attributes->to_string() : "Null" ); + result.append_fmt( "\n\tSpecs : %S", Specs ? Specs->to_string() : "Null" ); + result.append_fmt( "\n\tReturnType: %S", ReturnType ? ReturnType->to_string() : "Null" ); + result.append_fmt( "\n\tParams : %S", Params ? Params->to_string() : "Null" ); + result.append_fmt( "\n\tBody : %S", Body ? Body->debug_str() : "Null" ); + break; + + case Function_Fwd: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tAttributes: %S", Attributes ? Attributes->to_string() : "Null" ); + result.append_fmt( "\n\tSpecs : %S", Specs ? Specs->to_string() : "Null" ); + result.append_fmt( "\n\tReturnType: %S", ReturnType ? ReturnType->to_string() : "Null" ); + result.append_fmt( "\n\tParams : %S", Params ? Params->to_string() : "Null" ); + break; + + case Module: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + break; + + case Operator: + case Operator_Member: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tAttributes: %S", Attributes ? Attributes->to_string() : "Null" ); + result.append_fmt( "\n\tSpecs : %S", Specs ? Specs->to_string() : "Null" ); + result.append_fmt( "\n\tReturnType: %S", ReturnType ? ReturnType->to_string() : "Null" ); + result.append_fmt( "\n\tParams : %S", Params ? Params->to_string() : "Null" ); + result.append_fmt( "\n\tBody : %S", Body ? Body->debug_str() : "Null" ); + result.append_fmt( "\n\tOp : %S", to_str( Op ) ); + break; + + case Operator_Fwd: + case Operator_Member_Fwd: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tAttributes: %S", Attributes ? Attributes->to_string() : "Null" ); + result.append_fmt( "\n\tSpecs : %S", Specs ? Specs->to_string() : "Null" ); + result.append_fmt( "\n\tReturnType: %S", ReturnType ? ReturnType->to_string() : "Null" ); + result.append_fmt( "\n\tParams : %S", Params ? Params->to_string() : "Null" ); + result.append_fmt( "\n\tOp : %S", to_str( Op ) ); + break; + + case Operator_Cast: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tSpecs : %S", Specs ? Specs->to_string() : "Null" ); + result.append_fmt( "\n\tValueType : %S", ValueType ? ValueType->to_string() : "Null" ); + result.append_fmt( "\n\tBody : %S", Body ? Body->debug_str() : "Null" ); + break; + + case Operator_Cast_Fwd: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tSpecs : %S", Specs ? Specs->to_string() : "Null" ); + result.append_fmt( "\n\tValueType : %S", ValueType ? ValueType->to_string() : "Null" ); + break; + + case Parameters: + result.append_fmt( "\n\tNumEntries: %d", NumEntries ); + result.append_fmt( "\n\tLast : %S", Last->Name ); + result.append_fmt( "\n\tNext : %S", Next->Name ); + result.append_fmt( "\n\tValueType : %S", ValueType ? ValueType->to_string() : "Null" ); + result.append_fmt( "\n\tValue : %S", Value ? Value->to_string() : "Null" ); + break; + + case Specifiers: + { + result.append_fmt( "\n\tNumEntries: %d", NumEntries ); + result.append( "\n\tArrSpecs: " ); + + s32 idx = 0; + s32 left = NumEntries; + while ( left-- ) + { + StrC spec = ESpecifier::to_str( ArrSpecs[idx] ); + result.append_fmt( "%.*s, ", spec.Len, spec.Ptr ); + idx++; + } + result.append_fmt( "\n\tNextSpecs: %S", NextSpecs ? NextSpecs->debug_str() : "Null" ); + } + break; + + case Template: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tParams : %S", Params ? Params->to_string() : "Null" ); + result.append_fmt( "\n\tDeclaration: %S", Declaration ? Declaration->to_string() : "Null" ); + break; + + case Typedef: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tUnderlyingType: %S", UnderlyingType ? UnderlyingType->to_string() : "Null" ); + break; + + case Typename: + result.append_fmt( "\n\tAttributes : %S", Attributes ? Attributes->to_string() : "Null" ); + result.append_fmt( "\n\tSpecs : %S", Specs ? Specs->to_string() : "Null" ); + result.append_fmt( "\n\tReturnType : %S", ReturnType ? ReturnType->to_string() : "Null" ); + result.append_fmt( "\n\tParams : %S", Params ? Params->to_string() : "Null" ); + result.append_fmt( "\n\tArrExpr : %S", ArrExpr ? ArrExpr->to_string() : "Null" ); + break; + + case Union: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tAttributes: %S", Attributes ? Attributes->to_string() : "Null" ); + result.append_fmt( "\n\tBody : %S", Body ? Body->debug_str() : "Null" ); + break; + + case Using: + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tAttributes : %S", Attributes ? Attributes->to_string() : "Null" ); + result.append_fmt( "\n\tUnderlyingType: %S", UnderlyingType ? UnderlyingType->to_string() : "Null" ); + break; + + case Variable: + + if ( Parent && Parent->Type == Variable ) + { + // Its a NextVar + result.append_fmt( "\n\tSpecs : %S", Specs ? Specs->to_string() : "Null" ); + result.append_fmt( "\n\tValue : %S", Value ? Value->to_string() : "Null" ); + result.append_fmt( "\n\tBitfieldSize: %S", BitfieldSize ? BitfieldSize->to_string() : "Null" ); + result.append_fmt( "\n\tNextVar : %S", NextVar ? NextVar->debug_str() : "Null" ); + break; + } + + if ( Prev ) + result.append_fmt( "\n\tPrev: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + if ( Next ) + result.append_fmt( "\n\tNext: %S %S", Prev->type_str(), Prev->Name ? Prev->Name : "Null" ); + + result.append_fmt( "\n\tInlineCmt : %S", InlineCmt ? InlineCmt->Content : "Null" ); + result.append_fmt( "\n\tAttributes : %S", Attributes ? Attributes->to_string() : "Null" ); + result.append_fmt( "\n\tSpecs : %S", Specs ? Specs->to_string() : "Null" ); + result.append_fmt( "\n\tValueType : %S", ValueType ? ValueType->to_string() : "Null" ); + result.append_fmt( "\n\tBitfieldSize: %S", BitfieldSize ? BitfieldSize->to_string() : "Null" ); + result.append_fmt( "\n\tValue : %S", Value ? Value->to_string() : "Null" ); + result.append_fmt( "\n\tNextVar : %S", NextVar ? NextVar->debug_str() : "Null" ); + break; + } + + return result; +} + +AST* AST::duplicate() +{ + using namespace ECode; + + AST* result = make_code().ast; + + mem_copy( result, this, sizeof( AST ) ); + + result->Parent = nullptr; + return result; +} + +String AST::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + to_string( result ); + return result; +} + +void AST::to_string( String& result ) +{ + local_persist thread_local + char SerializationLevel = 0; + + switch ( Type ) + { + using namespace ECode; + + case Invalid: + #ifdef GEN_DONT_ALLOW_INVALID_CODE + log_failure("Attempted to serialize invalid code! - %S", Parent ? Parent->debug_str() : Name ); + #else + result.append_fmt( "Invalid Code!" ); + #endif + break; + + case NewLine: + result.append("\n"); + break; + + case Untyped: + case Execution: + case Comment: + case PlatformAttributes: + result.append( Content ); + break; + + case Access_Private: + case Access_Protected: + case Access_Public: + result.append( Name ); + break; + + case Class: + cast().to_string_def( result ); + break; + + case Class_Fwd: + cast().to_string_fwd( result ); + break; + + case Constructor: + cast().to_string_def( result ); + break; + + case Constructor_Fwd: + cast().to_string_fwd( result ); + break; + + case Destructor: + cast().to_string_def( result ); + break; + + case Destructor_Fwd: + cast().to_string_fwd( result ); + break; + + case Enum: + cast().to_string_def( result ); + break; + + case Enum_Fwd: + cast().to_string_fwd( result ); + break; + + case Enum_Class: + cast().to_string_class_def( result ); + break; + + case Enum_Class_Fwd: + cast().to_string_class_fwd( result ); + break; + + case Export_Body: + cast().to_string_export( result ); + break; + + case Extern_Linkage: + cast().to_string( result ); + break; + + case Friend: + cast().to_string( result ); + break; + + case Function: + cast().to_string_def( result ); + break; + + case Function_Fwd: + cast().to_string_fwd( result ); + break; + + case Module: + cast().to_string( result ); + break; + + case Namespace: + cast().to_string( result ); + break; + + case Operator: + case Operator_Member: + cast().to_string_def( result ); + break; + + case Operator_Fwd: + case Operator_Member_Fwd: + cast().to_string_fwd( result ); + break; + + case Operator_Cast: + cast().to_string_def( result ); + break; + + case Operator_Cast_Fwd: + cast().to_string_fwd( result ); + break; + + case Parameters: + cast().to_string( result ); + break; + + case Preprocess_Define: + cast().to_string( result ); + break; + + case Preprocess_If: + cast().to_string_if( result ); + break; + + case Preprocess_IfDef: + cast().to_string_ifdef( result ); + break; + + case Preprocess_IfNotDef: + cast().to_string_ifndef( result ); + break; + + case Preprocess_Include: + cast().to_string( result ); + break; + + case Preprocess_ElIf: + cast().to_string_elif( result ); + break; + + case Preprocess_Else: + cast().to_string_else( result ); + break; + + case Preprocess_EndIf: + cast().to_string_endif( result ); + break; + + case Preprocess_Pragma: + cast().to_string( result ); + break; + + case Specifiers: + cast().to_string( result ); + break; + + case Struct: + cast().to_string_def( result ); + break; + + case Struct_Fwd: + cast().to_string_fwd( result ); + break; + + case Template: + cast().to_string( result ); + break; + + case Typedef: + cast().to_string( result ); + break; + + case Typename: + cast().to_string( result ); + break; + + case Union: + cast().to_string( result ); + break; + + case Using: + cast().to_string( result ); + break; + + case Using_Namespace: + cast().to_string_ns( result ); + break; + + case Variable: + cast().to_string( result ); + break; + + case Enum_Body: + case Class_Body: + case Extern_Linkage_Body: + case Function_Body: + case Global_Body: + case Namespace_Body: + case Struct_Body: + case Union_Body: + cast().to_string( result ); + break; + } +} + +bool AST::is_equal( AST* other ) +{ +/* + AST values are either some u32 value, a cached string, or a pointer to another AST. + + u32 values are compared by value. + Cached strings are compared by pointer. + AST nodes are compared with AST::is_equal. +*/ + if ( other == nullptr ) + { + log_fmt( "AST::is_equal: other is null\nAST: %S", debug_str() ); + return false; + } + + if ( Type != other->Type ) + { + log_fmt("AST::is_equal: Type check failure with other\nAST: %S\nOther: %S" + , debug_str() + , other->debug_str() + ); + + return false; + } + + switch ( Type ) + { + using namespace ECode; + + #define check_member_val( val ) \ + if ( val != other->val ) \ + { \ + log_fmt("\nAST::is_equal: Member - " #val " failed\n" \ + "AST : %S\n" \ + "Other: %S\n" \ + , debug_str() \ + , other->debug_str() \ + ); \ + \ + return false; \ + } + + #define check_member_str( str ) \ + if ( str != other->str ) \ + { \ + log_fmt("\nAST::is_equal: Member string - "#str " failed\n" \ + "AST : %S\n" \ + "Other: %S\n" \ + , debug_str() \ + , other->debug_str() \ + ); \ + \ + return false; \ + } + + #define check_member_content( content ) \ + if ( content != other->content ) \ + { \ + log_fmt("\nAST::is_equal: Member content - "#content " failed\n" \ + "AST : %S\n" \ + "Other: %S\n" \ + , debug_str() \ + , other->debug_str() \ + ); \ + \ + log_fmt("Content cannot be trusted to be unique with this check " \ + "so it must be verified by eye for now\n" \ + "AST Content:\n%S\n" \ + "Other Content:\n%S\n" \ + , content.visualize_whitespace() \ + , other->content.visualize_whitespace() \ + ); \ + } + + #define check_member_ast( ast ) \ + if ( ast ) \ + { \ + if ( other->ast == nullptr ) \ + { \ + log_fmt("\nAST::is_equal: Failed for member " #ast " other equivalent param is null\n" \ + "AST : %s\n" \ + "Other: %s\n" \ + "For ast member: %s\n" \ + , debug_str() \ + , other->debug_str() \ + , ast->debug_str() \ + ); \ + \ + return false; \ + } \ + \ + if ( ! ast->is_equal( other->ast ) ) \ + { \ + log_fmt( "\nAST::is_equal: Failed for " #ast"\n" \ + "AST : %S\n" \ + "Other: %S\n" \ + "For ast member: %S\n" \ + "other's ast member: %S\n" \ + , debug_str() \ + , other->debug_str() \ + , ast->debug_str() \ + , other->ast->debug_str() \ + ); \ + \ + return false; \ + } \ + } + + case NewLine: + case Access_Public: + case Access_Protected: + case Access_Private: + case Preprocess_Else: + case Preprocess_EndIf: + return true; + + + // Comments are not validated. + case Comment: + return true; + + case Execution: + case PlatformAttributes: + case Untyped: + { + check_member_content( Content ); + + return true; + } + + case Class_Fwd: + case Struct_Fwd: + { + check_member_str( Name ); + check_member_ast( ParentType ); + check_member_val( ParentAccess ); + check_member_ast( Attributes ); + + return true; + } + + case Class: + case Struct: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( ParentType ); + check_member_val( ParentAccess ); + check_member_ast( Attributes ); + check_member_ast( Body ); + + return true; + } + + case Constructor: + { + check_member_ast( InitializerList ); + check_member_ast( Params ); + check_member_ast( Body ); + + return true; + } + + case Constructor_Fwd: + { + check_member_ast( InitializerList ); + check_member_ast( Params ); + + return true; + } + + case Destructor: + { + check_member_ast( Specs ); + check_member_ast( Body ); + + return true; + } + + case Destructor_Fwd: + { + check_member_ast( Specs ); + + return true; + } + + case Enum: + case Enum_Class: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( Attributes ); + check_member_ast( UnderlyingType ); + check_member_ast( Body ); + + return true; + } + + case Enum_Fwd: + case Enum_Class_Fwd: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( Attributes ); + check_member_ast( UnderlyingType ); + + return true; + } + + case Extern_Linkage: + { + check_member_str( Name ); + check_member_ast( Body ); + + return true; + } + + case Friend: + { + check_member_str( Name ); + check_member_ast( Declaration ); + + return true; + } + + case Function: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( ReturnType ); + check_member_ast( Attributes ); + check_member_ast( Specs ); + check_member_ast( Params ); + check_member_ast( Body ); + + return true; + } + + case Function_Fwd: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( ReturnType ); + check_member_ast( Attributes ); + check_member_ast( Specs ); + check_member_ast( Params ); + + return true; + } + + case Module: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + + return true; + } + + case Namespace: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( Body ); + + return true; + } + + case Operator: + case Operator_Member: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( ReturnType ); + check_member_ast( Attributes ); + check_member_ast( Specs ); + check_member_ast( Params ); + check_member_ast( Body ); + + return true; + } + + case Operator_Fwd: + case Operator_Member_Fwd: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( ReturnType ); + check_member_ast( Attributes ); + check_member_ast( Specs ); + check_member_ast( Params ); + + return true; + } + + case Operator_Cast: + { + check_member_str( Name ); + check_member_ast( Specs ); + check_member_ast( ValueType ); + check_member_ast( Body ); + + return true; + } + + case Operator_Cast_Fwd: + { + check_member_str( Name ); + check_member_ast( Specs ); + check_member_ast( ValueType ); + + return true; + } + + case Parameters: + { + if ( NumEntries > 1 ) + { + AST* curr = this; + AST* curr_other = other; + while ( curr != nullptr ) + { + if ( curr ) + { + if ( curr_other == nullptr ) + { + log_fmt("\nAST::is_equal: Failed for parameter, other equivalent param is null\n" + "AST : %S\n" + "Other: %S\n" + "For ast member: %S\n" + , curr->debug_str() + ); + + return false; + } + + if ( curr->Name != curr_other->Name ) + { + log_fmt( "\nAST::is_equal: Failed for parameter name check\n" + "AST : %S\n" + "Other: %S\n" + "For ast member: %S\n" + "other's ast member: %S\n" + , debug_str() + , other->debug_str() + , curr->debug_str() + , curr_other->debug_str() + ); + return false; + } + + if ( curr->ValueType && ! curr->ValueType->is_equal(curr_other->ValueType) ) + { + log_fmt( "\nAST::is_equal: Failed for parameter value type check\n" + "AST : %S\n" + "Other: %S\n" + "For ast member: %S\n" + "other's ast member: %S\n" + , debug_str() + , other->debug_str() + , curr->debug_str() + , curr_other->debug_str() + ); + return false; + } + + if ( curr->Value && ! curr->Value->is_equal(curr_other->Value) ) + { + log_fmt( "\nAST::is_equal: Failed for parameter value check\n" + "AST : %S\n" + "Other: %S\n" + "For ast member: %S\n" + "other's ast member: %S\n" + , debug_str() + , other->debug_str() + , curr->debug_str() + , curr_other->debug_str() + ); + return false; + } + } + + curr = curr->Next; + curr_other = curr_other->Next; + } + + check_member_val( NumEntries ); + + return true; + } + + check_member_str( Name ); + check_member_ast( ValueType ); + check_member_ast( Value ); + check_member_ast( ArrExpr ); + + return true; + } + + case Preprocess_Define: + { + check_member_str( Name ); + check_member_content( Content ); + + return true; + } + + case Preprocess_If: + case Preprocess_IfDef: + case Preprocess_IfNotDef: + case Preprocess_ElIf: + { + check_member_content( Content ); + + return true; + } + + case Preprocess_Include: + case Preprocess_Pragma: + { + check_member_content( Content ); + + return true; + } + + case Specifiers: + { + check_member_val( NumEntries ); + check_member_str( Name ); + for ( s32 idx = 0; idx < NumEntries; ++idx ) + { + check_member_val( ArrSpecs[ idx ] ); + } + return true; + } + + case Template: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( Params ); + check_member_ast( Declaration ); + + return true; + } + + case Typedef: + { + check_member_val( IsFunction ); + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( Specs ); + check_member_ast( UnderlyingType ); + + return true; + } + case Typename: + { + check_member_val( IsParamPack ); + check_member_str( Name ); + check_member_ast( Specs ); + check_member_ast( ArrExpr ); + + return true; + } + + case Union: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( Attributes ); + check_member_ast( Body ); + + return true; + } + + case Using: + case Using_Namespace: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( UnderlyingType ); + check_member_ast( Attributes ); + + return true; + } + + case Variable: + { + check_member_val( ModuleFlags ); + check_member_str( Name ); + check_member_ast( ValueType ); + check_member_ast( BitfieldSize ); + check_member_ast( Value ); + check_member_ast( Attributes ); + check_member_ast( Specs ); + check_member_ast( NextVar ); + + return true; + } + + case Class_Body: + case Enum_Body: + case Export_Body: + case Global_Body: + case Namespace_Body: + case Struct_Body: + case Union_Body: + { + check_member_ast( Front ); + check_member_ast( Back ); + + AST* curr = Front; + AST* curr_other = other->Front; + while ( curr != nullptr ) + { + if ( curr_other == nullptr ) + { + log_fmt("\nAST::is_equal: Failed for body, other equivalent param is null\n" + "AST : %S\n" + "Other: %S\n" + "For ast member: %S\n" + , curr->debug_str() + ); + + return false; + } + + if ( ! curr->is_equal( curr_other ) ) + { + log_fmt( "\nAST::is_equal: Failed for body\n" + "AST : %S\n" + "Other: %S\n" + "For ast member: %S\n" + "other's ast member: %S\n" + , debug_str() + , other->debug_str() + , curr->debug_str() + , curr_other->debug_str() + ); + + return false; + } + + curr = curr->Next; + curr_other = curr_other->Next; + } + + check_member_val( NumEntries ); + + return true; + } + + #undef check_member_val + #undef check_member_str + #undef check_member_ast + } + + return true; +} + +bool AST::validate_body() +{ + using namespace ECode; + +#define CheckEntries( Unallowed_Types ) \ + do \ + { \ + for ( Code entry : cast() ) \ + { \ + switch ( entry->Type ) \ + { \ + Unallowed_Types \ + log_failure( "AST::validate_body: Invalid entry in body %s", entry.debug_str() ); \ + return false; \ + } \ + } \ + } \ + while (0); + + switch ( Type ) + { + case Class_Body: + CheckEntries( GEN_AST_BODY_CLASS_UNALLOWED_TYPES ); + break; + case Enum_Body: + for ( Code entry : cast() ) + { + if ( entry->Type != Untyped ) + { + log_failure( "AST::validate_body: Invalid entry in enum body (needs to be untyped or comment) %s", entry.debug_str() ); + return false; + } + } + break; + case Export_Body: + CheckEntries( GEN_AST_BODY_CLASS_UNALLOWED_TYPES ); + break; + case Extern_Linkage: + CheckEntries( GEN_AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES ); + break; + case Function_Body: + CheckEntries( GEN_AST_BODY_FUNCTION_UNALLOWED_TYPES ); + break; + case Global_Body: + for (Code entry : cast()) + { + switch (entry->Type) + { + case Access_Public: + case Access_Protected: + case Access_Private: + case PlatformAttributes: + case Class_Body: + case Enum_Body: + case Execution: + case Friend: + case Function_Body: + case Global_Body: + case Namespace_Body: + case Operator_Member: + case Operator_Member_Fwd: + case Parameters: + case Specifiers: + case Struct_Body: + case Typename: + log_failure("AST::validate_body: Invalid entry in body %s", entry.debug_str()); + return false; + } + } + break; + case Namespace_Body: + CheckEntries( GEN_AST_BODY_NAMESPACE_UNALLOWED_TYPES ); + break; + case Struct_Body: + CheckEntries( GEN_AST_BODY_STRUCT_UNALLOWED_TYPES ); + break; + case Union_Body: + for ( Code entry : Body->cast() ) + { + if ( entry->Type != Untyped ) + { + log_failure( "AST::validate_body: Invalid entry in union body (needs to be untyped or comment) %s", entry.debug_str() ); + return false; + } + } + break; + + default: + log_failure( "AST::validate_body: Invalid this AST does not have a body %s", debug_str() ); + return false; + } + + return false; + +#undef CheckEntries +} + +String Code::to_string() +{ + if ( ast == nullptr ) + { + log_failure( "Code::to_string: Cannot convert code to string, AST is null!" ); + return { nullptr }; + } + return rcast( AST*, ast )->to_string(); +} + +String CodeAttributes::to_string() +{ + return ast->Content.duplicate( GlobalAllocator ); +} + +String CodeBody::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + switch ( ast->Type ) + { + using namespace ECode; + case Untyped: + case Execution: + result.append( raw()->Content ); + break; + + case Enum_Body: + case Class_Body: + case Extern_Linkage_Body: + case Function_Body: + case Global_Body: + case Namespace_Body: + case Struct_Body: + case Union_Body: + to_string( result ); + break; + + case Export_Body: + to_string_export( result ); + break; + } + return result; +} + +void CodeBody::to_string( String& result ) +{ + Code curr = ast->Front; + s32 left = ast->NumEntries; + while ( left -- ) + { + result.append_fmt( "%S", curr.to_string() ); + ++curr; + } +} + +void CodeBody::to_string_export( String& result ) +{ + result.append_fmt( "export\n{\n" ); + + Code curr = *this; + s32 left = ast->NumEntries; + while ( left-- ) + { + result.append_fmt( "%S", curr.to_string() ); + ++curr; + } + + result.append_fmt( "};\n" ); +} + +String CodeComment::to_string() +{ + return ast->Content.duplicate( GlobalAllocator ); +} + +String CodeConstructor::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + switch (ast->Type) + { + using namespace ECode; + case Constructor: + to_string_def( result ); + break; + case Constructor_Fwd: + to_string_fwd( result ); + break; + } + return result; +} + +void CodeConstructor::to_string_def( String& result ) +{ + AST* ClassStructParent = ast->Parent->Parent; + if (ClassStructParent) { + result.append( ClassStructParent->Name ); + } + else { + result.append( ast->Name ); + } + + if ( ast->Params ) + result.append_fmt( "( %S )", ast->Params.to_string() ); + else + result.append( "()" ); + + if ( ast->InitializerList ) + result.append_fmt( " : %S", ast->InitializerList.to_string() ); + + if ( ast->InlineCmt ) + result.append_fmt( " // %S", ast->InlineCmt->Content ); + + result.append_fmt( "\n{\n%S\n}\n", ast->Body.to_string() ); +} + +void CodeConstructor::to_string_fwd( String& result ) +{ + AST* ClassStructParent = ast->Parent->Parent; + if (ClassStructParent) { + result.append( ClassStructParent->Name ); + } + else { + result.append( ast->Name ); + } + + if ( ast->Params ) + result.append_fmt( "( %S )", ast->Params.to_string() ); + else + result.append_fmt("()"); + + if (ast->Body) + result.append_fmt( " = %S", ast->Body.to_string() ); + + if ( ast->InlineCmt ) + result.append_fmt( "; // %S\n", ast->InlineCmt->Content ); + else + result.append( ";\n" ); +} + +String CodeClass::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + switch ( ast->Type ) + { + using namespace ECode; + case Class: + to_string_def( result ); + break; + case Class_Fwd: + to_string_fwd( result ); + break; + } + return result; +} + +void CodeClass::to_string_def( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + result.append( "class " ); + + if ( ast->Attributes ) + { + result.append_fmt( "%S ", ast->Attributes.to_string() ); + } + + if ( ast->ParentType ) + { + char const* access_level = to_str( ast->ParentAccess ); + + result.append_fmt( "%S : %s %S", ast->Name, access_level, ast->ParentType.to_string() ); + + CodeType interface = ast->ParentType->Next->cast< CodeType >(); + if ( interface ) + result.append( "\n" ); + + while ( interface ) + { + result.append_fmt( ", %S", interface.to_string() ); + interface = interface->Next ? interface->Next->cast< CodeType >() : CodeType { nullptr }; + } + } + else if ( ast->Name ) + { + result.append( ast->Name ); + } + + if ( ast->InlineCmt ) + { + result.append_fmt( " // %S", ast->InlineCmt->Content ); + } + + result.append_fmt( "\n{\n%S\n}", ast->Body.to_string() ); + + if ( ast->Parent.ast == nullptr || ( ast->Parent->Type != ECode::Typedef && ast->Parent->Type != ECode::Variable ) ) + result.append(";\n"); +} + +void CodeClass::to_string_fwd( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + if ( ast->Attributes ) + result.append_fmt( "class %S %S", ast->Attributes.to_string(), ast->Name ); + + else result.append_fmt( "class %S", ast->Name ); + + // Check if it can have an end-statement + if ( ast->Parent.ast == nullptr || ( ast->Parent->Type != ECode::Typedef && ast->Parent->Type != ECode::Variable ) ) + { + if ( ast->InlineCmt ) + result.append_fmt( "; // %S\n", ast->InlineCmt->Content ); + else + result.append(";\n"); + } +} + +String CodeDefine::to_string() +{ + return String::fmt_buf( GlobalAllocator, "#define %S %S\n", ast->Name, ast->Content ); +} + +void CodeDefine::to_string( String& result ) +{ + result.append_fmt( "#define %S %S\n", ast->Name, ast->Content ); +} + +String CodeDestructor::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + switch ( ast->Type ) + { + using namespace ECode; + case Destructor: + to_string_def( result ); + break; + case Destructor_Fwd: + to_string_fwd( result ); + break; + } + return result; +} + +void CodeDestructor::to_string_def( String& result ) +{ + if ( ast->Name ) + { + result.append_fmt( "%S()", ast->Name ); + } + else if ( ast->Specs ) + { + if ( ast->Specs.has( ESpecifier::Virtual ) ) + result.append_fmt( "virtual ~%S()", ast->Parent->Name ); + else + result.append_fmt( "~%S()", ast->Parent->Name ); + } + else + result.append_fmt( "~%S()", ast->Parent->Name ); + + result.append_fmt( "\n{\n%S\n}\n", ast->Body.to_string() ); +} + +void CodeDestructor::to_string_fwd( String& result ) +{ + if ( ast->Specs ) + { + if ( ast->Specs.has( ESpecifier::Virtual ) ) + result.append_fmt( "virtual ~%S();\n", ast->Parent->Name ); + else + result.append_fmt( "~%S()", ast->Parent->Name ); + + if ( ast->Specs.has( ESpecifier::Pure ) ) + result.append( " = 0;" ); + else if (ast->Body) + result.append_fmt( " = %S;", ast->Body.to_string() ); + } + else + result.append_fmt( "~%S();", ast->Parent->Name ); + + if ( ast->InlineCmt ) + result.append_fmt( " %S", ast->InlineCmt->Content ); + else + result.append("\n"); +} + +String CodeEnum::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + switch ( ast->Type ) + { + using namespace ECode; + case Enum: + to_string_def( result ); + break; + case Enum_Fwd: + to_string_fwd( result ); + break; + case Enum_Class: + to_string_class_def( result ); + break; + case Enum_Class_Fwd: + to_string_class_fwd( result ); + break; + } + return result; +} + +void CodeEnum::to_string_def( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + if ( ast->Attributes || ast->UnderlyingType ) + { + result.append( "enum " ); + + if ( ast->Attributes ) + result.append_fmt( "%S ", ast->Attributes.to_string() ); + + if ( ast->UnderlyingType ) + result.append_fmt( "%S : %S\n{\n%S\n}" + , ast->Name + , ast->UnderlyingType.to_string() + , ast->Body.to_string() + ); + + else result.append_fmt( "%S\n{\n%S\n}", ast->Name, ast->Body.to_string() ); + } + else result.append_fmt( "enum %S\n{\n%S\n}", ast->Name, ast->Body.to_string() ); + + if ( ast->Parent.ast == nullptr || ( ast->Parent->Type != ECode::Typedef && ast->Parent->Type != ECode::Variable ) ) + result.append(";\n"); +} + +void CodeEnum::to_string_fwd( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + if ( ast->Attributes ) + result.append_fmt( "%S ", ast->Attributes.to_string() ); + + result.append_fmt( "enum %S : %S", ast->Name, ast->UnderlyingType.to_string() ); + + if ( ast->Parent.ast == nullptr || ( ast->Parent->Type != ECode::Typedef && ast->Parent->Type != ECode::Variable ) ) + { + if ( ast->InlineCmt ) + result.append_fmt("; %S", ast->InlineCmt->Content ); + else + result.append(";\n"); + } +} + +void CodeEnum::to_string_class_def( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + if ( ast->Attributes || ast->UnderlyingType ) + { + result.append( "enum class " ); + + if ( ast->Attributes ) + { + result.append_fmt( "%S ", ast->Attributes.to_string() ); + } + + if ( ast->UnderlyingType ) + { + result.append_fmt( "%S : %S\n{\n%S\n}", ast->Name, ast->UnderlyingType.to_string(), ast->Body.to_string() ); + } + else + { + result.append_fmt( "%S\n{\n%S\n}", ast->Name, ast->Body.to_string() ); + } + } + else + { + result.append_fmt( "enum class %S\n{\n%S\n}", ast->Body.to_string() ); + } + + if ( ast->Parent.ast == nullptr || ( ast->Parent->Type != ECode::Typedef && ast->Parent->Type != ECode::Variable ) ) + result.append(";\n"); +} + +void CodeEnum::to_string_class_fwd( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + result.append( "enum class " ); + + if ( ast->Attributes ) + result.append_fmt( "%S ", ast->Attributes.to_string() ); + + result.append_fmt( "%S : %S", ast->Name, ast->UnderlyingType.to_string() ); + + if ( ast->Parent.ast == nullptr || ( ast->Parent->Type != ECode::Typedef && ast->Parent->Type != ECode::Variable ) ) + { + if ( ast->InlineCmt ) + result.append_fmt("; %S", ast->InlineCmt->Content ); + else + result.append(";\n"); + } +} + +String CodeExec::to_string() +{ + return ast->Content.duplicate( GlobalAllocator ); +} + +void CodeExtern::to_string( String& result ) +{ + if ( ast->Body ) + result.append_fmt( "extern \"%S\"\n{\n%S\n}\n", ast->Name, ast->Body.to_string() ); + else + result.append_fmt( "extern \"%S\"\n{}\n", ast->Name ); +} + +String CodeInclude::to_string() +{ + return String::fmt_buf( GlobalAllocator, "#include %S\n", ast->Content ); +} + +void CodeInclude::to_string( String& result ) +{ + result.append_fmt( "#include %S\n", ast->Content ); +} + +String CodeFriend::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + to_string( result ); + return result; +} + +void CodeFriend::to_string( String& result ) +{ + result.append_fmt( "friend %S", ast->Declaration->to_string() ); + + if ( ast->Declaration->Type != ECode::Function && result[ result.length() - 1 ] != ';' ) + { + result.append( ";" ); + } + + if ( ast->InlineCmt ) + result.append_fmt(" %S", ast->InlineCmt->Content ); + else + result.append("\n"); +} + +String CodeFn::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + switch ( ast->Type ) + { + using namespace ECode; + case Function: + to_string_def( result ); + break; + case Function_Fwd: + to_string_fwd( result ); + break; + } + return result; +} + +void CodeFn::to_string_def( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export" ); + + if ( ast->Attributes ) + result.append_fmt( " %S ", ast->Attributes.to_string() ); + + bool prefix_specs = false; + if ( ast->Specs ) + { + for ( SpecifierT spec : ast->Specs ) + { + if ( ! ESpecifier::is_trailing( spec ) ) + { + StrC spec_str = ESpecifier::to_str( spec ); + result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr ); + + prefix_specs = true; + } + } + } + + if ( ast->Attributes || prefix_specs ) + result.append( "\n" ); + + if ( ast->ReturnType ) + result.append_fmt( "%S %S(", ast->ReturnType.to_string(), ast->Name ); + + else + result.append_fmt( "%S(", ast->Name ); + + if ( ast->Params ) + result.append_fmt( "%S)", ast->Params.to_string() ); + + else + result.append( ")" ); + + if ( ast->Specs ) + { + for ( SpecifierT spec : ast->Specs ) + { + if ( ESpecifier::is_trailing( spec ) ) + { + StrC spec_str = ESpecifier::to_str( spec ); + result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + } + + result.append_fmt( "\n{\n%S\n}\n", ast->Body.to_string() ); +} + +void CodeFn::to_string_fwd( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + if ( ast->Attributes ) + result.append_fmt( "%S ", ast->Attributes.to_string() ); + + b32 prefix_specs = false; + if ( ast->Specs ) + { + for ( SpecifierT spec : ast->Specs ) + { + if ( ! ESpecifier::is_trailing( spec ) || ! (spec != ESpecifier::Pure) ) + { + StrC spec_str = ESpecifier::to_str( spec ); + result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr ); + + prefix_specs = true; + } + } + } + + if ( ast->Attributes || prefix_specs ) + { + result.append("\n" ); + } + + if ( ast->ReturnType ) + result.append_fmt( "%S %S(", ast->ReturnType.to_string(), ast->Name ); + + else + result.append_fmt( "%S(", ast->Name ); + + if ( ast->Params ) + result.append_fmt( "%S)", ast->Params.to_string() ); + + else + result.append( ")" ); + + if ( ast->Specs ) + { + for ( SpecifierT spec : ast->Specs ) + { + if ( ESpecifier::is_trailing( spec ) ) + { + StrC spec_str = ESpecifier::to_str( spec ); + result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + } + + if ( ast->Specs && ast->Specs.has( ESpecifier::Pure ) >= 0 ) + result.append( " = 0;" ); + else if (ast->Body) + result.append_fmt( " = %S;", ast->Body.to_string() ); + + if ( ast->InlineCmt ) + result.append_fmt( "; %S", ast->InlineCmt->Content ); + else + result.append( ";\n" ); +} + +String CodeModule::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + to_string( result ); + return result; +} + +void CodeModule::to_string( String& result ) +{ + if (((u32(ModuleFlag::Export) & u32(ast->ModuleFlags)) == u32(ModuleFlag::Export))) + result.append("export "); + + if (((u32(ModuleFlag::Import) & u32(ast->ModuleFlags)) == u32(ModuleFlag::Import))) + result.append("import "); + + result.append_fmt( "%S;\n", ast->Name ); +} + +String CodeNS::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + to_string( result ); + return result; +} + +void CodeNS::to_string( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + result.append_fmt( "namespace %S\n{\n%S\n}\n", ast->Name , ast->Body.to_string() ); +} + +String CodeOperator::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + switch ( ast->Type ) + { + using namespace ECode; + case Operator: + case Operator_Member: + to_string_def( result ); + break; + case Operator_Fwd: + case Operator_Member_Fwd: + to_string_fwd( result ); + break; + } + return result; +} + +void CodeOperator::to_string_def( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + if ( ast->Attributes ) + result.append_fmt( "%S ", ast->Attributes.to_string() ); + + if ( ast->Attributes ) + result.append_fmt( "%S ", ast->Attributes.to_string() ); + + if ( ast->Specs ) + { + for ( SpecifierT spec : ast->Specs ) + { + if ( ! ESpecifier::is_trailing( spec ) ) + { + StrC spec_str = ESpecifier::to_str( spec ); + result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + } + + if ( ast->Attributes || ast->Specs ) + { + result.append("\n" ); + } + + if ( ast->ReturnType ) + result.append_fmt( "%S %S (", ast->ReturnType.to_string(), ast->Name ); + + if ( ast->Params ) + result.append_fmt( "%S)", ast->Params.to_string() ); + + else + result.append( ")" ); + + if ( ast->Specs ) + { + for ( SpecifierT spec : ast->Specs ) + { + if ( ESpecifier::is_trailing( spec ) ) + { + StrC spec_str = ESpecifier::to_str( spec ); + result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + } + + result.append_fmt( "\n{\n%S\n}\n" + , ast->Body.to_string() + ); +} + +void CodeOperator::to_string_fwd( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + if ( ast->Attributes ) + result.append_fmt( "%S\n", ast->Attributes.to_string() ); + + if ( ast->Specs ) + { + for ( SpecifierT spec : ast->Specs ) + { + if ( ! ESpecifier::is_trailing( spec ) ) + { + StrC spec_str = ESpecifier::to_str( spec ); + result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + } + + if ( ast->Attributes || ast->Specs ) + { + result.append("\n" ); + } + + result.append_fmt( "%S %S (", ast->ReturnType.to_string(), ast->Name ); + + if ( ast->Params ) + result.append_fmt( "%S)", ast->Params.to_string() ); + + else + result.append_fmt( ")" ); + + if ( ast->Specs ) + { + for ( SpecifierT spec : ast->Specs ) + { + if ( ESpecifier::is_trailing( spec ) ) + { + StrC spec_str = ESpecifier::to_str( spec ); + result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + } + + if ( ast->InlineCmt ) + result.append_fmt( "; %S", ast->InlineCmt->Content ); + else + result.append( ";\n" ); +} + +String CodeOpCast::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + switch ( ast->Type ) + { + using namespace ECode; + case Operator_Cast: + to_string_def( result ); + break; + case Operator_Cast_Fwd: + to_string_fwd( result ); + break; + } + return result; +} + +void CodeOpCast::to_string_def( String& result ) +{ + if ( ast->Specs ) + { + for ( SpecifierT spec : ast->Specs ) + { + if ( ! ESpecifier::is_trailing( spec ) ) + { + StrC spec_str = ESpecifier::to_str( spec ); + result.append_fmt( "%*s ", spec_str.Len, spec_str.Ptr ); + } + } + + if ( ast->Name && ast->Name.length() ) + result.append_fmt( "%Soperator %S()", ast->Name, ast->ValueType.to_string() ); + else + result.append_fmt( "operator %S()", ast->ValueType.to_string() ); + + for ( SpecifierT spec : ast->Specs ) + { + if ( ESpecifier::is_trailing( spec ) ) + { + StrC spec_str = ESpecifier::to_str( spec ); + result.append_fmt( " %.*s", spec_str.Len, spec_str.Ptr ); + } + } + + result.append_fmt( "\n{\n%S\n}\n", ast->Body.to_string() ); + return; + } + + if ( ast->Name && ast->Name.length() ) + result.append_fmt("%Soperator %S()\n{\n%S\n}\n", ast->Name, ast->ValueType.to_string(), ast->Body.to_string() ); + else + result.append_fmt("operator %S()\n{\n%S\n}\n", ast->ValueType.to_string(), ast->Body.to_string() ); +} + +void CodeOpCast::to_string_fwd( String& result ) +{ + if ( ast->Specs ) + { + for ( SpecifierT spec : ast->Specs ) + { + if ( ! ESpecifier::is_trailing( spec ) ) + { + StrC spec_str = ESpecifier::to_str( spec ); + result.append_fmt( "%*s ", spec_str.Len, spec_str.Ptr ); + } + } + + result.append_fmt( "operator %S()", ast->ValueType.to_string() ); + + for ( SpecifierT spec : ast->Specs ) + { + if ( ESpecifier::is_trailing( spec ) ) + { + StrC spec_str = ESpecifier::to_str( spec ); + result.append_fmt( " %*s", spec_str.Len, spec_str.Ptr ); + } + } + + if ( ast->InlineCmt ) + result.append_fmt( "; %S", ast->InlineCmt->Content ); + else + result.append( ";\n" ); + return; + } + + if ( ast->InlineCmt ) + result.append_fmt("operator %S(); %S", ast->ValueType.to_string() ); + else + result.append_fmt("operator %S();\n", ast->ValueType.to_string() ); +} + +String CodeParam::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + to_string( result ); + return result; +} + +void CodeParam::to_string( String& result ) +{ + if ( ast->Macro ) + { + // Related to parsing: ( , ... ) + result.append( ast->Macro.ast->Content ); + // Could also be: ( , ... ) + } + + if ( ast->Name ) + { + if ( ast->ValueType.ast == nullptr ) + result.append_fmt( " %S", ast->Name ); + else + result.append_fmt( " %S %S", ast->ValueType.to_string(), ast->Name ); + + } + else if ( ast->ValueType ) + result.append_fmt( " %S", ast->ValueType.to_string() ); + + if ( ast->PostNameMacro ) + { + result.append_fmt(" %S", ast->PostNameMacro.to_string() ); + } + + if ( ast->Value ) + result.append_fmt( " = %S", ast->Value.to_string() ); + + if ( ast->NumEntries - 1 > 0 ) + { + for ( CodeParam param : ast->Next ) + { + result.append_fmt( ", %S", param.to_string() ); + } + } +} + +String CodePreprocessCond::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + switch ( ast->Type ) + { + using namespace ECode; + case Preprocess_If: + to_string_if( result ); + break; + case Preprocess_IfDef: + to_string_ifdef( result ); + break; + case Preprocess_IfNotDef: + to_string_ifndef( result ); + break; + case Preprocess_ElIf: + to_string_elif( result ); + break; + case Preprocess_Else: + to_string_else( result ); + break; + case Preprocess_EndIf: + to_string_endif( result ); + break; + } + return result; +} + +void CodePreprocessCond::to_string_if( String& result ) +{ + result.append_fmt( "#if %S\n", ast->Content ); +} + +void CodePreprocessCond::to_string_ifdef( String& result ) +{ + result.append_fmt( "#ifdef %S\n", ast->Content ); +} + +void CodePreprocessCond::to_string_ifndef( String& result ) +{ + result.append_fmt( "#ifndef %S\n", ast->Content ); +} + +void CodePreprocessCond::to_string_elif( String& result ) +{ + result.append_fmt( "#elif %S\n", ast->Content ); +} + +void CodePreprocessCond::to_string_else( String& result ) +{ + result.append_fmt( "#else\n" ); +} + +void CodePreprocessCond::to_string_endif( String& result ) +{ + result.append_fmt( "#endif\n" ); +} + +String CodePragma::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + to_string( result ); + return result; +} + +void CodePragma::to_string( String& result ) +{ + result.append_fmt( "#pragma %S\n", ast->Content ); +} + +String CodeSpecifiers::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + to_string( result ); + return result; +} + +void CodeSpecifiers::to_string( String& result ) +{ + s32 idx = 0; + s32 left = ast->NumEntries; + while ( left-- ) + { + StrC spec = ESpecifier::to_str( ast->ArrSpecs[idx] ); + result.append_fmt( "%.*s ", spec.Len, spec.Ptr ); + idx++; + } +} + +String CodeStruct::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + switch ( ast->Type ) + { + using namespace ECode; + case Struct: + to_string_def( result ); + break; + case Struct_Fwd: + to_string_fwd( result ); + break; + } + return result; +} + +void CodeStruct::to_string_def( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + result.append( "struct " ); + + if ( ast->Attributes ) + { + result.append_fmt( "%S ", ast->Attributes.to_string() ); + } + + if ( ast->ParentType ) + { + char const* access_level = to_str( ast->ParentAccess ); + + result.append_fmt( "%S : %s %S", ast->Name, access_level, ast->ParentType.to_string() ); + + CodeType interface = ast->ParentType->Next->cast< CodeType >(); + if ( interface ) + result.append( "\n" ); + + while ( interface ) + { + result.append_fmt( ", %S", interface.to_string() ); + interface = interface->Next ? interface->Next->cast< CodeType >() : CodeType { nullptr }; + } + } + else if ( ast->Name ) + { + result.append( ast->Name ); + } + + if ( ast->InlineCmt ) + { + result.append_fmt( " // %S", ast->InlineCmt->Content ); + } + + result.append_fmt( "\n{\n%S\n}", ast->Body.to_string() ); + + if ( ast->Parent.ast == nullptr || ( ast->Parent->Type != ECode::Typedef && ast->Parent->Type != ECode::Variable ) ) + result.append(";\n"); +} + +void CodeStruct::to_string_fwd( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + if ( ast->Attributes ) + result.append_fmt( "struct %S %S", ast->Attributes.to_string(), ast->Name ); + + else result.append_fmt( "struct %S", ast->Name ); + + if ( ast->Parent.ast == nullptr || ( ast->Parent->Type != ECode::Typedef && ast->Parent->Type != ECode::Variable ) ) + { + if ( ast->InlineCmt ) + result.append_fmt("; %S", ast->InlineCmt->Content ); + else + result.append(";\n"); + } +} + +String CodeTemplate::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + to_string( result ); + return result; +} + +void CodeTemplate::to_string( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + if ( ast->Params ) + result.append_fmt( "template< %S >\n%S", ast->Params.to_string(), ast->Declaration.to_string() ); + else + result.append_fmt( "template<>\n%S", ast->Declaration.to_string() ); +} + +String CodeTypedef::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + to_string( result ); + return result; +} + +void CodeTypedef::to_string( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + result.append( "typedef "); + + // Determines if the typedef is a function typename + if ( ast->UnderlyingType->ReturnType ) + result.append( ast->UnderlyingType.to_string() ); + else + result.append_fmt( "%S %S", ast->UnderlyingType.to_string(), ast->Name ); + + if ( ast->UnderlyingType->Type == ECode::Typename && ast->UnderlyingType->ArrExpr ) + { + result.append_fmt( "[ %S ];", ast->UnderlyingType->ArrExpr->to_string() ); + + AST* next_arr_expr = ast->UnderlyingType->ArrExpr->Next; + while ( next_arr_expr ) + { + result.append_fmt( "[ %S ];", next_arr_expr->to_string() ); + next_arr_expr = next_arr_expr->Next; + } + } + else + { + result.append( ";" ); + } + + if ( ast->InlineCmt ) + result.append_fmt(" %S", ast->InlineCmt->Content); + else + result.append("\n"); +} + +String CodeType::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + to_string( result ); + return result; +} + +void CodeType::to_string( String& result ) +{ + #if defined(GEN_USE_NEW_TYPENAME_PARSING) + if ( ast->ReturnType && ast->Params ) + { + if ( ast->Attributes ) + result.append_fmt( "%S ", ast->Attributes.to_string() ); + else + { + if ( ast->Specs ) + result.append_fmt( "%S ( %S ) ( %S ) %S", ast->ReturnType.to_string(), ast->Name, ast->Params.to_string(), ast->Specs.to_string() ); + else + result.append_fmt( "%S ( %S ) ( %S )", ast->ReturnType.to_string(), ast->Name, ast->Params.to_string() ); + } + + break; + } + #else + if ( ast->ReturnType && ast->Params ) + { + if ( ast->Attributes ) + result.append_fmt( "%S ", ast->Attributes.to_string() ); + else + { + if ( ast->Specs ) + result.append_fmt( "%S %S ( %S ) %S", ast->ReturnType.to_string(), ast->Name, ast->Params.to_string(), ast->Specs.to_string() ); + else + result.append_fmt( "%S %S ( %S )", ast->ReturnType.to_string(), ast->Name, ast->Params.to_string() ); + } + + return; + } + #endif + + if ( ast->Attributes ) + result.append_fmt( "%S ", ast->Attributes.to_string() ); + + if ( ast->Specs ) + result.append_fmt( "%S %S", ast->Name, ast->Specs.to_string() ); + else + result.append_fmt( "%S", ast->Name ); + + if ( ast->IsParamPack ) + result.append("..."); +} + +String CodeUnion::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + to_string( result ); + return result; +} + +void CodeUnion::to_string( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + result.append( "union " ); + + if ( ast->Attributes ) + result.append_fmt( "%S ", ast->Attributes.to_string() ); + + if ( ast->Name ) + { + result.append_fmt( "%S\n{\n%S\n}" + , ast->Name + , ast->Body.to_string() + ); + } + else + { + // Anonymous union + result.append_fmt( "\n{\n%S\n}" + , ast->Body.to_string() + ); + } + + if ( ast->Parent.ast == nullptr || ( ast->Parent->Type != ECode::Typedef && ast->Parent->Type != ECode::Variable ) ) + result.append(";\n"); +} + +String CodeUsing::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + switch ( ast->Type ) + { + using namespace ECode; + case Using: + to_string( result ); + break; + case Using_Namespace: + to_string_ns( result ); + break; + } + return result; +} + +void CodeUsing::to_string( String& result ) +{ + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + if ( ast->Attributes ) + result.append_fmt( "%S ", ast->Attributes.to_string() ); + + if ( ast->UnderlyingType ) + { + result.append_fmt( "using %S = %S", ast->Name, ast->UnderlyingType.to_string() ); + + if ( ast->UnderlyingType->ArrExpr ) + { + result.append_fmt( "[ %S ]", ast->UnderlyingType->ArrExpr.to_string() ); + + AST* next_arr_expr = ast->UnderlyingType->ArrExpr->Next; + while ( next_arr_expr ) + { + result.append_fmt( "[ %S ]", next_arr_expr->to_string() ); + next_arr_expr = next_arr_expr->Next; + } + } + + result.append( ";" ); + } + else + result.append_fmt( "using %S;", ast->Name ); + + if ( ast->InlineCmt ) + result.append_fmt(" %S\n", ast->InlineCmt->Content ); + else + result.append("\n"); +} + +void CodeUsing::to_string_ns( String& result ) +{ + if ( ast->InlineCmt ) + result.append_fmt( "using namespace $S; %S", ast->Name, ast->InlineCmt->Content ); + else + result.append_fmt( "using namespace %s;\n", ast->Name ); +} + +String CodeVar::to_string() +{ + String result = String::make( GlobalAllocator, "" ); + to_string( result ); + return result; +} + +void CodeVar::to_string( String& result ) +{ + if ( ast->Parent && ast->Parent->Type == ECode::Variable ) + { + // Its a comma-separated variable ( a NextVar ) + + if ( ast->Specs ) + result.append_fmt( "%S ", ast->Specs.to_string() ); + + result.append( ast->Name ); + + if ( ast->ValueType->ArrExpr ) + { + result.append_fmt( "[ %S ]", ast->ValueType->ArrExpr.to_string() ); + + AST* next_arr_expr = ast->ValueType->ArrExpr->Next; + while ( next_arr_expr ) + { + result.append_fmt( "[ %S ]", next_arr_expr->to_string() ); + next_arr_expr = next_arr_expr->Next; + } + } + + if ( ast->Value ) + { + if ( ast->VarConstructorInit ) + result.append_fmt( "( %S ", ast->Value.to_string() ); + else + result.append_fmt( " = %S", ast->Value.to_string() ); + } + + // Keep the chain going... + if ( ast->NextVar ) + result.append_fmt( ", %S", ast->NextVar.to_string() ); + + if ( ast->VarConstructorInit ) + result.append( " )"); + + return; + } + + if ( bitfield_is_equal( u32, ast->ModuleFlags, ModuleFlag::Export )) + result.append( "export " ); + + if ( ast->Attributes || ast->Specs ) + { + if ( ast->Attributes ) + result.append_fmt( "%S ", ast->Specs.to_string() ); + + if ( ast->Specs ) + result.append_fmt( "%S\n", ast->Specs.to_string() ); + + result.append_fmt( "%S %S", ast->ValueType.to_string(), ast->Name ); + + if ( ast->ValueType->ArrExpr ) + { + result.append_fmt( "[ %S ]", ast->ValueType->ArrExpr.to_string() ); + + AST* next_arr_expr = ast->ValueType->ArrExpr->Next; + while ( next_arr_expr ) + { + result.append_fmt( "[ %S ]", next_arr_expr->to_string() ); + next_arr_expr = next_arr_expr->Next; + } + } + + if ( ast->BitfieldSize ) + result.append_fmt( " : %S", ast->BitfieldSize.to_string() ); + + if ( ast->Value ) + { + if ( ast->VarConstructorInit ) + result.append_fmt( "( %S ", ast->Value.to_string() ); + else + result.append_fmt( " = %S", ast->Value.to_string() ); + } + + if ( ast->NextVar ) + result.append_fmt( ", %S", ast->NextVar.to_string() ); + + if ( ast->VarConstructorInit ) + result.append( " )"); + + if ( ast->InlineCmt ) + result.append_fmt("; %S", ast->InlineCmt->Content); + else + result.append( ";\n" ); + + return; + } + + if ( ast->BitfieldSize ) + result.append_fmt( "%S %S : %S", ast->ValueType.to_string(), ast->Name, ast->BitfieldSize.to_string() ); + + else if ( ast->ValueType->ArrExpr ) + { + result.append_fmt( "%S %S[ %S ]", ast->ValueType.to_string(), ast->Name, ast->ValueType->ArrExpr.to_string() ); + + AST* next_arr_expr = ast->ValueType->ArrExpr->Next; + while ( next_arr_expr ) + { + result.append_fmt( "[ %S ]", next_arr_expr->to_string() ); + next_arr_expr = next_arr_expr->Next; + } + } + + else + result.append_fmt( "%S %S", ast->ValueType.to_string(), ast->Name ); + + if ( ast->Value ) + { + if ( ast->VarConstructorInit ) + result.append_fmt( "( %S ", ast->Value.to_string() ); + else + result.append_fmt( " = %S", ast->Value.to_string() ); + } + + if ( ast->NextVar ) + result.append_fmt( ", %S", ast->NextVar.to_string() ); + + if ( ast->VarConstructorInit ) + result.append( " )"); + + result.append( ";" ); + + if ( ast->InlineCmt ) + result.append_fmt(" %S", ast->InlineCmt->Content); + else + result.append("\n"); +} +#pragma endregion AST + +#pragma region Interface + +namespace parser { +internal void init(); +internal void deinit(); +} + +internal +void* Global_Allocator_Proc( void* allocator_data, AllocType type, ssize size, ssize alignment, void* old_memory, ssize old_size, u64 flags ) +{ + Arena* last = & Global_AllocatorBuckets.back(); + + switch ( type ) + { + case EAllocation_ALLOC: + { + if ( ( last->TotalUsed + size ) > last->TotalSize ) + { + Arena bucket = Arena::init_from_allocator( heap(), Global_BucketSize ); + + if ( bucket.PhysicalStart == nullptr ) + GEN_FATAL( "Failed to create bucket for Global_AllocatorBuckets"); + + if ( ! Global_AllocatorBuckets.append( bucket ) ) + GEN_FATAL( "Failed to append bucket to Global_AllocatorBuckets"); + + last = & Global_AllocatorBuckets.back(); + } + + return alloc_align( * last, size, alignment ); + } + case EAllocation_FREE: + { + // Doesn't recycle. + } + break; + case EAllocation_FREE_ALL: + { + // Memory::cleanup instead. + } + break; + case EAllocation_RESIZE: + { + if ( last->TotalUsed + size > last->TotalSize ) + { + Arena bucket = Arena::init_from_allocator( heap(), Global_BucketSize ); + + if ( bucket.PhysicalStart == nullptr ) + GEN_FATAL( "Failed to create bucket for Global_AllocatorBuckets"); + + if ( ! Global_AllocatorBuckets.append( bucket ) ) + GEN_FATAL( "Failed to append bucket to Global_AllocatorBuckets"); + + last = & Global_AllocatorBuckets.back(); + } + + void* result = alloc_align( last->Backing, size, alignment ); + + if ( result != nullptr && old_memory != nullptr ) + { + mem_copy( result, old_memory, old_size ); + } + + return result; + } + } + + return nullptr; +} + +internal +void define_constants() +{ + Code::Global = make_code(); + Code::Global->Name = get_cached_string( txt("Global Code") ); + Code::Global->Content = Code::Global->Name; + + Code::Invalid = make_code(); + Code::Invalid.set_global(); + + t_empty = (CodeType) make_code(); + t_empty->Type = ECode::Typename; + t_empty->Name = get_cached_string( txt("") ); + t_empty.set_global(); + + access_private = make_code(); + access_private->Type = ECode::Access_Private; + access_private->Name = get_cached_string( txt("private:\n") ); + access_private.set_global(); + + access_protected = make_code(); + access_protected->Type = ECode::Access_Protected; + access_protected->Name = get_cached_string( txt("protected:\n") ); + access_protected.set_global(); + + access_public = make_code(); + access_public->Type = ECode::Access_Public; + access_public->Name = get_cached_string( txt("public:\n") ); + access_public.set_global(); + + attrib_api_export = def_attributes( code(GEN_API_Export_Code)); + attrib_api_export.set_global(); + + attrib_api_import = def_attributes( code(GEN_API_Import_Code)); + attrib_api_import.set_global(); + + module_global_fragment = make_code(); + module_global_fragment->Type = ECode::Untyped; + module_global_fragment->Name = get_cached_string( txt("module;") ); + module_global_fragment->Content = module_global_fragment->Name; + module_global_fragment.set_global(); + + module_private_fragment = make_code(); + module_private_fragment->Type = ECode::Untyped; + module_private_fragment->Name = get_cached_string( txt("module : private;") ); + module_private_fragment->Content = module_private_fragment->Name; + module_private_fragment.set_global(); + + fmt_newline = make_code(); + fmt_newline->Type = ECode::NewLine; + fmt_newline.set_global(); + + pragma_once = (CodePragma) make_code(); + pragma_once->Type = ECode::Preprocess_Pragma; + pragma_once->Name = get_cached_string( txt("once") ); + pragma_once->Content = pragma_once->Name; + pragma_once.set_global(); + + param_varadic = (CodeType) make_code(); + param_varadic->Type = ECode::Parameters; + param_varadic->Name = get_cached_string( txt("...") ); + param_varadic->ValueType = t_empty; + param_varadic.set_global(); + + preprocess_else = (CodePreprocessCond) make_code(); + preprocess_else->Type = ECode::Preprocess_Else; + preprocess_else.set_global(); + + preprocess_endif = (CodePreprocessCond) make_code(); + preprocess_endif->Type = ECode::Preprocess_EndIf; + preprocess_endif.set_global(); + +# define def_constant_code_type( Type_ ) \ + t_##Type_ = def_type( name(Type_) ); \ + t_##Type_.set_global(); + + def_constant_code_type( auto ); + def_constant_code_type( void ); + def_constant_code_type( int ); + def_constant_code_type( bool ); + def_constant_code_type( char ); + def_constant_code_type( wchar_t ); + def_constant_code_type( class ); + def_constant_code_type( typename ); + +#ifdef GEN_DEFINE_LIBRARY_CODE_CONSTANTS + t_b32 = def_type( name(b32) ); + + def_constant_code_type( s8 ); + def_constant_code_type( s16 ); + def_constant_code_type( s32 ); + def_constant_code_type( s64 ); + + def_constant_code_type( u8 ); + def_constant_code_type( u16 ); + def_constant_code_type( u32 ); + def_constant_code_type( u64 ); + + def_constant_code_type( ssize ); + def_constant_code_type( usize ); + + def_constant_code_type( f32 ); + def_constant_code_type( f64 ); +#endif +# undef def_constant_code_type + + +# define def_constant_spec( Type_, ... ) \ + spec_##Type_ = def_specifiers( num_args(__VA_ARGS__), __VA_ARGS__); \ + spec_##Type_.set_global(); + +# pragma push_macro("forceinline") +# pragma push_macro("global") +# pragma push_macro("internal") +# pragma push_macro("local_persist") +# pragma push_macro("neverinline") +# undef forceinline +# undef global +# undef internal +# undef local_persist +# undef neverinline + def_constant_spec( const, ESpecifier::Const ); + def_constant_spec( consteval, ESpecifier::Consteval ); + def_constant_spec( constexpr, ESpecifier::Constexpr ); + def_constant_spec( constinit, ESpecifier::Constinit ); + def_constant_spec( extern_linkage, ESpecifier::External_Linkage ); + def_constant_spec( final, ESpecifier::Final ); + def_constant_spec( forceinline, ESpecifier::ForceInline ); + def_constant_spec( global, ESpecifier::Global ); + def_constant_spec( inline, ESpecifier::Inline ); + def_constant_spec( internal_linkage, ESpecifier::Internal_Linkage ); + def_constant_spec( local_persist, ESpecifier::Local_Persist ); + def_constant_spec( mutable, ESpecifier::Mutable ); + def_constant_spec( neverinline, ESpecifier::NeverInline ); + def_constant_spec( noexcept, ESpecifier::NoExceptions ); + def_constant_spec( override, ESpecifier::Override ); + def_constant_spec( ptr, ESpecifier::Ptr ); + def_constant_spec( pure, ESpecifier::Pure ) + def_constant_spec( ref, ESpecifier::Ref ); + def_constant_spec( register, ESpecifier::Register ); + def_constant_spec( rvalue, ESpecifier::RValue ); + def_constant_spec( static_member, ESpecifier::Static ); + def_constant_spec( thread_local, ESpecifier::Thread_Local ); + def_constant_spec( virtual, ESpecifier::Virtual ); + def_constant_spec( volatile, ESpecifier::Volatile) + + spec_local_persist = def_specifiers( 1, ESpecifier::Local_Persist ); + spec_local_persist.set_global(); + +# pragma pop_macro("forceinline") +# pragma pop_macro("global") +# pragma pop_macro("internal") +# pragma pop_macro("local_persist") +# pragma pop_macro("neverinline") + +# undef def_constant_spec +} + +void init() +{ + // Setup global allocator + { + GlobalAllocator = AllocatorInfo { & Global_Allocator_Proc, nullptr }; + + Global_AllocatorBuckets = Array::init_reserve( heap(), 128 ); + + if ( Global_AllocatorBuckets == nullptr ) + GEN_FATAL( "Failed to reserve memory for Global_AllocatorBuckets"); + + Arena bucket = Arena::init_from_allocator( heap(), Global_BucketSize ); + + if ( bucket.PhysicalStart == nullptr ) + GEN_FATAL( "Failed to create first bucket for Global_AllocatorBuckets"); + + Global_AllocatorBuckets.append( bucket ); + + } + + // Setup the arrays + { + CodePools = Array::init_reserve( Allocator_DataArrays, InitSize_DataArrays ); + + if ( CodePools == nullptr ) + GEN_FATAL( "gen::init: Failed to initialize the CodePools array" ); + + StringArenas = Array::init_reserve( Allocator_DataArrays, InitSize_DataArrays ); + + if ( StringArenas == nullptr ) + GEN_FATAL( "gen::init: Failed to initialize the StringArenas array" ); + } + + // Setup the code pool and code entries arena. + { + Pool code_pool = Pool::init( Allocator_CodePool, CodePool_NumBlocks, sizeof(AST) ); + + if ( code_pool.PhysicalStart == nullptr ) + GEN_FATAL( "gen::init: Failed to initialize the code pool" ); + + CodePools.append( code_pool ); + + LexArena = Arena::init_from_allocator( Allocator_Lexer, LexAllocator_Size ); + + Arena string_arena = Arena::init_from_allocator( Allocator_StringArena, SizePer_StringArena ); + + if ( string_arena.PhysicalStart == nullptr ) + GEN_FATAL( "gen::init: Failed to initialize the string arena" ); + + StringArenas.append( string_arena ); + } + + // Setup the hash tables + { + StringCache = StringTable::init( Allocator_StringTable ); + + if ( StringCache.Entries == nullptr ) + GEN_FATAL( "gen::init: Failed to initialize the StringCache"); + } + + // Preprocessor Defines + PreprocessorDefines = Array::init_reserve( GlobalAllocator, kilobytes(1) ); + + define_constants(); + parser::init(); +} + +void deinit() +{ + usize index = 0; + usize left = CodePools.num(); + do + { + Pool* code_pool = & CodePools[index]; + code_pool->free(); + index++; + } + while ( left--, left ); + + index = 0; + left = StringArenas.num(); + do + { + Arena* string_arena = & StringArenas[index]; + string_arena->free(); + index++; + } + while ( left--, left ); + + StringCache.destroy(); + + CodePools.free(); + StringArenas.free(); + + LexArena.free(); + + PreprocessorDefines.free(); + + index = 0; + left = Global_AllocatorBuckets.num(); + do + { + Arena* bucket = & Global_AllocatorBuckets[ index ]; + bucket->free(); + index++; + } + while ( left--, left ); + + Global_AllocatorBuckets.free(); + parser::deinit(); +} + +void reset() +{ + s32 index = 0; + s32 left = CodePools.num(); + do + { + Pool* code_pool = & CodePools[index]; + code_pool->clear(); + index++; + } + while ( left--, left ); + + index = 0; + left = StringArenas.num(); + do + { + Arena* string_arena = & StringArenas[index]; + string_arena->TotalUsed = 0;; + index++; + } + while ( left--, left ); + + StringCache.clear(); + + define_constants(); +} + +AllocatorInfo get_string_allocator( s32 str_length ) +{ + Arena* last = & StringArenas.back(); + + usize size_req = str_length + sizeof(String::Header) + sizeof(char*); + + if ( last->TotalUsed + ssize(size_req) > last->TotalSize ) + { + Arena new_arena = Arena::init_from_allocator( Allocator_StringArena, SizePer_StringArena ); + + if ( ! StringArenas.append( new_arena ) ) + GEN_FATAL( "gen::get_string_allocator: Failed to allocate a new string arena" ); + + last = & StringArenas.back(); + } + + return * last; +} + +// Will either make or retrive a code string. +StringCached get_cached_string( StrC str ) +{ + s32 hash_length = str.Len > kilobytes(1) ? kilobytes(1) : str.Len; + u64 key = crc32( str.Ptr, hash_length ); + { + StringCached* result = StringCache.get( key ); + + if ( result ) + return * result; + } + + String result = String::make( get_string_allocator( str.Len ), str ); + StringCache.set( key, result ); + + return result; +} + +// Used internally to retireve a Code object form the CodePool. +Code make_code() +{ + Pool* allocator = & CodePools.back(); + if ( allocator->FreeList == nullptr ) + { + Pool code_pool = Pool::init( Allocator_CodePool, CodePool_NumBlocks, sizeof(AST) ); + + if ( code_pool.PhysicalStart == nullptr ) + GEN_FATAL( "gen::make_code: Failed to allocate a new code pool - CodePool allcoator returned nullptr." ); + + if ( ! CodePools.append( code_pool ) ) + GEN_FATAL( "gen::make_code: Failed to allocate a new code pool - CodePools failed to append new pool." ); + + allocator = & CodePools.back(); + } + + Code result { rcast( AST*, alloc( * allocator, sizeof(AST) )) }; + mem_set( result.ast, 0, sizeof(AST) ); + // result->Type = ECode::Invalid; + + // result->Content = { nullptr }; + // result->Prev = { nullptr }; + // result->Next = { nullptr }; + // result->Token = nullptr; + // result->Parent = { nullptr }; + // result->Name = { nullptr }; + // result->Type = ECode::Invalid; + // result->ModuleFlags = ModuleFlag::Invalid; + // result->NumEntries = 0; + + return result; +} + +void set_allocator_data_arrays( AllocatorInfo allocator ) +{ + Allocator_DataArrays = allocator; +} + +void set_allocator_code_pool( AllocatorInfo allocator ) +{ + Allocator_CodePool = allocator; +} + +void set_allocator_lexer( AllocatorInfo allocator ) +{ + Allocator_Lexer = allocator; +} + +void set_allocator_string_arena( AllocatorInfo allocator ) +{ + Allocator_StringArena = allocator; +} + +void set_allocator_string_table( AllocatorInfo allocator ) +{ + Allocator_StringArena = allocator; +} + +#pragma region Upfront + +enum class OpValidateResult : u32 +{ + Fail, + Global, + Member +}; + +OpValidateResult operator__validate( OperatorT op, CodeParam params_code, CodeType ret_type, CodeSpecifiers specifier ) +{ + using namespace EOperator; + + if ( op == EOperator::Invalid ) + { + log_failure("gen::def_operator: op cannot be invalid"); + return OpValidateResult::Fail; + } + +#pragma region Helper Macros +# define check_params() \ + if ( ! params_code ) \ + { \ + log_failure("gen::def_operator: params is null and operator%s requires it", to_str(op)); \ + return OpValidateResult::Fail; \ + } \ + if ( params_code->Type != ECode::Parameters ) \ + { \ + log_failure("gen::def_operator: params is not of Parameters type - %s", params_code.debug_str()); \ + return OpValidateResult::Fail; \ + } + +# define check_param_eq_ret() \ + if ( ! is_member_symbol && ! params_code->ValueType.is_equal( ret_type) ) \ + { \ + log_failure("gen::def_operator: operator%s requires first parameter to equal return type\n" \ + "param types: %s\n" \ + "return type: %s", \ + to_str(op).Ptr, \ + params_code.debug_str(), \ + ret_type.debug_str() \ + ); \ + return OpValidateResult::Fail; \ + } +#pragma endregion Helper Macros + + if ( ! ret_type ) + { + log_failure("gen::def_operator: ret_type is null but is required by operator%s", to_str(op)); + } + + if ( ret_type->Type != ECode::Typename ) + { + log_failure("gen::def_operator: ret_type is not of typename type - %s", ret_type.debug_str()); + return OpValidateResult::Fail; + } + + bool is_member_symbol = false; + + switch ( op ) + { +# define specs( ... ) num_args( __VA_ARGS__ ), __VA_ARGS__ + case Assign: + check_params(); + + if ( params_code->NumEntries > 1 ) + { + log_failure("gen::def_operator: " + "operator%s does not support non-member definition (more than one parameter provided) - %s", + to_str(op), + params_code.debug_str() + ); + return OpValidateResult::Fail; + } + + is_member_symbol = true; + break; + + case Assign_Add: + case Assign_Subtract: + case Assign_Multiply: + case Assign_Divide: + case Assign_Modulo: + case Assign_BAnd: + case Assign_BOr: + case Assign_BXOr: + case Assign_LShift: + case Assign_RShift: + check_params(); + + if ( params_code->NumEntries == 1 ) + is_member_symbol = true; + + else + check_param_eq_ret(); + + if (params_code->NumEntries > 2 ) + { + log_failure("gen::def_operator: operator%s may not be defined with more than two parametes - param count; %d\n%s" + , to_str(op) + , params_code->NumEntries + , params_code.debug_str() + ); + return OpValidateResult::Fail; + } + break; + + case Increment: + case Decrement: + // If its not set, it just means its a prefix member op. + if ( params_code ) + { + if ( params_code->Type != ECode::Parameters ) + { + log_failure("gen::def_operator: operator%s params code provided is not of Parameters type - %s" + , to_str(op) + , params_code.debug_str() + ); + return OpValidateResult::Fail; + } + + switch ( params_code->NumEntries ) + { + case 1: + if ( params_code->ValueType.is_equal( t_int ) ) + is_member_symbol = true; + + else + check_param_eq_ret(); + break; + + case 2: + check_param_eq_ret(); + + if ( ! params_code.get(1).is_equal( t_int ) ) + { + log_failure("gen::def_operator: " + "operator%s requires second parameter of non-member definition to be int for post-decrement", + to_str(op) + ); + return OpValidateResult::Fail; + } + break; + + default: + log_failure("gen::def_operator: operator%s recieved unexpected number of parameters recived %d instead of 0-2" + , to_str(op) + , params_code->NumEntries + ); + return OpValidateResult::Fail; + } + } + break; + + case Unary_Plus: + case Unary_Minus: + if ( ! params_code ) + is_member_symbol = true; + + else + { + if ( params_code->Type != ECode::Parameters ) + { + log_failure("gen::def_operator: params is not of Parameters type - %s", params_code.debug_str()); + return OpValidateResult::Fail; + } + + if ( params_code->ValueType.is_equal( ret_type ) ) + { + log_failure("gen::def_operator: " + "operator%s is non-member symbol yet first paramter does not equal return type\n" + "param type: %s\n" + "return type: %s\n" + , params_code.debug_str() + , ret_type.debug_str() + ); + return OpValidateResult::Fail; + } + + if ( params_code->NumEntries > 1 ) + { + log_failure("gen::def_operator: operator%s may not have more than one parameter - param count: %d" + , to_str(op) + , params_code->NumEntries + ); + return OpValidateResult::Fail; + } + } + break; + + case BNot: + { + // Some compilers let you do this... + #if 0 + if ( ! ret_type.is_equal( t_bool) ) + { + log_failure( "gen::def_operator: return type is not a boolean - %s", params_code.debug_str() ); + return OpValidateResult::Fail; + } + #endif + + if ( ! params_code ) + is_member_symbol = true; + + else + { + if ( params_code->Type != ECode::Parameters ) + { + log_failure( "gen::def_operator: params is not of Parameters type - %s", params_code.debug_str() ); + return OpValidateResult::Fail; + } + + if ( params_code->NumEntries > 1 ) + { + log_failure( + "gen::def_operator: operator%s may not have more than one parameter - param count: %d", + to_str( op ), + params_code->NumEntries + ); + return OpValidateResult::Fail; + } + } + break; + } + + case Add: + case Subtract: + case Multiply: + case Divide: + case Modulo: + case BAnd: + case BOr: + case BXOr: + case LShift: + case RShift: + check_params(); + + switch ( params_code->NumEntries ) + { + case 1: + is_member_symbol = true; + break; + + case 2: + if ( ! params_code->ValueType.is_equal( ret_type ) ) + { + log_failure("gen::def_operator: " + "operator%s is non-member symbol yet first paramter does not equal return type\n" + "param type: %s\n" + "return type: %s\n" + , params_code.debug_str() + , ret_type.debug_str() + ); + return OpValidateResult::Fail; + } + break; + + default: + log_failure("gen::def_operator: operator%s recieved unexpected number of paramters recived %d instead of 0-2" + , to_str(op) + , params_code->NumEntries + ); + return OpValidateResult::Fail; + } + break; + + case UnaryNot: + if ( ! params_code ) + is_member_symbol = true; + + else + { + if ( params_code->Type != ECode::Parameters ) + { + log_failure("gen::def_operator: params is not of Parameters type - %s", params_code.debug_str()); + return OpValidateResult::Fail; + } + + if ( params_code->NumEntries != 1 ) + { + log_failure("gen::def_operator: operator%s recieved unexpected number of paramters recived %d instead of 0-1" + , to_str(op) + , params_code->NumEntries + ); + return OpValidateResult::Fail; + } + } + + if ( ! ret_type.is_equal( t_bool )) + { + log_failure("gen::def_operator: operator%s return type must be of type bool - %s" + , to_str(op) + , ret_type.debug_str() + ); + return OpValidateResult::Fail; + } + break; + + case LAnd: + case LOr: + case LEqual: + case LNot: + case Lesser: + case Greater: + case LesserEqual: + case GreaterEqual: + check_params(); + + switch ( params_code->NumEntries ) + { + case 1: + is_member_symbol = true; + break; + + case 2: + break; + + default: + log_failure("gen::def_operator: operator%s recieved unexpected number of paramters recived %d instead of 1-2" + , to_str(op) + , params_code->NumEntries + ); + return OpValidateResult::Fail; + } + break; + + case Indirection: + case AddressOf: + case MemberOfPointer: + if ( params_code && params_code->NumEntries > 1) + { + log_failure("gen::def_operator: operator%s recieved unexpected number of paramters recived %d instead of 0-1" + , to_str(op) + , params_code->NumEntries + ); + return OpValidateResult::Fail; + } + else + { + is_member_symbol = true; + } + break; + + case PtrToMemOfPtr: + if ( params_code ) + { + log_failure("gen::def_operator: operator%s expects no paramters - %s", to_str(op), params_code.debug_str()); + return OpValidateResult::Fail; + } + break; + + case Subscript: + case FunctionCall: + case Comma: + check_params(); + break; + + case New: + case Delete: + // This library doesn't support validating new and delete yet. + break; +# undef specs + } + + return is_member_symbol ? OpValidateResult::Member : OpValidateResult::Global; +# undef check_params +# undef check_ret_type +# undef check_param_eq_ret +} + + +#pragma region Helper Marcos +// This snippet is used in nearly all the functions. +#define name_check( Context_, Name_ ) \ +{ \ + if ( Name_.Len <= 0 ) \ + { \ + log_failure( "gen::" stringize(Context_) ": Invalid name length provided - %d", Name_.Len ); \ + return CodeInvalid; \ + } \ + \ + if ( Name_.Ptr == nullptr ) \ + { \ + log_failure( "gen::" stringize(Context_) ": name is null" ); \ + return CodeInvalid; \ + } \ +} + +#define null_check( Context_, Code_ ) \ + if ( ! Code_ ) \ + { \ + log_failure( "gen::" stringize(Context_) ": " stringize(Code_) " provided is null" ); \ + return CodeInvalid; \ + } + +#define null_or_invalid_check( Context_, Code_ ) \ +{ \ + if ( ! Code_ ) \ + { \ + log_failure( "gen::" stringize(Context_) ": " stringize(Code_) " provided is null" ); \ + return CodeInvalid; \ + } \ + \ + if ( Code_->is_invalid() ) \ + { \ + log_failure("gen::" stringize(Context_) ": " stringize(Code_) " provided is invalid" ); \ + return CodeInvalid; \ + } \ +} + +#define not_implemented( Context_ ) \ + log_failure( "gen::%s: This function is not implemented" ); \ + return CodeInvalid; +#pragma endregion Helper Marcos + + +/* +The implementaiton of the upfront constructors involves doing three things: +* Validate the arguments given to construct the intended type of AST is valid. +* Construct said AST type. +* Lock the AST (set to readonly) and return the valid object. + +If any of the validation fails, it triggers a call to log_failure with as much info the give the user so that they can hopefully +identify the issue without having to debug too much (at least they can debug though...) + +The largest of the functions is related to operator overload definitions. +The library validates a good protion of their form and thus the argument processing for is quite a bit. +*/ +CodeAttributes def_attributes( StrC content ) +{ + if ( content.Len <= 0 || content.Ptr == nullptr ) + { + log_failure( "gen::def_attributes: Invalid attributes provided" ); + return CodeInvalid; + } + + Code + result = make_code(); + result->Type = ECode::PlatformAttributes; + result->Name = get_cached_string( content ); + result->Content = result->Name; + + return (CodeAttributes) result; +} + +CodeComment def_comment( StrC content ) +{ + if ( content.Len <= 0 || content.Ptr == nullptr ) + { + log_failure( "gen::def_comment: Invalid comment provided:" ); + return CodeInvalid; + } + + static char line[ MaxCommentLineLength ]; + + String cmt_formatted = String::make_reserve( GlobalAllocator, kilobytes(1) ); + char const* end = content.Ptr + content.Len; + char const* scanner = content.Ptr; + s32 curr = 0; + do + { + char const* next = scanner; + s32 length = 0; + while ( next != end && scanner[ length ] != '\n' ) + { + next = scanner + length; + length++; + } + length++; + + str_copy( line, scanner, length ); + cmt_formatted.append_fmt( "//%.*s", length, line ); + mem_set( line, 0, MaxCommentLineLength ); + + scanner += length; + } + while ( scanner <= end ); + + if ( cmt_formatted.back() != '\n' ) + cmt_formatted.append( "\n" ); + + Code + result = make_code(); + result->Type = ECode::Comment; + result->Name = get_cached_string( cmt_formatted ); + result->Content = result->Name; + + cmt_formatted.free(); + + return (CodeComment) result; +} + +CodeConstructor def_constructor( CodeParam params, Code initializer_list, Code body ) +{ + using namespace ECode; + + if ( params && params->Type != Parameters ) + { + log_failure("gen::def_constructor: params must be of Parameters type - %s", params.debug_str()); + return CodeInvalid; + } + + CodeConstructor + result = (CodeConstructor) make_code(); + + if ( params ) + { + result->Params = params; + } + + if ( initializer_list ) + { + result->InitializerList = initializer_list; + } + + if ( body ) + { + switch ( body->Type ) + { + case Function_Body: + case Untyped: + break; + + default: + log_failure("gen::def_constructor: body must be either of Function_Body or Untyped type - %s", body.debug_str()); + return CodeInvalid; + } + + result->Type = Constructor; + result->Body = body; + } + else + { + result->Type = Constructor_Fwd; + } + + return result; +} + +CodeClass def_class( StrC name + , Code body + , CodeType parent, AccessSpec parent_access + , CodeAttributes attributes + , ModuleFlag mflags + , CodeType* interfaces, s32 num_interfaces ) +{ + using namespace ECode; + + name_check( def_class, name ); + + if ( attributes && attributes->Type != PlatformAttributes ) + { + log_failure( "gen::def_class: attributes was not a 'PlatformAttributes' type: %s", attributes.debug_str() ); + return CodeInvalid; + } + + if ( parent && ( parent->Type != Class && parent->Type != Struct && parent->Type != Typename && parent->Type != Untyped ) ) + { + log_failure( "gen::def_class: parent provided is not type 'Class', 'Struct', 'Typeanme', or 'Untyped': %s", parent.debug_str() ); + return CodeInvalid; + } + + CodeClass + result = (CodeClass) make_code(); + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + + if ( body ) + { + switch ( body->Type ) + { + case Class_Body: + case Untyped: + break; + + default: + log_failure("gen::def_class: body must be either of Class_Body or Untyped type - %s", body.debug_str()); + return CodeInvalid; + } + + result->Type = Class; + result->Body = body; + result->Body->Parent = result; // TODO(Ed): Review this? + } + else + { + result->Type = Class_Fwd; + } + + if ( attributes ) + result->Attributes = attributes; + + if ( parent ) + { + result->ParentAccess = parent_access; + result->ParentType = parent; + } + + if ( interfaces ) + { + for (s32 idx = 0; idx < num_interfaces; idx++ ) + { + result.add_interface( interfaces[idx] ); + } + } + + return result; +} + +CodeDefine def_define( StrC name, StrC content ) +{ + using namespace ECode; + + name_check( def_define, name ); + + // Defines can be empty definitions +#if 0 + if ( content.Len <= 0 || content.Ptr == nullptr ) + { + log_failure( "gen::def_define: Invalid value provided" ); + return CodeInvalid; + } +#endif + + CodeDefine + result = (CodeDefine) make_code(); + result->Type = Preprocess_Define; + result->Name = get_cached_string( name ); + if ( content.Len <= 0 || content.Ptr == nullptr ) + { + result->Content = get_cached_string( txt("") ); + } + else + result->Content = get_cached_string( content ); + + return result; +} + +CodeDestructor def_destructor( Code body, CodeSpecifiers specifiers ) +{ + using namespace ECode; + + if ( specifiers && specifiers->Type != Specifiers ) + { + log_failure( "gen::def_destructor: specifiers was not a 'Specifiers' type: %s", specifiers.debug_str() ); + return CodeInvalid; + } + + CodeDestructor result = (CodeDestructor) make_code(); + + if ( specifiers ) + result->Specs = specifiers; + + if ( body ) + { + switch ( body->Type ) + { + case Function_Body: + case Untyped: + break; + + default: + log_failure("gen::def_destructor: body must be either of Function_Body or Untyped type - %s", body.debug_str()); + return CodeInvalid; + } + + result->Type = Destructor; + result->Body = body; + } + else + { + result->Type = Destructor_Fwd; + } + + return result; +} + +CodeEnum def_enum( StrC name + , Code body, CodeType type + , EnumT specifier, CodeAttributes attributes + , ModuleFlag mflags ) +{ + using namespace ECode; + + name_check( def_enum, name ); + + if ( type && type->Type != Typename ) + { + log_failure( "gen::def_enum: enum underlying type provided was not of type Typename: %s", type.debug_str() ); + return CodeInvalid; + } + + if ( attributes && attributes->Type != PlatformAttributes ) + { + log_failure( "gen::def_enum: attributes was not a 'PlatformAttributes' type: %s", attributes.debug_str() ); + return CodeInvalid; + } + + CodeEnum + result = (CodeEnum) make_code(); + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + + if ( body ) + { + switch ( body->Type ) + { + case Enum_Body: + case Untyped: + break; + + default: + log_failure( "gen::def_enum: body must be of Enum_Body or Untyped type %s", body.debug_str()); + return CodeInvalid; + } + + result->Type = specifier == EnumClass ? + Enum_Class : Enum; + + result->Body = body; + } + else + { + result->Type = specifier == EnumClass ? + Enum_Class_Fwd : Enum_Fwd; + } + + if ( attributes ) + result->Attributes = attributes; + + if ( type ) + { + result->UnderlyingType = type; + } + else if ( result->Type != Enum_Class_Fwd && result->Type != Enum_Fwd ) + { + log_failure( "gen::def_enum: enum forward declaration must have an underlying type" ); + return CodeInvalid; + } + + return result; +} + +CodeExec def_execution( StrC content ) +{ + if ( content.Len <= 0 || content.Ptr == nullptr ) + { + log_failure( "gen::def_execution: Invalid execution provided" ); + return CodeInvalid; + } + + Code + result = make_code(); + result->Type = ECode::Execution; + result->Name = get_cached_string( content ); + result->Content = result->Name; + + return (CodeExec) result; +} + +CodeExtern def_extern_link( StrC name, Code body ) +{ + using namespace ECode; + + name_check( def_extern_linkage, name ); + null_check( def_extern_linkage, body ); + + if ( body->Type != Extern_Linkage_Body && body->Type != Untyped ) + { + log_failure("gen::def_extern_linkage: body is not of extern_linkage or untyped type %s", body->debug_str()); + return CodeInvalid; + } + + CodeExtern + result = (CodeExtern)make_code(); + result->Type = Extern_Linkage; + result->Name = get_cached_string( name ); + result->Body = body; + + return (CodeExtern) result; +} + +CodeFriend def_friend( Code declaration ) +{ + using namespace ECode; + + null_check( def_friend, declaration ); + + switch ( declaration->Type ) + { + case Class_Fwd: + case Function_Fwd: + case Operator_Fwd: + case Struct_Fwd: + case Class: + case Function: + case Operator: + case Struct: + break; + + default: + log_failure("gen::def_friend: requires declartion to have class, function, operator, or struct - %s", declaration->debug_str()); + return CodeInvalid; + } + + CodeFriend + result = (CodeFriend) make_code(); + result->Type = Friend; + + result->Declaration = declaration; + + return result; +} + +CodeFn def_function( StrC name + , CodeParam params , CodeType ret_type, Code body + , CodeSpecifiers specifiers, CodeAttributes attributes + , ModuleFlag mflags ) +{ + using namespace ECode; + + name_check( def_function, name ); + + if ( params && params->Type != Parameters ) + { + log_failure( "gen::def_function: params was not a `Parameters` type: %s", params.debug_str() ); + return CodeInvalid; + } + + if ( ret_type && ret_type->Type != Typename ) + { + log_failure( "gen::def_function: ret_type was not a Typename: %s", ret_type.debug_str() ); + return CodeInvalid; + } + + if ( specifiers && specifiers->Type != Specifiers ) + { + log_failure( "gen::def_function: specifiers was not a `Specifiers` type: %s", specifiers.debug_str() ); + return CodeInvalid; + } + + if ( attributes && attributes->Type != PlatformAttributes ) + { + log_failure( "gen::def_function: attributes was not a `PlatformAttributes` type: %s", attributes.debug_str() ); + return CodeInvalid; + } + + CodeFn + result = (CodeFn) make_code(); + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + + if ( body ) + { + switch ( body->Type ) + { + case Function_Body: + case Execution: + case Untyped: + break; + + default: + { + log_failure("gen::def_function: body must be either of Function_Body, Execution, or Untyped type. %s", body->debug_str()); + return CodeInvalid; + } + } + + result->Type = Function; + result->Body = body; + } + else + { + result->Type = Function_Fwd; + } + + if ( attributes ) + result->Attributes = attributes; + + if ( specifiers ) + result->Specs = specifiers; + + if ( ret_type ) + { + result->ReturnType = ret_type; + } + else + { + result->ReturnType = t_void; + } + + if ( params ) + result->Params = params; + + return result; +} + +CodeInclude def_include( StrC path, bool foreign ) +{ + if ( path.Len <= 0 || path.Ptr == nullptr ) + { + log_failure( "gen::def_include: Invalid path provided - %d" ); + return CodeInvalid; + } + + StrC content = foreign ? + to_str( str_fmt_buf( "<%.*s>", path.Len, path.Ptr )) + : to_str( str_fmt_buf( "\"%.*s\"", path.Len, path.Ptr )); + + Code + result = make_code(); + result->Type = ECode::Preprocess_Include; + result->Name = get_cached_string( content ); + result->Content = result->Name; + + return (CodeInclude) result; +} + +CodeModule def_module( StrC name, ModuleFlag mflags ) +{ + name_check( def_module, name ); + + Code + result = make_code(); + result->Type = ECode::Module; + result->Name = get_cached_string( name ); + result->Content = result->Name; + result->ModuleFlags = mflags; + + return (CodeModule) result; +} + +CodeNS def_namespace( StrC name, Code body, ModuleFlag mflags ) +{ + using namespace ECode; + + name_check( def_namespace, name ); + null_check( def_namespace, body ); + + if ( body->Type != Namespace_Body && body->Type != Untyped ) + { + log_failure("gen::def_namespace: body is not of namespace or untyped type %s", body.debug_str()); + return CodeInvalid; + } + + CodeNS + result = (CodeNS) make_code(); + result->Type = Namespace; + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + result->Body = body; + + return result; +} + +CodeOperator def_operator( OperatorT op, StrC nspace + , CodeParam params_code, CodeType ret_type, Code body + , CodeSpecifiers specifiers, CodeAttributes attributes + , ModuleFlag mflags ) +{ + using namespace ECode; + + if ( attributes && attributes->Type != PlatformAttributes ) + { + log_failure( "gen::def_operator: PlatformAttributes was provided but its not of attributes type: %s", attributes.debug_str() ); + return CodeInvalid; + } + + if ( specifiers && specifiers->Type != Specifiers ) + { + log_failure( "gen::def_operator: Specifiers was provided but its not of specifiers type: %s", specifiers.debug_str() ); + return CodeInvalid; + } + + OpValidateResult check_result = operator__validate( op, params_code, ret_type, specifiers ); + + if ( check_result == OpValidateResult::Fail ) + { + return CodeInvalid; + } + + char const* name = nullptr; + + StrC op_str = to_str( op ); + if ( nspace.Len > 0 ) + name = str_fmt_buf( "%.*soperator %.*s", nspace.Len, nspace.Ptr, op_str.Len, op_str.Ptr ); + else + name = str_fmt_buf( "operator %.*s", op_str.Len, op_str.Ptr ); + CodeOperator + result = (CodeOperator) make_code(); + result->Name = get_cached_string( { str_len(name), name } ); + result->ModuleFlags = mflags; + result->Op = op; + + if ( body ) + { + switch ( body->Type ) + { + case Function_Body: + case Execution: + case Untyped: + break; + + default: + { + log_failure("gen::def_operator: body must be either of Function_Body, Execution, or Untyped type. %s", body->debug_str()); + return CodeInvalid; + } + } + + result->Type = check_result == OpValidateResult::Global ? + Operator : Operator_Member; + + result->Body = body; + } + else + { + result->Type = check_result == OpValidateResult::Global ? + Operator_Fwd : Operator_Member_Fwd; + } + + if ( attributes ) + result->Attributes = attributes; + + if ( specifiers ) + result->Specs = specifiers; + + result->ReturnType = ret_type; + + if (params_code) + result->Params = params_code; + + return result; +} + +CodeOpCast def_operator_cast( CodeType type, Code body, CodeSpecifiers const_spec ) +{ + using namespace ECode; + null_check( def_operator_cast, type ); + + if ( type->Type != Typename ) + { + log_failure( "gen::def_operator_cast: type is not a typename - %s", type.debug_str() ); + return CodeInvalid; + } + + CodeOpCast result = (CodeOpCast) make_code(); + + if (body) + { + result->Type = Operator_Cast; + + if ( body->Type != Function_Body && body->Type != Execution ) + { + log_failure( "gen::def_operator_cast: body is not of function body or execution type - %s", body.debug_str() ); + return CodeInvalid; + } + + result->Body = body; + } + else + { + result->Type = Operator_Cast_Fwd; + } + + if ( const_spec ) + { + result->Specs = const_spec; + } + + result->ValueType = type; + return result; +} + +CodeParam def_param( CodeType type, StrC name, Code value ) +{ + using namespace ECode; + + name_check( def_param, name ); + null_check( def_param, type ); + + if ( type->Type != Typename ) + { + log_failure( "gen::def_param: type is not a typename - %s", type.debug_str() ); + return CodeInvalid; + } + + if ( value && value->Type != Untyped ) + { + log_failure( "gen::def_param: value is not untyped - %s", value.debug_str() ); + return CodeInvalid; + } + + CodeParam + result = (CodeParam) make_code(); + result->Type = Parameters; + result->Name = get_cached_string( name ); + + result->ValueType = type; + + if ( value ) + result->Value = value; + + result->NumEntries++; + + return result; +} + +CodePragma def_pragma( StrC directive ) +{ + using namespace ECode; + + if ( directive.Len <= 0 || directive.Ptr == nullptr ) + { + log_failure( "gen::def_comment: Invalid comment provided:" ); + return CodeInvalid; + } + + CodePragma + result = (CodePragma) make_code(); + result->Type = Preprocess_Pragma; + result->Content = get_cached_string( directive ); + + return result; +} + +CodePreprocessCond def_preprocess_cond( EPreprocessCond type, StrC expr ) +{ + using namespace ECode; + + if ( expr.Len <= 0 || expr.Ptr == nullptr ) + { + log_failure( "gen::def_comment: Invalid comment provided:" ); + return CodeInvalid; + } + + CodePreprocessCond + result = (CodePreprocessCond) make_code(); + result->Content = get_cached_string( expr ); + + switch (type) + { + case EPreprocessCond::If: + result->Type = Preprocess_If; + break; + case EPreprocessCond::IfDef: + result->Type = Preprocess_IfDef; + break; + case EPreprocessCond::IfNotDef: + result->Type = Preprocess_IfNotDef; + break; + case EPreprocessCond::ElIf: + result->Type = Preprocess_ElIf; + break; + } + + return result; +} + +CodeSpecifiers def_specifier( SpecifierT spec ) +{ + CodeSpecifiers + result = (CodeSpecifiers) make_code(); + result->Type = ECode::Specifiers; + result.append( spec ); + + return result; +} + +CodeStruct def_struct( StrC name + , Code body + , CodeType parent, AccessSpec parent_access + , CodeAttributes attributes + , ModuleFlag mflags + , CodeType* interfaces, s32 num_interfaces ) +{ + using namespace ECode; + + if ( attributes && attributes->Type != PlatformAttributes ) + { + log_failure( "gen::def_struct: attributes was not a `PlatformAttributes` type - %s", attributes.debug_str() ); + return CodeInvalid; + } + + if ( parent && parent->Type != Typename ) + { + log_failure( "gen::def_struct: parent was not a `Struct` type - %s", parent.debug_str() ); + return CodeInvalid; + } + + if ( body && body->Type != Struct_Body ) + { + log_failure( "gen::def_struct: body was not a Struct_Body type - %s", body.debug_str() ); + return CodeInvalid; + } + + CodeStruct + result = (CodeStruct) make_code(); + result->ModuleFlags = mflags; + + if ( name ) + result->Name = get_cached_string( name ); + + if ( body ) + { + result->Type = Struct; + result->Body = body; + } + else + { + result->Type = Struct_Fwd; + } + + if ( attributes ) + result->Attributes = attributes; + + if ( parent ) + { + result->ParentAccess = parent_access; + result->ParentType = parent; + } + + if ( interfaces ) + { + for (s32 idx = 0; idx < num_interfaces; idx++ ) + { + result.add_interface( interfaces[idx] ); + } + } + + return result; +} + +CodeTemplate def_template( CodeParam params, Code declaration, ModuleFlag mflags ) +{ + null_check( def_template, declaration ); + + if ( params && params->Type != ECode::Parameters ) + { + log_failure( "gen::def_template: params is not of parameters type - %s", params.debug_str() ); + return CodeInvalid; + } + + switch (declaration->Type ) + { + case ECode::Class: + case ECode::Function: + case ECode::Struct: + case ECode::Variable: + case ECode::Using: + break; + + default: + log_failure( "gen::def_template: declaration is not of class, function, struct, variable, or using type - %s", declaration.debug_str() ); + } + + CodeTemplate + result = (CodeTemplate) make_code(); + result->Type = ECode::Template; + result->ModuleFlags = mflags; + result->Params = params; + result->Declaration = declaration; + + return result; +} + +CodeType def_type( StrC name, Code arrayexpr, CodeSpecifiers specifiers, CodeAttributes attributes ) +{ + name_check( def_type, name ); + + if ( attributes && attributes->Type != ECode::PlatformAttributes ) + { + log_failure( "gen::def_type: attributes is not of attributes type - %s", attributes.debug_str() ); + return CodeInvalid; + } + + if ( specifiers && specifiers->Type != ECode::Specifiers ) + { + log_failure( "gen::def_type: specifiers is not of specifiers type - %s", specifiers.debug_str() ); + return CodeInvalid; + } + + if ( arrayexpr && arrayexpr->Type != ECode::Untyped ) + { + log_failure( "gen::def_type: arrayexpr is not of untyped type - %s", arrayexpr->debug_str() ); + return CodeInvalid; + } + + CodeType + result = (CodeType) make_code(); + result->Name = get_cached_string( name ); + result->Type = ECode::Typename; + + if ( attributes ) + result->Attributes = attributes; + + if ( specifiers ) + result->Specs = specifiers; + + if ( arrayexpr ) + result->ArrExpr = arrayexpr; + + return result; +} + +CodeTypedef def_typedef( StrC name, Code type, CodeAttributes attributes, ModuleFlag mflags ) +{ + using namespace ECode; + + null_check( def_typedef, type ); + + switch ( type->Type ) + { + case Class: + case Class_Fwd: + case Enum: + case Enum_Fwd: + case Enum_Class: + case Enum_Class_Fwd: + case Function_Fwd: + case Struct: + case Struct_Fwd: + case Union: + case Typename: + break; + default: + log_failure( "gen::def_typedef: type was not a Class, Enum, Function Forward, Struct, Typename, or Union - %s", type.debug_str() ); + return CodeInvalid; + } + + if ( attributes && attributes->Type != ECode::PlatformAttributes ) + { + log_failure( "gen::def_typedef: attributes was not a PlatformAttributes - %s", attributes.debug_str() ); + return CodeInvalid; + } + + // Registering the type. + Code registered_type = def_type( name ); + + if ( ! registered_type ) + { + log_failure( "gen::def_typedef: failed to register type" ); + return CodeInvalid; + } + + CodeTypedef + result = (CodeTypedef) make_code(); + result->Type = ECode::Typedef; + result->ModuleFlags = mflags; + + result->UnderlyingType = type; + + if ( name.Len <= 0 ) + { + if (type->Type != Untyped) + { + log_failure( "gen::def_typedef: name was empty and type was not untyped (indicating its a function typedef) - %s", type.debug_str() ); + return CodeInvalid; + } + + result->Name = get_cached_string( type->Name ); + result->IsFunction = true; + } + else + { + result->Name = get_cached_string( name ); + result->IsFunction = false; + } + + return result; +} + +CodeUnion def_union( StrC name, Code body, CodeAttributes attributes, ModuleFlag mflags ) +{ + null_check( def_union, body ); + + if ( body->Type != ECode::Union_Body ) + { + log_failure( "gen::def_union: body was not a Union_Body type - %s", body.debug_str() ); + return CodeInvalid; + } + + if ( attributes && attributes->Type != ECode::PlatformAttributes ) + { + log_failure( "gen::def_union: attributes was not a PlatformAttributes type - %s", attributes.debug_str() ); + return CodeInvalid; + } + + CodeUnion + result = (CodeUnion) make_code(); + result->ModuleFlags = mflags; + result->Type = ECode::Union; + + if ( name.Ptr ) + result->Name = get_cached_string( name ); + + result->Body = body; + + if ( attributes ) + result->Attributes = attributes; + + return result; +} + +CodeUsing def_using( StrC name, CodeType type + , CodeAttributes attributes + , ModuleFlag mflags ) +{ + name_check( def_using, name ); + null_check( def_using, type ); + + Code register_type = def_type( name ); + + if ( ! register_type ) + { + log_failure( "gen::def_using: failed to register type" ); + return CodeInvalid; + } + + if ( attributes && attributes->Type != ECode::PlatformAttributes ) + { + log_failure( "gen::def_using: attributes was not a PlatformAttributes type - %s", attributes.debug_str() ); + return CodeInvalid; + } + + CodeUsing + result = (CodeUsing) make_code(); + result->Name = get_cached_string( name ); + result->ModuleFlags = mflags; + result->Type = ECode::Using; + + result->UnderlyingType = type; + + if ( attributes ) + result->Attributes = attributes; + + return result; +} + +CodeUsing def_using_namespace( StrC name ) +{ + name_check( def_using_namespace, name ); + + Code + result = make_code(); + result->Name = get_cached_string( name ); + result->Content = result->Name; + result->Type = ECode::Using_Namespace; + + return (CodeUsing) result; +} + +CodeVar def_variable( CodeType type, StrC name, Code value + , CodeSpecifiers specifiers, CodeAttributes attributes + , ModuleFlag mflags ) +{ + name_check( def_variable, name ); + null_check( def_variable, type ); + + if ( attributes && attributes->Type != ECode::PlatformAttributes ) + { + log_failure( "gen::def_variable: attributes was not a `PlatformAttributes` type - %s", attributes.debug_str() ); + return CodeInvalid; + } + + if ( specifiers && specifiers->Type != ECode::Specifiers ) + { + log_failure( "gen::def_variable: specifiers was not a `Specifiers` type - %s", specifiers.debug_str() ); + return CodeInvalid; + } + + if ( type->Type != ECode::Typename ) + { + log_failure( "gen::def_variable: type was not a Typename - %s", type.debug_str() ); + return CodeInvalid; + } + + if ( value && value->Type != ECode::Untyped ) + { + log_failure( "gen::def_variable: value was not a `Untyped` type - %s", value.debug_str() ); + return CodeInvalid; + } + + CodeVar + result = (CodeVar) make_code(); + result->Name = get_cached_string( name ); + result->Type = ECode::Variable; + result->ModuleFlags = mflags; + + result->ValueType = type; + + if ( attributes ) + result->Attributes = attributes; + + if ( specifiers ) + result->Specs = specifiers; + + if ( value ) + result->Value = value; + + return result; +} + +#pragma region Helper Macros for def_**_body functions +#define def_body_start( Name_ ) \ +using namespace ECode; \ + \ +if ( num <= 0 ) \ +{ \ + log_failure("gen::" stringize(Name_) ": num cannot be zero or negative"); \ + return CodeInvalid; \ +} + +#define def_body_code_array_start( Name_ ) \ +using namespace ECode; \ + \ +if ( num <= 0 ) \ +{ \ + log_failure("gen::" stringize(Name_) ": num cannot be zero or negative"); \ + return CodeInvalid; \ +} \ + \ +if ( codes == nullptr ) \ +{ \ + log_failure("gen::" stringize(Name_)" : Provided a null array of codes"); \ + return CodeInvalid; \ +} + +#pragma endregion Helper Macros for def_**_body functions + +CodeBody def_class_body( s32 num, ... ) +{ + def_body_start( def_class_body ); + + CodeBody result = ( CodeBody )make_code(); + result->Type = Class_Body; + + va_list va; + va_start( va, num ); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + + if (!entry) + { + log_failure("gen::" + "def_class_body" + ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + GEN_AST_BODY_CLASS_UNALLOWED_TYPES + log_failure("gen::" "def_class_body" ": Entry type is not allowed: %s", entry.debug_str()); + return CodeInvalid; + + default: + break; + } + + result.append(entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_class_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_class_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Function_Body; + + do + { + Code entry = *codes; + codes++; + + if (!entry) + { + log_failure("gen::" "def_class_body" ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + GEN_AST_BODY_CLASS_UNALLOWED_TYPES + log_failure("gen::" "def_class_body" ": Entry type is not allowed: %s", entry.debug_str()); + return CodeInvalid; + + default: + break; + } + + result.append(entry); + } + while (num--, num > 0); + + return result; +} + +CodeBody def_enum_body( s32 num, ... ) +{ + def_body_start( def_enum_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Enum_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + + if ( ! entry ) + { + log_failure("gen::def_enum_body: Provided a null entry"); + return CodeInvalid; + } + + if ( entry->Type != Untyped && entry->Type != Comment ) + { + log_failure("gen::def_enum_body: Entry type is not allowed - %s. Must be of untyped or comment type.", entry.debug_str() ); + return CodeInvalid; + } + + result.append( entry ); + } + while ( num--, num > 0 ); + va_end(va); + + return (CodeBody) result; +} + +CodeBody def_enum_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_enum_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Enum_Body; + + do + { + Code entry = *codes; + + if ( ! entry ) + { + log_failure("gen::def_enum_body: Provided a null entry"); + return CodeInvalid; + } + + if ( entry->Type != Untyped && entry->Type != Comment ) + { + log_failure("gen::def_enum_body: Entry type is not allowed: %s", entry.debug_str() ); + return CodeInvalid; + } + + result.append( entry ); + } + while ( codes++, num--, num > 0 ); + + return result; +} + +CodeBody def_export_body( s32 num, ... ) +{ + def_body_start( def_export_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Export_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + + if (!entry) + { + log_failure("gen::" "def_export_body" ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + GEN_AST_BODY_EXPORT_UNALLOWED_TYPES + log_failure("gen::" "def_export_body" ": Entry type is not allowed: %s", entry.debug_str()); + return CodeInvalid; + + default: + break; + } + + result.append(entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_export_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_export_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Export_Body; + + do + { + Code entry = *codes; + codes++; + + if (!entry) + { + log_failure("gen::" "def_export_body" ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + GEN_AST_BODY_EXPORT_UNALLOWED_TYPES + log_failure("gen::" "def_export_body" ": Entry type is not allowed: %s", entry.debug_str()); + return CodeInvalid; + + default: + break; + } + + result.append(entry); + } + while (num--, num > 0); + + return result; +} + +CodeBody def_extern_link_body( s32 num, ... ) +{ + def_body_start( def_extern_linkage_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Extern_Linkage_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + + if (!entry) + { + log_failure("gen::" "def_extern_linkage_body" ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + GEN_AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES + log_failure("gen::" "def_extern_linkage_body" ": Entry type is not allowed: %s", entry.debug_str()); + return CodeInvalid; + + default: + break; + } + + result.append(entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_extern_link_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_extern_linkage_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Extern_Linkage_Body; + + do + { + Code entry = *codes; + codes++; + + if (!entry) + { + log_failure("gen::" "def_extern_linkage_body" ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + GEN_AST_BODY_EXTERN_LINKAGE_UNALLOWED_TYPES + log_failure("gen::" "def_extern_linkage_body" ": Entry type is not allowed: %s", entry.debug_str()); + return CodeInvalid; + + default: + break; + } + + result.append(entry); + + } + while (num--, num > 0); + + return result; +} + +CodeBody def_function_body( s32 num, ... ) +{ + def_body_start( def_function_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Function_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + + if (!entry) + { + log_failure("gen::" stringize(def_function_body) ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + + GEN_AST_BODY_FUNCTION_UNALLOWED_TYPES + log_failure("gen::" stringize(def_function_body) ": Entry type is not allowed: %s", entry.debug_str()); + return CodeInvalid; + + default: + break; + } + + result.append(entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_function_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_function_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Function_Body; + + do + { + Code entry = *codes; + codes++; + + if (!entry) + { + log_failure("gen::" "def_function_body" ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + GEN_AST_BODY_FUNCTION_UNALLOWED_TYPES + log_failure("gen::" "def_function_body" ": Entry type is not allowed: %s", entry.debug_str()); + return CodeInvalid; + + default: + break; + } + result.append(entry); + } + while (num--, num > 0); + + return result; +} + +CodeBody def_global_body( s32 num, ... ) +{ + def_body_start( def_global_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Global_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + + if (!entry) + { + log_failure("gen::" "def_global_body" ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + case Global_Body: + result.append( entry.cast() ) ; + continue; + + GEN_AST_BODY_GLOBAL_UNALLOWED_TYPES + log_failure("gen::" "def_global_body" ": Entry type is not allowed: %s", entry.debug_str()); + return (*Code::Invalid.ast); + + default: + break; + } + + result.append(entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_global_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_global_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Global_Body; + + do + { + Code entry = *codes; + codes++; + + if (!entry) + { + log_failure("gen::" "def_global_body" ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + case Global_Body: + result.append( entry.cast() ) ; + continue; + + GEN_AST_BODY_GLOBAL_UNALLOWED_TYPES + log_failure("gen::" "def_global_body" ": Entry type is not allowed: %s", entry.debug_str()); + return CodeInvalid; + + default: + break; + } + + result.append(entry); + } + while (num--, num > 0); + + return result; +} + +CodeBody def_namespace_body( s32 num, ... ) +{ + def_body_start( def_namespace_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Namespace_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + + if (!entry) + { + log_failure("gen::" "def_namespace_body" ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + GEN_AST_BODY_NAMESPACE_UNALLOWED_TYPES + log_failure("gen::" "def_namespace_body" ": Entry type is not allowed: %s", entry.debug_str()); + return CodeInvalid; + + default: + break; + } + + result.append(entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_namespace_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_namespace_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Global_Body; + + do + { + Code entry = *codes; + codes++; + + if (!entry) + { + log_failure("gen::" "def_namespace_body" ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + GEN_AST_BODY_NAMESPACE_UNALLOWED_TYPES + log_failure("gen::" "def_namespace_body" ": Entry type is not allowed: %s", entry.debug_str() ); + return CodeInvalid; + + default: break; + } + + result.append(entry); + } + while (num--, num > 0); + + return result; +} + +CodeParam def_params( s32 num, ... ) +{ + def_body_start( def_params ); + + va_list va; + va_start(va, num); + + Code_POD pod = va_arg(va, Code_POD); + CodeParam param = pcast( CodeParam, pod ); + + null_check( def_params, param ); + + if ( param->Type != Parameters ) + { + log_failure( "gen::def_params: param %d is not a Parameters", num - num + 1 ); + return CodeInvalid; + } + + CodeParam result = (CodeParam) param.duplicate(); + + while ( -- num ) + { + pod = va_arg(va, Code_POD); + param = pcast( CodeParam, pod ); + + if ( param->Type != Parameters ) + { + log_failure( "gen::def_params: param %d is not a Parameters", num - num + 1 ); + return CodeInvalid; + } + + result.append( param ); + } + va_end(va); + + return result; +} + +CodeParam def_params( s32 num, CodeParam* codes ) +{ + def_body_code_array_start( def_params ); + +# define check_current() \ + if ( current.ast == nullptr ) \ + { \ + log_failure("gen::def_params: Provide a null code in codes array"); \ + return CodeInvalid; \ + } \ + \ + if (current->Type != Parameters ) \ + { \ + log_failure("gen::def_params: Code in coes array is not of paramter type - %s", current.debug_str() ); \ + return CodeInvalid; \ + } + + CodeParam current = (CodeParam) codes->duplicate(); + check_current(); + + CodeParam + result = (CodeParam) make_code(); + result->Name = current->Name; + result->Type = current->Type; + result->ValueType = current->ValueType; + + while( codes++, current = * codes, num--, num > 0 ) + { + check_current(); + result.append( current ); + } +# undef check_current + + return result; +} + +CodeSpecifiers def_specifiers( s32 num, ... ) +{ + if ( num <= 0 ) + { + log_failure("gen::def_specifiers: num cannot be zero or less"); + return CodeInvalid; + } + + if ( num > AST::ArrSpecs_Cap ) + { + log_failure("gen::def_specifiers: num of speciifers to define AST larger than AST specicifier capacity - %d", num); + return CodeInvalid; + } + + CodeSpecifiers + result = (CodeSpecifiers) make_code(); + result->Type = ECode::Specifiers; + + va_list va; + va_start(va, num); + do + { + SpecifierT type = (SpecifierT)va_arg(va, int); + + result.append( type ); + } + while ( --num, num ); + va_end(va); + + return result; +} + +CodeSpecifiers def_specifiers( s32 num, SpecifierT* specs ) +{ + if ( num <= 0 ) + { + log_failure("gen::def_specifiers: num cannot be zero or less"); + return CodeInvalid; + } + + if ( num > AST::ArrSpecs_Cap ) + { + log_failure("gen::def_specifiers: num of speciifers to define AST larger than AST specicifier capacity - %d", num); + return CodeInvalid; + } + + CodeSpecifiers + result = (CodeSpecifiers) make_code(); + result->Type = ECode::Specifiers; + + s32 idx = 0; + do + { + result.append( specs[idx] ); + idx++; + } + while ( --num, num ); + + return result; +} + +CodeBody def_struct_body( s32 num, ... ) +{ + def_body_start( def_struct_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Struct_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast(Code, pod); + + if (!entry) + { + log_failure("gen::" "def_struct_body" ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + GEN_AST_BODY_STRUCT_UNALLOWED_TYPES + log_failure("gen::" "def_struct_body" ": Entry type is not allowed: %s", entry.debug_str()); + return CodeInvalid; + + default: + break; + } + + result.append(entry); + } + while (num--, num > 0); + va_end(va); + + return result; +} + +CodeBody def_struct_body( s32 num, Code* codes ) +{ + def_body_code_array_start( def_struct_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Struct_Body; + + do + { + Code entry = *codes; + codes++; + + if (!entry) + { + log_failure("gen::" "def_struct_body" ": Provided an null entry"); + return CodeInvalid; + } + + switch (entry->Type) + { + GEN_AST_BODY_STRUCT_UNALLOWED_TYPES + log_failure("gen::" "def_struct_body" ": Entry type is not allowed: %s", entry.debug_str() ); + return CodeInvalid; + + default: + break; + } + + result.append(entry); + } + while (num--, num > 0); + + return result; +} + +CodeBody def_union_body( s32 num, ... ) +{ + def_body_start( def_union_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Union_Body; + + va_list va; + va_start(va, num); + do + { + Code_POD pod = va_arg(va, Code_POD); + Code entry = pcast( Code, pod ); + + if ( ! entry ) + { + log_failure("gen::def_union_body: Provided a null entry"); + return CodeInvalid; + } + + if ( entry->Type != Untyped && entry->Type != Comment ) + { + log_failure("gen::def_union_body: Entry type is not allowed - %s. Must be of untyped or comment type.", entry.debug_str() ); + return CodeInvalid; + } + + result.append( entry ); + } + while ( num--, num > 0 ); + va_end(va); + + return result; +} + +CodeBody def_union_body( s32 num, CodeUnion* codes ) +{ + def_body_code_array_start( def_union_body ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Union_Body; + + do + { + Code entry = *codes; + + if ( ! entry ) + { + log_failure("gen::def_union_body: Provided a null entry"); + return CodeInvalid; + } + + if ( entry->Type != Untyped && entry->Type != Comment ) + { + log_failure("gen::def_union_body: Entry type is not allowed: %s", entry.debug_str() ); + return CodeInvalid; + } + + result.append( entry ); + } + while ( codes++, num--, num > 0 ); + + return (CodeBody) result; +} + +# undef name_check +# undef null_check +# undef null_or_invalid_check +# undef def_body_start +# undef def_body_code_array_start + +#pragma endregion Upfront + +#pragma region Parsing + +namespace parser +{ + namespace ETokType + { +#define GEN_DEFINE_ATTRIBUTE_TOKENS Entry( Attribute_API_Export, "GEN_API_Export_Code" ) Entry( Attribute_API_Import, "GEN_API_Import_Code" ) + + enum Type : u32 + { + Invalid, + Access_Private, + Access_Protected, + Access_Public, + Access_MemberSymbol, + Access_StaticSymbol, + Ampersand, + Ampersand_DBL, + Assign_Classifer, + Attribute_Open, + Attribute_Close, + BraceCurly_Open, + BraceCurly_Close, + BraceSquare_Open, + BraceSquare_Close, + Capture_Start, + Capture_End, + Comment, + Comment_End, + Comment_Start, + Char, + Comma, + Decl_Class, + Decl_GNU_Attribute, + Decl_MSVC_Attribute, + Decl_Enum, + Decl_Extern_Linkage, + Decl_Friend, + Decl_Module, + Decl_Namespace, + Decl_Operator, + Decl_Struct, + Decl_Template, + Decl_Typedef, + Decl_Using, + Decl_Union, + Identifier, + Module_Import, + Module_Export, + NewLine, + Number, + Operator, + Preprocess_Hash, + Preprocess_Define, + Preprocess_If, + Preprocess_IfDef, + Preprocess_IfNotDef, + Preprocess_ElIf, + Preprocess_Else, + Preprocess_EndIf, + Preprocess_Include, + Preprocess_Pragma, + Preprocess_Content, + Preprocess_Macro, + Preprocess_Unsupported, + Spec_Alignas, + Spec_Const, + Spec_Consteval, + Spec_Constexpr, + Spec_Constinit, + Spec_Explicit, + Spec_Extern, + Spec_Final, + Spec_ForceInline, + Spec_Global, + Spec_Inline, + Spec_Internal_Linkage, + Spec_LocalPersist, + Spec_Mutable, + Spec_NeverInline, + Spec_Override, + Spec_Static, + Spec_ThreadLocal, + Spec_Volatile, + Spec_Virtual, + Star, + Statement_End, + StaticAssert, + String, + Type_Typename, + Type_Unsigned, + Type_Signed, + Type_Short, + Type_Long, + Type_bool, + Type_char, + Type_int, + Type_double, + Type_MS_int8, + Type_MS_int16, + Type_MS_int32, + Type_MS_int64, + Type_MS_W64, + Varadic_Argument, + __Attributes_Start, + Attribute_API_Export, + Attribute_API_Import, + NumTokens + }; + + inline StrC to_str( Type type ) + { + local_persist StrC lookup[] { + { sizeof( "__invalid__" ), "__invalid__" }, + { sizeof( "private" ), "private" }, + { sizeof( "protected" ), "protected" }, + { sizeof( "public" ), "public" }, + { sizeof( "." ), "." }, + { sizeof( "::" ), "::" }, + { sizeof( "&" ), "&" }, + { sizeof( "&&" ), "&&" }, + { sizeof( ":" ), ":" }, + { sizeof( "[[" ), "[[" }, + { sizeof( "]]" ), "]]" }, + { sizeof( "{" ), "{" }, + { sizeof( "}" ), "}" }, + { sizeof( "[" ), "[" }, + { sizeof( "]" ), "]" }, + { sizeof( "(" ), "(" }, + { sizeof( ")" ), ")" }, + { sizeof( "__comment__" ), "__comment__" }, + { sizeof( "__comment_end__" ), "__comment_end__" }, + { sizeof( "__comment_start__" ), "__comment_start__" }, + { sizeof( "__character__" ), "__character__" }, + { sizeof( "," ), "," }, + { sizeof( "class" ), "class" }, + { sizeof( "__attribute__" ), "__attribute__" }, + { sizeof( "__declspec" ), "__declspec" }, + { sizeof( "enum" ), "enum" }, + { sizeof( "extern" ), "extern" }, + { sizeof( "friend" ), "friend" }, + { sizeof( "module" ), "module" }, + { sizeof( "namespace" ), "namespace" }, + { sizeof( "operator" ), "operator" }, + { sizeof( "struct" ), "struct" }, + { sizeof( "template" ), "template" }, + { sizeof( "typedef" ), "typedef" }, + { sizeof( "using" ), "using" }, + { sizeof( "union" ), "union" }, + { sizeof( "__identifier__" ), "__identifier__" }, + { sizeof( "import" ), "import" }, + { sizeof( "export" ), "export" }, + { sizeof( "__new_line__" ), "__new_line__" }, + { sizeof( "__number__" ), "__number__" }, + { sizeof( "__operator__" ), "__operator__" }, + { sizeof( "#" ), "#" }, + { sizeof( "define" ), "define" }, + { sizeof( "if" ), "if" }, + { sizeof( "ifdef" ), "ifdef" }, + { sizeof( "ifndef" ), "ifndef" }, + { sizeof( "elif" ), "elif" }, + { sizeof( "else" ), "else" }, + { sizeof( "endif" ), "endif" }, + { sizeof( "include" ), "include" }, + { sizeof( "pragma" ), "pragma" }, + { sizeof( "__macro_content__" ), "__macro_content__" }, + { sizeof( "__macro__" ), "__macro__" }, + { sizeof( "__unsupported__" ), "__unsupported__" }, + { sizeof( "alignas" ), "alignas" }, + { sizeof( "const" ), "const" }, + { sizeof( "consteval" ), "consteval" }, + { sizeof( "constexpr" ), "constexpr" }, + { sizeof( "constinit" ), "constinit" }, + { sizeof( "explicit" ), "explicit" }, + { sizeof( "extern" ), "extern" }, + { sizeof( "final" ), "final" }, + { sizeof( "forceinline" ), "forceinline" }, + { sizeof( "global" ), "global" }, + { sizeof( "inline" ), "inline" }, + { sizeof( "internal" ), "internal" }, + { sizeof( "local_persist" ), "local_persist" }, + { sizeof( "mutable" ), "mutable" }, + { sizeof( "neverinline" ), "neverinline" }, + { sizeof( "override" ), "override" }, + { sizeof( "static" ), "static" }, + { sizeof( "thread_local" ), "thread_local" }, + { sizeof( "volatile" ), "volatile" }, + { sizeof( "virtual" ), "virtual" }, + { sizeof( "*" ), "*" }, + { sizeof( ";" ), ";" }, + { sizeof( "static_assert" ), "static_assert" }, + { sizeof( "__string__" ), "__string__" }, + { sizeof( "typename" ), "typename" }, + { sizeof( "unsigned" ), "unsigned" }, + { sizeof( "signed" ), "signed" }, + { sizeof( "short" ), "short" }, + { sizeof( "long" ), "long" }, + { sizeof( "bool" ), "bool" }, + { sizeof( "char" ), "char" }, + { sizeof( "int" ), "int" }, + { sizeof( "double" ), "double" }, + { sizeof( "__int8" ), "__int8" }, + { sizeof( "__int16" ), "__int16" }, + { sizeof( "__int32" ), "__int32" }, + { sizeof( "__int64" ), "__int64" }, + { sizeof( "_W64" ), "_W64" }, + { sizeof( "..." ), "..." }, + { sizeof( "__attrib_start__" ), "__attrib_start__" }, + { sizeof( "GEN_API_Export_Code" ), "GEN_API_Export_Code" }, + { sizeof( "GEN_API_Import_Code" ), "GEN_API_Import_Code" }, + }; + return lookup[type]; + } + + inline Type to_type( StrC str ) + { + local_persist u32 keymap[NumTokens]; + do_once_start for ( u32 index = 0; index < NumTokens; index++ ) + { + StrC enum_str = to_str( (Type)index ); + keymap[index] = crc32( enum_str.Ptr, enum_str.Len - 1 ); + } + do_once_end u32 hash = crc32( str.Ptr, str.Len ); + for ( u32 index = 0; index < NumTokens; index++ ) + { + if ( keymap[index] == hash ) + return (Type)index; + } + return Invalid; + } + + } // namespace ETokType + + using TokType = ETokType::Type; + +} // namespace parser + +namespace parser { + +enum TokFlags : u32 +{ + TF_Operator = bit(0), + TF_Assign = bit(1), + TF_Preprocess = bit(2), + TF_Preprocess_Cond = bit(3), + TF_Attribute = bit(6), + TF_AccessOperator = bit( 7 ), + TF_AccessSpecifier = bit( 8 ), + TF_Specifier = bit( 9 ), + TF_EndDefinition = bit( 10 ), // Either ; or } + TF_Formatting = bit( 11 ), + TF_Literal = bit( 12 ), + + TF_Null = 0, +}; + +struct Token +{ + char const* Text; + sptr Length; + TokType Type; + s32 Line; + s32 Column; + u32 Flags; + + operator bool() + { + return Text && Length && Type != TokType::Invalid; + } + + operator StrC() + { + return { Length, Text }; + } + + bool is_access_operator() + { + return bitfield_is_equal( u32, Flags, TF_AccessOperator ); + } + + bool is_access_specifier() + { + return bitfield_is_equal( u32, Flags, TF_AccessSpecifier ); + } + + bool is_attribute() + { + return bitfield_is_equal( u32, Flags, TF_Attribute ); + } + + bool is_operator() + { + return bitfield_is_equal( u32, Flags, TF_Operator ); + } + + bool is_preprocessor() + { + return bitfield_is_equal( u32, Flags, TF_Preprocess ); + } + + bool is_preprocess_cond() + { + return bitfield_is_equal( u32, Flags, TF_Preprocess_Cond ); + } + + bool is_specifier() + { + return bitfield_is_equal( u32, Flags, TF_Specifier ); + } + + bool is_end_definition() + { + return bitfield_is_equal( u32, Flags, TF_EndDefinition ); + } + + AccessSpec to_access_specifier() + { + return scast(AccessSpec, Type); + } + + String to_string() + { + String result = String::make_reserve( GlobalAllocator, kilobytes(4) ); + + StrC type_str = ETokType::to_str( Type ); + + result.append_fmt( "Line: %d Column: %d, Type: %.*s Content: %.*s" + , Line, Column + , type_str.Len, type_str.Ptr + , Length, Text + ); + + return result; + } +}; + +constexpr Token NullToken { nullptr, 0, TokType::Invalid, false, 0, TF_Null }; + +struct TokArray +{ + Array Arr; + s32 Idx; + + bool __eat( TokType type ); + + Token& current( bool skip_formatting = true ) + { + if ( skip_formatting ) + { + while ( Arr[Idx].Type == TokType::NewLine || Arr[Idx].Type == TokType::Comment ) + Idx++; + } + + return Arr[Idx]; + } + + Token& previous( bool skip_formatting = false ) + { + s32 idx = this->Idx; + + if ( skip_formatting ) + { + while ( Arr[idx].Type == TokType::NewLine ) + idx--; + + return Arr[idx]; + } + + return Arr[idx - 1]; + } + + Token& next( bool skip_formatting = false ) + { + s32 idx = this->Idx; + + if ( skip_formatting ) + { + while ( Arr[idx].Type == TokType::NewLine ) + idx++; + + return Arr[idx + 1]; + } + + return Arr[idx + 1]; + } + + Token& operator []( s32 idx ) + { + return Arr[idx]; + } +}; + +global Arena_256KB defines_map_arena; +global HashTable defines; +global Array Tokens; + +#define current ( * scanner ) + +#define move_forward() \ + { \ + if ( current == '\n' ) \ + { \ + line++; \ + column = 1; \ + } \ + else \ + { \ + column++; \ + } \ + left--; \ + scanner++; \ + } + +#define SkipWhitespace() \ + while ( left && char_is_space( current ) ) \ + { \ + move_forward(); \ + } + +#define end_line() \ + do \ + { \ + while ( left && current == ' ' ) \ + { \ + move_forward(); \ + } \ + if ( left && current == '\r' ) \ + { \ + move_forward(); \ + move_forward(); \ + } \ + else if ( left && current == '\n' ) \ + { \ + move_forward(); \ + } \ + } \ + while (0) + +enum +{ + Lex_Continue, + Lex_ReturnNull, +}; + +forceinline +s32 lex_preprocessor_directive( + StrC& content + , s32& left + , char const*& scanner + , s32& line + , s32& column + , HashTable& defines + , Token& token ) +{ + char const* hash = scanner; + Tokens.append( { hash, 1, TokType::Preprocess_Hash, line, column, TF_Preprocess } ); + + move_forward(); + SkipWhitespace(); + + token.Text = scanner; + while (left && ! char_is_space(current) ) + { + move_forward(); + token.Length++; + } + + token.Type = ETokType::to_type( token ); + + bool is_preprocessor = token.Type >= TokType::Preprocess_Define && token.Type <= TokType::Preprocess_Pragma; + if ( ! is_preprocessor ) + { + token.Type = TokType::Preprocess_Unsupported; + + // Its an unsupported directive, skip it + s32 within_string = false; + s32 within_char = false; + while ( left ) + { + if ( current == '"' && ! within_char ) + within_string ^= true; + + if ( current == '\'' && ! within_string ) + within_char ^= true; + + if ( current == '\\' && ! within_string && ! within_char ) + { + move_forward(); + token.Length++; + + if ( current == '\r' ) + { + move_forward(); + token.Length++; + } + + if ( current == '\n' ) + { + move_forward(); + token.Length++; + continue; + } + else + { + log_failure( "gen::Parser::lex: Invalid escape sequence '\\%c' (%d, %d)" + " in preprocessor directive (%d, %d)\n%.100s" + , current, line, column + , token.Line, token.Column, token.Text ); + break; + } + } + + if ( current == '\r' ) + { + move_forward(); + token.Length++; + } + + if ( current == '\n' ) + { + move_forward(); + token.Length++; + break; + } + + move_forward(); + token.Length++; + } + + token.Length = token.Length + token.Text - hash; + token.Text = hash; + Tokens.append( token ); + return Lex_Continue; // Skip found token, its all handled here. + } + + if ( token.Type == TokType::Preprocess_Else || token.Type == TokType::Preprocess_EndIf ) + { + token.Flags |= TF_Preprocess_Cond; + Tokens.append( token ); + end_line(); + return Lex_Continue; + } + else if ( token.Type >= TokType::Preprocess_If && token.Type <= TokType::Preprocess_ElIf ) + { + token.Flags |= TF_Preprocess_Cond; + } + + Tokens.append( token ); + + SkipWhitespace(); + + if ( token.Type == TokType::Preprocess_Define ) + { + Token name = { scanner, 0, TokType::Identifier, line, column, TF_Preprocess }; + + name.Text = scanner; + name.Length = 1; + move_forward(); + + while ( left && ( char_is_alphanumeric(current) || current == '_' ) ) + { + move_forward(); + name.Length++; + } + + if ( left && current == '(' ) + { + move_forward(); + name.Length++; + } + + Tokens.append( name ); + + u64 key = crc32( name.Text, name.Length ); + defines.set( key, name ); + } + + Token preprocess_content = { scanner, 0, TokType::Preprocess_Content, line, column, TF_Preprocess }; + + if ( token.Type == TokType::Preprocess_Include ) + { + preprocess_content.Type = TokType::String; + + if ( current != '"' && current != '<' ) + { + String directive_str = String::fmt_buf( GlobalAllocator, "%.*s", min( 80, left + preprocess_content.Length ), token.Text ); + + log_failure( "gen::Parser::lex: Expected '\"' or '<' after #include, not '%c' (%d, %d)\n%s" + , current + , preprocess_content.Line + , preprocess_content.Column + , directive_str.Data + ); + return Lex_ReturnNull; + } + move_forward(); + preprocess_content.Length++; + + while ( left && current != '"' && current != '>' ) + { + move_forward(); + preprocess_content.Length++; + } + + move_forward(); + preprocess_content.Length++; + + if ( current == '\r' && scanner[1] == '\n' ) + { + move_forward(); + move_forward(); + } + else if ( current == '\n' ) + { + move_forward(); + } + + Tokens.append( preprocess_content ); + return Lex_Continue; // Skip found token, its all handled here. + } + + s32 within_string = false; + s32 within_char = false; + + // SkipWhitespace(); + while ( left ) + { + if ( current == '"' && ! within_char ) + within_string ^= true; + + if ( current == '\'' && ! within_string ) + within_char ^= true; + + if ( current == '\\' && ! within_string && ! within_char ) + { + move_forward(); + preprocess_content.Length++; + + if ( current == '\r' ) + { + move_forward(); + preprocess_content.Length++; + } + + if ( current == '\n' ) + { + move_forward(); + preprocess_content.Length++; + continue; + } + else + { + String directive_str = String::make_length( GlobalAllocator, token.Text, token.Length ); + String content_str = String::fmt_buf( GlobalAllocator, "%.*s", min( 400, left + preprocess_content.Length ), preprocess_content.Text ); + + log_failure( "gen::Parser::lex: Invalid escape sequence '\\%c' (%d, %d)" + " in preprocessor directive '%s' (%d, %d)\n%s" + , current, line, column + , directive_str, preprocess_content.Line, preprocess_content.Column + , content_str ); + break; + } + } + + if ( current == '\r' ) + { + move_forward(); + } + + if ( current == '\n' ) + { + move_forward(); + break; + } + + move_forward(); + preprocess_content.Length++; + } + + Tokens.append( preprocess_content ); + return Lex_Continue; // Skip found token, its all handled here. +} + +forceinline +void lex_found_token( StrC& content + , s32& left + , char const*& scanner + , s32& line + , s32& column + , HashTable& defines + , Token& token ) +{ + if ( token.Type != TokType::Invalid ) + { + Tokens.append( token ); + return; + } + + TokType type = ETokType::to_type( token ); + + if (type <= TokType::Access_Public && type >= TokType::Access_Private ) + { + token.Flags |= TF_AccessSpecifier; + } + + if ( type > TokType::__Attributes_Start ) + { + token.Flags |= TF_Attribute; + } + + if ( type == ETokType::Decl_Extern_Linkage ) + { + SkipWhitespace(); + + if ( current != '"' ) + { + type = ETokType::Spec_Extern; + token.Flags |= TF_Specifier; + } + + token.Type = type; + Tokens.append( token ); + return; + } + + if ( ( type <= TokType::Star && type >= TokType::Spec_Alignas) + || type == TokType::Ampersand + || type == TokType::Ampersand_DBL ) + { + token.Type = type; + token.Flags |= TF_Specifier; + Tokens.append( token ); + return; + } + + + if ( type != TokType::Invalid ) + { + token.Type = type; + Tokens.append( token ); + return; + } + + u64 key = 0; + if ( current == '(') + key = crc32( token.Text, token.Length + 1 ); + else + key = crc32( token.Text, token.Length ); + + StrC* define = defines.get( key ); + if ( define ) + { + token.Type = TokType::Preprocess_Macro; + + // Want to ignore any arguments the define may have as they can be execution expressions. + if ( left && current == '(' ) + { + move_forward(); + token.Length++; + + s32 level = 0; + while ( left && (current != ')' || level > 0) ) + { + if ( current == '(' ) + level++; + + else if ( current == ')' && level > 0 ) + level--; + + move_forward(); + token.Length++; + } + + move_forward(); + token.Length++; + } + + if ( current == '\r' && scanner[1] == '\n' ) + { + move_forward(); + } + else if ( current == '\n' ) + { + move_forward(); + } + } + else + { + token.Type = TokType::Identifier; + } + + Tokens.append( token ); +} + + +neverinline +// TokArray lex( Array tokens, StrC content ) +TokArray lex( StrC content ) +{ + s32 left = content.Len; + char const* scanner = content.Ptr; + + char const* word = scanner; + s32 word_length = 0; + + s32 line = 1; + s32 column = 1; + + SkipWhitespace(); + if ( left <= 0 ) + { + log_failure( "gen::lex: no tokens found (only whitespace provided)" ); + return { { nullptr }, 0 }; + } + + for ( StringCached entry : PreprocessorDefines ) + { + s32 length = 0; + char const* scanner = entry.Data; + while ( entry.length() > length && (char_is_alphanumeric( *scanner ) || *scanner == '_') ) + { + scanner++; + length ++; + } + if ( scanner[0] == '(' ) + { + length++; + } + + u64 key = crc32( entry.Data, length ); + defines.set( key, entry ); + } + + Tokens.clear(); + + while (left ) + { + #if 0 + if (Tokens.num()) + { + log_fmt("\nLastTok: %S", Tokens.back().to_string()); + } + #endif + + Token token = { scanner, 0, TokType::Invalid, line, column, TF_Null }; + + bool is_define = false; + + if ( column == 1 ) + { + if ( current == '\r') + { + move_forward(); + token.Length = 1; + } + + if ( current == '\n' ) + { + move_forward(); + + token.Type = TokType::NewLine; + token.Length++; + + Tokens.append( token ); + continue; + } + } + + token.Length = 0; + + SkipWhitespace(); + if ( left <= 0 ) + break; + + switch ( current ) + { + case '#': + { + s32 result = lex_preprocessor_directive( content, left, scanner, line, column, defines, token ); + switch ( result ) + { + case Lex_Continue: + continue; + + case Lex_ReturnNull: + return { { nullptr }, 0 }; + } + } + case '.': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Access_MemberSymbol; + token.Flags = TF_AccessOperator; + + if (left) { + move_forward(); + } + + if ( current == '.' ) + { + move_forward(); + if( current == '.' ) + { + token.Length = 3; + token.Type = TokType::Varadic_Argument; + token.Flags = TF_Null; + move_forward(); + } + else + { + String context_str = String::fmt_buf( GlobalAllocator, "%s", scanner, min( 100, left ) ); + + log_failure( "gen::lex: invalid varadic argument, expected '...' got '..%c' (%d, %d)\n%s", current, line, column, context_str ); + } + } + + goto FoundToken; + } + case '&' : + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Ampersand; + token.Flags |= TF_Operator; + token.Flags |= TF_Specifier; + + if (left) + move_forward(); + + if ( current == '&' ) // && + { + token.Length = 2; + token.Type = TokType::Ampersand_DBL; + + if (left) + move_forward(); + } + + goto FoundToken; + } + case ':': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Assign_Classifer; + // Can be either a classifier (ParentType, Bitfield width), or ternary else + // token.Type = TokType::Colon; + + if (left) + move_forward(); + + if ( current == ':' ) + { + move_forward(); + token.Type = TokType::Access_StaticSymbol; + token.Length++; + } + goto FoundToken; + } + case '{': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::BraceCurly_Open; + + if (left) + move_forward(); + goto FoundToken; + } + case '}': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::BraceCurly_Close; + token.Flags = TF_EndDefinition; + + if (left) + move_forward(); + + end_line(); + goto FoundToken; + } + case '[': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::BraceSquare_Open; + if ( left ) + { + move_forward(); + + if ( current == ']' ) + { + token.Length = 2; + token.Type = TokType::Operator; + move_forward(); + } + } + goto FoundToken; + } + case ']': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::BraceSquare_Close; + + if (left) + move_forward(); + goto FoundToken; + } + case '(': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Capture_Start; + + if (left) + move_forward(); + goto FoundToken; + } + case ')': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Capture_End; + + if (left) + move_forward(); + goto FoundToken; + } + case '\'': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Char; + token.Flags = TF_Literal; + + move_forward(); + + if ( left && current == '\\' ) + { + move_forward(); + token.Length++; + + if ( current == '\'' ) + { + move_forward(); + token.Length++; + } + } + + while ( left && current != '\'' ) + { + move_forward(); + token.Length++; + } + + if ( left ) + { + move_forward(); + token.Length++; + } + goto FoundToken; + } + case ',': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Comma; + token.Flags = TF_Operator; + + if (left) + move_forward(); + goto FoundToken; + } + case '*': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Star; + token.Flags |= TF_Specifier; + token.Flags |= TF_Operator; + + if (left) + move_forward(); + + if ( current == '=' ) + { + token.Length++; + token.Flags |= TF_Assign; + // token.Type = TokType::Assign_Multiply; + + if ( left ) + move_forward(); + } + + goto FoundToken; + } + case ';': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Statement_End; + token.Flags = TF_EndDefinition; + + if (left) + move_forward(); + + end_line(); + goto FoundToken; + } + case '"': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::String; + token.Flags |= TF_Literal; + + move_forward(); + while ( left ) + { + if ( current == '"' ) + { + move_forward(); + break; + } + + if ( current == '\\' ) + { + move_forward(); + token.Length++; + + if ( left ) + { + move_forward(); + token.Length++; + } + continue; + } + + move_forward(); + token.Length++; + } + goto FoundToken; + } + case '?': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Operator; + // token.Type = TokType::Ternary; + token.Flags = TF_Operator; + + if (left) + move_forward(); + + goto FoundToken; + } + case '=': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Operator; + // token.Type = TokType::Assign; + token.Flags = TF_Operator; + token.Flags |= TF_Assign; + + if (left) + move_forward(); + + if ( current == '=' ) + { + token.Length++; + token.Flags = TF_Operator; + + if (left) + move_forward(); + } + + goto FoundToken; + } + case '+': + { + // token.Type = TokType::Add + + } + case '%': + { + // token.Type = TokType::Modulo; + + } + case '^': + { + // token.Type = TokType::B_XOr; + } + case '~': + { + // token.Type = TokType::Unary_Not; + + } + case '!': + { + // token.Type = TokType::L_Not; + } + case '<': + { + // token.Type = TokType::Lesser; + + } + case '>': + { + // token.Type = TokType::Greater; + + } + case '|': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Operator; + token.Flags = TF_Operator; + // token.Type = TokType::L_Or; + + if (left) + move_forward(); + + if ( current == '=' ) + { + token.Length++; + token.Flags |= TF_Assign; + // token.Flags |= TokFlags::Assignment; + // token.Type = TokType::Assign_L_Or; + + if (left) + move_forward(); + } + else while ( left && current == *(scanner - 1) && token.Length < 3 ) + { + token.Length++; + + if (left) + move_forward(); + } + goto FoundToken; + } + + // Dash is unfortunatlly a bit more complicated... + case '-': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Operator; + // token.Type = TokType::Subtract; + token.Flags = TF_Operator; + if ( left ) + { + move_forward(); + + if ( current == '>' ) + { + token.Length++; +// token.Type = TokType::Access_PointerToMemberSymbol; + token.Flags |= TF_AccessOperator; + move_forward(); + + if ( current == '*' ) + { +// token.Type = TokType::Access_PointerToMemberOfPointerSymbol; + token.Length++; + move_forward(); + } + } + else if ( current == '=' ) + { + token.Length++; + // token.Type = TokType::Assign_Subtract; + token.Flags |= TF_Assign; + + if (left) + move_forward(); + } + else while ( left && current == *(scanner - 1) && token.Length < 3 ) + { + token.Length++; + + if (left) + move_forward(); + } + } + goto FoundToken; + } + case '/': + { + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Operator; + // token.Type = TokType::Divide; + token.Flags = TF_Operator; + move_forward(); + + if ( left ) + { + if ( current == '=' ) + { + // token.Type = TokeType::Assign_Divide; + move_forward(); + token.Length++; + token.Flags = TF_Assign; + } + else if ( current == '/' ) + { + token.Type = TokType::Comment; + token.Length = 2; + token.Flags = TF_Null; + move_forward(); + + while ( left && current != '\n' && current != '\r' ) + { + move_forward(); + token.Length++; + } + + if ( current == '\r' ) + { + move_forward(); + token.Length++; + } + if ( current == '\n' ) + { + move_forward(); + token.Length++; + } + Tokens.append( token ); + continue; + } + else if ( current == '*' ) + { + token.Type = TokType::Comment; + token.Length = 2; + token.Flags = TF_Null; + move_forward(); + + bool star = current == '*'; + bool slash = scanner[1] == '/'; + bool at_end = star && slash; + while ( left && ! at_end ) + { + move_forward(); + token.Length++; + + star = current == '*'; + slash = scanner[1] == '/'; + at_end = star && slash; + } + token.Length += 2; + move_forward(); + move_forward(); + + if ( current == '\r' ) + { + move_forward(); + token.Length++; + } + if ( current == '\n' ) + { + move_forward(); + token.Length++; + } + Tokens.append( token ); + // end_line(); + continue; + } + } + goto FoundToken; + } + } + + if ( char_is_alpha( current ) || current == '_' ) + { + token.Text = scanner; + token.Length = 1; + move_forward(); + + while ( left && ( char_is_alphanumeric(current) || current == '_' ) ) + { + move_forward(); + token.Length++; + } + + goto FoundToken; + } + else if ( char_is_digit(current) ) + { + // This is a very brute force lex, no checks are done for validity of literal. + + token.Text = scanner; + token.Length = 1; + token.Type = TokType::Number; + token.Flags = TF_Literal; + move_forward(); + + if (left + && ( current == 'x' || current == 'X' + || current == 'b' || current == 'B' + || current == 'o' || current == 'O' ) + ) + { + move_forward(); + token.Length++; + + while ( left && char_is_hex_digit(current) ) + { + move_forward(); + token.Length++; + } + + goto FoundToken; + } + + while ( left && char_is_digit(current) ) + { + move_forward(); + token.Length++; + } + + if ( left && current == '.' ) + { + move_forward(); + token.Length++; + + while ( left && char_is_digit(current) ) + { + move_forward(); + token.Length++; + } + + // Handle number literal suffixes in a botched way + if (left && ( + current == 'l' || current == 'L' || // long/long long + current == 'u' || current == 'U' || // unsigned + current == 'f' || current == 'F' || // float + current == 'i' || current == 'I' || // imaginary + current == 'z' || current == 'Z')) // complex + { + char prev = current; + move_forward(); + token.Length++; + + // Handle 'll'/'LL' as a special case when we just processed an 'l'/'L' + if (left && (prev == 'l' || prev == 'L') && (current == 'l' || current == 'L')) + { + move_forward(); + token.Length++; + } + } + } + + goto FoundToken; + } + else + { + s32 start = max( 0, Tokens.num() - 100 ); + log_fmt("\n%d\n", start); + for ( s32 idx = start; idx < Tokens.num(); idx++ ) + { + log_fmt( "Token %d Type: %s : %.*s\n" + , idx + , ETokType::to_str( Tokens[ idx ].Type ).Ptr + , Tokens[ idx ].Length, Tokens[ idx ].Text + ); + } + + String context_str = String::fmt_buf( GlobalAllocator, "%.*s", min( 100, left ), scanner ); + log_failure( "Failed to lex token '%c' (%d, %d)\n%s", current, line, column, context_str ); + + // Skip to next whitespace since we can't know if anything else is valid until then. + while ( left && ! char_is_space( current ) ) + { + move_forward(); + } + } + + FoundToken: + lex_found_token( content, left, scanner, line, column, defines, token ); + } + + if ( Tokens.num() == 0 ) + { + log_failure( "Failed to lex any tokens" ); + return { { nullptr }, 0 }; + } + + defines.clear(); + // defines_map_arena.free(); + return { Tokens, 0 }; +} +#undef current +#undef move_forward +#undef SkipWhitespace + +// namespace parser +} + +namespace parser { + +// TODO(Ed) : Rename ETokType::Capture_Start, ETokType::Capture_End to Open_Parenthesis adn Close_Parenthesis + +constexpr bool dont_skip_formatting = false; + +struct StackNode +{ + StackNode* Prev; + + Token Start; + Token Name; // The name of the AST node (if parsed) + StrC ProcName; // The name of the procedure +}; + +struct ParseContext +{ + TokArray Tokens; + StackNode* Scope; + + void push( StackNode* node ) + { + node->Prev = Scope; + Scope = node; + + #if 0 && Build_Debug + log_fmt("\tEntering Context: %.*s\n", Scope->ProcName.Len, Scope->ProcName.Ptr ); + #endif + } + + void pop() + { + #if 0 && Build_Debug + log_fmt("\tPopping Context: %.*s\n", Scope->ProcName.Len, Scope->ProcName.Ptr ); + #endif + Scope = Scope->Prev; + } + + String to_string() + { + String result = String::make_reserve( GlobalAllocator, kilobytes(4) ); + + Token scope_start = Scope->Start; + Token last_valid = Tokens.Idx >= Tokens.Arr.num() ? Tokens.Arr[Tokens.Arr.num() -1] : Tokens.current(); + + sptr length = scope_start.Length; + char const* current = scope_start.Text + length; + while ( current <= Tokens.Arr.back().Text && *current != '\n' && length < 74 ) + { + current++; + length++; + } + + String line = String::make( GlobalAllocator, { length, scope_start.Text } ); + result.append_fmt("\tScope : %s\n", line ); + line.free(); + + sptr dist = (sptr)last_valid.Text - (sptr)scope_start.Text + 2; + sptr length_from_err = dist; + String line_from_err = String::make( GlobalAllocator, { length_from_err, last_valid.Text } ); + + if ( length_from_err < 100 ) + result.append_fmt("\t(%d, %d):%*c\n", last_valid.Line, last_valid.Column, length_from_err, '^' ); + else + result.append_fmt("\t(%d, %d)\n", last_valid.Line, last_valid.Column ); + + StackNode* curr_scope = Scope; + s32 level = 0; + do + { + if ( curr_scope->Name ) + { + result.append_fmt("\t%d: %s, AST Name: %.*s\n", level, curr_scope->ProcName.Ptr, curr_scope->Name.Length, curr_scope->Name.Text ); + } + else + { + result.append_fmt("\t%d: %s\n", level, curr_scope->ProcName.Ptr ); + } + + curr_scope = curr_scope->Prev; + level++; + } + while ( curr_scope ); + return result; + } +}; + +global ParseContext Context; + +bool TokArray::__eat( TokType type ) +{ + if ( Arr.num() - Idx <= 0 ) + { + log_failure( "No tokens left.\n%s", Context.to_string() ); + return false; + } + + if ( ( Arr[ Idx ].Type == TokType::NewLine && type != TokType::NewLine ) + || ( Arr[ Idx ].Type == TokType::Comment && type != TokType::Comment ) ) + { + Idx++; + } + + if ( Arr[Idx].Type != type ) + { + log_failure( "Parse Error, TokArray::eat, Expected: ' %s ' not ' %.*s ' (%d, %d)`\n%s" + , ETokType::to_str(type).Ptr + , Arr[Idx].Length, Arr[Idx].Text + , current().Line + , current().Column + , Context.to_string() + ); + + return false; + } + +#if 0 && Build_Debug + log_fmt("Ate: %S\n", Arr[Idx].to_string() ); +#endif + + Idx++; + return true; +} + +internal +void init() +{ + Tokens = Array::init_reserve( LexArena + , ( LexAllocator_Size - sizeof( Array::Header ) ) / sizeof(Token) + ); + + defines_map_arena = Arena_256KB::init(); + defines = HashTable::init_reserve( defines_map_arena, 256 ); +} + +internal +void deinit() +{ + parser::Tokens = { nullptr }; +} + +#pragma region Helper Macros + +# define check_parse_args( def ) \ +if ( def.Len <= 0 ) \ +{ \ + log_failure( "gen::" stringize(__func__) ": length must greater than 0" ); \ + parser::Context.pop(); \ + return CodeInvalid; \ +} \ +if ( def.Ptr == nullptr ) \ +{ \ + log_failure( "gen::" stringize(__func__) ": def was null" ); \ + parser::Context.pop(); \ + return CodeInvalid; \ +} + +# define currtok_noskip Context.Tokens.current( dont_skip_formatting ) +# define currtok Context.Tokens.current() +# define prevtok Context.Tokens.previous() +# define nexttok Context.Tokens.next() +# define eat( Type_ ) Context.Tokens.__eat( Type_ ) +# define left ( Context.Tokens.Arr.num() - Context.Tokens.Idx ) + +#ifdef check +#define CHECK_WAS_DEFINED +#pragma push_macro("check") +#undef check +#endif + +# define check_noskip( Type_ ) ( left && currtok_noskip.Type == Type_ ) +# define check( Type_ ) ( left && currtok.Type == Type_ ) + +# define push_scope() \ + StackNode scope { nullptr, currtok_noskip, NullToken, txt( __func__ ) }; \ + Context.push( & scope ) + +#pragma endregion Helper Macros + +// Procedure Forwards ( Entire parser internal parser interface ) + +internal Code parse_array_decl (); +internal CodeAttributes parse_attributes (); +internal CodeComment parse_comment (); +internal Code parse_complicated_definition ( TokType which ); +internal CodeBody parse_class_struct_body ( TokType which, Token name = NullToken ); +internal Code parse_class_struct ( TokType which, bool inplace_def ); +internal CodeDefine parse_define (); +internal Code parse_expression (); +internal Code parse_forward_or_definition ( TokType which, bool is_inplace ); +internal CodeFn parse_function_after_name ( ModuleFlag mflags, CodeAttributes attributes, CodeSpecifiers specifiers, CodeType ret_type, Token name ); +internal Code parse_function_body (); +internal Code parse_global_nspace (); +internal Code parse_global_nspace_constructor_destructor( CodeSpecifiers specifiers ); +internal Token parse_identifier ( bool* possible_member_function = nullptr ); +internal CodeInclude parse_include (); +internal CodeOperator parse_operator_after_ret_type ( ModuleFlag mflags, CodeAttributes attributes, CodeSpecifiers specifiers, CodeType ret_type ); +internal Code parse_operator_function_or_variable( bool expects_function, CodeAttributes attributes, CodeSpecifiers specifiers ); +internal CodePragma parse_pragma (); +internal CodeParam parse_params ( bool use_template_capture = false ); +internal CodePreprocessCond parse_preprocess_cond (); +internal Code parse_simple_preprocess ( TokType which ); +internal Code parse_static_assert (); +internal void parse_template_args ( Token& token ); +internal CodeVar parse_variable_after_name ( ModuleFlag mflags, CodeAttributes attributes, CodeSpecifiers specifiers, CodeType type, StrC name ); +internal CodeVar parse_variable_declaration_list (); + +internal CodeClass parse_class ( bool inplace_def = false ); +internal CodeConstructor parse_constructor ( CodeSpecifiers specifiers ); +internal CodeDestructor parse_destructor ( CodeSpecifiers specifiers = NoCode ); +internal CodeEnum parse_enum ( bool inplace_def = false ); +internal CodeBody parse_export_body (); +internal CodeBody parse_extern_link_body(); +internal CodeExtern parse_extern_link (); +internal CodeFriend parse_friend (); +internal CodeFn parse_function (); +internal CodeNS parse_namespace (); +internal CodeOpCast parse_operator_cast ( CodeSpecifiers specifiers = NoCode ); +internal CodeStruct parse_struct ( bool inplace_def = false ); +internal CodeVar parse_variable (); +internal CodeTemplate parse_template (); +internal CodeType parse_type ( bool from_template = false, bool* is_function = nullptr ); +internal CodeTypedef parse_typedef (); +internal CodeUnion parse_union ( bool inplace_def = false ); +internal CodeUsing parse_using (); + +constexpr bool inplace_def = true; + +// Internal parsing functions + +constexpr bool strip_formatting_dont_preserve_newlines = false; +/* + This function was an attempt at stripping formatting from any c++ code. + It has edge case failures that prevent it from being used in function bodies. +*/ +internal +String strip_formatting( StrC raw_text, bool preserve_newlines = true ) +{ + String content = String::make_reserve( GlobalAllocator, raw_text.Len ); + + if ( raw_text.Len == 0 ) + return content; + +#define cut_length ( scanner - raw_text.Ptr - last_cut ) +#define cut_ptr ( raw_text.Ptr + last_cut ) +#define pos ( sptr( scanner ) - sptr( raw_text.Ptr ) ) +#define move_fwd() do { scanner++; tokleft--; } while(0) + + s32 tokleft = raw_text.Len; + sptr last_cut = 0; + char const* scanner = raw_text.Ptr; + + if ( scanner[0] == ' ' ) + { + move_fwd(); + last_cut = 1; + } + + bool within_string = false; + bool within_char = false; + bool must_keep_newline = false; + while ( tokleft ) + { + // Skip over the content of string literals + if ( scanner[0] == '"' ) + { + move_fwd(); + + while ( tokleft && ( scanner[0] != '"' || *( scanner - 1 ) == '\\' ) ) + { + if ( scanner[0] == '\\' && tokleft > 1 ) + { + scanner += 2; + tokleft -= 2; + } + else + { + move_fwd(); + } + } + + // Skip the closing " + if ( tokleft ) + move_fwd(); + + content.append( cut_ptr, cut_length ); + last_cut = sptr( scanner ) - sptr( raw_text.Ptr ); + continue; + } + + // Skip over the content of character literals + if ( scanner[0] == '\'' ) + { + move_fwd(); + + while ( tokleft + && ( scanner[0] != '\'' + || ( *(scanner -1 ) == '\\' ) + ) ) + { + move_fwd(); + } + + // Skip the closing ' + if ( tokleft ) + move_fwd(); + + content.append( cut_ptr, cut_length ); + last_cut = sptr( scanner ) - sptr( raw_text.Ptr ); + continue; + } + + // Block comments + if ( tokleft > 1 && scanner[0] == '/' && scanner[1] == '*' ) + { + while ( tokleft > 1 && !(scanner[0] == '*' && scanner[1] == '/') ) + move_fwd(); + + scanner += 2; + tokleft -= 2; + + content.append( cut_ptr, cut_length ); + last_cut = sptr( scanner ) - sptr( raw_text.Ptr ); + continue; + } + + // Line comments + if ( tokleft > 1 && scanner[0] == '/' && scanner[1] == '/' ) + { + must_keep_newline = true; + + scanner += 2; + tokleft -= 2; + + while ( tokleft && scanner[ 0 ] != '\n' ) + move_fwd(); + + if (tokleft) + move_fwd(); + + content.append( cut_ptr, cut_length ); + last_cut = sptr( scanner ) - sptr( raw_text.Ptr ); + continue; + } + + // Tabs + if (scanner[0] == '\t') + { + if (pos > last_cut) + content.append(cut_ptr, cut_length); + + if ( content.back() != ' ' ) + content.append(' '); + + move_fwd(); + last_cut = sptr(scanner) - sptr(raw_text.Ptr); + continue; + } + + if ( tokleft > 1 && scanner[0] == '\r' && scanner[1] == '\n' ) + { + if ( must_keep_newline || preserve_newlines ) + { + must_keep_newline = false; + + scanner += 2; + tokleft -= 2; + + content.append( cut_ptr, cut_length ); + last_cut = sptr( scanner ) - sptr( raw_text.Ptr ); + continue; + } + + if ( pos > last_cut ) + content.append( cut_ptr, cut_length ); + + // Replace with a space + if ( content.back() != ' ' ) + content.append( ' ' ); + + scanner += 2; + tokleft -= 2; + + last_cut = sptr( scanner ) - sptr( raw_text.Ptr ); + continue; + } + + if ( scanner[0] == '\n' ) + { + if ( must_keep_newline || preserve_newlines ) + { + must_keep_newline = false; + + move_fwd(); + + content.append( cut_ptr, cut_length ); + last_cut = sptr( scanner ) - sptr( raw_text.Ptr ); + continue; + } + + if ( pos > last_cut ) + content.append( cut_ptr, cut_length ); + + // Replace with a space + if ( content.back() != ' ' ) + content.append( ' ' ); + + move_fwd(); + + last_cut = sptr( scanner ) - sptr( raw_text.Ptr ); + continue; + } + + // Escaped newlines + if ( scanner[0] == '\\' ) + { + content.append( cut_ptr, cut_length ); + + s32 amount_to_skip = 1; + if ( tokleft > 1 && scanner[1] == '\n' ) + { + amount_to_skip = 2; + } + else if ( tokleft > 2 && scanner[1] == '\r' && scanner[2] == '\n' ) + { + amount_to_skip = 3; + } + + if ( amount_to_skip > 1 && pos == last_cut ) + { + scanner += amount_to_skip; + tokleft -= amount_to_skip; + } + else + move_fwd(); + + last_cut = sptr( scanner ) - sptr( raw_text.Ptr ); + continue; + } + + // Consectuive spaces + if ( tokleft > 1 && char_is_space( scanner[0] ) && char_is_space( scanner[ 1 ] ) ) + { + content.append( cut_ptr, cut_length ); + do + { + move_fwd(); + } + while ( tokleft && char_is_space( scanner[0] ) ); + + last_cut = sptr( scanner ) - sptr( raw_text.Ptr ); + + // Preserve only 1 space of formattting + if ( content.back() != ' ' ) + content.append( ' ' ); + + continue; + } + + move_fwd(); + } + + if ( last_cut < raw_text.Len ) + { + content.append( cut_ptr, raw_text.Len - last_cut ); + } + +#undef cut_ptr +#undef cut_length +#undef pos +#undef move_fwd + + return content; +} + +internal +Code parse_array_decl() +{ + push_scope(); + + if ( check( TokType::Operator ) && currtok.Text[0] == '[' && currtok.Text[1] == ']' ) + { + Code array_expr = untyped_str( currtok ); + eat( TokType::Operator ); + // [] + + Context.pop(); + return array_expr; + } + + if ( check( TokType::BraceSquare_Open ) ) + { + eat( TokType::BraceSquare_Open ); + // [ + + if ( left == 0 ) + { + log_failure( "Error, unexpected end of array declaration ( '[]' scope started )\n%s", Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + if ( currtok.Type == TokType::BraceSquare_Close ) + { + log_failure( "Error, empty array expression in definition\n%s", Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + Token untyped_tok = currtok; + + while ( left && currtok.Type != TokType::BraceSquare_Close ) + { + eat( currtok.Type ); + } + + untyped_tok.Length = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)untyped_tok.Text; + + Code array_expr = untyped_str( untyped_tok ); + // [ + + if ( left == 0 ) + { + log_failure( "Error, unexpected end of array declaration, expected ]\n%s", Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + if ( currtok.Type != TokType::BraceSquare_Close ) + { + log_failure( "%s: Error, expected ] in array declaration, not %s\n%s", ETokType::to_str( currtok.Type ), Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + eat( TokType::BraceSquare_Close ); + // [ ] + + // Its a multi-dimensional array + if ( check( TokType::BraceSquare_Open )) + { + Code adjacent_arr_expr = parse_array_decl(); + // [ ][ ]... + + array_expr->Next = adjacent_arr_expr.ast; + } + + Context.pop(); + return array_expr; + } + + Context.pop(); + return { nullptr }; +} + +internal inline +CodeAttributes parse_attributes() +{ + push_scope(); + + Token start = currtok; + s32 len = 0; + + // There can be more than one attribute. If there is flatten them to a single string. + // TODO(Ed): Support keeping an linked list of attributes similar to parameters + while ( left && currtok.is_attribute() ) + { + if ( check( TokType::Attribute_Open ) ) + { + eat( TokType::Attribute_Open ); + // [[ + + while ( left && currtok.Type != TokType::Attribute_Close ) + { + eat( currtok.Type ); + } + // [[ + + eat( TokType::Attribute_Close ); + // [[ ]] + + len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text; + } + else if ( check( TokType::Decl_GNU_Attribute ) ) + { + eat( TokType::Decl_GNU_Attribute ); + eat( TokType::Capture_Start ); + eat( TokType::Capture_Start ); + // __attribute__(( + + while ( left && currtok.Type != TokType::Capture_End ) + { + eat( currtok.Type ); + } + // __attribute__(( + + eat( TokType::Capture_End ); + eat( TokType::Capture_End ); + // __attribute__(( )) + + len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text; + } + else if ( check( TokType::Decl_MSVC_Attribute ) ) + { + eat( TokType::Decl_MSVC_Attribute ); + eat( TokType::Capture_Start ); + // __declspec( + + while ( left && currtok.Type != TokType::Capture_End ) + { + eat( currtok.Type ); + } + // __declspec( + + eat( TokType::Capture_End ); + // __declspec( ) + + len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text; + } + else if ( currtok.is_attribute() ) + { + eat( currtok.Type ); + // + + // If its a macro based attribute, this could be a functional macro such as Unreal's UE_DEPRECATED(...) + if ( check( TokType::Capture_Start)) + { + eat( TokType::Capture_Start ); + + s32 level = 0; + while (left && currtok.Type != TokType::Capture_End && level == 0) + { + if (currtok.Type == TokType::Capture_Start) + ++ level; + if (currtok.Type == TokType::Capture_End) + --level; + eat(currtok.Type); + } + eat(TokType::Capture_End); + } + + len = ( ( sptr )prevtok.Text + prevtok.Length ) - ( sptr )start.Text; + // ( ... ) + } + } + + if ( len > 0 ) + { + StrC attribute_txt = { len, start.Text }; + Context.pop(); + + String name_stripped = strip_formatting( attribute_txt, strip_formatting_dont_preserve_newlines ); + + Code result = make_code(); + result->Type = ECode::PlatformAttributes; + result->Name = get_cached_string( name_stripped ); + result->Content = result->Name; + // result->Token = + + return ( CodeAttributes )result; + } + + Context.pop(); + return { nullptr }; +} + +internal +Code parse_class_struct( TokType which, bool inplace_def = false ) +{ + if ( which != TokType::Decl_Class && which != TokType::Decl_Struct ) + { + log_failure( "Error, expected class or struct, not %s\n%s", ETokType::to_str( which ), Context.to_string() ); + return CodeInvalid; + } + + Token name { nullptr, 0, TokType::Invalid }; + + AccessSpec access = AccessSpec::Default; + CodeType parent = { nullptr }; + CodeBody body = { nullptr }; + CodeAttributes attributes = { nullptr }; + ModuleFlag mflags = ModuleFlag::None; + + CodeClass result = CodeInvalid; + + if ( check(TokType::Module_Export) ) + { + mflags = ModuleFlag::Export; + eat( TokType::Module_Export ); + } + // + + eat( which ); + // + + attributes = parse_attributes(); + // + + if ( check( TokType::Identifier ) ) + { + name = parse_identifier(); + Context.Scope->Name = name; + } + // + + local_persist + char interface_arr_mem[ kilobytes(4) ] {0}; + Array interfaces = Array::init_reserve( Arena::init_from_memory(interface_arr_mem, kilobytes(4) ), 4 ); + + // TODO(Ed) : Make an AST_DerivedType, we'll store any arbitary derived type into there as a linear linked list of them. + if ( check( TokType::Assign_Classifer ) ) + { + eat( TokType::Assign_Classifer ); + // : + + if ( currtok.is_access_specifier() ) + { + access = currtok.to_access_specifier(); + // : + eat( currtok.Type ); + } + + Token parent_tok = parse_identifier(); + parent = def_type( parent_tok ); + // : + + while ( check(TokType::Comma) ) + { + eat( TokType::Comma ); + // : , + + if ( currtok.is_access_specifier() ) + { + eat(currtok.Type); + } + Token interface_tok = parse_identifier(); + + interfaces.append( def_type( interface_tok ) ); + // : , ... + } + } + + if ( check( TokType::BraceCurly_Open ) ) + { + body = parse_class_struct_body( which, name ); + } + // : , ... { } + + CodeComment inline_cmt = NoCode; + if ( ! inplace_def ) + { + Token stmt_end = currtok; + eat( TokType::Statement_End ); + // : , ... { }; + + if ( currtok_noskip.Type == TokType::Comment && currtok_noskip.Line == stmt_end.Line ) + inline_cmt = parse_comment(); + // : , ... { }; + } + + if ( which == TokType::Decl_Class ) + result = def_class( name, body, parent, access, attributes, mflags ); + + else + result = def_struct( name, body, (CodeType)parent, access, attributes, mflags ); + + if ( inline_cmt ) + result->InlineCmt = inline_cmt; + + interfaces.free(); + return result; +} + +internal neverinline +CodeBody parse_class_struct_body( TokType which, Token name ) +{ + using namespace ECode; + push_scope(); + + eat( TokType::BraceCurly_Open ); + // { + + CodeBody + result = (CodeBody) make_code(); + + if ( which == TokType::Decl_Class ) + result->Type = Class_Body; + + else + result->Type = Struct_Body; + + while ( left && currtok_noskip.Type != TokType::BraceCurly_Close ) + { + Code member = Code::Invalid; + CodeAttributes attributes = { nullptr }; + CodeSpecifiers specifiers = { nullptr }; + + bool expects_function = false; + + // Context.Scope->Start = currtok_noskip; + + if ( currtok_noskip.Type == TokType::Preprocess_Hash ) + eat( TokType::Preprocess_Hash ); + + switch ( currtok_noskip.Type ) + { + case TokType::Statement_End: + { + // TODO(Ed): Convert this to a general warning procedure + log_fmt("Dangling end statement found %S\n", currtok_noskip.to_string()); + eat( TokType::Statement_End ); + continue; + } + case TokType::NewLine: + member = fmt_newline; + eat( TokType::NewLine ); + break; + + case TokType::Comment: + member = parse_comment(); + break; + + case TokType::Access_Public: + member = access_public; + eat( TokType::Access_Public ); + eat( TokType::Assign_Classifer ); + // public: + break; + + case TokType::Access_Protected: + member = access_protected; + eat( TokType::Access_Protected ); + eat( TokType::Assign_Classifer ); + // protected: + break; + + case TokType::Access_Private: + member = access_private; + eat( TokType::Access_Private ); + eat( TokType::Assign_Classifer ); + // private: + break; + + case TokType::Decl_Class: + member = parse_complicated_definition( TokType::Decl_Class ); + // class + break; + + case TokType::Decl_Enum: + member = parse_complicated_definition( TokType::Decl_Enum ); + // enum + break; + + case TokType::Decl_Friend: + member = parse_friend(); + // friend + break; + + case TokType::Decl_Operator: + member = parse_operator_cast(); + // operator () + break; + + case TokType::Decl_Struct: + member = parse_complicated_definition( TokType::Decl_Struct ); + // struct + break; + + case TokType::Decl_Template: + member = parse_template(); + // template< ... > + break; + + case TokType::Decl_Typedef: + member = parse_typedef(); + // typedef + break; + + case TokType::Decl_Union: + member = parse_complicated_definition( TokType::Decl_Union ); + // union + break; + + case TokType::Decl_Using: + member = parse_using(); + // using + break; + + case TokType::Operator: + if ( currtok.Text[0] != '~' ) + { + log_failure( "Operator token found in global body but not destructor unary negation\n%s", Context.to_string() ); + return CodeInvalid; + } + + member = parse_destructor(); + // ~() + break; + + case TokType::Preprocess_Define: + member = parse_define(); + // #define + break; + + case TokType::Preprocess_Include: + member = parse_include(); + // #include + break; + + case TokType::Preprocess_If: + case TokType::Preprocess_IfDef: + case TokType::Preprocess_IfNotDef: + case TokType::Preprocess_ElIf: + member = parse_preprocess_cond(); + // # + break; + + case TokType::Preprocess_Else: + member = preprocess_else; + eat( TokType::Preprocess_Else ); + // #else + break; + + case TokType::Preprocess_EndIf: + member = preprocess_endif; + eat( TokType::Preprocess_EndIf ); + // #endif + break; + + case TokType::Preprocess_Macro: + member = parse_simple_preprocess( TokType::Preprocess_Macro ); + // + break; + + case TokType::Preprocess_Pragma: + member = parse_pragma(); + // #pragma + break; + + case TokType::Preprocess_Unsupported: + member = parse_simple_preprocess( TokType::Preprocess_Unsupported ); + // # + break; + + case TokType::StaticAssert: + member = parse_static_assert(); + // static_assert + break; + + case TokType::Attribute_Open: + case TokType::Decl_GNU_Attribute: + case TokType::Decl_MSVC_Attribute: + #define Entry( attribute, str ) case TokType::attribute: + GEN_DEFINE_ATTRIBUTE_TOKENS + #undef Entry + { + attributes = parse_attributes(); + // + } + //! Fallthrough intended + case TokType::Spec_Consteval: + case TokType::Spec_Constexpr: + case TokType::Spec_Constinit: + case TokType::Spec_Explicit: + case TokType::Spec_ForceInline: + case TokType::Spec_Inline: + case TokType::Spec_Mutable: + case TokType::Spec_NeverInline: + case TokType::Spec_Static: + case TokType::Spec_Volatile: + case TokType::Spec_Virtual: + { + SpecifierT specs_found[16] { ESpecifier::NumSpecifiers }; + s32 NumSpecifiers = 0; + + while ( left && currtok.is_specifier() ) + { + SpecifierT spec = ESpecifier::to_type( currtok ); + + b32 ignore_spec = false; + + switch ( spec ) + { + case ESpecifier::Constexpr: + case ESpecifier::Constinit: + case ESpecifier::Explicit: + case ESpecifier::Inline: + case ESpecifier::ForceInline: + case ESpecifier::Mutable: + case ESpecifier::NeverInline: + case ESpecifier::Static: + case ESpecifier::Volatile: + case ESpecifier::Virtual: + break; + + case ESpecifier::Consteval: + expects_function = true; + break; + + case ESpecifier::Const : + ignore_spec = true; + break; + + default: + log_failure( "Invalid specifier %s for variable\n%s", ESpecifier::to_str(spec), Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + // Every specifier after would be considered part of the type type signature + if (ignore_spec) + break; + + specs_found[NumSpecifiers] = spec; + NumSpecifiers++; + eat( currtok.Type ); + } + + if ( NumSpecifiers ) + { + specifiers = def_specifiers( NumSpecifiers, specs_found ); + } + // + + if ( currtok.is_attribute() ) + { + // Unfortuantely Unreal has code where there is attirbutes before specifiers + CodeAttributes more_attributes = parse_attributes(); + + if ( attributes ) + { + String fused = String::make_reserve( GlobalAllocator, attributes->Content.length() + more_attributes->Content.length() ); + fused.append_fmt( "%S %S", attributes->Content, more_attributes->Content ); + + attributes->Name = get_cached_string(fused); + attributes->Content = attributes->Name; + // + } + + attributes = more_attributes; + } + + if ( currtok.Type == TokType::Operator && currtok.Text[0] == '~' ) + { + member = parse_destructor( specifiers ); + // ~() + break; + } + + if ( currtok.Type == TokType::Decl_Operator ) + { + member = parse_operator_cast( specifiers ); + // operator () + break; + } + } + //! Fallthrough intentional + case TokType::Identifier: + case TokType::Spec_Const: + case TokType::Type_Unsigned: + case TokType::Type_Signed: + case TokType::Type_Short: + case TokType::Type_Long: + case TokType::Type_bool: + case TokType::Type_char: + case TokType::Type_int: + case TokType::Type_double: + { + if ( nexttok.Type == TokType::Capture_Start && name.Length && currtok.Type == TokType::Identifier ) + { + if ( str_compare( name.Text, currtok.Text, name.Length ) == 0 ) + { + member = parse_constructor( specifiers ); + // () + break; + } + } + + member = parse_operator_function_or_variable( expects_function, attributes, specifiers ); + // operator ... + // or + // ... + } + break; + + default: + Token untyped_tok = currtok; + + while ( left && currtok.Type != TokType::BraceCurly_Close ) + { + untyped_tok.Length = ( (sptr)currtok.Text + currtok.Length ) - (sptr)untyped_tok.Text; + eat( currtok.Type ); + } + + member = untyped_str( untyped_tok ); + // Something unknown + break; + } + + if ( member == Code::Invalid ) + { + log_failure( "Failed to parse member\n%s", Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + result.append( member ); + } + + eat( TokType::BraceCurly_Close ); + // { } + Context.pop(); + return result; +} + +internal +CodeComment parse_comment() +{ + StackNode scope { nullptr, currtok_noskip, NullToken, txt( __func__ ) }; + Context.push( & scope ); + + CodeComment + result = (CodeComment) make_code(); + result->Type = ECode::Comment; + result->Content = get_cached_string( currtok_noskip ); + result->Name = result->Content; + // result->Token = currtok_noskip; + eat( TokType::Comment ); + + Context.pop(); + return result; +} + +internal +Code parse_complicated_definition( TokType which ) +{ + push_scope(); + + bool is_inplace = false; + + TokArray tokens = Context.Tokens; + + s32 idx = tokens.Idx; + s32 level = 0; + for ( ; idx < tokens.Arr.num(); idx++ ) + { + if ( tokens[ idx ].Type == TokType::BraceCurly_Open ) + level++; + + if ( tokens[ idx ].Type == TokType::BraceCurly_Close ) + level--; + + if ( level == 0 && tokens[ idx ].Type == TokType::Statement_End ) + break; + } + + if ( ( idx - 2 ) == tokens.Idx ) + { + // Its a forward declaration only + Code result = parse_forward_or_definition( which, is_inplace ); + // ; + Context.pop(); + return result; + } + + Token tok = tokens[ idx - 1 ]; + if ( tok.is_specifier() && is_trailing( ESpecifier::to_type(tok)) ) + { + // (...) ...; + + s32 spec_idx = idx - 1; + Token spec = tokens[spec_idx]; + while ( spec.is_specifier() && is_trailing( ESpecifier::to_type(spec)) ) + { + -- spec_idx; + spec = tokens[spec_idx]; + } + + if ( tokens[spec_idx].Type == TokType::Capture_End ) + { + // Forward declaration with trailing specifiers for a procedure + tok = tokens[spec_idx]; + + Code result = parse_operator_function_or_variable( false, { nullptr }, { nullptr } ); + // , or Name> ... + Context.pop(); + return result; + } + + log_failure( "Unsupported or bad member definition after %s declaration\n%s", to_str(which), Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + if ( tok.Type == TokType::Identifier ) + { + tok = tokens[ idx - 2 ]; + bool is_indirection = tok.Type == TokType::Ampersand || tok.Type == TokType::Star; + bool ok_to_parse = false; + + if ( tok.Type == TokType::BraceCurly_Close ) + { + // Its an inplace definition + // { ... } ; + ok_to_parse = true; + is_inplace = true; + } + else if ( tok.Type == TokType::Identifier && tokens[ idx - 3 ].Type == which ) + { + // Its a variable with type ID using namespace. + // ; + ok_to_parse = true; + } + else if ( tok.Type == TokType::Assign_Classifer + && ( ( tokens[idx - 5].Type == which && tokens[idx - 4].Type == TokType::Decl_Class ) + || ( tokens[idx - 4].Type == which)) + ) + { + // Its a forward declaration of an enum + // : ; + // : ; + ok_to_parse = true; + Code result = parse_enum(); + Context.pop(); + return result; + } + else if ( is_indirection ) + { + // Its a indirection type with type ID using struct namespace. + // * ; + ok_to_parse = true; + } + + if ( ! ok_to_parse ) + { + log_failure( "Unsupported or bad member definition after %s declaration\n%s", to_str(which), Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + Code result = parse_operator_function_or_variable( false, { nullptr }, { nullptr } ); + // , or Name> ... + Context.pop(); + return result; + } + else if ( tok.Type >= TokType::Type_Unsigned && tok.Type <= TokType::Type_MS_W64 ) + { + tok = tokens[ idx - 2 ]; + + if ( tok.Type != TokType::Assign_Classifer + || ( ( tokens[idx - 5].Type != which && tokens[idx - 4].Type != TokType::Decl_Class ) + && ( tokens[idx - 4].Type != which)) + ) + { + log_failure( "Unsupported or bad member definition after %s declaration\n%s", to_str(which), Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + // Its a forward declaration of an enum class + // : ; + // : ; + Code result = parse_enum(); + Context.pop(); + return result; + } + else if ( tok.Type == TokType::BraceCurly_Close ) + { + // Its a definition + Code result = parse_forward_or_definition( which, is_inplace ); + // { ... }; + Context.pop(); + return result; + } + else if ( tok.Type == TokType::BraceSquare_Close ) + { + // Its an array definition + Code result = parse_operator_function_or_variable( false, { nullptr }, { nullptr } ); + // [ ... ]; + Context.pop(); + return result; + } + else + { + log_failure( "Unsupported or bad member definition after %s declaration\n%S", to_str(which).Ptr, Context.to_string() ); + Context.pop(); + return CodeInvalid; + } +} + +internal inline +CodeDefine parse_define() +{ + push_scope(); + eat( TokType::Preprocess_Define ); + // #define + + CodeDefine + define = (CodeDefine) make_code(); + define->Type = ECode::Preprocess_Define; + + if ( ! check( TokType::Identifier ) ) + { + log_failure( "Error, expected identifier after #define\n%s", Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + Context.Scope->Name = currtok; + define->Name = get_cached_string( currtok ); + eat( TokType::Identifier ); + // #define + + if ( ! check( TokType::Preprocess_Content )) + { + log_failure( "Error, expected content after #define %s\n%s", define->Name, Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + if ( currtok.Length == 0 ) + { + define->Content = get_cached_string( currtok ); + eat( TokType::Preprocess_Content ); + // #define + + Context.pop(); + return define; + } + + define->Content = get_cached_string( strip_formatting( currtok, strip_formatting_dont_preserve_newlines ) ); + eat( TokType::Preprocess_Content ); + // #define + + Context.pop(); + return define; +} + +internal inline +Code parse_assignment_expression() +{ + Code expr = { nullptr }; + + eat( TokType::Operator ); + // = + + Token expr_tok = currtok; + + if ( currtok.Type == TokType::Statement_End && currtok.Type != TokType::Comma ) + { + log_failure( "Expected expression after assignment operator\n%s", Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + s32 level = 0; + while ( left && currtok.Type != TokType::Statement_End && (currtok.Type != TokType::Comma || level > 0) ) + { + if (currtok.Type == TokType::BraceCurly_Open ) + level++; + if (currtok.Type == TokType::BraceCurly_Close ) + level--; + if (currtok.Type == TokType::Capture_Start) + level++; + else if (currtok.Type == TokType::Capture_End) + level--; + + eat( currtok.Type ); + } + + expr_tok.Length = ( ( sptr )currtok.Text + currtok.Length ) - ( sptr )expr_tok.Text - 1; + expr = untyped_str( expr_tok ); + // = + return expr; +} + +internal inline +Code parse_forward_or_definition( TokType which, bool is_inplace ) +{ + Code result = CodeInvalid; + + switch ( which ) + { + case TokType::Decl_Class: + result = parse_class( is_inplace ); + return result; + + case TokType::Decl_Enum: + result = parse_enum( is_inplace ); + return result; + + case TokType::Decl_Struct: + result = parse_struct( is_inplace ); + return result; + + case TokType::Decl_Union: + result = parse_union( is_inplace ); + return result; + + default: + log_failure( "Error, wrong token type given to parse_complicated_definition " + "(only supports class, enum, struct, union) \n%s" + , Context.to_string() ); + + return CodeInvalid; + } +} + +// Function parsing is handled in multiple places because its initial signature is shared with variable parsing +internal inline +CodeFn parse_function_after_name( + ModuleFlag mflags + , CodeAttributes attributes + , CodeSpecifiers specifiers + , CodeType ret_type + , Token name +) +{ + push_scope(); + CodeParam params = parse_params(); + // ( ) + + // TODO(Ed), Review old comment : These have to be kept separate from the return type's specifiers. + while ( left && currtok.is_specifier() ) + { + if ( specifiers.ast == nullptr ) + { + specifiers = def_specifier( ESpecifier::to_type(currtok) ); + eat( currtok.Type ); + continue; + } + + specifiers.append( ESpecifier::to_type(currtok) ); + eat( currtok.Type ); + } + // ( ) + + CodeBody body = NoCode; + CodeComment inline_cmt = NoCode; + if ( check( TokType::BraceCurly_Open ) ) + { + body = parse_function_body(); + if ( body == Code::Invalid ) + { + Context.pop(); + return CodeInvalid; + } + // ( ) { } + } + else if ( check(TokType::Operator) && currtok.Text[0] == '=' ) + { + eat(TokType::Operator); + specifiers.append( ESpecifier::Pure ); + + eat( TokType::Number); + Token stmt_end = currtok; + eat( TokType::Statement_End ); + // ( ) = 0; + + if ( currtok_noskip.Type == TokType::Comment && currtok_noskip.Line == stmt_end.Line ) + inline_cmt = parse_comment(); + // ( ) ; + } + else + { + Token stmt_end = currtok; + eat( TokType::Statement_End ); + // ( ) ; + + if ( currtok_noskip.Type == TokType::Comment && currtok_noskip.Line == stmt_end.Line ) + inline_cmt = parse_comment(); + // ( ) ; + } + + using namespace ECode; + + String + name_stripped = String::make( GlobalAllocator, name ); + name_stripped.strip_space(); + + CodeFn + result = (CodeFn) make_code(); + result->Name = get_cached_string( name_stripped ); + result->ModuleFlags = mflags; + + if ( body ) + { + switch ( body->Type ) + { + case Function_Body: + case Untyped: + break; + + default: + { + log_failure("Body must be either of Function_Body or Untyped type, %s\n%s", body.debug_str(), Context.to_string()); + Context.pop(); + return CodeInvalid; + } + } + + result->Type = Function; + result->Body = body; + } + else + { + result->Type = Function_Fwd; + } + + if ( attributes ) + result->Attributes = attributes; + + if ( specifiers ) + result->Specs = specifiers; + + result->ReturnType = ret_type; + + if ( params ) + result->Params = params; + + if ( inline_cmt ) + result->InlineCmt = inline_cmt; + + Context.pop(); + return result; +} + +internal +Code parse_function_body() +{ + using namespace ECode; + push_scope(); + + eat( TokType::BraceCurly_Open ); + + CodeBody + result = (CodeBody) make_code(); + result->Type = Function_Body; + + // TODO : Support actual parsing of function body + Token start = currtok_noskip; + + s32 level = 0; + while ( left && ( currtok_noskip.Type != TokType::BraceCurly_Close || level > 0 ) ) + { + if ( currtok_noskip.Type == TokType::BraceCurly_Open ) + level++; + + else if ( currtok_noskip.Type == TokType::BraceCurly_Close && level > 0 ) + level--; + + eat( currtok_noskip.Type ); + } + + Token previous = prevtok; + + s32 len = ( (sptr)prevtok.Text + prevtok.Length ) - (sptr)start.Text; + + if ( len > 0 ) + { + result.append( def_execution( { len, start.Text } ) ); + } + + eat( TokType::BraceCurly_Close ); + + Context.pop(); + return result; +} + +internal neverinline +CodeBody parse_global_nspace( CodeT which ) +{ + using namespace ECode; + + push_scope(); + + if ( which != Namespace_Body && which != Global_Body && which != Export_Body && which != Extern_Linkage_Body ) + return CodeInvalid; + + if ( which != Global_Body ) + eat( TokType::BraceCurly_Open ); + // { + + CodeBody + result = (CodeBody) make_code(); + result->Type = which; + + while ( left && currtok_noskip.Type != TokType::BraceCurly_Close ) + { + Code member = Code::Invalid; + CodeAttributes attributes = { nullptr }; + CodeSpecifiers specifiers = { nullptr }; + + bool expects_function = false; + + // Context.Scope->Start = currtok_noskip; + + if ( currtok_noskip.Type == TokType::Preprocess_Hash ) + eat( TokType::Preprocess_Hash ); + + switch ( currtok_noskip.Type ) + { + case TokType::Statement_End: + { + // TODO(Ed): Convert this to a general warning procedure + log_fmt("Dangling end statement found %S\n", currtok_noskip.to_string()); + eat( TokType::Statement_End ); + continue; + } + case TokType::NewLine: + // Empty lines are auto skipped by Tokens.current() + member = fmt_newline; + eat( TokType::NewLine ); + break; + + case TokType::Comment: + member = parse_comment(); + break; + + case TokType::Decl_Class: + member = parse_complicated_definition( TokType::Decl_Class ); + // class + break; + + case TokType::Decl_Enum: + member = parse_complicated_definition( TokType::Decl_Enum ); + // enum + break; + + case TokType::Decl_Extern_Linkage: + if ( which == Extern_Linkage_Body ) + log_failure( "Nested extern linkage\n%s", Context.to_string() ); + + member = parse_extern_link(); + // extern "..." { ... } + break; + + case TokType::Decl_Namespace: + member = parse_namespace(); + // namespace { ... } + break; + + case TokType::Decl_Struct: + member = parse_complicated_definition( TokType::Decl_Struct ); + // struct ... + break; + + case TokType::Decl_Template: + member = parse_template(); + // template<...> ... + break; + + case TokType::Decl_Typedef: + member = parse_typedef(); + // typedef ... + break; + + case TokType::Decl_Union: + member = parse_complicated_definition( TokType::Decl_Union ); + // union ... + break; + + case TokType::Decl_Using: + member = parse_using(); + // using ... + break; + + case TokType::Preprocess_Define: + member = parse_define(); + // #define ... + break; + + case TokType::Preprocess_Include: + member = parse_include(); + // #include ... + break; + + case TokType::Preprocess_If: + case TokType::Preprocess_IfDef: + case TokType::Preprocess_IfNotDef: + case TokType::Preprocess_ElIf: + member = parse_preprocess_cond(); + // # ... + break; + + case TokType::Preprocess_Else: + member = preprocess_else; + eat( TokType::Preprocess_Else ); + // #else + break; + + case TokType::Preprocess_EndIf: + member = preprocess_endif; + eat( TokType::Preprocess_EndIf ); + // #endif + break; + + case TokType::Preprocess_Macro: + member = parse_simple_preprocess( TokType::Preprocess_Macro ); + // + break; + + case TokType::Preprocess_Pragma: + member = parse_pragma(); + // #pragma ... + break; + + case TokType::Preprocess_Unsupported: + member = parse_simple_preprocess( TokType::Preprocess_Unsupported ); + // # ... + break; + + case TokType::StaticAssert: + member = parse_static_assert(); + // static_assert( , ... ); + break; + + case TokType::Module_Export: + if ( which == Export_Body ) + log_failure( "Nested export declaration\n%s", Context.to_string() ); + + member = parse_export_body(); + // export { ... } + break; + + case TokType::Module_Import: + { + not_implemented( context ); + // import ... + } + //! Fallthrough intentional + case TokType::Attribute_Open: + case TokType::Decl_GNU_Attribute: + case TokType::Decl_MSVC_Attribute: + #define Entry( attribute, str ) case TokType::attribute: + GEN_DEFINE_ATTRIBUTE_TOKENS + #undef Entry + { + attributes = parse_attributes(); + // + } + //! Fallthrough intentional + case TokType::Spec_Consteval: + case TokType::Spec_Constexpr: + case TokType::Spec_Constinit: + case TokType::Spec_Extern: + case TokType::Spec_ForceInline: + case TokType::Spec_Global: + case TokType::Spec_Inline: + case TokType::Spec_Internal_Linkage: + case TokType::Spec_NeverInline: + case TokType::Spec_Static: + { + SpecifierT specs_found[16] { ESpecifier::NumSpecifiers }; + s32 NumSpecifiers = 0; + + while ( left && currtok.is_specifier() ) + { + SpecifierT spec = ESpecifier::to_type( currtok ); + + bool ignore_spec = false; + + switch ( spec ) + { + case ESpecifier::Constexpr: + case ESpecifier::Constinit: + case ESpecifier::ForceInline: + case ESpecifier::Global: + case ESpecifier::External_Linkage: + case ESpecifier::Internal_Linkage: + case ESpecifier::Inline: + case ESpecifier::Mutable: + case ESpecifier::NeverInline: + case ESpecifier::Static: + case ESpecifier::Volatile: + break; + + case ESpecifier::Consteval: + expects_function = true; + break; + + case ESpecifier::Const: + ignore_spec = true; + break; + + default: + StrC spec_str = ESpecifier::to_str(spec); + + log_failure( "Invalid specifier %.*s for variable\n%s", spec_str.Len, spec_str, Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + if (ignore_spec) + break; + + specs_found[NumSpecifiers] = spec; + NumSpecifiers++; + eat( currtok.Type ); + } + + if ( NumSpecifiers ) + { + specifiers = def_specifiers( NumSpecifiers, specs_found ); + } + // + } + //! Fallthrough intentional + case TokType::Identifier: + case TokType::Spec_Const: + case TokType::Type_Long: + case TokType::Type_Short: + case TokType::Type_Signed: + case TokType::Type_Unsigned: + case TokType::Type_bool: + case TokType::Type_char: + case TokType::Type_double: + case TokType::Type_int: + { + Code constructor_destructor = parse_global_nspace_constructor_destructor( specifiers ); + // Possible constructor implemented at global file scope. + if ( constructor_destructor ) + { + member = constructor_destructor; + break; + } + + bool found_operator_cast_outside_class_implmentation = false; + s32 idx = Context.Tokens.Idx; + + for ( ; idx < Context.Tokens.Arr.num(); idx++ ) + { + Token tok = Context.Tokens[ idx ]; + + if ( tok.Type == TokType::Identifier ) + { + idx++; + tok = Context.Tokens[ idx ]; + if ( tok.Type == TokType::Access_StaticSymbol ) + continue; + + break; + } + + if ( tok.Type == TokType::Decl_Operator ) + found_operator_cast_outside_class_implmentation = true; + + break; + } + + if ( found_operator_cast_outside_class_implmentation ) + { + member = parse_operator_cast( specifiers ); + // ::operator () { ... } + break; + } + + member = parse_operator_function_or_variable( expects_function, attributes, specifiers ); + // ... + } + } + + if ( member == Code::Invalid ) + { + log_failure( "Failed to parse member\n%s", Context.to_string() ); + Context.pop(); + return CodeInvalid; + } + + // log_fmt("Global Body Member: %s", member->debug_str()); + result.append( member ); + } + + if ( which != Global_Body ) + eat( TokType::BraceCurly_Close ); + // { } + + Context.pop(); + return result; +} + +internal inline +Code parse_global_nspace_constructor_destructor( CodeSpecifiers specifiers ) +{ + Code result = { nullptr }; + + /* + To check if a definition is for a constructor we can go straight to the opening parenthesis for its parameters + From There we work backwards to see if we come across two identifiers with the same name between an member access + :: operator, there can be template parameters on the left of the :: so we ignore those. + Whats important is that its back to back. + + This has multiple possible faults. What we parse using this method may not filter out if something has a "return type" + This is bad since technically you could have a namespace nested into another namespace with the same name. + If this awful pattern is done the only way to distiguish with this coarse parse is to know there is no return type defined. + + TODO(Ed): We could fix this by attempting to parse a type, but we would have to have a way to have it soft fail and rollback. + */ + TokArray tokens = Context.Tokens; + + s32 idx = tokens.Idx; + Token nav = tokens[ idx ]; + for ( ; idx < tokens.Arr.num(); idx++, nav = tokens[ idx ] ) + { + if ( nav.Text[0] == '<' ) + { + // Skip templated expressions as they mey have expressions with the () operators + s32 capture_level = 0; + s32 template_level = 0; + for ( ; idx < tokens.Arr.num(); idx++, nav = tokens[idx] ) + { + if (nav.Text[ 0 ] == '<') + ++ template_level; + + if (nav.Text[ 0 ] == '>') + -- template_level; + if (nav.Type == TokType::Operator && nav.Text[1] == '>') + -- template_level; + + if ( nav.Type == ETokType::Capture_Start) + { + if (template_level != 0 ) + ++ capture_level; + else + break; + } + + if ( template_level != 0 && nav.Type == ETokType::Capture_End) + -- capture_level; + } + } + + if ( nav.Type == TokType::Capture_Start ) + break; + } + + -- idx; + Token tok_right = tokens[idx]; + Token tok_left = NullToken; + + if (tok_right.Type != TokType::Identifier) + { + // We're not dealing with a constructor if there is no identifier right before the opening of a parameter's scope. + return result; + } + + -- idx; + tok_left = tokens[idx]; + // ... + + bool possible_destructor = false; + if ( tok_left.Type == TokType::Operator && tok_left.Text[0] == '~') + { + possible_destructor = true; + -- idx; + tok_left = tokens[idx]; + } + + if ( tok_left.Type != TokType::Access_StaticSymbol ) + return result; + + -- idx; + tok_left = tokens[idx]; + // ... :: + + // We search toward the left until we find the next valid identifier + s32 capture_level = 0; + s32 template_level = 0; + while ( idx != tokens.Idx ) + { + if (tok_left.Text[ 0 ] == '<') + ++ template_level; + + if (tok_left.Text[ 0 ] == '>') + -- template_level; + if (tok_left.Type == TokType::Operator && tok_left.Text[1] == '>') + -- template_level; + + if ( template_level != 0 && tok_left.Type == ETokType::Capture_Start) + ++ capture_level; + + if ( template_level != 0 && tok_left.Type == ETokType::Capture_End) + -- capture_level; + + if ( capture_level == 0 && template_level == 0 && tok_left.Type == TokType::Identifier ) + break; + + -- idx; + tok_left = tokens[idx]; + } + + bool is_same = str_compare( tok_right.Text, tok_left.Text, tok_right.Length ) == 0; + if (tok_left.Type == TokType::Identifier && is_same) + { + // We have found the pattern we desired + if (possible_destructor) + { + // :: ~ ( + result = parse_destructor( specifiers ); + } + else { + // :: ( + result = parse_constructor( specifiers ); + } + } + + return result; +} + +// TODO(Ed): I want to eventually change the identifier to its own AST type. +// This would allow distinction of the qualifier for a symbol :: +// This would also allow +internal +Token parse_identifier( bool* possible_member_function ) +{ + push_scope(); + + Token name = currtok; + Context.Scope->Name = name; + eat( TokType::Identifier ); + // + + parse_template_args( name ); + //