mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-13 13:53:43 +00:00
Merge pull request #6731 from odin-lang/bill/expand-values-operator
Support `**` as `expand_values` operator: `**x` == `expand_values(x)`
This commit is contained in:
@@ -3550,7 +3550,8 @@ parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
|
||||
case .Add, .Sub,
|
||||
.Not, .Xor,
|
||||
.And:
|
||||
.And,
|
||||
.Mul_Mul:
|
||||
op := advance_token(p)
|
||||
expr := parse_unary_expr(p, lhs)
|
||||
|
||||
|
||||
@@ -67,6 +67,8 @@ Token_Kind :: enum u32 {
|
||||
Cmp_And, // &&
|
||||
Cmp_Or, // ||
|
||||
|
||||
Mul_Mul, // **
|
||||
|
||||
B_Assign_Op_Begin,
|
||||
Add_Eq, // +=
|
||||
Sub_Eq, // -=
|
||||
@@ -202,6 +204,8 @@ tokens := [Token_Kind.COUNT]string {
|
||||
"&&",
|
||||
"||",
|
||||
|
||||
"**",
|
||||
|
||||
"",
|
||||
"+=",
|
||||
"-=",
|
||||
|
||||
@@ -679,6 +679,9 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
||||
if t.ch == '=' {
|
||||
advance_rune(t)
|
||||
kind = .Mul_Eq
|
||||
} else if t.ch == '*' {
|
||||
advance_rune(t)
|
||||
kind = .Mul_Mul
|
||||
}
|
||||
case '=':
|
||||
kind = .Eq
|
||||
|
||||
@@ -1173,7 +1173,7 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan
|
||||
|
||||
// masked_load (ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) -> #simd[N]T
|
||||
// masked_store(ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool)
|
||||
// masked_expand_load (ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) -> #simd[N]T
|
||||
// masked_expand_load (ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool) -> #simd[N]T
|
||||
// masked_compress_store(ptr: rawptr, values: #simd[N]T, mask: #simd[N]int_or_bool)
|
||||
|
||||
Operand ptr = {};
|
||||
|
||||
@@ -2950,6 +2950,43 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case Token_MulMul: { // 'expand_values' operator
|
||||
if (!o->type) {
|
||||
return;
|
||||
}
|
||||
|
||||
Type *type = base_type(o->type);
|
||||
if (!is_type_struct(type) && !is_type_array(type)) {
|
||||
gbString type_str = type_to_string(o->type);
|
||||
error(node, "Expected a struct or array type to 'expand_values', got '%s'", type_str);
|
||||
gb_string_free(type_str);
|
||||
return;
|
||||
}
|
||||
|
||||
Type *tuple = alloc_type_tuple();
|
||||
|
||||
if (is_type_struct(type)) {
|
||||
isize variable_count = type->Struct.fields.count;
|
||||
tuple->Tuple.variables = permanent_slice_make<Entity *>(variable_count);
|
||||
// NOTE(bill): don't copy the entities, this should be good enough
|
||||
gb_memmove_array(tuple->Tuple.variables.data, type->Struct.fields.data, variable_count);
|
||||
} else if (is_type_array(type)) {
|
||||
isize variable_count = cast(isize)type->Array.count;
|
||||
tuple->Tuple.variables = permanent_slice_make<Entity *>(variable_count);
|
||||
for (isize i = 0; i < variable_count; i++) {
|
||||
tuple->Tuple.variables[i] = alloc_entity_array_elem(nullptr, blank_token, type->Array.elem, cast(i32)i);
|
||||
}
|
||||
}
|
||||
o->type = tuple;
|
||||
o->mode = Addressing_Value;
|
||||
|
||||
if (tuple->Tuple.variables.count == 1) {
|
||||
o->type = tuple->Tuple.variables[0]->type;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!check_unary_op(c, o, op)) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
gb_internal lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise);
|
||||
gb_internal lbValue lb_build_slice_expr_value(lbProcedure *p, Ast *expr);
|
||||
gb_internal lbValue lb_expand_values(lbProcedure *p, lbValue val, Type *type);
|
||||
|
||||
gb_internal LLVMValueRef lb_const_low_bits_mask(LLVMTypeRef type, u64 bit_count) {
|
||||
GB_ASSERT(bit_count <= 64);
|
||||
@@ -4668,6 +4669,11 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
|
||||
switch (ue->op.kind) {
|
||||
case Token_And:
|
||||
return lb_build_unary_and(p, expr);
|
||||
case Token_MulMul:
|
||||
{
|
||||
lbValue val = lb_build_expr(p, ue->expr);
|
||||
return lb_expand_values(p, val, type);
|
||||
}
|
||||
default:
|
||||
{
|
||||
lbValue v = lb_build_expr(p, ue->expr);
|
||||
|
||||
@@ -1324,6 +1324,48 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_internal lbValue lb_expand_values(lbProcedure *p, lbValue val, Type *type) {
|
||||
Type *t = base_type(val.type);
|
||||
|
||||
if (!is_type_tuple(type)) {
|
||||
if (t->kind == Type_Struct) {
|
||||
GB_ASSERT(t->Struct.fields.count == 1);
|
||||
return lb_emit_struct_ev(p, val, 0);
|
||||
} else if (t->kind == Type_Array) {
|
||||
GB_ASSERT(t->Array.count == 1);
|
||||
return lb_emit_struct_ev(p, val, 0);
|
||||
} else {
|
||||
GB_PANIC("Unknown type of expand_values");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GB_ASSERT(is_type_tuple(type));
|
||||
// NOTE(bill): Doesn't need to be zero because it will be initialized in the loops
|
||||
lbValue tuple = lb_addr_get_ptr(p, lb_add_local_generated(p, type, false));
|
||||
if (t->kind == Type_Struct) {
|
||||
for_array(src_index, t->Struct.fields) {
|
||||
Entity *field = t->Struct.fields[src_index];
|
||||
i32 field_index = field->Variable.field_index;
|
||||
lbValue f = lb_emit_struct_ev(p, val, field_index);
|
||||
lbValue ep = lb_emit_struct_ep(p, tuple, cast(i32)src_index);
|
||||
lb_emit_store(p, ep, f);
|
||||
}
|
||||
} else if (is_type_array_like(t)) {
|
||||
// TODO(bill): Clean-up this code
|
||||
lbValue ap = lb_address_from_load_or_generate_local(p, val);
|
||||
i32 n = cast(i32)get_array_type_count(t);
|
||||
for (i32 i = 0; i < n; i++) {
|
||||
lbValue f = lb_emit_load(p, lb_emit_array_epi(p, ap, i));
|
||||
lbValue ep = lb_emit_struct_ep(p, tuple, i);
|
||||
lb_emit_store(p, ep, f);
|
||||
}
|
||||
} else {
|
||||
GB_PANIC("Unknown type of expand_values");
|
||||
}
|
||||
return lb_emit_load(p, tuple);
|
||||
}
|
||||
|
||||
gb_internal LLVMValueRef llvm_splat_int(i64 count, LLVMTypeRef type, i64 value, bool is_signed=false) {
|
||||
LLVMValueRef v = LLVMConstInt(type, value, is_signed);
|
||||
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, count);
|
||||
@@ -3054,45 +3096,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
|
||||
case BuiltinProc_expand_values: {
|
||||
lbValue val = lb_build_expr(p, ce->args[0]);
|
||||
Type *t = base_type(val.type);
|
||||
|
||||
if (!is_type_tuple(tv.type)) {
|
||||
if (t->kind == Type_Struct) {
|
||||
GB_ASSERT(t->Struct.fields.count == 1);
|
||||
return lb_emit_struct_ev(p, val, 0);
|
||||
} else if (t->kind == Type_Array) {
|
||||
GB_ASSERT(t->Array.count == 1);
|
||||
return lb_emit_struct_ev(p, val, 0);
|
||||
} else {
|
||||
GB_PANIC("Unknown type of expand_values");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GB_ASSERT(is_type_tuple(tv.type));
|
||||
// NOTE(bill): Doesn't need to be zero because it will be initialized in the loops
|
||||
lbValue tuple = lb_addr_get_ptr(p, lb_add_local_generated(p, tv.type, false));
|
||||
if (t->kind == Type_Struct) {
|
||||
for_array(src_index, t->Struct.fields) {
|
||||
Entity *field = t->Struct.fields[src_index];
|
||||
i32 field_index = field->Variable.field_index;
|
||||
lbValue f = lb_emit_struct_ev(p, val, field_index);
|
||||
lbValue ep = lb_emit_struct_ep(p, tuple, cast(i32)src_index);
|
||||
lb_emit_store(p, ep, f);
|
||||
}
|
||||
} else if (is_type_array_like(t)) {
|
||||
// TODO(bill): Clean-up this code
|
||||
lbValue ap = lb_address_from_load_or_generate_local(p, val);
|
||||
i32 n = cast(i32)get_array_type_count(t);
|
||||
for (i32 i = 0; i < n; i++) {
|
||||
lbValue f = lb_emit_load(p, lb_emit_array_epi(p, ap, i));
|
||||
lbValue ep = lb_emit_struct_ep(p, tuple, i);
|
||||
lb_emit_store(p, ep, f);
|
||||
}
|
||||
} else {
|
||||
GB_PANIC("Unknown type of expand_values");
|
||||
}
|
||||
return lb_emit_load(p, tuple);
|
||||
return lb_expand_values(p, val, tv.type);
|
||||
}
|
||||
|
||||
case BuiltinProc_compress_values: {
|
||||
|
||||
@@ -3474,6 +3474,7 @@ gb_internal Ast *parse_unary_expr(AstFile *f, bool lhs) {
|
||||
case Token_Xor:
|
||||
case Token_And:
|
||||
case Token_Not:
|
||||
case Token_MulMul: // 'expand_values' operator
|
||||
case Token_Mul: // Used for error handling when people do C-like things
|
||||
{
|
||||
Token token = advance_token(f);
|
||||
|
||||
@@ -35,6 +35,7 @@ TOKEN_KIND(Token__OperatorBegin, ""), \
|
||||
TOKEN_KIND(Token_Shr, ">>"), \
|
||||
TOKEN_KIND(Token_CmpAnd, "&&"), \
|
||||
TOKEN_KIND(Token_CmpOr, "||"), \
|
||||
TOKEN_KIND(Token_MulMul, "**"), \
|
||||
\
|
||||
TOKEN_KIND(Token__AssignOpBegin, ""), \
|
||||
TOKEN_KIND(Token_AddEq, "+="), \
|
||||
@@ -879,6 +880,9 @@ gb_internal void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
|
||||
if (t->curr_rune == '=') {
|
||||
advance_to_next_rune(t);
|
||||
token->kind = Token_MulEq;
|
||||
} else if (t->curr_rune == '*') {
|
||||
advance_to_next_rune(t);
|
||||
token->kind = Token_MulMul;
|
||||
}
|
||||
break;
|
||||
case '=':
|
||||
|
||||
Reference in New Issue
Block a user