proc_to_ast 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/proc_to_ast.rb +81 -45
- data/lib/proc_to_ast/version.rb +1 -1
- data/spec/proc_to_ast_spec.rb +44 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af92479d75ab4508025af6183f02826935da753f
|
4
|
+
data.tar.gz: e7d4661d31825657ab4d3f87b5f5f773a083d89f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb3e438c238c4e153e9cb24adfcc11fc090a6dc713bad95dcd72748062983a62fc0eae58cc9a79721a365e4ffa7022babeb74188b3208acc010d0d13717a2048
|
7
|
+
data.tar.gz: ea3ca120b291b266d5bda7b2fc0541400e38d696c80ac836d36d9b48bc90f3171e976c3f2e18efbe107ee2480ae5870cca16e45330db32832c12712f34f3438c
|
data/lib/proc_to_ast.rb
CHANGED
@@ -7,74 +7,110 @@ require 'coderay'
|
|
7
7
|
module ProcToAst
|
8
8
|
class MultiMatchError < StandardError; end
|
9
9
|
|
10
|
-
class
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
}.compact
|
18
|
-
else
|
19
|
-
[node]
|
20
|
-
end
|
10
|
+
class Parser
|
11
|
+
attr_reader :parser
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@parser = ::Parser::CurrentRuby.default_parser
|
15
|
+
@parser.diagnostics.consumer = ->(diagnostic) {} # suppress error message
|
21
16
|
end
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
# Read file and try parsing
|
19
|
+
# if success parse, find proc AST
|
20
|
+
#
|
21
|
+
# @param filename [String] reading file path
|
22
|
+
# @param linenum [Integer] start line number
|
23
|
+
# @param retry_limit [Integer]
|
24
|
+
# @return [Parser::AST::Node] Proc AST
|
25
|
+
def parse(filename, linenum, retry_limit = 20)
|
26
|
+
@filename, @linenum = filename, linenum
|
27
|
+
file = File.open(filename, "rb")
|
26
28
|
|
27
|
-
|
29
|
+
(linenum - 1).times { file.gets }
|
30
|
+
buf = []
|
31
|
+
try_count = 0
|
28
32
|
|
29
|
-
|
33
|
+
begin
|
34
|
+
try_count += 1
|
30
35
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
36
|
+
buf << file.gets
|
37
|
+
do_parse(buf)
|
38
|
+
rescue ::Parser::SyntaxError => e
|
39
|
+
node = trim_and_retry(buf)
|
35
40
|
|
36
|
-
return
|
37
|
-
|
41
|
+
return node unless node.nil?
|
42
|
+
retry if try_count < retry_limit
|
38
43
|
|
39
|
-
|
44
|
+
raise e
|
45
|
+
ensure
|
46
|
+
file.close
|
47
|
+
end
|
40
48
|
end
|
41
|
-
end
|
42
|
-
end
|
43
49
|
|
44
|
-
|
45
|
-
def to_ast(retry_limit = 20)
|
46
|
-
filename, linenum = source_location
|
47
|
-
file = File.open(filename, "rb")
|
50
|
+
private
|
48
51
|
|
49
|
-
(
|
50
|
-
buf = []
|
51
|
-
try_count = 0
|
52
|
-
|
53
|
-
parser = Parser::CurrentRuby.default_parser
|
54
|
-
parser.diagnostics.consumer = ->(diagnostic) {} # suppress error message
|
55
|
-
begin
|
52
|
+
def do_parse(buf)
|
56
53
|
parser.reset
|
57
|
-
try_count += 1
|
58
|
-
|
59
|
-
buf << file.gets
|
60
54
|
source = buf.join.force_encoding(parser.default_encoding)
|
61
55
|
|
62
|
-
source_buffer = Parser::Source::Buffer.new(filename, linenum)
|
56
|
+
source_buffer = ::Parser::Source::Buffer.new(@filename, @linenum)
|
63
57
|
source_buffer.source = source
|
64
58
|
node = parser.parse(source_buffer)
|
65
|
-
block_nodes =
|
59
|
+
block_nodes = traverse_node(node)
|
60
|
+
|
66
61
|
if block_nodes.length == 1
|
67
62
|
block_nodes.first
|
68
63
|
else
|
69
64
|
raise ProcToAst::MultiMatchError
|
70
65
|
end
|
71
|
-
rescue Parser::SyntaxError
|
72
|
-
retry if try_count < retry_limit
|
73
66
|
end
|
67
|
+
|
68
|
+
# Remove tail comma or Hash syntax, and retry parsing
|
69
|
+
def trim_and_retry(buf)
|
70
|
+
*lines, last = buf
|
71
|
+
|
72
|
+
# For inner Array or Hash or Arguments list.
|
73
|
+
lines << last.gsub(/,\s*/, "")
|
74
|
+
|
75
|
+
lines[0] = lines[0]
|
76
|
+
.gsub(/[0-9a-zA-Z_]+:\s*/, "")
|
77
|
+
.gsub(/[^\s]+\s*=>\s*/, "")
|
78
|
+
|
79
|
+
begin
|
80
|
+
do_parse(lines)
|
81
|
+
rescue ::Parser::SyntaxError
|
82
|
+
nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def traverse_node(node)
|
87
|
+
if node.type != :block
|
88
|
+
node.children.flat_map { |child|
|
89
|
+
if child.is_a?(AST::Node)
|
90
|
+
traverse_node(child)
|
91
|
+
end
|
92
|
+
}.compact
|
93
|
+
else
|
94
|
+
[node]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class Proc
|
101
|
+
# @param retry_limit [Integer]
|
102
|
+
# @return [Parser::AST::Node] Proc AST
|
103
|
+
def to_ast(retry_limit = 20)
|
104
|
+
filename, linenum = source_location
|
105
|
+
parser = ProcToAst::Parser.new
|
106
|
+
parser.parse(filename, linenum, retry_limit)
|
74
107
|
end
|
75
108
|
|
76
|
-
|
77
|
-
|
109
|
+
# @param highlight [Boolean] enable output highlight
|
110
|
+
# @param retry_limit [Integer]
|
111
|
+
# @return [String] proc source code
|
112
|
+
def to_source(highlight: false, retry_limit: 20)
|
113
|
+
source = Unparser.unparse(to_ast(retry_limit))
|
78
114
|
if highlight
|
79
115
|
CodeRay.scan(source, :ruby).terminal
|
80
116
|
else
|
data/lib/proc_to_ast/version.rb
CHANGED
data/spec/proc_to_ast_spec.rb
CHANGED
@@ -48,6 +48,50 @@ describe Proc do
|
|
48
48
|
|
49
49
|
expect{ fuga.to_ast }.to raise_error(ProcToAst::MultiMatchError)
|
50
50
|
end
|
51
|
+
|
52
|
+
it "inner array" do
|
53
|
+
array = [
|
54
|
+
1,
|
55
|
+
-> { 2 },
|
56
|
+
[
|
57
|
+
-> {
|
58
|
+
3
|
59
|
+
},
|
60
|
+
],
|
61
|
+
proc do |a|
|
62
|
+
[
|
63
|
+
a,
|
64
|
+
]
|
65
|
+
end
|
66
|
+
]
|
67
|
+
expect(array[1].to_ast).to be_a(AST::Node)
|
68
|
+
expect(array[1].to_source).to eq("lambda do\n 2\nend")
|
69
|
+
expect(array[2][0].to_source).to eq("lambda do\n 3\nend")
|
70
|
+
expect(array[3].to_source).to eq("proc do |a|\n [a]\nend")
|
71
|
+
end
|
72
|
+
|
73
|
+
it "inner hash" do
|
74
|
+
hash_oneline = {a: -> { 1 }}
|
75
|
+
expect(hash_oneline[:a].to_ast).to be_a(AST::Node)
|
76
|
+
expect(hash_oneline[:a].to_source).to eq("lambda do\n 1\nend")
|
77
|
+
|
78
|
+
hash = {
|
79
|
+
a: -> { 1 },
|
80
|
+
:b => -> { 2 },
|
81
|
+
c: -> {
|
82
|
+
3
|
83
|
+
},
|
84
|
+
d: -> { 4 }
|
85
|
+
}
|
86
|
+
expect(hash[:a].to_ast).to be_a(AST::Node)
|
87
|
+
expect(hash[:a].to_source).to eq("lambda do\n 1\nend")
|
88
|
+
expect(hash[:b].to_ast).to be_a(AST::Node)
|
89
|
+
expect(hash[:b].to_source).to eq("lambda do\n 2\nend")
|
90
|
+
expect(hash[:c].to_ast).to be_a(AST::Node)
|
91
|
+
expect(hash[:c].to_source).to eq("lambda do\n 3\nend")
|
92
|
+
expect(hash[:d].to_ast).to be_a(AST::Node)
|
93
|
+
expect(hash[:d].to_source).to eq("lambda do\n 4\nend")
|
94
|
+
end
|
51
95
|
end
|
52
96
|
end
|
53
97
|
|