add array multiplication operator
parent
46b0b84b90
commit
d908afe105
|
@ -121,7 +121,7 @@ MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression |
|
|||
|
||||
CurlySuffixExpression = TypeExpr option(ContainerInitExpression)
|
||||
|
||||
MultiplyOperator = "*" | "/" | "%"
|
||||
MultiplyOperator = "*" | "/" | "%" | "**"
|
||||
|
||||
PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression
|
||||
|
||||
|
|
|
@ -342,6 +342,7 @@ enum BinOpType {
|
|||
BinOpTypeMod,
|
||||
BinOpTypeUnwrapMaybe,
|
||||
BinOpTypeStrCat,
|
||||
BinOpTypeArrayMult,
|
||||
};
|
||||
|
||||
struct AstNodeBinOpExpr {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 "[";
|
||||
|
|
|
@ -47,6 +47,7 @@ enum TokenId {
|
|||
TokenIdRParen,
|
||||
TokenIdComma,
|
||||
TokenIdStar,
|
||||
TokenIdStarStar,
|
||||
TokenIdLBrace,
|
||||
TokenIdRBrace,
|
||||
TokenIdLBracket,
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue