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