| Class | CodeRay::Scanners::SQL |
| In: |
lib/coderay/scanners/sql.Keith.rb
lib/coderay/scanners/sql.rb |
| Parent: | Scanner |
by Josh Goebel
| RESERVED_WORDS | = | %w( all alter and any as asc at authid avg begin between body bulk by case char check close cluster coalesce collect comment commit compress connect constant create current currval cursor day declare default delete desc distinct do drop else elsif end exception exclusive execute exists exit extends extract fetch for forall from function goto group having heap hour if immediate in index indicator insert interface intersect interval into is isolation java level like limited lock loop max min minus minute mlslabel mod mode month natural naturaln new nextval nocopy not nowait null nullif number_base ocirowid of on opaque open operator option or order organization others out package partition pctfree pls_integer positive positiven pragma prior private procedure public raise range raw real record ref release return reverse rollback row rowid rownum rowtype savepoint second select separate set share space sql sqlcode sqlerrm start stddev subtype successful sum synonym sysdate table then timezone_region timezone_abbr timezone_minute to trigger true type uid union unique update use user validate values variance view when whenever where while with work write year zone ) |
| PREDEFINED_TYPES | = | %w( array bigint bit binary blob boolean binary_integer char character clob date decimal double float char_base int integer nchar nclob smallint timestamp long number timestamp_hour timestamp_minute varchar varying smallint varchar2 nvarchar money time ) |
| 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) |
| RESERVED_WORDS | = | %w( create 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 ) |
| 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 ) |
| 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 | = | / [rbfnrtv\n\\\/'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x |
| UNICODE_ESCAPE | = | / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x |
# File lib/coderay/scanners/sql.Keith.rb, line 53
53: def scan_tokens tokens, options
54:
55: state = :initial
56:
57: until eos?
58:
59: kind = nil
60: match = nil
61:
62: case state
63:
64: when :initial
65:
66: if scan(/ \s+ | \\\n /x)
67: kind = :space
68:
69: elsif scan(%r! -- [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .* ) !mx)
70: kind = :comment
71:
72: elsif scan(/ [-+*\/=<>?:;,!&^|()~%]+ | \.(?!\d) /x)
73: kind = :operator
74:
75: elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
76: kind = IDENT_KIND[match]
77: if kind == :ident and check(/:(?!:)/)
78: match << scan(/:/)
79: kind = :label
80: end
81:
82: elsif match = scan(/'/)
83: tokens << [:open, :string]
84: state = :string
85: kind = :delimiter
86:
87: elsif scan(/(?:\d+)(?![.eEfF])/)
88: kind = :integer
89:
90: elsif scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/)
91: kind = :float
92:
93: else
94: getch
95: kind = :error
96:
97: end
98:
99: when :string
100: if scan(/[^\\\n']+/)
101: kind = :content
102: elsif scan(/'/)
103: tokens << ["'", :delimiter]
104: tokens << [:close, :string]
105: state = :initial
106: next
107: elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
108: kind = :char
109: elsif scan(/ \\ | $ /x)
110: tokens << [:close, :string]
111: kind = :error
112: state = :initial
113: else
114: raise_inspect "else case \" reached; %p not handled." % peek(1), tokens
115: end
116:
117: else
118: raise_inspect 'Unknown state', tokens
119:
120: end
121:
122: match ||= matched
123: if $DEBUG and not kind
124: raise_inspect 'Error token %p in line %d' %
125: [[match, kind], line], tokens
126: end
127: raise_inspect 'Empty token', tokens unless match
128:
129: tokens << [match, kind]
130:
131: end
132:
133: if state == :string
134: tokens << [:close, :string]
135: end
136:
137: tokens
138: end
# File lib/coderay/scanners/sql.rb, line 41
41: def scan_tokens tokens, options
42:
43: state = :initial
44: string_type = nil
45: string_content = ''
46:
47: until eos?
48:
49: kind = nil
50: match = nil
51:
52: if state == :initial
53:
54: if scan(/ \s+ | \\\n /x)
55: kind = :space
56:
57: elsif scan(/^(?:--\s|#).*/)
58: kind = :comment
59:
60: elsif scan(%r! /\* (?: .*? \*/ | .* ) !mx)
61: kind = :comment
62:
63: elsif scan(/ [-+*\/=<>;,!&^|()\[\]{}~%] | \.(?!\d) /x)
64: kind = :operator
65:
66: elsif match = scan(/ [A-Za-z_][A-Za-z_0-9]* /x)
67: kind = IDENT_KIND[match.downcase]
68:
69: elsif match = scan(/[`"']/)
70: tokens << [:open, :string]
71: string_type = matched
72: state = :string
73: kind = :delimiter
74:
75: elsif scan(/0[xX][0-9A-Fa-f]+/)
76: kind = :hex
77:
78: elsif scan(/0[0-7]+(?![89.eEfF])/)
79: kind = :oct
80:
81: elsif scan(/\d+(?![.eEfF])/)
82: kind = :integer
83:
84: elsif scan(/\d[fF]?|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/)
85: kind = :float
86:
87: else
88: getch
89: kind = :error
90:
91: end
92:
93: elsif state == :string
94: if match = scan(/[^\\"'`]+/)
95: string_content << match
96: next
97: elsif match = scan(/["'`]/)
98: if string_type == match
99: if peek(1) == string_type # doubling means escape
100: string_content << string_type << getch
101: next
102: end
103: unless string_content.empty?
104: tokens << [string_content, :content]
105: string_content = ''
106: end
107: tokens << [matched, :delimiter]
108: tokens << [:close, :string]
109: state = :initial
110: string_type = nil
111: next
112: else
113: string_content << match
114: end
115: next
116: elsif scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
117: unless string_content.empty?
118: tokens << [string_content, :content]
119: string_content = ''
120: end
121: kind = :char
122: elsif match = scan(/ \\ . /mox)
123: string_content << match
124: next
125: elsif scan(/ \\ | $ /x)
126: unless string_content.empty?
127: tokens << [string_content, :content]
128: string_content = ''
129: end
130: kind = :error
131: state = :initial
132: else
133: raise "else case \" reached; %p not handled." % peek(1), tokens
134: end
135:
136: else
137: raise 'else-case reached', tokens
138:
139: end
140:
141: match ||= matched
142: # raise [match, kind], tokens if kind == :error
143:
144: tokens << [match, kind]
145:
146: end
147: # RAILS_DEFAULT_LOGGER.info tokens.inspect
148: tokens
149:
150: end