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