| 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,7 +121,7 @@ MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression | |
| 121 | 121 | ||
| 122 | CurlySuffixExpression = TypeExpr option(ContainerInitExpression) | 122 | CurlySuffixExpression = TypeExpr option(ContainerInitExpression) |
| 123 | 123 | ||
| 124 | MultiplyOperator = "*" | "/" | "%" | 124 | MultiplyOperator = "*" | "/" | "%" | "**" |
| 125 | 125 | ||
| 126 | PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression | 126 | PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression |
| 127 | 127 |
src/all_types.hpp+1| ... | @@ -342,6 +342,7 @@ enum BinOpType { | ... | @@ -342,6 +342,7 @@ enum BinOpType { |
| 342 | BinOpTypeMod, | 342 | BinOpTypeMod, |
| 343 | BinOpTypeUnwrapMaybe, | 343 | BinOpTypeUnwrapMaybe, |
| 344 | BinOpTypeStrCat, | 344 | BinOpTypeStrCat, |
| 345 | BinOpTypeArrayMult, | ||
| 345 | }; | 346 | }; |
| 346 | 347 | ||
| 347 | struct AstNodeBinOpExpr { | 348 | struct AstNodeBinOpExpr { |
src/analyze.cpp+87| ... | @@ -2937,6 +2937,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) { | ... | @@ -2937,6 +2937,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) { |
| 2937 | case BinOpTypeMod: | 2937 | case BinOpTypeMod: |
| 2938 | case BinOpTypeUnwrapMaybe: | 2938 | case BinOpTypeUnwrapMaybe: |
| 2939 | case BinOpTypeStrCat: | 2939 | case BinOpTypeStrCat: |
| 2940 | case BinOpTypeArrayMult: | ||
| 2940 | zig_unreachable(); | 2941 | zig_unreachable(); |
| 2941 | } | 2942 | } |
| 2942 | zig_unreachable(); | 2943 | zig_unreachable(); |
| ... | @@ -3121,9 +3122,93 @@ static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *i | ... | @@ -3121,9 +3122,93 @@ static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *i |
| 3121 | return g->builtin_types.entry_bool; | 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 | static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | 3208 | static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 3125 | TypeTableEntry *expected_type, AstNode *node) | 3209 | TypeTableEntry *expected_type, AstNode *node) |
| 3126 | { | 3210 | { |
| 3211 | assert(node->type == NodeTypeBinOpExpr); | ||
| 3127 | BinOpType bin_op_type = node->data.bin_op_expr.bin_op; | 3212 | BinOpType bin_op_type = node->data.bin_op_expr.bin_op; |
| 3128 | switch (bin_op_type) { | 3213 | switch (bin_op_type) { |
| 3129 | case BinOpTypeAssign: | 3214 | case BinOpTypeAssign: |
| ... | @@ -3320,6 +3405,8 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, | ... | @@ -3320,6 +3405,8 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, |
| 3320 | 3405 | ||
| 3321 | return str_type; | 3406 | return str_type; |
| 3322 | } | 3407 | } |
| 3408 | case BinOpTypeArrayMult: | ||
| 3409 | return analyze_array_mult(g, import, context, expected_type, node); | ||
| 3323 | case BinOpTypeInvalid: | 3410 | case BinOpTypeInvalid: |
| 3324 | zig_unreachable(); | 3411 | zig_unreachable(); |
| 3325 | } | 3412 | } |
src/ast_render.cpp+1| ... | @@ -38,6 +38,7 @@ static const char *bin_op_str(BinOpType bin_op) { | ... | @@ -38,6 +38,7 @@ static const char *bin_op_str(BinOpType bin_op) { |
| 38 | case BinOpTypeAssignBoolOr: return "||="; | 38 | case BinOpTypeAssignBoolOr: return "||="; |
| 39 | case BinOpTypeUnwrapMaybe: return "??"; | 39 | case BinOpTypeUnwrapMaybe: return "??"; |
| 40 | case BinOpTypeStrCat: return "++"; | 40 | case BinOpTypeStrCat: return "++"; |
| 41 | case BinOpTypeArrayMult: return "**"; | ||
| 41 | } | 42 | } |
| 42 | zig_unreachable(); | 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,6 +1466,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, |
| 1466 | case BinOpTypeAssignBoolOr: | 1466 | case BinOpTypeAssignBoolOr: |
| 1467 | case BinOpTypeUnwrapMaybe: | 1467 | case BinOpTypeUnwrapMaybe: |
| 1468 | case BinOpTypeStrCat: | 1468 | case BinOpTypeStrCat: |
| 1469 | case BinOpTypeArrayMult: | ||
| 1469 | zig_unreachable(); | 1470 | zig_unreachable(); |
| 1470 | } | 1471 | } |
| 1471 | zig_unreachable(); | 1472 | zig_unreachable(); |
| ... | @@ -1772,6 +1773,7 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { | ... | @@ -1772,6 +1773,7 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { |
| 1772 | switch (node->data.bin_op_expr.bin_op) { | 1773 | switch (node->data.bin_op_expr.bin_op) { |
| 1773 | case BinOpTypeInvalid: | 1774 | case BinOpTypeInvalid: |
| 1774 | case BinOpTypeStrCat: | 1775 | case BinOpTypeStrCat: |
| 1776 | case BinOpTypeArrayMult: | ||
| 1775 | zig_unreachable(); | 1777 | zig_unreachable(); |
| 1776 | case BinOpTypeAssign: | 1778 | case BinOpTypeAssign: |
| 1777 | case BinOpTypeAssignTimes: | 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,7 +232,7 @@ int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, |
| 232 | case BinOpTypeUnwrapMaybe: | 232 | case BinOpTypeUnwrapMaybe: |
| 233 | zig_panic("TODO"); | 233 | zig_panic("TODO"); |
| 234 | case BinOpTypeStrCat: | 234 | case BinOpTypeStrCat: |
| 235 | zig_panic("TODO"); | 235 | case BinOpTypeArrayMult: |
| 236 | case BinOpTypeInvalid: | 236 | case BinOpTypeInvalid: |
| 237 | zig_unreachable(); | 237 | zig_unreachable(); |
| 238 | } | 238 | } |
src/parser.cpp+11-1| ... | @@ -1297,6 +1297,7 @@ static PrefixOp tok_to_prefix_op(Token *token) { | ... | @@ -1297,6 +1297,7 @@ static PrefixOp tok_to_prefix_op(Token *token) { |
| 1297 | case TokenIdPercentPercent: return PrefixOpUnwrapError; | 1297 | case TokenIdPercentPercent: return PrefixOpUnwrapError; |
| 1298 | case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe; | 1298 | case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe; |
| 1299 | case TokenIdBoolAnd: return PrefixOpAddressOf; | 1299 | case TokenIdBoolAnd: return PrefixOpAddressOf; |
| 1300 | case TokenIdStarStar: return PrefixOpDereference; | ||
| 1300 | default: return PrefixOpInvalid; | 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,6 +1332,14 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo |
| 1331 | parent_node->data.prefix_op_expr.primary_expr = node; | 1332 | parent_node->data.prefix_op_expr.primary_expr = node; |
| 1332 | parent_node->data.prefix_op_expr.prefix_op = PrefixOpAddressOf; | 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 | node->column += 1; | 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,6 +1364,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo |
| 1355 | static BinOpType tok_to_mult_op(Token *token) { | 1364 | static BinOpType tok_to_mult_op(Token *token) { |
| 1356 | switch (token->id) { | 1365 | switch (token->id) { |
| 1357 | case TokenIdStar: return BinOpTypeMult; | 1366 | case TokenIdStar: return BinOpTypeMult; |
| 1367 | case TokenIdStarStar: return BinOpTypeArrayMult; | ||
| 1358 | case TokenIdSlash: return BinOpTypeDiv; | 1368 | case TokenIdSlash: return BinOpTypeDiv; |
| 1359 | case TokenIdPercent: return BinOpTypeMod; | 1369 | case TokenIdPercent: return BinOpTypeMod; |
| 1360 | default: return BinOpTypeInvalid; | 1370 | default: return BinOpTypeInvalid; |
| ... | @@ -1362,7 +1372,7 @@ static BinOpType tok_to_mult_op(Token *token) { | ... | @@ -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 | static BinOpType ast_parse_mult_op(ParseContext *pc, int *token_index, bool mandatory) { | 1377 | static BinOpType ast_parse_mult_op(ParseContext *pc, int *token_index, bool mandatory) { |
| 1368 | Token *token = &pc->tokens->at(*token_index); | 1378 | Token *token = &pc->tokens->at(*token_index); |
src/tokenizer.cpp+6| ... | @@ -626,6 +626,11 @@ void tokenize(Buf *buf, Tokenization *out) { | ... | @@ -626,6 +626,11 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 626 | end_token(&t); | 626 | end_token(&t); |
| 627 | t.state = TokenizeStateStart; | 627 | t.state = TokenizeStateStart; |
| 628 | break; | 628 | break; |
| 629 | case '*': | ||
| 630 | t.cur_tok->id = TokenIdStarStar; | ||
| 631 | end_token(&t); | ||
| 632 | t.state = TokenizeStateStart; | ||
| 633 | break; | ||
| 629 | default: | 634 | default: |
| 630 | t.pos -= 1; | 635 | t.pos -= 1; |
| 631 | end_token(&t); | 636 | end_token(&t); |
| ... | @@ -1235,6 +1240,7 @@ const char * token_name(TokenId id) { | ... | @@ -1235,6 +1240,7 @@ const char * token_name(TokenId id) { |
| 1235 | case TokenIdRParen: return ")"; | 1240 | case TokenIdRParen: return ")"; |
| 1236 | case TokenIdComma: return ","; | 1241 | case TokenIdComma: return ","; |
| 1237 | case TokenIdStar: return "*"; | 1242 | case TokenIdStar: return "*"; |
| 1243 | case TokenIdStarStar: return "**"; | ||
| 1238 | case TokenIdLBrace: return "{"; | 1244 | case TokenIdLBrace: return "{"; |
| 1239 | case TokenIdRBrace: return "}"; | 1245 | case TokenIdRBrace: return "}"; |
| 1240 | case TokenIdLBracket: return "["; | 1246 | case TokenIdLBracket: return "["; |
src/tokenizer.hpp+1| ... | @@ -47,6 +47,7 @@ enum TokenId { | ... | @@ -47,6 +47,7 @@ enum TokenId { |
| 47 | TokenIdRParen, | 47 | TokenIdRParen, |
| 48 | TokenIdComma, | 48 | TokenIdComma, |
| 49 | TokenIdStar, | 49 | TokenIdStar, |
| 50 | TokenIdStarStar, | ||
| 50 | TokenIdLBrace, | 51 | TokenIdLBrace, |
| 51 | TokenIdRBrace, | 52 | TokenIdRBrace, |
| 52 | TokenIdLBracket, | 53 | TokenIdLBracket, |
test/self_hosted.zig+6| ... | @@ -1392,3 +1392,9 @@ fn test_take_address_of_parameter_noeval(f: f32) { | ... | @@ -1392,3 +1392,9 @@ fn test_take_address_of_parameter_noeval(f: f32) { |
| 1392 | const f_ptr = &f; | 1392 | const f_ptr = &f; |
| 1393 | assert(*f_ptr == 12.34); | 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 | } |