1 /*
2 * $Id: fwrap.c,v 1.6 2010-08-08 04:05:22 dhmunro Exp $
3 * implement function argument wrapping with wrap_args
4 */
5 /* Copyright (c) 2009, David H. Munro.
6 * All rights reserved.
7 * This file is part of yorick (http://yorick.sourceforge.net).
8 * Read the accompanying LICENSE file for details.
9 */
10
11 #include "yapi.h"
12 #include "ydata.h"
13 #include "pstdlib.h"
14 #include <stdio.h>
15 #include <string.h>
16
17 /* interpreted interface consists of wrap_args function and
18 * wrapped_func and wrapped_args objects
19 */
20
21 extern ybuiltin_t Y_wrap_args;
22 typedef struct y_wrapped_func y_wrapped_func;
23 typedef struct y_wrapped_args y_wrapped_args;
24
25 static void ywrap_f_free(void *obj);
26 static void ywrap_f_print(void *obj);
27 static void ywrap_fake_eval(void *obj, int argc);
28 static void ywrap_f_eval(Operand *op);
29 y_userobj_t ywrap_f_ops = {
30 "wrapped_func", &ywrap_f_free, &ywrap_f_print, &ywrap_fake_eval, 0, 0 };
31
32 static void ywrap_a_free(void *obj);
33 static void ywrap_a_print(void *obj);
34 static void ywrap_a_eval(void *obj, int argc);
35 y_userobj_t ywrap_a_ops = {
36 "wrapped_args", &ywrap_a_free, &ywrap_a_print, &ywrap_a_eval, 0, 0 };
37
38 struct y_wrapped_func {
39 Function *f;
40 };
41
42 struct y_wrapped_args {
43 int npos, nkey;
44 Symbol *args;
45 };
46
47 int
yarg_func(int iarg)48 yarg_func(int iarg)
49 {
50 int is_func = 0;
51 if (iarg >= 0) {
52 Symbol *s = sp - iarg;
53 if (s->ops==&referenceSym) s = &globTab[s->index];
54 if (s->ops == &dataBlockSym) {
55 Operations *ops= s->value.db->ops;
56 if (ops == &functionOps) is_func = 1;
57 else if (ops == &builtinOps) is_func = 2;
58 else if (ops == &auto_ops) is_func = 3;
59 else if (s->value.db->ops->typeName==ywrap_f_ops.type_name) is_func = 4;
60 else if (yo_is_closure(iarg)) is_func = 5;
61 }
62 }
63 return is_func;
64 }
65
66 void
Y_wrap_args(int argc)67 Y_wrap_args(int argc)
68 {
69 y_wrapped_func *wf;
70 Function *f;
71 long index = sp->index;
72 if (sp->ops == &referenceSym) ReplaceRef(sp);
73 else index = -1;
74 f = (Function *)sp->value.db;
75 if (argc!=1 || sp->ops!=&dataBlockSym || f->ops!=&functionOps || index<0)
76 y_error("wrap_args takes one interpreted function as its argument");
77 /* hasPosList bit 1 is .. parameter (va), bit 2 marks 1st param as output */
78 if (f->nPos!=1 || f->nKey!=0 || f->hasPosList&3)
79 y_error("wrap_args argument function must have one non-output parameter");
80 if (!ywrap_f_ops.uo_ops) {
81 yfunc_obj(&ywrap_f_ops);
82 ((Operations *)ywrap_f_ops.uo_ops)->Eval = &ywrap_f_eval;
83 yfunc_obj(&ywrap_a_ops);
84 }
85 wf = ypush_obj(&ywrap_f_ops, sizeof(y_wrapped_func));
86 wf->f = Ref(f);
87 yput_global(index, 0);
88 }
89
90 static void
ywrap_f_print(void * obj)91 ywrap_f_print(void *obj)
92 {
93 y_wrapped_func *wf = obj;
94 Instruction *pc = wf->f->code;
95 char *name = yfind_name(pc->index);
96 y_print("wrapped func ", 0);
97 y_print(name? name : "<nameless>", 0);
98 y_print("(args)", 1);
99 }
100
101 static void
ywrap_a_print(void * obj)102 ywrap_a_print(void *obj)
103 {
104 y_wrapped_args *wa = obj;
105 char msg[80];
106 sprintf(msg, "wrapped args, %d positionals, %d keywords",
107 wa->npos, wa->nkey);
108 y_print(msg, 1);
109 }
110
111 static void
ywrap_f_free(void * obj)112 ywrap_f_free(void *obj)
113 {
114 y_wrapped_func *wf = obj;
115 Unref(wf->f);
116 }
117
118 static void
ywrap_a_free(void * obj)119 ywrap_a_free(void *obj)
120 {
121 y_wrapped_args *wa = obj;
122 Symbol *args = wa->args;
123 if (args && wa->npos) {
124 /* handle output args now
125 * - we are being invoked from the Drop call in the final line
126 * of fnctn.c:Return()
127 * - note that weird stuff happens if wrapped function is written
128 * in such a way that args survives beyond function lifetime
129 */
130 Symbol *glob;
131 int i;
132 /* move output argument to globTab, or discard non-output argument */
133 for (i=0 ; i<wa->npos+(wa->nkey<<1) ; ++i) {
134 if (!args[i].ops) continue;
135 if (args[i].index != -1) {
136 /* this referenceSym argument may have been set with output value
137 * all symbols have been restored to their external values, so
138 * can go ahead and overwrite with output value
139 */
140 glob = globTab + args[i].index;
141 if (glob->ops == &dataBlockSym) {
142 /* delete current value */
143 glob->ops = &intScalar;
144 Unref(glob->value.db);
145 }
146 /* transfer args[i] dataBlockSym use counter value to glob */
147 glob->value = args[i].value;
148 glob->ops = args[i].ops;
149 } else if (args[i].ops == &dataBlockSym) {
150 Unref(args[i].value.db);
151 }
152 args[i].ops = &intScalar; /* dud Symbol just to be safe */
153 }
154 wa->args = 0;
155 }
156 p_free(args);
157 }
158
159 static void
ywrap_fake_eval(void * obj,int argc)160 ywrap_fake_eval(void *obj, int argc)
161 {
162 y_error("(BUG) wrap_args should have overridden ywrap_fake_eval");
163 }
164
165 static void
ywrap_f_eval(Operand * op)166 ywrap_f_eval(Operand *op)
167 {
168 int argc = op->references; /* (sic) # of actual parameters supplied */
169 Symbol *args, *me = sp - argc;
170 DataBlock *uo = op->value;
171 y_wrapped_func *wf = (y_wrapped_func *)yget_obj_s(uo);
172 y_wrapped_args *wa;
173 int i, j, k;
174 /* wf has been called, time to wrap its arguments */
175 wa = ypush_obj(&ywrap_a_ops, sizeof(y_wrapped_args));
176 wa->npos = wa->nkey = 0;
177 if (argc) {
178 wa->args = args = p_malloc(sizeof(Symbol)*argc);
179 for (i=j=k=0 ; i<argc ; i++) {
180 if (!me[1+i].ops) {
181 if (!k) k = 1+i;
182 i++;
183 continue;
184 }
185 args[j] = me[1+i];
186 if (args[j].ops == &referenceSym) {
187 ReplaceRef(args+j); /* crucial: leaves args[].index untouched */
188 } else {
189 args[j].index = -1; /* mark as not a reference sym */
190 }
191 j++;
192 }
193 if (k) {
194 for (i=0 ; k<argc ; k++) {
195 if (me[k].ops) continue;
196 args[j+(i++)] = me[k++];
197 args[j+i] = me[k];
198 if (args[j+i].ops == &referenceSym) {
199 /* never actually get here, keywords never reference syms */
200 ReplaceRef(args+j+i); /* crucial: leaves args[].index untouched */
201 } else {
202 args[j+i].index = -1; /* mark as not a reference sym */
203 }
204 i++;
205 }
206 } else {
207 i = 0;
208 }
209 me[1].ops = &intScalar;
210 me[1].value.db = sp->value.db;
211 sp = me + 1;
212 me[1].ops = &dataBlockSym;
213 } else {
214 i = j = 0;
215 wa->args = 0;
216 }
217 wa->npos = j;
218 wa->nkey = i>>1;
219 /* chain to EvalFN as f(args), post-call work done in ywrap_a_free */
220 me[0].ops = &intScalar;
221 Unref(uo);
222 me[0].value.db = op->value = Ref(wf->f);
223 op->references = 1; /* (sic) set # of actual parameters supplied */
224 wf->f->ops->Eval(op);
225 }
226
227 static void
ywrap_a_eval(void * obj,int argc)228 ywrap_a_eval(void *obj, int argc)
229 {
230 y_wrapped_args *wa = obj;
231 long junk[3];
232 int isquery = (argc>0)? yget_range(argc-1, junk) : 0;
233 int iskey = 0;
234 Symbol *arg = 0;
235 long iarg = 0;
236 char *key = 0;
237 if (argc!=1 && argc!=2)
238 y_error("wrapped arg only accepts one or two arguments");
239 if (!sp->ops)
240 y_error("wrapped arg only accepts no keyword arguments");
241 if (isquery == (Y_PSEUDO|Y_MIN_DFLT|Y_MAX_DFLT)) {
242 isquery = 1;
243 } else if (isquery == (Y_RUBBER1|Y_MIN_DFLT|Y_MAX_DFLT)) {
244 if (argc == 1) isquery = 2;
245 else isquery = 3;
246 }
247 if (!isquery) iskey = yarg_string(argc-1)==1;
248 if (!iskey && !isquery) {
249 iarg = ygets_l(argc-1);
250 if (!iarg) isquery = 2;
251 } else if (iskey) {
252 key = ygets_q(argc-1);
253 }
254 if (isquery && argc==2) {
255 if (isquery != 3) {
256 iskey = (yarg_string(0) == 1);
257 if (!iskey) iarg = ygets_l(0);
258 else key = ygets_q(0);
259 } else {
260 isquery = 1;
261 if (!yarg_nil(0)) iarg = ygets_l(0);
262 }
263 }
264 if (key) {
265 char *name;
266 int i;
267 for (i=0 ; i<(wa->nkey<<1) ; i+=2) {
268 name = yfind_name(wa->args[wa->npos+i].index);
269 if (name && !strcmp(name, key)) break;
270 }
271 iarg = -1 - (i>>1);
272 }
273 if (iarg > 0) {
274 if (iarg <= wa->npos) arg = wa->args + iarg-1;
275 } else if (iarg < 0) {
276 if (iarg >= -wa->nkey) arg = wa->args + wa->npos + 1 + ((-1-iarg)<<1);
277 }
278 if (isquery == 2) {
279 if (!iarg) { /* ARGS(0) or ARGS(*) */
280 ypush_long(wa->npos);
281 } else { /* ARGS(0,i) */
282 if (!arg) ypush_long(2);
283 else if (arg->index == -1) ypush_long(1);
284 else ypush_long(0);
285 }
286 } else if (isquery == 1) {
287 char **q;
288 if (!iarg) { /* ARGS(-) */
289 if (wa->nkey) {
290 int i;
291 junk[0] = 1;
292 junk[1] = wa->nkey;
293 q = ypush_q(junk);
294 for (i=0 ; i<wa->nkey ; i++)
295 q[i] = p_strcpy(yfind_name(wa->args[wa->npos+2*i].index));
296 } else {
297 ypush_nil();
298 }
299 } else { /* ARGS(-,i) */
300 q = ypush_q(0);
301 if (arg && arg->index!=-1) {
302 q[0] = p_strcpy(yfind_name(arg->index));
303 } else {
304 q[0] = 0;
305 }
306 }
307 } else {
308 if (argc==2 && yget_range(0, junk)==(1|Y_MIN_DFLT|Y_MAX_DFLT))
309 argc = 3;
310 if (argc == 1 || argc == 3) { /* ARGS(i) or ARGS(i,:) */
311 if (arg) {
312 if (arg->ops == &dataBlockSym) {
313 /* fetch lvalues now unless specifically directed not to */
314 if (argc==1 && arg->value.db->ops == &lvalueOps)
315 FetchLValue(arg->value.db, arg);
316 sp[1].ops = &dataBlockSym;
317 sp[1].value.db = Ref(arg->value.db);
318 } else {
319 sp[1].ops = arg->ops;
320 sp[1].value = arg->value;
321 }
322 sp++;
323 } else {
324 ypush_nil();
325 }
326 } else { /* ARGS, i, value */
327 long index = arg? arg->index : -1;
328 Symbol *s = (sp->ops == &referenceSym)? globTab+sp->index : sp;
329 if (index != -1) {
330 if (arg->ops == &dataBlockSym) {
331 arg->ops = &intScalar;
332 Unref(arg->value.db);
333 }
334 if (s->ops == &dataBlockSym) {
335 arg->value.db = Ref(s->value.db);
336 arg->ops = &dataBlockSym;
337 if (arg->value.db->ops == &lvalueOps)
338 FetchLValue(arg->value.db, arg);
339 } else {
340 arg->value = s->value;
341 arg->ops = s->ops;
342 }
343 }
344 if (!yarg_subroutine()) ypush_nil();
345 }
346 }
347 }
348