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]) – The arguments that will be passed to the lambda

Raises:

SyntaxError – If you provided an invalid input

Returns:

The result of calling the function.

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.

Raises:

SyntaxError – If you provided an invalid input

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) – The expression to evaluate

  • env (dict[str, int | float | bool | str | Callable | Closure | dict | list | Collection | None] | None) – The environment to use

  • addFunctions (bool) – Should the builtin functions be added?

Returns:

The result of evaluating that string

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) – The file to read

  • env (dict[str, int | float | bool | str | Callable | Closure | dict | list | Collection | None] | None) – The environment to use

  • addFunctions (bool) – Should the builtin functions be added?

Returns:

The result of evaluating that file.

Return type:

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