Introducing Expressions for Python
Expressions is a lightweight arithmetic expression parser for creating simple arithmetic expression compilers.
Goal is to provide minimal and understandable interface for handling arithmetic expressions of the same grammar but slightly different dialects (see below). The framework will stay lightweight and it is unlikely that it will provide any more complex gramatical constructs.
Parser is hand-written to avoid any dependencies. The only requirement is Python 3.
The expression is expected to be an infix expression that might contain:
- numbers and strings (literals)
- binary and unary operators
- function calls with variable number of arguments
The compiler is then used to build an object as a result of the compilation of each of the tokens.
Grammar of the expression is fixed. Slight differences can be specified using
dialect structure which contains:
- list of operators, their precedence and associativeness
- case sensitivity (currently used only for keyword based operators)
Planned options of a dialect that will be included in the future releases:
- string quoting characters (currently single
- identifier quoting characters (currently unsupported)
- identifier characters (currently
_and alpha-numeric characters)
- decimal separator (currently
- function argument list separator (currently comma
Intended use is embedding of customized expression evaluation into an application.
- Variable checking compiler with an access control to variables.
- Unified expression language where various other backends are possible.
- Compiler for custom object structures, such as for frameworks providing functional-programing like interface.
Write a custom compiler class and implement methods:
compile_literaltaking a number or a string object
compile_variabletaking a variable name
compile_operatortaking a binary operator and two operands
compile_unarytaking an unary operator and one operand
compile_functiontaking a function name and list of arguments
Every method receives a compilation context which is a custom object passed to
the compiler in
compile(expression, context) call.
The following compiler re-compiles an expression back into it's original form with optional access restriction just to certain variables specified as the compilation context:
class AllowingCompiler(Compiler): def compile_literal(self, context, literal): return repr(literal) def compile_variable(self, context, variable): """Returns the variable if it is allowed in the `context`""" if context and variable not in context: raise ExpressionError("Variable %s is not allowed" % variable) return variable def compile_operator(self, context, operator, op1, op2): return "(%s %s %s)" % (op1, operator, op2) def compile_function(self, context, function, args): arglist = ", " % args return "%s(%s)" % (function, arglist)
Create a compiler instance and try to get the result:
compiler = AllowingCompiler() result = compiler.compile("a + b", context=["a", "b"]) a = 1 b = 1 print(eval(result))
The output would be
2 as expected. The following will fail:
result = compiler.compile("a + c")
If you have any questions, comments, requests, do not hesitate to ask.