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