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