Getting things working
[RExecServer.git] / RInterpreter.m
blob71e38d91ac833b2ba8c04e0d95d8365c30d2252f
1 //
2 //  RInterpreter.m
3 //  RExecServer
4 //
5 //  Created by Byron Ellis on 6/25/07.
6 //  Copyright 2007 __MyCompanyName__. All rights reserved.
7 //
9 #import "RInterpreter.h"
10 #import "RDevice.h"
11 #import "DeviceWindowController.h"
12 #import "NSData+RSerialize.h"
13 #import "TerminalDelegate.h"
15 #define R_INTERFACE_PTRS 1
16 #define CSTACK_DEFNS     1
18 #include <Rinternals.h>
19 #include <Rinterface.h>
20 #include <R_ext/Utils.h>
21 #include <Rgraphics.h>
22 #include <R_ext/GraphicsDevice.h>
23 #include <R_ext/eventloop.h>
24 #include <R_ext/Rdynload.h>
25 #include <Rversion.h>
27 #include <pthread.h>
28 #include <signal.h>
31 #undef Boolean
32 #undef error
34 @interface RInterpreter (Private)
35 - (void)configure;
36 - (void)readyToEvaluate;
37 - (Rboolean)openDevice:(NewDevDesc *)dev withDisplay:(char *)display width:(double)width height:(double)height
38         pointsize:(double)ps family:(char*)family antialias:(Rboolean)antialias autorefresh:(Rboolean)autorefreash 
39         quartzpos:(int)quartzpos background:(int)bg;
40 - (void)registerInterface;
41 @end
43 @implementation RInterpreter
45 + (RInterpreter*)sharedInterpreter {
46         static RInterpreter *interp = nil;
47         if(nil == interp) interp = [[RInterpreter alloc] init];
48         return interp;
51 - (id)init {
52         if(nil == [super init]) return nil;
53         
54         //Some configuration defaults
55         bufferSize           = 2048;
56         buffer               = [[NSMutableAttributedString alloc] init];
59         suppressOutput       = NO;
60         allowTerminal        = NO;
61         vend                 = YES;
62         waiting              = YES;
63         delegate             = nil;
65         deviceList = [[NSMutableArray alloc] init];
66         evalLock   = [[NSLock alloc] init];
67         
68         _argc = 0;
69         _argv = NULL;
71         outputTag = [[NSDictionary alloc] initWithObjectsAndKeys:@"ROutput",@"RTextType",nil];
72         promptTag = [[NSDictionary alloc] initWithObjectsAndKeys:@"RInput",@"RTextType",nil];
73         errorTag  = [[NSDictionary alloc] initWithObjectsAndKeys:@"RError",@"RTextType",nil];
75         //Set some environment variables to their defaults if they are not presently set.
76         setenv("R_HOME",[[[NSBundle bundleWithIdentifier:@"org.r-project.R-framework"] resourcePath] UTF8String],0);
77         setenv("LANG",[[NSString stringWithFormat:@"%@.UTF-8",[[NSLocale currentLocale] localeIdentifier]] UTF8String],0);
78         return self;
83 - (BOOL)isConfigured { return configured; }
85 - (void)setArgv:(char**)argv argc:(int)argc {
86         _argc = argc;
87         _argv = argv;
90 - (id)delegate { return delegate; }
91 - (void)setDelegate:(id)aDelegate { delegate = aDelegate; }
93 - (long)bufferSize   { return bufferSize; }
94 - (void)setBufferSize:(long)aSize {
95         bufferSize = aSize;
97 - (BOOL)allowTerminal { return allowTerminal; }
98 - (void)setAllowTerminal:(BOOL)aBool { allowTerminal = aBool; }
100 - (BOOL)vend { return vend; }
101 - (void)setVend:(BOOL)aBool { vend = aBool; }
103 - (NSString*)homePath { return (NULL == getenv("R_HOME")) ? nil : [NSString stringWithUTF8String:getenv("R_HOME")]; }
104 - (void)setHomePath:(NSString*)aPath {
105         setenv("R_HOME",[aPath UTF8String],1);
108 - (NSString*)localeIdentifier { return (NULL == getenv("LANG")) ? nil : [NSString stringWithUTF8String:getenv("LANG")]; }
109 - (void)setLocaleIdentifier:(NSString*)aLocale {
110         setenv("LANG",[aLocale UTF8String],1);
113 - (void)_run {
114         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
115         [evalLock lock];
116         if(nil != delegate) [delegate didBeginEvaluationForInterpreter:self];
117         setup_Rmainloop();
118         [self registerInterface];
119         [pool release];
120         run_Rmainloop();
123 - (void)run {
124         if(NO == [self isConfigured]) [self configure];
125         if(YES == [self vend]) {
126                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
127                 if(NULL != getenv("RVENDNAME")) {
128                         vendName = [[NSString alloc] initWithUTF8String:getenv("RVENDNAME")];
129                         NSConnection *conn = [NSConnection defaultConnection];
130                         [conn setRootObject:self];
131                         if(NO == [conn registerName:vendName]) {
132                                 NSLog(@"Unable to register server as %@");
133                                 [vendName release];
134                                 vendName = nil;
135                         }
136                 } else {
137                         int vend_num = 0;
138                         NSConnection *conn = [NSConnection defaultConnection];
139                         [conn setRootObject:self];
140                         while(vend_num < 17) {
141                                 vendName = [[NSString alloc] initWithFormat:@"R Execution Server %d",++vend_num];
142                                 if(YES == [conn registerName:vendName]) {
143                                         break;
144                                 } else
145                                         [vendName release];
146                         }
147                         if(vend_num == 11) {
148                                 NSLog(@"Unable to register server. Presently, a maximum of 16 local execution servers are allowed per machine");
149                                 vendName = nil;
150                         }
151                 }
152                 [pool release];
153         }
154         //If we don't have a delegate yet, enter the event loop. The delegate (when it connects)
155         //should post a stop message via awakeConsole.
156         if(nil == delegate) {
157                 NSLog(@"Waiting for a delegate");
158                 waiting = YES;
159                 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
160                 [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"RExecServerStartedVending" object:vendName];
161                 [NSApp run];
162                 [pool release];
163         } else
164                 NSLog(@"Finished vending with delegate %@",[delegate description]);
165         
166         waiting = NO;
167         [self _run];
171 - (void)awakeConsole {
172         if(YES == waiting) {
173                 NSLog(@"Awake Console");
174                 //Post a stop event so that we fall out of the wait loop at the 
175                 [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined 
176                                                                                 location:NSMakePoint(0,0)
177                                                                                 modifierFlags:0
178                                                                                 timestamp:0 
179                                                                                 windowNumber:0 
180                                                                                 context:nil 
181                                                                                 subtype:1337 
182                                                                                 data1:0 
183                                                                                 data2:0] atStart:NO];   
184         }
187 - (void)evaluateInput:(NSString*)aString {
188         readerBufferUsed = [aString length];
189         if(readerBufferUsed > readerBufferLength) readerBufferUsed = readerBufferLength;
190         memcpy(readerBuffer,[aString UTF8String],sizeof(unsigned char)*readerBufferUsed);
191         [self readyToEvaluate];
194 - (NSArray*)devices { return deviceList; }
196 - (NSString*)serverName { return vendName; }
198 - (NSData*)serializeObjectWithName:(NSString*)anName error:(NSError**)anError {
199         const char *name = [anName UTF8String];
200         SEXP  obj  = findVar(install(name),R_GlobalEnv);
201         if(obj != R_UnboundValue) {
202                 PROTECT(obj);
203                 NSMutableData *data = [[NSMutableData alloc] init];
204                 [data serialize:obj];
205                 UNPROTECT(1);
206                 return [data autorelease];
207         } else { 
208                 return nil;
209         }
211 - (BOOL)deserializeObject:(NSData*)anObject withName:(NSString*)aName replace:(BOOL)shouldReplace error:(NSError**)error {
212         error = nil;
213         if(anObject == nil) {
214                 *error = [NSError errorWithDomain:@"org.r-project.RExecServer" code:1 userInfo:nil];
215                 return NO;
216         }
217         
218         
219         const char *name = [aName UTF8String];
220         SEXP  cur  = findVar(install(name),R_GlobalEnv);
221         if(cur != R_UnboundValue && NO == shouldReplace) {
222                 *error = [NSError errorWithDomain:@"org.r-project.RExecServer" code:1 userInfo:nil];
223                 return NO;
224         }
225         Rf_setVar(install(name),[anObject unserialize],R_GlobalEnv);
226         return YES;
229 - (void)copyObjectWithName:(NSString*)aName toServer:(NSString*)aServer error:(NSError**)error {
230         id theProxy = [[NSConnection rootProxyForConnectionWithRegisteredName:aServer host:nil] retain];
231         *error = nil;
232         NSData *serialized = [self serializeObjectWithName:aName error:error];
233         if(nil == error) 
234                 [theProxy deserializeObject:serialized withName:aName replace:YES error:error];
235         [theProxy release];
238 @end
241 #pragma mark Function Prototypes
242 void RInterp_Suicide(char*);
243 void RInterp_ShowMessage(char*);
244 void RInterp_FlushConsole();
245 void RInterp_WritePrompt(char*);
246 int  RInterp_ReadConsole(char*,unsigned char*,int,int);
247 void RInterp_ResetConsole();
248 void RInterp_WriteConsole(char*,int);
249 void RInterp_ClearerrConsole();
250 void RInterp_Busy();
251 void RInterp_CleanUp(SA_TYPE,int,int);
252 int  RInterp_ShowFiles(int,char**,char**,char*,Rboolean,char*);
253 int  RInterp_ChooseFile(int,char*,int);
254 int  RInterp_EditFile(char*);
255 void RInterp_System(char*);
256 void RInterp_ProcessEvents();
257 Rboolean RInterp_Device(NewDevDesc*,char*,double,double,double,char*,Rboolean,Rboolean,int,int);
258 void RInterp_DeviceParams(double*,double*,double*,char*,Rboolean*,Rboolean*,int*);
259 int  RInterp_CustomPrint(char *,SEXP);
260 SEXP RInterp_do_selectlist(SEXP,SEXP,SEXP,SEXP);
264 extern void     (*ptr_R_ProcessEvents)(void);
265 extern void     (*ptr_CocoaSystem)(char*);
266 extern Rboolean (*ptr_CocoaInnerQuartzDevice)(NewDevDesc*,char*,double,double,double,char*,Rboolean,Rboolean,int,int);
267 extern void     (*ptr_CocoaGetQuartzParameters)(double*,double*,double*,char*,Rboolean*,Rboolean*,int*);
268 extern int      (*ptr_Raqua_CustomPrint)(char *, SEXP);
270 extern DL_FUNC ptr_do_wsbrowser,
271     ptr_do_dataentry, ptr_do_browsepkgs, ptr_do_datamanger,
272     ptr_do_packagemanger, ptr_do_flushconsole, ptr_do_hsbrowser,
273     ptr_do_selectlist;
274         
277 extern void Rstd_WriteConsole(char*,int);
279 @implementation RInterpreter (Private)
281 - (void)_dummy { }
283 - (void)configure {
284         char *argv_orig[] = {"R","--gui=cocoa","--no-save","--no-restore-data"};
285         char **argv;
286         int   argc = 0;
287         
288         if(_argc > 0) {
289                 int i,j,has_gui=0,has_g=0;
290                 
291                 argc=_argc;
292                 for(i=1;i<_argc;i++) {
293                         if(strncmp(_argv[i],"-g",2)==0) 
294                                 has_g = 1; 
295                         else if(strncmp(_argv[i],"--gui",5)==0)
296                                 has_gui = 1;
297                 }
298                 if(has_g || has_gui)
299                         printf("warning: Execution server will ignore GUI settings.\n");
300                 if(has_g) argc -= 1; else if(!has_g && !has_gui) argc++;
301                 argv = malloc(sizeof(char*)*argc);
302                 
303                 j=0;
304                 for(i=0;i<_argc;i++) {
305                         if(strncmp(_argv[i],"-g",2)==0) {
306                                 i++;
307                         } else if(strncmp(_argv[i],"--gui",5)==0) {
308                         } else {
309                                 argv[j++] = _argv[i];
310                         }
311                 }
312                 argv[j++] = "--gui=cocoa";
313         } else {
314                 argc = 4;
315                 argv = argv_orig;
316         }
317         Rf_initialize_R(argc,argv);
318         //R_GUIType = "RExecServer";    //We are NOT the Aqua GUI, but not being the aqua gui is even more annoying.
319         if(YES == allowTerminal) {
320                 NSLog(@"Installing terminal delegate");
321                 TerminalDelegate *tempDel = [[TerminalDelegate alloc] init];
322                 [tempDel setReaderFunction:ptr_R_ReadConsole];
323                 [tempDel setWriterFunction:Rstd_WriteConsole];
324                 [tempDel setFlushFunction:ptr_R_FlushConsole];
325                 [self setDelegate:tempDel];
326         }
327         R_Outputfile  = NULL;
328         R_Consolefile = NULL;
330 #ifdef R_USING_TRAMPOLINE
331 //On systems supporting libffi we can generate a direct trampoline
332 //function that dispatches to the R level. This is coming later.
333 #else
334     ptr_R_Suicide                = RInterp_Suicide;
335     ptr_R_ShowMessage            = RInterp_ShowMessage;
336     ptr_R_ReadConsole            = RInterp_ReadConsole;
337     ptr_R_WriteConsole           = RInterp_WriteConsole;
338         ptr_R_WriteConsoleEx         = NULL;
339     ptr_R_ShowFiles              = RInterp_ShowFiles;
340     ptr_R_EditFile               = RInterp_EditFile;
341     ptr_R_ChooseFile             = RInterp_ChooseFile;
342     ptr_Raqua_CustomPrint        = RInterp_CustomPrint;
343         ptr_R_ProcessEvents          = RInterp_ProcessEvents;
344     ptr_CocoaInnerQuartzDevice   = RInterp_Device;
345     ptr_CocoaGetQuartzParameters = RInterp_DeviceParams;
346     ptr_CocoaSystem              = RInterp_System;
347 #endif
348         
349         //Put ourselves into a multithreaded state
350         //[NSThread detachNewThreadSelector:@selector(_dummy) toTarget:self withObject:nil];
353 - (void)writeConsole:(char*)output length:(int)aLength {
354         char c = output[aLength];
355         output[aLength] = '\0';
356         if(nil != delegate) [delegate appendString:[NSString stringWithUTF8String:output] ofType:0 forInterpreter:self];
357         output[aLength] = c;
360 - (int)readConsoleWithPrompt:(char*)aPrompt buffer:(unsigned char*)aBuffer length:(int)aLength history:(int)useHistory {
361         //Put those there for something that wants to write a string
362         readerPrompt = aPrompt;
363         readerBuffer = aBuffer;
364         readerBufferLength = aLength;
365         readerAddToHistory = useHistory;
366         
367         memset(readerBuffer,0,sizeof(unsigned char)*aLength);
368         
369         //Flush any drawing that may have happened since the last time we were here.
370         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
371         if(nil != delegate) [delegate didFinishEvaluationForInterpreter:self];
372         NSEnumerator *e = [deviceList objectEnumerator];
373         RDevice *dev;
374         while((dev = (RDevice*)[e nextObject]) != nil) {
375                 [dev flushDrawing];
376         }
377         [pool release];
378         [evalLock unlock];
379         pool = [[NSAutoreleasePool alloc] init];
380         if(nil != delegate) [delegate appendString:[NSString stringWithUTF8String:aPrompt] ofType:2 forInterpreter:self];
381         if(nil != delegate) [delegate didBeginWaitingForInputWithMaximumLength:readerBufferLength addToHistory:(useHistory == 1) ? YES : NO forInterpreter:self];
382         [NSApp run];
383         if(nil != delegate) [delegate didGetInputForInterpreter:self];
384         [evalLock lock];
385         if(nil != delegate) [delegate didBeginEvaluationForInterpreter:self];
386         [pool release];
387         return readerBufferUsed;
390 - (void)removeDevice:(NSNotification*)aNotify {
391         if(nil != delegate) [delegate didCloseDevice:[aNotify object] forInterpreter:self];
392         [deviceList removeObject:[aNotify object]];
394         
395 - (Rboolean)openDevice:(NewDevDesc *)dev withDisplay:(char *)display width:(double)width height:(double)height
396         pointsize:(double)ps family:(char*)family antialias:(Rboolean)antialias autorefresh:(Rboolean)autorefreash 
397         quartzpos:(int)quartzpos background:(int)bg {
398         NSString *aDisplay = [NSString stringWithUTF8String:display];
399         NSArray  *bits     = [[aDisplay componentsSeparatedByString:@":"] retain];
400         Class deviceClass = nil;
401         deviceClass = [RDevice deviceForDisplay:([bits count] < 2) ? @"" : [bits objectAtIndex:1]];
403         //Can't create device.
404         if(nil == deviceClass) return 0;
405         RDevice *rd = [[deviceClass alloc] initWithDevice:dev size:NSMakeSize(width,height) pointSize:ps 
406                 display:[bits objectAtIndex:0]
407                 target:[bits count] < 2 ? nil : [bits objectAtIndex:1] background:bg antialias:antialias];
408         [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeDevice:) name:@"RDeviceClosed" object:rd];
409         [deviceList addObject:rd];
410         if(nil != delegate) [delegate didOpenDevice:rd forInterpreter:self];
411         [rd finishOpening];
412         [rd release];
413         return TRUE;
416 - (void)flushConsole {
419 - (void)showMessage:(char*)aMsg {
420         [self writeConsole:aMsg length:strlen(aMsg)];
423 - (void)suicide:(char*)aMsg {
424         [self writeConsole:aMsg length:strlen(aMsg)];
427 - (void)resetConsole {
430 - (void)clearErrorConsole {
433 - (void)busy {
436 - (void)system:(char*)cmd {
437         system(cmd);
440 - (int)editFile:(char*)aFileName {
441         if(YES == allowTerminal) {
442                 NSLog(@"Editing file: %s",aFileName);
443                 return 0;
444         } else {
445                 return 0;
446         }
449 - (int)showFiles:(int)nfiles file:(char**)file headers:(char**)headers wtitle:(char*)wtitle del:(Rboolean)del pager:(char*)pager {
450         return 0;
453 - (int)customPrintType:(char*)aType list:(SEXP)aList {
454         NSLog(@"This is hosed for right now. Might need support from R itself.");
455         return 0;
458 - (SEXP)selectListWithCall:(SEXP)call op:(SEXP)op args:(SEXP)args rho:(SEXP)rho {
459         return R_NilValue;
462 - (void)readyToEvaluate {
463         [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined 
464                                                                         location:NSMakePoint(0,0)
465                                                                          modifierFlags:0
466                                                                          timestamp:0 
467                                                                          windowNumber:0 
468                                                                          context:nil 
469                                                                          subtype:1337 
470                                                                          data1:0 
471                                                                          data2:0] atStart:NO];  
474 SEXP RES_ServerName() {
475         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
476         const char *str = [[[RInterpreter sharedInterpreter] serverName] UTF8String];
477         SEXP ret;
478         PROTECT(ret = allocVector(STRSXP,1));
479         SET_STRING_ELT(ret,0,mkChar(str));
480         UNPROTECT(1);
481         [pool release];
482         return ret;
485 SEXP RES_CopyObject(SEXP name,SEXP server) {
486         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
487         NSString *aStr = [[NSString alloc] initWithUTF8String:CHAR(STRING_ELT(name,0))];
488         NSString *bStr = [[NSString alloc] initWithUTF8String:CHAR(STRING_ELT(server,0))];
489         NSError *error = nil;
490         [[RInterpreter sharedInterpreter] copyObjectWithName:aStr toServer:bStr error:&error];
491         [aStr release];
492         [bStr release];
493         [pool release];
494         if(nil != error)
495                 Rf_error("Problem copying object");
496         return R_NilValue;
499 static const R_CallMethodDef R_CallDef[] = {
500         {"RES_ServerName",(DL_FUNC)RES_ServerName,0},
501         {"RES_CopyObject",(DL_FUNC)RES_CopyObject,2},
502         NULL
505 extern void
506 R_addCallRoutine(DllInfo *info, const R_CallMethodDef * const croutine,void*sym);
508 - (void)registerInterface {
509         R_registerRoutines(R_getEmbeddingDllInfo(),NULL,R_CallDef,NULL,NULL);
512 @end
514 #ifdef R_USING_TRAMPOLINE
515 #else
516 void RInterp_Suicide(char*aMsg) { 
517         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
518         [[RInterpreter sharedInterpreter] suicide:aMsg]; 
519         [pool release];
521 void RInterp_ShowMessage(char*aMsg) { 
522         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
523         [[RInterpreter sharedInterpreter] showMessage:aMsg]; 
524         [pool release];
526 void RInterp_FlushConsole() { 
527         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
528         [[RInterpreter sharedInterpreter] flushConsole]; 
529         [pool release];
531 int  RInterp_ReadConsole(char*prompt,unsigned char*buffer,int length,int history) {
532         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
533         int ret = [[RInterpreter sharedInterpreter] readConsoleWithPrompt:prompt buffer:buffer length:length history:history];
534         [pool release];
535         return ret;
537 void RInterp_ResetConsole() { [[RInterpreter sharedInterpreter] resetConsole]; }
538 void RInterp_WriteConsole(char*buffer,int length) { 
539         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
540         [[RInterpreter sharedInterpreter] writeConsole:buffer length:length]; 
541         [pool release];
543 void RInterp_ClearerrConsole() { [[RInterpreter sharedInterpreter] clearErrorConsole]; }
544 void RInterp_Busy() { [[RInterpreter sharedInterpreter] busy]; }
545 void RInterp_CleanUp(SA_TYPE type,int a,int b) { }
546 int  RInterp_ShowFiles(int a,char**b,char**c,char*d,Rboolean e,char*f) { 
547         return [[RInterpreter sharedInterpreter] showFiles:a file:b headers:c wtitle:d del:e pager:f];
549 int  RInterp_ChooseFile(int a,char*b,int c) { return 0; }
550 int  RInterp_EditFile(char*a) { return [[RInterpreter sharedInterpreter] editFile:a]; }
551 void RInterp_System(char*a) { [[RInterpreter sharedInterpreter] system:a]; }
552 void RInterp_ProcessEvents() { 
553         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
554         [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined 
555                                                                         location:NSMakePoint(0,0)
556                                                                          modifierFlags:0
557                                                                          timestamp:0 
558                                                                          windowNumber:0 
559                                                                          context:nil 
560                                                                          subtype:1337 
561                                                                          data1:0 
562                                                                          data2:0] atStart:NO];
563         [NSApp run];
564         [pool release];
566 Rboolean RInterp_Device(NewDevDesc *dev,
567         char *display,double width,double height,
568         double ps,char *family,Rboolean antialias,Rboolean autorefresh,int quartzpos,int bg) {
569         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
570         Rboolean ret = [[RInterpreter sharedInterpreter] openDevice:dev
571          withDisplay:display width:width height:height pointsize:ps family:family
572           antialias:antialias autorefresh:autorefresh quartzpos:quartzpos background:bg];
573         [pool release];
574         return ret;
576 void RInterp_DeviceParams(double*a,double*b,double*c,char*d,Rboolean*e,Rboolean*f,int*g) { }
577 int  RInterp_CustomPrint(char *a,SEXP b) { return [[RInterpreter sharedInterpreter] customPrintType:a list:b]; }
579 SEXP RInterp_do_selectlist(SEXP call,SEXP op,SEXP args,SEXP rho) {
580         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
581         SEXP ret = [[RInterpreter sharedInterpreter] selectListWithCall:call op:op args:args rho:rho];
582         [pool release];
583         return ret;
586 #endif