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