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 |