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