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
         |