Parser: Started to support proper errors as values, error recovery, and verbose testing of upfront and parsing interface.
Also fix to c_library generation for Genric_ on code body.
This commit is contained in:
1 parent
ed755e34f1
commit
8ba02de6ed
9 files changed
+1982
-604
No files matched your search
@@ -371,8 +371,7 @@ struct ParseStackNode
|
||||
// TODO(Ed): When an error occurs, the parse stack is not released and instead the scope is left dangling.
|
||||
};
|
||||
|
||||
struct ParseOpts
|
||||
{
|
||||
struct ParseOpts {
|
||||
AllocatorInfo backing_msgs;
|
||||
AllocatorInfo backing_tokens;
|
||||
AllocatorInfo backing_ast;
|
||||
@@ -380,34 +379,53 @@ struct ParseOpts
|
||||
|
||||
ParseInfo wip_parse_str( LexedInfo lexed, ParseOpts* opts GEN_PARAM_DEFAULT );
|
||||
|
||||
GEN_API CodeClass parse_class ( Str class_def );
|
||||
GEN_API CodeConstructor parse_constructor ( Str constructor_def );
|
||||
GEN_API CodeDefine parse_define ( Str define_def );
|
||||
GEN_API CodeDestructor parse_destructor ( Str destructor_def );
|
||||
GEN_API CodeEnum parse_enum ( Str enum_def );
|
||||
GEN_API CodeBody parse_export_body ( Str export_def );
|
||||
GEN_API CodeExtern parse_extern_link ( Str exten_link_def );
|
||||
GEN_API CodeFriend parse_friend ( Str friend_def );
|
||||
GEN_API CodeFn parse_function ( Str fn_def );
|
||||
|
||||
struct Opts_parse_global_body
|
||||
{
|
||||
struct Opts_parse {
|
||||
Context* ctx;
|
||||
ParseInfo* info;
|
||||
};
|
||||
GEN_API void parse_global_body_base( Context* ctx, Str body_def, ParseInfo* info );
|
||||
GEN_API CodeBody parse_global_body( Str body_def, Opts_parse_global_body opts GEN_PARAM_DEFAULT );
|
||||
typedef Opts_parse Opts_parse_global_body;
|
||||
|
||||
GEN_API CodeNS parse_namespace ( Str namespace_def );
|
||||
GEN_API CodeOperator parse_operator ( Str operator_def );
|
||||
GEN_API CodeOpCast parse_operator_cast( Str operator_def );
|
||||
GEN_API CodeStruct parse_struct ( Str struct_def );
|
||||
GEN_API CodeTemplate parse_template ( Str template_def );
|
||||
GEN_API CodeTypename parse_type ( Str type_def );
|
||||
GEN_API CodeTypedef parse_typedef ( Str typedef_def );
|
||||
GEN_API CodeUnion parse_union ( Str union_def );
|
||||
GEN_API CodeUsing parse_using ( Str using_def );
|
||||
GEN_API CodeVar parse_variable ( Str var_def );
|
||||
GEN_API void parse_class_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_constructor_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_define_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_destructor_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_enum_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_export_body_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_extern_link_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_friend_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_function_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_global_body_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_namespace_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_operator_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_operator_cast_base(Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_struct_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_template_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_type_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_typedef_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_union_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_using_base (Context* ctx, Str def, ParseInfo* info);
|
||||
GEN_API void parse_variable_base (Context* ctx, Str def, ParseInfo* info);
|
||||
|
||||
GEN_API CodeClass parse_class (Str class_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeConstructor parse_constructor (Str constructor_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeDefine parse_define (Str define_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeDestructor parse_destructor (Str destructor_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeEnum parse_enum (Str enum_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeBody parse_export_body (Str export_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeExtern parse_extern_link (Str exten_link_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeFriend parse_friend (Str friend_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeFn parse_function (Str fn_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeBody parse_global_body (Str body_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeNS parse_namespace (Str namespace_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeOperator parse_operator (Str operator_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeOpCast parse_operator_cast(Str operator_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeStruct parse_struct (Str struct_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeTemplate parse_template (Str template_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeTypename parse_type (Str type_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeTypedef parse_typedef (Str typedef_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeUnion parse_union (Str union_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeUsing parse_using (Str using_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
GEN_API CodeVar parse_variable (Str var_def, Opts_parse opts GEN_PARAM_DEFAULT);
|
||||
|
||||
#pragma endregion Parsing
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
ParseInfo wip_parse_str(LexedInfo lexed, ParseOpts* opts)
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
ParseInfo info = struct_zero(ParseInfo);
|
||||
Context* ctx = _ctx; ParseInfo info = struct_zero(ParseInfo);
|
||||
|
||||
if (lexed.tokens.num == 0 && lexed.tokens.ptr == nullptr) {
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
@@ -26,92 +25,74 @@ ParseInfo wip_parse_str(LexedInfo lexed, ParseOpts* opts)
|
||||
|
||||
// TODO(Ed): ParseInfo should be set to the parser context.
|
||||
|
||||
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);
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
ctx->parser.tokens = lexed.tokens; {
|
||||
ctx->parser.lexed_messages = lexed.messages;
|
||||
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;
|
||||
}
|
||||
|
||||
CodeClass parse_class( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeClass result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
internal b32 parse_base_begin(Context* ctx, Str def, ParseInfo* info, char const* function_name) {
|
||||
if (ctx == nullptr || info == nullptr) GEN_FATAL("parse base: null context or info");
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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;
|
||||
info[0] = struct_zero(ParseInfo);
|
||||
info->result = Code_Invalid;
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (_check_parse_args(ctx, def, function_name) == false) {
|
||||
info->messages = ctx->parser.messages;
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
ParseStackNode scope = NullScope;
|
||||
parser_push(& ctx->parser, & scope);
|
||||
result = (CodeClass) parse_class_struct( ctx, Tok_Decl_Class, parser_not_inplace_def );
|
||||
parser_pop(& ctx->parser);
|
||||
info->lexed = lex(ctx, def);
|
||||
ctx->parser.lexed_messages = info->lexed.messages;
|
||||
if (info->lexed.tokens.num == 0) {
|
||||
info->messages = ctx->parser.messages;
|
||||
return false;
|
||||
}
|
||||
done:
|
||||
return result;
|
||||
|
||||
ctx->parser.tokens = info->lexed.tokens;
|
||||
return true;
|
||||
}
|
||||
|
||||
CodeConstructor parse_constructor(Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeConstructor result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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;
|
||||
void parse_class_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeClass result = InvalidCode;
|
||||
if (parse_base_begin(ctx, def, info, "parse_class") == false) return;
|
||||
/*Parse*/ {
|
||||
ParseStackNode scope = NullScope; parser_push(& ctx->parser, & scope);
|
||||
result = (CodeClass) parse_class_struct( ctx, Tok_Decl_Class, parser_not_inplace_def );
|
||||
parser_pop(& ctx->parser);
|
||||
}
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
{
|
||||
ParseStackNode scope = NullScope;
|
||||
parser_push(& ctx->parser, & scope);
|
||||
CodeClass parse_class(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_class: no context");
|
||||
ParseInfo* info = resolved.info; if (info == nullptr) info = & ctx->parse_info;
|
||||
parse_class_base(ctx, def, info); return cast(CodeClass, info->result);
|
||||
}
|
||||
|
||||
// TODO(Ed): Constructors can have prefix attributes
|
||||
void parse_constructor_base(Context* ctx, Str def, ParseInfo* info)
|
||||
{
|
||||
CodeConstructor result = InvalidCode;
|
||||
if (parse_base_begin(ctx, def, info, "parse_constructor") == false) return;
|
||||
/*Parse*/ {
|
||||
ParseStackNode scope = NullScope; parser_push(& ctx->parser, & scope);
|
||||
|
||||
CodeSpecifiers specifiers = NullCode;
|
||||
Specifier specs_found[ 16 ] = { Spec_NumSpecifiers };
|
||||
s32 NumSpecifiers = 0;
|
||||
|
||||
while ( left && tok_is_specifier(currtok) )
|
||||
{
|
||||
Specifier spec = str_to_specifier( currtok.Text );
|
||||
|
||||
b32 ignore_spec = false;
|
||||
|
||||
switch ( spec )
|
||||
// TODO(Ed): Constructors can have prefix attributes
|
||||
CodeSpecifiers specifiers = NullCode;
|
||||
Specifier specs_found[ 16 ] = { Spec_NumSpecifiers };
|
||||
s32 NumSpecifiers = 0;
|
||||
while ( left && tok_is_specifier(currtok) )
|
||||
{
|
||||
b32 ignore_spec = false;
|
||||
Specifier spec = str_to_specifier( currtok.Text );
|
||||
switch ( spec ) {
|
||||
case Spec_Constexpr :
|
||||
case Spec_Explicit:
|
||||
case Spec_Inline :
|
||||
@@ -126,227 +107,160 @@ 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);
|
||||
goto done;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
return;
|
||||
}
|
||||
|
||||
// Every specifier after would be considered part of the type type signature
|
||||
if (ignore_spec) break;
|
||||
|
||||
specs_found[ NumSpecifiers ] = spec;
|
||||
++ NumSpecifiers;
|
||||
eat( currtok.Type );
|
||||
}
|
||||
|
||||
// Every specifier after would be considered part of the type type signature
|
||||
if (ignore_spec)
|
||||
break;
|
||||
|
||||
specs_found[ NumSpecifiers ] = spec;
|
||||
NumSpecifiers++;
|
||||
eat( currtok.Type );
|
||||
if ( NumSpecifiers ) {
|
||||
specifiers = def_specifiers_arr( NumSpecifiers, specs_found );
|
||||
// <specifiers> ...
|
||||
}
|
||||
result = parser_parse_constructor(ctx, specifiers);
|
||||
parser_pop(& ctx->parser);
|
||||
}
|
||||
|
||||
if ( NumSpecifiers ) {
|
||||
specifiers = def_specifiers_arr( NumSpecifiers, specs_found );
|
||||
// <specifiers> ...
|
||||
}
|
||||
|
||||
result = parser_parse_constructor(ctx, specifiers);
|
||||
parser_pop(& ctx->parser);
|
||||
}
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeDefine parse_define( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeConstructor parse_constructor(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_constructor: no context");
|
||||
ParseInfo* info = resolved.info; if (info == nullptr) info = & ctx->parse_info;
|
||||
parse_constructor_base(ctx, def, info); return cast(CodeConstructor, info->result);
|
||||
}
|
||||
|
||||
void parse_define_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeDefine result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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 (parse_base_begin(ctx, def, info, "parse_define") == false) return;
|
||||
/*Parse*/ {
|
||||
ParseStackNode scope = NullScope; parser_push(& ctx->parser, & scope);
|
||||
result = parser_parse_define(ctx);
|
||||
parser_pop(& ctx->parser);
|
||||
}
|
||||
|
||||
{
|
||||
ParseStackNode scope = NullScope;
|
||||
parser_push(& ctx->parser, & scope);
|
||||
result = parser_parse_define(ctx);
|
||||
parser_pop(& ctx->parser);
|
||||
}
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeDestructor parse_destructor( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeDefine parse_define(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_define: no context");
|
||||
ParseInfo* info = resolved.info; if (info == nullptr) info = & ctx->parse_info;
|
||||
parse_define_base(ctx, def, info); return cast(CodeDefine, info->result);
|
||||
}
|
||||
|
||||
void parse_destructor_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeDestructor result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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 (parse_base_begin(ctx, def, info, "parse_destructor") == false) return;
|
||||
|
||||
// TODO(Ed): Destructors can have prefix attributes
|
||||
// TODO(Ed): Destructors can have virtual
|
||||
|
||||
result = parser_parse_destructor(ctx, NullCode);
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeEnum parse_enum( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeDestructor parse_destructor(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_destructor: no context");
|
||||
ParseInfo* info = resolved.info; if (info == nullptr) info = & ctx->parse_info;
|
||||
parse_destructor_base(ctx, def, info); return cast(CodeDestructor, info->result);
|
||||
}
|
||||
|
||||
void parse_enum_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeEnum result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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 (parse_base_begin(ctx, def, info, "parse_enum") == false) return;
|
||||
|
||||
result = parser_parse_enum(ctx, parser_not_inplace_def);
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeBody parse_export_body( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeEnum parse_enum(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_enum: no context");
|
||||
ParseInfo* info = resolved.info; if (info == nullptr) info = & ctx->parse_info;
|
||||
parse_enum_base(ctx, def, info); return cast(CodeEnum, info->result);
|
||||
}
|
||||
|
||||
void parse_export_body_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeBody result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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 (parse_base_begin(ctx, def, info, "parse_export_body") == false) return;
|
||||
|
||||
result = parser_parse_export_body(ctx);
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeExtern parse_extern_link( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeBody parse_export_body(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_export_body: no context");
|
||||
ParseInfo* info = resolved.info; if (info == nullptr) info = & ctx->parse_info;
|
||||
parse_export_body_base(ctx, def, info); return cast(CodeBody, info->result);
|
||||
}
|
||||
|
||||
void parse_extern_link_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeExtern result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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 (parse_base_begin(ctx, def, info, "parse_extern_link") == false) return;
|
||||
|
||||
result = parser_parse_extern_link(ctx);
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeFriend parse_friend( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeExtern parse_extern_link(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_extern_link: no context");
|
||||
ParseInfo* info = resolved.info; if (info == nullptr) info = & ctx->parse_info;
|
||||
parse_extern_link_base(ctx, def, info); return cast(CodeExtern, info->result);
|
||||
}
|
||||
|
||||
void parse_friend_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeFriend result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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 (parse_base_begin(ctx, def, info, "parse_friend") == false) return;
|
||||
|
||||
result = parser_parse_friend(ctx);
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeFn parse_function( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeFn result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
CodeFriend parse_friend(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_friend: no context");
|
||||
ParseInfo* info = resolved.info; if (info == nullptr) info = & ctx->parse_info;
|
||||
parse_friend_base(ctx, def, info); return cast(CodeFriend, info->result);
|
||||
}
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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;
|
||||
}
|
||||
void parse_function_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeFn result = InvalidCode;
|
||||
if (parse_base_begin(ctx, def, info, "parse_function") == false) return;
|
||||
|
||||
result = (CodeFn) parser_parse_function(ctx);
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeFn parse_function(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts);
|
||||
Context* ctx = resolved.ctx;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_function: no context");
|
||||
ParseInfo* info = resolved.info; if (info == nullptr) info = & ctx->parse_info;
|
||||
parse_function_base(ctx, def, info); return cast(CodeFn, info->result);
|
||||
}
|
||||
|
||||
void parse_global_body_base( Context* ctx, Str def, ParseInfo* info )
|
||||
{
|
||||
LexedInfo lexed;
|
||||
CodeBody body;
|
||||
|
||||
if ( ctx == nullptr || info == nullptr )
|
||||
GEN_FATAL( "parse_global_body_base: null context or info" );
|
||||
LexedInfo lexed; CodeBody body;
|
||||
if ( ctx == nullptr || info == nullptr ) GEN_FATAL( "parse_global_body_base: null context or info" );
|
||||
|
||||
info[0] = struct_zero( ParseInfo );
|
||||
lexed = struct_zero( LexedInfo );
|
||||
@@ -362,6 +276,7 @@ void parse_global_body_base( Context* ctx, Str def, ParseInfo* info )
|
||||
|
||||
lexed = lex( ctx, def );
|
||||
info->lexed = lexed;
|
||||
ctx->parser.lexed_messages = lexed.messages;
|
||||
if ( lexed.tokens.num == 0 ) {
|
||||
info->result = cast( Code, body );
|
||||
info->messages = ctx->parser.messages;
|
||||
@@ -378,286 +293,179 @@ void parse_global_body_base( Context* ctx, Str def, ParseInfo* info )
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeBody parse_global_body( Str def, Opts_parse_global_body opts ) {
|
||||
Opts_parse_global_body resolved = get_optional( opts );
|
||||
Context* ctx = resolved.ctx;
|
||||
ParseInfo* info;
|
||||
|
||||
if ( ctx == nullptr ) ctx = _ctx;
|
||||
if ( ctx == nullptr ) GEN_FATAL( "parse_global_body: no context" );
|
||||
info = resolved.info; if ( info == nullptr ) info = & ctx->parse_info;
|
||||
|
||||
parse_global_body_base( ctx, def, info );
|
||||
return cast( CodeBody, info->result );
|
||||
CodeBody parse_global_body( Str def, Opts_parse opts ) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx;
|
||||
if ( ctx == nullptr ) ctx = _ctx; if ( ctx == nullptr ) GEN_FATAL( "parse_global_body: no context" );
|
||||
ParseInfo* info = resolved.info; if ( info == nullptr ) info = & ctx->parse_info;
|
||||
parse_global_body_base( ctx, def, info ); return cast( CodeBody, info->result );
|
||||
}
|
||||
|
||||
CodeNS parse_namespace( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeNS result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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;
|
||||
}
|
||||
void parse_namespace_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeNS result = InvalidCode;
|
||||
if (parse_base_begin(ctx, def, info, "parse_namespace") == false) return;
|
||||
|
||||
result = parser_parse_namespace(ctx);
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeOperator parse_operator( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeNS parse_namespace(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_namespace: no context");
|
||||
ParseInfo* info = resolved.info; if (info == nullptr) info = &ctx->parse_info;
|
||||
parse_namespace_base(ctx, def, info); return cast(CodeNS, info->result);
|
||||
}
|
||||
|
||||
void parse_operator_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeOperator result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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 (parse_base_begin(ctx, def, info, "parse_operator") == false) return;
|
||||
|
||||
result = (CodeOperator) parser_parse_operator(ctx);
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeOpCast parse_operator_cast( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeOperator parse_operator(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx; ParseInfo* info;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_operator: no context");
|
||||
info = resolved.info; if (info == nullptr) info = &ctx->parse_info;
|
||||
parse_operator_base(ctx, def, info); return cast(CodeOperator, info->result);
|
||||
}
|
||||
|
||||
void parse_operator_cast_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeOpCast result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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 (parse_base_begin(ctx, def, info, "parse_operator_cast") == false) return;
|
||||
|
||||
result = parser_parse_operator_cast(ctx, NullCode);
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeStruct parse_struct( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeOpCast parse_operator_cast(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx; ParseInfo* info;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_operator_cast: no context");
|
||||
info = resolved.info; if (info == nullptr) info = &ctx->parse_info;
|
||||
parse_operator_cast_base(ctx, def, info); return cast(CodeOpCast, info->result);
|
||||
}
|
||||
|
||||
void parse_struct_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeStruct result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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 (parse_base_begin(ctx, def, info, "parse_struct") == false) return;
|
||||
/*Parse*/ {
|
||||
ParseStackNode scope = NullScope;
|
||||
parser_push(& ctx->parser, & scope);
|
||||
result = (CodeStruct) parse_class_struct( ctx, Tok_Decl_Struct, parser_not_inplace_def );
|
||||
parser_pop(& ctx->parser);
|
||||
}
|
||||
|
||||
{
|
||||
ParseStackNode scope = NullScope;
|
||||
parser_push(& ctx->parser, & scope);
|
||||
result = (CodeStruct) parse_class_struct( ctx, Tok_Decl_Struct, parser_not_inplace_def );
|
||||
parser_pop(& ctx->parser);
|
||||
}
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeTemplate parse_template( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeStruct parse_struct(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx; ParseInfo* info;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_struct: no context");
|
||||
info = resolved.info; if (info == nullptr) info = &ctx->parse_info;
|
||||
parse_struct_base(ctx, def, info); return cast(CodeStruct, info->result);
|
||||
}
|
||||
|
||||
void parse_template_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeTemplate result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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 (parse_base_begin(ctx, def, info, "parse_template") == false) return;
|
||||
|
||||
result = parser_parse_template(ctx);
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeTypename parse_type( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeTemplate parse_template(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx; ParseInfo* info;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_template: no context");
|
||||
info = resolved.info; if (info == nullptr) info = &ctx->parse_info;
|
||||
parse_template_base(ctx, def, info); return cast(CodeTemplate, info->result);
|
||||
}
|
||||
|
||||
void parse_type_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeTypename result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
if (parse_base_begin(ctx, def, info, "parse_type") == false) return;
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
result = parser_parse_type( ctx, parser_not_from_template, nullptr);
|
||||
done:
|
||||
return result;
|
||||
result = parser_parse_type(ctx, parser_not_from_template, nullptr);
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeTypedef parse_typedef( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeTypename parse_type(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx; ParseInfo* info;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_type: no context");
|
||||
info = resolved.info; if (info == nullptr) info = &ctx->parse_info;
|
||||
parse_type_base(ctx, def, info); return cast(CodeTypename, info->result);
|
||||
}
|
||||
|
||||
void parse_typedef_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeTypedef result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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 (parse_base_begin(ctx, def, info, "parse_typedef") == false) return;
|
||||
|
||||
result = parser_parse_typedef(ctx);
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeUnion parse_union( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeTypedef parse_typedef(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx; ParseInfo* info;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_typedef: no context");
|
||||
info = resolved.info; if (info == nullptr) info = &ctx->parse_info;
|
||||
parse_typedef_base(ctx, def, info); return cast(CodeTypedef, info->result);
|
||||
}
|
||||
|
||||
void parse_union_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeUnion result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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 (parse_base_begin(ctx, def, info, "parse_union") == false) return;
|
||||
|
||||
result = parser_parse_union(ctx, parser_not_inplace_def);
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeUsing parse_using( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeUnion parse_union(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx; ParseInfo* info;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_union: no context");
|
||||
info = resolved.info; if (info == nullptr) info = &ctx->parse_info;
|
||||
parse_union_base(ctx, def, info); return cast(CodeUnion, info->result);
|
||||
}
|
||||
|
||||
void parse_using_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeUsing result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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 (parse_base_begin(ctx, def, info, "parse_using") == false) return;
|
||||
|
||||
result = parser_parse_using(ctx);
|
||||
done:
|
||||
return result;
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
CodeVar parse_variable( Str def )
|
||||
{
|
||||
// TODO(Ed): Lift this.
|
||||
Context* ctx = _ctx;
|
||||
CodeVar result = InvalidCode;
|
||||
LexedInfo lexed = struct_zero(LexedInfo);
|
||||
CodeUsing parse_using(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx; ParseInfo* info;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_using: no context");
|
||||
info = resolved.info; if (info == nullptr) info = &ctx->parse_info;
|
||||
parse_using_base(ctx, def, info); return cast(CodeUsing, info->result);
|
||||
}
|
||||
|
||||
ctx->parser = struct_zero(ParseContext);
|
||||
if (check_parse_args(def) == false)
|
||||
goto done;
|
||||
|
||||
lexed = lex(ctx, def);
|
||||
ctx->parser.tokens = lexed.tokens;
|
||||
if (lexed.messages != nullptr || ctx->parser.tokens.num == 0) {
|
||||
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)) {
|
||||
void parse_variable_base(Context* ctx, Str def, ParseInfo* info) {
|
||||
CodeVar result = InvalidCode;
|
||||
if (parse_base_begin(ctx, def, info, "parse_variable") == false) return;
|
||||
if (parser_slice_is_formatting_only(& ctx->parser)) {
|
||||
parser_record_failure(ctx, txt("parse_variable: only formatting tokens"));
|
||||
goto done;
|
||||
}
|
||||
else {
|
||||
result = parser_parse_variable(ctx);
|
||||
}
|
||||
info->result = cast(Code, result);
|
||||
info->messages = ctx->parser.messages;
|
||||
}
|
||||
|
||||
result = parser_parse_variable(ctx);
|
||||
done:
|
||||
return result;
|
||||
CodeVar parse_variable(Str def, Opts_parse opts) {
|
||||
Opts_parse resolved = get_optional(opts); Context* ctx = resolved.ctx; ParseInfo* info;
|
||||
if (ctx == nullptr) ctx = _ctx; if (ctx == nullptr) GEN_FATAL("parse_variable: no context");
|
||||
info = resolved.info; if (info == nullptr) info = &ctx->parse_info;
|
||||
parse_variable_base(ctx, def, info); return cast(CodeVar, info->result);
|
||||
}
|
||||
|
||||
// Undef helper macros
|
||||
|
||||
+439
-29
@@ -104,14 +104,49 @@ void parser_record_failure(Context* ctx, Str text) {
|
||||
ctx->parser.messages = msg;
|
||||
}
|
||||
|
||||
internal b32 parser_is_recoverable_body(ParseContext* parser) {
|
||||
ParseStackNode* scope = parser->scope;
|
||||
b32 in_function_body = false;
|
||||
while ( scope )
|
||||
{
|
||||
if ( scope->code_rel )
|
||||
{
|
||||
CodeType type = scope->code_rel->Type;
|
||||
if ( type == CT_Function_Body ) {
|
||||
in_function_body = true;
|
||||
scope = scope->prev;
|
||||
continue;
|
||||
}
|
||||
if ( in_function_body && type != CT_Global_Body )
|
||||
return false;
|
||||
return type == CT_Global_Body || type == CT_Namespace_Body || type == CT_Class_Body || type == CT_Struct_Body;
|
||||
}
|
||||
scope = scope->prev;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool lex__eat(Context* ctx, ParseContext* parser, TokType type)
|
||||
{
|
||||
b32 ate = false;
|
||||
Token at_idx;
|
||||
b32 not_accepted;
|
||||
b32 is_identifier;
|
||||
if (parser->tokens.num - parser->token_id <= 0)
|
||||
s32 start_id = parser->token_id;
|
||||
if ( parser->token_mismatch )
|
||||
goto done;
|
||||
if (parser->tokens.num - parser->token_id <= 0)
|
||||
{
|
||||
if ( parser_is_recoverable_body(parser) ) {
|
||||
StrBuilder message = strbuilder_fmt_buf(ctx->Allocator_Temp, "Parse Error, TokArray::eat, Expected: ' %S ' at end of token slice\n%SB"
|
||||
, toktype_to_str(type)
|
||||
, parser_to_strbuilder(parser, ctx->Allocator_Temp)
|
||||
);
|
||||
parser_record_failure(ctx, strbuilder_to_str(message));
|
||||
parser->token_mismatch = true;
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
at_idx = parser->tokens.ptr[ parser->token_id ];
|
||||
if ( ( at_idx.Type == Tok_NewLine && type != Tok_NewLine )
|
||||
@@ -129,15 +164,26 @@ bool lex__eat(Context* ctx, ParseContext* parser, TokType type)
|
||||
}
|
||||
if ( not_accepted )
|
||||
{
|
||||
Token tok = * lex_current( parser, lex_skip_formatting );
|
||||
log_failure( "Parse Error, TokArray::eat, Expected: ' %S ' not ' %S ' (%d, %d)`\n%SB"
|
||||
parser->token_id = start_id;
|
||||
Token tok = * lex_current( parser, lex_skip_formatting );
|
||||
StrBuilder message = strbuilder_fmt_buf(ctx->Allocator_Temp, "Parse Error, TokArray::eat, Expected: ' %S ' not ' %S ' (%d, %d)`\n%SB"
|
||||
, toktype_to_str(type)
|
||||
, at_idx.Text
|
||||
, tok.Line
|
||||
, tok.Column
|
||||
, parser_to_strbuilder(parser, ctx->Allocator_Temp)
|
||||
);
|
||||
GEN_DEBUG_TRAP();
|
||||
Str message_text = strbuilder_to_str(message);
|
||||
if ( parser_is_recoverable_body(parser) ) {
|
||||
parser_record_failure(ctx, message_text);
|
||||
parser->token_mismatch = true;
|
||||
}
|
||||
else if ( parser->lexed_messages )
|
||||
parser_record_failure(ctx, message_text);
|
||||
else {
|
||||
log_failure("%S", message_text);
|
||||
GEN_DEBUG_TRAP();
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
@@ -206,7 +252,7 @@ done:
|
||||
# define nexttok (* lex_next( & ctx->parser, lex_skip_formatting ))
|
||||
# define nexttok_noskip (* lex_next( & ctx->parser, lex_dont_skip_formatting))
|
||||
# define eat( Type_ ) lex__eat(ctx, & ctx->parser, Type_ )
|
||||
# define left ( ctx->parser.tokens.num - ctx->parser.token_id )
|
||||
# define left ( ! ctx->parser.token_mismatch && ctx->parser.tokens.num - ctx->parser.token_id )
|
||||
|
||||
#if GEN_COMPILER_CPP
|
||||
# define def_assign( ... ) { __VA_ARGS__ }
|
||||
@@ -233,6 +279,8 @@ done:
|
||||
|
||||
// Procedure Forwards ( Entire parser internal parser interface )
|
||||
|
||||
internal void parser_recover_body_member(Context* ctx, CodeBody body, Token member_start_token, s32 member_start_id, b32 allow_comma);
|
||||
|
||||
internal Code parse_array_decl (Context* ctx);
|
||||
internal CodeAttributes parse_attributes (Context* ctx);
|
||||
internal CodeComment parse_comment (Context* ctx);
|
||||
@@ -533,8 +581,7 @@ StrBuilder parser_strip_formatting(Context* ctx, Str raw_text, bool preserve_new
|
||||
return content;
|
||||
}
|
||||
|
||||
StrBuilder parser_strip_formatting_2(TokenSlice tokens)
|
||||
{
|
||||
StrBuilder parser_strip_formatting_2(TokenSlice tokens) {
|
||||
// TODO(Ed): Use this to produce strings for validation purposes. We shouldn't serialize down from tokens once we start storing token slices for content.
|
||||
StrBuilder result = struct_zero(StrBuilder);
|
||||
return result;
|
||||
@@ -870,8 +917,14 @@ CodeBody parse_class_struct_body(Context* ctx, TokType which, Token name)
|
||||
else
|
||||
result->Type = CT_Struct_Body;
|
||||
|
||||
scope.code_rel = cast(Code, result);
|
||||
|
||||
while ( left && currtok_noskip.Type != Tok_BraceCurly_Close )
|
||||
{
|
||||
ParseMessage* messages_before_member = ctx->parser.messages;
|
||||
s32 member_start_id = ctx->parser.token_id;
|
||||
Token member_start_token = currtok_noskip;
|
||||
|
||||
Code member = Code_Invalid;
|
||||
CodeAttributes attributes = { nullptr };
|
||||
CodeSpecifiers specifiers = { nullptr };
|
||||
@@ -1180,11 +1233,26 @@ CodeBody parse_class_struct_body(Context* ctx, TokType which, Token name)
|
||||
break;
|
||||
}
|
||||
|
||||
if ( member == Code_Invalid )
|
||||
b32 member_is_nested_scope = member != Code_Invalid && (member->Type == CT_Class || member->Type == CT_Struct);
|
||||
b32 member_failed = ctx->parser.token_mismatch || (ctx->parser.messages != messages_before_member && member_is_nested_scope == false);
|
||||
if (member_failed) {
|
||||
if ( ctx->parser.messages != messages_before_member ) ctx->parser.messages->Next = messages_before_member;
|
||||
ctx->parser.token_id = member_start_id;
|
||||
ctx->parser.token_mismatch = false;
|
||||
member = Code_Invalid;
|
||||
}
|
||||
|
||||
if (member == Code_Invalid)
|
||||
{
|
||||
log_failure( "Failed to parse member\n%SB", parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) );
|
||||
parser_pop(& ctx->parser);
|
||||
return InvalidCode;
|
||||
if ( member_failed == false ) {
|
||||
log_failure( "Failed to parse member\n%SB", parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) );
|
||||
parser_pop(& ctx->parser);
|
||||
return InvalidCode;
|
||||
}
|
||||
ctx->parser.token_id = member_start_id;
|
||||
ctx->parser.token_mismatch = false;
|
||||
parser_recover_body_member(ctx, result, member_start_token, member_start_id, false);
|
||||
continue;
|
||||
}
|
||||
body_append(result, member );
|
||||
}
|
||||
@@ -1196,10 +1264,8 @@ CodeBody parse_class_struct_body(Context* ctx, TokType which, Token name)
|
||||
}
|
||||
|
||||
internal
|
||||
CodeComment parse_comment(Context* ctx)
|
||||
{
|
||||
ParseStackNode scope = NullScope;
|
||||
parser_push(& ctx->parser, & scope );
|
||||
CodeComment parse_comment(Context* ctx) {
|
||||
ParseStackNode scope = NullScope; parser_push(& ctx->parser, & scope );
|
||||
|
||||
CodeComment
|
||||
result = (CodeComment) make_code();
|
||||
@@ -1215,8 +1281,7 @@ CodeComment parse_comment(Context* ctx)
|
||||
internal
|
||||
Code parse_complicated_definition(Context* ctx, TokType which)
|
||||
{
|
||||
ParseStackNode scope = NullScope;
|
||||
parser_push(& ctx->parser, & scope );
|
||||
ParseStackNode scope = NullScope; parser_push(& ctx->parser, & scope );
|
||||
|
||||
b32 is_inplace = false;
|
||||
b32 is_fn_def = false;
|
||||
@@ -1399,8 +1464,7 @@ Code parse_complicated_definition(Context* ctx, TokType which)
|
||||
internal inline
|
||||
Code parse_assignment_expression(Context* ctx)
|
||||
{
|
||||
ParseStackNode scope = NullScope;
|
||||
parser_push(& ctx->parser, & scope );
|
||||
ParseStackNode scope = NullScope; parser_push(& ctx->parser, & scope );
|
||||
|
||||
Code expr = { nullptr };
|
||||
|
||||
@@ -1445,7 +1509,6 @@ internal inline
|
||||
Code parse_forward_or_definition(Context* ctx, TokType which, bool is_inplace )
|
||||
{
|
||||
Code result = InvalidCode;
|
||||
|
||||
switch ( which )
|
||||
{
|
||||
case Tok_Decl_Class:
|
||||
@@ -1483,8 +1546,7 @@ CodeFn parse_function_after_name(Context* ctx
|
||||
, Token name
|
||||
)
|
||||
{
|
||||
ParseStackNode scope = NullScope;
|
||||
parser_push(& ctx->parser, & scope );
|
||||
ParseStackNode scope = NullScope; parser_push(& ctx->parser, & scope );
|
||||
|
||||
CodeParams params = parse_params(ctx, parser_use_parenthesis);
|
||||
// <Attributes> <Specifiers> <ReturnType> <Name> ( <Parameters> )
|
||||
@@ -1639,14 +1701,14 @@ CodeFn parse_function_after_name(Context* ctx
|
||||
internal
|
||||
Code parse_function_body(Context* ctx)
|
||||
{
|
||||
ParseStackNode scope = NullScope;
|
||||
parser_push(& ctx->parser, & scope );
|
||||
ParseStackNode scope = NullScope; parser_push(& ctx->parser, & scope );
|
||||
|
||||
eat( Tok_BraceCurly_Open );
|
||||
|
||||
CodeBody
|
||||
result = (CodeBody) make_code();
|
||||
result->Type = CT_Function_Body;
|
||||
scope.code_rel = cast(Code, result);
|
||||
|
||||
// TODO : Support actual parsing of function body
|
||||
Token start = currtok_noskip;
|
||||
@@ -1667,8 +1729,7 @@ Code parse_function_body(Context* ctx)
|
||||
|
||||
s32 len = ( (sptr)prevtok.Text.Ptr + prevtok.Text.Len ) - (sptr)start.Text.Ptr;
|
||||
|
||||
if ( len > 0 )
|
||||
{
|
||||
if ( len > 0 ) {
|
||||
Str str = { start.Text.Ptr, len };
|
||||
body_append( result, cast(Code, def_execution( str )) );
|
||||
}
|
||||
@@ -1679,6 +1740,294 @@ Code parse_function_body(Context* ctx)
|
||||
return cast(Code, result);
|
||||
}
|
||||
|
||||
internal
|
||||
s32 parser_recovery_scope_close_id(ParseContext const* parser, s32 member_start_id, b32* nested_body, b32* has_scope_close)
|
||||
{
|
||||
nested_body[0] = false;
|
||||
has_scope_close[0] = false;
|
||||
ParseStackNode* scope = parser->scope;
|
||||
while ( scope != nullptr )
|
||||
{
|
||||
b32 scope_has_code = scope->code_rel != nullptr;
|
||||
if (scope_has_code)
|
||||
{
|
||||
CodeType type = scope->code_rel->Type;
|
||||
|
||||
b32 is_nested_scope_body = false;
|
||||
is_nested_scope_body |= type == CT_Namespace_Body;
|
||||
is_nested_scope_body |= type == CT_Class_Body;
|
||||
is_nested_scope_body |= type == CT_Struct_Body;
|
||||
|
||||
// Function-body frames have their own brace owner; inspect the enclosing body frame.
|
||||
if ( type == CT_Function_Body ) { scope = scope->prev; continue; }
|
||||
|
||||
b32 nothing_to_recover = type == CT_Global_Body || is_nested_scope_body == false;
|
||||
// Global recovery uses the full token slice. Other unsupported scope types have no nested boundary.
|
||||
if (nothing_to_recover) return parser->tokens.num; // TODO(Ed): Drain to single return.
|
||||
|
||||
// Nested recovery needs the active body's closing brace as a hard delimiter boundary.
|
||||
nested_body[0] = true;
|
||||
|
||||
s32 scope_start_id = parser->tokens.num;
|
||||
b32 scope_has_start = scope->start != nullptr;
|
||||
if (scope_has_start) scope_start_id = scast(s32, scope->start - parser->tokens.ptr);
|
||||
|
||||
// Scope start may point at formatting before the body's opening brace.
|
||||
s32 body_open_id = scope_start_id;
|
||||
while ( body_open_id < parser->tokens.num ) {
|
||||
b32 token_opens_body = parser->tokens.ptr[body_open_id].Type == Tok_BraceCurly_Open;
|
||||
if (token_opens_body == true) break;
|
||||
++ body_open_id;
|
||||
}
|
||||
|
||||
b32 found_body_open = body_open_id < parser->tokens.num;
|
||||
if (found_body_open == false) return parser->tokens.num;
|
||||
|
||||
// A depth-zero close belongs to this body; nested closes only reduce depth.
|
||||
s32 body_depth = 0;
|
||||
for (s32 idx = body_open_id; idx < parser->tokens.num; ++ idx)
|
||||
{
|
||||
TokType token_type = parser->tokens.ptr[idx].Type;
|
||||
if (token_type == Tok_BraceCurly_Open)
|
||||
++ body_depth;
|
||||
else if (token_type == Tok_BraceCurly_Close)
|
||||
{
|
||||
if (body_depth > 0) // inside nested brace
|
||||
{
|
||||
-- body_depth;
|
||||
if (body_depth == 0) { // found owner close
|
||||
has_scope_close[0] = true;
|
||||
// TODO(Ed): Drain to single return.
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s32 fallback_close_id = parser->tokens.num;
|
||||
if (type == CT_Class_Body || type == CT_Struct_Body)
|
||||
{
|
||||
// A malformed member opener can keep the brace-depth scan from reaching the owner close.
|
||||
// Direct typed entries scan to their last candidate; nested bodies stop at the first candidate.
|
||||
b32 has_parent_body = false;
|
||||
for (ParseStackNode* parent = scope->prev; parent != nullptr; parent = parent->prev)
|
||||
{
|
||||
b32 parent_has_code = parent->code_rel != nullptr;
|
||||
if (parent_has_code == false) continue;
|
||||
|
||||
b32 parent_is_function_body = parent->code_rel->Type == CT_Function_Body;
|
||||
if (parent_is_function_body) continue;
|
||||
|
||||
CodeType parent_type = parent->code_rel->Type;
|
||||
b32 parent_owns_body = false;
|
||||
parent_owns_body |= parent_type == CT_Global_Body;
|
||||
parent_owns_body |= parent_type == CT_Namespace_Body;
|
||||
parent_owns_body |= parent_type == CT_Class_Body;
|
||||
parent_owns_body |= parent_type == CT_Struct_Body;
|
||||
has_parent_body = parent_owns_body;
|
||||
break;
|
||||
}
|
||||
for (s32 idx = member_start_id; idx < parser->tokens.num; ++ idx)
|
||||
{
|
||||
if (parser->tokens.ptr[idx].Type != Tok_BraceCurly_Close) continue;
|
||||
|
||||
s32 next_id = idx + 1;
|
||||
while (next_id < parser->tokens.num) {
|
||||
TokType next_type = parser->tokens.ptr[next_id].Type;
|
||||
b32 next_is_formatting = next_type == Tok_NewLine || next_type == Tok_Comment;
|
||||
if (next_is_formatting == false) break;
|
||||
++ next_id;
|
||||
}
|
||||
|
||||
b32 next_ends_statement = false;
|
||||
b32 close_ends_declaration = false;
|
||||
b32 next_in_slice = next_id < parser->tokens.num;
|
||||
if (next_in_slice) {
|
||||
next_ends_statement = parser->tokens.ptr[next_id].Type == Tok_Statement_End;
|
||||
close_ends_declaration = next_ends_statement;
|
||||
}
|
||||
if (close_ends_declaration) {
|
||||
fallback_close_id = idx;
|
||||
if (has_parent_body) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// A namespace close follows a member terminator, unlike a nested type close followed by `;`.
|
||||
for (s32 idx = member_start_id; idx < parser->tokens.num; ++ idx)
|
||||
{
|
||||
if (parser->tokens.ptr[idx].Type != Tok_BraceCurly_Close) continue;
|
||||
|
||||
s32 prev_id = idx - 1;
|
||||
while (prev_id >= body_open_id) {
|
||||
TokType prev_type = parser->tokens.ptr[prev_id].Type;
|
||||
b32 prev_is_formatting = prev_type == Tok_NewLine || prev_type == Tok_Comment;
|
||||
if ( prev_is_formatting == false ) break;
|
||||
-- prev_id;
|
||||
}
|
||||
|
||||
s32 next_id = idx + 1;
|
||||
while (next_id < parser->tokens.num) {
|
||||
TokType next_type = parser->tokens.ptr[next_id].Type;
|
||||
b32 next_is_formatting = next_type == Tok_NewLine || next_type == Tok_Comment;
|
||||
if ( next_is_formatting == false ) break;
|
||||
++ next_id;
|
||||
}
|
||||
|
||||
b32 prev_in_body = prev_id >= body_open_id;
|
||||
b32 prev_ends_member = false;
|
||||
if (prev_in_body) prev_ends_member = parser->tokens.ptr[prev_id].Type == Tok_Statement_End;
|
||||
|
||||
b32 next_ends_declaration = false;
|
||||
b32 next_past_slice = next_id >= parser->tokens.num;
|
||||
if (next_past_slice == false) next_ends_declaration = parser->tokens.ptr[next_id].Type == Tok_Statement_End;
|
||||
|
||||
b32 member_ended_before_close = false;
|
||||
b32 close_starts_next_declaration = false;
|
||||
b32 namespace_owner_close = false;
|
||||
if (prev_in_body && prev_ends_member) member_ended_before_close = true;
|
||||
if (next_past_slice == false && next_ends_declaration) close_starts_next_declaration = true;
|
||||
if (member_ended_before_close && close_starts_next_declaration == false) namespace_owner_close = true;
|
||||
if (namespace_owner_close) {
|
||||
fallback_close_id = idx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fallback_close_id < parser->tokens.num) { // found fallback close
|
||||
has_scope_close[0] = true;
|
||||
return fallback_close_id;
|
||||
}
|
||||
return parser->tokens.num;
|
||||
}
|
||||
scope = scope->prev;
|
||||
}
|
||||
return parser->tokens.num;
|
||||
}
|
||||
|
||||
internal
|
||||
b32 parser_recovery_delimiter_is_balanced(ParseContext const* parser
|
||||
, s32 start_id
|
||||
, TokType open_type
|
||||
, TokType close_type
|
||||
, s32 scope_close_id
|
||||
, b32 nested_body
|
||||
, b32 has_scope_close
|
||||
){
|
||||
// Inner delimiters shield semicolons only when their close precedes the owning body close.
|
||||
if ( open_type == Tok_BraceCurly_Open && nested_body && has_scope_close == false ) return false;
|
||||
s32 delimiter_depth = 0;
|
||||
s32 curly_depth = 0;
|
||||
for ( s32 idx = start_id; idx < scope_close_id; ++ idx )
|
||||
{
|
||||
TokType type = parser->tokens.ptr[idx].Type;
|
||||
if ( type == Tok_BraceCurly_Open )
|
||||
++ curly_depth;
|
||||
else if ( type == Tok_BraceCurly_Close )
|
||||
{
|
||||
if ( curly_depth == 0 ) return false;
|
||||
-- curly_depth;
|
||||
}
|
||||
|
||||
if ( type == open_type )
|
||||
++ delimiter_depth;
|
||||
else if ( type == close_type && delimiter_depth > 0 )
|
||||
{
|
||||
-- delimiter_depth;
|
||||
if ( delimiter_depth == 0 ) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal
|
||||
void parser_recover_body_member(Context* ctx, CodeBody body, Token member_start_token, s32 member_start_id, b32 allow_comma)
|
||||
{
|
||||
// Keep the original member start for the untyped source span.
|
||||
Token recovery_end_token = member_start_token;
|
||||
b32 has_recovery_tokens = false;
|
||||
s32 paren_depth = 0;
|
||||
s32 square_depth = 0;
|
||||
s32 curly_depth = 0;
|
||||
b32 nested_body = false;
|
||||
b32 has_scope_close = false;
|
||||
// Bound delimiter matches before scanning the rejected member.
|
||||
s32 scope_close_id = parser_recovery_scope_close_id(& ctx->parser, member_start_id, &nested_body, &has_scope_close);
|
||||
while ( left )
|
||||
{
|
||||
Token token = currtok_noskip;
|
||||
// Leave the active body's close for its owner.
|
||||
b32 is_scope_close = token.Type == Tok_BraceCurly_Close && curly_depth == 0;
|
||||
if (is_scope_close) break;
|
||||
|
||||
b32 at_member_start = ctx->parser.token_id == member_start_id;
|
||||
b32 at_delimiter_depth_zero = paren_depth == 0 && square_depth == 0 && curly_depth == 0;
|
||||
b32 sync_at_comma = allow_comma && at_member_start && at_delimiter_depth_zero && token.Type == Tok_Comma;
|
||||
b32 sync_at_statement_end = token.Type == Tok_Statement_End && at_delimiter_depth_zero;
|
||||
b32 sync_at_member_boundary = sync_at_comma || sync_at_statement_end;
|
||||
// Semicolons sync at depth zero; only global recovery accepts a leading comma.
|
||||
if (sync_at_member_boundary)
|
||||
{
|
||||
TokType sync_type = Tok_Statement_End;
|
||||
if (sync_at_comma) sync_type = Tok_Comma;
|
||||
if (eat( sync_type )) {
|
||||
recovery_end_token = token;
|
||||
has_recovery_tokens = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Add delimiter depth only when its matching close precedes scope_close_id.
|
||||
switch ( token.Type ) { default: break;
|
||||
case Tok_Paren_Open: {
|
||||
b32 res = parser_recovery_delimiter_is_balanced(& ctx->parser, ctx->parser.token_id
|
||||
, Tok_Paren_Open, Tok_Paren_Close
|
||||
, scope_close_id, nested_body, has_scope_close);
|
||||
if (res) ++ paren_depth;
|
||||
break;
|
||||
}
|
||||
|
||||
case Tok_Paren_Close:
|
||||
if ( paren_depth > 0 ) -- paren_depth;
|
||||
break;
|
||||
|
||||
case Tok_BraceSquare_Open: {
|
||||
b32 res = parser_recovery_delimiter_is_balanced(& ctx->parser, ctx->parser.token_id
|
||||
, Tok_BraceSquare_Open, Tok_BraceSquare_Close
|
||||
, scope_close_id, nested_body, has_scope_close);
|
||||
if (res) ++ square_depth;
|
||||
break;
|
||||
}
|
||||
|
||||
case Tok_BraceSquare_Close:
|
||||
if ( square_depth > 0 ) -- square_depth;
|
||||
break;
|
||||
|
||||
case Tok_BraceCurly_Open: {
|
||||
b32 res = parser_recovery_delimiter_is_balanced(& ctx->parser, ctx->parser.token_id
|
||||
, Tok_BraceCurly_Open, Tok_BraceCurly_Close
|
||||
, scope_close_id, nested_body, has_scope_close);
|
||||
if (res) ++ curly_depth;
|
||||
break;
|
||||
}
|
||||
|
||||
case Tok_BraceCurly_Close:
|
||||
if ( curly_depth > 0 ) -- curly_depth;
|
||||
break;
|
||||
}
|
||||
|
||||
if (eat( token.Type )) {
|
||||
recovery_end_token = token;
|
||||
has_recovery_tokens = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Retain the failed source through its synchronization token as untyped code.
|
||||
if (has_recovery_tokens) body_append(body, untyped_str(token_range_to_str(member_start_token, recovery_end_token)));
|
||||
}
|
||||
|
||||
internal neverinline
|
||||
CodeBody parse_global_nspace(Context* ctx, CodeType which)
|
||||
{
|
||||
@@ -1695,9 +2044,13 @@ CodeBody parse_global_nspace(Context* ctx, CodeType which)
|
||||
CodeBody
|
||||
result = (CodeBody) make_code();
|
||||
result->Type = which;
|
||||
scope.code_rel = cast(Code, result);
|
||||
|
||||
while ( left && currtok_noskip.Type != Tok_BraceCurly_Close )
|
||||
{
|
||||
ParseMessage* messages_before_member = ctx->parser.messages;
|
||||
s32 member_start_id = ctx->parser.token_id;
|
||||
Token member_start_token = currtok_noskip;
|
||||
Code member = Code_Invalid;
|
||||
CodeAttributes attributes = { nullptr };
|
||||
CodeSpecifiers specifiers = { nullptr };
|
||||
@@ -1716,8 +2069,12 @@ CodeBody parse_global_nspace(Context* ctx, CodeType which)
|
||||
case Tok_Comma:
|
||||
{
|
||||
parser_record_failure(ctx, txt("Dangling comma"));
|
||||
parser_pop( & ctx->parser);
|
||||
return result;
|
||||
if ( which != CT_Global_Body )
|
||||
{
|
||||
parser_pop( & ctx->parser);
|
||||
return result;
|
||||
}
|
||||
member = Code_Invalid;
|
||||
}
|
||||
break;
|
||||
case Tok_Statement_End:
|
||||
@@ -1849,6 +2206,12 @@ CodeBody parse_global_nspace(Context* ctx, CodeType which)
|
||||
|
||||
case Tok_Module_Import: {
|
||||
// import ...
|
||||
if ( which == CT_Global_Body )
|
||||
{
|
||||
parser_record_failure(ctx, txt("Module import parsing is not implemented"));
|
||||
member = Code_Invalid;
|
||||
break;
|
||||
}
|
||||
log_failure( "gen::%s: This function is not implemented" );
|
||||
return InvalidCode;
|
||||
}
|
||||
@@ -1858,6 +2221,12 @@ CodeBody parse_global_nspace(Context* ctx, CodeType which)
|
||||
{
|
||||
if ( ! tok_is_attribute(currtok))
|
||||
{
|
||||
if ( which == CT_Global_Body )
|
||||
{
|
||||
parser_record_failure(ctx, txt("Unbounded macro expression in global body"));
|
||||
member = Code_Invalid;
|
||||
break;
|
||||
}
|
||||
log_failure("Unbounded macro expression residing in class/struct body\n%SB", parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp));
|
||||
return InvalidCode;
|
||||
}
|
||||
@@ -1878,6 +2247,7 @@ CodeBody parse_global_nspace(Context* ctx, CodeType which)
|
||||
{
|
||||
Specifier specs_found[16] = { Spec_NumSpecifiers };
|
||||
s32 NumSpecifiers = 0;
|
||||
b32 invalid_global_specifier = false;
|
||||
|
||||
while ( left && tok_is_specifier(currtok) )
|
||||
{
|
||||
@@ -1900,11 +2270,22 @@ CodeBody parse_global_nspace(Context* ctx, CodeType which)
|
||||
|
||||
default:
|
||||
Str spec_str = spec_to_str(spec);
|
||||
|
||||
if ( which == CT_Global_Body )
|
||||
{
|
||||
StrBuilder message = strbuilder_fmt_buf(ctx->Allocator_Temp, "Invalid specifier %S for variable\n%SB"
|
||||
, spec_str
|
||||
, parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp)
|
||||
);
|
||||
parser_record_failure(ctx, strbuilder_to_str(message));
|
||||
invalid_global_specifier = true;
|
||||
break;
|
||||
}
|
||||
log_failure( "Invalid specifier %S for variable\n%S", spec_str, strbuilder_to_str( parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp)) );
|
||||
parser_pop(& ctx->parser);
|
||||
return InvalidCode;
|
||||
}
|
||||
if ( invalid_global_specifier )
|
||||
break;
|
||||
|
||||
if (ignore_spec)
|
||||
break;
|
||||
@@ -1913,6 +2294,11 @@ CodeBody parse_global_nspace(Context* ctx, CodeType which)
|
||||
NumSpecifiers++;
|
||||
eat( currtok.Type );
|
||||
}
|
||||
if ( invalid_global_specifier )
|
||||
{
|
||||
member = Code_Invalid;
|
||||
break;
|
||||
}
|
||||
|
||||
if ( NumSpecifiers )
|
||||
{
|
||||
@@ -1979,8 +2365,32 @@ CodeBody parse_global_nspace(Context* ctx, CodeType which)
|
||||
}
|
||||
}
|
||||
|
||||
b32 member_is_nested_scope = member != Code_Invalid && (member->Type == CT_Class || member->Type == CT_Struct || member->Type == CT_Namespace);
|
||||
b32 member_failed = ctx->parser.token_mismatch || (ctx->parser.messages != messages_before_member && ! member_is_nested_scope);
|
||||
b32 can_recover_member = which == CT_Global_Body || which == CT_Namespace_Body;
|
||||
if (can_recover_member && member_failed) {
|
||||
if (ctx->parser.messages != messages_before_member) ctx->parser.messages->Next = messages_before_member;
|
||||
ctx->parser.token_id = member_start_id;
|
||||
ctx->parser.token_mismatch = false;
|
||||
member = Code_Invalid;
|
||||
}
|
||||
|
||||
if ( member == Code_Invalid )
|
||||
{
|
||||
if ( which == CT_Global_Body || (which == CT_Namespace_Body && member_failed) )
|
||||
{
|
||||
if ( ctx->parser.messages == messages_before_member )
|
||||
{
|
||||
StrBuilder message = strbuilder_fmt_buf(ctx->Allocator_Temp, "Failed to parse member\nToken: %SB\nContext:\n%SB"
|
||||
, tok_to_strbuilder(ctx->Allocator_Temp, currtok_noskip)
|
||||
, parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp)
|
||||
);
|
||||
parser_record_failure(ctx, strbuilder_to_str(message));
|
||||
}
|
||||
parser_recover_body_member(ctx, result, member_start_token, member_start_id, which == CT_Global_Body);
|
||||
continue;
|
||||
}
|
||||
|
||||
log_failure( "Failed to parse member\nToken: %SB\nContext:\n%SB", tok_to_strbuilder(ctx->Allocator_Temp, currtok_noskip), parser_to_strbuilder(& ctx->parser, ctx->Allocator_Temp) );
|
||||
parser_pop(& ctx->parser);
|
||||
return InvalidCode;
|
||||
|
||||
@@ -162,6 +162,8 @@ struct ParseContext
|
||||
// TokArray Tokens;
|
||||
TokenSlice tokens;
|
||||
s32 token_id;
|
||||
LexerMessage* lexed_messages;
|
||||
b32 token_mismatch;
|
||||
};
|
||||
|
||||
enum MacroType : u16
|
||||
|
||||
+120
-31
@@ -57,6 +57,68 @@ Code refactor_and_format( Code code ) {
|
||||
return code_refactor_and_format(code, scratch_file, path_refactor_script, path_format_style );
|
||||
}
|
||||
|
||||
internal Code rewrite_parse_wrapper_signature(Code source, Str function_name)
|
||||
{
|
||||
Str scanned = source->Content;
|
||||
Str needle = StrBuilder::fmt_buf(_ctx->Allocator_Temp, "%S(", function_name).to_str();
|
||||
Str option_param = txt("Opts_parse opts");
|
||||
ssize function_at = -1;
|
||||
ssize option_at = -1;
|
||||
ssize last_offset = scanned.Len - needle.Len;
|
||||
for (ssize scan_offset = 0; scan_offset <= last_offset; ++ scan_offset) {
|
||||
char const* scan_at = scanned.Ptr + scan_offset;
|
||||
if (c_str_compare_len(scan_at, needle.Ptr, needle.Len) == 0) {
|
||||
if (scan_offset > 0) {
|
||||
ssize prev_at = scan_offset - 1;
|
||||
if (scanned.Ptr[prev_at] == '_') continue;
|
||||
}
|
||||
function_at = scan_offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (function_at < 0) GEN_FATAL("c_library: parser wrapper signature missing");
|
||||
|
||||
for (ssize params_end = function_at + needle.Len;
|
||||
params_end < scanned.Len && scanned.Ptr[params_end] != ')';
|
||||
++ params_end
|
||||
) {
|
||||
ssize option_fit_end = params_end + option_param.Len;
|
||||
if (option_fit_end > scanned.Len) break;
|
||||
char const* params_at = scanned.Ptr + params_end;
|
||||
if (c_str_compare_len(params_at, option_param.Ptr, option_param.Len) == 0) {
|
||||
option_at = params_end;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (option_at < 0) GEN_FATAL("c_library: parser wrapper options parameter missing for %S", function_name);
|
||||
|
||||
Str new_name = StrBuilder::fmt_buf(_ctx->Allocator_Temp, "%S_opts", function_name).to_str();
|
||||
Str pointer_param = txt("Opts_parse* opts");
|
||||
|
||||
ssize name_end = function_at + function_name.Len;
|
||||
char const* before_opts_ptr = scanned.Ptr + name_end;
|
||||
ssize before_opts_len = option_at - name_end;
|
||||
Str before_name = { scanned.Ptr, function_at };
|
||||
Str before_opts = { before_opts_ptr, before_opts_len };
|
||||
|
||||
ssize after_opts_at = option_at + option_param.Len;
|
||||
char const* after_opts_ptr = scanned.Ptr + after_opts_at;
|
||||
ssize after_opts_len = scanned.Len - after_opts_at;
|
||||
Str after_opts = { after_opts_ptr, after_opts_len };
|
||||
|
||||
ssize opts_star_len = 1;
|
||||
ssize rebuilt_cap = scanned.Len;
|
||||
rebuilt_cap += new_name.Len;
|
||||
rebuilt_cap += opts_star_len;
|
||||
StrBuilder rebuilt = StrBuilder::make_reserve(_ctx->Allocator_Temp, rebuilt_cap);
|
||||
strbuilder_append_str(& rebuilt, before_name);
|
||||
strbuilder_append_str(& rebuilt, new_name);
|
||||
strbuilder_append_str(& rebuilt, before_opts);
|
||||
strbuilder_append_str(& rebuilt, pointer_param);
|
||||
strbuilder_append_str(& rebuilt, after_opts);
|
||||
return untyped_str(strbuilder_to_str(rebuilt));
|
||||
}
|
||||
|
||||
constexpr bool helper_use_c_definition = true;
|
||||
|
||||
int gen_main()
|
||||
@@ -962,6 +1024,18 @@ R"(#define AST_ArrSpecs_Cap \
|
||||
txt("CodeUsing"),
|
||||
txt("CodeVar"),
|
||||
};
|
||||
// append, entry, and validate_body. Code is the untyped handle. CodeBody is the list / body pod.
|
||||
Str code_body_slots[] = {
|
||||
txt("Code"),
|
||||
txt("CodeBody"),
|
||||
};
|
||||
// has_entries reads NumEntries. These pods keep that word as an entry count.
|
||||
Str code_has_entries_slots[] = {
|
||||
txt("Code"),
|
||||
txt("CodeBody"),
|
||||
txt("CodeParams"),
|
||||
txt("CodeDefineParams"),
|
||||
};
|
||||
|
||||
CodeBody parsed_code_types = parse_file( path_base "components/code_types.hpp" );
|
||||
CodeBody code_types = def_body(CT_Global_Body);
|
||||
@@ -1034,9 +1108,18 @@ R"(#define <interface_name>( code ) _Generic( (code), \
|
||||
, tmpl_def_start
|
||||
));
|
||||
|
||||
// Append slots
|
||||
for(Str type : code_typenames ) {
|
||||
generic_selector.append_fmt("%S : %S,\\\n", type, fn->Name );
|
||||
// Append slots. Restricted procedures omit static types that cannot call them.
|
||||
if (actual_name.is_equal(txt("append")) || actual_name.is_equal(txt("entry")) || actual_name.is_equal(txt("validate_body"))) {
|
||||
for (Str type : code_body_slots)
|
||||
generic_selector.append_fmt("%S : %S,\\\n", type, fn->Name);
|
||||
}
|
||||
else if (actual_name.is_equal(txt("has_entries"))) {
|
||||
for (Str type : code_has_entries_slots)
|
||||
generic_selector.append_fmt("%S : %S,\\\n", type, fn->Name);
|
||||
}
|
||||
else {
|
||||
for (Str type : code_typenames)
|
||||
generic_selector.append_fmt("%S : %S,\\\n", type, fn->Name);
|
||||
}
|
||||
generic_selector.append(txt("default: gen_generic_selection_fail \\\n"));
|
||||
|
||||
@@ -1152,19 +1235,22 @@ R"(#define <interface_name>( code ) _Generic( (code), \
|
||||
for ( CodeParams opt_param : fn->Params ) if (opt_param->ValueType->Name.starts_with(txt("Opts_")))
|
||||
{
|
||||
// Convert the definition to use a default struct: https://vxtwitter.com/vkrajacic/status/1749816169736073295
|
||||
if ( fn->Name.is_equal( txt("parse_global_body") ) )
|
||||
if (fn->Name.starts_with(txt("parse_")))
|
||||
{
|
||||
Str new_name = txt("parse_global_body_opts");
|
||||
char const* tmpl_fn_macro = "#define <def_name>( <params> ... ) <def__name>( <params> & (<opts_type>) { __VA_ARGS__ } )\n";
|
||||
Code fn_macro;
|
||||
Str new_name = StrBuilder::fmt_buf (_ctx->Allocator_Temp, "%S_opts", fn->Name).to_str();
|
||||
StrBuilder params_str = StrBuilder::make_reserve(_ctx->Allocator_Temp, 32);
|
||||
for (CodeParams other_param : fn->Params) {
|
||||
if (other_param == opt_param) break;
|
||||
params_str.append_fmt("%S, ", other_param->Name);
|
||||
}
|
||||
|
||||
opt_param->ValueType->Specs = def_specifier( Spec_Ptr );
|
||||
fn_macro = untyped_str( token_fmt(
|
||||
Code fn_macro = untyped_str( token_fmt(
|
||||
"def_name", fn->Name
|
||||
, "def__name", new_name
|
||||
, "params", txt("def, ")
|
||||
, "params", params_str.to_str()
|
||||
, "opts_type", opt_param->ValueType->Name
|
||||
, tmpl_fn_macro
|
||||
, "#define <def_name>( <params> ... ) <def__name>( <params> & (<opts_type>) { __VA_ARGS__ } )\n"
|
||||
));
|
||||
fn->Name = cache_str( new_name );
|
||||
interface.append( fn );
|
||||
@@ -1441,27 +1527,30 @@ R"(#define <interface_name>( code ) _Generic( (code), \
|
||||
|
||||
Code src_parsing_interface = scan_file( path_base "components/interface.parsing.cpp" );
|
||||
{
|
||||
Str needle = txt("CodeBody parse_global_body( Str def, Opts_parse_global_body opts )");
|
||||
Str scanned = src_parsing_interface->Content;
|
||||
ssize signature_at = -1;
|
||||
ssize scan_offset;
|
||||
ssize last_offset = scanned.Len - needle.Len;
|
||||
for ( scan_offset = 0; scan_offset <= last_offset; ++ scan_offset )
|
||||
{
|
||||
if ( c_str_compare_len( scanned.Ptr + scan_offset, needle.Ptr, needle.Len ) == 0 ) {
|
||||
signature_at = scan_offset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( signature_at < 0 ) GEN_FATAL( "c_library: parse_global_body signature missing from scanned interface.parsing.cpp" );
|
||||
/*Resolved replacement*/ {
|
||||
Str replacement = txt("CodeBody parse_global_body_opts( Str def, Opts_parse_global_body* opts )");
|
||||
StrBuilder rebuilt = strbuilder_make_reserve( _ctx->Allocator_Temp, scanned.Len + replacement.Len );
|
||||
strbuilder_append_str( & rebuilt, Str { scanned.Ptr, signature_at } );
|
||||
strbuilder_append_str( & rebuilt, replacement );
|
||||
strbuilder_append_str( & rebuilt, Str { scanned.Ptr + signature_at + needle.Len, scanned.Len - signature_at - needle.Len } );
|
||||
src_parsing_interface = untyped_str( strbuilder_to_str( rebuilt ) );
|
||||
}
|
||||
Str parser_names[] = {
|
||||
txt("parse_class"),
|
||||
txt("parse_constructor"),
|
||||
txt("parse_define"),
|
||||
txt("parse_destructor"),
|
||||
txt("parse_enum"),
|
||||
txt("parse_export_body"),
|
||||
txt("parse_extern_link"),
|
||||
txt("parse_friend"),
|
||||
txt("parse_function"),
|
||||
txt("parse_global_body"),
|
||||
txt("parse_namespace"),
|
||||
txt("parse_operator"),
|
||||
txt("parse_operator_cast"),
|
||||
txt("parse_struct"),
|
||||
txt("parse_template"),
|
||||
txt("parse_type"),
|
||||
txt("parse_typedef"),
|
||||
txt("parse_union"),
|
||||
txt("parse_using"),
|
||||
txt("parse_variable"),
|
||||
};
|
||||
for (s32 idx = 0; idx < (s32)(sizeof(parser_names) / sizeof(parser_names[0])); ++idx)
|
||||
src_parsing_interface = rewrite_parse_wrapper_signature(src_parsing_interface, parser_names[idx]);
|
||||
}
|
||||
Code src_untyped = scan_file( path_base "components/interface.untyped.cpp" );
|
||||
Code src_parser_case_macros = scan_file( path_base "components/parser_case_macros.cpp" );
|
||||
|
||||
+564
-6
@@ -34,6 +34,14 @@ gen_internal gen_Code_POD code_pod(gen_Code code) {
|
||||
return result;
|
||||
}
|
||||
|
||||
gen_internal void expect_parse_info(void* result, ParseInfo* info, gen_Str label) {
|
||||
expect(result != 0, label);
|
||||
void* info_result = info->result;
|
||||
expect(info_result == result, label);
|
||||
expect(info->lexed.tokens.num > 0, label);
|
||||
expect(info->lexed.messages == 0 && info->messages == 0, label);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
gen_Context ctx = {0}; gen_init(&ctx);
|
||||
@@ -475,24 +483,574 @@ int main()
|
||||
expect(untyped != 0, gen_txt("c11 untyped"));
|
||||
|
||||
gen_Str untyped_text = gen_strbuilder_to_str(gen_code_to_strbuilder(untyped));
|
||||
expect_fragments(untyped_text, gen_txt("c11 untyped content"));
|
||||
expect_fragments(untyped_text, gen_txt("c11 untyped content"));
|
||||
expect_variable(gen_parse_variable(untyped_text), gen_txt("c11 untyped reparsed"));
|
||||
|
||||
gen_CodeVar parsed = gen_parse_variable(code(char const* baseline_message = "GENCPP_BASELINE"; ));
|
||||
expect_variable(parsed, gen_txt("c11 parsed"));
|
||||
expect_variable(parsed, gen_txt("c11 parsed"));
|
||||
expect_fragments(gen_strbuilder_to_str(gen_code_to_strbuilder(parsed)), gen_txt("c11 parsed serialized"));
|
||||
|
||||
gen_CodeClass parser_class = gen_parse_class(gen_txt("class ParseClass {};"));
|
||||
gen_CodeConstructor parser_constructor = gen_parse_constructor(gen_txt("ParseClass() {}"));
|
||||
gen_CodeDefine parser_define = gen_parse_define(gen_txt("#define PARSE_DEFINE 1"));
|
||||
gen_CodeDestructor parser_destructor = gen_parse_destructor(gen_txt("~ParseClass();"));
|
||||
gen_CodeEnum parser_enum = gen_parse_enum(gen_txt("enum ParseEnum { ParseValue };"));
|
||||
gen_CodeBody parser_export_body = gen_parse_export_body(gen_txt("{ int export_value; }"));
|
||||
gen_CodeExtern parser_extern = gen_parse_extern_link(gen_txt("extern \"C\" { int extern_value; }"));
|
||||
gen_CodeFriend parser_friend = gen_parse_friend(gen_txt("friend int parse_friend_function();"));
|
||||
gen_CodeFn parser_function = gen_parse_function(gen_txt("int parse_function();"));
|
||||
gen_CodeBody parser_global_body = gen_parse_global_body(gen_txt("struct ParseGlobal { int value; };"));
|
||||
gen_CodeNS parser_namespace = gen_parse_namespace(gen_txt("namespace ParseNamespace {}"));
|
||||
gen_CodeOperator parser_operator = gen_parse_operator(gen_txt("s32 operator +(s32 lhs, s32 rhs);"));
|
||||
gen_CodeOpCast parser_operator_cast = gen_parse_operator_cast(gen_txt("operator s32() const;"));
|
||||
gen_CodeStruct parser_struct = gen_parse_struct(gen_txt("struct ParseStruct {};"));
|
||||
gen_CodeTemplate parser_template = gen_parse_template(gen_txt("template<class T> struct ParseTemplate {};"));
|
||||
gen_CodeTypename parser_type = gen_parse_type(gen_txt("const s32*"));
|
||||
gen_CodeTypedef parser_typedef = gen_parse_typedef(gen_txt("typedef s32 ParseTypedef;"));
|
||||
gen_CodeUnion parser_union = gen_parse_union(gen_txt("union ParseUnion { s32 value; };"));
|
||||
gen_CodeUsing parser_using = gen_parse_using(gen_txt("using ParseUsing = s32;"));
|
||||
gen_CodeVar parser_variable = gen_parse_variable(gen_txt("s32 parse_variable;"));
|
||||
|
||||
expect(parser_class != 0 && parser_class->Type == CT_Class, gen_txt("c11 parse class fixture"));
|
||||
expect(parser_constructor != 0 && parser_constructor->Type == CT_Constructor, gen_txt("c11 parse constructor fixture"));
|
||||
expect(parser_define != 0 && parser_define->Type == CT_Preprocess_Define, gen_txt("c11 parse define fixture"));
|
||||
expect(parser_destructor != 0 && parser_destructor->Type == CT_Destructor_Fwd, gen_txt("c11 parse destructor fixture"));
|
||||
expect(parser_enum != 0 && parser_enum->Type == CT_Enum, gen_txt("c11 parse enum fixture"));
|
||||
expect(parser_export_body != 0 && parser_export_body->Type == CT_Export_Body, gen_txt("c11 parse export body fixture"));
|
||||
expect(parser_extern != 0 && parser_extern->Type == CT_Extern_Linkage, gen_txt("c11 parse extern fixture"));
|
||||
expect(parser_friend != 0 && parser_friend->Type == CT_Friend, gen_txt("c11 parse friend fixture"));
|
||||
expect(parser_function != 0 && parser_function->Type == CT_Function_Fwd, gen_txt("c11 parse function fixture"));
|
||||
expect(parser_global_body != 0 && parser_global_body->Type == CT_Global_Body, gen_txt("c11 parse global body fixture"));
|
||||
expect(parser_namespace != 0 && parser_namespace->Type == CT_Namespace, gen_txt("c11 parse namespace fixture"));
|
||||
expect(parser_operator != 0 && parser_operator->Type == CT_Operator_Fwd, gen_txt("c11 parse operator fixture"));
|
||||
expect(parser_operator_cast != 0 && parser_operator_cast->Type == CT_Operator_Cast_Fwd, gen_txt("c11 parse operator cast fixture"));
|
||||
expect(parser_struct != 0 && parser_struct->Type == CT_Struct, gen_txt("c11 parse struct fixture"));
|
||||
expect(parser_template != 0 && parser_template->Type == CT_Template, gen_txt("c11 parse template fixture"));
|
||||
expect(parser_type != 0 && parser_type->Type == CT_Typename, gen_txt("c11 parse type fixture"));
|
||||
expect(parser_typedef != 0 && parser_typedef->Type == CT_Typedef, gen_txt("c11 parse typedef fixture"));
|
||||
expect(parser_union != 0 && parser_union->Type == CT_Union, gen_txt("c11 parse union fixture"));
|
||||
expect(parser_using != 0 && parser_using->Type == CT_Using, gen_txt("c11 parse using fixture"));
|
||||
expect(parser_variable != 0 && parser_variable->Type == CT_Variable, gen_txt("c11 parse variable fixture"));
|
||||
|
||||
gen_CodeBody parsed_global_options = gen_parse_global_body(gen_txt("struct ParseGlobalOptions { s32 value; };"));
|
||||
expect(parsed_global_options != 0, gen_txt("c11 default parse global body options"));
|
||||
void* default_result = parsed_global_options;
|
||||
void* default_info_result = ctx.gen_parse_info.result;
|
||||
expect(default_info_result == default_result, gen_txt("c11 default parse info result"));
|
||||
expect(ctx.gen_parse_info.lexed.tokens.num > 0, gen_txt("c11 default parse info tokens"));
|
||||
expect(ctx.gen_parse_info.lexed.messages == 0 && ctx.gen_parse_info.messages == 0, gen_txt("c11 default parse info messages"));
|
||||
|
||||
ParseInfo caller_info = {0};
|
||||
gen_CodeBody parsed_explicit_global = gen_parse_global_body(gen_txt("struct ParseExplicitOptions { s32 value; };"), .ctx = & ctx, .info = & caller_info);
|
||||
expect(parsed_explicit_global != 0, gen_txt("c11 explicit parse global body options"));
|
||||
void* explicit_result = parsed_explicit_global;
|
||||
void* explicit_info_result = caller_info.result;
|
||||
expect(explicit_info_result == explicit_result, gen_txt("c11 explicit parse info result"));
|
||||
expect(caller_info.lexed.tokens.num > 0, gen_txt("c11 explicit parse info tokens"));
|
||||
|
||||
ParseInfo variable_info = {0};
|
||||
gen_CodeVar parsed_option_variable = gen_parse_variable(gen_txt("s32 parsed_option_variable;"), .ctx = & ctx, .info = & variable_info);
|
||||
expect(parsed_option_variable != 0 && parsed_option_variable->Type == CT_Variable, gen_txt("c11 explicit parse variable options"));
|
||||
expect(gen_str_contains(parsed_option_variable->Name, gen_txt("parsed_option_variable")), gen_txt("c11 explicit parse variable name"));
|
||||
void* variable_result = parsed_option_variable;
|
||||
void* variable_info_result = variable_info.result;
|
||||
expect(variable_info_result == variable_result, gen_txt("c11 explicit parse variable info result"));
|
||||
|
||||
ParseInfo lexer_info = {0};
|
||||
gen_CodeVar parsed_lexer_prefix = gen_parse_variable(gen_txt("s32 parsed_lexer_prefix;@"), .ctx = & ctx, .info = & lexer_info);
|
||||
expect(parsed_lexer_prefix != 0 && parsed_lexer_prefix->Type == CT_Variable, gen_txt("c11 parse lexer prefix result"));
|
||||
void* lexer_result = parsed_lexer_prefix;
|
||||
void* lexer_info_result = lexer_info.result;
|
||||
expect(lexer_info_result == lexer_result, gen_txt("c11 parse lexer prefix info result"));
|
||||
expect(lexer_info.lexed.tokens.num > 0, gen_txt("c11 parse lexer prefix retained tokens"));
|
||||
expect(lexer_info.lexed.messages != 0, gen_txt("c11 parse lexer prefix diagnostic"));
|
||||
|
||||
ParseInfo matrix_info = {0};
|
||||
gen_Opts_parse_global_body legacy_parse_options = { .ctx = & ctx, .info = & matrix_info };
|
||||
gen_CodeBody matrix_global_body = gen_parse_global_body(gen_txt("struct ParseGlobal { int value; };"), .ctx = legacy_parse_options.ctx, .info = legacy_parse_options.info);
|
||||
expect(matrix_global_body != 0 && matrix_global_body->Type == CT_Global_Body, gen_txt("c11 global body options matrix"));
|
||||
expect_parse_info(matrix_global_body, & matrix_info, gen_txt("c11 global body ParseInfo matrix"));
|
||||
|
||||
gen_CodeClass matrix_class = gen_parse_class(gen_txt("class ParseClass {};"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_class != 0 && matrix_class->Type == CT_Class, gen_txt("c11 class options matrix"));
|
||||
expect_parse_info(matrix_class, & matrix_info, gen_txt("c11 class ParseInfo matrix"));
|
||||
|
||||
gen_CodeConstructor matrix_constructor = gen_parse_constructor(gen_txt("ParseClass() {}"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_constructor != 0 && matrix_constructor->Type == CT_Constructor, gen_txt("c11 constructor options matrix"));
|
||||
expect_parse_info(matrix_constructor, & matrix_info, gen_txt("c11 constructor ParseInfo matrix"));
|
||||
|
||||
gen_CodeDefine matrix_define = gen_parse_define(gen_txt("#define PARSE_DEFINE 1"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_define != 0 && matrix_define->Type == CT_Preprocess_Define, gen_txt("c11 define options matrix"));
|
||||
expect_parse_info(matrix_define, & matrix_info, gen_txt("c11 define ParseInfo matrix"));
|
||||
|
||||
gen_CodeDestructor matrix_destructor = gen_parse_destructor(gen_txt("~ParseClass();"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_destructor != 0 && matrix_destructor->Type == CT_Destructor_Fwd, gen_txt("c11 destructor options matrix"));
|
||||
expect_parse_info(matrix_destructor, & matrix_info, gen_txt("c11 destructor ParseInfo matrix"));
|
||||
|
||||
gen_CodeEnum matrix_enum = gen_parse_enum(gen_txt("enum ParseEnum { ParseValue };"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_enum != 0 && matrix_enum->Type == CT_Enum, gen_txt("c11 enum options matrix"));
|
||||
expect_parse_info(matrix_enum, & matrix_info, gen_txt("c11 enum ParseInfo matrix"));
|
||||
|
||||
gen_CodeBody matrix_export_body = gen_parse_export_body(gen_txt("{ int export_value; }"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_export_body != 0 && matrix_export_body->Type == CT_Export_Body, gen_txt("c11 export body options matrix"));
|
||||
expect_parse_info(matrix_export_body, & matrix_info, gen_txt("c11 export body ParseInfo matrix"));
|
||||
|
||||
gen_CodeExtern matrix_extern = gen_parse_extern_link(gen_txt("extern \"C\" { int extern_value; }"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_extern != 0 && matrix_extern->Type == CT_Extern_Linkage, gen_txt("c11 extern options matrix"));
|
||||
expect_parse_info(matrix_extern, & matrix_info, gen_txt("c11 extern ParseInfo matrix"));
|
||||
|
||||
gen_CodeFriend matrix_friend = gen_parse_friend(gen_txt("friend int parse_friend_function();"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_friend != 0 && matrix_friend->Type == CT_Friend, gen_txt("c11 friend options matrix"));
|
||||
expect_parse_info(matrix_friend, & matrix_info, gen_txt("c11 friend ParseInfo matrix"));
|
||||
|
||||
gen_CodeFn matrix_function = gen_parse_function(gen_txt("int parse_function();"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_function != 0 && matrix_function->Type == CT_Function_Fwd, gen_txt("c11 function options matrix"));
|
||||
expect_parse_info(matrix_function, & matrix_info, gen_txt("c11 function ParseInfo matrix"));
|
||||
|
||||
gen_CodeNS matrix_namespace = gen_parse_namespace(gen_txt("namespace ParseNamespace {}"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_namespace != 0 && matrix_namespace->Type == CT_Namespace, gen_txt("c11 namespace options matrix"));
|
||||
expect_parse_info(matrix_namespace, & matrix_info, gen_txt("c11 namespace ParseInfo matrix"));
|
||||
|
||||
gen_CodeOperator matrix_operator = gen_parse_operator(gen_txt("s32 operator +(s32 lhs, s32 rhs);"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_operator != 0 && matrix_operator->Type == CT_Operator_Fwd, gen_txt("c11 operator options matrix"));
|
||||
expect_parse_info(matrix_operator, & matrix_info, gen_txt("c11 operator ParseInfo matrix"));
|
||||
|
||||
gen_CodeOpCast matrix_operator_cast = gen_parse_operator_cast(gen_txt("operator s32() const;"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_operator_cast != 0 && matrix_operator_cast->Type == CT_Operator_Cast_Fwd, gen_txt("c11 operator cast options matrix"));
|
||||
expect_parse_info(matrix_operator_cast, & matrix_info, gen_txt("c11 operator cast ParseInfo matrix"));
|
||||
|
||||
gen_CodeStruct matrix_struct = gen_parse_struct(gen_txt("struct ParseStruct {};"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_struct != 0 && matrix_struct->Type == CT_Struct, gen_txt("c11 struct options matrix"));
|
||||
expect_parse_info(matrix_struct, & matrix_info, gen_txt("c11 struct ParseInfo matrix"));
|
||||
|
||||
gen_CodeTemplate matrix_template = gen_parse_template(gen_txt("template<class T> struct ParseTemplate {};"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_template != 0 && matrix_template->Type == CT_Template, gen_txt("c11 template options matrix"));
|
||||
expect_parse_info(matrix_template, & matrix_info, gen_txt("c11 template ParseInfo matrix"));
|
||||
|
||||
gen_CodeTypename matrix_type = gen_parse_type(gen_txt("const s32*"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_type != 0 && matrix_type->Type == CT_Typename, gen_txt("c11 type options matrix"));
|
||||
expect_parse_info(matrix_type, & matrix_info, gen_txt("c11 type ParseInfo matrix"));
|
||||
|
||||
gen_CodeTypedef matrix_typedef = gen_parse_typedef(gen_txt("typedef s32 ParseTypedef;"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_typedef != 0 && matrix_typedef->Type == CT_Typedef, gen_txt("c11 typedef options matrix"));
|
||||
expect_parse_info(matrix_typedef, & matrix_info, gen_txt("c11 typedef ParseInfo matrix"));
|
||||
|
||||
gen_CodeUnion matrix_union = gen_parse_union(gen_txt("union ParseUnion { s32 value; };"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_union != 0 && matrix_union->Type == CT_Union, gen_txt("c11 union options matrix"));
|
||||
expect_parse_info(matrix_union, & matrix_info, gen_txt("c11 union ParseInfo matrix"));
|
||||
|
||||
gen_CodeUsing matrix_using = gen_parse_using(gen_txt("using ParseUsing = s32;"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_using != 0 && matrix_using->Type == CT_Using, gen_txt("c11 using options matrix"));
|
||||
expect_parse_info(matrix_using, & matrix_info, gen_txt("c11 using ParseInfo matrix"));
|
||||
|
||||
gen_CodeVar matrix_variable = gen_parse_variable(gen_txt("s32 parse_variable;"), .ctx = & ctx, .info = & matrix_info);
|
||||
expect(matrix_variable != 0 && matrix_variable->Type == CT_Variable, gen_txt("c11 variable options matrix"));
|
||||
expect_parse_info(matrix_variable, & matrix_info, gen_txt("c11 variable ParseInfo matrix"));
|
||||
|
||||
ParseInfo comma_info = {0};
|
||||
gen_CodeBody parsed_comma_body = gen_parse_global_body(gen_txt("struct ParseComma { s32 value; };\n,"), .ctx = & ctx, .info = & comma_info);
|
||||
expect(parsed_comma_body != 0 && parsed_comma_body->Type == CT_Global_Body, gen_txt("c11 comma parser result"));
|
||||
void* comma_result = parsed_comma_body;
|
||||
void* comma_info_result = comma_info.result;
|
||||
expect(comma_info_result == comma_result, gen_txt("c11 comma ParseInfo result"));
|
||||
expect(comma_info.lexed.tokens.num > 0 && comma_info.lexed.messages == 0, gen_txt("c11 comma retained tokens"));
|
||||
expect(comma_info.messages != 0, gen_txt("c11 comma parser diagnostic"));
|
||||
if (comma_info.messages != 0)
|
||||
expect(gen_str_contains(comma_info.messages->Content, gen_txt("Dangling comma")), gen_txt("c11 comma diagnostic content"));
|
||||
|
||||
ParseInfo recovery_info = {0};
|
||||
gen_CodeBody recovered_body = gen_parse_global_body(gen_txt("s32 before; s32 broken(s32 value; s32 after;"), .ctx = &ctx, .info = &recovery_info);
|
||||
gen_Str recovery_text = {0};
|
||||
if (recovered_body != 0) recovery_text = gen_strbuilder_to_str(gen_code_to_strbuilder((gen_Code)recovered_body));
|
||||
expect(recovered_body != 0 && recovered_body->Type == CT_Global_Body, gen_txt("c11 file-scope recovery body"));
|
||||
|
||||
void* recovered_result = recovered_body;
|
||||
expect(recovery_info.result == recovered_result, gen_txt("c11 file-scope recovery ParseInfo result"));
|
||||
expect(recovery_info.messages != 0, gen_txt("c11 file-scope recovery diagnostic"));
|
||||
if (recovery_info.messages != 0) {
|
||||
expect(gen_str_contains(recovery_info.messages->Content, gen_txt("Expected")), gen_txt("c11 file-scope recovery mismatch text"));
|
||||
expect(recovery_info.messages->Next == 0, gen_txt("c11 file-scope recovery single diagnostic"));
|
||||
}
|
||||
expect(gen_str_contains(recovery_text, gen_txt("before")), gen_txt("c11 file-scope recovery keeps preceding member"));
|
||||
expect(gen_str_contains(recovery_text, gen_txt("after")), gen_txt("c11 file-scope recovery keeps following member"));
|
||||
gen_Code recovered_member = 0;
|
||||
if (recovered_body != 0) recovered_member = recovered_body->Front;
|
||||
gen_Code recovered_before = recovered_member;
|
||||
expect(recovered_before != 0 && recovered_before->Type == CT_Variable, gen_txt("c11 mismatch recovery keeps preceding member typed"));
|
||||
if (recovered_before != 0)
|
||||
expect(gen_str_are_equal(recovered_before->Name, gen_txt("before")), gen_txt("c11 mismatch recovery preceding member name"));
|
||||
if (recovered_member != 0) recovered_member = recovered_member->Next;
|
||||
expect(recovered_member != 0 && recovered_member->Type == CT_Untyped, gen_txt("c11 file-scope recovery stores malformed member as untyped"));
|
||||
if (recovered_member != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(recovered_member)), gen_txt("s32 broken(s32 value;")), gen_txt("c11 file-scope recovery retains exact mismatch span"));
|
||||
gen_Code recovered_after = recovered_member != 0 ? recovered_member->Next : 0;
|
||||
expect(recovered_after != 0 && recovered_after->Type == CT_Variable, gen_txt("c11 file-scope recovery keeps following member typed"));
|
||||
if (recovered_after != 0)
|
||||
expect(gen_str_are_equal(recovered_after->Name, gen_txt("after")), gen_txt("c11 file-scope recovery following member name"));
|
||||
|
||||
ParseInfo invalid_member_info = {0};
|
||||
gen_CodeBody invalid_member_body = gen_parse_global_body(gen_txt("s32 before; = { 1; 2 }; s32 after;"), .ctx = &ctx, .info = &invalid_member_info);
|
||||
gen_Str invalid_member_text = {0};
|
||||
if (invalid_member_body != 0)
|
||||
invalid_member_text = gen_strbuilder_to_str(gen_code_to_strbuilder((gen_Code)invalid_member_body));
|
||||
expect(invalid_member_body != 0 && invalid_member_body->Type == CT_Global_Body, gen_txt("c11 invalid member recovery body"));
|
||||
void* invalid_member_result = invalid_member_body;
|
||||
expect(invalid_member_info.result == invalid_member_result, gen_txt("c11 invalid member recovery ParseInfo result"));
|
||||
expect(invalid_member_info.messages != 0, gen_txt("c11 invalid member recovery diagnostic"));
|
||||
if (invalid_member_info.messages != 0) {
|
||||
expect(gen_str_contains(invalid_member_info.messages->Content, gen_txt("Failed to parse member")), gen_txt("c11 invalid member recovery diagnostic text"));
|
||||
expect(invalid_member_info.messages->Next == 0, gen_txt("c11 invalid member recovery single diagnostic"));
|
||||
}
|
||||
expect(gen_str_contains(invalid_member_text, gen_txt("before")), gen_txt("c11 invalid member recovery keeps preceding member"));
|
||||
expect(gen_str_contains(invalid_member_text, gen_txt("after")), gen_txt("c11 invalid member recovery keeps following member"));
|
||||
gen_Code invalid_member_node = 0;
|
||||
if (invalid_member_body != 0) invalid_member_node = invalid_member_body->Front;
|
||||
gen_Code invalid_member_before = invalid_member_node;
|
||||
expect(invalid_member_before != 0 && invalid_member_before->Type == CT_Variable, gen_txt("c11 invalid member keeps preceding member typed"));
|
||||
if (invalid_member_before != 0)
|
||||
expect(gen_str_are_equal(invalid_member_before->Name, gen_txt("before")), gen_txt("c11 invalid member preceding member name"));
|
||||
if (invalid_member_node != 0) invalid_member_node = invalid_member_node->Next;
|
||||
expect(invalid_member_node != 0 && invalid_member_node->Type == CT_Untyped, gen_txt("c11 invalid member recovery stores member as untyped"));
|
||||
if (invalid_member_node != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(invalid_member_node)), gen_txt("= { 1; 2 };")), gen_txt("c11 invalid member recovery retains exact source span"));
|
||||
gen_Code invalid_member_after = invalid_member_node != 0 ? invalid_member_node->Next : 0;
|
||||
expect(invalid_member_after != 0 && invalid_member_after->Type == CT_Variable, gen_txt("c11 invalid member recovery keeps following member typed"));
|
||||
if (invalid_member_after != 0)
|
||||
expect(gen_str_are_equal(invalid_member_after->Name, gen_txt("after")), gen_txt("c11 invalid member recovery following member name"));
|
||||
|
||||
ParseInfo import_recovery_info = {0};
|
||||
gen_Opts_parse import_recovery_options = { .ctx = &ctx, .info = &import_recovery_info };
|
||||
gen_CodeBody import_recovery_body = gen_parse_global_body(gen_txt("s32 before; import foo; s32 after;"), .ctx = import_recovery_options.ctx, .info = import_recovery_options.info);
|
||||
gen_Code import_recovery_node = 0;
|
||||
if (import_recovery_body != 0) import_recovery_node = import_recovery_body->Front;
|
||||
gen_Code import_recovery_before = import_recovery_node;
|
||||
expect(import_recovery_before != 0 && import_recovery_before->Type == CT_Variable, gen_txt("c11 import recovery keeps preceding member typed"));
|
||||
if (import_recovery_before != 0)
|
||||
expect(gen_str_are_equal(import_recovery_before->Name, gen_txt("before")), gen_txt("c11 import recovery preceding member name"));
|
||||
if (import_recovery_node != 0) import_recovery_node = import_recovery_node->Next;
|
||||
expect(import_recovery_body != 0 && import_recovery_body->Type == CT_Global_Body, gen_txt("c11 import recovery body"));
|
||||
expect(import_recovery_info.messages != 0, gen_txt("c11 import recovery diagnostic"));
|
||||
if (import_recovery_info.messages != 0)
|
||||
expect(import_recovery_info.messages->Next == 0, gen_txt("c11 import recovery single diagnostic"));
|
||||
expect(import_recovery_node != 0 && import_recovery_node->Type == CT_Untyped, gen_txt("c11 import recovery preserves member as untyped"));
|
||||
if (import_recovery_node != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(import_recovery_node)), gen_txt("import foo;")), gen_txt("c11 import recovery exact source span"));
|
||||
gen_Code import_recovery_after = import_recovery_node != 0 ? import_recovery_node->Next : 0;
|
||||
expect(import_recovery_after != 0 && import_recovery_after->Type == CT_Variable, gen_txt("c11 import recovery keeps following member typed"));
|
||||
if (import_recovery_after != 0)
|
||||
expect(gen_str_are_equal(import_recovery_after->Name, gen_txt("after")), gen_txt("c11 import recovery following member name"));
|
||||
|
||||
gen_Macro unbounded_macro = { gen_txt("UnboundedRecoveryMacro"), MT_Expression, MF_Null };
|
||||
gen_register_macro(unbounded_macro);
|
||||
ParseInfo macro_recovery_info = {0};
|
||||
gen_Opts_parse macro_recovery_options = { .ctx = &ctx, .info = ¯o_recovery_info };
|
||||
gen_CodeBody macro_recovery_body = gen_parse_global_body(gen_txt("s32 before; UnboundedRecoveryMacro; s32 after;"), .ctx = macro_recovery_options.ctx, .info = macro_recovery_options.info);
|
||||
gen_Code macro_recovery_node = 0;
|
||||
if (macro_recovery_body != 0) macro_recovery_node = macro_recovery_body->Front;
|
||||
gen_Code macro_recovery_before = macro_recovery_node;
|
||||
expect(macro_recovery_before != 0 && macro_recovery_before->Type == CT_Variable, gen_txt("c11 macro recovery keeps preceding member typed"));
|
||||
if (macro_recovery_before != 0)
|
||||
expect(gen_str_are_equal(macro_recovery_before->Name, gen_txt("before")), gen_txt("c11 macro recovery preceding member name"));
|
||||
if (macro_recovery_node != 0) macro_recovery_node = macro_recovery_node->Next;
|
||||
expect(macro_recovery_body != 0 && macro_recovery_body->Type == CT_Global_Body, gen_txt("c11 unbounded macro recovery body"));
|
||||
expect(macro_recovery_info.messages != 0, gen_txt("c11 unbounded macro recovery diagnostic"));
|
||||
if (macro_recovery_info.messages != 0)
|
||||
expect(macro_recovery_info.messages->Next == 0, gen_txt("c11 unbounded macro recovery single diagnostic"));
|
||||
expect(macro_recovery_node != 0 && macro_recovery_node->Type == CT_Untyped, gen_txt("c11 unbounded macro recovery preserves member as untyped"));
|
||||
if (macro_recovery_node != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(macro_recovery_node)), gen_txt("UnboundedRecoveryMacro;")), gen_txt("c11 unbounded macro recovery exact source span"));
|
||||
gen_Code macro_recovery_after = macro_recovery_node != 0 ? macro_recovery_node->Next : 0;
|
||||
expect(macro_recovery_after != 0 && macro_recovery_after->Type == CT_Variable, gen_txt("c11 unbounded macro recovery keeps following member typed"));
|
||||
if (macro_recovery_after != 0)
|
||||
expect(gen_str_are_equal(macro_recovery_after->Name, gen_txt("after")), gen_txt("c11 unbounded macro recovery following member name"));
|
||||
|
||||
ParseInfo eof_recovery_info = {0};
|
||||
gen_CodeBody eof_recovery_body = gen_parse_global_body(gen_txt("s32 before; s32 broken(s32 value"), .ctx = &ctx, .info = &eof_recovery_info);
|
||||
gen_Code eof_recovery_node = 0;
|
||||
if (eof_recovery_body != 0) eof_recovery_node = eof_recovery_body->Front;
|
||||
gen_Code eof_recovery_before = eof_recovery_node;
|
||||
expect(eof_recovery_before != 0 && eof_recovery_before->Type == CT_Variable, gen_txt("c11 EOF recovery keeps preceding member typed"));
|
||||
if (eof_recovery_before != 0)
|
||||
expect(gen_str_are_equal(eof_recovery_before->Name, gen_txt("before")), gen_txt("c11 EOF recovery preceding member name"));
|
||||
if (eof_recovery_node != 0) eof_recovery_node = eof_recovery_node->Next;
|
||||
expect(eof_recovery_body != 0 && eof_recovery_body->Type == CT_Global_Body, gen_txt("c11 end-of-slice recovery body"));
|
||||
expect(eof_recovery_info.messages != 0, gen_txt("c11 end-of-slice recovery diagnostic"));
|
||||
if (eof_recovery_info.messages != 0)
|
||||
expect(eof_recovery_info.messages->Next == 0, gen_txt("c11 end-of-slice recovery single diagnostic"));
|
||||
expect(eof_recovery_node != 0 && eof_recovery_node->Type == CT_Untyped, gen_txt("c11 end-of-slice member is untyped"));
|
||||
if (eof_recovery_node != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(eof_recovery_node)), gen_txt("s32 broken(s32 value")), gen_txt("c11 end-of-slice recovery exact source span"));
|
||||
if (eof_recovery_node != 0)
|
||||
expect(eof_recovery_node->Next == 0, gen_txt("c11 end-of-slice recovery has no following node"));
|
||||
|
||||
ParseInfo comma_recovery_info = {0};
|
||||
gen_CodeBody comma_recovery_body = gen_parse_global_body(gen_txt("s32 before; , s32 after;"), .ctx = &ctx, .info = &comma_recovery_info);
|
||||
gen_Code comma_recovery_node = 0;
|
||||
if (comma_recovery_body != 0) comma_recovery_node = comma_recovery_body->Front;
|
||||
gen_Code comma_recovery_before = comma_recovery_node;
|
||||
expect(comma_recovery_before != 0 && comma_recovery_before->Type == CT_Variable, gen_txt("c11 comma recovery keeps preceding member typed"));
|
||||
if (comma_recovery_before != 0)
|
||||
expect(gen_str_are_equal(comma_recovery_before->Name, gen_txt("before")), gen_txt("c11 comma recovery preceding member name"));
|
||||
if (comma_recovery_node != 0) comma_recovery_node = comma_recovery_node->Next;
|
||||
expect(comma_recovery_body != 0 && comma_recovery_body->Type == CT_Global_Body, gen_txt("c11 comma recovery body"));
|
||||
expect(comma_recovery_info.messages != 0, gen_txt("c11 comma recovery diagnostic"));
|
||||
if (comma_recovery_info.messages != 0) {
|
||||
expect(gen_str_contains(comma_recovery_info.messages->Content, gen_txt("Dangling comma")), gen_txt("c11 comma recovery diagnostic text"));
|
||||
expect(comma_recovery_info.messages->Next == 0, gen_txt("c11 comma recovery single diagnostic"));
|
||||
}
|
||||
expect(comma_recovery_node != 0 && comma_recovery_node->Type == CT_Untyped, gen_txt("c11 comma recovery preserves token as untyped"));
|
||||
if (comma_recovery_node != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(comma_recovery_node)), gen_txt(",")), gen_txt("c11 comma recovery exact source span"));
|
||||
gen_Code comma_recovery_after = comma_recovery_node != 0 ? comma_recovery_node->Next : 0;
|
||||
expect(comma_recovery_after != 0 && comma_recovery_after->Type == CT_Variable, gen_txt("c11 comma recovery keeps following member typed"));
|
||||
if (comma_recovery_after != 0)
|
||||
expect(gen_str_are_equal(comma_recovery_after->Name, gen_txt("after")), gen_txt("c11 comma recovery following member name"));
|
||||
|
||||
gen_CodeBody recovery_reuse = gen_parse_global_body(gen_txt("s32 reused_after_recovery;"), .ctx = &ctx, .info = &recovery_info);
|
||||
gen_Str recovery_reuse_text = {0};
|
||||
if (recovery_reuse != 0)
|
||||
recovery_reuse_text = gen_strbuilder_to_str(gen_code_to_strbuilder((gen_Code)recovery_reuse));
|
||||
expect(recovery_reuse != 0 && recovery_reuse->Type == CT_Global_Body, gen_txt("c11 parser context reuse after recovery"));
|
||||
expect(recovery_info.messages == 0, gen_txt("c11 parser context reuse clears prior diagnostics"));
|
||||
expect(gen_str_contains(recovery_reuse_text, gen_txt("reused_after_recovery")), gen_txt("c11 parser context reuse parses later input"));
|
||||
|
||||
ParseInfo nested_struct_info = {0};
|
||||
gen_Opts_parse nested_struct_options = { .ctx = &ctx, .info = &nested_struct_info };
|
||||
gen_CodeStruct nested_struct = gen_parse_struct(gen_txt("struct NestedRecovery { s32 before; s32 broken(s32 value; s32 after; };"), .ctx = nested_struct_options.ctx, .info = nested_struct_options.info);
|
||||
expect(nested_struct != 0 && nested_struct->Type == CT_Struct, gen_txt("c11 nested struct recovery result"));
|
||||
void* nested_struct_result = nested_struct;
|
||||
void* nested_struct_info_result = nested_struct_info.result;
|
||||
expect(nested_struct_info_result == nested_struct_result, gen_txt("c11 nested struct recovery ParseInfo result"));
|
||||
expect(nested_struct_info.messages != 0, gen_txt("c11 nested struct recovery diagnostic"));
|
||||
if (nested_struct_info.messages != 0)
|
||||
expect(gen_str_contains(nested_struct_info.messages->Content, gen_txt("Expected")), gen_txt("c11 nested struct recovery diagnostic text"));
|
||||
gen_Code nested_struct_member = nested_struct != 0 && nested_struct->Body != 0 ? nested_struct->Body->Front : 0;
|
||||
expect(nested_struct_member != 0 && nested_struct_member->Type == CT_Variable, gen_txt("c11 nested struct recovery keeps preceding member typed"));
|
||||
if (nested_struct_member != 0)
|
||||
expect(gen_str_are_equal(nested_struct_member->Name, gen_txt("before")), gen_txt("c11 nested struct recovery preceding member name"));
|
||||
if (nested_struct_member != 0) nested_struct_member = nested_struct_member->Next;
|
||||
expect(nested_struct_member != 0 && nested_struct_member->Type == CT_Untyped, gen_txt("c11 nested struct recovery retains failed member"));
|
||||
if (nested_struct_member != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(nested_struct_member)), gen_txt("s32 broken(s32 value;")), gen_txt("c11 nested struct recovery exact failed span"));
|
||||
gen_Code nested_struct_after = nested_struct_member != 0 ? nested_struct_member->Next : 0;
|
||||
expect(nested_struct_after != 0 && nested_struct_after->Type == CT_Variable, gen_txt("c11 nested struct recovery keeps following member typed"));
|
||||
if (nested_struct_after != 0)
|
||||
expect(gen_str_are_equal(nested_struct_after->Name, gen_txt("after")), gen_txt("c11 nested struct recovery following member name"));
|
||||
gen_Str nested_struct_text = {0};
|
||||
if (nested_struct != 0)
|
||||
nested_struct_text = gen_strbuilder_to_str(gen_code_to_strbuilder((gen_Code)nested_struct));
|
||||
expect(gen_str_contains(nested_struct_text, gen_txt("s32 after")), gen_txt("c11 nested struct recovery serializes following member"));
|
||||
|
||||
ParseInfo nested_namespace_info = {0};
|
||||
gen_Opts_parse nested_namespace_options = { .ctx = &ctx, .info = &nested_namespace_info };
|
||||
gen_CodeNS nested_namespace = gen_parse_namespace(gen_txt("namespace NestedRecovery { s32 before; s32 broken(s32 value; s32 after; }"), .ctx = nested_namespace_options.ctx, .info = nested_namespace_options.info);
|
||||
expect(nested_namespace != 0 && nested_namespace->Type == CT_Namespace, gen_txt("c11 nested namespace recovery result"));
|
||||
void* nested_namespace_result = nested_namespace;
|
||||
void* nested_namespace_info_result = nested_namespace_info.result;
|
||||
expect(nested_namespace_info_result == nested_namespace_result, gen_txt("c11 nested namespace recovery ParseInfo result"));
|
||||
expect(nested_namespace_info.messages != 0, gen_txt("c11 nested namespace recovery diagnostic"));
|
||||
if (nested_namespace_info.messages != 0)
|
||||
expect(gen_str_contains(nested_namespace_info.messages->Content, gen_txt("Expected")), gen_txt("c11 nested namespace recovery diagnostic text"));
|
||||
gen_Code nested_namespace_member = nested_namespace != 0 && nested_namespace->Body != 0 ? nested_namespace->Body->Front : 0;
|
||||
expect(nested_namespace_member != 0 && nested_namespace_member->Type == CT_Variable, gen_txt("c11 nested namespace recovery keeps preceding member typed"));
|
||||
if (nested_namespace_member != 0)
|
||||
expect(gen_str_are_equal(nested_namespace_member->Name, gen_txt("before")), gen_txt("c11 nested namespace recovery preceding member name"));
|
||||
if (nested_namespace_member != 0) nested_namespace_member = nested_namespace_member->Next;
|
||||
expect(nested_namespace_member != 0 && nested_namespace_member->Type == CT_Untyped, gen_txt("c11 nested namespace recovery retains failed member"));
|
||||
if (nested_namespace_member != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(nested_namespace_member)), gen_txt("s32 broken(s32 value;")), gen_txt("c11 nested namespace recovery exact failed span"));
|
||||
gen_Code nested_namespace_after = nested_namespace_member != 0 ? nested_namespace_member->Next : 0;
|
||||
expect(nested_namespace_after != 0 && nested_namespace_after->Type == CT_Variable, gen_txt("c11 nested namespace recovery keeps following member typed"));
|
||||
if (nested_namespace_after != 0)
|
||||
expect(gen_str_are_equal(nested_namespace_after->Name, gen_txt("after")), gen_txt("c11 nested namespace recovery following member name"));
|
||||
gen_Str nested_namespace_text = {0};
|
||||
if (nested_namespace != 0)
|
||||
nested_namespace_text = gen_strbuilder_to_str(gen_code_to_strbuilder((gen_Code)nested_namespace));
|
||||
expect(gen_str_contains(nested_namespace_text, gen_txt("s32 after")), gen_txt("c11 nested namespace recovery serializes following member"));
|
||||
|
||||
ParseInfo nested_brace_info = {0};
|
||||
gen_Opts_parse nested_brace_options = { .ctx = &ctx, .info = &nested_brace_info };
|
||||
gen_CodeStruct nested_brace_struct = gen_parse_struct(gen_txt("struct NestedBraceRecovery { s32 before; s32 broken(s32 { 1; s32 after; };"), .ctx = nested_brace_options.ctx, .info = nested_brace_options.info);
|
||||
expect(nested_brace_struct != 0 && nested_brace_struct->Type == CT_Struct, gen_txt("c11 nested brace recovery result"));
|
||||
void* nested_brace_result = nested_brace_struct;
|
||||
void* nested_brace_info_result = nested_brace_info.result;
|
||||
expect(nested_brace_info_result == nested_brace_result, gen_txt("c11 nested brace recovery ParseInfo result"));
|
||||
expect(nested_brace_info.messages != 0, gen_txt("c11 nested brace recovery diagnostic"));
|
||||
gen_Code nested_brace_member = nested_brace_struct != 0 && nested_brace_struct->Body != 0 ? nested_brace_struct->Body->Front : 0;
|
||||
expect(nested_brace_member != 0 && nested_brace_member->Type == CT_Variable, gen_txt("c11 nested brace recovery keeps preceding member typed"));
|
||||
if (nested_brace_member != 0) nested_brace_member = nested_brace_member->Next;
|
||||
expect(nested_brace_member != 0 && nested_brace_member->Type == CT_Untyped, gen_txt("c11 nested brace recovery retains failed member"));
|
||||
if (nested_brace_member != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(nested_brace_member)), gen_txt("s32 broken(s32 { 1;")), gen_txt("c11 nested brace recovery exact failed span"));
|
||||
gen_Code nested_brace_after = nested_brace_member != 0 ? nested_brace_member->Next : 0;
|
||||
expect(nested_brace_after != 0 && nested_brace_after->Type == CT_Variable, gen_txt("c11 nested brace recovery keeps following member typed"));
|
||||
if (nested_brace_after != 0)
|
||||
expect(gen_str_are_equal(nested_brace_after->Name, gen_txt("after")), gen_txt("c11 nested brace recovery following member name"));
|
||||
gen_Str nested_brace_text = {0};
|
||||
if (nested_brace_struct != 0)
|
||||
nested_brace_text = gen_strbuilder_to_str(gen_code_to_strbuilder((gen_Code)nested_brace_struct));
|
||||
expect(gen_str_contains(nested_brace_text, gen_txt("s32 after")), gen_txt("c11 nested brace recovery serializes following member"));
|
||||
|
||||
ParseInfo nested_namespace_brace_info = {0};
|
||||
gen_Opts_parse nested_namespace_brace_options = { .ctx = &ctx, .info = &nested_namespace_brace_info };
|
||||
gen_CodeNS nested_namespace_brace = gen_parse_namespace(gen_txt("namespace NestedBraceRecovery { s32 before; s32 broken(s32 { 1; s32 after; }"), .ctx = nested_namespace_brace_options.ctx, .info = nested_namespace_brace_options.info);
|
||||
expect(nested_namespace_brace != 0 && nested_namespace_brace->Type == CT_Namespace, gen_txt("c11 nested namespace brace recovery result"));
|
||||
void* nested_namespace_brace_result = nested_namespace_brace;
|
||||
void* nested_namespace_brace_info_result = nested_namespace_brace_info.result;
|
||||
expect(nested_namespace_brace_info_result == nested_namespace_brace_result, gen_txt("c11 nested namespace brace recovery ParseInfo result"));
|
||||
gen_Code nested_namespace_brace_member = nested_namespace_brace != 0 && nested_namespace_brace->Body != 0 ? nested_namespace_brace->Body->Front : 0;
|
||||
expect(nested_namespace_brace_member != 0 && nested_namespace_brace_member->Type == CT_Variable, gen_txt("c11 nested namespace brace recovery keeps preceding member typed"));
|
||||
if (nested_namespace_brace_member != 0) nested_namespace_brace_member = nested_namespace_brace_member->Next;
|
||||
expect(nested_namespace_brace_member != 0 && nested_namespace_brace_member->Type == CT_Untyped, gen_txt("c11 nested namespace brace recovery retains failed member"));
|
||||
if (nested_namespace_brace_member != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(nested_namespace_brace_member)), gen_txt("s32 broken(s32 { 1;")), gen_txt("c11 nested namespace brace recovery exact failed span"));
|
||||
gen_Code nested_namespace_brace_after = nested_namespace_brace_member != 0 ? nested_namespace_brace_member->Next : 0;
|
||||
expect(nested_namespace_brace_after != 0 && nested_namespace_brace_after->Type == CT_Variable, gen_txt("c11 nested namespace brace recovery keeps following member typed"));
|
||||
if (nested_namespace_brace_after != 0)
|
||||
expect(gen_str_are_equal(nested_namespace_brace_after->Name, gen_txt("after")), gen_txt("c11 nested namespace brace recovery following member name"));
|
||||
gen_Str nested_namespace_brace_text = {0};
|
||||
if (nested_namespace_brace != 0)
|
||||
nested_namespace_brace_text = gen_strbuilder_to_str(gen_code_to_strbuilder((gen_Code)nested_namespace_brace));
|
||||
expect(gen_str_contains(nested_namespace_brace_text, gen_txt("s32 after")), gen_txt("c11 nested namespace brace recovery serializes following member"));
|
||||
|
||||
ParseInfo nested_brace_tail_info = {0};
|
||||
gen_Opts_parse nested_brace_tail_options = { .ctx = &ctx, .info = &nested_brace_tail_info };
|
||||
gen_CodeStruct nested_brace_tail_struct = gen_parse_struct(gen_txt("struct NestedBraceTailRecovery { s32 before; s32 broken(s32 { 1; 2 } foo{ 3; s32 after; };"), .ctx = nested_brace_tail_options.ctx, .info = nested_brace_tail_options.info);
|
||||
expect(nested_brace_tail_struct != 0 && nested_brace_tail_struct->Type == CT_Struct, gen_txt("c11 nested brace tail recovery result"));
|
||||
gen_Code nested_brace_tail_member = nested_brace_tail_struct != 0 && nested_brace_tail_struct->Body != 0 ? nested_brace_tail_struct->Body->Front : 0;
|
||||
if (nested_brace_tail_member != 0) nested_brace_tail_member = nested_brace_tail_member->Next;
|
||||
expect(nested_brace_tail_member != 0 && nested_brace_tail_member->Type == CT_Untyped, gen_txt("c11 nested brace tail recovery retains failed member"));
|
||||
if (nested_brace_tail_member != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(nested_brace_tail_member)), gen_txt("s32 broken(s32 { 1; 2 } foo{ 3;")), gen_txt("c11 nested brace tail recovery exact failed span"));
|
||||
gen_Code nested_brace_tail_after = nested_brace_tail_member != 0 ? nested_brace_tail_member->Next : 0;
|
||||
expect(nested_brace_tail_after != 0 && nested_brace_tail_after->Type == CT_Variable, gen_txt("c11 nested brace tail recovery keeps following member typed"));
|
||||
if (nested_brace_tail_after != 0)
|
||||
expect(gen_str_are_equal(nested_brace_tail_after->Name, gen_txt("after")), gen_txt("c11 nested brace tail recovery following member name"));
|
||||
|
||||
ParseInfo nested_namespace_tail_info = {0};
|
||||
gen_Opts_parse nested_namespace_tail_options = { .ctx = &ctx, .info = &nested_namespace_tail_info };
|
||||
gen_CodeNS nested_namespace_tail = gen_parse_namespace(gen_txt("namespace NestedBraceTailRecovery { s32 before; s32 broken(s32 { 1; 2 } foo{ 3; s32 after; }"), .ctx = nested_namespace_tail_options.ctx, .info = nested_namespace_tail_options.info);
|
||||
expect(nested_namespace_tail != 0 && nested_namespace_tail->Type == CT_Namespace, gen_txt("c11 nested namespace brace tail recovery result"));
|
||||
void* nested_namespace_tail_result = nested_namespace_tail;
|
||||
void* nested_namespace_tail_info_result = nested_namespace_tail_info.result;
|
||||
expect(nested_namespace_tail_info_result == nested_namespace_tail_result, gen_txt("c11 nested namespace brace tail recovery ParseInfo result"));
|
||||
gen_Code nested_namespace_tail_member = nested_namespace_tail != 0 && nested_namespace_tail->Body != 0 ? nested_namespace_tail->Body->Front : 0;
|
||||
if (nested_namespace_tail_member != 0) nested_namespace_tail_member = nested_namespace_tail_member->Next;
|
||||
expect(nested_namespace_tail_member != 0 && nested_namespace_tail_member->Type == CT_Untyped, gen_txt("c11 nested namespace brace tail recovery retains failed member"));
|
||||
if (nested_namespace_tail_member != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(nested_namespace_tail_member)), gen_txt("s32 broken(s32 { 1; 2 } foo{ 3;")), gen_txt("c11 nested namespace brace tail recovery exact failed span"));
|
||||
gen_Code nested_namespace_tail_after = nested_namespace_tail_member != 0 ? nested_namespace_tail_member->Next : 0;
|
||||
expect(nested_namespace_tail_after != 0 && nested_namespace_tail_after->Type == CT_Variable, gen_txt("c11 nested namespace brace tail recovery keeps following member typed"));
|
||||
if (nested_namespace_tail_after != 0)
|
||||
expect(gen_str_are_equal(nested_namespace_tail_after->Name, gen_txt("after")), gen_txt("c11 nested namespace brace tail recovery following member name"));
|
||||
|
||||
ParseInfo nested_inner_close_info = {0};
|
||||
gen_Opts_parse nested_inner_close_options = { .ctx = &ctx, .info = &nested_inner_close_info };
|
||||
gen_CodeStruct nested_inner_close_struct = gen_parse_struct(gen_txt("struct NestedInnerCloseRecovery { s32 before; s32 broken(s32 { 1; 2 } foo{ 3; s32 after; struct Inner { s32 x; }; s32 tail; };"), .ctx = nested_inner_close_options.ctx, .info = nested_inner_close_options.info);
|
||||
expect(nested_inner_close_struct != 0 && nested_inner_close_struct->Type == CT_Struct, gen_txt("c11 nested inner-close recovery result"));
|
||||
gen_Code nested_inner_close_member = nested_inner_close_struct != 0 && nested_inner_close_struct->Body != 0 ? nested_inner_close_struct->Body->Front : 0;
|
||||
if (nested_inner_close_member != 0) nested_inner_close_member = nested_inner_close_member->Next;
|
||||
expect(nested_inner_close_member != 0 && nested_inner_close_member->Type == CT_Untyped, gen_txt("c11 nested inner-close recovery retains failed member"));
|
||||
if (nested_inner_close_member != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(nested_inner_close_member)), gen_txt("s32 broken(s32 { 1; 2 } foo{ 3;")), gen_txt("c11 nested inner-close recovery exact failed span"));
|
||||
gen_Code nested_inner_close_after = nested_inner_close_member != 0 ? nested_inner_close_member->Next : 0;
|
||||
expect(nested_inner_close_after != 0 && nested_inner_close_after->Type == CT_Variable, gen_txt("c11 nested inner-close recovery keeps following member typed"));
|
||||
if (nested_inner_close_after != 0)
|
||||
expect(gen_str_are_equal(nested_inner_close_after->Name, gen_txt("after")), gen_txt("c11 nested inner-close recovery following member name"));
|
||||
gen_Code nested_inner_close_inner = nested_inner_close_after != 0 ? nested_inner_close_after->Next : 0;
|
||||
expect(nested_inner_close_inner != 0 && nested_inner_close_inner->Type == CT_Struct, gen_txt("c11 nested inner-close recovery parses nested struct"));
|
||||
gen_Code nested_inner_close_tail = nested_inner_close_inner != 0 ? nested_inner_close_inner->Next : 0;
|
||||
expect(nested_inner_close_tail != 0 && nested_inner_close_tail->Type == CT_Variable, gen_txt("c11 nested inner-close recovery parses trailing member"));
|
||||
|
||||
ParseInfo inner_before_sync_info = {0};
|
||||
gen_Opts_parse inner_before_sync_options = { .ctx = &ctx, .info = &inner_before_sync_info };
|
||||
gen_CodeStruct inner_before_sync = gen_parse_struct(gen_txt("struct NestedInnerBeforeSync { s32 before; s32 broken(foo{ struct Inner { s32 x; }; s32 after; };"), .ctx = inner_before_sync_options.ctx, .info = inner_before_sync_options.info);
|
||||
expect(inner_before_sync != 0 && inner_before_sync->Type == CT_Struct, gen_txt("c11 inner-before-sync recovery result"));
|
||||
gen_Code inner_before_sync_member = inner_before_sync != 0 && inner_before_sync->Body != 0 ? inner_before_sync->Body->Front : 0;
|
||||
expect(inner_before_sync_member != 0 && inner_before_sync_member->Type == CT_Variable, gen_txt("c11 inner-before-sync recovery keeps preceding member typed"));
|
||||
if (inner_before_sync_member != 0) inner_before_sync_member = inner_before_sync_member->Next;
|
||||
expect(inner_before_sync_member != 0 && inner_before_sync_member->Type == CT_Untyped, gen_txt("c11 inner-before-sync recovery retains failed member"));
|
||||
if (inner_before_sync_member != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(inner_before_sync_member)), gen_txt("s32 broken(foo{ struct Inner { s32 x; };")), gen_txt("c11 inner-before-sync recovery exact failed span"));
|
||||
gen_Code inner_before_sync_after = inner_before_sync_member != 0 ? inner_before_sync_member->Next : 0;
|
||||
expect(inner_before_sync_after != 0 && inner_before_sync_after->Type == CT_Variable, gen_txt("c11 inner-before-sync recovery keeps following member typed"));
|
||||
if (inner_before_sync_after != 0)
|
||||
expect(gen_str_are_equal(inner_before_sync_after->Name, gen_txt("after")), gen_txt("c11 inner-before-sync recovery following member name"));
|
||||
|
||||
ParseInfo global_namespace_recovery_info = {0};
|
||||
gen_Opts_parse global_namespace_recovery_options = { .ctx = &ctx, .info = &global_namespace_recovery_info };
|
||||
gen_CodeBody global_namespace_recovery = gen_parse_global_body(gen_txt("namespace NestedScopeInGlobal { s32 before; s32 broken(s32 { 1; 2 } foo{ 3; s32 after; } struct Later { s32 value; };"), .ctx = global_namespace_recovery_options.ctx, .info = global_namespace_recovery_options.info);
|
||||
expect(global_namespace_recovery != 0 && global_namespace_recovery->Type == CT_Global_Body, gen_txt("c11 global namespace recovery body"));
|
||||
void* global_namespace_recovery_result = global_namespace_recovery;
|
||||
void* global_namespace_recovery_info_result = global_namespace_recovery_info.result;
|
||||
expect(global_namespace_recovery_info_result == global_namespace_recovery_result, gen_txt("c11 global namespace recovery ParseInfo result"));
|
||||
gen_Code global_namespace_node = global_namespace_recovery != 0 ? global_namespace_recovery->Front : 0;
|
||||
expect(global_namespace_node != 0 && global_namespace_node->Type == CT_Namespace, gen_txt("c11 global namespace recovery preserves namespace"));
|
||||
gen_Code global_namespace_member = global_namespace_node != 0 && global_namespace_node->Body != 0 ? global_namespace_node->Body->Front : 0;
|
||||
if (global_namespace_member != 0) global_namespace_member = global_namespace_member->Next;
|
||||
expect(global_namespace_member != 0 && global_namespace_member->Type == CT_Untyped, gen_txt("c11 global namespace recovery retains failed member"));
|
||||
if (global_namespace_member != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(global_namespace_member)), gen_txt("s32 broken(s32 { 1; 2 } foo{ 3;")), gen_txt("c11 global namespace recovery exact failed span"));
|
||||
gen_Code global_namespace_after = global_namespace_member != 0 ? global_namespace_member->Next : 0;
|
||||
expect(global_namespace_after != 0 && global_namespace_after->Type == CT_Variable, gen_txt("c11 global namespace recovery keeps following member typed"));
|
||||
if (global_namespace_after != 0)
|
||||
expect(gen_str_are_equal(global_namespace_after->Name, gen_txt("after")), gen_txt("c11 global namespace recovery following member name"));
|
||||
gen_Code global_later_struct = global_namespace_node != 0 ? global_namespace_node->Next : 0;
|
||||
expect(global_later_struct != 0 && global_later_struct->Type == CT_Struct, gen_txt("c11 global namespace recovery preserves following global struct"));
|
||||
if (global_later_struct != 0)
|
||||
expect(gen_str_are_equal(global_later_struct->Name, gen_txt("Later")), gen_txt("c11 global namespace recovery following global name"));
|
||||
|
||||
ParseInfo global_struct_recovery_info = {0};
|
||||
gen_Opts_parse global_struct_recovery_options = { .ctx = &ctx, .info = &global_struct_recovery_info };
|
||||
gen_CodeBody global_struct_recovery = gen_parse_global_body(gen_txt("struct GlobalNestedRecovery { s32 before; s32 broken(s32 value; s32 after; }; struct LaterGlobal { s32 value; };"), .ctx = global_struct_recovery_options.ctx, .info = global_struct_recovery_options.info);
|
||||
expect(global_struct_recovery != 0 && global_struct_recovery->Type == CT_Global_Body, gen_txt("c11 global struct recovery body"));
|
||||
gen_Code global_struct_node = global_struct_recovery != 0 ? global_struct_recovery->Front : 0;
|
||||
gen_Code global_struct_before = global_struct_node != 0 && global_struct_node->Body != 0 ? global_struct_node->Body->Front : 0;
|
||||
gen_Code global_struct_member = global_struct_before != 0 ? global_struct_before->Next : 0;
|
||||
gen_Code global_struct_after = global_struct_member != 0 ? global_struct_member->Next : 0;
|
||||
gen_Code global_later_class = global_struct_node != 0 ? global_struct_node->Next : 0;
|
||||
expect(global_struct_node != 0 && global_struct_node->Type == CT_Struct, gen_txt("c11 global struct recovery preserves partial struct"));
|
||||
expect(global_struct_member != 0 && global_struct_member->Type == CT_Untyped, gen_txt("c11 global struct recovery retains failed member"));
|
||||
if (global_struct_member != 0)
|
||||
expect(gen_str_are_equal(gen_strbuilder_to_str(gen_code_to_strbuilder(global_struct_member)), gen_txt("s32 broken(s32 value;")), gen_txt("c11 global struct recovery exact failed span"));
|
||||
expect(global_struct_after != 0 && global_struct_after->Type == CT_Variable, gen_txt("c11 global struct recovery keeps following member"));
|
||||
if (global_struct_after != 0)
|
||||
expect(gen_str_are_equal(global_struct_after->Name, gen_txt("after")), gen_txt("c11 global struct recovery following member name"));
|
||||
expect(global_later_class != 0 && global_later_class->Type == CT_Struct, gen_txt("c11 global struct recovery keeps following global struct"));
|
||||
|
||||
ParseInfo nested_owner_info = {0};
|
||||
gen_Opts_parse nested_owner_options = { .ctx = & ctx, .info = & nested_owner_info };
|
||||
gen_CodeStruct nested_owner = gen_parse_struct(gen_txt("struct NestedOwner { s32 before; struct InnerRecovery { s32 inner_before; s32 broken(s32 value; s32 inner_after; }; s32 after; };"), .ctx = nested_owner_options.ctx, .info = nested_owner_options.info);
|
||||
expect(nested_owner != 0 && nested_owner->Type == CT_Struct, gen_txt("c11 nested owner recovery result"));
|
||||
void* nested_owner_result = nested_owner;
|
||||
void* nested_owner_info_result = nested_owner_info.result;
|
||||
expect(nested_owner_info_result == nested_owner_result, gen_txt("c11 nested owner recovery ParseInfo result"));
|
||||
expect(nested_owner_info.messages != 0, gen_txt("c11 nested owner recovery diagnostic"));
|
||||
|
||||
gen_Code nested_owner_before = nested_owner != 0 && nested_owner->Body != 0 ? nested_owner->Body->Front : 0;
|
||||
gen_Code nested_owner_inner = nested_owner_before != 0 ? nested_owner_before->Next : 0;
|
||||
gen_Code nested_owner_after = nested_owner_inner != 0 ? nested_owner_inner->Next : 0;
|
||||
gen_Code nested_owner_inner_before = nested_owner_inner != 0 && nested_owner_inner->Body != 0 ? nested_owner_inner->Body->Front : 0;
|
||||
gen_Code nested_owner_inner_failed = nested_owner_inner_before != 0 ? nested_owner_inner_before->Next : 0;
|
||||
gen_Code nested_owner_inner_after = nested_owner_inner_failed != 0 ? nested_owner_inner_failed->Next : 0;
|
||||
expect(nested_owner_before != 0 && nested_owner_before->Type == CT_Variable, gen_txt("c11 nested owner keeps preceding member typed"));
|
||||
expect(nested_owner_inner != 0 && nested_owner_inner->Type == CT_Struct, gen_txt("c11 nested owner retains inner struct AST"));
|
||||
expect(nested_owner_inner_before != 0 && nested_owner_inner_before->Type == CT_Variable, gen_txt("c11 nested inner recovery keeps preceding member typed"));
|
||||
expect(nested_owner_inner_failed != 0 && nested_owner_inner_failed->Type == CT_Untyped, gen_txt("c11 nested inner recovery retains failed member"));
|
||||
expect(nested_owner_inner_after != 0 && nested_owner_inner_after->Type == CT_Variable, gen_txt("c11 nested inner recovery keeps following member typed"));
|
||||
expect(nested_owner_after != 0 && nested_owner_after->Type == CT_Variable, gen_txt("c11 nested owner keeps following member typed"));
|
||||
|
||||
#if ! GEN_BUILD_DEBUG
|
||||
gen_CodeVar wrong_array_value = gen_def_variable(gen_def_type(name(u32)), name(invalid_array_expr));
|
||||
gen_CodeVar wrong_array_value = gen_def_variable(gen_def_type(name(u32)), name(invalid_array_expr));
|
||||
gen_CodeTypename invalid_array_type = gen_def_type(name(InvalidArrayOption), .gen_array_expr = (gen_Code)wrong_array_value);
|
||||
expect((gen_Code)invalid_array_type == gen_Code_Invalid, gen_txt("c11 wrong type invalid code"));
|
||||
|
||||
gen_CodeBody missing_union_body = 0;
|
||||
gen_CodeUnion missing_union = gen_def_union(name(MissingUnionBody), missing_union_body);
|
||||
gen_CodeBody missing_union_body = 0;
|
||||
gen_CodeUnion missing_union = gen_def_union(name(MissingUnionBody), missing_union_body);
|
||||
expect((gen_Code)missing_union == gen_Code_Invalid, gen_txt("c11 missing body invalid code"));
|
||||
#endif
|
||||
|
||||
gen_deinit(&ctx);
|
||||
gen_deinit(& ctx);
|
||||
if (g_failures != 0) { gen_log_fmt("c11 baseline smoke failed: %d\n", g_failures); return 1; }
|
||||
gen_log_fmt("c11 baseline smoke passed\n");
|
||||
return 0;
|
||||
|
||||
+488
-8
@@ -30,6 +30,14 @@ internal void expect_variable(gen::CodeVar var, gen::Str label) {
|
||||
}
|
||||
}
|
||||
|
||||
internal void expect_parse_result(gen::Code result, gen::ParseInfo* info, gen::CodeType expected, gen::Str label) {
|
||||
expect(result.ast != nullptr, label);
|
||||
if (result.ast != nullptr) expect(result->Type == expected, label);
|
||||
expect(info->result.ast == result.ast, label);
|
||||
expect(info->lexed.tokens.num > 0, label);
|
||||
expect(info->lexed.messages == nullptr && info->messages == nullptr, label);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
using namespace gen;
|
||||
@@ -401,10 +409,10 @@ int main()
|
||||
|
||||
CodeTypename operator_type = def_type(name(OperatorOptionType));
|
||||
CodeTypename operator_return = def_type(name(OperatorOptionReturn));
|
||||
CodeParams operator_lhs = def_param(operator_type, name(operator_option_lhs));
|
||||
CodeParams operator_rhs = def_param(operator_type, name(operator_option_rhs));
|
||||
CodeParams operator_params = def_params(args(operator_lhs, operator_rhs));
|
||||
CodeBody operator_body = def_function_body(args(untyped_str(txt("return operator_option_lhs;"))));
|
||||
CodeParams operator_lhs = def_param(operator_type, name(operator_option_lhs));
|
||||
CodeParams operator_rhs = def_param(operator_type, name(operator_option_rhs));
|
||||
CodeParams operator_params = def_params(args(operator_lhs, operator_rhs));
|
||||
CodeBody operator_body = def_function_body(args(untyped_str(txt("return operator_option_lhs;"))));
|
||||
Opts_def_operator operator_options = {};
|
||||
operator_options.params = operator_params;
|
||||
operator_options.ret_type = operator_return;
|
||||
@@ -452,19 +460,491 @@ int main()
|
||||
expect(str_contains(cast_text, txt("return 0")), txt("cpp operator cast body serialization"));
|
||||
}
|
||||
|
||||
Code untyped = code_str(char const* baseline_message = "GENCPP_BASELINE";); expect(untyped.ast != nullptr, txt("cpp untyped"));
|
||||
Str untyped_text = strbuilder_to_str(code_to_strbuilder(untyped)); expect_fragments(untyped_text, txt("cpp untyped content"));
|
||||
CodeVar parsed_untyped = parse_variable(untyped_text); expect_variable(parsed_untyped, txt("cpp untyped reparsed"));
|
||||
Code untyped = code_str(char const* baseline_message = "GENCPP_BASELINE";);
|
||||
Str untyped_text = strbuilder_to_str(code_to_strbuilder(untyped));
|
||||
CodeVar parsed_untyped = parse_variable(untyped_text);
|
||||
expect(untyped.ast != nullptr, txt("cpp untyped"));
|
||||
expect_fragments(untyped_text, txt("cpp untyped content"));
|
||||
expect_variable(parsed_untyped, txt("cpp untyped reparsed"));
|
||||
|
||||
CodeVar parsed = parse_variable(code(char const* baseline_message = "GENCPP_BASELINE"; ));
|
||||
expect_variable(parsed, txt("cpp parsed"));
|
||||
expect_fragments(strbuilder_to_str(code_to_strbuilder((Code)parsed)), txt("cpp parsed serialized"));
|
||||
|
||||
CodeClass parser_class = parse_class(txt("class ParseClass {};"));
|
||||
CodeConstructor parser_constructor = parse_constructor(txt("ParseClass() {}"));
|
||||
CodeDefine parser_define = parse_define(txt("#define PARSE_DEFINE 1"));
|
||||
CodeDestructor parser_destructor = parse_destructor(txt("~ParseClass();"));
|
||||
CodeEnum parser_enum = parse_enum(txt("enum ParseEnum { ParseValue };"));
|
||||
CodeBody parser_export_body = parse_export_body(txt("{ int export_value; }"));
|
||||
CodeExtern parser_extern = parse_extern_link(txt("extern \"C\" { int extern_value; }"));
|
||||
CodeFriend parser_friend = parse_friend(txt("friend int parse_friend_function();"));
|
||||
CodeFn parser_function = parse_function(txt("int parse_function();"));
|
||||
CodeBody parser_global_body = parse_global_body(txt("struct ParseGlobal { int value; };"));
|
||||
CodeNS parser_namespace = parse_namespace(txt("namespace ParseNamespace {}"));
|
||||
CodeOperator parser_operator = parse_operator(txt("s32 operator +(s32 lhs, s32 rhs);"));
|
||||
CodeOpCast parser_operator_cast = parse_operator_cast(txt("operator s32() const;"));
|
||||
CodeStruct parser_struct = parse_struct(txt("struct ParseStruct {};"));
|
||||
CodeTemplate parser_template = parse_template(txt("template<class T> struct ParseTemplate {};"));
|
||||
CodeTypename parser_type = parse_type(txt("const s32*"));
|
||||
CodeTypedef parser_typedef = parse_typedef(txt("typedef s32 ParseTypedef;"));
|
||||
CodeUnion parser_union = parse_union(txt("union ParseUnion { s32 value; };"));
|
||||
CodeUsing parser_using = parse_using(txt("using ParseUsing = s32;"));
|
||||
CodeVar parser_variable = parse_variable(txt("s32 parse_variable;"));
|
||||
|
||||
expect(parser_class.ast != nullptr && parser_class->Type == CT_Class, txt("cpp parse class fixture"));
|
||||
expect(parser_constructor.ast != nullptr && parser_constructor->Type == CT_Constructor, txt("cpp parse constructor fixture"));
|
||||
expect(parser_define.ast != nullptr && parser_define->Type == CT_Preprocess_Define, txt("cpp parse define fixture"));
|
||||
expect(parser_destructor.ast != nullptr && parser_destructor->Type == CT_Destructor_Fwd, txt("cpp parse destructor fixture"));
|
||||
expect(parser_enum.ast != nullptr && parser_enum->Type == CT_Enum, txt("cpp parse enum fixture"));
|
||||
expect(parser_export_body.ast != nullptr && parser_export_body->Type == CT_Export_Body, txt("cpp parse export body fixture"));
|
||||
expect(parser_extern.ast != nullptr && parser_extern->Type == CT_Extern_Linkage, txt("cpp parse extern fixture"));
|
||||
expect(parser_friend.ast != nullptr && parser_friend->Type == CT_Friend, txt("cpp parse friend fixture"));
|
||||
expect(parser_function.ast != nullptr && parser_function->Type == CT_Function_Fwd, txt("cpp parse function fixture"));
|
||||
expect(parser_global_body.ast != nullptr && parser_global_body->Type == CT_Global_Body, txt("cpp parse global body fixture"));
|
||||
expect(parser_namespace.ast != nullptr && parser_namespace->Type == CT_Namespace, txt("cpp parse namespace fixture"));
|
||||
expect(parser_operator.ast != nullptr && parser_operator->Type == CT_Operator_Fwd, txt("cpp parse operator fixture"));
|
||||
expect(parser_operator_cast.ast != nullptr && parser_operator_cast->Type == CT_Operator_Cast_Fwd, txt("cpp parse operator cast fixture"));
|
||||
expect(parser_struct.ast != nullptr && parser_struct->Type == CT_Struct, txt("cpp parse struct fixture"));
|
||||
expect(parser_template.ast != nullptr && parser_template->Type == CT_Template, txt("cpp parse template fixture"));
|
||||
expect(parser_type.ast != nullptr && parser_type->Type == CT_Typename, txt("cpp parse type fixture"));
|
||||
expect(parser_typedef.ast != nullptr && parser_typedef->Type == CT_Typedef, txt("cpp parse typedef fixture"));
|
||||
expect(parser_union.ast != nullptr && parser_union->Type == CT_Union, txt("cpp parse union fixture"));
|
||||
expect(parser_using.ast != nullptr && parser_using->Type == CT_Using, txt("cpp parse using fixture"));
|
||||
expect(parser_variable.ast != nullptr && parser_variable->Type == CT_Variable, txt("cpp parse variable fixture"));
|
||||
|
||||
CodeBody parsed_global_options = parse_global_body(txt("struct ParseGlobalOptions { s32 value; };"));
|
||||
Code global_options_code = parsed_global_options;
|
||||
expect(parsed_global_options.ast != nullptr, txt("cpp parse global body options"));
|
||||
expect(ctx.parse_info.result.ast == global_options_code.ast, txt("cpp default parse info result"));
|
||||
expect(ctx.parse_info.lexed.tokens.num > 0, txt("cpp default parse info tokens"));
|
||||
expect(ctx.parse_info.lexed.messages == nullptr && ctx.parse_info.messages == nullptr, txt("cpp default parse info messages"));
|
||||
|
||||
ParseInfo caller_info = {};
|
||||
Opts_parse parse_options = { & ctx, & caller_info };
|
||||
CodeVar parsed_option_variable = parse_variable(txt("s32 parsed_option_variable;"), parse_options);
|
||||
Code parsed_option_code = parsed_option_variable;
|
||||
expect(parsed_option_variable.ast != nullptr && parsed_option_variable->Type == CT_Variable, txt("cpp explicit parse variable options"));
|
||||
expect(str_contains(parsed_option_variable->Name, txt("parsed_option_variable")), txt("cpp explicit parse variable name"));
|
||||
expect(caller_info.result.ast == parsed_option_code.ast, txt("cpp explicit parse info result"));
|
||||
expect(caller_info.lexed.tokens.num > 0, txt("cpp explicit parse info tokens"));
|
||||
|
||||
ParseInfo lexer_info = {};
|
||||
Opts_parse lexer_options = { & ctx, & lexer_info };
|
||||
CodeVar parsed_lexer_prefix = parse_variable(txt("s32 parsed_lexer_prefix;@"), lexer_options);
|
||||
Code parsed_lexer_code = parsed_lexer_prefix;
|
||||
expect(parsed_lexer_prefix.ast != nullptr && parsed_lexer_prefix->Type == CT_Variable, txt("cpp parse lexer prefix result"));
|
||||
expect(lexer_info.result.ast == parsed_lexer_code.ast, txt("cpp parse lexer prefix info result"));
|
||||
expect(lexer_info.lexed.tokens.num > 0, txt("cpp parse lexer prefix retained tokens"));
|
||||
expect(lexer_info.lexed.messages != nullptr, txt("cpp parse lexer prefix diagnostic"));
|
||||
|
||||
expect_parse_result(parse_class (txt("class ParseClass {};"), parse_options), & caller_info, CT_Class, txt("cpp class options matrix"));
|
||||
expect_parse_result(parse_constructor (txt("ParseClass() {}"), parse_options), & caller_info, CT_Constructor, txt("cpp constructor options matrix"));
|
||||
expect_parse_result(parse_define (txt("#define PARSE_DEFINE 1"), parse_options), & caller_info, CT_Preprocess_Define, txt("cpp define options matrix"));
|
||||
expect_parse_result(parse_destructor (txt("~ParseClass();"), parse_options), & caller_info, CT_Destructor_Fwd, txt("cpp destructor options matrix"));
|
||||
expect_parse_result(parse_enum (txt("enum ParseEnum { ParseValue };"), parse_options), & caller_info, CT_Enum, txt("cpp enum options matrix"));
|
||||
expect_parse_result(parse_export_body (txt("{ int export_value; }"), parse_options), & caller_info, CT_Export_Body, txt("cpp export body options matrix"));
|
||||
expect_parse_result(parse_extern_link (txt("extern \"C\" { int extern_value; }"), parse_options), & caller_info, CT_Extern_Linkage, txt("cpp extern options matrix"));
|
||||
expect_parse_result(parse_friend (txt("friend int parse_friend_function();"), parse_options), & caller_info, CT_Friend, txt("cpp friend options matrix"));
|
||||
expect_parse_result(parse_function (txt("int parse_function();"), parse_options), & caller_info, CT_Function_Fwd, txt("cpp function options matrix"));
|
||||
expect_parse_result(parse_global_body (txt("struct ParseGlobal { int value; };"), parse_options), & caller_info, CT_Global_Body, txt("cpp global body options matrix"));
|
||||
expect_parse_result(parse_namespace (txt("namespace ParseNamespace {}"), parse_options), & caller_info, CT_Namespace, txt("cpp namespace options matrix"));
|
||||
expect_parse_result(parse_operator (txt("s32 operator +(s32 lhs, s32 rhs);"), parse_options), & caller_info, CT_Operator_Fwd, txt("cpp operator options matrix"));
|
||||
expect_parse_result(parse_operator_cast(txt("operator s32() const;"), parse_options), & caller_info, CT_Operator_Cast_Fwd, txt("cpp operator cast options matrix"));
|
||||
expect_parse_result(parse_struct (txt("struct ParseStruct {};"), parse_options), & caller_info, CT_Struct, txt("cpp struct options matrix"));
|
||||
expect_parse_result(parse_template (txt("template<class T> struct ParseTemplate {};"), parse_options), & caller_info, CT_Template, txt("cpp template options matrix"));
|
||||
expect_parse_result(parse_type (txt("const s32*"), parse_options), & caller_info, CT_Typename, txt("cpp type options matrix"));
|
||||
expect_parse_result(parse_typedef (txt("typedef s32 ParseTypedef;"), parse_options), & caller_info, CT_Typedef, txt("cpp typedef options matrix"));
|
||||
expect_parse_result(parse_union (txt("union ParseUnion { s32 value; };"), parse_options), & caller_info, CT_Union, txt("cpp union options matrix"));
|
||||
expect_parse_result(parse_using (txt("using ParseUsing = s32;"), parse_options), & caller_info, CT_Using, txt("cpp using options matrix"));
|
||||
expect_parse_result(parse_variable (txt("s32 parse_variable;"), parse_options), & caller_info, CT_Variable, txt("cpp variable options matrix"));
|
||||
|
||||
ParseInfo comma_info = {};
|
||||
Opts_parse comma_options = { &ctx, &comma_info };
|
||||
CodeBody parsed_comma_body = parse_global_body(txt("struct ParseComma { s32 value; };\n,"), comma_options);
|
||||
Code parsed_comma_code = parsed_comma_body;
|
||||
expect(parsed_comma_body.ast != nullptr && parsed_comma_body->Type == CT_Global_Body, txt("cpp comma parser result"));
|
||||
expect(comma_info.result.ast == parsed_comma_code.ast, txt("cpp comma ParseInfo result"));
|
||||
expect(comma_info.lexed.tokens.num > 0 && comma_info.lexed.messages == nullptr, txt("cpp comma retained tokens"));
|
||||
expect(comma_info.messages != nullptr, txt("cpp comma parser diagnostic"));
|
||||
if (comma_info.messages != nullptr)
|
||||
expect(str_contains(comma_info.messages->Content, txt("Dangling comma")), txt("cpp comma diagnostic content"));
|
||||
|
||||
ParseInfo recovery_info = {};
|
||||
Opts_parse recovery_options = { &ctx, &recovery_info };
|
||||
CodeBody recovered_body = parse_global_body(txt("s32 before; s32 broken(s32 value; s32 after;"), recovery_options);
|
||||
Str recovery_text = { nullptr, 0 };
|
||||
if (recovered_body.ast != nullptr)
|
||||
recovery_text = strbuilder_to_str(code_to_strbuilder((Code)recovered_body));
|
||||
expect(recovered_body.ast != nullptr && recovered_body->Type == CT_Global_Body, txt("cpp file-scope recovery body"));
|
||||
expect(recovery_info.result.ast == ((Code)recovered_body).ast, txt("cpp file-scope recovery ParseInfo result"));
|
||||
expect(recovery_info.messages != nullptr, txt("cpp file-scope recovery diagnostic"));
|
||||
if (recovery_info.messages != nullptr)
|
||||
expect(str_contains(recovery_info.messages->Content, txt("Expected")), txt("cpp file-scope recovery mismatch text"));
|
||||
if (recovery_info.messages != nullptr)
|
||||
expect(recovery_info.messages->Next == nullptr, txt("cpp file-scope recovery single diagnostic"));
|
||||
expect(str_contains(recovery_text, txt("before")), txt("cpp file-scope recovery keeps preceding member"));
|
||||
expect(str_contains(recovery_text, txt("after")), txt("cpp file-scope recovery keeps following member"));
|
||||
Code recovered_member = { nullptr };
|
||||
if (recovered_body.ast != nullptr) recovered_member = recovered_body->Front;
|
||||
Code recovered_before = recovered_member;
|
||||
expect(recovered_before != nullptr && recovered_before->Type == CT_Variable, txt("cpp mismatch recovery keeps preceding member typed"));
|
||||
if (recovered_before)
|
||||
expect(str_are_equal(recovered_before->Name, txt("before")), txt("cpp mismatch recovery preceding member name"));
|
||||
if (recovered_member) recovered_member = recovered_member->Next;
|
||||
expect(recovered_member != nullptr && recovered_member->Type == CT_Untyped, txt("cpp file-scope recovery stores malformed member as untyped"));
|
||||
if (recovered_member)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(recovered_member)), txt("s32 broken(s32 value;")), txt("cpp file-scope recovery retains exact mismatch span"));
|
||||
Code recovered_after = recovered_member ? recovered_member->Next : Code{ nullptr };
|
||||
expect(recovered_after != nullptr && recovered_after->Type == CT_Variable, txt("cpp file-scope recovery keeps following member typed"));
|
||||
if (recovered_after)
|
||||
expect(str_are_equal(recovered_after->Name, txt("after")), txt("cpp file-scope recovery following member name"));
|
||||
|
||||
ParseInfo invalid_member_info = {};
|
||||
Opts_parse invalid_member_options = { &ctx, &invalid_member_info };
|
||||
CodeBody invalid_member_body = parse_global_body(txt("s32 before; = { 1; 2 }; s32 after;"), invalid_member_options);
|
||||
Str invalid_member_text = { nullptr, 0 };
|
||||
if (invalid_member_body.ast != nullptr)
|
||||
invalid_member_text = strbuilder_to_str(code_to_strbuilder((Code)invalid_member_body));
|
||||
expect(invalid_member_body.ast != nullptr && invalid_member_body->Type == CT_Global_Body, txt("cpp invalid member recovery body"));
|
||||
expect(invalid_member_info.result.ast == ((Code)invalid_member_body).ast, txt("cpp invalid member recovery ParseInfo result"));
|
||||
expect(invalid_member_info.messages != nullptr, txt("cpp invalid member recovery diagnostic"));
|
||||
if (invalid_member_info.messages != nullptr) {
|
||||
expect(str_contains(invalid_member_info.messages->Content, txt("Failed to parse member")), txt("cpp invalid member recovery diagnostic text"));
|
||||
expect(invalid_member_info.messages->Next == nullptr, txt("cpp invalid member recovery single diagnostic"));
|
||||
}
|
||||
expect(str_contains(invalid_member_text, txt("before")), txt("cpp invalid member recovery keeps preceding member"));
|
||||
expect(str_contains(invalid_member_text, txt("after")), txt("cpp invalid member recovery keeps following member"));
|
||||
Code invalid_member_node = { nullptr };
|
||||
if (invalid_member_body.ast != nullptr) invalid_member_node = invalid_member_body->Front;
|
||||
Code invalid_member_before = invalid_member_node;
|
||||
expect(invalid_member_before != nullptr && invalid_member_before->Type == CT_Variable, txt("cpp invalid member keeps preceding member typed"));
|
||||
if (invalid_member_before)
|
||||
expect(str_are_equal(invalid_member_before->Name, txt("before")), txt("cpp invalid member preceding member name"));
|
||||
if (invalid_member_node) invalid_member_node = invalid_member_node->Next;
|
||||
expect(invalid_member_node != nullptr && invalid_member_node->Type == CT_Untyped, txt("cpp invalid member recovery stores member as untyped"));
|
||||
if (invalid_member_node)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(invalid_member_node)), txt("= { 1; 2 };")), txt("cpp invalid member recovery retains exact source span"));
|
||||
Code invalid_member_after = invalid_member_node ? invalid_member_node->Next : Code{ nullptr };
|
||||
expect(invalid_member_after != nullptr && invalid_member_after->Type == CT_Variable, txt("cpp invalid member recovery keeps following member typed"));
|
||||
if (invalid_member_after)
|
||||
expect(str_are_equal(invalid_member_after->Name, txt("after")), txt("cpp invalid member recovery following member name"));
|
||||
|
||||
ParseInfo import_recovery_info = {};
|
||||
Opts_parse import_recovery_options = { &ctx, &import_recovery_info };
|
||||
CodeBody import_recovery_body = parse_global_body(txt("s32 before; import foo; s32 after;"), import_recovery_options);
|
||||
Code import_recovery_node = { nullptr };
|
||||
if (import_recovery_body.ast != nullptr) import_recovery_node = import_recovery_body->Front;
|
||||
Code import_recovery_before = import_recovery_node;
|
||||
expect(import_recovery_before != nullptr && import_recovery_before->Type == CT_Variable, txt("cpp import recovery keeps preceding member typed"));
|
||||
if (import_recovery_before)
|
||||
expect(str_are_equal(import_recovery_before->Name, txt("before")), txt("cpp import recovery preceding member name"));
|
||||
if (import_recovery_node) import_recovery_node = import_recovery_node->Next;
|
||||
expect(import_recovery_body.ast != nullptr && import_recovery_body->Type == CT_Global_Body, txt("cpp import recovery body"));
|
||||
expect(import_recovery_info.messages != nullptr, txt("cpp import recovery diagnostic"));
|
||||
if (import_recovery_info.messages != nullptr)
|
||||
expect(import_recovery_info.messages->Next == nullptr, txt("cpp import recovery single diagnostic"));
|
||||
expect(import_recovery_node != nullptr && import_recovery_node->Type == CT_Untyped, txt("cpp import recovery preserves member as untyped"));
|
||||
if (import_recovery_node)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(import_recovery_node)), txt("import foo;")), txt("cpp import recovery exact source span"));
|
||||
Code import_recovery_after = import_recovery_node ? import_recovery_node->Next : Code{ nullptr };
|
||||
expect(import_recovery_after != nullptr && import_recovery_after->Type == CT_Variable, txt("cpp import recovery keeps following member typed"));
|
||||
if (import_recovery_after)
|
||||
expect(str_are_equal(import_recovery_after->Name, txt("after")), txt("cpp import recovery following member name"));
|
||||
|
||||
Macro unbounded_macro = { txt("UnboundedRecoveryMacro"), MT_Expression, MF_Null };
|
||||
register_macro(unbounded_macro);
|
||||
ParseInfo macro_recovery_info = {};
|
||||
Opts_parse macro_recovery_options = { &ctx, ¯o_recovery_info };
|
||||
CodeBody macro_recovery_body = parse_global_body(txt("s32 before; UnboundedRecoveryMacro; s32 after;"), macro_recovery_options);
|
||||
Code macro_recovery_node = { nullptr };
|
||||
if (macro_recovery_body.ast != nullptr) macro_recovery_node = macro_recovery_body->Front;
|
||||
Code macro_recovery_before = macro_recovery_node;
|
||||
expect(macro_recovery_before != nullptr && macro_recovery_before->Type == CT_Variable, txt("cpp macro recovery keeps preceding member typed"));
|
||||
if (macro_recovery_before)
|
||||
expect(str_are_equal(macro_recovery_before->Name, txt("before")), txt("cpp macro recovery preceding member name"));
|
||||
if (macro_recovery_node) macro_recovery_node = macro_recovery_node->Next;
|
||||
expect(macro_recovery_body.ast != nullptr && macro_recovery_body->Type == CT_Global_Body, txt("cpp unbounded macro recovery body"));
|
||||
expect(macro_recovery_info.messages != nullptr, txt("cpp unbounded macro recovery diagnostic"));
|
||||
if (macro_recovery_info.messages != nullptr)
|
||||
expect(macro_recovery_info.messages->Next == nullptr, txt("cpp unbounded macro recovery single diagnostic"));
|
||||
expect(macro_recovery_node != nullptr && macro_recovery_node->Type == CT_Untyped, txt("cpp unbounded macro recovery preserves member as untyped"));
|
||||
if (macro_recovery_node)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(macro_recovery_node)), txt("UnboundedRecoveryMacro;")), txt("cpp unbounded macro recovery exact source span"));
|
||||
Code macro_recovery_after = macro_recovery_node ? macro_recovery_node->Next : Code{ nullptr };
|
||||
expect(macro_recovery_after != nullptr && macro_recovery_after->Type == CT_Variable, txt("cpp unbounded macro recovery keeps following member typed"));
|
||||
if (macro_recovery_after)
|
||||
expect(str_are_equal(macro_recovery_after->Name, txt("after")), txt("cpp unbounded macro recovery following member name"));
|
||||
|
||||
ParseInfo eof_recovery_info = {};
|
||||
Opts_parse eof_recovery_options = { &ctx, &eof_recovery_info };
|
||||
CodeBody eof_recovery_body = parse_global_body(txt("s32 before; s32 broken(s32 value"), eof_recovery_options);
|
||||
Code eof_recovery_node = { nullptr };
|
||||
if (eof_recovery_body.ast != nullptr) eof_recovery_node = eof_recovery_body->Front;
|
||||
Code eof_recovery_before = eof_recovery_node;
|
||||
expect(eof_recovery_before != nullptr && eof_recovery_before->Type == CT_Variable, txt("cpp EOF recovery keeps preceding member typed"));
|
||||
if (eof_recovery_before)
|
||||
expect(str_are_equal(eof_recovery_before->Name, txt("before")), txt("cpp EOF recovery preceding member name"));
|
||||
if (eof_recovery_node) eof_recovery_node = eof_recovery_node->Next;
|
||||
expect(eof_recovery_body.ast != nullptr && eof_recovery_body->Type == CT_Global_Body, txt("cpp end-of-slice recovery body"));
|
||||
expect(eof_recovery_info.messages != nullptr, txt("cpp end-of-slice recovery diagnostic"));
|
||||
if (eof_recovery_info.messages != nullptr)
|
||||
expect(eof_recovery_info.messages->Next == nullptr, txt("cpp end-of-slice recovery single diagnostic"));
|
||||
expect(eof_recovery_node != nullptr && eof_recovery_node->Type == CT_Untyped, txt("cpp end-of-slice member is untyped"));
|
||||
if (eof_recovery_node)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(eof_recovery_node)), txt("s32 broken(s32 value")), txt("cpp end-of-slice recovery exact source span"));
|
||||
if (eof_recovery_node)
|
||||
expect(eof_recovery_node->Next == nullptr, txt("cpp end-of-slice recovery has no following node"));
|
||||
|
||||
ParseInfo comma_recovery_info = {};
|
||||
Opts_parse comma_recovery_options = { &ctx, &comma_recovery_info };
|
||||
CodeBody comma_recovery_body = parse_global_body(txt("s32 before; , s32 after;"), comma_recovery_options);
|
||||
Code comma_recovery_node = { nullptr };
|
||||
if (comma_recovery_body.ast != nullptr) comma_recovery_node = comma_recovery_body->Front;
|
||||
Code comma_recovery_before = comma_recovery_node;
|
||||
expect(comma_recovery_before != nullptr && comma_recovery_before->Type == CT_Variable, txt("cpp comma recovery keeps preceding member typed"));
|
||||
if (comma_recovery_before)
|
||||
expect(str_are_equal(comma_recovery_before->Name, txt("before")), txt("cpp comma recovery preceding member name"));
|
||||
if (comma_recovery_node) comma_recovery_node = comma_recovery_node->Next;
|
||||
expect(comma_recovery_body.ast != nullptr && comma_recovery_body->Type == CT_Global_Body, txt("cpp comma recovery body"));
|
||||
expect(comma_recovery_info.messages != nullptr, txt("cpp comma recovery diagnostic"));
|
||||
if (comma_recovery_info.messages != nullptr) {
|
||||
expect(str_contains(comma_recovery_info.messages->Content, txt("Dangling comma")), txt("cpp comma recovery diagnostic text"));
|
||||
expect(comma_recovery_info.messages->Next == nullptr, txt("cpp comma recovery single diagnostic"));
|
||||
}
|
||||
expect(comma_recovery_node != nullptr && comma_recovery_node->Type == CT_Untyped, txt("cpp comma recovery preserves token as untyped"));
|
||||
if (comma_recovery_node)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(comma_recovery_node)), txt(",")), txt("cpp comma recovery exact source span"));
|
||||
Code comma_recovery_after = comma_recovery_node ? comma_recovery_node->Next : Code{ nullptr };
|
||||
expect(comma_recovery_after != nullptr && comma_recovery_after->Type == CT_Variable, txt("cpp comma recovery keeps following member typed"));
|
||||
if (comma_recovery_after)
|
||||
expect(str_are_equal(comma_recovery_after->Name, txt("after")), txt("cpp comma recovery following member name"));
|
||||
|
||||
CodeBody recovery_reuse = parse_global_body(txt("s32 reused_after_recovery;"), recovery_options);
|
||||
Str recovery_reuse_text = { nullptr, 0 };
|
||||
if (recovery_reuse.ast != nullptr)
|
||||
recovery_reuse_text = strbuilder_to_str(code_to_strbuilder((Code)recovery_reuse));
|
||||
expect(recovery_reuse.ast != nullptr && recovery_reuse->Type == CT_Global_Body, txt("cpp parser context reuse after recovery"));
|
||||
expect(recovery_info.messages == nullptr, txt("cpp parser context reuse clears prior diagnostics"));
|
||||
expect(str_contains(recovery_reuse_text, txt("reused_after_recovery")), txt("cpp parser context reuse parses later input"));
|
||||
|
||||
ParseInfo nested_struct_info = {};
|
||||
Opts_parse nested_struct_options = { &ctx, &nested_struct_info };
|
||||
CodeStruct nested_struct = parse_struct(txt("struct NestedRecovery { s32 before; s32 broken(s32 value; s32 after; };"), nested_struct_options);
|
||||
expect(nested_struct.ast != nullptr && nested_struct->Type == CT_Struct, txt("cpp nested struct recovery result"));
|
||||
expect(nested_struct_info.result.ast == ((Code)nested_struct).ast, txt("cpp nested struct recovery ParseInfo result"));
|
||||
expect(nested_struct_info.messages != nullptr, txt("cpp nested struct recovery diagnostic"));
|
||||
if (nested_struct_info.messages != nullptr)
|
||||
expect(str_contains(nested_struct_info.messages->Content, txt("Expected")), txt("cpp nested struct recovery diagnostic text"));
|
||||
Code nested_struct_member = nested_struct.ast != nullptr && nested_struct->Body.ast != nullptr ? nested_struct->Body->Front : Code{ nullptr };
|
||||
expect(nested_struct_member != nullptr && nested_struct_member->Type == CT_Variable, txt("cpp nested struct recovery keeps preceding member typed"));
|
||||
if (nested_struct_member != nullptr)
|
||||
expect(str_are_equal(nested_struct_member->Name, txt("before")), txt("cpp nested struct recovery preceding member name"));
|
||||
if (nested_struct_member != nullptr) nested_struct_member = nested_struct_member->Next;
|
||||
expect(nested_struct_member != nullptr && nested_struct_member->Type == CT_Untyped, txt("cpp nested struct recovery retains failed member"));
|
||||
if (nested_struct_member != nullptr)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(nested_struct_member)), txt("s32 broken(s32 value;")), txt("cpp nested struct recovery exact failed span"));
|
||||
Code nested_struct_after = nested_struct_member != nullptr ? nested_struct_member->Next : Code{ nullptr };
|
||||
expect(nested_struct_after != nullptr && nested_struct_after->Type == CT_Variable, txt("cpp nested struct recovery keeps following member typed"));
|
||||
if (nested_struct_after != nullptr)
|
||||
expect(str_are_equal(nested_struct_after->Name, txt("after")), txt("cpp nested struct recovery following member name"));
|
||||
Str nested_struct_text = { nullptr, 0 };
|
||||
if (nested_struct.ast != nullptr)
|
||||
nested_struct_text = strbuilder_to_str(code_to_strbuilder((Code)nested_struct));
|
||||
expect(str_contains(nested_struct_text, txt("s32 after")), txt("cpp nested struct recovery serializes following member"));
|
||||
|
||||
ParseInfo nested_namespace_info = {};
|
||||
Opts_parse nested_namespace_options = { &ctx, &nested_namespace_info };
|
||||
CodeNS nested_namespace = parse_namespace(txt("namespace NestedRecovery { s32 before; s32 broken(s32 value; s32 after; }"), nested_namespace_options);
|
||||
expect(nested_namespace.ast != nullptr && nested_namespace->Type == CT_Namespace, txt("cpp nested namespace recovery result"));
|
||||
expect(nested_namespace_info.result.ast == ((Code)nested_namespace).ast, txt("cpp nested namespace recovery ParseInfo result"));
|
||||
expect(nested_namespace_info.messages != nullptr, txt("cpp nested namespace recovery diagnostic"));
|
||||
if (nested_namespace_info.messages != nullptr)
|
||||
expect(str_contains(nested_namespace_info.messages->Content, txt("Expected")), txt("cpp nested namespace recovery diagnostic text"));
|
||||
Code nested_namespace_member = nested_namespace.ast != nullptr && nested_namespace->Body.ast != nullptr ? nested_namespace->Body->Front : Code{ nullptr };
|
||||
expect(nested_namespace_member != nullptr && nested_namespace_member->Type == CT_Variable, txt("cpp nested namespace recovery keeps preceding member typed"));
|
||||
if (nested_namespace_member != nullptr)
|
||||
expect(str_are_equal(nested_namespace_member->Name, txt("before")), txt("cpp nested namespace recovery preceding member name"));
|
||||
if (nested_namespace_member != nullptr) nested_namespace_member = nested_namespace_member->Next;
|
||||
expect(nested_namespace_member != nullptr && nested_namespace_member->Type == CT_Untyped, txt("cpp nested namespace recovery retains failed member"));
|
||||
if (nested_namespace_member != nullptr)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(nested_namespace_member)), txt("s32 broken(s32 value;")), txt("cpp nested namespace recovery exact failed span"));
|
||||
Code nested_namespace_after = nested_namespace_member != nullptr ? nested_namespace_member->Next : Code{ nullptr };
|
||||
expect(nested_namespace_after != nullptr && nested_namespace_after->Type == CT_Variable, txt("cpp nested namespace recovery keeps following member typed"));
|
||||
if (nested_namespace_after != nullptr)
|
||||
expect(str_are_equal(nested_namespace_after->Name, txt("after")), txt("cpp nested namespace recovery following member name"));
|
||||
Str nested_namespace_text = { nullptr, 0 };
|
||||
if (nested_namespace.ast != nullptr)
|
||||
nested_namespace_text = strbuilder_to_str(code_to_strbuilder((Code)nested_namespace));
|
||||
expect(str_contains(nested_namespace_text, txt("s32 after")), txt("cpp nested namespace recovery serializes following member"));
|
||||
|
||||
ParseInfo nested_brace_info = {};
|
||||
Opts_parse nested_brace_options = { &ctx, &nested_brace_info };
|
||||
CodeStruct nested_brace_struct = parse_struct(txt("struct NestedBraceRecovery { s32 before; s32 broken(s32 { 1; s32 after; };"), nested_brace_options);
|
||||
expect(nested_brace_struct.ast != nullptr && nested_brace_struct->Type == CT_Struct, txt("cpp nested brace recovery result"));
|
||||
expect(nested_brace_info.result.ast == ((Code)nested_brace_struct).ast, txt("cpp nested brace recovery ParseInfo result"));
|
||||
expect(nested_brace_info.messages != nullptr, txt("cpp nested brace recovery diagnostic"));
|
||||
Code nested_brace_member = nested_brace_struct.ast != nullptr && nested_brace_struct->Body.ast != nullptr ? nested_brace_struct->Body->Front : Code{ nullptr };
|
||||
expect(nested_brace_member != nullptr && nested_brace_member->Type == CT_Variable, txt("cpp nested brace recovery keeps preceding member typed"));
|
||||
if (nested_brace_member != nullptr) nested_brace_member = nested_brace_member->Next;
|
||||
expect(nested_brace_member != nullptr && nested_brace_member->Type == CT_Untyped, txt("cpp nested brace recovery retains failed member"));
|
||||
if (nested_brace_member != nullptr)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(nested_brace_member)), txt("s32 broken(s32 { 1;")), txt("cpp nested brace recovery exact failed span"));
|
||||
Code nested_brace_after = nested_brace_member != nullptr ? nested_brace_member->Next : Code{ nullptr };
|
||||
expect(nested_brace_after != nullptr && nested_brace_after->Type == CT_Variable, txt("cpp nested brace recovery keeps following member typed"));
|
||||
if (nested_brace_after != nullptr)
|
||||
expect(str_are_equal(nested_brace_after->Name, txt("after")), txt("cpp nested brace recovery following member name"));
|
||||
Str nested_brace_text = { nullptr, 0 };
|
||||
if (nested_brace_struct.ast != nullptr)
|
||||
nested_brace_text = strbuilder_to_str(code_to_strbuilder((Code)nested_brace_struct));
|
||||
expect(str_contains(nested_brace_text, txt("s32 after")), txt("cpp nested brace recovery serializes following member"));
|
||||
|
||||
ParseInfo nested_namespace_brace_info = {};
|
||||
Opts_parse nested_namespace_brace_options = { &ctx, &nested_namespace_brace_info };
|
||||
CodeNS nested_namespace_brace = parse_namespace(txt("namespace NestedBraceRecovery { s32 before; s32 broken(s32 { 1; s32 after; }"), nested_namespace_brace_options);
|
||||
expect(nested_namespace_brace.ast != nullptr && nested_namespace_brace->Type == CT_Namespace, txt("cpp nested namespace brace recovery result"));
|
||||
expect(nested_namespace_brace_info.result.ast == ((Code)nested_namespace_brace).ast, txt("cpp nested namespace brace recovery ParseInfo result"));
|
||||
Code nested_namespace_brace_member = nested_namespace_brace.ast != nullptr && nested_namespace_brace->Body.ast != nullptr ? nested_namespace_brace->Body->Front : Code{ nullptr };
|
||||
expect(nested_namespace_brace_member != nullptr && nested_namespace_brace_member->Type == CT_Variable, txt("cpp nested namespace brace recovery keeps preceding member typed"));
|
||||
if (nested_namespace_brace_member != nullptr) nested_namespace_brace_member = nested_namespace_brace_member->Next;
|
||||
expect(nested_namespace_brace_member != nullptr && nested_namespace_brace_member->Type == CT_Untyped, txt("cpp nested namespace brace recovery retains failed member"));
|
||||
if (nested_namespace_brace_member != nullptr)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(nested_namespace_brace_member)), txt("s32 broken(s32 { 1;")), txt("cpp nested namespace brace recovery exact failed span"));
|
||||
Code nested_namespace_brace_after = nested_namespace_brace_member != nullptr ? nested_namespace_brace_member->Next : Code{ nullptr };
|
||||
expect(nested_namespace_brace_after != nullptr && nested_namespace_brace_after->Type == CT_Variable, txt("cpp nested namespace brace recovery keeps following member typed"));
|
||||
if (nested_namespace_brace_after != nullptr)
|
||||
expect(str_are_equal(nested_namespace_brace_after->Name, txt("after")), txt("cpp nested namespace brace recovery following member name"));
|
||||
Str nested_namespace_brace_text = { nullptr, 0 };
|
||||
if (nested_namespace_brace.ast != nullptr)
|
||||
nested_namespace_brace_text = strbuilder_to_str(code_to_strbuilder((Code)nested_namespace_brace));
|
||||
expect(str_contains(nested_namespace_brace_text, txt("s32 after")), txt("cpp nested namespace brace recovery serializes following member"));
|
||||
|
||||
ParseInfo nested_brace_tail_info = {};
|
||||
Opts_parse nested_brace_tail_options = { &ctx, &nested_brace_tail_info };
|
||||
CodeStruct nested_brace_tail_struct = parse_struct(txt("struct NestedBraceTailRecovery { s32 before; s32 broken(s32 { 1; 2 } foo{ 3; s32 after; };"), nested_brace_tail_options);
|
||||
expect(nested_brace_tail_struct.ast != nullptr && nested_brace_tail_struct->Type == CT_Struct, txt("cpp nested brace tail recovery result"));
|
||||
expect(nested_brace_tail_info.result.ast == ((Code)nested_brace_tail_struct).ast, txt("cpp nested brace tail recovery ParseInfo result"));
|
||||
expect(nested_brace_tail_info.messages != nullptr, txt("cpp nested brace tail recovery diagnostic"));
|
||||
Code nested_brace_tail_member = nested_brace_tail_struct.ast != nullptr && nested_brace_tail_struct->Body.ast != nullptr ? nested_brace_tail_struct->Body->Front : Code{ nullptr };
|
||||
if (nested_brace_tail_member != nullptr) nested_brace_tail_member = nested_brace_tail_member->Next;
|
||||
expect(nested_brace_tail_member != nullptr && nested_brace_tail_member->Type == CT_Untyped, txt("cpp nested brace tail recovery retains failed member"));
|
||||
if (nested_brace_tail_member != nullptr)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(nested_brace_tail_member)), txt("s32 broken(s32 { 1; 2 } foo{ 3;")), txt("cpp nested brace tail recovery exact failed span"));
|
||||
Code nested_brace_tail_after = nested_brace_tail_member != nullptr ? nested_brace_tail_member->Next : Code{ nullptr };
|
||||
expect(nested_brace_tail_after != nullptr && nested_brace_tail_after->Type == CT_Variable, txt("cpp nested brace tail recovery keeps following member typed"));
|
||||
if (nested_brace_tail_after != nullptr)
|
||||
expect(str_are_equal(nested_brace_tail_after->Name, txt("after")), txt("cpp nested brace tail recovery following member name"));
|
||||
|
||||
ParseInfo nested_namespace_tail_info = {};
|
||||
Opts_parse nested_namespace_tail_options = { &ctx, &nested_namespace_tail_info };
|
||||
CodeNS nested_namespace_tail = parse_namespace(txt("namespace NestedBraceTailRecovery { s32 before; s32 broken(s32 { 1; 2 } foo{ 3; s32 after; }"), nested_namespace_tail_options);
|
||||
expect(nested_namespace_tail.ast != nullptr && nested_namespace_tail->Type == CT_Namespace, txt("cpp nested namespace brace tail recovery result"));
|
||||
expect(nested_namespace_tail_info.result.ast == ((Code)nested_namespace_tail).ast, txt("cpp nested namespace brace tail recovery ParseInfo result"));
|
||||
Code nested_namespace_tail_member = nested_namespace_tail.ast != nullptr && nested_namespace_tail->Body.ast != nullptr ? nested_namespace_tail->Body->Front : Code{ nullptr };
|
||||
if (nested_namespace_tail_member != nullptr) nested_namespace_tail_member = nested_namespace_tail_member->Next;
|
||||
expect(nested_namespace_tail_member != nullptr && nested_namespace_tail_member->Type == CT_Untyped, txt("cpp nested namespace brace tail recovery retains failed member"));
|
||||
if (nested_namespace_tail_member != nullptr)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(nested_namespace_tail_member)), txt("s32 broken(s32 { 1; 2 } foo{ 3;")), txt("cpp nested namespace brace tail recovery exact failed span"));
|
||||
Code nested_namespace_tail_after = nested_namespace_tail_member != nullptr ? nested_namespace_tail_member->Next : Code{ nullptr };
|
||||
expect(nested_namespace_tail_after != nullptr && nested_namespace_tail_after->Type == CT_Variable, txt("cpp nested namespace brace tail recovery keeps following member typed"));
|
||||
if (nested_namespace_tail_after != nullptr)
|
||||
expect(str_are_equal(nested_namespace_tail_after->Name, txt("after")), txt("cpp nested namespace brace tail recovery following member name"));
|
||||
|
||||
ParseInfo nested_inner_close_info = {};
|
||||
Opts_parse nested_inner_close_options = { &ctx, &nested_inner_close_info };
|
||||
CodeStruct nested_inner_close_struct = parse_struct(txt("struct NestedInnerCloseRecovery { s32 before; s32 broken(s32 { 1; 2 } foo{ 3; s32 after; struct Inner { s32 x; }; s32 tail; };"), nested_inner_close_options);
|
||||
expect(nested_inner_close_struct.ast != nullptr && nested_inner_close_struct->Type == CT_Struct, txt("cpp nested inner-close recovery result"));
|
||||
Code nested_inner_close_member = nested_inner_close_struct.ast != nullptr && nested_inner_close_struct->Body.ast != nullptr ? nested_inner_close_struct->Body->Front : Code{ nullptr };
|
||||
if (nested_inner_close_member != nullptr) nested_inner_close_member = nested_inner_close_member->Next;
|
||||
expect(nested_inner_close_member != nullptr && nested_inner_close_member->Type == CT_Untyped, txt("cpp nested inner-close recovery retains failed member"));
|
||||
if (nested_inner_close_member != nullptr)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(nested_inner_close_member)), txt("s32 broken(s32 { 1; 2 } foo{ 3;")), txt("cpp nested inner-close recovery exact failed span"));
|
||||
Code nested_inner_close_after = nested_inner_close_member != nullptr ? nested_inner_close_member->Next : Code{ nullptr };
|
||||
expect(nested_inner_close_after != nullptr && nested_inner_close_after->Type == CT_Variable, txt("cpp nested inner-close recovery keeps following member typed"));
|
||||
if (nested_inner_close_after != nullptr)
|
||||
expect(str_are_equal(nested_inner_close_after->Name, txt("after")), txt("cpp nested inner-close recovery following member name"));
|
||||
Code nested_inner_close_inner = nested_inner_close_after != nullptr ? nested_inner_close_after->Next : Code{ nullptr };
|
||||
expect(nested_inner_close_inner != nullptr && nested_inner_close_inner->Type == CT_Struct, txt("cpp nested inner-close recovery parses nested struct"));
|
||||
Code nested_inner_close_tail = nested_inner_close_inner != nullptr ? nested_inner_close_inner->Next : Code{ nullptr };
|
||||
expect(nested_inner_close_tail != nullptr && nested_inner_close_tail->Type == CT_Variable, txt("cpp nested inner-close recovery parses trailing member"));
|
||||
|
||||
ParseInfo inner_before_sync_info = {};
|
||||
Opts_parse inner_before_sync_options = { &ctx, &inner_before_sync_info };
|
||||
CodeStruct inner_before_sync = parse_struct(txt("struct NestedInnerBeforeSync { s32 before; s32 broken(foo{ struct Inner { s32 x; }; s32 after; };"), inner_before_sync_options);
|
||||
expect(inner_before_sync.ast != nullptr && inner_before_sync->Type == CT_Struct, txt("cpp inner-before-sync recovery result"));
|
||||
Code inner_before_sync_member = inner_before_sync.ast != nullptr && inner_before_sync->Body.ast != nullptr ? inner_before_sync->Body->Front : Code{ nullptr };
|
||||
if (inner_before_sync_member != nullptr) inner_before_sync_member = inner_before_sync_member->Next;
|
||||
expect(inner_before_sync_member != nullptr && inner_before_sync_member->Type == CT_Untyped, txt("cpp inner-before-sync recovery retains failed member"));
|
||||
if (inner_before_sync_member != nullptr)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(inner_before_sync_member)), txt("s32 broken(foo{ struct Inner { s32 x; };")), txt("cpp inner-before-sync recovery exact failed span"));
|
||||
Code inner_before_sync_after = inner_before_sync_member != nullptr ? inner_before_sync_member->Next : Code{ nullptr };
|
||||
expect(inner_before_sync_after != nullptr && inner_before_sync_after->Type == CT_Variable, txt("cpp inner-before-sync recovery keeps following member typed"));
|
||||
if (inner_before_sync_after != nullptr)
|
||||
expect(str_are_equal(inner_before_sync_after->Name, txt("after")), txt("cpp inner-before-sync recovery following member name"));
|
||||
|
||||
ParseInfo global_namespace_recovery_info = {};
|
||||
Opts_parse global_namespace_recovery_options = { &ctx, &global_namespace_recovery_info };
|
||||
CodeBody global_namespace_recovery = parse_global_body(txt("namespace NestedScopeInGlobal { s32 before; s32 broken(s32 { 1; 2 } foo{ 3; s32 after; } struct Later { s32 value; };"), global_namespace_recovery_options);
|
||||
expect(global_namespace_recovery.ast != nullptr && global_namespace_recovery->Type == CT_Global_Body, txt("cpp global namespace recovery body"));
|
||||
expect(global_namespace_recovery_info.result.ast == ((Code)global_namespace_recovery).ast, txt("cpp global namespace recovery ParseInfo result"));
|
||||
Code global_namespace_node = global_namespace_recovery.ast != nullptr ? global_namespace_recovery->Front : Code{ nullptr };
|
||||
expect(global_namespace_node != nullptr && global_namespace_node->Type == CT_Namespace, txt("cpp global namespace recovery preserves namespace"));
|
||||
Code global_namespace_member = global_namespace_node != nullptr && global_namespace_node->Body.ast != nullptr ? global_namespace_node->Body->Front : Code{ nullptr };
|
||||
if (global_namespace_member != nullptr) global_namespace_member = global_namespace_member->Next;
|
||||
expect(global_namespace_member != nullptr && global_namespace_member->Type == CT_Untyped, txt("cpp global namespace recovery retains failed member"));
|
||||
if (global_namespace_member != nullptr)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(global_namespace_member)), txt("s32 broken(s32 { 1; 2 } foo{ 3;")), txt("cpp global namespace recovery exact failed span"));
|
||||
Code global_namespace_after = global_namespace_member != nullptr ? global_namespace_member->Next : Code{ nullptr };
|
||||
expect(global_namespace_after != nullptr && global_namespace_after->Type == CT_Variable, txt("cpp global namespace recovery keeps following member typed"));
|
||||
if (global_namespace_after != nullptr)
|
||||
expect(str_are_equal(global_namespace_after->Name, txt("after")), txt("cpp global namespace recovery following member name"));
|
||||
Code global_later_struct = global_namespace_node != nullptr ? global_namespace_node->Next : Code{ nullptr };
|
||||
expect(global_later_struct != nullptr && global_later_struct->Type == CT_Struct, txt("cpp global namespace recovery preserves following global struct"));
|
||||
if (global_later_struct != nullptr)
|
||||
expect(str_are_equal(global_later_struct->Name, txt("Later")), txt("cpp global namespace recovery following global name"));
|
||||
|
||||
ParseInfo global_struct_recovery_info = {};
|
||||
Opts_parse global_struct_recovery_options = { &ctx, &global_struct_recovery_info };
|
||||
CodeBody global_struct_recovery = parse_global_body(txt("struct GlobalNestedRecovery { s32 before; s32 broken(s32 value; s32 after; }; struct LaterGlobal { s32 value; };"), global_struct_recovery_options);
|
||||
expect(global_struct_recovery.ast != nullptr && global_struct_recovery->Type == CT_Global_Body, txt("cpp global struct recovery body"));
|
||||
Code global_struct_node = global_struct_recovery.ast != nullptr ? global_struct_recovery->Front : Code{ nullptr };
|
||||
Code global_struct_before = global_struct_node != nullptr && global_struct_node->Body.ast != nullptr ? global_struct_node->Body->Front : Code{ nullptr };
|
||||
Code global_struct_member = global_struct_before != nullptr ? global_struct_before->Next : Code{ nullptr };
|
||||
Code global_struct_after = global_struct_member != nullptr ? global_struct_member->Next : Code{ nullptr };
|
||||
Code global_later_class = global_struct_node != nullptr ? global_struct_node->Next : Code{ nullptr };
|
||||
expect(global_struct_node != nullptr && global_struct_node->Type == CT_Struct, txt("cpp global struct recovery preserves partial struct"));
|
||||
expect(global_struct_member != nullptr && global_struct_member->Type == CT_Untyped, txt("cpp global struct recovery retains failed member"));
|
||||
if (global_struct_member != nullptr)
|
||||
expect(str_are_equal(strbuilder_to_str(code_to_strbuilder(global_struct_member)), txt("s32 broken(s32 value;")), txt("cpp global struct recovery exact failed span"));
|
||||
expect(global_struct_after != nullptr && global_struct_after->Type == CT_Variable, txt("cpp global struct recovery keeps following member"));
|
||||
if (global_struct_after != nullptr)
|
||||
expect(str_are_equal(global_struct_after->Name, txt("after")), txt("cpp global struct recovery following member name"));
|
||||
expect(global_later_class != nullptr && global_later_class->Type == CT_Struct, txt("cpp global struct recovery keeps following global struct"));
|
||||
|
||||
ParseInfo nested_owner_info = {};
|
||||
Opts_parse nested_owner_options = { &ctx, &nested_owner_info };
|
||||
CodeStruct nested_owner = parse_struct(txt("struct NestedOwner { s32 before; struct InnerRecovery { s32 inner_before; s32 broken(s32 value; s32 inner_after; }; s32 after; };"), nested_owner_options);
|
||||
expect(nested_owner.ast != nullptr && nested_owner->Type == CT_Struct, txt("cpp nested owner recovery result"));
|
||||
expect(nested_owner_info.result.ast == ((Code)nested_owner).ast, txt("cpp nested owner recovery ParseInfo result"));
|
||||
expect(nested_owner_info.messages != nullptr, txt("cpp nested owner recovery diagnostic"));
|
||||
Code nested_owner_before = nested_owner.ast != nullptr && nested_owner->Body.ast != nullptr ? nested_owner->Body->Front : Code{ nullptr };
|
||||
Code nested_owner_inner = nested_owner_before != nullptr ? nested_owner_before->Next : Code{ nullptr };
|
||||
Code nested_owner_after = nested_owner_inner != nullptr ? nested_owner_inner->Next : Code{ nullptr };
|
||||
Code nested_owner_inner_before = nested_owner_inner != nullptr && nested_owner_inner->Body.ast != nullptr ? nested_owner_inner->Body->Front : Code{ nullptr };
|
||||
Code nested_owner_inner_failed = nested_owner_inner_before != nullptr ? nested_owner_inner_before->Next : Code{ nullptr };
|
||||
Code nested_owner_inner_after = nested_owner_inner_failed != nullptr ? nested_owner_inner_failed->Next : Code{ nullptr };
|
||||
expect(nested_owner_before != nullptr && nested_owner_before->Type == CT_Variable, txt("cpp nested owner keeps preceding member typed"));
|
||||
expect(nested_owner_inner != nullptr && nested_owner_inner->Type == CT_Struct, txt("cpp nested owner retains inner struct AST"));
|
||||
expect(nested_owner_inner_before != nullptr && nested_owner_inner_before->Type == CT_Variable, txt("cpp nested inner recovery keeps preceding member typed"));
|
||||
expect(nested_owner_inner_failed != nullptr && nested_owner_inner_failed->Type == CT_Untyped, txt("cpp nested inner recovery retains failed member"));
|
||||
expect(nested_owner_inner_after != nullptr && nested_owner_inner_after->Type == CT_Variable, txt("cpp nested inner recovery keeps following member typed"));
|
||||
expect(nested_owner_after != nullptr && nested_owner_after->Type == CT_Variable, txt("cpp nested owner keeps following member typed"));
|
||||
|
||||
#if ! GEN_BUILD_DEBUG
|
||||
CodeVar wrong_array_value = def_variable(def_type(name(u32)), name(invalid_array_expr));
|
||||
Opts_def_type wrong_array_options = {};
|
||||
wrong_array_options.array_expr = (Code)wrong_array_value;
|
||||
CodeTypename invalid_array_type = def_type(name(InvalidArrayOption), wrong_array_options);
|
||||
CodeTypename invalid_array_type = def_type(name(InvalidArrayOption), wrong_array_options);
|
||||
expect(((Code)invalid_array_type).ast == Code_Invalid.ast, txt("cpp wrong type invalid code"));
|
||||
|
||||
CodeBody missing_union_body = {};
|
||||
|
||||
@@ -12,14 +12,16 @@ internal void expect(int condition, Str label) {
|
||||
}
|
||||
|
||||
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);
|
||||
Context ctx = {}; init(& ctx);
|
||||
ParseInfo info = {}; Opts_parse options = { & ctx, & info };
|
||||
Code parsed = parse_variable(def, options);
|
||||
expect(info.result.ast == parsed.ast, label);
|
||||
expect(info.lexed.messages != nullptr, label);
|
||||
if (info.lexed.messages != nullptr) expect(str_contains(info.lexed.messages->content, needle), label);
|
||||
if (info.lexed.tokens.num == 0) expect(parsed == Code_Invalid, label);
|
||||
|
||||
CodeVar again = parse_variable(txt("char const* baseline_message = \"GENCPP_BASELINE\";")); expect(again.ast != nullptr, label);
|
||||
CodeVar again = parse_variable(txt("char const* baseline_message = \"GENCPP_BASELINE\";"), options); expect(again.ast != nullptr, label);
|
||||
expect(info.lexed.messages == nullptr && info.messages == nullptr, label);
|
||||
if (again.ast != nullptr) {
|
||||
expect(again->Type == CT_Variable, label);
|
||||
expect(str_contains(again->Name, txt("baseline_message")), label);
|
||||
|
||||
+27
-16
@@ -125,13 +125,13 @@ internal void expect_skip_end(Str label)
|
||||
Token storage[5] = {}; plant(storage, 2, newlines, 2);
|
||||
|
||||
ParseContext parser = {}; bind(& parser, storage, 2, 0);
|
||||
Token* got = lex_current(&parser, lex_skip_formatting);
|
||||
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);
|
||||
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);
|
||||
@@ -139,29 +139,40 @@ internal void expect_skip_end(Str 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);
|
||||
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);
|
||||
internal void expect_boundary(Str def, Str label, Str mark, b32 expect_lexer_message) {
|
||||
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);
|
||||
ParseMessage* parse_message = _ctx->parse_info.messages;
|
||||
LexerMessage* lexer_message = _ctx->parse_info.lexed.messages;
|
||||
if (expect_lexer_message) {
|
||||
expect(lexer_message != nullptr, label);
|
||||
expect(parse_message == nullptr, label);
|
||||
if (lexer_message != nullptr) expect(str_contains(lexer_message->content, mark), label);
|
||||
}
|
||||
else {
|
||||
expect(parse_message != nullptr, label);
|
||||
expect(lexer_message == nullptr, label);
|
||||
}
|
||||
if (expect_lexer_message == false && parse_message != nullptr) {
|
||||
expect(parse_message->Scope == nullptr, label);
|
||||
expect(parse_message->Content.Ptr != nullptr && parse_message->Content.Len > 0, label);
|
||||
expect(str_contains(parse_message->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);
|
||||
deinit(& ctx);
|
||||
}
|
||||
|
||||
internal void expect_eat_end(Str label) {
|
||||
@@ -222,11 +233,11 @@ int main()
|
||||
|
||||
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_boundary(null_def, txt("null-len"), txt("gen::parse_variable: length must greater than 0"), false);
|
||||
expect_boundary(null_ptr, txt("null-ptr"), txt("gen::parse_variable: def was null"), false);
|
||||
expect_boundary(txt(""), txt("zero-len"), txt("gen::parse_variable: length must greater than 0"), false);
|
||||
expect_boundary(txt(" \n\t"), txt("whitespace"), txt("no tokens found"), true);
|
||||
expect_boundary(txt("// note\n"), txt("comment"), txt("only formatting tokens"), false);
|
||||
expect_eat_end (txt("eat"));
|
||||
expect_direct (txt("direct"));
|
||||
|
||||
|
||||
Reference in new issue
Block a user