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