xref: /aosp_15_r20/external/emboss/compiler/front_end/format_emb.py (revision 99e0aae7469b87d12f0ad23e61142c2d74c1ef70)
1# Copyright 2019 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Formatter for Emboss source files.
16
17This module exports a single function, format_emboss_parse_tree(), which
18pretty-prints an Emboss parse tree.
19"""
20
21from __future__ import print_function
22
23import collections
24import itertools
25
26from compiler.front_end import module_ir
27from compiler.front_end import tokenizer
28from compiler.util import parser_types
29
30
31class Config(collections.namedtuple('Config',
32                                    ['indent_width', 'show_line_types'])):
33  """Configuration for formatting."""
34
35  def __new__(cls, indent_width=2, show_line_types=False):
36    return super(cls, Config).__new__(cls, indent_width, show_line_types)
37
38
39class _Row(collections.namedtuple('Row', ['name', 'columns', 'indent'])):
40  """Structured contents of a single line."""
41
42  def __new__(cls, name, columns=None, indent=0):
43    return super(cls, _Row).__new__(cls, name, tuple(columns or []), indent)
44
45
46class _Block(collections.namedtuple('Block', ['prefix', 'header', 'body'])):
47  """Structured block of multiple lines."""
48
49  def __new__(cls, prefix, header, body):
50    assert header
51    return super(cls, _Block).__new__(cls, prefix, header, body)
52
53
54# Map of productions to their formatters.
55_formatters = {}
56
57
58def format_emboss_parse_tree(parse_tree, config, used_productions=None):
59  """Formats Emboss source code.
60
61  Arguments:
62      parse_tree: A parse tree of an Emboss source file.
63      config: A Config tuple with formatting options.
64      used_productions: An optional set to which all used productions will be
65          added.  Intended for use by test code to ensure full production
66          coverage.
67
68  Returns:
69      A string of the reformatted source text.
70  """
71  if hasattr(parse_tree, 'children'):
72    parsed_children = [format_emboss_parse_tree(child, config, used_productions)
73                       for child in parse_tree.children]
74    args = parsed_children + [config]
75    if used_productions is not None:
76      used_productions.add(parse_tree.production)
77    return _formatters[parse_tree.production](*args)
78  else:
79    assert isinstance(parse_tree, parser_types.Token), str(parse_tree)
80    return parse_tree.text
81
82
83def sanity_check_format_result(formatted_text, original_text):
84  """Checks that the given texts are equivalent."""
85  # The texts are considered equivalent if they tokenize to the same token
86  # stream, except that:
87  #
88  # Multiple consecutive newline tokens are equivalent to a single newline
89  # token.
90  #
91  # Extra newline tokens at the start of the stream should be ignored.
92  #
93  # Whitespace at the start or end of a token should be ignored.  This matters
94  # for documentation and comment tokens, which may have had trailing whitespace
95  # in the original text, and for indent tokens, which may contain a different
96  # number of space and/or tab characters.
97  original_tokens, errors = tokenizer.tokenize(original_text, '')
98  if errors:
99    return ['BUG: original text is not tokenizable: {!r}'.format(errors)]
100
101  formatted_tokens, errors = tokenizer.tokenize(formatted_text, '')
102  if errors:
103    return ['BUG: formatted text is not tokenizable: {!r}'.format(errors)]
104
105  o_tokens = _collapse_newline_tokens(original_tokens)
106  f_tokens = _collapse_newline_tokens(formatted_tokens)
107  for i in range(len(o_tokens)):
108    if (o_tokens[i].symbol != f_tokens[i].symbol or
109        o_tokens[i].text.strip() != f_tokens[i].text.strip()):
110      return ['BUG: Symbol {} differs: {!r} vs {!r}'.format(i, o_tokens[i],
111                                                            f_tokens[i])]
112  return []
113
114
115def _collapse_newline_tokens(token_list):
116  r"""Collapses multiple consecutive "\\n" tokens into a single newline."""
117  result = []
118  for symbol, group in itertools.groupby(token_list, lambda x: x.symbol):
119    if symbol == '"\\n"':
120      # Skip all newlines if they are at the start, otherwise add a single
121      # newline for each consecutive run of newlines.
122      if result:
123        result.append(list(group)[0])
124    else:
125      result.extend(group)
126  return result
127
128
129def _indent_row(row):
130  """Adds one level of indent to the given row, returning a new row."""
131  assert isinstance(row, _Row), repr(row)
132  return _Row(name=row.name,
133              columns=row.columns,
134              indent=row.indent + 1)
135
136
137def _indent_rows(rows):
138  """Adds one level of indent to the given rows, returning a new list."""
139  return list(map(_indent_row, rows))
140
141
142def _indent_blocks(blocks):
143  """Adds one level of indent to the given blocks, returning a new list."""
144  return [_Block(prefix=_indent_rows(block.prefix),
145                 header=_indent_row(block.header),
146                 body=_indent_rows(block.body))
147          for block in blocks]
148
149
150def _intersperse(interspersed, sections):
151  """Intersperses `interspersed` between non-empty `sections`."""
152  result = []
153  for section in sections:
154    if section:
155      if result:
156        result.extend(interspersed)
157      result.extend(section)
158  return result
159
160
161def _should_add_blank_lines(blocks):
162  """Returns true if blank lines should be added between blocks."""
163  other_non_empty_lines = 0
164  last_non_empty_lines = 0
165  for block in blocks:
166    last_non_empty_lines = len([line for line in
167                                block.body + block.prefix
168                                if line.columns])
169    other_non_empty_lines += last_non_empty_lines
170  # Vertical spaces should be added if there are more interior
171  # non-empty-non-header lines than header lines.
172  return len(blocks) <= other_non_empty_lines - last_non_empty_lines
173
174
175def _columnize(blocks, indent_width, indent_columns=1):
176  """Aligns columns in the header rows of the given blocks.
177
178  The `indent_columns` argument is used to determine how many columns should be
179  indented.  With `indent_columns == 1`, the result would be:
180
181  AA     BB  CC
182    AAA  BBB  CCC
183  A      B    C
184
185  With `indent_columns == 2`:
186
187  AA   BB     CC
188    AAA  BBB  CCC
189  A    B      C
190
191  With `indent_columns == 1`, only the first column is indented compared to
192  surrounding rows; with `indent_columns == 2`, both the first and second
193  columns are indented.
194
195  Arguments:
196      blocks: A list of _Blocks to columnize.
197      indent_width: The number of spaces per level of indent.
198      indent_columns: The number of columns to indent.
199
200  Returns:
201      A list of _Rows of the prefix, header, and body _Rows of each block, where
202      the header _Rows of each type have had their columns aligned.
203  """
204  single_width_separators = {'enum-value': {0, 1}, 'field': {0}}
205  # For each type of row, figure out how many characters each column needs.
206  row_types = collections.defaultdict(
207      lambda: collections.defaultdict(lambda: 0))
208  for block in blocks:
209    max_lengths = row_types[block.header.name]
210    for i in range(len(block.header.columns)):
211      if i == indent_columns - 1:
212        adjustment = block.header.indent * indent_width
213      else:
214        adjustment = 0
215      max_lengths[i] = max(max_lengths[i],
216                           len(block.header.columns[i]) + adjustment)
217
218  assert len(row_types) < 3
219
220  # Then, for each row, actually columnize it.
221  result = []
222  for block in blocks:
223    columns = []
224    for i in range(len(block.header.columns)):
225      column_width = row_types[block.header.name][i]
226      if column_width == 0:
227        # Zero-width columns are entirely omitted, including their column
228        # separators.
229        pass
230      else:
231        if i == indent_columns - 1:
232          # This function only performs the right padding for each column.
233          # Since the left padding for indent will be added later, the
234          # corresponding space needs to be removed from the right padding of
235          # the first column.
236          column_width -= block.header.indent * indent_width
237        if i in single_width_separators.get(block.header.name, []):
238          # Only one space around the "=" in enum values and between the start
239          # and size in field locations.
240          column_width += 1
241        else:
242          column_width += 2
243      columns.append(block.header.columns[i].ljust(column_width))
244    result.append(block.prefix + [_Row(block.header.name,
245                                       [''.join(columns).rstrip()],
246                                       block.header.indent)] + block.body)
247  return result
248
249
250def _indent_blanks_and_comments(rows):
251  """Indents blank and comment lines to match the next non-blank line."""
252  result = []
253  previous_indent = 0
254  for row in reversed(rows):
255    if not ''.join(row.columns) or row.name == 'comment':
256      result.append(_Row(row.name, row.columns, previous_indent))
257    else:
258      result.append(row)
259      previous_indent = row.indent
260  return reversed(result)
261
262
263def _add_blank_rows_on_dedent(rows):
264  """Adds blank rows before dedented lines, where needed."""
265  result = []
266  previous_indent = 0
267  previous_row_was_blank = True
268  for row in rows:
269    row_is_blank = not ''.join(row.columns)
270    found_dedent = previous_indent > row.indent
271    if found_dedent and not previous_row_was_blank and not row_is_blank:
272      result.append(_Row('dedent-space', [], row.indent))
273    result.append(row)
274    previous_indent = row.indent
275    previous_row_was_blank = row_is_blank
276  return result
277
278
279def _render_row_to_text(row, indent_width):
280  assert len(row.columns) < 2, '{!r}'.format(row)
281  text = ' ' * indent_width * row.indent
282  text += ''.join(row.columns)
283  return text.rstrip()
284
285
286def _render_rows_to_text(rows, indent_width, show_line_types):
287  max_row_name_len = max([0] + [len(row.name) for row in rows])
288  flattened_rows = []
289  for row in rows:
290    row_text = _render_row_to_text(row, indent_width)
291    if show_line_types:
292      row_text = row.name.ljust(max_row_name_len) + '|' + row_text
293    flattened_rows.append(row_text)
294  return '\n'.join(flattened_rows + [''])
295
296
297def _check_productions():
298  """Asserts that the productions in this module match those in module_ir."""
299  productions_ok = True
300  for production in module_ir.PRODUCTIONS:
301    if production not in _formatters:
302      productions_ok = False
303      print('@_formats({!r})'.format(str(production)))
304
305  for production in _formatters:
306    if production not in module_ir.PRODUCTIONS:
307      productions_ok = False
308      print('not @_formats({!r})'.format(str(production)))
309
310  assert productions_ok, 'Grammar mismatch.'
311
312
313def _formats_with_config(production_text):
314  """Marks a function as a formatter requiring a config argument."""
315  production = parser_types.Production.parse(production_text)
316
317  def formats(f):
318    assert production not in _formatters, production
319    _formatters[production] = f
320    return f
321
322  return formats
323
324
325def _formats(production_text):
326  """Marks a function as the formatter for a particular production."""
327
328  def strip_config_argument(f):
329    _formats_with_config(production_text)(lambda *a, **kw: f(*a[:-1], **kw))
330    return f
331
332  return strip_config_argument
333
334
335################################################################################
336# From here to the end of the file are functions which recursively format an
337# Emboss parse tree.
338#
339# The format_parse_tree() function will call formatters, bottom-up, for the
340# entire parse tree.  Each formatter will be called with the results of the
341# formatters for each child node.  (The "formatter" for leaf nodes is the
342# original text of the token.)
343#
344# Formatters can be roughly divided into three types:
345#
346# The _module formatter is the top-level formatter.  It handles final rendering
347# into text, and returns a string.
348#
349# Formatters for productions that are at least one full line return lists of
350# _Rows.  The production 'attribute-line' falls into this category, but
351# 'attribute' does not.  This form allows parallel constructs in separate lines
352# to be lined up column-wise, even when there are intervening lines that should
353# not be lined up -- for example, the types and names of struct fields will be
354# aligned, even if there are documentation, comment, or attribute lines mixed
355# in.
356#
357# Formatters for productions that are smaller than one full line just return
358# strings.
359
360
361@_formats_with_config('module -> comment-line* doc-line* import-line*'
362                      '          attribute-line* type-definition*')
363def _module(comments, docs, imports, attributes, types, config):
364  """Performs top-level formatting for an Emboss source file."""
365
366  # The top-level sections other than types should be separated by single lines.
367  header_rows = _intersperse(
368      [_Row('section-break')],
369      [_strip_empty_leading_trailing_comment_lines(comments), docs, imports,
370       attributes])
371
372  # Top-level types should be separated by double lines from themselves and from
373  # the header rows.
374  rows = _intersperse(
375      [_Row('top-type-separator'), _Row('top-type-separator')],
376      [header_rows] + types)
377
378  # Final fixups.
379  rows = _indent_blanks_and_comments(rows)
380  rows = _add_blank_rows_on_dedent(rows)
381  return _render_rows_to_text(rows, config.indent_width, config.show_line_types)
382
383
384@_formats('doc-line -> doc Comment? eol')
385def _doc_line(doc, comment, eol):
386  assert not comment, 'Comment should not be possible on the same line as doc.'
387  return [_Row('doc', [doc])] + eol
388
389
390@_formats('import-line -> "import" string-constant "as" snake-word Comment?'
391          '               eol')
392def _import_line(import_, filename, as_, name, comment, eol):
393  return [_Row('import', ['{} {} {} {}  {}'.format(
394      import_, filename, as_, name, comment)])] + eol
395
396
397@_formats('attribute-line -> attribute Comment? eol')
398def _attribute_line(attribute, comment, eol):
399  return [_Row('attribute', ['{}  {}'.format(attribute, comment)])] + eol
400
401
402@_formats('attribute -> "[" attribute-context? "$default"? snake-word ":"'
403          '             attribute-value "]"')
404def _attribute(open_, context, default, name, colon, value, close):
405  return ''.join([open_,
406                  _concatenate_with_spaces(context, default, name + colon,
407                                           value),
408                  close])
409
410
411@_formats('parameter-definition -> snake-name ":" type')
412def _parameter_definition(name, colon, type_specifier):
413  return '{}{} {}'.format(name, colon, type_specifier)
414
415
416@_formats('type-definition* -> type-definition type-definition*')
417def _type_defitinions(definition, definitions):
418  return [definition] + definitions
419
420
421@_formats('bits -> "bits" type-name delimited-parameter-definition-list? ":"'
422          '        Comment? eol bits-body')
423@_formats('struct -> "struct" type-name delimited-parameter-definition-list?'
424          '          ":" Comment? eol struct-body')
425def _structure_type(struct, name, parameters, colon, comment, eol, body):
426  return ([_Row('type-header',
427                ['{} {}{}{}  {}'.format(
428                    struct, name, parameters, colon, comment)])] +
429          eol + body)
430
431
432@_formats('enum -> "enum" type-name ":" Comment? eol enum-body')
433@_formats('external -> "external" type-name ":" Comment? eol external-body')
434def _type(struct, name, colon, comment, eol, body):
435  return ([_Row('type-header',
436                ['{} {}{}  {}'.format(struct, name, colon, comment)])] +
437          eol + body)
438
439
440@_formats_with_config('bits-body -> Indent doc-line* attribute-line*'
441                      '             type-definition* bits-field-block Dedent')
442@_formats_with_config(
443    'struct-body -> Indent doc-line* attribute-line*'
444    '               type-definition* struct-field-block Dedent')
445def _structure_body(indent, docs, attributes, type_definitions, fields, dedent,
446                    config):
447  del indent, dedent  # Unused.
448  spacing = [_Row('field-separator')] if _should_add_blank_lines(fields) else []
449  columnized_fields = _columnize(fields, config.indent_width, indent_columns=2)
450  return _indent_rows(_intersperse(
451      spacing, [docs, attributes] + type_definitions + columnized_fields))
452
453
454@_formats('field-location -> expression "[" "+" expression "]"')
455def _field_location(start, open_bracket, plus, size, close_bracket):
456  return [start, open_bracket + plus + size + close_bracket]
457
458
459@_formats('anonymous-bits-field-block -> conditional-anonymous-bits-field-block'
460          '                              anonymous-bits-field-block')
461@_formats('anonymous-bits-field-block -> unconditional-anonymous-bits-field'
462          '                              anonymous-bits-field-block')
463@_formats('bits-field-block -> conditional-bits-field-block bits-field-block')
464@_formats('bits-field-block -> unconditional-bits-field bits-field-block')
465@_formats('struct-field-block -> conditional-struct-field-block'
466          '                      struct-field-block')
467@_formats('struct-field-block -> unconditional-struct-field struct-field-block')
468@_formats('unconditional-anonymous-bits-field* ->'
469          '    unconditional-anonymous-bits-field'
470          '    unconditional-anonymous-bits-field*')
471@_formats('unconditional-anonymous-bits-field+ ->'
472          '    unconditional-anonymous-bits-field'
473          '    unconditional-anonymous-bits-field*')
474@_formats('unconditional-bits-field* -> unconditional-bits-field'
475          '                             unconditional-bits-field*')
476@_formats('unconditional-bits-field+ -> unconditional-bits-field'
477          '                             unconditional-bits-field*')
478@_formats('unconditional-struct-field* -> unconditional-struct-field'
479          '                               unconditional-struct-field*')
480@_formats('unconditional-struct-field+ -> unconditional-struct-field'
481          '                               unconditional-struct-field*')
482def _structure_block(field, block):
483  """Prepends field to block."""
484  return field + block
485
486
487@_formats('virtual-field -> "let" snake-name "=" expression Comment? eol'
488          '                 field-body?')
489def _virtual_field(let_keyword, name, equals, value, comment, eol, body):
490  # This formatting doesn't look the best when there are blocks of several
491  # virtual fields next to each other, but works pretty well when they're
492  # intermixed with physical fields.  It's probably good enough for now, since
493  # there aren't (yet) any virtual fields in real .embs, and will probably only
494  # be a few in the near future.
495  return [_Block([],
496                 _Row('virtual-field',
497                      [_concatenate_with(
498                          '  ',
499                          _concatenate_with_spaces(let_keyword, name, equals,
500                                                   value),
501                          comment)]),
502                 eol + body)]
503
504
505@_formats('field -> field-location type snake-name abbreviation?'
506          '         attribute* doc? Comment? eol field-body?')
507def _unconditional_field(location, type_, name, abbreviation, attributes, doc,
508                         comment, eol, body):
509  return [_Block([],
510                 _Row('field',
511                      location + [type_,
512                                  _concatenate_with_spaces(name, abbreviation),
513                                  attributes, doc, comment]),
514                 eol + body)]
515
516
517@_formats('field-body -> Indent doc-line* attribute-line* Dedent')
518def _field_body(indent, docs, attributes, dedent):
519  del indent, dedent  # Unused
520  return _indent_rows(docs + attributes)
521
522
523@_formats('anonymous-bits-field-definition ->'
524          '    field-location "bits" ":" Comment? eol anonymous-bits-body')
525def _inline_bits(location, bits, colon, comment, eol, body):
526  # Even though an anonymous bits field technically defines a new, anonymous
527  # type, conceptually it's more like defining a bunch of fields on the
528  # surrounding type, so it is treated as an inline list of blocks, instead of
529  # being separately formatted.
530  header_row = _Row('field', [location[0], location[1] + '  ' + bits + colon,
531                              '', '', '', '', comment])
532  return ([_Block([], header_row, eol + body.header_lines)] +
533          body.field_blocks)
534
535
536@_formats('inline-enum-field-definition ->'
537          '    field-location "enum" snake-name abbreviation? ":" Comment? eol'
538          '    enum-body')
539@_formats(
540    'inline-struct-field-definition ->'
541    '    field-location "struct" snake-name abbreviation? ":" Comment? eol'
542    '    struct-body')
543@_formats('inline-bits-field-definition ->'
544          '    field-location "bits" snake-name abbreviation? ":" Comment? eol'
545          '    bits-body')
546def _inline_type(location, keyword, name, abbreviation, colon, comment, eol,
547                 body):
548  """Formats an inline type in a struct or bits."""
549  header_row = _Row(
550      'field', location + [keyword,
551                           _concatenate_with_spaces(name, abbreviation) + colon,
552                           '', '', comment])
553  return [_Block([], header_row, eol + body)]
554
555
556@_formats('conditional-struct-field-block -> "if" expression ":" Comment? eol'
557          '                                  Indent unconditional-struct-field+'
558          '                                  Dedent')
559@_formats('conditional-bits-field-block -> "if" expression ":" Comment? eol'
560          '                                Indent unconditional-bits-field+'
561          '                                Dedent')
562@_formats('conditional-anonymous-bits-field-block ->'
563          '    "if" expression ":" Comment? eol'
564          '    Indent unconditional-anonymous-bits-field+ Dedent')
565def _conditional_field(if_, condition, colon, comment, eol, indent, body,
566                       dedent):
567  """Formats an `if` construct."""
568  del indent, dedent  # Unused
569  # The body of an 'if' should be columnized with the surrounding blocks, so
570  # much like an inline 'bits', its body is treated as an inline list of blocks.
571  header_row = _Row('if',
572                    ['{} {}{}  {}'.format(if_, condition, colon, comment)])
573  indented_body = _indent_blocks(body)
574  assert indented_body, 'Expected body of if condition.'
575  return [_Block([header_row] + eol + indented_body[0].prefix,
576                 indented_body[0].header,
577                 indented_body[0].body)] + indented_body[1:]
578
579
580_InlineBitsBodyType = collections.namedtuple('InlineBitsBodyType',
581                                             ['header_lines', 'field_blocks'])
582
583
584@_formats('anonymous-bits-body ->'
585          '    Indent attribute-line* anonymous-bits-field-block Dedent')
586def _inline_bits_body(indent, attributes, fields, dedent):
587  del indent, dedent  # Unused
588  return _InlineBitsBodyType(header_lines=_indent_rows(attributes),
589                             field_blocks=_indent_blocks(fields))
590
591
592@_formats_with_config(
593    'enum-body -> Indent doc-line* attribute-line* enum-value+'
594    '             Dedent')
595def _enum_body(indent, docs, attributes, values, dedent, config):
596  del indent, dedent  # Unused
597  spacing = [_Row('value-separator')] if _should_add_blank_lines(values) else []
598  columnized_values = _columnize(values, config.indent_width)
599  return _indent_rows(_intersperse(spacing,
600                                   [docs, attributes] + columnized_values))
601
602
603@_formats('enum-value* -> enum-value enum-value*')
604@_formats('enum-value+ -> enum-value enum-value*')
605def _enum_values(value, block):
606  return value + block
607
608
609@_formats('enum-value -> constant-name "=" expression attribute* doc? Comment? eol'
610          '              enum-value-body?')
611def _enum_value(name, equals, value, attributes, docs, comment, eol, body):
612  return [_Block([], _Row('enum-value', [name, equals, value, attributes, docs, comment]),
613                 eol + body)]
614
615
616@_formats('enum-value-body -> Indent doc-line* attribute-line* Dedent')
617def _enum_value_body(indent, docs, attributes, dedent):
618  del indent, dedent  # Unused
619  return _indent_rows(docs + attributes)
620
621
622@_formats('external-body -> Indent doc-line* attribute-line* Dedent')
623def _external_body(indent, docs, attributes, dedent):
624  del indent, dedent  # Unused
625  return _indent_rows(_intersperse([_Row('section-break')], [docs, attributes]))
626
627
628@_formats('comment-line -> Comment? "\\n"')
629def _comment_line(comment, eol):
630  del eol  # Unused
631  if comment:
632    return [_Row('comment', [comment])]
633  else:
634    return [_Row('comment')]
635
636
637@_formats('eol -> "\\n" comment-line*')
638def _eol(eol, comments):
639  del eol  # Unused
640  return _strip_empty_leading_trailing_comment_lines(comments)
641
642
643def _strip_empty_leading_trailing_comment_lines(comments):
644  first_non_empty_line = None
645  last_non_empty_line = None
646  for i in range(len(comments)):
647    if comments[i].columns:
648      if first_non_empty_line is None:
649        first_non_empty_line = i
650      last_non_empty_line = i
651  if first_non_empty_line is None:
652    return []
653  else:
654    return comments[first_non_empty_line:last_non_empty_line + 1]
655
656
657@_formats('attribute-line* -> ')
658@_formats('anonymous-bits-field-block -> ')
659@_formats('bits-field-block -> ')
660@_formats('comment-line* -> ')
661@_formats('doc-line* -> ')
662@_formats('enum-value* -> ')
663@_formats('enum-value-body? -> ')
664@_formats('field-body? -> ')
665@_formats('import-line* -> ')
666@_formats('struct-field-block -> ')
667@_formats('type-definition* -> ')
668@_formats('unconditional-anonymous-bits-field* -> ')
669@_formats('unconditional-bits-field* -> ')
670@_formats('unconditional-struct-field* -> ')
671def _empty_list():
672  return []
673
674
675@_formats('abbreviation? -> ')
676@_formats('additive-expression-right* -> ')
677@_formats('and-expression-right* -> ')
678@_formats('argument-list -> ')
679@_formats('array-length-specifier* -> ')
680@_formats('attribute* -> ')
681@_formats('attribute-context? -> ')
682@_formats('comma-then-expression* -> ')
683@_formats('Comment? -> ')
684@_formats('"$default"? -> ')
685@_formats('delimited-argument-list? -> ')
686@_formats('delimited-parameter-definition-list? -> ')
687@_formats('doc? -> ')
688@_formats('equality-expression-right* -> ')
689@_formats('equality-or-greater-expression-right* -> ')
690@_formats('equality-or-less-expression-right* -> ')
691@_formats('field-reference-tail* -> ')
692@_formats('or-expression-right* -> ')
693@_formats('parameter-definition-list -> ')
694@_formats('parameter-definition-list-tail* -> ')
695@_formats('times-expression-right* -> ')
696@_formats('type-size-specifier? -> ')
697def _empty_string():
698  return ''
699
700
701@_formats('abbreviation? -> abbreviation')
702@_formats('additive-operator -> "-"')
703@_formats('additive-operator -> "+"')
704@_formats('and-operator -> "&&"')
705@_formats('attribute-context? -> attribute-context')
706@_formats('attribute-value -> expression')
707@_formats('attribute-value -> string-constant')
708@_formats('boolean-constant -> BooleanConstant')
709@_formats('bottom-expression -> boolean-constant')
710@_formats('bottom-expression -> builtin-reference')
711@_formats('bottom-expression -> constant-reference')
712@_formats('bottom-expression -> field-reference')
713@_formats('bottom-expression -> numeric-constant')
714@_formats('builtin-field-word -> "$max_size_in_bits"')
715@_formats('builtin-field-word -> "$max_size_in_bytes"')
716@_formats('builtin-field-word -> "$min_size_in_bits"')
717@_formats('builtin-field-word -> "$min_size_in_bytes"')
718@_formats('builtin-field-word -> "$size_in_bits"')
719@_formats('builtin-field-word -> "$size_in_bytes"')
720@_formats('builtin-reference -> builtin-word')
721@_formats('builtin-word -> "$is_statically_sized"')
722@_formats('builtin-word -> "$next"')
723@_formats('builtin-word -> "$static_size_in_bits"')
724@_formats('choice-expression -> logical-expression')
725@_formats('Comment? -> Comment')
726@_formats('comparison-expression -> additive-expression')
727@_formats('constant-name -> constant-word')
728@_formats('constant-reference -> constant-reference-tail')
729@_formats('constant-reference-tail -> constant-word')
730@_formats('constant-word -> ShoutyWord')
731@_formats('"$default"? -> "$default"')
732@_formats('delimited-argument-list? -> delimited-argument-list')
733@_formats('doc? -> doc')
734@_formats('doc -> Documentation')
735@_formats('enum-value-body? -> enum-value-body')
736@_formats('equality-operator -> "=="')
737@_formats('equality-or-greater-expression-right -> equality-expression-right')
738@_formats('equality-or-greater-expression-right -> greater-expression-right')
739@_formats('equality-or-less-expression-right -> equality-expression-right')
740@_formats('equality-or-less-expression-right -> less-expression-right')
741@_formats('expression -> choice-expression')
742@_formats('field-body? -> field-body')
743@_formats('function-name -> "$lower_bound"')
744@_formats('function-name -> "$present"')
745@_formats('function-name -> "$max"')
746@_formats('function-name -> "$upper_bound"')
747@_formats('greater-operator -> ">="')
748@_formats('greater-operator -> ">"')
749@_formats('inequality-operator -> "!="')
750@_formats('less-operator -> "<="')
751@_formats('less-operator -> "<"')
752@_formats('logical-expression -> and-expression')
753@_formats('logical-expression -> comparison-expression')
754@_formats('logical-expression -> or-expression')
755@_formats('multiplicative-operator -> "*"')
756@_formats('negation-expression -> bottom-expression')
757@_formats('numeric-constant -> Number')
758@_formats('or-operator -> "||"')
759@_formats('snake-name -> snake-word')
760@_formats('snake-reference -> builtin-field-word')
761@_formats('snake-reference -> snake-word')
762@_formats('snake-word -> SnakeWord')
763@_formats('string-constant -> String')
764@_formats('type-definition -> bits')
765@_formats('type-definition -> enum')
766@_formats('type-definition -> external')
767@_formats('type-definition -> struct')
768@_formats('type-name -> type-word')
769@_formats('type-reference-tail -> type-word')
770@_formats('type-reference -> type-reference-tail')
771@_formats('type-size-specifier? -> type-size-specifier')
772@_formats('type-word -> CamelWord')
773@_formats('unconditional-anonymous-bits-field -> field')
774@_formats('unconditional-anonymous-bits-field -> inline-bits-field-definition')
775@_formats('unconditional-anonymous-bits-field -> inline-enum-field-definition')
776@_formats('unconditional-bits-field -> unconditional-anonymous-bits-field')
777@_formats('unconditional-bits-field -> virtual-field')
778@_formats('unconditional-struct-field -> anonymous-bits-field-definition')
779@_formats('unconditional-struct-field -> field')
780@_formats('unconditional-struct-field -> inline-bits-field-definition')
781@_formats('unconditional-struct-field -> inline-enum-field-definition')
782@_formats('unconditional-struct-field -> inline-struct-field-definition')
783@_formats('unconditional-struct-field -> virtual-field')
784def _identity(x):
785  return x
786
787
788@_formats('argument-list -> expression comma-then-expression*')
789@_formats('times-expression -> negation-expression times-expression-right*')
790@_formats('type -> type-reference delimited-argument-list? type-size-specifier?'
791          '        array-length-specifier*')
792@_formats('array-length-specifier -> "[" expression "]"')
793@_formats('array-length-specifier* -> array-length-specifier'
794          '                           array-length-specifier*')
795@_formats('type-size-specifier -> ":" numeric-constant')
796@_formats('attribute-context -> "(" snake-word ")"')
797@_formats('constant-reference -> snake-reference "." constant-reference-tail')
798@_formats('constant-reference-tail -> type-word "." constant-reference-tail')
799@_formats('constant-reference-tail -> type-word "." snake-reference')
800@_formats('type-reference-tail -> type-word "." type-reference-tail')
801@_formats('field-reference -> snake-reference field-reference-tail*')
802@_formats('abbreviation -> "(" snake-word ")"')
803@_formats('additive-expression-right -> additive-operator times-expression')
804@_formats('additive-expression-right* -> additive-expression-right'
805          '                              additive-expression-right*')
806@_formats('additive-expression -> times-expression additive-expression-right*')
807@_formats('array-length-specifier -> "[" "]"')
808@_formats('delimited-argument-list -> "(" argument-list ")"')
809@_formats('delimited-parameter-definition-list? ->'
810          '    delimited-parameter-definition-list')
811@_formats('delimited-parameter-definition-list ->'
812          '    "(" parameter-definition-list ")"')
813@_formats('parameter-definition-list -> parameter-definition'
814          '                             parameter-definition-list-tail*')
815@_formats('parameter-definition-list-tail* -> parameter-definition-list-tail'
816          '                                   parameter-definition-list-tail*')
817@_formats('times-expression-right -> multiplicative-operator'
818          '                          negation-expression')
819@_formats('times-expression-right* -> times-expression-right'
820          '                           times-expression-right*')
821@_formats('field-reference-tail -> "." snake-reference')
822@_formats('field-reference-tail* -> field-reference-tail field-reference-tail*')
823@_formats('negation-expression -> additive-operator bottom-expression')
824@_formats('type-reference -> snake-word "." type-reference-tail')
825@_formats('bottom-expression -> "(" expression ")"')
826@_formats('bottom-expression -> function-name "(" argument-list ")"')
827@_formats('comma-then-expression* -> comma-then-expression'
828          '                          comma-then-expression*')
829@_formats('or-expression-right* -> or-expression-right or-expression-right*')
830@_formats('less-expression-right-list -> equality-expression-right*'
831          '                              less-expression-right'
832          '                              equality-or-less-expression-right*')
833@_formats('or-expression-right+ -> or-expression-right or-expression-right*')
834@_formats('and-expression -> comparison-expression and-expression-right+')
835@_formats('comparison-expression -> additive-expression'
836          '                         greater-expression-right-list')
837@_formats('comparison-expression -> additive-expression'
838          '                         equality-expression-right+')
839@_formats('or-expression -> comparison-expression or-expression-right+')
840@_formats('equality-expression-right+ -> equality-expression-right'
841          '                              equality-expression-right*')
842@_formats('and-expression-right* -> and-expression-right and-expression-right*')
843@_formats('equality-or-greater-expression-right* ->'
844          '    equality-or-greater-expression-right'
845          '    equality-or-greater-expression-right*')
846@_formats('and-expression-right+ -> and-expression-right and-expression-right*')
847@_formats('equality-or-less-expression-right* ->'
848          '    equality-or-less-expression-right'
849          '    equality-or-less-expression-right*')
850@_formats('equality-expression-right* -> equality-expression-right'
851          '                              equality-expression-right*')
852@_formats('greater-expression-right-list ->'
853          '    equality-expression-right* greater-expression-right'
854          '    equality-or-greater-expression-right*')
855@_formats('comparison-expression -> additive-expression'
856          '                         less-expression-right-list')
857def _concatenate(*elements):
858  """Concatenates all arguments with no delimiters."""
859  return ''.join(elements)
860
861
862@_formats('equality-expression-right -> equality-operator additive-expression')
863@_formats('less-expression-right -> less-operator additive-expression')
864@_formats('greater-expression-right -> greater-operator additive-expression')
865@_formats('or-expression-right -> or-operator comparison-expression')
866@_formats('and-expression-right -> and-operator comparison-expression')
867def _concatenate_with_prefix_spaces(*elements):
868  return ''.join(' ' + element for element in elements if element)
869
870
871@_formats('attribute* -> attribute attribute*')
872@_formats('comma-then-expression -> "," expression')
873@_formats('comparison-expression -> additive-expression inequality-operator'
874          '                         additive-expression')
875@_formats('choice-expression -> logical-expression "?" logical-expression'
876          '                                        ":" logical-expression')
877@_formats('parameter-definition-list-tail -> "," parameter-definition')
878def _concatenate_with_spaces(*elements):
879  return _concatenate_with(' ', *elements)
880
881
882def _concatenate_with(joiner, *elements):
883  return joiner.join(element for element in elements if element)
884
885
886@_formats('attribute-line* -> attribute-line attribute-line*')
887@_formats('comment-line* -> comment-line comment-line*')
888@_formats('doc-line* -> doc-line doc-line*')
889@_formats('import-line* -> import-line import-line*')
890def _concatenate_lists(head, tail):
891  return head + tail
892
893
894_check_productions()
895