You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
147 lines
4.3 KiB
147 lines
4.3 KiB
4 months ago
|
#!/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:])
|