To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / vendor / gems / coderay-0.9.7 / lib / coderay / scanners / cpp.rb @ 442:753f1380d6bc

History | View | Annotate | Download (6.14 KB)

1
module CodeRay
2
module Scanners
3

    
4
  class CPlusPlus < Scanner
5

    
6
    include Streamable
7
    
8
    register_for :cpp
9
    file_extension 'cpp'
10
    title 'C++'
11
    
12
    # http://www.cppreference.com/wiki/keywords/start
13
    RESERVED_WORDS = [
14
      'and', 'and_eq', 'asm', 'bitand', 'bitor', 'break',
15
      'case', 'catch', 'class', 'compl', 'const_cast',
16
      'continue', 'default', 'delete', 'do', 'dynamic_cast', 'else',
17
      'enum', 'export', 'for', 'goto', 'if', 'namespace', 'new',
18
      'not', 'not_eq', 'or', 'or_eq', 'reinterpret_cast', 'return',
19
      'sizeof', 'static_cast', 'struct', 'switch', 'template',
20
      'throw', 'try', 'typedef', 'typeid', 'typename', 'union',
21
      'while', 'xor', 'xor_eq'
22
    ]
23

    
24
    PREDEFINED_TYPES = [
25
      'bool', 'char', 'double', 'float', 'int', 'long',
26
      'short', 'signed', 'unsigned', 'wchar_t', 'string'
27
    ]
28
    PREDEFINED_CONSTANTS = [
29
      'false', 'true',
30
      'EOF', 'NULL',
31
    ]
32
    PREDEFINED_VARIABLES = [
33
      'this'
34
    ]
35
    DIRECTIVES = [
36
      'auto', 'const', 'explicit', 'extern', 'friend', 'inline', 'mutable', 'operator',
37
      'private', 'protected', 'public', 'register', 'static', 'using', 'virtual', 'void',
38
      'volatile'
39
    ]
40

    
41
    IDENT_KIND = WordList.new(:ident).
42
      add(RESERVED_WORDS, :reserved).
43
      add(PREDEFINED_TYPES, :pre_type).
44
      add(PREDEFINED_VARIABLES, :local_variable).
45
      add(DIRECTIVES, :directive).
46
      add(PREDEFINED_CONSTANTS, :pre_constant)
47

    
48
    ESCAPE = / [rbfntv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
49
    UNICODE_ESCAPE =  / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
50

    
51
    def scan_tokens tokens, options
52

    
53
      state = :initial
54
      label_expected = true
55
      case_expected = false
56
      label_expected_before_preproc_line = nil
57
      in_preproc_line = false
58

    
59
      until eos?
60

    
61
        kind = nil
62
        match = nil
63
        
64
        case state
65

    
66
        when :initial
67

    
68
          if match = scan(/ \s+ | \\\n /x)
69
            if in_preproc_line && match != "\\\n" && match.index(?\n)
70
              in_preproc_line = false
71
              label_expected = label_expected_before_preproc_line
72
            end
73
            tokens << [match, :space]
74
            next
75

    
76
          elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
77
            kind = :comment
78

    
79
          elsif match = scan(/ \# \s* if \s* 0 /x)
80
            match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos?
81
            kind = :comment
82

    
83
          elsif match = scan(/ [-+*=<>?:;,!&^|()\[\]{}~%]+ | \/=? | \.(?!\d) /x)
84
            label_expected = match =~ /[;\{\}]/
85
            if case_expected
86
              label_expected = true if match == ':'
87
              case_expected = false
88
            end
89
            kind = :operator
90

    
91
          elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
92
            kind = IDENT_KIND[match]
93
            if kind == :ident && label_expected && !in_preproc_line && scan(/:(?!:)/)
94
              kind = :label
95
              match << matched
96
            else
97
              label_expected = false
98
              if kind == :reserved
99
                case match
100
                when 'class'
101
                  state = :class_name_expected
102
                when 'case', 'default'
103
                  case_expected = true
104
                end
105
              end
106
            end
107

    
108
          elsif scan(/\$/)
109
            kind = :ident
110
          
111
          elsif match = scan(/L?"/)
112
            tokens << [:open, :string]
113
            if match[0] == ?L
114
              tokens << ['L', :modifier]
115
              match = '"'
116
            end
117
            state = :string
118
            kind = :delimiter
119

    
120
          elsif scan(/#[ \t]*(\w*)/)
121
            kind = :preprocessor
122
            in_preproc_line = true
123
            label_expected_before_preproc_line = label_expected
124
            state = :include_expected if self[1] == 'include'
125

    
126
          elsif scan(/ L?' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox)
127
            label_expected = false
128
            kind = :char
129

    
130
          elsif scan(/0[xX][0-9A-Fa-f]+/)
131
            label_expected = false
132
            kind = :hex
133

    
134
          elsif scan(/(?:0[0-7]+)(?![89.eEfF])/)
135
            label_expected = false
136
            kind = :oct
137

    
138
          elsif scan(/(?:\d+)(?![.eEfF])L?L?/)
139
            label_expected = false
140
            kind = :integer
141

    
142
          elsif scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/)
143
            label_expected = false
144
            kind = :float
145

    
146
          else
147
            getch
148
            kind = :error
149

    
150
          end
151

    
152
        when :string
153
          if scan(/[^\\"]+/)
154
            kind = :content
155
          elsif scan(/"/)
156
            tokens << ['"', :delimiter]
157
            tokens << [:close, :string]
158
            state = :initial
159
            label_expected = false
160
            next
161
          elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
162
            kind = :char
163
          elsif scan(/ \\ | $ /x)
164
            tokens << [:close, :string]
165
            kind = :error
166
            state = :initial
167
            label_expected = false
168
          else
169
            raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
170
          end
171

    
172
        when :include_expected
173
          if scan(/<[^>\n]+>?|"[^"\n\\]*(?:\\.[^"\n\\]*)*"?/)
174
            kind = :include
175
            state = :initial
176

    
177
          elsif match = scan(/\s+/)
178
            kind = :space
179
            state = :initial if match.index ?\n
180

    
181
          else
182
            state = :initial
183
            next
184

    
185
          end
186
        
187
        when :class_name_expected
188
          if scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
189
            kind = :class
190
            state = :initial
191

    
192
          elsif match = scan(/\s+/)
193
            kind = :space
194

    
195
          else
196
            getch
197
            kind = :error
198
            state = :initial
199

    
200
          end
201
          
202
        else
203
          raise_inspect 'Unknown state', tokens
204

    
205
        end
206

    
207
        match ||= matched
208
        if $CODERAY_DEBUG and not kind
209
          raise_inspect 'Error token %p in line %d' %
210
            [[match, kind], line], tokens
211
        end
212
        raise_inspect 'Empty token', tokens unless match
213

    
214
        tokens << [match, kind]
215

    
216
      end
217

    
218
      if state == :string
219
        tokens << [:close, :string]
220
      end
221

    
222
      tokens
223
    end
224

    
225
  end
226

    
227
end
228
end