import source_location

type
  TokenKind* = enum
    ##Literals
    tkIntLiteral      # 42  0xFF  0b1010  0o77
    tkFloatLiteral    # 3.14  1.0e-9
    tkStringLiteral   # "hello"  c8"hello"  c16"hello"  c32"hello"  `raw multi-line`
    tkCharLiteral     # 'A'  c8'A'  c16'A'  c32'A'
    tkBoolLiteral     # true  false

    ##Identifiers
    tkIdent           # foo  Bar  _x
    tkUnderscore      # _

    ##Control flow keywords
    tkIf              # if
    tkElse            # else
    tkWhile           # while
    tkDo              # do
    tkLoop            # loop
    tkFor             # for
    tkIn              # in
    tkBreak           # break
    tkContinue        # continue
    tkReturn          # return
    tkMatch           # match
    tkSwitch          # switch
    tkCase            # case
    tkDefault         # default

    ##Declaration keywords
    tkFunc            # func
    tkLet             # let
    tkVar             # var
    tkConst           # const
    tkType            # type
    tkStruct          # struct
    tkEnum            # enum
    tkUnion           # union
    tkInterface       # interface
    tkExtend          # extend
    tkModule          # module
    tkImport          # import
    tkPub             # pub
    tkExtern          # extern

    ##Other keywords
    tkAs              # as
    tkIs              # is
    tkNull            # null
    tkSelf            # self
    tkSuper           # super
    tkSizeOf          # sizeof
    tkOwn             # own (gradual ownership transfer)
    tkMut             # mut (mutable reference)
    tkBorrow          # borrow (explicit borrow expression)
    tkDiscard         # discard (evaluate and throw away)
    tkAsync           # async
    tkAwait           # await
    tkSpawn           # spawn
    tkStaticAssert    # static_assert
    tkComptime        # comptime
    tkDyn             # dyn
    tkDefer           # defer
    tkLifetime        # 'a (lifetime parameter)

    ##Punctuation
    tkLParen          # (
    tkRParen          # )
    tkLBrace          # {
    tkRBrace          # }
    tkLBracket        # [
    tkRBracket        # ]
    tkComma           # ,
    tkSemicolon       # ;
    tkColon           # :
    tkColonColon      # ::
    tkDot             # .
    tkDotDot          # ..
    tkDotDotDot       # ...
    tkDotDotEqual     # ..=
    tkArrow           # ->
    tkFatArrow        # =>
    tkAt              # @
    tkHash            # #
    tkQuestion        # ?

    ##Arithmetic operators
    tkPlus            # +
    tkMinus           # -
    tkStar            # *
    tkSlash           # /
    tkPercent         # %
    tkStarStar        # **
    tkPlusPlus        # ++
    tkMinusMinus      # --

    ##Bitwise operators
    tkAmp             # &
    tkPipe            # |
    tkCaret           # ^
    tkTilde           # ~
    tkShl             # <<
    tkShr             # >>

    ##Logical operators
    tkAmpAmp          # &&
    tkPipePipe        # ||
    tkBang            # !

    ##Comparison operators
    tkEq              # ==
    tkNe              # !=
    tkLt              # <
    tkLe              # <=
    tkGt              # >
    tkGe              # >=

    ##Assignment operators
    tkAssign          # =
    tkPlusAssign      # +=
    tkMinusAssign     # -=
    tkStarAssign      # *=
    tkSlashAssign     # /=
    tkPercentAssign   # %=
    tkAmpAssign       # &=
    tkPipeAssign      # |=
    tkCaretAssign     # ^=
    tkShlAssign       # <<=
    tkShrAssign       # >>=

    ##Compile-time intrinsics
    tkHashLine        # #line
    tkHashColumn      # #column
    tkHashFile        # #file
    tkHashFunction    # #function
    tkHashDate        # #date
    tkHashTime        # #time
    tkHashModule      # #module
    tkHashEmit        # #emit

    ##Special
    tkNewLine         # significant newline (if grammar uses them)
    tkEndOfFile       # end of file
    tkUnknown         # unrecognized character

  Token* = object
    kind*: TokenKind
    text*: string       # original source spelling
    loc*: SourceLocation

proc isKeyword*(kind: TokenKind): bool =
  case kind
  of tkIf, tkElse, tkWhile, tkDo, tkLoop, tkFor, tkIn,
     tkBreak, tkContinue, tkReturn, tkMatch, tkSwitch, tkCase, tkDefault,
     tkFunc, tkLet, tkVar, tkConst, tkType, tkStruct, tkEnum,
     tkUnion, tkInterface, tkExtend, tkModule, tkImport,
       tkPub, tkExtern, tkAs, tkIs, tkNull, tkSelf, tkSuper, tkOwn, tkMut, tkBorrow, tkDiscard, tkAsync, tkAwait, tkSpawn, tkStaticAssert, tkComptime, tkDyn, tkDefer:
    true
  else:
    false

proc isLiteral*(kind: TokenKind): bool =
  case kind
  of tkIntLiteral, tkFloatLiteral, tkStringLiteral, tkCharLiteral, tkBoolLiteral:
    true
  else:
    false

proc isOperator*(kind: TokenKind): bool =
  case kind
  of tkPlus..tkShrAssign:
    true
  else:
    false

proc isEof*(kind: TokenKind): bool = kind == tkEndOfFile

proc keywordKind*(text: string): TokenKind =
  case text
  of "func": tkFunc
  of "let": tkLet
  of "var": tkVar
  of "const": tkConst
  of "type": tkType
  of "struct": tkStruct
  of "enum": tkEnum
  of "union": tkUnion
  of "interface": tkInterface
  of "extend": tkExtend
  of "module": tkModule
  of "import": tkImport
  of "pub": tkPub
  of "extern": tkExtern
  of "if": tkIf
  of "else": tkElse
  of "while": tkWhile
  of "do": tkDo
  of "loop": tkLoop
  of "for": tkFor
  of "in": tkIn
  of "break": tkBreak
  of "continue": tkContinue
  of "return": tkReturn
  of "match": tkMatch
  of "switch": tkSwitch
  of "case": tkCase
  of "default": tkDefault
  of "as": tkAs
  of "is": tkIs
  of "null": tkNull
  of "self": tkSelf
  of "super": tkSuper
  of "sizeof": tkSizeOf
  of "own": tkOwn
  of "mut": tkMut
  of "borrow": tkBorrow
  of "discard": tkDiscard
  of "async": tkAsync
  of "await": tkAwait
  of "spawn": tkSpawn
  of "static_assert": tkStaticAssert
  of "comptime": tkComptime
  of "dyn": tkDyn
  of "defer": tkDefer
  of "true", "false": tkBoolLiteral
  else: tkIdent

proc tokenKindName*(kind: TokenKind): string =
  case kind
  of tkIntLiteral: "integer literal"
  of tkFloatLiteral: "float literal"
  of tkStringLiteral: "string literal"
  of tkCharLiteral: "char literal"
  of tkBoolLiteral: "boolean literal"
  of tkIdent: "identifier"
  of tkUnderscore: "'_'"
  of tkSizeOf: "'sizeof'"
  of tkIf: "'if'"
  of tkElse: "'else'"
  of tkWhile: "'while'"
  of tkDo: "'do'"
  of tkLoop: "'loop'"
  of tkFor: "'for'"
  of tkIn: "'in'"
  of tkBreak: "'break'"
  of tkContinue: "'continue'"
  of tkReturn: "'return'"
  of tkMatch: "'match'"
  of tkSwitch: "'switch'"
  of tkCase: "'case'"
  of tkDefault: "'default'"
  of tkFunc: "'func'"
  of tkLet: "'let'"
  of tkVar: "'var'"
  of tkConst: "'const'"
  of tkType: "'type'"
  of tkStruct: "'struct'"
  of tkEnum: "'enum'"
  of tkUnion: "'union'"
  of tkInterface: "'interface'"
  of tkExtend: "'extend'"
  of tkModule: "'module'"
  of tkImport: "'import'"
  of tkPub: "'pub'"
  of tkExtern: "'extern'"
  of tkAs: "'as'"
  of tkIs: "'is'"
  of tkNull: "'null'"
  of tkSelf: "'self'"
  of tkSuper: "'super'"
  of tkOwn: "'own'"
  of tkMut: "'mut'"
  of tkBorrow: "'borrow'"
  of tkDiscard: "'discard'"
  of tkAsync: "'async'"
  of tkAwait: "'await'"
  of tkSpawn: "'spawn'"
  of tkStaticAssert: "'static_assert'"
  of tkComptime: "'comptime'"
  of tkDyn: "'dyn'"
  of tkDefer: "'defer'"
  of tkLifetime: "lifetime"
  of tkLParen: "'('"
  of tkRParen: "')'"
  of tkLBrace: "'{'"
  of tkRBrace: "'}'"
  of tkLBracket: "'['"
  of tkRBracket: "']'"
  of tkComma: "','"
  of tkSemicolon: "';'"
  of tkColon: "':'"
  of tkColonColon: "'::'"
  of tkDot: "'.'"
  of tkDotDot: "'..'"
  of tkDotDotDot: "'...'"
  of tkDotDotEqual: "'..='"
  of tkArrow: "'->'"
  of tkFatArrow: "'=>'"
  of tkAt: "'@'"
  of tkHash: "'#'"
  of tkQuestion: "'?'"
  of tkPlus: "'+'"
  of tkMinus: "'-'"
  of tkStar: "'*'"
  of tkSlash: "'/'"
  of tkPercent: "'%'"
  of tkStarStar: "'**'"
  of tkPlusPlus: "'++'"
  of tkMinusMinus: "'--'"
  of tkAmp: "'&'"
  of tkPipe: "'|'"
  of tkCaret: "'^'"
  of tkTilde: "'~'"
  of tkShl: "'<<'"
  of tkShr: "'>>'"
  of tkAmpAmp: "'&&'"
  of tkPipePipe: "'||'"
  of tkBang: "'!'"
  of tkEq: "'=='"
  of tkNe: "'!='"
  of tkLt: "'<'"
  of tkLe: "'<='"
  of tkGt: "'>'"
  of tkGe: "'>='"
  of tkAssign: "'='"
  of tkPlusAssign: "'+='"
  of tkMinusAssign: "'-='"
  of tkStarAssign: "'*='"
  of tkSlashAssign: "'/='"
  of tkPercentAssign: "'%='"
  of tkAmpAssign: "'&='"
  of tkPipeAssign: "'|='"
  of tkCaretAssign: "'^='"
  of tkShlAssign: "'<<='"
  of tkShrAssign: "'>>='"
  of tkHashLine: "'#line'"
  of tkHashColumn: "'#column'"
  of tkHashFile: "'#file'"
  of tkHashFunction: "'#function'"
  of tkHashDate: "'#date'"
  of tkHashTime: "'#time'"
  of tkHashModule: "'#module'"
  of tkHashEmit: "'#emit'"
  of tkNewLine: "newline"
  of tkEndOfFile: "end of file"
  of tkUnknown: "unknown token"

proc `$`*(tok: Token): string =
  result = tokenKindName(tok.kind) & " '" & tok.text & "' @ " & $tok.loc
