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