From 172ae8a3a54544aca0bffc656f15a2edbec798ac Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 21 Mar 2023 23:56:49 +0100 Subject: [PATCH] Only rudimentary parsing of "private" in prefix location. Make contract handling more correct. --- resources/grammar/grammar.y | 53 ++++++++- src/compiler/parse_global.c | 226 +++++++++++------------------------- 2 files changed, 115 insertions(+), 164 deletions(-) diff --git a/resources/grammar/grammar.y b/resources/grammar/grammar.y index 9da68c370..578f0d336 100644 --- a/resources/grammar/grammar.y +++ b/resources/grammar/grammar.y @@ -21,14 +21,14 @@ void yyerror(char *s); %token XOR_ASSIGN OR_ASSIGN VAR NIL ELVIS NEXTCASE %token TYPEDEF MODULE IMPORT DEFINE %token CHAR SHORT INT LONG FLOAT DOUBLE CONST VOID -%token BYTE USHORT UINT ULONG BOOL -%token TYPEID +%token ICHAR USHORT UINT ULONG BOOL INT128 UINT128 FLOAT16 FLOAT128 +%token TYPEID BITSTRUCT STATIC %token STRUCT UNION ENUM ELLIPSIS DOTDOT %token CASE DEFAULT IF ELSE SWITCH WHILE DO FOR GOTO CONTINUE BREAK RETURN %token FN FAULT MACRO GENERIC CT_IF CT_ENDIF CT_ELSE CT_SWITCH CT_CASE CT_DEFAULT CT_FOR CT_FOREACH CT_ENDFOREACH -%token CT_ENDFOR CT_ENDSWITCH BUILTIN IMPLIES -%token TRY CATCH SCOPE PUBLIC DEFER ATTRIBUTE TRY_Q CATCH_Q LVEC RVEC OPTELSE +%token CT_ENDFOR CT_ENDSWITCH BUILTIN IMPLIES INITIALIZE FINALIZE CT_ECHO CT_ASSERT CT_EVALTYPE CT_VATYPE +%token TRY CATCH SCOPE PUBLIC DEFER ATTRIBUTE TRY_Q CATCH_Q LVEC RVEC OPTELSE CT_TYPEFROM CT_TYPEOF %token FN_BLOCK_START FN_BLOCK_END @@ -270,18 +270,26 @@ base_type : VOID | BOOL | CHAR - | BYTE + | ICHAR | SHORT | USHORT | INT | UINT | LONG | ULONG + | INT128 + | UINT128 | FLOAT + | FLOAT16 + | FLOAT128 | DOUBLE | TYPE_IDENT | path TYPE_IDENT | CT_TYPE_IDENT + | CT_TYPEOF '(' expression ')' + | CT_TYPEFROM '(' constant_expression ')' + | CT_VATYPE '(' constant_expression ')' + | CT_EVALTYPE '(' constant_expression ')' ; type @@ -419,6 +427,37 @@ decl_expr_list | decl_expr_list ',' declaration ; +ct_assert_stmt + : CT_ASSERT '(' expression ',' expression ')' ';' + | CT_ASSERT '(' expression ')' ';' + ; + +ct_echo_stmt + : CT_ECHO '(' expression ')' ';' + +bitstruct_declaration + : BITSTRUCT IDENT ':' type opt_attributes bitstruct_body + +bitstruct_body + : '{' '}' + | '{' bitstruct_defs '}' + ; + +bitstruct_defs + : bitstruct_def + | bitstruct_defs bitstruct_def + ; + +bitstruct_def + : type IDENT ':' constant_expression DOTDOT constant_expression ';' + | type IDENT ':' constant_expression ';' + ; + +static_declaration + : STATIC INITIALIZE opt_attributes compound_statement + | STATIC FINALIZE opt_attributes compound_statement + ; + for_statement : FOR '(' decl_expr_list ';' expression_statement ')' statement | FOR '(' decl_expr_list ';' expression_statement expression_list ')' statement @@ -627,6 +666,10 @@ top_level | macro_declaration | typedef_declaration | define_declaration + | static_declaration + | ct_assert_stmt + | ct_echo_stmt + | bitstruct_declaration ; diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index f94d569b7..b8991c925 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -487,20 +487,23 @@ bool parse_path_prefix(ParseContext *c, Path** path_ref) * base_type * : VOID * | BOOL + * | ICHAR * | CHAR - * | BYTE * | SHORT * | USHORT * | INT * | UINT * | LONG * | ULONG + * | INT128 + * | UINT128 * | FLOAT * | DOUBLE + * | FLOAT16 + * | FLOAT128 * | TYPE_IDENT * | ident_scope TYPE_IDENT * | CT_TYPE_IDENT - * | VIRTUAL (ident_scope TYPE_IDENT)? * | CT_TYPEOF '(' expr ')' * | CT_EVALTYPE '(' expr ')' * ; @@ -2179,13 +2182,12 @@ static inline Decl *parse_macro_declaration(ParseContext *c, AstId docs) * | FAULT TYPE_IDENT '{' error_data '}' * ; */ -static inline Decl *parse_fault_declaration(ParseContext *c, bool is_private) +static inline Decl *parse_fault_declaration(ParseContext *c) { advance(c); // advance_and_verify(context, TOKEN_ERRTYPE); Decl *decl = decl_new_with_type(symstr(c), c->span, DECL_FAULT); - if (is_private) decl->visibility = VISIBLE_PRIVATE; if (!consume_type_name(c, "fault")) return poisoned_decl; TypeInfo *type = NULL; @@ -2197,7 +2199,6 @@ static inline Decl *parse_fault_declaration(ParseContext *c, bool is_private) while (!try_consume(c, TOKEN_RBRACE)) { Decl *fault_const = decl_new(DECL_FAULTVALUE, symstr(c), c->span); - if (is_private) decl->visibility = VISIBLE_PRIVATE; if (!consume_const_name(c, "fault value")) { return poisoned_decl; @@ -2282,12 +2283,11 @@ static inline bool parse_enum_spec(ParseContext *c, TypeInfo **type_ref, Decl*** * ; * */ -static inline Decl *parse_enum_declaration(ParseContext *c, bool is_private) +static inline Decl *parse_enum_declaration(ParseContext *c) { advance_and_verify(c, TOKEN_ENUM); Decl *decl = decl_new_with_type(symstr(c), c->span, DECL_ENUM); - if (is_private) decl->visibility = VISIBLE_PRIVATE; if (!consume_type_name(c, "enum")) return poisoned_decl; TypeInfo *type = NULL; @@ -2821,88 +2821,60 @@ END: } /** - * top_level_statement ::= visibility? top_level + * top_level_statement ::= struct_declaration | enum_declaration | fault_declaration | const_declaration + * | global_declaration | macro_declaration | func_definition | typedef_declaration + * | conditional_compilation | define_declaration | import_declaration | module_declaration + * | static_declaration | ct_assert_declaration | ct_echo_declaration | bitstruct_declaration * - * top_level - * : struct_declaration - * | enum_declaration - * | error_declaration - * | const_declaration - * | global_declaration - * | macro_declaration - * | func_definition - * | generics_declaration - * | typedef_declaration - * | conditional_compilation - * | attribute_declaration - * ; - * @param visibility * @return Decl* or a poison value if parsing failed */ Decl *parse_top_level_statement(ParseContext *c, ParseContext **c_ref) { - AstId docs = 0; - if (!parse_contracts(c, &docs)) return poisoned_decl; + AstId contracts = 0; + if (!parse_contracts(c, &contracts)) return poisoned_decl; Decl *decl; + bool is_private = try_consume(c, TOKEN_PRIVATE); // TODO remove + if (is_private) + { + sema_warning_at(c->prev_span, "The use of 'private' is deprecated. Use '@private' instead."); + } + TokenType tok = c->tok; if (tok != TOKEN_MODULE && !c->unit->module) { if (!context_set_module_from_filename(c)) return poisoned_decl; // Pass the docs to the next thing. } - bool is_private = false; -AFTER_VISIBILITY: + switch (tok) { - case TOKEN_PRIVATE: - if (is_private) - { - SEMA_ERROR_HERE("Two 'private' modifiers cannot be used in a row."); - return poisoned_decl; - } - sema_warning_at(c->span, "The use of 'private' is deprecated. Use '@private' instead."); - is_private = true; - advance(c); - tok = c->tok; - goto AFTER_VISIBILITY; case TOKEN_EXTERN: - if (is_private) - { - SEMA_ERROR_HERE("'private' cannot be combined with 'extern'."); - } + // Extern declarations advance(c); tok = c->tok; switch (tok) { case TOKEN_FN: - { - ASSIGN_DECL_OR_RET(decl, parse_func_definition(c, docs, false), poisoned_decl); + decl = parse_func_definition(c, contracts, false); break; - } case TOKEN_CONST: - { - ASSIGN_DECL_OR_RET(decl, parse_top_level_const_declaration(c), poisoned_decl); + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_top_level_const_declaration(c); break; - } case TOKEN_IDENT: case TOKEN_TLOCAL: case TYPELIKE_TOKENS: - { - ASSIGN_DECL_OR_RET(decl, parse_global_declaration(c), poisoned_decl); + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_global_declaration(c); break; - } default: SEMA_ERROR_HERE("Expected 'extern' to be followed by a function, constant or global variable."); return poisoned_decl; } + if (!decl_ok(decl)) return decl; decl->is_extern = true; break; case TOKEN_MODULE: - if (is_private) - { - SEMA_ERROR_HERE("Did not expect 'private' before 'module'."); - return poisoned_decl; - } if (!c_ref) { SEMA_ERROR_HERE("'module' cannot appear inside of conditional compilation."); @@ -2911,167 +2883,101 @@ AFTER_VISIBILITY: advance(c); if (c->unit->module) { + // We might run into another module declaration. If so, create a new unit. ParseContext *new_context = CALLOCS(ParseContext); *new_context = *c; new_context->unit = unit_create(c->unit->file); *c_ref = c = new_context; } - if (!parse_module(c, docs)) return poisoned_decl; + if (!parse_module(c, contracts)) return poisoned_decl; return NULL; case TOKEN_DOCS_START: - if (is_private) - { - SEMA_ERROR_HERE("Did not expect doc comments after 'private'."); - return poisoned_decl; - } SEMA_ERROR_HERE("There are more than one doc comment in a row, that is not allowed."); return poisoned_decl; case TOKEN_TYPEDEF: - { - ASSIGN_DECL_OR_RET(decl, parse_define_type(c), poisoned_decl); - if (is_private) decl->visibility = VISIBLE_PRIVATE; + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_define_type(c); break; - } case TOKEN_DEFINE: - { - ASSIGN_DECL_OR_RET(decl, parse_define(c), poisoned_decl); - if (is_private) decl->visibility = VISIBLE_PRIVATE; + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_define(c); break; - } case TOKEN_FN: - { - ASSIGN_DECL_OR_RET(decl, parse_func_definition(c, docs, false), poisoned_decl); - if (is_private) decl->visibility = VISIBLE_PRIVATE; + decl = parse_func_definition(c, contracts, false); break; - } case TOKEN_STATIC: - { - if (!check_no_visibility_before(c, is_private)) return poisoned_decl; - ASSIGN_DECL_OR_RET(decl, parse_static_top_level(c), poisoned_decl); - if (docs) - { - SEMA_ERROR(astptr(docs), "Unexpected doc comment before 'static', did you mean to use a regular comment?"); - return poisoned_decl; - } + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_static_top_level(c); break; - } case TOKEN_CT_ASSERT: - if (!check_no_visibility_before(c, is_private)) return poisoned_decl; { + if (contracts) goto CONTRACT_NOT_ALLOWED; ASSIGN_AST_OR_RET(Ast *ast, parse_ct_assert_stmt(c), poisoned_decl); decl = decl_new_ct(DECL_CT_ASSERT, ast->span); decl->ct_assert_decl = ast; - if (docs) - { - SEMA_ERROR(astptr(docs), "Unexpected doc comment before $assert, did you mean to use a regular comment?"); - return poisoned_decl; - } return decl; } case TOKEN_CT_ECHO: - if (!check_no_visibility_before(c, is_private)) return poisoned_decl; { + if (contracts) goto CONTRACT_NOT_ALLOWED; ASSIGN_AST_OR_RET(Ast *ast, parse_ct_echo_stmt(c), poisoned_decl); decl = decl_new_ct(DECL_CT_ECHO, ast->span); decl->ct_echo_decl = ast; - if (docs) - { - SEMA_ERROR(astptr(docs), "Unexpected doc comment before $echo, did you mean to use a regular comment?"); - return poisoned_decl; - } - return decl; + break; } case TOKEN_CT_IF: - { - if (!check_no_visibility_before(c, is_private)) return poisoned_decl; - ASSIGN_DECL_OR_RET(decl, parse_ct_if_top_level(c), poisoned_decl); - if (docs) - { - SEMA_ERROR(astptr(docs), "Unexpected doc comment before $if, did you mean to use a regular comment?"); - return poisoned_decl; - } + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_ct_if_top_level(c); break; - } case TOKEN_IMPORT: - if (!check_no_visibility_before(c, is_private)) return poisoned_decl; + if (contracts) goto CONTRACT_NOT_ALLOWED; if (!c_ref) { SEMA_ERROR_HERE("'import' may not appear inside a compile time statement."); return poisoned_decl; } if (!parse_import(c)) return poisoned_decl; - if (docs) - { - SEMA_ERROR(astptr(docs), "Unexpected doc comment before import, did you mean to use a regular comment?"); - return poisoned_decl; - } return NULL; case TOKEN_CT_SWITCH: - { - if (!check_no_visibility_before(c, is_private)) return poisoned_decl; - ASSIGN_DECL_OR_RET(decl, parse_ct_switch_top_level(c), poisoned_decl); - if (docs) - { - SEMA_ERROR(astptr(docs), "Unexpected doc comment before $switch, did you mean to use a regular comment?"); - return poisoned_decl; - } + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_ct_switch_top_level(c); break; - } case TOKEN_CT_INCLUDE: - if (!check_no_visibility_before(c, is_private)) return poisoned_decl; - if (docs) - { - SEMA_ERROR(astptr(docs), "Unexpected doc comment before $include, did you mean to use a regular comment?"); - return poisoned_decl; - } - ASSIGN_DECL_OR_RET(decl, parse_include(c), poisoned_decl); + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_include(c); break; case TOKEN_BITSTRUCT: - { - ASSIGN_DECL_OR_RET(decl, parse_bitstruct_declaration(c), poisoned_decl); - if (is_private) decl->visibility = VISIBLE_PRIVATE; + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_bitstruct_declaration(c); break; - } case TOKEN_CONST: - { - ASSIGN_DECL_OR_RET(decl, parse_top_level_const_declaration(c), poisoned_decl); - if (is_private) decl->visibility = VISIBLE_PRIVATE; + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_top_level_const_declaration(c); break; - } case TOKEN_STRUCT: case TOKEN_UNION: - { - ASSIGN_DECL_OR_RET(decl, parse_struct_declaration(c), poisoned_decl); - if (is_private) decl->visibility = VISIBLE_PRIVATE; + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_struct_declaration(c); break; - } case TOKEN_MACRO: - { - ASSIGN_DECL_OR_RET(decl, parse_macro_declaration(c, docs), poisoned_decl); - if (is_private) decl->visibility = VISIBLE_PRIVATE; + decl = parse_macro_declaration(c, contracts); break; - } case TOKEN_ENUM: - { - ASSIGN_DECL_OR_RET(decl, parse_enum_declaration(c, is_private), poisoned_decl); + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_enum_declaration(c); break; - } case TOKEN_FAULT: - { - ASSIGN_DECL_OR_RET(decl, parse_fault_declaration(c, is_private), poisoned_decl); + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_fault_declaration(c); break; - } case TOKEN_IDENT: - { - ASSIGN_DECL_OR_RET(decl, parse_global_declaration(c), poisoned_decl); - if (is_private) decl->visibility = VISIBLE_PRIVATE; + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_global_declaration(c); break; - } case TOKEN_EOF: - SEMA_ERROR_LAST("Expected a top level declaration"); + SEMA_ERROR_LAST("Expected a top level declaration."); return poisoned_decl; case TOKEN_CT_CONST_IDENT: - { if (peek(c) == TOKEN_EQ) { SEMA_ERROR_HERE("Did you forget a 'const' before the name of this compile time constant?"); @@ -3080,15 +2986,12 @@ AFTER_VISIBILITY: { SEMA_ERROR_HERE("Compile time constant unexpectedly found."); } - } return poisoned_decl; case TOKEN_TLOCAL: case TYPELIKE_TOKENS: - { - ASSIGN_DECL_OR_RET(decl, parse_global_declaration(c), poisoned_decl); - if (is_private) decl->visibility = VISIBLE_PRIVATE; + if (contracts) goto CONTRACT_NOT_ALLOWED; + decl = parse_global_declaration(c); break; - } case TOKEN_EOS: SEMA_ERROR_HERE("';' wasn't expected here, try removing it."); return poisoned_decl; @@ -3096,8 +2999,13 @@ AFTER_VISIBILITY: SEMA_ERROR_HERE("Expected the start of a global declaration here."); return poisoned_decl; } + if (!decl_ok(decl)) return decl; + if (is_private) decl->visibility = VISIBLE_PRIVATE; // TODO remove assert(decl); return decl; +CONTRACT_NOT_ALLOWED: + SEMA_ERROR(astptr(contracts), "Contracts are only used for modules, functions and macros."); + return poisoned_decl; } void consume_deprecated_symbol(ParseContext *c, TokenType type)