Skip to content

Commit

Permalink
complete doc
Browse files Browse the repository at this point in the history
* complete changelog
* write bytecode example with conditional jump and labels
  • Loading branch information
vstinner committed Feb 29, 2016
1 parent f638927 commit a9fdc2b
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 19 deletions.
23 changes: 17 additions & 6 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ Functions

.. function:: dump_bytecode(bytecode, \*, lineno=False)

Dump a bytecode to the standard output.
Dump a bytecode to the standard output. :class:`ConcreteBytecode`,
:class:`Bytecode` and :class:`BytecodeBlocks` are accepted for *bytecode*.

If *lineno*, show line numbers. For blocks, show also instruction index
relative to the current block.
If *lineno*, show also line numbers and instruction index/offset.

This function is written for debug purpose.

Expand Down Expand Up @@ -91,10 +91,10 @@ Instr

.. method:: has_jump()

Has the operation a jump? Return a boolean.
Does the operation have a jump argument? Return a boolean.

More generic than `is_cond_jump` and :meth:`is_uncond_jump`, it includes
other operations. Examples:
More general than :meth:`is_cond_jump` and :meth:`is_uncond_jump`, it
includes other operations. Examples:

* FOR_ITER
* SETUP_EXCEPT
Expand Down Expand Up @@ -359,6 +359,9 @@ BytecodeBlocks

Labels (:class:`Label`) must not be used in blocks.

This class is not designed to emit code, but to analyze and modify existing
code. Use :class:`Bytecode` to emit code.

Attributes:

.. attribute:: argnames
Expand All @@ -372,10 +375,18 @@ BytecodeBlocks
Create a :class:`Bytecode` object to a :class:`BytecodeBlocks` object:
replace labels with blocks.

Splits blocks after final instructions (:meth:`Instr.is_final`) and after
conditional jumps (:meth:`Instr.is_cond_jump`).

.. method:: add_block(instructions=None)

Add a new block. Return the new :class:`Block`.

.. method:: split_block(block: Block, index: int)

Split a block into two blocks at the specific instruction. Return
the newly created block, or *block* if index equals ``0``.


Line Numbers
============
Expand Down
39 changes: 33 additions & 6 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,46 @@ ChangeLog

* Version 0.2

- New major rework of the API.
- Again, the API is deeply reworked.
- The project has now a documentation:
`bytecode documentation <https://bytecode.readthedocs.org/>`_
- Fix bug #1: support jumps larger than 2^16.
- Add a new :ref:`bytecode.peephole_opt module <peephole_opt>`: a peephole
optimizer, code based on peephole optimizer of CPython 3.6 which is
implemented in C
- :class:`BytecodeBlocks` don't use labels anymore. Jump targets are now
blocks (:class:`Block`).
- :class:`Instr` now uses variable name instead of integer for cell and free
variables.
- Add :func:`dump_bytecode` function to ease debug.
- :class:`Instr`:

* Add :func:`Instr.is_final` method
* Add :meth:`Instr.copy` and :meth:`ConcreteInstr.copy` methods
* :class:`Instr` now uses variable name instead of integer for cell and
free variables.
* Rename ``Instr.is_jump`` to :meth:`Instr.has_jump`


- :class:`ConcreteInstr` is now mutable
- Add :meth:`Instr.copy` and :meth:`ConcreteInstr.copy` methods
- Redesign the :class:`BytecodeBlocks` class:

- :class:`Block` have no more label attribute: jump targets are now
directly blocks
- Rename ``BytecodeBlocks.add_label()`` method to
:meth:`BytecodeBlocks.split_block`
- Labels are not more allowed in blocks
- :meth:`BytecodeBlocks.from_bytecode` now splits blocks after final
instructions (:meth:`Instr.is_final`) and after conditional jumps
(:meth:`Instr.is_cond_jump`). It helps the peephole optimizer to
respect the control flow and to remove dead code.

- Rework API to convert bytecode classes:

- BytecodeBlocks: Remove ``to_concrete_bytecode()`` and ``to_code()``
methods. Now you first have to convert blocks to bytecode using
:meth:`~BytecodeBlocks.to_bytecode`.
- Remove ``Bytecode.to_bytecode_blocks()`` method, replaced with
:meth:`BytecodeBlocks.from_bytecode`
- Remove ``ConcreteBytecode.to_concrete_bytecode()`` and
``Bytecode.to_bytecode()`` methods which did nothing (return ``self``)

- Fix :class:`ConcreteBytecode` for code with no constant (empty list of
constants)
- Fix argnames in :meth:`ConcreteBytecode.to_bytecode`: use CO_VARARGS and
Expand Down
49 changes: 42 additions & 7 deletions doc/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,12 @@ Example using abstract bytecode to execute ``print('Hello World!')``::

from bytecode import Instr, Bytecode

bytecode = Bytecode()
bytecode.extend([Instr("LOAD_NAME", 'print'),
Instr("LOAD_CONST", 'Hello World!'),
Instr("CALL_FUNCTION", 1),
Instr("POP_TOP"),
Instr("LOAD_CONST", None),
Instr("RETURN_VALUE")])
bytecode = Bytecode([Instr("LOAD_NAME", 'print'),
Instr("LOAD_CONST", 'Hello World!'),
Instr("CALL_FUNCTION", 1),
Instr("POP_TOP"),
Instr("LOAD_CONST", None),
Instr("RETURN_VALUE")])
code = bytecode.to_code()
exec(code)

Expand Down Expand Up @@ -59,3 +58,39 @@ Example using concrete bytecode to execute ``print('Hello World!')``::
Output::

Hello World!


Conditional jump
================

Bytecode of the Python code ``print('yes' if test else 'no')``::

from bytecode import Label, Instr, Bytecode

label_else = Label()
label_print = Label()
bytecode = Bytecode([Instr('LOAD_NAME', 'print'),
Instr('LOAD_NAME', 'test'),
Instr('POP_JUMP_IF_FALSE', label_else),
Instr('LOAD_CONST', 'yes'),
Instr('JUMP_FORWARD', label_print),
label_else,
Instr('LOAD_CONST', 'no'),
label_print,
Instr('CALL_FUNCTION', 1),
Instr('LOAD_CONST', None),
Instr('RETURN_VALUE')])
code = bytecode.to_code()

test = 0
exec(code)

test = 1
exec(code)

Output::

no
yes

Instructions are only indented for readability.

0 comments on commit a9fdc2b

Please sign in to comment.