vorax 0.4.2 → 5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/README.md +4 -29
  2. data/vorax.gemspec +3 -11
  3. metadata +4 -92
  4. data/.rspec +0 -1
  5. data/Rakefile +0 -30
  6. data/lib/vorax.rb +0 -60
  7. data/lib/vorax/base_funnel.rb +0 -30
  8. data/lib/vorax/output/html_convertor.rb +0 -120
  9. data/lib/vorax/output/html_funnel.rb +0 -79
  10. data/lib/vorax/output/pagezip_convertor.rb +0 -20
  11. data/lib/vorax/output/tablezip_convertor.rb +0 -22
  12. data/lib/vorax/output/vertical_convertor.rb +0 -53
  13. data/lib/vorax/output/zip_convertor.rb +0 -117
  14. data/lib/vorax/parser/argument.rb~ +0 -125
  15. data/lib/vorax/parser/conn_string.rb +0 -104
  16. data/lib/vorax/parser/grammars/alias.rb +0 -904
  17. data/lib/vorax/parser/grammars/alias.rl +0 -138
  18. data/lib/vorax/parser/grammars/column.rb +0 -454
  19. data/lib/vorax/parser/grammars/column.rl +0 -64
  20. data/lib/vorax/parser/grammars/common.rl +0 -107
  21. data/lib/vorax/parser/grammars/declare.rb +0 -9606
  22. data/lib/vorax/parser/grammars/declare.rl +0 -160
  23. data/lib/vorax/parser/grammars/for_block.rb +0 -440
  24. data/lib/vorax/parser/grammars/for_block.rl +0 -73
  25. data/lib/vorax/parser/grammars/plsql_def.rb +0 -539
  26. data/lib/vorax/parser/grammars/plsql_def.rl +0 -73
  27. data/lib/vorax/parser/grammars/statement.rb +0 -925
  28. data/lib/vorax/parser/grammars/statement.rl +0 -83
  29. data/lib/vorax/parser/parser.rb +0 -344
  30. data/lib/vorax/parser/plsql_structure.rb +0 -222
  31. data/lib/vorax/parser/plsql_walker.rb +0 -143
  32. data/lib/vorax/parser/statement_inspector.rb~ +0 -52
  33. data/lib/vorax/parser/stmt_inspector.rb +0 -78
  34. data/lib/vorax/parser/target_ref.rb +0 -110
  35. data/lib/vorax/sqlplus.rb +0 -273
  36. data/lib/vorax/version.rb +0 -7
  37. data/lib/vorax/vorax_io.rb +0 -70
  38. data/spec/column_spec.rb +0 -40
  39. data/spec/conn_string_spec.rb +0 -53
  40. data/spec/declare_spec.rb +0 -281
  41. data/spec/pagezip_spec.rb +0 -153
  42. data/spec/parser_spec.rb +0 -352
  43. data/spec/plsql_structure_spec.rb +0 -68
  44. data/spec/spec_helper.rb +0 -13
  45. data/spec/sql/create_objects.sql +0 -69
  46. data/spec/sql/dbms_crypto.spc +0 -339
  47. data/spec/sql/dbms_stats.spc +0 -4097
  48. data/spec/sql/drop_user.sql +0 -10
  49. data/spec/sql/muci.spc +0 -26
  50. data/spec/sql/setup_user.sql +0 -22
  51. data/spec/sql/test.fnc +0 -12
  52. data/spec/sql/test.pkg +0 -83
  53. data/spec/sqlplus_spec.rb +0 -52
  54. data/spec/stmt_inspector_spec.rb +0 -96
  55. data/spec/tablezip_spec.rb +0 -111
  56. data/spec/vertical_spec.rb +0 -150
@@ -1,83 +0,0 @@
1
- %%{
2
-
3
- machine statement;
4
-
5
- action parse_start {
6
- eof = pe
7
- }
8
-
9
- action parse_error {
10
- }
11
-
12
- action mark_as_anonymous {
13
- stmt_type = 'ANONYMOUS'
14
- }
15
-
16
- action mark_as_sqlplus_command {
17
- stmt_type = 'SQLPLUS'
18
- }
19
-
20
- action mark_as_sql {
21
- stmt_type = nil
22
- }
23
-
24
- action mark_type {
25
- tail = data[(0...p)]
26
- type = tail[/\w+\Z/]
27
- stmt_type = type.upcase if type
28
- }
29
-
30
- action mark_body {
31
- stmt_type << ' BODY'
32
- }
33
-
34
- include common "common.rl";
35
-
36
- # parsing rules baby
37
- anonymous_block = ((K_BEGIN | K_DECLARE) ws+) @mark_as_anonymous;
38
- simple_module = (K_TRIGGER | K_FUNCTION | K_PROCEDURE) %mark_type;
39
- package_module = (K_PACKAGE %mark_type) (ws+ K_BODY %mark_body)?;
40
- type_module = (K_TYPE %mark_type) (ws+ K_BODY %mark_body)?;
41
- java_module = ((K_AND ws+ (K_RESOLVE | K_COMPILE) ws+ K_NOFORCE ws+) |
42
- (K_AND ws+ (K_RESOLVE | K_COMPILE) ws+) |
43
- (K_NOFORCE ws+))? (K_JAVA %mark_type);
44
- plsql_module = K_CREATE ws+ (K_OR ws+ K_REPLACE ws+)?
45
- (simple_module |
46
- package_module |
47
- type_module |
48
- java_module) ws+;
49
- set_transaction = (K_SET ws+ K_TRANSACTION ws+) @ mark_as_sql;
50
- sqlplus_command = (((K_ACCEPT | K_ARCHIVE | K_ATTRIBUTE | K_BREAK | K_BTITLE | K_CLEAR | K_COLUMN |
51
- K_COMPUTE | K_CONNECT | K_COPY | K_DEFINE | K_DESCRIBE | K_DISCONNECT | K_EXECUTE |
52
- K_EXIT | K_HELP | K_HOST | K_PASSWORD | K_PAUSE | K_PRINT | K_PROMPT |
53
- K_RECOVER | K_REMARK | K_REPFOOTER | K_REPHEADER | K_RUN | K_SAVE | K_SET | K_SHOW | K_SHUTDOWN |
54
- K_SPOOL | K_START | K_STARTUP | K_STORE | K_TIMING | K_TITLE | K_UNDEFINE | K_VARIABLE | K_WHENEVER |
55
- K_XQUERY) ws+) | ('@@' | '@' | '/' | '!')) @ mark_as_sqlplus_command;
56
-
57
- main := ws* (anonymous_block | set_transaction | sqlplus_command | plsql_module) >parse_start $err(parse_error);
58
-
59
- }%%
60
-
61
- module Vorax
62
-
63
- module Parser
64
-
65
- # Gets the type of the provided statement.
66
- #
67
- # @param data [String] the statement
68
- # @return [String] 'SQLPLUS' for an sqlplus statement, 'FUNCTION|PROCEDURE|PACKAGE|TYPE...' for
69
- # a PL/SQL block, 'ANONYMOUS' for an anonymous plsql block
70
- def self.statement_type(data)
71
- stmt_type = nil
72
- data << "\n"
73
- %% write data;
74
- %% write init;
75
- %% write exec;
76
- data.chop!
77
- return stmt_type
78
- end
79
-
80
- end
81
-
82
- end
83
-
@@ -1,344 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module Vorax
4
-
5
- # Provides parsing utilities.
6
- module Parser
7
-
8
- END_LINE = /\r\n?|\n/ unless defined?(END_LINE)
9
- SQLPLUS_TERMINATOR = END_LINE unless defined?(SQLPLUS_TERMINATOR)
10
- SEMI_COLON_TERMINATOR = /;/ unless defined?(SEMI_COLON_TERMINATOR)
11
- SLASH_TERMINATOR = Regexp.new('(?:' + END_LINE.to_s + '\s*\/[ \t]*' + END_LINE.to_s + ')') unless defined?(SLASH_TERMINATOR)
12
- PLSQL_SPEC = /(?:\bpackage\b|\btype\b)/i
13
- SUBPROG = /(?:\bfunction\b|\bprocedure\b)/i
14
- BEGIN_MODULE = /(?:\bbegin\b)/i
15
- END_MODULE = /(?:\bend\b)/i
16
-
17
- # Given an expression with parenthesis, it is walking it so that to
18
- # keep track of the open/close paren, in a balanced way.
19
- #
20
- # @param text [String] the string to be walked
21
- # @return [String] the paren expression
22
- def self.walk_balanced_paren(text)
23
- walker = PLSQLWalker.new(text)
24
- level = 0
25
- start_pos = 0
26
- end_pos = 0
27
- walker.register_spot(/[(]/) do |scanner|
28
- start_pos = scanner.pos - 1 if level == 0
29
- level += 1
30
- end
31
- walker.register_spot(/[)]/) do |scanner|
32
- level -= 1
33
- if level <= 0
34
- end_pos = scanner.pos
35
- scanner.terminate
36
- end
37
- end
38
- walker.walk
39
- text[start_pos, end_pos]
40
- end
41
-
42
- # Remove all comments from the provided statement. Pay attention that every
43
- # comment is replaced by a blank in order to cover the case where a comment is
44
- # used as a whitespace (e.g. select * from/*comment*/dual).
45
- #
46
- # @param statement [String] the statement to be cleaned up of comments
47
- # @return [String] the statement without any comment
48
- def self.remove_all_comments(statement)
49
- comment_areas = []
50
- result = statement
51
- walker = PLSQLWalker.new(statement, false)
52
-
53
- callback = lambda do |scanner, end_pattern|
54
- start_pos = scanner.pos - scanner.matched.length
55
- text = scanner.scan_until(end_pattern)
56
- if text
57
- comment_areas << (start_pos..scanner.pos - 1)
58
- else
59
- scanner.terminate
60
- end
61
- end
62
-
63
- walker.register_spot(PLSQLWalker::BEGIN_ML_COMMENT) do |scanner|
64
- callback.call(scanner, PLSQLWalker::END_ML_COMMENT)
65
- end
66
-
67
- walker.register_spot(PLSQLWalker::BEGIN_SL_COMMENT) do |scanner|
68
- callback.call(scanner, PLSQLWalker::END_SL_COMMENT)
69
- end
70
-
71
- walker.register_default_plsql_quoting_spot()
72
- walker.register_default_double_quoting_spot()
73
- walker.register_default_single_quoting_spot()
74
- walker.walk
75
- offset = 0
76
- comment_areas.each do |interval|
77
- r = (interval.min - offset .. interval.max - offset)
78
- result[r] = " "
79
- offset += (interval.max - interval.min)
80
- end
81
- result
82
- end
83
-
84
- # Remove the trailing comments from the provided statement.
85
- #
86
- # @param statement [String] the statement to be cleaned up
87
- # @return the statement without the trailing comments.
88
- def self.remove_trailing_comments(statement)
89
- stmt = statement
90
- begin
91
- stmt.gsub!(/(?:--[^\n]*\s*\z)|(?:\/\*.*?\*\/\s*\z)/m, '')
92
- end while !$~.nil?
93
- stmt
94
- end
95
-
96
- # Get the function/procedure to which the argument on the
97
- # provided position belongs.
98
- #
99
- # @param statement [String] the statement to be parsed
100
- # @param position [int] the position index where the
101
- # argument should be given
102
- def self.argument_belongs_to(statement, position = nil)
103
- position = statement.length unless position
104
- stmt = Parser.remove_all_comments(statement[(0...position)])
105
- stmt.reverse!
106
- level = 0
107
- walker = PLSQLWalker.new(stmt, false)
108
- arg_owner = ""
109
-
110
- squote_fallback = lambda do |scanner|
111
- scanner.skip_until(PLSQLWalker::BEGIN_SINGLE_QUOTING)
112
- if scanner.matched == "'"
113
- begin
114
- scanner.skip_until(/\'+/)
115
- end while (scanner.matched != "'" && !scanner.eos?)
116
- end
117
- end
118
-
119
- extract_module = lambda do |scanner|
120
- module_name = ""
121
- while !scanner.eos?
122
- # consume leading whitspaces
123
- scanner.scan(/\s*/)
124
- if scanner.check(/"/) == '"'
125
- # we have a quoted identifier
126
- module_name << scanner.scan(/"/)
127
- module_name << scanner.scan_until(/"/)
128
- else
129
- # unquoted identifier
130
- module_name << scanner.scan(/\S+/)
131
- end
132
- # consume trailing whitespaces
133
- scanner.scan(/\s*/)
134
-
135
- # might be a dblink
136
- if scanner.check(/@/) == '@'
137
- module_name << scanner.scan(/@/)
138
- next
139
- end
140
-
141
- # might be package or a schema
142
- if scanner.check(/\./) == '.'
143
- module_name << scanner.scan(/\./)
144
- next
145
- end
146
- scanner.terminate
147
- end
148
- module_name.reverse!
149
- end
150
-
151
- walker.register_spot(/'[\]})>]/) do |scanner|
152
- # pay attention, it's reveresed
153
- if scanner.matched =~ /\'\]/
154
- squote_fallback.call(scanner) unless scanner.skip_until(/\[\'q/)
155
- elsif scanner.matched =~ /\'[}]/
156
- squote_fallback.call(scanner) unless scanner.skip_until(/[{]\'q/)
157
- elsif scanner.matched =~ /\'[)]/
158
- squote_fallback.call(scanner) unless scanner.skip_until(/[(]\'q/)
159
- elsif scanner.matched =~ /\'[>]/
160
- squote_fallback.call(scanner) unless scanner.skip_until(/[<]\'q/)
161
- end
162
- end
163
-
164
- walker.register_spot(/[)]/) do |scanner|
165
- level += 1
166
- end
167
-
168
- walker.register_spot(/[(]/) do |scanner|
169
- if level == 0
170
- arg_owner = extract_module.call(scanner)
171
- else
172
- level -= 1
173
- scanner.terminate if level < 0 #give up, it's an invalid statement
174
- end
175
- end
176
-
177
- walker.walk
178
- return arg_owner
179
- end
180
-
181
- # Given the html output of a script, it extracts all tables into a nice
182
- # ruby array. This method returns a hash with the following meaning:
183
- # :resultset => an array with resultsets from all queries which
184
- # generated the <html> output. For example, if the
185
- # html parameter contains the output of two valid
186
- # queries, the :resultset will contain:
187
- #
188
- # [ # an array with all result sets
189
- # [ # the resultset of the first query
190
- # [val11, val12],
191
- # [val21, val22],
192
- # ...
193
- # [valn1, valn2]
194
- # ],
195
- # [ # the result set of the second query
196
- # [v11, v12, v13],
197
- # [v21, v22, v23],
198
- # ...
199
- # [vn1, vn2, vn3]
200
- # ]
201
- # ]
202
- # If errors are detected into the output they are extracted into the
203
- # :errors attribute of the returining hash.
204
- #
205
- # @param html [String] the html to be parsed
206
- # @return a hash with the parsed content
207
- def self.query_result(html)
208
- nbsp = Nokogiri::HTML("&nbsp;").text
209
- hash = {:resultset => [], :errors => []}
210
- doc = Nokogiri::HTML(html)
211
- hash[:errors] = doc.xpath('/html/body/text()').map{ |n| n.text }.grep(/\nORA-[0-9]+/)
212
- doc.xpath('//table').each do |table|
213
- resultset = []
214
- table.xpath('tr').each do |tr|
215
- row = []
216
- # replace nbsp with a plain blank in order to not confuse
217
- # the ragel parser, in case it's used
218
- tr.xpath('td').each { |td| row << td.text.strip.gsub(nbsp, " ") }
219
- resultset << row unless row.empty?
220
- end
221
- hash[:resultset] << resultset
222
- end
223
- return hash
224
- end
225
-
226
- # Prepare the provided statement for sqlplus execution. The prepare phase consists in
227
- # adding the right end separator according to the statement type.
228
- #
229
- # @param statement [String] the statement to be prepared
230
- # @return [String] the statement with the proper end separator appended
231
- def self.prepare_exec(statement)
232
- stmt = Parser.remove_trailing_comments(statement)
233
- type = Parser.statement_type(stmt)
234
- if type == 'SQLPLUS'
235
- # do nothing
236
- elsif !type.nil?
237
- # a plsql block. We need a trailing /
238
- stmt = "#{stmt.strip}\n/\n" if stmt !~ /\n\s*\/\s*\z/
239
- else
240
- # normal statement. It should have a trailing ;
241
- stmt = "#{stmt.strip};" if stmt !~ /;\s*\z/
242
- end
243
- return stmt
244
- end
245
-
246
- # Get the current statement for the provided position.
247
- #
248
- # @param script_content [String] the script within which the current statement must
249
- # be detected
250
- # @param position [int] the absolute position within the script content for which
251
- # the current statement must be found out
252
- # @param params [Hash] additional options. The following parameters may be
253
- # provided:
254
- #
255
- # :plsql_blocks => whenever or not to consider PL/SQL blocks when the current
256
- # statement is detected. By default is true.
257
- # :sqlplus_commands => whenever or not to consider SQLPLUS commands when
258
- # trying to detect the current statement
259
- #
260
- # @return [Hash] a hash with the following keys: :statement => the current statement
261
- # which corresponds to the provided position, :range => the statement boundaries
262
- # within the whole script
263
- def self.current_statement(script_content, position=0, params = {})
264
- opts = {
265
- :plsql_blocks => true,
266
- :sqlplus_commands => true
267
- }.merge(params)
268
- start_pos = 0
269
- end_pos = 0
270
-
271
- walker = PLSQLWalker.new(script_content)
272
-
273
- walker.register_spot(Parser::SEMI_COLON_TERMINATOR) do |scanner|
274
- type = Parser.statement_type(scanner.string[(start_pos..scanner.pos)])
275
- if type
276
- if opts[:plsql_blocks] && type != 'SQLPLUS'
277
- #this is a plsql block, eat till the slash terminator
278
- unless scanner.scan_until(Parser::SLASH_TERMINATOR)
279
- #it's an invalid statement
280
- scanner.terminate
281
- end
282
- end
283
- end
284
- if (start_pos..scanner.pos).include?(position)
285
- # include the terminator
286
- end_pos = scanner.pos
287
- scanner.terminate
288
- else
289
- start_pos = scanner.pos
290
- end
291
- end
292
-
293
- walker.register_spot(Parser::SLASH_TERMINATOR) do |scanner|
294
- if (start_pos..scanner.pos).include?(position)
295
- # include the terminator
296
- end_pos = scanner.pos
297
- scanner.terminate
298
- else
299
- start_pos = scanner.pos
300
- end
301
- end
302
-
303
- if opts[:sqlplus_commands]
304
- walker.register_spot(Parser::SQLPLUS_TERMINATOR) do |scanner|
305
- type = Parser.statement_type(scanner.string[(start_pos..scanner.pos)])
306
- if type
307
- if type == 'SQLPLUS'
308
- if (start_pos..scanner.pos-1).include?(position)
309
- end_pos = scanner.pos - scanner.matched.length
310
- scanner.terminate
311
- else
312
- start_pos = scanner.pos
313
- end
314
- else
315
- if opts[:plsql_blocks]
316
- #this is a plsql block, eat till the slash terminator
317
- if scanner.scan_until(Parser::SLASH_TERMINATOR)
318
- if (start_pos..scanner.pos-1).include?(position)
319
- end_pos = scanner.pos
320
- scanner.terminate
321
- else
322
- start_pos = scanner.pos
323
- end
324
- else
325
- #it's an invalid statement
326
- scanner.terminate
327
- end
328
- end
329
- end
330
- #else
331
- #start_pos = scanner.pos
332
- end
333
- end
334
- end
335
-
336
- walker.walk
337
- end_pos = script_content.length if end_pos == 0 #partial statement
338
- {:statement => script_content[(start_pos...end_pos)], :range => (start_pos...end_pos)}
339
-
340
- end
341
-
342
- end
343
-
344
- end
@@ -1,222 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'tree'
4
-
5
- module Vorax
6
-
7
- module Parser
8
-
9
- class Region
10
-
11
- attr_accessor :start_pos, :end_pos, :body_start_pos, :context
12
- attr_reader :name, :type
13
-
14
- def initialize(name, type, start_pos = 0, end_pos = 0)
15
- @name = name
16
- @type = type
17
- @start_pos = start_pos
18
- @end_pos = end_pos
19
- @body_start_pos = 0
20
- @context = nil
21
- end
22
-
23
- def to_s
24
- "#{@name}[#{@type}]: #{@start_pos}"
25
- end
26
-
27
- end
28
-
29
- class PlsqlStructure
30
-
31
- PLSQL_SPEC = /(?:\bpackage\b|\btype\b)/i unless defined?(PLSQL_SPEC)
32
- SUBPROG = /(?:\bfunction\b|\bprocedure\b)/i unless defined?(SUBPROG)
33
- BEGIN_MODULE = /(?:\bbegin\b)/i unless defined?(BEGIN_MODULE)
34
- END_MODULE = /(?:\bend\b)/i unless defined?(END_MODULE)
35
- FOR_STMT = /(?:\bfor\b)/i unless defined?(FOR_STMT)
36
- LOOP_STMT = /(?:\bloop\b)/i unless defined?(LOOP_STMT)
37
- IF_STMT = /(?:\bif\b)/i unless defined?(IF_STMT)
38
-
39
- attr_reader :text
40
-
41
- def initialize(text)
42
- @text = text
43
- @root = Tree::TreeNode.new("root", nil)
44
- @walker = PLSQLWalker.new(text)
45
- @level = 0
46
- @current_parent = @root
47
- @begin_level = 0
48
- register_spots()
49
- @walker.walk
50
- rescue Exception => e
51
- # be prepare for any nasting parse error.
52
- # Failing here is kind of usual, having in mind
53
- # that we often parse incomplete code.
54
- Vorax.debug(e.to_s)
55
- end
56
-
57
- def tree
58
- #@root.each { |t| t.content.end_pos = @text.length if t.content && t.content.end_pos == 0 }
59
- @root
60
- end
61
-
62
- private
63
-
64
- def assign_parent(node)
65
- @current_parent = node
66
- end
67
-
68
- def register_spots
69
- register_plsql_spec_spot()
70
- register_slash_terminator_spot()
71
- register_subprog_spot()
72
- register_begin_spot()
73
- register_for_spot()
74
- register_loop_spot()
75
- register_if_spot()
76
- register_end_spot()
77
- end
78
-
79
- def register_plsql_spec_spot
80
- @walker.register_spot(PLSQL_SPEC) do |scanner|
81
- if @level == 0
82
- meta_data = Parser.plsql_def("#{scanner.matched}#{scanner.rest}")
83
- if meta_data[:type] == 'SPEC' || meta_data[:type] == 'BODY'
84
- # is it a spec or a body?
85
- region = Region.new(meta_data[:name], meta_data[:type], scanner.pos)
86
- assign_parent(@current_parent << Tree::TreeNode.new(region.to_s, region))
87
- @level += 1
88
- end
89
- end
90
- end
91
- end
92
-
93
- def register_slash_terminator_spot
94
- @walker.register_spot(Parser::SLASH_TERMINATOR) do |scanner|
95
- # this should apply to the last top level node
96
- if @root.has_children?
97
- if @root.children.last.content
98
- @root.children.last.content.end_pos = scanner.pos
99
- end
100
- assign_parent(@root)
101
- @level = 0
102
- end
103
- end
104
- end
105
-
106
- def register_subprog_spot
107
- @walker.register_spot(SUBPROG) do |scanner|
108
- subprog_name = scanner.peek(32)[/(?:"[^"]+")|(?:[A-Z0-9$_#]+)/i]
109
- if scanner.matched =~ /function/i
110
- subprog_type = 'FUNCTION'
111
- elsif scanner.matched =~ /procedure/i
112
- subprog_type = 'PROCEDURE'
113
- end
114
- start_pos = scanner.pos - scanner.matched.length
115
- region = Region.new(subprog_name, subprog_type, start_pos)
116
- node = Tree::TreeNode.new(region.to_s, region)
117
- @current_parent << node
118
- if @current_parent && @current_parent.content && @current_parent.content.type == 'SPEC'
119
- node.content.end_pos = node.content.start_pos
120
- else
121
- @level += 1
122
- assign_parent(node)
123
- end
124
- end
125
- end
126
-
127
- def register_begin_spot
128
- @walker.register_spot(BEGIN_MODULE) do |scanner|
129
- @begin_level += 1
130
- if @begin_level > 1
131
- # start a new region
132
- region = Region.new('anonymous', 'BLOCK', scanner.pos)
133
- region.body_start_pos = scanner.pos - scanner.matched.length
134
- @level += 1
135
- assign_parent(@current_parent << Tree::TreeNode.new(region.to_s, region))
136
- else
137
- if @current_parent && @current_parent.content
138
- @current_parent.content.body_start_pos = scanner.pos - scanner.matched.length
139
- end
140
- end
141
- end
142
- end
143
-
144
- def register_for_spot
145
- @walker.register_spot(FOR_STMT) do |scanner|
146
- stmt = "#{scanner.matched}#{scanner.rest}"
147
- handler = Parser.describe_for(stmt)
148
- if handler[:end_pos] > 0
149
- region = Region.new('for', 'FOR_BLOCK', scanner.pos - scanner.matched.length + 1)
150
- region.body_start_pos = region.start_pos + handler[:end_pos]
151
- region.context = handler
152
- assign_parent(@current_parent << Tree::TreeNode.new(region.to_s, region))
153
- scanner.pos = region.body_start_pos
154
- @level += 1
155
- end
156
- end
157
- end
158
-
159
- def register_loop_spot
160
- @walker.register_spot(LOOP_STMT) do |scanner|
161
- stmt = "#{scanner.matched}#{scanner.rest}"
162
- region = Region.new('loop', 'LOOP_BLOCK', scanner.pos - scanner.matched.length + 1)
163
- region.body_start_pos = region.start_pos + 1
164
- assign_parent(@current_parent << Tree::TreeNode.new(region.to_s, region))
165
- @level += 1
166
- end
167
- end
168
-
169
- def register_if_spot
170
- @walker.register_spot(IF_STMT) do |scanner|
171
- stmt = "#{scanner.matched}#{scanner.rest}"
172
- region = Region.new('if', 'IF_BLOCK', scanner.pos - scanner.matched.length + 1)
173
- region.body_start_pos = region.start_pos + 1
174
- assign_parent(@current_parent << Tree::TreeNode.new(region.to_s, region))
175
- @level += 1
176
- end
177
- end
178
-
179
- def register_end_spot
180
- @walker.register_spot(END_MODULE) do |scanner|
181
- # we have an "end" match. first of all check if it's not part
182
- # of an conditional compiling "$end" definition
183
- char_behind = scanner.string[scanner.pos - scanner.matched.length - 1, 1]
184
- if char_behind != '$'
185
- metadata = Parser.plsql_def("#{scanner.matched}#{scanner.rest}")
186
- if metadata[:end_def] > 0
187
- @level -= 1 if @level > 0
188
- if metadata[:type] == 'END'
189
- @begin_level -= 1 if @begin_level > 0
190
- if @current_parent.content
191
- @current_parent.content.end_pos = (scanner.pos - 1) + (metadata[:end_def] - 1)
192
- end
193
- assign_parent(@current_parent.parent)
194
- elsif metadata[:type] == 'END_LOOP'
195
- if @current_parent.content && (@current_parent.content.type == 'FOR_BLOCK' || @current_parent.content.type == "LOOP_BLOCK")
196
- @current_parent.content.end_pos = (scanner.pos - 1) + (metadata[:end_def] - 1)
197
- scanner.pos = @current_parent.content.end_pos
198
- assign_parent(@current_parent.parent)
199
- else
200
- # something's fishy
201
- scanner.terminate
202
- end
203
- elsif metadata[:type] == 'END_IF'
204
- if @current_parent.content && @current_parent.content.type == 'IF_BLOCK'
205
- @current_parent.content.end_pos = (scanner.pos - 1) + (metadata[:end_def] - 1)
206
- scanner.pos = @current_parent.content.end_pos
207
- assign_parent(@current_parent.parent)
208
- else
209
- # something's fishy
210
- scanner.terminate
211
- end
212
- end
213
- end
214
- end
215
- end
216
- end
217
-
218
- end
219
-
220
- end
221
-
222
- end