Unifies function and macro argument parsing. Removed generic and attribute parsing. Prepare removal of yield.

This commit is contained in:
Christoffer Lerno
2021-06-27 23:04:46 +02:00
committed by Christoffer Lerno
parent 1e8a71ea62
commit 1d0c2ab2b0
14 changed files with 342 additions and 339 deletions

View File

@@ -77,7 +77,7 @@ typedef struct
struct
{
char* chars;
int len;
uint32_t len;
} string;
Decl *enum_constant;
struct

View File

@@ -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, &params, TOKEN_RPAREN, &unsplat)) return poisoned_expr;
if (!parse_arg_list(context, &params, 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 },

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -1 +1 @@
#define COMPILER_VERSION "A229"
#define COMPILER_VERSION "A230"

View 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 ')'

View 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.

View 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

View File

@@ -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++)
{

View File

@@ -2,7 +2,7 @@ module test;
extern func int printf(char *, ...);
macro foo(x; y)
macro foo(x; @body(y))
{
yield x;
}