1 /*
2  * $Id: oxy.c,v 1.6 2010-11-09 02:39:45 dhmunro Exp $
3  * implementation of object extension
4  */
5 /* Copyright (c) 2010 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 /* changed:
12  * fnctn.c, ydata.h, yapi.h, yapi.c, fwrap.c
13  */
14 
15 #include "ydata.h"
16 #include "pstdlib.h"
17 #include "phash.h"
18 #include <stdio.h>
19 #include <string.h>
20 
21 extern BuiltIn Y_use, Y_save, Y_restore, Y_is_obj, Y_closure, Y_gaccess;
22 extern BuiltIn Y_use_method;
23 
24 /* ------------------------------------------------------------------------ */
25 /* oxy_object is the base class for yorick's object oriented extension
26  */
27 
28 static void yo_on_free(void *uo);
29 static void yo_on_print(void *uo);
30 static void yo_on_extract(void *uo, char *name);
31 static void yo_on_eval(void *uo, int nargs);
32 static void yo_on_evalx(void *uo, int nargs, int icx, int meth);
33 
34 static y_userobj_t yo_uops =
35   { "oxy_object", yo_on_free, yo_on_print, yo_on_eval, yo_on_extract, 0 };
36 
37 typedef struct yo_data_t yo_data_t;
38 struct yo_data_t {
39   yo_ops_t *ops;
40   void *on_destroy;  /* all oxy_object have optional destructor(s) */
41   void *obj;
42 };
43 
44 union yo_align_t { double d; void *p; void (*f)(void); };
45 struct yo_extend_t {
46   yo_data_t c;
47   union yo_align_t u;
48 };
49 #define YO_DATA_T_SZ (sizeof(struct yo_extend_t)-sizeof(union yo_align_t))
50 
51 void *
yo_push_alloc(yo_ops_t * ops,unsigned long size)52 yo_push_alloc(yo_ops_t *ops, unsigned long size)
53 {
54   yo_data_t *uo = ypush_obj(&yo_uops, YO_DATA_T_SZ+size);
55   uo->ops = ops;
56   uo->on_destroy = 0;
57   return uo->obj = ((char*)uo) + YO_DATA_T_SZ;
58 }
59 
60 void *
yo_push(yo_ops_t * ops,void * obj)61 yo_push(yo_ops_t *ops, void *obj)
62 {
63   yo_data_t *uo = ypush_obj(&yo_uops, sizeof(yo_data_t));
64   uo->ops = ops;
65   uo->on_destroy = 0;
66   return uo->obj = obj;
67 }
68 
69 static yo_data_t *yo_iostream(int iarg);  /* create IOStream wrapper */
70 
71 void *
yo_get(int iarg,yo_ops_t ** ops)72 yo_get(int iarg, yo_ops_t **ops)
73 {
74   yo_data_t *uo = 0;
75   if (yget_obj(iarg,0) == yo_uops.type_name) uo = yget_obj(iarg, &yo_uops);
76   else if (yarg_typeid(iarg) == Y_STREAM) uo = yo_iostream(iarg);
77   if (uo && ops) *ops = uo->ops;
78   return uo? uo->obj : 0;
79 }
80 
81 static void
yo_on_free(void * vuo)82 yo_on_free(void *vuo)
83 {
84   yo_data_t *uo = vuo;
85   /* interpreted on_destroy callbacks independent of object type */
86   /* if (uo->on_destroy) yo_do_hooks(uo->u.on_destroy, uo); */
87   if (uo->obj) {
88     /* dealloc method does p_free if yo_push, but not if yo_push_alloc */
89     if (uo->ops->dealloc) uo->ops->dealloc(uo->obj);
90     uo->obj = 0;
91   }
92 }
93 
94 static void
yo_on_print(void * vuo)95 yo_on_print(void *vuo)
96 {
97   yo_ops_t *ops = ((yo_data_t*)vuo)->ops;
98   void *obj = ((yo_data_t*)vuo)->obj;
99   if (ops->print) {
100     ops->print(obj);
101   } else {
102     char msg[256];
103     long count = ops->count(obj);
104     sprintf(msg, "object [%ld]: ", count);
105     strncat(msg, ops->type_name, 255);
106     msg[255] = '\0';
107     y_print(msg, 1);
108   }
109 }
110 
111 static void
yo_on_extract(void * uo,char * name)112 yo_on_extract(void *uo, char *name)
113 {
114   yo_ops_t *ops = ((yo_data_t*)uo)->ops;
115   void *obj = ((yo_data_t*)uo)->obj;
116   ops->get_q(obj, name, -1L);
117 }
118 
119 /* ------------------------------------------------------------------------ */
120 /* oxy_context is internal object to implement use function
121  * -replaces oxy_object, just under func/wrapped_func/builtin on stack
122  * three external functions:
123  *   void yo_mk_context(int iarg)        replace oxy_object by oxy_context
124  *   int yo_cswap(int iarg, long index)  add one use item to oxy_context
125  *   void yo_cupdate(int iarg)           update oxy_object from all use items
126  *
127  * on obj(member,...) on_eval, stack begins like this:
128  *   obj  member  arg1  ...
129  * mk_context replaces obj by context:
130  *   context  member  arg1  ...
131  *      +-->obj    <context owns stack reference to obj>
132  *      +-->xlist  <context maintains list of externals>
133  * The xlist contains external values of variables shadowed by object
134  * members swapped into globtab by the use function.  Return from the
135  * (interpreted) caller of use moves those variables from globtab back
136  * into the object, and replaces the external values stored in xlist.
137  *
138  * What we really want is to replace the object itself by this context,
139  * so that anyone requesting a member gets the thing in globtab.  But
140  * this doesn't work for a general object, since its members might not
141  * be anything storable in globtab.  When the object is such a thing,
142  * however, the use function is always going to be problematic.  We also
143  * want compiled functions to "play nice" with the use function, so that
144  * instead of directly reading or writing from their context object,
145  * they use globtab for members being "use"d.
146  * Problem 1:
147  *   Functions called in an object context should be able to call sibling
148  * methods via use(sibling, ...).  This should work even if the object
149  * (or its sibling caller) has called use to work with some members in
150  * globtab.  This requires use to be smart enough to realize when a member
151  * is already in use by a function up the call chain.
152  * A related problem is that compiled methods also need to be able to
153  * detect when a "use" function has swapped object members into globtab,
154  * so it can operate on those instead of the obsolete copies in the
155  * object itself.  Do this with yo_use analogue to interpreted use.
156  * To handle the main problem, it would suffice for use to replace itself
157  * by an "indirect context" that refers back to the original context,
158  * rather than to the object.  Any interpreted returns with this modified
159  * context would not unswap globtab with object members, while any use
160  * functions would act on the original context, so that the outermost
161  * return would pick them up.
162  * --This requires that cswap be a no-op when a member is already in use,
163  *   making it somewhat difficult to handle situations in which the
164  *   context has new members added during the call.
165  * --Note that save,use and restore,use don't play nice with use.
166  *   Probably want just use instead of save,use.  The restore,use form
167  *   would undo any changes to a used variable.
168  * Problem 2:
169  *   There should be a way to invoke a "friend" method in the current
170  * context, even when the "friend" is not a member of the context
171  * object.  The primary example is a method of a derived class which
172  * needs to call the method of the same name in the base class.  This
173  * can be done easily if use(base_api(method), ...) were accepted, that
174  * is, if use accepts a non-simple variable reference.
175  */
176 static void yo_mk_context(int iarg, int icx);
177 static int yo_cswap(int iarg, long index);
178 static void *yo_seek_context(int iarg, int *piarg, int push);
179 
180 static void yo_con_free(void *uo);
181 static y_userobj_t yo_cops =
182   { "oxy_context", yo_con_free, 0, 0, 0, 0 };
183 
184 typedef struct yo_symbol_t yo_symbol_t;
185 struct yo_symbol_t {
186   long m;    /* member index */
187   Symbol s;  /* external value, s.index is globtab index */
188 };
189 
190 typedef struct yo_context_t yo_context_t;
191 struct yo_context_t {
192   /* obj is the actual context object
193    * ocount is the last known number of members in obj, which
194    * determines how many bits in xlist are allocated
195    * ocount<0 indicates that this is an "indirect" context generated
196    *   by the use function, with the true context at this sp[ocount]
197    *   nxlist = xlist = 0 in that case
198    */
199   DataBlock *obj;
200   long ocount;
201   /* xlist starts nil, grows on subroutine calls to use
202    * xlist[i] is external value of item saved before loading from obj
203    * xlist[i].index is member index in obj, from which global index
204    *   can be retrieved
205    * - bit flags begin at xlist[nxlist], set to mark members which
206    *   currently reside in globtab; there are ocount bit flags
207    * - on delete, restored in reverse order created
208    */
209   long nxlist;
210   yo_symbol_t *xlist;
211   unsigned char *bits;  /* set indicates already swapped */
212 };
213 
214 static void
yo_mk_context(int iarg,int icx)215 yo_mk_context(int iarg, int icx)
216 {
217   if (icx>iarg || yget_obj(iarg,0)==yo_uops.type_name) {
218     yo_context_t *uo = ypush_obj(&yo_cops, sizeof(yo_context_t));
219     uo->nxlist = 0;
220     uo->xlist = 0;
221     uo->bits = 0;
222     if (icx > iarg) {  /* make indirect context */
223       uo->ocount = iarg - icx;  /* location of direct context */
224       uo->obj = 0;
225     } else {           /* make direct context */
226       uo->ocount = 0;
227       uo->obj = yget_use(iarg+1);
228     }
229     yarg_swap(0, iarg+1);
230     yarg_drop(1);
231   } else {
232     y_error("(BUG) yo_mk_context expected oxy_object");
233   }
234 }
235 
236 static yo_symbol_t *yo_xlistx(yo_symbol_t *s, long n,
237                               long ocount, long ncount);
238 
239 static int
yo_cswap(int iarg,long index)240 yo_cswap(int iarg, long index)
241 {
242   if (yget_obj(iarg,0) == yo_cops.type_name) {
243     yo_context_t *uc = yget_obj(iarg, &yo_cops);
244     yo_data_t *uo;
245     yo_ops_t *ops;
246     void *obj;
247     yo_symbol_t *s;
248     long m = 0, ocount, n;
249     if (uc->ocount < 0) {
250       iarg -= uc->ocount;
251       uc = (yget_obj(iarg,0)==yo_cops.type_name)? yget_obj(iarg,&yo_cops) : 0;
252       if (!uc || uc->ocount<0)
253         y_error("(BUG) yo_cswap bad indirect oxy_context");
254     }
255     uo = yget_obj_s(uc->obj);
256     ops = uo->ops;
257     obj = uo->obj;
258     if (index<0 || index>=globalTable.nItems)
259       return 1;  /* no such index in globtab */
260     if (ops->find_mndx) {
261       m = ops->find_mndx(obj, globalTable.names[index], index);
262       if (m < 1)
263         return 2;  /* no member of same name in oxy_object */
264     }
265     ocount = uc->ocount;
266     s = uc->xlist;
267     n = uc->nxlist;
268     if (!n || (n>=4 && !(n&(n-1)))) {
269       long ncount = ops->count(obj);
270       uc->xlist = s = yo_xlistx(s, n, ocount, ncount);
271       uc->ocount = ocount = ncount;
272       uc->bits = (unsigned char*)(s + (n?n+n:4));
273     }
274     if (m > ocount)
275       y_error("(BUG) yo_cswap object count less than returned index");
276     if (uc->bits[((unsigned long)m-1)>>3] & (1<<((m-1)&7)))
277       return 0;  /* this member already in global */
278     /* copy globtab[index] to oxy_context xlist */
279     ypush_global(index);
280     s[n].m = m;
281     s[n].s.ops = sp->ops;
282     s[n].s.index = index;
283     s[n].s.value = sp->value;
284     sp--;  /* just transfer use from stack to xlist */
285     uc->nxlist = ++n;
286     /* copy corresponding oxy_object member to globtab[index] */
287     if (ops->find_mndx) ops->get_i(obj, m);
288     else ops->get_q(obj, globalTable.names[index], index);
289     yput_global(index, 0);
290     yarg_drop(1);
291     /* mark this member */
292     uc->bits[((unsigned long)m-1)>>3] |= 1<<((m-1)&7);
293   } else {
294     y_error("(BUG) yo_cswap expected oxy_context");
295   }
296   return 0;  /* success */
297 }
298 
299 static yo_symbol_t *
yo_xlistx(yo_symbol_t * s,long n,long ocount,long ncount)300 yo_xlistx(yo_symbol_t *s, long n, long ocount, long ncount)
301 {
302   long i, nn = n? n+n : 4;
303   unsigned char *bo, *bn;
304   ncount = ((unsigned long)ncount >> 3) + 1;  /* overestimate, at least 1 */
305   s = p_realloc(s, nn*sizeof(yo_symbol_t)+ncount);
306   bo = (unsigned char *)(s+n);
307   bn = (unsigned char *)(s+nn);
308   ocount = (ocount>0)? ((unsigned long)(ocount-1) >> 3)+1 : 0; /* exact */
309   for (i=ncount-1 ; i>=ocount ; i--) bn[i] = 0;
310   for ( ; i>=0 ; i--) bn[i] = bo[i];
311   return s;
312 }
313 
314 void
yo_cupdate(int iarg)315 yo_cupdate(int iarg)
316 {
317   if (yget_obj(iarg,0) == yo_cops.type_name) {
318     yo_context_t *uc = yget_obj(iarg, &yo_cops);
319     yo_data_t *uo;
320     yo_ops_t *ops;
321     void *obj;
322     yo_symbol_t *s;
323     long i;
324     int zap = 1;
325     if (uc->ocount < 0) {
326       iarg -= uc->ocount;
327       uc = (yget_obj(iarg,0)==yo_cops.type_name)? yget_obj(iarg,&yo_cops) : 0;
328       if (!uc || uc->ocount<0)
329         y_error("(BUG) yo_cupdate bad indirect oxy_context");
330       zap = 0;
331     }
332     uo = yget_obj_s(uc->obj);
333     ops = uo->ops;
334     obj = uo->obj;
335     s = uc->xlist;
336     i = uc->nxlist;
337     /* in reverse order of yo_cswap */
338     while (--i >= 0) {
339       if (s[i].s.index<0 || s[i].m<0) continue;
340       /* copy globtab[index] to corresponding oxy_object member */
341       ypush_global(s[i].s.index);
342       if (ops->find_mndx) ops->set_i(obj, s[i].m, 0);
343       else ops->set_q(obj, globalTable.names[s[i].s.index], s[i].s.index, 0);
344       if (zap)
345         s[i].m = -1;  /* remove xlist correspondance to oxy_object member */
346       yarg_drop(1);
347     }
348   } else {
349     y_error("(BUG) yo_cupdate expected oxy_context");
350   }
351 }
352 
353 static void
yo_con_free(void * vuc)354 yo_con_free(void *vuc)
355 {
356   yo_context_t *uc = vuc;
357   DataBlock *db = uc->obj;
358   if (db) {
359     yo_symbol_t *s = uc->xlist;
360     long i = uc->nxlist;
361     if (s) {
362       long index;
363       /* in reverse order of yo_cswap */
364       while (--i >= 0) {
365         index = s[i].s.index;
366         if (index < 0) continue;
367         /* move oxy_context xlist element onto stack, transferring use */
368         sp[1].ops = s[i].s.ops;
369         sp[1].value = s[i].s.value;
370         s[i].s.index = -1;  /* disable this xlist element */
371         sp++;
372         /* restore corresponding globtab[index] */
373         yput_global(index, 0);
374         yarg_drop(1);
375       }
376       uc->nxlist = 0;
377       uc->xlist = 0;
378       uc->bits = 0;
379       p_free(s);
380     }
381     /* now that xlist is gone, decrement object uses */
382     uc->obj = 0;
383     Unref(db);
384   } else {
385     uc->ocount = 0;
386   }
387 }
388 
389 static void *
yo_seek_context(int iarg,int * piarg,int push)390 yo_seek_context(int iarg, int *piarg, int push)
391 {
392   if (iarg >= 0) {
393     iarg++;
394   } else {
395     for (iarg=0 ; sp[-iarg].ops!=&returnSym ; iarg++)
396       if (sp-iarg <= spBottom) return 0;
397     iarg += sp[-iarg].index;
398   }
399   if (yget_obj(iarg,0) == yo_cops.type_name) {
400     yo_context_t *uc = yget_obj(iarg, &yo_cops);
401     yo_data_t *uo;
402     if (uc->ocount < 0) {
403       iarg -= uc->ocount;
404       uc = (yget_obj(iarg,0)==yo_cops.type_name)? yget_obj(iarg,&yo_cops) : 0;
405       if (!uc || uc->ocount<0)
406         y_error("(BUG) yo_seek_context bad indirect oxy_context");
407     }
408     uo = yget_obj_s(uc->obj);
409     if (push) {
410       sp[1].ops = &dataBlockSym;
411       sp[1].value.db = Ref(uc->obj);
412       sp++;
413     }
414     if (piarg) *piarg = iarg;
415     return uo;
416   }
417   return 0;
418 }
419 
420 void *
yo_get_context(int iarg,yo_ops_t ** ops,int push)421 yo_get_context(int iarg, yo_ops_t **ops, int push)
422 {
423   yo_data_t *uo = yo_seek_context(-1, 0, push);
424   if (uo) {
425     if (ops) *ops = uo->ops;
426     return uo->obj;
427   }
428   if (ops) *ops = 0;
429   return 0;
430 }
431 
432 /* ------------------------------------------------------------------------ */
433 
434 static void yo_use_guts(int argc, int meth);
435 
436 void
Y_use(int argc)437 Y_use(int argc)
438 {
439   yo_use_guts(argc, 0);
440 }
441 
442 void
Y_use_method(int argc)443 Y_use_method(int argc)
444 {
445   yo_use_guts(argc, 1);
446 }
447 
448 static void
yo_use_guts(int argc,int meth)449 yo_use_guts(int argc, int meth)
450 {
451   int icx, sub = meth? 0 : yarg_subroutine();
452   yo_data_t *obj = yo_seek_context(-1, &icx, !sub);
453 
454   if (sub) {
455     int i;
456     if (!obj) y_error("use called without any context object");
457     for (i=0 ; i<argc ; i++)
458       if (yget_ref(i) < 0)
459         y_error("use accepts only simple variable references");
460     for (i=argc-1 ; i>=0 ; i--)
461       if (yo_cswap(icx, yget_ref(i)))
462         y_errorq("use: context object has no member %s",
463                  yfind_name(yget_ref(i)));
464 
465   } else if (obj) {
466     /* use(arg1, arg2, ...) same as obj(arg1, arg2, ...) */
467     long isp = (sp-spBottom) - (argc+1);
468     yarg_swap(argc+1, 0);
469     yarg_drop(1);
470     /* context obj replaced use builtin, just invoke on_eval obj method */
471     yo_on_evalx(obj, argc, icx, meth);
472     /* ensure EvalBI gets correctly positioned stack if possible */
473     isp = (sp-spBottom) - isp;
474     if (isp>0 && sp->ops!=&returnSym) {
475       yarg_swap(isp, 0);
476       yarg_drop(isp);
477     }
478 
479   } else {
480     /* use() with no context */
481     ypush_nil();
482   }
483 }
484 
485 int
yo_use(long index)486 yo_use(long index)
487 {
488   int icx;
489   yo_data_t *obj = yo_seek_context(-1, &icx, 0);
490   return obj? yo_cswap(icx, index) : 3;
491 }
492 
493 typedef struct yo_membarg_t yo_membarg_t;
494 struct yo_membarg_t {
495   long n;   /* n<0 indicates error, n=0 single or special form, n>=1 multi */
496   long dims[Y_DIMSIZE];
497   long mndx;
498   char *name;
499   long iname;
500   char **names;
501   long *mndxs;
502   long *range;     /* points into unused part of dims */
503   int special;     /* 0 not, 1 nil, 2 -, 3 *, 4 .. */
504   int frnd;
505 };
506 
507 /* may want to publish this API... */
508 static int yo_membarg(int iarg, void *obj, yo_ops_t *ops, yo_membarg_t *ma,
509                       int flag);
510 /* flag&1 = abort on error, flag&2 = permit scalar mndx=nmembers+1 */
511 static int
yo_membarg(int iarg,void * obj,yo_ops_t * ops,yo_membarg_t * ma,int flag)512 yo_membarg(int iarg, void *obj, yo_ops_t *ops, yo_membarg_t *ma, int flag)
513 {
514   ma->n = ma->dims[0] = ma->mndx = 0;
515   ma->name = 0;
516   ma->iname = yget_ref(iarg);
517   ma->names = 0;
518   ma->mndxs = ma->range = 0;
519   ma->special = 0;
520   ma->frnd = 0;
521 
522   if (ma->iname >= 0) {
523     ma->name = yfind_name(ma->iname);
524     ma->n = 0;
525     return 0;
526 
527   } else {
528     int tid = yarg_typeid(iarg);
529 
530     if (tid == Y_STRING) {        /* member name(s) */
531       ma->names = ygeta_q(iarg, &ma->n, ma->dims);
532       if (!ma->dims[0]) {
533         ma->name = ma->names[0];
534         ma->names = 0;
535         ma->n = 0;
536       }
537 
538     } else if (tid <= Y_LONG) {   /* member index(s) */
539       if (ops->find_mndx) {
540         long n = ops->count(obj);
541         ma->mndxs = ygeta_l(iarg, &ma->n, ma->dims);
542         if (!ma->dims[0]) {
543           ma->mndx = ma->mndxs[0];
544           ma->mndxs = 0;
545           ma->n = 0;
546           if (ma->mndx <= 0) ma->mndx += n;
547           if (ma->mndx<=0 || ma->mndx>n+((flag&2)!=0)) n = -3;
548         } else {
549           long i;
550           for (i=0 ; i<ma->n ; i++)
551             if (ma->mndxs[i]<=0 || ma->mndxs[i]>n) break;
552           if (i < ma->n) {
553             if (!yarg_scratch(iarg)) {
554               long *mndxs = ypush_l(ma->dims);
555               for (i=0 ; i<ma->n ; i++) mndxs[i] = ma->mndxs[i];
556               yarg_swap(iarg, 0);
557               yarg_drop(1);
558               ma->mndxs = mndxs;
559             }
560             for (i=0 ; i<ma->n ; i++) {
561               if (ma->mndxs[i] <= 0) ma->mndxs[i] += n;
562               if (ma->mndxs[i]<=0 || ma->mndxs[i]>n) n = -3;
563               if (ma->mndxs[i]<=0 || ma->mndxs[i]>n) break;
564             }
565             if (i < ma->n) n = -3;
566           }
567         }
568         if (n < 0) {
569           if (flag&1) y_error("bad member index argument");
570           ma->n = -3;
571         }
572       } else {
573         if (flag&1) y_error("this object does not support member index");
574         ma->n = -2;
575       }
576 
577     } else if (tid == Y_VOID) {
578       ma->n = 0;
579       ma->special = 1;
580 
581     } else if (tid==Y_RANGE) {  /* indices or special forms */
582       int flags = yget_range(iarg, (ma->range = ma->dims+1));
583       if ((flags&7) == 1) {
584         /* member indices */
585         if (ops->find_mndx) {
586           long n = ops->count(obj);
587           if (flags&Y_MIN_DFLT) ma->dims[1] = (ma->dims[3]<0)? n : 1;
588           if (flags&Y_MAX_DFLT) ma->dims[2] = (ma->dims[3]<0)? 1 : n;
589           if (ma->dims[1] <= 0) ma->dims[1] += n;
590           if (ma->dims[2] <= 0) ma->dims[2] += n;
591           if (ma->dims[1]<=0 || ma->dims[2]<=0 || ma->dims[1]>n ||
592               ma->dims[2]>n) {
593             ma->dims[3] = 0;
594           } else if (!ma->dims[3]) {
595             if (ma->dims[1]==ma->dims[2]) ma->dims[3] = 1;
596           } else if ((ma->dims[1]!=ma->dims[2])
597                      && (ma->dims[1]>ma->dims[2]) != (ma->dims[3]<0)) {
598             ma->dims[3] = 0;
599           }
600           if (ma->dims[3]) {
601             ma->n = 1 + (ma->dims[2]-ma->dims[1])/ma->dims[3];
602             ma->dims[2] = ma->dims[1] + (ma->n-1)*ma->dims[3];
603           } else {
604             if (flag&1) y_error("bad member index range argument");
605             ma->n = -3;
606           }
607         } else {
608           if (flag&1)
609             y_error("this object does not support member index range");
610           ma->n = -2;
611         }
612 
613       } else if ((flags & (Y_MIN_DFLT|Y_MAX_DFLT)) ==
614                  (Y_MIN_DFLT|Y_MAX_DFLT)) { /* special forms */
615         flags &= ~(Y_MIN_DFLT|Y_MAX_DFLT);
616         if (flags == Y_PSEUDO) ma->special = 2;        /*  -  */
617         else if (flags == Y_RUBBER) ma->special = 4;   /*  .. */
618         else if (flags == Y_RUBBER1) ma->special = 3;  /*  *  */
619         else if (flag&1) y_error("unrecognized member argument");
620         else ma->n = -4;
621 
622       } else {
623         if (flag&1) y_error("unrecognized member index range argument");
624         ma->n = -5;
625       }
626 
627     } else {
628       int ff = yarg_func(iarg);
629       if (ff<1 || ff>4) {
630         if (flag&1) y_error("unrecognized member specifier argument");
631         ma->n = -1;
632       } else {
633         ma->frnd = 1; /* function that will be called in context of object */
634       }
635     }
636   }
637 
638   return 1;
639 }
640 
641 void
Y_is_obj(int argc)642 Y_is_obj(int argc)
643 {
644   yo_ops_t *ops;
645   void *obj = (argc>0)? yo_get(argc-1, &ops) : 0;
646   int err = 0, isobj = (obj != 0);
647   int hasmndx = (isobj && ops->find_mndx);
648   if (argc<1 || argc>3) y_error("is_obj accepts only 1, 2, or 3 arguments");
649 
650   if (argc == 1) {
651     if (hasmndx) isobj |= 2;
652     /* should also set bit for read-only?  any other properties? */
653     ypush_int(isobj);
654     return;
655 
656   } else if (argc == 3) {
657     err = (!yarg_nil(0) && ygets_l(0));
658     yarg_drop(1);
659   }
660 
661   if (isobj) {
662     long i;
663     yo_membarg_t ma;
664     int erri = 0;
665     yo_membarg(0, obj, ops, &ma, !err);
666     if (ma.n >= 0) {
667       if (!ma.n) {
668         if (!ma.special) {   /* is_obj a single member */
669           if (ma.name) {
670             if (ops->get_q(obj, ma.name, ma.iname)) {
671               if (!err) y_errorq("is_obj object has no member %s", ma.name);
672               erri = 1;
673             }
674           } else {
675             if (!ma.mndx || ops->get_i(obj, ma.mndx)) {
676               if (!err) y_errorn("is_obj object has no member %ld", ma.mndx);
677               erri = 1;
678             }
679           }
680           isobj = erri? 0 : (yo_get(0, &ops) != 0);
681           if (isobj && ops->find_mndx) isobj |= 2;
682           yarg_drop(1);
683           ypush_int(erri? -1 : isobj);
684         } else if (ma.special == 1) {  /* nil */
685           yo_ops_t *ops2;
686           int *rslt;
687           ma.dims[0] = 1;
688           ma.dims[1] = ops->count(obj);
689           if (ma.dims[1]) {
690             rslt = ypush_i(ma.dims);
691             for (i=0 ; i<ma.dims[1] ; i++) {
692               if (ops->get_i(obj, i+1)) {
693                 if (!err) y_errorn("(BUG) is_obj object has no member %ld", i);
694                 erri = 1;
695               }
696               isobj = erri? 0 : (yo_get(0, &ops2) != 0);
697               if (isobj && ops2->find_mndx) isobj |= 2;
698               rslt[i] = erri? -1 : isobj;
699               yarg_drop(1);
700               erri = 0;
701             }
702           } else {
703             ypush_nil();
704           }
705         } else {             /* is_obj some special form */
706           y_error("unrecognized member specifier in is_obj");
707         }
708 
709       } else {               /* is_obj multiple members as a group */
710         yo_ops_t *ops2;
711         int *rslt;
712         long j = ma.range? ma.range[0] : 0;
713         ma.dims[4] = 1;
714         ma.dims[5] = ma.n;
715         rslt = ypush_i(ma.dims+4);
716         for (i=0 ; i<ma.n ; i++) {
717           if (ma.names) {
718             if (ops->get_q(obj, ma.names[i], -1L)) {
719               if(!err) y_errorq("is_obj object has no member %s", ma.names[i]);
720               erri = 1;
721             }
722           } else {
723             if (!ma.range) j = ma.mndxs[i];
724             if (ops->get_i(obj, j)) {
725               if (!err) y_errorn("is_obj object has no member %ld", j);
726               erri = 1;
727             }
728             if (ma.range) j += ma.range[2];
729           }
730           isobj = erri? 0 : (yo_get(0, &ops2) != 0);
731           if (isobj && ops2->find_mndx) isobj |= 2;
732           rslt[i] = erri? -1 : isobj;
733           yarg_drop(1);
734           erri = 0;
735         }
736       }
737     } else {
738       ypush_int(-1);
739     }
740   } else {
741     if (!err) y_error("is_obj(obj,m) obj argument not an object");
742     ypush_int(-2);
743   }
744 }
745 
746 static long yo_use_iname = -1L;
747 
748 void
Y_restore(int argc)749 Y_restore(int argc)
750 {
751   yo_ops_t *ops;
752   void *obj;
753   if (yo_use_iname<0) yo_use_iname = yget_global("use", 0);
754   if (yget_ref(--argc) == yo_use_iname) {
755     /* special form restore, use, arg1, arg2, ... */
756     obj = yo_get_context(argc, &ops, 1);
757     if (!obj) y_error("no context for restore,use");
758     yarg_swap(argc+1, 0);  /* replace use by context */
759     yarg_drop(1);
760   } else {
761     obj = yo_get(argc, &ops);
762   }
763   if (obj) {
764     char *name;
765     long iname;
766     yo_ops_t *ops2 = 0;
767     void *obj2 = 0;
768     if (!yarg_subroutine())
769       /* create output group containing global values before restore */
770       obj2 = yo_new_group(&ops2);
771     else
772       /* called as subroutine, make argc same as function call case */
773       ypush_nil();
774 
775     if (!argc) {       /* restore all non-anonymous members */
776       long mndx, n = ops->count(obj);
777       for (mndx=1 ; mndx<=n ; mndx++) {
778         name = ops->find_name(obj, mndx, &iname);
779         if (!name) continue;
780         if (iname<0) iname = yget_global(name, 0);
781         if (obj2) {  /* save globtab variable in return object */
782           if (ypush_global(iname)) y_error("(BUG) bad iname in restore()");
783           ops2->set_q(obj2, name, iname, 0);
784           yarg_drop(1);
785         }
786         /* copy object member to globtab */
787         ops->get_q(obj, name, iname);
788         yput_global(iname, 0);
789         yarg_drop(1);
790       }
791 
792     } else {                    /* restore specific members */
793       yo_ops_t *ops3 = 0;
794       void *obj3 = 0;
795       long i, j;
796       yo_membarg_t ma;
797       do {
798         argc -= yo_membarg(argc, obj, ops, &ma, 1);
799         iname = (argc>0)? yget_ref(argc) : -1;
800         if (iname < 0)
801           y_error("restore output argument must be simple variable reference");
802         if (obj2) {  /* save globtab variable in return object */
803           if (ypush_global(iname)) y_error("(BUG) bad iname in restore()");
804           ops2->set_q(obj2, yfind_name(iname), iname, 0);
805           yarg_drop(1);
806         }
807 
808         if (!ma.n) {
809           if (!ma.special) {   /* restore a single member */
810             if (ma.name) {
811               if (ops->get_q(obj, ma.name, ma.iname))
812                 y_errorq("restore object has no member %s", ma.name);
813             } else {
814               if (!ma.mndx || ops->get_i(obj, ma.mndx))
815                 y_errorn("restore object has no member %ld", ma.mndx);
816             }
817 
818           } else {             /* restore some special form */
819             y_error("unrecognized member specifier in restore");
820           }
821 
822         } else {               /* restore multiple members as a group */
823           obj3 = yo_new_group(&ops3);
824           j = ma.range? ma.range[0] : 0;
825           for (i=0 ; i<ma.n ; i++) {
826             if (ma.names) {
827               if (ops->get_q(obj, ma.names[i], -1L))
828                 y_errorq("restore object has no member %s", ma.names[i]);
829               ops3->set_q(obj3, ma.names[i], -1L, 0);
830             } else {
831               if (!ma.range) j = ma.mndxs[i];
832               if (ops->get_i(obj, j))
833                 y_errorn("restore object has no member %ld", j);
834               if (ma.range) j += ma.range[2];
835               /* preserve member names in restored object */
836               ma.name = ops->find_name(obj, j, &ma.iname);
837               /* if ma.name==0, set_q creates next anonymous member */
838               ops3->set_q(obj3, ma.name, ma.iname, 0);
839             }
840             yarg_drop(1);
841           }
842         }
843         /* move restored object to globtab */
844         yput_global(iname, 0);
845         yarg_drop(1);
846       } while (--argc > 0);
847     }
848     if (ops->sr_hook) ops->sr_hook(obj, 1);
849   } else {
850     y_error("restore needs a source object or file");
851   }
852 }
853 
854 static void yo_new_globobj(void);
855 
856 void
Y_save(int argc)857 Y_save(int argc)
858 {
859   long i, j, n, iname;
860   char *name;
861   yo_ops_t *ops;
862   void *obj;
863   if (yo_use_iname<0) yo_use_iname = yget_global("use", 0);
864   if (yarg_subroutine()) {
865     if (yget_ref(--argc) == yo_use_iname) {
866       /* special form save, use, arg1, arg2, ... */
867       obj = yo_get_context(argc, &ops, 1);
868       if (!obj) y_error("no context for save,use");
869       yarg_swap(argc+1, 0);  /* replace use by context */
870       yarg_drop(1);
871     } else {
872       obj = yo_get(argc, &ops);
873       if (!obj) y_error("save needs a destination object or file");
874     }
875     ypush_nil();  /* make stack look same as function call */
876   } else {
877     long range[3];
878     if ((argc==1)
879         && yget_range(0, range)==(Y_RUBBER1|Y_MAX_DFLT|Y_MIN_DFLT)) {
880       /* create special object representing all of globtab */
881       yo_new_globobj();
882       return;
883     }
884     obj = yo_new_group(&ops);
885     if (argc==1 && yget_ref(1)<0 && yarg_nil(1))
886       return;  /* save() creates empty object */
887   }
888 
889   /* argc = number of sspec arguments, one non-sspec on top of stack */
890   if (ops->sr_hook) ops->sr_hook(obj, argc?2:6);
891   if (!argc) {           /* save all of globtab */
892     for (iname=1 ; (name=yfind_name(iname)) ; iname++) {
893       if (ypush_global(iname))
894         y_error("(BUG) problem with globtab in save");
895       argc = ops->set_q(obj, name, iname, 0);
896       if (argc == 2)
897         y_errorq("cannot save to read-only member %s", name);
898       else if (argc == 3)
899         y_errorq("object to be saved incommensurate with member %s", name);
900       /* argc==4 just skips this member, unsupported type */
901       yarg_drop(1);
902     }
903 
904   } else {           /* save specific list of variables */
905     yo_membarg_t ma;
906     yo_ops_t *ops3 = 0;
907     void *obj3 = 0;
908     do {
909       if (sp[-argc].ops) {
910         argc -= yo_membarg(argc, obj, ops, &ma, 3);
911       } else {
912         /* permit member= keywords on same basis as simple variable refs */
913         ma.n = ma.dims[0] = ma.mndx = 0;
914         ma.iname = sp[-argc].index;
915         ma.name = yfind_name(ma.iname);
916         ma.names = 0;
917         ma.mndxs = ma.range = 0;
918         ma.special = 0;
919         ma.frnd = 0;
920         argc--;
921       }
922       if (argc <= 0)
923         y_error("save: missing final argument of sspec pair");
924 
925       if (!ma.n) {
926         if (ma.special == 1) {  /* nil */
927           obj3 = yo_get(argc, &ops3);
928           if (obj3) {      /* merge object argument */
929             n = ops3->count(obj3);
930             for (i=0 ; i<n ; i++) {
931               name = ops3->find_name(obj3, i+1, &iname);
932               if (!name) {
933                 if (!ops3->get_i || ops3->get_i(obj3, i+1))
934                   y_error("(BUG) object to be saved has unfetchable member");
935                 if (ops->set_q(obj, 0, -1, argc-1))
936                   y_error("unable to save by appending anonymous member");
937               } else {
938                 if (ops3->get_q(obj3, name, iname))
939                   y_error("(BUG) object to be saved has unreadable member");
940                 if (ops->set_q(obj, name, iname, 0))
941                   y_errorq("unable to save to member %s",
942                            name? name : "<anon>");
943               }
944               yarg_drop(1);
945             }
946           } else {    /* append non-object argument */
947             ma.special = 0;    /* same as string(0) */
948           }
949         }
950         if (!ma.special && !ma.frnd) {   /* save a single member */
951           if (ma.name) {
952             if (ops->set_q(obj, ma.name, ma.iname, argc))
953               y_errorq("unable to save to member %s", ma.name);
954           } else if (ma.mndx) {
955             if (ops->set_i(obj, ma.mndx, argc))
956               y_errorn("unable to save to member %ld", ma.mndx);
957           } else {
958             /* string(0) means append anonymously */
959             if (ops->set_q(obj, 0, -1, argc))
960               y_error("unable to save by appending anonymous member");
961           }
962 
963         } else if (ma.special != 1) { /* 1 nil, 2 -, 3 *, 4 .. */
964           y_error("unrecognized member specifier in save");
965         }
966 
967       } else {               /* save multiple members from a group */
968         obj3 = yo_get(argc, &ops3);
969         j = ma.range? ma.range[0] : 0;
970         for (i=0 ; i<ma.n ; i++) {
971           if (ma.names) {
972             if (ops3->get_q(obj3, ma.names[i], -1L))
973               y_errorq("object to be saved has no member %s", ma.names[i]);
974             if (ops->set_q(obj, ma.names[i], -1L, 0))
975               y_errorq("unable to save member %s",
976                        ma.names[i]? ma.names[i] : "<anon>");
977           } else {
978             if (ops3->get_i(obj3, i+1))
979               y_errorn("object to be saved has no member %ld", j);
980             if (!ma.range) j = ma.mndxs[i];
981             if (ops->set_i(obj, j, 0))
982               y_errorn("unable to save member %ld", ma.mndxs[i]);
983             if (ma.range) j += ma.range[2];
984           }
985           yarg_drop(1);
986         }
987       }
988     } while (--argc > 0);
989   }
990 
991   if (ops->sr_hook) ops->sr_hook(obj, 0);
992 }
993 
994 extern void FormEvalOp(int nArgs, Operand *obj);
995 
996 static void
yo_on_eval(void * uo,int nargs)997 yo_on_eval(void *uo, int nargs)
998 {
999   yo_on_evalx(uo, nargs, -1, 0);
1000 }
1001 
1002 static void
yo_on_evalx(void * uo,int nargs,int icx,int meth)1003 yo_on_evalx(void *uo, int nargs, int icx, int meth)
1004 {
1005   yo_ops_t *ops = ((yo_data_t*)uo)->ops;
1006   void *obj = ((yo_data_t*)uo)->obj;
1007   yo_membarg_t ma;
1008   int iarg = nargs-1;
1009 
1010   if (!meth && iarg>0 && !sp[-iarg].ops && yarg_subroutine()) {
1011     /* obj, member1=expr1, member2=expr2, ... */
1012     for (;;) {
1013       ma.iname = sp[-iarg].index;
1014       ma.name = yfind_name(ma.iname);
1015       if (ops->set_q(obj, ma.name, ma.iname, --iarg))
1016         y_errorq("unable to save to member %s", ma.name);
1017       if (iarg <= 0) return;
1018       iarg--;
1019       if (iarg<1 || sp[-iarg].ops)
1020         y_error("obj,m=value,... idiom can only accept keyword arguments");
1021     }
1022   }
1023 
1024   yo_membarg(iarg, obj, ops, &ma, 1);
1025   if (meth && (ma.special || ma.n))
1026     y_error("use_method requires single member specifier");
1027 
1028   if (!ma.special) {             /* obj(m) */
1029     if (ma.n) {  /* just extract member(s) */
1030       yo_ops_t *ops2;
1031       void *obj2;
1032       long i, j;
1033       if (iarg)
1034         y_error("obj(m,args) illegal when m specifies multiple members");
1035       obj2 = yo_new_group(&ops2);
1036       j = ma.range? ma.range[0] : 0;
1037       for (i=0 ; i<ma.n ; i++) {
1038         if (ma.names) {
1039           if (!ma.names[0])
1040             y_error("string(0) does not specify an object member");
1041           if (ops->get_q(obj, ma.names[i], -1L))
1042             y_errorq("unable to get member %s", ma.names[i]);
1043           if (ops2->set_q(obj2, ma.names[i], -1L, 0))
1044             y_errorq("unable to set member %s", ma.names[i]);
1045         } else {
1046           if (ma.mndxs) j = ma.mndxs[i];
1047           if (ops->get_i(obj, j))
1048             y_errorn("unable to get member %ld", j);
1049           /* preserve member name in result object */
1050           ma.name = ops->find_name(obj, j, &ma.iname);
1051           if (ma.range) j += ma.range[2];
1052           if (ops2->set_q(obj2, ma.name, ma.iname, 0))
1053             y_errorq("unable to set member %s", ma.name? ma.name : "<anon>");
1054         }
1055         yarg_drop(1);
1056       }
1057     } else { /* extract single member, possibly eval */
1058       if (ma.name) {
1059         if (ops->get_q(obj, ma.name, -1L))
1060           y_errorq("unable to get member %s", ma.name);
1061       } else if (ma.mndx) {
1062         if (ops->get_i(obj, ma.mndx))
1063           y_errorn("unable to get member %ld", ma.mndx);
1064       } else if (ma.frnd) {
1065         ypush_use(yget_use(iarg)); /* duplicate friend to top of stack */
1066       } else {
1067         y_error("string(0) does not specify an object member");
1068       }
1069       if (meth || yarg_subroutine() || iarg) {
1070         Operand obj;
1071         long isp;
1072         /* instead of simply extracting the member, eval it */
1073         /* move member into stack slot occupied by member specifier */
1074         yarg_swap(iarg+1, 0);
1075         yarg_drop(1); /* throw away member specifier, have thing itself */
1076         if (yarg_func(iarg))
1077           yo_mk_context(iarg+1, icx);
1078         else if (meth)
1079           y_error("use_method requires first argument to be a function");
1080         FormEvalOp(iarg, &obj);
1081         isp = sp - spBottom;
1082         obj.ops->Eval(&obj);
1083         if (sp->ops == &returnSym) {
1084           /* context is at spBottom+isp - (iarg+1)
1085            * want sp-iarg = spBottom+isp - (iarg+1)
1086            *   iarg = iarg+1 + (sp-spBottom)-isp
1087            */
1088           iarg += 1 + sp - (spBottom + isp);  /* new location of context */
1089           /* if (sp->index) this function has been called recursively
1090            * obj1(obj2,obj3,obj4,func,args)
1091            * will end up with stack = [ctx1,ctx2,ctx3,ctx4,func,...return]
1092            * sp->index points to ctx4
1093            */
1094           if (!sp->index) sp->index = iarg;  /* set for use */
1095           /* also must move -2 marker */
1096           if (sp[1-iarg].index != -2)
1097             y_error("(BUG) stack end-of-func return marker garbled");
1098           sp[1-iarg].index = -1;
1099           sp[-iarg].index = -2;
1100         }
1101       }
1102     }
1103 
1104   } else if (ma.special == 1) {  /* obj() */
1105     if (iarg) y_error("obj(,extra) bad argument list to object");
1106     yarg_drop(1);   /* leaves obj on top of stack as return value */
1107 
1108   } else if (ma.special == 3) {  /* obj(*) */
1109     if (!iarg) {
1110       ypush_long(ops->count(obj));
1111     } else if (iarg == 1) {
1112       long i;
1113       int tid = yarg_typeid(0);
1114       if (tid == Y_VOID) {
1115         ma.dims[0] = 1;
1116         ma.dims[1] = ops->count(obj);
1117         if (ma.dims[1]) {
1118           ma.names = ypush_q(ma.dims);
1119           for (i=0 ; i<ma.dims[1] ; i++)
1120             ma.names[i] = p_strcpy(ops->find_name(obj, i+1, &ma.iname));
1121         } else {
1122           ypush_nil();
1123         }
1124       } else if (!ops->find_mndx) {
1125         y_error("object does not support member indices");
1126       } else if (tid>=Y_CHAR && tid<=Y_LONG) {
1127         long j, n = ops->count(obj);
1128         ma.mndxs = ygeta_l(0, &ma.n, ma.dims);
1129         ma.names = ypush_q(ma.dims);
1130         for (i=0 ; i<ma.n ; i++) {
1131           j = ma.mndxs[i];
1132           if (j <= 0) j += n;
1133           if (j<=0 || j>n)
1134             y_error("bad obj(*,m) call, index m out of range");
1135           ma.names[i] = p_strcpy(ops->find_name(obj, j, &ma.iname));
1136         }
1137       } else if (tid == Y_STRING) {
1138         ma.names = ygeta_q(0, &ma.n, ma.dims);
1139         ma.mndxs = ypush_l(ma.dims);
1140         for (i=0 ; i<ma.n ; i++)
1141           ma.mndxs[i] = ops->find_mndx(obj, ma.names[i], -1L);
1142       } else if (tid == Y_RANGE) {
1143         long j;
1144         int flags = yget_range(0, ma.dims+1);
1145         if ((flags&7) != 1)
1146           y_error("bad obj(*,m) call, unrecognized index range m");
1147         ma.n = ops->count(obj);
1148         if (flags&Y_MIN_DFLT) ma.dims[1] = (ma.dims[3]<0)? ma.n : 1;
1149         if (flags&Y_MAX_DFLT) ma.dims[2] = (ma.dims[3]<0)? 1 : ma.n;
1150         if (ma.dims[1] <= 0) ma.dims[1] += ma.n;
1151         if (ma.dims[2] <= 0) ma.dims[2] += ma.n;
1152         if (ma.dims[1]<=0 || ma.dims[2]<=0 || ma.dims[1]>ma.n ||
1153             ma.dims[2]>ma.n) {
1154           ma.dims[3] = 0;
1155         } else if (!ma.dims[3]) {
1156           if (ma.dims[1]==ma.dims[2]) ma.dims[3] = 1;
1157         } else if ((ma.dims[1]!=ma.dims[2])
1158                    && (ma.dims[1]>ma.dims[2]) != (ma.dims[3]<0)) {
1159           ma.dims[3] = 0;
1160         }
1161         if (!ma.dims[3])
1162           y_error("bad obj(*,m) call, bad index range m");
1163         ma.n = (ma.dims[2]-ma.dims[1])/ma.dims[3] + 1;
1164         ma.dims[4] = 1;
1165         ma.dims[5] = ma.n;
1166         ma.names = ypush_q(ma.dims+4);
1167         for (i=0,j=ma.dims[1] ; i<ma.n ; i++,j+=ma.dims[3])
1168           ma.names[i] = p_strcpy(ops->find_name(obj, j, &ma.iname));
1169       } else {
1170         y_error("bad obj(*,m) call, unrecognized m");
1171       }
1172     } else {
1173       y_error("bad obj(*,m) call, too many arguments");
1174     }
1175 
1176   } else if (ma.special == 4) {  /* obj(..) */
1177     if (!ops->get_atts) ypush_nil();
1178     else ops->get_atts(obj);
1179 
1180   } else if (ma.special == 2) {  /* obj(-) */
1181 
1182   } else {
1183     y_error("(BUG) impossible ma.special in yo_on_eval");
1184   }
1185 }
1186 
1187 /* yo_do_hooks */
1188 /* if (uo->on_destroy) yo_do_hooks(uo->u.on_destroy, uo); */
1189 
1190 /* ------------------------------------------------------------------------ */
1191 /* basic group object */
1192 
1193 static void yog_dealloc(void *obj);
1194 static long yog_count(void *obj);
1195 static long yog_findm(void *obj, const char *name, long iname);
1196 static char *yog_findn(void *obj, long mndx, long *iname);
1197 static int yog_geti(void *obj, long mndx);
1198 static int yog_getq(void *obj, const char *name, long iname);
1199 static int yog_seti(void *obj, long mndx, int iarg);
1200 static int yog_setq(void *obj, const char *name, long iname, int iarg);
1201 static void yog_geta(void *obj);
1202 
1203 static yo_ops_t yog_ops = {
1204   "group", yog_dealloc, yog_count, yog_findm, yog_findn,
1205   yog_geti, yog_getq, yog_seti, yog_setq, yog_geta, 0, 0 };
1206 
1207 typedef struct yog_t yog_t;
1208 struct yog_t {
1209   long n_memb;    /* memb[] size is 2^n, n>=2 */
1210   Symbol *memb;   /* yfind_name(memb->index) is member name */
1211   p_hashtab *ht;  /* globtab index --> member index */
1212   void *attrib;   /* attribute object */
1213   void *destruct; /* reserved for destructor */
1214   int flags;
1215 };
1216 
1217 void *
yo_new_group(yo_ops_t ** ops)1218 yo_new_group(yo_ops_t **ops)
1219 {
1220   yog_t *grp = yo_push_alloc(&yog_ops, sizeof(yog_t));
1221   grp->n_memb = 0;
1222   grp->memb = 0;
1223   grp->ht = 0;
1224   grp->attrib = grp->destruct = 0;
1225   grp->flags = 0;
1226   if (ops) *ops = &yog_ops;
1227   return grp;
1228 }
1229 
1230 static void
yog_dealloc(void * vgrp)1231 yog_dealloc(void *vgrp)
1232 {
1233   yog_t *grp = vgrp;
1234   void *obj = grp->destruct;
1235   p_hashtab *ht = grp->ht;
1236   if (ht) {
1237     grp->ht = 0;
1238     p_hfree(ht, 0);
1239   }
1240   if (obj) {
1241     grp->destruct = 0;
1242     ypush_use(obj);
1243     /* this is where destructor(s) would be invoked */
1244     yarg_drop(1);
1245   }
1246   obj = grp->attrib;
1247   if (obj) {
1248     grp->attrib = 0;
1249     ypush_use(obj);
1250     yarg_drop(1);
1251   }
1252   obj = grp->memb;
1253   if (obj) {
1254     long n;
1255     for (n=--grp->n_memb ; n>=0 ; n=--grp->n_memb)
1256       if (grp->memb[n].ops == &dataBlockSym) {
1257         Unref(grp->memb[n].value.db);
1258         grp->memb[n].ops = &intScalar;
1259       }
1260     grp->memb = 0;
1261     p_free(obj);
1262   }
1263 }
1264 
1265 static long
yog_count(void * obj)1266 yog_count(void *obj)
1267 {
1268   yog_t *grp = obj;
1269   return grp->n_memb;
1270 }
1271 
1272 static long
yog_findm(void * obj,const char * name,long iname)1273 yog_findm(void *obj, const char *name, long iname)
1274 {
1275   yog_t *grp = obj;
1276   long i, n = grp->n_memb;
1277   if (!n || !name || !name[0]) return 0;
1278   if (iname < 0) {
1279     iname = yfind_global(name, 0);
1280     if (iname < 0) return 0;
1281   }
1282   if (n <= 4) {
1283     /* just do straight search for <= 4 members */
1284     for (i=0 ; i<n ; ++i) if (grp->memb[i].index == iname) break;
1285     return (i >= n)? 0 : i+1;
1286   } else {
1287     /* create and use hash table for > 4 members */
1288     if (!grp->ht) {
1289       long jname;
1290       grp->ht = p_halloc(n);
1291       for (i=0 ; i<n ; ++i) {
1292         jname = grp->memb[i].index;
1293         if (jname < 0) continue;
1294         p_hinsert(grp->ht, P_IHASH(jname), i+1+(char*)0);
1295       }
1296     }
1297     return (char*)p_hfind(grp->ht, P_IHASH(iname)) - (char*)0;
1298   }
1299 }
1300 
1301 static char *
yog_findn(void * obj,long mndx,long * iname)1302 yog_findn(void *obj, long mndx, long *iname)
1303 {
1304   yog_t *grp = obj;
1305   if (mndx<1 || mndx>grp->n_memb || !grp->n_memb) {
1306     if (iname) *iname = -1;
1307     return 0;
1308   } else {
1309     if (iname) *iname = grp->memb[mndx-1].index;
1310     return yfind_name(grp->memb[mndx-1].index);
1311   }
1312 }
1313 
1314 static int
yog_geti(void * obj,long mndx)1315 yog_geti(void *obj, long mndx)
1316 {
1317   yog_t *grp = obj;
1318   (void)CheckStack(1);
1319   if (mndx<1 || mndx>grp->n_memb) {
1320     ypush_nil();
1321     return 1;
1322   }
1323   sp[1] = grp->memb[mndx-1];
1324   if (sp[1].ops==&dataBlockSym) (void)Ref(sp[1].value.db);
1325   sp++;
1326   return 0;
1327 }
1328 
1329 static int
yog_getq(void * obj,const char * name,long iname)1330 yog_getq(void *obj, const char *name, long iname)
1331 {
1332   return yog_geti(obj, yog_findm(obj, name, iname));
1333 }
1334 
1335 static int
yog_seti(void * obj,long mndx,int iarg)1336 yog_seti(void *obj, long mndx, int iarg)
1337 {
1338   yog_t *grp = obj;
1339   Symbol *s = sp - iarg;
1340   if (mndx<1 || mndx>grp->n_memb || iarg<0) return 1;
1341   if (s->ops == &referenceSym) ReplaceRef(s);
1342 
1343   if (grp->flags & 2) {  /* implement gaccess assign= semantics */
1344     LValue lvfake, *lv = 0;
1345     Operand op;
1346     Symbol *d = grp->memb + mndx-1;
1347     s->ops->FormOperand(s, &op);
1348     if (d->ops == &dataBlockSym) {
1349       DataBlock *db = d->value.db;
1350       if (db->ops == &lvalueOps) {
1351         lv = (LValue *)db;
1352       } else if (db->ops->isArray) {
1353         Array *a = (Array *)db;
1354         lvfake.references = 10;
1355         lvfake.owner = 0;
1356         lvfake.type.base = a->type.base;
1357         lvfake.type.dims = a->type.dims;
1358         lvfake.type.number = a->type.number;
1359         lvfake.address.m = a->value.c;
1360         lvfake.strider = 0;
1361         lv = &lvfake;
1362       } else if (db->ops == op.ops) {
1363         return (op.value == db)? 0 : 2;
1364       }
1365     } else {
1366       lvfake.references = 10;
1367       lvfake.owner = 0;
1368       lvfake.type.dims = 0;
1369       lvfake.type.number = 1;
1370       lvfake.strider = 0;
1371       if (d->ops==&doubleScalar) {
1372         lvfake.ops = &doubleOps;
1373         lvfake.type.base = &doubleStruct;
1374         lvfake.address.m = (char *)&d->value.d;
1375       } else if (d->ops==&longScalar) {
1376         lvfake.ops = &longOps;
1377         lvfake.type.base = &longStruct;
1378         lvfake.address.m = (char *)&d->value.l;
1379       } else if (d->ops==&intScalar) {
1380         lvfake.ops = &intOps;
1381         lvfake.type.base = &intStruct;
1382         lvfake.address.m = (char *)&d->value.i;
1383       } else {
1384         return 4; /* this is returnSym, keyword, etc */
1385       }
1386       lv = &lvfake;
1387     }
1388     /* see ops3.c:DoAssign */
1389     if (!op.ops->isArray || RightConform(lv->type.dims, &op))
1390       return 3;
1391     lv->type.base->dataOps->Assign((Operand *)lv, &op);
1392     return 0;
1393   }
1394   if (grp->memb[mndx-1].ops == &dataBlockSym) {
1395     /* note that this discards LValue created by reshape,
1396      * which is same behavior as pre-2.1.06 restore function
1397      */
1398     grp->memb[mndx-1].ops = &intScalar;
1399     Unref(grp->memb[mndx-1].value.db);
1400   }
1401   if (s->ops == &dataBlockSym) {
1402     if (s->value.db->ops==&lvalueOps) FetchLValue(s->value.db, s);
1403     if (s->ops != &dataBlockSym) grp->memb[mndx-1].value = s->value;
1404     else grp->memb[mndx-1].value.db = Ref(s->value.db);
1405   } else if (s->ops==&doubleScalar ||
1406              s->ops==&longScalar || s->ops==&intScalar) {
1407     grp->memb[mndx-1].value = s->value;
1408   } else {
1409     return 4;  /* this is returnSym, keyword, etc */
1410   }
1411   grp->memb[mndx-1].ops = s->ops;
1412   return 0;
1413 }
1414 
1415 static int
yog_setq(void * obj,const char * name,long iname,int iarg)1416 yog_setq(void *obj, const char *name, long iname, int iarg)
1417 {
1418   yog_t *grp = obj;
1419   long mndx = yog_findm(obj, name, iname);
1420   if (!mndx) {
1421     /* create new group member */
1422     long n = grp->n_memb;
1423     if (grp->flags & 1) return 1;
1424     if (name && !name[0]) name = 0;
1425     if (name && iname<0) iname = yget_global(name, 0);
1426     if (!n || (n>2 && !(n&(n-1))))
1427       grp->memb = p_realloc(grp->memb, ((n>2)? n+n : 4)*sizeof(Symbol));
1428     grp->memb[n].ops = &dataBlockSym;
1429     grp->memb[n].index = name? iname : -1;
1430     grp->memb[n].value.db = RefNC(&nilDB);
1431     grp->n_memb += 1;
1432     mndx = n+1;
1433     if (iname>=0 && grp->ht)
1434       p_hinsert(grp->ht, P_IHASH(iname), mndx+(char*)0);
1435   }
1436   return yog_seti(obj, mndx, iarg);
1437 }
1438 
1439 static void
yog_geta(void * obj)1440 yog_geta(void *obj)
1441 {
1442   yog_t *grp = obj;
1443   if (grp->attrib) {
1444     ypush_use(grp->attrib);
1445     grp->attrib = yget_use(0);
1446   }
1447 }
1448 
1449 void
Y_gaccess(int argc)1450 Y_gaccess(int argc)
1451 {
1452   yo_ops_t *ops = 0;
1453   yog_t *obj = (argc>0)? yo_get(argc-1, &ops) : 0;
1454   if (!obj || ops!=&yog_ops)
1455     y_error("gaccess first argument not a group object");
1456   if (argc > 1) {  /* gaccess(grp,flags) sets group access flags */
1457     if (argc > 2) y_error("gaccess takes at most two arguments");
1458     obj->flags = ygets_i(0);
1459     yarg_drop(1);
1460   } else {         /* gaccess(grp) returns group access flags */
1461     ypush_long(obj->flags);
1462   }
1463 }
1464 
1465 /* ------------------------------------------------------------------------ */
1466 /* make globtab a simple object (obsoletes symbol_def, symbol_set?) */
1467 
1468 static void yo_gt_dealloc(void *obj);
1469 static long yo_gt_count(void *obj);
1470 static long yo_gt_findm(void *obj, const char *name, long iname);
1471 static char *yo_gt_findn(void *obj, long mndx, long *iname);
1472 static int yo_gt_geti(void *obj, long mndx);
1473 static int yo_gt_getq(void *obj, const char *name, long iname);
1474 static int yo_gt_seti(void *obj, long mndx, int iarg);
1475 static int yo_gt_setq(void *obj, const char *name, long iname, int iarg);
1476 /* static void yo_gt_geta(void *obj); */
1477 /* static void yo_gt_print(void *obj); */
1478 
1479 static yo_ops_t yo_globtab_ops = {
1480   "oxy_globtab", yo_gt_dealloc, yo_gt_count, yo_gt_findm, yo_gt_findn,
1481   yo_gt_geti, yo_gt_getq, yo_gt_seti, yo_gt_setq, 0, 0, 0 };
1482 
1483 static void
yo_new_globobj(void)1484 yo_new_globobj(void)
1485 {
1486   yo_push(&yo_globtab_ops, yo_globtab_ops.type_name);
1487 }
1488 
1489 static void
yo_gt_dealloc(void * obj)1490 yo_gt_dealloc(void *obj)
1491 {
1492   return;
1493 }
1494 
1495 static long
yo_gt_count(void * obj)1496 yo_gt_count(void *obj)
1497 {
1498   return globalTable.nItems;
1499 }
1500 
1501 static long
yo_gt_findm(void * obj,const char * name,long iname)1502 yo_gt_findm(void *obj, const char *name, long iname)
1503 {
1504   /*      iname=-1 if correspondence with globtab unknown
1505    *      find_mndx==0 permitted, means no fixed member indices
1506    */
1507   return (iname>=0)? iname : yfind_global(name, 0);
1508 }
1509 
1510 static char *
yo_gt_findn(void * obj,long mndx,long * iname)1511 yo_gt_findn(void *obj, long mndx, long *iname)
1512 {
1513   /*      returns iname=-1 if correspondence with globtab unknown
1514    *      return value owned by obj, caller must copy string
1515    *        next call to find_name may invalidate return value
1516    *      if find_mndx==0, this will only be called in a sequence
1517    *        from mndx=1 to mndx=count(obj) to list all names
1518    *        get_q or set_q may be called during listing sequence,
1519    *        but only with name just returned
1520    */
1521   char *name = yfind_name(mndx);
1522   if (iname) *iname = name? mndx : -1;
1523   return name;
1524 }
1525 
1526 static int
yo_gt_geti(void * obj,long mndx)1527 yo_gt_geti(void *obj, long mndx)
1528 {
1529   int err = ypush_global(mndx);
1530   if (err) ypush_nil();
1531   return err;
1532 }
1533 
1534 static int
yo_gt_getq(void * obj,const char * name,long iname)1535 yo_gt_getq(void *obj, const char *name, long iname)
1536   /*      push member onto stack
1537    *      get_i unused (0 permitted) when find_mndx==0
1538    *      pass iname=-1 if correspondence with globtab unknown
1539    *      returns 0 on success, otherwise push nil and return:
1540    *        1 if no such member
1541    */
1542 {
1543   /* should this be yget_global?? */
1544   return yo_gt_geti(obj, (iname>=0)? iname : yfind_global(name,0));
1545 }
1546 
1547 static int
yo_gt_seti(void * obj,long mndx,int iarg)1548 yo_gt_seti(void *obj, long mndx, int iarg)
1549 {
1550   int err = (mndx>=0 && mndx<globalTable.nItems);
1551   if (!err) yput_global(mndx, iarg);
1552   return err;
1553 }
1554 
1555 static int
yo_gt_setq(void * obj,const char * name,long iname,int iarg)1556 yo_gt_setq(void *obj, const char *name, long iname, int iarg)
1557 {
1558   /*      set member to value at iarg on stack
1559    *      set_i unused (0 permitted) when find_mndx==0
1560    *      both set_i==0, set_q==0 permitted if changing values unsupported
1561    *      pass iname=-1 if correspondence with globtab unknown
1562    *      returns 0 on success, otherwise return:
1563    *        1 if no such member and creating member not allowed
1564    *        2 if member is read-only
1565    *        3 if type or shape of iarg cannot be converted to member type
1566    *        4 if type not supported by this object
1567    */
1568   if (iname<0) iname = yget_global(name, 0);
1569   return yo_gt_seti(obj, iname, iarg);
1570 }
1571 
1572 /* ------------------------------------------------------------------------ */
1573 /* wrap IOStream as an object for backward compatibility */
1574 
1575 static void yo_io_dealloc(void *obj);
1576 static long yo_io_count(void *obj);
1577 static char *yo_io_findn(void *obj, long mndx, long *iname);
1578 static int yo_io_getq(void *obj, const char *name, long iname);
1579 static int yo_io_setq(void *obj, const char *name, long iname, int iarg);
1580 static void yo_sr_hook(void *obj, int flags);
1581 
1582 static yo_ops_t yo_io_ops = {
1583   "oxy_iostream", yo_io_dealloc, yo_io_count, 0, yo_io_findn,
1584   0, yo_io_getq, 0, yo_io_setq, 0, 0, yo_sr_hook };
1585 
1586 typedef struct yo_io_t yo_io_t;
1587 struct yo_io_t {
1588   IOStream *ios;
1589   int flags;  /* used by sr_hook */
1590 };
1591 
1592 static yo_data_t *
yo_iostream(int iarg)1593 yo_iostream(int iarg)
1594 {
1595   /* create oxy wrapper for IOStream */
1596   IOStream *ios = YGetFile(sp-iarg);  /* will not fail */
1597   yo_io_t *io = yo_push_alloc(&yo_io_ops, sizeof(yo_io_t));
1598   io->ios = Ref(ios);
1599   io->flags = 0;
1600   yarg_swap(iarg+1, 0);
1601   yarg_drop(1);
1602   return yget_obj(iarg, &yo_uops);
1603 }
1604 
1605 static void
yo_io_dealloc(void * obj)1606 yo_io_dealloc(void *obj)
1607 {
1608   yo_io_t *io = obj;
1609   sp[1].ops = &dataBlockSym;
1610   sp[1].value.db = (DataBlock *)io->ios;
1611   io->ios = 0;
1612   sp++;
1613   yarg_drop(1);
1614 }
1615 
1616 static long
yo_io_count(void * obj)1617 yo_io_count(void *obj)
1618 {
1619   IOStream *file = ((yo_io_t *)obj)->ios;
1620   /* only used by restore all, which restores either record or non-record
1621    * variables, but not both
1622    * also used for save all
1623    */
1624   if (file->history) {
1625     HistoryInfo *history = file->history;
1626     if (history->nRecords>0 && history->recNumber>=0)
1627       file = history->child;
1628   }
1629   return file->dataTable.nItems;
1630 }
1631 
1632 static char *
yo_io_findn(void * obj,long mndx,long * iname)1633 yo_io_findn(void *obj, long mndx, long *iname)
1634 {
1635   IOStream *file = ((yo_io_t *)obj)->ios;
1636   long n;
1637   /* this is never actually used??  make it work like restore all */
1638   if (file->history) {
1639     HistoryInfo *history = file->history;
1640     if (history->nRecords>0 && history->recNumber>=0)
1641       file = history->child;
1642   }
1643   n = file->dataTable.nItems;
1644   mndx--;
1645   if (iname) *iname = -1;
1646   return (mndx<0 || mndx>=n)? 0 : file->dataTable.names[mndx];
1647 }
1648 
1649 extern void ReadGather(void *dst, void *srcM, long srcD, StructDef *base,
1650                        long number, const Strider *strider);
1651 
1652 static int
yo_io_getq(void * obj,const char * name,long iname)1653 yo_io_getq(void *obj, const char *name, long iname)
1654 {
1655   IOStream *file = ((yo_io_t *)obj)->ios;
1656   StructDef *model, *base;
1657   Dimension *dims;
1658   long address, number;
1659   void *memory;
1660   int no_recurse = 1;
1661 
1662   if (!HashFind(&file->dataTable, name, 0L)) {
1663     HistoryInfo *history = file->history;
1664     if (!history || history->nRecords<=0 || history->recNumber<0 ||
1665         !history->child) return 1;
1666     file = history->child;
1667     if (!HashFind(&file->dataTable, name, 0L)) return 1;
1668   }
1669   base = file->types[hashIndex].base;
1670   dims = file->types[hashIndex].dims;
1671   address = file->addresses[hashIndex] + file->offset;
1672   model = base->model;
1673   while (model->model) model = model->model;
1674 
1675   /* check for scalar types and simplify if possible */
1676   memory = 0;
1677   if (!dims) {
1678     Operations *ops = model->dataOps;
1679     if (ops==&doubleOps) {
1680       sp[1].ops = &doubleScalar;
1681       memory = &sp[1].value.d;
1682     } else if (ops==&longOps) {
1683       sp[1].ops = &longScalar;
1684       memory = &sp[1].value.l;
1685     } else if (ops==&intOps) {
1686       sp[1].ops = &intScalar;
1687       memory = &sp[1].value.i;
1688     }
1689   }
1690 
1691   /* otherwise, create an array to hold the result */
1692   if (!memory) {
1693     Array *array = NewArray(model, dims);
1694     sp[1].value.db = (DataBlock *)array;
1695     sp[1].ops= &dataBlockSym;
1696     memory = array->value.c;
1697     number = array->type.number;
1698   } else {
1699     number = 1;
1700   }
1701   sp++;
1702 
1703   ReadGather(memory, &no_recurse, address, base, number, (Strider *)0);
1704   return 0;
1705 }
1706 
1707 extern void WriteScatter(void *src, void *dstM, long dstD, StructDef *base,
1708                          long number, const Strider *strider);
1709 extern void SetSequentialWrite(IOStream *file, long last);
1710 
1711 static int
yo_io_setq(void * obj,const char * name,long iname,int iarg)1712 yo_io_setq(void *obj, const char *name, long iname, int iarg)
1713 {
1714   yo_io_t *io = obj;
1715   IOStream *file = io->ios;
1716   HistoryInfo *history = file->history;
1717   Symbol *s = sp - iarg;
1718   StructDef *base;
1719   long address;
1720   int no_recurse = 1;
1721 
1722   Operand op;
1723   int not_new;
1724 
1725   s->ops->FormOperand(s, &op);
1726   if (op.ops == &structDefOps) {
1727     if (!CopyStruct(file, (StructDef *)op.value))
1728       y_error("problem saving struct to binary file (name conflict?)");
1729     return 0;
1730   }
1731   if (!op.type.base)
1732     return 4;
1733 
1734   if (history) {
1735     if (io->flags & 4)
1736       y_error("no save,f (save all) to history record");
1737     if (history->nRecords<=0 || history->recNumber<0)
1738       y_error("file has no current record for save");
1739     file = history->child;
1740   }
1741 
1742   not_new = AddVariable(file, -1L, name, op.type.base, op.type.dims);
1743   if (not_new > 1)
1744     y_error("data type (struct) name conflict in save to binary file");
1745 
1746   base= file->types[hashIndex].base;
1747   address= file->addresses[hashIndex]+file->offset;
1748 
1749   if (not_new) {
1750     /* this is an assignment to an existing variable --
1751      * verify operand data type and number */
1752     long number = file->types[hashIndex].number;
1753     if (!EquivStruct(base, op.type.base) || number!=op.type.number)
1754       y_error("variable type or dimensions have changed since last save");
1755     /* possibly return 3 instead of error */
1756   }
1757 
1758   if (base->addressType==2)
1759     SetSequentialWrite(file, address+base->size*op.type.number);
1760   WriteScatter(op.value, &no_recurse, address, base, op.type.number,
1761                (Strider *)0);
1762 
1763   return 0;
1764 }
1765 
1766 static void
yo_sr_hook(void * obj,int flags)1767 yo_sr_hook(void *obj, int flags)
1768 {
1769   yo_io_t *io = obj;
1770   IOStream *file = io->ios;
1771   IOStream *child = file->history? file->history->child : 0;
1772   if (flags & 1) {           /* after restore */
1773     ClearPointees(file, 0);
1774     if (child) ClearPointees(child, 0);
1775   } else if (flags & 2) {    /* before save */
1776     io->flags = flags;
1777   } else {                   /* after save */
1778     io->flags = 0;
1779     ClearPointees(file, 1);
1780     FlushFile(file, 0);
1781     if (child) {
1782       ClearPointees(child, 1);
1783       FlushFile(child, 0);
1784     }
1785   }
1786 }
1787 
1788 /* ------------------------------------------------------------------------ */
1789 /* closure, invented by Eric Thiebaut */
1790 
1791 static void yoc_on_free(void *uo);
1792 static void yoc_on_extract(void *uo, char *name);
1793 static void yoc_on_eval(void *uo, int nargs);
1794 
1795 static y_userobj_t yoc_ops =
1796   { "closure", yoc_on_free, 0, yoc_on_eval, yoc_on_extract, 0 };
1797 
1798 typedef struct yoc_obj_t yoc_obj_t;
1799 struct yoc_obj_t {
1800   void *f, *d;
1801   long fndx, dndx;
1802 };
1803 
1804 int
yo_is_closure(int iarg)1805 yo_is_closure(int iarg)
1806 {
1807   return (yget_obj(iarg,0) == yoc_ops.type_name);
1808 }
1809 
1810 int
yo_closure(int farg,int darg)1811 yo_closure(int farg, int darg)
1812 {
1813   long fref = -1L;
1814   long dref = yget_ref(darg);
1815   int fid = yarg_func(farg);
1816   yoc_obj_t *co;
1817   if (!fid) {
1818     yo_ops_t *ops;
1819     if (yo_get(farg, &ops)) {
1820       fid = -1;
1821     } else if (yarg_string(farg)==1) {
1822       char *name = ygets_q(farg);
1823       if (name[0]=='o' && name[1]==':') name+=2, fid=-1;
1824       else fid = -2;
1825       fref = yget_global(name, 0L);
1826     } else {
1827       return 1;
1828     }
1829   }
1830   if (fid>0 || dref<0) {
1831     if (yarg_typeid(darg) >= 100) return 1;
1832     dref = -1L;
1833   }
1834   co = ypush_obj(yfunc_obj(&yoc_ops), sizeof(yoc_obj_t));
1835   co->f = (fref>=0)? 0 : yget_use(farg+1);
1836   co->fndx = fref;
1837   co->d = (dref>=0 && fid==-1)? 0 : yget_use(darg+1);
1838   co->dndx = dref;
1839   if (!co->d && dref<0)
1840     y_error("bad second argument passed to closure()");
1841   return 0;
1842 }
1843 
1844 static void
yoc_on_free(void * vco)1845 yoc_on_free(void *vco)
1846 {
1847   yoc_obj_t *co = vco;
1848   void *p = co->f;
1849   if (p) {
1850     co->f = 0;
1851     ydrop_use(p);
1852   }
1853   p = co->d;
1854   if (p) {
1855     co->d = 0;
1856     ydrop_use(p);
1857   }
1858 }
1859 
1860 static void
yoc_on_extract(void * vco,char * name)1861 yoc_on_extract(void *vco, char *name)
1862 {
1863   yoc_obj_t *co = vco;
1864   if (!strcmp(name, "function")) {
1865     if (!co->f && co->fndx>=0) ypush_global(co->fndx);
1866     else ykeep_use(co->f);
1867   } else if (!strcmp(name, "data")) {
1868     if (!co->d && co->dndx>=0) ypush_global(co->dndx);
1869     else ykeep_use(co->d);
1870   } else if (!strcmp(name, "function_name")) {
1871     char **q = ypush_q(0);
1872     if (co->fndx >= 0) q[0] = p_strcpy(globalTable.names[co->fndx]);
1873   } else if (!strcmp(name, "data_name")) {
1874     char **q = ypush_q(0);
1875     if (co->dndx >= 0) q[0] = p_strcpy(globalTable.names[co->dndx]);
1876   } else {
1877     y_error("unrecognized closure object member name");
1878   }
1879 }
1880 
1881 static void
yoc_on_eval(void * vco,int nargs)1882 yoc_on_eval(void *vco, int nargs)
1883 {
1884   Operand op;
1885   int i;
1886   yoc_obj_t *co = vco;
1887   long dref = co->dndx;
1888 
1889   if (co->fndx >= 0) {
1890     ypush_global(co->fndx);
1891     if (yarg_func(0)) dref = -1L;
1892   } else {
1893     ykeep_use(co->f);
1894   }
1895   /* sp[-nargs-1] is co, sp[0] is func */
1896   for (i=0 ; i<=nargs ; i++) yarg_swap(i, i+1);
1897   /* sp[-nargs-1] is func, sp[-nargs] is co (func moved back nargs+1 steps) */
1898 
1899   if (dref < 0) {
1900     ykeep_use(co->d);
1901   } else {  /* object(member,...) semantics */
1902     sp[1].ops = &referenceSym;
1903     sp[1].index = dref;
1904     sp++;
1905   }
1906   /* sp[-nargs-2] is func, sp[-nargs-1] is co, sp[0] is data */
1907   nargs++;
1908   yarg_swap(nargs, 0);
1909   yarg_drop(1);
1910   /* sp[-nargs] is func, sp[1-nargs] is data (with incremented nargs) */
1911 
1912   FormEvalOp(nargs, &op);
1913   op.ops->Eval(&op);
1914 }
1915 
1916 void
Y_closure(int argc)1917 Y_closure(int argc)
1918 {
1919   if (argc != 2)
1920     y_error("closure requires exactly two arguments");
1921   if (yo_closure(1, 0))
1922     y_error("illegal argument type in closure(func,data)");
1923 }
1924 
1925 /* ------------------------------------------------------------------------ */
1926