diff --git a/calculator.py b/calculator.py deleted file mode 100644 index 35d66bf..0000000 --- a/calculator.py +++ /dev/null @@ -1,237 +0,0 @@ -# https://www.mycompiler.io/view/3TFZagC - -class ParseError(Exception): - def __init__(self, pos, msg, *args): - self.pos = pos - self.msg = msg - self.args = args - - def __str__(self): - return '%s at position %s' % (self.msg % self.args, self.pos) - -class Parser: - def __init__(self): - self.cache = {} - - def parse(self, text): - self.text = text - self.pos = -1 - self.len = len(text) - 1 - rv = self.start() - self.assert_end() - return rv - - def assert_end(self): - if self.pos < self.len: - raise ParseError( - self.pos + 1, - 'Expected end of string but got %s', - self.text[self.pos + 1] - ) - - def eat_whitespace(self): - while self.pos < self.len and self.text[self.pos + 1] in " \f\v\r\t\n": - self.pos += 1 - - def split_char_ranges(self, chars): - try: - return self.cache[chars] - except KeyError: - pass - - rv = [] - index = 0 - length = len(chars) - - while index < length: - if index + 2 < length and chars[index + 1] == '-': - if chars[index] >= chars[index + 2]: - raise ValueError('Bad character range') - - rv.append(chars[index:index + 3]) - index += 3 - else: - rv.append(chars[index]) - index += 1 - - self.cache[chars] = rv - return rv - - def char(self, chars=None): - if self.pos >= self.len: - raise ParseError( - self.pos + 1, - 'Expected %s but got end of string', - 'character' if chars is None else '[%s]' % chars - ) - - next_char = self.text[self.pos + 1] - if chars == None: - self.pos += 1 - return next_char - - for char_range in self.split_char_ranges(chars): - if len(char_range) == 1: - if next_char == char_range: - self.pos += 1 - return next_char - elif char_range[0] <= next_char <= char_range[2]: - self.pos += 1 - return next_char - - raise ParseError( - self.pos + 1, - 'Expected %s but got %s', - 'character' if chars is None else '[%s]' % chars, - next_char - ) - - def keyword(self, *keywords): - self.eat_whitespace() - if self.pos >= self.len: - raise ParseError( - self.pos + 1, - 'Expected %s but got end of string', - ','.join(keywords) - ) - - for keyword in keywords: - low = self.pos + 1 - high = low + len(keyword) - - if self.text[low:high] == keyword: - self.pos += len(keyword) - self.eat_whitespace() - return keyword - - raise ParseError( - self.pos + 1, - 'Expected %s but got %s', - ','.join(keywords), - self.text[self.pos + 1], - ) - - def match(self, *rules): - self.eat_whitespace() - last_error_pos = -1 - last_exception = None - last_error_rules = [] - - for rule in rules: - initial_pos = self.pos - try: - rv = getattr(self, rule)() - self.eat_whitespace() - return rv - except ParseError as e: - self.pos = initial_pos - - if e.pos > last_error_pos: - last_exception = e - last_error_pos = e.pos - last_error_rules.clear() - last_error_rules.append(rule) - elif e.pos == last_error_pos: - last_error_rules.append(rule) - - if len(last_error_rules) == 1: - raise last_exception - else: - raise ParseError( - last_error_pos, - 'Expected %s but got %s', - ','.join(last_error_rules), - self.text[last_error_pos] - ) - - def maybe_char(self, chars=None): - try: - return self.char(chars) - except ParseError: - return None - - def maybe_match(self, *rules): - try: - return self.match(*rules) - except ParseError: - return None - - def maybe_keyword(self, *keywords): - try: - return self.keyword(*keywords) - except ParseError: - return None - -class CalcParser(Parser): - def start(self): - return self.expression() - - def expression(self): - rv = self.match('term') - while True: - op = self.maybe_keyword('+', '-') - if op is None: - break - - term = self.match('term') - if op == '+': - rv += term - else: - rv -= term - - return rv - - def term(self): - rv = self.match('factor') - while True: - op = self.maybe_keyword('*', '/') - if op is None: - break - - term = self.match('factor') - if op == '*': - rv *= term - else: - rv /= term - - return rv - - def factor(self): - if self.maybe_keyword('('): - rv = self.match('expression') - self.keyword(')') - - return rv - - return self.match('number') - - def number(self): - chars = [] - - sign = self.maybe_keyword('+', '-') - if sign is not None: - chars.append(sign) - - chars.append(self.char('0-9')) - - while True: - char = self.maybe_char('0-9') - if char is None: - break - - chars.append(char) - - if self.maybe_char('.'): - chars.append('.') - chars.append(self.char('0-9')) - - while True: - char = self.maybe_char('0-9') - if char is None: - break - - chars.append(char) - - rv = float(''.join(chars)) - return rv -