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
26 supported_preprocessor_commands = ['if']
27 preprocessor_command_re=re.compile(
28 "\s*(?P<command>%s)\s*\(\s*(?P<body>.*)\s*\)\s*{\s*"\
29 %('|'.join(supported_preprocessor_commands)))
30 preprocessor_endif_re=re.compile(\
31 "\s*}\s*(?P<endif>else)?\s*(\((?P<body>.*)\))?\s*(?P<new_block>{)?\s*")
32
34 """Exception raised if an error occurs in the definition
35 or the execution of a Writer."""
36
37 pass
38
40 """Exception raised if an error occurs in the handling of the
41 preprocessor tags '##' in the template file."""
42 pass
43
45 """Initialize file to write to"""
46
47 return file.__init__(self, name, opt)
48
50 """Write a line with proper indent and splitting of long lines
51 for the language in question."""
52
53 pass
54
60
82
84 """Extends the regular file.writeline() function to write out
85 nicely formatted code. When defining a context, then the lines
86 will be preprocessed to apply possible conditional statements on the
87 content of the template depending on the contextual variables specified."""
88
89 splitlines = []
90 if isinstance(lines, list):
91 for line in lines:
92 if not isinstance(line, str):
93 raise self.FileWriterError("%s not string" % repr(line))
94 splitlines.extend(line.split('\n'))
95 elif isinstance(lines, str):
96 splitlines.extend(lines.split('\n'))
97 else:
98 raise self.FileWriterError("%s not string" % repr(lines))
99
100 if len(context)>0:
101 splitlines = self.preprocess_template(splitlines,context=context)
102
103 for line in splitlines:
104 res_lines = self.write_line(line)
105 for line_to_write in res_lines:
106 self.write(line_to_write)
107
109 """ This class takes care of applying the pre-processing statements
110 starting with ## in the template .inc files, using the contextual
111 variables specified in the dictionary 'context' given in input with
112 the variable names given as keys and their respective value as values."""
113
114 template_lines = []
115 if isinstance(input_lines, list):
116 for line in input_lines:
117 if not isinstance(line, str):
118 raise self.FileWriterError("%s not string" % repr(input_lines))
119 template_lines.extend(line.split('\n'))
120 elif isinstance(input_lines, str):
121 template_lines.extend(input_lines.split('\n'))
122 else:
123 raise self.FileWriterError("%s not string" % repr(input_lines))
124
125
126 for contextual_variable, value in context.items():
127 exec('%s=%s'%(str(contextual_variable),repr(value)))
128
129 res = []
130
131 if_stack = []
132 for i, line in enumerate(template_lines):
133 if not line.startswith('##'):
134 if all(if_stack):
135 res.append(line)
136 continue
137 preproc_command = self.preprocessor_command_re.match(line[2:])
138
139 if preproc_command is None:
140 preproc_endif = self.preprocessor_endif_re.match(line[2:])
141 if len(if_stack)==0 or preproc_endif is None:
142 raise self.FilePreProcessingError, 'Incorrect '+\
143 'preprocessing command %s at line %d.'%(line,i)
144 if preproc_endif.group('new_block') is None:
145 if_stack.pop()
146 elif preproc_endif.group('endif')=='else':
147 if_stack[-1]=(not if_stack[-1])
148
149 elif preproc_command.group('command')=='if':
150 try:
151 if_stack.append(eval(preproc_command.group('body'))==True)
152 except Exception, e:
153 raise self.FilePreProcessingError, 'Could not evaluate'+\
154 "python expression '%s' given the context %s provided."%\
155 (preproc_command.group('body'),str(context))+\
156 "\nLine %d of file %s."%(i,self.name)
157
158 if len(if_stack)>0:
159 raise self.FilePreProcessingError, 'Some conditional statements are'+\
160 ' not properly terminated.'
161 return res
162
163
164
165
167 """Routines for writing fortran lines. Keeps track of indentation
168 and splitting of long lines"""
169
171 """Exception raised if an error occurs in the definition
172 or the execution of a FortranWriter."""
173 pass
174
175
176 keyword_pairs = {'^if.+then\s*$': ('^endif', 2),
177 '^type(?!\s*\()\s*.+\s*$': ('^endtype', 2),
178 '^do(?!\s+\d+)\s+': ('^enddo\s*$', 2),
179 '^subroutine': ('^end\s*$', 0),
180 'function': ('^end\s*$', 0)}
181 single_indents = {'^else\s*$':-2,
182 '^else\s*if.+then\s*$':-2}
183 number_re = re.compile('^(?P<num>\d+)\s+(?P<rest>.*)')
184 line_cont_char = '$'
185 comment_char = 'c'
186 downcase = False
187 line_length = 71
188 max_split = 10
189 split_characters = "+-*/,) "
190 comment_split_characters = " "
191
192
193 __indent = 0
194 __keyword_list = []
195 __comment_pattern = re.compile(r"^(\s*#|c$|(c\s+([^=]|$)))", re.IGNORECASE)
196
198 """Write a fortran line, with correct indent and line splits"""
199
200
201 assert(isinstance(line, str) and line.find('\n') == -1)
202
203
204 res_lines = []
205
206
207 if not line.lstrip():
208 res_lines.append("\n")
209 return res_lines
210
211
212 if self.__comment_pattern.search(line):
213
214 res_lines = self.write_comment_line(line.lstrip()[1:])
215 return res_lines
216
217 else:
218
219
220
221 myline = line.lstrip()
222
223
224 num_group = self.number_re.search(myline)
225 num = ""
226 if num_group:
227 num = num_group.group('num')
228 myline = num_group.group('rest')
229
230
231
232 (myline, part, post_comment) = myline.partition("!")
233
234 if part:
235 part = " " + part
236
237 myline = myline.replace('\"', '\'')
238
239 splitline = myline.split('\'')
240 myline = ""
241 i = 0
242 while i < len(splitline):
243 if i % 2 == 1:
244
245 while splitline[i] and splitline[i][-1] == '\\':
246 splitline[i] = splitline[i] + '\'' + splitline.pop(i + 1)
247 else:
248
249 if FortranWriter.downcase:
250 splitline[i] = splitline[i].lower()
251 else:
252 splitline[i] = splitline[i].upper()
253 i = i + 1
254
255 myline = "\'".join(splitline).rstrip()
256
257
258 if self.__keyword_list and re.search(self.keyword_pairs[\
259 self.__keyword_list[-1]][0], myline.lower()):
260 key = self.__keyword_list.pop()
261 self.__indent = self.__indent - self.keyword_pairs[key][1]
262
263
264 single_indent = 0
265 for key in self.single_indents.keys():
266 if re.search(key, myline.lower()):
267 self.__indent = self.__indent + self.single_indents[key]
268 single_indent = -self.single_indents[key]
269 break
270
271
272
273 res = self.split_line(" " + num + \
274 " " * (5 + self.__indent - len(num)) + myline,
275 self.split_characters,
276 " " * 5 + self.line_cont_char + \
277 " " * (self.__indent + 1))
278
279
280 for key in self.keyword_pairs.keys():
281 if re.search(key, myline.lower()):
282 self.__keyword_list.append(key)
283 self.__indent = self.__indent + self.keyword_pairs[key][1]
284 break
285
286
287 if single_indent != None:
288 self.__indent = self.__indent + single_indent
289 single_indent = None
290
291
292 res_lines.append("\n".join(res) + part + post_comment + "\n")
293
294 return res_lines
295
322
323 - def split_line(self, line, split_characters, line_start):
324 """Split a line if it is longer than self.line_length
325 columns. Split in preferential order according to
326 split_characters, and start each new line with line_start."""
327
328 res_lines = [line]
329
330 while len(res_lines[-1]) > self.line_length:
331 split_at = self.line_length
332 for character in split_characters:
333 index = res_lines[-1][(self.line_length - self.max_split): \
334 self.line_length].rfind(character)
335 if index >= 0:
336 split_at = self.line_length - self.max_split + index
337 break
338 newline = res_lines[-1][split_at:]
339 nquotes = self.count_number_of_quotes(newline)
340 res_lines.append(line_start +
341 ('//\''+res_lines[-1][(split_at-1):] if nquotes%2==1 else
342 ''+res_lines[-1][split_at:]))
343 res_lines[-2] = (res_lines[-2][:(split_at-1)]+'\'' if nquotes%2==1 \
344 else res_lines[-2][:split_at])
345 return res_lines
346
348 """ Count the number of real quotes (not escaped ones) in a line. """
349
350 splitline = line.split('\'')
351 i = 0
352 while i < len(splitline):
353 if i % 2 == 1:
354
355 while splitline[i] and splitline[i][-1] == '\\':
356 splitline[i] = splitline[i] + '\'' + splitline.pop(i + 1)
357 i = i + 1
358 return len(splitline)-1
359
360
361
362
364 """Routines for writing C++ lines. Keeps track of brackets,
365 spaces, indentation and splitting of long lines"""
366
368 """Exception raised if an error occurs in the definition
369 or the execution of a CPPWriter."""
370 pass
371
372
373 standard_indent = 2
374 line_cont_indent = 4
375
376 indent_par_keywords = {'^if': standard_indent,
377 '^else if': standard_indent,
378 '^for': standard_indent,
379 '^while': standard_indent,
380 '^switch': standard_indent}
381 indent_single_keywords = {'^else': standard_indent}
382 indent_content_keywords = {'^class': standard_indent,
383 '^namespace': 0}
384 cont_indent_keywords = {'^case': standard_indent,
385 '^default': standard_indent,
386 '^public': standard_indent,
387 '^private': standard_indent,
388 '^protected': standard_indent}
389
390 spacing_patterns = [('\s*\"\s*}', '\"'),
391 ('\s*,\s*', ', '),
392 ('\s*-\s*', ' - '),
393 ('([{(,=])\s*-\s*', '\g<1> -'),
394 ('(return)\s*-\s*', '\g<1> -'),
395 ('\s*\+\s*', ' + '),
396 ('([{(,=])\s*\+\s*', '\g<1> +'),
397 ('\(\s*', '('),
398 ('\s*\)', ')'),
399 ('\{\s*', '{'),
400 ('\s*\}', '}'),
401 ('\s*=\s*', ' = '),
402 ('\s*>\s*', ' > '),
403 ('\s*<\s*', ' < '),
404 ('\s*!\s*', ' !'),
405 ('\s*/\s*', '/'),
406 ('\s*\*\s*', ' * '),
407 ('\s*-\s+-\s*', '-- '),
408 ('\s*\+\s+\+\s*', '++ '),
409 ('\s*-\s+=\s*', ' -= '),
410 ('\s*\+\s+=\s*', ' += '),
411 ('\s*\*\s+=\s*', ' *= '),
412 ('\s*/=\s*', ' /= '),
413 ('\s*>\s+>\s*', ' >> '),
414 ('<\s*double\s*>>\s*', '<double> > '),
415 ('\s*<\s+<\s*', ' << '),
416 ('\s*-\s+>\s*', '->'),
417 ('\s*=\s+=\s*', ' == '),
418 ('\s*!\s+=\s*', ' != '),
419 ('\s*>\s+=\s*', ' >= '),
420 ('\s*<\s+=\s*', ' <= '),
421 ('\s*&&\s*', ' && '),
422 ('\s*\|\|\s*', ' || '),
423 ('\s*{\s*}', ' {}'),
424 ('\s*;\s*', '; '),
425 (';\s*\}', ';}'),
426 (';\s*$}', ';'),
427 ('\s*<\s*([a-zA-Z0-9]+?)\s*>', '<\g<1>>'),
428 ('^#include\s*<\s*(.*?)\s*>', '#include <\g<1>>'),
429 ('(\d+\.{0,1}\d*|\.\d+)\s*[eE]\s*([+-]{0,1})\s*(\d+)',
430 '\g<1>e\g<2>\g<3>'),
431 ('\s+',' ')]
432 spacing_re = dict([(key[0], re.compile(key[0])) for key in \
433 spacing_patterns])
434
435 init_array_pattern = re.compile(r"=\s*\{.*\}")
436 short_clause_pattern = re.compile(r"\{.*\}")
437
438 comment_char = '//'
439 comment_pattern = re.compile(r"^(\s*#\s+|\s*//)")
440 start_comment_pattern = re.compile(r"^(\s*/\*)")
441 end_comment_pattern = re.compile(r"(\s*\*/)$")
442
443 quote_chars = re.compile(r"[^\\][\"\']|^[\"\']")
444 no_space_comment_patterns = re.compile(r"--|\*\*|==|\+\+")
445 line_length = 80
446 max_split = 40
447 split_characters = " "
448 comment_split_characters = " "
449
450
451 __indent = 0
452 __keyword_list = collections.deque()
453 __comment_ongoing = False
454
456 """Write a C++ line, with correct indent, spacing and line splits"""
457
458
459 assert(isinstance(line, str) and line.find('\n') == -1)
460
461 res_lines = []
462
463
464 if self.comment_pattern.search(line) or \
465 self.start_comment_pattern.search(line) or \
466 self.__comment_ongoing:
467
468 res_lines = self.write_comment_line(line.lstrip())
469 return res_lines
470
471
472
473
474 myline = line.lstrip()
475
476
477 if not myline:
478 return ["\n"]
479
480
481 if myline[0] == "{":
482
483 indent = self.__indent
484 key = ""
485 if self.__keyword_list:
486 key = self.__keyword_list[-1]
487 if key in self.indent_par_keywords:
488 indent = indent - self.indent_par_keywords[key]
489 elif key in self.indent_single_keywords:
490 indent = indent - self.indent_single_keywords[key]
491 elif key in self.indent_content_keywords:
492 indent = indent - self.indent_content_keywords[key]
493 else:
494
495 self.__indent = self.__indent + self.standard_indent
496
497 res_lines.append(" " * indent + "{" + "\n")
498
499 self.__keyword_list.append("{")
500 myline = myline[1:].lstrip()
501 if myline:
502
503 res_lines.extend(self.write_line(myline))
504 return res_lines
505
506
507 if myline[0] == "}":
508
509 if not self.__keyword_list:
510 raise self.CPPWriterError(\
511 'Non-matching } in C++ output: ' \
512 + myline)
513
514 if self.__keyword_list[-1] in self.cont_indent_keywords.keys():
515 key = self.__keyword_list.pop()
516 self.__indent = self.__indent - self.cont_indent_keywords[key]
517
518 if not self.__keyword_list.pop() == "{":
519 raise self.CPPWriterError(\
520 'Non-matching } in C++ output: ' \
521 + ",".join(self.__keyword_list) + myline)
522
523 key = ""
524 if self.__keyword_list:
525 key = self.__keyword_list[-1]
526 if key in self.indent_par_keywords:
527 self.__indent = self.__indent - \
528 self.indent_par_keywords[key]
529 self.__keyword_list.pop()
530 elif key in self.indent_single_keywords:
531 self.__indent = self.__indent - \
532 self.indent_single_keywords[key]
533 self.__keyword_list.pop()
534 elif key in self.indent_content_keywords:
535 self.__indent = self.__indent - \
536 self.indent_content_keywords[key]
537 self.__keyword_list.pop()
538 else:
539
540 self.__indent = self.__indent - self.standard_indent
541
542
543 breakline_index = 1
544 if len(myline) > 1:
545 if myline[1] == ";":
546 breakline_index = 2
547 elif myline[1:].lstrip()[:2] == "//":
548 if myline.endswith('\n'):
549 breakline_index = len(myline) - 1
550 else:
551 breakline_index = len(myline)
552 res_lines.append("\n".join(self.split_line(\
553 myline[:breakline_index],
554 self.split_characters)) + "\n")
555 myline = myline[breakline_index + 1:].lstrip()
556 if myline:
557
558 res_lines.extend(self.write_line(myline))
559 return res_lines
560
561
562 for key in self.indent_par_keywords.keys():
563 if re.search(key, myline):
564
565 parenstack = collections.deque()
566 for i, ch in enumerate(myline[len(key)-1:]):
567 if ch == '(':
568 parenstack.append(ch)
569 elif ch == ')':
570 try:
571 parenstack.pop()
572 except IndexError:
573
574 raise self.CPPWriterError(\
575 'Non-matching parenthesis in C++ output' \
576 + myline)
577 if not parenstack:
578
579 break
580 endparen_index = len(key) + i
581
582 res_lines.append("\n".join(self.split_line(\
583 myline[:endparen_index], \
584 self.split_characters)) + \
585 "\n")
586 myline = myline[endparen_index:].lstrip()
587
588 self.__keyword_list.append(key)
589 self.__indent = self.__indent + \
590 self.indent_par_keywords[key]
591 if myline:
592
593 res_lines.extend(self.write_line(myline))
594
595 return res_lines
596
597
598 for key in self.indent_single_keywords.keys():
599 if re.search(key, myline):
600 end_index = len(key) - 1
601
602 res_lines.append(" " * self.__indent + myline[:end_index] + \
603 "\n")
604 myline = myline[end_index:].lstrip()
605
606 self.__keyword_list.append(key)
607 self.__indent = self.__indent + \
608 self.indent_single_keywords[key]
609 if myline:
610
611 res_lines.extend(self.write_line(myline))
612
613 return res_lines
614
615
616 for key in self.indent_content_keywords.keys():
617 if re.search(key, myline):
618
619 if "{" in myline:
620 end_index = myline.index("{")
621 else:
622 end_index = len(myline)
623 res_lines.append("\n".join(self.split_line(\
624 myline[:end_index], \
625 self.split_characters)) + \
626 "\n")
627 myline = myline[end_index:].lstrip()
628
629 self.__keyword_list.append(key)
630 self.__indent = self.__indent + \
631 self.indent_content_keywords[key]
632 if myline:
633
634 res_lines.extend(self.write_line(myline))
635
636 return res_lines
637
638
639 for key in self.cont_indent_keywords.keys():
640 if re.search(key, myline):
641
642 if self.__keyword_list[-1] in self.cont_indent_keywords.keys():
643 self.__indent = self.__indent - \
644 self.cont_indent_keywords[\
645 self.__keyword_list.pop()]
646
647 res_lines.append("\n".join(self.split_line(myline, \
648 self.split_characters)) + \
649 "\n")
650
651 self.__keyword_list.append(key)
652 self.__indent = self.__indent + \
653 self.cont_indent_keywords[key]
654
655 return res_lines
656
657
658 if self.init_array_pattern.search(myline):
659 res_lines.append("\n".join(self.split_line(\
660 myline,
661 self.split_characters)) + \
662 "\n")
663 return res_lines
664
665
666 if self.short_clause_pattern.search(myline):
667 lines = self.split_line(myline,
668 self.split_characters)
669 if len(lines) == 1:
670 res_lines.append("\n".join(lines) + "\n")
671 return res_lines
672
673
674 if "{" in myline:
675 end_index = myline.index("{")
676 res_lines.append("\n".join(self.split_line(\
677 myline[:end_index], \
678 self.split_characters)) + \
679 "\n")
680 myline = myline[end_index:].lstrip()
681 if myline:
682
683 res_lines.extend(self.write_line(myline))
684 return res_lines
685
686
687 if "}" in myline:
688 end_index = myline.index("}")
689 res_lines.append("\n".join(self.split_line(\
690 myline[:end_index], \
691 self.split_characters)) + \
692 "\n")
693 myline = myline[end_index:].lstrip()
694 if myline:
695
696 res_lines.extend(self.write_line(myline))
697 return res_lines
698
699
700 res_lines.append("\n".join(self.split_line(myline, \
701 self.split_characters)) + "\n")
702
703
704 if self.__keyword_list:
705 if self.__keyword_list[-1] in self.indent_par_keywords:
706 self.__indent = self.__indent - \
707 self.indent_par_keywords[self.__keyword_list.pop()]
708 elif self.__keyword_list[-1] in self.indent_single_keywords:
709 self.__indent = self.__indent - \
710 self.indent_single_keywords[self.__keyword_list.pop()]
711 elif self.__keyword_list[-1] in self.indent_content_keywords:
712 self.__indent = self.__indent - \
713 self.indent_content_keywords[self.__keyword_list.pop()]
714
715 return res_lines
716
749
751 """Split a line if it is longer than self.line_length
752 columns. Split in preferential order according to
753 split_characters. Also fix spacing for line."""
754
755
756 comment = ""
757 if line.find(self.comment_char) > -1:
758 line, dum, comment = line.partition(self.comment_char)
759
760
761 quotes = self.quote_chars.finditer(line)
762
763 start_pos = 0
764 line_quotes = []
765 line_no_quotes = []
766 for i, quote in enumerate(quotes):
767 if i % 2 == 0:
768
769 line_no_quotes.append(line[start_pos:quote.start()])
770 start_pos = quote.start()
771 else:
772
773 line_quotes.append(line[start_pos:quote.end()])
774 start_pos = quote.end()
775
776 line_no_quotes.append(line[start_pos:])
777
778
779 line.rstrip()
780 for i, no_quote in enumerate(line_no_quotes):
781 for key in self.spacing_patterns:
782 no_quote = self.spacing_re[key[0]].sub(key[1], no_quote)
783 line_no_quotes[i] = no_quote
784
785
786 line = line_no_quotes[0]
787 for i in range(len(line_quotes)):
788 line += line_quotes[i]
789 if len(line_no_quotes) > i + 1:
790 line += line_no_quotes[i+1]
791
792
793 res_lines = [" " * self.__indent + line]
794
795 while len(res_lines[-1]) > self.line_length:
796 long_line = res_lines[-1]
797 split_at = -1
798 for character in split_characters:
799 index = long_line[(self.line_length - self.max_split): \
800 self.line_length].rfind(character)
801 if index >= 0:
802 split_at = self.line_length - self.max_split + index + 1
803 break
804
805
806 if split_at == -1:
807 split_at = len(long_line)
808 for character in split_characters:
809 split = long_line[self.line_length].find(character)
810 if split > 0:
811 split_at = min(split, split_at)
812 if split_at == len(long_line):
813 break
814
815
816 quotes = self.quote_chars.findall(long_line[:split_at])
817 if quotes and len(quotes) % 2 == 1:
818 quote_match = self.quote_chars.search(long_line[split_at:])
819 if not quote_match:
820 raise self.CPPWriterError(\
821 "Error: Unmatched quote in line " + long_line)
822 split_at = quote_match.end() + split_at + 1
823 split_match = re.search(self.split_characters,
824 long_line[split_at:])
825 if split_match:
826 split_at = split_at + split_match.start()
827 else:
828 split_at = len(long_line) + 1
829
830
831 if long_line[split_at:].lstrip():
832
833 res_lines[-1] = long_line[:split_at].rstrip()
834 res_lines.append(" " * \
835 (self.__indent + self.line_cont_indent) + \
836 long_line[split_at:].strip())
837 else:
838 break
839
840 if comment:
841 res_lines[-1] += " " + self.comment_char + comment
842
843 return res_lines
844
873
879
881
885
887 """Extends the regular file.writeline() function to write out
888 nicely formatted code"""
889
890 self.write(lines)
891