Last active
April 10, 2020 12:59
-
-
Save b4tman/59f6debb388bf6fe98d3bccbd82b376d to your computer and use it in GitHub Desktop.
Калькулятор для вычисления выражений со значениями указанными в форме времени. Например "(12:05 - 11:30) / 2".
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
""" | |
simple calculator for time | |
based on fourFn.py (pyparsing module examlple) | |
""" | |
from pyparsing import Literal,CaselessLiteral,Word,Combine,Optional,\ | |
ZeroOrMore,Forward,nums,alphas | |
import sys, operator | |
exprStack = [] | |
def pushFirst( strg, loc, toks ): | |
exprStack.append( toks[0] ) | |
def pushUMinus( strg, loc, toks ): | |
if toks and toks[0]=='-': | |
exprStack.append( 'unary -' ) | |
#~ exprStack.append( '-1' ) | |
#~ exprStack.append( '*' ) | |
bnf = None | |
def BNF(): | |
""" | |
expop :: '^' | |
multop :: '*' | '/' | |
addop :: '+' | '-' | |
integer :: ['+' | '-'] '0'..'9'+ | |
atom :: E | real | fn '(' expr ')' | '(' expr ')' | |
factor :: atom [ expop factor ]* | |
term :: factor [ multop factor ]* | |
expr :: term [ addop term ]* | |
""" | |
global bnf | |
if not bnf: | |
point = Literal( "." ) | |
colon = Literal( ":" ) | |
e = CaselessLiteral( "E" ) | |
fnumber = Combine( Word( "+-"+nums, nums ) + | |
Optional( point + Optional( Word( nums ) ) ) + | |
Optional( e + Word( "+-"+nums, nums ) ) ) | |
tnumber = Combine( fnumber + Optional( colon + Optional(fnumber + Optional( colon + Optional(fnumber) ) ) ) ) | |
ident = Word(alphas, alphas+nums+":_$") | |
plus = Literal( "+" ) | |
minus = Literal( "-" ) | |
mult = Literal( "*" ) | |
div = Literal( "/" ) | |
lpar = Literal( "(" ).suppress() | |
rpar = Literal( ")" ).suppress() | |
addop = plus | minus | |
multop = mult | div | |
expop = Literal( "^" ) | |
expr = Forward() | |
atom = (Optional("-") + ( tnumber | ident + lpar + expr + rpar ).setParseAction( pushFirst ) | ( lpar + expr.suppress() + rpar )).setParseAction(pushUMinus) | |
factor = Forward() | |
factor << atom + ZeroOrMore( ( expop + factor ).setParseAction( pushFirst ) ) | |
term = factor + ZeroOrMore( ( multop + factor ).setParseAction( pushFirst ) ) | |
expr << term + ZeroOrMore( ( addop + term ).setParseAction( pushFirst ) ) | |
bnf = expr | |
return bnf | |
opn = { "+" : operator.add, | |
"-" : operator.sub, | |
"*" : operator.mul, | |
"/" : operator.truediv, | |
"^" : operator.pow } | |
fn = { "abs" : abs, | |
"trunc" : lambda a: int(a), | |
"round" : round} | |
def evaluateStack( s ): | |
if 0 == len(s): | |
return 0 | |
op = s.pop() | |
if 0 == len(op): | |
return 0 | |
if op == 'unary -': | |
return -evaluateStack( s ) | |
if op in "+-*/^": | |
op2 = evaluateStack( s ) | |
op1 = evaluateStack( s ) | |
return opn[op]( op1, op2 ) | |
elif op in fn: | |
return fn[op]( evaluateStack( s ) ) | |
elif op[0].isalpha(): | |
return 0 | |
else: | |
return float( sec_from_time(op) ) | |
def evaluateString(s): | |
global exprStack | |
exprStack = [] | |
results = BNF().parseString( s ) | |
return evaluateStack( exprStack[:] ) | |
def sec_from_time(time): | |
parts = time.split(':') | |
parts.reverse() | |
level = 1 | |
ret = 0 | |
for part in parts: | |
if len(part): | |
ret += float(part) * level | |
level *= 60 | |
return ret | |
def time_from_sec(sec): | |
parts = [] | |
sec_remain = float(sec) | |
while(sec_remain): | |
if 60 > sec_remain: | |
part = sec_remain | |
sec_remain = 0 | |
else: | |
part = float(sec_remain % 60) | |
sec_remain = float(int(sec_remain/60)) | |
parts.append(part) | |
parts.reverse() | |
return ':'.join(map(lambda p: '{:02.4n}'.format(p), parts)) | |
def test( s, expVal ): | |
global exprStack | |
exprStack = [] | |
results = BNF().parseString( s ) | |
val = evaluateStack( exprStack[:] ) | |
if val == expVal: | |
print ('== "{}" = {} // {} => {}'.format(s, time_from_sec(val), results, exprStack)) | |
else: | |
print ('!!! "{}" = {} != {} // {} => {}'.format(s, time_from_sec(val), time_from_sec(expVal), results, exprStack)) | |
if __name__ == "__main__": | |
if 1 == len(sys.argv): | |
test( "2:1 + 1:2 + 3: +2", 3+3*60 + 3*60+2 ) | |
test( "15:21 - 13:04 + 16:25 - 16:18", 2*60+24 ) | |
test( "0 - (13:04 - 15:21) - (16:18 - 16:25)", 2*60+24 ) | |
test( "12.5:0: + 45:01", 13*60*60+15*60+2 ) | |
else: | |
arguments = ' '.join(sys.argv[1:]) | |
result = evaluateString(arguments) | |
print(time_from_sec(result)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment