WIP: Starting to improve failure/error handling (errors as values)

This commit is contained in:
ed committed 2026-10-03 20:33:50 -04:00
1 parent 61d53362e8
commit b754adcd96
16 files changed
+983 -292

No files matched your search

+2
View File
@@ -6,6 +6,8 @@
#pragma region Constants
// Predefined typename codes. Are set to readonly and are setup during gen::init()
GEN_API extern Token NullToken;
GEN_API extern Macro enum_underlying_macro;
GEN_API extern Code access_public;
+318 -123
View File
@@ -12,12 +12,16 @@ ParseInfo wip_parse_str(LexedInfo lexed, ParseOpts* opts)
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
ParseInfo info = struct_zero(ParseInfo);
if (lexed.tokens.num == 0 && lexed.tokens.ptr == nullptr) {
check_parse_args(lexed.text);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(lexed.text) == false) {
info.messages = ctx->parser.messages;
goto done;
}
lexed = lex(ctx, lexed.text);
}
ParseInfo info = struct_zero(ParseInfo);
info.lexed = lexed;
// TODO(Ed): ParseInfo should be set to the parser context.
@@ -25,12 +29,16 @@ ParseInfo wip_parse_str(LexedInfo lexed, ParseOpts* opts)
ctx->parser = struct_zero(ParseContext);
ctx->parser.tokens = lexed.tokens;
{
ParseStackNode scope = NullScope;
parser_push(& ctx->parser, & scope);
CodeBody result = parse_global_nspace(ctx,CT_Global_Body);
(void)result;
parser_pop(& ctx->parser);
}
done:
return info;
}
@@ -38,20 +46,30 @@ CodeClass parse_class( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeClass result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
{
ParseStackNode scope = NullScope;
parser_push(& ctx->parser, & scope);
CodeClass result = (CodeClass) parse_class_struct( ctx, Tok_Decl_Class, parser_not_inplace_def );
result = (CodeClass) parse_class_struct( ctx, Tok_Decl_Class, parser_not_inplace_def );
parser_pop(& ctx->parser);
}
done:
return result;
}
@@ -59,16 +77,24 @@ CodeConstructor parse_constructor(Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeConstructor result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
{
ParseStackNode scope = NullScope;
parser_push(& ctx->parser, & scope);
@@ -100,7 +126,7 @@ CodeConstructor parse_constructor(Str def )
default :
log_failure( "Invalid specifier %s for variable\n%S", spec_to_str( spec ), parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) );
parser_pop(& ctx->parser);
return InvalidCode;
goto done;
}
// Every specifier after would be considered part of the type type signature
@@ -117,8 +143,10 @@ CodeConstructor parse_constructor(Str def )
// <specifiers> ...
}
CodeConstructor result = parser_parse_constructor(ctx, specifiers);
result = parser_parse_constructor(ctx, specifiers);
parser_pop(& ctx->parser);
}
done:
return result;
}
@@ -126,20 +154,30 @@ CodeDefine parse_define( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeDefine result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
{
ParseStackNode scope = NullScope;
parser_push(& ctx->parser, & scope);
CodeDefine result = parser_parse_define(ctx);
result = parser_parse_define(ctx);
parser_pop(& ctx->parser);
}
done:
return result;
}
@@ -147,20 +185,28 @@ CodeDestructor parse_destructor( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeDestructor result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
// TODO(Ed): Destructors can have prefix attributes
// TODO(Ed): Destructors can have virtual
CodeDestructor result = parser_parse_destructor(ctx, NullCode);
result = parser_parse_destructor(ctx, NullCode);
done:
return result;
}
@@ -168,106 +214,160 @@ CodeEnum parse_enum( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeEnum result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr ) {
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
return parser_parse_enum(ctx, parser_not_inplace_def);
result = parser_parse_enum(ctx, parser_not_inplace_def);
done:
return result;
}
CodeBody parse_export_body( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeBody result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
return parser_parse_export_body(ctx);
result = parser_parse_export_body(ctx);
done:
return result;
}
CodeExtern parse_extern_link( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeExtern result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
return parser_parse_extern_link(ctx);
result = parser_parse_extern_link(ctx);
done:
return result;
}
CodeFriend parse_friend( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeFriend result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
return parser_parse_friend(ctx);
result = parser_parse_friend(ctx);
done:
return result;
}
CodeFn parse_function( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeFn result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
return (CodeFn) parser_parse_function(ctx);
result = (CodeFn) parser_parse_function(ctx);
done:
return result;
}
CodeBody parse_global_body( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeBody result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
{
ParseStackNode scope = NullScope;
parser_push(& ctx->parser, & scope);
CodeBody result = parse_global_nspace(ctx, CT_Global_Body );
result = parse_global_nspace(ctx, CT_Global_Body );
parser_pop(& ctx->parser);
}
done:
return result;
}
@@ -275,71 +375,108 @@ CodeNS parse_namespace( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeNS result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
return parser_parse_namespace(ctx);
result = parser_parse_namespace(ctx);
done:
return result;
}
CodeOperator parse_operator( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeOperator result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
return (CodeOperator) parser_parse_operator(ctx);
result = (CodeOperator) parser_parse_operator(ctx);
done:
return result;
}
CodeOpCast parse_operator_cast( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeOpCast result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
return parser_parse_operator_cast(ctx, NullCode);
result = parser_parse_operator_cast(ctx, NullCode);
done:
return result;
}
CodeStruct parse_struct( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeStruct result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
{
ParseStackNode scope = NullScope;
parser_push(& ctx->parser, & scope);
CodeStruct result = (CodeStruct) parse_class_struct( ctx, Tok_Decl_Struct, parser_not_inplace_def );
result = (CodeStruct) parse_class_struct( ctx, Tok_Decl_Struct, parser_not_inplace_def );
parser_pop(& ctx->parser);
}
done:
return result;
}
@@ -347,102 +484,160 @@ CodeTemplate parse_template( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeTemplate result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
return parser_parse_template(ctx);
result = parser_parse_template(ctx);
done:
return result;
}
CodeTypename parse_type( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeTypename result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
return parser_parse_type( ctx, parser_not_from_template, nullptr);
result = parser_parse_type( ctx, parser_not_from_template, nullptr);
done:
return result;
}
CodeTypedef parse_typedef( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeTypedef result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
return parser_parse_typedef(ctx);
result = parser_parse_typedef(ctx);
done:
return result;
}
CodeUnion parse_union( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeUnion result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
return parser_parse_union(ctx, parser_not_inplace_def);
result = parser_parse_union(ctx, parser_not_inplace_def);
done:
return result;
}
CodeUsing parse_using( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeUsing result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
return parser_parse_using(ctx);
result = parser_parse_using(ctx);
done:
return result;
}
CodeVar parse_variable( Str def )
{
// TODO(Ed): Lift this.
Context* ctx = _ctx;
check_parse_args( def );
CodeVar result = InvalidCode;
LexedInfo lexed = struct_zero(LexedInfo);
ctx->parser = struct_zero(ParseContext);
if (check_parse_args(def) == false)
goto done;
LexedInfo lexed = lex(ctx, def);
lexed = lex(ctx, def);
ctx->parser.tokens = lexed.tokens;
if ( ctx->parser.tokens.ptr == nullptr )
return InvalidCode;
if (ctx->parser.tokens.ptr == nullptr) {
if (lexed.messages != nullptr && lexed.messages->content.Ptr != nullptr)
parser_record_failure(ctx, lexed.messages->content);
else
parser_record_failure(ctx, txt("parse: lex produced no tokens"));
goto done;
}
if (parser_slice_is_formatting_only(&ctx->parser)) {
parser_record_failure(ctx, txt("parse_variable: only formatting tokens"));
goto done;
}
return parser_parse_variable(ctx);
result = parser_parse_variable(ctx);
done:
return result;
}
// Undef helper macros
+103 -70
View File
@@ -4,11 +4,9 @@
#include "gen/etoktype.hpp"
#endif
StrBuilder tok_to_strbuilder(AllocatorInfo ainfo, Token tok)
{
StrBuilder tok_to_strbuilder(AllocatorInfo ainfo, Token tok) {
StrBuilder result = strbuilder_make_reserve( ainfo, kilobytes(4) );
Str type_str = toktype_to_str( tok.Type );
strbuilder_append_fmt( & result, "Line: %d Column: %d, Type: %.*s Content: %.*s"
, tok.Line, tok.Column
, type_str.Len, type_str.Ptr
@@ -19,57 +17,68 @@ StrBuilder tok_to_strbuilder(AllocatorInfo ainfo, Token tok)
bool lex__eat(Context* ctx, ParseContext* self, TokType type );
Token* lex_current(ParseContext* self, bool skip_formatting )
{
if ( skip_formatting )
{
while ( self->tokens.ptr[self->token_id].Type == Tok_NewLine || self->tokens.ptr[self->token_id].Type == Tok_Comment )
self->token_id++;
}
return & self->tokens.ptr[self->token_id];
forceinline
b32 lex_index_in_range(ParseContext const* self, s32 idx) {
return self->tokens.ptr != nullptr
&& idx >= 0
&& idx < self->tokens.num;
}
Token* lex_peek(ParseContext const* self, bool skip_formatting)
{
s32 idx = self->token_id;
if ( skip_formatting )
{
while ( self->tokens.ptr[idx].Type == Tok_NewLine )
idx++;
forceinline
Token* lex_token_at(ParseContext const* self, s32 idx) {
Token* result = & NullToken;
if (lex_index_in_range(self, idx)) result = & self->tokens.ptr[idx];
return result;
}
return & self->tokens.ptr[idx];
Token* lex_current(ParseContext* self, bool skip_formatting) {
if (skip_formatting) while (lex_index_in_range(self, self->token_id)) {
TokType type = self->tokens.ptr[self->token_id].Type;
if (type != Tok_NewLine && type != Tok_Comment) break;
++ self->token_id;
}
return & self->tokens.ptr[idx];
return lex_token_at(self, self->token_id);
}
Token* lex_peek(ParseContext const* self, bool skip_formatting) {
s32 idx = self->token_id;
if (skip_formatting) {
while (lex_index_in_range(self, idx) && self->tokens.ptr[idx].Type == Tok_NewLine)
++ idx;
}
return lex_token_at(self, idx);
}
Token* lex_previous(ParseContext const* self, bool skip_formatting)
{
s32 idx = self->token_id;
if ( skip_formatting )
Token* result = & NullToken;
s32 idx = self->token_id;
if (skip_formatting)
{
while ( self->tokens.ptr[idx].Type == Tok_NewLine )
idx --;
return & self->tokens.ptr[idx];
if (self->tokens.num > 0 && idx >= self->tokens.num)
idx = self->tokens.num - 1;
while (lex_index_in_range(self, idx) && self->tokens.ptr[idx].Type == Tok_NewLine && idx > 0)
-- idx;
if (lex_index_in_range(self, idx) && self->tokens.ptr[idx].Type != Tok_NewLine)
result = &self->tokens.ptr[idx];
goto done;
}
return & self->tokens.ptr[idx - 1];
if (self->tokens.num > 0 && idx >= self->tokens.num) result = lex_token_at(self, self->tokens.num - 1);
else result = lex_token_at(self, idx - 1);
done:
return result;
}
Token* lex_next(ParseContext const* self, bool skip_formatting)
{
Token* lex_next(ParseContext const* self, bool skip_formatting) {
s32 idx = self->token_id;
if ( skip_formatting )
{
while ( self->tokens.ptr[idx].Type == Tok_NewLine )
idx++;
return & self->tokens.ptr[idx + 1];
if (skip_formatting) {
while (lex_index_in_range(self, idx) && self->tokens.ptr[idx].Type == Tok_NewLine)
-- idx;
}
return & self->tokens.ptr[idx + 1];
return lex_token_at(self, idx + 1);
}
enum
{
enum {
Lex_Continue,
Lex_ReturnNull,
};
@@ -90,19 +99,16 @@ void lexer_move_forward( LexContext* ctx )
#define move_forward() lexer_move_forward(ctx)
forceinline
void lexer_skip_whitespace( LexContext* ctx )
{
void lexer_skip_whitespace( LexContext* ctx ) {
while ( ctx->left && char_is_space( * ctx->scanner ) )
move_forward();
}
#define skip_whitespace() lexer_skip_whitespace(ctx)
forceinline
void lexer_end_line( LexContext* ctx )
{
void lexer_end_line( LexContext* ctx ) {
while ( ctx->left && (* ctx->scanner) == ' ' )
move_forward();
if ( ctx->left && (* ctx->scanner) == '\r' ) {
move_forward();
move_forward();
@@ -113,7 +119,9 @@ void lexer_end_line( LexContext* ctx )
#define end_line() lexer_end_line(ctx)
// TODO(Ed): We need to to attempt to recover from a lex failure?
s32 lex_preprocessor_define( LexContext* ctx )
internal void lexer_record_failure_fmt(Context* lib_ctx, LexContext* ctx, char const* fmt, ...);
s32 lex_preprocessor_define(Context* lib_ctx, LexContext* ctx)
{
Token name = { { ctx->scanner, 1 }, Tok_Identifier, ctx->line, ctx->column, TF_Preprocess };
move_forward();
@@ -178,7 +186,7 @@ s32 lex_preprocessor_define( LexContext* ctx )
}
if (* ctx->scanner != ')' )
{
log_failure("lex_preprocessor_define(%d, %d): Expected a ')' after '...' (varaidc macro param) %S\n"
lexer_record_failure_fmt(lib_ctx, ctx, "lex_preprocessor_define(%d, %d): Expected a ')' after '...' (varaidc macro param) %S\n"
, ctx->line
, ctx->column
, name.Text
@@ -207,7 +215,7 @@ s32 lex_preprocessor_define( LexContext* ctx )
last_parameter = parameter;
}
else {
log_failure("lex_preprocessor_define(%d, %d): Expected a '_' or alpha character for a parameter name for %S\n"
lexer_record_failure_fmt(lib_ctx, ctx, "lex_preprocessor_define(%d, %d): Expected a '_' or alpha character for a parameter name for %S\n"
, ctx->line
, ctx->column
, name.Text
@@ -220,7 +228,7 @@ s32 lex_preprocessor_define( LexContext* ctx )
// There should be a comma
if ( * ctx->scanner != ',' ) {
log_failure("lex_preprocessor_define(%d, %d): Expected a comma after parameter %S for %S\n"
lexer_record_failure_fmt(lib_ctx, ctx, "lex_preprocessor_define(%d, %d): Expected a comma after parameter %S for %S\n"
, ctx->line
, ctx->column
, last_parameter.Text
@@ -234,7 +242,7 @@ s32 lex_preprocessor_define( LexContext* ctx )
}
if ( * ctx->scanner != ')' ) {
log_failure("lex_preprocessor_define(%d, %d): Expected a ')' after last_parameter %S for %S (ran out of characters...)\n"
lexer_record_failure_fmt(lib_ctx, ctx, "lex_preprocessor_define(%d, %d): Expected a ')' after last_parameter %S for %S (ran out of characters...)\n"
, ctx->line
, ctx->column
, last_parameter.Text
@@ -264,7 +272,7 @@ s32 lex_preprocessor_define( LexContext* ctx )
}
// TODO(Ed): We need to to attempt to recover from a lex failure?
s32 lex_preprocessor_directive( LexContext* ctx )
s32 lex_preprocessor_directive(Context* lib_ctx, LexContext* ctx)
{
char const* hash = ctx->scanner;
Token hash_tok = { { hash, 1 }, Tok_Preprocess_Hash, ctx->line, ctx->column, TF_Preprocess };
@@ -317,11 +325,11 @@ s32 lex_preprocessor_directive( LexContext* ctx )
}
else
{
log_failure( "gen::Parser::lex: Invalid escape sequence '\\%c' (%d, %d)"
lexer_record_failure_fmt(lib_ctx, ctx, "gen::Parser::lex: Invalid escape sequence '\\%c' (%d, %d)"
" in preprocessor directive (%d, %d)\n%.100s"
, (* ctx->scanner), ctx->line, ctx->column
, ctx->token.Line, ctx->token.Column, ctx->token.Text );
break;
return Lex_ReturnNull;
}
}
@@ -366,7 +374,7 @@ s32 lex_preprocessor_directive( LexContext* ctx )
if ( ctx->token.Type == Tok_Preprocess_Define )
{
u32 result = lex_preprocessor_define(ctx); // handles: #define <name>( <params> ) - define's content handled later on within this scope.
u32 result = lex_preprocessor_define(lib_ctx, ctx); // handles: #define <name>( <params> ) - define's content handled later on within this scope.
if (result != Lex_Continue)
return Lex_ReturnNull;
}
@@ -381,7 +389,7 @@ s32 lex_preprocessor_directive( LexContext* ctx )
{
StrBuilder directive_str = strbuilder_fmt_buf( ctx->allocator_temp, "%.*s", min( 80, ctx->left + preprocess_content.Text.Len ), ctx->token.Text.Ptr );
log_failure( "gen::Parser::lex: Expected '\"' or '<' after #include, not '%c' (%d, %d)\n%s"
lexer_record_failure_fmt(lib_ctx, ctx, "gen::Parser::lex: Expected '\"' or '<' after #include, not '%c' (%d, %d)\n%s"
, (* ctx->scanner)
, preprocess_content.Line
, preprocess_content.Column
@@ -449,13 +457,12 @@ s32 lex_preprocessor_directive( LexContext* ctx )
StrBuilder directive_str = strbuilder_make_length( ctx->allocator_temp, ctx->token.Text.Ptr, ctx->token.Text.Len );
StrBuilder content_str = strbuilder_fmt_buf( ctx->allocator_temp, "%.*s", min( 400, ctx->left + preprocess_content.Text.Len ), preprocess_content.Text.Ptr );
log_failure( "gen::Parser::lex: Invalid escape sequence '\\%c' (%d, %d)"
lexer_record_failure_fmt(lib_ctx, ctx, "gen::Parser::lex: Invalid escape sequence '\\%c' (%d, %d)"
" in preprocessor directive '%s' (%d, %d)\n%s"
, (* ctx->scanner), ctx->line, ctx->column
, directive_str, preprocess_content.Line, preprocess_content.Column
, content_str );
return Lex_ReturnNull;
break;
}
}
@@ -568,6 +575,29 @@ void lex_found_token( LexContext* ctx )
// TODO(Ed): We need to to attempt to recover from a lex failure?
internal
void lexer_record_failure(Context* lib_ctx, LexContext* ctx, Str text) {
LexerMessage* msg = rcast(LexerMessage*, alloc(lib_ctx->Allocator_DyanmicContainers, size_of(LexerMessage)));
if (msg == nullptr) GEN_FATAL("lexer_record_failure: allocation failed");
msg->next = ctx->messages;
msg->content = cache_str(text);
msg->level = LL_Error;
ctx->messages = msg;
}
internal
void lexer_record_failure_fmt(Context* lib_ctx, LexContext* ctx, char const* fmt, ...) {
va_list va;
char* buf;
Str text;
va_start(va, fmt);
buf = c_str_fmt_buf_va(fmt, va);
va_end(va);
text.Ptr = buf;
text.Len = c_str_len(buf);
lexer_record_failure(lib_ctx, ctx, text);
}
neverinline
LexedInfo lex(Context* lib_ctx, Str content)
{
@@ -586,14 +616,15 @@ LexedInfo lex(Context* lib_ctx, Str content)
// 1. Ability to continue on error
// 2. Return a lexed info.
b32 preprocess_args = true;
skip_whitespace();
if ( c.left <= 0 ) {
log_failure( "gen::lex: no tokens found (only whitespace provided)" );
return info;
lexer_record_failure(lib_ctx, ctx, txt("gen::lex: no tokens found (only whitespace provided)"));
info.messages = c.messages;
goto Lex_Done;
}
b32 preprocess_args = true;
while (c.left )
{
c.token = struct_init(Token) { { c.scanner, 0 }, Tok_Invalid, c.line, c.column, TF_Null };
@@ -630,7 +661,7 @@ LexedInfo lex(Context* lib_ctx, Str content)
{
case '#':
{
s32 result = lex_preprocessor_directive( ctx );
s32 result = lex_preprocessor_directive(lib_ctx, ctx);
switch ( result )
{
case Lex_Continue:
@@ -662,6 +693,7 @@ LexedInfo lex(Context* lib_ctx, Str content)
case Lex_ReturnNull:
{
info.messages = c.messages;
return info;
}
}
@@ -691,7 +723,9 @@ LexedInfo lex(Context* lib_ctx, Str content)
{
StrBuilder context_str = strbuilder_fmt_buf( lib_ctx->Allocator_Temp, "%s", c.scanner, min( 100, c.left ) );
log_failure( "gen::lex: invalid varadic argument, expected '...' got '..%c' (%d, %d)\n%s", (* ctx->scanner), c.line, c.column, context_str );
lexer_record_failure_fmt(lib_ctx, ctx, "gen::lex: invalid varadic argument, expected '...' got '..%c' (%d, %d)\n%s", (* ctx->scanner), c.line, c.column, context_str );
info.messages = c.messages;
return info;
}
}
@@ -1245,13 +1279,10 @@ LexedInfo lex(Context* lib_ctx, Str content)
);
}
StrBuilder context_str = strbuilder_fmt_buf( _ctx->Allocator_Temp, "%.*s", min( 100, c.left ), c.scanner );
log_failure( "Failed to lex token '%c' (%d, %d)\n%s", (* ctx->scanner), c.line, c.column, context_str );
// Skip to next whitespace since we can't know if anything else is valid until then.
while ( c.left && ! char_is_space( (* ctx->scanner) ) ) {
move_forward();
}
StrBuilder context_str = strbuilder_fmt_buf( lib_ctx->Allocator_Temp, "%.*s", min( 100, c.left ), c.scanner );
lexer_record_failure_fmt(lib_ctx, ctx, "Failed to lex token '%c' (%d, %d)\n%s", (* ctx->scanner), c.line, c.column, context_str );
info.messages = c.messages;
return info;
}
FoundToken:
@@ -1280,13 +1311,15 @@ LexedInfo lex(Context* lib_ctx, Str content)
}
if ( array_num(c.tokens) == 0 ) {
log_failure( "Failed to lex any tokens" );
return info;
lexer_record_failure(lib_ctx, ctx, txt("gen::lex: failed to lex any tokens"));
info.messages = c.messages;
goto Lex_Done;
}
info.messages = c.messages;
info.text = content;
info.tokens = struct_init(TokenSlice) { pcast(Token*, c.tokens), scast(s32, array_num(c.tokens)) };
Lex_Done:
return info;
}
+125 -71
View File
@@ -31,70 +31,96 @@ void parser_pop(ParseContext* ctx)
StrBuilder parser_to_strbuilder(ParseContext const* ctx, AllocatorInfo temp)
{
StrBuilder result = strbuilder_make_reserve( temp, kilobytes(4) );
Token scope_start = * ctx->scope->start;
Token last_valid = (ctx->token_id >= ctx->tokens.num) ? ctx->tokens.ptr[ctx->tokens.num -1] : (* lex_peek(ctx, true));
sptr length = scope_start.Text.Len;
char const* current = scope_start.Text.Ptr + length;
while ( current <= ctx->tokens.ptr[ctx->tokens.num - 1].Text.Ptr && (* current) != '\n' && length < 74 )
{
current++;
length++;
StrBuilder result;
b32 empty_slice = ctx->tokens.ptr == nullptr || ctx->tokens.num <= 0;
if (empty_slice == false)
empty_slice = ctx->scope->start == nullptr || ctx->scope->start->Text.Ptr == nullptr;
// empty_scope
if (empty_slice) {
result = strbuilder_make_reserve(temp, 64);
strbuilder_append_fmt(&result, "\tScope : <empty>\n");
goto done;
}
Str scope_str = { scope_start.Text.Ptr, length };
StrBuilder line = strbuilder_make_str( temp, scope_str );
strbuilder_append_fmt( & result, "\tScope : %s\n", line );
strbuilder_free(& line);
sptr dist = (sptr)last_valid.Text.Ptr - (sptr)scope_start.Text.Ptr + 2;
sptr length_from_err = dist;
Str err_str = { last_valid.Text.Ptr, length_from_err };
StrBuilder line_from_err = strbuilder_make_str( temp, err_str );
if ( length_from_err < 100 )
strbuilder_append_fmt(& result, "\t(%d, %d):%*c\n", last_valid.Line, last_valid.Column, length_from_err, '^' );
else
strbuilder_append_fmt(& result, "\t(%d, %d)\n", last_valid.Line, last_valid.Column );
ParseStackNode* curr_scope = ctx->scope;
s32 level = 0;
do
// non_empty
{
if ( curr_scope->name.Ptr ) {
strbuilder_append_fmt(& result, "\t%d: %S, AST Name: %S\n", level, curr_scope->proc_name, curr_scope->name );
}
else {
strbuilder_append_fmt(& result, "\t%d: %S\n", level, curr_scope->proc_name );
result = strbuilder_make_reserve( temp, kilobytes(4) );
Token scope_start = * ctx->scope->start;
Token last_valid = (ctx->token_id >= ctx->tokens.num)
? ctx->tokens.ptr[ctx->tokens.num - 1]
: (*lex_peek(ctx, true));
sptr length = scope_start.Text.Len;
char const* current = scope_start.Text.Ptr + length;
while ( current <= ctx->tokens.ptr[ctx->tokens.num - 1].Text.Ptr && (* current) != '\n' && length < 74 )
{
current++;
length++;
}
curr_scope = curr_scope->prev;
level++;
Str scope_str = { scope_start.Text.Ptr, length };
StrBuilder line = strbuilder_make_str( temp, scope_str );
strbuilder_append_fmt( & result, "\tScope : %s\n", line );
strbuilder_free(& line);
sptr dist = (sptr)last_valid.Text.Ptr - (sptr)scope_start.Text.Ptr + 2;
sptr length_from_err = dist;
Str err_str = { last_valid.Text.Ptr, length_from_err };
StrBuilder line_from_err = strbuilder_make_str( temp, err_str );
if ( length_from_err < 100 )
strbuilder_append_fmt(& result, "\t(%d, %d):%*c\n", last_valid.Line, last_valid.Column, length_from_err, '^' );
else
strbuilder_append_fmt(& result, "\t(%d, %d)\n", last_valid.Line, last_valid.Column );
ParseStackNode* curr_scope = ctx->scope;
s32 level = 0;
do
{
if ( curr_scope->name.Ptr ) {
strbuilder_append_fmt(& result, "\t%d: %S, AST Name: %S\n", level, curr_scope->proc_name, curr_scope->name );
}
else {
strbuilder_append_fmt(& result, "\t%d: %S\n", level, curr_scope->proc_name );
}
curr_scope = curr_scope->prev;
level++;
}
while ( curr_scope );
}
while ( curr_scope );
done:
return result;
}
void parser_record_failure(Context* ctx, Str text) {
ParseMessage* msg = rcast(ParseMessage*, alloc(ctx->Allocator_DyanmicContainers, size_of(ParseMessage)));
if (msg == nullptr) GEN_FATAL("parser_record_failure: allocation failed");
msg->Next = ctx->parser.messages;
msg->Scope = nullptr;
msg->Content = cache_str(text);
msg->Level = LL_Error;
ctx->parser.messages = msg;
}
bool lex__eat(Context* ctx, ParseContext* parser, TokType type)
{
if ( parser->tokens.num - parser->token_id <= 0 ) {
log_failure( "No tokens left.\n%SB", parser_to_strbuilder(parser, ctx->Allocator_Temp) );
return false;
}
Token at_idx = parser->tokens.ptr[ parser->token_id ];
b32 ate = false;
Token at_idx;
b32 not_accepted;
b32 is_identifier;
if (parser->tokens.num - parser->token_id <= 0)
goto done;
at_idx = parser->tokens.ptr[ parser->token_id ];
if ( ( at_idx.Type == Tok_NewLine && type != Tok_NewLine )
|| ( at_idx.Type == Tok_Comment && type != Tok_Comment ) )
{
|| ( at_idx.Type == Tok_Comment && type != Tok_Comment ) ) {
parser->token_id ++;
}
b32 not_accepted = at_idx.Type != type;
b32 is_identifier = at_idx.Type == Tok_Identifier;
not_accepted = at_idx.Type != type;
is_identifier = at_idx.Type == Tok_Identifier;
if ( not_accepted )
{
Macro* macro = lookup_macro(at_idx.Text);
@@ -112,7 +138,7 @@ bool lex__eat(Context* ctx, ParseContext* parser, TokType type)
, parser_to_strbuilder(parser, ctx->Allocator_Temp)
);
GEN_DEBUG_TRAP();
return false;
goto done;
}
#if 0 && GEN_BUILD_DEBUG
@@ -120,7 +146,9 @@ bool lex__eat(Context* ctx, ParseContext* parser, TokType type)
#endif
parser->token_id ++;
return true;
ate = true;
done:
return ate;
}
internal
@@ -135,22 +163,40 @@ void parser_deinit(Context* ctx)
#pragma region Helper Macros
#define check_parse_args( def ) _check_parse_args(& ctx->parser, def, stringize(_func_) )
bool _check_parse_args(ParseContext* parser, Str def, char const* func_name )
b32 parser_slice_is_formatting_only(ParseContext const* parser)
{
if ( def.Len <= 0 )
b32 formatting_only = true;
if (parser->tokens.ptr == nullptr || parser->tokens.num <= 0)
goto done;
for (s32 idx = 0; idx < parser->tokens.num; ++idx)
{
log_failure( c_str_fmt_buf("gen::%s: length must greater than 0", func_name) );
parser_pop(parser);
return false;
TokType type = parser->tokens.ptr[idx].Type;
if (type != Tok_NewLine && type != Tok_Comment) {
formatting_only = false;
goto done;
}
}
if ( def.Ptr == nullptr )
{
log_failure( c_str_fmt_buf("gen::%s: def was null", func_name) );
parser_pop(parser);
return false;
done:
return formatting_only;
}
#define check_parse_args( def ) _check_parse_args(ctx, def, __func__)
bool _check_parse_args(Context* ctx, Str def, char const* func_name )
{
b32 ok = true;
char const* text;
if ( def.Len <= 0 ) {
text = c_str_fmt_buf("gen::%s: length must greater than 0", func_name);
parser_record_failure(ctx, to_str_from_c_str(text));
ok = false; goto done;
}
return true;
if ( def.Ptr == nullptr ) {
text = c_str_fmt_buf("gen::%s: def was null", func_name);
parser_record_failure(ctx, to_str_from_c_str(text));
ok = false; goto done;
}
done:
return ok;
}
# define currtok_noskip (* lex_current( & ctx->parser, lex_dont_skip_formatting ))
@@ -2455,9 +2501,12 @@ CodeOperator parse_operator_after_ret_type(Context* ctx
was_new_or_delete = true;
s32 idx = ctx->parser.token_id + 1;
{
while ( ctx->parser.tokens.ptr[ idx ].Type == Tok_NewLine )
idx++;
while (idx < ctx->parser.tokens.num && ctx->parser.tokens.ptr[idx].Type == Tok_NewLine)
idx++;
if (idx >= ctx->parser.tokens.num) {
parser_record_failure(ctx, txt("parse: operator new/delete ended at slice end"));
parser_pop(&ctx->parser);
return InvalidCode;
}
Token next = ctx->parser.tokens.ptr[idx];
if ( currtok.Type == Tok_Operator && c_str_compare_len(currtok.Text.Ptr, "[]", 2) == 0)
@@ -2479,9 +2528,12 @@ CodeOperator parse_operator_after_ret_type(Context* ctx
was_new_or_delete = true;
s32 idx = ctx->parser.token_id + 1;
{
while ( ctx->parser.tokens.ptr[ idx ].Type == Tok_NewLine )
idx++;
while (idx < ctx->parser.tokens.num && ctx->parser.tokens.ptr[idx].Type == Tok_NewLine)
idx++;
if (idx >= ctx->parser.tokens.num) {
parser_record_failure(ctx, txt("parse: operator new/delete ended at slice end"));
parser_pop(&ctx->parser);
return InvalidCode;
}
Token next = ctx->parser.tokens.ptr[idx];
if ( currtok.Type == Tok_Operator && c_str_compare_len(currtok.Text.Ptr, "[]", 2) == 0)
@@ -2673,7 +2725,9 @@ Code parse_operator_function_or_variable(Context* ctx, bool expects_function, Co
// ( 350.0f , <--- Could be the scenario
// Example : <Capture_Start> <Value> <Comma>
// idx +1 +2
bool detected_comma = ctx->parser.tokens.ptr[ ctx->parser.token_id + 2 ].Type == Tok_Comma;
b32 detected_comma = false;
if (ctx->parser.token_id + 2 < ctx->parser.tokens.num)
detected_comma = ctx->parser.tokens.ptr[ctx->parser.token_id + 2].Type == Tok_Comma;
b32 detected_non_varadic_unpaired_param = detected_comma && nexttok.Type != Tok_Varadic_Argument;
if (! detected_non_varadic_unpaired_param && nexttok.Type == Tok_Preprocess_Macro_Expr) for( s32 break_scope = 0; break_scope == 0; ++ break_scope)
@@ -2701,7 +2755,7 @@ Code parse_operator_function_or_variable(Context* ctx, bool expects_function, Co
}
++ idx; // Will incremnt to possible comma position
if ( ctx->parser.tokens.ptr[ idx ].Type != Tok_Comma )
if (idx >= ctx->parser.tokens.num || ctx->parser.tokens.ptr[idx].Type != Tok_Comma)
break;
detected_non_varadic_unpaired_param = true;
-2
View File
@@ -37,8 +37,6 @@ struct Token
u32 Flags;
};
constexpr Token NullToken { {}, Tok_Invalid, 0, 0, TF_Null };
forceinline
AccessSpec tok_to_access_specifier(Token tok) {
return scast(AccessSpec, tok.Type);
+2
View File
@@ -8,6 +8,8 @@ GEN_API global Context* _ctx;
GEN_API global u32 context_counter;
#pragma region Constants
GEN_API read_only global Token NullToken = { { 0, 0 }, Tok_Invalid, 0, 0, TF_Null };
GEN_API global Macro enum_underlying_macro;
GEN_API global Code Code_Global;
+13 -3
View File
@@ -38,6 +38,16 @@
#ifndef local_persist
#define local_persist static // Local Persisting variables
#endif
#ifndef read_only
# if GEN_COMPILER_MSVC || ( defined(GEN_COMPILER_CLANG) && GEN_COMPILER_CLANG && GEN_SYSTEM_WINDOWS )
# pragma section(".rdata$", read)
# define read_only __declspec(allocate(".rdata$"))
# elif defined(GEN_COMPILER_CLANG) && GEN_COMPILER_CLANG
# define read_only __attribute__((section(".rodata")))
# else
# define read_only
# endif
#endif
#ifndef bit
#define bit( Value ) ( 1 << Value )
@@ -98,9 +108,9 @@
#define src_line_str stringize(__LINE__)
#ifndef do_once
#define do_once() \
local_persist int __do_once_counter_##src_line_str = 0; \
for(; __do_once_counter_##src_line_str != 1; __do_once_counter_##src_line_str = 1 ) \
#define do_once() \
local_persist int __do_once_counter_##src_line_str = 0; \
for(; __do_once_counter_##src_line_str != 1; __do_once_counter_##src_line_str = 1 ) \
#define do_once_defer( expression ) \
local_persist int __do_once_counter_##src_line_str = 0; \
-13
View File
@@ -767,20 +767,7 @@ do \
break;
case CT_Variable:
{
CodeVar var = cast(CodeVar, entry);
if (var->Specs && var->Specs.has(Spec_Constexpr)) {
Code define_ver = untyped_str(token_fmt(
"name", var->Name
, "value", var->Value->Content
, "type", var->ValueType.to_strbuilder().to_str()
, "#define <name> (<type>) <value>\n"
));
parser_types.append(define_ver);
continue;
}
parser_types.append(entry);
}
break;
default:
+1
View File
@@ -20,6 +20,7 @@
word global, gen_global
word internal, gen_internal
word local_persist, gen_local_persist
word read_only, gen_read_only
word bit, gen_bit
word bitfield_is_set, gen_bitfield_is_set
word cast, gen_cast
+3
View File
@@ -25,6 +25,9 @@ args:
segmented
singleheader
unreal
test
parser_bounds
lexer_failures
clang
msvc : By default this project builds with clang, specifying msvc will build with MSVC.
debug
+62 -9
View File
@@ -74,8 +74,10 @@ Push-Location $path_root
[bool] $c_lib = $false
[bool] $c_lib_static = $false
[bool] $c_lib_dyn = $false
[bool] $unreal = $false
[bool] $test = $false
[bool] $unreal = $false
[bool] $test = $false
[bool] $parser_bounds = $false
[bool] $lexer_failures = $false
[array] $vendors = @( "clang", "msvc" )
@@ -93,8 +95,10 @@ if ( $args ) { $args | ForEach-Object {
"c_lib" { $c_lib = $true }
"c_lib_static" { $c_lib_static = $true }
"c_lib_dyn" { $c_lib_dyn = $true }
"unreal" { $unreal = $true }
"test" { $test = $true }
"unreal" { $unreal = $true }
"test" { $test = $true }
"parser_bounds" { $parser_bounds = $true }
"lexer_failures" { $lexer_failures = $true }
}
}}
#endregion Arguments
@@ -114,6 +118,7 @@ if ( $vendor -eq $null ) {
if ( $release -eq $null ) {
write-host "No build type specified, assuming debug"
$release = $false
$debug = $true
}
elseif ( $release -eq $false ) {
$debug = $true
@@ -129,7 +134,9 @@ $cannot_build = $cannot_build -and $c_lib -eq $false
$cannot_build = $cannot_build -and $c_lib_static -eq $false
$cannot_build = $cannot_build -and $c_lib_dyn -eq $false
$cannot_build = $cannot_build -and $unreal -eq $false
$cannot_build = $cannot_build -and $test -eq $false
$cannot_build = $cannot_build -and $test -eq $false
$cannot_build = $cannot_build -and $parser_bounds -eq $false
$cannot_build = $cannot_build -and $lexer_failures -eq $false
if ( $cannot_build ) {
Stop-stage "No build target specified"
}
@@ -362,7 +369,6 @@ if ( $test -and $true )
$compiler_args += $flag_all_c
$compiler_args += $flag_updated_cpp_macro
$compiler_args += $flag_c11
$compiler_args += "-H"
$linker_args = @(
$flag_link_win_subsystem_console
@@ -421,10 +427,9 @@ if ($test)
$compiler_args = @()
$compiler_args += ( $flag_define + 'GEN_TIME' )
$compiler_args += @("-x", "c++")
$compiler_args += "-std=c++17"
$compiler_args += $flag_all_cpp
$compiler_args += $flag_cpp17
$compiler_args += ($flag_define + "GEN_BUILD_DEBUG=1")
$compiler_args += "-H"
$linker_args = @(
$flag_link_win_subsystem_console
@@ -442,6 +447,54 @@ if ($test) {
}
}
}
if ($parser_bounds)
{
$path_probe = join-path $path_test parser_bounds
$path_build = join-path $path_probe build
if ( -not(Test-Path($path_build) )) {
new-item -ItemType Directory -Path $path_build | Out-Null
}
$includes = @( $path_base )
$unit = join-path $path_probe "test.cpp"
$executable = join-path $path_build "parser_bounds.exe"
$compiler_args = @()
$compiler_args += ( $flag_define + 'GEN_TIME' )
$compiler_args += $flag_cpp17
$linker_args = @(
$flag_link_win_subsystem_console
)
$result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable
invoke-stageTool -stage "parser_bounds" -compiled $result -executable $executable -path_work $path_probe
}
if ($lexer_failures)
{
$path_probe = join-path $path_test lexer_failures
$path_build = join-path $path_probe build
if ( -not(Test-Path($path_build) )) {
new-item -ItemType Directory -Path $path_build | Out-Null
}
$includes = @( $path_base )
$unit = join-path $path_probe "test.cpp"
$executable = join-path $path_build "lexer_failures.exe"
$compiler_args = @()
$compiler_args += ( $flag_define + 'GEN_TIME' )
$compiler_args += $flag_cpp17
$linker_args = @(
$flag_link_win_subsystem_console
)
$result = build-simple $path_build $includes $compiler_args $linker_args $unit $executable
invoke-stageTool -stage "lexer_failures" -compiled $result -executable $executable -path_work $path_probe
}
#endregion Building
pop-location # $path_root
+8
View File
@@ -19,6 +19,8 @@ $path_unreal_gen = Join-Path $path_unreal gen
$path_test = Join-Path $path_root test
$path_test_build = Join-Path $path_test build
$path_test_gen = Join-Path $path_test gen
$path_parser_bounds_build = Join-Path $path_test "parser_bounds\build"
$path_lexer_failures_build = Join-Path $path_test "lexer_failures\build"
$path_x64 = Join-Path $path_root x64
$path_release = Join-Path $path_root release
@@ -58,6 +60,12 @@ if ( Test-Path $path_test_build ) {
if ( Test-Path $path_test_gen ) {
Remove-Item $path_test_gen -Recurse -Verbose
}
if ( Test-Path $path_parser_bounds_build ) {
Remove-Item $path_parser_bounds_build -Recurse -Verbose
}
if ( Test-Path $path_lexer_failures_build ) {
Remove-Item $path_lexer_failures_build -Recurse -Verbose
}
if ( Test-Path $path_x64) {
Remove-Item $path_x64 -Recurse -Verbose
}
+3 -1
View File
@@ -149,7 +149,8 @@ if ( $vendor -match "clang" )
# https://clang.llvm.org/docs/ClangCommandLineReference.html
$flag_all_c = @('-x', 'c')
$flag_c11 = '-std=c11'
$flag_all_cpp = '-x c++'
$flag_cpp17 = '-std=c++17'
$flag_all_cpp = @('-x', 'c++')
$flag_charset_utf8 = '-utf-8'
$flag_compile = '-c'
$flag_color_diagnostics = '-fcolor-diagnostics'
@@ -389,6 +390,7 @@ if ( $vendor -match "msvc" )
# https://learn.microsoft.com/en-us/cpp/build/reference/compiler-options-listed-by-category?view=msvc-170
$flag_all_c = '/TC'
$flag_c11 = '/std:c11'
$flag_cpp17 = '/std:c++17'
$flag_all_cpp = '/TP'
$flag_charset_utf8 = '/utf-8'
$flag_compile = '/c'
+24
View File
@@ -13,3 +13,27 @@ The first command generates base component tables, segmented C++, single-header
It compiles and runs the C++17 and C11 single-header host smokes. The second command reuses the same mirror.
The verification wrapper copies them into the disposable mirror; it does not embed C or C++ source text.
## Parser bounds probe
`test/parser_bounds/test.cpp` is a GEN_TIME program. It includes `base/gen.cpp`. It is not part of `verify_baseline.ps1`.
From the project root:
```powershell
.\scripts\build.ps1 clang parser_bounds
```
The script builds and runs the probe. A pass prints `parser bounds passed` and `PASSED: parser_bounds`.
The probe covers helper positions, formatting skip at the slice end, empty and whitespace `parse_variable`, and two direct end-of-slice reads.
It does not prove in-slice syntax errors return.
## Lexer failure probe
```powershell
.\scripts\build.ps1 clang lexer_failures
```
A pass prints `lexer failures passed` and `PASSED: lexer_failures`.
The probe covers nine lexer failures, six successful sibling inputs, and a baseline variable after each failure.
It does not prove in-slice parser errors return.
+81
View File
@@ -0,0 +1,81 @@
#define GEN_TIME
#define GEN_DEFINE_LIBRARY_CODE_CONSTANTS
#define GEN_ENFORCE_STRONG_CODE_TYPES
#include "gen.cpp"
using namespace gen;
global int g_failures = 0;
internal void expect(int condition, Str label) {
if (condition == false) { log_fmt("FAIL %S\n", label); ++g_failures; }
}
internal void expect_public(Str def, Str needle, Str label)
{
Context ctx = {}; init(&ctx);
CodeVar parsed = parse_variable(def); expect(cast(Code, parsed) == Code_Invalid, label);
expect(_ctx->parser.messages != nullptr, label);
expect(_ctx->parser.messages->Scope == nullptr, label);
expect(_ctx->parser.messages->Content.Ptr != nullptr, label);
expect(str_contains(_ctx->parser.messages->Content, needle), label);
CodeVar again = parse_variable(txt("char const* baseline_message = \"GENCPP_BASELINE\";")); expect(again.ast != nullptr, label);
if (again.ast != nullptr) {
expect(again->Type == CT_Variable, label);
expect(str_contains(again->Name, txt("baseline_message")), label);
}
deinit(&ctx);
}
internal void expect_lex_fail(Str def, Str needle, Str label) {
Context ctx = {}; init(&ctx);
LexedInfo lexed = lex(&ctx, def);
expect(lexed.tokens.ptr == nullptr, label);
expect(lexed.messages != nullptr, label);
if (lexed.messages != nullptr) expect(str_contains(lexed.messages->content, needle), label);
deinit(&ctx);
}
internal void expect_lex_ok(Str def, Str label) {
Context ctx = {}; init(&ctx);
LexedInfo lexed = lex(&ctx, def);
expect(lexed.tokens.ptr != nullptr, label);
expect(lexed.tokens.num > 0, label);
expect(lexed.messages == nullptr, label);
deinit(&ctx);
}
int main()
{
expect_public(txt("#define FOO(... x)"), txt("Expected a ')' after '...'"), txt("L01-public"));
expect_public(txt("#define FOO(1)"), txt("Expected a '_' or alpha character"), txt("L02-public"));
expect_public(txt("#define FOO(a b)"), txt("Expected a comma after parameter"), txt("L03-public"));
expect_public(txt("#define FOO(a,"), txt("Expected a ')' after last_parameter"), txt("L04-public"));
expect_public(txt("#foo \\q"), txt("in preprocessor directive ("), txt("L05-public"));
expect_public(txt("#include foo"), txt("after #include"), txt("L06-public"));
expect_public(txt("#pragma \\q"), txt("in preprocessor directive '"), txt("L07-public"));
expect_public(txt("..x"), txt("invalid varadic argument"), txt("L08-public"));
expect_public(txt("@"), txt("Failed to lex token"), txt("L09-public"));
expect_lex_fail(txt("#define FOO(... x)"), txt("Expected a ')' after '...'"), txt("L01-lex"));
expect_lex_fail(txt("#define FOO(1)"), txt("Expected a '_' or alpha character"), txt("L02-lex"));
expect_lex_fail(txt("#define FOO(a b)"), txt("Expected a comma after parameter"), txt("L03-lex"));
expect_lex_fail(txt("#define FOO(a,"), txt("Expected a ')' after last_parameter"), txt("L04-lex"));
expect_lex_fail(txt("#foo \\q"), txt("in preprocessor directive ("), txt("L05-lex"));
expect_lex_fail(txt("#include foo"), txt("after #include"), txt("L06-lex"));
expect_lex_fail(txt("#pragma \\q"), txt("in preprocessor directive '"), txt("L07-lex"));
expect_lex_fail(txt("..x"), txt("invalid varadic argument"), txt("L08-lex"));
expect_lex_fail(txt("@"), txt("Failed to lex token"), txt("L09-lex"));
expect_lex_ok(txt("#define FOO(a, b) a"), txt("S01"));
expect_lex_ok(txt("#define FOO(...)"), txt("S02"));
expect_lex_ok(txt("#include \"a.h\""), txt("S03"));
expect_lex_ok(txt("#pragma once"), txt("S04"));
expect_lex_ok(txt("#foo bar"), txt("S05"));
expect_lex_ok(txt("..."), txt("S06"));
if (g_failures != 0) { log_fmt("lexer failures failed: %d\n", g_failures); return 1; }
log_fmt("lexer failures passed\n");
return 0;
}
+238
View File
@@ -0,0 +1,238 @@
#define GEN_TIME
#define GEN_DEFINE_LIBRARY_CODE_CONSTANTS
#define GEN_ENFORCE_STRONG_CODE_TYPES
#include "gen.cpp"
using namespace gen;
global s32 g_failures = 0;
internal inline void expect(int condition, Str label) {
if (condition == false) { log_fmt("FAIL %S\n", label); ++ g_failures; }
}
internal inline Token canary_at(Str text) {
Token tok = NullToken;
tok.Type = Tok_Comma;
tok.Text = text;
tok.Line = 99;
tok.Column = 99;
return tok;
}
internal inline Token ident(Str text) {
Token tok = NullToken;
tok.Type = Tok_Identifier;
tok.Text = text;
tok.Line = 1;
tok.Column = 1;
return tok;
}
internal b32 canaries_intact(Token* storage, s32 logical) {
Token expected = canary_at(txt("CANARY"));
for (s32 id = 0; id < 3; ++id) {
Token got = storage[logical + id];
if (got.Type != expected.Type || got.Text.Ptr != expected.Text.Ptr || got.Line != 99)
return false;
}
return true;
}
internal inline b32 points_at_canary(Token* got, Token* storage, s32 logical) {
return got == & storage[logical] || got == & storage[logical + 1] || got == & storage[logical + 2];
}
internal void plant(Token* storage, s32 logical, Token* src, s32 src_num) {
for (s32 id = 0; id < src_num; ++id) storage[id] = src[id];
for (s32 id = 0; id < 3; ++id) storage[logical + id] = canary_at(txt("CANARY"));
}
internal void bind(ParseContext* parser, Token* storage, s32 logical, s32 token_id) {
parser->tokens.ptr = storage;
parser->tokens.num = logical;
parser->token_id = token_id;
parser->scope = nullptr;
parser->messages = nullptr;
}
internal void expect_helper(ParseContext* parser, Token* storage, s32 logical, Str label) {
s32 before = parser->token_id;
Token* cur = lex_current (parser, lex_dont_skip_formatting);
Token* peek = lex_peek (parser, lex_dont_skip_formatting);
Token* next = lex_next (parser, lex_dont_skip_formatting);
Token* prev = lex_previous(parser, lex_dont_skip_formatting);
expect(parser->token_id == before, label);
expect(canaries_intact(storage, logical), label);
expect(points_at_canary(cur, storage, logical) == false, label);
expect(points_at_canary(peek, storage, logical) == false, label);
expect(points_at_canary(next, storage, logical) == false, label);
expect(points_at_canary(prev, storage, logical) == false, label);
expect(cur != nullptr && peek != nullptr && next != nullptr && prev != nullptr, label);
}
internal void expect_row_empty(Str label) {
Token storage[3] = {}; plant(storage, 0, nullptr, 0);
ParseContext parser = {}; bind(& parser, storage, 0, 0);
expect_helper(& parser, storage, 0, label);
expect(lex_current (& parser, lex_dont_skip_formatting)->Type == Tok_Invalid, label);
expect(lex_peek (& parser, lex_dont_skip_formatting)->Type == Tok_Invalid, label);
expect(lex_next (& parser, lex_dont_skip_formatting)->Type == Tok_Invalid, label);
expect(lex_previous(& parser, lex_dont_skip_formatting)->Type == Tok_Invalid, label);
}
internal void expect_row_single(Str label) {
Token src [1] = { ident(txt("only")) };
Token storage[4] = {}; plant(storage, 1, src, 1);
ParseContext parser = {}; bind(& parser, storage, 1, 0);
expect_helper(& parser, storage, 1, label);
expect(lex_current (& parser, lex_dont_skip_formatting)->Text.Ptr == src[0].Text.Ptr, label);
expect(lex_peek (& parser, lex_dont_skip_formatting)->Text.Ptr == src[0].Text.Ptr, label);
expect(lex_next (& parser, lex_dont_skip_formatting)->Type == Tok_Invalid, label);
expect(lex_previous(& parser, lex_dont_skip_formatting)->Type == Tok_Invalid, label);
}
internal void expect_row_ends(Str label) {
Token src [2] = { ident(txt("first")), ident(txt("last")) };
Token storage[5] = {}; plant(storage, 2, src, 2);
ParseContext parser = {};
bind(& parser, storage, 2, 0);
expect_helper(& parser, storage, 2, label);
expect(lex_current (& parser, lex_dont_skip_formatting)->Text.Ptr == src[0].Text.Ptr, label);
expect(lex_next (& parser, lex_dont_skip_formatting)->Text.Ptr == src[1].Text.Ptr, label);
expect(lex_previous(& parser, lex_dont_skip_formatting)->Type == Tok_Invalid, label);
bind(& parser, storage, 2, 1);
expect_helper(& parser, storage, 2, label);
expect(lex_current (& parser, lex_dont_skip_formatting)->Text.Ptr == src[1].Text.Ptr, label);
expect(lex_next (& parser, lex_dont_skip_formatting)->Type == Tok_Invalid, label);
expect(lex_previous(& parser, lex_dont_skip_formatting)->Text.Ptr == src[0].Text.Ptr, label);
bind(& parser, storage, 2, 2);
expect_helper(& parser, storage, 2, label);
expect(lex_current (& parser, lex_dont_skip_formatting)->Type == Tok_Invalid, label);
expect(lex_peek (& parser, lex_dont_skip_formatting)->Type == Tok_Invalid, label);
expect(lex_next (& parser, lex_dont_skip_formatting)->Type == Tok_Invalid, label);
expect(lex_previous(& parser, lex_dont_skip_formatting)->Text.Ptr == src[1].Text.Ptr, label);
}
internal void expect_skip_end(Str label)
{
Token newlines[2] = { NullToken, NullToken };
newlines[0].Type = Tok_NewLine; newlines[0].Text = txt("\n");
newlines[1].Type = Tok_NewLine; newlines[1].Text = txt("\n");
Token storage[5] = {}; plant(storage, 2, newlines, 2);
ParseContext parser = {}; bind(& parser, storage, 2, 0);
Token* got = lex_current(&parser, lex_skip_formatting);
expect(got->Type == Tok_Invalid, label);
expect(parser.token_id == 2, label);
expect(canaries_intact(storage, 2), label);
expect(points_at_canary(got, storage, 2) == false, label);
bind(& parser, storage, 2, 0); got = lex_peek(&parser, lex_skip_formatting);
expect(parser.token_id == 0, label);
expect(got->Type == Tok_Invalid, label);
expect(canaries_intact(storage, 2), label);
Token comments[1] = { NullToken };
comments[0].Type = Tok_Comment; comments[0].Text = txt("// note");
plant(storage, 1, comments, 1); bind(& parser, storage, 1, 0);
got = lex_current(&parser, lex_skip_formatting);
expect(got->Type == Tok_Invalid, label);
expect(parser.token_id == 1, label);
expect(canaries_intact(storage, 1), label);
expect(points_at_canary(got, storage, 1) == false, label);
}
internal void expect_boundary(Str def, Str label, Str mark) {
Context ctx = {};
init(&ctx);
CodeVar parsed = parse_variable(def);
expect(cast(Code, parsed) == Code_Invalid, label);
expect(_ctx->parser.messages != nullptr, label);
expect(_ctx->parser.messages->Scope == nullptr, label);
expect(_ctx->parser.messages->Content.Ptr != nullptr && _ctx->parser.messages->Content.Len > 0, label);
expect(str_contains(_ctx->parser.messages->Content, mark), label);
CodeVar again = parse_variable(txt("char const* baseline_message = \"GENCPP_BASELINE\";"));
expect(again.ast != nullptr, label);
if (again.ast != nullptr) {
expect(again->Type == CT_Variable, label);
expect(str_contains(again->Name, txt("baseline_message")), label);
}
deinit(&ctx);
}
internal void expect_eat_end(Str label) {
Context ctx = {}; init(&ctx);
Token src [1] = { ident(txt("x")) };
Token storage[4] = {};
plant(storage, 1, src, 1); bind(& ctx.parser, storage, 1, 1);
b32 ate = lex__eat(&ctx, & ctx.parser, Tok_Identifier);
expect(ate == false, label);
expect(ctx.parser.token_id <= ctx.parser.tokens.num, label);
expect(canaries_intact(storage, 1), label);
deinit(& ctx);
}
internal void expect_direct(Str label) {
Context ctx = {}; init(& ctx);
Token src [2] = { ident(txt("int")), ident(txt("name")) };
Token storage[5] = {}; plant(storage, 2, src, 2);
bind(& ctx.parser, storage, 2, 0);
Code got = parse_operator_function_or_variable(& ctx, false, NullCode, NullCode);
expect(canaries_intact(storage, 2), label);
(void)got;
Token op[2] = {};
op[0] = NullToken;
op[0].Type = Tok_Decl_Operator;
op[0].Text = txt("operator");
op[0].Line = 1;
op[1] = ident(operator_to_str(Op_Delete));
plant(storage, 2, op, 2);
ctx.parser = {}; bind(& ctx.parser, storage, 2, 0);
CodeOperator op_got = parse_operator_after_ret_type(& ctx, ModuleFlag_None, NullCode, NullCode, NullCode);
expect(cast(Code, op_got) == Code_Invalid, label);
expect(canaries_intact(storage, 2), label);
deinit(& ctx);
}
internal void expect_builder(Str label) {
Token storage[3] = {}; plant(storage, 0, nullptr, 0);
ParseContext parser = {}; bind(& parser, storage, 0, 0);
StrBuilder built = parser_to_strbuilder(& parser, _ctx->Allocator_Temp);
Str text = strbuilder_to_str(built);
expect(str_contains(text, txt("empty")), label);
expect(canaries_intact(storage, 0), label);
strbuilder_free(& built);
}
int main()
{
using namespace gen;
Context ctx = {}; init(& ctx);
expect_row_empty (txt("empty"));
expect_row_single(txt("single"));
expect_row_ends (txt("ends"));
expect_skip_end (txt("skip"));
expect_builder (txt("builder"));
deinit(& ctx);
Str null_def = { nullptr, 0 };
Str null_ptr = { nullptr, 4 };
expect_boundary(null_def, txt("null-len"), txt("gen::parse_variable: length must greater than 0"));
expect_boundary(null_ptr, txt("null-ptr"), txt("gen::parse_variable: def was null"));
expect_boundary(txt(""), txt("zero-len"), txt("gen::parse_variable: length must greater than 0"));
expect_boundary(txt(" \n\t"), txt("whitespace"), txt("no tokens found"));
expect_boundary(txt("// note\n"), txt("comment"), txt("only formatting tokens"));
expect_eat_end (txt("eat"));
expect_direct (txt("direct"));
if (g_failures != 0) {
log_fmt("parser bounds failed: %d\n", g_failures); return 1;
}
log_fmt("parser bounds passed\n");
return 0;
}