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.
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