1 /*
2 * $Id: ops3.c,v 1.2 2010-02-28 21:52:29 dhmunro Exp $
3 * Implement assignment operations:
4 *
5 * Assign = Increment += (and ++) Decrement -= (and --)
6 *
7 * Also, implement struct definition and various Eval operations.
8 */
9 /* Copyright (c) 2005, The Regents of the University of California.
10 * All rights reserved.
11 * This file is part of yorick (http://yorick.sourceforge.net).
12 * Read the accompanying LICENSE file for details.
13 */
14
15 #include "bcast.h"
16 #include "pstdlib.h"
17 #include "play.h"
18
19 /*--------------------------------------------------------------------------*/
20
21 extern VMaction Assign, Define;
22
23 extern BinaryOp AssignC, AssignS, AssignI, AssignL,
24 AssignF, AssignD, AssignZ, AssignQ, AssignP,
25 AssignSI, AssignX;
26
27 extern VMaction OpenStruct, DeclareMember, CloseStruct;
28 extern VMaction Eval, Deref, Address;
29 extern VMaction GetMember, DerefMember;
30
31 extern MemberOp GetMemberAY, GetMemberLV, GetMemberIO, GetMemberX;
32
33 extern BuiltIn Y_get_member;
34
35 extern void FormEvalOp(int nArgs, Operand *obj);
36
37 extern void BuildDimList(Symbol *stack, int nArgs); /* ops3.c */
38
39 /*--------------------------------------------------------------------------*/
40
41 extern Operand *FormOperandIS(Symbol *owner, Operand *op);
42 extern Operand *FormOperandLS(Symbol *owner, Operand *op);
43 extern Operand *FormOperandDS(Symbol *owner, Operand *op);
44 extern Operand *FormOperandDB(Symbol *owner, Operand *op);
45
46 extern void PopToI(Symbol *s);
47 extern void PopToL(Symbol *s);
48 extern void PopToD(Symbol *s);
49
50 static void DoAssign(LValue *lvalue, Symbol *rowner);
51
52 extern DataBlock *ForceToDB(Symbol *s);
53 static char *GetMemberName(void *db);
54
55 /*--------------------------------------------------------------------------*/
56 /* Assign */
57
58 /* Assignment syntax "x(index_list)= expression" assures that an LValue
59 is under the expression at the top of the stack. */
60
DoAssign(LValue * lvalue,Symbol * rowner)61 static void DoAssign(LValue *lvalue, Symbol *rowner)
62 {
63 StructDef *base= lvalue->type.base;
64 Operations *ops= base->dataOps;
65 Operand rhs;
66
67 /* rhs is ordinary operand */
68 rowner->ops->FormOperand(sp, &rhs);
69
70 /* for assignment, only makes sense to broadcast rhs */
71 if (rhs.ops->isArray && RightConform(lvalue->type.dims, &rhs))
72 YError("rhs not conformable with lhs in assign =");
73
74 /* Assign virtual functions assume their first parameter is an LValue*
75 rather than an Operation* (like all other BinaryOps). */
76 ops->Assign((Operand *)lvalue, &rhs);
77 }
78
Assign(void)79 void Assign(void)
80 {
81 Symbol *spl= sp-1;
82 LValue *lvalue= (LValue *)spl->value.db;
83
84 P_SOFTFPE_TEST;
85
86 /* guard against x(...)= expr, where x turns out to be a function */
87 if (spl->ops!=&dataBlockSym || lvalue->ops!=&lvalueOps)
88 YError("LHS of assignment not an l-value (scalar double, long, int?)");
89
90 DoAssign(lvalue, sp);
91 PopTo(spl); /* leaves (broadcast and retyped) rhs on top of stack */
92 }
93
94 #undef OPERATION
95 #define OPERATION(opname, typeD, ToType) \
96 void opname(Operand *l, Operand *r) \
97 { if (r->ops!=&typeD) r->ops->ToType(r); StoreLValue(l, r->value); }
98 /* Note-- parameter l is actually a cast LValue* in DoAssign */
99
100 OPERATION(AssignC, charOps, ToChar)
101 OPERATION(AssignS, shortOps, ToShort)
102 OPERATION(AssignI, intOps, ToInt)
103 OPERATION(AssignL, longOps, ToLong)
104 OPERATION(AssignF, floatOps, ToFloat)
105 OPERATION(AssignD, doubleOps, ToDouble)
106 OPERATION(AssignZ, complexOps, ToComplex)
107
108 static void AssignError(void);
AssignError(void)109 static void AssignError(void)
110 { YError("cannot convert rhs of assign = to pointer or string"); }
111
112 #undef OPERATION
113 #define OPERATION(opname, typeD) \
114 void opname(Operand *l, Operand *r) \
115 { if (r->ops!=&typeD) AssignError(); StoreLValue(l, r->value); }
116 /* Note-- parameter l is actually a cast LValue* in DoAssign */
117
OPERATION(AssignQ,stringOps)118 OPERATION(AssignQ, stringOps)
119 OPERATION(AssignP, pointerOps)
120
121 void AssignSI(Operand *l, Operand *r)
122 {
123 StructDef *base = l->type.base;
124 while (base->model) base = base->model;
125 if (r->ops->typeID>T_STRUCT || !StructEqual(base, r->type.base))
126 YError("rhs struct not equivalent to lhs struct in assign =");
127 StoreLValue(l, r->value);
128 }
129 /* Note-- parameter l is actually a cast LValue* in DoAssign */
130
AssignX(Operand * l,Operand * r)131 void AssignX(Operand *l, Operand *r)
132 { YError("(BUG) impossible lvalue->type.base in assign ="); }
133
134 /*--------------------------------------------------------------------------*/
135 /* Define */
136
137 /* Definition syntax "x= expression".
138 If the referenceSym points to an LValue, Define calls DoAssign.
139 Otherwise, Define replaces the referenced globTab entry with the
140 expression, which remains on the stack. */
141
Define(void)142 void Define(void)
143 {
144 Symbol *glob= &globTab[(pc++)->index];
145 P_SOFTFPE_TEST;
146 if (glob->ops==&dataBlockSym) {
147 DataBlock *db= glob->value.db;
148 if (db->ops==&lvalueOps) {
149 /* Note that an explicit UnDefine operation is required in order
150 for the interpreter to be able to ever get rid of a reference
151 to an LValue in globTab. This is provided as a part of a
152 non-kernal Yorick package, since it is not a crucial feature
153 of the language (see Y_reshape). */
154 DoAssign((LValue *)db, sp);
155 return;
156 } else {
157 glob->ops= &intScalar;
158 Unref(db);
159 }
160 }
161 if (sp->ops==&dataBlockSym) {
162 Array *array= (Array *)sp->value.db;
163 if (array->references && array->ops->isArray) {
164 /* copy non-temporary arrays to avoid unexpected aliasing */
165 Array *result= NewArray(array->type.base, array->type.dims);
166 glob->value.db= (DataBlock *)result;
167 array->type.base->Copy(array->type.base, result->value.c,
168 array->value.c, array->type.number);
169 } else {
170 if (array->ops==&lvalueOps) FetchLValue(array, sp);
171 glob->value.db= Ref(sp->value.db);
172 }
173 } else {
174 glob->value= sp->value;
175 }
176 glob->ops= sp->ops;
177 }
178
179 /*--------------------------------------------------------------------------*/
180
OpenStruct(void)181 void OpenStruct(void)
182 {
183 long index= (pc++)->index;
184 StructDef *base= AddStruct((IOStream *)0, globalTable.names[index], 0L);
185 DataBlock *db= 0;
186 if (!base) YError("(BUG?) unable to create struct for some reason");
187 PushDataBlock(base);
188 /* zap struct-building function */
189 if (globTab[index].ops==&dataBlockSym) db= globTab[index].value.db;
190 globTab[index].value.db= RefNC(&nilDB);
191 globTab[index].ops= &dataBlockSym;
192 Unref(db);
193 }
194
CloseStruct(void)195 void CloseStruct(void)
196 {
197 StructDef *base= (StructDef *)sp->value.db;
198 InstallStruct(base, (StructDef *)0);
199 Drop(1);
200 }
201
BuildDimList(Symbol * stack,int nArgs)202 void BuildDimList(Symbol *stack, int nArgs)
203 {
204 Dimension *tmp= tmpDims;
205 tmpDims= 0;
206 FreeDimension(tmp);
207
208 while (nArgs--) {
209 if (stack->ops==&referenceSym) ReplaceRef(stack);
210 if (stack->ops==&longScalar) {
211 if (stack->value.l<=0) goto badl;
212 tmpDims= NewDimension(stack->value.l, 1L, tmpDims);
213 } else if (stack->ops==&intScalar) {
214 if (stack->value.i<=0) goto badl;
215 tmpDims= NewDimension(stack->value.i, 1L, tmpDims);
216
217 } else if (stack->ops==&dataBlockSym) {
218 Operand op;
219 FormOperandDB(stack, &op);
220 if (op.ops==&rangeOps) {
221 Range *range= op.value;
222 long len;
223 if (range->rf || range->nilFlags || range->inc!=1)
224 YError("only min:max ranges allowed in dimension list");
225 len= range->max-range->min+1;
226 if (len<=0) goto badl;
227 tmpDims= NewDimension(len, range->min, tmpDims);
228
229 } else if (op.ops->promoteID<=T_LONG &&
230 (!op.type.dims || !op.type.dims->next)) {
231 long len;
232 op.ops->ToLong(&op);
233 if (!op.type.dims) {
234 len= *(long *)op.value;
235 if (len<=0) goto badl;
236 tmpDims= NewDimension(len, 1L, tmpDims);
237 } else {
238 long *dim= op.value;
239 long n= *dim++;
240 if (n>10 || n>=op.type.number)
241 YError("dimension list format [#dims, len1, len2, ...]");
242 while (n--) {
243 len= *dim++;
244 if (len<=0) goto badl;
245 tmpDims= NewDimension(len, 1L, tmpDims);
246 }
247 }
248
249 } else if (op.ops!=&voidOps) {
250 goto badl;
251 }
252 } else {
253 badl:
254 YError("bad dimension list");
255 }
256 stack++;
257 }
258 }
259
DeclareMember(void)260 void DeclareMember(void)
261 {
262 /* struct_def, member_name, dimlist, type */
263 int nDims= (pc++)->count;
264 StructDef *base, *memType= (StructDef *)sp->value.db;
265 Symbol *stack= sp-nDims; /* first dimension in dimlist */
266 Array *name;
267
268 /* get data type off top of stack */
269 if (sp->ops!=&dataBlockSym || memType->ops!=&structDefOps)
270 YError("invalid member data type in struct definition");
271
272 /* build tmpDims from next nDims stack elements (under data type) */
273 BuildDimList(stack, nDims);
274
275 /* get member name from under dimlist-- guaranteed a non-0 string
276 by the parser */
277 stack--;
278 name= (Array *)stack->value.db;
279
280 /* get struct itself from under member name-- guaranteed by the parser */
281 stack--;
282 base= (StructDef *)stack->value.db;
283
284 /* add name to hash table for this struct */
285 if (AddMember(base, -1L, name->value.q[0], memType, tmpDims))
286 YError("duplicate member name in struct definition");
287
288 /* clean stack,
289 leaving struct itself for next DeclareMember or CloseStruct */
290 Drop(nDims+2);
291 }
292
293 /*--------------------------------------------------------------------------*/
294
StructEqual(StructDef * l,StructDef * r)295 int StructEqual(StructDef *l, StructDef *r)
296 {
297 long i, nItems;
298
299 /* return quickly if same */
300 if (l==r) return 1;
301
302 /* if number of items, size, or alignment differs, structs are not equal */
303 nItems= l->table.nItems;
304 if (nItems!=r->table.nItems || l->size!=r->size ||
305 l->alignment!=r->alignment ||
306 (nItems==0 && (l->order!=r->order || l->fpLayout!=r->fpLayout)))
307 return 0;
308
309 /* otherwise, the structs are equal if and only if all offsets are
310 the same and all member types are equal */
311 for (i=0 ; i<nItems ; i++)
312 if (l->offsets[i]!=r->offsets[i] ||
313 !StructEqual(l->members[i].base, r->members[i].base)) return 0;
314 return 1;
315 }
316
317 /*--------------------------------------------------------------------------*/
318
ForceToDB(Symbol * s)319 DataBlock *ForceToDB(Symbol *s)
320 {
321 OpTable *ops= s->ops;
322 if (ops==&referenceSym) { ReplaceRef(s); ops= s->ops; }
323 if (ops==&doubleScalar) {
324 Array *array= NewArray(&doubleStruct, (Dimension *)0);
325 array->value.d[0]= s->value.d;
326 s->value.db= (DataBlock *)array;
327 s->ops= &dataBlockSym;
328 return (DataBlock *)array;
329 } else if (ops==&longScalar) {
330 Array *array= NewArray(&longStruct, (Dimension *)0);
331 array->value.l[0]= s->value.l;
332 s->value.db= (DataBlock *)array;
333 s->ops= &dataBlockSym;
334 return (DataBlock *)array;
335 } else if (ops==&intScalar) {
336 Array *array= NewArray(&intStruct, (Dimension *)0);
337 array->value.i[0]= s->value.i;
338 s->value.db= (DataBlock *)array;
339 s->ops= &dataBlockSym;
340 return (DataBlock *)array;
341 } else if (ops==&dataBlockSym) {
342 return s->value.db;
343 } else {
344 YError("(BUG) bad Symbol type in ForceToDB");
345 }
346 return 0;
347 }
348
Address(void)349 void Address(void)
350 {
351 Array *pointer, *array;
352 void *value= 0;
353
354 /* The & operator can only return the address of an Array. */
355 if (sp->ops==&referenceSym) {
356 /* Taking the address of a variable x, where x is a scalar constant,
357 causes x to be replaced by an Array. This is obscure, but there
358 is no other obvious way to get both the efficiency of the scalar
359 Symbols, AND the reference-count safety of Yorick pointers. Notice
360 that if the address of a scalar is taken, the efficient
361 representation is lost. */
362 Symbol *glob= &globTab[sp->index];
363 if (glob->ops==&dataBlockSym) array= (Array *)glob->value.db;
364 else array= (Array *)ForceToDB(glob);
365 ReplaceRef(sp);
366 } else {
367 /* The address of a Yorick temporary actually makes more sense than
368 the address of a scalar variable. The semantics of Yorick pointers
369 are really dramatically different than C pointers... Sigh. */
370 if (sp->ops==&dataBlockSym) array= (Array *)sp->value.db;
371 else array= (Array *)ForceToDB(sp);
372 }
373
374 if (array->ops->isArray) {
375 value= array->value.c;
376 } else if (array->ops==&lvalueOps) {
377 /* Yes, an LValue address is always the address of temporary data. */
378 array= FetchLValue(array, sp);
379 value= array->value.c;
380 } else if (array->ops==&voidOps) {
381 /* Since dereferencing 0 gives nil, the address of nil is 0. */
382 value= 0;
383 } else {
384 YError("no address (&) for non-array object");
385 }
386
387 pointer= NewArray(&pointerStruct, (Dimension *)0);
388 /* array->references remains the same-- the owner of array merely
389 switches from sp->value.db to the pointer value
390 EXCEPT for nil, which loses a reference... */
391 pointer->value.p[0]= value;
392 sp->value.db= (DataBlock *)pointer;
393 if (!value) UnrefNC(&nilDB); /* ...since sp->value.db was &nilDB */
394 }
395
Deref(void)396 void Deref(void)
397 {
398 Array *array, *pnte= 0;
399
400 if (sp->ops==&referenceSym) ReplaceRef(sp);
401 if (sp->ops==&dataBlockSym) {
402 array= (Array *)sp->value.db;
403 if (array->ops==&lvalueOps) array= FetchLValue(array, sp);
404 if (array->ops==&pointerOps && !array->type.dims) {
405 pnte= Pointee(array->value.p[0]);
406 } else {
407 array= 0;
408 }
409 } else {
410 array= 0;
411 }
412
413 if (!array)
414 YError("cannot dereference (* or ->) non-pointer or non-scalar pointer");
415
416 if ((DataBlock *)pnte != &nilDB)
417 sp->value.db= (DataBlock *)NewLValueM(pnte, pnte->value.c,
418 pnte->type.base, pnte->type.dims);
419 else
420 sp->value.db= RefNC(&nilDB);
421 Unref(array);
422 }
423
424 /*--------------------------------------------------------------------------*/
425
Eval(void)426 void Eval(void)
427 {
428 int nArgs= (pc++)->count;
429 Operand obj;
430 FormEvalOp(nArgs, &obj);
431 obj.ops->Eval(&obj);
432 }
433
434 /*--------------------------------------------------------------------------*/
435
GetMemberName(void * db)436 static char *GetMemberName(void *db)
437 {
438 Array *array= db;
439 return array->value.q[0];
440 }
441
GetMember(void)442 void GetMember(void)
443 {
444 char *name= GetMemberName((pc++)->constant->value.db);
445 Operand op;
446 FormEvalOp(0, &op);
447 op.ops->GetMember(&op, name);
448 }
449
Y_get_member(int nArgs)450 void Y_get_member(int nArgs)
451 {
452 char *name;
453 Operand op;
454 if (nArgs!=2) YError("get_member function requires exactly two arguments");
455 name= YGetString(sp);
456 FormEvalOp(1, &op);
457 op.ops->GetMember(&op, name);
458 Drop(1);
459 }
460
DerefMember(void)461 void DerefMember(void)
462 {
463 char *name= GetMemberName((pc++)->constant->value.db);
464 Operand op;
465 Deref(); /* now guaranteed to have an Array or Void on stack */
466 FormEvalOp(0, &op);
467 op.ops->GetMember(&op, name);
468 }
469
470 static void GetMembErr(long nItems);
GetMembErr(long nItems)471 static void GetMembErr(long nItems)
472 {
473 if (nItems)
474 YError("right operand to . or -> names non-existent member");
475 else
476 YError("left operand to . (->) not a struct (struct*)");
477 }
478
GetMemberAY(Operand * op,char * name)479 void GetMemberAY(Operand *op, char *name)
480 {
481 Array *array= op->value;
482 StructDef *subbase, *base= array->type.base;
483 char *address= array->value.c;
484 LValue *result;
485 Dimension *dims;
486 Member *member;
487
488 if (!HashFind(&base->table, name, 0L)) GetMembErr(base->table.nItems);
489
490 address+= base->offsets[hashIndex];
491 member= &base->members[hashIndex];
492 subbase= member->base;
493 if (member->dims) dims= CopyDims(array->type.dims, Ref(member->dims), 1);
494 else dims= Ref(array->type.dims);
495
496 result= PushDataBlock(NewLValueM(array, address, subbase, dims));
497 FreeDimension(dims);
498 if (subbase->size!=base->size) {
499 result->strider= NewStrider(base->size, array->type.number);
500 if (member->number>1)
501 result->strider->next= NewStrider(subbase->size, member->number);
502 }
503
504 PopTo(op->owner);
505 }
506
GetMemberLV(Operand * op,char * name)507 void GetMemberLV(Operand *op, char *name)
508 {
509 LValue *lvalue= op->value;
510 StructDef *subbase, *base= lvalue->type.base;
511 Strider *strider;
512 LValue *result;
513 Dimension *dims;
514 Member *member;
515 int addressType= base->addressType;
516 long offset;
517
518 if (addressType>1) {
519 /* sequential objects in a disk file must be read as a whole */
520 op->value= FetchLValue(lvalue, op->owner); /* sic - see FormEvalOp */
521 GetMemberAY(op, name);
522 return;
523 }
524
525 if (!HashFind(&base->table, name, 0L)) GetMembErr(base->table.nItems);
526
527 member= &base->members[hashIndex];
528 offset= base->offsets[hashIndex];
529 subbase= member->base;
530 if (member->dims) dims= CopyDims(lvalue->type.dims, Ref(member->dims), 1);
531 else dims= Ref(lvalue->type.dims);
532
533 result= PushDataBlock(addressType?
534 NewLValueD(lvalue->address.d, subbase, dims) :
535 NewLValueM(lvalue->owner, lvalue->address.m,
536 subbase, dims));
537 FreeDimension(dims);
538 if (addressType) result->address.d+= offset;
539 else result->address.m+= offset;
540 if (subbase->size!=base->size) {
541 if (!lvalue->strider && lvalue->type.number>1)
542 lvalue->strider= NewStrider(base->size, lvalue->type.number);
543 if (member->number>1)
544 strider= NewStrider(subbase->size, member->number);
545 else
546 strider= 0;
547 } else {
548 strider= 0;
549 }
550 result->strider= CopyStrider(lvalue->strider, strider);
551
552 PopTo(op->owner);
553 }
554
GetMemberIO(Operand * op,char * name)555 void GetMemberIO(Operand *op, char *name)
556 {
557 IOStream *file= op->value;
558 long address;
559 Member *member;
560
561 if (file->history) {
562 /* check history child data table first */
563 HistoryInfo *history= file->history;
564 IOStream *child= history->child;
565 if (HashFind(&child->dataTable, name, 0L) &&
566 history->recNumber>=0 && history->recNumber<history->nRecords)
567 file= child;
568 else if (!HashFind(&file->dataTable, name, 0L))
569 file= 0;
570 } else {
571 if (!HashFind(&file->dataTable, name, 0L)) file= 0;
572 }
573
574 if (!file) YError("no such variable in binary file");
575
576 address= file->addresses[hashIndex]+file->offset;
577 member= &file->types[hashIndex];
578
579 PushDataBlock(NewLValueD(address, member->base, member->dims));
580 PopTo(op->owner);
581 }
582
583 /* ARGSUSED */
GetMemberX(Operand * op,char * name)584 void GetMemberX(Operand *op, char *name)
585 {
586 YError("left operand to . or -> has illegal data type");
587 }
588
589 /*--------------------------------------------------------------------------*/
590