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