1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 """Parsers for algebraic expressions coming from UFO, outputting into
17 different languages/frameworks (Fortran and Pythia8). Uses the PLY 3.3
18 Lex + Yacc framework"""
19
20 import logging
21 import os
22 import re
23 import sys
24
25 root_path = os.path.split(os.path.dirname(os.path.realpath( __file__ )))[0]
26 sys.path.append(os.path.join(root_path, os.path.pardir))
27
28 from madgraph import MadGraph5Error
29 import vendor.ply.lex as lex
30 import vendor.ply.yacc as yacc
31 logger = logging.getLogger('madgraph.ufo_parsers')
32
33
34
36 """Appropriate Error for a wrong parsing"""
37
39 """A base class for parsers for algebraic expressions coming from UFO."""
40
41 parsed_string = ""
42
44 """Ininitialize the lex and yacc"""
45
46 modname = self.__class__.__name__
47 self.debugfile = os.path.devnull
48 self.tabmodule = os.path.join(root_path, "iolibs", modname + "_" + "parsetab.py")
49 lex.lex(module=self, debug=0)
50 yacc.yacc(module=self, debug=0, debugfile=self.debugfile,
51 tabmodule=self.tabmodule)
52
57
58
59 tokens = (
60 'POWER', 'CSC', 'SEC', 'ACSC', 'ASEC',
61 'SQRT', 'CONJ', 'RE', 'IM', 'PI', 'COMPLEX', 'FUNCTION',
62 'VARIABLE', 'NUMBER'
63 )
64 literals = "=+-*/(),"
65
66
67
69 r'(?<!\w)csc(?=\()'
70 return t
72 r'(?<!\w)sec(?=\()'
73 return t
75 r'(?<!\w)acsc(?=\()'
76 return t
78 r'(?<!\w)asec(?=\()'
79 return t
81 r'cmath\.sqrt'
82 return t
84 r'cmath\.pi'
85 return t
87 r'complexconjugate'
88 return t
90 r'(?<!\w)im(?=\()'
91 return t
93 r'(?<!\w)re(?=\()'
94 return t
96 r'(?<!\w)complex(?=\()'
97 return t
99 r'(cmath\.){0,1}[a-zA-Z_][0-9a-zA-Z_]*(?=\()'
100 return t
102 r'[a-zA-Z_][0-9a-zA-Z_]*'
103 return t
104
105 t_NUMBER = r'([0-9]+\.[0-9]*|\.[0-9]+|[0-9]+)([eE][+-]{0,1}[0-9]+){0,1}'
106 t_POWER = r'\*\*'
107
108 t_ignore = " \t"
109
110 re_cmath_function = re.compile("cmath\.(?P<name>[0-9a-zA-Z_]+)")
111
113 r'\n+'
114 t.lexer.lineno += t.value.count("\n")
115
117 logger.error("Illegal character '%s'" % t.value[0])
118 t.lexer.skip(1)
119
120
121 - def build(self,**kwargs):
122 self.lexer = lex.lex(module=self, **kwargs)
123
124
125
126
127 precedence = (
128 ('left','='),
129 ('left','+','-'),
130 ('left','*','/'),
131 ('right','UMINUS'),
132 ('left','POWER'),
133 ('right','CSC'),
134 ('right','SEC'),
135 ('right','ACSC'),
136 ('right','ASEC'),
137 ('right','SQRT'),
138 ('right','CONJ'),
139 ('right','RE'),
140 ('right','IM'),
141 ('right','FUNCTION'),
142 ('right','COMPLEX')
143 )
144
145
149
151 '''expression : expression '=' expression
152 | expression '+' expression
153 | expression '-' expression
154 | expression '*' expression
155 | expression '/' expression'''
156 p[0] = p[1] + p[2] + p[3]
157
159 "expression : '-' expression %prec UMINUS"
160 p[0] = '-' + p[2]
161
163 "group : '(' expression ')'"
164 p[0] = '(' + p[2] +')'
165
167 "expression : group"
168 p[0] = p[1]
169
171 "expression : FUNCTION '(' expression ')'"
172 p1 = p[1]
173 re_groups = self.re_cmath_function.match(p1)
174 if re_groups:
175 p1 = re_groups.group("name")
176 p[0] = p1 + '(' + p[3] + ')'
177
179 "expression : FUNCTION '(' expression ',' expression ')'"
180 p1 = p[1]
181 re_groups = self.re_cmath_function.match(p1)
182 if re_groups:
183 p1 = re_groups.group("name")
184 p[0] = p1 + '(' + p[3] + ',' + p[5] + ')'
185
187 if p:
188 raise ModelError("Syntax error at '%s' in '%s'" % (p.value, self.f))
189 else:
190 logger.error("Syntax error at EOF")
191 self.parsed_string = "Error"
192
194 """A parser for UFO algebraic expressions, outputting
195 Fortran-style code."""
196
197
198
199
201 "expression : NUMBER"
202 p[0] = ('%e' % float(p[1])).replace('e', 'd')
203
205 "expression : VARIABLE"
206 p[0] = p[1].lower()
207
209 'expression : expression POWER expression'
210 try:
211 p3 = float(p[3].replace('d','e'))
212
213 if p3 == int(p3):
214 p3 = str(int(p3))
215 p[0] = p[1] + "**" + p3
216 else:
217 p[0] = p[1] + "**" + p[3]
218 except:
219 p[0] = p[1] + "**" + p[3]
220
222 "expression : COMPLEX '(' expression ',' expression ')'"
223 p[0] = '(' + p[3] + ',' + p[5] + ')'
224
226 '''expression : CSC group
227 | SEC group
228 | ACSC group
229 | ASEC group
230 | RE group
231 | IM group
232 | SQRT group
233 | CONJ group'''
234 if p[1] == 'csc': p[0] = '1d0/cos' + p[2]
235 elif p[1] == 'sec': p[0] = '1d0/sin' + p[2]
236 elif p[1] == 'acsc': p[0] = 'asin(1./' + p[2] + ')'
237 elif p[1] == 'asec': p[0] = 'acos(1./' + p[2] + ')'
238 elif p[1] == 're': p[0] = 'dble' + p[2]
239 elif p[1] == 'im': p[0] = 'dimag' + p[2]
240 elif p[1] == 'cmath.sqrt' or p[1] == 'sqrt': p[0] = 'dsqrt' + p[2]
241 elif p[1] == 'complexconjugate': p[0] = 'conjg' + p[2]
242
244 '''expression : PI'''
245 p[0] = 'pi'
246
248 """A parser for UFO algebraic expressions, outputting
249 C++-style code."""
250
251
252
253
255 "expression : NUMBER"
256 p[0] = p[1]
257
258 if float(p[1]) == int(float(p[1])) and float(p[1]) < 1000:
259 p[0] = str(int(float(p[1]))) + '.'
260
262 "expression : VARIABLE"
263 p[0] = p[1]
264
266 'expression : expression POWER expression'
267 p1=p[1]
268 p3=p[3]
269 if p[1][0] == '(' and p[1][-1] == ')':
270 p1 = p[1][1:-1]
271 if p[3][0] == '(' and p[3][-1] == ')':
272 p3 = p[3][1:-1]
273 p[0] = 'pow(' + p1 + ',' + p3 + ')'
274
276 "expression : COMPLEX '(' expression ',' expression ')'"
277 p[0] = 'std::complex<double>(' + p[3] + ',' + p[5] + ')'
278
280 '''expression : CSC group
281 | SEC group
282 | ACSC group
283 | ASEC group
284 | RE group
285 | IM group
286 | SQRT group
287 | CONJ group'''
288 if p[1] == 'csc': p[0] = '1./cos' + p[2]
289 elif p[1] == 'sec': p[0] = '1./sin' + p[2]
290 elif p[1] == 'acsc': p[0] = 'asin(1./' + p[2] + ')'
291 elif p[1] == 'asec': p[0] = 'acos(1./' + p[2] + ')'
292 elif p[1] == 're': p[0] = 'real' + p[2]
293 elif p[1] == 'im': p[0] = 'imag' + p[2]
294 elif p[1] == 'cmath.sqrt' or p[1] == 'sqrt': p[0] = 'sqrt' + p[2]
295 elif p[1] == 'complexconjugate': p[0] = 'conj' + p[2]
296
298 '''expression : PI'''
299 p[0] = 'M_PI'
300
301
302
303 if __name__ == '__main__':
304
305 if len(sys.argv) == 1:
306 print "Please specify a parser: fortran or pythia8"
307 exit()
308 if sys.argv[1] == "fortran":
309 calc = UFOExpressionParserFortran()
310 elif sys.argv[1] == "c++":
311 calc = UFOExpressionParserCPP()
312 else:
313 print "Please specify a parser: tex, fortran or c++"
314 print "You gave", sys.argv
315 exit()
316
317 while 1:
318 try:
319 s = raw_input('calc > ')
320 except EOFError:
321 break
322 if not s: continue
323 print calc.parse(s)
324