parsejs 0.0.1
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.
- checksums.yaml +7 -0
 - data/.gitignore +18 -0
 - data/.rspec +1 -0
 - data/.travis.yml +9 -0
 - data/Gemfile +5 -0
 - data/README.markdown +85 -0
 - data/Rakefile +15 -0
 - data/lib/parsejs.rb +14 -0
 - data/lib/parsejs/ast.rb +84 -0
 - data/lib/parsejs/docs.rb +237 -0
 - data/lib/parsejs/grammar.kpeg +575 -0
 - data/lib/parsejs/grammar.kpeg.rb +9460 -0
 - data/lib/parsejs/scope.rb +183 -0
 - data/lib/parsejs/stringifier.rb +450 -0
 - data/lib/parsejs/version.rb +3 -0
 - data/lib/parsejs/visitor.rb +257 -0
 - data/parsejs.gemspec +24 -0
 - data/spec/fixtures/jquery-1.6.js +8982 -0
 - data/spec/fixtures/jquery-1.7.js +9289 -0
 - data/spec/fixtures/jquery-ajax.js +1000 -0
 - data/spec/fixtures/jquery-attributes.js +650 -0
 - data/spec/fixtures/jquery-traversing.js +330 -0
 - data/spec/fixtures/metamorph.js +324 -0
 - data/spec/fixtures/sizzle.js +1442 -0
 - data/spec/fixtures/sproutcore-core.js +195 -0
 - data/spec/fixtures/sproutcore-each-proxy.js +218 -0
 - data/spec/fixtures/sproutcore-native-array.js +139 -0
 - data/spec/fixtures/sproutcore.js +14319 -0
 - data/spec/scope_spec.rb +111 -0
 - data/spec/stringify_spec.rb +587 -0
 - data/test.rb +76 -0
 - metadata +145 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA1:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 41b39d4f01ad6648dbb147d8cf758bb0b5cd45ff
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 3877eb40098d127ed7a2a036f2ceee54983e2c6b
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: e4fe5a4c22dea6bb05393054ea312a3eedf38bc76b083b24a73cc0b23736889fb9ca2a8019dd7e8a5cfd0cdd2cbd90d8a87264086746d7bc68f3bd16d13bcd1f
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: e56c48ca152263de721598472e4b03ac2145455193dd38e4d37dcee4fd66f79a402963e1cd90ec843142050e2e5d4731b937a477f7b972ad78a02fad7d910a49
         
     | 
    
        data/.gitignore
    ADDED
    
    
    
        data/.rspec
    ADDED
    
    | 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            -cfs
         
     | 
    
        data/.travis.yml
    ADDED
    
    
    
        data/Gemfile
    ADDED
    
    
    
        data/README.markdown
    ADDED
    
    | 
         @@ -0,0 +1,85 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # ParseJS
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            ParseJS is a JavaScript parser written in Ruby using the KPeg parser generator. It also
         
     | 
| 
      
 4 
     | 
    
         
            +
            takes a JavaScript parse tree and emits semantically identical JavaScript. The parser is
         
     | 
| 
      
 5 
     | 
    
         
            +
            tested by parsing large, popular JavaScript libraries (such as jQuery) and confirming that
         
     | 
| 
      
 6 
     | 
    
         
            +
            the minified output after a round-trip through ParseJS is the same as minifying the original
         
     | 
| 
      
 7 
     | 
    
         
            +
            source.
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            The ParseJS stringifier does not guarantee equivalent whitespace and comments in a
         
     | 
| 
      
 10 
     | 
    
         
            +
            round-trip, but it should guarantee semantic equivalence.
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            The ParseJS parser maintains comments in most cases where comments would represent inline
         
     | 
| 
      
 13 
     | 
    
         
            +
            documentation.
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            * top-level statement: a ParseJS top-level statement node contains any comment that
         
     | 
| 
      
 16 
     | 
    
         
            +
            	immediately preceded it.
         
     | 
| 
      
 17 
     | 
    
         
            +
            * property: in a property list (object literal), a property node contains any comment
         
     | 
| 
      
 18 
     | 
    
         
            +
            	that immediately preceded it.
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            There is a work-in-progress AST walker that associates comments with particular structures.
         
     | 
| 
      
 21 
     | 
    
         
            +
            The ultimate goal of this walker is to identify JavaScript structures that represent
         
     | 
| 
      
 22 
     | 
    
         
            +
            "classes" or similar structures and associate their comments with information extracted
         
     | 
| 
      
 23 
     | 
    
         
            +
            from the code.
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
            # Usage
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            ParseJS is provided as a Rubygem. At the moment, you can use it in your Gemfile by using
         
     | 
| 
      
 28 
     | 
    
         
            +
            Bundler's git feature.
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 31 
     | 
    
         
            +
            gem "parsejs", :git => "git://github.com/wycats/parsejs.git"
         
     | 
| 
      
 32 
     | 
    
         
            +
            ```
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            You can parse a String of JavaScript and receive an AST by using
         
     | 
| 
      
 35 
     | 
    
         
            +
            `ParseJS.parse`.
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 38 
     | 
    
         
            +
            ast = ParseJS.parse(some_data)
         
     | 
| 
      
 39 
     | 
    
         
            +
            ```
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
            You can convert the AST back into a JavaScript String using the
         
     | 
| 
      
 42 
     | 
    
         
            +
            stringifier. You can mutate the AST before converting it into a String
         
     | 
| 
      
 43 
     | 
    
         
            +
            if you wish.
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
            ```
         
     | 
| 
      
 46 
     | 
    
         
            +
            ParseJS::Stringifier.to_string(ast)
         
     | 
| 
      
 47 
     | 
    
         
            +
            ```
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            You can write your own AST walker without implementing visitors for all
         
     | 
| 
      
 50 
     | 
    
         
            +
            nodes by subclassing `ParseJS::Visitor`. Take a look
         
     | 
| 
      
 51 
     | 
    
         
            +
            [at that class](https://github.com/wycats/parsejs/blob/master/lib/parsejs/visitor.rb)
         
     | 
| 
      
 52 
     | 
    
         
            +
            to see the default visitor behavior for a particular node.
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
            By default, nodes `accept` their children. Nodes that have children that
         
     | 
| 
      
 55 
     | 
    
         
            +
            are `Array`s of nodes (e.g. `FunctionDeclaration`, which has an `Array` of
         
     | 
| 
      
 56 
     | 
    
         
            +
            parameters and an `Array` of statements as children) default to looping
         
     | 
| 
      
 57 
     | 
    
         
            +
            over the `Array`s and accepting their members.
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
            You can take a look at the in-progress
         
     | 
| 
      
 60 
     | 
    
         
            +
            [docs extractor](https://github.com/wycats/parsejs/blob/master/lib/parsejs/docs.rb)
         
     | 
| 
      
 61 
     | 
    
         
            +
            or the [stringifier](https://github.com/wycats/parsejs/blob/master/lib/parsejs/stringifier.rb)
         
     | 
| 
      
 62 
     | 
    
         
            +
            for examples of subclasses of `ParseJS::Visitor`.
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
            # LICENSE
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
            Copyright (C) 2012 Yehuda Katz
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy of
         
     | 
| 
      
 69 
     | 
    
         
            +
            this software and associated documentation files (the "Software"), to deal in
         
     | 
| 
      
 70 
     | 
    
         
            +
            the Software without restriction, including without limitation the rights to
         
     | 
| 
      
 71 
     | 
    
         
            +
            use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
         
     | 
| 
      
 72 
     | 
    
         
            +
            of the Software, and to permit persons to whom the Software is furnished to do
         
     | 
| 
      
 73 
     | 
    
         
            +
            so, subject to the following conditions:
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
            The above copyright notice and this permission notice shall be included in all
         
     | 
| 
      
 76 
     | 
    
         
            +
            copies or substantial portions of the Software.
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         
     | 
| 
      
 79 
     | 
    
         
            +
            IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         
     | 
| 
      
 80 
     | 
    
         
            +
            FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         
     | 
| 
      
 81 
     | 
    
         
            +
            AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         
     | 
| 
      
 82 
     | 
    
         
            +
            LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         
     | 
| 
      
 83 
     | 
    
         
            +
            OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         
     | 
| 
      
 84 
     | 
    
         
            +
            SOFTWARE.
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
    
        data/Rakefile
    ADDED
    
    | 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env rake
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "bundler/gem_tasks"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "bundler/setup"
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            file "lib/parsejs/grammar.kpeg.rb" => "lib/parsejs/grammar.kpeg" do
         
     | 
| 
      
 6 
     | 
    
         
            +
              sh "kpeg -f lib/parsejs/grammar.kpeg --stand-alone --debug"
         
     | 
| 
      
 7 
     | 
    
         
            +
            end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            task :compile => "lib/parsejs/grammar.kpeg.rb"
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            task :spec => :compile do
         
     | 
| 
      
 12 
     | 
    
         
            +
              sh "rspec -cfs spec"
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            task :default => :compile
         
     | 
    
        data/lib/parsejs.rb
    ADDED
    
    | 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "parsejs/version"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "parsejs/grammar.kpeg"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "parsejs/stringifier"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "parsejs/ast"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "parsejs/scope"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            module ParseJS
         
     | 
| 
      
 8 
     | 
    
         
            +
              def self.parse(string)
         
     | 
| 
      
 9 
     | 
    
         
            +
                string = string.force_encoding('BINARY') if string.respond_to?(:force_encoding)
         
     | 
| 
      
 10 
     | 
    
         
            +
                parser = ParseJS::Parser.new(string)
         
     | 
| 
      
 11 
     | 
    
         
            +
                parser.parse
         
     | 
| 
      
 12 
     | 
    
         
            +
                parser.result
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/parsejs/ast.rb
    ADDED
    
    | 
         @@ -0,0 +1,84 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "parsejs/scope"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module ParseJS
         
     | 
| 
      
 4 
     | 
    
         
            +
              module AST
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Node
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def type?(string)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    self.class.name.split("::").last == string
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def cuddly?
         
     | 
| 
      
 11 
     | 
    
         
            +
                    false
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def statement?
         
     | 
| 
      
 15 
     | 
    
         
            +
                    false
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def needs_newline?
         
     | 
| 
      
 19 
     | 
    
         
            +
                    false
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                class Comment
         
     | 
| 
      
 24 
     | 
    
         
            +
                  def multiline?
         
     | 
| 
      
 25 
     | 
    
         
            +
                    @type == 'multiline'
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                class SequenceExpression
         
     | 
| 
      
 30 
     | 
    
         
            +
                  attr_accessor :parens
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                class BlockStatement
         
     | 
| 
      
 34 
     | 
    
         
            +
                  attr_accessor :cuddly
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  def cuddle!
         
     | 
| 
      
 37 
     | 
    
         
            +
                    @cuddly = true
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def needs_newline?
         
     | 
| 
      
 41 
     | 
    
         
            +
                    !@cuddly
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  def cuddly?
         
     | 
| 
      
 45 
     | 
    
         
            +
                    true
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  def statement?
         
     | 
| 
      
 49 
     | 
    
         
            +
                    true
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                class FunctionExpression
         
     | 
| 
      
 54 
     | 
    
         
            +
                  include ParseJS::AST::Scope
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                class FunctionDeclaration
         
     | 
| 
      
 58 
     | 
    
         
            +
                  include ParseJS::AST::Scope
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                class Program
         
     | 
| 
      
 62 
     | 
    
         
            +
                  include ParseJS::AST::Scope
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                statements  = [VariableDeclaration, EmptyStatement, ExpressionStatement, IfStatement]
         
     | 
| 
      
 66 
     | 
    
         
            +
                statements += [WhileStatement, ForStatement, ForInStatement, DoWhileStatement]
         
     | 
| 
      
 67 
     | 
    
         
            +
                statements += [ContinueStatement, BreakStatement, ReturnStatement, WithStatement]
         
     | 
| 
      
 68 
     | 
    
         
            +
                statements += [LabeledStatement, SwitchStatement, ThrowStatement, TryStatement]
         
     | 
| 
      
 69 
     | 
    
         
            +
                statements += [DebuggerStatement, FunctionDeclaration]
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                statements.each do |statement|
         
     | 
| 
      
 72 
     | 
    
         
            +
                  statement.class_eval do
         
     | 
| 
      
 73 
     | 
    
         
            +
                    def needs_newline?
         
     | 
| 
      
 74 
     | 
    
         
            +
                      true
         
     | 
| 
      
 75 
     | 
    
         
            +
                    end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    def statement?
         
     | 
| 
      
 78 
     | 
    
         
            +
                      true
         
     | 
| 
      
 79 
     | 
    
         
            +
                    end
         
     | 
| 
      
 80 
     | 
    
         
            +
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
              end
         
     | 
| 
      
 83 
     | 
    
         
            +
            end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
    
        data/lib/parsejs/docs.rb
    ADDED
    
    | 
         @@ -0,0 +1,237 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "parsejs/visitor"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "yard"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module ParseJS
         
     | 
| 
      
 5 
     | 
    
         
            +
              class CommentScanner < Visitor
         
     | 
| 
      
 6 
     | 
    
         
            +
                include ParseJS::AST
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize(*)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @current_comment = nil
         
     | 
| 
      
 10 
     | 
    
         
            +
                  super
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def comment?
         
     | 
| 
      
 14 
     | 
    
         
            +
                  return false unless @current_comment
         
     | 
| 
      
 15 
     | 
    
         
            +
                  return false if @current_comment.empty?
         
     | 
| 
      
 16 
     | 
    
         
            +
                  return @current_comment.any?(&:multiline?)
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def stringify(node)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  Stringifier.to_string(Marshal.load(Marshal.dump(node)))
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def visit_AssignmentExpression(expr)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  process_comment(expr)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  super
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def visit_CommentedStatement(comment)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @current_comment = comment.comments
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  super
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def visit_Property(property)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @current_comment = property.comments
         
     | 
| 
      
 36 
     | 
    
         
            +
                  process_comment(property)
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  super
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def process_comment(node)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  print_comment(node)
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                def print_comment(node)
         
     | 
| 
      
 46 
     | 
    
         
            +
                  return unless comment?
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  puts
         
     | 
| 
      
 49 
     | 
    
         
            +
                  puts "-" * 10 + node.class.name + "-" * 10
         
     | 
| 
      
 50 
     | 
    
         
            +
                  puts @current_comment.map { |c| next unless c.multiline?; c.body }.join("\n")
         
     | 
| 
      
 51 
     | 
    
         
            +
                  puts "-" * (10 + node.class.name.size + 10)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  puts
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  puts Stringifier.to_string(Marshal.load(Marshal.dump(node)))
         
     | 
| 
      
 55 
     | 
    
         
            +
                  puts
         
     | 
| 
      
 56 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 57 
     | 
    
         
            +
                  @current_comment = nil
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                def strip_leading_whitespace(string)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  min = string.scan(/^[ \t]*(?=\S)/).min
         
     | 
| 
      
 62 
     | 
    
         
            +
                  size = min ? min.size : 0
         
     | 
| 
      
 63 
     | 
    
         
            +
                  string.gsub(/^[ \t]{#{size}}/, '')
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
              class ExtractDocs < CommentScanner
         
     | 
| 
      
 68 
     | 
    
         
            +
                include AST
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                def initialize(*)
         
     | 
| 
      
 71 
     | 
    
         
            +
                  @current_variables = []
         
     | 
| 
      
 72 
     | 
    
         
            +
                  @current_class = []
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def visit_Program(program)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  with_variables(program, []) { super }
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                def visit_FunctionDeclaration(decl)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  with_variables(decl, decl.params.list.map(&:val)) { super }
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                def visit_FunctionExpression(expr)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  with_variables(expr, expr.params.list.map(&:val)) { super }
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                def with_variables(expr, params=expr.params.map(&:val))
         
     | 
| 
      
 88 
     | 
    
         
            +
                  locals = FindVars.find_variables(expr)
         
     | 
| 
      
 89 
     | 
    
         
            +
                  @current_variables.push(locals | params)
         
     | 
| 
      
 90 
     | 
    
         
            +
                  yield
         
     | 
| 
      
 91 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 92 
     | 
    
         
            +
                  @current_variables.pop
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                def variable?(name)
         
     | 
| 
      
 96 
     | 
    
         
            +
                  @current_variables.find do |list|
         
     | 
| 
      
 97 
     | 
    
         
            +
                    list.include?(name)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                def member_left(expr)
         
     | 
| 
      
 102 
     | 
    
         
            +
                  object = expr
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                  while object
         
     | 
| 
      
 105 
     | 
    
         
            +
                    if object.is_a?(Identifier)
         
     | 
| 
      
 106 
     | 
    
         
            +
                      return object.val
         
     | 
| 
      
 107 
     | 
    
         
            +
                    elsif object.is_a?(ThisExpression)
         
     | 
| 
      
 108 
     | 
    
         
            +
                      return "this"
         
     | 
| 
      
 109 
     | 
    
         
            +
                    elsif object.respond_to?(:object)
         
     | 
| 
      
 110 
     | 
    
         
            +
                      object = object.object
         
     | 
| 
      
 111 
     | 
    
         
            +
                    else
         
     | 
| 
      
 112 
     | 
    
         
            +
                      return nil
         
     | 
| 
      
 113 
     | 
    
         
            +
                    end
         
     | 
| 
      
 114 
     | 
    
         
            +
                  end
         
     | 
| 
      
 115 
     | 
    
         
            +
                end
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                def visit_AssignmentExpression(expr)
         
     | 
| 
      
 118 
     | 
    
         
            +
                  right = expr.right
         
     | 
| 
      
 119 
     | 
    
         
            +
                  in_class = false
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                  if right.is_a?(CallExpression)
         
     | 
| 
      
 122 
     | 
    
         
            +
                    callee = expr.right.callee
         
     | 
| 
      
 123 
     | 
    
         
            +
                    if callee.is_a?(MemberExpression) && !callee.computed
         
     | 
| 
      
 124 
     | 
    
         
            +
                      property = callee.property.val
         
     | 
| 
      
 125 
     | 
    
         
            +
                      if property == "extend"
         
     | 
| 
      
 126 
     | 
    
         
            +
                        if expr.left.is_a?(MemberExpression)
         
     | 
| 
      
 127 
     | 
    
         
            +
                          left_id = member_left(expr.left)
         
     | 
| 
      
 128 
     | 
    
         
            +
                          extends = stringify(expr.right.callee).sub(/\.extend$/, '')
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                          if variable?(left_id)
         
     | 
| 
      
 131 
     | 
    
         
            +
                            # found private class
         
     | 
| 
      
 132 
     | 
    
         
            +
                          else
         
     | 
| 
      
 133 
     | 
    
         
            +
                            klass = stringify(expr.left)
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                            left_string = stringify(expr.left)
         
     | 
| 
      
 136 
     | 
    
         
            +
                            namespace_obj = build_namespace left_string.split(".")[0...-1].join("::")
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
                            class_name = left_string.split(".")[-1]
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                            obj = YARD::CodeObjects::ClassObject.new(namespace_obj, class_name)
         
     | 
| 
      
 141 
     | 
    
         
            +
                            obj.docstring = stripped_comment
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                            @current_class.push [klass, extends, obj]
         
     | 
| 
      
 144 
     | 
    
         
            +
                            in_class = true
         
     | 
| 
      
 145 
     | 
    
         
            +
                          end
         
     | 
| 
      
 146 
     | 
    
         
            +
                        end
         
     | 
| 
      
 147 
     | 
    
         
            +
                      end
         
     | 
| 
      
 148 
     | 
    
         
            +
                    end
         
     | 
| 
      
 149 
     | 
    
         
            +
                  end
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                  super
         
     | 
| 
      
 152 
     | 
    
         
            +
             
     | 
| 
      
 153 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 154 
     | 
    
         
            +
                  @current_class.pop if in_class
         
     | 
| 
      
 155 
     | 
    
         
            +
                end
         
     | 
| 
      
 156 
     | 
    
         
            +
             
     | 
| 
      
 157 
     | 
    
         
            +
                def build_namespace(namespace)
         
     | 
| 
      
 158 
     | 
    
         
            +
                  if namespace.empty?
         
     | 
| 
      
 159 
     | 
    
         
            +
                    YARD::Registry.root
         
     | 
| 
      
 160 
     | 
    
         
            +
                  elsif ns = YARD::Registry.at(namespace)
         
     | 
| 
      
 161 
     | 
    
         
            +
                    ns
         
     | 
| 
      
 162 
     | 
    
         
            +
                  else
         
     | 
| 
      
 163 
     | 
    
         
            +
                    name = namespace.gsub('::', '.')
         
     | 
| 
      
 164 
     | 
    
         
            +
                    YARD::CodeObjects::NamespaceObject.new(:root, name)
         
     | 
| 
      
 165 
     | 
    
         
            +
                  end
         
     | 
| 
      
 166 
     | 
    
         
            +
                end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                def current_class_name
         
     | 
| 
      
 169 
     | 
    
         
            +
                  klass, parent = @current_class.last
         
     | 
| 
      
 170 
     | 
    
         
            +
                  "#{klass} < #{parent}"
         
     | 
| 
      
 171 
     | 
    
         
            +
                end
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
                def current_yard_class
         
     | 
| 
      
 174 
     | 
    
         
            +
                  @current_class.last[2]
         
     | 
| 
      
 175 
     | 
    
         
            +
                end
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
                def visit_Property(property)
         
     | 
| 
      
 178 
     | 
    
         
            +
                  return if @current_class.empty?
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
                  @current_comment = property.comments
         
     | 
| 
      
 181 
     | 
    
         
            +
                  return if stripped_comment.empty?
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
                  case property.value
         
     | 
| 
      
 184 
     | 
    
         
            +
                  when FunctionDeclaration, FunctionExpression
         
     | 
| 
      
 185 
     | 
    
         
            +
                    obj = YARD::CodeObjects::MethodObject.new(current_yard_class, property.key.val)
         
     | 
| 
      
 186 
     | 
    
         
            +
                    obj.docstring = stripped_comment
         
     | 
| 
      
 187 
     | 
    
         
            +
                  else
         
     | 
| 
      
 188 
     | 
    
         
            +
                    # found a non-method property
         
     | 
| 
      
 189 
     | 
    
         
            +
                  end
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
                  super
         
     | 
| 
      
 192 
     | 
    
         
            +
                end
         
     | 
| 
      
 193 
     | 
    
         
            +
             
     | 
| 
      
 194 
     | 
    
         
            +
                def stripped_comment
         
     | 
| 
      
 195 
     | 
    
         
            +
                  comments = @current_comment.map do |c|
         
     | 
| 
      
 196 
     | 
    
         
            +
                    next unless c.multiline?
         
     | 
| 
      
 197 
     | 
    
         
            +
                    c.body
         
     | 
| 
      
 198 
     | 
    
         
            +
                  end
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
                  string = comments.join("\n")
         
     | 
| 
      
 201 
     | 
    
         
            +
                  string = string.gsub(/\A\**\n/, '')
         
     | 
| 
      
 202 
     | 
    
         
            +
                  strip_leading_whitespace(string)
         
     | 
| 
      
 203 
     | 
    
         
            +
                end
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                def process_comment(*)
         
     | 
| 
      
 206 
     | 
    
         
            +
                end
         
     | 
| 
      
 207 
     | 
    
         
            +
              end
         
     | 
| 
      
 208 
     | 
    
         
            +
             
     | 
| 
      
 209 
     | 
    
         
            +
              require "set"
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
              class FindVars < Visitor
         
     | 
| 
      
 212 
     | 
    
         
            +
                def self.find_variables(ast)
         
     | 
| 
      
 213 
     | 
    
         
            +
                  ast = Marshal.load(Marshal.dump(ast))
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
                  finder = new
         
     | 
| 
      
 216 
     | 
    
         
            +
                  finder.accept(ast)
         
     | 
| 
      
 217 
     | 
    
         
            +
                  finder.variables
         
     | 
| 
      
 218 
     | 
    
         
            +
                end
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
                attr_reader :variables
         
     | 
| 
      
 221 
     | 
    
         
            +
             
     | 
| 
      
 222 
     | 
    
         
            +
                def initialize(*)
         
     | 
| 
      
 223 
     | 
    
         
            +
                  @variables = Set.new
         
     | 
| 
      
 224 
     | 
    
         
            +
                  super
         
     | 
| 
      
 225 
     | 
    
         
            +
                end
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
                def visit_VariableDeclarator(expr)
         
     | 
| 
      
 228 
     | 
    
         
            +
                  @variables << expr.id.val
         
     | 
| 
      
 229 
     | 
    
         
            +
                end
         
     | 
| 
      
 230 
     | 
    
         
            +
             
     | 
| 
      
 231 
     | 
    
         
            +
                def visit_FunctionDeclaration(*)
         
     | 
| 
      
 232 
     | 
    
         
            +
                end
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
                def visit_FunctionExpression(*)
         
     | 
| 
      
 235 
     | 
    
         
            +
                end
         
     | 
| 
      
 236 
     | 
    
         
            +
              end
         
     | 
| 
      
 237 
     | 
    
         
            +
            end
         
     |