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