diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index f8955487e..c39bdec1e 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -77,7 +77,7 @@ typedef struct struct { char* chars; - int len; + uint32_t len; } string; Decl *enum_constant; struct diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 7d27f5caa..4dda9a49a 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -32,7 +32,6 @@ typedef struct extern ParseRule rules[TOKEN_EOF + 1]; -static Expr *parse_identifier_with_path(Context *context, Path *path); inline Expr *parse_precedence_with_left_side(Context *context, Expr *left_side, Precedence precedence) { @@ -134,7 +133,7 @@ static bool parse_param_path(Context *context, DesignatorElement ***path) * * parameter ::= (param_path '=')? expr */ -bool parse_param_list(Context *context, Expr ***result, TokenType param_end, bool *unsplat) +bool parse_arg_list(Context *context, Expr ***result, TokenType param_end, bool *unsplat) { *result = NULL; if (unsplat) *unsplat = false; @@ -179,6 +178,9 @@ bool parse_param_list(Context *context, Expr ***result, TokenType param_end, boo } } +/** + * macro_expansion ::= '@' non_at_expression + */ static Expr *parse_macro_expansion(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); @@ -371,7 +373,7 @@ Expr *parse_initializer_list(Context *context) CONSUME_OR(TOKEN_LBRACE, poisoned_expr); if (!try_consume(context, TOKEN_RBRACE)) { - if (!parse_param_list(context, &initializer_list->initializer_expr.initializer_expr, TOKEN_RBRACE, NULL)) return poisoned_expr; + if (!parse_arg_list(context, &initializer_list->initializer_expr.initializer_expr, TOKEN_RBRACE, NULL)) return poisoned_expr; CONSUME_OR(TOKEN_RBRACE, poisoned_expr); } RANGE_EXTEND_PREV(initializer_list); @@ -448,11 +450,11 @@ static Expr *parse_call_expr(Context *context, Expr *left) Decl **body_args = NULL; if (!TOKEN_IS(TOKEN_RPAREN)) { - if (!parse_param_list(context, ¶ms, TOKEN_RPAREN, &unsplat)) return poisoned_expr; + if (!parse_arg_list(context, ¶ms, TOKEN_RPAREN, &unsplat)) return poisoned_expr; } - if (try_consume(context, TOKEN_EOS) && left->expr_kind == EXPR_MACRO_EXPANSION) + if (try_consume(context, TOKEN_EOS) && parse_next_is_type(context)) { - if (!parse_macro_argument_declarations(context, VISIBLE_LOCAL, &body_args, false)) return poisoned_expr; + if (!parse_parameters(context, VISIBLE_LOCAL, &body_args)) return poisoned_expr; } if (!TOKEN_IS(TOKEN_RPAREN)) { @@ -472,7 +474,7 @@ static Expr *parse_call_expr(Context *context, Expr *left) SEMA_TOKEN_ERROR(context->tok, "Expected a macro body here."); return poisoned_expr; } - if (TOKEN_IS(TOKEN_LBRACE) && left->expr_kind == EXPR_MACRO_EXPANSION) + if (TOKEN_IS(TOKEN_LBRACE)) { call->call_expr.body = TRY_AST_OR(parse_compound_stmt(context), poisoned_expr); } @@ -551,14 +553,6 @@ static Expr *parse_access_expr(Context *context, Expr *left) } -static Expr *parse_identifier_with_path(Context *context, Path *path) -{ - Expr *expr = EXPR_NEW_TOKEN(context->tok.type == TOKEN_CONST_IDENT ? EXPR_CONST_IDENTIFIER : EXPR_IDENTIFIER , context->tok); - expr->identifier_expr.identifier = context->tok.id; - expr->identifier_expr.path = path; - advance(context); - return expr; -} static Expr *parse_ct_ident(Context *context, Expr *left) { @@ -586,11 +580,14 @@ static Expr *parse_hash_ident(Context *context, Expr *left) static Expr *parse_identifier(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); - return parse_identifier_with_path(context, NULL); + Expr *expr = EXPR_NEW_TOKEN(context->tok.type == TOKEN_CONST_IDENT ? EXPR_CONST_IDENTIFIER : EXPR_IDENTIFIER , context->tok); + expr->identifier_expr.identifier = context->tok.id; + advance(context); + return expr; } -static Expr *parse_maybe_scope(Context *context, Expr *left) +static Expr *parse_identifier_starting_expression(Context *context, Expr *left) { assert(!left && "Unexpected left hand side"); bool had_error; @@ -600,7 +597,11 @@ static Expr *parse_maybe_scope(Context *context, Expr *left) { case TOKEN_IDENT: case TOKEN_CONST_IDENT: - return parse_identifier_with_path(context, path); + { + Expr *expr = parse_identifier(context, NULL); + expr->identifier_expr.path = path; + return expr; + } case TOKEN_TYPE_IDENT: return parse_type_expression_with_path(context, path); default: @@ -922,11 +923,15 @@ static Expr *parse_string_literal(Context *context, Expr *left) } advance_and_verify(context, TOKEN_STRING); } - + if (len > UINT32_MAX) + { + SEMA_TOKEN_ERROR(context->tok, "String exceeded max size."); + return poisoned_expr; + } assert(str); str[len] = '\0'; expr_string->const_expr.string.chars = str; - expr_string->const_expr.string.len = len; + expr_string->const_expr.string.len = (uint32_t)len; expr_set_type(expr_string, type_compstr); expr_string->const_expr.kind = TYPE_STRLIT; return expr_string; @@ -1001,7 +1006,7 @@ Expr *parse_type_expression_with_path(Context *context, Path *path) /** * function_block - * : '({' stmt_list '})' + * : '{|' stmt_list '|}' */ static Expr* parse_expr_block(Context *context, Expr *left) { @@ -1098,7 +1103,7 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_SHR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_SHL_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, - [TOKEN_IDENT] = { parse_maybe_scope, NULL, PREC_NONE }, + [TOKEN_IDENT] = { parse_identifier_starting_expression, NULL, PREC_NONE }, [TOKEN_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_CT_IDENT] = { parse_ct_ident, NULL, PREC_NONE }, [TOKEN_CONST_IDENT] = { parse_identifier, NULL, PREC_NONE }, diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index cb2a812c5..e6d887d77 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -51,7 +51,7 @@ static bool context_next_is_type_and_not_ident(Context *context) if (context->next_tok.type != TOKEN_COLON) return false; return context_next_is_type_with_path_prefix(context); } - return true; + return token_is_any_type(context->tok.type); } @@ -986,25 +986,11 @@ bool parse_next_is_type(Context *context) TokenType next_tok = context->next_tok.type; switch (context->tok.type) { - case TOKEN_VOID: - case TOKEN_CHAR: - case TOKEN_BOOL: - case TOKEN_ICHAR: - case TOKEN_DOUBLE: - case TOKEN_FLOAT: - case TOKEN_INT: - case TOKEN_ISIZE: - case TOKEN_LONG: - case TOKEN_SHORT: - case TOKEN_UINT: - case TOKEN_ULONG: - case TOKEN_USHORT: - case TOKEN_USIZE: - case TOKEN_QUAD: + case TYPE_TOKENS: + case TOKEN_VIRTUAL: case TOKEN_TYPE_IDENT: case TOKEN_CT_TYPE_IDENT: case TOKEN_ERR: - case TOKEN_TYPEID: return true; case TOKEN_IDENT: if (next_tok != TOKEN_SCOPE) return false; @@ -1020,25 +1006,11 @@ bool parse_next_is_case_type(Context *context) TokenType next_tok = context->next_tok.type; switch (context->tok.type) { - case TOKEN_VOID: - case TOKEN_BOOL: - case TOKEN_CHAR: - case TOKEN_DOUBLE: - case TOKEN_FLOAT: - case TOKEN_ICHAR: - case TOKEN_INT: - case TOKEN_ISIZE: - case TOKEN_LONG: - case TOKEN_SHORT: - case TOKEN_UINT: - case TOKEN_ULONG: - case TOKEN_USHORT: - case TOKEN_USIZE: - case TOKEN_QUAD: + case TYPE_TOKENS: + case TOKEN_VIRTUAL: case TOKEN_TYPE_IDENT: case TOKEN_CT_TYPE_IDENT: case TOKEN_ERR: - case TOKEN_TYPEID: return (next_tok == TOKEN_STAR) | (next_tok == TOKEN_LBRACKET) | (next_tok == TOKEN_COMMA) | (next_tok == TOKEN_COLON) | (next_tok == TOKEN_EOS); case TOKEN_IDENT: if (next_tok != TOKEN_SCOPE) return false; @@ -1152,48 +1124,142 @@ static inline bool parse_param_decl(Context *context, Visibility parent_visibili } /** - * - * parameter_type_list - * : parameter_list - * | parameter_list ',' ELLIPSIS - * | parameter_list ',' type_expression ELLIPSIS - * ; - * - * opt_parameter_type_list - * : '(' ')' - * | '(' parameter_type_list ')' - * ; - * - * parameter_list - * : param_declaration - * | parameter_list ',' param_declaration - * ; - * + * parameters ::= (parameter (',' parameter)*)? + * non_type_ident = IDENT | HASH_IDENT | CT_IDENT + * parameter ::= type ELLIPSIS? (non_type_ident ('=' expr))? + * | ELLIPSIS (CT_TYPE_IDENT | non_type_ident ('=' expr)?)? */ -static inline bool parse_opt_parameter_type_list(Context *context, Visibility parent_visibility, FunctionSignature *signature, bool is_interface) +bool parse_parameters(Context *context, Visibility visibility, Decl ***params_ref) { - CONSUME_OR(TOKEN_LPAREN, false); - while (!try_consume(context, TOKEN_RPAREN)) + Decl** params = NULL; + bool var_arg_found = false; + + while (!TOKEN_IS(TOKEN_EOS) && !TOKEN_IS(TOKEN_RPAREN)) { - if (signature->variadic || signature->typed_variadic) + TypeInfo *type = NULL; + + bool ellipsis = try_consume(context, TOKEN_ELLIPSIS); + + // Special case, we might see foo($Type). + // there is an ambiguity here, since ($Type) and ($Type x) is potentially possible + // to evaluate. However, at the top level we never have global compile time values. + // so consequently we need fix this and ignore CT_TYPE_IDENT + if (!ellipsis && context_next_is_type_and_not_ident(context) && context->tok.type != TOKEN_CT_TYPE_IDENT ) { - SEMA_TOKEN_ERROR(context->tok, "Variadic arguments should be the last in a parameter list."); + type = TRY_TYPE_OR(parse_type(context), false); + ellipsis = try_consume(context, TOKEN_ELLIPSIS); + } + + if (ellipsis && var_arg_found) + { + SEMA_TOKID_ERROR(context->prev_tok, "Only a single vararg parameter is allowed."); return false; } - if (try_consume(context, TOKEN_ELLIPSIS)) + + VarDeclKind param_kind; + TokenId token = context->tok.id; + bool no_name = false; + + switch (context->tok.type) { - signature->variadic = true; + case TOKEN_IDENT: + // normal foo + param_kind = VARDECL_PARAM; + break; + case TOKEN_CT_IDENT: + // ct_var $foo + param_kind = VARDECL_PARAM_CT; + break; + case TOKEN_AMP: + // reference &foo + advance(context); + token = context->tok.id; + if (!TOKEN_IS(TOKEN_IDENT)) + { + SEMA_TOKEN_ERROR(context->tok, "Only normal variables may be passed by reference."); + return false; + } + param_kind = VARDECL_PARAM_REF; + break; + case TOKEN_HASH_TYPE_IDENT: + // #Foo (not allowed) + SEMA_TOKEN_ERROR(context->tok, "An unevaluated expression can never be a type, did you mean to use $Type?"); + return false; + case TOKEN_HASH_IDENT: + // expression #foo + param_kind = VARDECL_PARAM_EXPR; + break; + // Compile time type $Type + case TOKEN_CT_TYPE_IDENT: + param_kind = VARDECL_PARAM_CT_TYPE; + break; + case TOKEN_COMMA: + case TOKEN_EOS: + case TOKEN_RPAREN: + if (!type && !ellipsis) + { + SEMA_TOKEN_ERROR(context->tok, "Expected a parameter."); + return false; + } + no_name = true; + token = context->prev_tok; + param_kind = VARDECL_PARAM; + break; + default: + SEMA_TOKEN_ERROR(context->tok, "Expected a parameter."); + return false; + } + Decl *param = decl_new_var(token, type, param_kind, visibility); + param->var.type_info = type; + if (no_name) + { + param->name = NULL; } else { - if (!parse_param_decl(context, parent_visibility, &(signature->params), false)) return false; - signature->typed_variadic = VECLAST(signature->params)->var.vararg; + advance(context); + if (try_consume(context, TOKEN_EQ)) + { + param->var.init_expr = TRY_EXPR_OR(parse_initializer(context), false); + } } - if (!try_consume(context, TOKEN_COMMA)) + var_arg_found |= ellipsis; + param->var.vararg = ellipsis; + vec_add(params, param); + if (!try_consume(context, TOKEN_COMMA)) break; + } + *params_ref = params; + return true; +} + + +/** + * + * parameter_type_list ::= '(' parameters ')' + */ +static inline bool parse_parameter_list(Context *context, Visibility parent_visibility, FunctionSignature *signature, bool is_interface) +{ + CONSUME_OR(TOKEN_LPAREN, false); + Decl **decls; + if (!parse_parameters(context, parent_visibility, &decls)) return false; + if (vec_size(decls)) + { + Decl *last = VECLAST(decls); + if (last->var.vararg) { - EXPECT_OR(TOKEN_RPAREN, false); + if (!last->var.type_info) + { + vec_resize(decls, vec_size(decls) - 1); + signature->variadic = true; + } + else + { + signature->typed_variadic = true; + } } } + signature->params = decls; + CONSUME_OR(TOKEN_RPAREN, false); return true; } @@ -1346,106 +1412,35 @@ static inline Decl *parse_top_level_const_declaration(Context *context, Visibili return decl; } + /** - * Parse statements up to the next '}', 'case' or 'default' + * macro_arguments ::= '(' parameters (EOS trailing_block_parameter )? ')' + * + * trailing_block_parameter ::= '@' IDENT ( '(' parameters ')' )? */ -static inline Ast *parse_generics_statements(Context *context) -{ - Ast *ast = AST_NEW_TOKEN(AST_COMPOUND_STMT, context->tok); - while (!TOKEN_IS(TOKEN_RBRACE) && !TOKEN_IS(TOKEN_CASE) && !TOKEN_IS(TOKEN_DEFAULT)) - { - Ast *stmt = TRY_AST_OR(parse_stmt(context), poisoned_ast); - ast->compound_stmt.stmts = VECADD(ast->compound_stmt.stmts, stmt); - } - return ast; -} - -bool parse_macro_argument_declarations(Context *context, Visibility visibility, Decl ***params_ref, bool allow_vararg) -{ - bool vararg = false; - while (!TOKEN_IS(TOKEN_EOS) && !TOKEN_IS(TOKEN_RPAREN)) - { - if (*params_ref) - { - TRY_CONSUME(TOKEN_COMMA, false); - } - TypeInfo *parm_type = NULL; - VarDeclKind param_kind; - TEST_TYPE: - switch (context->tok.type) - { - // normal foo - case TOKEN_IDENT: - param_kind = VARDECL_PARAM; - break; - // ct_var $foo - case TOKEN_CT_IDENT: - param_kind = VARDECL_PARAM_CT; - break; - // reference &foo - case TOKEN_AMP: - advance(context); - if (!TOKEN_IS(TOKEN_IDENT)) - { - SEMA_TOKEN_ERROR(context->tok, "Only normal variables may be passed by reference."); - return false; - } - param_kind = VARDECL_PARAM_REF; - break; - // #Foo (not allowed) - case TOKEN_HASH_TYPE_IDENT: - SEMA_TOKEN_ERROR(context->tok, "An unevaluated expression can never be a type, did you mean to use $Type?"); - return false; - // expression #foo - case TOKEN_HASH_IDENT: - // Note that the HASH_TYPE_IDENT will be an error later on. - param_kind = VARDECL_PARAM_EXPR; - break; - // Compile time type $Type - case TOKEN_CT_TYPE_IDENT: - param_kind = VARDECL_PARAM_CT_TYPE; - break; - default: - if (parm_type || vararg) - { - SEMA_TOKEN_ERROR(context->tok, "Expected a macro parameter"); - return false; - } - // We either have "... var" or "int... var" - if (allow_vararg && try_consume(context, TOKEN_ELLIPSIS)) - { - vararg = true; - } - else - { - parm_type = TRY_TYPE_OR(parse_type(context), false); - if (allow_vararg && try_consume(context, TOKEN_ELLIPSIS)) - { - vararg = true; - } - } - goto TEST_TYPE; - } - Decl *param = decl_new_var(context->tok.id, parm_type, param_kind, visibility); - param->var.vararg = vararg; - advance(context); - vec_add(*params_ref, param); - TokenType current_token = context->tok.type; - } - return true; - -} static bool parse_macro_arguments(Context *context, Visibility visibility, Decl ***params_ref, Decl ***body_params, bool *has_trailing_body) { CONSUME_OR(TOKEN_LPAREN, false); *params_ref = NULL; *body_params = NULL; *has_trailing_body = false; - if (!parse_macro_argument_declarations(context, visibility, params_ref, true)) return false; + // Parse the regular parameters. + if (!parse_parameters(context, visibility, params_ref)) return false; + + // Do we have trailing block parameters? if (try_consume(context, TOKEN_EOS)) { + // Consume '@' IDENT *has_trailing_body = true; - if (!parse_macro_argument_declarations(context, visibility, body_params, true)) return false; + TRY_CONSUME_OR(TOKEN_AT, "Expected a trailing block with the format '@block(...).", false); + if (!consume_ident(context, "variable name")) return false; + TokenId name = context->prev_tok; + if (try_consume(context, TOKEN_LPAREN)) + { + if (!parse_parameters(context, visibility, body_params)) return false; + CONSUME_OR(TOKEN_RPAREN, false); + } + // TODO use the body param. } TRY_CONSUME(TOKEN_RPAREN, false); return true; @@ -1555,7 +1550,7 @@ static inline Decl *parse_define_type(Context *context, Visibility visibility) { decl->typedef_decl.function_signature.failable = true; } - if (!parse_opt_parameter_type_list(context, decl->visibility, &(decl->typedef_decl.function_signature), true)) + if (!parse_parameter_list(context, decl->visibility, &(decl->typedef_decl.function_signature), true)) { return poisoned_decl; } @@ -1692,108 +1687,86 @@ static AttributeDomain TOKEN_TO_ATTR[TOKEN_EOF + 1] = { [TOKEN_ERR] = ATTR_ERROR, }; + /** - * attribute_declaration - * : ATTRIBUTE attribute_domains IDENT ';' - * | ATTRIBUTE attribute_domains IDENT '(' parameter_type_list ')' ';' - * ; - * - * attribute_domains - * : attribute_domain - * | attribute_domains ',' attribute_domain - * ; - * - * attribute_domain - * : FUNC - * | VAR - * | ENUM - * | STRUCT - * | UNION - * | TYPEDEF - * | CONST - * | ERROR - * ; - * - * @param visibility - * @return Decl* + * func_header ::= type '!'? (type '.')? IDENT + * macro_header ::= (type '!'?)? (type '.')? IDENT */ -static inline Decl *parse_attribute_declaration(Context *context, Visibility visibility) +static inline bool parse_func_macro_header(Context *context, bool rtype_is_optional, TypeInfo **rtype_ref, bool *failable_ref, TypeInfo **method_type_ref, TokenId *name_ref) { - advance_and_verify(context, TOKEN_ATTRIBUTE); - AttributeDomain domains = 0; - AttributeDomain last_domain; - last_domain = TOKEN_TO_ATTR[context->tok.type]; - while (last_domain) + TypeInfo *rtype = NULL; + TypeInfo *method_type = NULL; + bool failable = false; + + // 1. We have a macro with just a name, if so, then we set the name and we're done. + if (rtype_is_optional && !context_next_is_type_and_not_ident(context)) { - advance(context); - if ((domains & last_domain) != 0) + goto RESULT; + } + + // 2. Now we must have a type - either that is the return type or the method type. + rtype = TRY_TYPE_OR(parse_type(context), false); + + // 3. We possibly have a failable return at this point. + failable = try_consume(context, TOKEN_BANG); + + // 4. We might have a type here, if so then we read it. + if (!TOKEN_IS(TOKEN_DOT) && context_next_is_type_and_not_ident(context)) + { + method_type = TRY_TYPE_OR(parse_type(context), false); + } + + // 5. If we have a dot here, then we need to interpret this as method function. + if (try_consume(context, TOKEN_DOT)) + { + // 5a. What if we don't have a method type? + if (!method_type) { - SEMA_TOKEN_ERROR(context->tok, "'%s' appeared more than once.", TOKSTR(context->tok.id)); - continue; + // 5b. If the rtype is not optional or the return type was a failable, then this is an error. + if (!rtype_is_optional || failable) + { + SEMA_TOKID_ERROR(context->prev_tok, + "This looks like you are declaring a method without a return type?"); + return false; + } + method_type = rtype; + rtype = NULL; } - domains |= last_domain; - if (!try_consume(context, TOKEN_COMMA)) break; - last_domain = TOKEN_TO_ATTR[context->tok.type]; } - Decl *decl = decl_new(DECL_ATTRIBUTE, context->tok.id, visibility); - TRY_CONSUME_OR(TOKEN_IDENT, "Expected an attribute name.", poisoned_decl); - if (last_domain == 0) + else if (method_type) { - SEMA_TOKEN_ERROR(context->tok, "Expected at least one domain for attribute '%s'.", decl->name); - return poisoned_decl; + // 5d. A method type but no dot is also wrong. + SEMA_ERROR(method_type, "There is unexpectedly a type after the return type, did you forget a '.'?"); + return false; } - if (!parse_opt_parameter_type_list(context, visibility, &decl->attr.attr_signature, false)) return poisoned_decl; - TRY_CONSUME_EOS_OR(poisoned_decl); - return decl; + RESULT: + TRY_CONSUME_OR(TOKEN_IDENT, "Expected a name here.", false); + *failable_ref = failable; + *name_ref = context->prev_tok; + *rtype_ref = rtype; + *method_type_ref = method_type; + return true; } - /** - * macro ::= MACRO (type '!'?)? identifier '!'? '(' macro_params ')' compound_statement + * macro ::= macro_header '(' macro_params ')' compound_statement */ static inline Decl *parse_macro_declaration(Context *context, Visibility visibility) { advance_and_verify(context, TOKEN_MACRO); - TypeInfo *rtype = NULL; - bool failable = false; - - // 1. Return type? - if (context_next_is_type_and_not_ident(context)) - { - rtype = TRY_TYPE_OR(parse_type(context), poisoned_decl); - failable = try_consume(context, TOKEN_BANG); - } Decl *decl = decl_new(DECL_MACRO, context->tok.id, visibility); - decl->macro_decl.rtype = rtype; - decl->macro_decl.failable = failable; - if (rtype) - { - if (TOKEN_IS(TOKEN_DOT)) - { - if (failable) - { - SEMA_ERROR(rtype, "Expected a macro name here."); - return poisoned_decl; - } - advance(context); - decl->macro_decl.type_parent = rtype; - decl->macro_decl.rtype = NULL; - } - else - { - if (parse_next_is_type(context)) - { - decl->macro_decl.type_parent = TRY_TYPE_OR(parse_type(context), poisoned_decl); - TRY_CONSUME_OR(TOKEN_DOT, "Did you forget a ',' here?", poisoned_decl); - } - } - } + TypeInfo **rtype_ref = &decl->macro_decl.rtype; + TypeInfo **method_type_ref = &decl->macro_decl.type_parent; + bool failable; + TokenId name; + if (!parse_func_macro_header(context, true, rtype_ref, &failable, method_type_ref, &name)) return poisoned_decl; + + decl->macro_decl.failable = failable; + decl->name = TOKSTR(name); + decl->name_token = name; - decl->name_token = context->tok.id; - decl->name = TOKSTR(context->tok); - TRY_CONSUME_OR(TOKEN_IDENT, "Expected a macro name here.", poisoned_decl); bool trailing_body = false; if (!parse_macro_arguments(context, visibility, &decl->macro_decl.parameters, &decl->macro_decl.body_parameters, &trailing_body)) return poisoned_decl; decl->has_body_param = trailing_body; @@ -1924,7 +1897,7 @@ static inline Decl *parse_enum_declaration(Context *context, Visibility visibili if (try_consume(context, TOKEN_LPAREN)) { Expr **result = NULL; - if (!parse_param_list(context, &result, TOKEN_RPAREN, NULL)) return poisoned_decl; + if (!parse_arg_list(context, &result, TOKEN_RPAREN, NULL)) return poisoned_decl; enum_const->enum_constant.args = result; CONSUME_OR(TOKEN_RPAREN, poisoned_decl); } @@ -1962,7 +1935,6 @@ static inline Decl *parse_enum_declaration(Context *context, Visibility visibili * * func_declaration * : FUNC failable_type func_name '(' opt_parameter_type_list ')' opt_attributes - * | FUNC failable_type func_name '(' opt_parameter_type_list ')' throw_declaration opt_attributes * ; * * @param visibility @@ -1972,32 +1944,16 @@ static inline Decl *parse_func_definition(Context *context, Visibility visibilit { Decl *func = decl_new(DECL_FUNC, context->next_tok.id, visibility); advance_and_verify(context, TOKEN_FUNC); - func->func_decl.function_signature.rtype = TRY_TYPE_OR(parse_type(context), poisoned_decl); - func->func_decl.function_signature.failable = try_consume(context, TOKEN_BANG); - SourceSpan start = source_span_from_token_id(context->tok.id); - bool had_error = false; - Path *path = parse_path_prefix(context, &had_error); - if (had_error) return poisoned_decl; - if (path || TOKEN_IS(TOKEN_TYPE_IDENT)) - { - // Special case, actually an extension - TRY_EXPECT_OR(TOKEN_TYPE_IDENT, "A type was expected after '::'.", poisoned_decl); - // TODO span is incorrect - TypeInfo *type = type_info_new(TYPE_INFO_IDENTIFIER, start); - type->unresolved.path = path; - type->unresolved.name_loc = context->tok.id; - func->func_decl.type_parent = type; - advance_and_verify(context, TOKEN_TYPE_IDENT); - - TRY_CONSUME_OR(TOKEN_DOT, "Expected '.' after the type in a method declaration.", poisoned_decl); - } - - EXPECT_IDENT_FOR_OR("function name", poisoned_decl); - func->name = TOKSTR(context->tok); - func->name_token = context->tok.id; - advance_and_verify(context, TOKEN_IDENT); + TypeInfo **rtype_ref = &func->func_decl.function_signature.rtype; + TypeInfo **method_type_ref = &func->func_decl.type_parent; + bool failable; + TokenId name; + if (!parse_func_macro_header(context, false, rtype_ref, &failable, method_type_ref, &name)) return poisoned_decl; + func->func_decl.function_signature.failable = failable; + func->name = TOKSTR(name); + func->name_token = name; RANGE_EXTEND_PREV(func); - if (!parse_opt_parameter_type_list(context, visibility, &(func->func_decl.function_signature), is_interface)) return poisoned_decl; + if (!parse_parameter_list(context, visibility, &(func->func_decl.function_signature), is_interface)) return poisoned_decl; if (!parse_attributes(context, func)) return poisoned_decl; @@ -2272,6 +2228,7 @@ Decl *parse_top_level_statement(Context *context) } Decl *decl; + TokenType type = context->tok.type; switch (context->tok.type) { case TOKEN_DOCS_START: @@ -2286,7 +2243,7 @@ Decl *parse_top_level_statement(Context *context) decl = TRY_DECL_OR(parse_define(context, visibility), poisoned_decl); break; case TOKEN_ATTRIBUTE: - decl = TRY_DECL_OR(parse_attribute_declaration(context, visibility), poisoned_decl); + TODO break; case TOKEN_FUNC: decl = TRY_DECL_OR(parse_func_definition(context, visibility, false), poisoned_decl); @@ -2388,6 +2345,7 @@ Decl *parse_top_level_statement(Context *context) decl = TRY_DECL_OR(parse_global_declaration(context, visibility), poisoned_decl); break; } + assert(decl); decl->docs = docs; return decl; } \ No newline at end of file diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index 329a2d39a..fa14583e6 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -52,8 +52,9 @@ bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, Toke bool allow_multiple_values); Expr *parse_expression_list(Context *context); Decl *parse_decl_after_type(Context *context, TypeInfo *type); -bool parse_macro_argument_declarations(Context *context, Visibility visibility, Decl ***params_ref, bool allow_vararg); -bool parse_param_list(Context *context, Expr ***result, TokenType param_end, bool *unsplat); + +bool parse_parameters(Context *context, Visibility visibility, Decl ***params_ref); +bool parse_arg_list(Context *context, Expr ***result, TokenType param_end, bool *unsplat); Expr *parse_type_compound_literal_expr_after_type(Context *context, TypeInfo *type_info); Expr *parse_type_access_expr_after_type(Context *context, TypeInfo *type_info); bool parse_next_is_decl(Context *context); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 2331ee8c5..cbf6a2705 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -357,7 +357,17 @@ static inline bool sema_analyse_function_param(Context *context, Decl *param, bo { *has_default = false; assert(param->decl_kind == DECL_VAR); - assert(param->var.kind == VARDECL_PARAM); + // We need to check that the parameters are not typeless nor are of any macro parameter kind. + if (param->var.kind != VARDECL_PARAM) + { + SEMA_ERROR(param, "Only regular parameters are allowed for functions."); + return false; + } + if (!param->var.type_info) + { + SEMA_ERROR(param, "Only typed parameters are allowed for functions."); + return false; + } if (!sema_resolve_type_info(context, param->var.type_info)) { return false; @@ -387,7 +397,7 @@ static inline bool sema_analyse_function_param(Context *context, Decl *param, bo return true; } -static inline Type *sema_analyse_function_signature(Context *context, FunctionSignature *signature, bool is_function) +static inline Type *sema_analyse_function_signature(Context *context, FunctionSignature *signature, bool is_real_function) { bool all_ok = true; all_ok = sema_resolve_type_info(context, signature->rtype) && all_ok; @@ -405,7 +415,7 @@ static inline Type *sema_analyse_function_signature(Context *context, FunctionSi assert(param->resolve_status == RESOLVE_NOT_DONE); param->resolve_status = RESOLVE_RUNNING; bool has_default; - if (!sema_analyse_function_param(context, param, is_function, &has_default)) + if (!sema_analyse_function_param(context, param, is_real_function, &has_default)) { decl_poison(param); all_ok = false; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 3acf65c01..aa8d5671c 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -757,7 +757,19 @@ static inline bool expr_may_unpack_as_vararg(Expr *expr, Type *variadic_base_typ } static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionSignature *signature, Expr *expr, Decl *decl, Type *to, Expr *struct_var) { - // 1. Builtin? We handle that elsewhere. + // 1. Check that we don't have any macro style arguments. + if (vec_size(expr->call_expr.body_arguments)) + { + SEMA_ERROR(expr, "Only macro calls may have arguments for a trailing block after ';'."); + return false; + } + if (vec_size(expr->call_expr.body_arguments)) + { + SEMA_ERROR(expr, "Only macro calls may take a trailing block."); + return false; + } + + // 2. Builtin? We handle that elsewhere. if (decl->func_decl.is_builtin) { assert(!struct_var); @@ -766,15 +778,15 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS Decl **func_params = signature->params; Expr **args = expr->call_expr.arguments; - // 2. If this is a type call, then we have an implicit first argument. + // 3. If this is a type call, then we have an implicit first argument. unsigned struct_args = struct_var == NULL ? 0 : 1; - // 3. There might be unpacking for typed varargs. + // 4. There might be unpacking for typed varargs. unsigned num_args = vec_size(args) + struct_args; bool unsplat = expr->call_expr.unsplat_last; if (unsplat) { - // 4. Is this *not* a naked vararg or typed vararg? That's an error. + // 5. Is this *not* a naked vararg or typed vararg? That's an error. if (!signature->typed_variadic && !signature->variadic) { SEMA_ERROR(VECLAST(expr->call_expr.arguments), "Unpacking is only allowed for functions with variable parameters."); @@ -782,48 +794,48 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS } } - // 4. Zero out all argument slots. + // 6. Zero out all argument slots. unsigned func_param_count = vec_size(func_params); - // 4a. We need at least as many function locations as we have parameters. + // 6a. We need at least as many function locations as we have parameters. unsigned entries_needed = func_param_count > num_args ? func_param_count : num_args; Expr **actual_args = VECNEW(Expr*, entries_needed); for (unsigned i = 0; i < entries_needed; i++) vec_add(actual_args, NULL); memset(actual_args, 0, entries_needed * sizeof(Expr*)); - // 5. We might have a typed variadic call e.g. foo(int, double...) + // 7. We might have a typed variadic call e.g. foo(int, double...) // get that type. Type *variadic_type = NULL; if (signature->typed_variadic) { - // 5a. The parameter type is [], so we get the + // 7a. The parameter type is [], so we get the assert(func_params[func_param_count - 1]->type->type_kind == TYPE_SUBARRAY); variadic_type = func_params[func_param_count - 1]->type->array.base; - // 5b. The last function parameter is implicit, so remove it. + // 7b. The last function parameter is implicit, so remove it. func_param_count--; } - // 6. Loop through the parameters. + // 8. Loop through the parameters. bool uses_named_parameters = false; for (unsigned i = 0; i < num_args; i++) { - // 7. We might pick up the argument as the type if this is a type + // 9. We might pick up the argument as the type if this is a type // method. bool is_implicit = i < struct_args; Expr *arg = is_implicit ? struct_var : args[i - struct_args]; - // 8. Handle named parameters + // 10. Handle named parameters if (arg->expr_kind == EXPR_DESIGNATOR) { - // 8a. We have named parameters, that will add some restrictions. + // 10a. We have named parameters, that will add some restrictions. uses_named_parameters = true; - // 8b. Find the location of the parameter. + // 10b. Find the location of the parameter. int index = find_index_of_named_parameter(func_params, arg); - // 8c. If it's not found then this is an error. + // 10c. If it's not found then this is an error. if (index < 0) return false; - // 8d. We might actually be finding the typed vararg at the end, + // 10d. We might actually be finding the typed vararg at the end, // this is an error. if (func_params[index]->var.vararg) { @@ -831,23 +843,23 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS return false; } - // 8e. We might have already set this parameter, that is not allowed. + // 10e. We might have already set this parameter, that is not allowed. if (actual_args[index]) { SEMA_ERROR(arg, "The parameter '%s' was already set once.", func_params[index]->name); return false; } - // 8f. Check the parameter + // 10f. Check the parameter if (!sema_analyse_expr_of_required_type(context, func_params[index]->type, arg->designator_expr.value, true)) return false; - // 8g. Set the parameter and update failability. + // 10g. Set the parameter and update failability. actual_args[index] = arg->designator_expr.value; expr->failable |= arg->designator_expr.value->failable; continue; } - // 9. Check for previous use of named parameters, this is not allowed + // 11. Check for previous use of named parameters, this is not allowed // like foo(.a = 1, 3) => error. if (uses_named_parameters) { @@ -855,27 +867,27 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS return false; } - // 10. If we exceed the function parameter count (remember we reduced this by one + // 12. If we exceed the function parameter count (remember we reduced this by one // in the case of typed vararg) we're now in a variadic list. if (i >= func_param_count) { - // 11. We might have a typed variadic argument. + // 13. We might have a typed variadic argument. if (variadic_type) { - // 11a. Look if we did an unsplat + // 13a. Look if we did an unsplat if (expr->call_expr.unsplat_last) { - // 11b. Is this the last argument, or did we get some before the unpack? + // 13b. Is this the last argument, or did we get some before the unpack? if (i < num_args - 1) { SEMA_ERROR(arg, "This looks like a variable argument before an unpacked variable which isn't allowed. Did you add too many arguments?"); return false; } - // 11c. Analyse the expression. We don't use any type inference here since + // 13c. Analyse the expression. We don't use any type inference here since // foo(...{ 1, 2, 3 }) is a fairly worthless thing. if (!sema_analyse_expr(context, NULL, arg)) return false; - // 11d. If it is allowed. + // 13d. If it is allowed. if (!expr_may_unpack_as_vararg(arg, variadic_type)) { SEMA_ERROR(arg, "It's not possible to unpack %s as vararg of type %s", @@ -886,7 +898,7 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS } else { - // 11e. A simple variadic value: + // 13e. A simple variadic value: if (!sema_analyse_expr_of_required_type(context, variadic_type, arg, true)) return false; } // Set the argument at the location. @@ -894,30 +906,30 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS expr->failable |= arg->failable; continue; } - // 12. We might have a naked variadic argument + // 14. We might have a naked variadic argument if (signature->variadic) { - // 12a. Analyse the expression. + // 14a. Analyse the expression. if (!sema_analyse_expr(context, NULL, arg)) return false; - // 12b. In the case of a compile time variable we cast to c_int / double. + // 14b. In the case of a compile time variable we cast to c_int / double. Type *arg_type = arg->type->canonical; if (type_is_ct(arg_type)) { // TODO Fix the int conversion. - // 12c. Pick double / CInt + // 14c. Pick double / CInt Type *target_type = type_is_any_integer(arg_type) ? type_cint() : type_double; if (!cast_implicit(arg, target_type)) return false; arg_type = target_type; } - // 12d. Promote any integer or bool to at least CInt + // 14d. Promote any integer or bool to at least CInt if (type_is_promotable_integer(arg_type) || arg_type == type_bool) { Type *cint = type_cint(); cast(arg, cint); arg_type = cint; } - // 12e. Promote any float to at least Double + // 14e. Promote any float to at least Double if (type_is_promotable_float(arg->type)) { cast(arg, type_double); @@ -928,24 +940,24 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS expr->failable |= arg->failable; continue; } - // 13. We have too many parameters... + // 15. We have too many parameters... SEMA_ERROR(expr, "Too many parameters for this function."); return false; } - // 14. Analyse a regular argument. + // 16. Analyse a regular argument. if (!sema_analyse_expr_of_required_type(context, func_params[i]->type, arg, true)) return false; expr->failable |= arg->failable; actual_args[i] = arg; } - // 15. Set default values. + // 17. Set default values. for (unsigned i = 0; i < entries_needed; i++) { - // 15a. Assigned a value - skip + // 17a. Assigned a value - skip if (actual_args[i]) continue; - // 15b. Set the init expression. + // 17b. Set the init expression. if (func_params[i]->var.init_expr) { assert(func_params[i]->var.init_expr->resolve_status == RESOLVE_DONE); @@ -953,14 +965,14 @@ static inline bool sema_expr_analyse_func_invocation(Context *context, FunctionS continue; } - // 15c. Vararg not set? That's fine. + // 17c. Vararg not set? That's fine. if (func_params[i]->var.vararg) continue; - // 15d. Argument missing, that's bad. + // 17d. Argument missing, that's bad. SEMA_ERROR(expr, "Parameter '%s' was not set.", func_params[i]->name); return false; } - // 16. Set the type and failable. + // 18. Set the type and failable. expr_set_type(expr, signature->rtype->type); expr->call_expr.arguments = actual_args; expr->failable |= signature->failable; diff --git a/src/compiler/tokens.c b/src/compiler/tokens.c index de17c4603..65d58cf24 100644 --- a/src/compiler/tokens.c +++ b/src/compiler/tokens.c @@ -370,5 +370,5 @@ bool token_is_type(TokenType type) bool token_is_any_type(TokenType type) { - return (type >= TOKEN_VOID && type <= TOKEN_TYPEID) || type == TOKEN_CT_TYPE_IDENT || type == TOKEN_TYPE_IDENT; + return (type >= TOKEN_VOID && type <= TOKEN_TYPEID) || type == TOKEN_CT_TYPE_IDENT || type == TOKEN_TYPE_IDENT || type == TOKEN_VIRTUAL; } diff --git a/src/main.c b/src/main.c index d713cd99f..e9cd55631 100644 --- a/src/main.c +++ b/src/main.c @@ -10,11 +10,8 @@ int main(int argc, const char *argv[]) // First setup memory memory_init(); - - DEBUG_LOG("Version: %s", COMPILER_VERSION); - // Parse arguments. BuildOptions build_options = parse_arguments(argc, argv); @@ -44,7 +41,7 @@ int main(int argc, const char *argv[]) compile_file_list(&build_options); break; case COMMAND_MISSING: - printf("TODO\n"); + UNREACHABLE } diff --git a/src/version.h b/src/version.h index fd6f811ef..b991b44b9 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "A229" \ No newline at end of file +#define COMPILER_VERSION "A230" \ No newline at end of file diff --git a/test/test_suite/functions/body_argument_fail.c3 b/test/test_suite/functions/body_argument_fail.c3 new file mode 100644 index 000000000..bd9ae861c --- /dev/null +++ b/test/test_suite/functions/body_argument_fail.c3 @@ -0,0 +1,5 @@ +func void foo1(int... x;) {} // #error: Expected ')' +func void foo2(... x;) {} // #error: Expected ')' +func void foo3(... x;) {} // #error: Expected ')' +func void foo4(;) {} // #error: Expected ')' +func void foo5(int x;) {} // #error: Expected ')' \ No newline at end of file diff --git a/test/test_suite/functions/macro_arguments.c3 b/test/test_suite/functions/macro_arguments.c3 new file mode 100644 index 000000000..e00c2c98f --- /dev/null +++ b/test/test_suite/functions/macro_arguments.c3 @@ -0,0 +1,10 @@ + +func void foo1(int #foo) { } // #error: Only regular parameters are allowed for functions. + +func void foo2(int $foo) { } // #error: Only regular parameters are allowed for functions. + +func void foo3(bar) { } // #error: Only typed parameters are allowed for functions + +func void foo4($Type) { } // #error: Only regular parameters are allowed for functions. + +func void foo8(int &foo) {} // #error: Only regular parameters are allowed for functions. \ No newline at end of file diff --git a/test/test_suite/functions/vararg_argument_fails.c3 b/test/test_suite/functions/vararg_argument_fails.c3 new file mode 100644 index 000000000..e5cb1efa6 --- /dev/null +++ b/test/test_suite/functions/vararg_argument_fails.c3 @@ -0,0 +1,5 @@ +func void foo5(..., ...) {} // #error: Only a single vararg parameter is allowed + +func void foo6(int ..., int ...) {} // #error: Only a single vararg parameter is allowed + +func void foo7(int... x, int ... y) {} // #error: Only a single vararg parameter is allowed \ No newline at end of file diff --git a/test/test_suite/macros/macro_with_body.c3t b/test/test_suite/macros/macro_with_body.c3t index 76c40907a..e9d974732 100644 --- a/test/test_suite/macros/macro_with_body.c3t +++ b/test/test_suite/macros/macro_with_body.c3t @@ -14,13 +14,13 @@ func int Foo.mutate(Foo *foo) return 10 * ++foo.x; } -macro macro_with_body(foo, &x; x, y) +macro macro_with_body(foo, &x; @body(x, y)) { x = foo.x; yield foo.mutate(), x; } -macro repeat(int times; x) +macro repeat(int times; @body(x)) { for (int i = 0; i < times; i++) { diff --git a/test/test_suite/macros/no_body.c3 b/test/test_suite/macros/no_body.c3 index b8362dcd9..b24aed82b 100644 --- a/test/test_suite/macros/no_body.c3 +++ b/test/test_suite/macros/no_body.c3 @@ -2,7 +2,7 @@ module test; extern func int printf(char *, ...); -macro foo(x; y) +macro foo(x; @body(y)) { yield x; }