1 /****************************************************************************
2 * Copyright (C) 2012 by Matteo Franchin *
3 * *
4 * This file is part of Box. *
5 * *
6 * Box is free software: you can redistribute it and/or modify it *
7 * under the terms of the GNU Lesser General Public License as published *
8 * by the Free Software Foundation, either version 3 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * Box is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU Lesser General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU Lesser General Public *
17 * License along with Box. If not, see <http://www.gnu.org/licenses/>. *
18 ****************************************************************************/
19
20 #include <assert.h>
21
22 #include <box/types_priv.h>
23 #include <box/messages.h>
24 #include <box/obj.h>
25
26 /* Initialise a #BoxCombs structure (list of combinations). */
27 void
BoxCombs_Init(BoxCombs * combs)28 BoxCombs_Init(BoxCombs *combs) {
29 combs->node.next = NULL;
30 combs->node.previous = NULL;
31 }
32
33 /**
34 * @brief Utility function to retrieve the combinations for a type.
35 *
36 * @param parent The parent type whose combinations are to be obtained.
37 * @return A #BoxTypeNode object representing the linked list of combinations
38 * for the parent type @p parent.
39 */
40 static BoxTypeNode *
My_Get_Combs(BoxType * parent)41 My_Get_Combs(BoxType *parent) {
42 if (parent->type_class == BOXTYPECLASS_IDENT) {
43 BoxTypeIdent *td = BoxType_Get_Data(parent);
44 return & td->combs.node;
45
46 } else if (parent->type_class == BOXTYPECLASS_SUBTYPE_NODE) {
47 BoxTypeSubtypeNode *td = BoxType_Get_Data(parent);
48 return & td->combs.node;
49
50 } else if (parent->type_class == BOXTYPECLASS_ANY) {
51 BoxTypeAny *td = BoxType_Get_Data(parent);
52 return & td->combs.node;
53
54 } else
55 return NULL;
56 }
57
58 /* Get the iterator over the combinations of the given identifier type. */
BoxType_Get_Combinations(BoxType * t,BoxTypeIter * iter)59 BoxBool BoxType_Get_Combinations(BoxType *t, BoxTypeIter *iter) {
60 BoxTypeNode *node = My_Get_Combs(t);
61 if (node) {
62 iter->current_node = node->next;
63 return BOXBOOL_TRUE;
64 }
65
66 return BOXBOOL_FALSE;
67 }
68
69 /* Define a combination 'child'@'parent' and associate a callable to it. */
70 BoxType *
BoxType_Define_Combination(BoxType * parent,BoxCombType comb_type,BoxType * child,BOXIN BoxCallable * callable)71 BoxType_Define_Combination(BoxType *parent, BoxCombType comb_type,
72 BoxType *child, BOXIN BoxCallable *callable) {
73 BoxType *comb;
74 BoxTypeCombNode *cn;
75 BoxTypeNode *combs_of_parent = My_Get_Combs(parent);
76
77 if (!combs_of_parent) {
78 BoxSPtr_Unlink(callable);
79 MSG_FATAL("Parent is not an identifier type (%d).", parent->type_class);
80 return NULL;
81 }
82
83 /* Create the node. */
84 cn = BoxType_Alloc(& comb, BOXTYPECLASS_COMB_NODE);
85 cn->comb_type = comb_type;
86 cn->child = BoxType_Link(child);
87 cn->callable = callable;
88 BoxTypeNode_Prepend_Node(combs_of_parent, comb);
89 return comb;
90 }
91
92 /* Register a combination with a #BoxCall2 implementation. */
93 BoxBool
BoxType_Define_CCall2_Combination(BoxType * child,BoxCombType comb_type,BoxType * parent,const char * uid,BoxCCall2 fn)94 BoxType_Define_CCall2_Combination(BoxType *child, BoxCombType comb_type,
95 BoxType *parent, const char *uid,
96 BoxCCall2 fn) {
97 BoxCallable *callable;
98 BoxType *comb;
99
100 callable = BoxCallable_Create_Undefined(parent, child);
101 callable = BoxCallable_Define_From_CCall2(callable, fn);
102 comb = BoxType_Define_Combination(parent, comb_type, child, callable);
103 assert(comb);
104 BoxCallable_Set_Uid(callable, (BoxUid *) uid);
105 return BOXBOOL_TRUE;
106 }
107
108 /* Undefine the given combination. */
109 void
BoxType_Undefine_Combination(BoxType * parent,BoxType * comb)110 BoxType_Undefine_Combination(BoxType *parent, BoxType *comb) {
111 BoxTypeNode *combs_of_parent = My_Get_Combs(parent);
112
113 if (!combs_of_parent) {
114 MSG_FATAL("Object does not have combinations (type=%d).",
115 (int) parent->type_class);
116 return;
117 }
118
119 /* Create the node. */
120 (void) BoxTypeNode_Remove_Node(combs_of_parent, comb);
121 (void) BoxType_Unlink(comb);
122 }
123
BoxCombDef_Define(BoxCombDef * defs,size_t num_defs)124 size_t BoxCombDef_Define(BoxCombDef *defs, size_t num_defs) {
125 size_t i;
126 for (i = 0; i < num_defs; i++) {
127 BoxCombDef *d = & defs[i];
128 BoxType *parent = d->parent ? d->parent : Box_Get_Core_Type(d->parent_id),
129 *child = d->child ? d->child : Box_Get_Core_Type(d->child_id);
130 if (!BoxType_Define_CCall2_Combination(child, d->comb_type, parent,
131 d->name, d->call2))
132 return i;
133
134 }
135 return num_defs;
136 }
137
138 /* Find the non-inherited procedure 'left'@'right'. */
139 BoxType *
BoxType_Find_Own_Combination(BoxType * parent,BoxCombType type,BoxType * child,BoxTypeCmp * expand)140 BoxType_Find_Own_Combination(BoxType *parent, BoxCombType type,
141 BoxType *child, BoxTypeCmp *expand) {
142 BoxTypeIter ti;
143
144 if (parent && child && BoxType_Get_Combinations(parent, & ti)) {
145 BoxType *t;
146 for (; BoxTypeIter_Get_Next(& ti, & t);) {
147 BoxTypeCombNode *node = BoxType_Get_Data(t);
148 assert(t->type_class == BOXTYPECLASS_COMB_NODE);
149 if (node->comb_type == type) {
150 BoxTypeCmp cmp = BoxType_Compare(node->child, child);
151 if (cmp != BOXTYPECMP_DIFFERENT) {
152 if (expand)
153 *expand = cmp;
154 return t;
155 }
156 }
157 }
158 }
159
160 return NULL;
161 }
162
163 /* Find a (possibly inherited procedure) child@parent of parent. */
164 BoxType *
BoxType_Find_Combination(BoxType * parent,BoxCombType comb_type,BoxType * child,BoxTypeCmp * expand)165 BoxType_Find_Combination(BoxType *parent, BoxCombType comb_type,
166 BoxType *child, BoxTypeCmp *expand) {
167 BoxType *found_comb, *former_parent;
168
169 if (!(parent && child))
170 return NULL;
171
172 do {
173 /* Find a combination for the parent type, if found return. */
174 found_comb =
175 BoxType_Find_Own_Combination(parent, comb_type, child, expand);
176
177 if (found_comb)
178 return found_comb;
179
180 /* Remember the parent. */
181 former_parent = parent;
182
183 /* If not found, resolve the parent and try again. */
184 parent = BoxType_Resolve(parent,
185 (BOXTYPERESOLVE_IDENT | BOXTYPERESOLVE_SPECIES
186 | BOXTYPERESOLVE_RAISED),
187 1);
188
189 } while (parent != former_parent);
190
191 return NULL;
192 }
193
194 BoxType *
BoxType_Find_Own_Combination_With_Id(BoxType * parent,BoxCombType type,BoxTypeId child_id,BoxTypeCmp * expand)195 BoxType_Find_Own_Combination_With_Id(BoxType *parent, BoxCombType type,
196 BoxTypeId child_id, BoxTypeCmp *expand) {
197 /* Quick hack: we should do this better!
198 * This code relies on BoxType_Find_Combination not trying to link or
199 * unlink the child type.
200 */
201 BoxTypeBundle child;
202 child.header.type_class = BOXTYPECLASS_PRIMARY;
203 child.data.primary.id = child_id;
204 return BoxType_Find_Own_Combination(parent, type, (BoxType *) & child,
205 expand);
206 }
207
208 /* Get details about a combination found with BoxType_Find_Combination. */
209 BoxBool
BoxType_Get_Combination_Info(BoxType * comb,BoxType ** child,BoxCallable ** callable)210 BoxType_Get_Combination_Info(BoxType *comb, BoxType **child,
211 BoxCallable **callable) {
212 if (comb->type_class == BOXTYPECLASS_COMB_NODE) {
213 BoxTypeCombNode *td = BoxType_Get_Data(comb);
214 if (child)
215 *child = td->child;
216 if (callable)
217 *callable = td->callable;
218 return BOXBOOL_TRUE;
219 }
220
221 return BOXBOOL_FALSE;
222 }
223
224 /* Generate a call number for calling a combination from bytecode. */
225 BoxBool
BoxType_Generate_Combination_Call_Num(BoxType * comb,BoxVM * vm,BoxVMCallNum * cn)226 BoxType_Generate_Combination_Call_Num(BoxType *comb, BoxVM *vm,
227 BoxVMCallNum *cn) {
228 if (comb->type_class == BOXTYPECLASS_COMB_NODE) {
229 BoxTypeCombNode *td = BoxType_Get_Data(comb);
230 BoxCallable *new_cb;
231
232 if (BoxCallable_Request_VM_Call_Num(td->callable, vm, cn, & new_cb)) {
233 /* Substitute the callable, if necessary. */
234 if (new_cb) {
235 (void) BoxCallable_Unlink(td->callable);
236 td->callable = new_cb;
237 }
238
239 return BOXBOOL_TRUE;
240 }
241 }
242
243 return BOXBOOL_FALSE;
244 }
245