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 / .svn / pristine / 8a / 8a838241ecb1de57abec573d9ab086b1b83f3ab1.svn-base @ 1297:0a574315af3e

History | View | Annotate | Download (5.51 KB)

1
module CodeRay module Scanners
2
  
3
  # by Josh Goebel
4
  class SQL < Scanner
5

    
6
    register_for :sql
7
    
8
    KEYWORDS = %w(
9
      all and any as before begin between by case check collate
10
      each else end exists
11
      for foreign from full group having if in inner is join
12
      like not of on or order outer over references
13
      then to union using values when where
14
      left right distinct
15
    )
16
    
17
    OBJECTS = %w(
18
      database databases table tables column columns fields index constraint
19
      constraints transaction function procedure row key view trigger
20
    )
21
    
22
    COMMANDS = %w(
23
      add alter comment create delete drop grant insert into select update set
24
      show prompt begin commit rollback replace truncate
25
    )
26
    
27
    PREDEFINED_TYPES = %w(
28
      char varchar varchar2 enum binary text tinytext mediumtext
29
      longtext blob tinyblob mediumblob longblob timestamp
30
      date time datetime year double decimal float int
31
      integer tinyint mediumint bigint smallint unsigned bit
32
      bool boolean hex bin oct
33
    )
34
    
35
    PREDEFINED_FUNCTIONS = %w( sum cast substring abs pi count min max avg now )
36
    
37
    DIRECTIVES = %w( 
38
      auto_increment unique default charset initially deferred
39
      deferrable cascade immediate read write asc desc after
40
      primary foreign return engine
41
    )
42
    
43
    PREDEFINED_CONSTANTS = %w( null true false )
44
    
45
    IDENT_KIND = WordList::CaseIgnoring.new(:ident).
46
      add(KEYWORDS, :keyword).
47
      add(OBJECTS, :type).
48
      add(COMMANDS, :class).
49
      add(PREDEFINED_TYPES, :predefined_type).
50
      add(PREDEFINED_CONSTANTS, :predefined_constant).
51
      add(PREDEFINED_FUNCTIONS, :predefined).
52
      add(DIRECTIVES, :directive)
53
    
54
    ESCAPE = / [rbfntv\n\\\/'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} | . /mx
55
    UNICODE_ESCAPE =  / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
56
    
57
    STRING_PREFIXES = /[xnb]|_\w+/i
58
    
59
    def scan_tokens encoder, options
60
      
61
      state = :initial
62
      string_type = nil
63
      string_content = ''
64
      name_expected = false
65
      
66
      until eos?
67
        
68
        if state == :initial
69
          
70
          if match = scan(/ \s+ | \\\n /x)
71
            encoder.text_token match, :space
72
          
73
          elsif match = scan(/(?:--\s?|#).*/)
74
            encoder.text_token match, :comment
75
            
76
          elsif match = scan(%r( /\* (!)? (?: .*? \*/ | .* ) )mx)
77
            encoder.text_token match, self[1] ? :directive : :comment
78
            
79
          elsif match = scan(/ [*\/=<>:;,!&^|()\[\]{}~%] | [-+\.](?!\d) /x)
80
            name_expected = true if match == '.' && check(/[A-Za-z_]/)
81
            encoder.text_token match, :operator
82
            
83
          elsif match = scan(/(#{STRING_PREFIXES})?([`"'])/o)
84
            prefix = self[1]
85
            string_type = self[2]
86
            encoder.begin_group :string
87
            encoder.text_token prefix, :modifier if prefix
88
            match = string_type
89
            state = :string
90
            encoder.text_token match, :delimiter
91
            
92
          elsif match = scan(/ @? [A-Za-z_][A-Za-z_0-9]* /x)
93
            encoder.text_token match, name_expected ? :ident : (match[0] == ?@ ? :variable : IDENT_KIND[match])
94
            name_expected = false
95
            
96
          elsif match = scan(/0[xX][0-9A-Fa-f]+/)
97
            encoder.text_token match, :hex
98
            
99
          elsif match = scan(/0[0-7]+(?![89.eEfF])/)
100
            encoder.text_token match, :octal
101
            
102
          elsif match = scan(/[-+]?(?>\d+)(?![.eEfF])/)
103
            encoder.text_token match, :integer
104
            
105
          elsif match = scan(/[-+]?(?:\d[fF]|\d*\.\d+(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+)/)
106
            encoder.text_token match, :float
107
          
108
          elsif match = scan(/\\N/)
109
            encoder.text_token match, :predefined_constant
110
            
111
          else
112
            encoder.text_token getch, :error
113
            
114
          end
115
          
116
        elsif state == :string
117
          if match = scan(/[^\\"'`]+/)
118
            string_content << match
119
            next
120
          elsif match = scan(/["'`]/)
121
            if string_type == match
122
              if peek(1) == string_type  # doubling means escape
123
                string_content << string_type << getch
124
                next
125
              end
126
              unless string_content.empty?
127
                encoder.text_token string_content, :content
128
                string_content = ''
129
              end
130
              encoder.text_token match, :delimiter
131
              encoder.end_group :string
132
              state = :initial
133
              string_type = nil
134
            else
135
              string_content << match
136
            end
137
          elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
138
            unless string_content.empty?
139
              encoder.text_token string_content, :content
140
              string_content = ''
141
            end
142
            encoder.text_token match, :char
143
          elsif match = scan(/ \\ . /mox)
144
            string_content << match
145
            next
146
          elsif match = scan(/ \\ | $ /x)
147
            unless string_content.empty?
148
              encoder.text_token string_content, :content
149
              string_content = ''
150
            end
151
            encoder.text_token match, :error
152
            state = :initial
153
          else
154
            raise "else case \" reached; %p not handled." % peek(1), encoder
155
          end
156
          
157
        else
158
          raise 'else-case reached', encoder
159
          
160
        end
161
        
162
      end
163
      
164
      if state == :string
165
        encoder.end_group state
166
      end
167
      
168
      encoder
169
      
170
    end
171
    
172
  end
173
  
174
end end