csharp.rb

percy li, 2010-02-20 20:24

Download (4.77 KB)

 
1
module CodeRay
2
  module Scanners
3
    class CSharp < Scanner
4
    
5
      register_for :csharp
6
    
7
      RESERVED_WORDS = %w(abstract as base break case catch checked class
8
      const continue default delegate do else enum event explicit extern
9
      finally fixed for foreach goto if implicit in interface internal is
10
      lock namespace new operator out override params private protected 
11
      public readonly ref return sealed sizeof stackalloc static struct
12
      switch this throw try typeof unchecked unsafe using virtual volatile
13
      void while
14
      add dynamic from get global group into join let orderby partial
15
      remove select set value var where yield)
16
    
17
      PREDEFINED_TYPES = %w(bool byte char double float int long short
18
      decimal uint ulong ushort object sbyte string)
19
    
20
      PREDEFINED_CONSTANTS = %w(true false null)
21
    
22
      IDENT_KIND = WordList.new(:ident).
23
        add(RESERVED_WORDS, :reserved).
24
        add(PREDEFINED_TYPES, :pre_type).
25
        add(PREDEFINED_CONSTANTS, :pre_constant)
26
    
27
      ESCAPE = / [rbfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x
28
      UNICODE_ESCAPE =  / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
29
    
30
      def scan_tokens tokens, options
31
        state = :initial
32
  
33
        until eos?
34
          kind = nil
35
          match = nil
36
  
37
          case state
38
          when :initial
39
  
40
            if scan(/ \s+ | \\\n /x)
41
              kind = :space
42
              
43
            elsif scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
44
              kind = :comment
45
  
46
            elsif match = scan(/ \# \s* if \s* 0 /x)
47
              match << scan_until(/ ^\# (?:elif|else|endif) .*? $ | \z /xm) unless eos?
48
              kind = :comment
49
  
50
            elsif scan(/ [-+*\/=<>?:;,!&^|()\[\]{}~%]+ | \.(?!\d) /x)
51
              kind = :operator
52
  
53
            elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
54
              kind = IDENT_KIND[match]
55
              if kind == :ident and check(/:(?!:)/)
56
                match << scan(/:/)
57
                kind = :label
58
              end
59
  
60
            elsif match = scan(/"/)
61
              tokens << [:open, :string]
62
              state = :string
63
              kind = :delimiter
64
  
65
            elsif match = scan(/@"/)
66
              tokens << [:open, :string]
67
              state = :stringat
68
              kind = :delimiter
69
  
70
            elsif scan(/#\s*(\w*)/)
71
              kind = :preprocessor
72

    
73
            elsif scan(/ ' (?: [^\'\n\\] | \\ #{ESCAPE} )? '? /ox)
74
              kind = :char
75
  
76
            elsif scan(/0[xX][0-9A-Fa-f]+/)
77
              kind = :hex
78
  
79
            elsif scan(/(?:0[0-7]+)(?![89.eEfF])/)
80
              kind = :oct
81
  
82
            elsif scan(/(?:\d+)(?![.eEfFdDmML])/)
83
              kind = :integer
84
  
85
            elsif scan(/\d[fFdDmM]?|\d*\.\d+(?:[eE][+-]?\d+)?[fFdDmM]?|\d+[eE][+-]?\d+[fFdDmM]?/)
86
              kind = :float
87
  
88
            else
89
              getch
90
              kind = :error
91
  
92
            end
93
  
94
          when :string
95
            if scan(/[^\\\n"]+/)
96
              kind = :content
97
            elsif scan(/"/)
98
              tokens << ['"', :delimiter]
99
              tokens << [:close, :string]
100
              state = :initial
101
              next
102
            elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
103
              kind = :char
104
            elsif scan(/ \\ | $ /x)
105
              tokens << [:close, :string]
106
              kind = :error
107
              state = :initial
108
            else
109
              raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
110
            end
111
  
112
          when :stringat
113
            if scan(/[^"]+/)
114
              kind = :content
115
            elsif scan(/""/)
116
              kind = :char
117
            elsif scan(/"/)
118
              tokens << ['"', :delimiter]
119
              tokens << [:close, :string]
120
              state = :initial
121
              next
122
            elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
123
              kind = :char
124
            elsif scan(/ $ /x)
125
              tokens << [:close, :string]
126
              kind = :error
127
              state = :initial
128
            else
129
              raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
130
            end
131

    
132
          else
133
            raise_inspect 'Unknown state', tokens
134
  
135
          end
136
  
137
          match ||= matched
138
          if $DEBUG and not kind
139
            raise_inspect 'Error token %p in line %d' %
140
              [[match, kind], line], tokens
141
          end
142
          raise_inspect 'Empty token', tokens unless match
143
  
144
          tokens << [match, kind]
145
  
146
        end
147
  
148
        if state == :string
149
          tokens << [:close, :string]
150
        end
151
  
152
        tokens
153
      end
154
    end
155
  end
156
end