yard2steep 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +52 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +68 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +86 -0
  8. data/Rakefile +2 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/bin/steep-check +8 -0
  12. data/bin/steep-gen +16 -0
  13. data/bin/steep-gen-check +7 -0
  14. data/example/sample1/lib/example1.rb +107 -0
  15. data/example/sample1/sig/example1.rbi +21 -0
  16. data/example/sample2/lib/2.rb +68 -0
  17. data/example/sample2/sig/2.rbi +25 -0
  18. data/exe/yard2steep +6 -0
  19. data/lib/yard2steep/ast/class_node.rb +95 -0
  20. data/lib/yard2steep/ast/constant_node.rb +21 -0
  21. data/lib/yard2steep/ast/i_var_node.rb +15 -0
  22. data/lib/yard2steep/ast/method_node.rb +21 -0
  23. data/lib/yard2steep/ast/p_node.rb +23 -0
  24. data/lib/yard2steep/ast/p_type_node.rb +26 -0
  25. data/lib/yard2steep/ast.rb +11 -0
  26. data/lib/yard2steep/cli/option.rb +27 -0
  27. data/lib/yard2steep/cli.rb +70 -0
  28. data/lib/yard2steep/engine.rb +23 -0
  29. data/lib/yard2steep/gen.rb +162 -0
  30. data/lib/yard2steep/parser.rb +584 -0
  31. data/lib/yard2steep/util.rb +11 -0
  32. data/lib/yard2steep/version.rb +3 -0
  33. data/lib/yard2steep.rb +9 -0
  34. data/sig/steep-scaffold/td.rbi +174 -0
  35. data/sig/yard2steep/yard2steep/ast/class_node.rbi +26 -0
  36. data/sig/yard2steep/yard2steep/ast/constant_node.rbi +8 -0
  37. data/sig/yard2steep/yard2steep/ast/i_var_node.rbi +5 -0
  38. data/sig/yard2steep/yard2steep/ast/method_node.rbi +9 -0
  39. data/sig/yard2steep/yard2steep/ast/p_node.rbi +8 -0
  40. data/sig/yard2steep/yard2steep/ast/p_type_node.rbi +10 -0
  41. data/sig/yard2steep/yard2steep/ast.rbi +0 -0
  42. data/sig/yard2steep/yard2steep/cli/option.rbi +12 -0
  43. data/sig/yard2steep/yard2steep/cli.rbi +9 -0
  44. data/sig/yard2steep/yard2steep/engine.rbi +3 -0
  45. data/sig/yard2steep/yard2steep/gen.rbi +15 -0
  46. data/sig/yard2steep/yard2steep/parser.rbi +52 -0
  47. data/sig/yard2steep/yard2steep/util.rbi +3 -0
  48. data/sig/yard2steep/yard2steep/version.rbi +1 -0
  49. data/sig/yard2steep/yard2steep.rbi +0 -0
  50. data/yard2steep.gemspec +29 -0
  51. 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