| Class | CodeRay::Scanners::SQL |
| In: |
lib/coderay/scanners/sql.rb
|
| Parent: | Scanner |
by Josh Goebel
| RESERVED_WORDS | = | %w( create database table index trigger drop primary key set select insert update delete replace into on from values before and or if exists case when then else as group order by avg where join inner outer union engine not like end using collate show columns begin ) |
| PREDEFINED_TYPES | = | %w( char varchar 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 bool boolean hex bin oct ) |
| PREDEFINED_FUNCTIONS | = | %w( sum cast abs pi count min max avg ) |
| DIRECTIVES | = | %w( auto_increment unique default charset ) |
| PREDEFINED_CONSTANTS | = | %w( null true false ) |
| IDENT_KIND | = | CaseIgnoringWordList.new(:ident). add(RESERVED_WORDS, :reserved). add(PREDEFINED_TYPES, :pre_type). add(PREDEFINED_CONSTANTS, :pre_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 |
# File lib/coderay/scanners/sql.rb, line 43
43: def scan_tokens tokens, options
44:
45: state = :initial
46: string_type = nil
47: string_content = ''
48:
49: until eos?
50:
51: kind = nil
52: match = nil
53:
54: if state == :initial
55:
56: if scan(/ \s+ | \\\n /x)
57: kind = :space
58:
59: elsif scan(/(?:--\s?|#).*/)
60: kind = :comment
61:
62: elsif scan(%r! /\* (?: .*? \*/ | .* ) !mx)
63: kind = :comment
64:
65: elsif scan(/ [-+*\/=<>;,!&^|()\[\]{}~%] | \.(?!\d) /x)
66: kind = :operator
67:
68: elsif scan(/(#{STRING_PREFIXES})?([`"'])/o)
69: prefix = self[1]
70: string_type = self[2]
71: tokens << [:open, :string]
72: tokens << [prefix, :modifier] if prefix
73: match = string_type
74: state = :string
75: kind = :delimiter
76:
77: elsif match = scan(/ @? [A-Za-z_][A-Za-z_0-9]* /x)
78: kind = match[0] == ?@ ? :variable : IDENT_KIND[match.downcase]
79:
80: elsif scan(/0[xX][0-9A-Fa-f]+/)
81: kind = :hex
82:
83: elsif scan(/0[0-7]+(?![89.eEfF])/)
84: kind = :oct
85:
86: elsif scan(/(?>\d+)(?![.eEfF])/)
87: kind = :integer
88:
89: elsif scan(/\d[fF]|\d*\.\d+(?:[eE][+-]?\d+)?|\d+[eE][+-]?\d+/)
90: kind = :float
91:
92: else
93: getch
94: kind = :error
95:
96: end
97:
98: elsif state == :string
99: if match = scan(/[^\\"'`]+/)
100: string_content << match
101: next
102: elsif match = scan(/["'`]/)
103: if string_type == match
104: if peek(1) == string_type # doubling means escape
105: string_content << string_type << getch
106: next
107: end
108: unless string_content.empty?
109: tokens << [string_content, :content]
110: string_content = ''
111: end
112: tokens << [matched, :delimiter]
113: tokens << [:close, :string]
114: state = :initial
115: string_type = nil
116: next
117: else
118: string_content << match
119: end
120: next
121: elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
122: unless string_content.empty?
123: tokens << [string_content, :content]
124: string_content = ''
125: end
126: kind = :char
127: elsif match = scan(/ \\ . /mox)
128: string_content << match
129: next
130: elsif scan(/ \\ | $ /x)
131: unless string_content.empty?
132: tokens << [string_content, :content]
133: string_content = ''
134: end
135: kind = :error
136: state = :initial
137: else
138: raise "else case \" reached; %p not handled." % peek(1), tokens
139: end
140:
141: else
142: raise 'else-case reached', tokens
143:
144: end
145:
146: match ||= matched
147: unless kind
148: raise_inspect 'Error token %p in line %d' %
149: [[match, kind], line], tokens, state
150: end
151: raise_inspect 'Empty token', tokens unless match
152:
153: tokens << [match, kind]
154:
155: end
156: tokens
157:
158: end