Example #1
0
def processAssign(tokens1: List[Token]) -> (Node, List[Error]):
    """Retrieve tokens and return a node according to the assign layout. """
    error = []
    tokens = copy.copy(tokens1)
    index = getIndexToken(lambda x: x.instance == TokenValues.ASSIGN,
                          tokens)  # get index of assignment
    if (index != 1):
        error.append(
            Error(Errornr.SYNTAX_ERROR, "too many arguments for assignment"))
    if (tokens[0].instance != TokenValues.VAR):
        error.append(
            Error(
                Errornr.SYNTAX_ERROR, "on line " + str(tokens[0].linenr) +
                " -> \"" + tokens[0].type + "\" cannot be an variable"))
    lhs = value_node(tokens[0].linenr, tokens[0].type, TokenValues.VAR_ASSIGN)
    # check if after assignment there are enough parameters (value and terminator)
    if (index + 1 >= len(tokens) - 1):
        error.append(
            Error(Errornr.SYNTAX_ERROR,
                  "On line " + str(tokens[index].linenr) + ", invalid syntax"))
        return None, error
    rhs, pv1 = processTokens(tokens[index + 1:])
    pv = copy.copy(pv1)
    pv.unprocessedTokens = []
    if (pv.state == State.ERROR):
        error += pv.error_list
    if (len(rhs) > 1):
        error += pv.error_list
    current_node = operator_node(tokens[index].linenr, tokens[index].type, lhs,
                                 tokens[index].instance, rhs[0])
    return current_node, error
Example #2
0
def processComparison(tokens1: List[Token]) -> (Node, List[Error]):
    """Retrieve tokens and return a node according to the comparison rules"""
    error = []
    tokens = copy.copy(tokens1)
    # get first element that meets the condition
    func = lambda currentToken: currentToken.instance == TokenValues.EQUAL or currentToken.instance == TokenValues.NOTEQUAL or currentToken.instance == TokenValues.GE \
        or currentToken.instance == TokenValues.SE or currentToken.instance == TokenValues.GREATER or currentToken.instance == TokenValues.SMALLER
    index = _getFirst(list(map(func, tokens)))

    lhs, pv_lhs = processTokens(
        tokens[:index])  # process the tokens till the comparison token
    if (len(pv_lhs.error_list) > 0):
        error += (pv_lhs.error_list)
    if (pv_lhs.state == State.ERROR or len(lhs) > 1):
        error += (Error(
            Errornr.SYNTAX_ERROR,
            "On line " + str(tokens[0].linenr) + " ^" + str(tokens[0].type) +
            " invalid syntax: too many arguments for comparison"))

    rhs, pv_rhs = processTokens(
        tokens[index + 1:])  # process the tokens after the comparison token
    if (len(pv_rhs.error_list) > 0):
        error += (pv_rhs.error_list)
    if ((pv_rhs.state == State.ERROR or len(rhs) > 1) and len(error) == 0):
        error.append(
            Error(
                Errornr.SYNTAX_ERROR, "On line " + str(tokens[0].linenr) +
                " ^" + str(tokens[index + 1].type) +
                " invalid syntax: too many arguments for comparison"))
    current_node = operator_node(tokens[index].linenr, tokens[index].type,
                                 lhs[0], tokens[index].instance, rhs[0])
    return current_node, error
Example #3
0
def processMath(currentToken1: Token, current_node1: Node) -> (Node, [Error]):
    """Retrieve node and append the token in this node return this Node and the Errorlist. Errorlist is empty if no errors occur.
    Append Node according to the math rules"""
    error = []
    currentToken = copy.copy(currentToken1)
    current_node = copy.copy(current_node1)
    if (currentToken.instance == TokenValues.PLUS
            or currentToken.instance == TokenValues.MIN):
        new_node = operator_node(currentToken.linenr, currentToken.type,
                                 current_node, currentToken.instance)
        return new_node, error
    elif (currentToken.instance == TokenValues.MULTIPLY
          or currentToken.instance == TokenValues.DIVIDED_BY):
        # find the node that is not filled yet
        node_ = findNode(current_node)
        if (node_ != None):
            lhs = node_.rhs
            new_node = operator_node(currentToken.linenr, currentToken.type,
                                     lhs, currentToken.instance)
            node_.rhs = new_node
        else:
            # if there are only values in the nodetree
            new_node = operator_node(currentToken.linenr, currentToken.type,
                                     current_node, currentToken.instance)
            current_node = new_node

    else:
        new_node = value_node(currentToken.linenr, currentToken.type,
                              currentToken.instance)
        # check if list is empty
        if (current_node.value == None):
            current_node = new_node
        else:
            # get empty operator_node
            node_ = findNode(current_node)
            if (node_ != None):
                if (node_.lhs == None):
                    node_.lhs = new_node
                elif (node_.rhs == None):
                    node_.rhs = new_node
                else:
                    error.append(
                        Error(
                            Errornr.MATH_ERROR,
                            "On line: " + str(currentToken.linenr) +
                            ", too many numbers versus operators"))
            else:
                error.append(
                    Error(
                        Errornr.MATH_ERROR,
                        "On line: " + str(currentToken.linenr) +
                        ", cannot calculate " + currentToken.type))
                pass
    return current_node, error
Example #4
0
def lexer(
        filename: str) -> Union[Tuple[List[Token], Error], Tuple[None, Error]]:
    """Reads from file the content. If None is returned, show error
    else retrieve the tokens matching to the inputted lines. If a Token could not be parsed, create error and return result"""
    fileContainer = readFromFile(filename)
    if (fileContainer != None):
        tokenlist = lex_rec(lex_func, fileContainer)
        errorlist = list(
            filter(lambda x: x.instance == TokenValues.ERROR, tokenlist))
        error = Error(Errornr.NO_ERROR)
        if (len(errorlist) > 0):
            error = (Error(
                Errornr.SYNTAX_ERROR, "On line " + str(errorlist[0].linenr) +
                ", cannot define " + " \"" + errorlist[0].type + "\" " + " "))
        return tokenlist, error
    return None, Error(Errornr.FileNotFoundError, "Cannot open " + filename)
Example #5
0
def processIf_While(
    tokens1: List[Token]
) -> Union[Tuple[Node, List[Error]], Tuple[None, List[Error]]]:
    """Retrieve tokens and return a node according to the if or while layout. Since if and while have the same rules,
    these commands can both be processed in this funcion"""
    error = []
    tokens = copy.copy(tokens1)
    # get index of all important tokens
    index_parL = getIndexToken(lambda x: x.instance == TokenValues.LPAREN,
                               tokens)
    index_parR = getIndexToken(lambda x: x.instance == TokenValues.RPAREN,
                               tokens)
    index_bracL = getIndexToken(lambda x: x.instance == TokenValues.LBRACE,
                                tokens)

    # check for syntax errors
    if (index_parL == -1 or index_parR == -1):
        error.append(
            Error(Errornr.SYNTAX_ERROR,
                  "Syntax Error on line " + str(tokens[0].linenr)))
        return None, error

    if ((index_bracL - index_parR) != 1):
        error.append(
            Error(Errornr.SYNTAX_ERROR,
                  "Syntax Error on line " + str(tokens[index_parR].linenr)))
        return None, error
    elif (index_bracL == -1):
        error.append(
            Error(Errornr.SYNTAX_ERROR,
                  "Syntax Error on line " + str(tokens[index_parR].linenr)))
        return None, error

    new_node = operator_node(tokens[0].linenr, tokens[0].type, None,
                             tokens[0].instance)
    compare_node, error = processComparison(tokens[index_parL + 1:index_parR])
    node_list_rhs, pv1 = processTokens(tokens[index_bracL + 1:-1])
    pv = copy.copy(pv1)
    pv.unprocessedTokens = []
    if (len(error) > 0):
        error += pv.error_list
    new_node.lhs = compare_node
    new_node.rhs = node_list_rhs
    return new_node, error
Example #6
0
def if_func(enum: ProgramActions, a: bool, b: List[Node]) -> Error:
    '''if the condition is true, execute the nodes in list b
    Return the first error'''
    error = []
    if (a):
        # execute the codeblock in the ifstatement
        result, _error = AST_to_actions(enum, b)
        error.append(_error)
    #check if an error has occured. If so, return the error. Else, return NO-Error
    errors = list(filter(lambda x: x.nr != Errornr.NO_ERROR, error))
    if (len(errors) > 0):
        return errors[0]
    return Error()
Example #7
0
def AST_to_actions(enum: ProgramActions,
                   nodes: List[Node]) -> Tuple[Union[None, float], Error]:
    '''Loop throught the list of nodes and give the AST to the _loopNodeFunction. If an error occurs, stop the loop and return None and the error'''
    if (len(nodes) == 0):
        return None, Error()
    result, error_ast = AST_to_actions(enum, nodes[0:-1])
    #check for errors
    if (error_ast.nr != Errornr.NO_ERROR):
        return None, error_ast
    # unpack the AST
    result, error = _loopNode(enum, nodes[-1])
    if (error.nr != Errornr.NO_ERROR):
        return None, error
    #useful for whileloop
    return result, error
Example #8
0
def _loopNode(enum: ProgramActions,
              node: Node) -> Tuple[Union[None, float, str], Error]:
    '''Loop through the node. If an error occurs, return the error'''
    if (isinstance(node, value_node)):
        if (node.type == TokenValues.NUMBER):
            return float(node.value), Error()
        if (node.type == TokenValues.VAR):
            # check if the value is in the dictionary
            if (node.value in enum.variables):
                return enum.variables[node.value], Error()
            else:
                return (None,
                        Error(
                            Errornr.NameError, "On line " + str(node.linenr) +
                            ", name \"" + node.value + "\" is not defined"))
        if (node.type == TokenValues.VAR_ASSIGN):
            return node.value, Error()

    if (isinstance(node, operator_node)):
        # use different call for whileloop
        if (node.operator == TokenValues.WHILE):
            error = execute(enum, node.operator, ([node.lhs], node.rhs))
            return None, error
        # Check if the nodes are filled, this is obligated
        if (node.lhs == None or node.rhs == None):
            return None, Error(Errornr.SYNTAX_ERROR,
                               "on line: " + str(node.linenr))
        # get lhs
        left, error = _loopNode(enum, node.lhs)
        if (error.nr != Errornr.NO_ERROR):
            return None, error
        #check if node is a list (for the if-statement function)
        if ((not isinstance(node.rhs, list))):
            right, error = _loopNode(enum, node.rhs)
            if (error.nr != Errornr.NO_ERROR):
                return None, error

        else:
            right = node.rhs

        result = execute(enum, node.operator, (left, right))
        # check if the result is an error
        if (isinstance(result, Error)):
            return None, result
        # return result and No-error
        return result, Error()
    # if empty node occurs, no big deal
    return None, Error()
Example #9
0
def while_func(enum: ProgramActions, while_condition: List[Node],
               codeBlock: List[Node]) -> Error:
    '''check if the condition is true, then execute the codeblock
    check the condition_nodes every time, since the condition can be updated due to the codeblock'''
    condition, error = AST_to_actions(enum, while_condition)
    if (error.nr != Errornr.NO_ERROR):
        return error
    while (condition):
        #execute the code in the codeblok of the whileloop
        result, error = AST_to_actions(enum, codeBlock)
        if (error.nr != Errornr.NO_ERROR):
            return error
        # check if the condition is still true
        condition, error = AST_to_actions(enum, while_condition)
        # check for errors
        if (error.nr != Errornr.NO_ERROR):
            return error
    return Error()
Example #10
0
def processTokens(tokens1: List[Token]) -> (List[Node], ProgramValues):
    """Retrieve the tokens that needs to be processed. Check in the token the instance and act accordingly
    It returns the nodes of the processed tokens and the ProgramValues"""
    tokens = copy.copy(tokens1)
    if (len(tokens) == 0):
        return [], ProgramValues(unprocessed=[])  # default values

    nodes, pv_old = processTokens(tokens[0:-1])
    pv = copy.copy(pv_old)
    currentToken = tokens[-1]
    if (pv.state == State.ERROR):
        return nodes, pv
    if (pv.state == State.PRINT):
        if (currentToken.instance == TokenValues.PRINT_END):
            new_node = operator_node(pv.unprocessedTokens[0].linenr,
                                     pv.unprocessedTokens[0].type, None,
                                     pv.unprocessedTokens[0].instance)
            rhs, pv = copy.copy(processTokens(pv.unprocessedTokens[1:]))
            pv.state = State.Idle
            if (len(rhs) > 0):
                new_node.lhs = rhs[0]
                new_node.rhs = rhs
            nodes.append(new_node)
            return nodes, pv
        pv.unprocessedTokens.append(currentToken)
        return nodes, pv

    if (pv.state == State.Math):
        # program rules, assignment before math
        if (currentToken.instance == TokenValues.ASSIGN):
            pv.state = State.ASSIGN
            # remove incorrect math equation
            nodes = nodes[:-1]
            pv.unprocessedTokens.append(currentToken)
        elif (currentToken.instance != TokenValues.NUMBER
              and currentToken.instance != TokenValues.VAR
              and currentToken.instance != TokenValues.PLUS
              and currentToken.instance != TokenValues.MIN
              and currentToken.instance != TokenValues.MULTIPLY
              and currentToken.instance != TokenValues.DIVIDED_BY):
            # math state is over, remove processed tokens
            pv.unprocessedTokens = []
            pv.state = State.Idle
        else:
            pv.unprocessedTokens.append(currentToken)
            nodes[-1], error = processMath(currentToken, nodes[-1])
            if (len(error) > 0):
                pv.state = State.ERROR
                pv.error_list += (error)
        return nodes, pv

    elif (pv.state == State.ASSIGN):
        if (currentToken.instance == TokenValues.SEMICOLON):
            pv.unprocessedTokens.append(currentToken)
            new_node, error_list = processAssign(pv.unprocessedTokens)
            if (len(error_list) > 0):
                pv.state = State.ERROR
                pv.error_list += error_list
                pv.unprocessedTokens = []
                return nodes, pv
            pv.unprocessedTokens = []
            nodes.append(new_node)
            pv.state = State.Idle
        else:
            pv.unprocessedTokens.append(currentToken)
        return nodes, pv

    elif (pv.state == State.IF_WHILE):
        pv.unprocessedTokens.append(currentToken)
        if (currentToken.instance == TokenValues.RBRACE):
            amountBraces = amountOpenBraces(pv.unprocessedTokens)
            if (amountBraces == 0):
                new_node, error_list = processIf_While(pv.unprocessedTokens)
                if (new_node == None):
                    pv.state = State.ERROR
                    pv.error_list += error_list
                    return nodes, pv
                nodes.append(new_node)
                pv.unprocessedTokens = []
                pv.state = State.Idle
                nodes.append(Node())
            elif (amountBraces < 0):
                pv.error_list.append(
                    Error(
                        Errornr.SYNTAX_ERROR,
                        "on line " + str(pv.unprocessedTokens[0].linenr) +
                        ", missing opening brace"))

        return nodes, pv

    elif (currentToken.instance == TokenValues.PLUS
          or currentToken.instance == TokenValues.MIN
          or currentToken.instance == TokenValues.MULTIPLY
          or currentToken.instance == TokenValues.DIVIDED_BY
          or currentToken.instance == TokenValues.NUMBER
          or currentToken.instance == TokenValues.VAR):
        if (pv.state == State.Idle):
            pv.state = State.Math
            nodes.append(Node())

            nodes[-1], error = processMath(currentToken, nodes[-1])
            if (len(error) > 0):
                pv.state = State.ERROR
                pv.error_list += (error)
                return nodes, pv

    elif (currentToken.instance == TokenValues.ASSIGN):
        if (pv.state == State.Idle):
            pv.state = State.ASSIGN

    elif (currentToken.instance == TokenValues.IF):
        pv.state = State.IF_WHILE

    elif (currentToken.instance == TokenValues.WHILE):
        pv.state = State.IF_WHILE

    elif (currentToken.instance == TokenValues.PRINT):
        pv.state = State.PRINT

    pv.unprocessedTokens.append(currentToken)

    return nodes, pv
Example #11
0
 def inner(a: A, b: B) -> Union[A, B, Error]:
     '''Check if b is not 0: if not, return the devision, else return a zeroDivisionError'''
     if (b == 0):
         return Error(Errornr.ZeroDivisionError,
                      "cannot divide " + str(a) + " with " + str(b))
     return f(a, b)