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 import copy
25
26 root_path = os.path.split(os.path.dirname(os.path.realpath( __file__ )))[0]
27 sys.path.append(os.path.join(root_path, os.path.pardir))
28
29 import madgraph.various.misc as misc
30
31 from madgraph import MadGraph5Error
32 import vendor.ply.lex as lex
33 import vendor.ply.yacc as yacc
34 import models.check_param_card as check_param_card
35
36 logger = logging.getLogger('madgraph.ufo_parsers')
37
38
39
41 """Appropriate Error for a wrong parsing"""
42
44 """A base class for parsers for algebraic expressions coming from UFO."""
45
46 parsed_string = ""
47 logical_equiv = {}
48
50 """Initialize the lex and yacc"""
51
52 modname = self.__class__.__name__
53 self.debugfile = os.path.devnull
54 self.tabmodule = os.path.join(root_path, "iolibs", modname + "_" + "parsetab.py")
55 lex.lex(module=self, debug=0)
56 self.y=yacc.yacc(module=self, debug=0, debugfile=self.debugfile,
57 tabmodule=self.tabmodule)
58
63
64
65 tokens = (
66 'LOGICAL','LOGICALCOMB','POWER', 'CSC', 'SEC', 'ACSC', 'ASEC', 'TAN',
67 'SQRT', 'CONJ', 'RE', 'RE2', 'IM', 'PI', 'COMPLEX', 'FUNCTION', 'IF','ELSE',
68 'VARIABLE', 'NUMBER','COND','REGLOG', 'ARG'
69 )
70 literals = "=+-*/(),"
71
72
73
75 r'(?<!\w)csc(?=\()'
76 return t
78 r'(?<!\w)sec(?=\()'
79 return t
81 r'(?<!\w)acsc(?=\()'
82 return t
84 r'(?<!\w)tan(?=\()|(?<!\w)cmath.tan(?=\()'
85 return t
87 r'(?<!\w)asec(?=\()'
88 return t
90 r'(?<!\w)reglog(?=\()'
91 return t
93 r'(?<!\w)cond(?=\()'
94 return t
96 r'(?<!\w)arg(?=\()'
97 return t
99 r'(?<!\w)if\s'
100 return t
102 r'(?<!\w)else\s'
103 return t
105 r'==|!=|<=|>=|<|>'
106 return t
108 r'(?<!\w)and(?=[\s\(])|(?<!\w)or(?=[\s\(])'
109 return t
111 r'cmath\.sqrt'
112 return t
114 r'cmath\.pi'
115 return t
117 r'complexconjugate'
118 return t
120 r'(?<!\w)im(?=\()'
121 return t
123 r'(?<!\w)re(?=\()'
124 return t
126 r'\.real|\.imag'
127 return t
128
130 r'(?<!\w)complex(?=\()'
131 return t
133 r'(cmath\.){0,1}[a-zA-Z_][0-9a-zA-Z_]*(?=\()'
134 return t
136 r'[a-zA-Z_][0-9a-zA-Z_]*'
137 return t
138
139 t_NUMBER = r'([0-9]+\.[0-9]*|\.[0-9]+|[0-9]+)([eE][+-]{0,1}[0-9]+){0,1}j{0,1}'
140 t_POWER = r'\*\*'
141
142 t_ignore = " \t"
143
144 re_cmath_function = re.compile("cmath\.(?P<name>[0-9a-zA-Z_]+)")
145
147 r'\n+'
148 t.lexer.lineno += t.value.count("\n")
149
151 logger.error("Illegal character '%s'" % t.value[0])
152 t.lexer.skip(1)
153
154
155 - def build(self,**kwargs):
156 self.lexer = lex.lex(module=self, **kwargs)
157
158
159
160 precedence = (
161 ('right', 'LOGICALCOMB'),
162 ('right', 'LOGICAL'),
163 ('right','IF'),
164 ('right','ELSE'),
165 ('left','='),
166 ('left','+','-'),
167 ('left','*','/'),
168 ('left', 'RE2'),
169 ('right','UMINUS'),
170 ('left','POWER'),
171 ('right','REGLOG'),
172 ('right','ARG'),
173 ('right','CSC'),
174 ('right','SEC'),
175 ('right','ACSC'),
176 ('right','ASEC'),
177 ('right','SQRT'),
178 ('right','CONJ'),
179 ('right','RE'),
180 ('right','IM'),
181 ('right','FUNCTION'),
182 ('right','COMPLEX'),
183 ('right','COND'),
184 )
185
186
190
192 '''expression : expression '=' expression
193 | expression '+' expression
194 | expression '-' expression
195 | expression '*' expression
196 | expression '/' expression'''
197 p[0] = p[1] + p[2] + p[3]
198
200 '''boolexpression : expression LOGICAL expression'''
201 if p[2] not in self.logical_equiv:
202 p[0] = p[1] + p[2] + p[3]
203 else:
204 p[0] = p[1] + self.logical_equiv[p[2]] + p[3]
205
207 '''boolexpression : boolexpression LOGICALCOMB boolexpression'''
208 if p[2] not in self.logical_equiv:
209 p[0] = p[1] + p[2] + p[3]
210 else:
211 p[0] = p[1] + self.logical_equiv[p[2]] + p[3]
212
214 "expression : '-' expression %prec UMINUS"
215 p[0] = '-' + p[2]
216
218 "group : '(' expression ')'"
219 p[0] = '(' + p[2] +')'
220
222 "boolexpression : '(' boolexpression ')'"
223 p[0] = '(' + p[2] +')'
224
226 "expression : group"
227 p[0] = p[1]
228
230 "expression : FUNCTION '(' expression ')'"
231 p1 = p[1]
232 re_groups = self.re_cmath_function.match(p1)
233 if re_groups:
234 p1 = re_groups.group("name")
235 p[0] = p1 + '(' + p[3] + ')'
236
238 "expression : FUNCTION '(' expression ',' expression ')'"
239 p1 = p[1]
240 re_groups = self.re_cmath_function.match(p1)
241 if re_groups:
242 p1 = re_groups.group("name")
243 p[0] = p1 + '(' + p[3] + ',' + p[5] + ')'
244
246 if p:
247 raise ModelError("Syntax error at '%s' (%s)." %(p.value,p))
248 else:
249 logger.error("Syntax error at EOF")
250 self.parsed_string = "Error"
251
253 """A parser for UFO algebraic expressions, outputting
254 Fortran-style code."""
255
256
257
258
259 logical_equiv = {'==':'.EQ.',
260 '>=':'.GE.',
261 '<=':'.LE.',
262 '!=':'.NE.',
263 '>':'.GT.',
264 '<':'.LT.',
265 'or':'.OR.',
266 'and':'.AND.'}
267
269 "expression : NUMBER"
270 if p[1].endswith('j'):
271 p[0] = ('DCMPLX(0d0, %e)' % float(p[1][:-1])).replace('e', 'd')
272 else:
273 p[0] = ('%e' % float(p[1])).replace('e', 'd')
274
276 "expression : VARIABLE"
277 p[0] = p[1].lower()
278
280 'expression : expression POWER expression'
281 try:
282 p3 = float(p[3].replace('d','e'))
283
284 if p3 == int(p3):
285 p3 = str(int(p3))
286 p[0] = p[1] + "**" + p3
287 else:
288 p[0] = p[1] + "**" + p[3]
289 except Exception:
290 p[0] = p[1] + "**" + p[3]
291
293 "expression : expression IF boolexpression ELSE expression "
294 p[0] = 'CONDIF(%s,DCMPLX(%s),DCMPLX(%s))' % (p[3], p[1], p[5])
295
297 "expression : expression IF expression ELSE expression "
298 p[0] = 'CONDIF(DCMPLX(%s).NE.(0d0,0d0),DCMPLX(%s),DCMPLX(%s))'\
299 %(p[3], p[1], p[5])
300
302 "expression : COND '(' expression ',' expression ',' expression ')'"
303 p[0] = 'COND(DCMPLX('+p[3]+'),DCMPLX('+p[5]+'),DCMPLX('+p[7]+'))'
304
306 "expression : COMPLEX '(' expression ',' expression ')'"
307 p[0] = 'DCMPLX(' + p[3] + ',' + p[5] + ')'
308
310 '''expression : CSC group
311 | SEC group
312 | ACSC group
313 | ASEC group
314 | RE group
315 | IM group
316 | ARG group
317 | SQRT group
318 | CONJ group
319 | REGLOG group
320 | TAN group'''
321
322 if p[1] == 'csc': p[0] = '1d0/cos' + p[2]
323 elif p[1] == 'sec': p[0] = '1d0/sin' + p[2]
324 elif p[1] == 'acsc': p[0] = 'asin(1./' + p[2] + ')'
325 elif p[1] == 'asec': p[0] = 'acos(1./' + p[2] + ')'
326 elif p[1] in ['tan', 'cmath.tan'] : p[0] = 'tan(dble' + p[2]+')'
327 elif p[1] == 're': p[0] = 'dble' + p[2]
328 elif p[1] == 'im': p[0] = 'dimag' + p[2]
329 elif p[1] == 'arg': p[0] = 'arg(DCMPLX'+p[2]+')'
330 elif p[1] == 'cmath.sqrt' or p[1] == 'sqrt': p[0] = 'sqrt(dcmplx' + p[2]+')'
331 elif p[1] == 'complexconjugate': p[0] = 'conjg(DCMPLX' + p[2]+')'
332 elif p[1] == 'reglog': p[0] = 'reglog(DCMPLX' + p[2] +')'
333
334
336 ''' expression : expression RE2 '''
337
338 if p[2] == '.real':
339 if p[1].startswith('('):
340 p[0] = 'dble' +p[1]
341 else:
342 p[0] = 'dble(%s)' % p[1]
343 elif p[2] == '.imag':
344 if p[1].startswith('('):
345 p[0] = 'dimag' +p[1]
346 else:
347 p[0] = 'dimag(%s)' % p[1]
348
350 '''expression : PI'''
351 p[0] = 'pi'
352
354 """A parser for UFO algebraic expressions, outputting
355 Fortran-style code for quadruple precision computation."""
356
357 mp_prefix = check_param_card.ParamCard.mp_prefix
358
359
360
361
363 "expression : NUMBER"
364
365 if p[1].endswith('j'):
366 p[0] = 'CMPLX(0.000000e+00_16, %e_16 ,KIND=16)' % float(p[1][:-1])
367 else:
368 p[0] = '%e_16' % float(p[1])
369
371 "expression : VARIABLE"
372
373 p[0] = (self.mp_prefix+p[1]).lower()
374
376 'expression : expression POWER expression'
377 try:
378 p3 = float(p[3].replace('_16',''))
379
380 if p3 == int(p3):
381 p3 = str(int(p3))
382 p[0] = p[1] + "**" + p3
383 else:
384 p[0] = p[1] + "**" + p[3]
385 except Exception:
386 p[0] = p[1] + "**" + p[3]
387
389 "expression : expression IF boolexpression ELSE expression "
390 p[0] = 'MP_CONDIF(%s,CMPLX(%s,KIND=16),CMPLX(%s,KIND=16))' % (p[3], p[1], p[5])
391
393 "expression : expression IF expression ELSE expression "
394 p[0] = 'MP_CONDIF(CMPLX(%s,KIND=16).NE.(0.0e0_16,0.0e0_16),CMPLX(%s,KIND=16),CMPLX(%s,KIND=16))'\
395 %(p[3], p[1], p[5])
396
398 "expression : COMPLEX '(' expression ',' expression ')'"
399 p[0] = 'CMPLX(' + p[3] + ',' + p[5] + ',KIND=16)'
400
402 "expression : COND '(' expression ',' expression ',' expression ')'"
403 p[0] = 'MP_COND(CMPLX('+p[3]+',KIND=16),CMPLX('+p[5]+\
404 ',KIND=16),CMPLX('+p[7]+',KIND=16))'
405
407 '''expression : CSC group
408 | SEC group
409 | ACSC group
410 | ASEC group
411 | RE group
412 | IM group
413 | ARG group
414 | SQRT group
415 | CONJ group
416 | TAN group
417 | REGLOG group'''
418 if p[1] == 'csc': p[0] = '1e0_16/cos' + p[2]
419 elif p[1] == 'sec': p[0] = '1e0_16/sin' + p[2]
420 elif p[1] == 'acsc': p[0] = 'asin(1e0_16/' + p[2] + ')'
421 elif p[1] == 'asec': p[0] = 'acos(1e0_16/' + p[2] + ')'
422 elif p[1] in ['tan', 'cmath.tan']: p[0] = 'tan(real' + p[2]+')'
423 elif p[1] == 're': p[0] = 'real' + p[2]
424 elif p[1] == 'im': p[0] = 'imag' + p[2]
425 elif p[1] == 'arg': p[0] = 'mp_arg(CMPLX(' + p[2] + ',KIND=16))'
426 elif p[1] == 'cmath.sqrt' or p[1] == 'sqrt': p[0] = 'sqrt(CMPLX(' + p[2] + ',KIND=16))'
427 elif p[1] == 'complexconjugate': p[0] = 'conjg(CMPLX(' + p[2] + ',KIND=16))'
428 elif p[1] == 'reglog': p[0] = 'mp_reglog(CMPLX(' + p[2] +',KIND=16))'
429
431 ''' expression : expression RE2 '''
432
433 if p[2] == '.real':
434 if p[1].startswith('('):
435 p[0] = 'real' +p[1]
436 else:
437 p[0] = 'real(%s)' % p[1]
438 elif p[2] == '.imag':
439 if p[1].startswith('('):
440 p[0] = 'imag' +p[1]
441 else:
442 p[0] = 'imag(%s)' % p[1]
443
444
446 '''expression : PI'''
447 p[0] = self.mp_prefix+'pi'
448
450 """A parser for UFO algebraic expressions, outputting
451 C++-style code."""
452
453 logical_equiv = {'==':'==',
454 '>=':'>=',
455 '<=':'<=',
456 '!=':'!=',
457 '>':'>',
458 '<':'<',
459 'or':'||',
460 'and':'&&'}
461
462
463
464
466 'expression : NUMBER'
467
468 if p[1].endswith('j'):
469 p[0] = 'std::complex<double>(0., %e)' % float(p[1][:-1])
470 else:
471 p[0] = ('%e' % float(p[1])).replace('e', 'd')
472
473
474 p[0] = p[1]
475
476 if float(p[1]) == int(float(p[1])) and float(p[1]) < 1000:
477 p[0] = str(int(float(p[1]))) + '.'
478
480 'expression : VARIABLE'
481 p[0] = p[1]
482
484 "expression : expression IF boolexpression ELSE expression "
485 p[0] = '(%s ? %s : %s)' % (p[3], p[1], p[5])
486
488 "expression : expression IF expression ELSE expression "
489 p[0] = '(%s ? %s : %s)' % (p[3], p[1], p[5])
490
492 "expression : COND '(' expression ',' expression ',' expression ')'"
493 p[0] = 'COND('+p[3]+','+p[5]+','+p[7]+')'
494
496 'expression : expression POWER expression'
497 p1=p[1]
498 p3=p[3]
499 if p[1][0] == '(' and p[1][-1] == ')':
500 p1 = p[1][1:-1]
501 if p[3][0] == '(' and p[3][-1] == ')':
502 p3 = p[3][1:-1]
503 p[0] = 'pow(' + p1 + ',' + p3 + ')'
504
506 "expression : COMPLEX '(' expression ',' expression ')'"
507 p[0] = 'std::complex<double>(' + p[3] + ',' + p[5] + ')'
508
510 '''expression : CSC group
511 | SEC group
512 | ACSC group
513 | ASEC group
514 | TAN group
515 | RE group
516 | IM group
517 | ARG group
518 | SQRT group
519 | CONJ group
520 | REGLOG group '''
521 if p[1] == 'csc': p[0] = '1./cos' + p[2]
522 elif p[1] == 'sec': p[0] = '1./sin' + p[2]
523 elif p[1] == 'acsc': p[0] = 'asin(1./' + p[2] + ')'
524 elif p[1] == 'asec': p[0] = 'acos(1./' + p[2] + ')'
525 elif p[1] in ['tan', 'cmath.tan']: p[0] = 'tan' +p[2]
526 elif p[1] == 're': p[0] = 'real' + p[2]
527 elif p[1] == 'im': p[0] = 'imag' + p[2]
528 elif p[1] == 'arg':p[0] = 'arg' + p[2]
529 elif p[1] == 'cmath.sqrt' or p[1] == 'sqrt': p[0] = 'sqrt' + p[2]
530 elif p[1] == 'complexconjugate': p[0] = 'conj' + p[2]
531 elif p[1] == 'reglog': p[0] = 'reglog' + p[2]
532
534 ''' expression : expression RE2 '''
535
536 if p[2] == '.real':
537 if p[1].startswith('('):
538 p[0] = 'real' +p[1]
539 else:
540 p[0] = 'real(%s)' % p[1]
541 elif p[2] == '.imag':
542 if p[1].startswith('('):
543 p[0] = 'imag' +p[1]
544 else:
545 p[0] = 'imag(%s)' % p[1]
546
547
549 '''expression : PI'''
550 p[0] = 'M_PI'
551
553 """An ad hoc parser for UFO algebraic expressions with if statement, outputting
554 Python-style code, with the conditional 'if' expressions simplified using
555 pre-defined set of variables specified when instanciating this parser."""
556
557 logical_equiv = {'==':'==',
558 '>=':'>=',
559 '<=':'<=',
560 '!=':'!=',
561 '>':'>',
562 '<':'<',
563 'or':' or ',
564 'and':' and '}
565
567 """Initialize the lex and yacc"""
568
569 self.changes_performed = 0
570
571 if len(args) > 0:
572 if isinstance(args[0],dict):
573 self.defined_variables = copy.copy(args[0])
574 elif isinstance(args[0],str):
575 try:
576 self.defined_variables = eval(args[0])
577 except:
578 raise ModelError, 'The expression "%s"'%str(args[0])+\
579 " given as defined variables for the UFOExpressionParserPythonIF"+\
580 " does not have a correct syntax."
581 if not isinstance(self.defined_variables, dict):
582 raise ModelError, 'The argument "%s"'%str(args[0])+\
583 " given as defined variables for the UFOExpressionParserPythonIF"+\
584 " is not a dictionary."
585 else:
586 raise ModelError, "The argument %s"%str(args[0])+\
587 " given as defined variables for the UFOExpressionParserPythonIF"+\
588 " must be either a dictionary or a string."
589 args = args[1:]
590 for key, value in self.defined_variables.items():
591 if not isinstance(key,str) or \
592 not any(isinstance(value,t) for t in [float,complex,int]):
593
594 del self.defined_variables[key]
595
596 else:
597
598
599
600 self.defined_variables = None
601
602 super(UFOExpressionParserPythonIF,self).__init__(*args, **kw)
603
604 - def parse(self, *args, **kw):
605 """ Wrapper around the parse function so as to also return the number
606 of if substitutions made."""
607 self.changes_performed = 0
608 new_expression = super(UFOExpressionParserPythonIF,self).parse(*args, **kw)
609 return new_expression, self.changes_performed
610
612 "expression : NUMBER"
613 p[0] = p[1]
614
616 "expression : VARIABLE"
617 p[0] = p[1]
618
620 'expression : expression POWER expression'
621 p[0] = p[1] + "**" + p[3]
622
624 "expression : expression IF boolexpression ELSE expression "
625 if self.defined_variables is None:
626 p[0] = '%s if %s else %s'%(p[1],p[3],p[5])
627 return
628 try:
629 p[0] = '%s'%p[1] if eval(p[3],self.defined_variables) else '%s'%p[5]
630 self.changes_performed += 1
631 except Exception:
632 p[0] = '%s if %s else %s'%(p[1],p[3],p[5])
633
635 "expression : expression IF expression ELSE expression "
636 if self.defined_variables is None:
637 p[0] = '%s if %s!=0.0 else %s'%(p[1],p[3],p[5])
638 return
639 try:
640 p[0] = '%s'%p[1] if eval(p[3]+'!= 0.0',self.defined_variables) else '%s'%p[5]
641 self.changes_performed += 1
642 except Exception:
643 p[0] = '%s if %s!=0.0 else %s'%(p[1],p[3],p[5])
644
646 "expression : COND '(' expression ',' expression ',' expression ')'"
647
648
649 p[0] = 'cond('+p[3]+','+p[5]+','+p[7]+')'
650
652 "expression : COMPLEX '(' expression ',' expression ')'"
653 p[0] = 'complex(' + p[3] + ',' + p[5] + ')'
654
656 '''expression : CSC group
657 | SEC group
658 | ACSC group
659 | ASEC group
660 | RE group
661 | IM group
662 | ARG group
663 | SQRT group
664 | TAN group
665 | CONJ group
666 | REGLOG group'''
667 if p[1] == 'csc': p[0] = 'csc' + p[2]
668 elif p[1] == 'sec': p[0] = 'sec' + p[2]
669 elif p[1] == 'acsc': p[0] = 'acsc' + p[2]
670 elif p[1] == 'asec': p[0] = 'asec' + p[2]
671 elif p[1] in ['tan','cmath.tan']: p[0] = 'tan' + p[2]
672 elif p[1] == 're': p[0] = 're' + p[2]
673 elif p[1] == 'im': p[0] = 'im' + p[2]
674 elif p[1] == 'arg': p[0] = 'arg' + p[2]
675 elif p[1] == 'cmath.sqrt' or p[1] == 'sqrt': p[0] = 'cmath.sqrt' + p[2]
676 elif p[1] == 'complexconjugate': p[0] = 'complexconjugate' + p[2]
677 elif p[1] == 'reglog': p[0] = 'reglog' + p[2]
678
680 ''' expression : expression RE2 '''
681 p[0] = p[1]+p[2]
682
684 '''expression : PI'''
685 p[0] = 'cmath.pi'
686
687
688
689
690 if __name__ == '__main__':
691
692 if len(sys.argv) == 1:
693 print "Please specify a parser: fortran, mpfortran or c++"
694 exit()
695 if sys.argv[1] == "fortran":
696 calc = UFOExpressionParserFortran()
697 elif sys.argv[1] == "mpfortran":
698 calc = UFOExpressionParserMPFortran()
699 elif sys.argv[1] == "c++":
700 calc = UFOExpressionParserCPP()
701 elif sys.argv[1] == "aloha":
702 calc = UFOExpressionParserCPP()
703 elif sys.argv[1] == "pythonif":
704 if len(sys.argv) > 2:
705 calc = UFOExpressionParserPythonIF(sys.argv[2])
706 else:
707 calc = UFOExpressionParserPythonIF()
708 else:
709 print "Please specify a parser: fortran, mpfortran, c++ or pythonif"
710 print "You gave", sys.argv[1]
711 if len(sys.argv) > 2:
712 print "with the second argument",sys.argv[2]
713 exit()
714
715 while 1:
716 try:
717 s = raw_input('calc > ')
718 except EOFError:
719 break
720 if not s: continue
721 print calc.parse(s)
722