Package madgraph :: Package iolibs :: Module file_writers
[hide private]
[frames] | no frames]

Source Code for Module madgraph.iolibs.file_writers

   1  ################################################################################ 
   2  # 
   3  # Copyright (c) 2009 The MadGraph5_aMC@NLO Development team and Contributors 
   4  # 
   5  # This file is a part of the MadGraph5_aMC@NLO project, an application which  
   6  # automatically generates Feynman diagrams and matrix elements for arbitrary 
   7  # high-energy processes in the Standard Model and beyond. 
   8  # 
   9  # It is subject to the MadGraph5_aMC@NLO license which should accompany this  
  10  # distribution. 
  11  # 
  12  # For more information, visit madgraph.phys.ucl.ac.be and amcatnlo.web.cern.ch 
  13  # 
  14  ################################################################################ 
  15   
  16  """Classes to write good-looking output in different languages: 
  17  Fortran, C++, etc.""" 
  18   
  19   
  20  from __future__ import absolute_import 
  21  import re 
  22  import collections 
  23  from six.moves import range 
  24  import six 
  25  import io 
  26   
  27  try: 
  28      import madgraph 
  29  except ImportError: 
  30          import internal.misc 
  31  else: 
  32      import madgraph.various.misc as misc 
  33   
34 -class FileWriter(io.FileIO):
35 """Generic Writer class. All writers should inherit from this class.""" 36 37 supported_preprocessor_commands = ['if'] 38 preprocessor_command_re=re.compile( 39 "\s*(?P<command>%s)\s*\(\s*(?P<body>.*)\s*\)\s*{\s*"\ 40 %('|'.join(supported_preprocessor_commands))) 41 preprocessor_endif_re=re.compile(\ 42 "\s*}\s*(?P<endif>else)?\s*(\((?P<body>.*)\))?\s*(?P<new_block>{)?\s*") 43
44 - class FileWriterError(IOError):
45 """Exception raised if an error occurs in the definition 46 or the execution of a Writer.""" 47 48 pass
49
50 - class FilePreProcessingError(IOError):
51 """Exception raised if an error occurs in the handling of the 52 preprocessor tags '##' in the template file.""" 53 pass
54
55 - def __init__(self, name, opt = 'w'):
56 """Initialize file to write to""" 57 return super(FileWriter, self).__init__(name, opt)
58
59 - def write(self, line):
60 if isinstance(line,str): 61 line=line.encode() 62 super(FileWriter,self).write(line)
63
64 - def write_line(self, line):
65 """Write a line with proper indent and splitting of long lines 66 for the language in question.""" 67 return ['%s\n' % l for l in line.split('\n')] 68 pass
69
70 - def write_comment_line(self, line):
71 """Write a comment line, with correct indent and line splits, 72 for the language in question""" 73 74 pass
75
76 - def write_comments(self, lines):
77 """Write set of comment lines, with correct indent and line splits, 78 for the language in question""" 79 80 splitlines = [] 81 if isinstance(lines, list): 82 for line in lines: 83 if not isinstance(line, str): 84 raise self.FileWriterError("%s not string" % repr(line)) 85 splitlines.extend(line.split('\n')) 86 elif isinstance(lines, str): 87 splitlines.extend(lines.split('\n')) 88 else: 89 raise self.FileWriterError("%s not string" % repr(lines)) 90 91 for line in splitlines: 92 res_lines = self.write_comment_line(line) 93 for line_to_write in res_lines: 94 self.write(line_to_write) 95 96 pass
97
98 - def writelines(self, lines, context={}, formatting=True):
99 """Extends the regular file.writeline() function to write out 100 nicely formatted code. When defining a context, then the lines 101 will be preprocessed to apply possible conditional statements on the 102 content of the template depending on the contextual variables specified.""" 103 104 splitlines = [] 105 if isinstance(lines, list): 106 for line in lines: 107 if not isinstance(line, str): 108 raise self.FileWriterError("%s not string" % repr(line)) 109 splitlines.extend(line.split('\n')) 110 elif isinstance(lines, str): 111 splitlines.extend(lines.split('\n')) 112 else: 113 raise self.FileWriterError("%s not string" % repr(lines)) 114 115 if len(context)>0: 116 splitlines = self.preprocess_template(splitlines,context=context) 117 118 for line in splitlines: 119 if formatting: 120 res_lines = self.write_line(line) 121 else: 122 res_lines = [line+'\n'] 123 for line_to_write in res_lines: 124 self.write(line_to_write)
125
126 - def preprocess_template(self, input_lines, context={}):
127 """ This class takes care of applying the pre-processing statements 128 starting with ## in the template .inc files, using the contextual 129 variables specified in the dictionary 'context' given in input with 130 the variable names given as keys and their respective value as values.""" 131 132 template_lines = [] 133 if isinstance(input_lines, list): 134 for line in input_lines: 135 if not isinstance(line, str): 136 raise self.FileWriterError("%s not string" % repr(input_lines)) 137 template_lines.extend(line.split('\n')) 138 elif isinstance(input_lines, str): 139 template_lines.extend(input_lines.split('\n')) 140 else: 141 raise self.FileWriterError("%s not string" % repr(input_lines)) 142 143 # Setup the contextual environment 144 for contextual_variable, value in context.items(): 145 exec('%s=%s'%(str(contextual_variable),repr(value))) 146 147 res = [] 148 # The variable below tracks the conditional statements structure 149 if_stack = [] 150 for i, line in enumerate(template_lines): 151 if not line.startswith('##'): 152 if all(if_stack): 153 res.append(line) 154 continue 155 preproc_command = self.preprocessor_command_re.match(line[2:]) 156 # Treat the follow up of an if statement 157 if preproc_command is None: 158 preproc_endif = self.preprocessor_endif_re.match(line[2:]) 159 if len(if_stack)==0 or preproc_endif is None: 160 raise self.FilePreProcessingError('Incorrect '+\ 161 'preprocessing command %s at line %d.'%(line,i)) 162 if preproc_endif.group('new_block') is None: 163 if_stack.pop() 164 elif preproc_endif.group('endif')=='else': 165 if_stack[-1]=(not if_stack[-1]) 166 # Treat an if statement 167 elif preproc_command.group('command')=='if': 168 try: 169 if_stack.append(eval(preproc_command.group('body'))==True) 170 except Exception as e: 171 raise self.FilePreProcessingError('Could not evaluate'+\ 172 "python expression '%s' given the context %s provided."%\ 173 (preproc_command.group('body'),str(context))+\ 174 "\nLine %d of file %s."%(i,self.name)) 175 176 if len(if_stack)>0: 177 raise self.FilePreProcessingError('Some conditional statements are'+\ 178 ' not properly terminated.') 179 return res
180 181 #=============================================================================== 182 # FortranWriter 183 #===============================================================================
184 -class FortranWriter(FileWriter):
185 """Routines for writing fortran lines. Keeps track of indentation 186 and splitting of long lines""" 187
188 - class FortranWriterError(FileWriter.FileWriterError):
189 """Exception raised if an error occurs in the definition 190 or the execution of a FortranWriter.""" 191 pass
192 193 # Parameters defining the output of the Fortran writer 194 keyword_pairs = {'^if.+then\s*$': ('^endif', 2), 195 '^type(?!\s*\()\s*.+\s*$': ('^endtype', 2), 196 '^do(?!\s+\d+)\s+': ('^enddo\s*$', 2), 197 '^subroutine': ('^end\s*$', 0), 198 '^module': ('^end\s*$', 0), 199 'function': ('^end\s*$', 0)} 200 single_indents = {'^else\s*$':-2, 201 '^else\s*if.+then\s*$':-2} 202 number_re = re.compile('^(?P<num>\d+)\s+(?P<rest>.*)') 203 line_cont_char = '$' 204 comment_char = 'c' 205 uniformcase = True #force everyting to be lower/upper case 206 downcase = False 207 line_length = 71 208 max_split = 20 209 split_characters = "+-*/,) " 210 comment_split_characters = " " 211 212 # Private variables 213 __indent = 0 214 __keyword_list = [] 215 __comment_pattern = re.compile(r"^(\s*#|c$|(c\s+([^=]|$))|cf2py|c\-\-|c\*\*)", re.IGNORECASE) 216 __continuation_line = re.compile(r"(?: )[$&]") 217
218 - def write_line(self, line):
219 """Write a fortran line, with correct indent and line splits""" 220 221 # This Routine is for a single line 222 assert(isinstance(line, str) and line.find('\n') == -1) 223 224 225 res_lines = [] 226 227 # Check if empty line and write it 228 if not line.lstrip(): 229 res_lines.append("\n") 230 return res_lines 231 232 # Check if this line is a comment 233 if self.__comment_pattern.search(line): 234 # This is a comment 235 res_lines = self.write_comment_line(line.lstrip()[1:]) 236 return res_lines 237 elif self.__continuation_line.search(line): 238 return line+'\n' 239 else: 240 # This is a regular Fortran line 241 242 # Strip leading spaces from line 243 myline = line.lstrip() 244 245 # Check if line starts with number 246 num_group = self.number_re.search(myline) 247 num = "" 248 if num_group: 249 num = num_group.group('num') 250 myline = num_group.group('rest') 251 252 # Convert to upper or lower case 253 # Here we need to make exception for anything within quotes. 254 (myline, part, post_comment) = myline.partition("!") 255 # Set space between line and post-comment 256 if part: 257 part = " " + part 258 # Replace all double quotes by single quotes 259 myline = myline.replace('\"', '\'') 260 # Downcase or upcase Fortran code, except for quotes 261 if self.uniformcase: 262 splitline = myline.split('\'') 263 myline = "" 264 i = 0 265 while i < len(splitline): 266 if i % 2 == 1: 267 # This is a quote - check for escaped \'s 268 while splitline[i] and splitline[i][-1] == '\\': 269 splitline[i] = splitline[i] + '\'' + splitline.pop(i + 1) 270 else: 271 # Otherwise downcase/upcase 272 if FortranWriter.downcase: 273 splitline[i] = splitline[i].lower() 274 else: 275 splitline[i] = splitline[i].upper() 276 i = i + 1 277 278 myline = "\'".join(splitline).rstrip() 279 280 # Check if line starts with dual keyword and adjust indent 281 if self.__keyword_list and re.search(self.keyword_pairs[\ 282 self.__keyword_list[-1]][0], myline.lower()): 283 key = self.__keyword_list.pop() 284 self.__indent = self.__indent - self.keyword_pairs[key][1] 285 286 # Check for else and else if 287 single_indent = 0 288 for key in self.single_indents.keys(): 289 if re.search(key, myline.lower()): 290 self.__indent = self.__indent + self.single_indents[key] 291 single_indent = -self.single_indents[key] 292 break 293 294 # Break line in appropriate places 295 # defined (in priority order) by the characters in split_characters 296 res = self.split_line(" " + num + \ 297 " " * (5 + self.__indent - len(num)) + myline, 298 self.split_characters, 299 " " * 5 + self.line_cont_char + \ 300 " " * (self.__indent + 1)) 301 302 # Check if line starts with keyword and adjust indent for next line 303 for key in self.keyword_pairs.keys(): 304 if re.search(key, myline.lower()): 305 self.__keyword_list.append(key) 306 self.__indent = self.__indent + self.keyword_pairs[key][1] 307 break 308 309 # Correct back for else and else if 310 if single_indent != None: 311 self.__indent = self.__indent + single_indent 312 single_indent = None 313 314 # Write line(s) to file 315 res_lines.append("\n".join(res) + part + post_comment + "\n") 316 317 return res_lines
318
319 - def write_comment_line(self, line):
320 """Write a comment line, with correct indent and line splits""" 321 322 # write_comment_line must have a single line as argument 323 assert(isinstance(line, str) and line.find('\n') == -1) 324 325 if line.startswith('F2PY'): 326 return ["C%s\n" % line.strip()] 327 elif line.startswith(('C','c')): 328 return ['%s\n' % line] 329 330 res_lines = [] 331 332 # This is a comment 333 myline = " " * (5 + self.__indent) + line.lstrip() 334 if FortranWriter.downcase: 335 self.comment_char = self.comment_char.lower() 336 else: 337 self.comment_char = self.comment_char.upper() 338 myline = self.comment_char + myline 339 # Break line in appropriate places 340 # defined (in priority order) by the characters in 341 # comment_split_characters 342 res = self.split_line(myline, 343 self.comment_split_characters, 344 self.comment_char + " " * (5 + self.__indent)) 345 346 # Write line(s) to file 347 res_lines.append("\n".join(res) + "\n") 348 349 return res_lines
350
351 - def split_line(self, line, split_characters, line_start):
352 """Split a line if it is longer than self.line_length 353 columns. Split in preferential order according to 354 split_characters, and start each new line with line_start.""" 355 356 def get_split_index(line, max_length, max_split, split_characters): 357 split_at = 0 358 for character in split_characters: 359 index = line[(max_length - max_split): \ 360 max_length].rfind(character) 361 if index >= 0: 362 split_at_tmp = max_length - max_split + index 363 if split_at_tmp > split_at: 364 split_at = split_at_tmp 365 return split_at
366 367 368 res_lines = [line] 369 370 while len(res_lines[-1]) > self.line_length: 371 split_at = get_split_index(res_lines[-1], self.line_length, 372 self.max_split, split_characters) 373 if split_at == 0: 374 split_at = get_split_index(res_lines[-1], self.line_length, 375 self.max_split + 30, split_characters) 376 if split_at == 0: 377 split_at = self.line_length 378 379 newline = res_lines[-1][split_at:] 380 nquotes = self.count_number_of_quotes(newline) 381 # res_lines.append(line_start + 382 # ('//\''+res_lines[-1][(split_at-1):] if nquotes%2==1 else 383 # ''+res_lines[-1][split_at:]) 384 offset = 0 385 if nquotes%2==1: 386 if res_lines[-1][(split_at-1)] == '\'': 387 offset = 1 388 nquotes -=1 389 res_lines.append(line_start +(res_lines[-1][(split_at-offset):])) 390 else: 391 res_lines.append(line_start +('//\''+res_lines[-1][(split_at-offset):])) 392 393 elif res_lines[-1][(split_at)] in self.split_characters: 394 if res_lines[-1][(split_at)] in ')': 395 # print "offset put in place" 396 offset = -1 397 # else: 398 # print "offset not put in place" 399 res_lines.append(line_start +res_lines[-1][(split_at-offset):]) 400 elif line_start.startswith(('c','C')) or res_lines[-1][(split_at)] in split_characters: 401 res_lines.append(line_start +res_lines[-1][(split_at):]) 402 else: 403 l_start = line_start.rstrip() 404 res_lines.append(l_start +res_lines[-1][(split_at):]) 405 406 res_lines[-2] = (res_lines[-2][:(split_at-offset)]+'\'' if nquotes%2==1 \ 407 else res_lines[-2][:split_at-offset]) 408 return res_lines
409
410 - def count_number_of_quotes(self, line):
411 """ Count the number of real quotes (not escaped ones) in a line. """ 412 413 splitline = line.split('\'') 414 i = 0 415 while i < len(splitline): 416 if i % 2 == 1: 417 # This is a quote - check for escaped \'s 418 while splitline[i] and splitline[i][-1] == '\\': 419 splitline[i] = splitline[i] + '\'' + splitline.pop(i + 1) 420 i = i + 1 421 return len(splitline)-1
422
423 - def remove_routine(self, text, fct_names, formatting=True):
424 """write the incoming text but fully removing the associate routine/function 425 text can be a path to a file, an iterator, a string 426 fct_names should be a list of functions to remove 427 """ 428 429 f77_type = ['real*8', 'integer', 'double precision', 'logical'] 430 pattern = re.compile('^\s+(?:SUBROUTINE|(?:%(type)s)\s+function)\s+([a-zA-Z]\w*)' \ 431 % {'type':'|'.join(f77_type)}, re.I) 432 433 removed = [] 434 if isinstance(text, str): 435 if '\n' in text: 436 text = text.split('\n') 437 else: 438 text = open(text) 439 if isinstance(fct_names, str): 440 fct_names = [fct_names] 441 442 to_write=True 443 for line in text: 444 fct = pattern.findall(line) 445 if fct: 446 if fct[0] in fct_names: 447 to_write = False 448 else: 449 to_write = True 450 451 if to_write: 452 if formatting: 453 if line.endswith('\n'): 454 line = line[:-1] 455 self.writelines(line) 456 else: 457 if not line.endswith('\n'): 458 line = '%s\n' % line 459 file.writelines(self, line) 460 else: 461 removed.append(line) 462 463 return removed
464 465 466 #=============================================================================== 467 # CPPWriter 468 #===============================================================================
469 -class CPPWriter(FileWriter):
470 """Routines for writing C++ lines. Keeps track of brackets, 471 spaces, indentation and splitting of long lines""" 472
473 - class CPPWriterError(FileWriter.FileWriterError):
474 """Exception raised if an error occurs in the definition 475 or the execution of a CPPWriter.""" 476 pass
477 478 # Parameters defining the output of the C++ writer 479 standard_indent = 2 480 line_cont_indent = 4 481 482 indent_par_keywords = {'^if': standard_indent, 483 '^else if': standard_indent, 484 '^for': standard_indent, 485 '^while': standard_indent, 486 '^switch': standard_indent} 487 indent_single_keywords = {'^else': standard_indent} 488 indent_content_keywords = {'^class': standard_indent, 489 '^namespace': 0} 490 cont_indent_keywords = {'^case': standard_indent, 491 '^default': standard_indent, 492 '^public': standard_indent, 493 '^private': standard_indent, 494 '^protected': standard_indent} 495 496 spacing_patterns = [('\s*\"\s*}', '\"'), 497 ('\s*,\s*', ', '), 498 ('\s*-\s*', ' - '), 499 ('([{(,=])\s*-\s*', '\g<1> -'), 500 ('(return)\s*-\s*', '\g<1> -'), 501 ('\s*\+\s*', ' + '), 502 ('([{(,=])\s*\+\s*', '\g<1> +'), 503 ('\(\s*', '('), 504 ('\s*\)', ')'), 505 ('\{\s*', '{'), 506 ('\s*\}', '}'), 507 ('\s*=\s*', ' = '), 508 ('\s*>\s*', ' > '), 509 ('\s*<\s*', ' < '), 510 ('\s*!\s*', ' !'), 511 ('\s*/\s*', '/'), 512 ('\s*\*\s*', ' * '), 513 ('\s*-\s+-\s*', '-- '), 514 ('\s*\+\s+\+\s*', '++ '), 515 ('\s*-\s+=\s*', ' -= '), 516 ('\s*\+\s+=\s*', ' += '), 517 ('\s*\*\s+=\s*', ' *= '), 518 ('\s*/=\s*', ' /= '), 519 ('\s*>\s+>\s*', ' >> '), 520 ('<\s*double\s*>>\s*', '<double> > '), 521 ('\s*<\s+<\s*', ' << '), 522 ('\s*-\s+>\s*', '->'), 523 ('\s*=\s+=\s*', ' == '), 524 ('\s*!\s+=\s*', ' != '), 525 ('\s*>\s+=\s*', ' >= '), 526 ('\s*<\s+=\s*', ' <= '), 527 ('\s*&&\s*', ' && '), 528 ('\s*\|\|\s*', ' || '), 529 ('\s*{\s*}', ' {}'), 530 ('\s*;\s*', '; '), 531 (';\s*\}', ';}'), 532 (';\s*$}', ';'), 533 ('\s*<\s*([a-zA-Z0-9]+?)\s*>', '<\g<1>>'), 534 ('^#include\s*<\s*(.*?)\s*>', '#include <\g<1>>'), 535 ('(\d+\.{0,1}\d*|\.\d+)\s*[eE]\s*([+-]{0,1})\s*(\d+)', 536 '\g<1>e\g<2>\g<3>'), 537 ('\s+',' ')] 538 spacing_re = dict([(key[0], re.compile(key[0])) for key in \ 539 spacing_patterns]) 540 541 init_array_pattern = re.compile(r"=\s*\{.*\}") 542 short_clause_pattern = re.compile(r"\{.*\}") 543 544 comment_char = '//' 545 comment_pattern = re.compile(r"^(\s*#\s+|\s*//)") 546 start_comment_pattern = re.compile(r"^(\s*/\*)") 547 end_comment_pattern = re.compile(r"(\s*\*/)$") 548 549 quote_chars = re.compile(r"[^\\][\"\']|^[\"\']") 550 no_space_comment_patterns = re.compile(r"--|\*\*|==|\+\+") 551 line_length = 80 552 max_split = 40 553 split_characters = " " 554 comment_split_characters = " " 555 556 # Private variables 557 __indent = 0 558 __keyword_list = collections.deque() 559 __comment_ongoing = False 560
561 - def write_line(self, line):
562 """Write a C++ line, with correct indent, spacing and line splits""" 563 564 # write_line must have a single line as argument 565 assert(isinstance(line, str) and line.find('\n') == -1) 566 567 res_lines = [] 568 569 # Check if this line is a comment 570 if self.comment_pattern.search(line) or \ 571 self.start_comment_pattern.search(line) or \ 572 self.__comment_ongoing: 573 # This is a comment 574 res_lines = self.write_comment_line(line.lstrip()) 575 return res_lines 576 577 # This is a regular C++ line 578 579 # Strip leading spaces from line 580 myline = line.lstrip() 581 582 # Return if empty line 583 if not myline: 584 return ["\n"] 585 586 # Check if line starts with "{" 587 if myline[0] == "{": 588 # Check for indent 589 indent = self.__indent 590 key = "" 591 if self.__keyword_list: 592 key = self.__keyword_list[-1] 593 if key in self.indent_par_keywords: 594 indent = indent - self.indent_par_keywords[key] 595 elif key in self.indent_single_keywords: 596 indent = indent - self.indent_single_keywords[key] 597 elif key in self.indent_content_keywords: 598 indent = indent - self.indent_content_keywords[key] 599 else: 600 # This is free-standing block, just use standard indent 601 self.__indent = self.__indent + self.standard_indent 602 # Print "{" 603 res_lines.append(" " * indent + "{" + "\n") 604 # Add "{" to keyword list 605 self.__keyword_list.append("{") 606 myline = myline[1:].lstrip() 607 if myline: 608 # If anything is left of myline, write it recursively 609 res_lines.extend(self.write_line(myline)) 610 return res_lines 611 612 # Check if line starts with "}" 613 if myline[0] == "}": 614 # First: Check if no keywords in list 615 if not self.__keyword_list: 616 raise self.CPPWriterError(\ 617 'Non-matching } in C++ output: ' \ 618 + myline) 619 # First take care of "case" and "default" 620 if self.__keyword_list[-1] in list(self.cont_indent_keywords.keys()): 621 key = self.__keyword_list.pop() 622 self.__indent = self.__indent - self.cont_indent_keywords[key] 623 # Now check that we have matching { 624 if not self.__keyword_list.pop() == "{": 625 raise self.CPPWriterError(\ 626 'Non-matching } in C++ output: ' \ 627 + ",".join(self.__keyword_list) + myline) 628 # Check for the keyword before and close 629 key = "" 630 if self.__keyword_list: 631 key = self.__keyword_list[-1] 632 if key in self.indent_par_keywords: 633 self.__indent = self.__indent - \ 634 self.indent_par_keywords[key] 635 self.__keyword_list.pop() 636 elif key in self.indent_single_keywords: 637 self.__indent = self.__indent - \ 638 self.indent_single_keywords[key] 639 self.__keyword_list.pop() 640 elif key in self.indent_content_keywords: 641 self.__indent = self.__indent - \ 642 self.indent_content_keywords[key] 643 self.__keyword_list.pop() 644 else: 645 # This was just a { } clause, without keyword 646 self.__indent = self.__indent - self.standard_indent 647 648 # Write } or }; and then recursively write the rest 649 breakline_index = 1 650 if len(myline) > 1: 651 if myline[1] in [";", ","]: 652 breakline_index = 2 653 elif myline[1:].lstrip()[:2] == "//": 654 if myline.endswith('\n'): 655 breakline_index = len(myline) - 1 656 else: 657 breakline_index = len(myline) 658 res_lines.append("\n".join(self.split_line(\ 659 myline[:breakline_index], 660 self.split_characters)) + "\n") 661 if len(myline) > breakline_index and myline[breakline_index] =='\n': 662 breakline_index +=1 663 myline = myline[breakline_index:].lstrip() 664 665 if myline: 666 # If anything is left of myline, write it recursively 667 res_lines.extend(self.write_line(myline)) 668 return res_lines 669 670 # Check if line starts with keyword with parentesis 671 for key in self.indent_par_keywords.keys(): 672 if re.search(key, myline): 673 # Step through to find end of parenthesis 674 parenstack = collections.deque() 675 for i, ch in enumerate(myline[len(key)-1:]): 676 if ch == '(': 677 parenstack.append(ch) 678 elif ch == ')': 679 try: 680 parenstack.pop() 681 except IndexError: 682 # no opening parenthesis left in stack 683 raise self.CPPWriterError(\ 684 'Non-matching parenthesis in C++ output' \ 685 + myline) 686 if not parenstack: 687 # We are done 688 break 689 endparen_index = len(key) + i 690 # Print line, make linebreak, check if next character is { 691 res_lines.append("\n".join(self.split_line(\ 692 myline[:endparen_index], \ 693 self.split_characters)) + \ 694 "\n") 695 myline = myline[endparen_index:].lstrip() 696 # Add keyword to list and add indent for next line 697 self.__keyword_list.append(key) 698 self.__indent = self.__indent + \ 699 self.indent_par_keywords[key] 700 if myline: 701 # If anything is left of myline, write it recursively 702 res_lines.extend(self.write_line(myline)) 703 704 return res_lines 705 706 # Check if line starts with single keyword 707 for key in self.indent_single_keywords.keys(): 708 if re.search(key, myline): 709 end_index = len(key) - 1 710 # Print line, make linebreak, check if next character is { 711 res_lines.append(" " * self.__indent + myline[:end_index] + \ 712 "\n") 713 myline = myline[end_index:].lstrip() 714 # Add keyword to list and add indent for next line 715 self.__keyword_list.append(key) 716 self.__indent = self.__indent + \ 717 self.indent_single_keywords[key] 718 if myline: 719 # If anything is left of myline, write it recursively 720 res_lines.extend(self.write_line(myline)) 721 722 return res_lines 723 724 # Check if line starts with content keyword 725 for key in self.indent_content_keywords.keys(): 726 if re.search(key, myline): 727 # Print line, make linebreak, check if next character is { 728 if "{" in myline: 729 end_index = myline.index("{") 730 else: 731 end_index = len(myline) 732 res_lines.append("\n".join(self.split_line(\ 733 myline[:end_index], \ 734 self.split_characters)) + \ 735 "\n") 736 myline = myline[end_index:].lstrip() 737 # Add keyword to list and add indent for next line 738 self.__keyword_list.append(key) 739 self.__indent = self.__indent + \ 740 self.indent_content_keywords[key] 741 if myline: 742 # If anything is left of myline, write it recursively 743 res_lines.extend(self.write_line(myline)) 744 745 return res_lines 746 747 # Check if line starts with continuous indent keyword 748 for key in self.cont_indent_keywords.keys(): 749 if re.search(key, myline): 750 # Check if we have a continuous indent keyword since before 751 if self.__keyword_list[-1] in list(self.cont_indent_keywords.keys()): 752 self.__indent = self.__indent - \ 753 self.cont_indent_keywords[\ 754 self.__keyword_list.pop()] 755 # Print line, make linebreak 756 res_lines.append("\n".join(self.split_line(myline, \ 757 self.split_characters)) + \ 758 "\n") 759 # Add keyword to list and add indent for next line 760 self.__keyword_list.append(key) 761 self.__indent = self.__indent + \ 762 self.cont_indent_keywords[key] 763 764 return res_lines 765 766 # Check if this line is an array initialization a ={b,c,d}; 767 if self.init_array_pattern.search(myline): 768 res_lines.append("\n".join(self.split_line(\ 769 myline, 770 self.split_characters)) + \ 771 "\n") 772 return res_lines 773 774 # Check if this is a short xxx {yyy} type line; 775 if self.short_clause_pattern.search(myline): 776 lines = self.split_line(myline, 777 self.split_characters) 778 if len(lines) == 1: 779 res_lines.append("\n".join(lines) + "\n") 780 return res_lines 781 782 # Check if there is a "{" somewhere in the line 783 if "{" in myline: 784 end_index = myline.index("{") 785 res_lines.append("\n".join(self.split_line(\ 786 myline[:end_index], \ 787 self.split_characters)) + \ 788 "\n") 789 myline = myline[end_index:].lstrip() 790 if myline: 791 # If anything is left of myline, write it recursively 792 res_lines.extend(self.write_line(myline)) 793 return res_lines 794 795 # Check if there is a "}" somewhere in the line 796 if "}" in myline: 797 end_index = myline.index("}") 798 res_lines.append("\n".join(self.split_line(\ 799 myline[:end_index], \ 800 self.split_characters)) + \ 801 "\n") 802 myline = myline[end_index:].lstrip() 803 if myline: 804 # If anything is left of myline, write it recursively 805 res_lines.extend(self.write_line(myline)) 806 return res_lines 807 808 # Write line(s) to file 809 res_lines.append("\n".join(self.split_line(myline, \ 810 self.split_characters)) + "\n") 811 812 # Check if this is a single indented line 813 if self.__keyword_list: 814 if self.__keyword_list[-1] in self.indent_par_keywords: 815 self.__indent = self.__indent - \ 816 self.indent_par_keywords[self.__keyword_list.pop()] 817 elif self.__keyword_list[-1] in self.indent_single_keywords: 818 self.__indent = self.__indent - \ 819 self.indent_single_keywords[self.__keyword_list.pop()] 820 elif self.__keyword_list[-1] in self.indent_content_keywords: 821 self.__indent = self.__indent - \ 822 self.indent_content_keywords[self.__keyword_list.pop()] 823 824 return res_lines
825
826 - def write_comment_line(self, line):
827 """Write a comment line, with correct indent and line splits""" 828 829 # write_comment_line must have a single line as argument 830 assert(isinstance(line, str) and line.find('\n') == -1) 831 832 res_lines = [] 833 834 # This is a comment 835 836 if self.start_comment_pattern.search(line): 837 self.__comment_ongoing = True 838 line = self.start_comment_pattern.sub("", line) 839 840 if self.end_comment_pattern.search(line): 841 self.__comment_ongoing = False 842 line = self.end_comment_pattern.sub("", line) 843 844 line = self.comment_pattern.sub("", line).strip() 845 # Avoid extra space for lines starting with certain multiple patterns 846 if self.no_space_comment_patterns.match(line): 847 myline = self.comment_char + line 848 else: 849 myline = self.comment_char + " " + line 850 # Break line in appropriate places defined (in priority order) 851 # by the characters in comment_split_characters 852 res = self.split_comment_line(myline) 853 854 # Write line(s) to file 855 res_lines.append("\n".join(res) + "\n") 856 857 return res_lines
858
859 - def split_line(self, line, split_characters):
860 """Split a line if it is longer than self.line_length 861 columns. Split in preferential order according to 862 split_characters. Also fix spacing for line.""" 863 864 # First split up line if there are comments 865 comment = "" 866 if line.find(self.comment_char) > -1: 867 line, dum, comment = line.partition(self.comment_char) 868 869 # Then split up line if there are quotes 870 quotes = self.quote_chars.finditer(line) 871 872 start_pos = 0 873 line_quotes = [] 874 line_no_quotes = [] 875 for i, quote in enumerate(quotes): 876 if i % 2 == 0: 877 # Add text before quote to line_no_quotes 878 line_no_quotes.append(line[start_pos:quote.start()]) 879 start_pos = quote.start() 880 else: 881 # Add quote to line_quotes 882 line_quotes.append(line[start_pos:quote.end()]) 883 start_pos = quote.end() 884 885 line_no_quotes.append(line[start_pos:]) 886 887 # Fix spacing for line, but only outside of quotes 888 line.rstrip() 889 for i, no_quote in enumerate(line_no_quotes): 890 for key in self.spacing_patterns: 891 no_quote = self.spacing_re[key[0]].sub(key[1], no_quote) 892 line_no_quotes[i] = no_quote 893 894 # Glue together quotes and non-quotes: 895 line = line_no_quotes[0] 896 for i in range(len(line_quotes)): 897 line += line_quotes[i] 898 if len(line_no_quotes) > i + 1: 899 line += line_no_quotes[i+1] 900 901 # Add indent 902 res_lines = [" " * self.__indent + line] 903 904 while len(res_lines[-1]) > self.line_length: 905 long_line = res_lines[-1] 906 split_at = -1 907 for character in split_characters: 908 index = long_line[(self.line_length - self.max_split): \ 909 self.line_length].rfind(character) 910 if index >= 0: 911 split_at = self.line_length - self.max_split + index + 1 912 break 913 914 # no valid breaking so find the first breaking allowed: 915 if split_at == -1: 916 split_at = len(long_line) 917 for character in split_characters: 918 split = long_line[self.line_length].find(character) 919 if split > 0: 920 split_at = min(split, split_at) 921 if split_at == len(long_line): 922 break 923 924 # Don't allow split within quotes 925 quotes = self.quote_chars.findall(long_line[:split_at]) 926 if quotes and len(quotes) % 2 == 1: 927 quote_match = self.quote_chars.search(long_line[split_at:]) 928 if not quote_match: 929 raise self.CPPWriterError(\ 930 "Error: Unmatched quote in line " + long_line) 931 split_at = quote_match.end() + split_at + 1 932 split_match = re.search(self.split_characters, 933 long_line[split_at:]) 934 if split_match: 935 split_at = split_at + split_match.start() 936 else: 937 split_at = len(long_line) + 1 938 939 # Append new line 940 if long_line[split_at:].lstrip(): 941 # Replace old line 942 res_lines[-1] = long_line[:split_at].rstrip() 943 res_lines.append(" " * \ 944 (self.__indent + self.line_cont_indent) + \ 945 long_line[split_at:].strip()) 946 else: 947 break 948 949 if comment: 950 res_lines[-1] += " " + self.comment_char + comment 951 952 return res_lines
953
954 - def split_comment_line(self, line):
955 """Split a line if it is longer than self.line_length 956 columns. Split in preferential order according to 957 split_characters.""" 958 959 # First fix spacing for line 960 line.rstrip() 961 res_lines = [" " * self.__indent + line] 962 963 while len(res_lines[-1]) > self.line_length: 964 long_line = res_lines[-1] 965 split_at = self.line_length 966 index = long_line[(self.line_length - self.max_split): \ 967 self.line_length].rfind(' ') 968 if index >= 0: 969 split_at = self.line_length - self.max_split + index + 1 970 971 # Append new line 972 if long_line[split_at:].lstrip(): 973 # Replace old line 974 res_lines[-1] = long_line[:split_at].rstrip() 975 res_lines.append(" " * \ 976 self.__indent + self.comment_char + " " + \ 977 long_line[split_at:].strip()) 978 else: 979 break 980 981 return res_lines
982
983 -class PythonWriter(FileWriter):
984
985 - def write_comments(self, text):
986 text = '#%s\n' % text.replace('\n','\n#') 987 self.write(text)
988
989 -class MakefileWriter(FileWriter):
990
991 - def write_comments(self, text):
992 text = '#%s\n' % text.replace('\n','\n#') 993 self.write(text)
994
995 - def writelines(self, lines):
996 """Extends the regular file.writeline() function to write out 997 nicely formatted code""" 998 999 self.write(lines)
1000