| Class | CodeRay::Scanners::SQL |
| In: |
lib/coderay/scanners/sql.rb
|
| Parent: | Scanner |
by Josh Goebel
| 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 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 |
# File lib/coderay/scanners/sql.rb, line 59
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