mirror of
https://github.com/Ed94/gencpp.git
synced 2025-06-15 03:01:47 -07:00
Docs update, parser impl design changes, AST::add fleshed out.
This commit is contained in:
@ -2,11 +2,6 @@
|
||||
#include "Bloat.hpp"
|
||||
|
||||
|
||||
namespace Global
|
||||
{
|
||||
bool ShouldShowDebug = false;
|
||||
}
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
using namespace zpl;
|
||||
@ -41,92 +36,3 @@ namespace Memory
|
||||
arena_free( & Global_Arena);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct TokEntry
|
||||
{
|
||||
char const* Str;
|
||||
sw Length;
|
||||
};
|
||||
|
||||
ZPL_TABLE( static, TokMap, tokmap_, TokEntry )
|
||||
|
||||
sw token_fmt_va( char* buf, uw buf_size, char const* fmt, s32 num_tokens, va_list va )
|
||||
{
|
||||
char const* buf_begin = buf;
|
||||
sw remaining = buf_size;
|
||||
|
||||
TokMap tok_map;
|
||||
{
|
||||
tokmap_init( & tok_map, g_allocator );
|
||||
|
||||
s32 left = num_tokens;
|
||||
|
||||
while ( left-- )
|
||||
{
|
||||
char const* token = va_arg( va, char const* );
|
||||
char const* value = va_arg( va, char const* );
|
||||
|
||||
TokEntry entry
|
||||
{
|
||||
value,
|
||||
str_len(value, (sw)128)
|
||||
};
|
||||
|
||||
u32 key = crc32( token, str_len(token, 32) );
|
||||
|
||||
tokmap_set( & tok_map, key, entry );
|
||||
}
|
||||
}
|
||||
|
||||
sw result = 0;
|
||||
char current = *fmt;
|
||||
|
||||
while ( current )
|
||||
{
|
||||
sw len = 0;
|
||||
|
||||
while ( current && current != '{' && remaining )
|
||||
{
|
||||
*buf = *fmt;
|
||||
buf++;
|
||||
fmt++;
|
||||
|
||||
current = *fmt;
|
||||
}
|
||||
|
||||
if ( current == '{' )
|
||||
{
|
||||
char const* scanner = fmt;
|
||||
|
||||
s32 tok_len = 0;
|
||||
|
||||
while ( *scanner != '}' )
|
||||
{
|
||||
tok_len++;
|
||||
scanner++;
|
||||
}
|
||||
|
||||
char const* token = fmt;
|
||||
|
||||
u32 key = crc32( token, tok_len );
|
||||
TokEntry value = * tokmap_get( & tok_map, key );
|
||||
sw left = value.Length;
|
||||
|
||||
while ( left-- )
|
||||
{
|
||||
*buf = *value.Str;
|
||||
buf++;
|
||||
value.Str++;
|
||||
}
|
||||
|
||||
scanner++;
|
||||
fmt = scanner;
|
||||
current = *fmt;
|
||||
}
|
||||
}
|
||||
|
||||
tokmap_clear( & tok_map );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -45,6 +45,8 @@ using zpl::u32;
|
||||
using zpl::u64;
|
||||
using zpl::uw;
|
||||
using zpl::sw;
|
||||
using zpl::sptr;
|
||||
using zpl::uptr;
|
||||
|
||||
using zpl::Arena;
|
||||
using zpl::AllocatorInfo;
|
||||
@ -62,6 +64,7 @@ using zpl::arena_allocator;
|
||||
using zpl::arena_init_from_memory;
|
||||
using zpl::arena_init_from_allocator;
|
||||
using zpl::arena_free;
|
||||
using zpl::assert_crash;
|
||||
using zpl::str_fmt_buf;
|
||||
using zpl::char_is_alpha;
|
||||
using zpl::char_is_alphanumeric;
|
||||
@ -102,7 +105,7 @@ using zpl::str_len;
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__) || true
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
// Supports 0-10 arguments
|
||||
#define macro_num_args_impl( _0, \
|
||||
_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
|
||||
@ -142,7 +145,6 @@ using zpl::str_len;
|
||||
|
||||
#define bit( Value_ ) ( 1 << Value_ )
|
||||
#define bitfield_is_equal( Field_, Mask_ ) ( ( (Mask_) & (Field_) ) == (Mask_) )
|
||||
#define ct constexpr
|
||||
#define forceinline ZPL_ALWAYS_INLINE
|
||||
#define print_nl( _) zpl_printf("\n")
|
||||
#define ccast( Type_, Value_ ) * const_cast< Type_* >( & (Value_) )
|
||||
@ -176,16 +178,12 @@ do \
|
||||
} \
|
||||
while(0);
|
||||
|
||||
ct char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED";
|
||||
|
||||
namespace Global
|
||||
{
|
||||
extern bool ShouldShowDebug;
|
||||
}
|
||||
constexpr
|
||||
char const* Msg_Invalid_Value = "INVALID VALUE PROVIDED";
|
||||
|
||||
namespace Memory
|
||||
{
|
||||
ct uw Initial_Reserve = megabytes(10);
|
||||
constexpr uw Initial_Reserve = megabytes(10);
|
||||
|
||||
extern Arena Global_Arena;
|
||||
// #define g_allocator arena_allocator( & Memory::Global_Arena)
|
||||
@ -198,28 +196,9 @@ namespace Memory
|
||||
void cleanup();
|
||||
}
|
||||
|
||||
sw token_fmt_va( char* buf, uw buf_size, char const* fmt, s32 num_tokens, va_list va );
|
||||
|
||||
inline
|
||||
char const* token_fmt( char const* fmt, sw num_tokens, ... )
|
||||
{
|
||||
local_persist thread_local
|
||||
char buf[ZPL_PRINTF_MAXLEN] = { 0 };
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
token_fmt_va(buf, ZPL_PRINTF_MAXLEN, fmt, num_tokens, va);
|
||||
va_end(va);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
inline
|
||||
sw log_fmt(char const *fmt, ...)
|
||||
{
|
||||
if ( Global::ShouldShowDebug == false )
|
||||
return 0;
|
||||
|
||||
sw res;
|
||||
va_list va;
|
||||
|
||||
|
@ -1,4 +0,0 @@
|
||||
/*
|
||||
Redefine any macros.
|
||||
Used if you have some code thats getting parsed and don't want it ot get preprocessed.
|
||||
*/
|
@ -1,18 +0,0 @@
|
||||
/*
|
||||
Remvoe any macro definitions related to the Bloat header.
|
||||
*/
|
||||
|
||||
#undef bit
|
||||
#undef bitfield_is_equal
|
||||
#undef ct
|
||||
#undef forceinline
|
||||
#undef print_nl
|
||||
#undef scast
|
||||
#undef rcast
|
||||
#undef pcast
|
||||
#undef txt
|
||||
|
||||
#undef do_once
|
||||
#undef do_once_start
|
||||
#undef do_once_end
|
||||
|
87
project/Readme.md
Normal file
87
project/Readme.md
Normal file
@ -0,0 +1,87 @@
|
||||
# Documentation
|
||||
|
||||
This library is currently in a bootstrapping phase.
|
||||
|
||||
Eventually it will have zero dependencies and have its code size severely constricted.
|
||||
|
||||
All dependencies are currently held within `Bloat.hpp` and `Bloat.cpp`
|
||||
|
||||
All the library code is contained in two files: `gen.hpp` and `gen.cpp`
|
||||
|
||||
|
||||
## Bloat.hpp/cpp
|
||||
|
||||
Currently acts as the isolation header for thridparty dependencies along with code not directly related to the library.
|
||||
|
||||
Organization:
|
||||
|
||||
* ZPL inclusion and selective symbol exposure to global scope.
|
||||
* Utility macro definitions used throughout the library.
|
||||
* Global memory arena definition
|
||||
* Token string formatter
|
||||
* Formatted and Fatal Logs
|
||||
|
||||
The cpp contains the implementation of the global memory arena and the token formmatter.
|
||||
|
||||
Any global symbol pollution will be removed when dependencies are intergrated properly into the library.
|
||||
|
||||
## gen.hpp
|
||||
|
||||
While getting fleshed out, all feature macros are defined on the top of the header.
|
||||
|
||||
These macros are:
|
||||
|
||||
* `GEN_DEFINE_DSL` : Define the preprocessor DSL for using the library interface
|
||||
* `GEN_DEFINE_LIBRARY_CORE_CONSTANTS` : Optional typename codes as they are non-standard to C/C++ and not necessary to library usage
|
||||
* `GEN_ENCORCE_READONLY_AST` : Defines checks in Code when accessing the AST to make sure readonly marked ASTs are not mutated
|
||||
* `GEN_FEATURE_INCREMENTAL` : Defines the incremental constructors
|
||||
* `GEN_FEATURE_PARSING` : Defines the parse constructors
|
||||
* `GEN_FEATURE_EDITOR` : Defines the file editing features for changing definitions based on ASTs
|
||||
* `GEN_FEATURE_SCANNER` : Defines the file scanning features for generating ASTs
|
||||
|
||||
Due to the design of `gen.hpp` to support being written alongside runtime intended code (in the same file), all the code is wrapped in a `gen_time` `#ifdef` and then wrapped further in a `gen` namespace to avoid pollution of the global scope.
|
||||
|
||||
*Note: Its possible with the scanner feature to support parsing runtime files that use "generic" macros or identifiers with certain patterns.
|
||||
This can be used to auto-queue generation of dependent definitions for the symbols used.*
|
||||
|
||||
### Organization
|
||||
|
||||
log_failure definition : based on whether to always use fatal on all errors
|
||||
|
||||
Major enum definitions and their associated functions used with the AST data
|
||||
|
||||
* `ECode` : Used to tag ASTs by their type
|
||||
* `EOperator` : Used to tag operator overloads with thier op type
|
||||
* `ESpecifier` : Used with specifier ASTs for all specifiers the user may tag an associated AST with.
|
||||
|
||||
#### Data Structures
|
||||
|
||||
`StringTable` : Hash table for cached strings. (`StringCached` typedef used to denote strings managed by it)
|
||||
|
||||
`AST` : The node data strucuture for the code.
|
||||
`Code` : Wrapper for `AST` with functionality for handling it appropriately.
|
||||
|
||||
`TypeTable` : Hash table for cached typename ASTs.
|
||||
|
||||
#### Gen Interface
|
||||
|
||||
First set of fowards are either backend functions used for various aspects of AST generation or configurating allocators used for different containers.
|
||||
|
||||
Interface fowards defined in order of: Upfront, Incremental, Parsing, Untyped.
|
||||
|
||||
From there forwards for the File handlers are defined: Builder, Editor, Scanner.
|
||||
|
||||
#### Macros
|
||||
|
||||
General helper macros are defined along with the optional DSL macros.
|
||||
|
||||
#### Constants
|
||||
|
||||
Constants including optional ones are defined.
|
||||
|
||||
#### Inlines
|
||||
|
||||
Inlined functions related to the AST datatype that required forwards for gen interface functions are defined.
|
||||
|
||||
## gen.cpp
|
||||
|
1944
project/gen.cpp
1944
project/gen.cpp
File diff suppressed because it is too large
Load Diff
617
project/gen.hpp
617
project/gen.hpp
@ -1,384 +1,16 @@
|
||||
/*
|
||||
gencpp: A simple staged metaprogramming library for C++.
|
||||
gencpp: An attempt at simple staged metaprogramming for c/c++.
|
||||
|
||||
The library is mostly a compositon of code element constructors.
|
||||
These build up a code AST to then serialize with a file builder.
|
||||
See Readme.md for more information from the project repository.
|
||||
|
||||
This library is intended for small-to midsize projects.
|
||||
|
||||
AST type checking supports only a small subset of c++.
|
||||
See the 'ECode' namespace and 'gen API' region to see what is supported.
|
||||
|
||||
### *WHAT IS NOT PROVIDED*
|
||||
|
||||
This library aims to be used in a "orthodox" or minimal C++ workspace.
|
||||
|
||||
* Macro or template generation : This library is to avoid those, adding support for them adds unnecessary complexity.
|
||||
* Vendor provided dynamic dispatch (virtuals) : Roll your own, this library might roll its own vtable/interface generation helpers in the future.
|
||||
* RTTI
|
||||
* Exceptions
|
||||
* Execution statement validation : Execution expressions are defined using the untyped string API.
|
||||
|
||||
Keywords in from "Modern C++":
|
||||
Specifiers:
|
||||
* constexpr : Great to store compile-time constants, (easier to garanteed when emitted from gentime)
|
||||
* consteval : Technically fine so long as templates are not used. Need to make sure to execute in moderation.
|
||||
* constinit : Better than constexpr at doing its job, however, its only c++ 20.
|
||||
* export : Useful if c++ modules ever come around to actually being usable.
|
||||
* import : ^^
|
||||
* module : ^^
|
||||
|
||||
These features are in as they either are not horrible when used conservatively or are a performance benefit (modules).
|
||||
|
||||
#### When it comes to expressions:
|
||||
|
||||
There is no support for validating expressions.
|
||||
The reason: thats where the can of worms open for parsing validation. This library would most likey more than double in size with that addition alone.
|
||||
For most metaprogramming (espcially for C/C++), expression validation is not necessary for metaprogramming, it can be done by the compiler for the runtime program.
|
||||
Most of the time, the critical complex metaprogramming conundrums are actaully producing the frame of abstractions around the expressions.
|
||||
Thus its not very much a priority to add such a level of complexity to the library when there isn't a high reward or need for it.
|
||||
|
||||
To further this point, lets say you do have an error with an expressions composition.
|
||||
It will either be caught by the c++ compiler when compiling the target program, or at runtime for the program.
|
||||
|
||||
* If its not caught by the compiler, the only downside is the error appers on the generated function.
|
||||
Those with knowledge of how that definition was generated know where to find the code that inlined that expression in that file for that definition.
|
||||
* If its caught at runtime. The expression will be shown in a stack trace if debug symbols are enabled in the generated function body.
|
||||
Yet again those with knowledge of how that definition was generated know where to find the code that inlined that expression.
|
||||
|
||||
In both these cases the user will get objectively better debug information than you would normally get on most c++ compilers/editors using complex macros or templates.
|
||||
|
||||
### The Data & Interface:
|
||||
|
||||
The AST is managed by the library and provided the user via its interface prodedures.
|
||||
|
||||
Notes:
|
||||
|
||||
* The allocator definitions used are exposed to the user incase they want to dictate memory usage
|
||||
* You'll find the memory handling in `init`, `gen_string_allocator`, `get_cached_string`, `make_code`, and `make_code_entries`.
|
||||
* ASTs are wrapped for the user in a Code struct which essentially a warpper for a AST* type.
|
||||
* Both AST and Code have member symbols but their data layout is enforced to be POD types.
|
||||
* This library treats memory failures as fatal.
|
||||
* The AST's data union is can either hold a static array of AST*'s, a dynmaic array if the the static capacity is not enough, or a cached string content.
|
||||
* The dynamic array is allocated to arenas currently and are pretty wasteful if they go over their reserve size (its never recycled).
|
||||
|
||||
Data layout of AST struct:
|
||||
|
||||
union {
|
||||
AST* ArrStatic[ArrS_Cap];
|
||||
Array(AST*) Entries;
|
||||
StringCached Content;
|
||||
|
||||
};
|
||||
AST* Parent;
|
||||
StringCached Name;
|
||||
StringCached Comment;
|
||||
CodeT Type;
|
||||
OperatorT Op;
|
||||
bool Readonly;
|
||||
bool DynamicEntries;
|
||||
u8 StaticIndex;
|
||||
u8 _Align_Pad[6];
|
||||
|
||||
*`CodeT` is a typedef for `ECode::Type` which is the type of the enum.*
|
||||
|
||||
AST widths are setup to be AST_POD_Size.
|
||||
The width dictates how much the static array can hold before it must give way to using an allocated array:
|
||||
constexpr static
|
||||
u32 ArrS_Cap =
|
||||
( AST_POD_Size
|
||||
- sizeof(AST*)
|
||||
- sizeof(StringCached) * 2
|
||||
- sizeof(CodeT)
|
||||
- sizeof(OperatorT)
|
||||
- sizeof(bool) * 2
|
||||
- sizeof(u8) * 7 )
|
||||
/ sizeof(AST*);
|
||||
|
||||
Ex: If the AST_POD_Size is 256 the capacity of the static array is 26.
|
||||
|
||||
ASTs can be set to readonly by calling Code's lock() member function.
|
||||
Adding comments is always available even if the AST is set to readonly.
|
||||
|
||||
#### Misc
|
||||
|
||||
* The allocator definitions used are exposed to the user incase they want to dictate memory usage
|
||||
* You'll find the memory handling in `init`, `gen_string_allocator`, `get_cached_string`, `make_code`, and `make_code_entries`.
|
||||
* ASTs are wrapped for the user in a Code struct which essentially a warpper for a AST* type.
|
||||
* Both AST and Code have member symbols but their data layout is enforced to be POD types.
|
||||
* This library treats memory failures as fatal.
|
||||
* The default setup assumes large definition sets may be provided to bodies so AST::Entires are dynamic arrays.
|
||||
* They're allocated to arenas currently and are pretty wasteful if they go over their reserve size (its never recycled).
|
||||
* Most likely will need to implement a dynamic-sized bucket allocation strategy for the entry arrays if memory is getting stressed.
|
||||
* Otherwise if you are using fixed size entries and your definitions are under 128~512 entries for the body, you may be better of with a fixed-sized array.
|
||||
* Strings are stored in their own set of arenas. AST constructors use cached strings for names, and content.
|
||||
|
||||
### There are four sets of interfaces for Code AST generation the library provides
|
||||
|
||||
* Upfront
|
||||
* Incremental
|
||||
* Parsing
|
||||
* Untyped
|
||||
|
||||
### Upfront Construction
|
||||
|
||||
All component ASTs must be previously constructed, and provided on creation of the code AST.
|
||||
The construction will fail and return InvalidCode otherwise.
|
||||
|
||||
Interface :
|
||||
|
||||
* def_class
|
||||
* def_enum
|
||||
* def_execution NOTE: This is equivalent to untyped_str, except that its intended for use only in execution scopes.
|
||||
* def_friend
|
||||
* def_function
|
||||
* def_namespace
|
||||
* def_operator
|
||||
* def_param
|
||||
* def_params
|
||||
* def_specifier
|
||||
* def_specifiers
|
||||
* def_struct
|
||||
* def_variable
|
||||
* def_type
|
||||
* def_typedef
|
||||
* def_using
|
||||
|
||||
* def_class_body
|
||||
* def_enum_body
|
||||
* def_function_body NOTE: Use this for operator bodies as well.
|
||||
* def_global_body
|
||||
* def_namespace_body
|
||||
* def_struct_body
|
||||
|
||||
Usage:
|
||||
Code <name> = def_<function type>( ... );
|
||||
|
||||
Code <name>
|
||||
{
|
||||
...
|
||||
<name> = def_<function name>( ... );
|
||||
}
|
||||
|
||||
### Incremental construction
|
||||
|
||||
A Code ast is provided but only completed upfront if all components are provided.
|
||||
Components are then added using the AST API for adding ASTs:
|
||||
|
||||
* code.add( AST* ) // Adds AST with validation.
|
||||
* code.add_entry( AST* ) // Adds AST entry without validation.
|
||||
|
||||
Code ASTs may be explictly validated at anytime using Code's check() member function.
|
||||
|
||||
Interface :
|
||||
|
||||
* make_class
|
||||
* make_enum
|
||||
* make_enum_class
|
||||
* make_function
|
||||
* make_global_body
|
||||
* make_namespace
|
||||
* make_operator
|
||||
* make_params
|
||||
* make_specifiers
|
||||
* make_struct
|
||||
|
||||
Usage:
|
||||
Code <name> = make_<function name>( ... )
|
||||
{
|
||||
<name>->add( ... );
|
||||
...
|
||||
}
|
||||
|
||||
### Parse construction
|
||||
|
||||
A string provided to the API is parsed for the intended language construct.
|
||||
|
||||
Interface :
|
||||
|
||||
* parse_class
|
||||
* parse_enum
|
||||
* parse_friend
|
||||
* parse_function
|
||||
* parse_global_body
|
||||
* parse_namespace
|
||||
* parse_operator
|
||||
* parse_struct
|
||||
* parse_strucs
|
||||
* parse_variable
|
||||
* parse_type
|
||||
* parse_typedef
|
||||
* parse_using
|
||||
|
||||
* parse_classes
|
||||
* parse_enums
|
||||
* parse_functions
|
||||
* parse_namespaces
|
||||
* parse_operators
|
||||
* parse_variables
|
||||
* parse_typedefs
|
||||
* parse_usings
|
||||
|
||||
The parse API treats any execution scope definitions with no validation and are turned into untyped Code ASTs.
|
||||
This includes the assignmetn of variables; due to the library not yet supporting c/c++ expression parsing.
|
||||
|
||||
The pluralvariants provide an array of codes, its up to the user to add them to a body AST
|
||||
(they are not auto-added to a body)
|
||||
|
||||
Usage:
|
||||
Code <name> = parse_<function name>( string with code );
|
||||
|
||||
Code <name> = def_<function name>( ..., parse_<function name>(
|
||||
<string with code>
|
||||
));
|
||||
|
||||
Code <name> = make_<function name>( ... )
|
||||
{
|
||||
<name>->add( parse_<function name>(
|
||||
<string with code>
|
||||
));
|
||||
}
|
||||
|
||||
### Untyped constructions
|
||||
|
||||
Code ASTs are constructed using unvalidated strings.
|
||||
|
||||
Interface :
|
||||
|
||||
* token_fmt
|
||||
* untyped_str
|
||||
* untyped_fmt
|
||||
* untyped_token_fmt
|
||||
|
||||
During serialization any untyped Code AST is has its string value directly injected inline of
|
||||
whatever context the content existed as an entry within.
|
||||
Even though thesee are not validated from somewhat correct c/c++ syntax or components, it doesn't mean that
|
||||
Untyped code can be added as any component of a Code AST:
|
||||
|
||||
* Untyped code cannot have children, thus there cannot be recursive injection this way.
|
||||
* Untyped code can only be a child of a parent of body AST, or for values of an assignment (ex: variable assignment).
|
||||
|
||||
These restrictions help prevent abuse of untyped code to some extent.
|
||||
|
||||
Usage Conventions:
|
||||
```
|
||||
Code <name> = def_varaible( <type>, <name>, untyped_<function name>(
|
||||
<string with code>
|
||||
));
|
||||
```
|
||||
|
||||
Template metaprogramming in the traditional sense becomes possible with the use of `token_fmt` and parse constructors:
|
||||
|
||||
```
|
||||
char const* token_key, token_value;
|
||||
char const* template = txt(
|
||||
Code with {key value} to replace with token_values
|
||||
...
|
||||
);
|
||||
char const* gen_code_str = token_fmt( template, num_tokens, token, ... );
|
||||
Code <name> = parse_<function name>( gen_code_str );
|
||||
```
|
||||
|
||||
## Extent of operator overload validation:
|
||||
|
||||
The AST and constructors will be able to validate that the arguments provided for the operator type match the expected form:
|
||||
* If return type must match a parameter
|
||||
* If number of parameters is correct
|
||||
* If added as a member symbol to a class or struct, that operator matches the requirements for the class (types match up)
|
||||
|
||||
The user is responsible for making sure the code types provided are correct
|
||||
and have the desired specifiers assigned to them beforehand.
|
||||
|
||||
## Code generation and modification
|
||||
|
||||
There are three provided interfaces:
|
||||
* Builder
|
||||
* Editor
|
||||
* Scanner
|
||||
|
||||
Editor and Scanner are disabled by default, use GEN_FEATURE_EDITOR and GEN_FEATURE_SCANNER to enable them.
|
||||
|
||||
### Builder is a similar object to the jai language's string_builder.
|
||||
* The purpose of it is to generate a file.
|
||||
* A file is specified and opened for writting using the open( file_path) ) fucntion.
|
||||
* The code is provided via print( code ) function will be seralized to its buffer.
|
||||
* When all seralization is finished, use the write() comamnd to write the buffer to the file.
|
||||
|
||||
### Editor is for editing a series of files based on a set of requests provided to it.
|
||||
* The purpose is to overrite a specific file, it places its contents in a buffer to scan.
|
||||
* Requests are populated using the following interface:
|
||||
* add : Add code.
|
||||
* remove : Remove code.
|
||||
* replace: Replace code.
|
||||
|
||||
All three have the same parameters with exception to remove which only has SymbolInfo and Policy:
|
||||
* SymbolInfo:
|
||||
* File : The file the symbol resides in.
|
||||
Leave null to indicate to search all files.
|
||||
* Marker : #define symbol that indicates a location or following signature is valid to manipulate.
|
||||
Leave null to indicate that the signature should only be used.
|
||||
* Signature : Use a Code symbol to find a valid location to manipulate, can be further filtered with the marker.
|
||||
Leave null to indicate that the marker should only be used.
|
||||
|
||||
* Policy : Additional policy info for completing the request (empty for now)
|
||||
* Code : Code to inject if adding, or replace existing code with.
|
||||
|
||||
Additionally if GEN_FEATURE_EDITOR_REFACTOR is defined, refactor( file_path, specification_path ) wil be made available.
|
||||
Refactor is based of the refactor library and uses its interface.
|
||||
It will on call add a request to the queue to run the refactor script on the file.
|
||||
|
||||
### Scanner allows the user to generate Code ASTs by reading files.
|
||||
* The purpose is to grab definitions to generate metadata or generate new code from these definitions.
|
||||
* Requests are populated using the add( SymbolInfo, Policy ) function.
|
||||
The symbol info is the same as the one used for the editor. So is the case with Policy.
|
||||
|
||||
The file will only be read from, no writting supported.
|
||||
|
||||
One great use case is for example: generating the single-header library for gencpp!
|
||||
|
||||
### Additional Info (Editor and Scanner)
|
||||
|
||||
When all requests have been populated, call process_requests().
|
||||
It will provide an output of receipt data of the results when it completes.
|
||||
|
||||
Files may be added to the Editor and Scanner additionally with add_files( num, files ).
|
||||
This is intended for when you have requests that are for multiple files.
|
||||
|
||||
Request queue in both Editor and Scanner are cleared once process_requests completes.
|
||||
|
||||
### On multi-threading:
|
||||
|
||||
Currently supported but want to. The following changes would have to be made:
|
||||
* Setup static data accesss with fences if more than one thread will generate ASTs ( or keep a different set for each thread)
|
||||
* Make sure local peristent data of functions are also thread local.
|
||||
* The builder should be done on a per-thread basis.
|
||||
* Due to the design of the editor and scanner, it will most likely
|
||||
be best to make each file a job to process request entries on.
|
||||
Receipts should have an an array to store per thread.
|
||||
They can be combined to the final reciepts array when all files have been processed.
|
||||
|
||||
For now single-threaded has a bunch of optimization that most likely have done to it and will be more than capable
|
||||
for the majority of projects this thing is intended for. (IF you use this on Unreal... well your asking for it...)
|
||||
|
||||
### Notes on extending with whatever features you want
|
||||
|
||||
This library is very small, and you can easily extend it.
|
||||
|
||||
The untyped codes and builder/editor/scanner can be technically be used to circumvent
|
||||
any sort of constrictions the library has with: modern c++, templates, macros, etc.
|
||||
|
||||
Typical use case is for getting define constants an old C/C++ library with the scanner:
|
||||
Code parse_defines() can emit a custom code AST with Macro_Constant type.
|
||||
|
||||
Another would be getting preprocessor or template metaprogramming Codes from Unreal Engine definitions.
|
||||
Public Address:
|
||||
https://github.com/Ed94/gencpp
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Bloat.hpp"
|
||||
|
||||
// Temporarily here for debugging purposes.
|
||||
#define GEN_BAN_CPP_TEMPLATES
|
||||
#define GEN_DEFINE_DSL
|
||||
#define GEN_DEFINE_LIBRARY_CODE_CONSTANTS
|
||||
// #define GEN_DONT_USE_FATAL
|
||||
@ -395,21 +27,17 @@ namespace gen
|
||||
{
|
||||
using LogFailType = sw(*)(char const*, ...);
|
||||
|
||||
# ifdef GEN_BAN_CPP_TEMPLATES
|
||||
# define template static_assert("Templates are banned within gen_time scope blocks")
|
||||
# endif
|
||||
|
||||
// By default this library will either crash or exit if an error is detected while generating codes.
|
||||
// Even if set to not use fatal, fatal will still be used for memory failures as the library is unusable when they occur.
|
||||
# ifdef GEN_DONT_USE_FATAL
|
||||
ct LogFailType log_failure = log_fmt;
|
||||
constexpr LogFailType log_failure = log_fmt;
|
||||
# else
|
||||
ct LogFailType log_failure = fatal;
|
||||
constexpr LogFailType log_failure = fatal;
|
||||
# endif
|
||||
|
||||
namespace ECode
|
||||
{
|
||||
# define Define_Types \
|
||||
# define Define_Types \
|
||||
Entry( Untyped ) \
|
||||
Entry( Access_Public ) \
|
||||
Entry( Access_Protected ) \
|
||||
@ -448,9 +76,9 @@ namespace gen
|
||||
|
||||
enum Type : u32
|
||||
{
|
||||
# define Entry( Type ) Type,
|
||||
# define Entry( Type ) Type,
|
||||
Define_Types
|
||||
# undef Entry
|
||||
# undef Entry
|
||||
|
||||
Num_Types,
|
||||
Invalid
|
||||
@ -461,15 +89,15 @@ namespace gen
|
||||
{
|
||||
static
|
||||
char const* lookup[Num_Types] = {
|
||||
# define Entry( Type ) txt( Type ),
|
||||
# define Entry( Type ) txt( Type ),
|
||||
Define_Types
|
||||
# undef Entry
|
||||
# undef Entry
|
||||
};
|
||||
|
||||
return lookup[ type ];
|
||||
}
|
||||
|
||||
# undef Define_Types
|
||||
#undef Define_Types
|
||||
}
|
||||
using CodeT = ECode::Type;
|
||||
|
||||
@ -480,8 +108,8 @@ namespace gen
|
||||
Class
|
||||
};
|
||||
|
||||
ct EnumT EnumClass = EnumT::Class;
|
||||
ct EnumT EnumRegular = EnumT::Regular;
|
||||
constexpr EnumT EnumClass = EnumT::Class;
|
||||
constexpr EnumT EnumRegular = EnumT::Regular;
|
||||
|
||||
enum class UsingT : u8
|
||||
{
|
||||
@ -489,59 +117,59 @@ namespace gen
|
||||
Namespace
|
||||
};
|
||||
|
||||
ct UsingT UsingRegular = UsingT::Regular;
|
||||
ct UsingT UsingNamespace = UsingT::Namespace;
|
||||
constexpr UsingT UsingRegular = UsingT::Regular;
|
||||
constexpr UsingT UsingNamespace = UsingT::Namespace;
|
||||
|
||||
namespace EOperator
|
||||
{
|
||||
# define Define_Operators \
|
||||
Entry( Assign, = ) \
|
||||
Entry( Assign_Add, += ) \
|
||||
Entry( Assign_Subtract, -= ) \
|
||||
Entry( Assgin_Multiply, *= ) \
|
||||
Entry( Assgin_Divide, /= ) \
|
||||
Entry( Assgin_Modulo, %= ) \
|
||||
Entry( Assgin_BAnd, &= ) \
|
||||
Entry( Assgin_BOr, |= ) \
|
||||
Entry( Assign_BXOr, ^= ) \
|
||||
Entry( Assign_LShift, <<= ) \
|
||||
Entry( Assign_RShift, >>= ) \
|
||||
Entry( Increment, ++ ) \
|
||||
Entry( Decrement, -- ) \
|
||||
Entry( Unary_Plus, + ) \
|
||||
Entry( Unary_Minus, - ) \
|
||||
Entry( Add, + ) \
|
||||
Entry( Subtract, - ) \
|
||||
Entry( Multiply, * ) \
|
||||
Entry( Divide, / ) \
|
||||
Entry( Modulo, % ) \
|
||||
Entry( BNot, ~ ) \
|
||||
Entry( BAnd, & ) \
|
||||
Entry( BOr, | ) \
|
||||
Entry( BXOr, ^ ) \
|
||||
Entry( LShift, << ) \
|
||||
Entry( RShift, >> ) \
|
||||
Entry( LNot, ! ) \
|
||||
Entry( LAnd, && ) \
|
||||
Entry( LOr, || ) \
|
||||
Entry( Equals, == ) \
|
||||
Entry( NotEquals, != ) \
|
||||
Entry( Lesser, < ) \
|
||||
Entry( Greater, > ) \
|
||||
Entry( LesserEqual, <= ) \
|
||||
Entry( GreaterEqual, >= ) \
|
||||
Entry( Subscript, [] ) \
|
||||
Entry( Indirection, * ) \
|
||||
Entry( AddressOf, & ) \
|
||||
Entry( MemberOfPointer, -> ) \
|
||||
Entry( PtrToMemOfPtr, ->* ) \
|
||||
Entry( FunctionCall, () )
|
||||
# define Define_Operators \
|
||||
Entry( Assign, = ) \
|
||||
Entry( Assign_Add, += ) \
|
||||
Entry( Assign_Subtract, -= ) \
|
||||
Entry( Assgin_Multiply, *= ) \
|
||||
Entry( Assgin_Divide, /= ) \
|
||||
Entry( Assgin_Modulo, %= ) \
|
||||
Entry( Assgin_BAnd, &= ) \
|
||||
Entry( Assgin_BOr, |= ) \
|
||||
Entry( Assign_BXOr, ^= ) \
|
||||
Entry( Assign_LShift, <<= ) \
|
||||
Entry( Assign_RShift, >>= ) \
|
||||
Entry( Increment, ++ ) \
|
||||
Entry( Decrement, -- ) \
|
||||
Entry( Unary_Plus, + ) \
|
||||
Entry( Unary_Minus, - ) \
|
||||
Entry( Add, + ) \
|
||||
Entry( Subtract, - ) \
|
||||
Entry( Multiply, * ) \
|
||||
Entry( Divide, / ) \
|
||||
Entry( Modulo, % ) \
|
||||
Entry( BNot, ~ ) \
|
||||
Entry( BAnd, & ) \
|
||||
Entry( BOr, | ) \
|
||||
Entry( BXOr, ^ ) \
|
||||
Entry( LShift, << ) \
|
||||
Entry( RShift, >> ) \
|
||||
Entry( LNot, ! ) \
|
||||
Entry( LAnd, && ) \
|
||||
Entry( LOr, || ) \
|
||||
Entry( Equals, == ) \
|
||||
Entry( NotEquals, != ) \
|
||||
Entry( Lesser, < ) \
|
||||
Entry( Greater, > ) \
|
||||
Entry( LesserEqual, <= ) \
|
||||
Entry( GreaterEqual, >= ) \
|
||||
Entry( Subscript, [] ) \
|
||||
Entry( Indirection, * ) \
|
||||
Entry( AddressOf, & ) \
|
||||
Entry( MemberOfPointer, -> ) \
|
||||
Entry( PtrToMemOfPtr, ->* ) \
|
||||
Entry( FunctionCall, () )
|
||||
|
||||
enum Type : u32
|
||||
{
|
||||
# define Entry( Type_, Token_ ) Type_,
|
||||
# define Entry( Type_, Token_ ) Type_,
|
||||
Define_Operators
|
||||
# undef Entry
|
||||
# undef Entry
|
||||
Comma,
|
||||
|
||||
Num_Ops,
|
||||
@ -556,32 +184,32 @@ namespace gen
|
||||
|
||||
local_persist
|
||||
char const* lookup[ Num_Ops ] = {
|
||||
# define Entry( Type_, Token_ ) txt(Token_),
|
||||
# define Entry( Type_, Token_ ) txt(Token_),
|
||||
Define_Operators
|
||||
# undef Entry
|
||||
# undef Entry
|
||||
","
|
||||
};
|
||||
|
||||
return lookup[ op ];
|
||||
}
|
||||
|
||||
# undef Define_Operators
|
||||
#undef Define_Operators
|
||||
}
|
||||
using OperatorT = EOperator::Type;
|
||||
|
||||
namespace ESpecifier
|
||||
{
|
||||
# if defined(ZPL_SYSTEM_WINDOWS)
|
||||
# define API_Export_Code __declspec(dllexport)
|
||||
# define API_Import_Code __declspec(dllimport)
|
||||
# define API_Keyword __declspec
|
||||
# elif defined(ZPL_SYSTEM_MACOS)
|
||||
# define API_Export_Code __attribute__ ((visibility ("default")))
|
||||
# define API_Import_Code __attribute__ ((visibility ("default")))
|
||||
# define API_Keyword __attribute__
|
||||
# endif
|
||||
#if defined(ZPL_SYSTEM_WINDOWS)
|
||||
# define API_Export_Code __declspec(dllexport)
|
||||
# define API_Import_Code __declspec(dllimport)
|
||||
# define API_Keyword __declspec
|
||||
#elif defined(ZPL_SYSTEM_MACOS)
|
||||
# define API_Export_Code __attribute__ ((visibility ("default")))
|
||||
# define API_Import_Code __attribute__ ((visibility ("default")))
|
||||
# define API_Keyword __attribute__
|
||||
#endif
|
||||
|
||||
# define Define_Specifiers \
|
||||
#define Define_Specifiers \
|
||||
Entry( API_Import, API_Export_Code ) \
|
||||
Entry( API_Export, API_Import_Code ) \
|
||||
Entry( Attribute, "You cannot stringize an attribute this way" ) \
|
||||
@ -614,9 +242,9 @@ namespace gen
|
||||
|
||||
enum Type : u32
|
||||
{
|
||||
# define Entry( Specifier, Code ) Specifier,
|
||||
# define Entry( Specifier, Code ) Specifier,
|
||||
Define_Specifiers
|
||||
# undef Entry
|
||||
# undef Entry
|
||||
|
||||
Num_Specifiers,
|
||||
Invalid,
|
||||
@ -628,9 +256,9 @@ namespace gen
|
||||
{
|
||||
local_persist
|
||||
char const* lookup[ Num_Specifiers ] = {
|
||||
# define Entry( Spec_, Code_ ) txt(Code_),
|
||||
# define Entry( Spec_, Code_ ) txt(Code_),
|
||||
Define_Specifiers
|
||||
# undef Entry
|
||||
# undef Entry
|
||||
};
|
||||
|
||||
return lookup[ specifier ];
|
||||
@ -661,7 +289,7 @@ namespace gen
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
# undef Define_Specifiers
|
||||
#undef Define_Specifiers
|
||||
}
|
||||
using SpecifierT = ESpecifier::Type;
|
||||
|
||||
@ -674,7 +302,7 @@ namespace gen
|
||||
using StringCached = char const*;
|
||||
|
||||
// Desired width of the AST data structure.
|
||||
ct u32 AST_POD_Size = 256;
|
||||
constexpr u32 AST_POD_Size = 256;
|
||||
|
||||
// TODO: If perf needs it, convert layout an SOA format.
|
||||
/*
|
||||
@ -688,7 +316,7 @@ namespace gen
|
||||
*/
|
||||
struct AST
|
||||
{
|
||||
# pragma region Member Functions
|
||||
#pragma region Member Functions
|
||||
|
||||
// Used with incremental constructors
|
||||
// Adds and checks entries to see if they are valid additions the type of ast.
|
||||
@ -702,7 +330,6 @@ namespace gen
|
||||
return Entries[0];
|
||||
}
|
||||
|
||||
forceinline
|
||||
bool check();
|
||||
|
||||
AST* duplicate();
|
||||
@ -784,13 +411,13 @@ namespace gen
|
||||
forceinline
|
||||
bool typename_is_ptr()
|
||||
{
|
||||
zpl::assert_crash("not implemented");
|
||||
assert_crash("not implemented");
|
||||
}
|
||||
|
||||
forceinline
|
||||
bool typename_is_ref()
|
||||
{
|
||||
zpl::assert_crash("not implemented");
|
||||
assert_crash("not implemented");
|
||||
}
|
||||
|
||||
forceinline
|
||||
@ -833,7 +460,8 @@ namespace gen
|
||||
}
|
||||
|
||||
String to_string() const;
|
||||
# pragma endregion Member Functions
|
||||
#pragma endregion Member Functions
|
||||
|
||||
constexpr static
|
||||
uw ArrS_Cap =
|
||||
( AST_POD_Size
|
||||
@ -848,7 +476,7 @@ namespace gen
|
||||
constexpr static
|
||||
uw ArrSpecs_Cap = ArrS_Cap * (sizeof(AST*) / sizeof(SpecifierT));
|
||||
|
||||
# define Using_Code_POD \
|
||||
# define Using_Code_POD \
|
||||
union { \
|
||||
AST* ArrStatic[AST::ArrS_Cap]; \
|
||||
Array(AST*) Entries; \
|
||||
@ -871,11 +499,11 @@ namespace gen
|
||||
struct CodePOD
|
||||
{
|
||||
Using_Code_POD
|
||||
# undef Using_CodePOD;
|
||||
# undef Using_CodePOD;
|
||||
};
|
||||
|
||||
ct sw size_AST = sizeof(AST);
|
||||
ct sw size_POD = sizeof(CodePOD);
|
||||
constexpr sw size_AST = sizeof(AST);
|
||||
constexpr sw size_POD = sizeof(CodePOD);
|
||||
|
||||
// Its intended for the AST to have equivalent size to its POD.
|
||||
// All extra functionality within the AST namespace should just be syntatic sugar.
|
||||
@ -892,12 +520,13 @@ namespace gen
|
||||
*/
|
||||
struct Code
|
||||
{
|
||||
# pragma region Statics
|
||||
#pragma region Statics
|
||||
// Used internally for the most part to identify invaidly generated code.
|
||||
static Code Invalid;
|
||||
# pragma endregion Statics
|
||||
#pragma endregion Statics
|
||||
|
||||
# pragma region Member Functions
|
||||
#pragma region Member Functions
|
||||
inline
|
||||
Code body()
|
||||
{
|
||||
if ( ast == nullptr )
|
||||
@ -1006,14 +635,14 @@ namespace gen
|
||||
|
||||
return ast;
|
||||
}
|
||||
# pragma endregion Member Functions
|
||||
#pragma endregion Member Functions
|
||||
|
||||
AST* ast;
|
||||
};
|
||||
static_assert( sizeof(Code) == sizeof(AST*), "ERROR: Code is not POD" );
|
||||
|
||||
// Used when the its desired when omission is allowed in a definition.
|
||||
ct Code NoCode = { nullptr };
|
||||
constexpr Code NoCode = { nullptr };
|
||||
// extern const Code InvalidCode;
|
||||
|
||||
/*
|
||||
@ -1117,8 +746,8 @@ namespace gen
|
||||
# endif
|
||||
# pragma endregion Incremental
|
||||
|
||||
# pragma region Parsing
|
||||
# ifdef GEN_FEATURE_PARSING
|
||||
#pragma region Parsing
|
||||
#ifdef GEN_FEATURE_PARSING
|
||||
Code parse_class ( s32 length, char const* class_def );
|
||||
Code parse_enum ( s32 length, char const* enum_def );
|
||||
Code parse_execution ( s32 length, char const* exec_def );
|
||||
@ -1143,14 +772,30 @@ namespace gen
|
||||
s32 parse_variables ( s32 length, char const* vars_def, Code* out_var_codes );
|
||||
s32 parse_typedefs ( s32 length, char const* typedef_def, Code* out_typedef_codes );
|
||||
s32 parse_usings ( s32 length, char const* usings_def, Code* out_using_codes );
|
||||
# endif
|
||||
# pragma endregion Parsing
|
||||
#endif
|
||||
#pragma endregion Parsing
|
||||
|
||||
#pragma region Untyped text
|
||||
sw token_fmt_va( char* buf, uw buf_size, char const* fmt, s32 num_tokens, va_list va );
|
||||
|
||||
inline
|
||||
char const* token_fmt( char const* fmt, sw num_tokens, ... )
|
||||
{
|
||||
local_persist thread_local
|
||||
char buf[ZPL_PRINTF_MAXLEN] = { 0 };
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
token_fmt_va(buf, ZPL_PRINTF_MAXLEN, fmt, num_tokens, va);
|
||||
va_end(va);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
# pragma region Untyped text
|
||||
Code untyped_str ( s32 length, char const* str);
|
||||
Code untyped_fmt ( char const* fmt, ... );
|
||||
Code untyped_token_fmt( char const* fmt, s32 num_tokens, ... );
|
||||
# pragma endregion Untyped text
|
||||
#pragma endregion Untyped text
|
||||
|
||||
struct Builder
|
||||
{
|
||||
@ -1237,7 +882,7 @@ namespace gen
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef GEN_FEATURE_SCANNER
|
||||
# ifdef GEN_FEATURE_SCANNER
|
||||
struct Scanner
|
||||
{
|
||||
struct RequestEntry
|
||||
@ -1266,7 +911,7 @@ namespace gen
|
||||
|
||||
bool process_requests( Array(Receipt) out_receipts );
|
||||
};
|
||||
#endif
|
||||
# endif
|
||||
#pragma endregion Gen Interface
|
||||
}
|
||||
|
||||
@ -1432,18 +1077,18 @@ namespace gen
|
||||
// or string handling while constructing or serializing.
|
||||
// Change them to suit your needs.
|
||||
|
||||
ct s32 InitSize_DataArrays = 16;
|
||||
ct s32 InitSize_StringTable = megabytes(4);
|
||||
ct s32 InitSize_TypeTable = megabytes(4);
|
||||
constexpr s32 InitSize_DataArrays = 16;
|
||||
constexpr s32 InitSize_StringTable = megabytes(4);
|
||||
constexpr s32 InitSize_TypeTable = megabytes(4);
|
||||
|
||||
ct s32 CodePool_NumBlocks = 4096;
|
||||
ct s32 InitSize_CodeEntiresArray = 512;
|
||||
ct s32 SizePer_CodeEntriresArena = megabytes(16);
|
||||
ct s32 SizePer_StringArena = megabytes(32);
|
||||
constexpr s32 CodePool_NumBlocks = 4096;
|
||||
constexpr s32 InitSize_CodeEntiresArray = 512;
|
||||
constexpr s32 SizePer_CodeEntriresArena = megabytes(16);
|
||||
constexpr s32 SizePer_StringArena = megabytes(32);
|
||||
|
||||
ct s32 MaxNameLength = 128;
|
||||
ct s32 MaxUntypedStrLength = kilobytes(640);
|
||||
ct s32 StringTable_MaxHashLength = kilobytes(1);
|
||||
constexpr s32 MaxNameLength = 128;
|
||||
constexpr s32 MaxUntypedStrLength = kilobytes(640);
|
||||
constexpr s32 StringTable_MaxHashLength = kilobytes(1);
|
||||
|
||||
// Predefined Codes. Are set to readonly and are setup during gen::init()
|
||||
|
||||
@ -1505,6 +1150,12 @@ namespace gen
|
||||
inline
|
||||
bool AST::add_param( AST* type, s32 length, char const* name )
|
||||
{
|
||||
if ( Type != ECode::Function )
|
||||
{
|
||||
log_failure( "gen::AST::add_param: this AST is not a function - %s", debug_str() );
|
||||
return Code::Invalid;
|
||||
}
|
||||
|
||||
if ( length <= 0 )
|
||||
{
|
||||
log_failure( "gen::AST::add_param: Invalid name length provided - %d", length );
|
||||
|
Reference in New Issue
Block a user