Package pysynphot :: Module spparser
[hide private]
[frames] | no frames]

Source Code for Module pysynphot.spparser

  1  from __future__ import division 
  2  from spark import GenericScanner, GenericParser, GenericASTTraversal 
  3  from spark import GenericASTBuilder, GenericASTMatcher 
  4  import spectrum 
  5  import reddening 
  6  import observationmode 
  7  import locations 
  8  import catalog 
  9  import os 
 10  from obsbandpass import ObsBandpass 
 11  
 
 12  # This file implements the pysynphot language parser.
 
 13  #
 
 14  # The language definition is in the docstring of class BaseParser,
 
 15  # function p_top.  The parser code in spark.py builds its internal
 
 16  # tables by reading the docstring, so you can't put anything else
 
 17  # (like documentation) there.
 
 18  #
 
 19  # l = scan('text') returns a list of tokens
 
 20  #
 
 21  # t = parse(l) converts the list of tokens into an Abstract Syntax Tree
 
 22  #
 
 23  # r = interpret(t) converts that abstract syntax tree into a (tree
 
 24  #     of?) pysynphot object, based on the conversion rules in class Interpreter
 
 25  #
 
 26  # In class Interpreter, the docstring of every function named with p_
 
 27  # is part of the instructions to the parser.
 
 28  
 
 29  syfunctions = [
 
 30      'spec',
 
 31      'unit',
 
 32      'box',
 
 33      'bb',
 
 34      'pl',
 
 35      'em',
 
 36      'icat',
 
 37      'rn',
 
 38      'z',
 
 39      'ebmvx',
 
 40      'band'
 
 41      ] 
 42  
 
 43  synforms = [
 
 44      'fnu',
 
 45      'flam',
 
 46      'photnu',
 
 47      'photlam',
 
 48      'counts',
 
 49      'abmag',
 
 50      'stmag',
 
 51      'obmag',
 
 52      'vegamag',
 
 53      'jy',
 
 54      'mjy'
 
 55      ] 
 56  
 
 57  syredlaws = [
 
 58      'gal1',
 
 59      'gal2',
 
 60      'gal3',
 
 61      'smc',
 
 62      'lmc',
 
 63      'xgal'
 
 64      ] 
 65  
 
66 -class Token:
67 - def __init__(self, type=None, attr=None):
68 self.type = type 69 self.attr = attr
70 - def __cmp__(self, o):
71 return cmp(self.type, o)
72 - def __repr__(self):
73 if self.attr is not None: 74 return str(self.attr) 75 else: 76 return self.type
77
78 -class AST:
79 - def __init__(self, type):
80 self.type = type 81 self._kids = []
82 - def __getitem__(self, i):
83 return self._kids[i]
84 - def __len__(self):
85 return len(self._kids)
86 - def __setslice__(self, low, high, seq):
87 self._kids[low:high] = seq
88 - def __cmp__(self, o):
89 return cmp(self.type, o)
90
91 -class BaseScanner(GenericScanner):
92 - def __init__(self):
94 - def tokenize(self, input):
95 self.rv = [] 96 GenericScanner.tokenize(self, input) 97 return self.rv
98 - def t_whitespace(self, s):
99 r' \s+ '
100 - def t_op(self, s):
101 r' \+ | \* | - ' 102 self.rv.append(Token(type=s))
103 - def t_lparens(self, s):
104 r' \( ' 105 self.rv.append(Token(type='LPAREN'))
106 - def t_rparens(self, s):
107 r' \) ' 108 self.rv.append(Token(type='RPAREN'))
109 - def t_comma(self, s):
110 r' , ' 111 self.rv.append(Token(type=s))
112 - def t_integer(self, s):
113 r' \d+ ' 114 self.rv.append(Token(type='INTEGER', attr=s))
115 - def t_identifier(self, s):
116 r' [$a-z_A-Z/\//][\w/\.\$:]*' 117 self.rv.append(Token(type='IDENTIFIER', attr=s))
118 - def t_filelist(self, s):
119 r' @\w+' 120 self.rv.append(Token(type='FILELIST', attr=s[1:]))
121
122 -class Scanner(BaseScanner):
123 - def __init__(self):
125 - def t_float(self, s):
126 r' ((\d*\.\d+)|(\d+\.d*)|(\d+)) ([eE][-+]?\d+)?' 127 self.rv.append(Token(type='FLOAT', attr=s))
128 - def t_divop(self, s):
129 r' \s/\s ' 130 self.rv.append(Token(type='/'))
131
132 -class BaseParser(GenericASTBuilder):
133 - def __init__(self, ASTclass, start='top'):
134 GenericASTBuilder.__init__(self, ASTclass, start)
135 - def p_top(self, args):
136 ''' 137 top ::= expr 138 top ::= FILELIST 139 expr ::= expr + term 140 expr ::= expr - term 141 expr ::= term 142 term ::= term * factor 143 term ::= term / factor 144 value ::= LPAREN expr RPAREN 145 term ::= factor 146 factor ::= unaryop value 147 factor ::= value 148 unaryop ::= + 149 unaryop ::= - 150 value ::= INTEGER 151 value ::= FLOAT 152 value ::= IDENTIFIER 153 value ::= function_call 154 function_call ::= IDENTIFIER LPAREN arglist RPAREN 155 arglist ::= arglist , expr 156 arglist ::= expr 157 '''
158 - def terminal(self, token):
159 rv = AST(token.type) 160 rv.attr = token.attr 161 return rv
162 - def nonterminal(self, type, args):
163 if len(args) == 1: 164 return args[0] 165 return GenericASTBuilder.nonterminal(self, type, args)
166
167 -class Interpreter(GenericASTMatcher):
168 - def __init__(self, ast):
169 GenericASTMatcher.__init__(self, 'V', ast)
170 - def error(self, token):
171 raise ValueError("problems in interpreting AST")
172 - def p_int(self, tree):
173 ''' V ::= INTEGER ''' 174 tree.value = int(tree.attr) 175 tree.svalue = tree.attr
176 - def p_float(self, tree):
177 ''' V ::= FLOAT ''' 178 tree.value = float(tree.attr) 179 tree.svalue = tree.attr
180 - def p_identifier(self, tree):
181 ''' V ::= IDENTIFIER ''' 182 tree.value = tree.attr 183 tree.svalue = tree.attr
184 - def p_factor_unary_plus(self, tree):
185 ''' V ::= factor ( + V ) ''' 186 tree.value = convertstr(tree[1].value)
187 - def p_factor_unary_minus(self, tree):
188 ''' V ::= factor ( - V ) ''' 189 tree.value = - convertstr(tree[1].value)
190 - def p_expr_plus(self, tree):
191 ''' V ::= expr ( V + V )''' 192 tree.value = convertstr(tree[0].value) + convertstr(tree[2].value)
193 - def p_expr_minus(self, tree):
194 ''' V ::= expr ( V - V )''' 195 tree.value = convertstr(tree[0].value) - convertstr(tree[2].value)
196 - def p_term_mult(self, tree):
197 ''' V ::= term ( V * V )''' 198 tree.value = convertstr(tree[0].value) * convertstr(tree[2].value)
199 - def p_term_div(self, tree):
200 ''' V ::= term ( V / V )''' 201 tree.value = convertstr(tree[0].value) / tree[2].value
202 - def p_value_paren(self, tree):
203 ''' V ::= value ( LPAREN V RPAREN )''' 204 tree.value = convertstr(tree[1].value) 205 tree.svalue = "(%s)"%str(tree[1].value)
206 - def p_arglist(self, tree):
207 ''' V ::= arglist ( V , V )''' 208 if type(tree[0].value) == type([]): 209 tree.value = tree[0].value + [tree[2].value] 210 else: 211 tree.value = [tree[0].value, tree[2].value] 212 try: 213 tree.svalue = "%s,%s"%(tree[0].svalue,tree[2].svalue) 214 except AttributeError: 215 pass #We only care about this for relatively simple constructs.
216
217 - def p_functioncall(self, tree):
218 # Where all the real interpreter action is 219 # Note that things that should only be done at the top level 220 # are performed in the interpret function defined below. 221 ''' V ::= function_call ( V LPAREN V RPAREN )''' 222 if type(tree[2].value) != type([]): 223 args = [tree[2].value] 224 else: 225 args = tree[2].value 226 fname = tree[0].value 227 if fname not in syfunctions: 228 print "Error: unknown function:", fname 229 self.error(fname) 230 else: 231 if fname == 'unit': 232 # constant spectrum 233 tree.value = spectrum.FlatSpectrum(args[0],fluxunits=args[1]) 234 elif fname == 'bb': 235 # black body 236 tree.value = spectrum.BlackBody(args[0]) 237 elif fname == 'pl': 238 # power law 239 if args[2] not in synforms: 240 print "Error: unrecognized units:", args[2] 241 # code to create powerlaw spectrum object 242 tree.value = spectrum.Powerlaw(args[0],args[1],fluxunits=args[2]) 243 elif fname == 'box': 244 # box throughput 245 tree.value = spectrum.Box(args[0],args[1]) 246 elif fname == 'spec': 247 # spectrum from reference file (for now....) 248 name = args[0] 249 tree.value = spectrum.TabularSourceSpectrum(_handleIRAFName(name)) 250 elif fname == 'band': 251 # passband 252 args=tree[2].svalue 253 tree.value = ObsBandpass(args) 254 elif fname == 'em': 255 # emission line 256 tree.value = spectrum.GaussianSource(args[2],args[0],args[1],fluxunits=args[3]) 257 elif fname == 'icat': 258 # catalog interpolation 259 tree.value = catalog.Icat(*args) 260 elif fname == 'rn': 261 # renormalize 262 sp = args[0] 263 if not isinstance(sp,spectrum.SourceSpectrum): 264 name=_handleIRAFName(args[0]) 265 sp = spectrum.TabularSourceSpectrum(name) 266 #Always force the renormalization to occur: prevent exceptions 267 #in case of partial overlap. Less robust but duplicates synphot. 268 269 try: 270 tree.value = sp.renorm(args[2],args[3],args[1]) 271 except ValueError: 272 tree.value = sp.renorm(args[2],args[3],args[1],force=True) 273 tree.value.warnings['force_renorm'] = 'Warning: Renormalization of the spectrum, to the specified value, in the specified units, exceeds the limit of the specified passband.' 274 275 elif fname == 'z': 276 # redshift 277 if args[0] != 'null': # the ETC generates junk sometimes.... 278 try: 279 tree.value = args[0].redshift(args[1]) 280 except AttributeError: 281 try: 282 #name = getName(args[0]) 283 sp = spectrum.TabularSourceSpectrum( \ 284 _handleIRAFName(args[0])) 285 tree.value = sp.redshift(args[1]) 286 except AttributeError: 287 tree.value = spectrum.FlatSpectrum(1.0) 288 else: 289 tree.value = spectrum.FlatSpectrum(1.0) 290 elif fname == 'ebmvx': 291 # extinction 292 tree.value = reddening.Extinction(args[0],args[1]) 293 294 else: 295 tree.value = "would call %s with the following args: %s" % (fname, repr(args))
296 297 298 # stuff not yet handled, namely, Filelist, should be handled in interp function 299 zzz = ''' 300 301 top ::= FILELIST 302 303 ''' 304
305 -def convertstr(value):
306 # Any string appearing in numeric expressions must be 307 # assumed to be a filename that should be read in as a table 308 # This is a utility function used by the interpreter to do the 309 # conversion from string to spectrum object 310 if type(value) == type(''): 311 return _handleThroughputFiles(_handleIRAFName(value)) 312 else: 313 return value
314
315 -def scan(input):
316 scanner = Scanner() 317 input = input.replace('%2b','+') 318 return scanner.tokenize(input)
319
320 -def parse(tokens):
321 parser = BaseParser(AST) 322 return parser.parse(tokens)
323
324 -def interpret(ast):
325 interpreter = Interpreter(ast) 326 interpreter.match() 327 value = ast.value 328 return convertstr(value)
329
330 -def ptokens(tlist):
331 for token in tlist: 332 print token.type, token.attr
333 334
335 -def _handleIRAFName(name):
336 """Calls locations.irafconvert() to translate shell or iraf variables""" 337 338 return locations.irafconvert(name)
339
340 -def _handleThroughputFiles(name):
341 #Most files will be spectrum files, but some will be throughput files. 342 try: 343 return spectrum.TabularSourceSpectrum(_handleIRAFName(name)) 344 except NameError: 345 return spectrum.TabularSourceSpectrum(_handleIRAFName(name))
346 347 #Convenience function
348 -def parse_spec(syncommand):
349 """Parse the synphot-classic command and return the resulting spectrum""" 350 sp = interpret(parse(scan(syncommand))) 351 return sp
352