decode 0.7.0 → 0.8.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.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/decode.gemspec +1 -1
- data/examples/{index → decode-index}/extract.rb +0 -1
- data/gems.rb +2 -0
- data/lib/decode/definition.rb +18 -5
- data/lib/decode/documentation.rb +24 -1
- data/lib/decode/index.rb +19 -1
- data/lib/decode/language.rb +2 -3
- data/lib/decode/language/ruby.rb +9 -2
- data/lib/decode/language/ruby/attribute.rb +5 -0
- data/lib/decode/language/ruby/class.rb +16 -0
- data/lib/decode/language/ruby/constant.rb +5 -0
- data/lib/decode/language/ruby/definition.rb +5 -0
- data/lib/decode/language/ruby/function.rb +2 -0
- data/lib/decode/language/ruby/method.rb +6 -0
- data/lib/decode/language/ruby/module.rb +5 -0
- data/lib/decode/language/ruby/parser.rb +7 -14
- data/lib/decode/language/ruby/reference.rb +7 -0
- data/lib/decode/language/ruby/segment.rb +6 -2
- data/lib/decode/segment.rb +16 -2
- data/lib/decode/source.rb +10 -0
- data/lib/decode/symbol.rb +11 -3
- data/lib/decode/trie.rb +42 -9
- data/lib/decode/version.rb +1 -1
- metadata +3 -6
- data/docs/config.ru +0 -6
- data/docs/gems.locked +0 -137
- data/docs/gems.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1ca1745dcb76b54a61777388aba926d1262967dc51ca7ad633debc369f67d9f
|
4
|
+
data.tar.gz: 4164ca61971b6573b8604767cfdf35766605893f8d63cba44a516d705c0d5093
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b247bcd2d6b0eaacf1bbaa444cd98df314251bca958914793ad455521c9f379233f737e6afeeb31197b76f0ce9876de1887304203eb5487fda113fa4b8eea11
|
7
|
+
data.tar.gz: 72b714cf3ad9b0f7969e23be899ca10186994661d6dbc0bb91bd27a200a6ce4ee18815bca5a7cc355316ea5f53b6ed2d818073b28f61a41caca0dbbf52196623
|
data/README.md
CHANGED
@@ -4,10 +4,12 @@ A Ruby code analysis tool and documentation generator.
|
|
4
4
|
|
5
5
|
## Usage
|
6
6
|
|
7
|
-
Please see the documentation
|
7
|
+
Please see the <a href="https://ioquatix.github.io/decode">project documentation</a> or run it locally using `bake utopia:project:serve`.
|
8
8
|
|
9
9
|
## Contributing
|
10
10
|
|
11
|
+
We welcome contributions to this project.
|
12
|
+
|
11
13
|
1. Fork it
|
12
14
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
13
15
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
data/decode.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
# Specify which files should be added to the gem when it is released.
|
17
17
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
18
18
|
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
19
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
19
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(docs|test|spec|features)/}) }
|
20
20
|
end
|
21
21
|
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
@@ -1,7 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
# This example demonstrates how to extract symbols using the index. An instance of {Decode::Index} is used for loading symbols from source code files. These symbols are available as a flat list and as a trie structure. You can look up specific symbols using a reference using {Decode::Index:lookup}.
|
4
|
-
|
5
4
|
require_relative '../../lib/decode/index'
|
6
5
|
|
7
6
|
# Firstly, construct the index:
|
data/gems.rb
CHANGED
data/lib/decode/definition.rb
CHANGED
@@ -21,6 +21,7 @@
|
|
21
21
|
require_relative 'symbol'
|
22
22
|
|
23
23
|
module Decode
|
24
|
+
# A symbol with attached documentation.
|
24
25
|
class Definition < Symbol
|
25
26
|
def initialize(kind, name, comments, **options)
|
26
27
|
super(kind, name, **options)
|
@@ -29,44 +30,56 @@ module Decode
|
|
29
30
|
@documentation = nil
|
30
31
|
end
|
31
32
|
|
33
|
+
# The comment lines which directly preceeded the definition.
|
34
|
+
# @attr [Array(String)]
|
32
35
|
attr :comments
|
33
36
|
|
34
|
-
# A short form of the definition
|
37
|
+
# A short form of the definition.
|
38
|
+
# e.g. `def short_form`.
|
39
|
+
#
|
35
40
|
# @return [String | nil]
|
36
41
|
def short_form
|
37
42
|
end
|
38
43
|
|
39
|
-
# A long form of the definition
|
44
|
+
# A long form of the definition.
|
45
|
+
# e.g. `def initialize(kind, name, comments, **options)`.
|
46
|
+
#
|
40
47
|
# @return [String | nil]
|
41
48
|
def long_form
|
42
49
|
self.short_form
|
43
50
|
end
|
44
51
|
|
45
|
-
# A long form which uses the qualified name if possible.
|
52
|
+
# A long form which uses the qualified name if possible.
|
53
|
+
# Defaults to {long_form}.
|
54
|
+
#
|
46
55
|
# @return [String | nil]
|
47
56
|
def qualified_form
|
48
57
|
self.long_form
|
49
58
|
end
|
50
59
|
|
51
60
|
# The full text of the definition.
|
61
|
+
#
|
52
62
|
# @return [String | nil]
|
53
63
|
def text
|
54
64
|
end
|
55
65
|
|
56
66
|
# Whether this definition can contain nested definitions.
|
67
|
+
#
|
57
68
|
# @return [Boolean]
|
58
69
|
def container?
|
59
70
|
false
|
60
71
|
end
|
61
72
|
|
62
73
|
# Whether this represents a single entity to be documented (along with it's contents).
|
74
|
+
#
|
63
75
|
# @return [Boolean]
|
64
76
|
def nested?
|
65
77
|
container?
|
66
78
|
end
|
67
79
|
|
68
|
-
#
|
69
|
-
#
|
80
|
+
# Structured access to the definitions comments.
|
81
|
+
#
|
82
|
+
# @return [Documentation | Nil] A `Documentation` if this definition has comments.
|
70
83
|
def documentation
|
71
84
|
if @comments&.any?
|
72
85
|
@documentation ||= Documentation.new(@comments)
|
data/lib/decode/documentation.rb
CHANGED
@@ -19,13 +19,26 @@
|
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
21
|
module Decode
|
22
|
+
# Structured access to a set of comment lines.
|
22
23
|
class Documentation
|
23
|
-
|
24
|
+
# Initialize the documenation with an array of comments, within a specific language.
|
25
|
+
#
|
26
|
+
# @param comments [Array(String)] An array of comment lines.
|
27
|
+
# @param language [Language] The language in which the comments were extracted.
|
28
|
+
def initialize(comments, language = nil)
|
24
29
|
@comments = comments
|
30
|
+
@language = language
|
25
31
|
end
|
26
32
|
|
33
|
+
# The language in which the documentation was extracted from.
|
34
|
+
attr :language
|
35
|
+
|
27
36
|
DESCRIPTION = /\A\s*([^@\s].*)?\z/
|
28
37
|
|
38
|
+
# The text-only lines of the comment block.
|
39
|
+
#
|
40
|
+
# @yield [String]
|
41
|
+
# @return [Enumerable]
|
29
42
|
def description
|
30
43
|
return to_enum(:description) unless block_given?
|
31
44
|
|
@@ -52,6 +65,11 @@ module Decode
|
|
52
65
|
|
53
66
|
ATTRIBUTE = /\A\s*@(?<name>.*?)\s+(?<value>.*?)\z/
|
54
67
|
|
68
|
+
# The attribute lines of the comment block.
|
69
|
+
# e.g. `@return [String]`.
|
70
|
+
#
|
71
|
+
# @yield [String]
|
72
|
+
# @return [Enumerable]
|
55
73
|
def attributes
|
56
74
|
return to_enum(:attributes) unless block_given?
|
57
75
|
|
@@ -64,6 +82,11 @@ module Decode
|
|
64
82
|
|
65
83
|
PARAMETER = /\A\s*@param\s+(?<name>.*?)\s+\[(?<type>.*?)\]\s+(?<details>.*?)\z/
|
66
84
|
|
85
|
+
# The parameter lines of the comment block.
|
86
|
+
# e.g. `@param value [String] The value.`
|
87
|
+
#
|
88
|
+
# @yield [String]
|
89
|
+
# @return [Enumerable]
|
67
90
|
def parameters
|
68
91
|
return to_enum(:parameters) unless block_given?
|
69
92
|
|
data/lib/decode/index.rb
CHANGED
@@ -22,7 +22,9 @@ require_relative 'source'
|
|
22
22
|
require_relative 'trie'
|
23
23
|
|
24
24
|
module Decode
|
25
|
+
# A list of symbols organised for quick lookup and lexical enumeration.
|
25
26
|
class Index
|
27
|
+
# Initialize an empty index.
|
26
28
|
def initialize
|
27
29
|
@sources = {}
|
28
30
|
@symbols = {}
|
@@ -31,11 +33,23 @@ module Decode
|
|
31
33
|
@trie = Trie.new
|
32
34
|
end
|
33
35
|
|
36
|
+
# All source files that have been parsed.
|
37
|
+
# @attr [Array(Source)]
|
34
38
|
attr :sources
|
39
|
+
|
40
|
+
# All symbols which have been parsed.
|
41
|
+
# @attr [Array(Symbol)]
|
35
42
|
attr :symbols
|
36
43
|
|
44
|
+
# A (prefix) trie of lexically scoped symbols.
|
45
|
+
# @attr [Trie]
|
46
|
+
|
37
47
|
attr :trie
|
38
48
|
|
49
|
+
# Updates the index by parsing the specified files.
|
50
|
+
# All extracted symbols are merged into the existing index.
|
51
|
+
#
|
52
|
+
# @param paths [Array(String)] The source file paths.
|
39
53
|
def update(paths)
|
40
54
|
paths.each do |path|
|
41
55
|
source = Source.new(path)
|
@@ -49,6 +63,10 @@ module Decode
|
|
49
63
|
end
|
50
64
|
end
|
51
65
|
|
66
|
+
# Lookup the specified reference and return matching symbols.
|
67
|
+
#
|
68
|
+
# @param reference [Reference] The reference to match.
|
69
|
+
# @param relative_to [Symbol] Lookup the reference relative to the scope of this symbol.
|
52
70
|
def lookup(reference, relative_to: nil)
|
53
71
|
if reference.absolute? || relative_to.nil?
|
54
72
|
lexical_path = []
|
@@ -59,7 +77,7 @@ module Decode
|
|
59
77
|
path = reference.path
|
60
78
|
|
61
79
|
while true
|
62
|
-
node = @trie.
|
80
|
+
node = @trie.lookup(lexical_path)
|
63
81
|
|
64
82
|
if node.children[path.first]
|
65
83
|
if target = node.lookup(path)
|
data/lib/decode/language.rb
CHANGED
@@ -21,13 +21,12 @@
|
|
21
21
|
require_relative 'language/ruby'
|
22
22
|
|
23
23
|
module Decode
|
24
|
+
# Language specific parsers and symbols.
|
24
25
|
module Language
|
25
26
|
def self.detect(path)
|
26
27
|
case File.extname(path)
|
27
|
-
when '.rb'
|
28
|
+
when '.rb', '.ru'
|
28
29
|
return Language::Ruby
|
29
|
-
else
|
30
|
-
raise ArgumentError, "Could not determine language for #{path}!"
|
31
30
|
end
|
32
31
|
end
|
33
32
|
end
|
data/lib/decode/language/ruby.rb
CHANGED
@@ -23,7 +23,10 @@ require_relative 'ruby/parser'
|
|
23
23
|
|
24
24
|
module Decode
|
25
25
|
module Language
|
26
|
+
# The Ruby language.
|
26
27
|
module Ruby
|
28
|
+
# The canoical name of the language for use in output formatting.
|
29
|
+
# e.g. source code highlighting.
|
27
30
|
def self.name
|
28
31
|
"ruby"
|
29
32
|
end
|
@@ -37,6 +40,7 @@ module Decode
|
|
37
40
|
defs: '.',
|
38
41
|
}.freeze
|
39
42
|
|
43
|
+
# Generate a language-specific fully qualified name.
|
40
44
|
def self.join(symbols, absolute = true)
|
41
45
|
buffer = String.new
|
42
46
|
|
@@ -53,18 +57,21 @@ module Decode
|
|
53
57
|
return buffer
|
54
58
|
end
|
55
59
|
|
60
|
+
# Generate a language-specific reference.
|
56
61
|
def self.reference(value)
|
57
62
|
Reference.new(value)
|
58
63
|
end
|
59
64
|
|
60
65
|
# Parse the input yielding symbols.
|
61
|
-
# @
|
66
|
+
# @block `{|definition| ...}`
|
67
|
+
# @yield definition [Definition]
|
62
68
|
def self.symbols_for(input, &block)
|
63
69
|
Parser.new.symbols_for(input, &block)
|
64
70
|
end
|
65
71
|
|
66
72
|
# Parse the input yielding interleaved comments and code segments.
|
67
|
-
# @
|
73
|
+
# @block `{|segment| ...}`
|
74
|
+
# @yield segment [Segment]
|
68
75
|
def self.segments_for(input, &block)
|
69
76
|
Parser.new.segments_for(input, &block)
|
70
77
|
end
|
@@ -23,11 +23,16 @@ require_relative 'definition'
|
|
23
23
|
module Decode
|
24
24
|
module Language
|
25
25
|
module Ruby
|
26
|
+
# A Ruby-specific attribute.
|
26
27
|
class Attribute < Definition
|
28
|
+
# The keyword that defined the attribute.
|
29
|
+
# @return [String]
|
27
30
|
def keyword
|
28
31
|
@node.children[1]
|
29
32
|
end
|
30
33
|
|
34
|
+
# The short form of the attribute.
|
35
|
+
# e.g. `attr :value`.
|
31
36
|
def short_form
|
32
37
|
"#{self.keyword} #{@name.inspect}"
|
33
38
|
end
|
@@ -23,15 +23,21 @@ require_relative 'definition'
|
|
23
23
|
module Decode
|
24
24
|
module Language
|
25
25
|
module Ruby
|
26
|
+
# A Ruby-specific class.
|
26
27
|
class Class < Definition
|
28
|
+
# A class is a container for other definitions.
|
27
29
|
def container?
|
28
30
|
true
|
29
31
|
end
|
30
32
|
|
33
|
+
# The short form of the class.
|
34
|
+
# e.g. `class Animal`.
|
31
35
|
def short_form
|
32
36
|
"class #{@name}"
|
33
37
|
end
|
34
38
|
|
39
|
+
# The long form of the class.
|
40
|
+
# e.g. `class Dog < Animal`.
|
35
41
|
def long_form
|
36
42
|
if super_node = @node.children[1]
|
37
43
|
@node.location.keyword.join(
|
@@ -42,24 +48,34 @@ module Decode
|
|
42
48
|
end
|
43
49
|
end
|
44
50
|
|
51
|
+
# The fully qualified name of the class.
|
52
|
+
# e.g. `class ::Barnyard::Dog`.
|
45
53
|
def qualified_form
|
46
54
|
"class #{self.qualified_name}"
|
47
55
|
end
|
48
56
|
end
|
49
57
|
|
58
|
+
# A Ruby-specific singleton class.
|
50
59
|
class Singleton < Definition
|
60
|
+
# A singleton class is a container for other definitions.
|
61
|
+
# @return [Boolean]
|
51
62
|
def container?
|
52
63
|
true
|
53
64
|
end
|
54
65
|
|
66
|
+
# Typically, a singleton class does not contain other definitions.
|
67
|
+
# @return [Boolean]
|
55
68
|
def nested?
|
56
69
|
false
|
57
70
|
end
|
58
71
|
|
72
|
+
# The short form of the class.
|
73
|
+
# e.g. `class << (self)`.
|
59
74
|
def short_form
|
60
75
|
"class << #{@name}"
|
61
76
|
end
|
62
77
|
|
78
|
+
# The long form is the same as the short form.
|
63
79
|
alias long_form short_form
|
64
80
|
end
|
65
81
|
end
|
@@ -23,11 +23,16 @@ require_relative 'definition'
|
|
23
23
|
module Decode
|
24
24
|
module Language
|
25
25
|
module Ruby
|
26
|
+
# A Ruby-specific constant.
|
26
27
|
class Constant < Definition
|
28
|
+
# The short form of the constant.
|
29
|
+
# e.g. `NAME`.
|
27
30
|
def short_form
|
28
31
|
@node.location.name.source
|
29
32
|
end
|
30
33
|
|
34
|
+
# The long form of the constant.
|
35
|
+
# e.g. `NAME = "Alice"`.
|
31
36
|
def long_form
|
32
37
|
if @node.location.line == @node.location.last_line
|
33
38
|
@node.location.expression.source
|
@@ -23,15 +23,20 @@ require_relative '../../definition'
|
|
23
23
|
module Decode
|
24
24
|
module Language
|
25
25
|
module Ruby
|
26
|
+
# A Ruby-specific definition.
|
26
27
|
class Definition < Decode::Definition
|
28
|
+
# Initialize the definition from the syntax tree node.
|
27
29
|
def initialize(kind, name, comments, node, **options)
|
28
30
|
super(kind, name, comments, **options)
|
29
31
|
|
30
32
|
@node = node
|
31
33
|
end
|
32
34
|
|
35
|
+
# The parser syntax tree node.
|
33
36
|
attr :node
|
34
37
|
|
38
|
+
# The source code associated with the definition.
|
39
|
+
# @return [String]
|
35
40
|
def text
|
36
41
|
@node.location.expression.source
|
37
42
|
end
|
@@ -23,7 +23,9 @@ require_relative 'method'
|
|
23
23
|
module Decode
|
24
24
|
module Language
|
25
25
|
module Ruby
|
26
|
+
# A Ruby-specific function.
|
26
27
|
class Function < Method
|
28
|
+
# The node which contains the function arguments.
|
27
29
|
def arguments_node
|
28
30
|
if node = @node.children[2]
|
29
31
|
if node.location.expression
|
@@ -23,11 +23,15 @@ require_relative 'definition'
|
|
23
23
|
module Decode
|
24
24
|
module Language
|
25
25
|
module Ruby
|
26
|
+
# A Ruby-specific method.
|
26
27
|
class Method < Definition
|
28
|
+
# The short form of the method.
|
29
|
+
# e.g. `def puts`.
|
27
30
|
def short_form
|
28
31
|
@node.location.keyword.join(@node.location.name).source
|
29
32
|
end
|
30
33
|
|
34
|
+
# The node which contains the function arguments.
|
31
35
|
def arguments_node
|
32
36
|
if node = @node.children[1]
|
33
37
|
if node.location.expression
|
@@ -36,6 +40,8 @@ module Decode
|
|
36
40
|
end
|
37
41
|
end
|
38
42
|
|
43
|
+
# The long form of the method.
|
44
|
+
# e.g. `def puts(*lines, separator: "\n")`.
|
39
45
|
def long_form
|
40
46
|
if arguments_node = self.arguments_node
|
41
47
|
@node.location.keyword.join(
|
@@ -23,15 +23,20 @@ require_relative 'definition'
|
|
23
23
|
module Decode
|
24
24
|
module Language
|
25
25
|
module Ruby
|
26
|
+
# A Ruby-specific module.
|
26
27
|
class Module < Definition
|
28
|
+
# A module is a container for other definitions.
|
27
29
|
def container?
|
28
30
|
true
|
29
31
|
end
|
30
32
|
|
33
|
+
# The short form of the module.
|
34
|
+
# e.g. `module Barnyard`.
|
31
35
|
def short_form
|
32
36
|
"module #{@name}"
|
33
37
|
end
|
34
38
|
|
39
|
+
# The long form is the same as the short form.
|
35
40
|
alias long_form short_form
|
36
41
|
end
|
37
42
|
end
|
@@ -32,16 +32,11 @@ require_relative 'segment'
|
|
32
32
|
module Decode
|
33
33
|
module Language
|
34
34
|
module Ruby
|
35
|
+
# The Ruby source code parser.
|
35
36
|
class Parser
|
36
|
-
|
37
|
-
@parser = parser
|
38
|
-
end
|
39
|
-
|
37
|
+
# Extract symbols from the given input file.
|
40
38
|
def symbols_for(input, &block)
|
41
|
-
|
42
|
-
buffer.source = input.read
|
43
|
-
|
44
|
-
top, comments = @parser.parse_with_comments(buffer)
|
39
|
+
top, comments = ::Parser::CurrentRuby.parse_with_comments(input.read)
|
45
40
|
|
46
41
|
if top
|
47
42
|
walk_symbols(top, comments, &block)
|
@@ -162,11 +157,9 @@ module Decode
|
|
162
157
|
end
|
163
158
|
end
|
164
159
|
|
160
|
+
# Extract segments from the given input file.
|
165
161
|
def segments_for(input, &block)
|
166
|
-
|
167
|
-
buffer.source = input.read
|
168
|
-
|
169
|
-
top, comments = @parser.parse_with_comments(buffer)
|
162
|
+
top, comments = ::Parser::CurrentRuby.parse_with_comments(input.read)
|
170
163
|
|
171
164
|
# We delete any leading comments:
|
172
165
|
line = 0
|
@@ -193,11 +186,11 @@ module Decode
|
|
193
186
|
if segment.nil?
|
194
187
|
segment = Segment.new(
|
195
188
|
extract_comments_for(child, comments),
|
196
|
-
child
|
189
|
+
Ruby, child
|
197
190
|
)
|
198
191
|
elsif next_comments = extract_comments_for(child, comments)
|
199
192
|
yield segment if segment
|
200
|
-
segment = Segment.new(next_comments, child)
|
193
|
+
segment = Segment.new(next_comments, Ruby, child)
|
201
194
|
else
|
202
195
|
segment.expand(child)
|
203
196
|
end
|
@@ -21,12 +21,15 @@
|
|
21
21
|
module Decode
|
22
22
|
module Language
|
23
23
|
module Ruby
|
24
|
+
# An Ruby-specific reference which can be resolved to zero or more symbols.
|
24
25
|
class Reference
|
25
26
|
KIND = {
|
26
27
|
':' => :def,
|
27
28
|
'.' => :defs,
|
28
29
|
}.freeze
|
29
30
|
|
31
|
+
# Initialize the reference.
|
32
|
+
# @param value [String] The string value of the reference.
|
30
33
|
def initialize(value)
|
31
34
|
@value = value
|
32
35
|
|
@@ -34,12 +37,15 @@ module Decode
|
|
34
37
|
@kind = nil
|
35
38
|
end
|
36
39
|
|
40
|
+
# Whether the reference starts at the base of the lexical tree.
|
37
41
|
def absolute?
|
38
42
|
@value.start_with?('::')
|
39
43
|
end
|
40
44
|
|
41
45
|
METHOD = /\A(?<scope>.*?)?(?<kind>:|\.)(?<name>.+?)\z/
|
42
46
|
|
47
|
+
# The lexical path of the reference.
|
48
|
+
# @return [Array(String)]
|
43
49
|
def path
|
44
50
|
if @path.nil?
|
45
51
|
@path = @value.split(/::/)
|
@@ -65,6 +71,7 @@ module Decode
|
|
65
71
|
return @path
|
66
72
|
end
|
67
73
|
|
74
|
+
# The kind of symbol to match.
|
68
75
|
def kind
|
69
76
|
self.path
|
70
77
|
|
@@ -23,20 +23,24 @@ require_relative '../../segment'
|
|
23
23
|
module Decode
|
24
24
|
module Language
|
25
25
|
module Ruby
|
26
|
+
# A Ruby specific code segment.
|
26
27
|
class Segment < Decode::Segment
|
27
|
-
def initialize(comments, node, **options)
|
28
|
-
super(comments, **options)
|
28
|
+
def initialize(comments, language, node, **options)
|
29
|
+
super(comments, language, **options)
|
29
30
|
|
30
31
|
@node = node
|
31
32
|
@expression = node.location.expression
|
32
33
|
end
|
33
34
|
|
35
|
+
# The parser syntax tree node.
|
34
36
|
attr :node
|
35
37
|
|
36
38
|
def expand(node)
|
37
39
|
@expression = @expression.join(node.location.expression)
|
38
40
|
end
|
39
41
|
|
42
|
+
# The source code trailing the comments.
|
43
|
+
# @return [String | nil]
|
40
44
|
def code
|
41
45
|
@expression.source
|
42
46
|
end
|
data/lib/decode/segment.rb
CHANGED
@@ -21,18 +21,32 @@
|
|
21
21
|
require_relative 'documentation'
|
22
22
|
|
23
23
|
module Decode
|
24
|
+
# A chunk of code with an optional preceeding comment block.
|
25
|
+
#
|
26
|
+
# ~~~ ruby
|
27
|
+
# # Get the first segment from a source file:
|
28
|
+
# segment = source.segments.first
|
29
|
+
# ~~~
|
30
|
+
#
|
24
31
|
class Segment
|
25
|
-
def initialize(comments)
|
32
|
+
def initialize(comments, language)
|
26
33
|
@comments = comments
|
34
|
+
@language = language
|
27
35
|
end
|
28
36
|
|
37
|
+
# The preceeding comments.
|
38
|
+
# @attr [Array(String)]
|
29
39
|
attr :comments
|
30
40
|
|
41
|
+
# The language of the code attached to this segment.
|
42
|
+
# @attr [Language]
|
43
|
+
attr :language
|
44
|
+
|
31
45
|
# An interface for accsssing the documentation of the definition.
|
32
46
|
# @return [Documentation | nil] A `Documentation` if this definition has comments.
|
33
47
|
def documentation
|
34
48
|
if @comments&.any?
|
35
|
-
@documentation ||= Documentation.new(@comments)
|
49
|
+
@documentation ||= Documentation.new(@comments, @language)
|
36
50
|
end
|
37
51
|
end
|
38
52
|
|
data/lib/decode/source.rb
CHANGED
@@ -22,11 +22,21 @@ require_relative 'language'
|
|
22
22
|
|
23
23
|
module Decode
|
24
24
|
class Source
|
25
|
+
def self.for?(path)
|
26
|
+
if language = Language.detect(path)
|
27
|
+
self.new(path, language)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
25
31
|
def initialize(path, language = nil)
|
26
32
|
@path = path
|
27
33
|
@language = language || Language.detect(path)
|
28
34
|
end
|
29
35
|
|
36
|
+
attr :path
|
37
|
+
|
38
|
+
attr :language
|
39
|
+
|
30
40
|
def open(&block)
|
31
41
|
File.open(@path, &block)
|
32
42
|
end
|
data/lib/decode/symbol.rb
CHANGED
@@ -21,10 +21,16 @@
|
|
21
21
|
require_relative 'documentation'
|
22
22
|
|
23
23
|
module Decode
|
24
|
+
# A language agnostic lexical scope.
|
24
25
|
Key = Struct.new(:kind, :name)
|
25
26
|
|
26
27
|
# A named element which represents some significant element of some kind in a computer program.
|
27
28
|
class Symbol
|
29
|
+
# Initialize the symbol.
|
30
|
+
# @param kind [Symbol] The kind of symbol.
|
31
|
+
# @param name [Symbol] The name of the symbol.
|
32
|
+
# @param parent [Symbol] The parent lexical scope.
|
33
|
+
# @param language [Language] The language in which the symbol is defined in.
|
28
34
|
def initialize(kind, name, parent: nil, language: parent.language)
|
29
35
|
@kind = kind
|
30
36
|
@name = name
|
@@ -45,10 +51,12 @@ module Decode
|
|
45
51
|
"\#<#{self.class} #{@kind} #{qualified_name}>"
|
46
52
|
end
|
47
53
|
|
48
|
-
# The kind of symbol
|
54
|
+
# The kind of symbol.
|
55
|
+
# e.g. `:module`.
|
49
56
|
attr :kind
|
50
57
|
|
51
|
-
# The symbol name
|
58
|
+
# The symbol name.
|
59
|
+
# e.g. `:Decode`.
|
52
60
|
attr :name
|
53
61
|
|
54
62
|
# The parent symbol, defining lexical scope.
|
@@ -62,7 +70,7 @@ module Decode
|
|
62
70
|
@qualified_name ||= @language.join(self.path).freeze
|
63
71
|
end
|
64
72
|
|
65
|
-
# The lexical scope as
|
73
|
+
# The lexical scope which is an array of lexical {Key} instances as generated by {key}.
|
66
74
|
# @return [Array]
|
67
75
|
def path
|
68
76
|
if @path
|
data/lib/decode/trie.rb
CHANGED
@@ -21,16 +21,27 @@
|
|
21
21
|
require_relative 'source'
|
22
22
|
|
23
23
|
module Decode
|
24
|
+
# A prefix-trie data structure for fast lexical lookups.
|
24
25
|
class Trie
|
26
|
+
# A single node in the trie.
|
25
27
|
class Node
|
26
28
|
def initialize
|
27
29
|
@values = nil
|
28
30
|
@children = Hash.new
|
29
31
|
end
|
30
32
|
|
33
|
+
# A mutable array of all values that terminate at this node.
|
34
|
+
# @attr [Array]
|
31
35
|
attr_accessor :values
|
36
|
+
|
37
|
+
# A hash table of all children nodes, indexed by name.
|
38
|
+
# @attr [Hash(String, Node)]
|
32
39
|
attr :children
|
33
40
|
|
41
|
+
# Look up a lexical path starting at this node.
|
42
|
+
#
|
43
|
+
# @param path [Array(String)] The path to resolve.
|
44
|
+
# @return [Node | Nil]
|
34
45
|
def lookup(path, index = 0)
|
35
46
|
if index < path.size
|
36
47
|
if child = @children[path[index]]
|
@@ -41,6 +52,15 @@ module Decode
|
|
41
52
|
end
|
42
53
|
end
|
43
54
|
|
55
|
+
# Traverse the trie from this node.
|
56
|
+
# Invoke `descend.call` to traverse the children of the current node.
|
57
|
+
#
|
58
|
+
# @param path [Array(String)] The current lexical path.
|
59
|
+
#
|
60
|
+
# @block `{|path, node, descend| descend.call}`
|
61
|
+
# @yield path [Array(String)] The current lexical path.
|
62
|
+
# @yield node [Node] The current node which is being traversed.
|
63
|
+
# @yield descend [Proc] The recursive method for traversing children.
|
44
64
|
def traverse(path = [], &block)
|
45
65
|
yield(path, self, ->{
|
46
66
|
@children.each do |name, node|
|
@@ -50,14 +70,18 @@ module Decode
|
|
50
70
|
end
|
51
71
|
end
|
52
72
|
|
53
|
-
|
54
|
-
|
73
|
+
# Initialize an empty trie.
|
55
74
|
def initialize
|
56
75
|
@root = Node.new
|
57
76
|
end
|
58
77
|
|
78
|
+
# The root of the trie.
|
79
|
+
# @attr [Node]
|
59
80
|
attr :root
|
60
81
|
|
82
|
+
# Insert the specified value at the given path into the trie.
|
83
|
+
# @param path [Array(String)] The lexical path where the value will be inserted.
|
84
|
+
# @param value [Object] The value to insert.
|
61
85
|
def insert(path, value)
|
62
86
|
node = @root
|
63
87
|
|
@@ -68,26 +92,35 @@ module Decode
|
|
68
92
|
(node.values ||= []) << value
|
69
93
|
end
|
70
94
|
|
95
|
+
# Lookup the values at the specified path.
|
96
|
+
#
|
97
|
+
# @param path [Array(String)] The lexical path which contains the values.
|
98
|
+
# @return [Array(Object) | Nil] The values that existed (or not) at the specified path.
|
71
99
|
def lookup(path)
|
72
100
|
@root.lookup(path)
|
73
101
|
end
|
74
102
|
|
75
|
-
#
|
76
|
-
#
|
103
|
+
# Enumerate all lexical scopes under the specified path.
|
104
|
+
#
|
105
|
+
# @block `{|path, values| ...}`
|
106
|
+
# @yield path [Array(String)] The lexical path.
|
107
|
+
# @yield values [Array(Object)] The values that exist at the given path.
|
77
108
|
def each(path = [], &block)
|
78
109
|
if node = @root.lookup(path)
|
79
|
-
node.traverse
|
110
|
+
node.traverse do |path, node, descend|
|
111
|
+
yield path, node.values
|
112
|
+
|
113
|
+
descend.call
|
114
|
+
end
|
80
115
|
end
|
81
116
|
end
|
82
117
|
|
118
|
+
# Traverse the trie.
|
119
|
+
# See {Node:traverse} for details.
|
83
120
|
def traverse(path = [], &block)
|
84
121
|
if node = @root.lookup(path)
|
85
122
|
node.traverse(&block)
|
86
123
|
end
|
87
124
|
end
|
88
|
-
|
89
|
-
def match(path, &block)
|
90
|
-
@root.lookup(path)
|
91
|
-
end
|
92
125
|
end
|
93
126
|
end
|
data/lib/decode/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: decode
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-05-
|
11
|
+
date: 2020-05-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parser
|
@@ -120,10 +120,7 @@ files:
|
|
120
120
|
- ".rspec"
|
121
121
|
- README.md
|
122
122
|
- decode.gemspec
|
123
|
-
-
|
124
|
-
- docs/gems.locked
|
125
|
-
- docs/gems.rb
|
126
|
-
- examples/index/extract.rb
|
123
|
+
- examples/decode-index/extract.rb
|
127
124
|
- gems.rb
|
128
125
|
- lib/decode.rb
|
129
126
|
- lib/decode/definition.rb
|
data/docs/config.ru
DELETED
data/docs/gems.locked
DELETED
@@ -1,137 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: ../../../socketry/utopia-project
|
3
|
-
specs:
|
4
|
-
utopia-project (0.1.0)
|
5
|
-
decode
|
6
|
-
falcon
|
7
|
-
kramdown
|
8
|
-
kramdown-parser-gfm
|
9
|
-
rackula
|
10
|
-
thread-local
|
11
|
-
utopia (~> 2.14)
|
12
|
-
utopia-gallery
|
13
|
-
|
14
|
-
PATH
|
15
|
-
remote: ..
|
16
|
-
specs:
|
17
|
-
decode (0.6.0)
|
18
|
-
parser
|
19
|
-
|
20
|
-
GEM
|
21
|
-
remote: https://rubygems.org/
|
22
|
-
specs:
|
23
|
-
ast (2.4.0)
|
24
|
-
async (1.25.2)
|
25
|
-
console (~> 1.0)
|
26
|
-
nio4r (~> 2.3)
|
27
|
-
timers (~> 4.1)
|
28
|
-
async-container (0.16.4)
|
29
|
-
async (~> 1.0)
|
30
|
-
async-io (~> 1.26)
|
31
|
-
process-group
|
32
|
-
async-http (0.52.0)
|
33
|
-
async (~> 1.25)
|
34
|
-
async-io (~> 1.28)
|
35
|
-
async-pool (~> 0.2)
|
36
|
-
protocol-http (~> 0.18.0)
|
37
|
-
protocol-http1 (~> 0.12.0)
|
38
|
-
protocol-http2 (~> 0.14.0)
|
39
|
-
async-http-cache (0.2.0)
|
40
|
-
async-http (~> 0.51)
|
41
|
-
async-io (1.29.0)
|
42
|
-
async (~> 1.14)
|
43
|
-
async-pool (0.3.0)
|
44
|
-
async (~> 1.25)
|
45
|
-
build-environment (1.13.0)
|
46
|
-
concurrent-ruby (1.1.6)
|
47
|
-
console (1.8.2)
|
48
|
-
falcon (0.36.4)
|
49
|
-
async (~> 1.13)
|
50
|
-
async-container (~> 0.16.0)
|
51
|
-
async-http (~> 0.52.0)
|
52
|
-
async-http-cache (~> 0.2.0)
|
53
|
-
async-io (~> 1.22)
|
54
|
-
build-environment (~> 1.13)
|
55
|
-
localhost (~> 1.1)
|
56
|
-
process-metrics (~> 0.2.0)
|
57
|
-
rack (>= 1.0)
|
58
|
-
samovar (~> 2.1)
|
59
|
-
ffi (1.12.2)
|
60
|
-
http-accept (2.1.1)
|
61
|
-
kramdown (2.2.1)
|
62
|
-
rexml
|
63
|
-
kramdown-parser-gfm (1.1.0)
|
64
|
-
kramdown (~> 2.0)
|
65
|
-
localhost (1.1.6)
|
66
|
-
mail (2.7.1)
|
67
|
-
mini_mime (>= 0.1.1)
|
68
|
-
mapping (1.1.1)
|
69
|
-
mime-types (3.3.1)
|
70
|
-
mime-types-data (~> 3.2015)
|
71
|
-
mime-types-data (3.2020.0425)
|
72
|
-
mini_mime (1.0.2)
|
73
|
-
msgpack (1.3.3)
|
74
|
-
nio4r (2.5.2)
|
75
|
-
parser (2.7.1.2)
|
76
|
-
ast (~> 2.4.0)
|
77
|
-
process-group (1.2.1)
|
78
|
-
process-terminal (~> 0.2.0)
|
79
|
-
process-metrics (0.2.1)
|
80
|
-
console (~> 1.8)
|
81
|
-
samovar (~> 2.1)
|
82
|
-
process-terminal (0.2.0)
|
83
|
-
ffi
|
84
|
-
protocol-hpack (1.4.2)
|
85
|
-
protocol-http (0.18.0)
|
86
|
-
protocol-http1 (0.12.0)
|
87
|
-
protocol-http (~> 0.18)
|
88
|
-
protocol-http2 (0.14.0)
|
89
|
-
protocol-hpack (~> 1.4)
|
90
|
-
protocol-http (~> 0.18)
|
91
|
-
rack (2.2.2)
|
92
|
-
rackula (1.1.0)
|
93
|
-
falcon (~> 0.34)
|
94
|
-
samovar (~> 2.1)
|
95
|
-
variant
|
96
|
-
rake (13.0.1)
|
97
|
-
rake-compiler (1.1.0)
|
98
|
-
rake
|
99
|
-
rexml (3.2.4)
|
100
|
-
samovar (2.1.4)
|
101
|
-
console (~> 1.0)
|
102
|
-
mapping (~> 1.0)
|
103
|
-
thread-local (1.0.0)
|
104
|
-
timers (4.3.0)
|
105
|
-
trenni (3.9.0)
|
106
|
-
rake-compiler
|
107
|
-
utopia (2.15.1)
|
108
|
-
concurrent-ruby (~> 1.0)
|
109
|
-
console (~> 1.0)
|
110
|
-
http-accept (~> 2.1)
|
111
|
-
mail (~> 2.6)
|
112
|
-
mime-types (~> 3.0)
|
113
|
-
msgpack
|
114
|
-
rack (~> 2.2)
|
115
|
-
samovar (~> 2.1)
|
116
|
-
trenni (~> 3.0)
|
117
|
-
variant (~> 0.1)
|
118
|
-
utopia-gallery (2.5.0)
|
119
|
-
trenni (~> 3.9)
|
120
|
-
utopia (~> 2.0)
|
121
|
-
vips-thumbnail (~> 1.6)
|
122
|
-
variant (0.1.1)
|
123
|
-
thread-local
|
124
|
-
vips (8.9.1)
|
125
|
-
ffi (~> 1.9)
|
126
|
-
vips-thumbnail (1.6.0)
|
127
|
-
vips (~> 8.9)
|
128
|
-
|
129
|
-
PLATFORMS
|
130
|
-
ruby
|
131
|
-
|
132
|
-
DEPENDENCIES
|
133
|
-
decode!
|
134
|
-
utopia-project!
|
135
|
-
|
136
|
-
BUNDLED WITH
|
137
|
-
2.1.4
|