Skip to content

Instantly share code, notes, and snippets.

@tilalis
Last active January 21, 2020 14:13
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 tilalis/78a506ca341cf5cf03b39c5005e80925 to your computer and use it in GitHub Desktop.
Save tilalis/78a506ca341cf5cf03b39c5005e80925 to your computer and use it in GitHub Desktop.
Money Calculator
from money import Money, Currency
from enum import Enum
from string import ascii_letters
class TokenType(Enum):
NAME = 0
NUMBER = 1
MONEY = 2
PLUS = 3
MINUS = 4
MUL = 5
DIV = 6
LPAREN = 7
RPAREN = 8
SET = 9
EOF = 10
class Token:
def __init__(self, token_type, value):
self.type = token_type
self.value = value
def __str__(self):
return "Token({}, {})".format(self.type, repr(self.value))
def __repr__(self):
return self.__str__()
class Lexer:
def __init__(self, lexer_input):
self.input = lexer_input
self.index = 0
self.current = self.input[self.index]
def move(self):
self.index = self.index + 1
self.current = self.input[self.index] if self.index < len(self.input) else None
def skip(self):
while self.current is not None and self.current.isspace():
self.move()
def parse_digit(self):
result = ''
while self.current is not None and self.current.isdigit() or self.current == '.':
result = result + self.current
self.move()
amount = float(result)
if self.current.isspace():
return Token(TokenType.NUMBER, amount)
result = ''
while self.current is not None and self.current in ascii_letters or self.current == "$":
result = result + self.current
self.move()
currency = {
'B': Currency.BYN,
'BY': Currency.BYN,
'BYN': Currency.BYN,
'$': Currency.USD,
'U': Currency.USD,
'US': Currency.USD,
'USD': Currency.USD
}.get(result, None)
if currency is None:
raise TypeError("No such currency '{}'!".format(result))
return Token(TokenType.MONEY, Money(amount, currency))
def parse_name(self):
result = ''
while self.current is not None and not self.current.isspace():
result = result + self.current
self.move()
return Token(TokenType.NAME, result)
def __iter__(self):
return self
def __next__(self):
if not self.current:
raise StopIteration
if self.current.isspace():
self.skip()
if self.current.isdigit():
return self.parse_digit()
token = {
'+': TokenType.PLUS,
'-': TokenType.MINUS,
'*': TokenType.MUL,
'/': TokenType.DIV,
'(': TokenType.LPAREN,
')': TokenType.RPAREN,
'=': TokenType.SET
}.get(self.current, None)
if token is not None:
char = self.current
self.move()
return Token(token, char)
return self.parse_name()
from requests import get
from enum import Enum
class Currency(Enum):
USD = 1
BYN = 2
def __str__(self):
return self.name
@staticmethod
def get_rate():
response = get("https://belarusbank.by/api/kursExchange?city=%D0%9C%D0%B8%D0%BD%D1%81%D0%BA")
if response.ok:
return float(response.json().pop().get('USD_out'))
else:
print("Could not fetch exchange rate, using default value (= 2)")
return 2.0
class Money:
_rates = None
def __init__(self, amount: float, currency: Currency = Currency.USD):
if not isinstance(amount, (int, float)):
raise TypeError("Type should be `int` or `float`!")
self.amount = round(float(amount), 2)
self.currency = currency
@staticmethod
def USD(amount: float):
return Money(amount, Currency.USD)
@staticmethod
def BYN(amount: float):
return Money(amount, Currency.BYN)
@staticmethod
def rate(currency):
if Money._rates is not None:
if currency.USD:
return Money._rates['USD_in']
return Money._rates['USD_out']
response = get("https://belarusbank.by/api/kursExchange?city=%D0%9C%D0%B8%D0%BD%D1%81%D0%BA")
if response.ok:
Money._rates = {
key: float(value)
for key, value in response.json().pop().items()
if key.startswith("USD_")
}
else:
Money.__rates = {'USD_in': 2, 'USD_out': 2}
return Money.rate(currency)
def convert(self):
rate = Money.rate(self.currency)
if self.currency is Currency.BYN:
amount = self.amount / rate
currency = Currency.USD
elif self.currency is Currency.USD:
amount = self.amount * rate
currency = Currency.BYN
return Money(round(amount, 2), currency)
def converted(self):
converted = self.convert()
self.amount = converted.amount
self.currency = converted.currency
return self
def __convert__(self, other):
if not isinstance(other, Money):
raise TypeError("Type should be `Money`!")
if self.currency != other.currency:
other.converted()
def __float__(self):
return self.amount
def __int__(self):
return int(self.amount)
def __add__(self, other):
self.__convert__(other)
return Money(self.amount + other.amount, self.currency)
def __sub__(self, other):
self.__convert__(other)
return Money(self.amount - other.amount, self.currency)
def __mul__(self, other):
if not isinstance(other, (int, float)):
raise TypeError("Type should be `int` or `float`!")
return Money(self.amount * other, self.currency)
def __div__(self, other):
if not isinstance(other, (int, float)):
raise TypeError("Type should be `int` or `float`!")
return Money(self.amount / other, self.currency)
def __str__(self):
return "{} {}".format(self.amount, str(self.currency))
def __repr__(self):
return str(self)
BYN = Money.BYN
USD = Money.USD
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment