added Exercise 28
This commit is contained in:
parent
c11fc9effe
commit
591a474795
186
ex_28.py
Normal file
186
ex_28.py
Normal 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())
|
Loading…
Reference in New Issue
Block a user