diff --git a/llvm/utils/TableGen/jupyter/.gitignore b/llvm/utils/TableGen/jupyter/.gitignore new file mode 100644 --- /dev/null +++ b/llvm/utils/TableGen/jupyter/.gitignore @@ -0,0 +1,5 @@ +__pycache__ +*.pyc +build/ +dist/ +MANIFEST diff --git a/llvm/utils/TableGen/jupyter/README.md b/llvm/utils/TableGen/jupyter/README.md new file mode 100644 --- /dev/null +++ b/llvm/utils/TableGen/jupyter/README.md @@ -0,0 +1,22 @@ +A Jupyter kernel for TableGen (llvm-tblgen) + +Then install into jupyter: + + python3 -m tablgen_kernel.install + +To use it, first put this folder on your PYTHONPATH so jupyter can find it: + +```shell + export PYTHONPATH=$PYTHONPATH: +``` + +Then run one of: + +```shell + jupyter notebook + # In the notebook interface, select 'LLVM TableGen' from the 'New' menu + jupyter console --kernel tablegen +``` + +`llvm-tblgen` is expected to be either in the `PATH` or you can set +the environment variable `LLVM_TBLGEN_EXECUTABLE` to point to it directly. diff --git a/llvm/utils/TableGen/jupyter/tablegen_kernel/__init__.py b/llvm/utils/TableGen/jupyter/tablegen_kernel/__init__.py new file mode 100644 --- /dev/null +++ b/llvm/utils/TableGen/jupyter/tablegen_kernel/__init__.py @@ -0,0 +1,6 @@ +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +"""A llvm-tblgen kernel for Jupyter""" + +from .kernel import __version__ diff --git a/llvm/utils/TableGen/jupyter/tablegen_kernel/__main__.py b/llvm/utils/TableGen/jupyter/tablegen_kernel/__main__.py new file mode 100644 --- /dev/null +++ b/llvm/utils/TableGen/jupyter/tablegen_kernel/__main__.py @@ -0,0 +1,8 @@ +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +from ipykernel.kernelapp import IPKernelApp +from .kernel import TableGenKernel + +IPKernelApp.launch_instance(kernel_class=TableGenKernel) diff --git a/llvm/utils/TableGen/jupyter/tablegen_kernel/assets/kernel.json b/llvm/utils/TableGen/jupyter/tablegen_kernel/assets/kernel.json new file mode 100644 --- /dev/null +++ b/llvm/utils/TableGen/jupyter/tablegen_kernel/assets/kernel.json @@ -0,0 +1,14 @@ +{ + "argv": [ + "python3", "-m", "tablegen_kernel", "-f", "{connection_file}" + ], + "display_name": "LLVM TableGen", + "language": "tablegen", + "language_info": { + "name": "tablegen", + "codemirror_mode": "tablgen", + "mimetype": "text/x-tablegen", + "file_extension": ".td", + "pygments_lexer": "text" + } +} diff --git a/llvm/utils/TableGen/jupyter/tablegen_kernel/install.py b/llvm/utils/TableGen/jupyter/tablegen_kernel/install.py new file mode 100644 --- /dev/null +++ b/llvm/utils/TableGen/jupyter/tablegen_kernel/install.py @@ -0,0 +1,50 @@ +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import os +import argparse +from jupyter_client.kernelspec import KernelSpecManager + + +def install_my_kernel_spec(user=True, prefix=None): + """Install the kernel spec for user in given prefix.""" + print("Installing llvm-tblgen IPython kernel spec") + pkgroot = os.path.dirname(__file__) + KernelSpecManager().install_kernel_spec( + os.path.join(pkgroot, "assets"), "tablegen", user=user, prefix=prefix + ) + + +def _is_root(): + """Returns whether the current user is root.""" + try: + return os.geteuid() == 0 + except AttributeError: + # Return false wherever unknown. + return False + + +def main(argv=None): + parser = argparse.ArgumentParser( + description="Install KernelSpec for LLVM TableGen Kernel" + ) + prefix_locations = parser.add_mutually_exclusive_group() + + prefix_locations.add_argument( + "--user", help="Install in user home directory", action="store_true" + ) + prefix_locations.add_argument( + "--prefix", help="Install directory prefix", default=None + ) + + args = parser.parse_args(argv) + + user = args.user or not _is_root() + prefix = args.prefix + + install_my_kernel_spec(user=user, prefix=prefix) + + +if __name__ == "__main__": + main() diff --git a/llvm/utils/TableGen/jupyter/tablegen_kernel/kernel.py b/llvm/utils/TableGen/jupyter/tablegen_kernel/kernel.py new file mode 100644 --- /dev/null +++ b/llvm/utils/TableGen/jupyter/tablegen_kernel/kernel.py @@ -0,0 +1,119 @@ +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import os +import shutil +import subprocess +import tempfile +from ipykernel.kernelbase import Kernel + +__version__ = "0.0.1" + + +class TableGenKernel(Kernel): + """Kernel using llvm-tblgen inside jupyter. + + All input is treated as TableGen unless the first non whitespace character + is "%" in which case it is a "magic" line. + + The only magic line supported is "%args". The rest of the line is the + arguments passed to llvm-tblgen. + + ```tablgen + %args --print-records --print-detailed-records + class Stuff { + string Name; + } + + def a_thing : Stuff {} + ``` + + """ + + implementation = "tablegen" + implementation_version = __version__ + + language_version = __version__ + language = "tablegen" + language_info = { + "name": "tablegen", + "mimetype": "text/x-tablegen", + "file_extension": ".td", + "pygments_lexer": "text", + } + + def __init__(self, **kwargs): + Kernel.__init__(self, **kwargs) + self._executable = None + + @property + def banner(self): + return "llvm-tblgen kernel %s" % __version__ + + @property + def executable(self): + """If this is the first run, search for llvm-tblgen. + Otherwise return the cached path to it.""" + if self._executable is None: + path = os.environ.get("LLVM_TBLGEN_EXECUTABLE") + if path is not None and os.path.isfile(path) and os.access(path, os.X_OK): + self._executable = path + else: + path = shutil.which("llvm-tblgen") + if path is None: + raise OSError("llvm-tblgen not found, please see README") + self._executable = path + + return self._executable + + def get_magic(self, text): + magic = [] + code = [] + for line in text.splitlines(): + if line.lstrip().startswith("%"): + magic.append(line.lstrip()) + else: + code.append(line) + + return "\n".join(code), magic + + def do_execute( + self, code, silent, store_history=True, user_expressions=None, allow_stdin=False + ): + """Execute user code using llvm-tblgen binary.""" + code, magic = self.get_magic(code) + + extra_args = [] + for m in magic: + if m.startswith("%args"): + # Last one in wins + extra_args = m.split()[1:] + + with tempfile.TemporaryFile("w+") as f: + f.write(code) + f.seek(0) + got = subprocess.run( + [self.executable, *extra_args], + stdin=f, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + universal_newlines=True, + ) + + if not silent: + if got.stderr: + self.send_response( + self.iopub_socket, "stream", {"name": "stderr", "text": got.stderr} + ) + else: + self.send_response( + self.iopub_socket, "stream", {"name": "stdout", "text": got.stdout} + ) + + return { + "status": "ok", + "execution_count": self.execution_count, + "payload": [], + "user_expressions": {}, + }