(var parser {})
(set sibilant 'parser parser)
(set parser 'tokens
     { 'comment            "(;.*)"
       'string             "(\"(([^\"]|(\\\\\"))*[^\\\\])?\")"
       'number             "(-?[0-9][0-9.,]*)"
       'literal            "(-?[*.$a-zA-Z_][/*.a-zA-Z0-9-_]*(\\?|!)?)"
       'special            "([&'])"
       'at                 "@"
       'tick               "[`']"
       'hat                "(\\^)"
       'dots               "(\\.+)"
       'arg-placeholder    "(#[0-9]+)"
       'other-char         "([\\|#><=!\\+\\/\\*-]+)"
       'open-expression    "(\\(|\\{|\\[)"
       'close-expression    "(\\)|\\}|\\])"
       'newline "\\n"
       'whitespace "\\s+"
       'ignored "." })
(set parser.tokens 'head ("(\\.*[*$a-zA-Z_\\|><=\\+\\/\\*-]+"
                          "[/*.a-zA-Z0-9-_\\|><=\\+\\/\\*-]*"
                          "(\\?|!)?\\()"))

(set parser 'token-precedence  `[ comment
                                  string
                                  number
                                  tick
                                  hat
                                  at
                                  special
                                  head
                                  dots
                                  literal
                                  arg-placeholder
                                  other-char
                                  open-expression
                                  close-expression
                                  newline
                                  whitespace
                                  ignored
                                  ]
     'ordered-regexes (parser.token-precedence.map
                       (#(x)
                         (merge-into (regex ("^" (get parser.tokens x))) {name x}))))

(var ordered-regexes parser.ordered-regexes)

(def parser.parse (string context)
     (default context { position 0
                        stack []
                        line 1
                        last-newline 0
                        col 0 })
     (var match true
          regex-name null
          remaining-input string)
     
     (while match
            (detect ordered-regexes
                    (#(r)
                      (assign regex-name r.name
                              match (r.exec remaining-input))))


            (when (exists? match)
                  (var match-string (first match)
                       length (length match-string))
                  
                  (context.stack.push
                   { file sibilant.file
                     token match-string
                     type regex-name
                     line context.line
                     col context.col
                     contents []})

                  (if
                   (= 'newline regex-name)
                   (do
                    (incr context.line)
                    (set context
                         'col 0
                         'last-newline context.position))

                   (and (= 'string regex-name)
                        (includes? match-string "\n"))
                   (do
                    (var string-newline-count (pipe match-string
                                                    (.split "\n")
                                                    length
                                                    (- 1)))

                    (incr-by context.line string-newline-count)
                    (set context 'col (- length (match-string.last-index-of "\n"))))

                   (incr-by context.col length))

                  (incr-by context.position length)
                  (assign remaining-input (if (and remaining-input remaining-input.length)
                                              (remaining-input.slice length)
                                              ""))))
     context.stack)

(var parse parser.parse)
