yard2steep 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|