added Exercise 28

This commit is contained in:
Daniel Knüttel 2018-12-12 16:43:20 +01:00
parent c11fc9effe
commit 591a474795

186
ex_28.py Normal file
View File

@ -0,0 +1,186 @@
from collections import deque
import string
from io import StringIO
TOKEN_NUMERIC_LITERAL = "0123456789"
TOKEN_DOT = "."
TOKEN_PARENTHESIS_OPEN = "("
TOKEN_PARENTHESIS_CLOSE = ")"
TOKEN_BINARY_OPERATOR = "+-*/"
TOKEN_SIGN = "-"
TOKEN_WHITESPACE = string.whitespace
STATE_BEGIN_EXPRESSION = 0
STATE_INSIDE_TERM = 1
STATE_EXPECT_NEXT_ARGUMENT = 2
STATE_PARENTHESIS_CLOSED = 4
class CharacterDevice(object):
def __init__(self, file_):
self._file = file_
self._offset = 0
self.character = deque([0])
self.line = 0
def getc(self):
self._offset += 1
character = self._file.read(1)
if(character == "\n"):
self.line += 1
self.character.append(0)
else:
self.character[-1] += 1
return character
def ungetc(self, c):
if(c == "\n"):
self.line -= 1
self.character.pop()
else:
self.character[-1] -= 1
self._offset -= 1
self._file.seek(self._offset)
class ParsingError(Exception):
pass
class InternalError(Exception):
pass
class ArithmeticParser(object):
def __init__(self, file_):
self._file = CharacterDevice(file_)
self._stack = deque()
self._current_expr = deque()
def parse_numeric_literal(self, carry = ""):
literal = carry
dot = False
character = None
while True:
character = self._file.getc()
if(not character):
break
if(character in TOKEN_NUMERIC_LITERAL):
literal += character
continue
elif(character in TOKEN_DOT):
if(dot):
raise self.build_parsing_error("Unexpected dot while parsing literal")
dot = True
literal += character
continue
break
self._file.ungetc(character)
self._current_expr.append(literal)
def parse_parenthesis(self):
parenthesis = self._file.getc()
if(parenthesis not in TOKEN_PARENTHESIS_OPEN):
raise InternalError("parse_parenthesis did not recieve an opening parenthesis")
self._stack.append(self._current_expr)
self._current_expr = deque()
state = self.parse_expression()
parenthesis = self._file.getc()
if(parenthesis not in TOKEN_PARENTHESIS_CLOSE):
raise self.build_parsing_error("Expected closing parenthesis")
if(not parenthesis):
raise self.build_parsing_error("Unexpected EOF while parsing")
nested = self._current_expr
self._current_expr = self._stack.pop()
self._current_expr.append(list(nested))
def parse_expression(self):
state = STATE_BEGIN_EXPRESSION
while True:
peek = self._file.getc()
if(not peek):
break
if(peek in TOKEN_WHITESPACE):
continue
if(state == STATE_BEGIN_EXPRESSION):
if(peek in TOKEN_NUMERIC_LITERAL):
self._file.ungetc(peek)
self.parse_numeric_literal()
state = STATE_INSIDE_TERM
continue
if(peek in TOKEN_SIGN):
self.parse_numeric_literal(carry=peek)
state = STATE_INSIDE_TERM
continue
if(peek in TOKEN_PARENTHESIS_OPEN):
self._file.ungetc(peek)
self.parse_parenthesis()
state = STATE_INSIDE_TERM
continue
raise self.build_parsing_error("Unexpected token: '{}'".format(peek))
if(state == STATE_INSIDE_TERM):
if(peek in TOKEN_BINARY_OPERATOR):
self._current_expr.append(peek)
state = STATE_EXPECT_NEXT_ARGUMENT
continue
if(peek in TOKEN_PARENTHESIS_CLOSE):
state = STATE_PARENTHESIS_CLOSED
self._file.ungetc(peek)
break
raise self.build_parsing_error("Unexpected token: '{}', expected binary operator".format(peek))
if(state == STATE_EXPECT_NEXT_ARGUMENT):
if(peek in TOKEN_NUMERIC_LITERAL):
self._file.ungetc(peek)
self.parse_numeric_literal()
state = STATE_INSIDE_TERM
continue
if(peek in TOKEN_SIGN):
self.parse_numeric_literal(carry=peek)
state = STATE_INSIDE_TERM
continue
if(peek in TOKEN_PARENTHESIS_OPEN):
self._file.ungetc(peek)
self.parse_parenthesis()
state = STATE_INSIDE_TERM
continue
raise self.build_parsing_error("Unexpected token: '{}'".format(peek))
if(state not in (STATE_BEGIN_EXPRESSION
, STATE_INSIDE_TERM
, STATE_PARENTHESIS_CLOSED)):
raise self.build_parsing_error("Unexpected EOF while parsing")
return state
def parse(self):
state = self.parse_expression()
if(state not in (STATE_BEGIN_EXPRESSION
, STATE_INSIDE_TERM)):
raise self.build_parsing_error("Unexpected EOF while parsing")
return list(self._current_expr)
def build_parsing_error(self, msg):
return ParsingError("Error: Line {}, Characer {}: {}".format(self._file.line, self._file.character, msg))
def check_expression(string):
parser = ArithmeticParser(StringIO(string))
try:
parser.parse()
return True
except ParsingError:
return False
if( __name__ == "__main__"):
print(ArithmeticParser(StringIO("((3.2+4)*(5)+7)")).parse())
print(check_expression("((3+4)*(5)+7)"))
print(check_expression("(-3+4)*(5/7)"))
print(check_expression("(-3+4)*(5/(7+5))"))
print(check_expression("(-3.3+4)*(5/(7+5)"))
print(ArithmeticParser(StringIO("((3+4)*(5)+7")).parse())