mirror of
https://github.com/Ed94/gencpp.git
synced 2024-12-22 15:54:45 -08:00
Reorganization of parser, refactor of parse_type( bool* ) and progression of parser docs
Wanted to make parser implementation easier to sift through, so I emphasized alphabetical order more. Since I couldn't just strip whitespace from typenames I decided to make the parse_type more aware of the typename's components if it was a function signature. This ofc lead to the dark & damp hell that is parsing typenames. Also made initial implementation to support parsing decltype within a typename signature.. The test failure for the singleheader is still a thing, these changes have not addressed that.
This commit is contained in:
parent
3868e1e811
commit
3e249d9bc5
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@ -24,7 +24,9 @@
|
||||
"filesystem": "cpp",
|
||||
"format": "cpp",
|
||||
"ratio": "cpp",
|
||||
"xstring": "cpp"
|
||||
"xstring": "cpp",
|
||||
"functional": "cpp",
|
||||
"vector": "cpp"
|
||||
},
|
||||
"C_Cpp.intelliSenseEngineFallback": "disabled",
|
||||
"mesonbuild.configureOnOpen": true,
|
||||
|
@ -561,6 +561,8 @@ Fields:
|
||||
```cpp
|
||||
CodeAttributes Attributes;
|
||||
CodeSpecifiers Specs;
|
||||
CodeReturnType ReturnType;
|
||||
CodeParam Params;
|
||||
Code ArrExpr;
|
||||
Code Prev;
|
||||
Code Next;
|
||||
|
91
docs/Parser_Algo.md
Normal file
91
docs/Parser_Algo.md
Normal file
@ -0,0 +1,91 @@
|
||||
# Parser's Algorithim
|
||||
|
||||
gencpp uses a hand-written recursive descent parser. Both the lexer and parser handle a full C/C++ file in a single pass.
|
||||
|
||||
## Notable implementation background
|
||||
|
||||
### Lexer
|
||||
|
||||
The lex procedure does the lexical pass of content provided as a `StrC` type.
|
||||
The tokens are stored (for now) in `gen::Parser::Tokens`.
|
||||
|
||||
Fields:
|
||||
```cpp
|
||||
Array<Token> Arr;
|
||||
s32 Idx;
|
||||
```
|
||||
|
||||
|
||||
What token types are supported can be found in [ETokType.csv](../project/enums/ETokType.csv) you can also find the token types in [ETokType.h](../project/components/gen/etoktype.cpp) , which is the generated enum from the csv file.
|
||||
|
||||
Tokens are defined with the struct `gen::Parser::Token`:
|
||||
|
||||
Fields:
|
||||
```cpp
|
||||
char const* Text;
|
||||
sptr Length;
|
||||
TokType Type;
|
||||
s32 Line;
|
||||
s32 Column;
|
||||
bool IsAssign;
|
||||
```
|
||||
|
||||
`IsAssign` is a flag that is set when the token is an assignment operator. Which is used for various purposes:
|
||||
|
||||
* Using statment assignment
|
||||
* Parameter argument default value assignment
|
||||
* Variable declaration initialization assignment
|
||||
|
||||
I plan to replace IsAssign with a general flags field and properly keep track of all operator types instead of abstracting it away to `ETokType::Operator`.
|
||||
|
||||
Traversing the tokens is done with the following interface macros:
|
||||
|
||||
| Macro | Description |
|
||||
| --- | --- |
|
||||
| `currtok_noskip` | Get the current token without skipping whitespace |
|
||||
| `currtok` | Get the current token, skip any whitespace tokens |
|
||||
| `prevtok` | Get the previous token (does not skip whitespace) |
|
||||
| `nexttok` | Get the next token (does not skip whitespace) |
|
||||
| `eat( Token Type )` | Check to see if the current token is of the given type, if so, advance Token's index to the next token |
|
||||
| `left` | Get the number of tokens left in the token array |
|
||||
| `check_noskip` | Check to see if the current token is of the given type, without skipping whitespace |
|
||||
| `check` | Check to see if the current token is of the given type, skip any whitespace tokens |
|
||||
|
||||
### Parser
|
||||
|
||||
The parser has a limited user interface, only specific types of definitions or statements are expected to be provided by the user directly when using to construct an AST dynamically (See SOA for example). It however does attempt to provide capability to parse a full C/C++ from production codebases.
|
||||
|
||||
Each public user interface procedure has the following format:
|
||||
|
||||
```cpp
|
||||
CodeStruct parse_<definition type>( StrC def )
|
||||
{
|
||||
check_parse_args( def );
|
||||
using namespace Parser;
|
||||
|
||||
TokArray toks = lex( def );
|
||||
if ( toks.Arr == nullptr )
|
||||
return CodeInvalid;
|
||||
|
||||
// Parse the tokens and return a constructed AST using internal procedures
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
The most top-level parsing procedure used for C/C++ file parsing is `parse_global_body`:
|
||||
|
||||
It uses a helper procedure called `parse_global_nspace`.
|
||||
|
||||
Each internal procedure will be
|
||||
|
||||
## parse_global_nspace
|
||||
|
||||
1. Make sure the type provided to the helper function is a `Namespace_Body`, `Global_Body`, `Export_Body`, `Extern_Linkage_body`.
|
||||
2. If its not a `Global_Body` eat the opening brace for the scope.
|
||||
3.
|
||||
|
||||
|
||||
## parse_type
|
||||
|
||||
This is the worst part of the parser. Because other than actual expression values in C++, typenames are the second worst thing to parse in the langauge.
|
||||
|
@ -1,11 +1,12 @@
|
||||
# Parsing
|
||||
|
||||
The library features a naive parser tailored for only what the library needs to construct the supported syntax of C++ into its AST.
|
||||
|
||||
This parser does not, and should not do the compiler's job. By only supporting this minimal set of features, the parser is kept (so far) around 5000 loc.
|
||||
|
||||
You can think of this parser of a frontend parser vs a semantic parser. Its intuitively similar to WYSIWYG. What you precerive as the syntax from the user-side before the compiler gets a hold of it, is what you get.
|
||||
|
||||
The parsing implementation supports the following for the user:
|
||||
User exposed interface:
|
||||
|
||||
```cpp
|
||||
CodeClass parse_class ( StrC class_def );
|
||||
@ -75,8 +76,6 @@ The lexing and parsing takes shortcuts from whats expected in the standard.
|
||||
* Assumed to *come before specifiers* (`const`, `constexpr`, `extern`, `static`, etc) for a function
|
||||
* Or in the usual spot for class, structs, (*right after the declaration keyword*)
|
||||
* typedefs have attributes with the type (`parse_type`)
|
||||
* As a general rule; if its not available from the upfront constructors, its not available in the parsing constructors.
|
||||
* *Upfront constructors are not necessarily used in the parsing constructors, this is just a good metric to know what can be parsed.*
|
||||
* Parsing attributes can be extended to support user defined macros by defining `GEN_DEFINE_ATTRIBUTE_TOKENS` (see `gen.hpp` for the formatting)
|
||||
|
||||
Empty lines used throughout the file are preserved for formatting purposes during ast serialization.
|
||||
|
@ -773,7 +773,8 @@ String AST::to_string()
|
||||
|
||||
result.append( "typedef ");
|
||||
|
||||
if ( IsFunction )
|
||||
// Determines if the typedef is a function typename
|
||||
if ( UnderlyingType->ReturnType )
|
||||
result.append( UnderlyingType->to_string() );
|
||||
else
|
||||
result.append_fmt( "%S %S", UnderlyingType->to_string(), Name );
|
||||
@ -796,21 +797,45 @@ String AST::to_string()
|
||||
|
||||
case Typename:
|
||||
{
|
||||
if ( Attributes || Specs )
|
||||
#if GEN_USE_NEW_TYPENAME_PARSING
|
||||
if ( ReturnType && Params )
|
||||
{
|
||||
if ( Attributes )
|
||||
result.append_fmt( "%S ", Attributes->to_string() );
|
||||
else
|
||||
{
|
||||
if ( Specs )
|
||||
result.append_fmt( "%S ( %S ) ( %S ) %S", ReturnType->to_string(), Name, Params->to_string(), Specs->to_string() );
|
||||
else
|
||||
result.append_fmt( "%S ( %S ) ( %S )", ReturnType->to_string(), Name, Params->to_string() );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
#else
|
||||
if ( ReturnType && Params )
|
||||
{
|
||||
if ( Attributes )
|
||||
result.append_fmt( "%S ", Attributes->to_string() );
|
||||
else
|
||||
{
|
||||
if ( Specs )
|
||||
result.append_fmt( "%S %S ( %S ) %S", ReturnType->to_string(), Name, Params->to_string(), Specs->to_string() );
|
||||
else
|
||||
result.append_fmt( "%S %S ( %S )", ReturnType->to_string(), Name, Params->to_string() );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( Attributes )
|
||||
result.append_fmt( "%S ", Attributes->to_string() );
|
||||
|
||||
if ( Specs )
|
||||
result.append_fmt( "%S %S", Name, Specs->to_string() );
|
||||
|
||||
else
|
||||
result.append_fmt( "%S", Name );
|
||||
}
|
||||
else
|
||||
{
|
||||
result.append_fmt( "%S", Name );
|
||||
}
|
||||
|
||||
if ( IsParamPack )
|
||||
result.append("...");
|
||||
|
@ -6,11 +6,6 @@
|
||||
#include "gen/especifier.hpp"
|
||||
#endif
|
||||
|
||||
namespace Parser
|
||||
{
|
||||
struct Token;
|
||||
}
|
||||
|
||||
struct AST;
|
||||
struct AST_Body;
|
||||
struct AST_Attributes;
|
||||
@ -234,12 +229,15 @@ struct AST
|
||||
union {
|
||||
struct
|
||||
{
|
||||
union {
|
||||
AST* InlineCmt; // Class, Constructor, Destructor, Enum, Friend, Functon, Operator, OpCast, Struct, Typedef, Using, Variable
|
||||
AST* SpecsFuncSuffix; // Only used with typenames, to store the function suffix if typename is function signature.
|
||||
};
|
||||
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
|
||||
AST* ParentType; // Class, Struct, ParentType->Next has a possible list of interfaces.
|
||||
AST* ReturnType; // Function, Operator
|
||||
AST* UnderlyingType; // Enum, Typedef
|
||||
AST* ValueType; // Parameter, Variable
|
||||
@ -286,12 +284,15 @@ struct AST_POD
|
||||
union {
|
||||
struct
|
||||
{
|
||||
union {
|
||||
AST* InlineCmt; // Class, Constructor, Destructor, Enum, Friend, Functon, Operator, OpCast, Struct, Typedef, Using, Variable
|
||||
AST* SpecsFuncSuffix; // Only used with typenames, to store the function suffix if typename is function signature.
|
||||
};
|
||||
AST* Attributes; // Class, Enum, Function, Struct, Typename, Union, Using, Variable
|
||||
AST* Specs; // Function, Operator, Typename, Variable
|
||||
union {
|
||||
AST* InitializerList; // Constructor
|
||||
AST* ParentType; // Class, Struct
|
||||
AST* ParentType; // Class, Struct, ParentType->Next has a possible list of interfaces.
|
||||
AST* ReturnType; // Function, Operator
|
||||
AST* UnderlyingType; // Enum, Typedef
|
||||
AST* ValueType; // Parameter, Variable
|
||||
|
@ -472,7 +472,7 @@ struct AST_Type
|
||||
char _PAD_[ sizeof(SpecifierT) * AST::ArrSpecs_Cap ];
|
||||
struct
|
||||
{
|
||||
char _PAD_CMT_[ sizeof(AST*) ];
|
||||
CodeSpecifiers SpecsFuncSuffix; // Only used for function signatures
|
||||
CodeAttributes Attributes;
|
||||
CodeSpecifiers Specs;
|
||||
CodeType ReturnType; // Only used for function signatures
|
||||
|
@ -31,6 +31,7 @@ namespace ESpecifier
|
||||
Virtual,
|
||||
Const,
|
||||
Final,
|
||||
NoExceptions,
|
||||
Override,
|
||||
Pure,
|
||||
NumSpecifiers
|
||||
@ -67,6 +68,7 @@ namespace ESpecifier
|
||||
{ sizeof( "virtual" ), "virtual" },
|
||||
{ sizeof( "const" ), "const" },
|
||||
{ sizeof( "final" ), "final" },
|
||||
{ sizeof( "noexcept" ), "noexcept" },
|
||||
{ sizeof( "override" ), "override" },
|
||||
{ sizeof( "= 0" ), "= 0" },
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,5 +21,6 @@ Volatile, volatile
|
||||
Virtual, virtual
|
||||
Const, const
|
||||
Final, final
|
||||
NoExceptions, noexcept
|
||||
Override, override
|
||||
Pure, = 0
|
||||
|
|
Loading…
Reference in New Issue
Block a user