Skip to content

Instantly share code, notes, and snippets.

@tekknolagi
Last active July 7, 2022 23:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save tekknolagi/97e5fb0e91013833a9a2525a3f05a21d to your computer and use it in GitHub Desktop.
Save tekknolagi/97e5fb0e91013833a9a2525a3f05a21d to your computer and use it in GitHub Desktop.
A small Python to C compiler...
import ast
import sys
import json
NoSemicolon = True
class CompileError(Exception):
pass
class DeclarationVisitor(ast.NodeVisitor):
def __init__(self):
super().__init__()
# name -> [type]
self.declarations = {}
def _declare(self, name, type):
if name in self.declarations:
self.declarations.append(type)
else:
self.declarations[name] = [type]
def visit_Assign(self, node):
for target in node.targets:
self._declare(target.id, "RawObject")
class Compiler(ast.NodeVisitor):
def __init__(self, stream):
super().__init__()
self.stream = stream
self.sym_counter = 0
def _write(self, *args):
for arg in args:
print(arg, file=self.stream, end=" ")
def _writeline(self, *args):
for arg in args:
print(arg, file=self.stream, end=" ")
print(file=self.stream)
def _gensym(self):
current = self.sym_counter
self.sym_counter += 1
return f"v{current}"
def visit_Assign(self, node):
value = self._gensym()
self._write("Object", value, "(&scope, ")
self.visit(node.value)
self._writeline(");")
for target in node.targets:
self._writeline(target.id, "=", value, ";")
return NoSemicolon
def visit_Num(self, node):
self._write(node.n)
def visit_Str(self, node):
# This will get us double quotes
self._write(json.dumps(node.s))
def visit_BinOp(self, node):
assert isinstance(node.op, ast.Add)
self.visit(node.left)
self._write("+")
self.visit(node.right)
def visit_Return(self, node):
self._write("return ")
self.visit(node.value)
def visit_Import(self, node):
for name in node.names:
self._writeline(f"#include <{name.name}>")
return NoSemicolon
def visit_Name(self, node):
self._write(node.id)
def visit_Call(self, node):
assert isinstance(node.func, ast.Name)
assert not node.keywords
self.visit(node.func)
self._write("(")
first = True
for arg in node.args:
if not first:
self._write(", ")
first = False
self.visit(arg)
self._write(")")
def visit_FunctionDef(self, node):
assert not node.args.defaults
assert not node.args.kw_defaults
assert not node.args.kwonlyargs
declaration_visitor = DeclarationVisitor()
declaration_visitor.visit(node)
return_type = node.returns.id if node.returns else "void"
args = [f"const Object& {arg.arg}" for arg in node.args.args]
joined_args = ", " + ", ".join(args) if args else ""
self._writeline(f"{return_type} {node.name}(Thread* thread{joined_args}) {{")
self._writeline("HandleScope scope(thread);")
for name, types in declaration_visitor.declarations.items():
self._writeline(f"Object {name}(&scope, NoneType::object());")
self._visit_block(node.body)
self._writeline("}")
return NoSemicolon
def _visit_block(self, body):
for stmt in body:
if self.visit(stmt) != NoSemicolon:
self._writeline(";")
def visit_Module(self, node):
self._visit_block(node.body)
if __name__ == "__main__":
filename = sys.argv[1]
with open(filename) as f:
tree = ast.parse(f.read(), filename, "exec")
Compiler(sys.stdout).visit(tree)
def foo(x, y) -> RawObject:
return x + y
def bar(x) -> RawObject:
return x
def main() -> int:
x = bar(foo(1, 2))
print(x)
return 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment