authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-04-28 18:03:44-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-04-28 18:03:44-07:00
logd908afe1050caca4dd33333adcd4844738c10e56
tree1813c1f48e3c8ea5bac47fb0cbb373f1c95993ff
parent46b0b84b90119a1eb70a30f78baa8239cafb2524

add array multiplication operator


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 |
121121
122CurlySuffixExpression = TypeExpr option(ContainerInitExpression)122CurlySuffixExpression = TypeExpr option(ContainerInitExpression)
123123
124MultiplyOperator = "*" | "/" | "%"124MultiplyOperator = "*" | "/" | "%" | "**"
125125
126PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression126PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression
127127
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};
346347
347struct AstNodeBinOpExpr {348struct 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}
31233124
3125static 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
3124static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,3208static 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,
33203405
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;
13331334
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 }
13361345
...@@ -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
1355static BinOpType tok_to_mult_op(Token *token) {1364static 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}
13631373
1364/*1374/*
1365MultiplyOperator : token(Star) | token(Slash) | token(Percent)1375MultiplyOperator = "*" | "/" | "%" | "**"
1366*/1376*/
1367static BinOpType ast_parse_mult_op(ParseContext *pc, int *token_index, bool mandatory) {1377static 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")
1398fn array_mult_operator() {
1399 assert(str.eql("ab" ** 5, "ababababab"));
1400}