1*99e0aae7SDavid Rees# Copyright 2019 Google LLC 2*99e0aae7SDavid Rees# 3*99e0aae7SDavid Rees# Licensed under the Apache License, Version 2.0 (the "License"); 4*99e0aae7SDavid Rees# you may not use this file except in compliance with the License. 5*99e0aae7SDavid Rees# You may obtain a copy of the License at 6*99e0aae7SDavid Rees# 7*99e0aae7SDavid Rees# https://www.apache.org/licenses/LICENSE-2.0 8*99e0aae7SDavid Rees# 9*99e0aae7SDavid Rees# Unless required by applicable law or agreed to in writing, software 10*99e0aae7SDavid Rees# distributed under the License is distributed on an "AS IS" BASIS, 11*99e0aae7SDavid Rees# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*99e0aae7SDavid Rees# See the License for the specific language governing permissions and 13*99e0aae7SDavid Rees# limitations under the License. 14*99e0aae7SDavid Rees 15*99e0aae7SDavid Rees"""Formatter for Emboss source files. 16*99e0aae7SDavid Rees 17*99e0aae7SDavid ReesThis program formats an Emboss source file given on the command line. 18*99e0aae7SDavid Rees""" 19*99e0aae7SDavid Rees 20*99e0aae7SDavid Reesfrom __future__ import absolute_import 21*99e0aae7SDavid Reesfrom __future__ import print_function 22*99e0aae7SDavid Rees 23*99e0aae7SDavid Reesimport argparse 24*99e0aae7SDavid Reesimport os 25*99e0aae7SDavid Reesimport sys 26*99e0aae7SDavid Rees 27*99e0aae7SDavid Reesfrom compiler.front_end import format_emb 28*99e0aae7SDavid Reesfrom compiler.front_end import parser 29*99e0aae7SDavid Reesfrom compiler.front_end import tokenizer 30*99e0aae7SDavid Reesfrom compiler.util import error 31*99e0aae7SDavid Rees 32*99e0aae7SDavid Rees 33*99e0aae7SDavid Reesdef _parse_command_line(argv): 34*99e0aae7SDavid Rees """Parses the given command-line arguments.""" 35*99e0aae7SDavid Rees argparser = argparse.ArgumentParser(description='Emboss compiler front end.', 36*99e0aae7SDavid Rees prog=argv[0]) 37*99e0aae7SDavid Rees argparser.add_argument('input_file', 38*99e0aae7SDavid Rees type=str, 39*99e0aae7SDavid Rees nargs='+', 40*99e0aae7SDavid Rees help='.emb file to compile.') 41*99e0aae7SDavid Rees argparser.add_argument('--no-check-result', 42*99e0aae7SDavid Rees default=True, 43*99e0aae7SDavid Rees action='store_false', 44*99e0aae7SDavid Rees dest='check_result', 45*99e0aae7SDavid Rees help='Verify that the resulting formatted text ' 46*99e0aae7SDavid Rees 'contains only whitespace changes.') 47*99e0aae7SDavid Rees argparser.add_argument('--debug-show-line-types', 48*99e0aae7SDavid Rees default=False, 49*99e0aae7SDavid Rees help='Show the computed type of each line.') 50*99e0aae7SDavid Rees argparser.add_argument('--no-edit-in-place', 51*99e0aae7SDavid Rees default=True, 52*99e0aae7SDavid Rees action='store_false', 53*99e0aae7SDavid Rees dest='edit_in_place', 54*99e0aae7SDavid Rees help='Write the formatted text back to the input ' 55*99e0aae7SDavid Rees 'file.') 56*99e0aae7SDavid Rees argparser.add_argument('--indent', 57*99e0aae7SDavid Rees type=int, 58*99e0aae7SDavid Rees default=2, 59*99e0aae7SDavid Rees help='Number of spaces to use for each level of ' 60*99e0aae7SDavid Rees 'indentation.') 61*99e0aae7SDavid Rees argparser.add_argument('--color-output', 62*99e0aae7SDavid Rees default='if-tty', 63*99e0aae7SDavid Rees choices=['always', 'never', 'if-tty', 'auto'], 64*99e0aae7SDavid Rees help="Print error messages using color. 'auto' is a " 65*99e0aae7SDavid Rees "synonym for 'if-tty'.") 66*99e0aae7SDavid Rees return argparser.parse_args(argv[1:]) 67*99e0aae7SDavid Rees 68*99e0aae7SDavid Rees 69*99e0aae7SDavid Reesdef _print_errors(errors, source_codes, flags): 70*99e0aae7SDavid Rees use_color = (flags.color_output == 'always' or 71*99e0aae7SDavid Rees (flags.color_output in ('auto', 'if-tty') and 72*99e0aae7SDavid Rees os.isatty(sys.stderr.fileno()))) 73*99e0aae7SDavid Rees print(error.format_errors(errors, source_codes, use_color), file=sys.stderr) 74*99e0aae7SDavid Rees 75*99e0aae7SDavid Rees 76*99e0aae7SDavid Reesdef main(argv=()): 77*99e0aae7SDavid Rees flags = _parse_command_line(argv) 78*99e0aae7SDavid Rees 79*99e0aae7SDavid Rees if not flags.edit_in_place and len(flags.input_file) > 1: 80*99e0aae7SDavid Rees print('Multiple files may only be formatted without --no-edit-in-place.', 81*99e0aae7SDavid Rees file=sys.stderr) 82*99e0aae7SDavid Rees return 1 83*99e0aae7SDavid Rees 84*99e0aae7SDavid Rees if flags.edit_in_place and flags.debug_show_line_types: 85*99e0aae7SDavid Rees print('The flag --debug-show-line-types requires --no-edit-in-place.', 86*99e0aae7SDavid Rees file=sys.stderr) 87*99e0aae7SDavid Rees return 1 88*99e0aae7SDavid Rees 89*99e0aae7SDavid Rees for file_name in flags.input_file: 90*99e0aae7SDavid Rees with open(file_name) as f: 91*99e0aae7SDavid Rees source_code = f.read() 92*99e0aae7SDavid Rees 93*99e0aae7SDavid Rees tokens, errors = tokenizer.tokenize(source_code, file_name) 94*99e0aae7SDavid Rees if errors: 95*99e0aae7SDavid Rees _print_errors(errors, {file_name: source_code}, flags) 96*99e0aae7SDavid Rees continue 97*99e0aae7SDavid Rees 98*99e0aae7SDavid Rees parse_result = parser.parse_module(tokens) 99*99e0aae7SDavid Rees if parse_result.error: 100*99e0aae7SDavid Rees _print_errors( 101*99e0aae7SDavid Rees [error.make_error_from_parse_error(file_name, parse_result.error)], 102*99e0aae7SDavid Rees {file_name: source_code}, 103*99e0aae7SDavid Rees flags) 104*99e0aae7SDavid Rees continue 105*99e0aae7SDavid Rees 106*99e0aae7SDavid Rees formatted_text = format_emb.format_emboss_parse_tree( 107*99e0aae7SDavid Rees parse_result.parse_tree, 108*99e0aae7SDavid Rees format_emb.Config(show_line_types=flags.debug_show_line_types, 109*99e0aae7SDavid Rees indent_width=flags.indent)) 110*99e0aae7SDavid Rees 111*99e0aae7SDavid Rees if flags.check_result and not flags.debug_show_line_types: 112*99e0aae7SDavid Rees errors = format_emb.sanity_check_format_result(formatted_text, 113*99e0aae7SDavid Rees source_code) 114*99e0aae7SDavid Rees if errors: 115*99e0aae7SDavid Rees for e in errors: 116*99e0aae7SDavid Rees print(e, file=sys.stderr) 117*99e0aae7SDavid Rees continue 118*99e0aae7SDavid Rees 119*99e0aae7SDavid Rees if flags.edit_in_place: 120*99e0aae7SDavid Rees with open(file_name, 'w') as f: 121*99e0aae7SDavid Rees f.write(formatted_text) 122*99e0aae7SDavid Rees else: 123*99e0aae7SDavid Rees sys.stdout.write(formatted_text) 124*99e0aae7SDavid Rees 125*99e0aae7SDavid Rees return 0 126*99e0aae7SDavid Rees 127*99e0aae7SDavid Rees 128*99e0aae7SDavid Reesif __name__ == '__main__': 129*99e0aae7SDavid Rees sys.exit(main(sys.argv)) 130