lib/rouge/lexers/c.rb



module Rouge
  module Lexers
    class C < RegexLexer
      tag 'c'
      filenames '*.c', '*.h', '*.idc'
      mimetypes 'text/x-chdr', 'text/x-csrc'

      # optional comment or whitespace
      ws = %r((?:\s|//.*?\n|/[*].*?[*]/)+)
      id = /[a-zA-Z_][a-zA-Z0-9_]*/

      keywords = %w(
        auto break case const continue default do else enum extern
        for goto if register restricted return sizeof static struct
        switch typedef union volatile virtual while
      )

      keywords_type = %w(int long float short double char unsigned signed void)

      __reserved = %w(
        asm int8 based except int16 stdcall cdecl fastcall int32
        declspec finally int61 try leave
      )

      state :whitespace do
        rule /^#if\s+0\b/, 'Comment.Preproc', :if_0
        rule /^#/, 'Comment.Preproc', :macro
        rule /^#{ws}#if\s+0\b/, 'Comment.Preproc', :if_0
        rule /^#{ws}#/, 'Comment.Preproc', :macro
        rule /^(\s*)(#{id}:(?!:))/ do
          group 'Text'
          group 'Name.Label'
        end

        rule /\s+/m, 'Text'
        rule /\\\n/, 'Text' # line continuation
        rule %r(//(\n|(.|\n)*?[^\\]\n)), 'Comment.Single'
        rule %r(/(\\\n)?[*](.|\n)*?[*](\\\n)?/), 'Comment.Multiline'
      end

      state :statements do
        rule /L?"/, 'Literal.String', :string
        rule %r(L?'(\\.|\\[0-7]{1,3}|\\x[a-f0-9]{1,2}|[^\\'\n])')i, 'Literal.String.Char'
        rule %r((\d+\.\d*|\.\d+|\d+)[e][+-]?\d+[lu]*)i, 'Literal.Number.Float'
        rule /0x[0-9a-f]+[lu]*/i, 'Literal.Number.Hex'
        rule /0[0-7]+[lu]*/i, 'Literal.Number.Oct'
        rule /\d+[lu]*/i, 'Literal.Number.Integer'
        rule %r(\*/), 'Error'
        rule %r([~!%^&*+=\|?:<>/-]), 'Operator'
        rule /[()\[\],.]/, 'Punctuation'
        rule /\bcase\b/, 'Keyword', :case
        rule /(?:#{keywords.join('|')})\b/, 'Keyword'
        rule /(?:#{keywords_type.join('|')})\b/, 'Keyword.Type'
        rule /(?:_{0,2}inline|naked|restrict|thread|typename)\b/, 'Keyword.Reserved'
        rule /__(?:#{__reserved.join('|')})\b/, 'Keyword.Reserved'
        rule /(?:true|false|NULL)\b/, 'Name.Builtin'
        rule id, 'Name'
      end

      state :case do
        rule /:/, 'Punctuation', :pop!
        mixin :statements
      end

      state :root do
        mixin :whitespace

        # functions
        rule %r(
          ([\w*\s]+?[\s*]) # return arguments
          (#{id})          # function name
          (\s*\([^;]*?\))  # signature
          (#{ws})({)         # open brace
        )mx do |m|
          # TODO: do this better.
          delegate C, m[1]
          token 'Name.Function', m[2]
          delegate C, m[3]
          delegate C, m[4]
          token 'Punctuation', m[5]
          push :function
        end

        # function declarations
        rule %r(
          ([\w*\s]+?[\s*]) # return arguments
          (#{id})          # function name
          (\s*\([^;]*?\))  # signature
          (#{ws})(;)       # semicolon
        )mx do |m|
          # TODO: do this better.
          delegate C, m[1]
          token 'Name.Function'
          delegate C, m[3]
          delegate C, m[4]
          token 'Punctuation'
          push :statement
        end

        rule(//) { push :statement }
      end

      state :statement do
        rule /;/, 'Punctuation', :pop!
        mixin :whitespace
        mixin :statements
        rule /[{}]/, 'Punctuation'
      end

      state :function do
        mixin :whitespace
        mixin :statements
        rule /;/, 'Punctuation'
        rule /{/, 'Punctuation', :function
        rule /}/, 'Punctuation', :pop!
      end

      state :string do
        rule /"/, 'Literal.String', :pop!
        rule /\\([\\abfnrtv"']|x[a-fA-F0-9]{2,4}|[0-7]{1,3})/, 'Literal.String.Escape'
        rule /[^\\"\n]+/, 'Literal.String'
        rule /\\\n/, 'Literal.String'
        rule /\\/, 'Literal.String' # stray backslash
      end

      state :macro do
        rule %r([^/\n]+), 'Comment.Preproc'
        rule %r(/[*].*?[*]/)m, 'Comment.Multiliine'
        rule %r(//.*$), 'Comment.Single'
        rule %r(/), 'Comment.Preproc'
        rule /(?<=\\)\n/, 'Comment.Preproc'
        rule /\n/, 'Comment.Preproc', :pop!
      end

      state :if_0 do
        rule /^\s*#if.*?(?<!\\)\n/, 'Comment.Preproc', :if_0
        rule /^\s*#el(?:se|if).*\n/, 'Comment.Preproc', :pop!
        rule /^\s*#endif.*?(?<!\\)\n/, 'Comment.Preproc', :pop!
        rule /.*?\n/, 'Comment'
      end
    end
  end
end