yard2steep 0.1.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 +7 -0
- data/.gitignore +52 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +68 -0
- data/LICENSE.txt +21 -0
- data/README.md +86 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/steep-check +8 -0
- data/bin/steep-gen +16 -0
- data/bin/steep-gen-check +7 -0
- data/example/sample1/lib/example1.rb +107 -0
- data/example/sample1/sig/example1.rbi +21 -0
- data/example/sample2/lib/2.rb +68 -0
- data/example/sample2/sig/2.rbi +25 -0
- data/exe/yard2steep +6 -0
- data/lib/yard2steep/ast/class_node.rb +95 -0
- data/lib/yard2steep/ast/constant_node.rb +21 -0
- data/lib/yard2steep/ast/i_var_node.rb +15 -0
- data/lib/yard2steep/ast/method_node.rb +21 -0
- data/lib/yard2steep/ast/p_node.rb +23 -0
- data/lib/yard2steep/ast/p_type_node.rb +26 -0
- data/lib/yard2steep/ast.rb +11 -0
- data/lib/yard2steep/cli/option.rb +27 -0
- data/lib/yard2steep/cli.rb +70 -0
- data/lib/yard2steep/engine.rb +23 -0
- data/lib/yard2steep/gen.rb +162 -0
- data/lib/yard2steep/parser.rb +584 -0
- data/lib/yard2steep/util.rb +11 -0
- data/lib/yard2steep/version.rb +3 -0
- data/lib/yard2steep.rb +9 -0
- data/sig/steep-scaffold/td.rbi +174 -0
- data/sig/yard2steep/yard2steep/ast/class_node.rbi +26 -0
- data/sig/yard2steep/yard2steep/ast/constant_node.rbi +8 -0
- data/sig/yard2steep/yard2steep/ast/i_var_node.rbi +5 -0
- data/sig/yard2steep/yard2steep/ast/method_node.rbi +9 -0
- data/sig/yard2steep/yard2steep/ast/p_node.rbi +8 -0
- data/sig/yard2steep/yard2steep/ast/p_type_node.rbi +10 -0
- data/sig/yard2steep/yard2steep/ast.rbi +0 -0
- data/sig/yard2steep/yard2steep/cli/option.rbi +12 -0
- data/sig/yard2steep/yard2steep/cli.rbi +9 -0
- data/sig/yard2steep/yard2steep/engine.rbi +3 -0
- data/sig/yard2steep/yard2steep/gen.rbi +15 -0
- data/sig/yard2steep/yard2steep/parser.rbi +52 -0
- data/sig/yard2steep/yard2steep/util.rbi +3 -0
- data/sig/yard2steep/yard2steep/version.rbi +1 -0
- data/sig/yard2steep/yard2steep.rbi +0 -0
- data/yard2steep.gemspec +29 -0
- metadata +164 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
module Yard2steep
|
2
|
+
module AST
|
3
|
+
# MethodNode represents `method` AST.
|
4
|
+
class MethodNode
|
5
|
+
# @dynamic p_list, r_type, m_name
|
6
|
+
attr_reader :p_list, :r_type, :m_name
|
7
|
+
|
8
|
+
# @param [String] m_name
|
9
|
+
# @param [Array<PNode>] p_list
|
10
|
+
# @param [String] r_type
|
11
|
+
def initialize(m_name:, p_list:, r_type:)
|
12
|
+
Util.assert! { m_name.is_a?(String) }
|
13
|
+
Util.assert! { p_list.is_a?(Array) }
|
14
|
+
Util.assert! { r_type.is_a?(String) }
|
15
|
+
@p_list = p_list
|
16
|
+
@r_type = r_type
|
17
|
+
@m_name = m_name
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Yard2steep
|
2
|
+
module AST
|
3
|
+
class PNode
|
4
|
+
# @dynamic type_node, style
|
5
|
+
attr_reader :type_node, :style
|
6
|
+
|
7
|
+
STYLE = {
|
8
|
+
normal: "STYLE.normal",
|
9
|
+
keyword: "STYLE.keyword",
|
10
|
+
keyword_with_default: "STYLE.keyword_with_default",
|
11
|
+
}
|
12
|
+
|
13
|
+
# @param [AST::PTypeNode] type_node
|
14
|
+
# @param [String] style
|
15
|
+
def initialize(type_node:, style:)
|
16
|
+
Util.assert! { type_node.is_a?(AST::PTypeNode) }
|
17
|
+
Util.assert! { STYLE.values.include?(style) }
|
18
|
+
@type_node = type_node
|
19
|
+
@style = style
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Yard2steep
|
2
|
+
module AST
|
3
|
+
# PTypeNode represents `parameter` AST.
|
4
|
+
class PTypeNode
|
5
|
+
# @dynamic p_type, p_name, kind
|
6
|
+
attr_reader :p_type, :p_name, :kind
|
7
|
+
|
8
|
+
KIND = {
|
9
|
+
normal: "KIND.normal",
|
10
|
+
block: "KIND.block",
|
11
|
+
}
|
12
|
+
|
13
|
+
# @param [String] p_type
|
14
|
+
# @param [String] p_name
|
15
|
+
# @param [String] kind
|
16
|
+
def initialize(p_type:, p_name:, kind:)
|
17
|
+
Util.assert! { p_type.is_a?(String) }
|
18
|
+
Util.assert! { p_name.is_a?(String) }
|
19
|
+
Util.assert! { KIND.values.include?(kind) }
|
20
|
+
@p_type = p_type
|
21
|
+
@p_name = p_name
|
22
|
+
@kind = kind
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'yard2steep/ast/class_node'
|
2
|
+
require 'yard2steep/ast/constant_node'
|
3
|
+
require 'yard2steep/ast/method_node'
|
4
|
+
require 'yard2steep/ast/i_var_node'
|
5
|
+
require 'yard2steep/ast/p_node'
|
6
|
+
require 'yard2steep/ast/p_type_node'
|
7
|
+
|
8
|
+
module Yard2steep
|
9
|
+
module AST
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Yard2steep
|
4
|
+
class CLI
|
5
|
+
class Option
|
6
|
+
# @dynamic src, dst, debug, debug_ast
|
7
|
+
attr_reader :src, :dst, :debug, :debug_ast
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@src = '.' # NOTE: Current directory is used as default
|
11
|
+
@dst = 'sig'
|
12
|
+
@debug = false
|
13
|
+
@debug_ast = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse!(argv)
|
17
|
+
opt = OptionParser.new
|
18
|
+
opt.on('--debug') { |v| @debug = true }
|
19
|
+
opt.on('--debug-ast') { |v| @debug_ast = true }
|
20
|
+
opt.parse!(argv)
|
21
|
+
|
22
|
+
@src = argv[0] if argv[0]
|
23
|
+
@dst = argv[1] if argv[1]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'yard2steep/cli/option'
|
2
|
+
|
3
|
+
module Yard2steep
|
4
|
+
class CLI
|
5
|
+
# @param [Array<String>] argv
|
6
|
+
# @return [void]
|
7
|
+
def self.run!(argv)
|
8
|
+
CLI.new(argv).run!
|
9
|
+
end
|
10
|
+
|
11
|
+
# @param [Array<String>] argv
|
12
|
+
def initialize(argv)
|
13
|
+
@option = Option.new
|
14
|
+
@option.parse!(argv)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [void]
|
18
|
+
def run!
|
19
|
+
traverse_dir!(src_dir)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# @return [String]
|
25
|
+
def src_dir
|
26
|
+
@src_dir ||= File.expand_path(@option.src)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [String]
|
30
|
+
def dst_dir
|
31
|
+
@dst_dir ||= File.expand_path(@option.dst)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param [String] dir
|
35
|
+
# @return [void]
|
36
|
+
def traverse_dir!(dir)
|
37
|
+
Dir.glob(File.join(dir, '*')).each do |f|
|
38
|
+
if File.file?(f)
|
39
|
+
if File.extname(f) == '.rb'
|
40
|
+
translate!(f)
|
41
|
+
end
|
42
|
+
elsif File.directory?(f)
|
43
|
+
traverse_dir!(f)
|
44
|
+
else
|
45
|
+
# Do nothing
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param [String] f
|
51
|
+
# @return [void]
|
52
|
+
def translate!(f)
|
53
|
+
text = File.read(f)
|
54
|
+
|
55
|
+
dst_file = File.join(
|
56
|
+
dst_dir,
|
57
|
+
f.gsub(/^#{Regexp.escape(src_dir)}/, '').gsub(/\.rb$/, '.rbi')
|
58
|
+
)
|
59
|
+
dst_dir = File.dirname(dst_file)
|
60
|
+
FileUtils.mkdir_p(dst_dir)
|
61
|
+
result = Engine.execute(
|
62
|
+
f,
|
63
|
+
text,
|
64
|
+
debug: @option.debug,
|
65
|
+
debug_ast: @option.debug_ast,
|
66
|
+
)
|
67
|
+
File.write(dst_file, result)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Yard2steep
|
2
|
+
class Engine
|
3
|
+
# @param [String] file
|
4
|
+
# @param [String] text
|
5
|
+
# @param [bool] debug
|
6
|
+
# @param [bool] debug_ast
|
7
|
+
# @return [String]
|
8
|
+
def self.execute(file, text, debug: false, debug_ast: false)
|
9
|
+
if debug
|
10
|
+
print "## execute\n"
|
11
|
+
print " file: #{file}\n"
|
12
|
+
end
|
13
|
+
|
14
|
+
ast = Parser.new.parse(file, text, debug: debug)
|
15
|
+
|
16
|
+
if debug_ast
|
17
|
+
print " ast: #{ast}\n\n"
|
18
|
+
end
|
19
|
+
|
20
|
+
Gen.new.gen(ast)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'yard2steep/util'
|
2
|
+
|
3
|
+
module Yard2steep
|
4
|
+
class Gen
|
5
|
+
def initialize
|
6
|
+
@out = StringIO.new # Output buffer
|
7
|
+
end
|
8
|
+
|
9
|
+
# @param [AST::ClassNode] ast
|
10
|
+
# @return [String]
|
11
|
+
def gen(ast)
|
12
|
+
gen_child!(ast, off: 0)
|
13
|
+
|
14
|
+
@out.rewind
|
15
|
+
@out.read
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param [String] s
|
19
|
+
# @param [Integer] off
|
20
|
+
# @return [void]
|
21
|
+
def emit!(s, off: 0)
|
22
|
+
Util.assert! { off >= 0 }
|
23
|
+
if off == 0
|
24
|
+
@out.print(s)
|
25
|
+
else
|
26
|
+
@out.print("#{' ' * off}#{s}")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param [AST::ClassNode] c_node
|
31
|
+
# @param [Integer] off
|
32
|
+
# @return [void]
|
33
|
+
def gen_child!(c_node, off:)
|
34
|
+
Util.assert! { c_node.is_a?(AST::ClassNode) }
|
35
|
+
|
36
|
+
# TODO(south37) We should check `main` by class_node's type (not name).
|
37
|
+
if c_node.c_name == 'main'
|
38
|
+
# NOTE: In main, steep does not check method type, so we does not
|
39
|
+
# generate type definition of methods.
|
40
|
+
gen_children!(c_node, off: 0)
|
41
|
+
else
|
42
|
+
if (c_node.m_list.size > 0) || (c_node.ivar_list.size > 0)
|
43
|
+
emit! "#{c_node.kind} #{c_node.long_name}\n", off: off
|
44
|
+
gen_ivar_list!(c_node, off: off + 2)
|
45
|
+
gen_m_list!(c_node, off: off + 2)
|
46
|
+
emit! "end\n", off: off
|
47
|
+
end
|
48
|
+
gen_c_list!(c_node, off: off)
|
49
|
+
gen_children!(c_node, off: off)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param [AST::ClassNode] c_node
|
54
|
+
# @param [Integer] off
|
55
|
+
# @return [void]
|
56
|
+
def gen_m_list!(c_node, off:)
|
57
|
+
c_node.m_list.each do |m_node|
|
58
|
+
gen_m!(m_node, off: off)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# NOTE: In current impl, ivar's type is declared as any
|
63
|
+
#
|
64
|
+
# @param [AST::ClassNode] c_node
|
65
|
+
# @param [Integer] off
|
66
|
+
# @return [void]
|
67
|
+
def gen_ivar_list!(c_node, off:)
|
68
|
+
c_node.ivar_list.each do |ivar_node|
|
69
|
+
emit! "@#{ivar_node.name}: any\n", off: off
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param [AST::ClassNode] c_node
|
74
|
+
# @param [Integer] off
|
75
|
+
# @return [void]
|
76
|
+
def gen_c_list!(c_node, off:)
|
77
|
+
c_node.c_list.each do |c_node|
|
78
|
+
gen_c!(c_node, off: off)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# @param [AST::ClassNode] c_node
|
83
|
+
# @param [Integer] off
|
84
|
+
# @return [void]
|
85
|
+
def gen_children!(c_node, off:)
|
86
|
+
c_node.children.each do |child|
|
87
|
+
gen_child!(child, off: off)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# @param [AST::ConstantNode] c_node
|
92
|
+
# @param [Integer] off
|
93
|
+
# @return [void]
|
94
|
+
def gen_c!(c_node, off:)
|
95
|
+
Util.assert! { c_node.is_a?(AST::ConstantNode) }
|
96
|
+
# NOTE: Use any as constant type.
|
97
|
+
emit! "#{c_node.long_name}: any\n", off: off
|
98
|
+
end
|
99
|
+
|
100
|
+
# @param [AST::MethodNode] m_node
|
101
|
+
# @param [Integer] off
|
102
|
+
# @return [void]
|
103
|
+
def gen_m!(m_node, off:)
|
104
|
+
Util.assert! { m_node.is_a?(AST::MethodNode) }
|
105
|
+
emit! "def #{m_node.m_name}: ", off: off
|
106
|
+
p_list = m_node.p_list
|
107
|
+
|
108
|
+
# NOTE: Check presence of block variable at last.
|
109
|
+
if p_list[-1] && (p_list[-1].type_node.kind == AST::PTypeNode::KIND[:block])
|
110
|
+
gen_p_list!(p_list[0..-2])
|
111
|
+
gen_block_p!(p_list[-1])
|
112
|
+
else
|
113
|
+
gen_p_list!(p_list)
|
114
|
+
end
|
115
|
+
|
116
|
+
emit! "-> #{m_node.r_type}\n"
|
117
|
+
end
|
118
|
+
|
119
|
+
# @param [Array<AST::PNode>] p_list
|
120
|
+
# @return [void]
|
121
|
+
def gen_p_list!(p_list)
|
122
|
+
len = p_list.size
|
123
|
+
if len > 0
|
124
|
+
emit! "("
|
125
|
+
|
126
|
+
p_list.each.with_index do |p, i|
|
127
|
+
gen_m_p!(p)
|
128
|
+
if i < (len - 1)
|
129
|
+
emit!(", ")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
emit! ") "
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# TODO(south37) Represents block param and return type separately
|
138
|
+
#
|
139
|
+
# @param [AST::PNode] p
|
140
|
+
# @return [void]
|
141
|
+
def gen_block_p!(p)
|
142
|
+
emit! "#{p.type_node.p_type} "
|
143
|
+
end
|
144
|
+
|
145
|
+
# @param [AST::PNode] p_node
|
146
|
+
# @return [void]
|
147
|
+
def gen_m_p!(p_node)
|
148
|
+
t = p_node.type_node
|
149
|
+
|
150
|
+
case p_node.style
|
151
|
+
when AST::PNode::STYLE[:normal]
|
152
|
+
emit! t.p_type
|
153
|
+
when AST::PNode::STYLE[:keyword]
|
154
|
+
emit! "#{t.p_name}: #{t.p_type}"
|
155
|
+
when AST::PNode::STYLE[:keyword_with_default]
|
156
|
+
emit! "?#{t.p_name}: #{t.p_type}"
|
157
|
+
else
|
158
|
+
raise "invalid style: #{p_node.style}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|