Add support for reading and writing SPV files.
[pspp] / src / output / spv / binary-parser-generator
1 #! /usr/bin/python
2
3 import getopt
4 import os
5 import struct
6 import sys
7
8 n_errors = 0
9
10 def error(msg):
11     global n_errors
12     sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg))
13     n_errors += 1
14
15
16 def fatal(msg):
17     error(msg)
18     sys.exit(1)
19
20
21 def get_line():
22     global line
23     global line_number
24     line = input_file.readline()
25     line_number += 1
26
27
28 def is_num(s):
29     return s.isdigit() or (s[0] == '-' and s[1].isdigit())
30
31
32 xdigits = "0123456789abcdefABCDEF"
33 def is_xdigits(s):
34     for c in s:
35         if c not in xdigits:
36             return False
37     return True
38
39
40 def expect(type):
41     if token[0] != type:
42         fatal("syntax error expecting %s" % type)
43
44
45 def match(type):
46     if token[0] == type:
47         get_token()
48         return True
49     else:
50         return False
51
52
53 def must_match(type):
54     expect(type)
55     get_token()
56
57
58 def get_token():
59     global token
60     global line
61     prev = token
62     if line == "":
63         if token == ('eof', ):
64             fatal("unexpected end of input")
65         get_line()
66         if not line:
67             token = ('eof', )
68             return
69         elif line == '\n':
70             token = (';', )
71             return
72         elif not line[0].isspace():
73             token = (';', )
74             return
75
76     line = line.lstrip()
77     if line == "":
78         get_token()
79     elif line[0] == '#':
80         line = ''
81         get_token()
82     elif line[0] in '[]()?|*':
83         token = (line[0], )
84         line = line[1:]
85     elif line.startswith('=>'):
86         token = (line[:2], )
87         line = line[2:]
88     elif line.startswith('...'):
89         token = (line[:3], )
90         line = line[3:]
91     elif line[0].isalnum() or line[0] == '-':
92         n = 1
93         while n < len(line) and (line[n].isalnum() or line[n] == '-'):
94             n += 1
95         s = line[:n]
96         line = line[n:]
97
98         if prev[0] == '*' and is_num(s):
99             token = ('number', int(s, 10))
100         elif len(s) == 2 and is_xdigits(s):
101             token = ('bytes', struct.pack('B', int(s, 16)))
102         elif s[0] == 'i' and is_num(s[1:]):
103             token = ('bytes', struct.pack('<i', int(s[1:])))
104         elif s[:2] == 'ib' and is_num(s[2:]):
105             token = ('bytes', struct.pack('>i', int(s[2:])))
106         elif s[0].isupper():
107             token = ('nonterminal', s)
108         elif s in ('bool', 'int16', 'int32', 'int64', 'be16', 'be32', 'be64',
109                    'string', 'bestring', 'byte', 'float', 'double',
110                    'count', 'becount', 'v1', 'v3', 'vAF', 'vB0',
111                    'case', 'else'):
112             token = (s, )
113         else:
114             token = ('id', s)
115     else:
116         fatal("unknown character %c" % line[0])
117
118
119 def usage():
120     argv0 = os.path.basename(sys.argv[0])
121     print('''\
122 %(argv0)s, parser generator for SPV binary members
123 usage: %(argv0)s GRAMMAR header
124        %(argv0)s GRAMMAR code HEADER_NAME
125   where GRAMMAR contains grammar definitions\
126 ''' % {"argv0": argv0})
127     sys.exit(0)
128
129
130 class Item(object):
131     def __init__(self, type_, name, n, content):
132         self.type_ = type_
133         self.name = name
134         self.n = n
135         self.content = content
136     def __repr__(self):
137         if self.type_ == 'constant':
138             return ' '.join(['%02x' % ord(x) for x in self.content])
139         elif self.content:
140             return "%s(%s)" % (self.type_, self.content)
141         else:
142             return self.type_
143
144 def parse_item():
145     t = token
146     name = None
147     if t[0] == 'bytes':
148         type_ = 'constant'
149         content = t[1]
150         get_token()
151     elif t[0] in ('bool', 'byte',
152                   'int16', 'int32', 'int64',
153                   'be16', 'be32', 'be64',
154                   'string', 'bestring',
155                   'float', 'double',
156                   'nonterminal', '...'):
157         type_ = 'variable'
158         content = t
159         get_token()
160         if t[0] == 'nonterminal':
161             name = name_to_id(content[1])
162     elif t[0] in ('v1', 'v3', 'vAF', 'vB0', 'count', 'becount'):
163         type_ = t[0]
164         get_token()
165         must_match('(')
166         content = parse_choice()
167         must_match(')')
168     elif match('case'):
169         return parse_case()
170     elif match('('):
171         type_ = '()'
172         content = parse_choice()
173         must_match(')')
174     else:
175         print token
176         fatal('syntax error expecting item')
177
178     n = 1
179     optional = False
180     if match('*'):
181         if token[0] == 'number':
182             n = token[1]
183             get_token()
184         elif match('['):
185             expect('id')
186             n = token[1]
187             get_token()
188             must_match(']')
189             if n.startswith('n-'):
190                 name = n[2:]
191         else:
192             fatal('expecting quantity')
193     elif match('?'):
194         optional = True
195
196     if match('['):
197         expect('id')
198         if type_ == 'constant' and not optional:
199             fatal("%s: cannot name a constant" % token[1])
200
201         name = token[1]
202         get_token()
203         must_match(']')
204
205     if type_ == 'constant':
206         content *= n
207         n = 1
208
209     item = Item(type_, name, n, content)
210     if optional:
211         item = Item('|', None, 1, [[item], []])
212     return item
213
214
215 def parse_concatenation():
216     items = []
217     while token[0] not in (')', ';', '|', 'eof'):
218         item = parse_item()
219         if (item.type_ == 'constant'
220             and items
221             and items[-1].type_ == 'constant'):
222             items[-1].content += item.content
223         else:
224             items.append(item)
225     return items
226
227
228 def parse_choice():
229     sub = parse_concatenation()
230     if token[0] != '|':
231         return sub
232
233     choices = [sub]
234     while match('|'):
235         choices.append(parse_concatenation())
236
237     return [Item('|', None, 1, choices)]
238
239
240 def parse_case():
241     must_match('(')
242     choices = {}
243     while True:
244         choice = None
245         if match('else'):
246             choice = 'else'
247
248         items = parse_concatenation()
249         if choice is None:
250             if (not items
251                 or items[0].type_ != 'constant'
252                 or len(items[0].content) != 1):
253                 fatal("choice must begin with xx (or 'else')")
254             choice = '%02x' % ord(items[0].content)
255
256         if choice in choices:
257             fatal("duplicate choice %s" % choice)
258         choices[choice] = items
259
260         if match(')'):
261             break
262         must_match('|')
263
264     case_name = None
265     if match('['):
266         expect('id')
267         case_name = token[1]
268         get_token()
269         must_match(']')
270
271     return Item('case', case_name, 1,
272                 { '%s_%s' % (case_name, k) : v for k, v in choices.items() })
273
274
275 def parse_production():
276     expect('nonterminal')
277     name = token[1]
278     get_token()
279     must_match('=>')
280     return name, parse_choice()
281
282
283 def print_members(p, indent):
284     for item in p:
285         if item.type_ == 'variable' and item.name:
286             if item.content[0] == 'nonterminal':
287                 typename = 'struct %s%s' % (prefix,
288                                             name_to_id(item.content[1]))
289                 n_stars = 1
290             else:
291                 c_types = {'bool': ('bool', 0),
292                            'byte': ('uint8_t', 0),
293                            'int16': ('uint16_t', 0),
294                            'int32': ('uint32_t', 0),
295                            'int64': ('uint64_t', 0),
296                            'be16': ('uint16_t', 0),
297                            'be32': ('uint32_t', 0),
298                            'be64': ('uint64_t', 0),
299                            'string': ('char', 1),
300                            'bestring': ('char', 1),
301                            'float': ('double', 0),
302                            'double': ('double', 0),
303                            '...': ('uint8_t', 1)}
304                 typename, n_stars = c_types[item.content[0]]
305
306             array_suffix = ''
307             if item.n:
308                 if isinstance(item.n, int):
309                     if item.n > 1:
310                         array_suffix = '[%d]' % item.n
311                 else:
312                     n_stars += 1
313             
314             print "%s%s %s%s%s;" % (indent, typename, '*' * n_stars,
315                                     name_to_id(item.name),
316                                     array_suffix)
317         elif item.type_ in ('v1', 'v3', 'vAF', 'vB0',
318                             'count', 'becount', '()'):
319             print_members(item.content, indent)
320         elif item.type_ == '|':
321             for choice in item.content:
322                 print_members(choice, indent)
323         elif item.type_ == 'case':
324             print "%sint %s;" % (indent, item.name)
325             print "%sunion {" % indent
326             for name, choice in sorted(item.content.items()):
327                 print "%s    struct {" % indent
328                 print_members(choice, indent + ' ' * 8)
329                 print "%s    } %s;" % (indent, name)
330             print "%s};" % indent
331         elif item.type_ == 'constant':
332             if item.name:
333                 print "%sbool %s;" % (indent, item.name)
334         elif item.type_ not in ("constant", "variable"):
335             fatal("unhandled type %s" % item.type_)
336
337
338 def bytes_to_hex(s):
339     return ''.join(['"'] + ["\\x%02x" % ord(x) for x in s] + ['"'])
340
341
342 class Parser_Context(object):
343     def __init__(self):
344         self.suffixes = {}
345         self.bail = 'error'
346         self.need_error_handler = False
347     def gen_name(self, prefix):
348         n = self.suffixes.get(prefix, 0) + 1
349         self.suffixes[prefix] = n
350         return '%s%d' % (prefix, n) if n > 1 else prefix
351     def save_pos(self, indent):
352         pos = self.gen_name('pos')
353         print "%sstruct spvbin_position %s = spvbin_position_save (input);" % (indent, pos)
354         return pos
355     def save_error(self, indent):
356         error = self.gen_name('save_n_errors')
357         print "%ssize_t %s = input->n_errors;" % (indent, error)
358         return error
359     def parse_limit(self, endian, indent):
360         limit = self.gen_name('saved_limit')
361         print """\
362 %sstruct spvbin_limit %s;
363 %sif (!spvbin_limit_parse%s (&%s, input))
364 %s    goto %s;""" % (
365     indent, limit,
366     indent, '_be' if endian == 'big' else '', limit,
367     indent, self.bail)
368         return limit
369         
370
371 def print_parser_items(name, production, indent, accessor, ctx):
372     for item_idx in range(len(production)):
373         if item_idx > 0:
374             print
375
376         item = production[item_idx]
377         if item.type_ == 'constant':
378             print """%sif (!spvbin_match_bytes (input, %s, %d))
379 %s    goto %s;""" % (
380                 indent, bytes_to_hex(item.content), len(item.content),
381                 indent, ctx.bail)
382             ctx.need_error_handler = True
383             if item.name:
384                 print "%sp->%s = true;" % (indent, item.name)
385         elif item.type_ == 'variable':
386             if item.content[0] == 'nonterminal':
387                 func = '%sparse_%s' % (prefix, name_to_id(item.content[1]))
388             else:
389                 func = 'spvbin_parse_%s' % item.content[0]
390
391             if item.name:
392                 dst = "&p->%s%s" % (accessor, name_to_id(item.name))
393             else:
394                 dst = "NULL"
395             if item.n == 1:
396                 print """%sif (!%s (input, %s))
397 %s    goto %s;""" % (indent, func, dst,
398                      indent, ctx.bail)
399
400                 if item.content[0] != 'nonterminal' and item.name == 'version':
401                     print "%sinput->version = p->%s%s;" % (
402                         indent, accessor, name_to_id(item.name))
403             else:
404                 if isinstance(item.n, int):
405                     count = item.n
406                 else:
407                     count = 'p->%s%s' % (accessor, name_to_id(item.n))
408
409                 i_name = ctx.gen_name('i')
410                 if item.name:
411                     if not isinstance(item.n, int):
412                         print "%sp->%s%s = xcalloc (%s, sizeof *p->%s%s);" % (
413                             indent,
414                             accessor, name_to_id(item.name), count,
415                             accessor, name_to_id(item.name))
416                     dst += '[%s]' % i_name
417                 print "%sfor (int %s = 0; %s < %s; %s++)" % (
418                     indent, i_name, i_name, count, i_name)
419                 print """%s    if (!%s (input, %s))
420 %s        goto %s;""" % (indent, func, dst,
421                      indent, ctx.bail)
422
423             ctx.need_error_handler = True
424         elif item.type_ == '()':
425             if item.n != 1:
426                 # Not yet implemented
427                 raise AssertionError
428
429             print_parser_items(name, item.content, indent, accessor, ctx)
430         elif item.type_ in  ('v1', 'v3', 'vAF', 'vB0'):
431             if item.n != 1:
432                 # Not yet implemented
433                 raise AssertionError
434
435             print "%sif (input->version == 0x%s) {" % (indent, item.type_[1:])
436             print_parser_items(name, item.content, indent + '    ', accessor, ctx)
437             print "%s}" % indent
438         elif item.type_ in ('count', 'becount'):
439             if item.n != 1:
440                 # Not yet implemented
441                 raise AssertionError
442
443             pos = ctx.save_pos(indent)
444             endian = 'big' if item.type_ == 'becount' else 'little'
445             limit = ctx.parse_limit(endian, indent)
446
447             save_bail = ctx.bail
448             ctx.bail = ctx.gen_name('backtrack')
449
450             print "%sdo {" % indent
451             indent += '    '
452             if (item.content
453                 and item.content[-1].type_ == 'variable'
454                 and item.content[-1].content[0] == '...'):
455                 content = item.content[:-1]
456                 ellipsis = True
457             else:
458                 content = item.content
459                 ellipsis = False
460             print_parser_items(name, content, indent, accessor, ctx)
461
462             if ellipsis:
463                 print "%sinput->ofs = input->size;" % indent
464             else:
465                 print """%sif (!spvbin_input_at_end (input))
466 %s    goto %s;""" % (indent,
467                      indent, ctx.bail)
468             print '%sspvbin_limit_pop (&%s, input);' % (indent, limit)
469             print '%sbreak;' % indent
470             print
471             print '%s%s:' % (indent[4:], ctx.bail)
472             # In theory, we should emit code to clear whatever we're
473             # backtracking from.  In practice, it's not important to
474             # do that.
475             print "%sspvbin_position_restore (&%s, input);" % (indent, pos)
476             print '%sspvbin_limit_pop (&%s, input);' % (indent, limit)
477             print '%sgoto %s;' % (indent, save_bail)
478             indent = indent[4:]
479             print "%s} while (0);" % indent
480
481             ctx.bail = save_bail
482         elif item.type_ == '|':
483             save_bail = ctx.bail
484
485             print "%sdo {" % indent
486             indent += '    '
487             pos = ctx.save_pos(indent)
488             error = ctx.save_error(indent)
489             i = 0
490             for choice in item.content:
491                 if i:
492                     print "%sspvbin_position_restore (&%s, input);" % (indent, pos)
493                     print "%sinput->n_errors = %s;" % (indent, error)
494                 i += 1
495
496                 if i != len(item.content):
497                     ctx.bail = ctx.gen_name('backtrack')
498                 else:
499                     ctx.bail = save_bail
500                 print_parser_items(name, choice, indent, accessor, ctx)
501                 print "%sbreak;" % indent
502                 if i != len(item.content):
503                     print
504                     print '%s%s:' % (indent[4:], ctx.bail)
505                     # In theory, we should emit code to clear whatever we're
506                     # backtracking from.  In practice, it's not important to
507                     # do that.
508             indent = indent[4:]
509             print "%s} while (0);" % indent
510         elif item.type_ == 'case':
511             i = 0
512             for choice_name, choice in sorted(item.content.items()):
513                 if choice_name.endswith('else'):
514                     print "%s} else {" % indent
515                     print "%s    p->%s%s = -1;" % (indent, accessor, item.name)
516                     print
517                 else:
518                     print "%s%sif (spvbin_match_byte (input, 0x%s)) {" % (
519                         indent, '} else ' if i else '', choice_name[-2:])
520                     print "%s    p->%s%s = 0x%s;" % (
521                         indent, accessor, item.name, choice_name[-2:])
522                     print
523                     choice = choice[1:]
524                 
525                 print_parser_items(name, choice, indent + '    ',
526                                    accessor + choice_name + '.', ctx)
527                 i += 1
528             print "%s}" % indent
529         else:
530             # Not implemented
531             raise AssertionError
532
533
534 def print_parser(name, production, indent):
535     print '''
536 bool
537 %(prefix)sparse_%(name)s (struct spvbin_input *input, struct %(prefix)s%(name)s **p_)
538 {
539     *p_ = NULL;
540     struct %(prefix)s%(name)s *p = xzalloc (sizeof *p);
541     p->start = input->ofs;
542 ''' % {'prefix': prefix,
543        'name': name_to_id(name)}
544
545     ctx = Parser_Context()
546     print_parser_items(name, production, indent, '', ctx)
547
548     print '''
549     p->len = input->ofs - p->start;
550     *p_ = p;
551     return true;'''
552
553     if ctx.need_error_handler:
554         print """
555 error:
556     spvbin_error (input, "%s", p->start);
557     %sfree_%s (p);
558     return false;""" % (name, prefix, name_to_id(name))
559
560     print "}"
561
562 def print_free_items(name, production, indent, accessor, ctx):
563     for item in production:
564         if item.type_ == 'constant':
565             pass
566         elif item.type_ == 'variable':
567             if not item.name:
568                 continue
569
570             if item.content[0] == 'nonterminal':
571                 free_func = '%sfree_%s' % (prefix, name_to_id(item.content[1]))
572             elif item.content[0] in ('string', 'bestring', '...'):
573                 free_func = 'free'
574             else:
575                 free_func = None
576
577             dst = "p->%s%s" % (accessor, name_to_id(item.name))
578
579             if item.n == 1:
580                 if free_func:
581                     print "%s%s (%s);" % (indent, free_func, dst)
582             else:
583                 if isinstance(item.n, int):
584                     count = item.n
585                 else:
586                     count = 'p->%s%s' % (accessor, name_to_id(item.n))
587
588                 i_name = ctx.gen_name('i')
589                 if free_func:
590                     print "%sfor (int %s = 0; %s < %s; %s++)" % (
591                         indent, i_name, i_name, count, i_name)
592                     print "%s    %s (%s[%s]);" % (
593                         indent, free_func, dst, i_name)
594                 if not isinstance(item.n, int):
595                     print "%sfree (p->%s%s);" % (
596                         indent, accessor, name_to_id(item.name))
597         elif item.type_ in ('()', 'v1', 'v3', 'vAF', 'vB0',
598                             'count', 'becount'):
599             if item.n != 1:
600                 # Not yet implemented
601                 raise AssertionError
602
603             print_free_items(name, item.content, indent, accessor, ctx)
604         elif item.type_ == '|':
605             for choice in item.content:
606                 print_free_items(name, choice, indent, accessor, ctx)
607         elif item.type_ == 'case':
608             i = 0
609             for choice_name, choice in sorted(item.content.items()):
610                 if choice_name.endswith('else'):
611                     value_name = '-1'
612                 else:
613                     value_name = '0x%s' % choice_name[-2:]
614
615                 print '%s%sif (p->%s%s == %s) {' % (
616                     indent, '} else ' if i else '', accessor, item.name,
617                     value_name)
618                 
619                 print_free_items(name, choice, indent + '    ',
620                                  accessor + choice_name + '.', ctx)
621                 i += 1
622             print "%s}" % indent
623         else:
624             # Not implemented
625             raise AssertionError
626
627 def print_free(name, production, indent):
628     print '''
629 void
630 %(prefix)sfree_%(name)s (struct %(prefix)s%(name)s *p)
631 {
632     if (p == NULL)
633         return;
634 ''' % {'prefix': prefix,
635        'name': name_to_id(name)}
636
637     print_free_items(name, production, indent, '', Parser_Context())
638
639     print "    free (p);"
640     print "}"
641
642 def print_print_items(name, production, indent, accessor, ctx):
643     for item_idx in range(len(production)):
644         if item_idx > 0:
645             print
646
647         item = production[item_idx]
648         if item.type_ == 'constant':
649             if item.name:
650                 print '%sspvbin_print_presence ("%s", indent + 1, p->%s);' % (
651                     indent, item.name, item.name)
652         elif item.type_ == 'variable':
653             if not item.name:
654                 continue
655
656             if item.content[0] == 'nonterminal':
657                 func = '%sprint_%s' % (prefix, name_to_id(item.content[1]))
658             else:
659                 c_types = {'bool': 'bool',
660                            'byte': 'byte',
661                            'int16': 'int16',
662                            'int32': 'int32',
663                            'int64': 'int64',
664                            'be16': 'int16',
665                            'be32': 'int32',
666                            'be64': 'int64',
667                            'string': 'string',
668                            'bestring': 'string',
669                            'float': 'double',
670                            'double': 'double',
671                            '...': ('uint8_t', 1)}
672                 func = 'spvbin_print_%s' % c_types[item.content[0]]
673
674             dst = "p->%s%s" % (accessor, name_to_id(item.name))
675             if item.n == 1:
676                 print '%s%s ("%s", indent + 1, %s);' % (indent, func,
677                                                       item.name, dst)
678             else:
679                 if isinstance(item.n, int):
680                     count = item.n
681                 else:
682                     count = 'p->%s%s' % (accessor, name_to_id(item.n))
683
684                 i_name = ctx.gen_name('i')
685                 elem_name = ctx.gen_name('elem_name')
686                 dst += '[%s]' % i_name
687                 print """\
688 %(indent)sfor (int %(index)s = 0; %(index)s < %(count)s; %(index)s++) {
689 %(indent)s    char *%(elem_name)s = xasprintf ("%(item.name)s[%%d]", %(index)s);
690 %(indent)s    %(func)s (%(elem_name)s, indent + 1, %(dst)s);
691 %(indent)s    free (%(elem_name)s);
692 %(indent)s}""" % {'indent': indent,
693                   'index': i_name,
694                   'count': count,
695                   'elem_name' : elem_name,
696                   'item.name': item.name,
697                   'func': func,
698                   'dst': dst}
699         elif item.type_ == '()':
700             if item.n != 1:
701                 # Not yet implemented
702                 raise AssertionError
703
704             print_print_items(name, item.content, indent, accessor, ctx)
705         elif item.type_ in  ('v1', 'v3', 'vAF', 'vB0'):
706             if item.n != 1:
707                 # Not yet implemented
708                 raise AssertionError
709
710             print_print_items(name, item.content, indent, accessor, ctx)
711         elif item.type_ in ('count', 'becount'):
712             if item.n != 1:
713                 # Not yet implemented
714                 raise AssertionError
715
716             indent += '    '
717             if (item.content
718                 and item.content[-1].type_ == 'variable'
719                 and item.content[-1].content[0] == '...'):
720                 content = item.content[:-1]
721             else:
722                 content = item.content
723             print_print_items(name, content, indent, accessor, ctx)
724         elif item.type_ == '|':
725             for choice in item.content:
726                 print_print_items(name, choice, indent, accessor, ctx)
727         elif item.type_ == 'case':
728             i = 0
729             print """\
730 %sspvbin_print_case ("%s", indent + 1, p->%s%s);""" % (
731     indent, item.name, accessor, name_to_id(item.name))
732             for choice_name, choice in sorted(item.content.items()):
733                 if choice_name.endswith('else'):
734                     value_name = '-1'
735                 else:
736                     value_name = '0x%s' % choice_name[-2:]
737
738                 print '%s%sif (p->%s%s == %s) {' % (
739                     indent, '} else ' if i else '', accessor, item.name,
740                     value_name)
741                 
742                 print_print_items(name, choice, indent + '    ',
743                                   accessor + choice_name + '.', ctx)
744                 i += 1
745             print "%s}" % indent
746         else:
747             # Not implemented
748             raise AssertionError
749
750
751 def print_print(name, production, indent):
752     print '''
753 void
754 %(prefix)sprint_%(name)s (const char *title, int indent, const struct %(prefix)s%(name)s *p)
755 {
756     spvbin_print_header (title, p ? p->start : -1, p ? p->len : -1, indent);
757     if (p == NULL) {
758         printf ("none\\n");
759         return;
760     }
761     putchar ('\\n');
762 ''' % {'prefix': prefix,
763        'rawname': name,
764        'name': name_to_id(name)}
765
766     ctx = Parser_Context()
767     print_print_items(name, production, indent, '', ctx)
768
769     print "}"
770
771 def name_to_id(s):
772     return s[0].lower() + ''.join(['_%c' % x.lower() if x.isupper() else x
773                                    for x in s[1:]]).replace('-', '_')
774     
775
776 if __name__ == "__main__":
777     argv0 = sys.argv[0]
778     try:
779         options, args = getopt.gnu_getopt(sys.argv[1:], 'h', ['help'])
780     except getopt.GetoptError as e:
781         sys.stderr.write("%s: %s\n" % (argv0, e.msg))
782         sys.exit(1)
783
784     for key, value in options:
785         if key in ['-h', '--help']:
786             usage()
787         else:
788             sys.exit(0)
789
790     if len(args) < 3:
791         sys.stderr.write("%s: bad usage (use --help for help)\n" % argv0)
792         sys.exit(1)
793
794     global file_name
795     file_name, output_type, prefix = args[:3]
796     input_file = open(file_name)
797
798     prefix = '%s_' % prefix
799
800     global line
801     global line_number
802     line = ""
803     line_number = 0
804
805     productions = {}
806
807     global token
808     token = ('start', )
809     get_token()
810     while True:
811         while match(';'):
812             pass
813         if token[0] == 'eof':
814             break
815
816         name, production = parse_production()
817         if name in productions:
818             fatal("%s: duplicate production" % name)
819         productions[name] = production
820
821     print '/* Generated automatically -- do not modify!    -*- buffer-read-only: t -*- */'
822     if output_type == 'code' and len(args) == 4:
823         header_name = args[3]
824
825         print """\
826 #include <config.h>
827 #include %s
828 #include <stdio.h>
829 #include <stdlib.h>
830 #include "libpspp/str.h"
831 #include "gl/xalloc.h"\
832 """ % header_name
833         for name, production in productions.items():
834             print_parser(name, production, ' ' * 4)
835             print_free(name, production, ' ' * 4)
836             print_print(name, production, ' ' * 4)
837     elif output_type == 'header' and len(args) == 3:
838         print """\
839 #ifndef %(PREFIX)sPARSER_H
840 #define %(PREFIX)sPARSER_H
841
842 #include <stddef.h>
843 #include <stdint.h>
844 #include <stdbool.h>
845 #include "output/spv/spvbin-helpers.h"\
846 """ % {'PREFIX': prefix.upper()}
847         for name, production in productions.items():
848             print '\nstruct %s%s {' % (prefix, name_to_id(name))
849             print "    size_t start, len;"
850             print_members(production, ' ' * 4)
851             print '''};
852 bool %(prefix)sparse_%(name)s (struct spvbin_input *, struct %(prefix)s%(name)s **);
853 void %(prefix)sfree_%(name)s (struct %(prefix)s%(name)s *);
854 void %(prefix)sprint_%(name)s (const char *title, int indent, const struct %(prefix)s%(name)s *);\
855 ''' % {'prefix': prefix,
856        'name': name_to_id(name)}
857         print """\
858
859 #endif /* %(PREFIX)sPARSER_H */""" % {'PREFIX': prefix.upper()}
860     else:
861         sys.stderr.write("%s: bad usage (use --help for help)" % argv0)