1*4bdc9457SAndroid Build Coastguard Worker#!/usr/bin/env python 2*4bdc9457SAndroid Build Coastguard Worker# Copyright 2019 Google LLC 3*4bdc9457SAndroid Build Coastguard Worker# 4*4bdc9457SAndroid Build Coastguard Worker# This source code is licensed under the BSD-style license found in the 5*4bdc9457SAndroid Build Coastguard Worker# LICENSE file in the root directory of this source tree. 6*4bdc9457SAndroid Build Coastguard Worker 7*4bdc9457SAndroid Build Coastguard Workerimport argparse 8*4bdc9457SAndroid Build Coastguard Workerimport codecs 9*4bdc9457SAndroid Build Coastguard Workerimport io 10*4bdc9457SAndroid Build Coastguard Workerimport os 11*4bdc9457SAndroid Build Coastguard Workerimport re 12*4bdc9457SAndroid Build Coastguard Workerimport sys 13*4bdc9457SAndroid Build Coastguard Workerfrom itertools import chain 14*4bdc9457SAndroid Build Coastguard Worker 15*4bdc9457SAndroid Build Coastguard Worker 16*4bdc9457SAndroid Build Coastguard Workerdef key_value_pair(line): 17*4bdc9457SAndroid Build Coastguard Worker key, value = line.split("=", 1) 18*4bdc9457SAndroid Build Coastguard Worker # represent value as integer, if possible, otherwise as str 19*4bdc9457SAndroid Build Coastguard Worker try: 20*4bdc9457SAndroid Build Coastguard Worker value = int(value) 21*4bdc9457SAndroid Build Coastguard Worker except ValueError: 22*4bdc9457SAndroid Build Coastguard Worker pass 23*4bdc9457SAndroid Build Coastguard Worker return key, value 24*4bdc9457SAndroid Build Coastguard Worker 25*4bdc9457SAndroid Build Coastguard Worker 26*4bdc9457SAndroid Build Coastguard Workerparser = argparse.ArgumentParser(description='XNNPACK generator') 27*4bdc9457SAndroid Build Coastguard Workerparser.add_argument("input", metavar="FILE", nargs=1, 28*4bdc9457SAndroid Build Coastguard Worker help="Input file") 29*4bdc9457SAndroid Build Coastguard Workerparser.add_argument("-D", dest="defines", metavar="KEY=VALUE", nargs="*", 30*4bdc9457SAndroid Build Coastguard Worker type=key_value_pair, action="append", 31*4bdc9457SAndroid Build Coastguard Worker help="Predefined variables") 32*4bdc9457SAndroid Build Coastguard Workerparser.add_argument("-o", "--output", 33*4bdc9457SAndroid Build Coastguard Worker help='Output file') 34*4bdc9457SAndroid Build Coastguard Workerparser.set_defaults(defines=list()) 35*4bdc9457SAndroid Build Coastguard Worker 36*4bdc9457SAndroid Build Coastguard Worker 37*4bdc9457SAndroid Build Coastguard WorkerLEADING_WHITESPACE_REGEX = re.compile(r"^\s*", flags=0) 38*4bdc9457SAndroid Build Coastguard Worker 39*4bdc9457SAndroid Build Coastguard Worker 40*4bdc9457SAndroid Build Coastguard Workerdef extract_leading_whitespace(line): 41*4bdc9457SAndroid Build Coastguard Worker match = re.match(r"\s*", line) 42*4bdc9457SAndroid Build Coastguard Worker return match.group(0) if match else "" 43*4bdc9457SAndroid Build Coastguard Worker 44*4bdc9457SAndroid Build Coastguard Worker 45*4bdc9457SAndroid Build Coastguard Workerdef escape(line): 46*4bdc9457SAndroid Build Coastguard Worker output_parts = [] 47*4bdc9457SAndroid Build Coastguard Worker while "${" in line: 48*4bdc9457SAndroid Build Coastguard Worker start_pos = line.index("${") 49*4bdc9457SAndroid Build Coastguard Worker end_pos = line.index("}", start_pos + 2) 50*4bdc9457SAndroid Build Coastguard Worker if start_pos != 0: 51*4bdc9457SAndroid Build Coastguard Worker output_parts.append("\"" + line[:start_pos].replace("\"", "\\\"") + "\"") 52*4bdc9457SAndroid Build Coastguard Worker output_parts.append("str(" + line[start_pos+2:end_pos] + ")") 53*4bdc9457SAndroid Build Coastguard Worker line = line[end_pos+1:] 54*4bdc9457SAndroid Build Coastguard Worker if line: 55*4bdc9457SAndroid Build Coastguard Worker output_parts.append("\"" + line.replace("\"", "\\\"") + "\"") 56*4bdc9457SAndroid Build Coastguard Worker return " + ".join(output_parts) 57*4bdc9457SAndroid Build Coastguard Worker 58*4bdc9457SAndroid Build Coastguard Worker 59*4bdc9457SAndroid Build Coastguard Workerdef preprocess(input_text, input_globals, input_path="codegen"): 60*4bdc9457SAndroid Build Coastguard Worker input_lines = input_text.splitlines() 61*4bdc9457SAndroid Build Coastguard Worker python_lines = ["from __future__ import print_function"] 62*4bdc9457SAndroid Build Coastguard Worker 63*4bdc9457SAndroid Build Coastguard Worker blank_lines = 0 64*4bdc9457SAndroid Build Coastguard Worker 65*4bdc9457SAndroid Build Coastguard Worker last_line = "" 66*4bdc9457SAndroid Build Coastguard Worker last_indent = "" 67*4bdc9457SAndroid Build Coastguard Worker 68*4bdc9457SAndroid Build Coastguard Worker # List of tuples (total_index, python_indent) 69*4bdc9457SAndroid Build Coastguard Worker indent_stack = [("", "")] 70*4bdc9457SAndroid Build Coastguard Worker 71*4bdc9457SAndroid Build Coastguard Worker # Indicates whether this is the first line inside Python 72*4bdc9457SAndroid Build Coastguard Worker # code block (i.e. for, while, if, elif, else) 73*4bdc9457SAndroid Build Coastguard Worker python_block_start = True 74*4bdc9457SAndroid Build Coastguard Worker for i, input_line in enumerate(input_lines): 75*4bdc9457SAndroid Build Coastguard Worker if input_line == "": 76*4bdc9457SAndroid Build Coastguard Worker blank_lines += 1 77*4bdc9457SAndroid Build Coastguard Worker continue 78*4bdc9457SAndroid Build Coastguard Worker # Skip lint markers. 79*4bdc9457SAndroid Build Coastguard Worker if 'LINT' in input_line: 80*4bdc9457SAndroid Build Coastguard Worker continue 81*4bdc9457SAndroid Build Coastguard Worker 82*4bdc9457SAndroid Build Coastguard Worker input_indent = extract_leading_whitespace(input_line) 83*4bdc9457SAndroid Build Coastguard Worker if python_block_start: 84*4bdc9457SAndroid Build Coastguard Worker assert input_indent.startswith(last_indent) 85*4bdc9457SAndroid Build Coastguard Worker extra_python_indent = input_indent[len(last_indent):] 86*4bdc9457SAndroid Build Coastguard Worker python_indent = indent_stack[-1][1] + extra_python_indent 87*4bdc9457SAndroid Build Coastguard Worker indent_stack.append((input_indent, python_indent)) 88*4bdc9457SAndroid Build Coastguard Worker assert input_indent.startswith(indent_stack[-1][0]) 89*4bdc9457SAndroid Build Coastguard Worker else: 90*4bdc9457SAndroid Build Coastguard Worker while not input_indent.startswith(indent_stack[-1][0]): 91*4bdc9457SAndroid Build Coastguard Worker del indent_stack[-1] 92*4bdc9457SAndroid Build Coastguard Worker python_block_start = False 93*4bdc9457SAndroid Build Coastguard Worker 94*4bdc9457SAndroid Build Coastguard Worker python_indent = indent_stack[-1][1] 95*4bdc9457SAndroid Build Coastguard Worker stripped_input_line = input_line.strip() 96*4bdc9457SAndroid Build Coastguard Worker if stripped_input_line.startswith("$") and not stripped_input_line.startswith("${"): 97*4bdc9457SAndroid Build Coastguard Worker if stripped_input_line.endswith(":"): 98*4bdc9457SAndroid Build Coastguard Worker python_block_start = True 99*4bdc9457SAndroid Build Coastguard Worker while blank_lines != 0: 100*4bdc9457SAndroid Build Coastguard Worker python_lines.append(python_indent + "print(file=OUT_STREAM)") 101*4bdc9457SAndroid Build Coastguard Worker blank_lines -= 1 102*4bdc9457SAndroid Build Coastguard Worker python_lines.append(python_indent + stripped_input_line.replace("$", "")) 103*4bdc9457SAndroid Build Coastguard Worker else: 104*4bdc9457SAndroid Build Coastguard Worker assert input_line.startswith(python_indent) 105*4bdc9457SAndroid Build Coastguard Worker while blank_lines != 0: 106*4bdc9457SAndroid Build Coastguard Worker python_lines.append(python_indent + "print(file=OUT_STREAM)") 107*4bdc9457SAndroid Build Coastguard Worker blank_lines -= 1 108*4bdc9457SAndroid Build Coastguard Worker python_lines.append(python_indent + "print(%s, file=OUT_STREAM)" % escape(input_line[len(python_indent):])) 109*4bdc9457SAndroid Build Coastguard Worker last_line = input_line 110*4bdc9457SAndroid Build Coastguard Worker last_indent = input_indent 111*4bdc9457SAndroid Build Coastguard Worker 112*4bdc9457SAndroid Build Coastguard Worker while blank_lines != 0: 113*4bdc9457SAndroid Build Coastguard Worker python_lines.append(python_indent + "print(file=OUT_STREAM)") 114*4bdc9457SAndroid Build Coastguard Worker blank_lines -= 1 115*4bdc9457SAndroid Build Coastguard Worker 116*4bdc9457SAndroid Build Coastguard Worker exec_globals = dict(input_globals) 117*4bdc9457SAndroid Build Coastguard Worker if sys.version_info > (3, 0): 118*4bdc9457SAndroid Build Coastguard Worker output_stream = io.StringIO() 119*4bdc9457SAndroid Build Coastguard Worker else: 120*4bdc9457SAndroid Build Coastguard Worker output_stream = io.BytesIO() 121*4bdc9457SAndroid Build Coastguard Worker exec_globals["OUT_STREAM"] = output_stream 122*4bdc9457SAndroid Build Coastguard Worker python_bytecode = compile("\n".join(python_lines), input_path, 'exec') 123*4bdc9457SAndroid Build Coastguard Worker exec(python_bytecode, exec_globals) 124*4bdc9457SAndroid Build Coastguard Worker 125*4bdc9457SAndroid Build Coastguard Worker return output_stream.getvalue() 126*4bdc9457SAndroid Build Coastguard Worker 127*4bdc9457SAndroid Build Coastguard Worker 128*4bdc9457SAndroid Build Coastguard WorkerPREAMBLE = """\ 129*4bdc9457SAndroid Build Coastguard Worker// Auto-generated file. Do not edit! 130*4bdc9457SAndroid Build Coastguard Worker// Template: {template} 131*4bdc9457SAndroid Build Coastguard Worker// Generator: {generator} 132*4bdc9457SAndroid Build Coastguard Worker// 133*4bdc9457SAndroid Build Coastguard Worker""" 134*4bdc9457SAndroid Build Coastguard Worker 135*4bdc9457SAndroid Build Coastguard Worker 136*4bdc9457SAndroid Build Coastguard Workerdef main(args): 137*4bdc9457SAndroid Build Coastguard Worker options = parser.parse_args(args) 138*4bdc9457SAndroid Build Coastguard Worker 139*4bdc9457SAndroid Build Coastguard Worker input_text = codecs.open(options.input[0], "r", encoding="utf-8").read() 140*4bdc9457SAndroid Build Coastguard Worker python_globals = dict(chain(*options.defines)) 141*4bdc9457SAndroid Build Coastguard Worker output_text = PREAMBLE.format(template=options.input[0], generator=sys.argv[0]) + preprocess(input_text, python_globals, options.input[0]) 142*4bdc9457SAndroid Build Coastguard Worker 143*4bdc9457SAndroid Build Coastguard Worker txt_changed = True 144*4bdc9457SAndroid Build Coastguard Worker if os.path.exists(options.output): 145*4bdc9457SAndroid Build Coastguard Worker with codecs.open(options.output, "r", encoding="utf-8") as output_file: 146*4bdc9457SAndroid Build Coastguard Worker txt_changed = output_file.read() != output_text 147*4bdc9457SAndroid Build Coastguard Worker 148*4bdc9457SAndroid Build Coastguard Worker if txt_changed: 149*4bdc9457SAndroid Build Coastguard Worker with codecs.open(options.output, "w", encoding="utf-8") as output_file: 150*4bdc9457SAndroid Build Coastguard Worker output_file.write(output_text) 151*4bdc9457SAndroid Build Coastguard Worker 152*4bdc9457SAndroid Build Coastguard Workerif __name__ == "__main__": 153*4bdc9457SAndroid Build Coastguard Worker main(sys.argv[1:]) 154