[Subversion] / BytecodeAssembler / README.txt  

Diff of /BytecodeAssembler/README.txt

Parent Directory | Revision Log

version 2205, Tue Jul 4 22:21:25 2006 UTC version 2451, Sat Dec 29 18:38:07 2007 UTC
Line 14 
Line 14 
 the work needed to transform tree-like structures into linear bytecode  the work needed to transform tree-like structures into linear bytecode
 instructions, and includes the ability to do compile-time constant folding.  instructions, and includes the ability to do compile-time constant folding.
   
   Changes since version 0.2:
   
   * Added a ``Getattr`` symbol that does static or dynamic attribute access and
     constant folding
   
   * Fixed ``code.from_function()`` not copying the ``co_filename`` attribute when
     ``copy_lineno`` was specified.
   
   * The ``repr()`` of AST nodes doesn't include a trailing comma for 1-argument
     node types any more.
   
   * Added a ``Pass`` symbol that generates no code, a ``Compare()`` node type
     that does n-way comparisons, and ``And()`` and ``Or()`` node types for doing
     logical operations.
   
   * The ``COMPARE_OP()`` method now accepts operator strings like ``"<="``,
     ``"not in"``, ``"exception match"``, and so on, as well as numeric opcodes.
     See the standard library's ``opcode`` module for a complete list of the
     strings accepted (in the ``cmp_op`` tuple).  ``"<>"`` is also accepted as an
     alias for ``"!="``.
   
 Changes since version 0.1:  Changes since version 0.1:
   
   * Constant handling has been fixed so that it doesn't confuse equal values of
     differing types (e.g. ``1.0`` and ``True``), or equal unhashable objects
     (e.g. two empty lists).
   
   * Removed ``nil``, ``ast_curry()`` and ``folding_curry()``, replacing them with
     the ``nodetype()`` decorator and ``fold_args()``; please see the docs for
     more details.
   
 * Added stack tracking across jumps, globally verifying stack level prediction  * Added stack tracking across jumps, globally verifying stack level prediction
   consistency and rejecting dead code.    consistency and automatically rejecting attempts to generate dead code.  It
     should now be virtually impossible to accidentally generate bytecode that can
     crash the interpreter.  (If you find a way, let me know!)
   
 Changes since version 0.0.1:  Changes since version 0.0.1:
   
Line 258 
Line 289 
     >>> c.LOAD_CONST(None)  # in real code, this'd be a Python code constant      >>> c.LOAD_CONST(None)  # in real code, this'd be a Python code constant
     >>> c.MAKE_CLOSURE(0,2) # no defaults, 2 free vars in the new function      >>> c.MAKE_CLOSURE(0,2) # no defaults, 2 free vars in the new function
   
   The ``COMPARE_OP`` method takes an argument which can be a valid comparison
   integer constant, or a string containing a Python operator, e.g.::
   
       >>> c = Code()
       >>> c.LOAD_CONST(1)
       >>> c.LOAD_CONST(2)
       >>> c.COMPARE_OP('not in')
       >>> dis(c.code())
         0           0 LOAD_CONST               1 (1)
                     3 LOAD_CONST               2 (2)
                     6 COMPARE_OP               7 (not in)
   
   The full list of valid operator strings can be found in the standard library's
   ``opcode`` module.  ``"<>"`` is also accepted as an alias for ``"!="``::
   
       >>> c.LOAD_CONST(3)
       >>> c.COMPARE_OP('<>')
       >>> dis(c.code())
         0           0 LOAD_CONST               1 (1)
                     3 LOAD_CONST               2 (2)
                     6 COMPARE_OP               7 (not in)
                     9 LOAD_CONST               3 (3)
                    12 COMPARE_OP               3 (!=)
   
   
 High-Level Code Generation  High-Level Code Generation
 ==========================  ==========================
Line 291 
Line 346 
                  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> at ...>)
   
   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
   the one you requested::
   
       >>> c = Code()
       >>> c(1, True, 1.0, 1L)     # equal, but different types
       >>> dis(c.code())
         0           0 LOAD_CONST               1 (1)
                     3 LOAD_CONST               2 (True)
                     6 LOAD_CONST               3 (1.0)
                     9 LOAD_CONST               4 (1L)
   
   
 Simple Containers  Simple Containers
 -----------------  -----------------
Line 335 
Line 402 
 a constant, rather than generating code to recreate the tuple using a series of  a constant, rather than generating code to recreate the tuple using a series of
 ``LOAD_CONST`` operations followed by a ``BUILD_TUPLE``.  ``LOAD_CONST`` operations followed by a ``BUILD_TUPLE``.
   
   If the value wrapped in a ``Const`` is not hashable, it is compared by identity
   rather than value.  This prevents equal mutable values from being reused by
   accident, e.g. if you plan to mutate the "constant" values later::
   
       >>> c = Code()
       >>> c(Const([]), Const([]))     # equal, but not the same object!
       >>> dis(c.code())
         0           0 LOAD_CONST               1 ([])
                     3 LOAD_CONST               2 ([])
   
   Thus, although ``Const`` objects hash and compare based on equality for
   hashable types::
   
       >>> hash(Const(3)) == hash(3)
       True
       >>> Const(3)==Const(3)
       True
   
   They hash and compare based on object identity for non-hashable types::
   
       >>> c = Const([])
       >>> hash(c) == hash(id(c.value))
       True
       >>> c == Const(c.value)     # compares equal if same object
       True
       >>> c == Const([])          # but is not equal to a merely equal object
       False
   
   
 Local and Global Names  Local and Global Names
 ----------------------  ----------------------
Line 383 
Line 478 
                   6 LOAD_DEREF               1 (z)                    6 LOAD_DEREF               1 (z)
   
   
   Obtaining Attributes
   --------------------
   
   The ``Getattr`` node type takes an expression and an attribute name.  The
   attribute name can be a constant string, in which case a ``LOAD_ATTR`` opcode
   is used, and constant folding is done if possible::
   
       >>> from peak.util.assembler import Getattr
   
       >>> c = Code()
       >>> c(Getattr(Local('x'), '__class__'))
       >>> dis(c.code())
         0           0 LOAD_FAST                0 (x)
                     3 LOAD_ATTR                0 (__class__)
   
   
       >>> Getattr(Const(object), '__class__') # const expression, const result
       Const(<type 'type'>)
   
   Or the attribute name can be an expression, in which case a ``getattr()`` call
   is compiled instead::
   
       >>> c = Code()
       >>> c(Getattr(Local('x'), Local('y')))
       >>> dis(c.code())
         0           0 LOAD_CONST               1 (<built-in function getattr>)
                     3 LOAD_FAST                0 (x)
                     6 LOAD_FAST                1 (y)
                     9 CALL_FUNCTION            2
   
   
 Calling Functions and Methods  Calling Functions and Methods
 -----------------------------  -----------------------------
   
Line 510 
Line 636 
     AssertionError: Label previously defined      AssertionError: Label previously defined
   
   
   N-Way Comparisons
   -----------------
   
   You can generate N-way comparisons using the ``Compare()`` node type::
   
       >>> from peak.util.assembler import Compare
   
       >>> c = Code()
       >>> c(Compare(Local('a'), [('<', Local('b'))]))
       >>> dis(c.code())
         0           0 LOAD_FAST                0 (a)
                     3 LOAD_FAST                1 (b)
                     6 COMPARE_OP               0 (<)
   
   3-way comparisons generate code that's a bit more complex.  Here's a three-way
   comparison (``a<b<c``)::
   
       >>> c = Code()
       >>> c.return_(Compare(Local('a'), [('<', Local('b')), ('<', Local('c'))]))
       >>> dis(c.code())
         0           0 LOAD_FAST                0 (a)
                     3 LOAD_FAST                1 (b)
                     6 DUP_TOP
                     7 ROT_THREE
                     8 COMPARE_OP               0 (<)
                    11 JUMP_IF_FALSE           10 (to 24)
                    14 POP_TOP
                    15 LOAD_FAST                2 (c)
                    18 COMPARE_OP               0 (<)
                    21 JUMP_FORWARD             2 (to 26)
               >>   24 ROT_TWO
                    25 POP_TOP
               >>   26 RETURN_VALUE
   
   And a four-way (``a<b>c!=d``)::
   
       >>> c = Code()
       >>> c.return_(
       ...     Compare( Local('a'), [
       ...         ('<', Local('b')), ('>', Local('c')), ('!=', Local('d'))
       ...     ])
       ... )
       >>> dis(c.code())
         0           0 LOAD_FAST                0 (a)
                     3 LOAD_FAST                1 (b)
                     6 DUP_TOP
                     7 ROT_THREE
                     8 COMPARE_OP               0 (<)
                    11 JUMP_IF_FALSE           22 (to 36)
                    14 POP_TOP
                    15 LOAD_FAST                2 (c)
                    18 DUP_TOP
                    19 ROT_THREE
                    20 COMPARE_OP               4 (>)
                    23 JUMP_IF_FALSE           10 (to 36)
                    26 POP_TOP
                    27 LOAD_FAST                3 (d)
                    30 COMPARE_OP               3 (!=)
                    33 JUMP_FORWARD             2 (to 38)
               >>   36 ROT_TWO
                    37 POP_TOP
               >>   38 RETURN_VALUE
   
   
 Constant Detection and Folding  Constant Detection and Folding
 ==============================  ==============================
   
Line 535 
Line 725 
     >>> const_value(Local('x'))      >>> const_value(Local('x'))
     Traceback (most recent call last):      Traceback (most recent call last):
       ...        ...
     NotAConstant: <bound method str.Local of 'x'>      NotAConstant: Local('x')
   
 Tuples of constants are recursively replaced by constant tuples::  Tuples of constants are recursively replaced by constant tuples::
   
Line 550 
Line 740 
     >>> const_value( (1,Global('y')) )      >>> const_value( (1,Global('y')) )
     Traceback (most recent call last):      Traceback (most recent call last):
       ...        ...
     NotAConstant: <bound method str.Global of 'y'>      NotAConstant: Global('y')
   
 As do any types not previously described here::  As do any types not previously described here::
   
Line 576 
Line 766 
 ``Const`` node instead of a ``Call`` node::  ``Const`` node instead of a ``Call`` node::
   
     >>> Call( Const(type), [1] )      >>> Call( Const(type), [1] )
     <bound method type.Const of <type 'int'>>      Const(<type 'int'>)
   
 Thus, you can also take the ``const_value()`` of such calls::  Thus, you can also take the ``const_value()`` of such calls::
   
Line 587 
Line 777 
 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)] )])
     <bound method type.Const of <type 'dict'>>      Const(<type '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
Line 625 
Line 815 
 ``globals()``, in other words.  ``globals()``, in other words.
   
   
   Logical And/Or
   --------------
   
   You can evaluate logical and/or expressions using the ``And`` and ``Or`` node
   types::
   
       >>> from peak.util.assembler import And, Or
   
       >>> c = Code()
       >>> c.return_( And([Local('x'), Local('y')]) )
       >>> dis(c.code())
         0           0 LOAD_FAST                0 (x)
                     3 JUMP_IF_FALSE            4 (to 10)
                     6 POP_TOP
                     7 LOAD_FAST                1 (y)
               >>   10 RETURN_VALUE
   
       >>> c = Code()
       >>> c.return_( Or([Local('x'), Local('y')]) )
       >>> dis(c.code())
         0           0 LOAD_FAST                0 (x)
                     3 JUMP_IF_TRUE             4 (to 10)
                     6 POP_TOP
                     7 LOAD_FAST                1 (y)
               >>   10 RETURN_VALUE
   
   
   True or false constants are folded automatically, avoiding code generation
   for intermediate values that will never be used in the result::
   
       >>> c = Code()
       >>> c.return_( And([1, 2, Local('y')]) )
       >>> dis(c.code())
         0           0 LOAD_FAST                0 (y)
                     3 RETURN_VALUE
   
       >>> c = Code()
       >>> c.return_( And([1, 2, Local('y'), 0]) )
       >>> dis(c.code())
         0           0 LOAD_FAST                0 (y)
                     3 JUMP_IF_FALSE            4 (to 10)
                     6 POP_TOP
                     7 LOAD_CONST               1 (0)
               >>   10 RETURN_VALUE
   
       >>> c = Code()
       >>> c.return_( Or([1, 2, Local('y')]) )
       >>> dis(c.code())
         0           0 LOAD_CONST               1 (1)
                     3 RETURN_VALUE
   
       >>> c = Code()
       >>> c.return_( Or([False, Local('y'), 3]) )
       >>> dis(c.code())
         0           0 LOAD_FAST                0 (y)
                     3 JUMP_IF_TRUE             4 (to 10)
                     6 POP_TOP
                     7 LOAD_CONST               1 (3)
               >>   10 RETURN_VALUE
   
   
 Custom Code Generation  Custom Code Generation
 ======================  ======================
   
Line 646 
Line 897 
 As you can see, the ``Code.DUP_TOP()`` is called on the code instance, causing  As you can see, the ``Code.DUP_TOP()`` is called on the code instance, causing
 a ``DUP_TOP`` opcode to be output.  This is sometimes a handy trick for  a ``DUP_TOP`` opcode to be output.  This is sometimes a handy trick for
 accessing values that are already on the stack.  More commonly, however, you'll  accessing values that are already on the stack.  More commonly, however, you'll
 want to implement more sophisticated callables, perhaps something like::  want to implement more sophisticated callables.
   
     >>> from peak.util.assembler import ast_curry  To make it easy to create diverse target types, a ``nodetype()`` decorator is
   provided::
   
       >>> from peak.util.assembler import nodetype
   
   It allows you to create code generation target types using functions.  Your
   function should take one or more arguments, with a ``code=None`` optional
   argument in the last position.  It should check whether ``code is None`` when
   called, and if so, return a tuple of the preceding arguments.  If ``code``
   is not ``None``, then it should do whatever code generating tasks are required.
   For example::
   
     >>> def TryFinally(block1, block2, code=None):      >>> def TryFinally(block1, block2, code=None):
     ...     if code is None:      ...     if code is None:
     ...         return ast_curry(TryFinally, block1, block2)      ...         return block1, block2
     ...     code(      ...     code(
     ...         Code.SETUP_FINALLY,      ...         Code.SETUP_FINALLY,
     ...             block1,      ...             block1,
Line 660 
Line 921 
     ...             block2,      ...             block2,
     ...         Code.END_FINALLY      ...         Code.END_FINALLY
     ...     )      ...     )
       >>> TryFinally = nodetype()(TryFinally)
   
   Note: although the nodetype() generator can be used above the function
   definition in either Python 2.3 or 2.4, it cannot be done in a doctest under
   Python 2.3, so this document doesn't attempt to demonstrate that.  Under
   2.4, you would do something like this::
   
       @nodetype()
       def TryFinally(...):
   
   and code that needs to also work under 2.3 should do something like this::
   
       nodetype()
       def TryFinally(...):
   
   But to keep the examples here working with doctest, we'll be doing our
   ``nodetype()`` calls after the end of the function definitions, e.g.::
   
     >>> def ExprStmt(value, code=None):      >>> def ExprStmt(value, code=None):
     ...     if code is None:      ...     if code is None:
     ...         return ast_curry(ExprStmt, value)      ...         return value,
     ...     code( value, Code.POP_TOP )      ...     code( value, Code.POP_TOP )
       >>> ExprStmt = nodetype()(ExprStmt)
   
     >>> c = Code()      >>> c = Code()
     >>> c( TryFinally(ExprStmt(1), ExprStmt(2)) )      >>> c( TryFinally(ExprStmt(1), ExprStmt(2)) )
Line 678 
Line 957 
                  14 POP_TOP                   14 POP_TOP
                  15 END_FINALLY                   15 END_FINALLY
   
   The ``nodetype()`` decorator is virtually identical to the ``struct()``
   decorator in the DecoratorTools package, except that it does not support
   ``*args``, does not create a field for the ``code`` argument, and generates a
   ``__call__()`` method that reinvokes the wrapped function to do the actual
   code generation.
   
   Among the benefits of this decorator are:
   
   * It gives your node types a great debugging format::
   
       >>> tf = TryFinally(ExprStmt(1), ExprStmt(2))
       >>> tf
       TryFinally(ExprStmt(1), ExprStmt(2))
   
   * It makes named fields accessible::
   
       >>> tf.block1
       ExprStmt(1)
   
 The ``ast_curry()`` utility function returns an ``instancemethod`` chain that      >>> tf.block2
 binds the given arguments to the given function, creating a hashable and      ExprStmt(2)
 comparable data structure -- a trivial sort of "AST node".  Just follow the  
 code pattern above, using a ``code=None`` final argument, and returning a  
 curried version of the function if ``code is None``.  Otherwise, your function  
 should simply do whatever is needed to "generate" the arguments.  
   
 (This is exactly the same pattern that ``peak.util.assembler`` uses internally  
 to implement ``Const``, ``Call``, ``Local``, and other wrapper functions.)  
   
 The ``ast_curry()`` utility function isn't quite perfect; due to a quirk of the  
 ``instancemethod`` type, it can't save arguments whose value is ``None``: if  
 you pass a ``None`` argument to ``ast_curry()``, it will be replaced with a  
 special ``nil`` object that tests as false, and generates a ``None`` constant  
 when code is generated for it.  If your function accepts any arguments that  
 might have a value of ``None``, you must correctly handle the cases where you  
 receive a value of ``nil`` (found in ``peak.util.assembler``) instead of  
 ``None``.  
   
 However, if you can use ``ast_curry()`` to generate your AST nodes, you will  
 have objects that are hashable and comparable by default, as long as none of  
 your child nodes are unhashable or incomparable.  This can be useful for  
 algorithms that require comparing AST subtrees, such as common subexpression  
 elimination.  
   
   * Hashing and comparison work as expected (handy for algorithms that require
     comparing or caching AST subtrees, such as common subexpression
     elimination)::
   
       >>> ExprStmt(1) == ExprStmt(1)
       True
       >>> ExprStmt(1) == ExprStmt(2)
       False
   
   
   Please see the `struct decorator documentation`_ for info on how to customize
   node types further.
   
   .. _struct decorator documentation: http://peak.telecommunity.com/DevCenter/DecoratorTools#the-struct-decorator
   
   Note: hashing only works if all the values you return in your argument tuple
   are hashable, so you should try to convert them if possible.  For example, if
   an argument accepts any sequence, you should probably convert it to a tuple
   before returning it.  Most of the examples in this document, and the node types
   supplied by ``peak.util.assembler`` itself do this.
   
   
 Constant Folding in Custom Targets  Constant Folding in Custom Targets
Line 711 
Line 1006 
   
 If you want to incorporate constant-folding into your AST nodes, you can do  If you want to incorporate constant-folding into your AST nodes, you can do
 so by checking for constant values and folding them at either construction  so by checking for constant values and folding them at either construction
 or code generation time.  For example, this ``And`` node type folds constants  or code generation time.  For example, this ``And`` node type (a simpler
 during code generation, by not generating unnecessary branches when it can  version of the one included in ``peak.util.assembler``) folds constants during
   code generation, by not generating unnecessary branches when it can
 prove which way a branch will go::  prove which way a branch will go::
   
     >>> from peak.util.assembler import NotAConstant      >>> from peak.util.assembler import NotAConstant
   
     >>> def And(values, code=None):      >>> def And(values, code=None):
     ...     if code is None:      ...     if code is None:
     ...         return ast_curry(And, tuple(values))      ...         return tuple(values),
     ...     end = Label()      ...     end = Label()
     ...     for value in values[:-1]:      ...     for value in values[:-1]:
     ...         try:      ...         try:
Line 730 
Line 1026 
     ...         else:       # and false constants end the chain right away      ...         else:       # and false constants end the chain right away
     ...             return code(value, end)      ...             return code(value, end)
     ...     code(values[-1], end)      ...     code(values[-1], end)
       >>> And = nodetype()(And)
   
     >>> c = Code()      >>> c = Code()
     >>> c.return_( And([1, 2]) )      >>> c.return_( And([1, 2]) )
Line 754 
Line 1051 
   
 The above example only folds constants at code generation time, however.  You  The above example only folds constants at code generation time, however.  You
 can also do constant folding at AST construction time, using the  can also do constant folding at AST construction time, using the
 ``folding_curry()`` function.  For example::  ``fold_args()`` function.  For example::
   
     >>> from peak.util.assembler import folding_curry      >>> from peak.util.assembler import fold_args
   
     >>> def Getattr(ob, name, code=None):      >>> def Getattr(ob, name, code=None):
     ...     try:      ...     try:
Line 764 
Line 1061 
     ...     except NotAConstant:      ...     except NotAConstant:
     ...         return Call(Const(getattr), [ob, name])      ...         return Call(Const(getattr), [ob, name])
     ...     if code is None:      ...     if code is None:
     ...         return folding_curry(Getattr, ob, name)      ...         return fold_args(Getattr, ob, name)
     ...     code(ob)      ...     code(ob)
     ...     code.LOAD_ATTR(name)      ...     code.LOAD_ATTR(name)
       >>> Getattr = nodetype()(Getattr)
   
     >>> const_value(Getattr(1, '__class__'))      >>> const_value(Getattr(1, '__class__'))
     <type 'int'>      <type 'int'>
   
 The ``folding_curry()`` function is essentially the same as ``ast_curry()``,  The ``fold_args()`` function tries to evaluate the node immediately, if all of
 unless all of the arguments it's given are recognized as constants.  In that  its arguments are constants, by creating a temporary ``Code`` object, and
 case, ``folding_curry()`` will create a temporary ``Code`` object, and run the  running the supplied function against it, then doing an ``eval()`` on the
 curried function against it, doing an ``eval()`` on the generated code and  generated code and wrapping the result in a ``Const``.  However, if any of the
 wrapping the result in a ``Const``.  arguments are non-constant, the original arguments (less the function) are
   returned. This causes a normal node instance to be created instead of a
   ``Const``.
   
 This isn't a very *fast* way of doing partial evaluation, but it makes it  This isn't a very *fast* way of doing partial evaluation, but it makes it
 really easy to define new code generation targets without writing custom  really easy to define new code generation targets without writing custom
 constant-folding code for each one.  Just use ``folding_curry()`` instead of  constant-folding code for each one.  Just ``return fold_args(ThisType, *args)``
 ``ast_curry()`` if you want your node constructor to be able to do eager  instead of ``return args``, if you want your node constructor to be able to do
 evaluation.  If you need to, you can check your parameters in order to decide  eager evaluation.  If you need to, you can check your parameters in order to
 whether to call ``ast_curry()`` or ``folding_curry()``; this is in fact how  decide whether to call ``fold_args()`` or not; this is in fact how ``Call``
 ``Call`` implements its ``fold`` argument and the suppression of folding when  implements its ``fold`` argument and the suppression of folding when
 the call has no arguments.  the call has no arguments.
   
   (By the way, this same ``Getattr`` node type is also available
   
   
 Setting the Code's Calling Signature  Setting the Code's Calling Signature
 ====================================  ====================================
Line 819 
Line 1121 
     >>> c1 = Code.from_function(f1, copy_lineno=True)      >>> c1 = Code.from_function(f1, copy_lineno=True)
     >>> c1.co_firstlineno      >>> c1.co_firstlineno
     1      1
       >>> c1.co_filename is f1.func_code.co_filename
       True
   
 If you create a ``Code`` instance from a function that has nested positional  If you create a ``Code`` instance from a function that has nested positional
 arguments, the returned code object will include a prologue to unpack the  arguments, the returned code object will include a prologue to unpack the
Line 1027 
Line 1331 
 code that might be unreachable.  For example, consider this ``If``  code that might be unreachable.  For example, consider this ``If``
 implementation::  implementation::
   
     >>> def Pass(code=None):      >>> from peak.util.assembler import Pass
     ...     if code is None:  
     ...         return Pass  
   
     >>> def If(cond, then, else_=Pass, code=None):      >>> def If(cond, then, else_=Pass, code=None):
     ...     if code is None:      ...     if code is None:
     ...         return ast_curry(If,cond,then,else_)      ...         return cond, then, else_
     ...     else_clause = Label()      ...     else_clause = Label()
     ...     end_if = Label()      ...     end_if = Label()
     ...     code(cond, else_clause.JUMP_IF_FALSE, Code.POP_TOP, then)      ...     code(cond, else_clause.JUMP_IF_FALSE, Code.POP_TOP, then)
     ...     code(end_if.JUMP_FORWARD, else_clause, Code.POP_TOP, else_)      ...     code(end_if.JUMP_FORWARD, else_clause, Code.POP_TOP, else_)
     ...     code(end_if)      ...     code(end_if)
       >>> If = nodetype()(If)
   
 It works okay if there's no dead code::  It works okay if there's no dead code::
   
     >>> c = Code()      >>> c = Code()
     >>> c( If(23, 42, 55) )      >>> c( If(23, 42, 55) )
     >>> dis(c.code())      >>> dis(c.code())   # Python 2.3 may peephole-optimize this code
       0           0 LOAD_CONST               1 (23)        0           0 LOAD_CONST               1 (23)
                   3 JUMP_IF_FALSE            7 (to 13)                    3 JUMP_IF_FALSE            7 (to 13)
                   6 POP_TOP                    6 POP_TOP
Line 1065 
Line 1367 
   
     >>> def If(cond, then, else_=Pass, code=None):      >>> def If(cond, then, else_=Pass, code=None):
     ...     if code is None:      ...     if code is None:
     ...         return ast_curry(If,cond,then,else_)      ...         return cond, then, else_
     ...     else_clause = Label()      ...     else_clause = Label()
     ...     end_if = Label()      ...     end_if = Label()
     ...     code(cond, else_clause.JUMP_IF_FALSE, Code.POP_TOP, then)      ...     code(cond, else_clause.JUMP_IF_FALSE, Code.POP_TOP, then)
     ...     if code.stack_size is not None:      ...     if code.stack_size is not None:
     ...         end_if.JUMP_FORWARD(code)      ...         end_if.JUMP_FORWARD(code)
     ...     code(else_clause, Code.POP_TOP, else_, end_if)      ...     code(else_clause, Code.POP_TOP, else_, end_if)
       >>> If = nodetype()(If)
   
 As you can see, the dead code is now eliminated::  As you can see, the dead code is now eliminated::
   
     >>> c = Code()      >>> c = Code()
     >>> c( If(23, Return(42), 55) )      >>> c( If(23, Return(42), 55) )
     >>> dis(c.code())      >>> dis(c.code())   # Python 2.3 may peephole-optimize this code
       0           0 LOAD_CONST               1 (23)        0           0 LOAD_CONST               1 (23)
                   3 JUMP_IF_FALSE            5 (to 11)                    3 JUMP_IF_FALSE            5 (to 11)
                   6 POP_TOP                    6 POP_TOP
Line 1742 
Line 2045 
                   3 RETURN_VALUE                    3 RETURN_VALUE
   
   
   
 Demo: "Computed Goto"/"Switch Statement"  Demo: "Computed Goto"/"Switch Statement"
 ========================================  ========================================
   
Line 1760 
Line 2062 
   
     >>> def Switch(expr, cases, default=Pass, code=None):      >>> def Switch(expr, cases, default=Pass, code=None):
     ...     if code is None:      ...     if code is None:
     ...         return ast_curry(Switch, expr, tuple(cases), default)      ...         return expr, tuple(cases), default
     ...      ...
     ...     d = {}      ...     d = {}
     ...     else_block  = Label()      ...     else_block  = Label()
Line 1789 
Line 2091 
     ...             Code.POP_BLOCK,      ...             Code.POP_BLOCK,
     ...         end_switch      ...         end_switch
     ...     )      ...     )
       >>> Switch = nodetype()(Switch)
   
     >>> c = Code()      >>> c = Code()
     >>> c.co_argcount=1      >>> c.co_argcount=1
Line 1826 
Line 2129 
 TODO  TODO
 ====  ====
   
 * AST introspection  
     * ast_type(node): called function, Const, or node.__class__  
       * tuples are Const if their contents are; no other types are Const  
     * ast_children(node): tuple of argument values for curried types, const value,  
       or empty tuple.  If node is a tuple, the value must be flattened.  
     * is_const(node): ast_type(node) is Const  
   
 * Inline builtins (getattr, operator.getitem, etc.) to opcodes  
     * Getattr/Op/Unary("symbol", arg1 [, arg2]) node types -> Call() if folding  
     * Call() translates functions back to Ops if inlining  
   
 * Pretty printing and short-naming of ASTs  
   
 * Test NAME vs. FAST operators flag checks/sets  * Test NAME vs. FAST operators flag checks/sets
   
 * Test code flags generation/cloning  * Test code flags generation/cloning
   
   * Exhaustive tests of all opcodes' stack history effects
   
   * YIELD_EXPR should set CO_GENERATOR; stack effects depend on Python version
   


Generate output suitable for use with a patch program
Legend:
Removed from v.2205  
changed lines
  Added in v.2451

cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help