pytorch/tools/codegen/code_template.py
Edward Yang 6ea89166bd Rewrite of ATen code generator (#42629)
Summary:
Pull Request resolved: https://github.com/pytorch/pytorch/pull/42629

How to approach reviewing this diff:

- The new codegen itself lives in `tools/codegen`. Start with `gen.py`, then read `model.py` and them the `api/` folder. The comments at the top of the files describe what is going on. The CLI interface of the new codegen is similar to the old one, but (1) it is no longer necessary to explicitly specify cwrap inputs (and now we will error if you do so) and (2) the default settings for source and install dir are much better; to the extent that if you run the codegen from the root source directory as just `python -m tools.codegen.gen`, something reasonable will happen.
- The old codegen is (nearly) entirely deleted; every Python file in `aten/src/ATen` was deleted except for `common_with_cwrap.py`, which now permanently finds its home in `tools/shared/cwrap_common.py` (previously cmake copied the file there), and `code_template.py`, which now lives in `tools/codegen/code_template.py`. We remove the copying logic for `common_with_cwrap.py`.
- All of the inputs to the old codegen are deleted.
- Build rules now have to be adjusted to not refer to files that no longer exist, and to abide by the (slightly modified) CLI.
- LegacyTHFunctions files have been generated and checked in. We expect these to be deleted as these final functions get ported to ATen. The deletion process is straightforward; just delete the functions of the ones you are porting. There are 39 more functions left to port.

Signed-off-by: Edward Z. Yang <ezyang@fb.com>

Test Plan: Imported from OSS

Reviewed By: bhosmer

Differential Revision: D23183978

Pulled By: ezyang

fbshipit-source-id: 6073ba432ad182c7284a97147b05f0574a02f763
2020-08-31 09:00:22 -07:00

92 lines
3.1 KiB
Python

import re
from typing import Match, Optional, Sequence, Mapping
# match $identifier or ${identifier} and replace with value in env
# If this identifier is at the beginning of whitespace on a line
# and its value is a list then it is treated as
# block substitution by indenting to that depth and putting each element
# of the list on its own line
# if the identifier is on a line starting with non-whitespace and a list
# then it is comma separated ${,foo} will insert a comma before the list
# if this list is not empty and ${foo,} will insert one after.
class CodeTemplate:
# Python 2.7.5 has a bug where the leading (^[^\n\S]*)? does not work,
# workaround via appending another [^\n\S]? inside
substitution_str = r'(^[^\n\S]*[^\n\S]?)?\$([^\d\W]\w*|\{,?[^\d\W]\w*\,?})'
# older versions of Python have a bug where \w* does not work,
# so we need to replace with the non-shortened version [a-zA-Z0-9_]*
# https://bugs.python.org/issue18647
substitution_str = substitution_str.replace(r'\w', r'[a-zA-Z0-9_]')
substitution = re.compile(substitution_str, re.MULTILINE)
pattern: str
filename: str
@staticmethod
def from_file(filename: str) -> 'CodeTemplate':
with open(filename, 'r') as f:
return CodeTemplate(f.read(), filename)
def __init__(self, pattern: str, filename: str = "") -> None:
self.pattern = pattern
self.filename = filename
def substitute(self, env: Optional[Mapping[str, object]] = None, **kwargs: object) -> str:
if env is None:
env = {}
def lookup(v: str) -> object:
assert env is not None
return kwargs[v] if v in kwargs else env[v]
def indent_lines(indent: str, v: Sequence[object]) -> str:
return "".join([indent + l + "\n" for e in v for l in str(e).splitlines()]).rstrip()
def replace(match: Match[str]) -> str:
indent = match.group(1)
key = match.group(2)
comma_before = ''
comma_after = ''
if key[0] == "{":
key = key[1:-1]
if key[0] == ",":
comma_before = ', '
key = key[1:]
if key[-1] == ',':
comma_after = ', '
key = key[:-1]
v = lookup(key)
if indent is not None:
if not isinstance(v, list):
v = [v]
return indent_lines(indent, v)
elif isinstance(v, list):
middle = ', '.join([str(x) for x in v])
if len(v) == 0:
return middle
return comma_before + middle + comma_after
else:
return str(v)
return self.substitution.sub(replace, self.pattern)
if __name__ == "__main__":
c = CodeTemplate("""\
int foo($args) {
$bar
$bar
$a+$b
}
int commatest(int a${,stuff})
int notest(int a${,empty,})
""")
print(c.substitute(args=["hi", 8], bar=["what", 7],
a=3, b=4, stuff=["things...", "others"], empty=[]))