Package: postfixcalc
postfixcalcSimple, stupid but easy and powerful infix expression evaluator usingpostfixnotation.User GuideHow to use?frompostfixcalcimportCalcexpr="(-1) ^ 2 + 3 - 4 * 8 - 2 ^ 3"calc=Calc(expr)print(calc.answer)print(type(calc.answer))-36
<class 'int'>Explanationexpression: (-1) ^ 2 + 3 - 4 * 8 - 2 ^ 3which with the math operator precedence is:expression: ((-1) ^ 2) + 3 - (4 * 8) - (2 ^ 3)
            = (1) + 3 - (32) - (8)
            = 4 - 32 - 8
            = -28 - 8
            = -36Initializationcalc=Calc('(2 ^ 32) ^ (2 ^ 15) + -1',calc_timeout=1,# timeout for the calculation (in seconds)str_repr_timeout=1.5# timeout to generate the string representation (in seconds))print(f"'(2 ^ 32) ^ (2 ^ 15) + -1's answer has '{len(calc.stranswer())}' digits")'(2 ^ 32) ^ (2 ^ 15) + -1's answer has '315653' digitsprint(f'answer is:{calc.stranswer(15,15)}')answer is: 674114012549907...068940335579135Other Attributesfromrich.prettyimportPrettyfromrichimportprintasrprintrprint(calc.parsed)<ast.BinOpobject at0x7fb65c3917b0>rprint(calc.extracted)[([([([2],<ast.Powobject at0x7fb661991120>,[32])],<ast.Powobject at0x7fb661991120>,[([2],<ast.Powobject at0x7fb661991120>,[15])])],<ast.Addobject at0x7fb661990ee0>,[(<ast.USubobject at0x7fb661991540>,[1])])]rprint(calc.flattened)(((([2],<ast.Powobject at0x7fb661991120>,[32]),),<ast.Powobject at0x7fb661991120>,(([2],<ast.Powobject at0x7fb661991120>,[15]),)),<ast.Addobject at0x7fb661990ee0>,(<ast.USubobject at0x7fb661991540>,[1]))rprint(calc.strparenthesized)(((2^32))^((2^15)))+(-1)rprint(calc.listparenthesized)['(','(','(',2,'^',32,')',')','^','(','(',2,'^',15,')',')',')','+','(','-1',')']rprint(calc.numerized)['(','(','(',2,'^',32,')',')','^','(','(',2,'^',15,')',')',')','+','(',-1,')']rprint(calc.postfix)[2,32,'^',2,15,'^','^',-1,'+']rprint(f'{calc.stranswer(15,15)}')674114012549907...068940335579135CalcDocumentationclass Calc(expr: str, calc_timeout: int | float = 0.1, str_repr_timeout: int | float = 0.2)expr:infixmath expressioncalc_timeout: the timeout of the math calculation, if a expression's calculation took longer than this time, it would be killed and aTimeoutErrorwill be raised.str_repr_timeout: Calculating a expression like:(2 ^ 32) ^ (2 ^ 15)takes about a seconds, but the result has 315653 digits; so printing (getting the string representation) takes some time, this timeout is controlled by this parameter; and aTimeoutErrorwill be raised.Properties ofCalcImportant NoteAll the properties ofCalctype (exceptstranswer) arecached properties. It means that they are calculated once and stored in the object and are not calculated every time you need them (if other properties and attributes are remained unchanged)Calc.parsedParse the object withast.parsefunction and return the parsed expression.
Becauseast.parseuses the grammar of Python any syntax error will be raised here!# underlying functionfrompostfixcalc.parserimportparseexpr='-1 ^ 2'parsed=parse(expr)rprint(parsed)<ast.UnaryOpobject at0x7fb6603566b0>Calc.extractedReturn a list of extracted numbers and operators from theparsed object# underlying functionfrompostfixcalc.parserimportextract_nums_and_opsextracted=extract_nums_and_ops(parsed)rprint(extracted)[(<ast.USubobject at0x7fb661991540>,[([1],<ast.Powobject at0x7fb661991120>,[2])])]Calc.flattenedFlatten the numbers and operators list, this will reduce the nested lists and tuples# underlying functionfrompostfixcalc.parserimportflatten_nodesflattened=flatten_nodes(extracted)rprint(flattened)(<ast.USubobject at0x7fb661991540>,([1],<ast.Powobject at0x7fb661991120>,[2]))Calc.strparenthesizedGenerate aparenthesizedversion of the expression passed in initialization according to the math operator precedence# underlying functionfrompostfixcalc.parserimportrestrexpressionrprint(restrexpression(flattened))-(1^2)Calc.listparenthesizedReturn the digits and parenthesis and operators in a list that will be used to generate the postfix list# underlying functionfrompostfixcalc.parserimportrelistexpressionlisted=relistexpression(flattened)rprint(listed)['-','(',1,'^',2,')']Calc.numerizedNumerize the string numbers returned bylistparenthesizedmethod. In some cased like:(-1) ^ 2)thelistparenthesizedlooks like this:['(', '-1', ')', '^', 2], so we have to make those strings numbers# underlying functionfrompostfixcalc.parserimportmake_numnumerized=make_num(listed)rprint(numerized)['-','(',1,'^',2,')']Calc.postfixReturn a list with the postfix notation of the expression# underlying functionfrompostfixcalc.parserimportinfix_to_postfixpostfixed=infix_to_postfix(numerized)rprint(postfixed)[1,2,'^','-']Calc.answerCalculate the answer respecting thecalc_timeoutIMPORTANT NOTE: DON'T CALLprintON THE RESULT OF THIS METHOD.This is because for instance calculating(2 ^ 32) ^ (2 ^ 15)is done undercalc_timeoutBUT generating the string
representationWILL TAKE MUCH LONGER!!!(it has 315653 digits)If you want toprintthe result, usestranswermethodmethod@cached_propertydefanswer(self):process=multiprocessing.Process(target=evaluate,args=(self.postfix,))process.start()process.join(timeout=self.calc_timeout)ifprocess.is_alive():process.terminate()raiseTimeoutError(f"Calculations of{self.strparenthesized!r}took longer than{self.calc_timeout}seconds",)fromNonereturnevaluate(self.postfix)# underlying functionfrompostfixcalc.pyevalimportevaluaterprint(evaluate(postfixed))-1Calc.stranswerReturn the string representation of theCalc.answerrespecting thestr_repr_timeoutmethoddefstranswer(self,beginning:Optional[int]=None,ending:Optional[int]=None,)->str:"""Return the string representation of the answer with the respect to the `str_repr_timeout`beginning: see the first n digits of the answer: '982734...'ending: see the last n digits of the answer: '...982734'if both are specified: '987234...873242'"""process=multiprocessing.Process(target=str,args=(self.answer,))process.start()process.join(self.str_repr_timeout)ifprocess.is_alive():process.terminate()raiseTimeoutError(f"Generating a string representation of{self.expr!r}took longer than{self.str_repr_timeout}seconds",)fromNonetry:answer=str(self.answer)match(beginning,ending):case(None,None):returnanswercase(x,None):returnf"{answer[:x]}..."case(None,x):returnf"...{answer[-x:]}"case(x,y):returnf"{answer[:x]}...{answer[-y:]}"case_:raiseValueError("Confusing beginning and ending")exceptValueError:raiseTimeoutError(f"Generating a string representation of{self.expr!r}took longer than{self.str_repr_timeout}seconds",)fromNoneHow the answer is calculatedCalctype has cached properties which rely on each others returned values, it means that when we access theCalc.answerproperty this sequential method and function calls, happen:print(calc.answer)calc.answerneeds the postfix:calc.postfixgets calledcalc.postfixneeds the numerized list of the digits and operators:calc.numerizedgets calledcalc.numerizedneeds the parenthesized list of the digits and operators:calc.listparenthsizedgets calledcalc.listprenthesizedneeds the flattened list of extracted nums and ops:calc.flattenedgets called.calc.flattenedneeds the extracted nums and ops:calc.extractedgets called.calc.extractedneeds the parsed expression of the input expression:calc.parsedgets called.Call graph for getting the answer