Created
January 25, 2019 13:27
-
-
Save spookylukey/e79cec2684a2f6c8f5c3578b76eac29e to your computer and use it in GitHub Desktop.
Beginnings of a patch to use Python AST for compiling
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/fluent.runtime/fluent/runtime/codegen.py b/fluent.runtime/fluent/runtime/codegen.py | |
index f00f750..b76b147 100644 | |
--- a/fluent.runtime/fluent/runtime/codegen.py | |
+++ b/fluent.runtime/fluent/runtime/codegen.py | |
@@ -3,6 +3,7 @@ Utilities for doing Python code generation | |
""" | |
from __future__ import absolute_import, unicode_literals | |
+import ast | |
import keyword | |
import re | |
import sys | |
@@ -52,6 +53,10 @@ UNKNOWN_TYPE = object | |
class PythonAst(object): | |
+ """ | |
+ Base class representing a simplified Python AST (not the real one). | |
+ Generates real `ast.*` nodes via `as_ast()` method. | |
+ """ | |
def simplify(self, changes): | |
""" | |
Simplify the statement/expression, returning either a modified | |
@@ -64,6 +69,9 @@ class PythonAst(object): | |
""" | |
return self | |
+ def as_ast(self): | |
+ raise NotImplementedError("{!r}.as_ast()".format(self.__class__)) | |
+ | |
class Scope(PythonAst): | |
def __init__(self, parent_scope=None): | |
@@ -205,7 +213,8 @@ def cleanup_name(name): | |
class Module(Scope): | |
- pass | |
+ def as_ast(self): | |
+ return ast.Module([s.as_ast() for s in self.statements]) | |
class Statement(PythonAst): | |
@@ -253,6 +262,20 @@ class Function(Block, Statement): | |
self.args = args | |
self.kwargs = kwargs | |
+ def as_ast(self): | |
+ return ast.FunctionDef(name=self.func_name, | |
+ args=ast.arguments(args=[ast.arg(arg=arg_name, annotation=None, | |
+ lineno=1, col_offset=1) | |
+ for arg_name in self.args], | |
+ vararg=None, | |
+ kwonlyargs=[], | |
+ kw_defaults=[], | |
+ kwarg=None, | |
+ defaults=[]), | |
+ body=[statement.as_ast() for statement in self.statements], | |
+ decorator_list=[], | |
+ lineno=1, col_offset=1) | |
+ | |
def as_source_code(self): | |
if not allowable_name(self.func_name): | |
raise AssertionError("Expected '{0}' to be a valid Python identifier".format(self.func_name)) | |
@@ -281,6 +304,9 @@ class Return(Statement): | |
def __init__(self, value): | |
self.value = value | |
+ def as_ast(self): | |
+ return ast.Return(self.value.as_ast(), lineno=1, col_offset=1) | |
+ | |
def as_source_code(self): | |
return 'return {0}'.format(self.value.as_source_code()) | |
@@ -371,6 +397,9 @@ class String(Expression): | |
def __init__(self, string_value): | |
self.string_value = string_value | |
+ def as_ast(self): | |
+ return ast.Str(self.string_value, lineno=1, col_offset=1) | |
+ | |
def as_source_code(self): | |
retval = repr(self.string_value) | |
# We eventually call 'exec' in a module that has 'from __future__ import | |
diff --git a/fluent.runtime/fluent/runtime/compiler.py b/fluent.runtime/fluent/runtime/compiler.py | |
index 85255df..49b0c28 100644 | |
--- a/fluent.runtime/fluent/runtime/compiler.py | |
+++ b/fluent.runtime/fluent/runtime/compiler.py | |
@@ -126,7 +126,9 @@ def compile_messages(messages, locale, use_isolating=True, functions=None, debug | |
# TODO - it would be nice to be able to get back to FTL source file lines, | |
# if were knew what they were, and pass absolute filename that to 'compile' | |
# builtin as the filepath. Instead of that just use 'exec' for now. | |
- exec(module.as_source_code(), module_globals) | |
+ | |
+ code_obj = compile(module.as_ast(), '<string>', 'exec') | |
+ exec(code_obj, module_globals) | |
retval = {} | |
for key, val in message_mapping.items(): | |
if key.startswith('-'): | |
diff --git a/fluent.runtime/tests/test_compiler.py b/fluent.runtime/tests/test_compiler.py | |
index 6690eac..3d96cdc 100644 | |
--- a/fluent.runtime/tests/test_compiler.py | |
+++ b/fluent.runtime/tests/test_compiler.py | |
@@ -5,6 +5,8 @@ from collections import OrderedDict | |
import babel | |
+from ast_decompiler import decompile | |
+ | |
from fluent.runtime.builtins import BUILTINS | |
from fluent.runtime.compiler import messages_to_module | |
from fluent.runtime.errors import FluentCyclicReferenceError, FluentError, FluentReferenceError | |
@@ -40,7 +42,7 @@ def compile_messages_to_python(source, locale, use_isolating=False, functions=No | |
messages, locale, | |
use_isolating=use_isolating, | |
functions=_functions) | |
- return module.as_source_code(), errors | |
+ return decompile(module.as_ast()), errors | |
class TestCompiler(unittest.TestCase): |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment