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