3 # PSPP - a program for statistical analysis.
4 # Copyright (C) 2017, 2018, 2019 Free Software Foundation, Inc.
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
29 sys.stderr.write("%s:%d: %s\n" % (file_name, line_number, msg))
41 line = input_file.readline()
42 line = re.sub('#.*', '\n', line)
48 fatal("syntax error expecting %s" % type)
65 if token == ('id', id_):
73 return c.isalnum() or c in '-_'
82 if token == ('eof', ):
83 fatal("unexpected end of input")
96 if line.startswith('=>'):
99 elif line[0] in '[]()?|*+=:':
102 elif is_idchar(line[0]):
104 while n < len(line) and is_idchar(line[n]):
110 fatal("unknown character %c" % line[0])
115 argv0 = os.path.basename(sys.argv[0])
117 %(argv0)s, parser generator for SPV XML members
118 usage: %(argv0)s GRAMMAR header PREFIX
119 %(argv0)s GRAMMAR code PREFIX HEADER_NAME
120 where GRAMMAR contains grammar definitions\
121 ''' % {"argv0": argv0})
127 sub = parse_alternation()
131 member_name, nonterminal_name = parse_name()
132 if member_name.isupper():
133 fatal('%s; unknown terminal' % member_name)
135 return {'type': 'nonterminal',
136 'nonterminal_name': nonterminal_name,
137 'member_name': member_name}
140 def parse_quantified():
142 if token[0] in ['*', '+', '?']:
143 item = {'type': token[0], 'item': item}
148 def parse_sequence():
149 if match_id('EMPTY'):
150 return {'type': 'empty'}
153 sub = parse_quantified()
154 if sub['type'] == 'sequence':
155 items.extend(sub[1:])
158 if token[0] in ('|', ';', ')', 'eof'):
160 return {'type': 'sequence', 'items': items} if len(items) > 1 else items[0]
163 def parse_alternation():
164 items = [parse_sequence()]
166 items.append(parse_sequence())
168 return {'type': '|', 'items': items}
174 # The name used in XML for the attribute or element always comes
180 # If a different name is needed to disambiguate when the same name
181 # is used in different contexts in XML, it comes later, in
185 unique_name = token[1]
189 unique_name = xml_name
191 return unique_name, xml_name
195 def parse_production():
196 unique_name, xml_name = parse_name()
198 attr_xml_names = set()
201 attr_unique_name, attr_xml_name = parse_name()
205 while not match(')'):
207 attr_value.add(token[1])
212 if attr_unique_name not in enums:
213 enums[attr_unique_name] = attr_value
214 elif enums[attr_unique_name] != attr_value:
215 sys.stderr.write('%s: different enums with same name\n'
218 elif match_id('bool'):
219 attr_value = set(('true', 'false'))
220 elif match_id('dimension'):
221 attr_value = 'dimension'
222 elif match_id('real'):
224 elif match_id('int'):
226 elif match_id('color'):
228 elif match_id('ref'):
231 attr_value = ('ref', ref_type)
235 while not match(')'):
237 ref_types.add(token[1])
240 attr_value = ('ref', ref_types)
242 attr_value = ('ref', None)
244 fatal("unknown attribute value type")
246 attr_value = 'string'
247 attr_required = not match('?')
249 if attr_xml_name == 'id':
250 if attr_value != 'string':
251 fatal("id attribute must have string type")
254 if attr_unique_name in attributes:
255 fatal("production %s has two attributes %s" % (unique_name,
257 if attr_xml_name in attr_xml_names:
258 fatal("production %s has two attributes %s" % (unique_name,
260 attr_xml_names.add(attr_xml_name)
261 attributes[attr_unique_name] = (attr_xml_name,
262 attr_value, attr_required)
263 if 'id' not in attributes:
264 attributes["id"] = ('id', 'id', False)
269 rhs = {'type': 'text'}
270 elif match_id('ETC'):
271 rhs = {'type': 'etc'}
273 rhs = parse_alternation()
276 for a in rhs['items'] if rhs['type'] == '|' else (rhs,):
277 for term in a['items'] if a['type'] == 'sequence' else (a,):
278 if term['type'] == 'empty':
280 elif term['type'] == 'nonterminal':
282 elif term['type'] == '?' and term['item']['type'] == 'nonterminal':
284 elif (term['type'] in ('*', '+')
285 and term['item']['type'] == 'nonterminal'):
289 term['seq_name'] = 'seq' if n == 1 else 'seq%d' % n
291 return unique_name, xml_name, attributes, rhs
295 def print_members(attributes, rhs, indent):
298 for unique_name, (xml_name, value, required) in sorted(attributes.items()):
299 c_name = name_to_id(unique_name)
300 if type(value) is set:
303 attrs += [('bool %s_present;' % c_name,
304 'True if attribute present')]
305 elif value == set(('true', 'false')):
307 attrs += [('bool %s;' % c_name, None)]
309 attrs += [('int %s;' % c_name,
310 '-1 if not present, otherwise 0 or 1')]
312 attrs += [('enum %s%s %s;' % (prefix, c_name, c_name),
313 'Always nonzero' if required else
314 'Zero if not present')]
317 if unique_name not in used_enums:
318 new_enums.add(unique_name)
319 elif value == 'dimension' or value == 'real':
320 attrs += [('double %s;' % c_name,
321 'In inches. ' + ('Always present' if required else
322 'DBL_MAX if not present'))]
324 attrs += [('int %s;' % c_name,
325 'Always present' if required
326 else 'INT_MIN if not present')]
327 elif value == 'color':
328 attrs += [('int %s;' % c_name,
329 'Always present' if required
330 else '-1 if not present')]
331 elif value == 'string':
332 attrs += [('char *%s;' % c_name,
333 'Always nonnull' if required else 'Possibly null')]
334 elif value[0] == 'ref':
335 struct = ('spvxml_node'
336 if value[1] is None or type(value[1]) is set
337 else '%s%s' % (prefix, name_to_id(value[1])))
338 attrs += [('struct %s *%s;' % (struct, c_name),
339 'Always nonnull' if required else 'Possibly null')]
345 for enum_name in sorted(new_enums):
346 used_enums.add(enum_name)
347 c_name = name_to_id(enum_name)
348 print('\nenum %s%s {' % (prefix, c_name))
350 for value in sorted(enums[enum_name]):
351 print(' %s%s_%s%s,' % (prefix.upper(),
353 name_to_id(value).upper(),
354 ' = 1' if i == 0 else ''))
357 print('const char *%s%s_to_string (enum %s%s);' % (
358 prefix, c_name, prefix, c_name))
360 print('\nstruct %s%s {' % (prefix, name_to_id(name)))
361 print('%sstruct spvxml_node node_;' % indent)
364 print('\n%s/* Attributes. */' % indent)
365 for decl, comment in attrs:
366 line = '%s%s' % (indent, decl)
368 n_spaces = max(35 - len(line), 1)
369 line += '%s/* %s. */' % (' ' * n_spaces, comment)
372 if rhs['type'] == 'etc' or rhs['type'] == 'empty':
375 print('\n%s/* Content. */' % indent)
376 if rhs['type'] == 'text':
377 print('%schar *text; /* Always nonnull. */' % indent)
380 for a in rhs['items'] if rhs['type'] == '|' else (rhs,):
381 for term in a['items'] if a['type'] == 'sequence' else (a,):
382 if term['type'] == 'empty':
384 elif term['type'] == 'nonterminal':
385 nt_name = name_to_id(term['nonterminal_name'])
386 member_name = name_to_id(term['member_name'])
387 print('%sstruct %s%s *%s; /* Always nonnull. */' % (
388 indent, prefix, nt_name, member_name))
389 elif term['type'] == '?' and term['item']['type'] == 'nonterminal':
390 nt_name = name_to_id(term['item']['nonterminal_name'])
391 member_name = name_to_id(term['item']['member_name'])
392 print('%sstruct %s%s *%s; /* Possibly null. */' % (
393 indent, prefix, nt_name, member_name))
394 elif (term['type'] in ('*', '+')
395 and term['item']['type'] == 'nonterminal'):
396 nt_name = name_to_id(term['item']['nonterminal_name'])
397 member_name = name_to_id(term['item']['member_name'])
398 print('%sstruct %s%s **%s;' % (indent, prefix,
399 nt_name, member_name))
400 print('%ssize_t n_%s;' % (indent, member_name))
402 seq_name = term['seq_name']
403 print('%sstruct spvxml_node **%s;' % (indent, seq_name))
404 print('%ssize_t n_%s;' % (indent, seq_name))
408 return ''.join(['"'] + ["\\x%02x" % ord(x) for x in s] + ['"'])
411 class Parser_Context(object):
412 def __init__(self, function_name, productions):
415 self.need_error_handler = False
417 self.parser_index = 0
418 self.productions = productions
420 self.function_name = function_name
422 def gen_name(self, prefix):
423 n = self.suffixes.get(prefix, 0) + 1
424 self.suffixes[prefix] = n
425 return '%s%d' % (prefix, n) if n > 1 else prefix
426 def new_function(self, type_name):
427 f = Function('%s_%d' % (self.function_name, len(self.functions) + 1),
429 self.functions += [f]
433 def print_attribute_decls(name, attributes):
436 for unique_name, (xml_name, value, required) in sorted(attributes.items()):
437 c_name = name_to_id(unique_name)
438 print(' ATTR_%s,' % c_name.upper())
440 print(' struct spvxml_attribute attrs[] = {')
441 for unique_name, (xml_name, value, required) in sorted(attributes.items()):
442 c_name = name_to_id(unique_name)
443 print(' [ATTR_%s] = { "%s", %s, NULL },'
444 % (c_name.upper(), xml_name, 'true' if required else 'false'))
446 print(' enum { N_ATTRS = sizeof attrs / sizeof *attrs };')
449 def print_parser_for_attributes(name, attributes):
450 print(' /* Parse attributes. */')
451 print(' spvxml_parse_attributes (&nctx);')
456 for unique_name, (xml_name, value, required) in sorted(attributes.items()):
457 c_name = name_to_id(unique_name)
458 params = '&nctx, &attrs[ATTR_%s]' % c_name.upper()
459 if type(value) is set:
462 print(' spvxml_attr_parse_fixed (%s, "%s");'
463 % (params, tuple(value)[0]))
465 print(' p->%s_present = spvxml_attr_parse_fixed (\n'
467 % (c_name, params, tuple(value)[0]))
468 elif value == set(('true', 'false')):
469 print(' p->%s = spvxml_attr_parse_bool (%s);'
472 map_name = '%s%s_map' % (prefix, c_name)
473 print(' p->%s = spvxml_attr_parse_enum (\n'
475 % (c_name, params, map_name))
476 elif value in ('real', 'dimension', 'int', 'color'):
477 print(' p->%s = spvxml_attr_parse_%s (%s);'
478 % (c_name, value, params))
479 elif value == 'string':
480 print(' p->%s = attrs[ATTR_%s].value;\n'
481 ' attrs[ATTR_%s].value = NULL;'
482 % (c_name, c_name.upper(),
485 print(' p->node_.id = attrs[ATTR_%s].value;\n'
486 ' attrs[ATTR_%s].value = NULL;'
487 % (c_name.upper(), c_name.upper()))
488 elif value[0] == 'ref':
494 spvxml_node_context_uninit (&nctx);
495 ctx->hard_error = true;
499 % (prefix, name_to_id(name)))
501 class Function(object):
502 def __init__(self, function_name, type_name):
503 self.function_name = function_name
504 self.type_name = type_name
507 def gen_name(self, prefix):
508 n = self.suffixes.get(prefix, 0) + 1
509 self.suffixes[prefix] = n
510 return '%s%d' % (prefix, n) if n > 1 else prefix
514 %s (struct spvxml_node_context *nctx, xmlNode **input, struct %s *p)
516 % (self.function_name, self.type_name))
517 while self.code and self.code[0] == '':
518 self.code = self.code[1:]
519 for line in self.code:
520 print(' %s' % line if line else '')
521 print(' return true;')
525 STATE_ALTERNATION = 1
531 def generate_content_parser(nonterminal, rhs, function, ctx, state, seq_name):
532 seq_name = seq_name if seq_name else rhs.get('seq_name')
533 ctx.parser_index += 1
535 if rhs['type'] == 'etc':
536 function.code += ['spvxml_content_parse_etc (input);']
537 elif rhs['type'] == 'text':
538 function.code += ['if (!spvxml_content_parse_text (nctx, input, &p->text))',
540 elif rhs['type'] == '|':
541 for i in range(len(rhs['items'])):
542 choice = rhs['items'][i]
543 subfunc = ctx.new_function(function.type_name)
544 generate_content_parser(nonterminal, choice, subfunc, ctx,
546 if state == STATE_START
547 else STATE_GENERAL, seq_name)
548 function.code += ['%(start)s!%(tryfunc)s (nctx, input, p, %(subfunc)s)%(end)s'
549 % {'start': 'if (' if i == 0 else ' && ',
550 'subfunc': subfunc.function_name,
551 'tryfunc': '%stry_parse_%s'
552 % (prefix, name_to_id(nonterminal)),
553 'end': ')' if i == len(rhs['items']) - 1 else ''}]
554 function.code += [' {',
555 ' spvxml_content_error (nctx, *input, "Syntax error.");',
558 elif rhs['type'] == 'sequence':
559 for element in rhs['items']:
560 generate_content_parser(nonterminal, element, function, ctx,
562 if state in (STATE_START,
564 else STATE_GENERAL, seq_name)
565 elif rhs['type'] == 'empty':
566 function.code += ['(void) nctx;']
567 function.code += ['(void) input;']
568 function.code += ['(void) p;']
569 elif rhs['type'] in ('*', '+', '?'):
570 subfunc = ctx.new_function(function.type_name)
571 generate_content_parser(nonterminal, rhs['item'], subfunc, ctx,
573 if rhs['type'] == '?'
574 else STATE_REPETITION)
575 if state in (STATE_START,
578 else STATE_GENERAL, seq_name)
579 next_name = function.gen_name('next')
580 args = {'subfunc': subfunc.function_name,
581 'tryfunc': '%stry_parse_%s' % (prefix,
582 name_to_id (nonterminal))}
583 if rhs['type'] == '?':
585 '%(tryfunc)s (nctx, input, p, %(subfunc)s);' % args]
587 if rhs['type'] == '+':
588 function.code += ['if (!%(subfunc)s (nctx, input, p))' % args,
591 'while (%(tryfunc)s (nctx, input, p, %(subfunc)s))' % args,
593 elif rhs['type'] == 'nonterminal':
594 node_name = function.gen_name('node')
597 'xmlNode *%s;' % node_name,
598 'if (!spvxml_content_parse_element (nctx, input, "%s", &%s))'
599 % (ctx.productions[rhs['nonterminal_name']][0], node_name),
601 if state in (STATE_START,
605 target = '&p->%s' % name_to_id(rhs['member_name'])
607 assert state in (STATE_REPETITION, STATE_GENERAL)
608 member = name_to_id(rhs['member_name']) if state == STATE_REPETITION else seq_name
609 function.code += ['struct %s%s *%s;' % (
610 prefix, name_to_id(rhs['nonterminal_name']), member)]
611 target = '&%s' % member
613 'if (!%sparse_%s (nctx->up, %s, %s))'
614 % (prefix, name_to_id(rhs['nonterminal_name']), node_name, target),
616 if state in (STATE_REPETITION, STATE_GENERAL):
618 'p->%s = xrealloc (p->%s, sizeof *p->%s * (p->n_%s + 1));'
619 % (member, member, member, member),
620 'p->%s[p->n_%s++] = %s;' % (member, member,
621 '&%s->node_' % member
622 if state == STATE_GENERAL
627 def print_parser(name, production, productions, indent):
628 xml_name, attributes, rhs = production
632 %(prefix)stry_parse_%(name)s (
633 struct spvxml_node_context *nctx, xmlNode **input,
634 struct %(prefix)s%(name)s *p,
635 bool (*sub) (struct spvxml_node_context *,
637 struct %(prefix)s%(name)s *))
639 xmlNode *next = *input;
640 bool ok = sub (nctx, &next, p);
643 else if (!nctx->up->hard_error) {
644 free (nctx->up->error);
645 nctx->up->error = NULL;
650 'name': name_to_id(name)})
652 ctx = Parser_Context('%sparse_%s' % (prefix, name_to_id(name)),
654 if rhs['type'] not in ('empty', 'etc'):
655 function = ctx.new_function('%s%s' % (prefix, name_to_id(name)))
656 generate_content_parser(name, rhs, function, ctx, 0, None)
657 for f in reversed(ctx.functions):
662 %(prefix)sparse_%(name)s (
663 struct spvxml_context *ctx, xmlNode *input,
664 struct %(prefix)s%(name)s **p_)
667 'name': name_to_id(name)})
669 print_attribute_decls(name, attributes)
671 print(' struct spvxml_node_context nctx = {')
673 print(' .parent = input,')
674 print(' .attrs = attrs,')
675 print(' .n_attrs = N_ATTRS,')
678 print(' *p_ = NULL;')
679 print(' struct %(prefix)s%(name)s *p = xzalloc (sizeof *p);'
681 'name': name_to_id(name)})
682 print(' p->node_.raw = input;')
683 print(' p->node_.class_ = &%(prefix)s%(name)s_class;'
685 'name': name_to_id(name)})
688 print_parser_for_attributes(name, attributes)
690 if rhs['type'] == 'empty':
693 if (!spvxml_content_parse_end (&nctx, input->children)) {
694 ctx->hard_error = true;
695 spvxml_node_context_uninit (&nctx);
699 % (prefix, name_to_id(name)))
700 elif rhs['type'] == 'etc':
702 /* Ignore content. */
707 input = input->children;
708 if (!%s (&nctx, &input, p)
709 || !spvxml_content_parse_end (&nctx, input)) {
710 ctx->hard_error = true;
711 spvxml_node_context_uninit (&nctx);
715 % (function.function_name,
716 prefix, name_to_id(name)))
719 spvxml_node_context_uninit (&nctx);
726 def print_free_members(attributes, rhs, indent):
727 for unique_name, (xml_name, value, required) in attributes.items():
728 c_name = name_to_id(unique_name)
729 if (type(value) is set
730 or value in ('dimension', 'real', 'int', 'color', 'id')
731 or value[0] == 'ref'):
733 elif value == 'string':
734 print(' free (p->%s);' % c_name);
738 if rhs['type'] in ('etc', 'empty'):
740 elif rhs['type'] == 'text':
741 print(' free (p->text);')
744 for a in rhs['items'] if rhs['type'] == '|' else (rhs,):
745 for term in a['items'] if a['type'] == 'sequence' else (a,):
746 if term['type'] == 'empty':
748 elif (term['type'] == 'nonterminal'
749 or (term['type'] == '?'
750 and term['item']['type'] == 'nonterminal')):
751 if term['type'] == '?':
753 nt_name = name_to_id(term['nonterminal_name'])
754 member_name = name_to_id(term['member_name'])
755 print(' %sfree_%s (p->%s);' % (prefix, nt_name,
757 elif (term['type'] in ('*', '+')
758 and term['item']['type'] == 'nonterminal'):
759 nt_name = name_to_id(term['item']['nonterminal_name'])
760 member_name = name_to_id(term['item']['member_name'])
762 for (size_t i = 0; i < p->n_%s; i++)
763 %sfree_%s (p->%s[i]);
766 prefix, nt_name, member_name,
770 seq_name = 'seq' if n == 1 else 'seq%d' % n
772 for (size_t i = 0; i < p->n_%s; i++)
773 p->%s[i]->class_->spvxml_node_free (p->%s[i]);
778 print(' free (p->node_.id);')
782 def print_free(name, production, indent):
783 xml_name, attributes, rhs = production
787 %(prefix)sfree_%(name)s (struct %(prefix)s%(name)s *p)
791 ''' % {'prefix': prefix,
792 'name': name_to_id(name)})
794 print_free_members(attributes, rhs, ' ' * 4)
799 return s[0].lower() + ''.join(['_%c' % x.lower() if x.isupper() else x
800 for x in s[1:]]).replace('-', '_')
803 def print_recurse_members(attributes, rhs, function):
804 if rhs['type'] == 'etc' or rhs['type'] == 'empty':
806 elif rhs['type'] == 'text':
810 for a in rhs['items'] if rhs['type'] == '|' else (rhs,):
811 for term in a['items'] if a['type'] == 'sequence' else (a,):
812 if term['type'] == 'empty':
814 elif (term['type'] == 'nonterminal'
815 or (term['type'] == '?'
816 and term['item']['type'] == 'nonterminal')):
817 if term['type'] == '?':
819 nt_name = name_to_id(term['nonterminal_name'])
820 member_name = name_to_id(term['member_name'])
821 print(' %s%s_%s (ctx, p->%s);'
822 % (prefix, function, nt_name, member_name))
823 elif (term['type'] in ('*', '+')
824 and term['item']['type'] == 'nonterminal'):
825 nt_name = name_to_id(term['item']['nonterminal_name'])
826 member_name = name_to_id(term['item']['member_name'])
828 for (size_t i = 0; i < p->n_%s; i++)
829 %s%s_%s (ctx, p->%s[i]);'''
831 prefix, function, nt_name, member_name))
834 seq_name = 'seq' if n == 1 else 'seq%d' % n
836 for (size_t i = 0; i < p->n_%s; i++)
837 p->%s[i]->class_->spvxml_node_%s (ctx, p->%s[i]);'''
839 seq_name, function, seq_name))
842 def print_collect_ids(name, production):
843 xml_name, attributes, rhs = production
847 %(prefix)scollect_ids_%(name)s (struct spvxml_context *ctx, struct %(prefix)s%(name)s *p)
852 spvxml_node_collect_id (ctx, &p->node_);
853 ''' % {'prefix': prefix,
854 'name': name_to_id(name)})
856 print_recurse_members(attributes, rhs, 'collect_ids')
861 def print_resolve_refs(name, production):
862 xml_name, attributes, rhs = production
866 %(prefix)sis_%(name)s (const struct spvxml_node *node)
868 return node->class_ == &%(prefix)s%(name)s_class;
871 struct %(prefix)s%(name)s *
872 %(prefix)scast_%(name)s (const struct spvxml_node *node)
874 return (node && %(prefix)sis_%(name)s (node)
875 ? UP_CAST (node, struct %(prefix)s%(name)s, node_)
880 %(prefix)sresolve_refs_%(name)s (struct spvxml_context *ctx UNUSED, struct %(prefix)s%(name)s *p UNUSED)
884 ''' % {'prefix': prefix,
885 'name': name_to_id(name)})
888 for unique_name, (xml_name, value, required) in sorted(attributes.items()):
889 c_name = name_to_id(unique_name)
890 if type(value) is set or value[0] != 'ref':
894 print(' p->%s = spvxml_node_resolve_ref (ctx, p->node_.raw, \"%s\", NULL, 0);'
895 % (c_name, xml_name))
901 if type(value[1]) is set:
902 print(' static const struct spvxml_node_class *const %s[] = {' % name)
903 for ref_type in sorted(value[1]):
904 print(' &%(prefix)s%(ref_type)s_class,'
906 'ref_type': name_to_id(ref_type)})
908 print(' const size_t n_%s = sizeof %s / sizeof *%s;'
909 % (name, name, name))
910 print(' p->%(member)s = spvxml_node_resolve_ref (ctx, p->node_.raw, \"%(attr)s\", %(name)s, n_%(name)s);'
917 print(' static const struct spvxml_node_class *const %s' % name)
918 print(' = &%(prefix)s%(ref_type)s_class;'
920 'ref_type': name_to_id(value[1])})
921 print(' p->%(member)s = %(prefix)scast_%(ref_type)s (spvxml_node_resolve_ref (ctx, p->node_.raw, \"%(attr)s\", &%(name)s, 1));'
926 'ref_type': name_to_id(value[1])})
929 print_recurse_members(attributes, rhs, 'resolve_refs')
935 return s[0].lower() + ''.join(['_%c' % x.lower() if x.isupper() else x
936 for x in s[1:]]).replace('-', '_')
939 if __name__ == "__main__":
942 options, args = getopt.gnu_getopt(sys.argv[1:], 'h', ['help'])
943 except getopt.GetoptError as e:
944 sys.stderr.write("%s: %s\n" % (argv0, e.msg))
947 for key, value in options:
948 if key in ['-h', '--help']:
954 sys.stderr.write("%s: bad usage (use --help for help)\n" % argv0)
959 file_name, output_type, prefix = args[:3]
960 input_file = open(file_name)
962 prefix = '%s_' % prefix
977 if token[0] == 'eof':
980 name, xml_name, attributes, rhs = parse_production()
981 if name in productions:
982 fatal("%s: duplicate production" % name)
983 productions[name] = (xml_name, attributes, rhs)
985 print('/* Generated automatically -- do not modify! -*- buffer-read-only: t -*- */')
986 if output_type == 'code' and len(args) == 4:
987 header_name = args[3]
995 #include "libpspp/cast.h"
996 #include "libpspp/str.h"
997 #include "gl/xalloc.h"
1000 for enum_name, values in sorted(enums.items()):
1001 if len(values) <= 1:
1004 c_name = name_to_id(enum_name)
1005 print('\nstatic const struct spvxml_enum %s%s_map[] = {'
1007 for value in sorted(values):
1008 print(' { "%s", %s%s_%s },' % (value, prefix.upper(),
1010 name_to_id(value).upper()))
1011 print(' { NULL, 0 },')
1013 print('\nconst char *')
1014 print('%s%s_to_string (enum %s%s %s)'
1015 % (prefix, c_name, prefix, c_name, c_name))
1017 print(' switch (%s) {' % c_name)
1018 for value in sorted(values):
1019 print(' case %s%s_%s: return "%s";'
1020 % (prefix.upper(), c_name.upper(),
1021 name_to_id(value).upper(), value))
1022 print(' default: return NULL;')
1026 for name, (xml_name, attributes, rhs) in sorted(productions.items()):
1027 print('static void %(prefix)scollect_ids_%(name)s (struct spvxml_context *, struct %(prefix)s%(name)s *);\n'
1028 'static void %(prefix)sresolve_refs_%(name)s (struct spvxml_context *ctx UNUSED, struct %(prefix)s%(name)s *p UNUSED);\n'
1029 % {'prefix': prefix,
1030 'name': name_to_id(name)})
1031 for name, production in sorted(productions.items()):
1032 print_parser(name, production, productions, ' ' * 4)
1033 print_free(name, production, ' ' * 4)
1034 print_collect_ids(name, production)
1035 print_resolve_refs(name, production)
1038 %(prefix)sdo_free_%(name)s (struct spvxml_node *node)
1040 %(prefix)sfree_%(name)s (UP_CAST (node, struct %(prefix)s%(name)s, node_));
1044 %(prefix)sdo_collect_ids_%(name)s (struct spvxml_context *ctx, struct spvxml_node *node)
1046 %(prefix)scollect_ids_%(name)s (ctx, UP_CAST (node, struct %(prefix)s%(name)s, node_));
1050 %(prefix)sdo_resolve_refs_%(name)s (struct spvxml_context *ctx, struct spvxml_node *node)
1052 %(prefix)sresolve_refs_%(name)s (ctx, UP_CAST (node, struct %(prefix)s%(name)s, node_));
1055 struct spvxml_node_class %(prefix)s%(name)s_class = {
1057 %(prefix)sdo_free_%(name)s,
1058 %(prefix)sdo_collect_ids_%(name)s,
1059 %(prefix)sdo_resolve_refs_%(name)s,
1062 % {'prefix': prefix,
1063 'name': name_to_id(name),
1064 'class': (name if name == production[0]
1065 else '%s (%s)' % (name, production[0]))})
1066 elif output_type == 'header' and len(args) == 3:
1068 #ifndef %(PREFIX)sPARSER_H
1069 #define %(PREFIX)sPARSER_H
1073 #include <stdbool.h>
1074 #include "output/spv/spvxml-helpers.h"\
1075 """ % {'PREFIX': prefix.upper()})
1076 for name, (xml_name, attributes, rhs) in sorted(productions.items()):
1077 print_members(attributes, rhs, ' ' * 4)
1080 extern struct spvxml_node_class %(prefix)s%(name)s_class;
1082 bool %(prefix)sparse_%(name)s (struct spvxml_context *, xmlNode *input, struct %(prefix)s%(name)s **);
1083 void %(prefix)sfree_%(name)s (struct %(prefix)s%(name)s *);
1084 bool %(prefix)sis_%(name)s (const struct spvxml_node *);
1085 struct %(prefix)s%(name)s *%(prefix)scast_%(name)s (const struct spvxml_node *);'''
1086 % {'prefix': prefix,
1087 'name': name_to_id(name)})
1090 #endif /* %(PREFIX)sPARSER_H */""" % {'PREFIX': prefix.upper()})
1092 sys.stderr.write("%s: bad usage (use --help for help)" % argv0)