Skip to content

Instantly share code, notes, and snippets.

@linnil1
Created February 14, 2023 07:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save linnil1/2714219a333f8a823980ea20ab865958 to your computer and use it in GitHub Desktop.
Save linnil1/2714219a333f8a823980ea20ab865958 to your computer and use it in GitHub Desktop.
Calculator made by python
from typing import Any, Iterable
from operator import add, sub, mul, truediv, neg
op_func: dict[str, Any] = {"+": add, "-": sub, "*": mul, "/": truediv, "**": pow, "--": neg}
op_param: dict[str, int] = {"+": 2, "-": 2, "*": 2, "/": 2, "**": 2, "--": 1}
priority: dict[str, int] = {"+": 0, "-": 0, "*": 2, "/": 2, "**": 5, "--": 4}
op_set = ["**", "*", "+", "-", "/", "(", ")"]
num_set = "0123456789."
Item = int | float | str
def isNumeric(item: Item) -> bool:
return isinstance(item, float) or isinstance(item, int)
def str2Infix(query_string: str) -> Iterable[Item]:
i = 0
prev_i = 0
while i < len(query_string):
# operator
prev_i = i
for op in op_set:
if query_string[i:].startswith(op):
yield op
i += len(op)
# number
num_str = ""
while i < len(query_string) and query_string[i] in num_set:
num_str += query_string[i]
i += 1
if num_str:
yield float(num_str)
# space
while i < len(query_string) and query_string[i] == " ":
i += 1
# cannot go forward
assert prev_i != i
def infixCheckOp(items: Iterable[Item], prev_op: str | None = None) -> Iterable[Item]:
prev: Item = "+"
for item in items:
assert isNumeric(item) or item in op_func
# split minus and sign
if not isNumeric(prev) and (item == "+" or item == "-"):
if item == "-":
yield "--"
# also check () matchness
# treat () as numeric
elif item == "(":
assert not isNumeric(prev)
yield item
yield from infixCheckOp(items, "(")
prev = 0
elif item == ")":
assert isNumeric(prev)
assert prev_op == "("
yield item
return
# basic rule: num + operator + num
elif ((not isNumeric(prev) and isNumeric(item)) or
( isNumeric(prev) and not isNumeric(item))):
yield item
prev = item
# error
else:
assert False
assert isNumeric(prev)
def infix2Sffuix(items: Iterable[Item]) -> Iterable[Item]:
stack_op: list[str] = []
for item in items:
if item == "(":
yield from infix2Sffuix(items)
elif item == ")":
break
elif isNumeric(item):
yield item
else:
assert isinstance(item, str)
while stack_op and priority[item] <= priority[stack_op[-1]] and item != "--":
yield stack_op.pop()
stack_op.append(item)
yield from reversed(stack_op)
def calcSuffix(items: Iterable[Item]) -> float:
stack: list[int | float] = []
k = 0
for item in items:
if not isNumeric(item):
assert isinstance(item, str)
if op_param[item] == 1:
stack[-1] = op_func[item](stack[-1])
elif op_param[item] == 2:
stack[-2] = op_func[item](stack[-2], stack[-1])
stack.pop()
else:
assert False
else:
assert isinstance(item, (int, float))
stack.append(item)
assert len(stack) == 1
return stack[-1]
def main(query: str) -> float:
items = str2Infix(query)
items = infixCheckOp(iter(items))
items = infix2Sffuix(iter(items))
result = calcSuffix(items)
return result
if __name__ == "__main__":
# s = "-7*5/2+(3* 1**-2+5)-8"
# s = "3 * -1 ** 2 + 2 ** -1"
# s = "3 * -1 ** 2 + 3 + 2 ** -1 + 1 + 1 + (2 + 3) + 5"
s = "3 * -1 + -1 ** 2"
print(s)
print("predit", main(s))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment