1
2
3
4
5
6
7
8
9
10
11
12
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
24 """Generic Writer class. All writers should inherit from this class."""
25
27 """Exception raised if an error occurs in the definition
28 or the execution of a Writer."""
29
30 pass
31
32
34 """Initialize file to write to"""
35
36 return file.__init__(self, name, opt)
37
39 """Write a line with proper indent and splitting of long lines
40 for the language in question."""
41
42 pass
43
49
71
73 """Extends the regular file.writeline() function to write out
74 nicely formatted code"""
75
76 splitlines = []
77 if isinstance(lines, list):
78 for line in lines:
79 if not isinstance(line, str):
80 raise self.FileWriterError("%s not string" % repr(line))
81 splitlines.extend(line.split('\n'))
82 elif isinstance(lines, str):
83 splitlines.extend(lines.split('\n'))
84 else:
85 raise self.FileWriterError("%s not string" % repr(lines))
86
87 for line in splitlines:
88 res_lines = self.write_line(line)
89 for line_to_write in res_lines:
90 self.write(line_to_write)
91
92
93
94
96 """Routines for writing fortran lines. Keeps track of indentation
97 and splitting of long lines"""
98
100 """Exception raised if an error occurs in the definition
101 or the execution of a FortranWriter."""
102 pass
103
104
105 keyword_pairs = {'^if.+then\s*$': ('^endif', 2),
106 '^do(?!\s+\d+)\s+': ('^enddo\s*$', 2),
107 '^subroutine': ('^end\s*$', 0),
108 'function': ('^end\s*$', 0)}
109 single_indents = {'^else\s*$':-2,
110 '^else\s*if.+then\s*$':-2}
111 number_re = re.compile('^(?P<num>\d+)\s+(?P<rest>.*)')
112 line_cont_char = '$'
113 comment_char = 'c'
114 downcase = False
115 line_length = 71
116 max_split = 10
117 split_characters = "+-*/,) "
118 comment_split_characters = " "
119
120
121 __indent = 0
122 __keyword_list = []
123 __comment_pattern = re.compile(r"^(\s*#|c$|(c\s+([^=]|$)))", re.IGNORECASE)
124
126 """Write a fortran line, with correct indent and line splits"""
127
128
129 assert(isinstance(line, str) and line.find('\n') == -1)
130
131
132 res_lines = []
133
134
135 if not line.lstrip():
136 res_lines.append("\n")
137 return res_lines
138
139
140 if self.__comment_pattern.search(line):
141
142 res_lines = self.write_comment_line(line.lstrip()[1:])
143 return res_lines
144
145 else:
146
147
148
149 myline = line.lstrip()
150
151
152 num_group = self.number_re.search(myline)
153 num = ""
154 if num_group:
155 num = num_group.group('num')
156 myline = num_group.group('rest')
157
158
159
160 (myline, part, post_comment) = myline.partition("!")
161
162 if part:
163 part = " " + part
164
165 myline = myline.replace('\"', '\'')
166
167 splitline = myline.split('\'')
168 myline = ""
169 i = 0
170 while i < len(splitline):
171 if i % 2 == 1:
172
173 while splitline[i] and splitline[i][-1] == '\\':
174 splitline[i] = splitline[i] + '\'' + splitline.pop(i + 1)
175 else:
176
177 if FortranWriter.downcase:
178 splitline[i] = splitline[i].lower()
179 else:
180 splitline[i] = splitline[i].upper()
181 i = i + 1
182
183 myline = "\'".join(splitline).rstrip()
184
185
186 if self.__keyword_list and re.search(self.keyword_pairs[\
187 self.__keyword_list[-1]][0], myline.lower()):
188 key = self.__keyword_list.pop()
189 self.__indent = self.__indent - self.keyword_pairs[key][1]
190
191
192 single_indent = 0
193 for key in self.single_indents.keys():
194 if re.search(key, myline.lower()):
195 self.__indent = self.__indent + self.single_indents[key]
196 single_indent = -self.single_indents[key]
197 break
198
199
200
201 res = self.split_line(" " + num + \
202 " " * (5 + self.__indent - len(num)) + myline,
203 self.split_characters,
204 " " * 5 + self.line_cont_char + \
205 " " * (self.__indent + 1))
206
207
208 for key in self.keyword_pairs.keys():
209 if re.search(key, myline.lower()):
210 self.__keyword_list.append(key)
211 self.__indent = self.__indent + self.keyword_pairs[key][1]
212 break
213
214
215 if single_indent != None:
216 self.__indent = self.__indent + single_indent
217 single_indent = None
218
219
220 res_lines.append("\n".join(res) + part + post_comment + "\n")
221
222 return res_lines
223
250
251 - def split_line(self, line, split_characters, line_start):
252 """Split a line if it is longer than self.line_length
253 columns. Split in preferential order according to
254 split_characters, and start each new line with line_start."""
255
256 res_lines = [line]
257
258 while len(res_lines[-1]) > self.line_length:
259 split_at = self.line_length
260 for character in split_characters:
261 index = res_lines[-1][(self.line_length - self.max_split): \
262 self.line_length].rfind(character)
263 if index >= 0:
264 split_at = self.line_length - self.max_split + index
265 break
266 newline = res_lines[-1][split_at:]
267 nquotes = self.count_number_of_quotes(newline)
268 res_lines.append(line_start +
269 ('//\''+res_lines[-1][(split_at-1):] if nquotes%2==1 else
270 ''+res_lines[-1][split_at:]))
271 res_lines[-2] = (res_lines[-2][:(split_at-1)]+'\'' if nquotes%2==1 \
272 else res_lines[-2][:split_at])
273 return res_lines
274
276 """ Count the number of real quotes (not escaped ones) in a line. """
277
278 splitline = line.split('\'')
279 i = 0
280 while i < len(splitline):
281 if i % 2 == 1:
282
283 while splitline[i] and splitline[i][-1] == '\\':
284 splitline[i] = splitline[i] + '\'' + splitline.pop(i + 1)
285 i = i + 1
286 return len(splitline)-1
287
288
289
290
292 """Routines for writing C++ lines. Keeps track of brackets,
293 spaces, indentation and splitting of long lines"""
294
296 """Exception raised if an error occurs in the definition
297 or the execution of a CPPWriter."""
298 pass
299
300
301 standard_indent = 2
302 line_cont_indent = 4
303
304 indent_par_keywords = {'^if': standard_indent,
305 '^else if': standard_indent,
306 '^for': standard_indent,
307 '^while': standard_indent,
308 '^switch': standard_indent}
309 indent_single_keywords = {'^else': standard_indent}
310 indent_content_keywords = {'^class': standard_indent,
311 '^namespace': 0}
312 cont_indent_keywords = {'^case': standard_indent,
313 '^default': standard_indent,
314 '^public': standard_indent,
315 '^private': standard_indent,
316 '^protected': standard_indent}
317
318 spacing_patterns = [('\s*\"\s*}', '\"'),
319 ('\s*,\s*', ', '),
320 ('\s*-\s*', ' - '),
321 ('([{(,=])\s*-\s*', '\g<1> -'),
322 ('(return)\s*-\s*', '\g<1> -'),
323 ('\s*\+\s*', ' + '),
324 ('([{(,=])\s*\+\s*', '\g<1> +'),
325 ('\(\s*', '('),
326 ('\s*\)', ')'),
327 ('\{\s*', '{'),
328 ('\s*\}', '}'),
329 ('\s*=\s*', ' = '),
330 ('\s*>\s*', ' > '),
331 ('\s*<\s*', ' < '),
332 ('\s*!\s*', ' !'),
333 ('\s*/\s*', '/'),
334 ('\s*\*\s*', ' * '),
335 ('\s*-\s+-\s*', '-- '),
336 ('\s*\+\s+\+\s*', '++ '),
337 ('\s*-\s+=\s*', ' -= '),
338 ('\s*\+\s+=\s*', ' += '),
339 ('\s*\*\s+=\s*', ' *= '),
340 ('\s*/=\s*', ' /= '),
341 ('\s*>\s+>\s*', ' >> '),
342 ('<\s*double\s*>>\s*', '<double> > '),
343 ('\s*<\s+<\s*', ' << '),
344 ('\s*-\s+>\s*', '->'),
345 ('\s*=\s+=\s*', ' == '),
346 ('\s*!\s+=\s*', ' != '),
347 ('\s*>\s+=\s*', ' >= '),
348 ('\s*<\s+=\s*', ' <= '),
349 ('\s*&&\s*', ' && '),
350 ('\s*\|\|\s*', ' || '),
351 ('\s*{\s*}', ' {}'),
352 ('\s*;\s*', '; '),
353 (';\s*\}', ';}'),
354 (';\s*$}', ';'),
355 ('\s*<\s*([a-zA-Z0-9]+?)\s*>', '<\g<1>>'),
356 ('^#include\s*<\s*(.*?)\s*>', '#include <\g<1>>'),
357 ('(\d+\.{0,1}\d*|\.\d+)\s*[eE]\s*([+-]{0,1})\s*(\d+)',
358 '\g<1>e\g<2>\g<3>'),
359 ('\s+',' ')]
360 spacing_re = dict([(key[0], re.compile(key[0])) for key in \
361 spacing_patterns])
362
363 init_array_pattern = re.compile(r"=\s*\{.*\}")
364 short_clause_pattern = re.compile(r"\{.*\}")
365
366 comment_char = '//'
367 comment_pattern = re.compile(r"^(\s*#\s+|\s*//)")
368 start_comment_pattern = re.compile(r"^(\s*/\*)")
369 end_comment_pattern = re.compile(r"(\s*\*/)$")
370
371 quote_chars = re.compile(r"[^\\][\"\']|^[\"\']")
372 no_space_comment_patterns = re.compile(r"--|\*\*|==|\+\+")
373 line_length = 80
374 max_split = 40
375 split_characters = " "
376 comment_split_characters = " "
377
378
379 __indent = 0
380 __keyword_list = collections.deque()
381 __comment_ongoing = False
382
384 """Write a C++ line, with correct indent, spacing and line splits"""
385
386
387 assert(isinstance(line, str) and line.find('\n') == -1)
388
389 res_lines = []
390
391
392 if self.comment_pattern.search(line) or \
393 self.start_comment_pattern.search(line) or \
394 self.__comment_ongoing:
395
396 res_lines = self.write_comment_line(line.lstrip())
397 return res_lines
398
399
400
401
402 myline = line.lstrip()
403
404
405 if not myline:
406 return ["\n"]
407
408
409 if myline[0] == "{":
410
411 indent = self.__indent
412 key = ""
413 if self.__keyword_list:
414 key = self.__keyword_list[-1]
415 if key in self.indent_par_keywords:
416 indent = indent - self.indent_par_keywords[key]
417 elif key in self.indent_single_keywords:
418 indent = indent - self.indent_single_keywords[key]
419 elif key in self.indent_content_keywords:
420 indent = indent - self.indent_content_keywords[key]
421 else:
422
423 self.__indent = self.__indent + self.standard_indent
424
425 res_lines.append(" " * indent + "{" + "\n")
426
427 self.__keyword_list.append("{")
428 myline = myline[1:].lstrip()
429 if myline:
430
431 res_lines.extend(self.write_line(myline))
432 return res_lines
433
434
435 if myline[0] == "}":
436
437 if not self.__keyword_list:
438 raise self.CPPWriterError(\
439 'Non-matching } in C++ output: ' \
440 + myline)
441
442 if self.__keyword_list[-1] in self.cont_indent_keywords.keys():
443 key = self.__keyword_list.pop()
444 self.__indent = self.__indent - self.cont_indent_keywords[key]
445
446 if not self.__keyword_list.pop() == "{":
447 raise self.CPPWriterError(\
448 'Non-matching } in C++ output: ' \
449 + ",".join(self.__keyword_list) + myline)
450
451 key = ""
452 if self.__keyword_list:
453 key = self.__keyword_list[-1]
454 if key in self.indent_par_keywords:
455 self.__indent = self.__indent - \
456 self.indent_par_keywords[key]
457 self.__keyword_list.pop()
458 elif key in self.indent_single_keywords:
459 self.__indent = self.__indent - \
460 self.indent_single_keywords[key]
461 self.__keyword_list.pop()
462 elif key in self.indent_content_keywords:
463 self.__indent = self.__indent - \
464 self.indent_content_keywords[key]
465 self.__keyword_list.pop()
466 else:
467
468 self.__indent = self.__indent - self.standard_indent
469
470
471 breakline_index = 1
472 if len(myline) > 1:
473 if myline[1] == ";":
474 breakline_index = 2
475 elif myline[1:].lstrip()[:2] == "//":
476 if myline.endswith('\n'):
477 breakline_index = len(myline) - 1
478 else:
479 breakline_index = len(myline)
480 res_lines.append("\n".join(self.split_line(\
481 myline[:breakline_index],
482 self.split_characters)) + "\n")
483 myline = myline[breakline_index + 1:].lstrip()
484 if myline:
485
486 res_lines.extend(self.write_line(myline))
487 return res_lines
488
489
490 for key in self.indent_par_keywords.keys():
491 if re.search(key, myline):
492
493 parenstack = collections.deque()
494 for i, ch in enumerate(myline[len(key)-1:]):
495 if ch == '(':
496 parenstack.append(ch)
497 elif ch == ')':
498 try:
499 parenstack.pop()
500 except IndexError:
501
502 raise self.CPPWriterError(\
503 'Non-matching parenthesis in C++ output' \
504 + myline)
505 if not parenstack:
506
507 break
508 endparen_index = len(key) + i
509
510 res_lines.append("\n".join(self.split_line(\
511 myline[:endparen_index], \
512 self.split_characters)) + \
513 "\n")
514 myline = myline[endparen_index:].lstrip()
515
516 self.__keyword_list.append(key)
517 self.__indent = self.__indent + \
518 self.indent_par_keywords[key]
519 if myline:
520
521 res_lines.extend(self.write_line(myline))
522
523 return res_lines
524
525
526 for key in self.indent_single_keywords.keys():
527 if re.search(key, myline):
528 end_index = len(key) - 1
529
530 res_lines.append(" " * self.__indent + myline[:end_index] + \
531 "\n")
532 myline = myline[end_index:].lstrip()
533
534 self.__keyword_list.append(key)
535 self.__indent = self.__indent + \
536 self.indent_single_keywords[key]
537 if myline:
538
539 res_lines.extend(self.write_line(myline))
540
541 return res_lines
542
543
544 for key in self.indent_content_keywords.keys():
545 if re.search(key, myline):
546
547 if "{" in myline:
548 end_index = myline.index("{")
549 else:
550 end_index = len(myline)
551 res_lines.append("\n".join(self.split_line(\
552 myline[:end_index], \
553 self.split_characters)) + \
554 "\n")
555 myline = myline[end_index:].lstrip()
556
557 self.__keyword_list.append(key)
558 self.__indent = self.__indent + \
559 self.indent_content_keywords[key]
560 if myline:
561
562 res_lines.extend(self.write_line(myline))
563
564 return res_lines
565
566
567 for key in self.cont_indent_keywords.keys():
568 if re.search(key, myline):
569
570 if self.__keyword_list[-1] in self.cont_indent_keywords.keys():
571 self.__indent = self.__indent - \
572 self.cont_indent_keywords[\
573 self.__keyword_list.pop()]
574
575 res_lines.append("\n".join(self.split_line(myline, \
576 self.split_characters)) + \
577 "\n")
578
579 self.__keyword_list.append(key)
580 self.__indent = self.__indent + \
581 self.cont_indent_keywords[key]
582
583 return res_lines
584
585
586 if self.init_array_pattern.search(myline):
587 res_lines.append("\n".join(self.split_line(\
588 myline,
589 self.split_characters)) + \
590 "\n")
591 return res_lines
592
593
594 if self.short_clause_pattern.search(myline):
595 lines = self.split_line(myline,
596 self.split_characters)
597 if len(lines) == 1:
598 res_lines.append("\n".join(lines) + "\n")
599 return res_lines
600
601
602 if "{" in myline:
603 end_index = myline.index("{")
604 res_lines.append("\n".join(self.split_line(\
605 myline[:end_index], \
606 self.split_characters)) + \
607 "\n")
608 myline = myline[end_index:].lstrip()
609 if myline:
610
611 res_lines.extend(self.write_line(myline))
612 return res_lines
613
614
615 if "}" in myline:
616 end_index = myline.index("}")
617 res_lines.append("\n".join(self.split_line(\
618 myline[:end_index], \
619 self.split_characters)) + \
620 "\n")
621 myline = myline[end_index:].lstrip()
622 if myline:
623
624 res_lines.extend(self.write_line(myline))
625 return res_lines
626
627
628 res_lines.append("\n".join(self.split_line(myline, \
629 self.split_characters)) + "\n")
630
631
632 if self.__keyword_list:
633 if self.__keyword_list[-1] in self.indent_par_keywords:
634 self.__indent = self.__indent - \
635 self.indent_par_keywords[self.__keyword_list.pop()]
636 elif self.__keyword_list[-1] in self.indent_single_keywords:
637 self.__indent = self.__indent - \
638 self.indent_single_keywords[self.__keyword_list.pop()]
639 elif self.__keyword_list[-1] in self.indent_content_keywords:
640 self.__indent = self.__indent - \
641 self.indent_content_keywords[self.__keyword_list.pop()]
642
643 return res_lines
644
677
679 """Split a line if it is longer than self.line_length
680 columns. Split in preferential order according to
681 split_characters. Also fix spacing for line."""
682
683
684 comment = ""
685 if line.find(self.comment_char) > -1:
686 line, dum, comment = line.partition(self.comment_char)
687
688
689 quotes = self.quote_chars.finditer(line)
690
691 start_pos = 0
692 line_quotes = []
693 line_no_quotes = []
694 for i, quote in enumerate(quotes):
695 if i % 2 == 0:
696
697 line_no_quotes.append(line[start_pos:quote.start()])
698 start_pos = quote.start()
699 else:
700
701 line_quotes.append(line[start_pos:quote.end()])
702 start_pos = quote.end()
703
704 line_no_quotes.append(line[start_pos:])
705
706
707 line.rstrip()
708 for i, no_quote in enumerate(line_no_quotes):
709 for key in self.spacing_patterns:
710 no_quote = self.spacing_re[key[0]].sub(key[1], no_quote)
711 line_no_quotes[i] = no_quote
712
713
714 line = line_no_quotes[0]
715 for i in range(len(line_quotes)):
716 line += line_quotes[i]
717 if len(line_no_quotes) > i + 1:
718 line += line_no_quotes[i+1]
719
720
721 res_lines = [" " * self.__indent + line]
722
723 while len(res_lines[-1]) > self.line_length:
724 long_line = res_lines[-1]
725 split_at = -1
726 for character in split_characters:
727 index = long_line[(self.line_length - self.max_split): \
728 self.line_length].rfind(character)
729 if index >= 0:
730 split_at = self.line_length - self.max_split + index + 1
731 break
732
733
734 if split_at == -1:
735 split_at = len(long_line)
736 for character in split_characters:
737 split = long_line[self.line_length].find(character)
738 if split > 0:
739 split_at = min(split, split_at)
740 if split_at == len(long_line):
741 break
742
743
744 quotes = self.quote_chars.findall(long_line[:split_at])
745 if quotes and len(quotes) % 2 == 1:
746 quote_match = self.quote_chars.search(long_line[split_at:])
747 if not quote_match:
748 raise self.CPPWriterError(\
749 "Error: Unmatched quote in line " + long_line)
750 split_at = quote_match.end() + split_at + 1
751 split_match = re.search(self.split_characters,
752 long_line[split_at:])
753 if split_match:
754 split_at = split_at + split_match.start()
755 else:
756 split_at = len(long_line) + 1
757
758
759 if long_line[split_at:].lstrip():
760
761 res_lines[-1] = long_line[:split_at].rstrip()
762 res_lines.append(" " * \
763 (self.__indent + self.line_cont_indent) + \
764 long_line[split_at:].strip())
765 else:
766 break
767
768 if comment:
769 res_lines[-1] += " " + self.comment_char + comment
770
771 return res_lines
772
801
807
809
813
815 """Extends the regular file.writeline() function to write out
816 nicely formatted code"""
817
818 self.write(lines)
819