| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "gwion_util.h" | ||
| 2 | #include "gwion_ast.h" | ||
| 3 | #include "gwion_env.h" | ||
| 4 | #include "vm.h" | ||
| 5 | #include "instr.h" | ||
| 6 | #include "emit.h" | ||
| 7 | #include "gwion.h" | ||
| 8 | #include "object.h" | ||
| 9 | #include "operator.h" | ||
| 10 | #include "import.h" | ||
| 11 | #include "gwi.h" | ||
| 12 | #include "specialid.h" | ||
| 13 | #include "gack.h" | ||
| 14 | #include "traverse.h" | ||
| 15 | |||
| 16 | ✗ | static GACK(gack_none) { INTERP_PRINTF("None") } | |
| 17 | |||
| 18 | 1 | static OP_CHECK(opck_none) { | |
| 19 | 1 | Exp_Binary *bin = (Exp_Binary *)data; | |
| 20 |
1/2✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
|
1 | CHECK_NN(opck_rassign(env, data)); |
| 21 | ✗ | exp_setvar(bin->rhs, 1); | |
| 22 | ✗ | return bin->rhs->type; | |
| 23 | } | ||
| 24 | |||
| 25 | ✗ | static OP_EMIT(opem_none) { | |
| 26 | ✗ | emit_regmove(emit, -SZ_INT); | |
| 27 | ✗ | return GW_OK; | |
| 28 | } | ||
| 29 | |||
| 30 | 10 | static OP_EMIT(opem_union_dot) { | |
| 31 | 10 | const Exp_Dot *member = (Exp_Dot *)data; | |
| 32 | 10 | const Map map = &member->base->type->nspc->info->value->map; | |
| 33 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
|
10 | CHECK_BB(emit_exp(emit, member->base)); |
| 34 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 9 times.
|
10 | if (is_func(emit->gwion, exp_self(member)->type)) { // is_callable? can only be a func |
| 35 | 1 | emit_pushimm(emit, (m_uint)exp_self(member)->type->info->func->code); | |
| 36 | 1 | return GW_OK; | |
| 37 | } | ||
| 38 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
|
9 | if (!strcmp(s_name(member->xid), "index")) { |
| 39 | 1 | emit_add_instr(emit, DotMember); | |
| 40 | 1 | return GW_OK; | |
| 41 | } | ||
| 42 |
1/2✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
|
23 | for (m_uint i = 0; i < map_size(map); ++i) { |
| 43 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 15 times.
|
23 | if (VKEY(map, i) == (m_uint)member->xid) { |
| 44 | 8 | const Value v = (Value)VVAL(map, i); | |
| 45 | 8 | const uint emit_addr = exp_getvar(exp_self(member)); | |
| 46 | 8 | emit_unionmember(emit, i, v->type->size, emit_addr); | |
| 47 | 8 | return GW_OK; | |
| 48 | } | ||
| 49 | } | ||
| 50 | ✗ | return GW_ERROR; | |
| 51 | } | ||
| 52 | |||
| 53 | 3 | static DTOR(UnionDtor) { | |
| 54 | 3 | const m_uint idx = *(m_uint *)o->data; | |
| 55 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | if (idx) { |
| 56 | 2 | const Map map = &o->type_ref->nspc->info->value->map; | |
| 57 | 2 | const Value v = (Value)map_at(map, idx - 1); | |
| 58 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | if (tflag(v->type, tflag_compound)) |
| 59 | ✗ | compound_release(shred, v->type, (o->data + SZ_INT)); | |
| 60 | } | ||
| 61 | 3 | } | |
| 62 | |||
| 63 | 1 | static OP_CHECK(opck_union_is) { | |
| 64 | 1 | const Exp e = (Exp)data; | |
| 65 | 1 | const Exp_Call *call = &e->d.exp_call; | |
| 66 | 1 | const Exp exp = call->args; | |
| 67 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if(!exp) |
| 68 | ✗ | ERR_N(e->pos, "Union.is() takes one argument of form id"); | |
| 69 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | if (exp->exp_type != ae_exp_primary || exp->d.prim.prim_type != ae_prim_id) |
| 70 | ✗ | ERR_N(exp->pos, "Union.is() argument must be of form id"); | |
| 71 | 1 | const Type t = call->func->d.exp_dot.base->type; | |
| 72 | 1 | const Value v = find_value(t, exp->d.prim.d.var); | |
| 73 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (!v) |
| 74 | ✗ | ERR_N(exp->pos, "'%s' has no member '%s'", t->name, | |
| 75 | s_name(exp->d.prim.d.var)); | ||
| 76 | 1 | const Map map = &t->nspc->info->value->map; | |
| 77 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | for (m_uint i = 0; i < map_size(map); ++i) { |
| 78 | 3 | const Value v = (Value)VVAL(map, i); | |
| 79 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
|
3 | if (!strcmp(s_name(exp->d.prim.d.var), v->name)) { |
| 80 | 1 | const Exp exp_func = call->func; | |
| 81 | 1 | const Exp exp_args = call->args; | |
| 82 | 1 | e->exp_type = ae_exp_binary; | |
| 83 | 1 | e->d.exp_binary.lhs = cpy_exp(env->gwion->mp, exp_func); | |
| 84 | 1 | e->d.exp_binary.lhs->d.exp_dot.xid = | |
| 85 | 1 | insert_symbol(env->gwion->st, "index"); | |
| 86 | // e->d.exp_binary.rhs = new_prim_int(env->gwion->mp, i+1, e->pos); | ||
| 87 | 1 | e->d.exp_binary.rhs = new_prim_int(env->gwion->mp, i, e->pos); | |
| 88 | 1 | free_exp(env->gwion->mp, exp_func); | |
| 89 | 1 | free_exp(env->gwion->mp, exp_args); | |
| 90 | 1 | e->d.exp_binary.op = insert_symbol(env->gwion->st, "=="); | |
| 91 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | CHECK_ON(check_exp(env, e)); |
| 92 | 1 | return e->type; | |
| 93 | } | ||
| 94 | } | ||
| 95 | ✗ | return env->gwion->type[et_error]; | |
| 96 | } | ||
| 97 | |||
| 98 | ✗ | static MFUN(union_is) { | |
| 99 | ✗ | *(m_uint *)RETURN = *(m_uint *)MEM(SZ_INT) == *(m_uint *)o->data; | |
| 100 | } | ||
| 101 | |||
| 102 | 1 | static MFUN(union_new) { | |
| 103 | 1 | memcpy(o->data, MEM(SZ_INT*2), *(m_uint*)MEM(SZ_INT)); | |
| 104 | 1 | *(M_Object *)RETURN = o; | |
| 105 | 1 | } | |
| 106 | |||
| 107 | 2 | static OP_CHECK(opck_union_new) { | |
| 108 | 2 | Exp_Call *call = (Exp_Call *)data; | |
| 109 | 2 | const Exp name = call->args; | |
| 110 |
4/6✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
|
2 | if (!name || !name->next || name->next->next) |
| 111 | 1 | ERR_N(call->func->pos, "Union constructor takes two arguments, " | |
| 112 | "'id' and 'value'"); | ||
| 113 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | if (name->exp_type != ae_exp_primary || name->d.prim.prim_type != ae_prim_id) |
| 114 | ✗ | return env->gwion->type[et_error]; | |
| 115 | 1 | const Exp val = name->next; | |
| 116 | 1 | const Type base = call->func->d.exp_dot.base->type; | |
| 117 | 1 | const Map map = &base->nspc->info->value->map; | |
| 118 |
1/2✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
|
2 | for (m_uint i = 0; i < map_size(map); ++i) { |
| 119 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | if (VKEY(map, i) == (m_uint)name->d.prim.d.var) { |
| 120 | 1 | const Value v = (Value)VVAL(map, i); | |
| 121 | 1 | name->d.prim.prim_type = ae_prim_num; | |
| 122 | 1 | name->d.prim.d.num = i; | |
| 123 | 1 | name->type = env->gwion->type[et_int]; | |
| 124 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | DECL_ON(const Type, t, = check_exp(env, val)); |
| 125 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (isa(t, v->type) < 0) { |
| 126 | ✗ | ERR_N(val->pos, "Invalid type '%s' for '%s', should be '%s'", t->name, | |
| 127 | v->name, v->type->name); | ||
| 128 | } | ||
| 129 | 1 | const Exp e = new_prim_int(env->gwion->mp, t->size + SZ_INT, val->pos); | |
| 130 | 1 | e->next = name; | |
| 131 | 1 | e->type = env->gwion->type[et_int]; | |
| 132 | 1 | call->args = e; | |
| 133 | 1 | return base; | |
| 134 | } | ||
| 135 | } | ||
| 136 | ✗ | return env->gwion->type[et_error]; | |
| 137 | } | ||
| 138 | |||
| 139 | 638 | ANN GWION_IMPORT(union) { | |
| 140 | 638 | const Type t_none = gwi_mk_type(gwi, "None", 0, NULL); | |
| 141 | 638 | GWI_BB(gwi_set_global_type(gwi, t_none, et_none)) | |
| 142 | 638 | GWI_BB(gwi_gack(gwi, t_none, gack_none)) | |
| 143 | 638 | gwi_add_type(gwi, t_none); | |
| 144 | 638 | struct SpecialId_ spid = { | |
| 145 | 638 | .type = gwi->gwion->type[et_none], .exec = NoOp, .is_const = 1}; | |
| 146 | 638 | gwi_specialid(gwi, "None", &spid); | |
| 147 | |||
| 148 | 638 | GWI_BB(gwi_oper_ini(gwi, "None", "None", "None")) | |
| 149 | 638 | GWI_BB(gwi_oper_add(gwi, opck_none)) | |
| 150 | 638 | GWI_BB(gwi_oper_emi(gwi, opem_none)) | |
| 151 | 638 | GWI_BB(gwi_oper_end(gwi, ":=>", NoOp)) | |
| 152 | |||
| 153 | 638 | const Type t_union = gwi_class_ini(gwi, "union", "Object"); | |
| 154 | 638 | gwi_class_xtor(gwi, NULL, UnionDtor); | |
| 155 | 638 | gwi->gwion->type[et_union] = t_union; | |
| 156 | |||
| 157 | 638 | GWI_BB(gwi_item_ini(gwi, "int", "index")) | |
| 158 | 638 | GWI_BB(gwi_item_end(gwi, ae_flag_const, num, 0)) | |
| 159 | 638 | GWI_BB(gwi_func_ini(gwi, "bool", "is")) | |
| 160 | 638 | GWI_BB(gwi_func_arg(gwi, "int", "member")) | |
| 161 | 638 | GWI_BB(gwi_func_end(gwi, union_is, ae_flag_none)) | |
| 162 | 638 | GWI_BB(gwi_func_ini(gwi, "auto", "new:[T]")) | |
| 163 | 638 | GWI_BB(gwi_func_arg(gwi, "int", "size")) | |
| 164 | 638 | GWI_BB(gwi_func_arg(gwi, "int", "id")) | |
| 165 | 638 | GWI_BB(gwi_func_arg(gwi, "T", "value")) | |
| 166 | 638 | GWI_BB(gwi_func_end(gwi, union_new, ae_flag_none)) | |
| 167 | 638 | GWI_BB(gwi_class_end(gwi)) | |
| 168 | |||
| 169 | 638 | const Func f = (Func)vector_front(&t_union->nspc->vtable); | |
| 170 | 638 | const struct Op_Func opfunc = {.ck = opck_union_is}; | |
| 171 | 638 | const struct Op_Import opi = { | |
| 172 | 638 | .rhs = f->value_ref->type, | |
| 173 | .func = &opfunc, | ||
| 174 | 638 | .data = (uintptr_t)f, | |
| 175 | .pos = gwi->loc, | ||
| 176 | 638 | .op = insert_symbol(gwi->gwion->st, "@func_check")}; | |
| 177 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 638 times.
|
638 | CHECK_BB(add_op(gwi->gwion, &opi)); |
| 178 | |||
| 179 | 638 | const Func f1 = (Func)vector_at(&t_union->nspc->vtable, 1); | |
| 180 | 638 | const struct Op_Func opfunc1 = {.ck = opck_union_new}; | |
| 181 | 638 | const struct Op_Import opi1 = { | |
| 182 | 638 | .rhs = f1->value_ref->type, | |
| 183 | .func = &opfunc1, | ||
| 184 | 638 | .data = (uintptr_t)f1, | |
| 185 | .pos = gwi->loc, | ||
| 186 | 638 | .op = insert_symbol(gwi->gwion->st, "@func_check")}; | |
| 187 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 638 times.
|
638 | CHECK_BB(add_op(gwi->gwion, &opi1)); |
| 188 | |||
| 189 | 638 | gwi->gwion->type[et_union] = t_union; | |
| 190 | |||
| 191 | 638 | GWI_BB(gwi_oper_ini(gwi, "union", (m_str)OP_ANY_TYPE, NULL)) | |
| 192 | 638 | GWI_BB(gwi_oper_emi(gwi, opem_union_dot)) | |
| 193 | 638 | GWI_BB(gwi_oper_end(gwi, ".", NULL)) | |
| 194 | |||
| 195 | 638 | GWI_BB(gwi_union_ini(gwi, "Option:[A]")) | |
| 196 | 638 | GWI_BB(gwi_union_add(gwi, "None", "none")) | |
| 197 | 638 | GWI_BB(gwi_union_add(gwi, "A", "val")) | |
| 198 | 638 | GWI_BB(gwi_union_end(gwi, ae_flag_none)) | |
| 199 | |||
| 200 | 638 | return GW_OK; | |
| 201 | } | ||
| 202 |