def __init__(self, variable, expression, symTbl, token): """ Initialize an AssignmentNode. :param variable: The variable node (VariableNode) :param expression: The expression that represents the value the variable will be associated with (LiteralNode, MathNode or VariableNode) :param symTbl: The symbol table which associates variable names (key=str) with values (value=int) :param token: The character associated with assignment when emitting (str) :exception: raises syntax_error.SyntaxError if there is an assignment to a non-variable, with the message, 'Bad assignment to non-variable' :exception: raises syntax_error.SyntaxError if the expression is bad, with the message, 'Bad assignment expression'. :return: None """ # first check for syntax errors and raise an appropriate exception if not isinstance(variable, variable_node.VariableNode): raise syntax_error.SyntaxError('Bad assignment to non-variable') if not isinstance(expression, (literal_node.LiteralNode, math_node.MathNode, variable_node.VariableNode)): raise syntax_error.SyntaxError('Bad assignment expression') self.variable = variable self.expression = expression self.symTbl = symTbl self.token = token
def __parse(self, tokens): """ The recursive parser that builds the parse tree from one line of source code. :param tokens: The tokens from the source line separated by whitespace in a list of strings. :exception: raises a syntax_error.SyntaxError with the message 'Incomplete statement' if the statement is incomplete (e.g. there are no tokens left and this method was called). :exception: raises a syntax_error.SyntaxError with the message 'Invalid token {token}' if an unrecognized token is encountered (e.g. not one of the tokens listed above). :return: """ if len(tokens) < 1: return firstToken = tokens[0] if firstToken is self.COMMENT_TOKEN: return if firstToken.isdigit(): node = literal_node.LiteralNode(firstToken) self.parseTrees.append(node) elif firstToken.isidentifier(): self.symTbl[firstToken] = '' node = variable_node.VariableNode(firstToken, self.symTbl) self.parseTrees.append(node) elif firstToken == self.ASSIGNMENT_TOKEN: first = self.parseTrees.pop() second = self.parseTrees.pop() valid = self.checkIfPoppedNodesAreValidForAssignmentNode( first, second) if valid == True: node = assignment_node.AssignmentNode(first, second, self.symTbl, firstToken) self.parseTrees.append(node) node.evaluate() else: self.syntaxError = True raise syntax_error.SyntaxError('Incomplete Statement' + ' line Number ' + str(self.lineNum)) elif firstToken in self.MATH_TOKENS: first = self.parseTrees.pop() second = self.parseTrees.pop() valid = self.checkIfPoppedNodesAreValidForMathNode(first, second) if valid == True: node = math_node.MathNode(first, second, firstToken) self.parseTrees.append(node) else: self.syntaxError = True raise syntax_error.SyntaxError('Incomplete Statement' + ' line Number ' + str(self.lineNum)) self.__parse(tokens[1:])
def __parse(self, tokens): """ The recursive parser that builds the parse tree from one line of source code. :param tokens: The tokens from the source line separated by whitespace in a list of strings. :exception: raises a syntax_error.SyntaxError with the message 'Incomplete statement' if the statement is incomplete (e.g. there are no tokens left and this method was called). :exception: raises a syntax_error.SyntaxError with the message 'Invalid token {token}' if an unrecognized token is encountered (e.g. not one of the tokens listed above). :return: """ if len(tokens) > 0: token = tokens.pop(0) else: return if token is self.COMMENT_TOKEN: return None elif token is self.PRINT_TOKEN: return print_node.PrintNode(self.__parse(tokens)) elif token.isdigit(): return literal_node.LiteralNode(token) elif token == '=': return assignment_node.AssignmentNode(self.__parse(tokens), self.__parse(tokens), self.symTbl, token) elif token in self.MATH_TOKENS: left = self.__parse(tokens) right = self.__parse(tokens) if not isinstance(left, (literal_node.LiteralNode, math_node.MathNode, variable_node.VariableNode)): raise syntax_error.SyntaxError('Incomplete expression') if not isinstance(right, (literal_node.LiteralNode, math_node.MathNode, variable_node.VariableNode)): raise syntax_error.SyntaxError('Incomplete expression') return math_node.MathNode(left, right, token) elif token.isidentifier(): return variable_node.VariableNode(token, self.symTbl) else: raise syntax_error.SyntaxError("Invalid token {" + str(token) + "}")
def isIncompleteExpression(self, mylist): length = len(mylist) if length < 3 and mylist[0] in self.MATH_TOKENS: self.syntaxError = True raise syntax_error.SyntaxError('Incomplete Statement' + ' line Number ' + str(self.lineNum)) return True if length < 3 and mylist[0] == self.ASSIGNMENT_TOKEN: self.syntaxError = True raise syntax_error.SyntaxError('Incomplete Statement' + ' line Number ' + str(self.lineNum)) return True return False
def __parse(self, tokens): """ The recursive parser that builds the parse tree from one line of source code. :param tokens: The tokens from the source line separated by whitespace in a list of strings. :exception: raises a syntax_error.SyntaxError with the message 'Incomplete statement' if the statement is incomplete (e.g. there are no tokens left and this method was called). :exception: raises a syntax_error.SyntaxError with the message 'Invalid token {token}' if an unrecognized token is encountered (e.g. not one of the tokens listed above). :return: """ if len(tokens) == 0: return elif tokens[0].isidentifier(): node = variable_node.VariableNode(tokens[0], self.symTbl) self.parseTrees.append(node) elif tokens[0].isdigit(): node = literal_node.LiteralNode(int(tokens[0])) self.parseTrees.append(node) elif tokens[0] == '=': node = assignment_node.AssignmentNode(self.parseTrees.pop(), self.parseTrees.pop(), self.symTbl, tokens[0]) self.parseTrees.append(node) elif tokens[0] == '@': node = print_node.PrintNode(self.parseTrees.pop()) self.parseTrees.append(node) elif tokens[0] in self.MATH_TOKENS: x = self.parseTrees.pop() y = self.parseTrees.pop() if not isinstance(x, (variable_node.VariableNode, literal_node.LiteralNode, math_node.MathNode)): raise syntax_error.SyntaxError('Incomplete Statement') if not isinstance(y, (variable_node.VariableNode, literal_node.LiteralNode, math_node.MathNode)): raise syntax_error.SyntaxError('Incomplete Statement') node = math_node.MathNode(x, y, tokens[0]) self.parseTrees.append(node) else: raise syntax_error.SyntaxError("Invalid token") self.__parse(tokens[1:]) """
def parse(self): """ The public parse is responsible for looping over the lines of source code and constructing the parseTree, as a series of calls to the helper function that are appended to this list. It needs to handle and display any syntax_error.SyntaxError exceptions that get raised. : return None """ count = 1 fh = open(self.srcFile) for i in fh: try: self.lineNum = count tokens = i.split() if i[0] == '#' or tokens == []: count += 1 continue elif any(self.MATH_TOKENS) in tokens: if len(tokens) < 3: raise syntax_error.SyntaxError("Incomplete Statement") if '=' in tokens[1:]: count += 1 raise syntax_error.SyntaxError("Invalid Token") if i[0] == '=' or i[0] == '@': count += 1 if len(tokens) == 1: if tokens[0] == '@': node = print_node.PrintNode( literal_node.LiteralNode(' ')) self.parseTrees.append(node) else: if '@' in tokens[1:]: raise syntax_error.SyntaxError( "Bad assignment expression") raise syntax_error.SyntaxError( "Incomplete Statement") self.__parse(tokens[::-1]) else: count += 1 if not (i[0] in self.MATH_TOKENS): raise syntax_error.SyntaxError("Invalid Token") except syntax_error.SyntaxError as syn: self.syntaxError = True print("Line ", self.lineNum, " ", syn)
def __parse(self, tokens): """ The recursive parser that builds the parse tree from one line of source code. :param tokens: The tokens from the source line separated by whitespace in a list of strings. :exception: raises a syntax_error.SyntaxError with the message 'Incomplete statement' if the statement is incomplete (e.g. there are no tokens left and this method was called). :exception: raises a syntax_error.SyntaxError with the message 'Invalid token {token}' if an unrecognized token is encountered (e.g. not one of the tokens listed above). :return: """ if len(tokens) <= 0: raise (syntax_error.SyntaxError("Invalid Expression")) current = tokens[0] if len(tokens) > 0: if type(tokens) is str: tokens = None else: tokens.pop(0) if current == self.ASSIGNMENT_TOKEN: if len(tokens) > 0: return assignment_node.AssignmentNode(self.__parse(tokens.pop(0)),self.__parse(tokens),self.symTbl,'=') else: raise syntax_error.SyntaxError('Incomplete statement') elif current in self.MATH_TOKENS: return math_node.MathNode(self.__parse(tokens),self.__parse(tokens), current) elif current == self.PRINT_TOKEN: if len(tokens)>0: return print_node.PrintNode(self.__parse(tokens)) else: return print_node.PrintNode() elif current.isdigit(): return literal_node.LiteralNode(current) elif current.isalpha(): return variable_node.VariableNode(current,self.symTbl) elif current == self.COMMENT_TOKEN: pass elif current is None: raise syntax_error.SyntaxError('Incomplete statement') else: raise syntax_error.SyntaxError('Invalid token ' + current)
def validate(self, mylist): count = 0 length = len(mylist) for i in mylist: count += 1 if i != self.ASSIGNMENT_TOKEN and i not in self.MATH_TOKENS and i != self.PRINT_TOKEN and not i.isidentifier( ) and not i.isdigit(): self.syntaxError = True raise syntax_error.SyntaxError('Invalid token ' + str(i) + ' line Number ' + str(self.lineNum)) return i if i == self.PRINT_TOKEN and count <= length and count != 1: self.syntaxError = True raise syntax_error.SyntaxError('Bad assignment expression' + ' line Number ' + str(self.lineNum)) return i return ""
def __init__(self, left, right, token): """ Initialize a MathNode. :param left: the left expression (LiteralNode, MathNode, VariableNode) :param right: the right expression (LiteralNode, MathNode, VariableNode) :param token: the character for the math operation (str) :return: None """ if not isinstance(left, (variable_node.VariableNode, math_node.MathNode, literal_node.LiteralNode)): raise syntax_error.SyntaxError('Illegal Epression.') if not isinstance(right, (variable_node.VariableNode, math_node.MathNode, literal_node.LiteralNode)): raise syntax_error.SyntaxError('Illegal Epression.') self.left = left self.right = right self.token = token
def __parse(self, tokens): """ The recursive parser that builds the parse tree from one line of source code. :param tokens: The tokens from the source line separated by whitespace in a list of strings. :exception: raises a syntax_error.SyntaxError with the message 'Incomplete statement' if the statement is incomplete (e.g. there are no tokens left and this method was called). :exception: raises a syntax_error.SyntaxError with the message 'Invalid token {token}' if an unrecognized token is encountered (e.g. not one of the tokens listed above). :return: """ # pass print('tokens are',tokens) token_list = ['+', '-', '*', '//', '#', '=', '@'] if not tokens: raise syntax_error.SyntaxError('Incomplete statement') for i in tokens: if not i.isdigit() and not i.isidentifier(): if i not in token_list: raise syntax_error.SyntaxError('Invalid token {i}') i =0 while(i < len(tokens)): if tokens[i] == PreTee.COMMENT_TOKEN: i+=1 elif tokens[i].isidentifier(): variable_node.VariableNode(tokens[i], self.symTbl[tokens[i]]) i+=1 elif tokens[i].isdigit(): literal_node.LiteralNode(tokens[i]) i+=1 elif tokens[i] in PreTee.MATH_TOKENS: left_val = self.__parse(tokens[i+1]) right_val = self.__parse(tokens[i+2]) math_node.MathNode(left_val, right_val, tokens[i]) i+=2
def __parse(self, tokens): """ The recursive parser that builds the parse tree from one line of source code. :param tokens: The tokens from the source line separated by whitespace in a list of strings. :exception: raises a syntax_error.SyntaxError with the message 'Incomplete statement' if the statement is incomplete (e.g. there are no tokens left and this method was called). :exception: raises a syntax_error.SyntaxError with the message 'Invalid token {token}' if an unrecognized token is encountered (e.g. not one of the tokens listed above). :return: the object of one of node classes based on the type token. Node classes: literal_node.LiteralNode, variable_node.VariableNode, assignment_node.AssignmentNode, print_node.PrintNode, math_node.MathNode """ if len(tokens) is 0: raise syntax_error.SyntaxError('Incomplete statement') token = tokens.pop() if token.isdigit(): return literal_node.LiteralNode(token) elif token.isidentifier(): return variable_node.VariableNode(token, self.symTbl) elif token is self.ASSIGNMENT_TOKEN: return assignment_node.AssignmentNode(self.__parse(tokens), self.__parse(tokens), self.symTbl, token) elif token is self.PRINT_TOKEN: if len(tokens) > 0: return print_node.PrintNode(self.__parse(tokens)) else: return print_node.PrintNode() elif token in self.MATH_TOKENS: return math_node.MathNode(self.__parse(tokens), self.__parse(tokens), token) else: raise syntax_error.SyntaxError('Invalid token ' + token)
def parse(self): """ The public parse is responsible for looping over the lines of source code and constructing the parseTree, as a series of calls to the helper function that are appended to this list. It needs to handle and display any syntax_error.SyntaxError exceptions that get raised. : return None """ with open(self.srcFile) as file: for line in file: self.lineNum += 1 try: line = line.strip() if '#' in line or line == '': continue myList = line.split() invalidToken = self.validate(myList) s1 = set(self.MATH_TOKENS) s2 = set(myList) if s1.intersection(s2): if self.ASSIGNMENT_TOKEN in myList: if (len(myList) - 1) < 3: self.syntaxError = True raise syntax_error.SyntaxError( 'Incomplete Statement' + ' line Number ' + str(self.lineNum)) if not self.isIncompleteExpression( myList) and invalidToken == "": myList = myList[::-1] self.__parse(myList) else: pass # self.syntaxError = True # raise syntax_error.SyntaxError('Invalid token ' + str(invalidToken) + ' line Number ' + str(self.lineNum)) except syntax_error.SyntaxError as e: print('***Syntax Error:', e)
def __parse(self, tokens): """ The recursive parser that builds the parse tree from one line of source code. :param tokens: The tokens from the source line separated by whitespace in a list of strings. :exception: raises a syntax_error.SyntaxError with the message 'Incomplete statement' if the statement is incomplete (e.g. there are no tokens left and this method was called). :exception: raises a syntax_error.SyntaxError with the message 'Invalid token {token}' if an unrecognized token is encountered (e.g. not one of the tokens listed above). :return: """ # no token left if len(tokens) < 1: # error message: Incomplete statement self.syntaxError = True return syntax_error.SyntaxError("Incomplete statement") elif len(tokens) == 1: if tokens[0].isdigit(): return literal_node.LiteralNode(int(tokens[0])) elif tokens[0].isidentifier(): return variable_node.VariableNode(tokens[0], self.symTbl) else: # error message: Invalid token self.syntaxError = True return syntax_error.SyntaxError("Invalid token %s" % tokens[0]) elif tokens[0] in self.MATH_TOKENS: # leaf nodes required to construct the left expression parse tree leavesNeed = 1 for i in range(1, len(tokens)): if tokens[i].isdigit() or tokens[i].isidentifier(): # a leaf node leavesNeed -= 1 elif tokens[i] in self.MATH_TOKENS: # a interior node, need one more leaf node to construct the # full binary tree leavesNeed += 1 else: # error message: Invalid token self.syntaxError = True return syntax_error.SyntaxError("Invalid token %s" % tokens[i]) # has all nodes required to construct the left expression if leavesNeed == 0: # construct the left and right expression left = self.__parse(tokens[1:i + 1]) right = self.__parse(tokens[i + 1:]) # error raised in construction of left expression if isinstance(left, syntax_error.SyntaxError): return left # error raised in construction of right expression if isinstance(right, syntax_error.SyntaxError): return right return math_node.MathNode(left, right, tokens[0]) else: # error message: Invalid token self.syntaxError = True return syntax_error.SyntaxError("Invalid token %s" % tokens[0])