vorax 0.2.0 → 0.3.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.
- 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
|