metamorpher 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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.rubocop.yml +16 -0
- data/.travis.yml +3 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +541 -0
- data/Rakefile +23 -0
- data/examples/refactorings/rails/where_first/app.rb +50 -0
- data/examples/refactorings/rails/where_first/refactorers/refactor_where_first_mocks.rb +31 -0
- data/examples/refactorings/rails/where_first/refactorers/refactor_where_first_not_called_expectations.rb +14 -0
- data/examples/refactorings/rails/where_first/refactorers/refactor_where_first_strict_mocks.rb +27 -0
- data/examples/refactorings/rails/where_first/refactorers/refactor_where_first_to_find_by.rb +14 -0
- data/examples/refactorings/rails/where_first/sample_controller.rb +184 -0
- data/lib/metamorpher/builders/ast/builder.rb +50 -0
- data/lib/metamorpher/builders/ast/derivation_builder.rb +20 -0
- data/lib/metamorpher/builders/ast/greedy_variable_builder.rb +29 -0
- data/lib/metamorpher/builders/ast/literal_builder.rb +31 -0
- data/lib/metamorpher/builders/ast/variable_builder.rb +29 -0
- data/lib/metamorpher/builders/ast.rb +11 -0
- data/lib/metamorpher/builders/ruby/builder.rb +38 -0
- data/lib/metamorpher/builders/ruby/deriving_visitor.rb +13 -0
- data/lib/metamorpher/builders/ruby/ensuring_visitor.rb +13 -0
- data/lib/metamorpher/builders/ruby/term.rb +35 -0
- data/lib/metamorpher/builders/ruby/uppercase_constant_rewriter.rb +31 -0
- data/lib/metamorpher/builders/ruby/uppercase_rewriter.rb +28 -0
- data/lib/metamorpher/builders/ruby/variable_replacement_visitor.rb +32 -0
- data/lib/metamorpher/builders/ruby.rb +11 -0
- data/lib/metamorpher/drivers/parse_error.rb +5 -0
- data/lib/metamorpher/drivers/ruby.rb +78 -0
- data/lib/metamorpher/matcher/match.rb +26 -0
- data/lib/metamorpher/matcher/matching.rb +61 -0
- data/lib/metamorpher/matcher/no_match.rb +18 -0
- data/lib/metamorpher/matcher.rb +6 -0
- data/lib/metamorpher/refactorer/merger.rb +18 -0
- data/lib/metamorpher/refactorer/site.rb +29 -0
- data/lib/metamorpher/refactorer.rb +48 -0
- data/lib/metamorpher/rewriter/replacement.rb +18 -0
- data/lib/metamorpher/rewriter/rule.rb +38 -0
- data/lib/metamorpher/rewriter/substitution.rb +45 -0
- data/lib/metamorpher/rewriter/traverser.rb +26 -0
- data/lib/metamorpher/rewriter.rb +12 -0
- data/lib/metamorpher/support/map_at.rb +8 -0
- data/lib/metamorpher/terms/derived.rb +13 -0
- data/lib/metamorpher/terms/literal.rb +47 -0
- data/lib/metamorpher/terms/term.rb +40 -0
- data/lib/metamorpher/terms/variable.rb +17 -0
- data/lib/metamorpher/version.rb +3 -0
- data/lib/metamorpher/visitable/visitable.rb +7 -0
- data/lib/metamorpher/visitable/visitor.rb +21 -0
- data/lib/metamorpher.rb +30 -0
- data/metamorpher.gemspec +30 -0
- data/spec/integration/ast/builder_spec.rb +13 -0
- data/spec/integration/ast/matcher_spec.rb +132 -0
- data/spec/integration/ast/rewriter_spec.rb +138 -0
- data/spec/integration/ruby/builder_spec.rb +125 -0
- data/spec/integration/ruby/refactorer_spec.rb +192 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/helpers/silence_stream.rb +10 -0
- data/spec/support/matchers/have_matched_matcher.rb +22 -0
- data/spec/support/matchers/have_substitution_matcher.rb +15 -0
- data/spec/support/shared_examples/shared_examples_for_derivation_builders.rb +53 -0
- data/spec/support/shared_examples/shared_examples_for_greedy_variable_builders.rb +49 -0
- data/spec/support/shared_examples/shared_examples_for_literal_builders.rb +93 -0
- data/spec/support/shared_examples/shared_examples_for_variable_builders.rb +49 -0
- data/spec/unit/builders/ast/derivation_builder_spec.rb +5 -0
- data/spec/unit/builders/ast/greedy_variable_builder_spec.rb +9 -0
- data/spec/unit/builders/ast/literal_builder_spec.rb +9 -0
- data/spec/unit/builders/ast/variable_builder_spec.rb +9 -0
- data/spec/unit/builders/ruby/variable_replacement_visitor_spec.rb +48 -0
- data/spec/unit/drivers/ruby_spec.rb +91 -0
- data/spec/unit/matcher/matching_spec.rb +230 -0
- data/spec/unit/metamorpher_spec.rb +22 -0
- data/spec/unit/refactorer/merger_spec.rb +84 -0
- data/spec/unit/refactorer/site_spec.rb +52 -0
- data/spec/unit/rewriter/replacement_spec.rb +73 -0
- data/spec/unit/rewriter/substitution_spec.rb +97 -0
- data/spec/unit/rewriter/traverser_spec.rb +51 -0
- data/spec/unit/support/map_at_spec.rb +18 -0
- data/spec/unit/terms/literal_spec.rb +60 -0
- data/spec/unit/terms/term_spec.rb +59 -0
- data/spec/unit/visitable/visitor_spec.rb +35 -0
- metadata +269 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require "rspec/expectations"
|
|
2
|
+
|
|
3
|
+
RSpec::Matchers.define :have_substitution do |expected|
|
|
4
|
+
match do |actual|
|
|
5
|
+
actual.substitution == expected
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
failure_message_for_should do |actual|
|
|
9
|
+
"expected the substitution #{actual.substitution}, but got #{expected}"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
failure_message_for_should_not do |actual|
|
|
13
|
+
"expected to not receive the substitution #{actual.substitution}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require "metamorpher/terms/literal"
|
|
2
|
+
|
|
3
|
+
module Metamorpher
|
|
4
|
+
module Terms
|
|
5
|
+
shared_examples "a derivation builder" do
|
|
6
|
+
describe "derivation!" do
|
|
7
|
+
it "should create an instance of Derivation" do
|
|
8
|
+
built = subject.derivation!(:method) do |method|
|
|
9
|
+
Literal.new(name: (method.name.to_s + "s").to_sym)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
expect(built.base).to eq([:method])
|
|
13
|
+
expect(built.derivation.call(Literal.new(name: :dog))).to eq(Literal.new(name: :dogs))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should capture all arguments as the base" do
|
|
17
|
+
built = subject.derivation!(:key, :value) {}
|
|
18
|
+
|
|
19
|
+
expect(built.base).to eq([:key, :value])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "should provide a builder for use in the block" do
|
|
23
|
+
built = subject.derivation!(:key, :value) do |key, value, builder|
|
|
24
|
+
builder.pair(key, value)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
derived = built.derivation.call(
|
|
28
|
+
Literal.new(name: :dog),
|
|
29
|
+
Literal.new(name: :lassie)
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
paired = Literal.new(
|
|
33
|
+
name: :pair,
|
|
34
|
+
children: [
|
|
35
|
+
Literal.new(name: :dog),
|
|
36
|
+
Literal.new(name: :lassie)
|
|
37
|
+
]
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
expect(derived).to eq(paired)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should raise if no arguments are passed" do
|
|
44
|
+
expect { subject.derivation! { nil } }.to raise_error(ArgumentError)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should raise if no block is passed" do
|
|
48
|
+
expect { subject.derivation!(:method) }.to raise_error(ArgumentError)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require "metamorpher/terms/variable"
|
|
2
|
+
|
|
3
|
+
module Metamorpher
|
|
4
|
+
module Terms
|
|
5
|
+
shared_examples "a greedy variable builder" do
|
|
6
|
+
describe "greedy_variable!" do
|
|
7
|
+
it "should create an instance of Variable with greedy? set to true" do
|
|
8
|
+
actual = subject.greedy_variable!(:a)
|
|
9
|
+
expected = Variable.new(name: :a, greedy?: true)
|
|
10
|
+
|
|
11
|
+
expect(actual).to eq(expected)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "should create condition from block" do
|
|
15
|
+
built = subject.greedy_variable!(:a) { |term| term > 0 }
|
|
16
|
+
|
|
17
|
+
expect(built.name).to eq(:a)
|
|
18
|
+
expect(built.condition.call(1)).to be_true
|
|
19
|
+
expect(built.condition.call(-1)).to be_false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "should not allow children" do
|
|
23
|
+
expect { subject.greedy_variable!(:a, 1) }.to raise_error(ArgumentError)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe "greedy variable shorthand" do
|
|
28
|
+
it "should create an instance of Variable with greedy? set to true" do
|
|
29
|
+
actual = subject.A_
|
|
30
|
+
expected = Variable.new(name: :a, greedy?: true)
|
|
31
|
+
|
|
32
|
+
expect(actual).to eq(expected)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should create condition from block" do
|
|
36
|
+
built = subject.A_ { |term| term > 0 }
|
|
37
|
+
|
|
38
|
+
expect(built.name).to eq(:a)
|
|
39
|
+
expect(built.condition.call(1)).to be_true
|
|
40
|
+
expect(built.condition.call(-1)).to be_false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should not allow children" do
|
|
44
|
+
expect { subject.A_(1) }.to raise_error(ArgumentError)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require "metamorpher/terms/literal"
|
|
2
|
+
require "metamorpher/terms/variable"
|
|
3
|
+
|
|
4
|
+
module Metamorpher
|
|
5
|
+
module Terms
|
|
6
|
+
shared_examples_for "a literal builder" do
|
|
7
|
+
describe "literal!" do
|
|
8
|
+
it "should create an instance of Literal" do
|
|
9
|
+
actual = subject.literal!(:a)
|
|
10
|
+
expected = Literal.new(name: :a)
|
|
11
|
+
|
|
12
|
+
expect(actual).to eq(expected)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should work with numeric names" do
|
|
16
|
+
actual = subject.literal!(4)
|
|
17
|
+
expected = Literal.new(name: 4)
|
|
18
|
+
|
|
19
|
+
expect(actual).to eq(expected)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "should capture single children" do
|
|
23
|
+
actual = subject.literal!(:inc, subject.literal!(1))
|
|
24
|
+
expected = Literal.new(name: :inc, children: [Literal.new(name: 1)])
|
|
25
|
+
|
|
26
|
+
expect(actual).to eq(expected)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "should capture several children" do
|
|
30
|
+
actual = subject.literal!(:add, subject.literal!(1), subject.literal!(2))
|
|
31
|
+
expected = Literal.new(
|
|
32
|
+
name: :add,
|
|
33
|
+
children: [
|
|
34
|
+
Literal.new(name: 1),
|
|
35
|
+
Literal.new(name: 2)
|
|
36
|
+
]
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
expect(actual).to eq(expected)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "should automatically convert children to literals" do
|
|
43
|
+
actual = subject.literal!(:add, 1, 2)
|
|
44
|
+
expected = Literal.new(
|
|
45
|
+
name: :add,
|
|
46
|
+
children: [
|
|
47
|
+
Literal.new(name: 1),
|
|
48
|
+
Literal.new(name: 2)
|
|
49
|
+
]
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
expect(actual).to eq(expected)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "should not automatically convert children that are already terms" do
|
|
56
|
+
actual = subject.literal!(:inc, Variable.new(name: :a))
|
|
57
|
+
expected = Literal.new(name: :inc, children: [Variable.new(name: :a)])
|
|
58
|
+
|
|
59
|
+
expect(actual).to eq(expected)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
describe "literal shorthand" do
|
|
64
|
+
it "should create an instance of Literal" do
|
|
65
|
+
actual = subject.a
|
|
66
|
+
expected = Literal.new(name: :a)
|
|
67
|
+
|
|
68
|
+
expect(actual).to eq(expected)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "should work for longer names" do
|
|
72
|
+
actual = subject.this_is_a_longer_name
|
|
73
|
+
expected = Literal.new(name: :this_is_a_longer_name)
|
|
74
|
+
|
|
75
|
+
expect(actual).to eq(expected)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "should capture children" do
|
|
79
|
+
actual = subject.add(1, 2)
|
|
80
|
+
expected = Literal.new(
|
|
81
|
+
name: :add,
|
|
82
|
+
children: [
|
|
83
|
+
Literal.new(name: 1),
|
|
84
|
+
Literal.new(name: 2)
|
|
85
|
+
]
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
expect(actual).to eq(expected)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require "metamorpher/terms/variable"
|
|
2
|
+
|
|
3
|
+
module Metamorpher
|
|
4
|
+
module Terms
|
|
5
|
+
shared_examples "a variable builder" do
|
|
6
|
+
describe "variable!" do
|
|
7
|
+
it "should create an instance of Variable" do
|
|
8
|
+
actual = subject.variable!(:a)
|
|
9
|
+
expected = Variable.new(name: :a)
|
|
10
|
+
|
|
11
|
+
expect(actual).to eq(expected)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "should create condition from block" do
|
|
15
|
+
built = subject.variable!(:a) { |term| term > 0 }
|
|
16
|
+
|
|
17
|
+
expect(built.name).to eq(:a)
|
|
18
|
+
expect(built.condition.call(1)).to be_true
|
|
19
|
+
expect(built.condition.call(-1)).to be_false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "should not allow children" do
|
|
23
|
+
expect { subject.variable!(:a, 1) }.to raise_error(ArgumentError)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe "variable shorthand" do
|
|
28
|
+
it "should create an instance of Variable" do
|
|
29
|
+
actual = subject.A
|
|
30
|
+
expected = Variable.new(name: :a)
|
|
31
|
+
|
|
32
|
+
expect(actual).to eq(expected)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
it "should create condition from block" do
|
|
36
|
+
built = subject.A { |term| term > 0 }
|
|
37
|
+
|
|
38
|
+
expect(built.name).to eq(:a)
|
|
39
|
+
expect(built.condition.call(1)).to be_true
|
|
40
|
+
expect(built.condition.call(-1)).to be_false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should not allow children" do
|
|
44
|
+
expect { subject.A(1) }.to raise_error(ArgumentError)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
require "metamorpher/builders/ast/greedy_variable_builder"
|
|
2
|
+
|
|
3
|
+
describe Metamorpher::Builders::AST::GreedyVariableBuilder do
|
|
4
|
+
it_behaves_like "a greedy variable builder"
|
|
5
|
+
|
|
6
|
+
it "should raise when incorrect shorthand is used" do
|
|
7
|
+
expect { subject.inc }.to raise_error(NoMethodError)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
require "metamorpher/builders/ast/literal_builder"
|
|
2
|
+
|
|
3
|
+
describe Metamorpher::Builders::AST::LiteralBuilder do
|
|
4
|
+
it_behaves_like "a literal builder"
|
|
5
|
+
|
|
6
|
+
it "should raise when variable shorthand is used" do
|
|
7
|
+
expect { subject.INC }.to raise_error(NoMethodError)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
require "metamorpher/builders/ast/variable_builder"
|
|
2
|
+
|
|
3
|
+
describe Metamorpher::Builders::AST::VariableBuilder do
|
|
4
|
+
it_behaves_like "a variable builder"
|
|
5
|
+
|
|
6
|
+
it "should raise when incorrect shorthand is used" do
|
|
7
|
+
expect { subject.inc }.to raise_error(NoMethodError)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require "metamorpher/builders/ruby/variable_replacement_visitor"
|
|
2
|
+
|
|
3
|
+
module Metamorpher
|
|
4
|
+
module Builders
|
|
5
|
+
module Ruby
|
|
6
|
+
describe VariableReplacementVisitor do
|
|
7
|
+
let(:builder) { Metamorpher::Builders::AST::Builder.new }
|
|
8
|
+
let(:replacement) { builder.literal!(:bar) }
|
|
9
|
+
subject { VariableReplacementVisitor.new(:foo, replacement) }
|
|
10
|
+
|
|
11
|
+
it "should replace a variable with the correct name" do
|
|
12
|
+
original = builder.variable!(:foo)
|
|
13
|
+
replaced = subject.visit(original)
|
|
14
|
+
|
|
15
|
+
expect(replaced).to eq(replacement)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should replace a nested variable" do
|
|
19
|
+
original = builder.literal!(:+, 2, builder.variable!(:foo))
|
|
20
|
+
replaced = subject.visit(original)
|
|
21
|
+
|
|
22
|
+
expect(replaced).to eq(original.replace(original.children.last.path, replacement))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "should not replace a variable with a different name" do
|
|
26
|
+
original = builder.variable!(:bar)
|
|
27
|
+
replaced = subject.visit(original)
|
|
28
|
+
|
|
29
|
+
expect(replaced).to eq(original)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "should not replace a literal" do
|
|
33
|
+
original = builder.literal!(:foo)
|
|
34
|
+
replaced = subject.visit(original)
|
|
35
|
+
|
|
36
|
+
expect(replaced).to eq(original)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "should not replace a derived" do
|
|
40
|
+
original = builder.derivation!(:foo) {}
|
|
41
|
+
replaced = subject.visit(original)
|
|
42
|
+
|
|
43
|
+
expect(replaced).to eq(original)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require "metamorpher/drivers/ruby"
|
|
2
|
+
require "metamorpher/builders/ast/builder"
|
|
3
|
+
|
|
4
|
+
module Metamorpher
|
|
5
|
+
module Drivers
|
|
6
|
+
describe Ruby do
|
|
7
|
+
let(:builder) { Builders::AST::Builder.new }
|
|
8
|
+
|
|
9
|
+
describe "for a simple program" do
|
|
10
|
+
let(:source) { "1 + 2" }
|
|
11
|
+
let(:literal) { builder.literal!(:send, builder.int(1), :+, builder.int(2)) }
|
|
12
|
+
|
|
13
|
+
it "should parse a simple program to literals" do
|
|
14
|
+
expect(subject.parse(source)).to eq(literal)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "should unparse valid literals to source" do
|
|
18
|
+
expect(subject.unparse(literal)).to eq(source)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "should provide source location of literals" do
|
|
22
|
+
subject.parse(source)
|
|
23
|
+
|
|
24
|
+
expect(subject.source_location_for(literal)).to eq(0..4)
|
|
25
|
+
expect(subject.source_location_for(literal.children.first)).to eq(0..0)
|
|
26
|
+
expect(subject.source_location_for(literal.children.last)).to eq(4..4)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe "for program containing identical statements" do
|
|
31
|
+
let(:source) { "1 + 1" }
|
|
32
|
+
let(:literal) { builder.literal!(:send, builder.int(1), :+, builder.int(1)) }
|
|
33
|
+
|
|
34
|
+
it "should provide different source locations for syntactically equal literals" do
|
|
35
|
+
subject.parse(source)
|
|
36
|
+
|
|
37
|
+
expect(subject.source_location_for(literal.children.first)).to eq(0..0)
|
|
38
|
+
expect(subject.source_location_for(literal.children.last)).to eq(4..4)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
describe "for program that parses to an AST containing nils" do
|
|
43
|
+
let(:source) { "LEFT + RIGHT" }
|
|
44
|
+
let(:literal) do
|
|
45
|
+
builder.literal!(
|
|
46
|
+
:send,
|
|
47
|
+
builder.const(nil, :LEFT),
|
|
48
|
+
:+,
|
|
49
|
+
builder.const(nil, :RIGHT)
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "should parse a simple program to literals" do
|
|
54
|
+
expect(subject.parse(source)).to eq(literal)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it "should unparse valid literals to source" do
|
|
58
|
+
expect(subject.unparse(literal)).to eq(source)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
%w(nil true false self).each do |keyword|
|
|
63
|
+
describe "for a program containing the '#{keyword}' keyword" do
|
|
64
|
+
let(:source) { "a = #{keyword}" }
|
|
65
|
+
let(:literal) { builder.lvasgn(:a, keyword.to_sym) }
|
|
66
|
+
|
|
67
|
+
it "should parse to the correct literal" do
|
|
68
|
+
expect(subject.parse(source)).to eq(literal)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "should unparse to the correct source" do
|
|
72
|
+
expect(subject.unparse(literal)).to eq(source)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
describe "for a program that is the '#{keyword}' keyword" do
|
|
77
|
+
let(:source) { keyword }
|
|
78
|
+
let(:literal) { builder.literal! keyword.to_sym }
|
|
79
|
+
|
|
80
|
+
it "should parse to the correct literal" do
|
|
81
|
+
expect(subject.parse(source)).to eq(literal)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it "should unparse to the correct source" do
|
|
85
|
+
expect(subject.unparse(literal)).to eq(source)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
require "metamorpher/terms/variable"
|
|
2
|
+
require "metamorpher/terms/literal"
|
|
3
|
+
require "metamorpher/terms/derived"
|
|
4
|
+
|
|
5
|
+
module Metamorpher
|
|
6
|
+
module Terms
|
|
7
|
+
describe Variable do
|
|
8
|
+
let(:root) do
|
|
9
|
+
Literal.new(
|
|
10
|
+
name: :root,
|
|
11
|
+
children: [
|
|
12
|
+
Literal.new(name: :first_child),
|
|
13
|
+
Literal.new(name: :second_child),
|
|
14
|
+
Literal.new(name: :third_child)
|
|
15
|
+
]
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let(:first_child) { root.children[0] }
|
|
20
|
+
let(:second_child) { root.children[1] }
|
|
21
|
+
let(:third_child) { root.children[2] }
|
|
22
|
+
|
|
23
|
+
describe "unconditional, non-greedy variable" do
|
|
24
|
+
subject { Variable.new(name: :type) }
|
|
25
|
+
|
|
26
|
+
it "should match any literal" do
|
|
27
|
+
expect(subject.match(root)).to have_matched(root)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "should include the match in the substitution" do
|
|
31
|
+
expect(subject.match(root)).to have_substitution(type: root)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "shouldn't match nil" do
|
|
35
|
+
expect(subject.match(nil)).not_to have_matched
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe "conditional, non-greedy variable" do
|
|
40
|
+
subject { Variable.new(name: :type, condition: -> (l) { l.name == :third_child }) }
|
|
41
|
+
|
|
42
|
+
it "should match when literal matches condition" do
|
|
43
|
+
expect(subject.match(third_child)).to have_matched(third_child)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "shouldn't match when literal doesn't match condition" do
|
|
47
|
+
expect(subject.match(second_child)).not_to have_matched
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "shouldn't match nil" do
|
|
51
|
+
expect(subject.match(nil)).not_to have_matched
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
describe "unconditional, greedy variable" do
|
|
56
|
+
subject { Variable.new(name: :type, greedy?: true) }
|
|
57
|
+
|
|
58
|
+
it "should match against literal and all younger siblings" do
|
|
59
|
+
result = subject.match(second_child)
|
|
60
|
+
|
|
61
|
+
expect(result).to have_matched([second_child, third_child])
|
|
62
|
+
expect(result).to have_substitution(type: [second_child, third_child])
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "should match against literal when there is no parent" do
|
|
66
|
+
result = subject.match(root)
|
|
67
|
+
|
|
68
|
+
expect(result).to have_matched([root])
|
|
69
|
+
expect(result).to have_substitution(type: [root])
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it "should allow parent to match when there are a different number of children" do
|
|
73
|
+
wrapped_subject = Literal.new(name: :root, children: [subject])
|
|
74
|
+
result = wrapped_subject.match(root)
|
|
75
|
+
|
|
76
|
+
expect(result).to have_matched(root)
|
|
77
|
+
expect(result).to have_substitution(type: root.children)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "shouldn't match nil" do
|
|
81
|
+
expect(subject.match(nil)).not_to have_matched
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
describe "conditional, greedy variable" do
|
|
86
|
+
subject { Variable.new(name: :type, greedy?: true, condition: -> (ls) { ls.size == 2 }) }
|
|
87
|
+
|
|
88
|
+
it "should match when literal matches condition" do
|
|
89
|
+
result = subject.match(second_child)
|
|
90
|
+
|
|
91
|
+
expect(result).to have_matched([second_child, third_child])
|
|
92
|
+
expect(result).to have_substitution(type: [second_child, third_child])
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it "should not match when literal doesn't match condition" do
|
|
96
|
+
expect(subject.match(third_child)).not_to have_matched
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "shouldn't match nil" do
|
|
100
|
+
expect(subject.match(nil)).not_to have_matched
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
describe Literal do
|
|
105
|
+
describe "with no children" do
|
|
106
|
+
let(:literal) { Literal.new(name: :root) }
|
|
107
|
+
|
|
108
|
+
it "should match when names match" do
|
|
109
|
+
matchee = Literal.new(name: :root)
|
|
110
|
+
|
|
111
|
+
expect(literal.match(matchee)).to have_matched(matchee)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it "shouldn't match when names don't match" do
|
|
115
|
+
matchee = Literal.new(name: :not_found)
|
|
116
|
+
|
|
117
|
+
expect(literal.match(matchee)).not_to have_matched
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it "should be able to match nils" do
|
|
121
|
+
literal = Literal.new(name: nil)
|
|
122
|
+
matchee = Literal.new(name: nil)
|
|
123
|
+
|
|
124
|
+
expect(literal.match(matchee)).to have_matched(matchee)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
describe "with children" do
|
|
129
|
+
let(:root) do
|
|
130
|
+
Literal.new(
|
|
131
|
+
name: :root,
|
|
132
|
+
children: [
|
|
133
|
+
Literal.new(name: :first_child),
|
|
134
|
+
Literal.new(name: :second_child),
|
|
135
|
+
Literal.new(name: :third_child)
|
|
136
|
+
]
|
|
137
|
+
)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
let(:first_child) { root.children[0] }
|
|
141
|
+
let(:second_child) { root.children[1] }
|
|
142
|
+
let(:third_child) { root.children[2] }
|
|
143
|
+
|
|
144
|
+
it "should match when children match" do
|
|
145
|
+
matchee = Literal.new(
|
|
146
|
+
name: :root,
|
|
147
|
+
children: [
|
|
148
|
+
Literal.new(name: :first_child),
|
|
149
|
+
Literal.new(name: :second_child),
|
|
150
|
+
Literal.new(name: :third_child)
|
|
151
|
+
]
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
expect(root.match(matchee)).to have_matched(matchee)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "should not match when children don't match" do
|
|
158
|
+
matchee = Literal.new(
|
|
159
|
+
name: :root,
|
|
160
|
+
children: [
|
|
161
|
+
Literal.new(name: :primer_hijo),
|
|
162
|
+
Literal.new(name: :segundo_hijo),
|
|
163
|
+
Literal.new(name: :tercero_hijo)
|
|
164
|
+
]
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
expect(root.match(matchee)).not_to have_matched
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
it "should not match when there are too few children" do
|
|
171
|
+
matchee = Literal.new(
|
|
172
|
+
name: :root,
|
|
173
|
+
children: [
|
|
174
|
+
Literal.new(name: :first_child),
|
|
175
|
+
Literal.new(name: :second_child)
|
|
176
|
+
]
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
expect(root.match(matchee)).not_to have_matched
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it "should not match when there are too many children" do
|
|
183
|
+
matchee = Literal.new(
|
|
184
|
+
name: :root,
|
|
185
|
+
children: [
|
|
186
|
+
Literal.new(name: :first_child),
|
|
187
|
+
Literal.new(name: :second_child),
|
|
188
|
+
Literal.new(name: :third_child),
|
|
189
|
+
Literal.new(name: :fourth_child)
|
|
190
|
+
]
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
expect(root.match(matchee)).not_to have_matched
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
it "builds substitution from children" do
|
|
197
|
+
root = Literal.new(
|
|
198
|
+
name: :root,
|
|
199
|
+
children: [
|
|
200
|
+
Variable.new(name: :first),
|
|
201
|
+
Literal.new(name: :second_child),
|
|
202
|
+
Variable.new(name: :last)
|
|
203
|
+
]
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
matchee = Literal.new(
|
|
207
|
+
name: :root,
|
|
208
|
+
children: [
|
|
209
|
+
Literal.new(name: :first_child),
|
|
210
|
+
Literal.new(name: :second_child),
|
|
211
|
+
Literal.new(name: :third_child)
|
|
212
|
+
]
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
expect(root.match(matchee)).to have_substitution(first: first_child, last: third_child)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
describe Derived do
|
|
221
|
+
it "should raise" do
|
|
222
|
+
root = Derived.new
|
|
223
|
+
matchee = Literal.new(name: :root)
|
|
224
|
+
|
|
225
|
+
expect { root.match(matchee) }.to raise_error(Matcher::MatchingError)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require "metamorpher"
|
|
2
|
+
|
|
3
|
+
module Metamorpher
|
|
4
|
+
describe Metamorpher do
|
|
5
|
+
it "should provide a Ruby builder by default" do
|
|
6
|
+
expect(subject.builder).to be_kind_of(Builders::Ruby::Builder)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
describe "configure" do
|
|
10
|
+
it "should be possible to change to an AST builder" do
|
|
11
|
+
subject.configure(builder: :ast)
|
|
12
|
+
expect(subject.builder).to be_kind_of(Builders::AST::Builder)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should be possible to change back to the Ruby builder" do
|
|
16
|
+
subject.configure(builder: :ast)
|
|
17
|
+
subject.configure(builder: :ruby)
|
|
18
|
+
expect(subject.builder).to be_kind_of(Builders::Ruby::Builder)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|