From 425733decaed18d9cde5cc55c00f0ac93bf64c6d Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Sat, 30 Nov 2019 17:33:12 +0100 Subject: [PATCH] calculator: add prefix parser --- calculator/calculator/parse/__init__.py | 1 + calculator/calculator/parse/prefix.py | 34 ++++++++++++++++++++++ calculator/calculator/parse/test_prefix.py | 30 +++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 calculator/calculator/parse/prefix.py create mode 100644 calculator/calculator/parse/test_prefix.py diff --git a/calculator/calculator/parse/__init__.py b/calculator/calculator/parse/__init__.py index 0213ec2..630a813 100644 --- a/calculator/calculator/parse/__init__.py +++ b/calculator/calculator/parse/__init__.py @@ -1 +1,2 @@ from .postfix import parse_postfix +from .prefix import parse_prefix diff --git a/calculator/calculator/parse/prefix.py b/calculator/calculator/parse/prefix.py new file mode 100644 index 0000000..ff9c10c --- /dev/null +++ b/calculator/calculator/parse/prefix.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, List, Union, cast + +from calculator.ast import BinOp, Constant, UnaryOp +from calculator.core import operations + +from .parsed_string import ParsedString + +if TYPE_CHECKING: + from calculator.ast import Node + + +def queue_to_tree(q: List[Union[int, str]]) -> Node: + top = q.pop(0) + if type(top) is int: + return Constant(top) + top = cast(str, top) + if top == "@": + rhs = queue_to_tree(q) + return UnaryOp(operations.negate, rhs) + lhs = queue_to_tree(q) + rhs = queue_to_tree(q) + return BinOp(operations.STR_TO_BIN[top], lhs, rhs) + + +def parse_prefix(input: str) -> Node: + """ + Parses the given string in prefix notation. + Negation is represented by the '@' sign. + """ + parsed = ParsedString(input).tokenize() + ans = queue_to_tree(parsed) + return ans diff --git a/calculator/calculator/parse/test_prefix.py b/calculator/calculator/parse/test_prefix.py new file mode 100644 index 0000000..ddc7a93 --- /dev/null +++ b/calculator/calculator/parse/test_prefix.py @@ -0,0 +1,30 @@ +from calculator.ast import BinOp, Constant, UnaryOp +from calculator.core import operations + +from .prefix import parse_prefix + + +def test_parse_constant(): + assert parse_prefix("42") == Constant(42) + + +def test_parse_negated_constant(): + assert parse_prefix("@ 42") == UnaryOp(operations.negate, Constant(42)) + + +def test_parse_doubly_negated_constant(): + assert parse_prefix("@@42") == UnaryOp( + operations.negate, UnaryOp(operations.negate, Constant(42)) + ) + + +def test_parse_binary_operation(): + assert parse_prefix("+ 12 27") == BinOp(operations.plus, Constant(12), Constant(27)) + + +def test_parse_complete_expression_tree(): + assert parse_prefix("*+12 27-42 51") == BinOp( + operations.times, + BinOp(operations.plus, Constant(12), Constant(27)), + BinOp(operations.minus, Constant(42), Constant(51)), + )