Commit d908afe105

Andrew Kelley <superjoe30@gmail.com>
2016-04-29 03:03:44
add array multiplication operator
1 parent 46b0b84
doc/langref.md
@@ -121,7 +121,7 @@ MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression |
 
 CurlySuffixExpression = TypeExpr option(ContainerInitExpression)
 
-MultiplyOperator = "*" | "/" | "%"
+MultiplyOperator = "*" | "/" | "%" | "**"
 
 PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression
 
src/all_types.hpp
@@ -342,6 +342,7 @@ enum BinOpType {
     BinOpTypeMod,
     BinOpTypeUnwrapMaybe,
     BinOpTypeStrCat,
+    BinOpTypeArrayMult,
 };
 
 struct AstNodeBinOpExpr {
src/analyze.cpp
@@ -2937,6 +2937,7 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) {
         case BinOpTypeMod:
         case BinOpTypeUnwrapMaybe:
         case BinOpTypeStrCat:
+        case BinOpTypeArrayMult:
             zig_unreachable();
     }
     zig_unreachable();
@@ -3121,9 +3122,93 @@ static TypeTableEntry *analyze_logic_bin_op_expr(CodeGen *g, ImportTableEntry *i
     return g->builtin_types.entry_bool;
 }
 
+static TypeTableEntry *analyze_array_mult(CodeGen *g, ImportTableEntry *import, BlockContext *context,
+        TypeTableEntry *expected_type, AstNode *node)
+{
+    assert(node->type == NodeTypeBinOpExpr);
+    assert(node->data.bin_op_expr.bin_op == BinOpTypeArrayMult);
+
+    AstNode **op1 = node->data.bin_op_expr.op1->parent_field;
+    AstNode **op2 = node->data.bin_op_expr.op2->parent_field;
+
+    TypeTableEntry *op1_type = analyze_expression(g, import, context, nullptr, *op1);
+    TypeTableEntry *op2_type = analyze_expression(g, import, context, nullptr, *op2);
+
+    if (op1_type->id == TypeTableEntryIdInvalid ||
+        op2_type->id == TypeTableEntryIdInvalid)
+    {
+        return g->builtin_types.entry_invalid;
+    }
+
+    ConstExprValue *op1_val = &get_resolved_expr(*op1)->const_val;
+    ConstExprValue *op2_val = &get_resolved_expr(*op2)->const_val;
+
+    AstNode *bad_node;
+    if (!op1_val->ok) {
+        bad_node = *op1;
+    } else if (!op2_val->ok) {
+        bad_node = *op2;
+    } else {
+        bad_node = nullptr;
+    }
+    if (bad_node) {
+        add_node_error(g, bad_node, buf_sprintf("array multiplication requires constant expression"));
+        return g->builtin_types.entry_invalid;
+    }
+
+    if (op1_type->id != TypeTableEntryIdArray) {
+        add_node_error(g, *op1,
+            buf_sprintf("expected array type, got '%s'", buf_ptr(&op1_type->name)));
+        return g->builtin_types.entry_invalid;
+    }
+
+    if (op2_type->id != TypeTableEntryIdNumLitInt &&
+        op2_type->id != TypeTableEntryIdInt)
+    {
+        add_node_error(g, *op2, buf_sprintf("expected integer type, got '%s'", buf_ptr(&op2_type->name)));
+        return g->builtin_types.entry_invalid;
+    }
+
+    if (op2_val->data.x_bignum.is_negative) {
+        add_node_error(g, *op2, buf_sprintf("expected positive number"));
+        return g->builtin_types.entry_invalid;
+    }
+
+    ConstExprValue *const_val = &get_resolved_expr(node)->const_val;
+    const_val->ok = true;
+    const_val->depends_on_compile_var = op1_val->depends_on_compile_var || op2_val->depends_on_compile_var;
+
+    TypeTableEntry *child_type = op1_type->data.array.child_type;
+    BigNum old_array_len;
+    bignum_init_unsigned(&old_array_len, op1_type->data.array.len);
+
+    BigNum new_array_len;
+    if (bignum_mul(&new_array_len, &old_array_len, &op2_val->data.x_bignum)) {
+        add_node_error(g, node, buf_sprintf("operation results in overflow"));
+        return g->builtin_types.entry_invalid;
+    }
+
+    uint64_t old_array_len_bare = op1_type->data.array.len;
+    uint64_t operand_amt = op2_val->data.x_bignum.data.x_uint;
+
+    uint64_t new_array_len_bare = new_array_len.data.x_uint;
+    const_val->data.x_array.fields = allocate<ConstExprValue*>(new_array_len_bare);
+
+    uint64_t i = 0;
+    for (uint64_t x = 0; x < operand_amt; x += 1) {
+        for (uint64_t y = 0; y < old_array_len_bare; y += 1) {
+            const_val->data.x_array.fields[i] = op1_val->data.x_array.fields[y];
+            i += 1;
+        }
+    }
+
+    return get_array_type(g, child_type, new_array_len_bare);
+}
+
 static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
+    assert(node->type == NodeTypeBinOpExpr);
     BinOpType bin_op_type = node->data.bin_op_expr.bin_op;
     switch (bin_op_type) {
         case BinOpTypeAssign:
@@ -3320,6 +3405,8 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import,
 
                 return str_type;
             }
+        case BinOpTypeArrayMult:
+            return analyze_array_mult(g, import, context, expected_type, node);
         case BinOpTypeInvalid:
             zig_unreachable();
     }
src/ast_render.cpp
@@ -38,6 +38,7 @@ static const char *bin_op_str(BinOpType bin_op) {
         case BinOpTypeAssignBoolOr:        return "||=";
         case BinOpTypeUnwrapMaybe:         return "??";
         case BinOpTypeStrCat:              return "++";
+        case BinOpTypeArrayMult:           return "**";
     }
     zig_unreachable();
 }
src/codegen.cpp
@@ -1466,6 +1466,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
         case BinOpTypeAssignBoolOr:
         case BinOpTypeUnwrapMaybe:
         case BinOpTypeStrCat:
+        case BinOpTypeArrayMult:
             zig_unreachable();
     }
     zig_unreachable();
@@ -1772,6 +1773,7 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) {
     switch (node->data.bin_op_expr.bin_op) {
         case BinOpTypeInvalid:
         case BinOpTypeStrCat:
+        case BinOpTypeArrayMult:
             zig_unreachable();
         case BinOpTypeAssign:
         case BinOpTypeAssignTimes:
src/eval.cpp
@@ -232,7 +232,7 @@ int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type,
         case BinOpTypeUnwrapMaybe:
             zig_panic("TODO");
         case BinOpTypeStrCat:
-            zig_panic("TODO");
+        case BinOpTypeArrayMult:
         case BinOpTypeInvalid:
             zig_unreachable();
     }
src/parser.cpp
@@ -1297,6 +1297,7 @@ static PrefixOp tok_to_prefix_op(Token *token) {
         case TokenIdPercentPercent: return PrefixOpUnwrapError;
         case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe;
         case TokenIdBoolAnd: return PrefixOpAddressOf;
+        case TokenIdStarStar: return PrefixOpDereference;
         default: return PrefixOpInvalid;
     }
 }
@@ -1331,6 +1332,14 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo
         parent_node->data.prefix_op_expr.primary_expr = node;
         parent_node->data.prefix_op_expr.prefix_op = PrefixOpAddressOf;
 
+        node->column += 1;
+    } else if (token->id == TokenIdStarStar) {
+        // pretend that we got 2 star tokens
+
+        parent_node = ast_create_node(pc, NodeTypePrefixOpExpr, token);
+        parent_node->data.prefix_op_expr.primary_expr = node;
+        parent_node->data.prefix_op_expr.prefix_op = PrefixOpDereference;
+
         node->column += 1;
     }
 
@@ -1355,6 +1364,7 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo
 static BinOpType tok_to_mult_op(Token *token) {
     switch (token->id) {
         case TokenIdStar: return BinOpTypeMult;
+        case TokenIdStarStar: return BinOpTypeArrayMult;
         case TokenIdSlash: return BinOpTypeDiv;
         case TokenIdPercent: return BinOpTypeMod;
         default: return BinOpTypeInvalid;
@@ -1362,7 +1372,7 @@ static BinOpType tok_to_mult_op(Token *token) {
 }
 
 /*
-MultiplyOperator : token(Star) | token(Slash) | token(Percent)
+MultiplyOperator = "*" | "/" | "%" | "**"
 */
 static BinOpType ast_parse_mult_op(ParseContext *pc, int *token_index, bool mandatory) {
     Token *token = &pc->tokens->at(*token_index);
src/tokenizer.cpp
@@ -626,6 +626,11 @@ void tokenize(Buf *buf, Tokenization *out) {
                         end_token(&t);
                         t.state = TokenizeStateStart;
                         break;
+                    case '*':
+                        t.cur_tok->id = TokenIdStarStar;
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
                     default:
                         t.pos -= 1;
                         end_token(&t);
@@ -1235,6 +1240,7 @@ const char * token_name(TokenId id) {
         case TokenIdRParen: return ")";
         case TokenIdComma: return ",";
         case TokenIdStar: return "*";
+        case TokenIdStarStar: return "**";
         case TokenIdLBrace: return "{";
         case TokenIdRBrace: return "}";
         case TokenIdLBracket: return "[";
src/tokenizer.hpp
@@ -47,6 +47,7 @@ enum TokenId {
     TokenIdRParen,
     TokenIdComma,
     TokenIdStar,
+    TokenIdStarStar,
     TokenIdLBrace,
     TokenIdRBrace,
     TokenIdLBracket,
test/self_hosted.zig
@@ -1392,3 +1392,9 @@ fn test_take_address_of_parameter_noeval(f: f32) {
     const f_ptr = &f;
     assert(*f_ptr == 12.34);
 }
+
+
+#attribute("test")
+fn array_mult_operator() {
+    assert(str.eql("ab" ** 5, "ababababab"));
+}