Package: pysel-lang
PySELThe Python Simple Expression Language (PySEL) is a simple but powerful expression language that supports
manipulation and creation of basic python literals (str,float,int) using a majority of the available
operators, but also offers notable features such as attribute/property accessors and method invocation, as well
as a global scope at runtime in which you can inject any python object.This is somewhat influenced by theSpring Expression Language (SpEL),
widely used by the Spring portfolio.InstallationPySEL can be installed using pip:$pipinstallpysel-langFeature OverviewThe expression language supports the following functionality:Literal expressionsLogical, relational and mathematical operatorsAttribute/property accessMethod invocationGetitem (list indexing, slicing, dict accessing, etc)Ternary operatorEnvironment value substitutionExpression EvaluationPySEL provides a utility class,Expressionto allow you to run a valid expression in very few lines of code.For example, the following expression, when evaluated, would return a string literal with valueHello World!:>>>importpysel>>>exp=pysel.Expression("'Hello World!'")To evaluate any expression, call theevaluate()method of theExpressionobject:>>>importpysel>>>exp=pysel.Expression("'Hello World!")>>>exp.evaluate()'Hello World!'You can also pass a mapping containing the execution environment for the expression. Any keys contained in the
environment can be accessed from within an expression using an identifier - the name of the key:>>>importpysel>>>exp=pysel.Expression("foo")>>>exp.evaluate({"foo":"bar"})'bar'If you do not wish to evaluate the expression immediately but would still like to know if it is syntactically valid,
you can call theto_ast()method of theExpressionclass. This will parse and validate the expression and return
the root node of the created abstract syntax tree.Invalid Expression:>>>importpysel>>>exp=pysel.Expression("'foo")# Invalid due to unclosed quotes>>>exp.to_ast()Traceback(mostrecentcalllast):...raiseerrors.ExpressionSyntaxError(pysel.errors.ExpressionSyntaxError:UnexpectedEOFwhileparsing"'foo"^Valid Expression:>>>importpysel>>>exp=pysel.Expression("'foo'")>>>exp.to_ast()Literal('foo')Language ReferenceLiteralsPySEL currently supports three different types of literals - string literals, integer literals, and float literals.A string literal is any set of characters surrounded by a matching pair of quotes. Quotes can be either single quotation
marks (') or double quotation marks ("). You can also include quotation marks in the string by escaping them using
a backslash:>>>pysel.Expression("'foo'").evaluate()'foo'>>>pysel.Expression('"foo"').evaluate()'foo'>>>pysel.Expression("'foo\\'s'").evaluate()"foo's"An integer literal is any set of consecutive digits (0-9). Unlike Python, PySEL allows integer literals to begin
with a0digit:>>>pysel.Expression("1234").evaluate()1234>>>pysel.Expression("01234").evaluate()1234A float literal is any set of consecutive digits0-9followed by a dot.. Float literals can also optionally
include digits after the decimal place - if none are specified, e.g.2.then the number will still parse correctly:>>>pysel.Expression("1.5").evaluate()1.5>>>pysel.Expression("1.").evaluate()1.0Logical, relational and mathematical operatorsThe logical operators that are supported are&&(and),||(or) and!(not). Their use is shown below:# --- NOT --->>>pysel.Expression("!foo").evaluate({"foo":True})False>>>pysel.Expression("!foo").evaluate({"foo":False})True# --- AND --->>>pysel.Expression("foo && bar").evaluate({"foo":True,"bar":False})False>>>pysel.Expression("foo && bar").evaluate({"foo":True,"bar":True})True# --- OR --->>>pysel.Expression("foo || bar").evaluate({"foo":False,"bar":False})False>>>pysel.Expression("foo || bar").evaluate({"foo":False,"bar":True})TrueThe relational operators that are supported are==,!=,>,<,>=, and<=- all using standard operator
notation:>>>pysel.Expression("2 == 2").evaluate()True>>>pysel.Expression("2 < 5").evaluate()True>>>pysel.Expression("2 != 2").evaluate()False...PySEL supports all the same mathematical operators supported by Python, excluding the bitwise operators (for now).
Operator precedence follows the order specified in the "Operator Precedence" section.>>>pysel.Expression("2 + 2").evaluate()4>>>pysel.Expression("2 * 3").evaluate()6>>>pysel.Expression("5 // 2").evaluate()2...Attribute/property accessAttribute access in PySEL functions identically to that of Python - using the.operator:>>>pysel.Expression("'foo'.__class__").evaluate()<class'str'>You can also chain attribute accessors to an unlimited depth, as with python:>>>pysel.Expression("'foo'.__class__.__name__").evaluate()'str'Method invocationAs with attribute access, methods are invoked identically to the way you would using Python:>>>pysel.Expression("str()").evaluate()''PySEL also supports calling methods with an infinite number of arguments - however you should note that all
arguments will be passedpositionally. Keyword arguments are not implemented:>>>pysel.Expression("str(10)").evaluate()'10'Getitem (indexing, slicing, dict accessing)PySEL's syntax for this is completely identical to Python's. A pair of square brackets immediately following any
expression are intepreted as a call toobject.__getitem__, as with Python. This allows you to perform indexing,
slicing, and dictionary accessing as you would normally:>>>pysel.Expression("'foobar'[0]").evaluate()'f'>>>pysel.Expression("'foobar'[::-1]").evaluate()'raboof'>>>pysel.Expression("dict['foo']").evaluate({"dict":{"foo":"bar"}})'bar'Ternary operatorPySEL supports the standard ternary operator found in many other languages including but not limited
to: C, JS, Java, etc.The syntax is:condition ? when_true : when_false.This is functionally equivalent to:when_trueifsome_conditionelsewhen_falseExample:>>>pysel.Expression("cond ? 'foo' : 'bar'").evaluate({"cond":True})'foo'>>>pysel.Expression("cond ? 'foo' : 'bar'").evaluate({"cond":False})'bar'Environment value substitutionAs you may have seen in the previous sections, PySEL allows values to be substituted in place of identifiers in
any given expression. When callingExpression.evaluate(), you can optionally pass a mapping of identifier name
to value which will be accessible from the expression when it is run:>>>pysel.Expression("foo").evaluate({"foo":"bar"})'bar'The default environment contains four identifiers -str,int,floatandbool- which are intended to be used
for casting values to different types within expressions, but of course you can use them for whatever you wish.If you pass a mapping to theevaluatemethod, and some of the identifier names conflict with the ones mentioned above,
then the default identifiers will be overridden with the value that you passed:>>>pysel.Expression("str").evaluate()<class'str'>>>>pysel.Expression("str").evaluate({"str":"foo"})'foo'Operator PrecedenceLiterals, parenthesesAccessor (., i.e.foo.bar)Method call (i.e.foo()), getitem (i.e.'foo'[0])**(exponent)Unary-,+*,/,//,%Binary-,+==,!=,>,<,>=,<=!&&||Ternary (i.e.expr ? expr : expr)Grammar SpecificationNote that this does not cover operator precedence, see the above section for that information.The below is written usingExtended Backus-Naur form:all characters=? All characters valid in a python string ?;letter=? All characters in set [a-zA-Z] ?;digit=? All characters in set [0-9] ?;unop="-"|"+"|"!";binop="=="|"!="|">"|"<"|">="|"<="|"&&"|"||"|"+"|"-"|"*"|"/"|"%"|"//"|"**";int={digit};float=int,"."[,int];str=("'"[,{all characters -"'"|"\'"}],"'")|('"'[,{all characters -'"'|'\"'}],'"');expr=int|str|float|expr binop expr|unop expr|ternary|identifier|accessor|methodcall|getitem;ternary=expr,"?",expr,":",expr;identifier=(letter|"_")[,{letter|digit|"_"}];accessor=expr,".",identifier;methodcall=expr,"(",expr[,{",",expr}],")";slice=[expr,]":"[,expr][,":"[expr]]getitem=expr"[",slice|expr,"]"TODODocstringsDoc generation