| author | |
| committer | |
| log | d908afe1050caca4dd33333adcd4844738c10e56 |
| tree | 1813c1f48e3c8ea5bac47fb0cbb373f1c95993ff |
| parent | 46b0b84b90119a1eb70a30f78baa8239cafb2524 |
10 files changed, 117 insertions(+), 3 deletions(-)
doc/langref.md+1-1| ... | ... | @@ -121,7 +121,7 @@ MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression | |
| 121 | 121 | |
| 122 | 122 | CurlySuffixExpression = TypeExpr option(ContainerInitExpression) |
| 123 | 123 | |
| 124 | MultiplyOperator = "*" | "/" | "%" | |
| 124 | MultiplyOperator = "*" | "/" | "%" | "**" | |
| 125 | 125 | |
| 126 | 126 | PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression |
| 127 | 127 |
src/all_types.hpp+1| ... | ... | @@ -342,6 +342,7 @@ enum BinOpType { |
| 342 | 342 | BinOpTypeMod, |
| 343 | 343 | BinOpTypeUnwrapMaybe, |
| 344 | 344 | BinOpTypeStrCat, |
| 345 | BinOpTypeArrayMult, | |
| 345 | 346 | }; |
| 346 | 347 | |
| 347 | 348 | struct AstNodeBinOpExpr { |
src/analyze.cpp+87| ... | ... | @@ -2937,6 +2937,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) { |
| 2937 | 2937 | case BinOpTypeMod: |
| 2938 | 2938 | case BinOpTypeUnwrapMaybe: |
| 2939 | 2939 | case BinOpTypeStrCat: |
| 2940 | case BinOpTypeArrayMult: | |
| 2940 | 2941 | zig_unreachable(); |
| 2941 | 2942 | } |
| 2942 | 2943 | zig_unreachable(); |
| ... | ... | @@ -3121,9 +3122,93 @@ static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *i |
| 3121 | 3122 | return g->builtin_types.entry_bool; |
| 3122 | 3123 | } |
| 3123 | 3124 | |
| 3125 | static TypeTableEntry *analyze_array_mult(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 3126 | TypeTableEntry *expected_type, AstNode *node) | |
| 3127 | { | |
| 3128 | assert(node->type == NodeTypeBinOpExpr); | |
| 3129 | assert(node->data.bin_op_expr.bin_op == BinOpTypeArrayMult); | |
| 3130 | ||
| 3131 | AstNode **op1 = node->data.bin_op_expr.op1->parent_field; | |
| 3132 | AstNode **op2 = node->data.bin_op_expr.op2->parent_field; | |
| 3133 | ||
| 3134 | TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1); | |
| 3135 | TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2); | |
| 3136 | ||
| 3137 | if (op1_type->id == TypeTableEntryIdInvalid || | |
| 3138 | op2_type->id == TypeTableEntryIdInvalid) | |
| 3139 | { | |
| 3140 | return g->builtin_types.entry_invalid; | |
| 3141 | } | |
| 3142 | ||
| 3143 | ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val; | |
| 3144 | ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val; | |
| 3145 | ||
| 3146 | AstNode *bad_node; | |
| 3147 | if (!op1_val->ok) { | |
| 3148 | bad_node = *op1; | |
| 3149 | } else if (!op2_val->ok) { | |
| 3150 | bad_node = *op2; | |
| 3151 | } else { | |
| 3152 | bad_node = nullptr; | |
| 3153 | } | |
| 3154 | if (bad_node) { | |
| 3155 | add_node_error(g, bad_node, buf_sprintf("array multiplication requires constant expression")); | |
| 3156 | return g->builtin_types.entry_invalid; | |
| 3157 | } | |
| 3158 | ||
| 3159 | if (op1_type->id != TypeTableEntryIdArray) { | |
| 3160 | add_node_error(g, *op1, | |
| 3161 | buf_sprintf("expected array type, got '%s'", buf_ptr(&op1_type->name))); | |
| 3162 | return g->builtin_types.entry_invalid; | |
| 3163 | } | |
| 3164 | ||
| 3165 | if (op2_type->id != TypeTableEntryIdNumLitInt && | |
| 3166 | op2_type->id != TypeTableEntryIdInt) | |
| 3167 | { | |
| 3168 | add_node_error(g, *op2, buf_sprintf("expected integer type, got '%s'", buf_ptr(&op2_type->name))); | |
| 3169 | return g->builtin_types.entry_invalid; | |
| 3170 | } | |
| 3171 | ||
| 3172 | if (op2_val->data.x_bignum.is_negative) { | |
| 3173 | add_node_error(g, *op2, buf_sprintf("expected positive number")); | |
| 3174 | return g->builtin_types.entry_invalid; | |
| 3175 | } | |
| 3176 | ||
| 3177 | ConstExprValue *const_val = &get_resolved_expr(node)->const_val; | |
| 3178 | const_val->ok = true; | |
| 3179 | const_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var; | |
| 3180 | ||
| 3181 | TypeTableEntry *child_type = op1_type->data.array.child_type; | |
| 3182 | BigNum old_array_len; | |
| 3183 | bignum_init_unsigned(&old_array_len, op1_type->data.array.len); | |
| 3184 | ||
| 3185 | BigNum new_array_len; | |
| 3186 | if (bignum_mul(&new_array_len, &old_array_len, &op2_val->data.x_bignum)) { | |
| 3187 | add_node_error(g, node, buf_sprintf("operation results in overflow")); | |
| 3188 | return g->builtin_types.entry_invalid; | |
| 3189 | } | |
| 3190 | ||
| 3191 | uint64_t old_array_len_bare = op1_type->data.array.len; | |
| 3192 | uint64_t operand_amt = op2_val->data.x_bignum.data.x_uint; | |
| 3193 | ||
| 3194 | uint64_t new_array_len_bare = new_array_len.data.x_uint; | |
| 3195 | const_val->data.x_array.fields = allocate<ConstExprValue*>(new_array_len_bare); | |
| 3196 | ||
| 3197 | uint64_t i = 0; | |
| 3198 | for (uint64_t x = 0; x < operand_amt; x += 1) { | |
| 3199 | for (uint64_t y = 0; y < old_array_len_bare; y += 1) { | |
| 3200 | const_val->data.x_array.fields[i] = op1_val->data.x_array.fields[y]; | |
| 3201 | i += 1; | |
| 3202 | } | |
| 3203 | } | |
| 3204 | ||
| 3205 | return get_array_type(g, child_type, new_array_len_bare); | |
| 3206 | } | |
| 3207 | ||
| 3124 | 3208 | static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 3125 | 3209 | TypeTableEntry *expected_type, AstNode *node) |
| 3126 | 3210 | { |
| 3211 | assert(node->type == NodeTypeBinOpExpr); | |
| 3127 | 3212 | BinOpType bin_op_type = node->data.bin_op_expr.bin_op; |
| 3128 | 3213 | switch (bin_op_type) { |
| 3129 | 3214 | case BinOpTypeAssign: |
| ... | ... | @@ -3320,6 +3405,8 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, |
| 3320 | 3405 | |
| 3321 | 3406 | return str_type; |
| 3322 | 3407 | } |
| 3408 | case BinOpTypeArrayMult: | |
| 3409 | return analyze_array_mult(g, import, context, expected_type, node); | |
| 3323 | 3410 | case BinOpTypeInvalid: |
| 3324 | 3411 | zig_unreachable(); |
| 3325 | 3412 | } |
src/ast_render.cpp+1| ... | ... | @@ -38,6 +38,7 @@ static const char *bin_op_str(BinOpType bin_op) { |
| 38 | 38 | case BinOpTypeAssignBoolOr: return "||="; |
| 39 | 39 | case BinOpTypeUnwrapMaybe: return "??"; |
| 40 | 40 | case BinOpTypeStrCat: return "++"; |
| 41 | case BinOpTypeArrayMult: return "**"; | |
| 41 | 42 | } |
| 42 | 43 | zig_unreachable(); |
| 43 | 44 | } |
src/codegen.cpp+2| ... | ... | @@ -1466,6 +1466,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, |
| 1466 | 1466 | case BinOpTypeAssignBoolOr: |
| 1467 | 1467 | case BinOpTypeUnwrapMaybe: |
| 1468 | 1468 | case BinOpTypeStrCat: |
| 1469 | case BinOpTypeArrayMult: | |
| 1469 | 1470 | zig_unreachable(); |
| 1470 | 1471 | } |
| 1471 | 1472 | zig_unreachable(); |
| ... | ... | @@ -1772,6 +1773,7 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { |
| 1772 | 1773 | switch (node->data.bin_op_expr.bin_op) { |
| 1773 | 1774 | case BinOpTypeInvalid: |
| 1774 | 1775 | case BinOpTypeStrCat: |
| 1776 | case BinOpTypeArrayMult: | |
| 1775 | 1777 | zig_unreachable(); |
| 1776 | 1778 | case BinOpTypeAssign: |
| 1777 | 1779 | case BinOpTypeAssignTimes: |
src/eval.cpp+1-1| ... | ... | @@ -232,7 +232,7 @@ int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, |
| 232 | 232 | case BinOpTypeUnwrapMaybe: |
| 233 | 233 | zig_panic("TODO"); |
| 234 | 234 | case BinOpTypeStrCat: |
| 235 | zig_panic("TODO"); | |
| 235 | case BinOpTypeArrayMult: | |
| 236 | 236 | case BinOpTypeInvalid: |
| 237 | 237 | zig_unreachable(); |
| 238 | 238 | } |
src/parser.cpp+11-1| ... | ... | @@ -1297,6 +1297,7 @@ static PrefixOp tok_to_prefix_op(Token *token) { |
| 1297 | 1297 | case TokenIdPercentPercent: return PrefixOpUnwrapError; |
| 1298 | 1298 | case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe; |
| 1299 | 1299 | case TokenIdBoolAnd: return PrefixOpAddressOf; |
| 1300 | case TokenIdStarStar: return PrefixOpDereference; | |
| 1300 | 1301 | default: return PrefixOpInvalid; |
| 1301 | 1302 | } |
| 1302 | 1303 | } |
| ... | ... | @@ -1331,6 +1332,14 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo |
| 1331 | 1332 | parent_node->data.prefix_op_expr.primary_expr = node; |
| 1332 | 1333 | parent_node->data.prefix_op_expr.prefix_op = PrefixOpAddressOf; |
| 1333 | 1334 | |
| 1335 | node->column += 1; | |
| 1336 | } else if (token->id == TokenIdStarStar) { | |
| 1337 | // pretend that we got 2 star tokens | |
| 1338 | ||
| 1339 | parent_node = ast_create_node(pc, NodeTypePrefixOpExpr, token); | |
| 1340 | parent_node->data.prefix_op_expr.primary_expr = node; | |
| 1341 | parent_node->data.prefix_op_expr.prefix_op = PrefixOpDereference; | |
| 1342 | ||
| 1334 | 1343 | node->column += 1; |
| 1335 | 1344 | } |
| 1336 | 1345 | |
| ... | ... | @@ -1355,6 +1364,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo |
| 1355 | 1364 | static BinOpType tok_to_mult_op(Token *token) { |
| 1356 | 1365 | switch (token->id) { |
| 1357 | 1366 | case TokenIdStar: return BinOpTypeMult; |
| 1367 | case TokenIdStarStar: return BinOpTypeArrayMult; | |
| 1358 | 1368 | case TokenIdSlash: return BinOpTypeDiv; |
| 1359 | 1369 | case TokenIdPercent: return BinOpTypeMod; |
| 1360 | 1370 | default: return BinOpTypeInvalid; |
| ... | ... | @@ -1362,7 +1372,7 @@ static BinOpType tok_to_mult_op(Token *token) { |
| 1362 | 1372 | } |
| 1363 | 1373 | |
| 1364 | 1374 | /* |
| 1365 | MultiplyOperator : token(Star) | token(Slash) | token(Percent) | |
| 1375 | MultiplyOperator = "*" | "/" | "%" | "**" | |
| 1366 | 1376 | */ |
| 1367 | 1377 | static BinOpType ast_parse_mult_op(ParseContext *pc, int *token_index, bool mandatory) { |
| 1368 | 1378 | Token *token = &pc->tokens->at(*token_index); |
src/tokenizer.cpp+6| ... | ... | @@ -626,6 +626,11 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 626 | 626 | end_token(&t); |
| 627 | 627 | t.state = TokenizeStateStart; |
| 628 | 628 | break; |
| 629 | case '*': | |
| 630 | t.cur_tok->id = TokenIdStarStar; | |
| 631 | end_token(&t); | |
| 632 | t.state = TokenizeStateStart; | |
| 633 | break; | |
| 629 | 634 | default: |
| 630 | 635 | t.pos -= 1; |
| 631 | 636 | end_token(&t); |
| ... | ... | @@ -1235,6 +1240,7 @@ const char * token_name(TokenId id) { |
| 1235 | 1240 | case TokenIdRParen: return ")"; |
| 1236 | 1241 | case TokenIdComma: return ","; |
| 1237 | 1242 | case TokenIdStar: return "*"; |
| 1243 | case TokenIdStarStar: return "**"; | |
| 1238 | 1244 | case TokenIdLBrace: return "{"; |
| 1239 | 1245 | case TokenIdRBrace: return "}"; |
| 1240 | 1246 | case TokenIdLBracket: return "["; |
src/tokenizer.hpp+1| ... | ... | @@ -47,6 +47,7 @@ enum TokenId { |
| 47 | 47 | TokenIdRParen, |
| 48 | 48 | TokenIdComma, |
| 49 | 49 | TokenIdStar, |
| 50 | TokenIdStarStar, | |
| 50 | 51 | TokenIdLBrace, |
| 51 | 52 | TokenIdRBrace, |
| 52 | 53 | TokenIdLBracket, |
test/self_hosted.zig+6| ... | ... | @@ -1392,3 +1392,9 @@ fn test_take_address_of_parameter_noeval(f: f32) { |
| 1392 | 1392 | const f_ptr = &f; |
| 1393 | 1393 | assert(*f_ptr == 12.34); |
| 1394 | 1394 | } |
| 1395 | ||
| 1396 | ||
| 1397 | #attribute("test") | |
| 1398 | fn array_mult_operator() { | |
| 1399 | assert(str.eql("ab" ** 5, "ababababab")); | |
| 1400 | } |