decode 0.9.0 → 0.10.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/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
|