| #!/usr/bin/env python |
| # Copyright 2019 Google LLC |
| # |
| # This source code is licensed under the BSD-style license found in the |
| # LICENSE file in the root directory of this source tree. |
| |
| import argparse |
| import codecs |
| import io |
| import re |
| import sys |
| from itertools import chain |
| |
| |
| def key_value_pair(line): |
| key, value = line.split("=", 1) |
| # represent value as integer, if possible, otherwise as str |
| try: |
| value = int(value) |
| except ValueError: |
| pass |
| return key, value |
| |
| |
| parser = argparse.ArgumentParser(description='XNNPACK generator') |
| parser.add_argument("input", metavar="FILE", nargs=1, |
| help="Input file") |
| parser.add_argument("-D", dest="defines", metavar="KEY=VALUE", nargs="*", |
| type=key_value_pair, action="append", |
| help="Predefined variables") |
| parser.add_argument("-o", "--output", |
| help='Output file') |
| parser.set_defaults(defines=list()) |
| |
| |
| LEADING_WHITESPACE_REGEX = re.compile(r"^\s*", flags=0) |
| |
| |
| def extract_leading_whitespace(line): |
| match = re.match(r"\s*", line) |
| return match.group(0) if match else "" |
| |
| |
| def escape(line): |
| output_parts = [] |
| while "${" in line: |
| start_pos = line.index("${") |
| end_pos = line.index("}", start_pos + 2) |
| if start_pos != 0: |
| output_parts.append("\"" + line[:start_pos].replace("\"", "\\\"") + "\"") |
| output_parts.append("str(" + line[start_pos+2:end_pos] + ")") |
| line = line[end_pos+1:] |
| if line: |
| output_parts.append("\"" + line.replace("\"", "\\\"") + "\"") |
| return " + ".join(output_parts) |
| |
| |
| def preprocess(input_text, input_globals, input_path="codegen"): |
| input_lines = input_text.splitlines() |
| python_lines = ["from __future__ import print_function"] |
| |
| blank_lines = 0 |
| |
| last_line = "" |
| last_indent = "" |
| |
| # List of tuples (total_index, python_indent) |
| indent_stack = [("", "")] |
| |
| # Indicates whether this is the first line inside Python |
| # code block (i.e. for, while, if, elif, else) |
| python_block_start = True |
| for i, input_line in enumerate(input_lines): |
| if input_line == "": |
| blank_lines += 1 |
| continue |
| |
| input_indent = extract_leading_whitespace(input_line) |
| if python_block_start: |
| assert input_indent.startswith(last_indent) |
| extra_python_indent = input_indent[len(last_indent):] |
| python_indent = indent_stack[-1][1] + extra_python_indent |
| indent_stack.append((input_indent, python_indent)) |
| assert input_indent.startswith(indent_stack[-1][0]) |
| else: |
| while not input_indent.startswith(indent_stack[-1][0]): |
| del indent_stack[-1] |
| python_block_start = False |
| |
| python_indent = indent_stack[-1][1] |
| stripped_input_line = input_line.strip() |
| if stripped_input_line.startswith("$") and not stripped_input_line.startswith("${"): |
| if stripped_input_line.endswith(":"): |
| python_block_start = True |
| while blank_lines != 0: |
| python_lines.append(python_indent + "print(file=OUT_STREAM)") |
| blank_lines -= 1 |
| python_lines.append(python_indent + stripped_input_line.replace("$", "")) |
| else: |
| assert input_line.startswith(python_indent) |
| while blank_lines != 0: |
| python_lines.append(python_indent + "print(file=OUT_STREAM)") |
| blank_lines -= 1 |
| python_lines.append(python_indent + "print(%s, file=OUT_STREAM)" % escape(input_line[len(python_indent):])) |
| last_line = input_line |
| last_indent = input_indent |
| |
| while blank_lines != 0: |
| python_lines.append(python_indent + "print(file=OUT_STREAM)") |
| blank_lines -= 1 |
| |
| exec_globals = dict(input_globals) |
| if sys.version_info > (3, 0): |
| output_stream = io.StringIO() |
| else: |
| output_stream = io.BytesIO() |
| exec_globals["OUT_STREAM"] = output_stream |
| python_bytecode = compile("\n".join(python_lines), input_path, 'exec') |
| exec(python_bytecode, exec_globals) |
| |
| return output_stream.getvalue() |
| |
| |
| PREAMBLE = """\ |
| // Auto-generated file. Do not edit! |
| // Template: {template} |
| // Generator: {generator} |
| // |
| """ |
| |
| |
| def main(args): |
| options = parser.parse_args(args) |
| |
| input_text = codecs.open(options.input[0], "r", encoding="utf-8").read() |
| python_globals = dict(chain(*options.defines)) |
| output_text = preprocess(input_text, python_globals, options.input[0]) |
| |
| with codecs.open(options.output, "w", encoding="utf-8") as output_file: |
| output_file.write(PREAMBLE.format( |
| template=options.input[0], generator=sys.argv[0])) |
| output_file.write(output_text) |
| |
| |
| if __name__ == "__main__": |
| main(sys.argv[1:]) |