
require 'latex/basescanner'

module LaTeX
  class Scanner < BaseScanner
    CSNAME = /\\(?:[a-zA-Z@][a-zA-Z@0-9]*\*?| )/
    UNITS = /cm|in|mm|pt|em|ex|pc|bp|dd|cc|sp/

    def start_context
      rule %r/\\begin\{document\}[^\n]*\n/m, :BEGIN_DOCUMENT do 
        push :document 
      end
      rule %r/[^\n]*\n/m, :NONBODY_LINE
    end

    def words_and_spaces_context
      rule %r/[0-9a-zA-Z'\x80-\xff]+/, :WORD
      rule %r/\n(?:[ \t]*\n)+|\r\n(?:[ \t]*\r\n)+/m, :PAR
      rule %r/[ \t]+|\r?\n[ \t]*/m, :SPACE
    end

    def math_and_normal_context
      rule %r/\\\\/, :BR
      # Comment eats up leading whitespace on following line
      rule %r/%[^\n]*\n\s*/m, :COMMENT
      rule %r/\{/, :LBRACE
      rule %r/\}/, :RBRACE
      rule %r/\$\$/, :DISPLAY_MATH
      rule %r/\$/, :INLINE_MATH
      rule %r/\[/, :LBRACKET
      rule %r/\]/, :RBRACKET

      rule %r/&/, :AMP
      rule %r/[_^]/, :STANDALONE_PUNCT
      rule %r/``|''|[~@\-#,=.:!*\/\(\)^+;_<>?|`'"]/, :PUNCT

      # Control sequences
      rule %r/\\let#{CSNAME}=?#{CSNAME}/, :LET
      rule %r/\\(hskip|vskip)\s*-?[\.\d]*\s*#{UNITS}/, :SKIP
      
      rule %r/\\begin\{([^}]+)\}/ do 
        case matches[1]
        when /\A\w*[vV]erbatim/, /\Atabular.*/
          push :verbatim, matches[1]
          [:BEGIN_VERBATIM, text + (match(/[^\n]*\n/m) or "")]
        #when /tabular/
        #  [:BEGIN_TABULAR, text]
        when "comment"
          push :comment
          [:BEGIN_COMMENT, text]
        else
          [:BEGIN_ENV, text]
        end
      end
      rule %r/\\end\{[^}]+\}/, :END_ENV 
      rule %r/\\catcode..=/, :CATCODE
      # Escaped punctuation (or some mini control sequences)
      rule %r/\\[ =^@!|~'`.()\/"&#,${}\[\]%_\n\-]/m, :PUNCT
      # Catch-all control sequence
      rule %r/#{CSNAME}/, :CS
      # Superscripts and subscripts
      rule %r/[_^]/, :CS
    end

    def document_context
      words_and_spaces_context
      rule %r/\\verb\*?\{[^}]*\}|\\verb\*?\[[^\]]*\]|\\verb\*?\([^\)]*\)|\\verb\*?([^\w]).*?\1/, :VERB
      rule %r/\\end\{document\}.*/, :END_DOCUMENT do pop end
      math_and_normal_context
    end
    
    def verbatim_context(env_name)
      rule %r|\\end\{#{env_name}\}\n|m, :END_ENV do pop end
      rule %r/.*?\n/m, :VERB_LINE
    end

    def comment_context
      rule %r/\\end\{comment\}/, :END_ENV do
        pop
      end
      rule %r/.*?\n/m, :COMMENT_LINE
    end

  end
end

