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