Class: CodeRay::Scanners::SQL

Inherits:
Scanner
  • Object
show all
Defined in:
lib/coderay/scanners/sql.rb

Overview

by Josh Goebel

Constant Summary collapse

KEYWORDS =
%w(
  all and any as before begin between by case check collate
  each else end exists
  for foreign from full group having if in inner is join
  like not of on or order outer over references
  then to union using values when where
  left right distinct
)
OBJECTS =
%w(
  database databases table tables column columns fields index constraint
  constraints transaction function procedure row key view trigger
)
COMMANDS =
%w(
  add alter comment create delete drop grant insert into select update set
  show prompt begin commit rollback replace truncate
)
PREDEFINED_TYPES =
%w(
  char varchar varchar2 enum binary text tinytext mediumtext
  longtext blob tinyblob mediumblob longblob timestamp
  date time datetime year double decimal float int
  integer tinyint mediumint bigint smallint unsigned bit numeric
  bool boolean hex bin oct
)
PREDEFINED_FUNCTIONS =
%w( sum cast substring abs pi count min max avg now )
DIRECTIVES =
%w( 
  auto_increment unique default charset initially deferred
  deferrable cascade immediate read write asc desc after
  primary foreign return engine
)
PREDEFINED_CONSTANTS =
%w( null true false )
IDENT_KIND =
WordList::CaseIgnoring.new(:ident).
add(KEYWORDS, :keyword).
add(OBJECTS, :type).
add(COMMANDS, :class).
add(PREDEFINED_TYPES, :predefined_type).
add(PREDEFINED_CONSTANTS, :predefined_constant).
add(PREDEFINED_FUNCTIONS, :predefined).
add(DIRECTIVES, :directive)
ESCAPE =
/ [rbfntv\n\\\/'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} | . /mx
UNICODE_ESCAPE =
/ u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x
STRING_PREFIXES =
/[xnb]|_\w+/i
STRING_CONTENT_PATTERN =
{
  '"' => / (?: [^\\"] | "" )+ /x,
  "'" => / (?: [^\\'] | '' )+ /x,
  '`' => / (?: [^\\`] | `` )+ /x,
}

Constants inherited from Scanner

CodeRay::Scanners::Scanner::DEFAULT_OPTIONS, CodeRay::Scanners::Scanner::KINDS_NOT_LOC, CodeRay::Scanners::Scanner::ScanError

Instance Attribute Summary

Attributes inherited from Scanner

#state

Attributes included from Plugin

#plugin_id

Instance Method Summary collapse

Methods inherited from Scanner

#binary_string, #column, #each, encoding, file_extension, #file_extension, #initialize, #lang, lang, #line, normalize, #reset, #string=, #tokenize, #tokens

Methods included from Plugin

#aliases, #plugin_host, #register_for, #title

Constructor Details

This class inherits a constructor from CodeRay::Scanners::Scanner

Instance Method Details

#scan_tokens(encoder, options) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/coderay/scanners/sql.rb', line 66

def scan_tokens encoder, options
  
  state = :initial
  string_type = nil
  string_content = ''
  name_expected = false
  
  until eos?
    
    if state == :initial
      
      if match = scan(/ \s+ | \\\n /x)
        encoder.text_token match, :space
      
      elsif match = scan(/(?:--\s?|#).*/)
        encoder.text_token match, :comment
        
      elsif match = scan(%r( /\* (!)? (?: .*? \*/ | .* ) )mx)
        encoder.text_token match, self[1] ? :directive : :comment
        
      elsif match = scan(/ [*\/=<>:;,!&^|()\[\]{}~%] | [-+\.](?!\d) /x)
        name_expected = true if match == '.' && check(/[A-Za-z_]/)
        encoder.text_token match, :operator
        
      elsif match = scan(/(#{STRING_PREFIXES})?([`"'])/o)
        prefix = self[1]
        string_type = self[2]
        encoder.begin_group :string
        encoder.text_token prefix, :modifier if prefix
        match = string_type
        state = :string
        encoder.text_token match, :delimiter
        
      elsif match = scan(/ @? [A-Za-z_][A-Za-z_0-9\$]* /x)
        encoder.text_token match, name_expected ? :ident : (match[0] == ?@ ? :variable : IDENT_KIND[match])
        name_expected = false
        
      elsif match = scan(/0[xX][0-9A-Fa-f]+/)
        encoder.text_token match, :hex
        
      elsif match = scan(/0[0-7]+(?![89.eEfF])/)
        encoder.text_token match, :octal
        
      elsif match = scan(/[-+]?(?>\d+)(?![.eEfF])/)
        encoder.text_token match, :integer
        
      elsif match = scan(/[-+]?(?:\d[fF]|\d*\.\d+(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+)/)
        encoder.text_token match, :float
      
      elsif match = scan(/\\N/)
        encoder.text_token match, :predefined_constant
        
      else
        encoder.text_token getch, :error
        
      end
      
    elsif state == :string
      if match = scan(STRING_CONTENT_PATTERN[string_type])
        encoder.text_token match, :content
      elsif match = scan(/["'`]/)
        if string_type == match
          if peek(1) == string_type  # doubling means escape
            encoder.text_token match + getch, :content
          else
            encoder.text_token match, :delimiter
            encoder.end_group :string
            state = :initial
            string_type = nil
          end
        else
          encoder.text_token match, :content
        end
      elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
        encoder.text_token match, :char
      elsif match = scan(/ \\ . /mox)
        encoder.text_token match, :content
      elsif match = scan(/ \\ | $ /x)
        encoder.text_token match, :error unless match.empty?
        encoder.end_group :string
        state = :initial
      else
        raise "else case \" reached; %p not handled." % peek(1), encoder
      end
      
    else
      raise 'else-case reached', encoder
      
    end
    
  end
  
  if state == :string
    encoder.end_group state
  end
  
  encoder
  
end