Package pyraf :: Module cl2py
[hide private]
[frames] | no frames]

Source Code for Module pyraf.cl2py

   1  """cl2py.py: Translate IRAF CL program to Python 
   2   
   3  $Id: cl2py.py 1244 2010-07-26 18:43:58Z sontag $ 
   4   
   5  R. White, 1999 December 20 
   6  """ 
   7  from __future__ import division # confidence high 
   8   
   9  import cStringIO, os, sys 
  10   
  11  from generic import GenericASTTraversal 
  12  from clast import AST 
  13  from cltoken import Token 
  14  import clscan, clparse 
  15  from clcache import codeCache 
  16   
  17  from pytools.irafglobals import Verbose 
  18  from pytools import basicpar, minmatch, irafutils 
  19  import irafpar, pyrafglobals 
  20   
  21  # The parser object can be constructed once and used many times. 
  22  # The other classes have instance variables (e.g. lineno in CLScanner), 
  23  # so using a single instance could screw up if several threads are trying 
  24  # to use the same object. 
  25  # 
  26  # I handled this in the CLScanner class by creating cached versions 
  27  # of the various scanners that are stateless. 
  28   
  29  _parser = None 
  30   
31 -def cl2py(filename=None, string=None, parlist=None, parfile="", mode="proc", 32 local_vars_dict=None, local_vars_list=None, usecache=1):
33 34 """Read CL program from file and return pycode object with Python equivalent 35 36 filename: Name of the CL source file or a filehandle from which the 37 source code can be read. 38 string: String containing the source code. Either filename or string must be 39 specified; if both are specified, only filename is used 40 parlist: IrafParList object with list of parameters (which may have already 41 been defined from a .par file) 42 parfile: Name of the .par file used to define parlist. parlist may be 43 defined even if parfile is null, but a null parfile is interpreted 44 to mean that the parameter definitions in the CL script should 45 override the parlist. If parfile is not null, it is an error if 46 the CL script parameters conflict with the parlist. 47 mode: Mode of translation. Default "proc" creates a procedure script 48 (which defines a Python function.) Normally CL scripts will be 49 translated using this default. If mode is "single" then the 50 necessary environment is assumed to be set and the Python 51 code simply gets executed directly. This is used in the 52 CL compatibility mode and other places where a single line of 53 CL must be executed. 54 Mode also determines whether parameter sets are saved in calls 55 to CL tasks. In "single" mode parameters do get saved; in 56 "proc" mode they do not get saved. This is intended to be 57 consistent with the behavior of the IRAF CL, where parameter 58 changes in scripts are not preserved. 59 local_vars_dict, local_vars_list: Initial definitions of local variables. 60 May be modified by declarations in the CL code. This is used only for 61 "single" mode to allow definitions to persist across statements. 62 usecache: Set to false value to omit use of code cache for either saving 63 or retrieving code. This is useful mainly for compiler testing. 64 """ 65 66 global _parser, codeCache 67 68 if _parser is None: 69 _parser = clparse.getParser() 70 71 if mode not in ["proc", "single"]: 72 raise ValueError("Mode = `%s', must be `proc' or `single'" % (mode,)) 73 74 if not filename in (None, ''): 75 if isinstance(filename,str): 76 efilename = os.path.expanduser(filename) 77 if usecache: 78 index, pycode = codeCache.get(efilename,mode=mode) 79 if pycode is not None: 80 if Verbose>1: 81 print efilename,"filename found in CL script cache" 82 return pycode 83 else: 84 index = None 85 fh = open(efilename) 86 clInput = fh.read() 87 fh.close() 88 elif hasattr(filename,'read'): 89 clInput = filename.read() 90 if usecache: 91 index, pycode = codeCache.get(filename,mode=mode,source=clInput) 92 if pycode is not None: 93 if Verbose>1: 94 print filename,"filehandle found in CL script cache" 95 return pycode 96 else: 97 index = None 98 if hasattr(filename,'name'): 99 efilename = filename.name 100 else: 101 efilename = '' 102 else: 103 raise TypeError('filename must be a string or a filehandle') 104 elif string is not None: 105 #if not isinstance(string,str): 106 #raise TypeError('string must be a string') 107 clInput = string 108 efilename = 'string_proc' # revisit this setting (tik #24), maybe '' ? 109 if usecache: 110 index, pycode = codeCache.get(None,mode=mode,source=clInput) 111 if pycode is not None: 112 if Verbose>1: 113 print "Found in CL script cache: ",clInput.strip()[:20] 114 return pycode 115 else: 116 index = None 117 else: 118 raise ValueError('Either filename or string must be specified') 119 120 if mode == "single": 121 taskObj = 'cl' 122 else: 123 taskObj = None 124 125 # tokenize and parse to create the abstract syntax tree 126 scanner = clscan.CLScanner() 127 tokens = scanner.tokenize(clInput) 128 tree = _parser.parse(tokens, fname=efilename) 129 # add filename to tree root 130 tree.filename = efilename 131 132 # first pass -- get variables 133 vars = VarList(tree,mode,local_vars_list,local_vars_dict,parlist) 134 135 # check variable list for consistency with the given parlist 136 # this may change the vars list 137 _checkVars(vars, parlist, parfile) 138 139 # second pass -- check all expression types 140 # type info is added to tree 141 TypeCheck(tree, vars, efilename) 142 143 # third pass -- generate python code 144 tree2python = Tree2Python(tree, vars, efilename, taskObj) 145 146 # just keep the relevant fields of Tree2Python output 147 # attach tokens to the code object too 148 pycode = Pycode(tree2python) 149 150 # add to cache 151 if index is not None: codeCache.add(index, pycode) 152 pycode.index = index 153 if Verbose>1: 154 print "File `%s' compiled by cl2py" % efilename 155 return pycode
156
157 -def checkCache(filename, pycode):
158 159 """Returns true if pycode is up-to-date""" 160 161 global codeCache 162 if pycode is None: 163 return 0 164 index = codeCache.getIndex(filename) 165 return (index is not None) and (pycode.index == index)
166 167
168 -class Container:
169 170 """Simple container class (no methods) for holding picklable objects""" 171 172 pass
173 174
175 -class Pycode:
176 177 """Container for Python CL translation""" 178
179 - def __init__(self, tree2python):
180 181 self.code = tree2python.code 182 self.vars = Container() 183 self.vars.local_vars_dict = tree2python.vars.local_vars_dict 184 self.vars.local_vars_list = tree2python.vars.local_vars_list 185 self.vars.parList = tree2python.vars.parList 186 self.vars.proc_name = tree2python.vars.proc_name 187 self.vars.has_proc_stmt = tree2python.vars.has_proc_stmt
188
189 - def setFilename(self, filename):
190 191 """Set the filename used for parameter list 192 193 This is used by codeCache, which needs to be able to read a Pycode 194 object created from some other file and attach it to the current file. 195 """ 196 197 self.vars.parList.setFilename(filename)
198 199
200 -def _checkVars(vars, parlist, parfile):
201 """Check variable list for consistency with the given parlist""" 202 203 # if there is no parfile specified, the parlist was created by default 204 # if parlist is None, the parfile was empty 205 # in either case, just use the parameter list specified in the CL code 206 207 if (not parfile) or (parlist is None): return 208 209 # parfile and parlist are specified, so create a new 210 # list of procedure variables from parlist 211 212 # check for consistency with the CL code if there was a procedure stmt 213 if vars.has_proc_stmt and not parlist.isConsistent(vars.parList): 214 # note we continue even if parameter lists are inconsistent. 215 # That agrees with IRAF's approach, in which the .par file 216 # overrides the CL script in determining parameters... 217 #XXX Maybe could improve this by allowing certain types of 218 #XXX mismatches (e.g. additional parameters) but not others 219 #XXX (name or type disagreements for the same parameters.) 220 if Verbose>0: 221 sys.stdout.flush() 222 sys.stderr.write("Parameters from CL code inconsistent " 223 "with .par file for task %s\n" % vars.getProcName()) 224 sys.stderr.flush() 225 226 # create copies of the list and dictionary 227 plist = parlist.getParList() 228 newlist = [] 229 newdict = {} 230 for par in plist: 231 newlist.append(par.name) 232 newdict[par.name] = Variable(irafParObject=par) 233 vars.proc_args_list = newlist 234 vars.proc_args_dict = newdict 235 # add mode, $nargs, other special parameters to all tasks 236 vars.addSpecialArgs() 237 # Check for local variables that conflict with parameters 238 vars.checkLocalConflict() 239 vars.parList = parlist
240
241 -class FindLineNumber(GenericASTTraversal):
242 243 """Helper class to find first line number in an AST""" 244
245 - class FoundIt(Exception): pass
246
247 - def __init__(self,ast):
248 GenericASTTraversal.__init__(self,ast) 249 self.lineno = 0 250 try: 251 self.preorder() 252 except self.FoundIt: 253 pass
254
255 - def default(self,node):
256 if hasattr(node,'lineno'): 257 self.lineno = node.lineno 258 raise self.FoundIt
259
260 -class ErrorTracker:
261 262 """Mixin class that does error tracking during AST traversal""" 263
264 - def _error_init(self):
265 self.errlist = [] 266 self.warnlist = []
267
268 - def error(self, msg, node=None):
269 270 """Add error to the list with line number""" 271 272 if not hasattr(self, 'errlist'): self._error_init() 273 self.errlist.append((self.getlineno(node),msg))
274
275 - def warning(self, msg, node=None):
276 277 """Add warning to the list with line number""" 278 279 if not hasattr(self, 'errlist'): self._error_init() 280 self.warnlist.append((self.getlineno(node),"Warning: %s" % msg))
281
282 - def getlineno(self, node):
283 # find terminal token that contains the line number 284 if node: 285 return FindLineNumber(node).lineno 286 else: 287 return 0
288
289 - def errorappend(self, other):
290 291 """Add errors from another ErrorTracker""" 292 293 if not hasattr(other, 'errlist'): return 294 if not hasattr(self, 'errlist'): self._error_init() 295 self.errlist.extend(other.errlist) 296 self.warnlist.extend(other.warnlist)
297
298 - def printerrors(self):
299 300 """Print all warnings and errors and raise SyntaxError if errors were found""" 301 302 if not hasattr(self,'errlist'): 303 return 304 if self.errlist: 305 self.errlist.extend(self.warnlist) 306 self.errlist.sort() 307 try: 308 errmsg = ["Error in CL script %s" % self.filename] 309 except AttributeError: 310 errmsg = ["Error in CL script"] 311 for lineno, msg in self.errlist: 312 if lineno: 313 errmsg.append("%s (line %d)" % (msg, lineno)) 314 else: 315 errmsg.append(msg) 316 raise SyntaxError("\n".join(errmsg)) 317 elif self.warnlist: 318 self.warnlist.sort() 319 try: 320 warnmsg = ["Warning in CL script %s" % self.filename] 321 except AttributeError: 322 warnmsg = ["Warning in CL script"] 323 for lineno, msg in self.warnlist: 324 if lineno: 325 warnmsg.append("%s (line %d)" % (msg, lineno)) 326 else: 327 warnmsg.append(msg) 328 warnmsg = "\n".join(warnmsg) 329 sys.stdout.flush() 330 sys.stderr.write(warnmsg) 331 if warnmsg[-1:] != '\n': sys.stderr.write('\n')
332 333
334 -class ExtractProcInfo(GenericASTTraversal):
335 336 """Extract name and args from procedure statement""" 337
338 - def __init__(self, ast):
339 GenericASTTraversal.__init__(self, ast) 340 self.preorder()
341
342 - def n_proc_stmt(self, node):
343 # get procedure name and list of argument names 344 self.proc_name = node[1].attr 345 self.proc_args_list = [] 346 if len(node[2]): 347 self.preorder(node[2]) 348 self.prune()
349
350 - def n_IDENT(self, node):
351 self.proc_args_list.append(irafutils.translateName(node.attr))
352 353 _longTypeName = { 354 "s": "string", 355 "f": "file", 356 "struct": "struct", 357 "i": "int", 358 "b": "bool", 359 "r": "real", 360 "d": "double", 361 "gcur": "gcur", 362 "imcur": "imcur", 363 "ukey": "ukey", 364 "pset": "pset", 365 } 366
367 -class Variable:
368 369 """Container for properties of a variable""" 370
371 - def __init__(self, name=None, type=None, mode="h", array_size=None, 372 init_value=None, list_flag=0, min=None, max=None, 373 prompt=None, enum=None, irafParObject=None):
374 if irafParObject is not None: 375 # define the variable info from an IrafPar object 376 ipo = irafParObject 377 self.name = ipo.name 378 if ipo.type[:1] == "*": 379 self.type = _longTypeName[ipo.type[1:]] 380 self.list_flag = 1 381 else: 382 self.type = _longTypeName[ipo.type] 383 self.list_flag = 0 384 if isinstance(ipo, basicpar.IrafArrayPar): 385 self.shape = ipo.shape 386 else: 387 self.shape = None 388 self.init_value = ipo.value 389 self.options = minmatch.MinMatchDict({ 390 "mode": ipo.mode, 391 "min": ipo.min, 392 "max": ipo.max, 393 "prompt": ipo.prompt, 394 "enum": ipo.choice, 395 "length": None, }) 396 else: 397 # define from the parameters 398 self.name = name 399 self.type = type 400 self.shape = array_size 401 self.list_flag = list_flag 402 self.options = minmatch.MinMatchDict({ 403 "mode": mode, 404 "min": min, 405 "max": max, 406 "prompt": prompt, 407 "enum": enum, 408 "length": None, }) 409 self.init_value = init_value
410
411 - def getName(self):
412 """Get name without translations""" 413 return irafutils.untranslateName(self.name)
414
415 - def toPar(self, strict=0):
416 """Convert this variable to an IrafPar object""" 417 return irafpar.makeIrafPar(self.init_value, 418 datatype=self.type, 419 name=self.getName(), 420 array_size=self.shape, 421 list_flag=self.list_flag, 422 mode=self.options["mode"], 423 min=self.options["min"], 424 max=self.options["max"], 425 enum=self.options["enum"], 426 prompt=self.options["prompt"], 427 strict=strict)
428
429 - def procLine(self):
430 """Return a string usable as parameter declaration with 431 default value in the function definition statement""" 432 433 name = irafutils.translateName(self.name) 434 if self.shape is None: 435 if self.init_value is None: 436 return name + "=None" 437 else: 438 return name + "=" + `self.init_value` 439 else: 440 # array 441 arg = name + "=[" 442 if self.init_value is None: 443 arglist = ["INDEF"]*len(self) 444 else: 445 arglist = [] 446 for iv in self.init_value: 447 arglist.append(`iv`) 448 return arg + ", ".join(arglist) + "]"
449
450 - def parDefLine(self, filename=None, strict=0, local=0):
451 """Return a list of string arguments for makeIrafPar""" 452 453 name = irafutils.translateName(self.name) 454 arglist = [name, 455 "datatype=" + `self.type`, 456 "name=" + `self.getName()` ] 457 # if local is set, use the default initial value instead of name 458 # also set mode="u" for locals so they never prompt 459 if local: 460 arglist[0] = `self.init_value` 461 self.options["mode"] = "u" 462 if self.shape is not None: 463 arglist.append("array_size=" + `self.shape`) 464 if self.list_flag: 465 arglist.append("list_flag=" + `self.list_flag`) 466 keylist = self.options.keys() 467 keylist.sort() 468 for key in keylist: 469 option = self.options[key] 470 if option is not None: 471 arglist.append(key + "=" + `self.options[key]`) 472 if filename: arglist.append("filename=" + `filename`) 473 if strict: arglist.append("strict=" + `strict`) 474 return arglist
475
476 - def __repr__(self):
477 s = self.type + " " 478 if self.list_flag: s = s + "*" 479 s = s + self.name 480 if self.init_value is not None: 481 s = s + " = " + `self.init_value` 482 optstring = "{" 483 for key, value in self.options.items(): 484 if (value is not None) and (key != "mode" or value != "h"): 485 # optstring = optstring + " " + key + "=" + str(value) 486 optstring = optstring + " " + key + "=" + str(value) 487 if len(optstring) > 1: 488 s = s + " " + optstring + " }" 489 return s
490
491 - def __len__(self):
492 array_size = 1 493 if self.shape: 494 for d in self.shape: 495 array_size = array_size*d 496 return array_size
497
498 -class ExtractDeclInfo(GenericASTTraversal, ErrorTracker):
499 500 """Extract list of variable definitions from parameter block""" 501
502 - def __init__(self, ast, var_list, var_dict, filename):
503 GenericASTTraversal.__init__(self, ast) 504 self.var_list = var_list 505 n = len(var_list) 506 self.var_dict = var_dict 507 self.filename = filename 508 self.preorder() 509 self.printerrors()
510
511 - def n_declaration_stmt(self, node):
512 self.current_type = node[0].attr
513
514 - def _get_dims(self, node, rv=None):
515 # expand array shape declaration 516 if len(node)>1: 517 return self._get_dims(node[0]) + (int(node[2]),) 518 else: 519 return (int(node[0]),)
520
521 - def n_decl_spec(self, node):
522 var_name = node[1] 523 name = irafutils.translateName(var_name[0].attr) 524 if len(var_name) > 1: 525 # array declaration 526 shape = tuple(self._get_dims(var_name[2])) 527 else: 528 # apparently not an array (but this may change later 529 # if multiple initial values are found) 530 shape = None 531 if self.var_dict.has_key(name): 532 if self.var_dict[name]: 533 self.error("Variable `%s' is multiply declared" % name, node) 534 self.prune() 535 else: 536 # existing but undefined entry comes from procedure line 537 # set mode = "a" by default 538 self.var_dict[name] = Variable(name, self.current_type, 539 array_size=shape, mode="a") 540 else: 541 self.var_list.append(name) 542 self.var_dict[name] = Variable(name, self.current_type, 543 array_size=shape) 544 self.current_var = self.var_dict[name] 545 self.preorder(node[0]) # list flag 546 self.preorder(node[2]) # initialization 547 self.preorder(node[3]) # declaration options 548 self.prune()
549
550 - def n_list_flag(self, node):
551 if len(node) > 0: 552 self.current_var.list_flag = 1 553 self.prune()
554
555 - def n_decl_init_list(self, node):
556 # begin list of initial values 557 if self.current_var.init_value is not None: 558 # oops, looks like this was already initialized 559 errmsg = \ 560 "%s: Variable `%s' has more than one set of initial values" % \ 561 (self.filename, self.current_var.name,) 562 self.error(errmsg, node) 563 else: 564 self.current_var.init_value = []
565
566 - def n_decl_init_list_exit(self, node):
567 # convert from list to scalar if not an array 568 # also convert all the initial values from tokens to native form 569 v = self.current_var 570 ilist = v.init_value 571 if len(ilist) == 1 and v.shape is None: 572 try: 573 v.init_value = _convFunc(v, ilist[0]) 574 except ValueError, e: 575 self.error("Bad initial value for variable `%s': %s" % 576 (v.name, e), node) 577 else: 578 # it is an array, set size or pad initial values 579 if v.shape is None: 580 v.shape = (len(ilist),) 581 elif len(v) > len(ilist): 582 for i in range(len(v)-len(ilist)): 583 v.init_value.append(None) 584 elif len(v) < len(ilist): 585 self.error("Variable `%s' has too many initial values" % (v.name,), node) 586 else: 587 try: 588 for i in range(len(v.init_value)): 589 v.init_value[i] = _convFunc(v, v.init_value[i]) 590 except ValueError, e: 591 self.error("Bad initial value for array variable `%s': %s" % 592 (v.name, e), node)
593
594 - def n_decl_init_value(self, node):
595 # initial value is token with value 596 vnode = node[0] 597 if isinstance(vnode, Token): 598 self.current_var.init_value.append(vnode) 599 else: 600 # have to create a new token for sign, number 601 self.current_var.init_value.append( 602 Token(type=vnode[1].type, attr=vnode[0].type+vnode[1].attr, 603 lineno=vnode[0].lineno)) 604 self.prune()
605
606 - def n_decl_option(self, node):
607 optname = node[0].attr 608 vnode = node[2] 609 if isinstance(vnode, Token): 610 optvalue = vnode.get() 611 else: 612 # have to combine sign, number 613 if vnode[0] == "-": 614 optvalue = - vnode[1].get() 615 else: 616 optvalue = vnode[1].get() 617 optdict = self.current_var.options 618 if not optdict.has_key(optname): 619 errmsg = "Unknown option `%s' for variable `%s'" % (optname, self.current_var.name) 620 self.error(errmsg, node) 621 else: 622 optdict[optname] = optvalue 623 self.prune()
624 625 626 # special keyword arguments added to parameter list 627 628 _SpecialArgs = { 629 'taskObj': None, 630 } 631
632 -class VarList(GenericASTTraversal, ErrorTracker):
633 634 """Scan tree and get info on procedure, parameters, and local variables""" 635
636 - def __init__(self, ast, mode="proc", local_vars_list=None, 637 local_vars_dict=None, parlist=None):
638 GenericASTTraversal.__init__(self, ast) 639 self.mode = mode 640 self.proc_name = "" 641 self.proc_args_list = [] 642 self.proc_args_dict = {} 643 self.has_proc_stmt = 0 644 if local_vars_list is None: 645 self.local_vars_list = [] 646 self.local_vars_count = 0 647 else: 648 self.local_vars_list = local_vars_list 649 self.local_vars_count = len(local_vars_list) 650 if local_vars_dict is None: 651 self.local_vars_dict = {} 652 else: 653 self.local_vars_dict = local_vars_dict 654 if hasattr(ast, 'filename'): 655 self.filename = ast.filename 656 else: 657 self.filename = '' 658 self.input_parlist = parlist 659 self.preorder() 660 del self.input_parlist 661 662 # If in "proc" mode, add default procedure name for 663 # non-procedure scripts 664 # (Need to do something like this so non-procedure scripts can 665 # be compiled, but this may not be ideal solution.) 666 if self.mode != "single" and not self.proc_name: 667 if not self.filename: 668 self.proc_name = 'proc' 669 else: 670 path, fname = os.path.split(self.filename) 671 root, ext = os.path.splitext(fname) 672 self.setProcName(root) 673 674 # add mode, $nargs, other special parameters to all tasks 675 self.addSpecialArgs() 676 677 # Check for local variables that conflict with parameters 678 self.checkLocalConflict() 679 680 self.printerrors() 681 682 # convert procedure arguments to IrafParList 683 p = [] 684 for var in self.proc_args_list: 685 if not _SpecialArgs.has_key(var): 686 arg = self.proc_args_dict[var].toPar() 687 p.append(arg) 688 self.parList = irafpar.IrafParList(self.getProcName(), 689 filename=self.filename, parlist=p)
690
691 - def has_key(self, name):
692 """Check both local and procedure dictionaries for this name""" 693 return self.proc_args_dict.has_key(name) or \ 694 self.local_vars_dict.has_key(name)
695
696 - def get(self, name):
697 """Return entry from local or procedure dictionary (None if none)""" 698 return self.proc_args_dict.get(name) or self.local_vars_dict.get(name)
699
700 - def setProcName(self, proc_name, node=None):
701 """Set procedure name""" 702 # names with embedded dots are allow by the CL but should be illegal 703 pdot = proc_name.find('.') 704 if pdot==0: 705 self.error("Illegal procedure name `%s' starts with `.'" % proc_name, node) 706 if pdot >= 0: 707 self.warning("Bad procedure name `%s' truncated after dot to `%s'" % 708 (proc_name, proc_name[:pdot]), node) 709 proc_name = proc_name[:pdot] 710 # Procedure name is stored in translated form ('PY' added 711 # to Python keywords, etc.) 712 self.proc_name = irafutils.translateName(proc_name)
713
714 - def getProcName(self):
715 """Get procedure name, undoing translations""" 716 return irafutils.untranslateName(self.proc_name)
717
718 - def addSpecial(self, name, type, value):
719 # just delete $nargs and add it back if it is already present 720 if self.proc_args_dict.has_key(name): 721 self.proc_args_list.remove(name) 722 del self.proc_args_dict[name] 723 724 targ = irafutils.translateName(name) 725 if not self.proc_args_dict.has_key(targ): 726 self.proc_args_list.append(targ) 727 self.proc_args_dict[targ] = Variable(targ, type, init_value=value)
728
729 - def addSpecialArgs(self):
730 """Add mode, $nargs, other special parameters to all tasks""" 731 732 if not self.proc_args_dict.has_key('mode'): 733 self.proc_args_list.append('mode') 734 self.proc_args_dict['mode'] = Variable('mode','string', 735 init_value='al') 736 737 self.addSpecial("$nargs", 'int', 0) 738 739 ## self.addSpecial("$errno", 'int', 0) 740 ## self.addSpecial("$errmsg", 'string', "") 741 ## self.addSpecial("$errtask", 'string',"") 742 ## self.addSpecial("$err_dzvalue", 'int', 1) 743 744 for parg, ivalue in _SpecialArgs.items(): 745 if not self.proc_args_dict.has_key(parg): 746 self.proc_args_list.append(parg) 747 self.proc_args_dict[parg] = ivalue
748
749 - def checkLocalConflict(self):
750 """Check for local variables that conflict with parameters""" 751 752 errlist = ["Error in procedure `%s'" % self.getProcName()] 753 for v in self.local_vars_list: 754 if self.proc_args_dict.has_key(v): 755 errlist.append( 756 "Local variable `%s' overrides parameter of same name" % 757 (v,)) 758 if len(errlist) > 1: 759 self.error("\n".join(errlist))
760
761 - def list(self):
762 """List variables""" 763 print "Procedure arguments:" 764 for var in self.proc_args_list: 765 v = self.proc_args_dict[var] 766 if _SpecialArgs.has_key(var): 767 print 'Special',var,'=',v 768 else: 769 print v 770 print "Local variables:" 771 for var in self.local_vars_list: 772 print self.local_vars_dict[var]
773
774 - def getParList(self):
775 """Return procedure arguments as IrafParList""" 776 return self.parList
777
778 - def n_proc_stmt(self, node):
779 self.has_proc_stmt = 1 780 # get procedure name and list of argument names 781 p = ExtractProcInfo(node) 782 self.setProcName(p.proc_name, node) 783 self.proc_args_list = p.proc_args_list 784 for arg in self.proc_args_list: 785 if self.proc_args_dict.has_key(arg): 786 errmsg = "Argument `%s' repeated in procedure statement %s" % \ 787 (arg,self.getProcName()) 788 self.error(errmsg, node) 789 else: 790 self.proc_args_dict[arg] = None 791 self.prune()
792
793 - def n_param_declaration_block(self, node):
794 # get list of parameter variables 795 p = ExtractDeclInfo(node, self.proc_args_list, self.proc_args_dict, 796 self.ast.filename) 797 # check for undefined parameters declared in procedure stmt 798 d = self.proc_args_dict 799 for arg in d.keys(): 800 if not d[arg]: 801 # try substituting from parlist parameter list 802 d[arg] = self.getFromInputList(arg) 803 if not d[arg]: 804 errmsg = "Procedure argument `%s' is not declared" % (arg,) 805 self.error(errmsg, node) 806 self.prune()
807
808 - def getFromInputList(self, param):
809 # look up missing parameter in input_parlist 810 if self.input_parlist and self.input_parlist.hasPar(param): 811 return Variable(irafParObject= 812 self.input_parlist.getParObject(param))
813
814 - def n_statement_block(self, node):
815 # declarations in executable section are local variables 816 p = ExtractDeclInfo(node, self.local_vars_list, self.local_vars_dict, 817 self.ast.filename) 818 self.prune()
819 820 821 # conversion between parameter types and data types 822 823 _typeDict = { 824 'int': 'int', 825 'real': 'float', 826 'double': 'float', 827 'bool': 'bool', 828 'string': 'string', 829 'char': 'string', 830 'struct': 'string', 831 'file': 'string', 832 'gcur': 'string', 833 'imcur': 'string', 834 'ukey': 'string', 835 'pset': 'unknown', 836 } 837 838 # nested dictionary mapping required data type (primary key) and 839 # expression type (secondary key) to the name of the function used to 840 # convert to the required type 841 842 _rfuncDict = { 843 'int': {'int': None, 844 'float': None, 845 'string': 'int', 846 'bool': None, 847 'unknown': 'int', 848 'indef': None}, 849 'float': {'int': None, 850 'float': None, 851 'string': 'float', 852 'bool': 'float', 853 'unknown': 'float', 854 'indef': None}, 855 'string':{'int': 'str', 856 'float': 'str', 857 'string': None, 858 'bool': 'iraf.bool2str', 859 'unknown': 'str', 860 'indef': None}, 861 'bool': {'int': 'iraf.boolean', 862 'float': 'iraf.boolean', 863 'string': 'iraf.boolean', 864 'bool': None, 865 'unknown': 'iraf.boolean', 866 'indef': None}, 867 'indef': {'int': None, 868 'float': None, 869 'string': None, 870 'bool': None, 871 'unknown': None, 872 'indef': None}, 873 'unknown': {'int': None, 874 'float': None, 875 'string': None, 876 'bool': None, 877 'unknown': None, 878 'indef': None}, 879 } 880
881 -def _funcName(requireType, exprType):
882 return _rfuncDict[requireType][exprType]
883 884 # given two nodes with defined types in an arithmetic expression, 885 # set their required times and return the result type 886 # (using standard promotion rules) 887 888 _numberTypes = ['float', 'int', 'unknown'] 889
890 -def _arithType(node1, node2):
891 if node1.exprType in _numberTypes: 892 if node2.exprType not in _numberTypes: 893 rv = node1.exprType 894 node2.requireType = rv 895 else: 896 # both numbers -- don't change required types, but 897 # determine result type 898 if 'float' in [node1.exprType, node2.exprType]: 899 rv = 'float' 900 elif 'unknown' in [node1.exprType, node2.exprType]: 901 rv = 'unknown' 902 else: 903 rv = node1.exprType 904 else: 905 if node2.exprType in _numberTypes: 906 rv = node2.exprType 907 node1.requireType = rv 908 else: 909 rv = 'float' 910 node1.requireType = rv 911 node2.requireType = rv 912 return rv
913 914 # force node to be a number type and return the type 915
916 -def _numberType(node):
917 if node.exprType in _numberTypes: 918 return node.exprType 919 else: 920 node.requireType = 'float' 921 return node.requireType
922 923 _CLVarDict = {} 924
925 -def _getCLVarType(name):
926 """Returns CL parameter data type if this is a CL variable, "unknown" if not 927 928 Note that this can be incorrect about the data type for CL variables 929 that are masked by package level variables. Too bad, that is just 930 too ugly to be believed anyway. Don't do that. 931 """ 932 global _CLVarDict 933 try: 934 if not _CLVarDict: 935 import iraf 936 d = iraf.cl.getParDict() 937 # construct type dictionary for all variables 938 # don't use minimum matching -- require exact match 939 for pname, pobj in d.items(): 940 iraftype = pobj.type 941 if iraftype[:1] == "*": 942 iraftype = iraftype[1:] 943 _CLVarDict[pname] = _typeDict[_longTypeName[iraftype]] 944 except AttributeError: 945 pass 946 return _CLVarDict.get(name, "unknown")
947
948 -class TypeCheck(GenericASTTraversal):
949 950 """Determine types of all expressions""" 951
952 - def __init__(self, ast, vars, filename):
953 GenericASTTraversal.__init__(self, ast) 954 self.vars = vars 955 self.filename = filename 956 self.postorder()
957 958 # atoms 959
960 - def n_FLOAT(self, node):
961 node.exprType = 'float' 962 node.requireType = node.exprType
963 - def n_INTEGER(self, node):
964 node.exprType = 'int' 965 node.requireType = node.exprType
966 - def n_SEXAGESIMAL(self, node):
967 node.exprType = 'float' 968 node.requireType = node.exprType
969 - def n_INDEF(self, node):
970 node.exprType = 'indef' 971 node.requireType = node.exprType
972 - def n_STRING(self, node):
973 node.exprType = 'string' 974 node.requireType = node.exprType
975 - def n_QSTRING(self, node):
976 node.exprType = 'string' 977 node.requireType = node.exprType
978 - def n_EOF(self, node):
979 node.exprType = 'string' 980 node.requireType = node.exprType
981 - def n_BOOL(self, node):
982 node.exprType = 'bool' 983 node.requireType = node.exprType
984
985 - def n_IDENT(self, node):
986 s = irafutils.translateName(node.attr) 987 v = self.vars.get(s) 988 if v is not None: 989 node.exprType = _typeDict[v.type] 990 node.requireType = node.exprType 991 else: 992 # not a local variable 993 # try CL as a common case 994 node.exprType = _getCLVarType(node.attr) 995 node.requireType = node.exprType
996
997 - def n_array_ref(self, node):
998 node.exprType = node[0].exprType 999 node.requireType = node.exprType
1000
1001 - def n_function_call(self, node):
1002 functionname = node[0].attr 1003 ftype = _functionType.get(functionname) 1004 if ftype is None: ftype = 'unknown' 1005 node.exprType = ftype 1006 node.requireType = node.exprType
1007
1008 - def n_atom(self, node):
1009 assert len(node)==3 1010 node.exprType = node[1].exprType 1011 node.requireType = node.exprType
1012
1013 - def n_power(self, node):
1014 assert len(node)==3 1015 node.exprType = _arithType(node[0], node[2]) 1016 node.requireType = node.exprType
1017
1018 - def n_factor(self, node):
1019 assert len(node)==2 1020 node.exprType = _numberType(node[1]) 1021 node.requireType = node.exprType
1022
1023 - def n_term(self, node):
1024 assert len(node)==3 1025 node.exprType = _arithType(node[0], node[2]) 1026 node.requireType = node.exprType
1027
1028 - def n_concat_expr(self, node):
1029 assert len(node)==3 1030 node.exprType = 'string' 1031 node.requireType = node.exprType 1032 node[0].requireType = 'string' 1033 node[2].requireType = 'string'
1034
1035 - def n_arith_expr(self, node):
1036 assert len(node)==3 1037 if node[1].type == '-': 1038 node.exprType = _arithType(node[0], node[2]) 1039 node.requireType = node.exprType 1040 else: 1041 # plus -- could mean add or concatenate 1042 if node[0].exprType == 'string' or node[2].exprType == 'string': 1043 node.exprType = 'string' 1044 node.requireType = node.exprType 1045 node[0].requireType = 'string' 1046 node[2].requireType = 'string' 1047 else: 1048 node.exprType = _arithType(node[0], node[2]) 1049 node.requireType = node.exprType
1050
1051 - def n_comp_expr(self, node):
1052 assert len(node) == 3 1053 node.exprType = 'bool' 1054 node.requireType = node.exprType
1055
1056 - def n_not_expr(self, node):
1057 assert len(node) == 2 1058 node.exprType = 'bool' 1059 node.requireType = node.exprType 1060 node[1].requireType = 'bool'
1061
1062 - def n_expr(self, node):
1063 assert len(node) == 3 1064 node.exprType = 'bool' 1065 node.requireType = node.exprType 1066 node[0].requireType = 'bool' 1067 node[2].requireType = 'bool'
1068
1069 - def n_assignment_stmt(self, node):
1070 assert len(node) == 3 1071 node[2].requireType = node[0].exprType
1072
1073 -class BlockInfo:
1074 1075 """Helper class to store block structure info for GOTO analysis""" 1076
1077 - def __init__(self, node, blockid, parent):
1078 self.node = node 1079 self.blockid = blockid 1080 self.parent = parent
1081
1082 -class GoToAnalyze(GenericASTTraversal, ErrorTracker):
1083 1084 """AST traversal for CL GOTO analysis 1085 1086 Analyze GOTO structure looking for branches into blocks (which are forbidden), 1087 backward branches (which are not supported), and other errors. Adds information 1088 to the AST that is used to generate Python equivalent code. 1089 """ 1090
1091 - def __init__(self, ast):
1092 GenericASTTraversal.__init__(self, ast) 1093 self.blocks = [] 1094 self.label_blockid = {} 1095 self.goto_blockidlist = {} 1096 self.goto_nodelist = {} 1097 self.current_blockid = -1 1098 1099 # walk the tree 1100 self.preorder() 1101 1102 # check for missing labels 1103 for label in self.goto_blockidlist.keys(): 1104 if not self.label_blockid.has_key(label): 1105 node = self.goto_nodelist[label][0] 1106 self.error("GOTO refers to unknown label `%s'" % label, node) 1107 1108 # note that we count on the Tree2Python class to print errors 1109 1110 # add label count info to blocks if all is OK 1111 label_count = [0]*len(self.blocks) 1112 for label, ib in self.label_blockid.items(): 1113 # only count labels that are actually used 1114 if self.goto_blockidlist.has_key(label): 1115 label_count[ib] += 1 1116 for ib in range(len(self.blocks)): 1117 self.blocks[ib].node.label_count = label_count[ib]
1118 1119 #------------------------- 1120 # public interface methods 1121 #------------------------- 1122
1123 - def labels(self):
1124 """Get a list of known labels used in GOTOs""" 1125 labels = self.goto_blockidlist.keys() 1126 labels.sort() 1127 return labels
1128
1129 - def has_key(self, label):
1130 """Check if label is used in a GOTO""" 1131 return self.goto_blockidlist.has_key(label)
1132 1133 #------------------------------------ 1134 # methods called during AST traversal 1135 #------------------------------------ 1136
1137 - def n_compound_stmt(self, node):
1138 newid = len(self.blocks) 1139 self.blocks.append(BlockInfo(node, newid, self.current_blockid)) 1140 self.current_blockid = newid
1141
1142 - def n_statement_block(self, node):
1143 newid = len(self.blocks) 1144 self.blocks.append(BlockInfo(node, newid, self.current_blockid)) 1145 self.current_blockid = newid
1146
1147 - def n_compound_stmt_exit(self, node):
1148 self.current_blockid = self.blocks[self.current_blockid].parent
1149
1150 - def n_statement_block_exit(self, node):
1151 self.current_blockid = self.blocks[self.current_blockid].parent
1152
1153 - def n_label_stmt(self, node):
1154 label = node[0].attr 1155 if self.label_blockid.has_key(label): 1156 self.error("Duplicate statement label `%s'" % label, node) 1157 else: 1158 cblockid = self.current_blockid 1159 self.label_blockid[label] = cblockid 1160 # make sure all gotos for this label are in this or deeper blocks 1161 for i in self.goto_blockidlist.get(label,[]): 1162 if self.blocks[i].blockid < cblockid: 1163 self.error("GOTO branches to label `%s' in inner block" 1164 % label, node)
1165
1166 - def n_goto_stmt(self, node):
1167 label = str(node[1]) 1168 if self.label_blockid.has_key(label): 1169 self.error("Backwards GOTO to label `%s' is not allowed" % label, node) 1170 elif self.goto_blockidlist.has_key(label): 1171 self.goto_blockidlist[label].append(self.current_blockid) 1172 self.goto_nodelist[label].append(node) 1173 else: 1174 self.goto_blockidlist[label] = [self.current_blockid] 1175 self.goto_nodelist[label] = [node]
1176 1177 1178 # tokens that are translated or skipped outright 1179 _translateList = { 1180 "{": "", 1181 "}": "", 1182 ";": "", 1183 "!": "not ", 1184 "//": " + ", 1185 } 1186 1187 # builtin task names that are translated 1188 1189 _taskList = { 1190 "print" : "clPrint", 1191 "_curpack" : "curpack", 1192 "_allocate" : "clAllocate", 1193 "_deallocate" : "clDeallocate", 1194 "_devstatus" : "clDevstatus", 1195 } 1196 1197 # builtin functions that are translated 1198 # other functions just have 'iraf.' prepended 1199 1200 _functionList = { 1201 "int": "iraf.integer", 1202 "str": "str", 1203 "abs": "iraf.absvalue", 1204 "min": "iraf.minimum", 1205 "max": "iraf.maximum", 1206 } 1207 1208 # return types of IRAF built-in functions 1209 1210 _functionType = { 1211 "int": "int", 1212 "real": "float", 1213 "sin": "float", 1214 "cos": "float", 1215 "tan": "float", 1216 "atan2": "float", 1217 "exp": "float", 1218 "log": "float", 1219 "log10": "float", 1220 "sqrt": "float", 1221 "frac": "float", 1222 "abs": "float", 1223 "min": "unknown", 1224 "max": "unknown", 1225 "fscan": "int", 1226 "scan": "int", 1227 "fscanf": "int", 1228 "scanf": "int", 1229 "nscan": "int", 1230 "stridx": "int", 1231 "strlen": "int", 1232 "str": "string", 1233 "substr": "string", 1234 "envget": "string", 1235 "mktemp": "string", 1236 "radix": "string", 1237 "osfn": "string", 1238 "_curpack": "string", 1239 "defpar": "bool", 1240 "access": "bool", 1241 "defvar": "bool", 1242 "deftask": "bool", 1243 "defpac": "bool", 1244 "imaccess": "bool", 1245 } 1246 1247 # logical operator conversion 1248 _LogOpDict = { 1249 "&&": " and ", 1250 "||": " or ", 1251 } 1252 1253 # redirection conversion 1254 _RedirDict = { 1255 ">": "Stdout", 1256 ">>": "StdoutAppend", 1257 ">&": "Stderr", 1258 ">>&": "StderrAppend", 1259 "<": "Stdin", 1260 } 1261 1262 # tokens printed with both leading and trailing space 1263 _bothSpaceList = { 1264 "=": 1, 1265 "ASSIGNOP": 1, 1266 "COMPOP": 1, 1267 "+": 1, 1268 "-": 1, 1269 "/": 1, 1270 "*": 1, 1271 "//": 1, 1272 } 1273 1274 # tokens printed with only trailing space 1275 _trailSpaceList = { 1276 ",": 1, 1277 "REDIR": 1, 1278 "IF": 1, 1279 "WHILE": 1, 1280 } 1281 1282 # Convert token value to IRAF type specified by Variable object 1283 # always returns a string, suitable for use in assignment like: 1284 # 'var = ' + _convFunc(var, value) 1285 # The only permitted conversion is int->float. 1286 1287 _stringTypes = { "string": 1, 1288 "char": 1, 1289 "file": 1, 1290 "struct": 1, 1291 "gcur": 1, 1292 "imcur": 1, 1293 "ukey": 1, 1294 "pset": 1, 1295 } 1296
1297 -def _convFunc(var, value):
1298 if var.list_flag or _stringTypes.has_key(var.type): 1299 if value is None: 1300 return "" 1301 else: 1302 return str(value) 1303 elif var.type == "int": 1304 if value is None: 1305 return "INDEF" 1306 elif isinstance(value,str) and value[:1] == ")": 1307 # parameter indirection 1308 return value 1309 else: 1310 return int(value) 1311 elif var.type == "real": 1312 if value is None: 1313 return "INDEF" 1314 elif isinstance(value,str) and value[:1] == ")": 1315 # parameter indirection 1316 return value 1317 else: 1318 return float(value) 1319 elif var.type == "bool": 1320 if value is None: 1321 return "INDEF" 1322 elif isinstance(value, (int,float)): 1323 if value == 0: 1324 return 'no' 1325 else: 1326 return 'yes' 1327 elif isinstance(value,str): 1328 s = value.lower() 1329 if s == "yes" or s == "y": 1330 s = "yes" 1331 elif s == "no" or s == "n": 1332 s = "'no'" 1333 elif s[:1] == ")": 1334 # parameter indirection 1335 return value 1336 else: 1337 raise ValueError( 1338 "Illegal value `%s' for boolean variable %s" % 1339 (s, var.name)) 1340 return s 1341 else: 1342 try: 1343 return value.bool() 1344 except AttributeError, e: 1345 raise AttributeError(var.name + ':' + str(e)) 1346 raise ValueError("unimplemented type `%s'" % (var.type,))
1347 1348
1349 -class CheckArgList(GenericASTTraversal, ErrorTracker):
1350 1351 """Check task argument list for errors""" 1352
1353 - def __init__(self, ast):
1354 GenericASTTraversal.__init__(self, ast) 1355 # keywords is a list of keyword dictionaries (to handle 1356 # nested task calls) 1357 self.keywords = [] 1358 self.taskname = [] 1359 self.tasknode = [] 1360 self.preorder()
1361 # note that we count on the Tree2Python class to print any errors 1362
1363 - def n_task_call_stmt(self, node):
1364 self.taskname.append(node[0].attr) 1365 self.tasknode.append(node) 1366 self.keywords.append({})
1367
1368 - def n_task_call_stmt_exit(self, node):
1369 self.taskname.pop() 1370 self.tasknode.pop() 1371 self.keywords.pop()
1372
1373 - def n_function_call(self, node):
1374 self.taskname.append(node[0].attr) 1375 self.tasknode.append(node) 1376 self.keywords.append({})
1377
1378 - def n_function_call_exit(self, node):
1379 self.taskname.pop() 1380 self.tasknode.pop() 1381 self.keywords.pop()
1382
1383 - def n_param_name(self, node):
1384 keyword = node[0].attr 1385 if self.keywords[-1].has_key(keyword): 1386 self.error("Duplicate keyword `%s' in call to %s" % 1387 (keyword, self.taskname[-1]), node) 1388 else: 1389 self.keywords[-1][keyword] = 1
1390
1391 - def n_non_empty_arg(self, node):
1392 if node[0].type not in ['keyword_arg', 'bool_arg', 1393 'redir_arg', 'non_expr_arg'] and self.keywords[-1]: 1394 self.error("Non-keyword arg after keyword arg in call to %s" % 1395 self.taskname[-1], node)
1396
1397 - def n_empty_arg(self, node):
1398 if self.keywords[-1]: 1399 # empty args don't have line number, so use task line 1400 self.error("Non-keyword (empty) arg after keyword arg in call to %s" % 1401 self.taskname[-1], self.tasknode[-1])
1402 1403
1404 -class Tree2Python(GenericASTTraversal, ErrorTracker):
1405 - def __init__(self, ast, vars, filename='', taskObj=None):
1406 self._ecl_iferr_entered = 0 1407 GenericASTTraversal.__init__(self, ast) 1408 self.filename = filename 1409 self.column = 0 1410 self.vars = vars 1411 self.inSwitch = 0 1412 self.caseCount = [] 1413 # printPass is an array of flags indicating whether the 1414 # corresponding indentation level is empty. If empty when 1415 # the block is terminated, a 'pass' statement is generated. 1416 # Start with a reasonable size for printPass array. 1417 # (It gets extended if necessary.) 1418 self.printPass = [1]*10 1419 self.code_buffer = cStringIO.StringIO() 1420 self.importDict = {} 1421 self.specialDict = {} 1422 self.pipeOut = [] 1423 self.pipeIn = [] 1424 self.pipeCount = 0 1425 # These three are used only by n_while_stmt, n_for_stmt, n_next_stmt, 1426 # and decrIndent; they are for incrementing the loop variable before 1427 # writing "continue" in a "for" loop (but not in a "while" loop). 1428 self.save_incr = [] # info to increment the loop variable 1429 self.save_indent = [] # indentation level in a while loop 1430 self.IN_A_WHILE_LOOP = "while" # this is a constant value 1431 1432 self._ecl_pyline = 1 1433 self._ecl_clline = None 1434 self._ecl_linemap = {} 1435 1436 if self.vars.proc_name: 1437 self.indent = 1 1438 else: 1439 self.indent = 0 1440 1441 if taskObj and self._ecl_iferr_entered: 1442 self.write("taskObj = iraf.getTask('%s')\n" % taskObj) 1443 1444 # analyze goto structure 1445 # this assigns the label_count field for statement blocks 1446 self.gotos = GoToAnalyze(ast) 1447 1448 # propagate any errors from goto analysis, but continue to see 1449 # if we can identify more problems 1450 self.errorappend(self.gotos) 1451 1452 self.preorder() 1453 self.write("\n") 1454 1455 self.code = self.code_buffer.getvalue() 1456 self.code_buffer.close() 1457 1458 # write the header second so it can be minimal 1459 self.code_buffer = cStringIO.StringIO() 1460 self.writeProcHeader() 1461 header = self.code_buffer.getvalue() 1462 if pyrafglobals._use_ecl: 1463 self.code = self._ecl_linemapping(header) + \ 1464 header + \ 1465 self.code 1466 else: 1467 self.code = header + self.code 1468 self.code_buffer.close() 1469 del self.code_buffer 1470 1471 ## print "-"*50 1472 ## print self.code 1473 1474 self.printerrors()
1475 1476
1477 - def _ecl_linemapping(self, header):
1478 lines = header.count("\n") + 2 # see below 1479 newmap = {} 1480 for key,value in self._ecl_linemap.items(): 1481 newmap[ key + lines ] = value 1482 return "_ecl_linemap_" + self.vars.proc_name + " = " + repr(newmap) + "\n\n"
1483
1484 - def incrIndent(self):
1485 """Increment indentation count""" 1486 # printPass is used to recognize empty indentation blocks 1487 # and add 'pass' statement when indentation level is decremented 1488 self.indent = self.indent+1 1489 if len(self.printPass) <= self.indent: 1490 # extend array to length self.indent+1 1491 self.printPass = self.printPass + \ 1492 (self.indent+1-len(self.printPass)) * [1] 1493 self.printPass[self.indent] = 1
1494
1495 - def decrIndent(self):
1496 """Decrement indentation count and write 'pass' if required""" 1497 if self.printPass[self.indent]: 1498 self.writeIndent('pass') 1499 self.indent = self.indent-1 1500 if len(self.save_indent) > 0 and self.save_indent[-1] == self.indent: 1501 del self.save_incr[-1] 1502 del self.save_indent[-1]
1503
1504 - def write(self, s, requireType=None, exprType=None):
1505 1506 """Write string to output code buffer""" 1507 1508 self._ecl_pyline += s.count("\n") 1509 self._ecl_linemap[ self._ecl_pyline ] = self._ecl_clline 1510 1511 if requireType != exprType: 1512 # need to wrap this subexpression in a conversion function 1513 cf = _funcName(requireType, exprType) 1514 if cf is not None: 1515 s = cf + '(' + s + ')' 1516 self.code_buffer.write(s) 1517 # maintain column count to help with breaking across lines 1518 self.column = self.column + len(s) 1519 # handle simple cases of a single initial tab or trailing newline 1520 if s[:1] == "\t": 1521 self.column = self.column + 3 1522 if s[-1:] == "\n": 1523 self.column = 0
1524
1525 - def writeIndent(self, value=None):
1526 1527 """Write newline and indent""" 1528 1529 self.write("\n") 1530 for i in range(self.indent): 1531 self.write("\t") 1532 if value: self.write(value) 1533 self.printPass[self.indent] = 0
1534
1535 - def writeProcHeader(self):
1536 1537 """Write function definition and other header info""" 1538 1539 # save printPass flag -- if it is set, the body of 1540 # the procedure is currently empty and so 'pass' may be added 1541 printPass = self.printPass[1] 1542 1543 # reset indentation level; never need 'pass' stmt in header 1544 self.indent = 0 1545 self.printPass[0] = 0 1546 1547 # most header info is omitted in 'single' translation mode 1548 noHdr = self.vars.mode == "single" and self.vars.proc_name == "" 1549 1550 # do basic imports and definitions outside procedure definition, 1551 # mainly so INDEF can be used as a default value for keyword 1552 # parameters in the def statement 1553 1554 if not noHdr: 1555 self.write("from pyraf import iraf") 1556 self.writeIndent("from pyraf.irafpar import makeIrafPar, IrafParList") 1557 self.writeIndent("from pytools.irafglobals import *") 1558 self.writeIndent("from pyraf.pyrafglobals import *") 1559 self.write("\n") 1560 1561 if self.vars.proc_name: 1562 # create list of procedure arguments 1563 # make list of IrafPar definitions at the same time 1564 n = len(self.vars.proc_args_list) 1565 namelist = n*[None] 1566 proclist = n*[None] 1567 deflist = n*[None] 1568 for i in range(n): 1569 p = self.vars.proc_args_list[i] 1570 v = self.vars.proc_args_dict[p] 1571 namelist[i] = irafutils.translateName(p) 1572 if _SpecialArgs.has_key(p): 1573 # special arguments are Python types 1574 proclist[i] = p + '=' + str(v) 1575 deflist[i] = '' 1576 else: 1577 try: 1578 proclist[i] = v.procLine() 1579 deflist[i] = v.parDefLine() 1580 except AttributeError, e: 1581 raise AttributeError(self.filename + ':' + str(e)) 1582 # allow long argument lists to be broken across lines 1583 self.writeIndent("def " + self.vars.proc_name + "(") 1584 self.writeChunks(proclist) 1585 self.write("):\n") 1586 self.incrIndent() 1587 # reset printPass in case procedure is empty 1588 self.printPass[self.indent] = printPass 1589 else: 1590 namelist = [] 1591 deflist = [] 1592 1593 # write additional required imports 1594 wnewline = 0 1595 if not noHdr: 1596 keylist = self.importDict.keys() 1597 if keylist: 1598 keylist.sort() 1599 self.writeIndent("import ") 1600 self.write(", ".join(keylist)) 1601 wnewline = 1 1602 1603 if self.specialDict.has_key("PkgName"): 1604 self.writeIndent("PkgName = iraf.curpack(); " 1605 "PkgBinary = iraf.curPkgbinary()") 1606 wnewline = 1 1607 if wnewline: self.write("\n") 1608 1609 # add local variables to deflist 1610 for p in self.vars.local_vars_list[self.vars.local_vars_count:]: 1611 v = self.vars.local_vars_dict[p] 1612 try: 1613 deflist.append(v.parDefLine(local=1)) 1614 except AttributeError, e: 1615 raise AttributeError(self.filename + ':' + str(e)) 1616 1617 if deflist: 1618 # add local and procedure parameters to Vars list 1619 if not noHdr: 1620 self.writeIndent("Vars = IrafParList(" + 1621 `self.vars.proc_name` + ")") 1622 for defargs in deflist: 1623 if defargs: 1624 self.writeIndent("Vars.addParam(makeIrafPar(") 1625 self.writeChunks(defargs) 1626 self.write("))") 1627 self.write("\n") 1628 1629 if pyrafglobals._use_ecl: 1630 self.writeIndent("from pyraf.irafecl import EclState") 1631 self.writeIndent("_ecl = EclState(_ecl_linemap_%s)\n" % self.vars.proc_name) 1632 1633 # write goto label definitions if needed 1634 for label in self.gotos.labels(): 1635 self.writeIndent("class GoTo_%s(Exception): pass" % label) 1636 1637 # decrement indentation (which writes the pass if necessary) 1638 self.decrIndent()
1639 1640 #------------------------------ 1641 # elements that can be ignored 1642 #------------------------------ 1643
1644 - def n_proc_stmt(self, node): self.prune()
1645 - def n_declaration_block(self, node): self.prune()
1646 - def n_declaration_stmt(self, node): self.prune()
1647
1648 - def n_BEGIN(self, node): pass
1649 - def n_END(self, node): pass
1650 - def n_NEWLINE(self, node): pass
1651 1652 #------------------------------ 1653 #XXX unimplemented features 1654 #------------------------------ 1655
1656 - def n_BKGD(self, node):
1657 # background execution ignored for now 1658 self.warning("Background execution ignored", node)
1659 1660 #------------------------------ 1661 # low-level conversions 1662 #------------------------------ 1663
1664 - def n_FLOAT(self, node):
1665 # convert d exponents to e for Python 1666 s = node.attr 1667 i = s.find('d') 1668 if i>=0: 1669 s = s[:i] + 'e' + s[i+1:] 1670 else: 1671 i = s.find('D') 1672 if i>=0: 1673 s = s[:i] + 'E' + s[i+1:] 1674 self.write(s, node.requireType, node.exprType)
1675
1676 - def n_INTEGER(self, node):
1677 # convert octal and hex constants 1678 value = node.attr 1679 last = value[-1].lower() 1680 if last == 'b': 1681 # octal 1682 self.write('0'+value[:-1], node.requireType, node.exprType) 1683 elif last == 'x': 1684 # hexadecimal 1685 self.write('0x'+value[:-1], node.requireType, node.exprType) 1686 else: 1687 # remove leading zeros on decimal values 1688 i=0 1689 for digit in value: 1690 if digit != '0': break 1691 i = i+1 1692 else: 1693 # all zeros 1694 i = i-1 1695 self.write(value[i:], node.requireType, node.exprType)
1696
1697 - def n_SEXAGESIMAL(self, node):
1698 # convert d:m:s values to float 1699 v = node.attr.split(':') 1700 # at least 2 values in expression 1701 s = 'iraf.clSexagesimal(' + v[0] + ',' + v[1] 1702 if len(v)>2: s = s + ',' + v[2] 1703 s = s + ')' 1704 self.write(s, node.requireType, node.exprType)
1705
1706 - def n_IDENT(self, node, array_ref=0):
1707 s = irafutils.translateName(node.attr) 1708 if self.vars.has_key(s) and not _SpecialArgs.has_key(s): 1709 1710 # Prepend 'Vars.' to all procedure and local variable references 1711 # except for special args, which are normal Python variables. 1712 # The main reason I do it this way is so the IRAF scan/fscan 1713 # functions can work correctly, but it simplifies 1714 # other code generation as well. Vars does all the type 1715 # conversions and applies constraints. 1716 #XXX Note we are not doing minimum match on parameter names 1717 1718 self.write('Vars.'+s, node.requireType, node.exprType) 1719 elif '.' in s: 1720 1721 # Looks like a task.parameter or field reference 1722 # Add 'Vars.' or 'iraf.' or 'taskObj.' prefix to name. 1723 # Also look for special p_ extensions -- need to use parameter 1724 # objects instead of parameter values if they are specified. 1725 1726 attribs = s.split('.') 1727 ipf = basicpar.isParField(attribs[-1]) 1728 if self.vars.has_key(attribs[0]): 1729 attribs.insert(0, 'Vars') 1730 elif ipf and (len(attribs)==2): 1731 attribs.insert(0, 'taskObj') 1732 else: 1733 attribs.insert(0, 'iraf') 1734 if ipf: 1735 attribs[-2] = 'getParObject(' + `attribs[-2]` + ')' 1736 self.write(".".join(attribs), 1737 node.requireType, node.exprType) 1738 1739 else: 1740 1741 # not a local variable; use task object to search other 1742 # dictionaries 1743 1744 if self.vars.mode == "single": 1745 self.write('iraf.cl.'+s, node.requireType, node.exprType) 1746 else: 1747 self.write('taskObj.'+s, node.requireType, node.exprType)
1748
1749 - def _print_subscript(self, node):
1750 # subtract one from IRAF subscripts to get Python subscripts 1751 # returns number of subscripts 1752 if len(node)>1: 1753 n = self._print_subscript(node[0]) 1754 self.write(", ") 1755 else: 1756 n = 0 1757 if node[-1].type == "INTEGER": 1758 self.write(str(int(node[-1])-1)) 1759 else: 1760 self.preorder(node[-1]) 1761 self.write("-1") 1762 return n+1
1763
1764 - def n_array_ref(self, node):
1765 # in array reference, do not add .p_value to parameter identifier 1766 # because we can index the parameter directly 1767 # wrap in a conversion function if necessary 1768 cf = _funcName(node.requireType, node.exprType) 1769 if cf: self.write(cf + "(") 1770 self.n_IDENT(node[0], array_ref=1) 1771 self.write("[") 1772 nsub = self._print_subscript(node[2]) 1773 self.write("]") 1774 if cf: self.write(")") 1775 # check for correct number of subscripts for local arrays 1776 s = irafutils.translateName(node[0].attr) 1777 if self.vars.has_key(s): 1778 v = self.vars.get(s) 1779 if nsub < len(v.shape): 1780 self.error("Too few subscripts for array %s" % s, node) 1781 elif nsub > len(v.shape): 1782 self.error("Too many subscripts for array %s" % s, node) 1783 self.prune()
1784
1785 - def n_param_name(self, node):
1786 s = irafutils.translateName(node[0].attr,dot=1) 1787 self.write(s) 1788 self.prune()
1789
1790 - def n_LOGOP(self, node):
1791 self.write(_LogOpDict[node.attr])
1792
1793 - def n_function_call(self, node):
1794 # all functions are built-in (since CL does not allow new definitions) 1795 # wrap in a conversion function if necessary 1796 cf = _funcName(node.requireType, node.exprType) 1797 if cf: self.write(cf + "(") 1798 functionname = node[0].attr 1799 newname = _functionList.get(functionname) 1800 if newname is None: 1801 # just add "iraf." prefix 1802 newname = "iraf." + functionname 1803 self.write(newname + "(") 1804 # argument list for scan statement 1805 sargs = self.captureArgs(node[2]) 1806 if functionname in ["scan", "fscan", "scanf", "fscanf"]: 1807 # scan is weird -- effectively uses call-by-name 1808 # call special routine to change the args 1809 sargs = self.modify_scan_args(functionname, sargs) 1810 self.writeChunks(sargs) 1811 self.write(")") 1812 if cf: self.write(")") 1813 self.prune()
1814
1815 - def modify_scan_args(self, functionname, sargs):
1816 # modify argument list for scan statement 1817 1818 # If fscan, first argument is the string to read from. 1819 # But we still want to pass it by name because if the 1820 # first argument is a list parameter, we want to postpone 1821 # its evaluation until we get into the fscan function so 1822 # we can catch EOF exceptions. 1823 1824 # Add quotes to names (we're literally passing the names, not 1825 # the values) 1826 sargs = map(repr, sargs) 1827 1828 # pass in locals dictionary so we can get names of variables to set 1829 sargs.insert(0, "locals()") 1830 return sargs
1831
1832 - def default(self, node):
1833 1834 """Handle other tokens""" 1835 1836 if hasattr(node, 'exprType'): 1837 requireType = node.requireType 1838 exprType = node.exprType 1839 else: 1840 requireType = None 1841 exprType = None 1842 1843 if isinstance(node, Token): 1844 s = _translateList.get(node.type) 1845 if s is not None: 1846 self.write(s, requireType, exprType) 1847 elif _trailSpaceList.has_key(node.type): 1848 self.write(`node`, requireType, exprType) 1849 self.write(" ") 1850 elif _bothSpaceList.has_key(node.type): 1851 self.write(" ") 1852 self.write(`node`, requireType, exprType) 1853 self.write(" ") 1854 else: 1855 self.write(`node`, requireType, exprType) 1856 elif requireType != exprType: 1857 cf = _funcName(requireType, exprType) 1858 if cf is not None: 1859 self.write(cf + '(') 1860 for nn in node: 1861 self.preorder(nn) 1862 self.write(')') 1863 self.prune()
1864
1865 - def n_term(self, node):
1866 if pyrafglobals._use_ecl and node[1] in ['/','%']: 1867 kind = {"/" : "divide", "%":"modulo"}[node[1]] 1868 self.write("taskObj._ecl_safe_%s(" % kind) 1869 self.preorder(node[0]) 1870 self.write(",") 1871 self.preorder(node[2]) 1872 self.write(")") 1873 self.prune() 1874 else: 1875 self.default(node)
1876 1877 #------------------------------ 1878 # block indentation control 1879 #------------------------------ 1880
1881 - def n_statement_block(self, node):
1882 for i in range(node.label_count): 1883 self.writeIndent("try:") 1884 self.incrIndent()
1885
1886 - def n_compound_stmt(self, node):
1887 self.write(":") 1888 self.incrIndent() 1889 for i in range(node.label_count): 1890 self.writeIndent("try:") 1891 self.incrIndent()
1892
1893 - def n_compound_stmt_exit(self, node):
1894 self.decrIndent()
1895
1896 - def n_nonnull_stmt(self, node):
1897 if node[0].type == "{": 1898 # indentation already done for compound statements 1899 self.preorder(node[1]) 1900 self.prune() 1901 else: 1902 ## if self._ecl_iferr_entered: 1903 ## self.writeIndent("try:") 1904 ## self.incrIndent() 1905 ## self.writeIndent() 1906 ## for kid in node: 1907 ## self.preorder(kid) 1908 ## self.decrIndent() 1909 ## self.writeIndent("except Exception, e:") 1910 ## self.incrIndent() 1911 ## self.writeIndent("taskObj._ecl_record_error(e)") 1912 ## self.decrIndent() 1913 ## self.prune() 1914 ## else: 1915 self._ecl_clline = FindLineNumber(node).lineno 1916 self.writeIndent()
1917 1918 1919 #------------------------------ 1920 # statements 1921 #------------------------------ 1922
1923 - def n_osescape_stmt(self, node):
1924 self.write("iraf.clOscmd(" + `node[0].attr` + ")") 1925 self.prune()
1926
1927 - def n_assignment_stmt(self, node):
1928 if node[1].type == "ASSIGNOP": 1929 # convert +=, -=, etc. 1930 self.preorder(node[0]) 1931 self.write(" = ") 1932 self.preorder(node[0]) 1933 self.write(" " + node[1].attr[0] + " ") 1934 self.preorder(node[2]) 1935 self.prune()
1936
1937 - def n_else_clause(self, node):
1938 # recognize special 'else if' case 1939 1940 # pattern is: 1941 # else_clause ::= opt_newline ELSE compound_stmt 1942 # compound_stmt ::= opt_newline one_compound_stmt 1943 # one_compound_stmt ::= nonnull_stmt 1944 # nonnull_stmt ::= if_stmt 1945 1946 if len(node) == 3: 1947 stmt = node[2][1] 1948 if stmt.type == "nonnull_stmt" and stmt[0].type == "if_stmt": 1949 self.writeIndent("el") 1950 self.preorder(stmt[0]) 1951 self.prune()
1952
1953 - def n_ELSE(self, node):
1954 # else clause is not a 'nonnull_stmt', so must explicitly 1955 # print the indentation 1956 self.writeIndent("else")
1957
1958 - def n_iferr_stmt(self, node):
1959 # iferr_stmt ::= if_kind guarded_stmt except_action 1960 # iferr_stmt ::= if_kind guarded_stmt opt_newline THEN except_action 1961 # iferr_stmt ::= if_kind guarded_stmt opt_newline THEN except_action opt_newline ELSE else_action 1962 # if_kind ::= IFERR 1963 # if_kind ::= IFNOERR 1964 # guarded_stmt ::= { opt_newline statement_list } 1965 # except_action ::= compound_stmt 1966 # else_action ::= compound_stmt 1967 if len(node) == 3: 1968 ifkind, guarded_stmt, except_action, else_action = node[0], node[1], node[2], None 1969 elif len(node) == 5: 1970 ifkind, guarded_stmt, except_action, else_action = node[0], node[1], node[4], None 1971 else: 1972 ifkind, guarded_stmt, except_action, else_action = node[0], node[1], node[4], node[7] 1973 if ifkind.type == "IFNOERR": 1974 except_action, else_action = else_action, except_action 1975 1976 self.writeIndent("taskObj._ecl_push_err()\n") 1977 1978 self._ecl_iferr_entered += 1 1979 self.preorder(guarded_stmt) 1980 self._ecl_iferr_entered -= 1 1981 1982 self.write("\n") 1983 self.writeIndent("if taskObj._ecl_pop_err()") 1984 1985 self.preorder(except_action) 1986 if else_action: 1987 self.writeIndent("else") 1988 self.preorder(else_action) 1989 self.prune()
1990 1991 ## self.writeIndent("try:") 1992 ## self.incrIndent() 1993 1994 ## self._ecl_iferr_entered += 1 1995 ## self.preorder(guarded_stmt) 1996 ## self._ecl_iferr_entered -= 1 1997 1998 ## self.decrIndent() 1999 2000 ## self.writeIndent("except") 2001 2002 ## self.preorder(except_action) 2003 ## if else_action: 2004 ## self.writeIndent("else") 2005 ## self.preorder(else_action) 2006 ## self.prune() 2007 2008
2009 - def n_while_stmt(self, node):
2010 """we've got a 'while' statement""" 2011 # Append this value as a flag to tell n_next_stmt that it should 2012 # not increment the loop variable before writing "continue". 2013 self.save_incr.append(self.IN_A_WHILE_LOOP) 2014 # Save the indentation level, so we can tell when we're leaving 2015 # the 'while' loop. 2016 self.save_indent.append(self.indent)
2017
2018 - def n_for_stmt(self, node):
2019 # convert for loop into while loop 2020 # 2021 # 0 1 2 3 4 5 6 7 8 2022 # for ( initialization ; condition ; increment ) compound_stmt 2023 # 2024 # any of the components inside the parentheses may be empty 2025 # 2026 # -------- initialization -------- 2027 init = node[2] 2028 if init.type == "opt_assign_stmt" and len(init)==0: 2029 # empty initialization 2030 self.write("while (") 2031 else: 2032 self.preorder(init) 2033 self.writeIndent("while (") 2034 # -------- condition -------- 2035 condition = node[4] 2036 if condition.type == "opt_bool" and len(condition)==0: 2037 # empty condition 2038 self.write("1") 2039 else: 2040 self.preorder(condition) 2041 self.write(")") 2042 # -------- execution block -------- 2043 # go down inside the compound_stmt item so the increment can 2044 # be included inside the same block 2045 self.save_incr.append(node[6]) # needed if there's a 'next' statement 2046 self.write(":") 2047 self.incrIndent() 2048 for i in range(node[8].label_count): 2049 self.writeIndent("try:") 2050 self.incrIndent() 2051 for subnode in node[8]: self.preorder(subnode) 2052 # -------- increment -------- 2053 incr = node[6] 2054 if incr.type == "opt_assign_stmt" and len(incr)==0: 2055 # empty increment 2056 pass 2057 else: 2058 self.writeIndent() 2059 self.preorder(incr) 2060 self.decrIndent() 2061 if len(self.save_incr) > 0: 2062 del(self.save_incr[-1]) 2063 self.prune()
2064
2065 - def n_next_stmt(self, node):
2066 if len(self.save_incr) > 0 and \ 2067 self.save_incr[-1] != self.IN_A_WHILE_LOOP: 2068 # increment the loop variable -- copied from n_for_stmt() 2069 incr = self.save_incr[-1] 2070 if incr.type == "opt_assign_stmt" and len(incr)==0: 2071 pass 2072 else: 2073 self.preorder(incr) 2074 self.writeIndent() 2075 self.write("continue") 2076 self.prune()
2077
2078 - def n_label_stmt(self, node):
2079 # labels translate to except statements 2080 # skip unsued labels 2081 label = node[0].attr 2082 if self.gotos.has_key(label): 2083 self.decrIndent() 2084 self.writeIndent("except GoTo_%s:" % irafutils.translateName(label)) 2085 self.incrIndent() 2086 self.writeIndent("pass") 2087 self.decrIndent() 2088 self.prune()
2089
2090 - def n_goto_stmt(self, node):
2091 self.write("raise GoTo_%s" % irafutils.translateName(node[1].attr)) 2092 self.prune()
2093
2094 - def n_inspect_stmt(self, node):
2095 self.write("print ") 2096 if node[0].type == "=": 2097 # '= expr' version of inspect 2098 self.preorder(node[1]) 2099 else: 2100 # 'IDENT =' version of inspect 2101 self.preorder(node[0]) 2102 self.prune()
2103
2104 - def n_switch_stmt(self, node):
2105 self.inSwitch = self.inSwitch + 1 2106 self.caseCount.append(0) 2107 self.write("SwitchVal%d = " % (self.inSwitch,)) 2108 self.preorder(node[2]) 2109 self.preorder(node[4]) 2110 self.inSwitch = self.inSwitch - 1 2111 del self.caseCount[-1] 2112 self.prune()
2113
2114 - def n_case_block(self, node):
2115 self.preorder(node[2]) 2116 self.preorder(node[3]) 2117 self.prune()
2118
2119 - def n_case_stmt_block(self, node):
2120 if self.caseCount[-1] == 0: 2121 self.caseCount[-1] = 1 2122 self.writeIndent("if ") 2123 else: 2124 self.writeIndent("elif ") 2125 self.write("SwitchVal%d in [" % (self.inSwitch,)) 2126 self.preorder(node[2]) 2127 self.write("]") 2128 self.preorder(node[4]) 2129 self.prune()
2130
2131 - def n_default_stmt_block(self, node):
2132 if len(node)>0: 2133 if self.caseCount[-1] == 0: 2134 # only a default in this switch 2135 self.writeIndent("if 1") 2136 else: 2137 self.writeIndent("else") 2138 self.preorder(node[3]) 2139 self.prune()
2140 2141 #------------------------------ 2142 # pipes implemented using redirection + task return values 2143 #------------------------------ 2144
2145 - def n_task_pipe_stmt(self, node):
2146 self.pipeCount = self.pipeCount+1 2147 pipename = 'Pipe' + str(self.pipeCount) 2148 self.pipeOut.append(pipename) 2149 self.preorder(node[0]) 2150 self.pipeOut.pop() 2151 self.pipeIn.append(pipename) 2152 self.writeIndent() 2153 self.preorder(node[2]) 2154 self.pipeIn.pop() 2155 self.pipeCount = self.pipeCount-1 2156 self.prune()
2157 2158 #------------------------------ 2159 # task execution 2160 #------------------------------ 2161
2162 - def n_task_call_stmt(self, node):
2163 self.errorappend(CheckArgList(node)) 2164 taskname = node[0].attr 2165 self.currentTaskname = taskname 2166 # '$' prefix means print time required for task (just ignore it for now) 2167 if taskname[:1] == '$': taskname = taskname[1:] 2168 # translate some special task names and add "iraf." to all names 2169 # additionalArguments will get appended at the end of the 2170 # argument list 2171 self.additionalArguments = [] 2172 addsep = "" 2173 # add plumbing for pipes if necessary 2174 if self.pipeIn: 2175 # read from existing input line list 2176 self.additionalArguments.append("Stdin=" + self.pipeIn[-1]) 2177 if self.pipeOut: 2178 self.write(self.pipeOut[-1] + " = ") 2179 self.additionalArguments.append("Stdout=1") 2180 # add extra arguments for task, package commands 2181 newname = _taskList.get(taskname, taskname) 2182 newname = "iraf." + irafutils.translateName(newname) 2183 if taskname in ('task', 'pyexecute'): 2184 # task, pyexecute need additional package, bin arguments 2185 self.specialDict['PkgName'] = 1 2186 self.additionalArguments.append("PkgName=PkgName") 2187 self.additionalArguments.append("PkgBinary=PkgBinary") 2188 elif taskname == 'package': 2189 # package needs additional package, bin arguments and returns args 2190 self.specialDict['PkgName'] = 1 2191 self.additionalArguments.append("PkgName=PkgName") 2192 self.additionalArguments.append("PkgBinary=PkgBinary") 2193 # package is a function returning new values for PkgName etc. 2194 # except when pipe is specified 2195 if not self.pipeOut: self.write("PkgName, PkgBinary = ") 2196 # add extra argument to save parameters if in "single" mode 2197 if self.vars.mode == "single": 2198 self.additionalArguments.append("_save=1") 2199 self.write(newname) 2200 self.preorder(node[1]) 2201 2202 if self.pipeIn: 2203 # done with this input pipe 2204 self.writeIndent("del " + self.pipeIn[-1]) 2205 2206 if taskname == "clbye" or taskname == "bye": 2207 # must do a return after clbye() or bye() if not in 'single' mode 2208 if self.vars.mode != "single": self.writeIndent("return") 2209 self.prune()
2210
2211 - def n_task_arglist(self, node):
2212 # print task_arglist, adding parentheses if necessary 2213 if len(node) == 3: 2214 # parenthesized arglist 2215 # i is index for args in node 2216 i = 1 2217 elif len(node) == 1: 2218 # unparenthesized arglist 2219 i = 0 2220 else: 2221 # len(node)==2 2222 # fix some common CL script errors 2223 # (these are parsed in sloppy mode) 2224 if node[0].type == "(": 2225 # missing close parenthesis 2226 self.warning("Missing closing parenthesis", node) 2227 i = 1 2228 elif node[1].type == ")": 2229 # missing open parenthesis 2230 self.warning("Missing opening parenthesis", node) 2231 i = 0 2232 2233 # tag argument list with parent for context analysis in case of 2234 # keyword args later 2235 node[i].parent = node 2236 # get the list of arguments 2237 2238 sargs = self.captureArgs(node[i]) 2239 2240 # Delete the extra parentheses on a single argument that already 2241 # has parentheses. This is fixing a parsing ambiguity created by 2242 # the ability to interpret a single parenthesized argument either 2243 # as a parenthesized list or as an unparenthesized list consisting 2244 # of an expression. 2245 if len(sargs) == 1: 2246 s = sargs[0] 2247 if s[:1] == "(" and s[-1:] == ")": sargs[0] = s[1:-1] 2248 2249 if self.currentTaskname in ["scan", "fscan", "scanf", "fscanf"]: 2250 # scan is weird -- effectively uses call-by-name 2251 # call special routine to change the args 2252 sargs = self.modify_scan_args(self.currentTaskname, sargs) 2253 2254 # combine CL arguments with additional (redirection) arguments 2255 sargs = sargs + self.additionalArguments 2256 self.additionalArguments = [] 2257 2258 # break up arg list into line-sized chunks 2259 self.write("(") 2260 self.writeChunks(sargs) 2261 self.write(")") 2262 self.prune()
2263
2264 - def captureArgs(self, node):
2265 """Process the arguments list and return a list of the args""" 2266 2267 # arguments get written to a separate string so we can 2268 # decide whether extra parens are really needed or not 2269 # Also add special character after arguments to make it 2270 # easier to break up long lines 2271 2272 arg_buffer = cStringIO.StringIO() 2273 saveColumn = self.column 2274 saveBuffer = self.code_buffer 2275 self.code_buffer = arg_buffer 2276 2277 # add a special character after commas to make it easy 2278 # to break up argument list for long lines 2279 global _translateList 2280 # save current translation for comma to handle nested lists 2281 curComma = _translateList.get(',') 2282 _translateList[','] = ',\255' 2283 2284 self.preorder(node) 2285 2286 # restore original comma translation and buffer pointers 2287 if curComma is None: 2288 del _translateList[','] 2289 else: 2290 _translateList[','] = curComma 2291 self.code_buffer = saveBuffer 2292 self.column = saveColumn 2293 2294 args = arg_buffer.getvalue() 2295 arg_buffer.close() 2296 2297 # split arguments into list 2298 sargs = args.split(',\255') 2299 if sargs[0] == '': del sargs[0] 2300 return sargs
2301
2302 - def writeChunks(self, arglist, linelength=78):
2303 # break up arg list into line-sized chunks 2304 if not arglist: return 2305 maxline = linelength - self.column 2306 newargs = arglist[0] 2307 for arg in arglist[1:]: 2308 if len(newargs)+len(arg)+2>maxline: 2309 self.write(newargs + ',') 2310 #self.writeIndent('\t') 2311 newargs = arg 2312 maxline = linelength - self.column 2313 else: 2314 newargs = newargs + ', ' + arg 2315 self.write(newargs)
2316
2317 - def n_empty_arg(self, node):
2318 #XXX This is an omitted argument 2319 #XXX Not really correct yet -- need to work on this 2320 self.write('None') 2321 self.prune()
2322
2323 - def n_bool_arg(self, node):
2324 self.preorder(node[0]) 2325 if node[1].type == "+": 2326 self.write("=yes") 2327 else: 2328 self.write("=no") 2329 self.prune()
2330
2331 - def n_redir_arg(self, node):
2332 # redirection is handled by special keyword parameters 2333 # Stdout=<filename>, Stdin=<filename>, Stderr=<filename>, etc. 2334 s = node[0].attr 2335 redir = _RedirDict.get(s) 2336 if redir is None: 2337 # must be GIP redirection, construct a standard name 2338 # using GIP in sorted order 2339 tail = [] 2340 while s[-1] in 'PIG': 2341 tail.append(s[-1]) 2342 s = s[:-1] 2343 tail.sort() 2344 redir = _RedirDict[s] + ''.join(tail) 2345 self.write(redir + '=') 2346 self.preorder(node[1]) 2347 self.prune()
2348
2349 - def n_keyword_arg(self, node):
2350 # This is needed to handle cursor parameters, which should 2351 # be passed as objects rather than by value. 2352 assert len(node)==3 2353 self.preorder(node[0]) 2354 self.preorder(node[1]) 2355 # only the value needs special handling 2356 if node[2].type == 'IDENT': 2357 s = irafutils.translateName(node[2].attr) 2358 v = self.vars.get(s) 2359 if v and v.type in ['gcur','imcur']: 2360 # pass cursors by value 2361 self.write('Vars.getParObject("'+s+'")') 2362 self.prune() 2363 return 2364 self.preorder(node[2]) 2365 self.prune()
2366 2367 if __name__ == "__main__": 2368 2369 import time 2370 2371 t0 = time.time() 2372 2373 # scan file "simple.cl" 2374 2375 filename = "simple.cl" 2376 lines = open(filename).read() 2377 scanner = clscan.CLScanner() 2378 tokens = scanner.tokenize(lines) 2379 t1 = time.time() 2380 2381 # parse 2382 tree = _parser.parse(tokens, fname=filename) 2383 tree.filename = filename 2384 t2 = time.time() 2385 2386 # first pass -- get variables 2387 2388 vars = VarList(tree) 2389 2390 # second pass -- check all expression types 2391 # type info is added to tree 2392 2393 TypeCheck(tree, vars, '') 2394 2395 # third pass -- generate python code 2396 2397 pycode = Tree2Python(tree, vars) 2398 2399 t3 = time.time() 2400 2401 print "Scan:", t1-t0, "sec, Parse:", t2-t1, "sec" 2402 print "CodeGen:", t3-t2, "sec" 2403