internal.interpreter

Implements a small interpreter.

Syntax:

This interpreter interprets a subset of the Python programming language, with an extension to the behavior of default arguments in lambdas. Since it uses the Python parser, it obeys Python’s operator precedence. If it encounters a name in the expression, it looks it up in the supplied environment.

Here are the supported operations:

  • Strings (with support for the .format method)

  • Integers and floats.

  • True, False, and None.

  • Lists and list comprehensions

  • Dictionaries and dict comprehensions

  • Indexing for lists and dictionaries.

  • Unary not, negation.

  • Binary multiplication, division, exponentiation, addition, subtraction.

  • Boolean and and or.

  • Comparison with <, >, <=, >=, ==, and !=.

  • Chained comparison, like 0.2 < x < 0.5.

  • Builtin functions: exp, log, sqrt, abs, any, all, min, max, sum, len, range.

  • Builtin constants: e, pi, true, false, and null. The true, false, and null constants are for compatibility with JSON. The Python literals True, False, and None are also supported.

  • User-defined functions with lambda syntax. By abusing default parameters, you can even create recursive functions!

The extension to the lambda default parameters lets you use letrec-style recursive functions without needing to use the Z combinator to implement recursion. For example, this is valid in my interpreter but not valid Python:

(lambda x=5, y=(lambda q: x + q): y(7))()

In effect, you can think of this being translated to:

(letrec ((x 5) (y (lambda (q) (+ x q)))) (y 7))

Note that if you use a lambda as a default parameter to a lambda, you cannot override any default arguments when you call it. So this:

(lambda x=5, y=(lambda q: x + q): y(7))(3)

is an error because the call to the outer lambda attempted to specify x.

<expression> ::=
    (<expression>)
  | "<list-of-character>"
  | [<list-of-expression>]
  | {<list-of-dict-elems>}
  | [<expression> <comprehension-suite>]
  | {<dict-elem> <comprehension-suite>}
  | <expression>[<expression>]
  | <expression>.format(<list-of-expression>)
  | <unary-op-expr>
  | <bin-op-expr>
  | <expression> <compare-suite>
  | <bool-op-expr>
  | <number>
  | <python-literal>
  | lambda <formals>: <expression>
  | <expression>(<list-of-expression>)
  | <list-of-character>
<comprehension-suite> ::=
    for <list-of-character> in <expression> « if <expression>»
  | <comprehension-suite> <comprehension-suite>
<list-of-dict-elems> ::=
    <empty>
  | <dict-elem>«, <list-of-dict-elems
<dict-elem> ::=
    <expression>: <expression>
<formals> ::=
    <empty>
  | <list-of-character>«, <formals>»
  | <formals-with-defaults>
<formals-with-defaults> ::=
    <empty>
  | <list-of-character>=<expression>«, <formals-with-defaults
<list-of-expression> ::=
    <empty>
  | <expression>«, <list-of-expression
<bool-op-expr> ::=
    <expression> and <expression>
  | <expression> or <expression>
<bin-op-expr> ::=
    <expression> ** <expression>
    <expression> * <expression>
    <expression> / <expression>
    <expression> + <expression>
    <expression> - <expression>
<unary-op-expr> ::=
    - <expression>
  | not <expression>
<compare-suite> ::=
    <compare-op> <expression> «<compare-suite
<compare-op> ::=
    < | > | < | >= | == | != | in | not in
<python-literal> ::=
    (Anything that is a literal in Python)
class bpreveal.internal.interpreter.Closure(env, args, body)

Represents a function and its environment.

You do not need to use this, it’s only necessary to set up letrec-style default parameters for lambdas.

Parameters:
  • env (dict[str, int | float | bool | str | Callable | Closure | dict | list | Collection | None])

  • args (arguments)

  • body (expr)

run(argList)

Actually evaluate the body of the lambda.

Parameters:

argList (list[int | float | bool | str | Callable | Closure | dict | list | Collection | None])

Return type:

int | float | bool | str | Callable | Closure | dict | list | Collection | None

bpreveal.internal.interpreter.evalAstRaw(t, env)

Evaluates the (ast.parse()d) filter string t using variables in the environment env.

Parameters:
  • t (AST) – The parsed AST that should be evaluated

  • env (dict[str, Any]) – The environment containing the variables used in the expression.

Returns:

The value of the expression.

Return type:

int | float | bool | str | Callable | Closure | dict | list | Collection | None

You should probably call evalAst() instead of this, since that function will add helpful builtins to your environment.

bpreveal.internal.interpreter.evalAst(t, env=None, addFunctions=True)

Evaluates the (ast.parse()d) filter string t using variables in the environment env.

Parameters:
  • t (AST) – The parsed AST that should be evaluated

  • env (dict[str, int | float | bool | str | Callable | Closure | dict | list | Collection | None] | None) – The environment containing the variables used in the expression.

  • addFunctions (bool) – If True (the default), then the functions exp, log, sqrt, abs, and len, along with the constants pi and e will be added to the environment (but they will be shadowed by existing declarations if they have already been defined.)

Returns:

The value of the expression.

Return type:

int | float | bool | str | Callable | Closure | dict | list | Collection | None

bpreveal.internal.interpreter.evalString(expr, env=None, addFunctions=True)

Parses the given string and then runs evalAst on it.

Parameters:
  • expr (str)

  • env (dict[str, int | float | bool | str | Callable | Closure | dict | list | Collection | None] | None)

  • addFunctions (bool)

Return type:

int | float | bool | str | Callable | Closure | dict | list | Collection | None

bpreveal.internal.interpreter.evalFile(fname, env=None, addFunctions=True)

Read in the named file and evaluate it.

Parameters:
  • fname (str)

  • env (dict[str, int | float | bool | str | Callable | Closure | dict | list | Collection | None] | None)

  • addFunctions (bool)

Return type:

int | float | bool | str | Callable | Closure | dict | list | Collection | None