Search code examples
pythonabstract-syntax-tree

replace variable names with actual values in an expression in AST python


I have an expression described in variable forms like this

's1*3 - (s2-s1)*1'

I have given values of s1 and s2 that can change according to the need

I can use python ast module to evaluate this expression by replacing the respective s1 and s2 values (s1 = 20,s2=30)

import ast
import operator as op

operators = {ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul,
             ast.Div: op.truediv, ast.Pow: op.pow, ast.BitXor: op.xor,
             ast.USub: op.neg}

def eval_(node):
    if isinstance(node, ast.Num): # <number>
        return node.n
    elif isinstance(node, ast.BinOp): # <left> <operator> <right>
        return operators[type(node.op)](eval_(node.left), eval_(node.right))
    elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1
        return operators[type(node.op)](eval_(node.operand))
    else:
    raise TypeError(node)

>>> str1 = '20*3 - (30-20)*1'
>>> node = ast.parse(str1, mode='eval')
>>> eval_(node.body)
50

How should I evaluate this expression without the need to replace the variables with their actual values.

Thanks


Solution

  • You can use eval function . But you must be careful about using eval because it execute any string, can be very dangerous if you accept strings to evaluate from untrusted input. for example Suppose the string being evaluated is "os.system('rm -rf /')" ? It will really start deleting all the files on your computer.

    >>> eval('20*3 - (30-20)*1')
    50 
    

    As a better solution you can parse your equation with python's internal compiler :

    >>> s1=20
    >>> s2=30
    >>> eq='s1*3 - (s2-s1)*1'
    >>> compiler.parse( eq )
    Module(None, Stmt([Discard(Sub((Mul((Name('s1'), Const(3))), Mul((Sub((Name('s2'), Name('s1'))), Const(1))))))]))
    

    So if you want to evaluate the equation , As a more safer than using input you can use compile and eval !

    >>> eq='s1*3 - (s2-s1)*1'
    >>> a=compile(eq,'','eval')
    >>> eval(a)
    50
    

    Also you can use sympy that is a Python library for symbolic mathematics. It aims to become a full-featured computer algebra system (CAS) while keeping the code as simple as possible in order to be comprehensible and easily extensible. SymPy is written entirely in Python and does not require any external libraries.