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