unparser 0.1.10 → 0.1.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Changelog.md +4 -0
- data/Gemfile +0 -2
- data/Guardfile +3 -1
- data/config/flay.yml +1 -1
- data/lib/unparser/ast.rb +24 -51
- data/lib/unparser/ast/local_variable_scope.rb +93 -29
- data/lib/unparser/emitter.rb +10 -4
- data/lib/unparser/emitter/if.rb +1 -1
- data/lib/unparser/emitter/repetition.rb +1 -1
- data/lib/unparser/emitter/send.rb +1 -1
- data/spec/integration/unparser/corpus_spec.rb +3 -0
- data/unparser.gemspec +2 -2
- metadata +34 -33
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e21010b4e759db5c1502593760f8480381ed63fd
|
4
|
+
data.tar.gz: c6549debc54796bf1151ee522d85caef6b6db7a2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 342af109c4ede135af5f555073dcb2b10c1bdef9b1873598e5fc84dd5c57d517e8d6d5f51c4a856695f10fd0ead95ab68a19acd060c8acda82564bf95a5de2e0
|
7
|
+
data.tar.gz: 9abf7e144000af5631f23440abc8557210a67dbc935793ab3add786ba838ae77f19c5f5a428932f92c6a596c6892b68d7051c2dbd0a5afc32c07d4f1d91ff74c
|
data/Changelog.md
CHANGED
data/Gemfile
CHANGED
data/Guardfile
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
ENV['GUARD'] = '1'
|
4
|
+
|
3
5
|
guard :bundler do
|
4
6
|
watch('Gemfile')
|
5
7
|
end
|
6
8
|
|
7
|
-
guard :rspec, :all_on_start => false, :all_after_pass => false, :
|
9
|
+
guard :rspec, :all_on_start => false, :all_after_pass => false, :cmd => 'bundle exec rspec --fail-fast --seed 1' do
|
8
10
|
# run all specs if the spec_helper or supporting files files are modified
|
9
11
|
watch('spec/spec_helper.rb') { 'spec/unit' }
|
10
12
|
watch(%r{\Aspec/(?:lib|support|shared)/.+\.rb\z}) { 'spec/unit' }
|
data/config/flay.yml
CHANGED
data/lib/unparser/ast.rb
CHANGED
@@ -7,73 +7,46 @@ module Unparser
|
|
7
7
|
FIRST_CHILD = ->(node) { node.children.first }.freeze
|
8
8
|
TAUTOLOGY = ->(node) { true }.freeze
|
9
9
|
|
10
|
-
|
10
|
+
RESET_NODES = [:module, :class, :sclass, :def, :defs].freeze
|
11
|
+
INHERIT_NODES = [:block].freeze
|
12
|
+
CLOSE_NODES = (RESET_NODES + INHERIT_NODES).freeze
|
13
|
+
|
14
|
+
# Nodes that assign a local variable
|
15
|
+
#
|
16
|
+
# FIXME: Kwargs are missing.
|
17
|
+
#
|
18
|
+
ASSIGN_NODES = [:lvasgn, :arg, :optarg, :restarg].freeze
|
19
|
+
|
20
|
+
# Test for local variable inherited scope reset
|
11
21
|
#
|
12
|
-
# @param [Parser::AST::Node]
|
13
|
-
# @param [Parser::AST::Node] assignment
|
22
|
+
# @param [Parser::AST::Node] node
|
14
23
|
#
|
15
24
|
# @return [true]
|
16
|
-
# if local variable
|
25
|
+
# if local variable scope must NOT be reset
|
17
26
|
#
|
18
27
|
# @return [false]
|
19
28
|
# otherwise
|
20
29
|
#
|
21
30
|
# @api private
|
22
31
|
#
|
23
|
-
def self.
|
24
|
-
|
25
|
-
AST::LocalVariableScope.each(root) do |node, current, before|
|
26
|
-
if node.equal?(assignment) && current.include?(name) && !before.include?(name)
|
27
|
-
return true
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
false
|
32
|
+
def self.not_close_scope?(node)
|
33
|
+
!CLOSE_NODES.include?(node.type)
|
32
34
|
end
|
33
35
|
|
34
|
-
# Test
|
36
|
+
# Test for local variable scope reset
|
35
37
|
#
|
36
|
-
# @param [Parser::AST::Node] root
|
37
38
|
# @param [Parser::AST::Node] node
|
38
|
-
# @param [Symbol] name
|
39
39
|
#
|
40
40
|
# @return [true]
|
41
|
-
# if local variable
|
41
|
+
# if local variable scope must NOT be reset
|
42
42
|
#
|
43
43
|
# @return [false]
|
44
44
|
# otherwise
|
45
45
|
#
|
46
46
|
# @api private
|
47
47
|
#
|
48
|
-
def self.
|
49
|
-
|
50
|
-
if child.equal?(node) && current.include?(name)
|
51
|
-
return true
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
false
|
56
|
-
end
|
57
|
-
|
58
|
-
# Test if local variables where first assigned in body and read by conditional
|
59
|
-
#
|
60
|
-
# @param [Parser::AST::Node] root
|
61
|
-
# @param [Parser::AST::Node] conditional
|
62
|
-
# @param [Parser::AST::Node] body
|
63
|
-
#
|
64
|
-
# @api private
|
65
|
-
#
|
66
|
-
def self.first_assignment_in_body_and_used_in_condition?(root, body, condition)
|
67
|
-
condition_reads = local_variables_read_in_scope(condition)
|
68
|
-
|
69
|
-
candidates = AST.local_variable_assignments_in_scope(body).select do |node|
|
70
|
-
name = node.children.first
|
71
|
-
condition_reads.include?(name)
|
72
|
-
end
|
73
|
-
|
74
|
-
candidates.any? do |node|
|
75
|
-
first_assignment?(root, node)
|
76
|
-
end
|
48
|
+
def self.not_reset_scope?(node)
|
49
|
+
!RESET_NODES.include?(node.type)
|
77
50
|
end
|
78
51
|
|
79
52
|
# Return local variables that get assigned in scope
|
@@ -84,11 +57,11 @@ module Unparser
|
|
84
57
|
#
|
85
58
|
# @api private
|
86
59
|
#
|
87
|
-
def self.
|
60
|
+
def self.local_variable_assignments(node)
|
88
61
|
Enumerator.new(
|
89
62
|
node,
|
90
|
-
|
91
|
-
).types(
|
63
|
+
method(:not_reset_scope?)
|
64
|
+
).types(ASSIGN_NODES)
|
92
65
|
end
|
93
66
|
|
94
67
|
# Return local variables read
|
@@ -99,10 +72,10 @@ module Unparser
|
|
99
72
|
#
|
100
73
|
# @api private
|
101
74
|
#
|
102
|
-
def self.
|
75
|
+
def self.local_variable_reads(node)
|
103
76
|
Enumerator.new(
|
104
77
|
node,
|
105
|
-
|
78
|
+
method(:not_close_scope?)
|
106
79
|
).type(:lvar).map(&FIRST_CHILD).to_set
|
107
80
|
end
|
108
81
|
|
@@ -2,73 +2,137 @@
|
|
2
2
|
|
3
3
|
module Unparser
|
4
4
|
module AST
|
5
|
-
# Local variable scope enumerator
|
6
|
-
class LocalVariableScope
|
7
|
-
include Enumerable
|
8
5
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
# Nodes that assign a local variable
|
14
|
-
#
|
15
|
-
# FIXME: Kwargs are missing.
|
16
|
-
#
|
17
|
-
ASSIGN_NODES = [:lvasgn, :arg, :optarg, :restarg].freeze
|
6
|
+
# Calculated local variable scope for a given node
|
7
|
+
class LocalVariableScope
|
8
|
+
include Enumerable, Adamantium, Concord.new(:node)
|
18
9
|
|
19
10
|
# Initialize object
|
20
11
|
#
|
12
|
+
# @param [Parser::AST::Node] node
|
13
|
+
#
|
21
14
|
# @return [undefined]
|
22
15
|
#
|
23
16
|
# @api private
|
24
17
|
#
|
25
|
-
def initialize
|
26
|
-
|
18
|
+
def initialize(node)
|
19
|
+
items = []
|
20
|
+
LocalVariableScopeEnumerator.each(node) do |*scope|
|
21
|
+
items << scope
|
22
|
+
end
|
23
|
+
@items = items
|
24
|
+
@node = node
|
27
25
|
end
|
28
26
|
|
29
|
-
#
|
27
|
+
# Test if local variable was first at given assignment
|
30
28
|
#
|
31
29
|
# @param [Parser::AST::Node] node
|
32
30
|
#
|
33
|
-
# @return [
|
31
|
+
# @return [true]
|
32
|
+
# if local variable was firstly introduced in body
|
33
|
+
#
|
34
|
+
# @return [false]
|
35
|
+
# otherwise
|
34
36
|
#
|
35
37
|
# @api private
|
36
38
|
#
|
37
|
-
def
|
38
|
-
|
39
|
-
|
39
|
+
def first_assignment?(node)
|
40
|
+
name = node.children.first
|
41
|
+
match(node) do |current, before|
|
42
|
+
current.include?(name) && !before.include?(name)
|
43
|
+
end
|
40
44
|
end
|
41
45
|
|
42
|
-
# Test
|
46
|
+
# Test if local variable is defined for given node
|
43
47
|
#
|
44
48
|
# @param [Parser::AST::Node] node
|
49
|
+
# @param [Symbol] name
|
45
50
|
#
|
46
51
|
# @return [true]
|
47
|
-
# if local variable
|
52
|
+
# if local variable is defined
|
48
53
|
#
|
49
54
|
# @return [false]
|
50
55
|
# otherwise
|
51
56
|
#
|
52
57
|
# @api private
|
53
58
|
#
|
54
|
-
def
|
55
|
-
|
59
|
+
def local_variable_defined_for_node?(node, name)
|
60
|
+
match(node) do |current|
|
61
|
+
current.include?(name)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Test if local variables where first assigned in body and read by conditional
|
66
|
+
#
|
67
|
+
# @param [Parser::AST::Node] conditional
|
68
|
+
# @param [Parser::AST::Node] body
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
#
|
72
|
+
def first_assignment_in_body_and_used_in_condition?(body, condition)
|
73
|
+
condition_reads = AST.local_variable_reads(condition)
|
74
|
+
|
75
|
+
candidates = AST.local_variable_assignments(body).select do |node|
|
76
|
+
name = node.children.first
|
77
|
+
condition_reads.include?(name)
|
78
|
+
end
|
79
|
+
|
80
|
+
candidates.any? do |node|
|
81
|
+
first_assignment?(node)
|
82
|
+
end
|
56
83
|
end
|
57
84
|
|
58
|
-
|
85
|
+
private
|
86
|
+
|
87
|
+
# Match node
|
59
88
|
#
|
60
89
|
# @param [Parser::AST::Node] node
|
90
|
+
# if block given
|
61
91
|
#
|
62
92
|
# @return [true]
|
63
|
-
# if
|
93
|
+
# if found an matched
|
64
94
|
#
|
65
95
|
# @return [false]
|
66
96
|
# otherwise
|
67
97
|
#
|
68
98
|
# @api private
|
69
99
|
#
|
70
|
-
def
|
71
|
-
|
100
|
+
def match(neddle)
|
101
|
+
@items.each do |node, current, before|
|
102
|
+
if node.equal?(neddle)
|
103
|
+
return yield(current, before)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
false
|
107
|
+
end
|
108
|
+
|
109
|
+
end # LocalVariableScope
|
110
|
+
|
111
|
+
# Local variable scope enumerator
|
112
|
+
class LocalVariableScopeEnumerator
|
113
|
+
include Enumerable
|
114
|
+
|
115
|
+
# Initialize object
|
116
|
+
#
|
117
|
+
# @return [undefined]
|
118
|
+
#
|
119
|
+
# @api private
|
120
|
+
#
|
121
|
+
def initialize
|
122
|
+
@stack = [Set.new]
|
123
|
+
end
|
124
|
+
|
125
|
+
# Enumerate each node with its local variable scope
|
126
|
+
#
|
127
|
+
# @param [Parser::AST::Node] node
|
128
|
+
#
|
129
|
+
# @return [self]
|
130
|
+
#
|
131
|
+
# @api private
|
132
|
+
#
|
133
|
+
def self.each(node, &block)
|
134
|
+
new.each(node, &block)
|
135
|
+
self
|
72
136
|
end
|
73
137
|
|
74
138
|
# Enumerate local variable scope scope
|
@@ -109,7 +173,7 @@ module Unparser
|
|
109
173
|
def visit(node, &block)
|
110
174
|
before = current.dup
|
111
175
|
enter(node)
|
112
|
-
yield node, current, before
|
176
|
+
yield node, current.dup, before
|
113
177
|
node.children.each do |child|
|
114
178
|
if child.kind_of?(Parser::AST::Node)
|
115
179
|
visit(child, &block)
|
@@ -193,6 +257,6 @@ module Unparser
|
|
193
257
|
@stack.pop
|
194
258
|
end
|
195
259
|
|
196
|
-
end #
|
260
|
+
end # LocalVariableScopeEnumerator
|
197
261
|
end # AST
|
198
262
|
end # Unparser
|
data/lib/unparser/emitter.rb
CHANGED
@@ -15,8 +15,14 @@ module Unparser
|
|
15
15
|
#
|
16
16
|
# @api private
|
17
17
|
#
|
18
|
-
def
|
19
|
-
node
|
18
|
+
def local_variable_scope
|
19
|
+
AST::LocalVariableScope.new(node)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.included(descendant)
|
23
|
+
descendant.class_eval do
|
24
|
+
memoize :local_variable_scope
|
25
|
+
end
|
20
26
|
end
|
21
27
|
|
22
28
|
end
|
@@ -27,8 +33,8 @@ module Unparser
|
|
27
33
|
#
|
28
34
|
# @api private
|
29
35
|
#
|
30
|
-
def
|
31
|
-
parent.
|
36
|
+
def local_variable_scope
|
37
|
+
parent.local_variable_scope
|
32
38
|
end
|
33
39
|
|
34
40
|
# Registry for node emitters
|
data/lib/unparser/emitter/if.rb
CHANGED
@@ -40,7 +40,7 @@ module Unparser
|
|
40
40
|
|
41
41
|
body = if_branch || else_branch
|
42
42
|
|
43
|
-
|
43
|
+
local_variable_scope.first_assignment_in_body_and_used_in_condition?(body, condition)
|
44
44
|
end
|
45
45
|
|
46
46
|
# Emit in postcondition style
|
@@ -70,7 +70,7 @@ module Unparser
|
|
70
70
|
#
|
71
71
|
def postcontrol?
|
72
72
|
return false unless body
|
73
|
-
|
73
|
+
local_variable_scope.first_assignment_in_body_and_used_in_condition?(body, condition)
|
74
74
|
end
|
75
75
|
|
76
76
|
# Emit keyword
|
@@ -7,6 +7,9 @@ describe 'Unparser on ruby corpus' do
|
|
7
7
|
if RUBY_VERSION == '1.9.3'
|
8
8
|
pending 'Corpus test not active for 1.9.3 because of limitations in encoding singalling, see Readme'
|
9
9
|
end
|
10
|
+
if ENV['GUARD']
|
11
|
+
pending 'Do not execute corpus spec under guard'
|
12
|
+
end
|
10
13
|
end
|
11
14
|
ROOT = Pathname.new(__FILE__).parent.parent.parent.parent
|
12
15
|
|
data/unparser.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |gem|
|
4
4
|
gem.name = 'unparser'
|
5
|
-
gem.version = '0.1.
|
5
|
+
gem.version = '0.1.11'
|
6
6
|
|
7
7
|
gem.authors = ['Markus Schirp']
|
8
8
|
gem.email = 'mbj@schir-dso.com'
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |gem|
|
|
19
19
|
|
20
20
|
gem.required_ruby_version
|
21
21
|
|
22
|
-
gem.add_dependency('parser', '~> 2.1
|
22
|
+
gem.add_dependency('parser', '~> 2.1')
|
23
23
|
gem.add_dependency('procto', '~> 0.0.2')
|
24
24
|
gem.add_dependency('concord', '~> 0.1.4')
|
25
25
|
gem.add_dependency('adamantium', '~> 0.2.0')
|
metadata
CHANGED
@@ -1,99 +1,99 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unparser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Markus Schirp
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-04-
|
11
|
+
date: 2014-04-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parser
|
15
|
-
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ~>
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 2.1
|
20
|
-
|
19
|
+
version: '2.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
21
23
|
requirements:
|
22
24
|
- - ~>
|
23
25
|
- !ruby/object:Gem::Version
|
24
|
-
version: 2.1
|
25
|
-
prerelease: false
|
26
|
-
type: :runtime
|
26
|
+
version: '2.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: procto
|
29
|
-
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ~>
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: 0.0.2
|
34
|
-
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
35
37
|
requirements:
|
36
38
|
- - ~>
|
37
39
|
- !ruby/object:Gem::Version
|
38
40
|
version: 0.0.2
|
39
|
-
prerelease: false
|
40
|
-
type: :runtime
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: concord
|
43
|
-
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ~>
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: 0.1.4
|
48
|
-
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
49
51
|
requirements:
|
50
52
|
- - ~>
|
51
53
|
- !ruby/object:Gem::Version
|
52
54
|
version: 0.1.4
|
53
|
-
prerelease: false
|
54
|
-
type: :runtime
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: adamantium
|
57
|
-
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: 0.2.0
|
62
|
-
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
63
65
|
requirements:
|
64
66
|
- - ~>
|
65
67
|
- !ruby/object:Gem::Version
|
66
68
|
version: 0.2.0
|
67
|
-
prerelease: false
|
68
|
-
type: :runtime
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: equalizer
|
71
|
-
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ~>
|
74
74
|
- !ruby/object:Gem::Version
|
75
75
|
version: 0.0.9
|
76
|
-
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
79
|
requirements:
|
78
80
|
- - ~>
|
79
81
|
- !ruby/object:Gem::Version
|
80
82
|
version: 0.0.9
|
81
|
-
prerelease: false
|
82
|
-
type: :runtime
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: abstract_type
|
85
|
-
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - ~>
|
88
88
|
- !ruby/object:Gem::Version
|
89
89
|
version: 0.0.7
|
90
|
-
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
93
|
requirements:
|
92
94
|
- - ~>
|
93
95
|
- !ruby/object:Gem::Version
|
94
96
|
version: 0.0.7
|
95
|
-
prerelease: false
|
96
|
-
type: :runtime
|
97
97
|
description: Generate equivalent source for parser gem AST nodes
|
98
98
|
email: mbj@schir-dso.com
|
99
99
|
executables:
|
@@ -209,7 +209,7 @@ homepage: http://github.com/mbj/unparser
|
|
209
209
|
licenses:
|
210
210
|
- MIT
|
211
211
|
metadata: {}
|
212
|
-
post_install_message:
|
212
|
+
post_install_message:
|
213
213
|
rdoc_options: []
|
214
214
|
require_paths:
|
215
215
|
- lib
|
@@ -224,9 +224,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
224
224
|
- !ruby/object:Gem::Version
|
225
225
|
version: '0'
|
226
226
|
requirements: []
|
227
|
-
rubyforge_project:
|
228
|
-
rubygems_version: 2.
|
229
|
-
signing_key:
|
227
|
+
rubyforge_project:
|
228
|
+
rubygems_version: 2.0.14
|
229
|
+
signing_key:
|
230
230
|
specification_version: 4
|
231
231
|
summary: Generate equivalent source for parser gem AST nodes
|
232
232
|
test_files:
|
@@ -247,3 +247,4 @@ test_files:
|
|
247
247
|
- spec/unit/unparser/comments/take_eol_comments_spec.rb
|
248
248
|
- spec/unit/unparser/emitter/class_methods/handle_spec.rb
|
249
249
|
- spec/unit/unparser_spec.rb
|
250
|
+
has_rdoc:
|