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+([^=]|$)))", 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 res_lines = [] 303 304 # This is a comment 305 myline = " " * (5 + self.__indent) + line.lstrip() 306 if FortranWriter.downcase: 307 self.comment_char = self.comment_char.lower() 308 else: 309 self.comment_char = self.comment_char.upper() 310 myline = self.comment_char + myline 311 # Break line in appropriate places 312 # defined (in priority order) by the characters in 313 # comment_split_characters 314 res = self.split_line(myline, 315 self.comment_split_characters, 316 self.comment_char + " " * (5 + self.__indent)) 317 318 # Write line(s) to file 319 res_lines.append("\n".join(res) + "\n") 320 321 return res_lines
322
323 - def split_line(self, line, split_characters, line_start):
324 """Split a line if it is longer than self.line_length 325 columns. Split in preferential order according to 326 split_characters, and start each new line with line_start.""" 327 328 res_lines = [line] 329 330 while len(res_lines[-1]) > self.line_length: 331 split_at = self.line_length 332 for character in split_characters: 333 index = res_lines[-1][(self.line_length - self.max_split): \ 334 self.line_length].rfind(character) 335 if index >= 0: 336 split_at = self.line_length - self.max_split + index 337 break 338 newline = res_lines[-1][split_at:] 339 nquotes = self.count_number_of_quotes(newline) 340 res_lines.append(line_start + 341 ('//\''+res_lines[-1][(split_at-1):] if nquotes%2==1 else 342 ''+res_lines[-1][split_at:])) 343 res_lines[-2] = (res_lines[-2][:(split_at-1)]+'\'' if nquotes%2==1 \ 344 else res_lines[-2][:split_at]) 345 return res_lines
346
347 - def count_number_of_quotes(self, line):
348 """ Count the number of real quotes (not escaped ones) in a line. """ 349 350 splitline = line.split('\'') 351 i = 0 352 while i < len(splitline): 353 if i % 2 == 1: 354 # This is a quote - check for escaped \'s 355 while splitline[i] and splitline[i][-1] == '\\': 356 splitline[i] = splitline[i] + '\'' + splitline.pop(i + 1) 357 i = i + 1 358 return len(splitline)-1
359 360 #=============================================================================== 361 # CPPWriter 362 #===============================================================================
363 -class CPPWriter(FileWriter):
364 """Routines for writing C++ lines. Keeps track of brackets, 365 spaces, indentation and splitting of long lines""" 366
367 - class CPPWriterError(FileWriter.FileWriterError):
368 """Exception raised if an error occurs in the definition 369 or the execution of a CPPWriter.""" 370 pass
371 372 # Parameters defining the output of the C++ writer 373 standard_indent = 2 374 line_cont_indent = 4 375 376 indent_par_keywords = {'^if': standard_indent, 377 '^else if': standard_indent, 378 '^for': standard_indent, 379 '^while': standard_indent, 380 '^switch': standard_indent} 381 indent_single_keywords = {'^else': standard_indent} 382 indent_content_keywords = {'^class': standard_indent, 383 '^namespace': 0} 384 cont_indent_keywords = {'^case': standard_indent, 385 '^default': standard_indent, 386 '^public': standard_indent, 387 '^private': standard_indent, 388 '^protected': standard_indent} 389 390 spacing_patterns = [('\s*\"\s*}', '\"'), 391 ('\s*,\s*', ', '), 392 ('\s*-\s*', ' - '), 393 ('([{(,=])\s*-\s*', '\g<1> -'), 394 ('(return)\s*-\s*', '\g<1> -'), 395 ('\s*\+\s*', ' + '), 396 ('([{(,=])\s*\+\s*', '\g<1> +'), 397 ('\(\s*', '('), 398 ('\s*\)', ')'), 399 ('\{\s*', '{'), 400 ('\s*\}', '}'), 401 ('\s*=\s*', ' = '), 402 ('\s*>\s*', ' > '), 403 ('\s*<\s*', ' < '), 404 ('\s*!\s*', ' !'), 405 ('\s*/\s*', '/'), 406 ('\s*\*\s*', ' * '), 407 ('\s*-\s+-\s*', '-- '), 408 ('\s*\+\s+\+\s*', '++ '), 409 ('\s*-\s+=\s*', ' -= '), 410 ('\s*\+\s+=\s*', ' += '), 411 ('\s*\*\s+=\s*', ' *= '), 412 ('\s*/=\s*', ' /= '), 413 ('\s*>\s+>\s*', ' >> '), 414 ('<\s*double\s*>>\s*', '<double> > '), 415 ('\s*<\s+<\s*', ' << '), 416 ('\s*-\s+>\s*', '->'), 417 ('\s*=\s+=\s*', ' == '), 418 ('\s*!\s+=\s*', ' != '), 419 ('\s*>\s+=\s*', ' >= '), 420 ('\s*<\s+=\s*', ' <= '), 421 ('\s*&&\s*', ' && '), 422 ('\s*\|\|\s*', ' || '), 423 ('\s*{\s*}', ' {}'), 424 ('\s*;\s*', '; '), 425 (';\s*\}', ';}'), 426 (';\s*$}', ';'), 427 ('\s*<\s*([a-zA-Z0-9]+?)\s*>', '<\g<1>>'), 428 ('^#include\s*<\s*(.*?)\s*>', '#include <\g<1>>'), 429 ('(\d+\.{0,1}\d*|\.\d+)\s*[eE]\s*([+-]{0,1})\s*(\d+)', 430 '\g<1>e\g<2>\g<3>'), 431 ('\s+',' ')] 432 spacing_re = dict([(key[0], re.compile(key[0])) for key in \ 433 spacing_patterns]) 434 435 init_array_pattern = re.compile(r"=\s*\{.*\}") 436 short_clause_pattern = re.compile(r"\{.*\}") 437 438 comment_char = '//' 439 comment_pattern = re.compile(r"^(\s*#\s+|\s*//)") 440 start_comment_pattern = re.compile(r"^(\s*/\*)") 441 end_comment_pattern = re.compile(r"(\s*\*/)$") 442 443 quote_chars = re.compile(r"[^\\][\"\']|^[\"\']") 444 no_space_comment_patterns = re.compile(r"--|\*\*|==|\+\+") 445 line_length = 80 446 max_split = 40 447 split_characters = " " 448 comment_split_characters = " " 449 450 # Private variables 451 __indent = 0 452 __keyword_list = collections.deque() 453 __comment_ongoing = False 454
455 - def write_line(self, line):
456 """Write a C++ line, with correct indent, spacing and line splits""" 457 458 # write_line must have a single line as argument 459 assert(isinstance(line, str) and line.find('\n') == -1) 460 461 res_lines = [] 462 463 # Check if this line is a comment 464 if self.comment_pattern.search(line) or \ 465 self.start_comment_pattern.search(line) or \ 466 self.__comment_ongoing: 467 # This is a comment 468 res_lines = self.write_comment_line(line.lstrip()) 469 return res_lines 470 471 # This is a regular C++ line 472 473 # Strip leading spaces from line 474 myline = line.lstrip() 475 476 # Return if empty line 477 if not myline: 478 return ["\n"] 479 480 # Check if line starts with "{" 481 if myline[0] == "{": 482 # Check for indent 483 indent = self.__indent 484 key = "" 485 if self.__keyword_list: 486 key = self.__keyword_list[-1] 487 if key in self.indent_par_keywords: 488 indent = indent - self.indent_par_keywords[key] 489 elif key in self.indent_single_keywords: 490 indent = indent - self.indent_single_keywords[key] 491 elif key in self.indent_content_keywords: 492 indent = indent - self.indent_content_keywords[key] 493 else: 494 # This is free-standing block, just use standard indent 495 self.__indent = self.__indent + self.standard_indent 496 # Print "{" 497 res_lines.append(" " * indent + "{" + "\n") 498 # Add "{" to keyword list 499 self.__keyword_list.append("{") 500 myline = myline[1:].lstrip() 501 if myline: 502 # If anything is left of myline, write it recursively 503 res_lines.extend(self.write_line(myline)) 504 return res_lines 505 506 # Check if line starts with "}" 507 if myline[0] == "}": 508 # First: Check if no keywords in list 509 if not self.__keyword_list: 510 raise self.CPPWriterError(\ 511 'Non-matching } in C++ output: ' \ 512 + myline) 513 # First take care of "case" and "default" 514 if self.__keyword_list[-1] in self.cont_indent_keywords.keys(): 515 key = self.__keyword_list.pop() 516 self.__indent = self.__indent - self.cont_indent_keywords[key] 517 # Now check that we have matching { 518 if not self.__keyword_list.pop() == "{": 519 raise self.CPPWriterError(\ 520 'Non-matching } in C++ output: ' \ 521 + ",".join(self.__keyword_list) + myline) 522 # Check for the keyword before and close 523 key = "" 524 if self.__keyword_list: 525 key = self.__keyword_list[-1] 526 if key in self.indent_par_keywords: 527 self.__indent = self.__indent - \ 528 self.indent_par_keywords[key] 529 self.__keyword_list.pop() 530 elif key in self.indent_single_keywords: 531 self.__indent = self.__indent - \ 532 self.indent_single_keywords[key] 533 self.__keyword_list.pop() 534 elif key in self.indent_content_keywords: 535 self.__indent = self.__indent - \ 536 self.indent_content_keywords[key] 537 self.__keyword_list.pop() 538 else: 539 # This was just a { } clause, without keyword 540 self.__indent = self.__indent - self.standard_indent 541 542 # Write } or }; and then recursively write the rest 543 breakline_index = 1 544 if len(myline) > 1: 545 if myline[1] == ";": 546 breakline_index = 2 547 elif myline[1:].lstrip()[:2] == "//": 548 if myline.endswith('\n'): 549 breakline_index = len(myline) - 1 550 else: 551 breakline_index = len(myline) 552 res_lines.append("\n".join(self.split_line(\ 553 myline[:breakline_index], 554 self.split_characters)) + "\n") 555 myline = myline[breakline_index + 1:].lstrip() 556 if myline: 557 # If anything is left of myline, write it recursively 558 res_lines.extend(self.write_line(myline)) 559 return res_lines 560 561 # Check if line starts with keyword with parentesis 562 for key in self.indent_par_keywords.keys(): 563 if re.search(key, myline): 564 # Step through to find end of parenthesis 565 parenstack = collections.deque() 566 for i, ch in enumerate(myline[len(key)-1:]): 567 if ch == '(': 568 parenstack.append(ch) 569 elif ch == ')': 570 try: 571 parenstack.pop() 572 except IndexError: 573 # no opening parenthesis left in stack 574 raise self.CPPWriterError(\ 575 'Non-matching parenthesis in C++ output' \ 576 + myline) 577 if not parenstack: 578 # We are done 579 break 580 endparen_index = len(key) + i 581 # Print line, make linebreak, check if next character is { 582 res_lines.append("\n".join(self.split_line(\ 583 myline[:endparen_index], \ 584 self.split_characters)) + \ 585 "\n") 586 myline = myline[endparen_index:].lstrip() 587 # Add keyword to list and add indent for next line 588 self.__keyword_list.append(key) 589 self.__indent = self.__indent + \ 590 self.indent_par_keywords[key] 591 if myline: 592 # If anything is left of myline, write it recursively 593 res_lines.extend(self.write_line(myline)) 594 595 return res_lines 596 597 # Check if line starts with single keyword 598 for key in self.indent_single_keywords.keys(): 599 if re.search(key, myline): 600 end_index = len(key) - 1 601 # Print line, make linebreak, check if next character is { 602 res_lines.append(" " * self.__indent + myline[:end_index] + \ 603 "\n") 604 myline = myline[end_index:].lstrip() 605 # Add keyword to list and add indent for next line 606 self.__keyword_list.append(key) 607 self.__indent = self.__indent + \ 608 self.indent_single_keywords[key] 609 if myline: 610 # If anything is left of myline, write it recursively 611 res_lines.extend(self.write_line(myline)) 612 613 return res_lines 614 615 # Check if line starts with content keyword 616 for key in self.indent_content_keywords.keys(): 617 if re.search(key, myline): 618 # Print line, make linebreak, check if next character is { 619 if "{" in myline: 620 end_index = myline.index("{") 621 else: 622 end_index = len(myline) 623 res_lines.append("\n".join(self.split_line(\ 624 myline[:end_index], \ 625 self.split_characters)) + \ 626 "\n") 627 myline = myline[end_index:].lstrip() 628 # Add keyword to list and add indent for next line 629 self.__keyword_list.append(key) 630 self.__indent = self.__indent + \ 631 self.indent_content_keywords[key] 632 if myline: 633 # If anything is left of myline, write it recursively 634 res_lines.extend(self.write_line(myline)) 635 636 return res_lines 637 638 # Check if line starts with continuous indent keyword 639 for key in self.cont_indent_keywords.keys(): 640 if re.search(key, myline): 641 # Check if we have a continuous indent keyword since before 642 if self.__keyword_list[-1] in self.cont_indent_keywords.keys(): 643 self.__indent = self.__indent - \ 644 self.cont_indent_keywords[\ 645 self.__keyword_list.pop()] 646 # Print line, make linebreak 647 res_lines.append("\n".join(self.split_line(myline, \ 648 self.split_characters)) + \ 649 "\n") 650 # Add keyword to list and add indent for next line 651 self.__keyword_list.append(key) 652 self.__indent = self.__indent + \ 653 self.cont_indent_keywords[key] 654 655 return res_lines 656 657 # Check if this line is an array initialization a ={b,c,d}; 658 if self.init_array_pattern.search(myline): 659 res_lines.append("\n".join(self.split_line(\ 660 myline, 661 self.split_characters)) + \ 662 "\n") 663 return res_lines 664 665 # Check if this is a short xxx {yyy} type line; 666 if self.short_clause_pattern.search(myline): 667 lines = self.split_line(myline, 668 self.split_characters) 669 if len(lines) == 1: 670 res_lines.append("\n".join(lines) + "\n") 671 return res_lines 672 673 # Check if there is a "{" somewhere in the line 674 if "{" in myline: 675 end_index = myline.index("{") 676 res_lines.append("\n".join(self.split_line(\ 677 myline[:end_index], \ 678 self.split_characters)) + \ 679 "\n") 680 myline = myline[end_index:].lstrip() 681 if myline: 682 # If anything is left of myline, write it recursively 683 res_lines.extend(self.write_line(myline)) 684 return res_lines 685 686 # Check if there is a "}" somewhere in the line 687 if "}" in myline: 688 end_index = myline.index("}") 689 res_lines.append("\n".join(self.split_line(\ 690 myline[:end_index], \ 691 self.split_characters)) + \ 692 "\n") 693 myline = myline[end_index:].lstrip() 694 if myline: 695 # If anything is left of myline, write it recursively 696 res_lines.extend(self.write_line(myline)) 697 return res_lines 698 699 # Write line(s) to file 700 res_lines.append("\n".join(self.split_line(myline, \ 701 self.split_characters)) + "\n") 702 703 # Check if this is a single indented line 704 if self.__keyword_list: 705 if self.__keyword_list[-1] in self.indent_par_keywords: 706 self.__indent = self.__indent - \ 707 self.indent_par_keywords[self.__keyword_list.pop()] 708 elif self.__keyword_list[-1] in self.indent_single_keywords: 709 self.__indent = self.__indent - \ 710 self.indent_single_keywords[self.__keyword_list.pop()] 711 elif self.__keyword_list[-1] in self.indent_content_keywords: 712 self.__indent = self.__indent - \ 713 self.indent_content_keywords[self.__keyword_list.pop()] 714 715 return res_lines
716
717 - def write_comment_line(self, line):
718 """Write a comment line, with correct indent and line splits""" 719 720 # write_comment_line must have a single line as argument 721 assert(isinstance(line, str) and line.find('\n') == -1) 722 723 res_lines = [] 724 725 # This is a comment 726 727 if self.start_comment_pattern.search(line): 728 self.__comment_ongoing = True 729 line = self.start_comment_pattern.sub("", line) 730 731 if self.end_comment_pattern.search(line): 732 self.__comment_ongoing = False 733 line = self.end_comment_pattern.sub("", line) 734 735 line = self.comment_pattern.sub("", line).strip() 736 # Avoid extra space for lines starting with certain multiple patterns 737 if self.no_space_comment_patterns.match(line): 738 myline = self.comment_char + line 739 else: 740 myline = self.comment_char + " " + line 741 # Break line in appropriate places defined (in priority order) 742 # by the characters in comment_split_characters 743 res = self.split_comment_line(myline) 744 745 # Write line(s) to file 746 res_lines.append("\n".join(res) + "\n") 747 748 return res_lines
749
750 - def split_line(self, line, split_characters):
751 """Split a line if it is longer than self.line_length 752 columns. Split in preferential order according to 753 split_characters. Also fix spacing for line.""" 754 755 # First split up line if there are comments 756 comment = "" 757 if line.find(self.comment_char) > -1: 758 line, dum, comment = line.partition(self.comment_char) 759 760 # Then split up line if there are quotes 761 quotes = self.quote_chars.finditer(line) 762 763 start_pos = 0 764 line_quotes = [] 765 line_no_quotes = [] 766 for i, quote in enumerate(quotes): 767 if i % 2 == 0: 768 # Add text before quote to line_no_quotes 769 line_no_quotes.append(line[start_pos:quote.start()]) 770 start_pos = quote.start() 771 else: 772 # Add quote to line_quotes 773 line_quotes.append(line[start_pos:quote.end()]) 774 start_pos = quote.end() 775 776 line_no_quotes.append(line[start_pos:]) 777 778 # Fix spacing for line, but only outside of quotes 779 line.rstrip() 780 for i, no_quote in enumerate(line_no_quotes): 781 for key in self.spacing_patterns: 782 no_quote = self.spacing_re[key[0]].sub(key[1], no_quote) 783 line_no_quotes[i] = no_quote 784 785 # Glue together quotes and non-quotes: 786 line = line_no_quotes[0] 787 for i in range(len(line_quotes)): 788 line += line_quotes[i] 789 if len(line_no_quotes) > i + 1: 790 line += line_no_quotes[i+1] 791 792 # Add indent 793 res_lines = [" " * self.__indent + line] 794 795 while len(res_lines[-1]) > self.line_length: 796 long_line = res_lines[-1] 797 split_at = -1 798 for character in split_characters: 799 index = long_line[(self.line_length - self.max_split): \ 800 self.line_length].rfind(character) 801 if index >= 0: 802 split_at = self.line_length - self.max_split + index + 1 803 break 804 805 # no valid breaking so find the first breaking allowed: 806 if split_at == -1: 807 split_at = len(long_line) 808 for character in split_characters: 809 split = long_line[self.line_length].find(character) 810 if split > 0: 811 split_at = min(split, split_at) 812 if split_at == len(long_line): 813 break 814 815 # Don't allow split within quotes 816 quotes = self.quote_chars.findall(long_line[:split_at]) 817 if quotes and len(quotes) % 2 == 1: 818 quote_match = self.quote_chars.search(long_line[split_at:]) 819 if not quote_match: 820 raise self.CPPWriterError(\ 821 "Error: Unmatched quote in line " + long_line) 822 split_at = quote_match.end() + split_at + 1 823 split_match = re.search(self.split_characters, 824 long_line[split_at:]) 825 if split_match: 826 split_at = split_at + split_match.start() 827 else: 828 split_at = len(long_line) + 1 829 830 # Append new line 831 if long_line[split_at:].lstrip(): 832 # Replace old line 833 res_lines[-1] = long_line[:split_at].rstrip() 834 res_lines.append(" " * \ 835 (self.__indent + self.line_cont_indent) + \ 836 long_line[split_at:].strip()) 837 else: 838 break 839 840 if comment: 841 res_lines[-1] += " " + self.comment_char + comment 842 843 return res_lines
844
845 - def split_comment_line(self, line):
846 """Split a line if it is longer than self.line_length 847 columns. Split in preferential order according to 848 split_characters.""" 849 850 # First fix spacing for line 851 line.rstrip() 852 res_lines = [" " * self.__indent + line] 853 854 while len(res_lines[-1]) > self.line_length: 855 long_line = res_lines[-1] 856 split_at = self.line_length 857 index = long_line[(self.line_length - self.max_split): \ 858 self.line_length].rfind(' ') 859 if index >= 0: 860 split_at = self.line_length - self.max_split + index + 1 861 862 # Append new line 863 if long_line[split_at:].lstrip(): 864 # Replace old line 865 res_lines[-1] = long_line[:split_at].rstrip() 866 res_lines.append(" " * \ 867 self.__indent + self.comment_char + " " + \ 868 long_line[split_at:].strip()) 869 else: 870 break 871 872 return res_lines
873
874 -class PythonWriter(FileWriter):
875
876 - def write_comments(self, text):
877 text = '#%s\n' % text.replace('\n','\n#') 878 file.write(self, text)
879
880 -class MakefileWriter(FileWriter):
881
882 - def write_comments(self, text):
883 text = '#%s\n' % text.replace('\n','\n#') 884 file.write(self, text)
885
886 - def writelines(self, lines):
887 """Extends the regular file.writeline() function to write out 888 nicely formatted code""" 889 890 self.write(lines)
891