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