xref: /aosp_15_r20/external/emboss/compiler/front_end/format.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 program formats an Emboss source file given on the command line.
18"""
19
20from __future__ import absolute_import
21from __future__ import print_function
22
23import argparse
24import os
25import sys
26
27from compiler.front_end import format_emb
28from compiler.front_end import parser
29from compiler.front_end import tokenizer
30from compiler.util import error
31
32
33def _parse_command_line(argv):
34  """Parses the given command-line arguments."""
35  argparser = argparse.ArgumentParser(description='Emboss compiler front end.',
36                                      prog=argv[0])
37  argparser.add_argument('input_file',
38                         type=str,
39                         nargs='+',
40                         help='.emb file to compile.')
41  argparser.add_argument('--no-check-result',
42                         default=True,
43                         action='store_false',
44                         dest='check_result',
45                         help='Verify that the resulting formatted text '
46                         'contains only whitespace changes.')
47  argparser.add_argument('--debug-show-line-types',
48                         default=False,
49                         help='Show the computed type of each line.')
50  argparser.add_argument('--no-edit-in-place',
51                         default=True,
52                         action='store_false',
53                         dest='edit_in_place',
54                         help='Write the formatted text back to the input '
55                              'file.')
56  argparser.add_argument('--indent',
57                         type=int,
58                         default=2,
59                         help='Number of spaces to use for each level of '
60                         'indentation.')
61  argparser.add_argument('--color-output',
62                         default='if-tty',
63                         choices=['always', 'never', 'if-tty', 'auto'],
64                         help="Print error messages using color.  'auto' is a "
65                         "synonym for 'if-tty'.")
66  return argparser.parse_args(argv[1:])
67
68
69def _print_errors(errors, source_codes, flags):
70  use_color = (flags.color_output == 'always' or
71               (flags.color_output in ('auto', 'if-tty') and
72                os.isatty(sys.stderr.fileno())))
73  print(error.format_errors(errors, source_codes, use_color), file=sys.stderr)
74
75
76def main(argv=()):
77  flags = _parse_command_line(argv)
78
79  if not flags.edit_in_place and len(flags.input_file) > 1:
80    print('Multiple files may only be formatted without --no-edit-in-place.',
81          file=sys.stderr)
82    return 1
83
84  if flags.edit_in_place and flags.debug_show_line_types:
85    print('The flag --debug-show-line-types requires --no-edit-in-place.',
86          file=sys.stderr)
87    return 1
88
89  for file_name in flags.input_file:
90    with open(file_name) as f:
91      source_code = f.read()
92
93    tokens, errors = tokenizer.tokenize(source_code, file_name)
94    if errors:
95      _print_errors(errors, {file_name: source_code}, flags)
96      continue
97
98    parse_result = parser.parse_module(tokens)
99    if parse_result.error:
100      _print_errors(
101          [error.make_error_from_parse_error(file_name, parse_result.error)],
102          {file_name: source_code},
103          flags)
104      continue
105
106    formatted_text = format_emb.format_emboss_parse_tree(
107        parse_result.parse_tree,
108        format_emb.Config(show_line_types=flags.debug_show_line_types,
109                          indent_width=flags.indent))
110
111    if flags.check_result and not flags.debug_show_line_types:
112      errors = format_emb.sanity_check_format_result(formatted_text,
113                                                     source_code)
114      if errors:
115        for e in errors:
116          print(e, file=sys.stderr)
117        continue
118
119    if flags.edit_in_place:
120      with open(file_name, 'w') as f:
121        f.write(formatted_text)
122    else:
123      sys.stdout.write(formatted_text)
124
125  return 0
126
127
128if __name__ == '__main__':
129  sys.exit(main(sys.argv))
130