// Copyright (c) 2020 Christoffer Lerno. All rights reserved. // Use of this source code is governed by a LGPLv3.0 // a copy of which can be found in the LICENSE file. #include "compiler_internal.h" #include "parser_internal.h" typedef Expr *(*ParseFn)(ParseContext *context, Expr *); typedef struct { ParseFn prefix; ParseFn infix; Precedence precedence; } ParseRule; extern ParseRule rules[TOKEN_EOF + 1]; bool parse_current_is_expr(ParseContext *c) { return rules[c->tok].prefix != NULL; } /** * maybe_range ::= range | ('^'? INTEGER) * range ::= dot_range | colon_range * dot_range ::= ('^'? INTEGER)? .. ('^'? INTEGER)? * colon_range ::= ('^'? INTEGER)? : ('^'? INTEGER)? */ bool parse_range(ParseContext *c, Range *range) { SourceSpan start = c->span; // Insert zero if missing if (tok_is(c, TOKEN_DOTDOT) || tok_is(c, TOKEN_COLON)) { // ..123 and :123 range->start_from_end = false; range->start = exprid(expr_new_const_int(c->span, type_uint, 0)); } else { // Parse ^123 and 123 range->start_from_end = try_consume(c, TOKEN_BIT_XOR); ASSIGN_EXPRID_OR_RET(range->start, parse_expr(c), false); } // Check if .. or : bool is_len_range = range->is_len = try_consume(c, TOKEN_COLON); if (!is_len_range && !try_consume(c, TOKEN_DOTDOT)) { // Otherwise this is not a range. range->is_range = false; return true; } range->is_range = true; // Is there an expression next? range->end_from_end = try_consume(c, TOKEN_BIT_XOR); if (range->end_from_end || parse_current_is_expr(c)) { ASSIGN_EXPRID_OR_RET(range->end, parse_expr(c), false); return true; } // Otherwise we have [1..] or [3:] if (range->is_len) { print_error_at(extend_span_with_token(start, c->prev_span), "Length-ranges using ':' may not elide the length."); return false; } range->end_from_end = false; range->end = 0; return true; } static bool parse_expr_list(ParseContext *c, Expr ***exprs_ref, TokenType end_token) { while (!try_consume(c, end_token)) { ASSIGN_EXPR_OR_RET(Expr *expr, parse_expr(c), false); vec_add(*exprs_ref, expr); if (!try_consume(c, TOKEN_COMMA)) { CONSUME_OR_RET(end_token, false); return true; } } return true; } /** * generic_parameters ::= '(<' expr (',' expr) '>)' */ bool parse_generic_parameters(ParseContext *c, Expr ***exprs_ref) { advance_and_verify(c, TOKEN_LGENPAR); while (true) { ASSIGN_EXPR_OR_RET(Expr *expr, parse_expr(c), false); vec_add(*exprs_ref, expr); if (try_consume(c, TOKEN_COMMA)) continue; CONSUME_OR_RET(TOKEN_RGENPAR, false); return true; } } /** * rethrow_expr ::= call_expr '!' */ static Expr *parse_rethrow_expr(ParseContext *c, Expr *left_side) { assert(expr_ok(left_side)); advance_and_verify(c, TOKEN_BANG); Expr *expr_ternary = expr_new_expr(EXPR_RETHROW, left_side); expr_ternary->rethrow_expr.inner = left_side; RANGE_EXTEND_PREV(expr_ternary); return expr_ternary; } /** * Parse lhs [op] [rhs] * This will return lhs if no candidate is found. */ inline Expr *parse_precedence_with_left_side(ParseContext *c, Expr *left_side, Precedence precedence) { while (1) { TokenType tok = c->tok; Precedence token_precedence = rules[tok].precedence; // See if the operator precedence is greater than the last, if so exit. // Note that if the token is not an operator then token_precedence = 0 if (precedence > token_precedence) break; // LHS may be poison. if (!expr_ok(left_side)) return left_side; // See if there is a rule for infix. ParseFn infix_rule = rules[tok].infix; // Otherwise we ran into a symbol that can't appear in this position. if (!infix_rule) { PRINT_ERROR_HERE("'%s' can't appear in this position, did you forget something before the operator?", token_type_to_string(tok)); return poisoned_expr; } // The rule exists, so run it. left_side = infix_rule(c, left_side); } return left_side; } /** * Parse an expression in any position. */ static Expr *parse_precedence(ParseContext *c, Precedence precedence) { // Get the rule for the previous token. ParseFn prefix_rule = rules[c->tok].prefix; // No prefix rule => either it's not an expression // or it is a token that can't be in a prefix position. if (!prefix_rule) { PRINT_ERROR_HERE("An expression was expected."); return poisoned_expr; } // Get the expression Expr *left_side = prefix_rule(c, NULL); // Exit if it's an error. if (!expr_ok(left_side)) return left_side; // Now parse the (optional) right hand side. return parse_precedence_with_left_side(c, left_side, precedence); } /* * Parse anything with higher precedence than && etc. */ static inline Expr *parse_try_chain_expr(ParseContext *c) { return parse_precedence(c, PREC_RELATIONAL); } /** * catch_unwrap ::= CATCH (IDENT | type? IDENT '=' catch_chain) | catch_chain * catch_chain ::= parse_try_catch_rhs_expr (',' parse_try_catch_rhs_expr)* */ static inline Expr *parse_catch_unwrap(ParseContext *c) { Expr *expr = expr_new(EXPR_CATCH_UNWRAP, c->span); advance_and_verify(c, TOKEN_CATCH); Expr **exprs = NULL; // First, try parsing as single expression ASSIGN_EXPR_OR_RET(Expr *sub_expr, parse_try_chain_expr(c), poisoned_expr); // Check if we have a chain. if (try_consume(c, TOKEN_COMMA)) { // Create the chain. vec_add(exprs, sub_expr); do { ASSIGN_EXPR_OR_RET(sub_expr, parse_try_chain_expr(c), poisoned_expr); vec_add(exprs, sub_expr); } while (try_consume(c, TOKEN_COMMA)); // We're done. expr->catch_unwrap_expr.exprs = exprs; return expr; } // We don't have a chain, so it's either "anyfault f" or "f" or an expression. if (sub_expr->expr_kind == EXPR_TYPEINFO) { // Assign the type expr->catch_unwrap_expr.type = sub_expr->type_expr; // Assign the variable ASSIGN_EXPR_OR_RET(expr->catch_unwrap_expr.variable, parse_try_chain_expr(c), poisoned_expr); } else { // Here we assume it's either "f" or an expression. expr->catch_unwrap_expr.type = NULL; expr->catch_unwrap_expr.variable = sub_expr; } // If we don't have an '=', we check whether if (!try_consume(c, TOKEN_EQ)) { // If we had "anyfault f", then we MUST have '=' if (expr->catch_unwrap_expr.type) { PRINT_ERROR_HERE("Expected a '=' here."); return poisoned_expr; } // We just have `catch foo`, so we add the "variable" as an expression vec_add(expr->catch_unwrap_expr.exprs, expr->catch_unwrap_expr.variable); expr->catch_unwrap_expr.variable = NULL; RANGE_EXTEND_PREV(expr); return expr; } // After '=' we have a chain of expressions. do { ASSIGN_EXPR_OR_RET(sub_expr, parse_try_chain_expr(c), poisoned_expr); vec_add(exprs, sub_expr); } while (try_consume(c, TOKEN_COMMA)); expr->catch_unwrap_expr.exprs = exprs; RANGE_EXTEND_PREV(expr); return expr; } /** * try_unwrap ::= TRY ((type? IDENT '=' try_rhs_expr) | try_rhs_expr) */ static inline Expr *parse_try_unwrap(ParseContext *c) { Expr *expr = EXPR_NEW_TOKEN(EXPR_TRY_UNWRAP); advance_and_verify(c, TOKEN_TRY); ASSIGN_EXPR_OR_RET(Expr *lhs, parse_try_chain_expr(c), poisoned_expr); if (lhs->expr_kind == EXPR_TYPEINFO) { expr->try_unwrap_expr.type = lhs->type_expr; ASSIGN_EXPR_OR_RET(expr->try_unwrap_expr.variable, parse_try_chain_expr(c), poisoned_expr); } else { expr->try_unwrap_expr.variable = lhs; } if (lhs->expr_kind == EXPR_TYPEINFO && expr->try_unwrap_expr.variable->expr_kind != EXPR_IDENTIFIER) { RETURN_PRINT_ERROR_AT(poisoned_expr, expr->try_unwrap_expr.variable, "A new variable was expected."); } if (try_consume(c, TOKEN_EQ)) { ASSIGN_EXPR_OR_RET(expr->try_unwrap_expr.init, parse_try_chain_expr(c), poisoned_expr); } RANGE_EXTEND_PREV(expr); return expr; } /** * try_unwrap_chain ::= try_unwrap ('&&' (try_unwrap | try_chain_expr))* */ static inline Expr *parse_try_unwrap_chain(ParseContext *c) { Expr **unwraps = NULL; ASSIGN_EXPR_OR_RET(Expr * first_unwrap , parse_try_unwrap(c), poisoned_expr); vec_add(unwraps, first_unwrap); while (try_consume(c, TOKEN_AND)) { if (tok_is(c, TOKEN_TRY)) { ASSIGN_EXPR_OR_RET(Expr * expr, parse_try_unwrap(c), poisoned_expr); vec_add(unwraps, expr); continue; } ASSIGN_EXPR_OR_RET(Expr * next_unwrap, parse_try_chain_expr(c), poisoned_expr); vec_add(unwraps, next_unwrap); } Expr *try_unwrap_chain = expr_new_expr(EXPR_TRY_UNWRAP_CHAIN, first_unwrap); try_unwrap_chain->try_unwrap_chain_expr = unwraps; RANGE_EXTEND_PREV(try_unwrap_chain); return try_unwrap_chain; } /** * cond_list ::= ((expr | decl-expr) COMMA)* (expr | decl-expr | try_unwrap_chain | catch_unwrap ) * * @return bool */ Expr *parse_cond(ParseContext *c) { Expr *decl_expr = EXPR_NEW_TOKEN(EXPR_COND); decl_expr->cond_expr = NULL; while (1) { if (tok_is(c, TOKEN_TRY)) { ASSIGN_EXPR_OR_RET(Expr * try_unwrap, parse_try_unwrap_chain(c), poisoned_expr); vec_add(decl_expr->cond_expr, try_unwrap); if (tok_is(c, TOKEN_COMMA)) { RETURN_PRINT_ERROR_AT(poisoned_expr, try_unwrap, "The 'try' must be placed last, can you change it?"); } break; } if (tok_is(c, TOKEN_CATCH)) { ASSIGN_EXPR_OR_RET(Expr* catch_unwrap, parse_catch_unwrap(c), poisoned_expr); vec_add(decl_expr->cond_expr, catch_unwrap); if (tok_is(c, TOKEN_COMMA)) { RETURN_PRINT_ERROR_AT(poisoned_expr, catch_unwrap, "The 'catch' must be placed last, can you change it?"); } break; } Decl *decl; ASSIGN_EXPR_OR_RET(Expr * expr, parse_decl_or_expr(c, &decl), poisoned_expr); if (!expr) { expr = expr_new(EXPR_DECL, decl->span); expr->decl_expr = decl; } vec_add(decl_expr->cond_expr, expr); if (!try_consume(c, TOKEN_COMMA)) break; } RANGE_EXTEND_PREV(decl_expr); return decl_expr; } // These used to be explicitly inlined, but that seems to lead to confusing MSVC linker errors. // They are probably still inlined by the compiler, though I haven't checked. Expr* parse_expr(ParseContext *c) { return parse_precedence(c, PREC_ASSIGNMENT); } Expr* parse_constant_expr(ParseContext *c) { return parse_precedence(c, PREC_ASSIGNMENT + 1); } /** * param_path ::= ('[' expr ']' | '.' IDENT)* * * @param c * @param path reference to the path to return * @return true if parsing succeeds, false otherwise. */ static bool parse_param_path(ParseContext *c, DesignatorElement ***path) { *path = NULL; while (true) { if (tok_is(c, TOKEN_LBRACKET)) { // Parse the inside of [ ] DesignatorElement *element = CALLOCS(DesignatorElement); element->kind = DESIGNATOR_ARRAY; advance_and_verify(c, TOKEN_LBRACKET); ASSIGN_EXPR_OR_RET(element->index_expr, parse_expr(c), false); // Possible range if (try_consume(c, TOKEN_DOTDOT)) { ASSIGN_EXPR_OR_RET(element->index_end_expr, parse_expr(c), false); element->kind = DESIGNATOR_RANGE; } CONSUME_OR_RET(TOKEN_RBRACKET, false); // Include right bracket in the expr vec_add(*path, element); continue; } if (tok_is(c, TOKEN_DOT)) { advance(c); DesignatorElement *element = CALLOCS(DesignatorElement); element->kind = DESIGNATOR_FIELD; ASSIGN_EXPR_OR_RET(element->field_expr, parse_precedence(c, PREC_PRIMARY), false); vec_add(*path, element); continue; } return true; } } static Expr *parse_lambda(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *expr = EXPR_NEW_TOKEN(EXPR_LAMBDA); advance_and_verify(c, TOKEN_FN); Decl *func = decl_calloc(); func->decl_kind = DECL_FUNC; func->visibility = VISIBLE_LOCAL; func->func_decl.generated_lambda = NULL; TypeInfo *return_type = NULL; if (!tok_is(c, TOKEN_LPAREN)) { ASSIGN_TYPE_OR_RET(return_type, parse_optional_type(c), poisoned_expr); } CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); Decl **params = NULL; Decl **decls = NULL; Variadic variadic = VARIADIC_NONE; int vararg_index = -1; if (!parse_parameters(c, &decls, NULL, &variadic, &vararg_index, PARAM_PARSE_LAMBDA)) return false; CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); Signature *sig = &func->func_decl.signature; sig->vararg_index = vararg_index < 0 ? vec_size(decls) : vararg_index; sig->params = decls; sig->rtype = return_type ? type_infoid(return_type) : 0; sig->variadic = variadic; if (!parse_attributes(c, &func->attributes, NULL, NULL, NULL)) return poisoned_expr; if (tok_is(c, TOKEN_IMPLIES)) { ASSIGN_ASTID_OR_RET(func->func_decl.body, parse_short_body(c, func->func_decl.signature.rtype, false), poisoned_expr); } else if (tok_is(c, TOKEN_LBRACE)) { ASSIGN_ASTID_OR_RET(func->func_decl.body, parse_compound_stmt(c), poisoned_expr); } else { PRINT_ERROR_HERE("Expected the beginning of a block or a short statement."); } expr->lambda_expr = func; return expr; } /** * vasplat ::= CT_VASPLAT '(' range_expr ')' */ Expr *parse_vasplat(ParseContext *c) { Expr *expr = EXPR_NEW_TOKEN(EXPR_VASPLAT); advance_and_verify(c, TOKEN_CT_VASPLAT); TRY_CONSUME_OR_RET(TOKEN_LPAREN, "'$vasplat' must be followed by '()'.", poisoned_expr); if (!try_consume(c, TOKEN_RPAREN)) { if (!parse_range(c, &expr->vasplat_expr)) return poisoned_expr; CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); } RANGE_EXTEND_PREV(expr); return expr; } /** * param_list ::= ('...' arg | arg (',' arg)*)? * * parameter ::= ((param_path '=')? expr) | param_path */ bool parse_arg_list(ParseContext *c, Expr ***result, TokenType param_end, bool *splat, bool vasplat) { *result = NULL; if (splat) *splat = false; while (1) { Expr *expr = NULL; DesignatorElement **path; SourceSpan start_span = c->span; if (!parse_param_path(c, &path)) return false; if (path != NULL) { // Create the parameter expr expr = expr_new(EXPR_DESIGNATOR, start_span); expr->designator_expr.path = path; if (try_consume(c, TOKEN_EQ)) { ASSIGN_EXPR_OR_RET(expr->designator_expr.value, parse_expr(c), false); } RANGE_EXTEND_PREV(expr); } else if (vasplat && tok_is(c, TOKEN_CT_VASPLAT)) { ASSIGN_EXPR_OR_RET(expr, parse_vasplat(c), false); } else { if (splat) { if (*splat) { PRINT_ERROR_HERE("'...' is only allowed on the last argument in a call."); return false; } *splat = try_consume(c, TOKEN_ELLIPSIS); } ASSIGN_EXPR_OR_RET(expr, parse_expr(c), false); } vec_add(*result, expr); if (!try_consume(c, TOKEN_COMMA)) { return true; } if (tok_is(c, param_end)) return true; if (splat && *splat) { } } } /** * expression_list ::= decl_or_expr+ */ Expr *parse_expression_list(ParseContext *c, bool allow_decl) { Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST); while (1) { Decl *decl; ASSIGN_EXPR_OR_RET(Expr *expr, parse_decl_or_expr(c, &decl), poisoned_expr); if (!expr) { if (!allow_decl) { PRINT_ERROR_HERE("This looks like a declaration, which isn't allowed here."); return poisoned_expr; } expr = expr_new(EXPR_DECL, decl->span); expr->decl_expr = decl; } vec_add(expr_list->expression_list, expr); if (!try_consume(c, TOKEN_COMMA)) break; } return expr_list; } Expr *parse_ct_expression_list(ParseContext *c, bool allow_decl) { Expr *expr_list = EXPR_NEW_TOKEN(EXPR_EXPRESSION_LIST); while (1) { Expr *expr; if (tok_is(c, TOKEN_VAR)) { ASSIGN_DECL_OR_RET(Decl *decl, parse_var_decl(c), poisoned_expr); if (!allow_decl) { PRINT_ERROR_HERE("This looks like a declaration, which isn't allowed here."); return poisoned_expr; } expr = expr_new(EXPR_DECL, decl->span); expr->decl_expr = decl; } else { ASSIGN_EXPR_OR_RET(expr, parse_expr(c), poisoned_expr); } vec_add(expr_list->expression_list, expr); if (!try_consume(c, TOKEN_COMMA)) break; } return expr_list; } /** * @param left must be null. * @return Expr* */ static Expr *parse_type_identifier(ParseContext *context, Expr *left) { assert(!left && "Unexpected left hand side"); return parse_type_expression_with_path(context, NULL); } /** * type_expr ::= type initializer_list? */ static Expr *parse_type_expr(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *expr = EXPR_NEW_TOKEN(EXPR_TYPEINFO); ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_optional_type(c), poisoned_expr); if (tok_is(c, TOKEN_LBRACE)) { return parse_type_compound_literal_expr_after_type(c, type); } expr->span = type->span; expr->type_expr = type; expr->type = type_typeinfo; if (type->resolve_status == RESOLVE_DONE) expr->resolve_status = RESOLVE_DONE; if (tok_is(c, TOKEN_SCOPE)) { PRINT_ERROR_HERE("A type is never followed by '::', did you mean '.'?"); return poisoned_expr; } return expr; } static Expr *parse_ct_stringify(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); SourceSpan start_span = c->span; const char *start = c->lexer.current; advance(c); CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); ASSIGN_EXPR_OR_RET(Expr *inner, parse_expr(c), poisoned_expr); const char *end = c->lexer.lexing_start - 1; CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); if (inner->expr_kind == EXPR_HASH_IDENT) { Expr *expr = expr_new(EXPR_STRINGIFY, start_span); expr->inner_expr = inner; RANGE_EXTEND_PREV(expr); return expr; } size_t len = end - start; const char *content = str_copy(start, len); Expr *expr = expr_new(EXPR_CONST, start_span); expr->const_expr.const_kind = CONST_STRING; expr->const_expr.bytes.ptr = content; expr->const_expr.bytes.len = len; expr->type = type_string; expr->resolve_status = RESOLVE_DONE; return expr; } /** * unary_expr ::= unary_op unary_prec_expr */ static Expr *parse_unary_expr(ParseContext *c, Expr *left) { assert(!left && "Did not expect a left hand side!"); Expr *unary = EXPR_NEW_TOKEN(EXPR_UNARY); unary->unary_expr.operator = unaryop_from_token(c->tok); advance(c); Expr *right_side = parse_precedence(c, PREC_UNARY); CHECK_EXPR_OR_RET(right_side); unary->unary_expr.expr = right_side; RANGE_EXTEND_PREV(unary); return unary; } /** * post_unary_expr ::= unary_op */ static Expr *parse_post_unary(ParseContext *c, Expr *left) { assert(expr_ok(left)); Expr *unary = expr_new_expr(EXPR_POST_UNARY, left); unary->unary_expr.expr = left; unary->unary_expr.operator = unaryop_from_token(c->tok); advance(c); RANGE_EXTEND_PREV(unary); return unary; } /** * elvis_expr := ?: ternary_prec_expr */ static Expr *parse_elvis_expr(ParseContext *c, Expr *left_side) { assert(expr_ok(left_side)); Expr *expr_ternary = expr_new_expr(EXPR_TERNARY, left_side); expr_ternary->ternary_expr.cond = exprid(left_side); advance_and_verify(c, TOKEN_ELVIS); expr_ternary->ternary_expr.then_expr = 0; ASSIGN_EXPRID_OR_RET(expr_ternary->ternary_expr.else_expr, parse_precedence(c, PREC_TERNARY), poisoned_expr); RANGE_EXTEND_PREV(expr_ternary); return expr_ternary; } /** * ternary_optional_expr ::= optional_expr | ternary_expr * optional_expr ::= '?' '!'? * ternary_expr ::= '?' expr ':' ternary_prec_expr */ static Expr *parse_ternary_expr(ParseContext *c, Expr *left_side) { assert(expr_ok(left_side)); Expr *expr = expr_new_expr(EXPR_TERNARY, left_side); advance_and_verify(c, TOKEN_QUESTION); // If we have no expression following *or* it is a '!' followed by no expression // in this case it's an optional expression. if (!rules[c->tok].prefix || (c->tok == TOKEN_BANG && !rules[peek(c)].prefix)) { expr->expr_kind = EXPR_OPTIONAL; expr->inner_expr = left_side; RANGE_EXTEND_PREV(expr); return expr; } // Otherwise we have a ternary expr->ternary_expr.cond = exprid(left_side); // LHS is a plain expression ASSIGN_EXPR_OR_RET(Expr *true_expr, parse_expr(c), poisoned_expr); expr->ternary_expr.then_expr = exprid(true_expr); CONSUME_OR_RET(TOKEN_COLON, poisoned_expr); // RHS is ternary prec, to get right -> left associativity ASSIGN_EXPRID_OR_RET(expr->ternary_expr.else_expr, parse_precedence(c, PREC_TERNARY), poisoned_expr); RANGE_EXTEND_PREV(expr); return expr; } /** * grouping_expr ::= cast_expr | group_expr * * cast_expr ::= '(' type ')' expr * group_expr ::= '(' expr ')' * * When parsing we retain EXPR_GROUP in order to require explicit parentheses later * as needed. */ static Expr *parse_grouping_expr(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *expr; advance_and_verify(c, TOKEN_LPAREN); ASSIGN_EXPR_OR_RET(expr, parse_expr(c), poisoned_expr); CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); // Look at what follows. switch (expr->expr_kind) { case EXPR_TYPEINFO: { TypeInfo *info = expr->type_expr; if (tok_is(c, TOKEN_LBRACE) && info->resolve_status != RESOLVE_DONE) { PRINT_ERROR_HERE("Unexpected start of a block '{' here. If you intended a compound literal, remove the () around the type."); return poisoned_expr; } // Create a cast expr if (rules[c->tok].prefix) { ASSIGN_EXPRID_OR_RET(ExprId inner, parse_precedence(c, PREC_CALL), poisoned_expr); SourceSpan span = expr->span; *expr = (Expr) {.expr_kind = EXPR_CAST, .span = span, .cast_expr.type_info = type_infoid(info), .cast_expr.expr = inner}; } break; } case EXPR_BINARY: expr->binary_expr.grouped = true; break; case EXPR_TERNARY: expr->ternary_expr.grouped = true; break; default: break; } RANGE_EXTEND_PREV(expr); return expr; } /** * initializer_list * : '{' initializer_values '}' * | '{' initializer_values ',' '}' * ; * * initializer_values * : initializer * | initializer_values ',' initializer * ; * * @param elements * @return */ Expr *parse_initializer_list(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *initializer_list = EXPR_NEW_TOKEN(EXPR_INITIALIZER_LIST); advance_and_verify(c, TOKEN_LBRACE); if (!try_consume(c, TOKEN_RBRACE)) { Expr **exprs = NULL; if (!parse_arg_list(c, &exprs, TOKEN_RBRACE, NULL, true)) return poisoned_expr; int designated = -1; FOREACH(Expr *, expr, exprs) { if (expr->expr_kind == EXPR_DESIGNATOR) { if (designated == 0) { RETURN_PRINT_ERROR_AT(poisoned_expr, expr, "Designated initialization with '[] = ...' and '.param = ...' cannot be mixed with normal initialization."); } designated = 1; continue; } if (designated == 1) { RETURN_PRINT_ERROR_AT(poisoned_expr, expr, "Normal initialization cannot be mixed with designated initialization."); } designated = 0; } CONSUME_OR_RET(TOKEN_RBRACE, poisoned_expr); RANGE_EXTEND_PREV(initializer_list); if (designated == 1) { initializer_list->designated_init_list = exprs; initializer_list->expr_kind = EXPR_DESIGNATED_INITIALIZER_LIST; } else { initializer_list->initializer_list = exprs; } } RANGE_EXTEND_PREV(initializer_list); return initializer_list; } static Expr *parse_orelse(ParseContext *c, Expr *left_side) { assert(left_side && expr_ok(left_side)); advance_and_verify(c, TOKEN_QUESTQUEST); Expr *right_side; // Assignment operators have precedence right -> left. ASSIGN_EXPR_OR_RET(right_side, parse_precedence(c, PREC_TERNARY), poisoned_expr); Expr *expr = expr_new_expr(EXPR_BINARY, left_side); expr->binary_expr.operator = BINARYOP_ELSE; expr->binary_expr.left = exprid(left_side); expr->binary_expr.right = exprid(right_side); RANGE_EXTEND_PREV(expr); return expr; } static Expr *parse_binary(ParseContext *c, Expr *left_side) { assert(left_side && expr_ok(left_side)); // Remember the operator. TokenType operator_type = c->tok; advance(c); Expr *right_side; // Assignment operators have precedence right -> left. if (rules[operator_type].precedence == PREC_ASSIGNMENT) { ASSIGN_EXPR_OR_RET(right_side, parse_precedence(c, PREC_ASSIGNMENT), poisoned_expr); } else { ASSIGN_EXPR_OR_RET(right_side, parse_precedence(c, rules[operator_type].precedence + 1), poisoned_expr); } Expr *expr = expr_new_expr(EXPR_BINARY, left_side); expr->binary_expr.operator = binaryop_from_token(operator_type); expr->binary_expr.left = exprid(left_side); expr->binary_expr.right = exprid(right_side); RANGE_EXTEND_PREV(expr); return expr; } static Expr *parse_call_expr(ParseContext *c, Expr *left) { assert(left && expr_ok(left)); Expr **params = NULL; advance_and_verify(c, TOKEN_LPAREN); bool splat = false; Decl **body_args = NULL; if (!tok_is(c, TOKEN_RPAREN) && !tok_is(c, TOKEN_EOS)) { // Pick a modest guess. params = VECNEW(Expr*, 4); if (!parse_arg_list(c, ¶ms, TOKEN_RPAREN, &splat, true)) return poisoned_expr; } if (try_consume(c, TOKEN_EOS)) { if (!parse_next_may_be_type_or_ident(c)) { PRINT_ERROR_LAST("Expected an ending ')'. Did you forget a ')' before this ';'?"); return poisoned_expr; } if (!parse_parameters(c, &body_args, NULL, NULL, NULL, PARAM_PARSE_CALL)) return poisoned_expr; } if (!tok_is(c, TOKEN_RPAREN)) { PRINT_ERROR_LAST("Expected the ending ')' here."); return poisoned_expr; } advance(c); Expr *call = expr_new_expr(EXPR_CALL, left); call->call_expr.function = exprid(left); call->call_expr.arguments = params; call->call_expr.splat_vararg = splat; RANGE_EXTEND_PREV(call); if (body_args && !tok_is(c, TOKEN_LBRACE)) { PRINT_ERROR_HERE("Expected a macro body here."); return poisoned_expr; } Attr *attr; int force_inline = -1; while (1) { if (!parse_attribute(c, &attr, true)) return poisoned_expr; if (!attr) break; AttributeType attr_type = attribute_by_name(attr->name); int new_inline = attr_type == ATTRIBUTE_INLINE; switch (attr_type) { case ATTRIBUTE_PURE: if (call->call_expr.attr_pure) { RETURN_PRINT_ERROR_AT(poisoned_expr, attr, "Repeat of the same attribute is not allowed."); } call->call_expr.attr_pure = true; continue; case ATTRIBUTE_INLINE: case ATTRIBUTE_NOINLINE: if (force_inline == new_inline) { RETURN_PRINT_ERROR_AT(poisoned_expr, attr, "Repeat of the same attribute is not allowed."); } if (force_inline != -1) { RETURN_PRINT_ERROR_AT(poisoned_expr, attr, "@inline and @noinline cannot be combined"); } force_inline = new_inline; continue; default: RETURN_PRINT_ERROR_AT(poisoned_expr, attr, "Only '@pure', '@inline' and '@noinline' are valid attributes for calls."); } } if (force_inline != -1) { call->call_expr.attr_force_inline = force_inline == 1; call->call_expr.attr_force_noinline = force_inline == 0; } Ast *body = NULL; if (tok_is(c, TOKEN_LBRACE)) { ASSIGN_AST_OR_RET(body, parse_compound_stmt(c), poisoned_expr); } if (body || body_args) { Expr *macro_body = expr_new(EXPR_MACRO_BODY, call->span); macro_body->macro_body_expr.body = body; macro_body->macro_body_expr.body_arguments = body_args; call->call_expr.macro_body = exprid(macro_body); } return call; } /** * subscript ::= '[' range_expr ']' */ static Expr *parse_subscript_expr(ParseContext *c, Expr *left) { assert(left && expr_ok(left)); advance_and_verify(c, TOKEN_LBRACKET); Expr *subs_expr = expr_new_expr(EXPR_SUBSCRIPT, left); subs_expr->subscript_expr.expr = exprid(left); if (!parse_range(c, &subs_expr->subscript_expr.range)) return poisoned_expr; CONSUME_OR_RET(TOKEN_RBRACKET, poisoned_expr); if (subs_expr->subscript_expr.range.is_range) { subs_expr->expr_kind = EXPR_SLICE; } RANGE_EXTEND_PREV(subs_expr); return subs_expr; } /** * generic_expr ::= IDENT generic_parameters */ static Expr *parse_generic_expr(ParseContext *c, Expr *left) { assert(left && expr_ok(left)); Expr *subs_expr = expr_new_expr(EXPR_GENERIC_IDENT, left); subs_expr->generic_ident_expr.parent = exprid(left); if (!parse_generic_parameters(c, &subs_expr->generic_ident_expr.parmeters)) return poisoned_expr; RANGE_EXTEND_PREV(subs_expr); return subs_expr; } /** * access_expr ::= '.' primary_expr */ static Expr *parse_access_expr(ParseContext *c, Expr *left) { assert(left && expr_ok(left)); advance_and_verify(c, TOKEN_DOT); Expr *access_expr = expr_new_expr(EXPR_ACCESS, left); access_expr->access_expr.parent = left; ASSIGN_EXPR_OR_RET(access_expr->access_expr.child, parse_precedence(c, PREC_PRIMARY), poisoned_expr); RANGE_EXTEND_PREV(access_expr); return access_expr; } static Expr *parse_ct_ident(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); if (try_consume(c, TOKEN_CT_CONST_IDENT)) { PRINT_ERROR_LAST("Compile time identifiers may not be constants."); return poisoned_expr; } Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_IDENT); expr->ct_ident_expr.identifier = symstr(c); advance_and_verify(c, TOKEN_CT_IDENT); return expr; } static Expr *parse_hash_ident(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *expr = EXPR_NEW_TOKEN(EXPR_HASH_IDENT); expr->ct_ident_expr.identifier = symstr(c); advance_and_verify(c, TOKEN_HASH_IDENT); return expr; } /** * ct_eval ::= CT_EVAL '(' expr ')' */ static Expr *parse_ct_eval(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_EVAL); advance(c); CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); ASSIGN_EXPR_OR_RET(expr->inner_expr, parse_expr(c), poisoned_expr); CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); RANGE_EXTEND_PREV(expr); return expr; } static Expr *parse_ct_defined(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *defined = expr_new(EXPR_CT_DEFINED, c->span); advance(c); CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); if (!parse_expr_list(c, &defined->expression_list, TOKEN_RPAREN)) return poisoned_expr; return defined; } /** * ct_sizeof ::= CT_SIZEOF '(' expr ')' * * Note that this is tranformed to $typeof(expr).sizeof. */ static Expr *parse_ct_sizeof(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *access = expr_new(EXPR_ACCESS, c->span); advance(c); CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); ASSIGN_EXPR_OR_RET(Expr *inner, parse_expr(c), poisoned_expr); CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); Expr *typeof_expr = expr_new(EXPR_TYPEINFO, inner->span); TypeInfo *type_info = type_info_new(TYPE_INFO_TYPEOF, inner->span); type_info->optional = try_consume(c, TOKEN_BANG); type_info->unresolved_type_expr = inner; typeof_expr->type_expr = type_info; access->access_expr.parent = typeof_expr; Expr *ident = expr_new(EXPR_IDENTIFIER, c->span); ident->identifier_expr.ident = type_property_list[TYPE_PROPERTY_SIZEOF]; access->access_expr.child = ident; RANGE_EXTEND_PREV(access); return access; } /** * ct_is_const ::= CT_IS_CONST '(' expr ')' */ static Expr *parse_ct_is_const(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *checks = expr_new(EXPR_CT_IS_CONST, c->span); advance_and_verify(c, TOKEN_CT_IS_CONST); CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); ASSIGN_EXPR_OR_RET(checks->inner_expr, parse_expr(c), poisoned_expr); CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); RANGE_EXTEND_PREV(checks); return checks; } /** * ct_checks ::= CT_EMBED '(' constant_expr (',' constant_expr)? ')' */ static Expr *parse_ct_embed(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *embed = expr_new(EXPR_EMBED, c->span); advance_and_verify(c, TOKEN_CT_EMBED); CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); ASSIGN_EXPR_OR_RET(embed->embed_expr.filename, parse_constant_expr(c), poisoned_expr); if (try_consume(c, TOKEN_COMMA)) { ASSIGN_EXPR_OR_RET(embed->embed_expr.len, parse_constant_expr(c), poisoned_expr); } CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); RANGE_EXTEND_PREV(embed); return embed; } static Expr *parse_ct_concat_append(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *expr = EXPR_NEW_TOKEN(tok_is(c, TOKEN_CT_CONCATFN) ? EXPR_CT_CONCAT : EXPR_CT_APPEND); advance(c); CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); if (!parse_expr_list(c, &expr->ct_concat, TOKEN_RPAREN)) return poisoned_expr; RANGE_EXTEND_PREV(expr); return expr; } /** * ct_call ::= (CT_ALIGNOF | CT_FEATURE | CT_EXTNAMEOF | CT_OFFSETOF | CT_NAMEOF | CT_QNAMEOF) '(' flat_path ')' * flat_path ::= expr ('.' primary) | '[' expr ']')* */ static Expr *parse_ct_call(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_CALL); expr->ct_call_expr.token_type = c->tok; advance(c); CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); ASSIGN_EXPR_OR_RET(Expr* internal, parse_precedence(c, PREC_PRIMARY), poisoned_expr); DesignatorElement **elements = NULL; if (!parse_param_path(c, &elements)) return poisoned_expr; expr->ct_call_expr.main_var = internal; expr->ct_call_expr.flat_path = elements; CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); RANGE_EXTEND_PREV(expr); return expr; } static Expr *parse_ct_and_or(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_AND_OR); expr->ct_and_or_expr.is_and = tok_is(c, TOKEN_CT_ANDFN); advance(c); CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); if (!parse_expr_list(c, &expr->ct_and_or_expr.args, TOKEN_RPAREN)) return poisoned_expr; RANGE_EXTEND_PREV(expr); return expr; } static Expr *parse_ct_castable(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_CASTABLE); expr->castable_expr.is_assign = c->tok == TOKEN_CT_ASSIGNABLE; advance(c); CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); ASSIGN_EXPRID_OR_RET(expr->castable_expr.expr, parse_expr(c), poisoned_expr); CONSUME_OR_RET(TOKEN_COMMA, poisoned_expr); ASSIGN_TYPEID_OR_RET(expr->castable_expr.type, parse_type(c), poisoned_expr); CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); RANGE_EXTEND_PREV(expr); return expr; } /** * ct_arg ::= VACOUNT | (VAARG | VAREF | VAEXPR | VACONST) '(' expr ')' */ static Expr *parse_ct_arg(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); Expr *expr = EXPR_NEW_TOKEN(EXPR_CT_ARG); TokenType type = expr->ct_arg_expr.type = c->tok; assert(type != TOKEN_CT_VATYPE); advance(c); if (type != TOKEN_CT_VACOUNT) { CONSUME_OR_RET(TOKEN_LPAREN, poisoned_expr); ASSIGN_EXPRID_OR_RET(expr->ct_arg_expr.arg, parse_expr(c), poisoned_expr); CONSUME_OR_RET(TOKEN_RPAREN, poisoned_expr); } RANGE_EXTEND_PREV(expr); return expr; } /** * identifier ::= CONST_IDENT | IDENT * Note: if the identifier is "return" (only possible in doc lexing "mode"), create an EXPR_RETVAL instead. */ static Expr *parse_identifier(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); if (symstr(c) == kw_return) { Expr *expr = EXPR_NEW_TOKEN(EXPR_RETVAL); advance(c); return expr; } Expr *expr = EXPR_NEW_TOKEN(EXPR_IDENTIFIER); expr->identifier_expr.ident = symstr(c); expr->identifier_expr.is_const = tok_is(c, TOKEN_CONST_IDENT); advance(c); return expr; } static Expr *parse_identifier_starting_expression(ParseContext *c, Expr *left) { assert(!left && "Unexpected left hand side"); bool had_error; Path *path; if (!parse_path_prefix(c, &path)) return poisoned_expr; switch (c->tok) { case TOKEN_IDENT: case TOKEN_CONST_IDENT: case TOKEN_AT_IDENT: { Expr *expr = parse_identifier(c, NULL); expr->identifier_expr.path = path; return expr; } case TOKEN_TYPE_IDENT: return parse_type_expression_with_path(c, path); default: PRINT_ERROR_HERE("Expected a type, function or constant."); return poisoned_expr; } } /** * force_unwrap ::= expr '!!' */ static Expr *parse_force_unwrap_expr(ParseContext *c, Expr *left) { Expr *force_unwrap_expr = expr_new_expr(EXPR_FORCE_UNWRAP, left); advance(c); force_unwrap_expr->inner_expr = left; RANGE_EXTEND_PREV(force_unwrap_expr); return force_unwrap_expr; } /** * builtin ::= '$$' IDENT * compiler_const ::= '$$' CONST_IDENT * * Note this code accepts any ident as builtin, and relies on the lexer to prevent space between tokens. */ static Expr *parse_builtin(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); Expr *expr = EXPR_NEW_TOKEN(EXPR_BUILTIN); if (!token_is_some_ident(peek(c))) { PRINT_ERROR_HERE("Unexpected '$$', did you mean to write a builtin?"); return poisoned_expr; } advance_and_verify(c, TOKEN_BUILTIN); expr->builtin_expr.ident = symstr(c); expr->span = extend_span_with_token(expr->span, c->span); if (try_consume(c, TOKEN_CONST_IDENT)) { expr->expr_kind = EXPR_COMPILER_CONST; return expr; } advance(c); return expr; } static int read_num_type(const char *string, size_t loc, size_t len) { int i = 0; loc++; if (string[loc] == '0') return -1; for (size_t z = loc; z < len; z++) { i *= 10; if (i > 1024) return i; i += string[z] - '0'; } return i; } int read_int_suffix(const char *string, int loc, int len, char c) { switch (c | 32) { case 'i': return read_num_type(string, loc, len); case 'l': if (loc != len - 1) return -1; return 64; case 'u': if (loc == len - 2 && (string[loc + 1] | 32) == 'l') return 64; return read_num_type(string, loc, len); default: return -1; } } Expr *parse_integer(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST); expr_int->resolve_status = RESOLVE_DONE; size_t len = c->data.lex_len; const char *string = symstr(c); Int128 i = { 0, 0 }; bool is_unsigned = false; int type_bits = 0; int hex_characters = 0; int oct_characters = 0; int binary_characters = 0; bool wrapped = false; uint64_t max; switch (len > 2 ? (string[1] | 32) : '0') { case 'x': is_unsigned = true; max = UINT64_MAX >> 4; for (size_t loc = 2; loc < len; loc++) { char ch = string[loc]; switch (ch | 32) { case 'u': type_bits = read_int_suffix(string, loc, len, ch); is_unsigned = true; goto EXIT; case 'l': case 'i': type_bits = read_int_suffix(string, loc, len, ch); is_unsigned = false; goto EXIT; case '_' | 32: continue; default: break; } if (i.high > max) wrapped = true; i = i128_shl64(i, 4); i = i128_add64(i, (uint64_t) char_hex_to_nibble(ch)); hex_characters++; } break; case 'o': is_unsigned = true; max = UINT64_MAX >> 3; for (size_t loc = 2; loc < len; loc++) { char ch = string[loc]; switch (ch | 32) { case 'u': type_bits = read_int_suffix(string, loc, len, ch); is_unsigned = true; goto EXIT; case 'l': case 'i': type_bits = read_int_suffix(string, loc, len, ch); is_unsigned = false; goto EXIT; case '_' | 32: continue; default: break; } if (i.high > max) wrapped = true; i = i128_shl64(i, 3); i = i128_add64(i, (uint64_t)(ch - '0')); oct_characters++; } break; case 'b': is_unsigned = true; max = UINT64_MAX >> 1; for (size_t loc = 2; loc < len; loc++) { char ch = string[loc]; switch (ch | 32) { case 'u': type_bits = read_int_suffix(string, loc, len, ch); is_unsigned = true; goto EXIT; case 'l': case 'i': type_bits = read_int_suffix(string, loc, len, ch); is_unsigned = false; goto EXIT; case '_' | 32: continue; default: break; } binary_characters++; if (i.high > max) wrapped = true; i = i128_shl64(i, 1); i = i128_add64(i, (uint64_t)(ch - '0')); } break; default: for (size_t loc = 0; loc < len; loc++) { char ch = string[loc]; switch (ch | 32) { case 'u': type_bits = read_int_suffix(string, loc, len, ch); is_unsigned = true; goto EXIT; case 'l': case 'i': type_bits = read_int_suffix(string, loc, len, ch); is_unsigned = false; goto EXIT; case '_' | 32: continue; default: break; } uint64_t old_top = i.high; i = i128_mult64(i, 10); i = i128_add64(i, (uint64_t)(ch - '0')); if (!wrapped && old_top > i.high) wrapped = true; } break; } EXIT: if (wrapped) { PRINT_ERROR_HERE("Integer size exceeded 128 bits, max 128 bits are supported."); return poisoned_expr; } expr_int->const_expr.const_kind = CONST_INTEGER; expr_int->const_expr.is_character = false; expr_int->const_expr.is_hex = hex_characters > 0; Type *type_base = NULL; if (type_bits) { if (type_bits < 0 || !is_power_of_two((uint64_t)type_bits) || type_bits > 128) { PRINT_ERROR_HERE("Integer type suffix should be i8, i16, i32, i64 or i128."); return poisoned_expr; } } else { if (hex_characters) { type_bits = 4 * hex_characters; if (type_bits > 128) { PRINT_ERROR_HERE("%d hex digits indicates a bit width over 128, which is not supported.", hex_characters); return poisoned_expr; } } if (oct_characters) { type_bits = 3 * oct_characters; if (type_bits > 128) { PRINT_ERROR_HERE("%d octal digits indicates a bit width over 128, which is not supported.", oct_characters); return poisoned_expr; } } if (binary_characters) { type_bits = binary_characters; if (type_bits > 128) { PRINT_ERROR_HERE("%d binary digits indicates a bit width over 128, which is not supported.", binary_characters); return poisoned_expr; } } if (type_bits && type_bits < 8) type_bits = 8; if (type_bits && !is_power_of_two((uint64_t)type_bits)) type_bits = (int)next_highest_power_of_2((uint32_t)type_bits); } if (type_bits) expr_int->const_expr.is_hex = false; if (type_bits) { type_base = is_unsigned ? type_int_unsigned_by_bitsize((unsigned)type_bits) : type_int_signed_by_bitsize((unsigned)type_bits); } else { int min_bits = type_size(type_cint) * 8; Int test = { .i = i }; for (int type_kind = 0; type_kind < 5; type_kind++) { TypeKind kind = (is_unsigned ? TYPE_U8 : TYPE_I8) + type_kind; int bitsize = type_kind_bitsize(kind); if (bitsize < min_bits) continue; test.type = kind; if (int_fits(test, kind)) { type_base = is_unsigned ? type_int_unsigned_by_bitsize(bitsize) : type_int_signed_by_bitsize(bitsize); break; } } if (!type_base) type_base = is_unsigned ? type_cuint : type_cint; } expr_int->const_expr.ixx = (Int) { i, type_base->type_kind }; if (!int_fits(expr_int->const_expr.ixx, type_base->type_kind)) { unsigned radix = 10; if (hex_characters) radix = 16; if (oct_characters) radix = 8; if (binary_characters) radix = 2; if (type_bits) { PRINT_ERROR_HERE("'%s' does not fit in a '%c%d' literal.", i128_to_string(i, radix, true), is_unsigned ? 'u' : 'i', type_bits); } else { PRINT_ERROR_HERE("'%s' does not fit in an %s literal.", i128_to_string(i, radix, true), is_unsigned ? "unsigned int" : "int"); } return poisoned_expr; } expr_int->type = type_base; advance(c); return expr_int; } /** * Parse hex, skipping over invalid characters. * @param result_pointer ref to place to put the data * @param data start pointer * @param end end pointer */ static void parse_hex(char *result_pointer, const char *data, const char *end) { char *data_current = result_pointer; assert(data_current); while (data < end) { int val, val2; while ((val = char_hex_to_nibble(*(data++))) < 0) if (data == end) return; while ((val2 = char_hex_to_nibble(*(data++))) < 0); *(data_current++) = (char)((val << 4) | val2); } } /** * Slow base64 -> sextet */ static char base64_to_sextet(char c) { if (c >= 'A' && c <= 'Z') return c - 'A'; if (c >= 'a' && c <= 'z') return c - 'a' + 26; if (c >= '0' && c <= '9') return c - '0' + 52; if (c == '+') return 62; if (c == '/') return 63; if (c == '=') return 0; return -1; } /** * Parse hex, skipping over invalid characters. * @param result_pointer ref to place to put the data * @param data start pointer * @param end end pointer */ static void parse_base64(char *result_pointer, char *result_pointer_end, const char *data, const char *end) { char *data_current = result_pointer; assert(data_current); while (data < end) { int val, val2, val3, val4; while ((val = base64_to_sextet(*(data++))) < 0) if (data == end) goto DONE; while ((val2 = base64_to_sextet(*(data++))) < 0); while ((val3 = base64_to_sextet(*(data++))) < 0); while ((val4 = base64_to_sextet(*(data++))) < 0); uint32_t triplet = (uint32_t)((val << 3 * 6) + (val2 << 2 * 6) + (val3 << 6) + val4); if (data_current < result_pointer_end) *(data_current++) = (char)((triplet >> 16) & 0xFF); if (data_current < result_pointer_end) *(data_current++) = (char)((triplet >> 8) & 0xFF); if (data_current < result_pointer_end) *(data_current++) = (char)(triplet & 0xFF); } DONE:; } static Expr *parse_bytes_expr(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); ArraySize len = 0; char *data = NULL; while (c->tok == TOKEN_BYTES) { ArraySize next_len = c->data.bytes_len; if (!next_len) { advance(c); continue; } ArraySize new_len = len + next_len; char *new_data = MALLOC(new_len); if (data) { memmove(new_data, data, len); } data = new_data; if (c->data.is_base64) { const char *base64data = c->data.lex_start + 4; const char *end = base64data + c->data.lex_len - 1 - 4; parse_base64(new_data + len, new_data + next_len, base64data, end); } else { const char *hexdata = c->data.lex_start + 2; const char *end = hexdata + c->data.lex_len - 1 - 2; parse_hex(new_data + len, hexdata, end); } len = new_len; advance(c); } Expr *expr_bytes = EXPR_NEW_TOKEN(EXPR_CONST); expr_bytes->const_expr.bytes.ptr = data; expr_bytes->const_expr.bytes.len = len; expr_bytes->const_expr.const_kind = CONST_BYTES; Type *type = type_get_array(type_char, len); expr_bytes->type = type; expr_bytes->resolve_status = RESOLVE_DONE; return expr_bytes; } /** * Char literals may be 1, 2, 4, 8 or 16 bytes. They are always unsigned. */ static Expr *parse_char_lit(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); Expr *expr_int = EXPR_NEW_TOKEN(EXPR_CONST); expr_int->const_expr.is_character = true; expr_int->resolve_status = RESOLVE_DONE; expr_int->const_expr.ixx.i = c->data.char_value; expr_int->const_expr.const_kind = CONST_INTEGER; switch (c->data.width) { case 1: expr_int->type = type_char; expr_int->const_expr.ixx.type = TYPE_U8; break; case 2: expr_int->type = type_ushort; expr_int->const_expr.ixx.type = TYPE_U16; break; case 3: case 4: expr_int->type = type_uint; expr_int->const_expr.ixx.type = TYPE_U32; break; case 5: case 6: case 7: case 8: expr_int->type = type_ulong; expr_int->const_expr.ixx.type = TYPE_U64; break; default: expr_int->type = type_u128; expr_int->const_expr.ixx.type = TYPE_U128; break; } advance(c); return expr_int; } /** * Parse a double from the underlying string into a constant. */ static Expr *parse_double(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); char *err; Expr *number = EXPR_NEW_TOKEN(EXPR_CONST); const char *original = symstr(c); bool is_hex = original[0] == '0' && original[1] == 'x'; // This is set to try to print in a similar manner as the input. number->const_expr.is_hex = is_hex; Float f = is_hex ? float_from_hex(original, &err) : float_from_string(original, &err); if (f.type == TYPE_POISONED) { PRINT_ERROR_HERE(err); return poisoned_expr; } number->const_expr.fxx = f; switch (number->const_expr.fxx.type) { case TYPE_F128: number->type = type_f128; break; case TYPE_F64: number->type = type_double; break; case TYPE_F32: number->type = type_float; break; case TYPE_F16: number->type = type_float16; break; case TYPE_BF16: number->type = type_bfloat; break; default: UNREACHABLE } number->const_expr.const_kind = CONST_FLOAT; number->resolve_status = RESOLVE_DONE; advance(c); return number; } /** * string_literal ::= STRING+ */ static Expr *parse_string_literal(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); Expr *expr_string = EXPR_NEW_TOKEN(EXPR_CONST); const char *str = symstr(c); size_t len = c->data.strlen; advance_and_verify(c, TOKEN_STRING); // This is wasteful for adding many tokens together // and can be optimized. while (tok_is(c, TOKEN_STRING)) { // Grab the token. size_t next_len = c->data.strlen; if (!next_len) { // Zero length so just continue. advance_and_verify(c, TOKEN_STRING); continue; } // Create new string and copy. char *buffer = malloc_string(len + next_len + 1); memcpy(buffer, str, len); memcpy(buffer + len, symstr(c), next_len); len += next_len; buffer[len] = '\0'; str = buffer; advance_and_verify(c, TOKEN_STRING); } if (len > UINT32_MAX) { PRINT_ERROR_HERE("String exceeded max size."); return poisoned_expr; } assert(str); expr_string->const_expr.bytes.ptr = str; expr_string->const_expr.bytes.len = (uint32_t)len; expr_string->type = type_string; expr_string->const_expr.const_kind = CONST_STRING; expr_string->resolve_status = RESOLVE_DONE; return expr_string; } /* * bool ::= 'true' | 'false' */ static Expr *parse_bool(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); Expr *number = EXPR_NEW_TOKEN(EXPR_CONST); number->const_expr = (ExprConst) { .b = tok_is(c, TOKEN_TRUE), .const_kind = CONST_BOOL }; number->type = type_bool; number->resolve_status = RESOLVE_DONE; advance(c); return number; } /** * Parse 'null', creating a const void* with zero address. */ static Expr *parse_null(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); Expr *number = EXPR_NEW_TOKEN(EXPR_CONST); number->const_expr.const_kind = CONST_POINTER; number->const_expr.ptr = 0; number->type = type_voidptr; number->resolve_status = RESOLVE_DONE; advance(c); return number; } /** * Expects an initializer list, then appends the type making it a compound expr rather than initializer list. */ Expr *parse_type_compound_literal_expr_after_type(ParseContext *c, TypeInfo *type_info) { Expr *expr = expr_new(EXPR_COMPOUND_LITERAL, type_info->span); expr->expr_compound_literal.type_info = type_info; EXPECT_OR_RET(TOKEN_LBRACE, poisoned_expr); ASSIGN_EXPR_OR_RET(expr->expr_compound_literal.initializer, parse_initializer_list(c, NULL), poisoned_expr); RANGE_EXTEND_PREV(expr); return expr; } /** * type_expression_with_path ::= TYPE_IDENT initializer_list? */ Expr *parse_type_expression_with_path(ParseContext *c, Path *path) { TypeInfo *type; if (path) { type = type_info_new(TYPE_INFO_IDENTIFIER, path->span); type->unresolved.path = path; type->unresolved.name = symstr(c); advance_and_verify(c, TOKEN_TYPE_IDENT); RANGE_EXTEND_PREV(type); ASSIGN_TYPE_OR_RET(type, parse_type_with_base(c, type), poisoned_expr); type->optional = try_consume(c, TOKEN_BANG); } else { ASSIGN_TYPE_OR_RET(type, parse_optional_type(c), poisoned_expr); } if (tok_is(c, TOKEN_LBRACE)) { return parse_type_compound_literal_expr_after_type(c, type); } Expr *expr = expr_new(EXPR_TYPEINFO, type->span); expr->type_expr = type; expr->type = type_typeinfo; if (type->resolve_status == RESOLVE_DONE) expr->resolve_status = RESOLVE_DONE; if (tok_is(c, TOKEN_SCOPE)) { PRINT_ERROR_HERE("A type is never followed by '::', did you mean '.'?"); return poisoned_expr; } return expr; } /** * expr_block ::= '{|' opt_stmt_list '|}' */ static Expr* parse_expr_block(ParseContext *c, Expr *left) { assert(!left && "Had left hand side"); Expr *expr = EXPR_NEW_TOKEN(EXPR_EXPR_BLOCK); advance_and_verify(c, TOKEN_LBRAPIPE); AstId *next = &expr->expr_block.first_stmt; while (!try_consume(c, TOKEN_RBRAPIPE)) { Ast *stmt = parse_stmt(c); if (!ast_ok(stmt)) return poisoned_expr; ast_append(&next, stmt); } RANGE_EXTEND_PREV(expr); return expr; } ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_BOOL] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_CHAR] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_ICHAR] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_SHORT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_USHORT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_INT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_UINT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_LONG] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_ULONG] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_INT128] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_UINT128] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_ISZ] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_USZ] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_IPTR] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_UPTR] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_FLOAT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_DOUBLE] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_BFLOAT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_FLOAT16] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_FLOAT128] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_VOID] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_TYPEID] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_ANYFAULT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_ANY] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_QUESTION] = { NULL, parse_ternary_expr, PREC_TERNARY }, [TOKEN_QUESTQUEST] = { NULL, parse_orelse, PREC_TERNARY }, [TOKEN_ELVIS] = { NULL, parse_elvis_expr, PREC_TERNARY }, [TOKEN_PLUSPLUS] = { parse_unary_expr, parse_post_unary, PREC_CALL }, [TOKEN_MINUSMINUS] = { parse_unary_expr, parse_post_unary, PREC_CALL }, [TOKEN_LPAREN] = { parse_grouping_expr, parse_call_expr, PREC_CALL }, [TOKEN_LBRAPIPE] = { parse_expr_block, NULL, PREC_NONE }, [TOKEN_BANGBANG] = { NULL, parse_force_unwrap_expr, PREC_CALL }, [TOKEN_LBRACKET] = { NULL, parse_subscript_expr, PREC_CALL }, [TOKEN_LGENPAR] = { NULL, parse_generic_expr, PREC_CALL }, [TOKEN_MINUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE }, [TOKEN_PLUS] = { parse_unary_expr, parse_binary, PREC_ADDITIVE }, [TOKEN_DIV] = { NULL, parse_binary, PREC_MULTIPLICATIVE }, [TOKEN_MOD] = { NULL, parse_binary, PREC_MULTIPLICATIVE }, [TOKEN_STAR] = { parse_unary_expr, parse_binary, PREC_MULTIPLICATIVE }, [TOKEN_DOT] = { NULL, parse_access_expr, PREC_CALL }, [TOKEN_BANG] = { parse_unary_expr, parse_rethrow_expr, PREC_CALL }, [TOKEN_BYTES] = { parse_bytes_expr, NULL, PREC_NONE }, [TOKEN_BIT_NOT] = { parse_unary_expr, NULL, PREC_UNARY }, [TOKEN_BIT_XOR] = { NULL, parse_binary, PREC_BIT }, [TOKEN_BIT_OR] = { NULL, parse_binary, PREC_BIT }, [TOKEN_AMP] = { parse_unary_expr, parse_binary, PREC_BIT }, [TOKEN_EQEQ] = { NULL, parse_binary, PREC_RELATIONAL }, [TOKEN_NOT_EQUAL] = { NULL, parse_binary, PREC_RELATIONAL }, [TOKEN_GREATER] = { NULL, parse_binary, PREC_RELATIONAL }, [TOKEN_GREATER_EQ] = { NULL, parse_binary, PREC_RELATIONAL }, [TOKEN_LESS] = { NULL, parse_binary, PREC_RELATIONAL }, [TOKEN_LESS_EQ] = { NULL, parse_binary, PREC_RELATIONAL }, [TOKEN_SHL] = { NULL, parse_binary, PREC_SHIFT }, [TOKEN_SHR] = { NULL, parse_binary, PREC_SHIFT }, [TOKEN_TRUE] = { parse_bool, NULL, PREC_NONE }, [TOKEN_FALSE] = { parse_bool, NULL, PREC_NONE }, [TOKEN_NULL] = { parse_null, NULL, PREC_NONE }, [TOKEN_INTEGER] = { parse_integer, NULL, PREC_NONE }, [TOKEN_BUILTIN] = { parse_builtin, NULL, PREC_NONE }, [TOKEN_CHAR_LITERAL] = { parse_char_lit, NULL, PREC_NONE }, [TOKEN_STRING] = { parse_string_literal, NULL, PREC_NONE }, [TOKEN_REAL] = { parse_double, NULL, PREC_NONE }, [TOKEN_OR] = { NULL, parse_binary, PREC_OR }, [TOKEN_CT_OR] = { NULL, parse_binary, PREC_OR }, [TOKEN_CT_CONCAT] = { NULL, parse_binary, PREC_MULTIPLICATIVE }, [TOKEN_AND] = { parse_unary_expr, parse_binary, PREC_AND }, [TOKEN_CT_AND] = { parse_unary_expr, parse_binary, PREC_AND }, [TOKEN_EQ] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_PLUS_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_MINUS_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_MULT_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_MOD_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_DIV_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_BIT_XOR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_BIT_AND_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_BIT_OR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_SHR_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_SHL_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [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 }, [TOKEN_CT_CONST_IDENT] = { parse_ct_ident, NULL, PREC_NONE }, [TOKEN_CT_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE }, [TOKEN_HASH_IDENT] = { parse_hash_ident, NULL, PREC_NONE }, [TOKEN_AT_IDENT] = { parse_identifier, NULL, PREC_NONE }, //[TOKEN_HASH_TYPE_IDENT] = { parse_type_identifier, NULL, PREC_NONE } [TOKEN_FN] = { parse_lambda, NULL, PREC_NONE }, [TOKEN_CT_CONCATFN] = {parse_ct_concat_append, NULL, PREC_NONE }, [TOKEN_CT_APPEND] = { parse_ct_concat_append, NULL, PREC_NONE }, [TOKEN_CT_SIZEOF] = { parse_ct_sizeof, NULL, PREC_NONE }, [TOKEN_CT_ALIGNOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_ANDFN] = {parse_ct_and_or, NULL, PREC_NONE }, [TOKEN_CT_ASSIGNABLE] = { parse_ct_castable, NULL, PREC_NONE }, [TOKEN_CT_DEFINED] = { parse_ct_defined, NULL, PREC_NONE }, [TOKEN_CT_IS_CONST] = {parse_ct_is_const, NULL, PREC_NONE }, [TOKEN_CT_EMBED] = { parse_ct_embed, NULL, PREC_NONE }, [TOKEN_CT_EVAL] = { parse_ct_eval, NULL, PREC_NONE }, [TOKEN_CT_FEATURE] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_EXTNAMEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_OFFSETOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_ORFN] = {parse_ct_and_or, NULL, PREC_NONE }, [TOKEN_CT_NAMEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_QNAMEOF] = { parse_ct_call, NULL, PREC_NONE }, [TOKEN_CT_TYPEFROM] = { parse_type_expr, NULL, PREC_NONE }, [TOKEN_CT_TYPEOF] = { parse_type_expr, NULL, PREC_NONE }, [TOKEN_CT_STRINGIFY] = { parse_ct_stringify, NULL, PREC_NONE }, [TOKEN_CT_EVALTYPE] = { parse_type_expr, NULL, PREC_NONE }, [TOKEN_LBRACE] = { parse_initializer_list, NULL, PREC_NONE }, [TOKEN_CT_VACOUNT] = { parse_ct_arg, NULL, PREC_NONE }, [TOKEN_CT_VAARG] = { parse_ct_arg, NULL, PREC_NONE }, [TOKEN_CT_VAREF] = { parse_ct_arg, NULL, PREC_NONE }, [TOKEN_CT_VATYPE] = { parse_type_expr, NULL, PREC_NONE }, [TOKEN_CT_VAEXPR] = { parse_ct_arg, NULL, PREC_NONE }, [TOKEN_CT_VACONST] = { parse_ct_arg, NULL, PREC_NONE }, };