vorax 0.4.2 → 5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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