1 /***************************************************************************
2 prognode_lexpr.cpp - LExpr functions
3 -------------------
4 begin : July 22 2002
5 copyright : (C) 2011 by Marc Schellens
6 email : m_schellens@users.sf.net
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "includefirst.hpp"
19
20 // from GDLInterpreter::l_expr
21 #include <cassert>
22 #include <string>
23
24 #include "dinterpreter.hpp"
25 #include "prognodeexpr.hpp"
26 #include "basegdl.hpp"
27 #include "arrayindexlistt.hpp"
28 //#include "envt.hpp"
29 #include "gdlexception.hpp"
30 #include "nullgdl.hpp"
31
32 // illegal
LExpr(BaseGDL * right)33 BaseGDL** ProgNode::LExpr( BaseGDL* right)
34 // default:
35 {
36 // case ARRAYDEF:
37 // case EXPR:
38 // case NSTRUC:
39 // case NSTRUC_REF:
40 // case POSTDEC:
41 // case POSTINC:
42 // case STRUC:
43 // case DEC:
44 // case INC:
45 // case CONSTANT:
46 throw GDLException( this, "Expression not allowed as l-value.",
47 true,false);
48 return NULL; // avoid compiler warning
49 }
50
51
LExpr(BaseGDL * right)52 BaseGDL** QUESTIONNode::LExpr( BaseGDL* right)
53 //case QUESTION:
54 {
55 ProgNodeP branch = this->GetThisBranch();
56 return branch->LExpr( right);
57 }
58
LExpr(BaseGDL * right)59 BaseGDL** ARRAYEXPRNode::LExpr( BaseGDL* right) // 'right' is not owned
60 //case ARRAYEXPR:
61 {
62 //res=l_array_expr(_t, right);
63 if( right == NULL)
64 throw GDLException( this, "Indexed expression not allowed in this context.", true,false);
65
66 ArrayIndexListT* aL;
67 ArrayIndexListGuard guard;
68 BaseGDL** res;
69 // try{
70 // res=interpreter->l_indexable_expr( this->getFirstChild());
71 res = this->getFirstChild()->LEval(); // throws
72 if( *res == NULL)
73 { // ERROR
74 // check not needed for SYSVAR
75 ProgNodeP _t = this->getFirstChild();
76 assert( _t->getType() != GDLTokenTypes::SYSVAR);
77 if( _t->getType() == GDLTokenTypes::VARPTR)
78 {
79 GDLException ex( _t, "Common block variable is undefined: "+
80 interpreter->CallStackBack()->GetString( *res),true,false);
81 ex.SetArrayexprIndexeeFailed( true);
82 throw ex;
83 }
84 if( _t->getType() == GDLTokenTypes::VAR)
85 {
86 GDLException ex( _t, "Variable is undefined: "+
87 interpreter->CallStackBack()->GetString(_t->varIx),true,false);
88 ex.SetArrayexprIndexeeFailed( true);
89 throw ex;
90 }
91 GDLException ex( _t, "Heap variable is undefined: "+interpreter->Name(res),true,false);
92 ex.SetArrayexprIndexeeFailed( true);
93 throw ex;
94 }
95
96 if( (*res)->IsAssoc())
97 {
98 aL=interpreter->arrayindex_list( this->getFirstChild()->getNextSibling(), false);
99 }
100 else
101 {
102 if( (*res)->Type() == GDL_OBJ && (*res)->StrictScalar())
103 {
104 // check for _overloadBracketsLeftSide
105 DObj s = (*static_cast<DObjGDL*>(*res))[0]; // is StrictScalar()
106 // if( s != 0) // no overloads for null object
107 // {
108 // DStructGDL* oStructGDL= GDLInterpreter::GetObjHeapNoThrow( s);
109 // if( oStructGDL != NULL) // if object not valid -> default behaviour
110 // {
111 // DStructDesc* desc = oStructGDL->Desc();
112 // DPro* bracketsLeftSideOverload = static_cast<DPro*>(desc->GetOperator( OOBracketsLeftSide));
113 DSubUD* bracketsLeftSideOverload = static_cast<DSubUD*>(GDLInterpreter::GetObjHeapOperator( s, OOBracketsLeftSide));
114 if( bracketsLeftSideOverload != NULL)
115 {
116 bool internalDSubUD = bracketsLeftSideOverload->GetTree()->IsWrappedNode();
117
118 // _overloadBracketsLeftSide
119 IxExprListT indexList;
120 interpreter->arrayindex_list_overload( this->getFirstChild()->getNextSibling(), indexList);
121 ArrayIndexListGuard guard(this->getFirstChild()->getNextSibling()->arrIxListNoAssoc);
122
123 // hidden SELF is counted as well
124 int nParSub = bracketsLeftSideOverload->NPar();
125 assert( nParSub >= 1); // SELF
126 // int indexListSizeDebug = indexList.size();
127 // indexList.size() + OBJREF + RVALUE > regular paramters w/o SELF
128 if( (indexList.size() + 2) > nParSub - 1)
129 {
130 indexList.Cleanup();
131 throw GDLException( this, bracketsLeftSideOverload->ObjectName() +
132 ": Incorrect number of arguments.",
133 false, false);
134 }
135
136 DObjGDL* self;
137 Guard<BaseGDL> selfGuard;
138 if( internalDSubUD)
139 {
140 self = static_cast<DObjGDL*>(*res); // internal subroutines behave well
141 }
142 else
143 {
144 self = static_cast<DObjGDL*>(*res)->Dup(); // res should be not changeable via SELF
145 selfGuard.Reset( self);
146 }
147
148 // adds already SELF parameter
149 EnvUDT* newEnv= new EnvUDT( this, bracketsLeftSideOverload, &self);
150 // Guard<EnvUDT> newEnvGuard( newEnv);
151
152 // parameters
153 newEnv->SetNextParUnchecked( res); // OBJREF parameter
154 // Dup() here is not optimal
155 // avoid at least for internal overload routines (which do/must not change RVALUE)
156 if( internalDSubUD)
157 newEnv->SetNextParUnchecked( &right); // RVALUE parameter, as reference to prevent cleanup in newEnv
158 else
159 newEnv->SetNextParUnchecked( right->Dup()); // RVALUE parameter, as value
160 // pass as reference would be more efficient, but as the data might
161 // be deleted in bracketsLeftSideOverload it is not possible.
162 // BaseGDL* rightCopy = right;
163 // newEnv->SetNextParUnchecked( &rightCopy); // RVALUE parameter
164 for( SizeT p=0; p<indexList.size(); ++p)
165 newEnv->SetNextParUnchecked( indexList[p]); // takes ownership
166
167 StackGuard<EnvStackT> stackGuard(interpreter->CallStack());
168 interpreter->CallStack().push_back( newEnv);
169
170 // make the call
171 interpreter->call_pro(static_cast<DSubUD*>(newEnv->GetPro())->GetTree());
172
173 if( !internalDSubUD && self != selfGuard.Get())
174 {
175 // always put out warning first, in case of a later crash
176 Warning( "WARNING: " + bracketsLeftSideOverload->ObjectName() +
177 ": Assignment to SELF detected (GDL session still ok).");
178 // assignment to SELF -> self was deleted and points to new variable
179 // which it owns
180 selfGuard.Release();
181 if( static_cast<BaseGDL*>(self) != NullGDL::GetSingleInstance())
182 selfGuard.Reset(self);
183 }
184
185 return res;
186 }
187 }
188
189 // aL=interpreter->arrayindex_list_noassoc( this->getFirstChild()->getNextSibling());
190
191 // IxExprListT cleanupList; // for cleanup
192 ProgNodeP ax = this->getFirstChild()->getNextSibling();
193 aL=interpreter->arrayindex_list( ax, true);
194
195 }
196 guard.reset(aL);
197
198 try {
199 aL->AssignAt( *res, right);
200 }
201 catch( GDLException& ex)
202 {
203 ex.SetErrorNodeP( this);
204 throw ex;
205 }
206 //_retTree = _t->getNextSibling();
207 return res;
208 }
209 // default ...Grab version
210
LExpr(BaseGDL * right)211 BaseGDL** SYSVARNode::LExpr( BaseGDL* right)
212 //case SYSVAR:
213 {
214 if( right == NULL)
215 throw GDLException( this, "System variable not allowed in this context.",
216 true,false);
217
218 BaseGDL** res=this->LEval(); //l_sys_var(this);
219 Guard<BaseGDL> conv_guard; //( rConv);
220 BaseGDL* rConv = right;
221 if( !(*res)->EqType( right))
222 {
223 rConv = right->Convert2( (*res)->Type(), BaseGDL::COPY);
224 conv_guard.Reset( rConv);
225 }
226 if( right->N_Elements() != 1 && ((*res)->N_Elements() != right->N_Elements()))
227 {
228 throw GDLException( this, "Conflicting data structures: <"+
229 right->TypeStr()+" "+right->Dim().ToString()+">, !"+
230 this->getText(),true,false);
231 }
232 (*res)->AssignAt( rConv); // linear copy
233 this->var->DoCallback();
234 return res;
235 }
236 // default ...Grab version
237
238 // BaseGDL** XXXNode::LExpr( BaseGDL* right)
239 // // case FCALL:
240 // // case FCALL_LIB:
241 // // case MFCALL:
242 // // case MFCALL_PARENT:
243 // // case DEREF:
244 // // case VAR:
245 // // case VARPTR:
246 // {
247 // BaseGDL** res=this->LEval(); //l_simple_var(_t);
248 // //_retTree = _t->getNextSibling();
249 // if( right != NULL && right != (*res))
250 // {
251 // delete *res;
252 // *res = right->Dup();
253 // }
254 // return res;
255 // }
256
257 // #define LEXPRGRAB \
258 // { BaseGDL** res=this->LEval(); \
259 // if( right != NULL && right != (*res)) \
260 // { delete *res; *res = right;} \
261 // return res;}
262 //
263 // BaseGDL** FCALLNode::LExprGrab( BaseGDL* right)
264 // LEXPRGRAB
265 // BaseGDL** MFCALLNode::LExprGrab( BaseGDL* right)
266 // LEXPRGRAB
267 // BaseGDL** MFCALL_PARENTNode::LExprGrab( BaseGDL* right)
268 // LEXPRGRAB
269 // BaseGDL** FCALL_LIBNode::LExprGrab( BaseGDL* right)
270 // LEXPRGRAB
271 // BaseGDL** DEREFNode::LExprGrab( BaseGDL* right)
272 // LEXPRGRAB
273 // BaseGDL** VARNode::LExprGrab( BaseGDL* right)
274 // LEXPRGRAB
275 // BaseGDL** VARPTRNode::LExprGrab( BaseGDL* right)
276 // LEXPRGRAB
277 // #undef LEXPRGRAB
278
279 #define LEXPR \
280 { BaseGDL** res=this->LEval(); \
281 if( right != NULL && right != (*res)) \
282 { GDLDelete(*res); *res = right->Dup();} \
283 return res;}
284
LExpr(BaseGDL * right)285 BaseGDL** FCALLNode::LExpr( BaseGDL* right)
286 LEXPR
287 BaseGDL** MFCALLNode::LExpr( BaseGDL* right)
288 LEXPR
289 BaseGDL** MFCALL_PARENTNode::LExpr( BaseGDL* right)
290 LEXPR
291 BaseGDL** FCALL_LIBNode::LExpr( BaseGDL* right)
292 LEXPR
293 BaseGDL** DEREFNode::LExpr( BaseGDL* right)
294 LEXPR
295 BaseGDL** VARNode::LExpr( BaseGDL* right)
296 LEXPR
297 BaseGDL** VARPTRNode::LExpr( BaseGDL* right)
298 LEXPR
299 #undef LEXPR
300
301 BaseGDL** ARRAYEXPR_FCALLNode::LExpr( BaseGDL* right)
302 {
303 if( fcallNodeFunIx >= 0)
304 return fcallNode->FCALLNode::LExpr( right);
305 else if( fcallNodeFunIx == -2)
306 {
307 return arrayExprNode->ARRAYEXPRNode::LExpr( right);
308 }
309
310 assert( fcallNodeFunIx == -1);
311 try{
312 BaseGDL** res = arrayExprNode->ARRAYEXPRNode::LExpr( right);
313 fcallNodeFunIx = -2; // mark as ARRAYEXPR succeeded
314 return res;
315 }
316 catch( GDLException& ex)
317 {
318 if( !ex.GetArrayexprIndexeeFailed())
319 {
320 fcallNodeFunIx = -2; // mark as ARRAYEXPR succeeded
321 throw ex;
322 }
323 try{
324 BaseGDL** res = fcallNode->FCALLNode::LExpr( right);
325 fcallNodeFunIx = fcallNode->funIx;
326 return res;
327 } // keep FCALL if already compiled (but runtime error)
328 catch( GDLException& innerEx)
329 {
330 if(fcallNode->funIx >= 0)
331 {
332 fcallNodeFunIx = fcallNode->funIx;
333 throw innerEx;
334 }
335 std::string msg = "Ambiguous: " + ex.ANTLRException::toString() +
336 " or: " + innerEx.ANTLRException::toString();
337 throw GDLException(this,msg,true,false);
338 }
339 }
340 }
341
342
LExpr(BaseGDL * right)343 BaseGDL** ARRAYEXPR_MFCALLNode::LExpr( BaseGDL* right)
344 {
345 return interpreter->l_arrayexpr_mfcall(this, right);
346 }
347 // default ...Grab version
348
LExpr(BaseGDL * right)349 BaseGDL** DOTNode::LExpr( BaseGDL* right)
350 {
351 if( right == NULL)
352 throw GDLException( this, "Struct expression not allowed in this context.",
353 true,false);
354
355 ProgNodeP _t = this->getFirstChild();
356
357 //SizeT nDot = tIn->nDot;
358 Guard<DotAccessDescT> aD( new DotAccessDescT(nDot+1));
359
360 //interpreter->l_dot_array_expr(_t, aD.get());
361
362 ArrayIndexListT* aL;
363 BaseGDL** rP;
364 if( _t->getType() == GDLTokenTypes::ARRAYEXPR)
365 {
366 // rP=l_indexable_expr(_t->getFirstChild());
367 rP = _t->getFirstChild()->LEval(); // throws
368 if( *rP == NULL)
369 { // ERROR
370 BaseGDL** res = rP;
371 _t = _t->getFirstChild();
372 // check not needed for SYSVAR
373 assert( _t->getType() != GDLTokenTypes::SYSVAR);
374 if( _t->getType() == GDLTokenTypes::VARPTR)
375 {
376 GDLException ex( _t, "Common block variable is undefined: "+
377 interpreter->CallStackBack()->GetString( *res),true,false);
378 ex.SetArrayexprIndexeeFailed( true);
379 throw ex;
380 }
381 if( _t->getType() == GDLTokenTypes::VAR)
382 {
383 GDLException ex( _t, "Variable is undefined: "+
384 interpreter->CallStackBack()->GetString(_t->GetVarIx()),true,false);
385 ex.SetArrayexprIndexeeFailed( true);
386 throw ex;
387 }
388 GDLException ex( _t, "Variable is undefined: "+interpreter->Name(res),true,false);
389 ex.SetArrayexprIndexeeFailed( true);
390 throw ex;
391 }
392
393 // aL=arrayindex_list(_t->getFirstChild()->getNextSibling());
394 bool handled = false;
395 if( !(*rP)->IsAssoc() && (*rP)->Type() == GDL_OBJ && (*rP)->StrictScalar())
396 {
397
398 // check for _overloadBracketsLeftSide
399 DObj s = (*static_cast<DObjGDL*>(*rP))[0]; // is StrictScalar()
400 DSubUD* bracketsLeftSideOverload = static_cast<DSubUD*>(GDLInterpreter::GetObjHeapOperator( s, OOBracketsLeftSide));
401 if( bracketsLeftSideOverload != NULL)
402 {
403 bool internalDSubUD = bracketsLeftSideOverload->GetTree()->IsWrappedNode();
404
405 // _overloadBracketsLeftSide
406 IxExprListT indexList;
407 interpreter->arrayindex_list_overload( _t->getFirstChild()->getNextSibling(), indexList);
408 ArrayIndexListGuard guard(_t->getFirstChild()->getNextSibling()->arrIxListNoAssoc);
409
410 // hidden SELF is counted as well
411 int nParSub = bracketsLeftSideOverload->NPar();
412 assert( nParSub >= 1); // SELF
413
414 // indexList.size() + OBJREF + RVALUE > regular paramters w/o SELF
415 if( (indexList.size() + 2) > nParSub - 1)
416 {
417 indexList.Cleanup();
418 throw GDLException( this, bracketsLeftSideOverload->ObjectName() +
419 ": Incorrect number of arguments.",
420 false, false);
421 }
422
423 DObjGDL* self;
424 Guard<BaseGDL> selfGuard;
425 if( internalDSubUD)
426 {
427 self = static_cast<DObjGDL*>(*rP); // internal subroutines behave well
428 }
429 else
430 {
431 self = static_cast<DObjGDL*>(*rP)->Dup(); // res should be not changeable via SELF
432 selfGuard.Reset( self);
433 }
434
435 // adds already SELF parameter
436 EnvUDT* newEnv= new EnvUDT( this, bracketsLeftSideOverload, &self);
437 // Guard<EnvUDT> newEnvGuard( newEnv);
438
439 // parameters
440 // special: we are in dot access here
441 // signal to _overloadBracketsLeftSide by setting OBJREF to NULL
442 BaseGDL* returnOBJREF = NULL;
443 newEnv->SetNextParUnchecked( &returnOBJREF); // OBJREF parameter
444 // Dup() here is not optimal
445 // avoid at least for internal overload routines (which do/must not change RVALUE)
446
447 BaseGDL* rValueNull = NULL;
448 newEnv->SetNextParUnchecked( rValueNull); // RVALUE parameter NULL, as value
449
450 for( SizeT p=0; p<indexList.size(); ++p)
451 newEnv->SetNextParUnchecked( indexList[p]); // takes ownership
452
453 StackGuard<EnvStackT> stackGuard(interpreter->CallStack());
454 interpreter->CallStack().push_back( newEnv);
455
456 // make the call
457 interpreter->call_pro(static_cast<DSubUD*>(newEnv->GetPro())->GetTree());
458
459 if( !internalDSubUD && self != selfGuard.Get())
460 {
461 // always put out warning first, in case of a later crash
462 Warning( "WARNING: " + bracketsLeftSideOverload->ObjectName() +
463 ": Assignment to SELF detected (GDL session still ok).");
464 // assignment to SELF -> self was deleted and points to new variable
465 // which it owns
466 selfGuard.Release();
467 if( static_cast<BaseGDL*>(self) != NullGDL::GetSingleInstance())
468 selfGuard.Reset(self);
469 }
470
471 if( returnOBJREF == NULL || returnOBJREF->Type() != GDL_PTR)
472 GDLException ex( _t, "OBJREF must return a PTR to the STRUCT to access.",true,false);
473
474 DPtr vID = (*static_cast<DPtrGDL*>(returnOBJREF))[0];
475 delete returnOBJREF;
476
477 BaseGDL* structToAccess = interpreter->GetHeap( vID);
478
479 interpreter->SetRootL( _t, aD.get(), structToAccess, NULL);
480 handled = true;
481 }
482 } // if( (*rP)->Type() == GDL_OBJ && (*rP)->StrictScalar())
483 if( !handled)
484 {
485 // regular (non-object) case
486 aL=interpreter->arrayindex_list( _t->getFirstChild()->getNextSibling(),!(*rP)->IsAssoc());
487 interpreter->SetRootL( _t, aD.get(), *rP, aL);
488 }
489 }
490 else
491 // case ARRAYEXPR_MFCALL:
492 // case DEREF:
493 // case EXPR:
494 // case FCALL:
495 // case FCALL_LIB:
496 // case MFCALL:
497 // case MFCALL_PARENT:
498 // case SYSVAR:
499 // case VAR:
500 // case VARPTR:
501 {
502 // rP=l_indexable_expr(_t);
503 rP = _t->LEval(); // throws
504 if( *rP == NULL)
505 { // ERROR
506 BaseGDL** res = rP;
507 // check not needed for SYSVAR
508 assert( _t->getType() != GDLTokenTypes::SYSVAR);
509 if( _t->getType() == GDLTokenTypes::VARPTR)
510 {
511 GDLException ex( _t, "Common block variable is undefined: "+
512 interpreter->CallStackBack()->GetString( *res),true,false);
513 ex.SetArrayexprIndexeeFailed( true);
514 throw ex;
515 }
516 if( _t->getType() == GDLTokenTypes::VAR)
517 {
518 GDLException ex( _t, "Variable is undefined: "+
519 interpreter->CallStackBack()->GetString(_t->GetVarIx()),true,false);
520 ex.SetArrayexprIndexeeFailed( true);
521 throw ex;
522 }
523 GDLException ex( _t, "Variable is undefined: "+interpreter->Name(res),true,false);
524 ex.SetArrayexprIndexeeFailed( true);
525 throw ex;
526 }
527 interpreter->SetRootL( _t, aD.get(), *rP, NULL);
528 }
529 ProgNodeP left=_t;
530 _t = _t->getNextSibling();
531 for( int d=0; d<nDot; ++d)
532 {
533 // if ((_t->getType() == ARRAYEXPR || _t->getType() == EXPR ||
534 // _t->getType() == IDENTIFIER)) {
535 interpreter->tag_array_expr(_t, aD.get());
536 _t = interpreter->GetRetTree();
537 // }
538 // else {
539 // break;
540 // }
541 }
542 aD->ADAssign( right);
543 // case SYSVAR: sysvar is now updated, run callback.
544 if( left->getType() == GDLTokenTypes::SYSVAR) left->var->DoCallback();
545 //res=NULL;
546 //SetRetTree( tIn->getNextSibling());
547 return NULL;
548 }
549 // default ...Grab version
550
LExpr(BaseGDL * right)551 BaseGDL** ASSIGNNode::LExpr( BaseGDL* right)
552 {
553 ProgNodeP _t = this->getFirstChild();
554 if( NonCopyNode(_t->getType()))
555 {
556 BaseGDL* e1 = _t->EvalNC();
557 _t = _t->getNextSibling();
558 }
559 else
560 {
561 BaseGDL* e1;
562 BaseGDL** ref =_t->EvalRefCheck(e1);
563 if( ref == NULL)
564 GDLDelete(e1);
565 _t = _t->getNextSibling();
566 }
567 return _t->LExpr( right); //l_expr(_t, right);
568 }
569
570 // something like: (( ((aFUNorVAR(aVAR))) =e1 ))=right
LExpr(BaseGDL * right)571 BaseGDL** ASSIGN_ARRAYEXPR_MFCALLNode::LExpr( BaseGDL* right)
572 {
573 ProgNodeP _t = this->getFirstChild();
574
575 if( NonCopyNode(_t->getType()))
576 {
577 BaseGDL* e1 = _t->EvalNC();
578 _t = _t->getNextSibling();
579 }
580 else
581 {
582 // e1 must be calculated due to possible side effects, but the result isn't used or even accessible
583 BaseGDL* e1;
584 BaseGDL** ref =_t->EvalRefCheck(e1);
585 if( ref == NULL)
586 GDLDelete(e1);
587 _t = _t->getNextSibling();
588 }
589 ProgNodeP l = _t;
590 BaseGDL** res;
591 // try MFCALL
592 try
593 {
594 res=interpreter->l_arrayexpr_mfcall_as_mfcall( l);
595 if( right != (*res))
596 {
597 GDLDelete(*res);
598 *res = right->Dup();
599 }
600 }
601 catch( GDLException& ex)
602 {
603 // try ARRAYEXPR
604 try
605 {
606 res=interpreter->l_arrayexpr_mfcall_as_arrayexpr(l, right);
607 }
608 catch( GDLException& ex2)
609 {
610 throw GDLException(ex.toString() + " or "+ex2.toString());
611 }
612 }
613 //SetRetTree( tIn->getNextSibling());
614 return res;
615 }
616
617
LExpr(BaseGDL * right)618 BaseGDL** ASSIGN_REPLACENode::LExpr( BaseGDL* right)
619 // case ASSIGN_REPLACE:
620 {
621 ProgNodeP _t = this->getFirstChild();
622
623 BaseGDL** res;
624 {
625 BaseGDL* e1;
626 BaseGDL** ref =_t->EvalRefCheck(e1);
627 if( ref != NULL)
628 e1 = *ref;
629
630 _t =_t->getNextSibling();
631
632 res =_t->LEval(); //l_function_call(_t);
633 if( *res != e1 && ref == NULL)
634 GDLDelete(e1);
635 }
636
637 if( right != (*res))
638 {
639 GDLDelete(*res);
640 assert( right != NULL);
641 *res = right->Dup();
642 }
643 return res;
644 }
645
646
647
648 // l_expr finish /////////////////////////////////////////////
649
650
651