Commit 29f233d6 authored by Jeremy Hylton's avatar Jeremy Hylton

API change:

compile() becomes replacement for builtin compile()
compileFile() generates a .pyc from a .py
both are exported in __init__

compiler.parse() gets optional second argument to specify compilation
mode, e.g. single, eval, exec

Add AbstractCompileMode as parent class and Module, Expression, and
Interactive as concrete subclasses.  Each corresponds to a compilation
mode.

THe AbstractCompileMode instances in turn delegate to CodeGeneration
subclasses specialized for their particular functions --
ModuleCodeGenerator, ExpressionCodeGeneration,
InteractiveCodeGenerator.
parent 50f0c155
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
There are several functions defined at the top level that are imported There are several functions defined at the top level that are imported
from modules contained in the package. from modules contained in the package.
parse(buf) -> AST parse(buf, mode="exec") -> AST
Converts a string containing Python source code to an abstract Converts a string containing Python source code to an abstract
syntax tree (AST). The AST is defined in compiler.ast. syntax tree (AST). The AST is defined in compiler.ast.
...@@ -14,11 +14,14 @@ walk(ast, visitor, verbose=None) ...@@ -14,11 +14,14 @@ walk(ast, visitor, verbose=None)
Does a pre-order walk over the ast using the visitor instance. Does a pre-order walk over the ast using the visitor instance.
See compiler.visitor for details. See compiler.visitor for details.
compile(filename) compile(source, filename, mode, flags=None, dont_inherit=None)
Returns a code object. A replacement for the builtin compile() function.
compileFile(filename)
Generates a .pyc file by compilining filename. Generates a .pyc file by compilining filename.
""" """
from transformer import parse, parseFile from transformer import parse, parseFile
from visitor import walk from visitor import walk
from pycodegen import compile from pycodegen import compile, compileFile
...@@ -41,7 +41,7 @@ class BlockStack(misc.Stack): ...@@ -41,7 +41,7 @@ class BlockStack(misc.Stack):
self.__super_init(self) self.__super_init(self)
self.loop = None self.loop = None
def compile(filename, display=0): def compileFile(filename, display=0):
f = open(filename) f = open(filename)
buf = f.read() buf = f.read()
f.close() f.close()
...@@ -55,16 +55,68 @@ def compile(filename, display=0): ...@@ -55,16 +55,68 @@ def compile(filename, display=0):
mod.dump(f) mod.dump(f)
f.close() f.close()
class Module: def compile(source, filename, mode, flags=None, dont_inherit=None):
"""Replacement for builtin compile() function"""
if flags is not None or dont_inherit is not None:
raise RuntimeError, "not implemented yet"
if mode == "single":
gen = Interactive(source, filename)
elif mode == "exec":
gen = Module(source, filename)
elif mode == "eval":
gen = Expression(source, filename)
else:
raise ValueError("compile() 3rd arg must be 'exec' or "
"'eval' or 'single'")
gen.compile()
return gen.code
class AbstractCompileMode:
mode = None # defined by subclass
def __init__(self, source, filename): def __init__(self, source, filename):
self.filename = os.path.abspath(filename)
self.source = source self.source = source
self.filename = filename
self.code = None self.code = None
def compile(self, display=0): def _get_tree(self):
tree = parse(self.source) tree = parse(self.source, self.mode)
misc.set_filename(self.filename, tree) misc.set_filename(self.filename, tree)
syntax.check(tree) syntax.check(tree)
return tree
def compile(self):
pass # implemented by subclass
def getCode(self):
return self.code
class Expression(AbstractCompileMode):
mode = "eval"
def compile(self):
tree = self._get_tree()
gen = ExpressionCodeGenerator(tree)
self.code = gen.getCode()
class Interactive(AbstractCompileMode):
mode = "single"
def compile(self):
tree = self._get_tree()
gen = InteractiveCodeGenerator(tree)
self.code = gen.getCode()
class Module(AbstractCompileMode):
mode = "exec"
def compile(self, display=0):
tree = self._get_tree()
gen = ModuleCodeGenerator(tree) gen = ModuleCodeGenerator(tree)
if display: if display:
import pprint import pprint
...@@ -1097,6 +1149,44 @@ class ModuleCodeGenerator(NestedScopeMixin, CodeGenerator): ...@@ -1097,6 +1149,44 @@ class ModuleCodeGenerator(NestedScopeMixin, CodeGenerator):
def get_module(self): def get_module(self):
return self return self
class ExpressionCodeGenerator(NestedScopeMixin, CodeGenerator):
__super_init = CodeGenerator.__init__
scopes = None
futures = ()
def __init__(self, tree):
self.graph = pyassem.PyFlowGraph("<expression>", tree.filename)
self.__super_init()
self.set_lineno(tree)
walk(tree, self)
self.emit('RETURN_VALUE')
def get_module(self):
return self
class InteractiveCodeGenerator(NestedScopeMixin, CodeGenerator):
__super_init = CodeGenerator.__init__
scopes = None
futures = ()
def __init__(self, tree):
self.graph = pyassem.PyFlowGraph("<interactive>", tree.filename)
self.__super_init()
self.set_lineno(tree)
walk(tree, self)
self.emit('RETURN_VALUE')
def get_module(self):
return self
def visitDiscard(self, node):
# XXX Discard means it's an expression. Perhaps this is a bad
# name.
self.visit(node.expr)
self.emit('PRINT_EXPR')
class AbstractFunctionCode: class AbstractFunctionCode:
optimized = 1 optimized = 1
lambdaCount = 0 lambdaCount = 0
......
...@@ -42,8 +42,14 @@ def parseFile(path): ...@@ -42,8 +42,14 @@ def parseFile(path):
f.close() f.close()
return parse(src) return parse(src)
def parse(buf): def parse(buf, mode="exec"):
return Transformer().parsesuite(buf) if mode == "exec" or mode == "single":
return Transformer().parsesuite(buf)
elif mode == "eval":
return Transformer().parseexpr(buf)
else:
raise ValueError("compile() arg 3 must be"
" 'exec' or 'eval' or 'single'")
def asList(nodes): def asList(nodes):
l = [] l = []
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
There are several functions defined at the top level that are imported There are several functions defined at the top level that are imported
from modules contained in the package. from modules contained in the package.
parse(buf) -> AST parse(buf, mode="exec") -> AST
Converts a string containing Python source code to an abstract Converts a string containing Python source code to an abstract
syntax tree (AST). The AST is defined in compiler.ast. syntax tree (AST). The AST is defined in compiler.ast.
...@@ -14,11 +14,14 @@ walk(ast, visitor, verbose=None) ...@@ -14,11 +14,14 @@ walk(ast, visitor, verbose=None)
Does a pre-order walk over the ast using the visitor instance. Does a pre-order walk over the ast using the visitor instance.
See compiler.visitor for details. See compiler.visitor for details.
compile(filename) compile(source, filename, mode, flags=None, dont_inherit=None)
Returns a code object. A replacement for the builtin compile() function.
compileFile(filename)
Generates a .pyc file by compilining filename. Generates a .pyc file by compilining filename.
""" """
from transformer import parse, parseFile from transformer import parse, parseFile
from visitor import walk from visitor import walk
from pycodegen import compile from pycodegen import compile, compileFile
...@@ -41,7 +41,7 @@ class BlockStack(misc.Stack): ...@@ -41,7 +41,7 @@ class BlockStack(misc.Stack):
self.__super_init(self) self.__super_init(self)
self.loop = None self.loop = None
def compile(filename, display=0): def compileFile(filename, display=0):
f = open(filename) f = open(filename)
buf = f.read() buf = f.read()
f.close() f.close()
...@@ -55,16 +55,68 @@ def compile(filename, display=0): ...@@ -55,16 +55,68 @@ def compile(filename, display=0):
mod.dump(f) mod.dump(f)
f.close() f.close()
class Module: def compile(source, filename, mode, flags=None, dont_inherit=None):
"""Replacement for builtin compile() function"""
if flags is not None or dont_inherit is not None:
raise RuntimeError, "not implemented yet"
if mode == "single":
gen = Interactive(source, filename)
elif mode == "exec":
gen = Module(source, filename)
elif mode == "eval":
gen = Expression(source, filename)
else:
raise ValueError("compile() 3rd arg must be 'exec' or "
"'eval' or 'single'")
gen.compile()
return gen.code
class AbstractCompileMode:
mode = None # defined by subclass
def __init__(self, source, filename): def __init__(self, source, filename):
self.filename = os.path.abspath(filename)
self.source = source self.source = source
self.filename = filename
self.code = None self.code = None
def compile(self, display=0): def _get_tree(self):
tree = parse(self.source) tree = parse(self.source, self.mode)
misc.set_filename(self.filename, tree) misc.set_filename(self.filename, tree)
syntax.check(tree) syntax.check(tree)
return tree
def compile(self):
pass # implemented by subclass
def getCode(self):
return self.code
class Expression(AbstractCompileMode):
mode = "eval"
def compile(self):
tree = self._get_tree()
gen = ExpressionCodeGenerator(tree)
self.code = gen.getCode()
class Interactive(AbstractCompileMode):
mode = "single"
def compile(self):
tree = self._get_tree()
gen = InteractiveCodeGenerator(tree)
self.code = gen.getCode()
class Module(AbstractCompileMode):
mode = "exec"
def compile(self, display=0):
tree = self._get_tree()
gen = ModuleCodeGenerator(tree) gen = ModuleCodeGenerator(tree)
if display: if display:
import pprint import pprint
...@@ -1097,6 +1149,44 @@ class ModuleCodeGenerator(NestedScopeMixin, CodeGenerator): ...@@ -1097,6 +1149,44 @@ class ModuleCodeGenerator(NestedScopeMixin, CodeGenerator):
def get_module(self): def get_module(self):
return self return self
class ExpressionCodeGenerator(NestedScopeMixin, CodeGenerator):
__super_init = CodeGenerator.__init__
scopes = None
futures = ()
def __init__(self, tree):
self.graph = pyassem.PyFlowGraph("<expression>", tree.filename)
self.__super_init()
self.set_lineno(tree)
walk(tree, self)
self.emit('RETURN_VALUE')
def get_module(self):
return self
class InteractiveCodeGenerator(NestedScopeMixin, CodeGenerator):
__super_init = CodeGenerator.__init__
scopes = None
futures = ()
def __init__(self, tree):
self.graph = pyassem.PyFlowGraph("<interactive>", tree.filename)
self.__super_init()
self.set_lineno(tree)
walk(tree, self)
self.emit('RETURN_VALUE')
def get_module(self):
return self
def visitDiscard(self, node):
# XXX Discard means it's an expression. Perhaps this is a bad
# name.
self.visit(node.expr)
self.emit('PRINT_EXPR')
class AbstractFunctionCode: class AbstractFunctionCode:
optimized = 1 optimized = 1
lambdaCount = 0 lambdaCount = 0
......
...@@ -42,8 +42,14 @@ def parseFile(path): ...@@ -42,8 +42,14 @@ def parseFile(path):
f.close() f.close()
return parse(src) return parse(src)
def parse(buf): def parse(buf, mode="exec"):
return Transformer().parsesuite(buf) if mode == "exec" or mode == "single":
return Transformer().parsesuite(buf)
elif mode == "eval":
return Transformer().parseexpr(buf)
else:
raise ValueError("compile() arg 3 must be"
" 'exec' or 'eval' or 'single'")
def asList(nodes): def asList(nodes):
l = [] l = []
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment