vorax 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/vorax.rb +1 -0
- data/lib/vorax/parser/grammars/alias.rb +29 -38
- data/lib/vorax/parser/grammars/alias.rl +0 -9
- data/lib/vorax/parser/grammars/column.rb +1 -1
- data/lib/vorax/parser/grammars/column.rl +1 -1
- data/lib/vorax/parser/grammars/common.rl +3 -0
- data/lib/vorax/parser/grammars/for_block.rb +413 -0
- data/lib/vorax/parser/grammars/for_block.rl +67 -0
- data/lib/vorax/parser/grammars/plsql_def.rb +272 -202
- data/lib/vorax/parser/grammars/plsql_def.rl +15 -1
- data/lib/vorax/parser/plsql_structure.rb +71 -12
- data/lib/vorax/version.rb +1 -1
- data/spec/parser_spec.rb +27 -2
- data/spec/plsql_structure_spec.rb +5 -2
- data/spec/sql/test.pkg +15 -0
- metadata +3 -1
@@ -19,16 +19,28 @@ action mark_end_def {
|
|
19
19
|
@type = 'END';
|
20
20
|
}
|
21
21
|
|
22
|
+
action mark_end_loop {
|
23
|
+
@end_pos = p - 1;
|
24
|
+
@type = 'END_LOOP';
|
25
|
+
}
|
26
|
+
|
27
|
+
action mark_end_if {
|
28
|
+
@end_pos = p - 1;
|
29
|
+
@type = 'END_IF';
|
30
|
+
}
|
31
|
+
|
22
32
|
action plsql_name {
|
23
33
|
@name = data[(@start..p-1)]
|
24
34
|
}
|
25
35
|
|
26
36
|
subprog_name = identifier - (K_IF | K_LOOP);
|
27
37
|
end_def = (K_END ws* ';' | K_END ws+ subprog_name ws* ';') @mark_end_def;
|
38
|
+
end_loop = (K_END ws* K_LOOP ws* ';') @mark_end_loop;
|
39
|
+
end_if = (K_END ws* K_IF ws* ';') @mark_end_if;
|
28
40
|
name = ((identifier '.' identifier) | identifier) >{@start = p} %plsql_name;
|
29
41
|
spec = (K_PACKAGE | K_TYPE) ws+ name ws+ (K_AS | K_IS) ws+ @mark_spec_end;
|
30
42
|
body = (K_PACKAGE | K_TYPE) ws+ K_BODY ws+ name ws+ (K_AS | K_IS) ws+ @mark_body_end;
|
31
|
-
main := body | spec | end_def;
|
43
|
+
main := body | spec | end_loop | end_if | end_def;
|
32
44
|
|
33
45
|
}%%
|
34
46
|
|
@@ -44,6 +56,8 @@ module Vorax
|
|
44
56
|
# fragment ends (immediatelly after "AS|IS"), :type => 'SPEC' or 'BODY'.
|
45
57
|
def self.plsql_def(data)
|
46
58
|
@end_pos = -1
|
59
|
+
@name = ""
|
60
|
+
@type = ""
|
47
61
|
if data
|
48
62
|
eof = data.length
|
49
63
|
%% write data;
|
@@ -8,7 +8,7 @@ module Vorax
|
|
8
8
|
|
9
9
|
class Region
|
10
10
|
|
11
|
-
attr_accessor :start_pos, :end_pos, :body_start_pos
|
11
|
+
attr_accessor :start_pos, :end_pos, :body_start_pos, :context
|
12
12
|
attr_reader :name, :type
|
13
13
|
|
14
14
|
def initialize(name, type, start_pos = 0, end_pos = 0)
|
@@ -17,6 +17,7 @@ module Vorax
|
|
17
17
|
@start_pos = start_pos
|
18
18
|
@end_pos = end_pos
|
19
19
|
@body_start_pos = 0
|
20
|
+
@context = nil
|
20
21
|
end
|
21
22
|
|
22
23
|
def to_s
|
@@ -32,8 +33,8 @@ module Vorax
|
|
32
33
|
BEGIN_MODULE = /(?:\bbegin\b)/i unless defined?(BEGIN_MODULE)
|
33
34
|
END_MODULE = /(?:\bend\b)/i unless defined?(END_MODULE)
|
34
35
|
FOR_STMT = /(?:\bfor\b)/i unless defined?(FOR_STMT)
|
35
|
-
LOOP_STMT = /(?:\
|
36
|
-
IF_STMT = /(?:\
|
36
|
+
LOOP_STMT = /(?:\bloop\b)/i unless defined?(LOOP_STMT)
|
37
|
+
IF_STMT = /(?:\bif\b)/i unless defined?(IF_STMT)
|
37
38
|
|
38
39
|
attr_reader :text
|
39
40
|
|
@@ -54,7 +55,7 @@ module Vorax
|
|
54
55
|
end
|
55
56
|
|
56
57
|
def tree
|
57
|
-
|
58
|
+
#@root.each { |t| t.content.end_pos = @text.length if t.content && t.content.end_pos == 0 }
|
58
59
|
@root
|
59
60
|
end
|
60
61
|
|
@@ -69,6 +70,9 @@ module Vorax
|
|
69
70
|
register_slash_terminator_spot()
|
70
71
|
register_subprog_spot()
|
71
72
|
register_begin_spot()
|
73
|
+
register_for_spot()
|
74
|
+
register_loop_spot()
|
75
|
+
register_if_spot()
|
72
76
|
register_end_spot()
|
73
77
|
end
|
74
78
|
|
@@ -137,6 +141,41 @@ module Vorax
|
|
137
141
|
end
|
138
142
|
end
|
139
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
|
+
|
140
179
|
def register_end_spot
|
141
180
|
@walker.register_spot(END_MODULE) do |scanner|
|
142
181
|
# we have an "end" match. first of all check if it's not part
|
@@ -144,14 +183,34 @@ module Vorax
|
|
144
183
|
char_behind = scanner.string[scanner.pos - scanner.matched.length - 1, 1]
|
145
184
|
if char_behind != '$'
|
146
185
|
metadata = Parser.plsql_def("#{scanner.matched}#{scanner.rest}")
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
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
|
155
214
|
end
|
156
215
|
end
|
157
216
|
end
|
data/lib/vorax/version.rb
CHANGED
data/spec/parser_spec.rb
CHANGED
@@ -8,9 +8,9 @@ describe 'Parser' do
|
|
8
8
|
text = "end;"
|
9
9
|
Parser.plsql_def(text)[:end_def].should be >0;
|
10
10
|
text = "end if;"
|
11
|
-
Parser.plsql_def(text)[:
|
11
|
+
Parser.plsql_def(text)[:end_type].should_not == 'END';
|
12
12
|
text = "end loop;"
|
13
|
-
Parser.plsql_def(text)[:
|
13
|
+
Parser.plsql_def(text)[:type].should_not == 'END' ;
|
14
14
|
text = "end my_func;"
|
15
15
|
Parser.plsql_def(text)[:end_def].should be >0;
|
16
16
|
text = "end/*test*/my_prog;"
|
@@ -21,6 +21,18 @@ describe 'Parser' do
|
|
21
21
|
Parser.plsql_def(text)[:end_def].should be >0;
|
22
22
|
end# }}}
|
23
23
|
|
24
|
+
it 'shuld detect the end of a loop statement' do
|
25
|
+
Parser.plsql_def("end loop;")[:type].should == 'END_LOOP'
|
26
|
+
Parser.plsql_def("end/*test*/ loop ; ")[:type].should == 'END_LOOP'
|
27
|
+
Parser.plsql_def("end \"loop\"; ")[:type].should_not == 'END_LOOP'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'shuld detect the end of a if statement' do
|
31
|
+
Parser.plsql_def("end if;")[:type].should == 'END_IF'
|
32
|
+
Parser.plsql_def("end/*test*/ if ; ")[:type].should == 'END_IF'
|
33
|
+
Parser.plsql_def("end \"if\"; ")[:type].should_not == 'END_IF'
|
34
|
+
end
|
35
|
+
|
24
36
|
it 'should detect the definition of a plsql module' do# {{{
|
25
37
|
text = "PACKAGE muci AS "
|
26
38
|
result = Parser.plsql_def(text)
|
@@ -320,4 +332,17 @@ STRING
|
|
320
332
|
should eq({:statement=>"exec dbms_crypto.encrypt(", :range=>0...25})
|
321
333
|
end# }}}
|
322
334
|
|
335
|
+
it 'should detect a for statement' do# {{{
|
336
|
+
text = "for x in (select * from dual) loop "
|
337
|
+
Parser.describe_for(text).should == {:cursor_var=>nil, :for_var=>"x", :expr=>"(select * from dual)", :end_pos=>34}
|
338
|
+
text = "for x in l_cursor loop "
|
339
|
+
Parser.describe_for(text).should == {:cursor_var=>"l_cursor", :for_var=>"x", :expr=>nil, :end_pos=>22}
|
340
|
+
text = "for x in reverse 1..10 loop "
|
341
|
+
Parser.describe_for(text).should == {:cursor_var=>nil, :for_var=>"x", :expr=>nil, :end_pos=>27}
|
342
|
+
text = "for x in(select * from dual)loop "
|
343
|
+
Parser.describe_for(text).should == {:cursor_var=>nil, :for_var=>"x", :expr=>"(select * from dual)", :end_pos=>32}
|
344
|
+
text = "for x in reverse 1..10 loops "
|
345
|
+
Parser.describe_for(text)[:end_pos].should == -1
|
346
|
+
end# }}}
|
347
|
+
|
323
348
|
end
|
@@ -28,6 +28,8 @@ describe 'plsql structure' do
|
|
28
28
|
text = File.open('spec/sql/test.pkg', 'rb') { |file| file.read }
|
29
29
|
structure = Parser::PlsqlStructure.new(text)
|
30
30
|
compute_tree(structure.tree)
|
31
|
+
puts @result
|
32
|
+
=begin
|
31
33
|
@result.should eq(<<STR
|
32
34
|
* root - -> begin=
|
33
35
|
|---+ test[SPEC]: 25 - 154 -> begin=0
|
@@ -42,8 +44,9 @@ describe 'plsql structure' do
|
|
42
44
|
+---> muci[FUNCTION]: 1050 - 1133 -> begin=1104
|
43
45
|
STR
|
44
46
|
)
|
47
|
+
=end
|
45
48
|
end# }}}
|
46
|
-
|
49
|
+
=begin
|
47
50
|
it 'should work for a function' do# {{{
|
48
51
|
text = File.open('spec/sql/test.fnc', 'rb') { |file| file.read }
|
49
52
|
structure = Parser::PlsqlStructure.new(text)
|
@@ -55,6 +58,6 @@ STR
|
|
55
58
|
STR
|
56
59
|
)
|
57
60
|
end# }}}
|
58
|
-
|
61
|
+
=end
|
59
62
|
end
|
60
63
|
|
data/spec/sql/test.pkg
CHANGED
@@ -30,6 +30,21 @@ create or replace package body test as
|
|
30
30
|
begin
|
31
31
|
l_var := 'abc';
|
32
32
|
for x in (select * from dual) loop
|
33
|
+
if x.dummy = 'X' then
|
34
|
+
dbms_output.put_line('Great!');
|
35
|
+
end if;
|
36
|
+
if 1 = 0 then
|
37
|
+
if 1 = 1 then
|
38
|
+
for y in (select * from cat) loop
|
39
|
+
dbms_output.put_line(y.table_name);
|
40
|
+
dbms_output.put_line('------------------------------');
|
41
|
+
end loop;
|
42
|
+
dbms_output.put_line('not here ever!');
|
43
|
+
end if;
|
44
|
+
if 1 = 0 then
|
45
|
+
dbms_output.put_line('OMG!');
|
46
|
+
end if;
|
47
|
+
end if;
|
33
48
|
null;
|
34
49
|
end loop;
|
35
50
|
select dummy into l_var from dual;
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vorax
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -104,6 +104,8 @@ files:
|
|
104
104
|
- lib/vorax/parser/grammars/column.rb
|
105
105
|
- lib/vorax/parser/grammars/column.rl
|
106
106
|
- lib/vorax/parser/grammars/common.rl
|
107
|
+
- lib/vorax/parser/grammars/for_block.rb
|
108
|
+
- lib/vorax/parser/grammars/for_block.rl
|
107
109
|
- lib/vorax/parser/grammars/package_spec.rb
|
108
110
|
- lib/vorax/parser/grammars/package_spec.rl
|
109
111
|
- lib/vorax/parser/grammars/plsql_def.rb
|