[pypy-svn] r23813 - in pypy/dist: lib-python/modified-2.4.1 pypy/interpreter pypy/interpreter/astcompiler pypy/interpreter/pyparser pypy/interpreter/pyparser/data pypy/interpreter/pyparser/test pypy/interpreter/pyparser/test/samples pypy/interpreter/test
stuart at codespeak.net
stuart at codespeak.net
Wed Mar 1 03:09:42 CET 2006
Author: stuart
Date: Wed Mar 1 03:09:41 2006
New Revision: 23813
Added:
pypy/dist/lib-python/modified-2.4.1/opcode.py
- copied, changed from r23743, pypy/dist/lib-python/2.4.1/opcode.py
pypy/dist/pypy/interpreter/pyparser/test/samples/snippet_with_1.py
pypy/dist/pypy/interpreter/pyparser/test/samples/snippet_with_2.py
Modified:
pypy/dist/pypy/interpreter/astcompiler/ast.py
pypy/dist/pypy/interpreter/astcompiler/ast.txt
pypy/dist/pypy/interpreter/astcompiler/pyassem.py
pypy/dist/pypy/interpreter/astcompiler/pycodegen.py
pypy/dist/pypy/interpreter/pyopcode.py
pypy/dist/pypy/interpreter/pyparser/astbuilder.py
pypy/dist/pypy/interpreter/pyparser/data/Grammar2.5a
pypy/dist/pypy/interpreter/pyparser/pysymbol.py
pypy/dist/pypy/interpreter/pyparser/test/test_astbuilder.py
pypy/dist/pypy/interpreter/pyparser/test/test_samples.py
pypy/dist/pypy/interpreter/test/test_syntax.py
Log:
(Arre, Ale, Stuart Williams)
Added with statement. Cleaned up code to no longer depend either on
dis module for byte code opcodes or on Python 2.4. Missing: more
tests and from __future__ import with_statement.
Modified: pypy/dist/pypy/interpreter/astcompiler/ast.py
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/ast.py (original)
+++ pypy/dist/pypy/interpreter/astcompiler/ast.py Wed Mar 1 03:09:41 2006
@@ -4195,10 +4195,19 @@
def getChildren(self):
"NOT_RPYTHON"
- return self.expr, self.body, self.var
+ children = []
+ children.append(self.expr)
+ children.append(self.body)
+ children.append(self.var)
+ return tuple(children)
def getChildNodes(self):
- return [self.expr, self.body]
+ nodelist = []
+ nodelist.append(self.expr)
+ nodelist.append(self.body)
+ if self.var is not None:
+ nodelist.append(self.var)
+ return nodelist
def __repr__(self):
return "With(%s, %s, %s)" % (self.expr.__repr__(), self.body.__repr__(), self.var.__repr__())
@@ -4215,9 +4224,12 @@
def fset_body( space, self, w_arg):
self.body = space.interp_w(Node, w_arg, can_be_None=False)
def fget_var( space, self):
- return space.wrap(self.var)
+ if self.var is None:
+ return space.w_None
+ else:
+ return space.wrap(self.var)
def fset_var( space, self, w_arg):
- self.var = space.str_w(w_arg)
+ self.var = space.interp_w(Node, w_arg, can_be_None=True)
def descr_With_new(space, w_subtype, w_expr, w_body, w_var, lineno=-1):
self = space.allocate_instance(With, w_subtype)
@@ -4225,7 +4237,7 @@
self.expr = expr
body = space.interp_w(Node, w_body, can_be_None=False)
self.body = body
- var = space.str_w(w_var)
+ var = space.interp_w(Node, w_var, can_be_None=True)
self.var = var
self.lineno = lineno
return space.wrap(self)
Modified: pypy/dist/pypy/interpreter/astcompiler/ast.txt
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/ast.txt (original)
+++ pypy/dist/pypy/interpreter/astcompiler/ast.txt Wed Mar 1 03:09:41 2006
@@ -27,7 +27,7 @@
Break:
Continue:
For: assign, list, body, else_&
-With: expr, body, var*str
+With: expr, body, var&
While: test, body, else_&
If: tests!, else_&
Exec: expr, locals&, globals&
Modified: pypy/dist/pypy/interpreter/astcompiler/pyassem.py
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/pyassem.py (original)
+++ pypy/dist/pypy/interpreter/astcompiler/pyassem.py Wed Mar 1 03:09:41 2006
@@ -1,6 +1,5 @@
"""A flow graph representation for Python bytecode"""
-import dis
import sys
from pypy.interpreter.astcompiler import misc, ast
@@ -9,7 +8,15 @@
from pypy.interpreter.pycode import PyCode
from pypy.interpreter.baseobjspace import W_Root
+# load opcode.py as pythonopcode from our own lib
+def load_opcode():
+ import new, py
+ global pythonopcode
+ pythonopcode = new.module('opcode')
+ opcode_path = py.path.local(__file__).dirpath().dirpath().dirpath().dirpath('lib-python/modified-2.4.1/opcode.py')
+ execfile(str(opcode_path), pythonopcode.__dict__)
+load_opcode()
class BlockSet:
"""A Set implementation specific to Blocks
@@ -633,11 +640,11 @@
self.stage = FLAT
hasjrel = {}
- for i in dis.hasjrel:
- hasjrel[dis.opname[i]] = True
+ for i in pythonopcode.hasjrel:
+ hasjrel[pythonopcode.opname[i]] = True
hasjabs = {}
- for i in dis.hasjabs:
- hasjabs[dis.opname[i]] = True
+ for i in pythonopcode.hasjabs:
+ hasjabs[pythonopcode.opname[i]] = True
def convertArgs(self):
"""Convert arguments from symbolic to concrete form"""
@@ -772,7 +779,7 @@
index = self._lookupName(arg, self.closure)
return InstrInt(inst.op, index)
- _cmp = list(dis.cmp_op)
+ _cmp = list(pythonopcode.cmp_op)
def _convert_COMPARE_OP(self, inst):
assert isinstance(inst, InstrName)
arg = inst.name
@@ -817,8 +824,9 @@
self.stage = DONE
opnum = {}
- for num in range(len(dis.opname)):
- opnum[dis.opname[num]] = num
+ for num in range(len(pythonopcode.opname)):
+ opnum[pythonopcode.opname[num]] = num
+ # This seems to duplicate dis.opmap from opcode.opmap
del num
def newCodeObject(self):
@@ -1048,6 +1056,7 @@
'SETUP_EXCEPT': 3,
'SETUP_FINALLY': 3,
'FOR_ITER': 1,
+ 'WITH_CLEANUP': 3,
}
# use pattern match
patterns = [
Modified: pypy/dist/pypy/interpreter/astcompiler/pycodegen.py
==============================================================================
--- pypy/dist/pypy/interpreter/astcompiler/pycodegen.py (original)
+++ pypy/dist/pypy/interpreter/astcompiler/pycodegen.py Wed Mar 1 03:09:41 2006
@@ -543,6 +543,50 @@
self.nextBlock(end)
+ __with_count = 0
+
+ def visitWith(self, node):
+ node.expr.accept(self)
+ self.emitop('LOAD_ATTR', '__context__')
+ self.emitop_int('CALL_FUNCTION', 0)
+ self.emit('DUP_TOP')
+
+ ## exit = ctx.__exit__
+ self.emitop('LOAD_ATTR', '__exit__')
+ exit = "$exit%d" % self.__with_count
+ var = "$var%d" % self.__with_count
+ self.__with_count = self.__with_count + 1
+ self._implicitNameOp('STORE', exit)
+
+ self.emitop('LOAD_ATTR', '__enter__')
+ self.emitop_int('CALL_FUNCTION', 0)
+ finally_block = self.newBlock()
+
+ if node.var is not None: # VAR is present
+ self._implicitNameOp('STORE', var)
+ self.emitop_block('SETUP_FINALLY', finally_block)
+ self._implicitNameOp('LOAD', var)
+ self._implicitNameOp('DELETE', var)
+ node.var.accept(self)
+ else:
+ self.emit('POP_TOP')
+ self.emitop_block('SETUP_FINALLY', finally_block)
+
+ node.body.accept(self)
+
+ self.emit('POP_BLOCK')
+ self.emitop_obj('LOAD_CONST', self.space.w_None) # WITH_CLEANUP checks for normal exit
+ self.nextBlock(finally_block)
+
+ # find local variable with is context.__exit__
+ self._implicitNameOp('LOAD', exit)
+ self._implicitNameOp('DELETE', exit)
+
+ self.emit('WITH_CLEANUP')
+ self.emitop_int('CALL_FUNCTION', 3)
+ self.emit('POP_TOP')
+ self.emit('END_FINALLY')
+
def visitCompare(self, node):
node.expr.accept( self )
cleanup = self.newBlock()
Modified: pypy/dist/pypy/interpreter/pyopcode.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyopcode.py (original)
+++ pypy/dist/pypy/interpreter/pyopcode.py Wed Mar 1 03:09:41 2006
@@ -20,7 +20,7 @@
import new, py
global pythonopcode
pythonopcode = new.module('opcode')
- opcode_path = py.path.local(__file__).dirpath().dirpath().dirpath('lib-python/2.4.1/opcode.py')
+ opcode_path = py.path.local(__file__).dirpath().dirpath().dirpath('lib-python/modified-2.4.1/opcode.py')
execfile(str(opcode_path), pythonopcode.__dict__)
load_opcode()
@@ -647,6 +647,25 @@
block = pyframe.FinallyBlock(f, f.next_instr + offsettoend)
f.blockstack.push(block)
+ def WITH_CLEANUP(f):
+ x = f.valuestack.top()
+ u = f.valuestack.top(1)
+ if (f.space.is_w(f.space.type(u), f.space.w_int)
+ or f.space.is_w(u, f.space.w_None)):
+ u = v = w = f.space.w_None
+ else:
+ v = f.valuestack.top(2)
+ w = f.valuestack.top(3)
+ f.valuestack.pop()
+ f.valuestack.pop()
+ f.valuestack.pop()
+ f.valuestack.pop()
+ f.valuestack.push(f.space.w_None)
+ f.valuestack.push(x)
+ f.valuestack.push(u)
+ f.valuestack.push(v)
+ f.valuestack.push(w)
+
def call_function(f, oparg, w_star=None, w_starstar=None):
n_arguments = oparg & 0xff
n_keywords = (oparg>>8) & 0xff
Modified: pypy/dist/pypy/interpreter/pyparser/astbuilder.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyparser/astbuilder.py (original)
+++ pypy/dist/pypy/interpreter/pyparser/astbuilder.py Wed Mar 1 03:09:41 2006
@@ -1236,9 +1236,9 @@
else_ = atoms[6]
builder.push(ast.While(test, body, else_, atoms[0].lineno))
-
def build_with_stmt(builder, nb):
- """with_stmt: 'with' test [ 'as' NAME ] ':' suite"""
+ """with_stmt: 'with' test [ NAME expr ] ':' suite"""
+
atoms = get_atoms(builder, nb)
# skip 'with'
test = atoms[1]
@@ -1247,11 +1247,15 @@
var = None
# if there is an "as" clause
else:
- var = atoms[3]
+ token = atoms[2]
+ assert isinstance(token, TokenObject)
+ if token.get_value() != 'as':
+ raise SyntaxError("invalid syntax", token.lineno, token.col)
+ varexpr = atoms[3]
+ var = to_lvalue(varexpr, consts.OP_ASSIGN)
body = atoms[5]
builder.push(ast.With(test, body, var, atoms[0].lineno))
-
def build_import_name(builder, nb):
"""import_name: 'import' dotted_as_names
@@ -1500,7 +1504,7 @@
sym['break_stmt'] : build_break_stmt,
sym['for_stmt'] : build_for_stmt,
sym['while_stmt'] : build_while_stmt,
-# sym['with_stmt'] : build_with_stmt,
+ sym['with_stmt'] : build_with_stmt,
sym['import_name'] : build_import_name,
sym['import_from'] : build_import_from,
sym['yield_stmt'] : build_yield_stmt,
Modified: pypy/dist/pypy/interpreter/pyparser/data/Grammar2.5a
==============================================================================
--- pypy/dist/pypy/interpreter/pyparser/data/Grammar2.5a (original)
+++ pypy/dist/pypy/interpreter/pyparser/data/Grammar2.5a Wed Mar 1 03:09:41 2006
@@ -63,12 +63,13 @@
exec_stmt: 'exec' expr ['in' test [',' test]]
assert_stmt: 'assert' test [',' test]
-compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef
+compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
while_stmt: 'while' test ':' suite ['else' ':' suite]
for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
try_stmt: ('try' ':' suite (except_clause ':' suite)+ #diagram:break
['else' ':' suite] | 'try' ':' suite 'finally' ':' suite)
+with_stmt: 'with' test [ NAME expr ] ':' suite
# NB compile.c makes sure that the default except clause is last
except_clause: 'except' [test [',' test]]
suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
Modified: pypy/dist/pypy/interpreter/pyparser/pysymbol.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyparser/pysymbol.py (original)
+++ pypy/dist/pypy/interpreter/pyparser/pysymbol.py Wed Mar 1 03:09:41 2006
@@ -49,19 +49,6 @@
_cpython_symbols = SymbolMapper( symbol.sym_name )
-
-### prepopulate symbol table from symbols used by CPython
-##for _value, _name in _cpython_symbols.sym_name.items():
-## globals()[_name] = _value
-
-
-### (This function does not seen to be called right now)
-##def update_symbols( parser ):
-## """Update the symbol module according to rules
-## in PythonParser instance : parser"""
-## for rule in parser.rules:
-## _cpython_symbols.add_symbol( rule )
-
# There is no symbol in this module until the grammar is loaded
# once loaded the grammar parser will fill the mappings with the
# grammar symbols
Added: pypy/dist/pypy/interpreter/pyparser/test/samples/snippet_with_1.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/interpreter/pyparser/test/samples/snippet_with_1.py Wed Mar 1 03:09:41 2006
@@ -0,0 +1,4 @@
+# EXPECT: Module(None, Stmt([With(Name('acontext'), Stmt([Pass()]), None)]))
+with acontext:
+ pass
+
Added: pypy/dist/pypy/interpreter/pyparser/test/samples/snippet_with_2.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/interpreter/pyparser/test/samples/snippet_with_2.py Wed Mar 1 03:09:41 2006
@@ -0,0 +1,4 @@
+# EXPECT: Module(None, Stmt([With(Name('acontext'), Stmt([Pass()]), AssName('avariable', OP_ASSIGN))]))
+with acontext as avariable:
+ pass
+
Modified: pypy/dist/pypy/interpreter/pyparser/test/test_astbuilder.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyparser/test/test_astbuilder.py (original)
+++ pypy/dist/pypy/interpreter/pyparser/test/test_astbuilder.py Wed Mar 1 03:09:41 2006
@@ -745,8 +745,12 @@
for expr in family:
yield check_expression, expr, 'exec'
+NEW_GRAMMAR_SNIPPETS = [
+ 'snippet_with_1.py',
+ 'snippet_with_2.py',
+ ]
+
SNIPPETS = [
-# 'snippet_with_1.py',
'snippet_1.py',
'snippet_several_statements.py',
'snippet_simple_function.py',
@@ -788,7 +792,7 @@
]
def test_snippets():
- for snippet_name in SNIPPETS:
+ for snippet_name in SNIPPETS + NEW_GRAMMAR_SNIPPETS:
filepath = os.path.join(os.path.dirname(__file__), 'samples', snippet_name)
source = file(filepath).read()
# To avoid using the stable compiler we pull an explicit AST out of the snippet
Modified: pypy/dist/pypy/interpreter/pyparser/test/test_samples.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyparser/test/test_samples.py (original)
+++ pypy/dist/pypy/interpreter/pyparser/test/test_samples.py Wed Mar 1 03:09:41 2006
@@ -21,6 +21,10 @@
#"snippet_import_statements.py",
"snippet_decorators.py",
]
+SKIP_ALWAYS = [
+ "snippet_with_1.py",
+ "snippet_with_2.py",
+]
REAL_EXPECTED_OUTPUT = {
# for snippets that show bugs of Python's compiler package
"snippet_transformer_bug.py":
@@ -73,6 +77,8 @@
for fname in os.listdir(samples_dir):
if not fname.endswith('.py'):
continue
+ if fname in SKIP_ALWAYS:
+ continue
if GRAMMAR_MISMATCH and fname in SKIP_IF_NOT_NATIVE:
print "Skipping", fname
continue
Modified: pypy/dist/pypy/interpreter/test/test_syntax.py
==============================================================================
--- pypy/dist/pypy/interpreter/test/test_syntax.py (original)
+++ pypy/dist/pypy/interpreter/test/test_syntax.py Wed Mar 1 03:09:41 2006
@@ -256,6 +256,30 @@
exec s
assert x == expected
+class AppTestWith:
+ def test_with(self):
+
+ s = """if 1:
+ # from __future__ import with_statement
+ class Context:
+ def __init__(self):
+ self.calls = list()
+ def __context__(self):
+ self.calls.append('__context__')
+ return self
+ def __enter__(self):
+ self.calls.append('__enter__')
+ pass
+ def __exit__(self, exc_type, exc_value, exc_tb):
+ self.calls.append('__exit__')
+ pass
+ acontext = Context()
+ with acontext:
+ pass
+ """
+ exec s
+
+ assert acontext.calls == '__context__ __enter__ __exit__'.split()
if __name__ == '__main__':
# only to check on top of CPython (you need 2.4)
More information about the Pypy-commit
mailing list