"""XPath lexing rules.

To understand how this module works, it is valuable to have a strong
understanding of the `ply <http://www.dabeaz.com/ply/>` module.
"""

from __future__ import unicode_literals

operator_names = {
    "or": "OR_OP",
    "and": "AND_OP",
    "div": "DIV_OP",
    "mod": "MOD_OP",
    "intersect": "INTERSECT_OP",
    "stream": "PIPELINE_OP",
}

tokens = [
    "PATH_SEP",
    "ABBREV_PATH_SEP",
    "ABBREV_STEP_SELF",
    "ABBREV_STEP_PARENT",
    "AXIS_SEP",
    "ABBREV_AXIS_AT",
    "OPEN_PAREN",
    "CLOSE_PAREN",
    "OPEN_BRACKET",
    "CLOSE_BRACKET",
    "UNION_OP",
    "EQUAL_OP",
    "REL_OP",
    "INTERSECT_OP",
    "PLUS_OP",
    "MINUS_OP",
    "MULT_OP",
    "STAR_OP",
    "COMMA",
    "LITERAL",
    "FLOAT",
    "INTEGER",
    "NCNAME",
    "NODETYPE",
    "FUNCNAME",
    "AXISNAME",
    "COLON",
    "DOLLAR",
] + list(operator_names.values())

t_PATH_SEP = r"/"
t_ABBREV_PATH_SEP = r"//"
t_ABBREV_STEP_SELF = r"\."
t_ABBREV_STEP_PARENT = r"\.\."
t_AXIS_SEP = r"::"
t_ABBREV_AXIS_AT = r"@"
t_OPEN_PAREN = r"\("
t_CLOSE_PAREN = r"\)"
t_OPEN_BRACKET = r"\["
t_CLOSE_BRACKET = r"\]"
t_UNION_OP = r"\|"
t_EQUAL_OP = r"!?="
t_REL_OP = r"[<>]=?"
t_PLUS_OP = r"\+"
t_MINUS_OP = r"-"
t_COMMA = r","
t_COLON = r":"
t_DOLLAR = r"\$"
t_STAR_OP = r"\*"

t_ignore = " \t\r\n"

# NOTE: some versions of python cannot compile regular expressions that
# contain unicode characters above U+FFFF, which are allowable in NCNames.
# These characters can be used in Python 2.6.4, but can NOT be used in 2.6.2
# (status in 2.6.3 is unknown).  The code below accounts for that and excludes
# the higher character range if Python can't handle it.

# Monster regex derived from:
#  http://www.w3.org/TR/REC-xml/#NT-NameStartChar
#  http://www.w3.org/TR/REC-xml/#NT-NameChar
# EXCEPT:
# Technically those productions allow ':'. NCName, on the other hand:
#  http://www.w3.org/TR/REC-xml-names/#NT-NCName
# explicitly excludes those names that have ':'. We implement this by
# simply removing ':' from our regexes.

# NameStartChar regex without characters about U+FFFF
NameStartChar = (
    r"[A-Z]|_|[a-z]|\xc0-\xd6]|[\xd8-\xf6]|[\xf8-\u02ff]|"
    + r"[\u0370-\u037d]|[\u037f-\u1fff]|[\u200c-\u200d]|[\u2070-\u218f]|"
    + r"[\u2c00-\u2fef]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD]"
)
# complete NameStartChar regex
Full_NameStartChar = r"(" + NameStartChar + r"|[\U00010000-\U000EFFFF]" + r")"
# additional characters allowed in NCNames after the first character
NameChar_extras = r"[-.0-9\xb7\u0300-\u036f\u203f-\u2040]"

try:
    import re

    # test whether or not re can compile unicode characters above U+FFFF
    re.compile(r"[\U00010000-\U00010001]")
    # if that worked, then use the full ncname regex
    NameStartChar = Full_NameStartChar
except:
    # if compilation failed, leave NameStartChar regex as is, which does not
    # include the unicode character ranges above U+FFFF
    pass

NCNAME_REGEX = (
    r"(" + NameStartChar + r")(" + NameStartChar + r"|" + NameChar_extras + r")*"
)

NODE_TYPES = set(["comment", "text", "processing-instruction", "node"])

t_NCNAME = NCNAME_REGEX


def t_LITERAL(t):
    r""" "[^"]*"|'[^']*'"""
    t.value = t.value[1:-1]
    return t


def t_FLOAT(t):
    r"\d+\.\d*|\.\d+"
    t.value = float(t.value)
    return t


def t_INTEGER(t):
    r"\d+"
    t.value = int(t.value)
    return t


def t_error(t):
    raise TypeError("Unknown text '%s'" % (t.value,))
