decode 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/decode/definition.rb +70 -5
- data/lib/decode/index.rb +14 -17
- data/lib/decode/language/ruby/attribute.rb +14 -7
- data/lib/decode/language/ruby/block.rb +72 -0
- data/lib/decode/language/ruby/call.rb +57 -0
- data/lib/decode/language/ruby/class.rb +4 -0
- data/lib/decode/language/ruby/constant.rb +4 -0
- data/lib/decode/language/ruby/definition.rb +6 -2
- data/lib/decode/language/ruby/function.rb +4 -0
- data/lib/decode/language/ruby/method.rb +11 -0
- data/lib/decode/language/ruby/module.rb +4 -0
- data/lib/decode/language/ruby/parser.rb +106 -24
- data/lib/decode/language/ruby/reference.rb +14 -37
- data/lib/decode/language/ruby.rb +3 -29
- data/lib/decode/language.rb +1 -1
- data/lib/decode/scope.rb +34 -0
- data/lib/decode/source.rb +3 -3
- data/lib/decode/version.rb +1 -1
- metadata +5 -3
- data/lib/decode/symbol.rb +0 -90
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fd42fdf97af9b39dd08a24eb01bf8e165f5f5b5af934024789d14e68d007d68b
|
4
|
+
data.tar.gz: dd47038eaacad07b6a00a28cd7942ccf397fbee3f86245277658bbc8988e3189
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39d1157dab86e1200394f89e19b8996f974ce62fee7843cebe1b9a4f9637cdf68629d91d431f2be7af77697443fefed8ad80b60b9297e284ca4ad65c42355990
|
7
|
+
data.tar.gz: 1385928ab0ec53208ddaffb9acceb2c2da1bd976ecea650291564e30b0d7191aded0f2d2b6946815a16f48534602f3ac365ed0c143e831bdb052704907bc2eb1
|
data/lib/decode/definition.rb
CHANGED
@@ -18,22 +18,87 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
require_relative 'symbol'
|
22
|
-
|
23
21
|
module Decode
|
24
22
|
# A symbol with attached documentation.
|
25
|
-
class Definition
|
26
|
-
|
27
|
-
|
23
|
+
class Definition
|
24
|
+
# Initialize the symbol.
|
25
|
+
# @param name [Symbol] The name of the definition.
|
26
|
+
# @param parent [Symbol] The parent lexical scope.
|
27
|
+
# @param language [Language] The language in which the symbol is defined in.
|
28
|
+
# @param comments [Array(String)] The comments associated with the definition.
|
29
|
+
def initialize(name, parent: nil, language: parent.language, comments: nil)
|
30
|
+
@name = name
|
28
31
|
|
29
32
|
@comments = comments
|
33
|
+
@language = language
|
34
|
+
@parent = parent
|
35
|
+
|
36
|
+
@path = nil
|
37
|
+
@qualified_name = nil
|
30
38
|
@documentation = nil
|
31
39
|
end
|
32
40
|
|
41
|
+
def to_s
|
42
|
+
"\#<#{self.class} #{qualified_name}>"
|
43
|
+
end
|
44
|
+
|
45
|
+
# The symbol name.
|
46
|
+
# e.g. `:Decode`.
|
47
|
+
attr :name
|
48
|
+
|
49
|
+
# The parent symbol, defining lexical scope.
|
50
|
+
attr :parent
|
51
|
+
|
52
|
+
# The language the symbol is defined within.
|
53
|
+
attr :language
|
54
|
+
|
33
55
|
# The comment lines which directly preceeded the definition.
|
34
56
|
# @attr [Array(String)]
|
35
57
|
attr :comments
|
36
58
|
|
59
|
+
# The qualified name is an absolute name which includes any and all namespacing.
|
60
|
+
# @return [String]
|
61
|
+
def qualified_name
|
62
|
+
@qualified_name ||= begin
|
63
|
+
if @parent
|
64
|
+
@parent.qualified_name + self.nested_name
|
65
|
+
else
|
66
|
+
@name.to_s
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# The name of this definition plus the nesting prefix.
|
72
|
+
# @return [String]
|
73
|
+
def nested_name
|
74
|
+
"::#{@name}"
|
75
|
+
end
|
76
|
+
|
77
|
+
def start_with?(prefix)
|
78
|
+
self.nested_name.start_with?(prefix)
|
79
|
+
end
|
80
|
+
|
81
|
+
def convert(kind)
|
82
|
+
raise ArgumentError, "Unable to convert #{self} into #{kind}!"
|
83
|
+
end
|
84
|
+
|
85
|
+
# The lexical scope which is an array of lexical {Key} instances as generated by {key}.
|
86
|
+
# @return [Array]
|
87
|
+
def path
|
88
|
+
if @path
|
89
|
+
# Cached version:
|
90
|
+
@path
|
91
|
+
elsif @parent
|
92
|
+
# Merge with parent:
|
93
|
+
@path = [*@parent.path, @name]
|
94
|
+
else
|
95
|
+
# At top:
|
96
|
+
@path = [@name]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
alias lexical_path path
|
101
|
+
|
37
102
|
# A short form of the definition.
|
38
103
|
# e.g. `def short_form`.
|
39
104
|
#
|
data/lib/decode/index.rb
CHANGED
@@ -22,12 +22,12 @@ require_relative 'source'
|
|
22
22
|
require_relative 'trie'
|
23
23
|
|
24
24
|
module Decode
|
25
|
-
# A list of
|
25
|
+
# A list of definitions organised for quick lookup and lexical enumeration.
|
26
26
|
class Index
|
27
27
|
# Initialize an empty index.
|
28
28
|
def initialize
|
29
29
|
@sources = {}
|
30
|
-
@
|
30
|
+
@definitions = {}
|
31
31
|
|
32
32
|
# This is essentially a prefix tree:
|
33
33
|
@trie = Trie.new
|
@@ -37,17 +37,17 @@ module Decode
|
|
37
37
|
# @attr [Array(Source)]
|
38
38
|
attr :sources
|
39
39
|
|
40
|
-
# All
|
40
|
+
# All definitions which have been parsed.
|
41
41
|
# @attr [Array(Symbol)]
|
42
|
-
attr :
|
42
|
+
attr :definitions
|
43
43
|
|
44
|
-
# A (prefix) trie of lexically scoped
|
44
|
+
# A (prefix) trie of lexically scoped definitions.
|
45
45
|
# @attr [Trie]
|
46
46
|
|
47
47
|
attr :trie
|
48
48
|
|
49
49
|
# Updates the index by parsing the specified files.
|
50
|
-
# All extracted
|
50
|
+
# All extracted definitions are merged into the existing index.
|
51
51
|
#
|
52
52
|
# @param paths [Array(String)] The source file paths.
|
53
53
|
def update(paths)
|
@@ -55,23 +55,24 @@ module Decode
|
|
55
55
|
source = Source.new(path)
|
56
56
|
@sources[path] = Source.new(path)
|
57
57
|
|
58
|
-
source.
|
59
|
-
|
58
|
+
source.definitions do |symbol|
|
59
|
+
# $stderr.puts "Adding #{symbol.qualified_name} to #{symbol.lexical_path.join(' -> ')}"
|
60
60
|
|
61
|
-
@
|
61
|
+
@definitions[symbol.qualified_name] = symbol
|
62
|
+
@trie.insert(symbol.path, symbol)
|
62
63
|
end
|
63
64
|
end
|
64
65
|
end
|
65
66
|
|
66
|
-
# Lookup the specified reference and return matching
|
67
|
+
# Lookup the specified reference and return matching definitions.
|
67
68
|
#
|
68
69
|
# @param reference [Reference] The reference to match.
|
69
|
-
# @param relative_to [
|
70
|
+
# @param relative_to [Definition] Lookup the reference relative to the scope of this definition.
|
70
71
|
def lookup(reference, relative_to: nil)
|
71
72
|
if reference.absolute? || relative_to.nil?
|
72
73
|
lexical_path = []
|
73
74
|
else
|
74
|
-
lexical_path = relative_to.
|
75
|
+
lexical_path = relative_to.path
|
75
76
|
end
|
76
77
|
|
77
78
|
path = reference.path
|
@@ -81,11 +82,7 @@ module Decode
|
|
81
82
|
|
82
83
|
if node.children[path.first]
|
83
84
|
if target = node.lookup(path)
|
84
|
-
|
85
|
-
return target.values.select{|symbol| symbol.kind == reference.kind}
|
86
|
-
else
|
87
|
-
return target.values
|
88
|
-
end
|
85
|
+
return reference.best(target.values)
|
89
86
|
else
|
90
87
|
return nil
|
91
88
|
end
|
@@ -25,16 +25,23 @@ module Decode
|
|
25
25
|
module Ruby
|
26
26
|
# A Ruby-specific attribute.
|
27
27
|
class Attribute < Definition
|
28
|
-
# The keyword that defined the attribute.
|
29
|
-
# @return [String]
|
30
|
-
def keyword
|
31
|
-
@node.children[1]
|
32
|
-
end
|
33
|
-
|
34
28
|
# The short form of the attribute.
|
35
29
|
# e.g. `attr :value`.
|
36
30
|
def short_form
|
37
|
-
|
31
|
+
case @node.type
|
32
|
+
when :block
|
33
|
+
"#{@name} { ... }"
|
34
|
+
else
|
35
|
+
@node.location.expression.source
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def long_form
|
40
|
+
if @node.location.line == @node.location.last_line
|
41
|
+
@node.location.expression.source
|
42
|
+
else
|
43
|
+
short_form
|
44
|
+
end
|
38
45
|
end
|
39
46
|
end
|
40
47
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative 'definition'
|
22
|
+
|
23
|
+
module Decode
|
24
|
+
module Language
|
25
|
+
module Ruby
|
26
|
+
# A Ruby-specific block which might carry other definitions.
|
27
|
+
class Block < Definition
|
28
|
+
# A block can sometimes be a container for other definitions.
|
29
|
+
def container?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def nested_name
|
34
|
+
".#{name}"
|
35
|
+
end
|
36
|
+
|
37
|
+
# The short form of the block.
|
38
|
+
# e.g. `foo`.
|
39
|
+
def short_form
|
40
|
+
@name.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
# The long form of the block.
|
44
|
+
# e.g. `foo(:bar)`.
|
45
|
+
def long_form
|
46
|
+
if @node.location.line == @node.location.last_line
|
47
|
+
@node.location.expression.source
|
48
|
+
else
|
49
|
+
@node.children[0].location.expression.source
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# The fully qualified name of the block.
|
54
|
+
# e.g. `::Barnyard::foo`.
|
55
|
+
def qualified_form
|
56
|
+
self.qualified_name
|
57
|
+
end
|
58
|
+
|
59
|
+
def convert(kind)
|
60
|
+
case kind
|
61
|
+
when :attr
|
62
|
+
Attribute.new(@node, @name,
|
63
|
+
comments: @comments, parent: @parent, language: @language
|
64
|
+
)
|
65
|
+
else
|
66
|
+
super
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative 'definition'
|
22
|
+
|
23
|
+
module Decode
|
24
|
+
module Language
|
25
|
+
module Ruby
|
26
|
+
# A Ruby-specific block which might carry other definitions.
|
27
|
+
class Call < Definition
|
28
|
+
# A block can sometimes be a container for other definitions.
|
29
|
+
def container?
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
# The short form of the class.
|
34
|
+
# e.g. `foo`.
|
35
|
+
def short_form
|
36
|
+
@name.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
# The long form of the class.
|
40
|
+
# e.g. `foo(:bar)`.
|
41
|
+
def long_form
|
42
|
+
if @node.location.line == @node.location.last_line
|
43
|
+
@node.location.expression.source
|
44
|
+
else
|
45
|
+
self.short_form
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# The fully qualified name of the block.
|
50
|
+
# e.g. `class ::Barnyard::Dog`.
|
51
|
+
def qualified_form
|
52
|
+
self.qualified_name
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -26,12 +26,16 @@ module Decode
|
|
26
26
|
# A Ruby-specific definition.
|
27
27
|
class Definition < Decode::Definition
|
28
28
|
# Initialize the definition from the syntax tree node.
|
29
|
-
def initialize(
|
30
|
-
super(
|
29
|
+
def initialize(node, *arguments, **options)
|
30
|
+
super(*arguments, **options)
|
31
31
|
|
32
32
|
@node = node
|
33
33
|
end
|
34
34
|
|
35
|
+
def nested_name
|
36
|
+
"\##{@name}"
|
37
|
+
end
|
38
|
+
|
35
39
|
# The parser syntax tree node.
|
36
40
|
attr :node
|
37
41
|
|
@@ -51,6 +51,17 @@ module Decode
|
|
51
51
|
self.short_form
|
52
52
|
end
|
53
53
|
end
|
54
|
+
|
55
|
+
def convert(kind)
|
56
|
+
case kind
|
57
|
+
when :attr
|
58
|
+
Attribute.new(@node, @name,
|
59
|
+
comments: @comments, parent: @parent, language: @language
|
60
|
+
)
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
64
|
+
end
|
54
65
|
end
|
55
66
|
end
|
56
67
|
end
|
@@ -20,7 +20,11 @@
|
|
20
20
|
|
21
21
|
require 'parser/current'
|
22
22
|
|
23
|
+
require_relative '../../scope'
|
24
|
+
|
23
25
|
require_relative 'attribute'
|
26
|
+
require_relative 'block'
|
27
|
+
require_relative 'call'
|
24
28
|
require_relative 'class'
|
25
29
|
require_relative 'constant'
|
26
30
|
require_relative 'function'
|
@@ -34,12 +38,12 @@ module Decode
|
|
34
38
|
module Ruby
|
35
39
|
# The Ruby source code parser.
|
36
40
|
class Parser
|
37
|
-
# Extract
|
38
|
-
def
|
41
|
+
# Extract definitions from the given input file.
|
42
|
+
def definitions_for(input, &block)
|
39
43
|
top, comments = ::Parser::CurrentRuby.parse_with_comments(input.read)
|
40
44
|
|
41
45
|
if top
|
42
|
-
|
46
|
+
walk_definitions(top, comments, &block)
|
43
47
|
end
|
44
48
|
end
|
45
49
|
|
@@ -69,68 +73,69 @@ module Decode
|
|
69
73
|
end
|
70
74
|
|
71
75
|
# Walk over the syntax tree and extract relevant definitions with their associated comments.
|
72
|
-
def
|
76
|
+
def walk_definitions(node, comments, parent = nil, &block)
|
73
77
|
case node.type
|
74
78
|
when :begin
|
75
79
|
node.children.each do |child|
|
76
|
-
|
80
|
+
walk_definitions(child, comments, parent, &block)
|
77
81
|
end
|
78
82
|
when :module
|
79
83
|
definition = Module.new(
|
80
|
-
|
81
|
-
extract_comments_for(node, comments),
|
82
|
-
parent: parent,
|
84
|
+
node, node.children[0].children[1],
|
85
|
+
comments: extract_comments_for(node, comments),
|
86
|
+
parent: parent,
|
87
|
+
language: Ruby
|
83
88
|
)
|
84
89
|
|
85
90
|
yield definition
|
86
91
|
|
87
92
|
if children = node.children[1]
|
88
|
-
|
93
|
+
walk_definitions(children, comments, definition, &block)
|
89
94
|
end
|
90
95
|
when :class
|
91
96
|
definition = Class.new(
|
92
|
-
|
93
|
-
extract_comments_for(node, comments),
|
97
|
+
node, node.children[0].children[1],
|
98
|
+
comments: extract_comments_for(node, comments),
|
94
99
|
parent: parent, language: Ruby
|
95
100
|
)
|
96
101
|
|
97
102
|
yield definition
|
98
103
|
|
99
104
|
if children = node.children[2]
|
100
|
-
|
105
|
+
walk_definitions(children, comments, definition, &block)
|
101
106
|
end
|
102
107
|
when :sclass
|
103
108
|
definition = Singleton.new(
|
104
|
-
|
105
|
-
extract_comments_for(node, comments),
|
109
|
+
node, node.children[0],
|
110
|
+
comments: extract_comments_for(node, comments),
|
106
111
|
parent: parent, language: Ruby
|
107
112
|
)
|
108
113
|
|
109
114
|
yield definition
|
110
115
|
|
111
116
|
if children = node.children[1]
|
112
|
-
|
117
|
+
walk_definitions(children, comments, definition, &block)
|
113
118
|
end
|
114
119
|
when :def
|
115
120
|
definition = Method.new(
|
116
|
-
|
117
|
-
extract_comments_for(node, comments),
|
121
|
+
node, node.children[0],
|
122
|
+
comments: extract_comments_for(node, comments),
|
118
123
|
parent: parent, language: Ruby
|
119
124
|
)
|
120
125
|
|
121
126
|
yield definition
|
122
127
|
when :defs
|
123
128
|
definition = Function.new(
|
124
|
-
|
125
|
-
extract_comments_for(node, comments),
|
129
|
+
node, node.children[1],
|
130
|
+
comments: extract_comments_for(node, comments),
|
126
131
|
parent: parent, language: Ruby
|
127
132
|
)
|
128
133
|
|
129
134
|
yield definition
|
130
135
|
when :casgn
|
131
136
|
definition = Constant.new(
|
132
|
-
|
133
|
-
extract_comments_for(node, comments),
|
137
|
+
node, node.children[1],
|
138
|
+
comments: extract_comments_for(node, comments),
|
134
139
|
parent: parent, language: Ruby
|
135
140
|
)
|
136
141
|
|
@@ -140,21 +145,98 @@ module Decode
|
|
140
145
|
case name
|
141
146
|
when :attr, :attr_reader, :attr_writer, :attr_accessor
|
142
147
|
definition = Attribute.new(
|
143
|
-
|
144
|
-
extract_comments_for(node, comments),
|
148
|
+
node, name_for(node.children[2]),
|
149
|
+
comments: extract_comments_for(node, comments),
|
145
150
|
parent: parent, language: Ruby
|
146
151
|
)
|
147
152
|
|
148
153
|
yield definition
|
154
|
+
else
|
155
|
+
extracted_comments = extract_comments_for(node, comments)
|
156
|
+
if kind = kind_for(node, extracted_comments)
|
157
|
+
definition = Call.new(
|
158
|
+
node, name_for(node, extracted_comments),
|
159
|
+
comments: extracted_comments,
|
160
|
+
parent: parent, language: Ruby
|
161
|
+
)
|
162
|
+
|
163
|
+
yield definition
|
164
|
+
end
|
165
|
+
end
|
166
|
+
when :block
|
167
|
+
extracted_comments = extract_comments_for(node, comments)
|
168
|
+
|
169
|
+
if name = name_for(node, extracted_comments)
|
170
|
+
definition = Block.new(
|
171
|
+
node, name,
|
172
|
+
comments: extracted_comments,
|
173
|
+
parent: scope_for(extracted_comments, parent, &block),
|
174
|
+
language: Ruby
|
175
|
+
)
|
176
|
+
|
177
|
+
if kind = kind_for(node, extracted_comments)
|
178
|
+
definition = definition.convert(kind)
|
179
|
+
end
|
180
|
+
|
181
|
+
yield definition
|
182
|
+
|
183
|
+
if children = node.children[2]
|
184
|
+
walk_definitions(children, comments, definition, &block)
|
185
|
+
end
|
149
186
|
end
|
150
187
|
end
|
151
188
|
end
|
152
189
|
|
153
|
-
|
190
|
+
NAME_ATTRIBUTE = /\A@name\s+(?<value>.*?)\Z/
|
191
|
+
|
192
|
+
def name_for(node, comments = nil)
|
193
|
+
comments&.each do |comment|
|
194
|
+
if match = comment.match(NAME_ATTRIBUTE)
|
195
|
+
return match[:value].to_sym
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
154
199
|
case node.type
|
155
200
|
when :sym
|
156
201
|
return node.children[0]
|
202
|
+
when :send
|
203
|
+
return node.children[1]
|
204
|
+
when :block
|
205
|
+
return node.children[0].children[1]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
KIND_ATTRIBUTE = /\A
|
210
|
+
(@(?<kind>attr)\s+(?<value>.*?))|
|
211
|
+
(@define\s+(?<kind>)\s+(?<value>.*?))
|
212
|
+
\Z/x
|
213
|
+
|
214
|
+
def kind_for(node, comments = nil)
|
215
|
+
comments&.each do |comment|
|
216
|
+
if match = comment.match(KIND_ATTRIBUTE)
|
217
|
+
return match[:kind].to_sym
|
218
|
+
end
|
157
219
|
end
|
220
|
+
|
221
|
+
return nil
|
222
|
+
end
|
223
|
+
|
224
|
+
SCOPE_ATTRIBUTE = /\A
|
225
|
+
(@scope\s+(?<names>.*?))
|
226
|
+
\Z/x
|
227
|
+
|
228
|
+
def scope_for(comments, parent = nil, &block)
|
229
|
+
comments&.each do |comment|
|
230
|
+
if match = comment.match(SCOPE_ATTRIBUTE)
|
231
|
+
return match[:names].split(/\s+/).map(&:to_sym).inject(nil) do |memo, name|
|
232
|
+
scope = Scope.new(name, parent: memo, language: Ruby)
|
233
|
+
yield scope
|
234
|
+
scope
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
return parent
|
158
240
|
end
|
159
241
|
|
160
242
|
# Extract segments from the given input file.
|
@@ -21,20 +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
|
24
|
+
# An Ruby-specific reference which can be resolved to zero or more definitions.
|
25
25
|
class Reference
|
26
|
-
KIND = {
|
27
|
-
':' => :def,
|
28
|
-
'.' => :defs,
|
29
|
-
}.freeze
|
30
|
-
|
31
26
|
# Initialize the reference.
|
32
27
|
# @param value [String] The string value of the reference.
|
33
28
|
def initialize(value)
|
34
29
|
@value = value
|
35
30
|
|
31
|
+
@lexical_path = nil
|
36
32
|
@path = nil
|
37
|
-
@kind = nil
|
38
33
|
end
|
39
34
|
|
40
35
|
# Whether the reference starts at the base of the lexical tree.
|
@@ -42,40 +37,22 @@ module Decode
|
|
42
37
|
@value.start_with?('::')
|
43
38
|
end
|
44
39
|
|
45
|
-
|
40
|
+
def lexical_path
|
41
|
+
@lexical_path ||= @value.scan(/(::|\.|#|:)?([^:.#]+)/)
|
42
|
+
end
|
46
43
|
|
47
|
-
|
48
|
-
|
49
|
-
def path
|
50
|
-
if @path.nil?
|
51
|
-
@path = @value.split(/::/)
|
52
|
-
|
53
|
-
if last = @path.pop
|
54
|
-
if match = last.match(METHOD)
|
55
|
-
@kind = KIND[match[:kind]]
|
56
|
-
|
57
|
-
if scope = match[:scope]
|
58
|
-
@path << scope
|
59
|
-
end
|
60
|
-
|
61
|
-
@path << match[:name]
|
62
|
-
else
|
63
|
-
@path << last
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
@path = @path.map(&:to_sym)
|
68
|
-
@path.freeze
|
69
|
-
end
|
44
|
+
def best(definitions)
|
45
|
+
prefix, name = lexical_path.last
|
70
46
|
|
71
|
-
|
47
|
+
definitions.select do |definition|
|
48
|
+
prefix.nil? || definition.start_with?(prefix)
|
49
|
+
end
|
72
50
|
end
|
73
51
|
|
74
|
-
# The
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
return @kind
|
52
|
+
# The lexical path of the reference.
|
53
|
+
# @return [Array(String)]
|
54
|
+
def path
|
55
|
+
@path ||= self.lexical_path.map{|_, name| name.to_sym}
|
79
56
|
end
|
80
57
|
end
|
81
58
|
end
|
data/lib/decode/language/ruby.rb
CHANGED
@@ -31,42 +31,16 @@ module Decode
|
|
31
31
|
"ruby"
|
32
32
|
end
|
33
33
|
|
34
|
-
# The symbol which is used to separate the specified definition from the parent scope.
|
35
|
-
PREFIX = {
|
36
|
-
class: '::',
|
37
|
-
module: '::',
|
38
|
-
def: ':',
|
39
|
-
constant: '::',
|
40
|
-
defs: '.',
|
41
|
-
}.freeze
|
42
|
-
|
43
|
-
# Generate a language-specific fully qualified name.
|
44
|
-
def self.join(symbols, absolute = true)
|
45
|
-
buffer = String.new
|
46
|
-
|
47
|
-
symbols.each do |symbol|
|
48
|
-
if absolute == false
|
49
|
-
absolute = true
|
50
|
-
else
|
51
|
-
buffer << PREFIX[symbol.kind]
|
52
|
-
end
|
53
|
-
|
54
|
-
buffer << symbol.name.to_s
|
55
|
-
end
|
56
|
-
|
57
|
-
return buffer
|
58
|
-
end
|
59
|
-
|
60
34
|
# Generate a language-specific reference.
|
61
35
|
def self.reference(value)
|
62
36
|
Reference.new(value)
|
63
37
|
end
|
64
38
|
|
65
|
-
# Parse the input yielding
|
39
|
+
# Parse the input yielding definitions.
|
66
40
|
# @block `{|definition| ...}`
|
67
41
|
# @yield definition [Definition]
|
68
|
-
def self.
|
69
|
-
Parser.new.
|
42
|
+
def self.definitions_for(input, &block)
|
43
|
+
Parser.new.definitions_for(input, &block)
|
70
44
|
end
|
71
45
|
|
72
46
|
# Parse the input yielding interleaved comments and code segments.
|
data/lib/decode/language.rb
CHANGED
data/lib/decode/scope.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative 'definition'
|
22
|
+
|
23
|
+
module Decode
|
24
|
+
# An abstract namespace for nesting definitions.
|
25
|
+
class Scope < Definition
|
26
|
+
def short_form
|
27
|
+
@name
|
28
|
+
end
|
29
|
+
|
30
|
+
def container?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/decode/source.rb
CHANGED
@@ -41,11 +41,11 @@ module Decode
|
|
41
41
|
File.open(@path, &block)
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
45
|
-
return to_enum(:
|
44
|
+
def definitions(&block)
|
45
|
+
return to_enum(:definitions) unless block_given?
|
46
46
|
|
47
47
|
self.open do |file|
|
48
|
-
@language.
|
48
|
+
@language.definitions_for(file, &block)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
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.10.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-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parser
|
@@ -129,6 +129,8 @@ files:
|
|
129
129
|
- lib/decode/language.rb
|
130
130
|
- lib/decode/language/ruby.rb
|
131
131
|
- lib/decode/language/ruby/attribute.rb
|
132
|
+
- lib/decode/language/ruby/block.rb
|
133
|
+
- lib/decode/language/ruby/call.rb
|
132
134
|
- lib/decode/language/ruby/class.rb
|
133
135
|
- lib/decode/language/ruby/constant.rb
|
134
136
|
- lib/decode/language/ruby/definition.rb
|
@@ -138,9 +140,9 @@ files:
|
|
138
140
|
- lib/decode/language/ruby/parser.rb
|
139
141
|
- lib/decode/language/ruby/reference.rb
|
140
142
|
- lib/decode/language/ruby/segment.rb
|
143
|
+
- lib/decode/scope.rb
|
141
144
|
- lib/decode/segment.rb
|
142
145
|
- lib/decode/source.rb
|
143
|
-
- lib/decode/symbol.rb
|
144
146
|
- lib/decode/trie.rb
|
145
147
|
- lib/decode/version.rb
|
146
148
|
homepage: https://github.com/ioquatix/decode
|
data/lib/decode/symbol.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
# Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
8
|
-
# furnished to do so, subject to the following conditions:
|
9
|
-
#
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
11
|
-
# all copies or substantial portions of the Software.
|
12
|
-
#
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
-
# THE SOFTWARE.
|
20
|
-
|
21
|
-
require_relative 'documentation'
|
22
|
-
|
23
|
-
module Decode
|
24
|
-
# A language agnostic lexical scope.
|
25
|
-
Key = Struct.new(:kind, :name)
|
26
|
-
|
27
|
-
# A named element which represents some significant element of some kind in a computer program.
|
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.
|
34
|
-
def initialize(kind, name, parent: nil, language: parent.language)
|
35
|
-
@kind = kind
|
36
|
-
@name = name
|
37
|
-
@parent = parent
|
38
|
-
@language = language
|
39
|
-
|
40
|
-
@path = nil
|
41
|
-
@qualified_name = nil
|
42
|
-
end
|
43
|
-
|
44
|
-
# A symbol-specific key which represents the lexical scope.
|
45
|
-
# @return [Key]
|
46
|
-
def key
|
47
|
-
Key.new(@kind, @name)
|
48
|
-
end
|
49
|
-
|
50
|
-
def inspect
|
51
|
-
"\#<#{self.class} #{@kind} #{qualified_name}>"
|
52
|
-
end
|
53
|
-
|
54
|
-
# The kind of symbol.
|
55
|
-
# e.g. `:module`.
|
56
|
-
attr :kind
|
57
|
-
|
58
|
-
# The symbol name.
|
59
|
-
# e.g. `:Decode`.
|
60
|
-
attr :name
|
61
|
-
|
62
|
-
# The parent symbol, defining lexical scope.
|
63
|
-
attr :parent
|
64
|
-
|
65
|
-
# The language the symbol is defined within.
|
66
|
-
attr :language
|
67
|
-
|
68
|
-
# The qualified name is an absolute name which includes any and all namespacing.
|
69
|
-
def qualified_name
|
70
|
-
@qualified_name ||= @language.join(self.path).freeze
|
71
|
-
end
|
72
|
-
|
73
|
-
# The lexical scope which is an array of lexical {Key} instances as generated by {key}.
|
74
|
-
# @return [Array]
|
75
|
-
def path
|
76
|
-
if @path
|
77
|
-
@path
|
78
|
-
elsif @parent
|
79
|
-
@path = [*@parent.path, self.key]
|
80
|
-
else
|
81
|
-
@path = [self.key]
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# The lexical path, only taking into account the symbol names.
|
86
|
-
def lexical_path
|
87
|
-
self.path.map(&:name)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|