Update Makefile to make it compile with recent pilex
[c_to_python.git] / y_to_python.py
1 #!/usr/bin/env python3
2
3 import t_def
4 import lex_yy
5 import os
6 import sys
7 import xml.etree.ElementTree
8 import y_tab
9
10 def get_text(root, i):
11   if i < 0:
12     i += len(root) + 1
13   text = root.text if i == 0 else root[i - 1].tail
14   return '' if text is None else text
15
16 def set_text(root, i, text):
17   if i < 0:
18     i += len(root) + 1
19   if len(text) == 0:
20     text = None
21   if i == 0:
22     root.text = text
23   else:
24     root[i - 1].tail = text
25
26 def to_text(root):
27   return ''.join(
28     [
29       j
30       for i in range(len(root))
31       for j in [get_text(root, i), to_text(root[i])]
32     ] +
33     [get_text(root, len(root))]
34   )
35
36 def c_to_python(context, text):
37   lex_yy.yyin = None
38   lex_yy.yy_buffer_stack = [lex_yy.YYBufferState()]
39   lex_yy.yytext_len = 0
40   lex_yy.unput(text)
41   root = y_tab.yyparse(t_def.AST.TranslationUnit)
42   context.lines = []
43   root.translate_translation_unit(context)
44   return ''.join(context.lines)
45
46 root = xml.etree.ElementTree.parse(sys.stdin).getroot()
47
48 context = t_def.Context()
49
50 actions = []
51 with open('a.c', 'w') as fout:
52   def extract(i):
53     if i.tag == 'AST_Section1_Prologue' or i.tag == 'AST_BracedCode':
54       node = i[0]
55       assert node.tag == 'AST_Text'
56       indent = '  '
57       initial = True
58       # we won't put code on same line as %{, though it's legal as input
59       set_text(
60         i,
61         0,
62         '{0:s}\n'.format(get_text(i, 0).rstrip())
63       )
64     elif (
65       i.tag == 'AST_Section1_InitialAction' or
66       i.tag == 'AST_Section1Or2_CodeProps'
67     ):
68       node = i[0]
69       assert node.tag == 'AST_Text'
70       indent = ''
71       initial = False
72     elif i.tag == 'AST_Production_Action':
73       node = i[0]
74       assert node.tag == 'AST_Text'
75       indent = '    '
76       initial = False
77     elif i.tag == 'AST_Section3':
78       node = i
79       indent = ''
80       initial = True
81     else:
82       if i.tag == 'AST':
83         set_text(
84           i,
85           0,
86           get_text(i, 0).lstrip()
87         )
88         for j in range(1, len(i)):
89           set_text(
90             i,
91             j,
92             '\n\n{0:s}\n\n'.format(get_text(i, j).strip())
93           )
94         set_text(
95           i,
96           len(i),
97           get_text(i, len(i)).lstrip()
98         )
99       elif i.tag == 'AST_Section2':
100         set_text(
101           i,
102           0,
103           get_text(i, 0).lstrip()
104         )
105         for j in range(1, len(i)):
106           set_text(
107             i,
108             j,
109             '\n\n{0:s}'.format(get_text(i, j).lstrip())
110           )
111         set_text(
112           i,
113           len(i),
114           get_text(i, len(i)).lstrip()
115         )
116       elif i.tag == 'AST_Section2_Rules':
117         set_text(
118           i,
119           0,
120           get_text(i, 0).lstrip()
121         )
122         # deal with <AST_SymbolRef><AST_ID>...</AST_ID> :</AST_SymbolRef>
123         assert i[0].tag == 'AST_SymbolRef'
124         set_text(
125           i[0],
126           1,
127           '\n  ' + get_text(i[0], 1).strip()
128         )
129         set_text(
130           i,
131           1,
132           get_text(i, 1).strip()
133         )
134         for j in range(2, len(i) + 1):
135           set_text(
136             i,
137             j,
138             '\n  {0:s}'.format(get_text(i, j).strip())
139           )
140       elif i.tag == 'AST_Production':
141         for j in range(len(i)):
142           set_text(
143             i,
144             j,
145             (
146               '\n    {0:s}'.format(get_text(i, j).lstrip())
147             if (
148               i[j].tag == 'AST_Production_Action' or
149               (j > 0 and i[j - 1].tag == 'AST_Production_Action')
150             ) else
151               ' {0:s}'.format(get_text(i, j).lstrip())
152             )
153           )
154         set_text(
155           i,
156           len(i),
157           get_text(i, len(i)).lstrip()
158         )
159       for j in i:
160         extract(j)
161       return
162     #assert len(node) == 0
163     #text = get_text(node, 0)
164     text = to_text(node)
165
166     lines = [i.rstrip() for i in text.split('\n')]
167     while len(lines) and len(lines[-1]) == 0:
168       del lines[-1]
169     while len(lines) and len(lines[0]) == 0:
170       del lines[0]
171     for line in lines:
172       temp = line.lstrip()
173       if (
174         (temp[:10] == '#include <' and temp[-3:] == '.h>') or
175         (temp[:10] == '#include "' and temp[-3:] == '.h"')
176       ):
177         fout.write(
178           '''@@@ IMPORT({0:s})
179 {1:s}
180 #undef NULL
181 #undef bool
182 #undef false
183 #undef true
184 @@@ IMPORT END\n'''.format(
185             temp[10:-3].replace('/', '.'),
186             temp
187           )
188         )
189       else:
190         fout.write(line + '\n')
191     fout.write('@@@\n')
192
193     actions.append((node, indent, initial))
194   extract(root)
195
196 os.system(
197   'gcc{0:s} -E a.c >a.i'.format(
198     ''.join([' "{0:s}"'.format(i) for i in sys.argv[1:]])
199   )
200 )
201 with open('a.i') as fin:
202   for node, indent, initial in actions:
203     lines = []
204     line = fin.readline()
205     while line != '@@@\n':
206       assert len(line)
207       if (
208         line[:1] == '#' or
209         (line == '\n' and len(lines) and lines[-1] == '\n')
210       ):
211         pass
212       elif line[:11] == '@@@ IMPORT(' and line[-2:] == ')\n':
213         # make the importing look like a function call in the C code:
214         #lines.append('import("{0:s}");\n'.format(line[11:-2]))
215         line = fin.readline()
216         while line != '@@@ IMPORT END\n':
217           assert len(line)
218           line = fin.readline()
219       else:
220         lines.append(line)
221       line = fin.readline()
222     text = ''.join(lines)
223
224     if initial:
225       context.indent = indent
226       text = c_to_python(context, text)
227     else:
228       context.indent = indent
229       text = c_to_python(
230         context,
231         'void a(void) {0:s}'.format(text) # already has braces and \n
232       )
233       assert text[:len(indent) + 9] == '{0:s}def a():\n'.format(indent)
234       text = '{{\n{0:s}{1:s}}}'.format(text[len(indent) + 9:], indent)
235     del node[:]
236     set_text(node, 0, text)
237
238 xml.etree.ElementTree.ElementTree(root).write(
239   sys.stdout,
240   encoding = 'unicode' # strangely does not seem to default to this
241 )