blob: 07d008940143a518e98567d04e9ebf61f71374c2 [file] [log] [blame]
XNNPACK Teamb455b122019-09-27 18:10:33 -07001#!/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
Marat Dukhane0970282019-11-13 12:01:29 -080058def preprocess(input_text, input_globals, input_path="codegen"):
XNNPACK Teamb455b122019-09-27 18:10:33 -070059 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)
Marat Dukhane0970282019-11-13 12:01:29 -0800113 if sys.version_info > (3, 0):
114 output_stream = io.StringIO()
115 else:
116 output_stream = io.BytesIO()
XNNPACK Teamb455b122019-09-27 18:10:33 -0700117 exec_globals["OUT_STREAM"] = output_stream
Marat Dukhane0970282019-11-13 12:01:29 -0800118 python_bytecode = compile("\n".join(python_lines), input_path, 'exec')
119 exec(python_bytecode, exec_globals)
XNNPACK Teamb455b122019-09-27 18:10:33 -0700120
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))
Marat Dukhane0970282019-11-13 12:01:29 -0800137 output_text = preprocess(input_text, python_globals, options.input[0])
XNNPACK Teamb455b122019-09-27 18:10:33 -0700138
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:])