* use new support to colorize reconciled bank statement lines * fixes tokenization of ``is not`` to not blow up on ``not foo`` * improves calling code to get truthiness of evaluated expression instead of truthiness of javascript-equivalent objects: an empty list is falsy in Python but truthy in javascript, since the evaluated expression is in Python one would expect Python truthiness rules without having to manually convert to boolean closes #5070
197 lines
5.5 KiB
ReStructuredText
197 lines
5.5 KiB
ReStructuredText
What
|
|
====
|
|
|
|
|
|
|
|
Syntax
|
|
------
|
|
|
|
* Lambdas and ternaries should be parsed but are not implemented (in
|
|
the evaluator)
|
|
* Only floats are implemented, ``int`` literals are parsed as floats.
|
|
* Octal and hexadecimal literals are not implemented
|
|
* Srings are backed by JavaScript strings and probably behave like
|
|
``unicode`` more than like ``str``
|
|
* Slices don't work
|
|
|
|
Builtins
|
|
--------
|
|
|
|
``py.js`` currently implements the following builtins:
|
|
|
|
``type``
|
|
Restricted to creating new types, can't be used to get an object's
|
|
type (yet)
|
|
|
|
``None``
|
|
|
|
``True``
|
|
|
|
``False``
|
|
|
|
``NotImplemented``
|
|
Returned from rich comparison methods when the comparison is not
|
|
implemented for this combination of operands. In ``py.js``, this
|
|
is also the default implementation for all rich comparison methods.
|
|
|
|
``issubclass``
|
|
|
|
``object``
|
|
|
|
``bool``
|
|
Does not inherit from ``int``, since ``int`` is not currently
|
|
implemented.
|
|
|
|
``float``
|
|
|
|
``str``
|
|
|
|
``tuple``
|
|
Constructor/coercer is not implemented, only handles literals
|
|
|
|
``list``
|
|
Same as tuple (``list`` is currently an alias for ``tuple``)
|
|
|
|
``dict``
|
|
Implements trivial getting, setting and len, nothing beyond that.
|
|
|
|
Note that most methods are probably missing from all of these.
|
|
|
|
Data model protocols
|
|
--------------------
|
|
|
|
``py.js`` currently implements the following protocols (or
|
|
sub-protocols) of the `Python 2.7 data model
|
|
<>`_:
|
|
|
|
Rich comparisons
|
|
Pretty much complete (including operator fallbacks), although the
|
|
behavior is currently undefined if an operation does not return
|
|
either a ``py.bool`` or ``NotImplemented``.
|
|
|
|
``__hash__`` is supported (and used), but it should return **a
|
|
javascript string**. ``py.js``'s dict build on javascript objects,
|
|
reimplementing numeral hashing is worthless complexity at this
|
|
point.
|
|
|
|
Boolean conversion
|
|
Implementing ``__nonzero__`` should work.
|
|
|
|
Customizing attribute access
|
|
Protocols for getting and setting attributes (including new-style
|
|
extension) fully implemented but for ``__delattr__`` (since
|
|
``del`` is a statement)
|
|
|
|
Descriptor protocol
|
|
As with attributes, ``__delete__`` is not implemented.
|
|
|
|
Callable objects
|
|
Work, although the handling of arguments isn't exactly nailed
|
|
down. For now, callables get two (javascript) arguments ``args``
|
|
and ``kwargs``, holding (respectively) positional and keyword
|
|
arguments.
|
|
|
|
Conflicts are *not* handled at this point.
|
|
|
|
Collections Abstract Base Classes
|
|
Container is the only implemented ABC protocol (ABCs themselves
|
|
are not currently implemented) (well technically Callable and
|
|
Hashable are kind-of implemented as well)
|
|
|
|
Numeric type emulation
|
|
Operators are implemented (but not tested), ``abs``, ``divmod``
|
|
and ``pow`` builtins are not implemented yet. Neither are ``oct``
|
|
and ``hex`` but I'm not sure we care (I'm not sure we care about
|
|
``pow`` or even ``divmod`` either, for that matter)
|
|
|
|
Utilities
|
|
---------
|
|
|
|
``py.js`` also provides (and exposes) a few utilities for "userland"
|
|
implementation:
|
|
|
|
``def``
|
|
Wraps a native javascript function into a ``py.js`` function, so
|
|
that it can be called from native expressions.
|
|
|
|
Does not ensure the return types are type-compatible with
|
|
``py.js`` types.
|
|
|
|
When accessing instance methods, ``py.js`` automatically wraps
|
|
these in a variant of ``py.def``, to behave as Python's (bound)
|
|
methods.
|
|
|
|
Why
|
|
===
|
|
|
|
Originally, to learn about Pratt parsers (which are very, very good at
|
|
parsing expressions with lots of infix or mixfix symbols). The
|
|
evaluator part came because "why not" and because I work on a product
|
|
with the "feature" of transmitting Python expressions (over the wire)
|
|
which the client is supposed to evaluate.
|
|
|
|
How
|
|
===
|
|
|
|
At this point, only three steps exist in ``py.js``: tokenizing,
|
|
parsing and evaluation. It is possible that a compilation step be
|
|
added later (for performance reasons).
|
|
|
|
To evaluate a Python expression, the caller merely needs to call
|
|
`py.eval`_. `py.eval`_ takes a mandatory Python
|
|
expression to evaluate (as a string) and an optional context, for the
|
|
substitution of the free variables in the expression::
|
|
|
|
> py.eval("type in ('a', 'b', 'c') and foo", {type: 'c', foo: true});
|
|
true
|
|
|
|
This is great for one-shot evaluation of expressions. If the
|
|
expression will need to be repeatedly evaluated with the same
|
|
parameters, the various parsing and evaluation steps can be performed
|
|
separately: `py.eval`_ is really a shortcut for sequentially calling
|
|
`py.tokenize`_, `py.parse`_ and `py.evaluate`_.
|
|
|
|
API
|
|
===
|
|
|
|
.. _py.eval:
|
|
|
|
``py.eval(expr[, context])``
|
|
"Do everything" function, to use for one-shot evaluation of a
|
|
Python expression: it will internally handle the tokenizing,
|
|
parsing and actual evaluation of the Python expression without
|
|
having to perform these separately.
|
|
|
|
``expr``
|
|
Python expression to evaluate
|
|
``context``
|
|
context dictionary holding the substitutions for the free
|
|
variables in the expression
|
|
|
|
.. _py.tokenize:
|
|
|
|
``py.tokenize(expr)``
|
|
``expr``
|
|
Python expression to tokenize
|
|
|
|
.. _py.parse:
|
|
|
|
``py.parse(tokens)``
|
|
Parses a token stream and returns an abstract syntax tree of the
|
|
expression (if the token stream represents a valid Python
|
|
expression).
|
|
|
|
A parse tree is stateless and can be memoized and used multiple
|
|
times in separate evaluations.
|
|
|
|
``tokens``
|
|
stream of tokens returned by `py.tokenize`_
|
|
|
|
.. _py.evaluate:
|
|
|
|
``py.evaluate(ast[, context])``
|
|
``ast``
|
|
The output of `py.parse`_
|
|
``context``
|
|
The evaluation context for the Python expression.
|