import sys
sys.path.append(".")
import nfa, string
digit = '+'.join("0123456789")
letter = '+'.join(string.ascii_letters) + "+_"
upper = '+'.join(string.ascii_uppercase)
lower = '+'.join(string.ascii_lowercase)
whitespace_no_newline = " +\f+\r+\t+\v"
whitespace = "(" + whitespace_no_newline + "+\n)*"
any_char = digit + "+" + letter + "+" + whitespace
any_string = "("+any_char+")*"

integer = nfa.compile("-?(" + digit + ")(" + digit + ")*")
integer.type = "integer"
identifier = nfa.compile("(" + letter + ")(" + letter + "+" + digit + ")*") # letter followed by any number of letters or digits
identifier.type = "identifier"
string = nfa.compile("(\"(" + any_string + ")\")+('("+any_string+")')")
string.type = "string"
comment = nfa.compile("--(" + digit + "+" + letter + "+" + whitespace_no_newline + ")*\n") # Untested
comment.type = "comment"
keyword = nfa.compile("+".join(["class", "else", "false", "fi", "if", "in", "inherits", "isvoid", "let", "loop", "pool", "then", "while", "case", "esac", "new", "of", "not", "true"]))
keyword.type = "keyword"
assign = nfa.compile("<-")
assign.type = "assign"
relop = nfa.compile("+".join(["<", "<=", ">", ">=", "=", "<>", "!="]))
relop.type = "relop"
semicolon = nfa.compile(";")
semicolon.type = "semicolon"
colon = nfa.compile(":")
colon.type = "colon"
comma = nfa.compile(",")
comma.type = "comma"
whitespace_nfa = nfa.compile(whitespace)
whitespace_nfa.type = "whitespace_nfa"
parens = nfa.either(nfa.build_from_char("("), nfa.build_from_char(")"))
parens.type = "parens"
mathbinop = nfa.either(nfa.either(nfa.compile("-+/+%+^+|+&"), nfa.build_from_char("+")), nfa.build_from_char("*"))
mathbinop.type = "mathbinop"
mathunop = nfa.compile("~")
mathunop.type = "mathunop"
brace = nfa.compile("{+}")
brace.type = "brace"
bracket = nfa.compile("[+]")
bracket.type = "bracket"
unop = nfa.compile("!")
unop.type = "unop"


test_data = """
if x = y then
    x <- 10;
else
    x <- x - (y * -1); -- comment test
    print("string literal test");
fi
"""

#print(nfa.match(keyword, "if"))
#print(nfa.match(nfa.compile("if+and"), "if"))
#print(nfa.match(integer, "10"))
#nfa.pmap(nfa.compile("if+and"))
#sys.exit(0)

class token():
    def __init__(self):
        self.matched_string = ""
        self.type = False

# returns a list of tokens in the order they appeared in the input string
def lex(data):
    # import subprocess
    # process = subprocess.Popen(["gpp", "+c", "--", "\\n"], stdin = subprocess.PIPE, stdout = subprocess.PIPE)
    # data = process.communicate(input=data.encode("utf-8"))[0].decode("utf-8")
    # whichever of these is the first to match a substring of the text is used to create the token
    priority_order = [whitespace_nfa, comment, integer, parens, bracket, brace, mathbinop, mathunop, unop, semicolon, colon, comma, keyword, assign, relop, string, identifier]
    done = []
    data_ptr = 0
    while data_ptr < len(data): # loop until we've read the whole input string
        one_matched = False
        # start by trying to match the whole rest of the input string, and chop one character off the end until there are no characters left. If none of those substrings matched, move on to the next regex in the priority order
        for regex in priority_order: # starting with the highest priority regex
            data_end = len(data) # MAXIMUM MUNCH - literally the largest lookahead possible
            this_regex_matched = False
            while data_end - data_ptr > 0:
                considering = data[data_ptr:data_end]
                if nfa.match(regex, considering): # If this regex matched the substring
                    data_ptr += len(considering) # add the length of what it matched to where we'll start reading the next token from
                    t = token() # construct a token and add it to the result list
                    t.matched_string = considering
                    t.type = regex.type
                    done.append(t)
                    one_matched = True # don't die
                    this_regex_matched = True
                    break
                data_end -= 1
            if this_regex_matched:
                break
        if not one_matched:
            print("Nothing matched '" + considering + "', bailing out") # if we encounter something that we can't parse, just die
            return []
    return done
# debug
# for tkn in lex(test_data):
    # if tkn.type != "whitespace_nfa": print("token was '" + tkn.matched_string.replace("\n", "\\n") + "' of type " + tkn.type)
