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