# # # # # # # # # # # # # # # # # # # # # # # # # # # #
#  Framework provided by Daan de Graaf(UvA)           #
#  For Automaten en Formele Talen                     #
#  Under Guidance of Inge Bethke(UvA)                 #
#  Last edited 2018-04-03 by Bas van den Heuvel (UvA) #
# # # # # # # # # # # # # # # # # # # # # # # # # # # #


# A DFA is not suited for matching brackets since it is not able
# to store the amount of opening brackets it encouters to enable
# a comparison of this number and the amount of opening brackets.


from Automaton import FA
import pickle
import sys


def create_parser():
    '''Creates the actual automaton with the definitions below'''
    Q = ['0', '1', '2', '3', '4']

    Sigma = ['ID', 'EQU', 'LBT', 'INT', 'ID', 'RBT', 'SUB', 'ADD', 'MUL',
             'DIV', 'EOF', 'WHITE']
    delta = {'0' : {'ID' : '1',
                    'WHITE' : '0'},
             '1' : {'EQU' : '2',
                    'WHITE' : '1'},
             '2' : {'LBT' : '2',
                    'INT' : '3',
                    'ID' : '3',
                    'WHITE' : '2'},
             '3' : {'RBT' : '3',
                    'SUB' : '2',
                    'ADD' : '2',
                    'MUL' : '2',
                    'DIV' : '2',
                    'WHITE' : '3',
                    'EOF' : '4'}}
    s = '0'
    F = ['4']
    M = FA(Q, Sigma, delta, s, F)
    return M


def parser(M, source):
    """Parses a given list of token and identifer tuples using
       a given FA"""
    for n, (_, iden) in enumerate(source):
        ret = M.move(iden)
        if not ret:
            tt = M.current_state.transition_table
            error_handler(source, n, [x for x in tt])
            return False
    return M.current_state in M.final_states


def error_handler(source, index, exp):
    """Prints an error with relevant information"""
    for tkn in source:
        print(tkn[0], end='')
    lensum = 0
    for i in range(index):
        lensum += len(source[i][0])
    print("^".rjust(lensum - 1))
    print("SyntaxError: Expected ", end="")
    print(" or ".join(exp))
    sys.exit(1)



def main(program):
    """Reads .lex source and sends it to a parser"""
    Syntax = create_parser()
    Syntax.plot(filename='parser')
    try:
        lexed = pickle.load(open(program, "rb"))
    except:
        exit('FileError: file "' + program + '" not found.')

    for line in lexed:
        parser(Syntax, line)
        Syntax.reset()

    print('Succesfully parsed ' + program)

if __name__ == '__main__':
    try:
        program = sys.argv[1]
    except:
        exit('RuntimeError: Use parser.py [.lex]')

    main(program)
