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+': ('^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][len(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
267 res_lines.append(line_start + \
268 res_lines[-1][split_at:])
269 res_lines[-2] = res_lines[-2][:split_at]
270
271 return res_lines
272
273
274
275
277 """Routines for writing C++ lines. Keeps track of brackets,
278 spaces, indentation and splitting of long lines"""
279
281 """Exception raised if an error occurs in the definition
282 or the execution of a CPPWriter."""
283 pass
284
285
286 standard_indent = 2
287 line_cont_indent = 4
288
289 indent_par_keywords = {'^if': standard_indent,
290 '^else if': standard_indent,
291 '^for': standard_indent,
292 '^while': standard_indent,
293 '^switch': standard_indent}
294 indent_single_keywords = {'^else': standard_indent}
295 indent_content_keywords = {'^class': standard_indent,
296 '^namespace': 0}
297 cont_indent_keywords = {'^case': standard_indent,
298 '^default': standard_indent,
299 '^public': standard_indent,
300 '^private': standard_indent,
301 '^protected': standard_indent}
302
303 spacing_patterns = [('\s*\"\s*}', '\"'),
304 ('\s*,\s*', ', '),
305 ('\s*-\s*', ' - '),
306 ('([{(,=])\s*-\s*', '\g<1> -'),
307 ('(return)\s*-\s*', '\g<1> -'),
308 ('\s*\+\s*', ' + '),
309 ('([{(,=])\s*\+\s*', '\g<1> +'),
310 ('\(\s*', '('),
311 ('\s*\)', ')'),
312 ('\{\s*', '{'),
313 ('\s*\}', '}'),
314 ('\s*=\s*', ' = '),
315 ('\s*>\s*', ' > '),
316 ('\s*<\s*', ' < '),
317 ('\s*!\s*', ' !'),
318 ('\s*/\s*', '/'),
319 ('\s*\*\s*', ' * '),
320 ('\s*-\s+-\s*', '-- '),
321 ('\s*\+\s+\+\s*', '++ '),
322 ('\s*-\s+=\s*', ' -= '),
323 ('\s*\+\s+=\s*', ' += '),
324 ('\s*\*\s+=\s*', ' *= '),
325 ('\s*/=\s*', ' /= '),
326 ('\s*>\s+>\s*', ' >> '),
327 ('<\s*double\s*>>\s*', '<double> > '),
328 ('\s*<\s+<\s*', ' << '),
329 ('\s*-\s+>\s*', '->'),
330 ('\s*=\s+=\s*', ' == '),
331 ('\s*!\s+=\s*', ' != '),
332 ('\s*>\s+=\s*', ' >= '),
333 ('\s*<\s+=\s*', ' <= '),
334 ('\s*&&\s*', ' && '),
335 ('\s*\|\|\s*', ' || '),
336 ('\s*{\s*}', ' {}'),
337 ('\s*;\s*', '; '),
338 (';\s*\}', ';}'),
339 (';\s*$}', ';'),
340 ('\s*<\s*([a-zA-Z0-9]+?)\s*>', '<\g<1>>'),
341 ('^#include\s*<\s*(.*?)\s*>', '#include <\g<1>>'),
342 ('(\d+\.{0,1}\d*|\.\d+)\s*[eE]\s*([+-]{0,1})\s*(\d+)',
343 '\g<1>e\g<2>\g<3>'),
344 ('\s+',' ')]
345 spacing_re = dict([(key[0], re.compile(key[0])) for key in \
346 spacing_patterns])
347
348 init_array_pattern = re.compile(r"=\s*\{.*\}")
349 short_clause_pattern = re.compile(r"\{.*\}")
350
351 comment_char = '//'
352 comment_pattern = re.compile(r"^(\s*#\s+|\s*//)")
353 start_comment_pattern = re.compile(r"^(\s*/\*)")
354 end_comment_pattern = re.compile(r"(\s*\*/)$")
355
356 quote_chars = re.compile(r"[^\\][\"\']|^[\"\']")
357 no_space_comment_patterns = re.compile(r"--|\*\*|==|\+\+")
358 line_length = 80
359 max_split = 40
360 split_characters = " "
361 comment_split_characters = " "
362
363
364 __indent = 0
365 __keyword_list = collections.deque()
366 __comment_ongoing = False
367
369 """Write a C++ line, with correct indent, spacing and line splits"""
370
371
372 assert(isinstance(line, str) and line.find('\n') == -1)
373
374 res_lines = []
375
376
377 if self.comment_pattern.search(line) or \
378 self.start_comment_pattern.search(line) or \
379 self.__comment_ongoing:
380
381 res_lines = self.write_comment_line(line.lstrip())
382 return res_lines
383
384
385
386
387 myline = line.lstrip()
388
389
390 if not myline:
391 return ["\n"]
392
393
394 if myline[0] == "{":
395
396 indent = self.__indent
397 key = ""
398 if self.__keyword_list:
399 key = self.__keyword_list[-1]
400 if key in self.indent_par_keywords:
401 indent = indent - self.indent_par_keywords[key]
402 elif key in self.indent_single_keywords:
403 indent = indent - self.indent_single_keywords[key]
404 elif key in self.indent_content_keywords:
405 indent = indent - self.indent_content_keywords[key]
406 else:
407
408 self.__indent = self.__indent + self.standard_indent
409
410 res_lines.append(" " * indent + "{" + "\n")
411
412 self.__keyword_list.append("{")
413 myline = myline[1:].lstrip()
414 if myline:
415
416 res_lines.extend(self.write_line(myline))
417 return res_lines
418
419
420 if myline[0] == "}":
421
422 if not self.__keyword_list:
423 raise self.CPPWriterError(\
424 'Non-matching } in C++ output: ' \
425 + myline)
426
427 if self.__keyword_list[-1] in self.cont_indent_keywords.keys():
428 key = self.__keyword_list.pop()
429 self.__indent = self.__indent - self.cont_indent_keywords[key]
430
431 if not self.__keyword_list.pop() == "{":
432 raise self.CPPWriterError(\
433 'Non-matching } in C++ output: ' \
434 + ",".join(self.__keyword_list) + myline)
435
436 key = ""
437 if self.__keyword_list:
438 key = self.__keyword_list[-1]
439 if key in self.indent_par_keywords:
440 self.__indent = self.__indent - \
441 self.indent_par_keywords[key]
442 self.__keyword_list.pop()
443 elif key in self.indent_single_keywords:
444 self.__indent = self.__indent - \
445 self.indent_single_keywords[key]
446 self.__keyword_list.pop()
447 elif key in self.indent_content_keywords:
448 self.__indent = self.__indent - \
449 self.indent_content_keywords[key]
450 self.__keyword_list.pop()
451 else:
452
453 self.__indent = self.__indent - self.standard_indent
454
455
456 breakline_index = 1
457 if len(myline) > 1:
458 if myline[1] == ";":
459 breakline_index = 2
460 elif myline[1:].lstrip()[:2] == "//":
461 breakline_index = len(myline) - 1
462 res_lines.append("\n".join(self.split_line(\
463 myline[:breakline_index],
464 self.split_characters)) + "\n")
465 myline = myline[breakline_index + 1:].lstrip()
466 if myline:
467
468 res_lines.extend(self.write_line(myline))
469 return res_lines
470
471
472 for key in self.indent_par_keywords.keys():
473 if re.search(key, myline):
474
475 parenstack = collections.deque()
476 for i, ch in enumerate(myline[len(key)-1:]):
477 if ch == '(':
478 parenstack.append(ch)
479 elif ch == ')':
480 try:
481 parenstack.pop()
482 except IndexError:
483
484 raise self.CPPWriterError(\
485 'Non-matching parenthesis in C++ output' \
486 + myline)
487 if not parenstack:
488
489 break
490 endparen_index = len(key) + i
491
492 res_lines.append("\n".join(self.split_line(\
493 myline[:endparen_index], \
494 self.split_characters)) + \
495 "\n")
496 myline = myline[endparen_index:].lstrip()
497
498 self.__keyword_list.append(key)
499 self.__indent = self.__indent + \
500 self.indent_par_keywords[key]
501 if myline:
502
503 res_lines.extend(self.write_line(myline))
504
505 return res_lines
506
507
508 for key in self.indent_single_keywords.keys():
509 if re.search(key, myline):
510 end_index = len(key) - 1
511
512 res_lines.append(" " * self.__indent + myline[:end_index] + \
513 "\n")
514 myline = myline[end_index:].lstrip()
515
516 self.__keyword_list.append(key)
517 self.__indent = self.__indent + \
518 self.indent_single_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_content_keywords.keys():
527 if re.search(key, myline):
528
529 if "{" in myline:
530 end_index = myline.index("{")
531 else:
532 end_index = len(myline)
533 res_lines.append("\n".join(self.split_line(\
534 myline[:end_index], \
535 self.split_characters)) + \
536 "\n")
537 myline = myline[end_index:].lstrip()
538
539 self.__keyword_list.append(key)
540 self.__indent = self.__indent + \
541 self.indent_content_keywords[key]
542 if myline:
543
544 res_lines.extend(self.write_line(myline))
545
546 return res_lines
547
548
549 for key in self.cont_indent_keywords.keys():
550 if re.search(key, myline):
551
552 if self.__keyword_list[-1] in self.cont_indent_keywords.keys():
553 self.__indent = self.__indent - \
554 self.cont_indent_keywords[\
555 self.__keyword_list.pop()]
556
557 res_lines.append("\n".join(self.split_line(myline, \
558 self.split_characters)) + \
559 "\n")
560
561 self.__keyword_list.append(key)
562 self.__indent = self.__indent + \
563 self.cont_indent_keywords[key]
564
565 return res_lines
566
567
568 if self.init_array_pattern.search(myline):
569 res_lines.append("\n".join(self.split_line(\
570 myline,
571 self.split_characters)) + \
572 "\n")
573 return res_lines
574
575
576 if self.short_clause_pattern.search(myline):
577 lines = self.split_line(myline,
578 self.split_characters)
579 if len(lines) == 1:
580 res_lines.append("\n".join(lines) + "\n")
581 return res_lines
582
583
584 if "{" in myline:
585 end_index = myline.index("{")
586 res_lines.append("\n".join(self.split_line(\
587 myline[:end_index], \
588 self.split_characters)) + \
589 "\n")
590 myline = myline[end_index:].lstrip()
591 if myline:
592
593 res_lines.extend(self.write_line(myline))
594 return res_lines
595
596
597 if "}" in myline:
598 end_index = myline.index("}")
599 res_lines.append("\n".join(self.split_line(\
600 myline[:end_index], \
601 self.split_characters)) + \
602 "\n")
603 myline = myline[end_index:].lstrip()
604 if myline:
605
606 res_lines.extend(self.write_line(myline))
607 return res_lines
608
609
610 res_lines.append("\n".join(self.split_line(myline, \
611 self.split_characters)) + "\n")
612
613
614 if self.__keyword_list:
615 if self.__keyword_list[-1] in self.indent_par_keywords:
616 self.__indent = self.__indent - \
617 self.indent_par_keywords[self.__keyword_list.pop()]
618 elif self.__keyword_list[-1] in self.indent_single_keywords:
619 self.__indent = self.__indent - \
620 self.indent_single_keywords[self.__keyword_list.pop()]
621 elif self.__keyword_list[-1] in self.indent_content_keywords:
622 self.__indent = self.__indent - \
623 self.indent_content_keywords[self.__keyword_list.pop()]
624
625 return res_lines
626
659
661 """Split a line if it is longer than self.line_length
662 columns. Split in preferential order according to
663 split_characters. Also fix spacing for line."""
664
665
666 comment = ""
667 if line.find(self.comment_char) > -1:
668 line, dum, comment = line.partition(self.comment_char)
669
670
671 quotes = self.quote_chars.finditer(line)
672
673 start_pos = 0
674 line_quotes = []
675 line_no_quotes = []
676 for i, quote in enumerate(quotes):
677 if i % 2 == 0:
678
679 line_no_quotes.append(line[start_pos:quote.start()])
680 start_pos = quote.start()
681 else:
682
683 line_quotes.append(line[start_pos:quote.end()])
684 start_pos = quote.end()
685
686 line_no_quotes.append(line[start_pos:])
687
688
689 line.rstrip()
690 for i, no_quote in enumerate(line_no_quotes):
691 for key in self.spacing_patterns:
692 no_quote = self.spacing_re[key[0]].sub(key[1], no_quote)
693 line_no_quotes[i] = no_quote
694
695
696 line = line_no_quotes[0]
697 for i in range(len(line_quotes)):
698 line += line_quotes[i]
699 if len(line_no_quotes) > i + 1:
700 line += line_no_quotes[i+1]
701
702
703 res_lines = [" " * self.__indent + line]
704
705 while len(res_lines[-1]) > self.line_length:
706 long_line = res_lines[-1]
707 split_at = self.line_length
708 for character in split_characters:
709 index = long_line[(self.line_length - self.max_split): \
710 self.line_length].rfind(character)
711 if index >= 0:
712 split_at = self.line_length - self.max_split + index + 1
713 break
714
715
716 quotes = self.quote_chars.findall(long_line[:split_at])
717 if quotes and len(quotes) % 2 == 1:
718 quote_match = self.quote_chars.search(long_line[split_at:])
719 if not quote_match:
720 raise self.CPPWriterError(\
721 "Error: Unmatched quote in line " + long_line)
722 split_at = quote_match.end() + split_at + 1
723 split_match = re.search(self.split_characters,
724 long_line[split_at:])
725 if split_match:
726 split_at = split_at + split_match.start()
727 else:
728 split_at = len(long_line) + 1
729
730
731 if long_line[split_at:].lstrip():
732
733 res_lines[-1] = long_line[:split_at].rstrip()
734 res_lines.append(" " * \
735 (self.__indent + self.line_cont_indent) + \
736 long_line[split_at:].strip())
737 else:
738 break
739
740 if comment:
741 res_lines[-1] += " " + self.comment_char + comment
742
743 return res_lines
744
773