that maps each ``set_lineno()`` to the corresponding position in the bytecode. |
that maps each ``set_lineno()`` to the corresponding position in the bytecode. |
|
|
And of course, the resulting code objects can be run with ``eval()`` or |
And of course, the resulting code objects can be run with ``eval()`` or |
``exec``, or used with ``new.function`` to create a function:: |
``exec``, or used with ``new.function``/``types.FunctionType`` to create a |
|
function:: |
|
|
>>> eval(c.code()) |
>>> eval(c.code()) |
42 |
42 |
|
|
>>> exec c.code() # exec discards the return value, so no output here |
>>> exec(c.code()) # exec discards the return value, so no output here |
|
|
>>> import new |
>>> try: |
>>> f = new.function(c.code(), globals()) |
... from new import function |
|
... except ImportError: # Python 3 workarounds |
|
... from types import FunctionType as function |
|
... long = int |
|
... unicode = str |
|
|
|
>>> f = function(c.code(), globals()) |
>>> f() |
>>> f() |
42 |
42 |
|
|
>>> c.RETURN_VALUE() |
>>> c.RETURN_VALUE() |
|
|
>>> eval(c.code()) # computes type(27) |
>>> eval(c.code()) # computes type(27) |
<type 'int'> |
<... 'int'> |
|
|
>>> c = Code() |
>>> c = Code() |
>>> c.LOAD_CONST(dict) |
>>> c.LOAD_CONST(dict) |
``None``, or Python code object, it is treated as though it was passed to |
``None``, or Python code object, it is treated as though it was passed to |
the ``LOAD_CONST`` method directly:: |
the ``LOAD_CONST`` method directly:: |
|
|
|
|
|
|
>>> c = Code() |
>>> c = Code() |
>>> c(1, 2L, 3.0, 4j+5, "6", u"7", False, None, c.code()) |
>>> c(1, long(2), 3.0, 4j+5, "6", unicode("7"), False, None, c.code()) |
>>> dis(c.code()) |
>>> dis(c.code()) |
0 0 LOAD_CONST 1 (1) |
0 0 LOAD_CONST 1 (1) |
3 LOAD_CONST 2 (2L) |
3 LOAD_CONST 2 (2...) |
6 LOAD_CONST 3 (3.0) |
6 LOAD_CONST 3 (3.0) |
9 LOAD_CONST 4 ((5+4j)) |
9 LOAD_CONST 4 ((5+4j)) |
12 LOAD_CONST 5 ('6') |
12 LOAD_CONST 5 ('6') |
15 LOAD_CONST 6 (u'7') |
15 LOAD_CONST 6 (...'7') |
18 LOAD_CONST 7 (False) |
18 LOAD_CONST 7 (False) |
21 LOAD_CONST 0 (None) |
21 LOAD_CONST 0 (None) |
24 LOAD_CONST 8 (<code object <lambda> at ...>) |
24 LOAD_CONST 8 (<code object <lambda>...>) |
|
|
Note that although some values of different types may compare equal to each |
Note that although some values of different types may compare equal to each |
other, ``Code`` objects will not substitute a value of a different type than |
other, ``Code`` objects will not substitute a value of a different type than |
the one you requested:: |
the one you requested:: |
|
|
>>> c = Code() |
>>> c = Code() |
>>> c(1, True, 1.0, 1L) # equal, but different types |
>>> c(1, True, 1.0) # equal, but different types |
>>> dis(c.code()) |
>>> dis(c.code()) |
0 0 LOAD_CONST 1 (1) |
0 0 LOAD_CONST 1 (1) |
3 LOAD_CONST 2 (True) |
3 LOAD_CONST 2 (True) |
6 LOAD_CONST 3 (1.0) |
6 LOAD_CONST 3 (1.0) |
9 LOAD_CONST 4 (1L) |
|
|
|
Simple Containers |
Simple Containers |
----------------- |
----------------- |
|
|
|
|
>>> Getattr(Const(object), '__class__') # const expression, const result |
>>> Getattr(Const(object), '__class__') # const expression, const result |
Const(<type 'type'>) |
Const(<... 'type'>) |
|
|
Or the attribute name can be an expression, in which case a ``getattr()`` call |
Or the attribute name can be an expression, in which case a ``getattr()`` call |
is compiled instead:: |
is compiled instead:: |
|
|
>>> from peak.util.assembler import const_value |
>>> from peak.util.assembler import const_value |
|
|
>>> simple_values = [1, 2L, 3.0, 4j+5, "6", u"7", False, None, c.code()] |
>>> simple_values = [1, long(2), 3.0, 4j+5, "6", unicode("7"), False, None, c.code()] |
|
|
>>> map(const_value, simple_values) |
>>> list(map(const_value, simple_values)) |
[1, 2L, 3.0, (5+4j), '6', u'7', False, None, <code object <lambda> ...>] |
[1, 2..., 3.0, (5+4j), '6', ...'7', False, None, <code object <lambda>...>] |
|
|
Values wrapped in a ``Const()`` are also returned as-is:: |
Values wrapped in a ``Const()`` are also returned as-is:: |
|
|
>>> map(const_value, map(Const, simple_values)) |
>>> list(map(const_value, map(Const, simple_values))) |
[1, 2L, 3.0, (5+4j), '6', u'7', False, None, <code object <lambda> ...>] |
[1, 2..., 3.0, (5+4j), '6', ...'7', False, None, <code object <lambda>...>] |
|
|
But no other node types produce constant values; instead, ``NotAConstant`` is |
But no other node types produce constant values; instead, ``NotAConstant`` is |
raised:: |
raised:: |
``Const`` node instead of a ``Call`` node:: |
``Const`` node instead of a ``Call`` node:: |
|
|
>>> Call( Const(type), [1] ) |
>>> Call( Const(type), [1] ) |
Const(<type 'int'>) |
Const(<... 'int'>) |
|
|
Thus, you can also take the ``const_value()`` of such calls:: |
Thus, you can also take the ``const_value()`` of such calls:: |
|
|
passed in to another ``Call``:: |
passed in to another ``Call``:: |
|
|
>>> Call(Const(type), [Call( Const(dict), [], [('x',27)] )]) |
>>> Call(Const(type), [Call( Const(dict), [], [('x',27)] )]) |
Const(<type 'dict'>) |
Const(<... 'dict'>) |
|
|
Notice that this folding takes place eagerly, during AST construction. If you |
Notice that this folding takes place eagerly, during AST construction. If you |
want to implement delayed folding after constant propagation or variable |
want to implement delayed folding after constant propagation or variable |
>>> c = Code() |
>>> c = Code() |
>>> c( Call(Const(type), [1]) ) |
>>> c( Call(Const(type), [1]) ) |
>>> dis(c.code()) |
>>> dis(c.code()) |
0 0 LOAD_CONST 1 (<type 'int'>) |
0 0 LOAD_CONST 1 (<... 'int'>) |
|
|
>>> c = Code() |
>>> c = Code() |
>>> c( Call(Const(type), [1], fold=False) ) |
>>> c( Call(Const(type), [1], fold=False) ) |
>>> dis(c.code()) |
>>> dis(c.code()) |
0 0 LOAD_CONST 1 (<type 'type'>) |
0 0 LOAD_CONST 1 (<... 'type'>) |
3 LOAD_CONST 2 (1) |
3 LOAD_CONST 2 (1) |
6 CALL_FUNCTION 1 |
6 CALL_FUNCTION 1 |
|
|
>>> Getattr = nodetype()(Getattr) |
>>> Getattr = nodetype()(Getattr) |
|
|
>>> const_value(Getattr(1, '__class__')) |
>>> const_value(Getattr(1, '__class__')) |
<type 'int'> |
<... 'int'> |
|
|
The ``fold_args()`` function tries to evaluate the node immediately, if all of |
The ``fold_args()`` function tries to evaluate the node immediately, if all of |
its arguments are constants, by creating a temporary ``Code`` object, and |
its arguments are constants, by creating a temporary ``Code`` object, and |
... pass |
... pass |
|
|
>>> c = Code.from_function(f1) |
>>> c = Code.from_function(f1) |
>>> f2 = new.function(c.code(), globals()) |
>>> f2 = function(c.code(), globals()) |
|
|
>>> import inspect |
>>> import inspect |
|
|
>>> def f3(a, (b,c), (d,(e,f))): |
>>> def f3(a, (b,c), (d,(e,f))): |
... pass |
... pass |
|
|
>>> f4 = new.function(Code.from_function(f3).code(), globals()) |
>>> f4 = function(Code.from_function(f3).code(), globals()) |
>>> dis(f4) |
>>> dis(f4) |
0 0 LOAD_FAST 1 (.1) |
0 0 LOAD_FAST 1 (.1) |
3 UNPACK_SEQUENCE 2 |
3 UNPACK_SEQUENCE 2 |
>>> c.LOAD_CONST(42) |
>>> c.LOAD_CONST(42) |
>>> c.RETURN_VALUE() |
>>> c.RETURN_VALUE() |
|
|
>>> f = new.function(c.code(), globals()) |
>>> f = function(c.code(), globals()) |
>>> f(1,2,3) |
>>> f(1,2,3) |
42 |
42 |
|
|
|
|
>>> c = Code() |
>>> c = Code() |
>>> fwd = c.JUMP_FORWARD() |
>>> fwd = c.JUMP_FORWARD() |
>>> print c.stack_size # forward jump marks stack size as unknown |
>>> print(c.stack_size) # forward jump marks stack size as unknown |
None |
None |
|
|
>>> c.LOAD_CONST(42) |
>>> c.LOAD_CONST(42) |
POP_BLOCK |
POP_BLOCK |
JUMP_FORWARD L4 |
JUMP_FORWARD L4 |
L1: DUP_TOP |
L1: DUP_TOP |
LOAD_CONST 2 (<...exceptions.KeyError...>) |
LOAD_CONST 2 (<...KeyError...>) |
COMPARE_OP 10 (exception match) |
COMPARE_OP 10 (exception match) |
JUMP_IF_FALSE L2 |
JUMP_IF_FALSE L2 |
POP_TOP |
POP_TOP |
JUMP_FORWARD L5 |
JUMP_FORWARD L5 |
L2: POP_TOP |
L2: POP_TOP |
DUP_TOP |
DUP_TOP |
LOAD_CONST 4 (<...exceptions.TypeError...>) |
LOAD_CONST 4 (<...TypeError...>) |
COMPARE_OP 10 (exception match) |
COMPARE_OP 10 (exception match) |
JUMP_IF_FALSE L3 |
JUMP_IF_FALSE L3 |
POP_TOP |
POP_TOP |
clause, and a loop body:: |
clause, and a loop body:: |
|
|
>>> from peak.util.assembler import For |
>>> from peak.util.assembler import For |
>>> y = Call(Const(range), (3,)) |
>>> y = Call(Const(list), (Call(Const(range), (3,)),)) |
>>> x = LocalAssign('x') |
>>> x = LocalAssign('x') |
>>> body = Suite([Local('x'), Code.PRINT_EXPR]) |
>>> body = Suite([Local('x'), Code.PRINT_EXPR]) |
|
|
>>> c.return_(Function(Return(Local('a')), 'f', ['a'], defaults=[42])) |
>>> c.return_(Function(Return(Local('a')), 'f', ['a'], defaults=[42])) |
>>> dis(c.code()) |
>>> dis(c.code()) |
0 0 LOAD_CONST 1 (42) |
0 0 LOAD_CONST 1 (42) |
3 LOAD_CONST 2 (<... f ..., file "<string>", line -1>) |
3 LOAD_CONST 2 (<... f..., file ...<string>..., line ...>) |
6 MAKE_FUNCTION 1 |
6 MAKE_FUNCTION 1 |
9 RETURN_VALUE |
9 RETURN_VALUE |
|
|
>>> dis(c.code()) |
>>> dis(c.code()) |
0 0 LOAD_CONST 1 (99) |
0 0 LOAD_CONST 1 (99) |
3 LOAD_CONST 2 (66) |
3 LOAD_CONST 2 (66) |
6 LOAD_CONST 3 (<... f ..., file "<string>", line -1>) |
6 LOAD_CONST 3 (<... f..., file ...<string>..., line ...>) |
9 MAKE_FUNCTION 2 |
9 MAKE_FUNCTION 2 |
12 RETURN_VALUE |
12 RETURN_VALUE |
|
|
|
|
>>> dis(f) |
>>> dis(f) |
0 0 LOAD_CLOSURE 0 (a) |
0 0 LOAD_CLOSURE 0 (a) |
... LOAD_CONST 1 (<... <lambda> ..., file "<string>", line -1>) |
... LOAD_CONST 1 (<... <lambda>..., file ...<string>..., line ...>) |
... MAKE_CLOSURE 0 |
... MAKE_CLOSURE 0 |
... RETURN_VALUE |
... RETURN_VALUE |
|
|
>>> c.stack_size |
>>> c.stack_size |
0 |
0 |
>>> if sys.version>='2.7': |
>>> if sys.version>='2.7': |
... print c.stack_history == [0, 1, 1, 1, 0, 0, 0, None, None, 1] |
... print(c.stack_history == [0, 1, 1, 1, 0, 0, 0, None, None, 1]) |
... else: |
... else: |
... print c.stack_history == [0, 1, 1, 1, 1, 1, 1, 0, None, None, 1] |
... print(c.stack_history == [0, 1, 1, 1, 1, 1, 1, 0, None, None, 1]) |
True |
True |
|
|
|
|
>>> c = Code() |
>>> c = Code() |
>>> c.return_(Call(Const(type), [], [], (1,))) |
>>> c.return_(Call(Const(type), [], [], (1,))) |
>>> dis(c.code()) |
>>> dis(c.code()) |
0 0 LOAD_CONST 1 (<type 'int'>) |
0 0 LOAD_CONST 1 (<... 'int'>) |
3 RETURN_VALUE |
3 RETURN_VALUE |
|
|
|
|
POP_BLOCK |
POP_BLOCK |
JUMP_FORWARD L3 |
JUMP_FORWARD L3 |
L1: DUP_TOP |
L1: DUP_TOP |
LOAD_CONST 1 (<...exceptions.AttributeError...>) |
LOAD_CONST 1 (<...AttributeError...>) |
COMPARE_OP 10 (exception match) |
COMPARE_OP 10 (exception match) |
JUMP_IF_FALSE L2 |
JUMP_IF_FALSE L2 |
POP_TOP |
POP_TOP |
POP_TOP |
POP_TOP |
POP_TOP |
POP_TOP |
POP_TOP |
POP_TOP |
LOAD_CONST 2 (<type 'type'>) |
LOAD_CONST 2 (<... 'type'>) |
ROT_TWO |
ROT_TWO |
CALL_FUNCTION 1 |
CALL_FUNCTION 1 |
JUMP_FORWARD L3 |
JUMP_FORWARD L3 |
END_FINALLY |
END_FINALLY |
L3: RETURN_VALUE |
L3: RETURN_VALUE |
|
|
>>> type_or_class.func_code = c.code() |
>>> type_or_class.__code__ = type_or_class.func_code = c.code() |
>>> type_or_class(23) |
>>> type_or_class(23) |
<type 'int'> |
<... 'int'> |
|
|
|
|
|
|
>>> c(Switch(Local('x'), [(1,Return(42)),(2,Return("foo"))], Return(27))) |
>>> c(Switch(Local('x'), [(1,Return(42)),(2,Return("foo"))], Return(27))) |
>>> c.return_() |
>>> c.return_() |
|
|
>>> f = new.function(c.code(), globals()) |
>>> f = function(c.code(), globals()) |
>>> f(1) |
>>> f(1) |
42 |
42 |
>>> f(2) |
>>> f(2) |
|
|
>>> dump(c.code()) |
>>> dump(c.code()) |
SETUP_LOOP L2 |
SETUP_LOOP L2 |
LOAD_CONST 1 (<...method get of dict...>) |
LOAD_CONST 1 (<...method ...get of ...>) |
LOAD_FAST 0 (x) |
LOAD_FAST 0 (x) |
CALL_FUNCTION 1 |
CALL_FUNCTION 1 |
JUMP_IF_FALSE L1 |
JUMP_IF_FALSE L1 |
* Exhaustive tests of all opcodes' stack history effects |
* Exhaustive tests of all opcodes' stack history effects |
|
|
* Test wide jumps and wide argument generation in general |
* Test wide jumps and wide argument generation in general |
|
|
|
* Remove/renumber local variables when a local is converted to free/cell |
|
|