mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Unifies function and macro argument parsing. Removed generic and attribute parsing. Prepare removal of yield.
This commit is contained in:
committed by
Christoffer Lerno
parent
1e8a71ea62
commit
1d0c2ab2b0
@@ -77,7 +77,7 @@ typedef struct
|
||||
struct
|
||||
{
|
||||
char* chars;
|
||||
int len;
|
||||
uint32_t len;
|
||||
} string;
|
||||
Decl *enum_constant;
|
||||
struct
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 <type>[], so we get the <type>
|
||||
// 7a. The parameter type is <type>[], so we get the <type>
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
#define COMPILER_VERSION "A229"
|
||||
#define COMPILER_VERSION "A230"
|
||||
5
test/test_suite/functions/body_argument_fail.c3
Normal file
5
test/test_suite/functions/body_argument_fail.c3
Normal file
@@ -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 ')'
|
||||
10
test/test_suite/functions/macro_arguments.c3
Normal file
10
test/test_suite/functions/macro_arguments.c3
Normal file
@@ -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.
|
||||
5
test/test_suite/functions/vararg_argument_fails.c3
Normal file
5
test/test_suite/functions/vararg_argument_fails.c3
Normal file
@@ -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
|
||||
@@ -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++)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@ module test;
|
||||
|
||||
extern func int printf(char *, ...);
|
||||
|
||||
macro foo(x; y)
|
||||
macro foo(x; @body(y))
|
||||
{
|
||||
yield x;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user