0

I'm new here and have a generall question. I'm trying to create a recursive descent parser for the evaluation of mathematic expressions, and the parser I have crafted until now also works very good for integers and floategers. But now I am trying to extend it with the evaluation of functions, like 'log', 'ln' or 'cbrt'. My parser is build up of eight function, excluding the function for evaluating. I crafted also one function for scanning the input string. This function will return a list of the expression with the syntax [5.0, '+', ....] if the string is '5+....'. This function works well and here is not the problem I have. My problem is, that the parser raises a 'list index out of range' error and I do not understand why. When I insert an expression without any function to evaluate, the parser delivers the correct abstract syntax tree and my evaluation function calculates the correct value. I found now a way to evaluate expressions with the shape 'function( expr )' (the parser does not throw an error), but when trying something like 'function1( expr1 ) + function2( expr2 )' or 'value + function( expr )', there is always the 'list index out of range error' thrown.

def expect_token( a, i ):
    if a[i] is None:
        raise SyntaxError( 'Unexpected end of input' )
    else:
        return a[i]

def atom( a, i, functions, dispatch ):
    t = expect_token( a, i )
    if isinstance( t, float ):
        return i+1, a[i]
    elif t == '(':
        i, x = expression( a, i+1, functions, dispatch )
        if expect_token( a, i ) != ')':
            raise SyntaxError( '")" was expected, but it came "{}"'.format( a[i] ) )
        return i+1, x
    elif t in dispatch.keys():
        i, x = expression( a, i+1, functions, dispatch )
        return i+1, a[i]
    else:
        raise SyntaxError( 'Unexpected symbol: "{}"'.format( t ) )

def power( a, i, functions, dispatch ):
    i, x = atom( a, i, functions, dispatch )
    if a[i] == '^':
        i, y = negation( a, i+1, functions, dispatch )
        return i, ['^', x, y]
    else:
        return i, x

def negation( a, i, functions, dispatch ):
    if a[i] == '-':
        i, x = power( a, i+1, functions, dispatch )
        return i, ['~', x]
    else:
        return power( a, i, functions, dispatch )

def multiplication( a, i, functions, dispatch ):
    i, x = negation( a, i, functions, dispatch )
    op = a[i]
    while op == '*' or op == '/':
        i, y = negation( a, i+1, functions, dispatch )
        x = [op, x, y]
        op = a[i]
    return i, x

def addition( a, i, functions, dispatch ):
    i, x = multiplication( a, i, functions, dispatch )
    op = a[i]
    while op == '+' or op == '-':
        i, y = multiplication( a, i+1, functions, dispatch )
        x = [op, x, y]
        op = a[i]
    return i, x

def expression( a, i, functions, dispatch ):
    if a[i] in functions:
        i_, y = expression( a, i+1, functions, dispatch )
        x = [a[i], y]
        return i_, x
    return addition( a, i, functions, dispatch )

def ast( a, functions, dispatch ):
    i, t = expression( a, 0, functions, dispatch )
    if a[i] is None:
        return t
    else:
        raise SyntaxError( 'End of input expected, but there came "{}"'.format( a[i] ) )

dispatch_ = {'+': lambda x, y: x+y,
            '-': lambda x, y: x-y,
            '*': lambda x, y: x*y,
            '/': lambda x, y: x/y,
            '^': lambda x, y: x**y,
            '~': lambda x: -x,
            'log': lambda x: log( x, 10 ),
            'ln': lambda x: log( x ),
            'sqrt': lambda x: sqrt( x ),
            'cbrt': lambda x: pow( x, 1/3 )
            }

functions_ = [ 'log', 'ln', 'sqrt', 'cbrt' ]
´´´
I don't know if it important, but I import log and sqrt from the 'math' libary.
I debugged the program very often and found that the error is caused by the 'atom function', but I do not know how to fix it.

-> cbrt( 8 )+ log( 10 )

> Traceback (most recent call last):   File
> "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line
> 150, in <module>
>     run()   File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line
> 140, in run
>     t = ast( a, functions_, dispatch_ )   File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line
> 100, in ast
>     i, t = expression( a, 0, functions, dispatch )   File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line
> 94, in expression
>     i_, y = expression( a, i+1, functions, dispatch )   File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line
> 97, in expression
>     return addition( a, i, functions, dispatch )   File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line
> 87, in addition
>     i, y = multiplication( a, i+1, functions, dispatch )   File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line
> 75, in multiplication
>     i, x = negation( a, i, functions, dispatch )   File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line
> 72, in negation
>     return power( a, i, functions, dispatch )   File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line
> 61, in power
>     if a[i] == '^': IndexError: list index out of range

´´´ When trying to evaluate '5+sqrt( 4 )', the same error is thrown.

My question is, where exactly is the bug in the program and how can I fix it. If you want, I will also show you my scanning and evaluating routine. Thank you for your future help.

EDIT:

This is my scanning routine:

def is_sign( sign ):
    if sign in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ':
        return True
    return False

def scan( s ):
    a = []
    i = 0
    n = len( s )
    while i < n:
        if s[i] in '+-*/^()':
            a.append( s[i] )
            i += 1
        elif s[i].isdigit():
            j = i
            while i < n and ( s[i].isdigit() or s[i] == '.' ):
                i += 1
            a.append( float( s[j:i] ) )
        elif s[i].isspace():
            i += 1
        elif is_sign( s[i] ):
            j = i
            while i < n and is_sign( s[i] ):
                i += 1
            a.append( s[j:i] )
        else:
            raise SyntaxError( 'Unexpected character: "{}"'.format( s[i] ) )
    a.append( None )
    return a

# And for evaluation I use this function:

def evaluate( t ):
    if isinstance( t, float ):
        return t
    else:
        return dispatch_[t[0]]( *map( evaluate, t[1:] ) )
´´´

Examples:

expr = '3+5*( 5+3-6 )-9'
-> tree = ['-', [ '+', 3.0, ['*', 5.0, ['-', ['+', 5.0, 3.0], 6.0]]], 9.0]
-> sol = 4.0

expr = 'cbrt( 4*3-4 )'
-> tree = ['cbrt', ['-', ['*', 4.0, 3.0], 4.0]]
-> sol = 2.0

expr = '3*log( 4-9 )'
-> error:
 Traceback (most recent call last):
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 150, in <module>
    run()
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 140, in run
    t = ast( a, functions_, dispatch_ )
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 100, in ast
    i, t = expression( a, 0, functions, dispatch )
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 97, in expression
    return addition( a, i, functions, dispatch )
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 84, in addition
    i, x = multiplication( a, i, functions, dispatch )
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 78, in multiplication
    i, y = negation( a, i+1, functions, dispatch )
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 72, in negation
    return power( a, i, functions, dispatch )
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 61, in power
    if a[i] == '^':
IndexError: list index out of range

expr = 'sqrt( 4 ) - ln( 2.7 )'
-> error:
Traceback (most recent call last):
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 150, in <module>
    run()
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 140, in run
    t = ast( a, functions_, dispatch_ )
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 100, in ast
    i, t = expression( a, 0, functions, dispatch )
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 94, in expression
    i_, y = expression( a, i+1, functions, dispatch )
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 97, in expression
    return addition( a, i, functions, dispatch )
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 87, in addition
    i, y = multiplication( a, i+1, functions, dispatch )
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 75, in multiplication
    i, x = negation( a, i, functions, dispatch )
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 72, in negation
    return power( a, i, functions, dispatch )
  File "F:\Privat\Programms\Python\Projects\Calculator\calc_tools.py", line 61, in power
    if a[i] == '^':
IndexError: list index out of range

´´´
I hope this additional information will help you to support me.
Lucy
  • 1
  • 2
  • Code of other routines will be helpful, In general, please provide [a minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) – Yuri Ginsburg Apr 03 '21 at 16:43
  • Thanks for your answer, I hope you can use it now better. – Lucy Apr 03 '21 at 17:05

0 Answers0