we-like-parsers / pegen

PEG parser generator for Python
https://we-like-parsers.github.io/pegen/
MIT License
150 stars 32 forks source link

Expression is not evaluating properly #72

Closed shahilahmed closed 2 years ago

shahilahmed commented 2 years ago

I am following the code of story1.

For expression like 1 + 2 * 3 is evaluating correctly but expression like 1 - 2 * 3 - 8 - 5 is not evaluating properly

The evaluation of 1 - 2 * 3 - 8 - 5 should be -18 not -2

Here is my code to evaluate expression

from io import StringIO
from token import NAME, NUMBER, NEWLINE, ENDMARKER
from tokenize import generate_tokens

from tokenizer import Tokenizer
from parser import Parser
from toy import ToyParser

def test_expr():     
    def _eval(node):
        if node.type == 'add':
            return _eval(node.children[0]) + _eval(node.children[1])
        elif node.type == 'sub':
            return _eval(node.children[0]) - _eval(node.children[1])
        elif node.type == 'mul':
            return _eval(node.children[0]) * _eval(node.children[1])
        elif node.type == 'div':
            return _eval(node.children[0]) / _eval(node.children[1])
        elif node.type == NUMBER:
            return int(node.string)
    def _print(node):
        if node.type == 'add':
            return ("{} + {}".format(
                    _print(node.children[0]), _print(node.children[1])))
        elif node.type == 'sub':
            return ("{} - {}".format(
                    _print(node.children[0]), _print(node.children[1])))
        elif node.type == 'mul':
            return ("{} * {}".format(
                    _print(node.children[0]), _print(node.children[1])))
        elif node.type == 'div':
            return ("{} / {}".format(
                    _print(node.children[0]), _print(node.children[1])))
        elif node.type == NUMBER:
            return str(int(node.string))
    program = "(1 + 2) * 3"
    program = "1 - 2 * 3 - 8 - 5"
    file = StringIO(program)
    tokengen = generate_tokens(file.readline)
    tok = Tokenizer(tokengen)
    p = ToyParser(tok)
    tree = p.statement()
    print(program)
    print(tree)
    print(_eval(tree))
    print(_print(tree))
    print(eval(program))

test_expr()

Here is the output

1 - 2 * 3 - 8 - 5
Node(sub, [TokenInfo(type=2 (NUMBER), string='1', start=(1, 0), end=(1, 1), line='1 - 2 * 3 - 8 - 5'), Node(sub, [Node(mul, [TokenInfo(type=2 (NUMBER), string='2', start=(1, 4), end=(1, 5), line='1 - 2 * 3 - 8 - 5'), TokenInfo(type=2 (NUMBER), string='3', start=(1, 8), end=(1, 9), line='1 - 2 * 3 - 8 - 5')]), Node(sub, [TokenInfo(type=2 (NUMBER), string='8', start=(1, 12), end=(1, 13), line='1 - 2 * 3 - 8 - 5'), TokenInfo(type=2 (NUMBER), string='5', start=(1, 16), end=(1, 17), line='1 - 2 * 3 - 8 - 5')])])])
-2
1 - 2 * 3 - 8 - 5
-18

I could figure it out whether my _eval() is correct or not Or There is a bug in ToyParser class.

Please help it out.Thank you.

Attachment: story1.zip

0dminnimda commented 2 years ago

I can replicate this. To see the reason clearly we can add parentheses to the _print to get (1 - ((2 * 3) - (8 - 5))) Which is incorrect, opposed to the correct (((1 - (2 * 3)) - 8) - 5)

But I'm not sure the stories are subject to any change as it supposed to capture the Guido's series on PEG parser and any bugs are preserved, I guess ;) Although to be sure I need to hear from @MatthieuDartiailh

MatthieuDartiailh commented 2 years ago

@pablogsal is better placed than myself to answer. I join only after pegen got integrated into cpython and well after the stories.

pablogsal commented 2 years ago

Yeah, unfortunately, we only keep the stories to keep the links in the original blog working and for historical reasons so we don't really "maintain" the stories. I'm closing the issue, but thanks for raising it with us.