1"""
2Contains a simple codegen helper
3"""
4
5
6class CodeGen(object):
7    def __init__(self, indent="    ", cgen=False):
8        self.indent_lvl = 0
9        self.indent_tab = indent
10        self.data = []
11        self.cgen = cgen
12
13    def indent(self, lvl=1):
14        """
15        Indents the code one or more levels
16        """
17
18        self.indent_lvl += lvl
19
20    def dedent(self, lvl=1):
21        """
22        Dedents the code one or more levels
23        """
24
25        self.indent_lvl -= lvl
26        if self.indent_lvl < 0:
27            last_lines = "\n".join(self.data[-4:])
28            raise ValueError("Indent level is negative! Last lines:\n\n%s" % last_lines)
29
30    def write(self, line, endl=None):
31        """
32        Write a line with the current indent
33        """
34        shift = self.indent_lvl * self.indent_tab
35        if self.cgen and (endl is None) and ("//" not in line) and ("#" not in line):
36            endl = ";"
37        if endl is None:
38            endl = ""
39
40        self.data.append(shift + line + endl)
41
42    def blankline(self):
43        """
44        Inserts a blankline
45        """
46        self.data.append("")
47
48    def repr(self, filename=None, combine="\n", clang_format=False):
49        """
50        Combine the data into a single string, optionally write to file, and format.
51        """
52        tmp = combine.join(self.data)
53        if clang_format:
54            if self.cgen is False:
55                raise KeyError("clang_format is only valid for c generation.")
56            try:
57                tmp = run_clang_format(tmp)
58            except (OSError, AttributeError) as e:
59                print(str(e))
60
61        if filename is not None:
62            with open(filename, "w") as outfile:
63                outfile.write(tmp)
64        return tmp
65
66    def start_c_block(self, line=None):
67        """
68        Opens a C block with open brackets and indention
69        """
70
71        if self.cgen is False:
72            raise KeyError("Start c block only valid for c generation.")
73
74        if line is None:
75            self.write("{", endl="")
76        else:
77            self.write(line + " {", endl="")
78
79        self.indent()
80
81    def close_c_block(self):
82        """
83        Ends a c block with a dedent and close line
84        """
85        if self.cgen is False:
86            raise KeyError("Start c block only valid for c generation.")
87
88        self.dedent()
89        self.write("}", endl="")
90
91
92def run_clang_format(text):
93    import subprocess as sp
94    import shutil
95    import os
96
97    cf_path = None
98    try:
99        cf_path = shutil.which("clang-format")
100    except AttributeError:
101        # Python 3.2 or less
102        for path in os.environ["PATH"].split(":"):
103            path = os.path.join(path, "clang-format")
104            if os.path.exists(path):
105                cf_path = path
106                break
107
108    if cf_path is None:
109        return text
110
111    fname = "codegen.cf.tmp"
112
113    with open(fname, "w") as cfile:
114        cfile.write(text)
115
116    # Run and check output code
117    retcode = sp.call([cf_path, "-i", fname], stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE)
118    if retcode:
119        raise OSError("Clang-format failed, skipping.")
120
121    with open(fname, "r") as cfile:
122        text = cfile.read()
123
124    os.unlink(fname)
125    return text
126