Fix several bugs to get Python scanner/parser basically working, processes ../tests...
[pilex.git] / generate_ast.py
1 #!/usr/bin/env python3
2
3 import re
4 import sys
5
6 if len(sys.argv) >= 2:
7   package_name = '{0:s}.'.format(sys.argv[1])
8 else:
9   package_name = ''
10
11 default_value = {
12   'bool': 'False',
13   'int': '-1',
14   'ref': 'None',
15   'str': '\'\'',
16   'list(bool)': '[]',
17   'list(int)': '[]',
18   'list(ref)': '[]',
19   'list(str)': '[]',
20   'set(bool)': 'set()',
21   'set(int)': 'set()',
22   'set(ref)': 'set()',
23   'set(str)': 'set()'
24 }
25 default_value_str = {
26   'bool': 'false',
27   'int': '-1',
28   'ref': '-1',
29   'str': ''
30 }
31
32 re_class = re.compile(
33   '([\t ]*)class ([A-Za-z_][A-Za-z0-9_]*)\(([A-Za-z_][A-Za-z0-9_.]*)'
34 )
35 re_element = re.compile(
36   '([\t ]*)# GENERATE ELEMENT\((([^()]|\([^()]*\))*)\)( BEGIN)?'
37 )
38 re_factory = re.compile(
39   '([\t ]*)# GENERATE FACTORY\(([^()]*)\)( BEGIN)?'
40 )
41 stack = []
42 classes = []
43 base_classes = [{'element.Element': []}] # params
44
45 line = sys.stdin.readline()
46 while len(line):
47   match = re_class.match(line)
48   if match is not None:
49     sys.stdout.write(line)
50     indent = match.group(1)
51     class_name = match.group(2)
52     base_class = match.group(3)
53     while len(stack) and stack[-1][0][:len(indent)] == indent:
54       _, temp_class_name, _, _ = stack.pop()
55       for temp_base_class, temp_fields in base_classes.pop().items():
56         base_classes[-1][
57           '{0:s}.{1:s}'.format(temp_class_name, temp_base_class)
58         ] = temp_fields
59     for i in range(len(base_classes) - 1, -1, -1):
60       if base_class in base_classes[i]:
61         classes.append(
62           '.'.join([j for _, j, _, _ in stack] + [class_name])
63         )
64         full_base_class = '.'.join(
65           [j for _, j, _, _ in stack[:i]] + [base_class]
66         )
67         base_classes[-1][class_name] = list(base_classes[i][base_class])
68         break
69     else:
70       full_base_class = base_class
71     stack.append((indent, class_name, base_class, full_base_class))
72     base_classes.append({})
73   else:
74     match = re_element.match(line)
75     if match is not None:
76       indent = match.group(1)
77       params = match.group(2)
78       begin = match.group(4)
79
80       while len(stack) and stack[-1][0][:len(indent)] == indent:
81         _, temp_class_name, _, _ = stack.pop()
82         for temp_base_class, temp_fields in base_classes.pop().items():
83           base_classes[-1][
84             '{0:s}.{1:s}'.format(temp_class_name, temp_base_class)
85           ] = temp_fields
86       _, class_name, base_class, full_base_class = stack[-1]
87  
88       fields = params.split(',')
89       if fields[-1] == '':
90         del fields[-1:]
91       fields = [i.split() for i in fields]
92       fields = [(type, name) for [type, name] in fields]
93       i = len(base_classes[-2][class_name])
94       base_classes[-2][class_name].extend(fields)
95
96       sys.stdout.write(
97         '''{0:s}# GENERATE ELEMENT({1:s}) BEGIN
98 {2:s}def __init__(
99 {3:s}  self,
100 {4:s}  tag = '{5:s}',
101 {6:s}  attrib = {{}},
102 {7:s}  text = '',
103 {8:s}  children = []{9:s}
104 {10:s}):
105 {11:s}  {12:s}.__init__(
106 {13:s}    self,
107 {14:s}    tag,
108 {15:s}    attrib,
109 {16:s}    text,
110 {17:s}    children{18:s}
111 {19:s}  )
112 '''.format(
113           indent,
114           params,
115           indent,
116           indent,
117           indent,
118           '_'.join([i for _, i, _, _ in stack]),
119           indent,
120           indent,
121           indent,
122           ''.join(
123             [
124               ',\n{0:s}  {1:s} = {2:s}'.format(
125                 indent,
126                 name,
127                 default_value[type]
128               )
129               for type, name in base_classes[-2][class_name]
130             ]
131           ),
132           indent,
133           indent,
134           full_base_class,
135           indent,
136           indent,
137           indent,
138           indent,
139           indent,
140           ''.join(
141             [
142               ',\n{0:s}    {1:s}'.format(
143                 indent,
144                 name
145               )
146               for type, name in base_classes[-2][class_name][:i]
147             ]
148           ),
149           indent
150         )
151       )
152       for type, name in fields:
153         if type == 'ref' or type == 'list(ref)' or type == 'set(ref)' or type == 'str':
154           sys.stdout.write(
155             '''{0:s}  self.{1:s} = {2:s}
156 '''.format(indent, name, name)
157           )
158         elif type[:5] == 'list(' and type[-1:] == ')':
159           subtype = type[5:-1]
160           sys.stdout.write(
161             '''{0:s}  self.{1:s} = (
162 {2:s}    [element.deserialize_{3:s}(i) for i in {4:s}.split()]
163 {5:s}  if isinstance({6:s}, str) else
164 {7:s}    {8:s}
165 {9:s}  )
166 '''.format(
167               indent,
168               name,
169               indent,
170               subtype,
171               name,
172               indent,
173               name,
174               indent,
175               name,
176               indent
177             )
178           )
179         elif type[:4] == 'set(' and type[-1:] == ')':
180           subtype = type[4:-1]
181           sys.stdout.write(
182             '''{0:s}  self.{1:s} = (
183 {2:s}    set([element.deserialize_{3:s}(i) for i in {4:s}.split()])
184 {5:s}  if isinstance({6:s}, str) else
185 {7:s}    {8:s}
186 {9:s}  )
187 '''.format(
188               indent,
189               name,
190               indent,
191               subtype,
192               name,
193               indent,
194               name,
195               indent,
196               name,
197               indent
198             )
199           )
200         else:
201           sys.stdout.write(
202             '''{0:s}  self.{1:s} = (
203 {2:s}    element.deserialize_{3:s}({4:s})
204 {5:s}  if isinstance({6:s}, str) else
205 {7:s}    {8:s}
206 {9:s}  )
207 '''.format(
208               indent,
209               name,
210               indent,
211               type,
212               name,
213               indent,
214               name,
215               indent,
216               name,
217               indent
218             )
219           )
220       if len(fields):
221         sys.stdout.write(
222           '''{0:s}def serialize(self, ref_list):
223 {1:s}  {2:s}.serialize(self, ref_list)
224 '''.format(indent, indent, full_base_class)
225         )
226         for type, name in fields:
227           if type[:5] == 'list(' and type[-1:] == ')':
228             subtype = type[5:-1]
229             sys.stdout.write(
230               '''{0:s}  self.set(
231 {1:s}    '{2:s}',
232 {3:s}    ' '.join([element.serialize_{4:s}(i{5:s}) for i in self.{6:s}])
233 {7:s}  )
234 '''.format(
235                 indent,
236                 indent,
237                 name,
238                 indent,
239                 subtype,
240                 ', ref_list' if subtype == 'ref' else '',
241                 name,
242                 indent
243               )
244             )
245           elif type[:4] == 'set(' and type[-1:] == ')':
246             subtype = type[4:-1]
247             sys.stdout.write(
248               '''{0:s}  self.set(
249 {1:s}    '{2:s}',
250 {3:s}    ' '.join([element.serialize_{4:s}(i{5:s}) for i in sorted(self.{6:s})])
251 {7:s}  )
252 '''.format(
253                 indent,
254                 indent,
255                 name,
256                 indent,
257                 subtype,
258                 ', ref_list' if subtype == 'ref' else '',
259                 name,
260                 indent
261               )
262             )
263           else:
264             sys.stdout.write(
265               '''{0:s}  self.set('{1:s}', element.serialize_{2:s}(self.{3:s}{4:s}))
266 '''.format(
267                 indent,
268                 name,
269                 type,
270                 name,
271                 ', ref_list' if type == 'ref' else ''
272               )
273             )
274         sys.stdout.write(
275           '''{0:s}def deserialize(self, ref_list):
276 {1:s}  {2:s}.deserialize(self, ref_list)
277 '''.format(indent, indent, full_base_class)
278         )
279         for type, name in fields:
280           if type[:5] == 'list(' and type[-1:] == ')':
281             subtype = type[5:-1]
282             sys.stdout.write(
283               '''{0:s}  self.{1:s} = [
284 {2:s}    element.deserialize_{3:s}(i{4:s})
285 {5:s}    for i in self.get('{6:s}', '').split()
286 {7:s}  ]
287 '''.format(
288                 indent,
289                 name,
290                 indent,
291                 subtype,
292                 ', ref_list' if subtype == 'ref' else '',
293                 indent,
294                 name,
295                 indent
296               )
297             )
298           elif type[:4] == 'set(' and type[-1:] == ')':
299             subtype = type[4:-1]
300             sys.stdout.write(
301               '''{0:s}  self.{1:s} = set(
302 {2:s}    [
303 {3:s}      element.deserialize_{4:s}(i{5:s})
304 {6:s}      for i in self.get('{7:s}', '').split()
305 {8:s}    ]
306 {9:s}  )
307 '''.format(
308                 indent,
309                 name,
310                 indent,
311                 indent,
312                 subtype,
313                 ', ref_list' if subtype == 'ref' else '',
314                 indent,
315                 name,
316                 indent,
317                 indent
318               )
319             )
320           else: 
321             sys.stdout.write(
322               '''{0:s}  self.{1:s} = element.deserialize_{2:s}(self.get('{3:s}', '{4:s}'){5:s})
323 '''.format(
324                 indent,
325                 name,
326                 type,
327                 name,
328                 default_value_str[type],
329                 ', ref_list' if type == 'ref' else ''
330               )
331             )
332       sys.stdout.write(
333         '''{0:s}def copy(self, factory = None):
334 {1:s}  result = {2:s}.copy(
335 {3:s}    self,
336 {4:s}    {5:s} if factory is None else factory
337 {6:s}  ){7:s}
338 {8:s}  return result
339 '''.format(
340           indent,
341           indent,
342           full_base_class,
343           indent,
344           indent,
345           class_name,
346           indent,
347           ''.join(
348             [
349               '\n{0:s}  result.{1:s} = self.{2:s}'.format(
350                 indent,
351                 name,
352                 name
353               )
354               for _, name in fields
355             ]
356           ),
357           indent
358         )
359       )
360       if len(fields):
361         sys.stdout.write(
362           '''{0:s}def repr_serialize(self, params):
363 {1:s}  {2:s}.repr_serialize(self, params)
364 '''.format(
365             indent,
366             indent,
367             full_base_class
368           )
369         )
370         for type, name in fields:
371           if type[:5] == 'list(' and type[-1:] == ')':
372             subtype = type[5:-1]
373             sys.stdout.write(
374               '''{0:s}  if len(self.{1:s}):
375 {2:s}    params.append(
376 {3:s}      '{4:s} = [{{0:s}}]'.format(
377 {5:s}        ', '.join([repr(i) for i in self.{6:s}])
378 {7:s}      )
379 {8:s}    )
380 '''.format(
381                 indent,
382                 name,
383                 indent,
384                 indent,
385                 name,
386                 indent,
387                 name,
388                 indent,
389                 indent
390               )
391             )
392           elif type[:4] == 'set(' and type[-1:] == ')':
393             subtype = type[4:-1]
394             sys.stdout.write(
395               '''{0:s}  if len(self.{1:s}):
396 {2:s}    params.append(
397 {3:s}      '{4:s} = set([{{0:s}}])'.format(
398 {5:s}        ', '.join([repr(i) for i in sorted(self.{6:s})])
399 {7:s}      )
400 {8:s}    )
401 '''.format(
402                 indent,
403                 name,
404                 indent,
405                 indent,
406                 name,
407                 indent,
408                 name,
409                 indent,
410                 indent
411               )
412             )
413           else:
414             sys.stdout.write(
415               '''{0:s}  if self.{1:s} != {2:s}:
416 {3:s}    params.append(
417 {4:s}      '{5:s} = {{0:s}}'.format(repr(self.{6:s}))
418 {7:s}    )
419 '''.format(
420                 indent,
421                 name,
422                 default_value[type],
423                 indent,
424                 indent,
425                 name,
426                 name,
427                 indent
428               )
429             )
430       sys.stdout.write(
431         '''{0:s}def __repr__(self):
432 {1:s}  params = []
433 {2:s}  self.repr_serialize(params)
434 {3:s}  return '{4:s}{5:s}({{0:s}})'.format(', '.join(params))
435 {6:s}# GENERATE END
436 '''.format(
437           indent,
438           indent,
439           indent,
440           indent,
441           package_name,
442           '.'.join([i for _, i, _, _ in stack]),
443           indent
444         )
445       )
446       if begin is not None:
447         line = sys.stdin.readline()
448         while len(line):
449           if line.strip() == '# GENERATE END':
450             break
451           line = sys.stdin.readline()
452         else:
453           assert False
454     else:
455       match = re_factory.match(line)
456       if match is not None:
457         indent = match.group(1)
458         param = match.group(2)
459         begin = match.group(3)
460
461         sys.stdout.write(
462           '''{0:s}# GENERATE FACTORY({1:s}) BEGIN
463 {2:s}tag_to_class = {{{3:s}
464 {4:s}}}
465 {5:s}def factory(tag, attrib = {{}}, *args, **kwargs):
466 {6:s}  return tag_to_class.get(tag, {7:s})(tag, attrib, *args, **kwargs)
467 {8:s}# GENERATE END
468 '''.format(
469             indent,
470             param,
471             indent,
472             ','.join(
473               [
474                 '\n{0:s}  \'{1:s}\': {2:s}'.format(
475                   indent,
476                   i.replace('.', '_'),
477                   i
478                 )
479                 for i in classes
480               ]
481             ),
482             indent,
483             indent,
484             indent,
485             param,
486             indent
487           )
488         )
489
490         if begin is not None:
491           line = sys.stdin.readline()
492           while len(line):
493             if line.strip() == '# GENERATE END':
494               break
495             line = sys.stdin.readline()
496           else:
497             assert False
498       else:
499         sys.stdout.write(line)
500   line = sys.stdin.readline()