machete 0.3.0 → 0.4.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.
- data/.gitignore +1 -0
- data/CHANGELOG +6 -0
- data/Gemfile +3 -0
- data/README.md +4 -2
- data/VERSION +1 -1
- data/lib/machete/matchers.rb +6 -23
- data/lib/machete/parser.y +26 -2
- data/spec/machete/matchers_spec.rb +10 -61
- data/spec/machete/parser_spec.rb +41 -2
- metadata +5 -4
data/.gitignore
CHANGED
data/CHANGELOG
CHANGED
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -70,18 +70,20 @@ If you want to match a specific attribute of a node, specify its value inside `<
|
|
70
70
|
Machete.matches?('42'.to_ast, 'FixnumLiteral<value = 42>') # => true
|
71
71
|
Machete.matches?('45'.to_ast, 'FixnumLiteral<value = 42>') # => false
|
72
72
|
|
73
|
-
The attribute value can be
|
73
|
+
The attribute value can be `true`, `false`, `nil`, integer, string, symbol, array or other pattern. The last option means you can easily match nested nodes recursively. You can also specify multiple attributes:
|
74
74
|
|
75
75
|
Machete.matches?('foo.bar'.to_ast, 'Send<receiver = Send<receiver = Self, name = :foo>, name = :bar>') # => true
|
76
76
|
|
77
77
|
#### String Attributes
|
78
78
|
|
79
|
-
When matching string attributes values, you don't have to do a whole-string match using the `=` operator. You can also match the beginning
|
79
|
+
When matching string attributes values, you don't have to do a whole-string match using the `=` operator. You can also match the beginning, the end or a part of a string attribute value using the `^=`, `$=` and `*=` operators:
|
80
80
|
|
81
81
|
Machete.matches?('"abcd"'.to_ast, 'StringLiteral<string ^= "ab">') # => true
|
82
82
|
Machete.matches?('"efgh"'.to_ast, 'StringLiteral<string ^= "ab">') # => false
|
83
83
|
Machete.matches?('"abcd"'.to_ast, 'StringLiteral<string $= "cd">') # => true
|
84
84
|
Machete.matches?('"efgh"'.to_ast, 'StringLiteral<string $= "cd">') # => false
|
85
|
+
Machete.matches?('"abcd"'.to_ast, 'StringLiteral<string *= "bc">') # => true
|
86
|
+
Machete.matches?('"efgh"'.to_ast, 'StringLiteral<string *= "bc">') # => false
|
85
87
|
|
86
88
|
#### Array Attributes
|
87
89
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/lib/machete/matchers.rb
CHANGED
@@ -137,36 +137,19 @@ module Machete
|
|
137
137
|
end
|
138
138
|
|
139
139
|
# @private
|
140
|
-
class
|
141
|
-
attr_reader :
|
140
|
+
class StringRegexpMatcher
|
141
|
+
attr_reader :regexp
|
142
142
|
|
143
|
-
def initialize(
|
144
|
-
@
|
143
|
+
def initialize(regexp)
|
144
|
+
@regexp = regexp
|
145
145
|
end
|
146
146
|
|
147
147
|
def ==(other)
|
148
|
-
other.instance_of?(self.class) && @
|
148
|
+
other.instance_of?(self.class) && @regexp == other.regexp
|
149
149
|
end
|
150
150
|
|
151
151
|
def matches?(node)
|
152
|
-
node.is_a?(String) && node
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
# @private
|
157
|
-
class EndsWithMatcher
|
158
|
-
attr_reader :suffix
|
159
|
-
|
160
|
-
def initialize(suffix)
|
161
|
-
@suffix = suffix
|
162
|
-
end
|
163
|
-
|
164
|
-
def ==(other)
|
165
|
-
other.instance_of?(self.class) && @suffix == other.suffix
|
166
|
-
end
|
167
|
-
|
168
|
-
def matches?(node)
|
169
|
-
node.is_a?(String) && node.end_with?(@suffix)
|
152
|
+
node.is_a?(String) && node =~ @regexp
|
170
153
|
end
|
171
154
|
end
|
172
155
|
|
data/lib/machete/parser.y
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
class Machete::Parser
|
2
2
|
|
3
|
+
token TRUE
|
4
|
+
token FALSE
|
5
|
+
token NIL
|
3
6
|
token INTEGER
|
4
7
|
token STRING
|
5
8
|
token ANY
|
@@ -40,12 +43,23 @@ attrs : attr
|
|
40
43
|
attr : method_name "=" expression { result = { val[0].to_sym => val[2] } }
|
41
44
|
| method_name "^=" STRING {
|
42
45
|
result = {
|
43
|
-
val[0].to_sym =>
|
46
|
+
val[0].to_sym => StringRegexpMatcher.new(
|
47
|
+
Regexp.new("^" + Regexp.escape(string_value(val[2])))
|
48
|
+
)
|
44
49
|
}
|
45
50
|
}
|
46
51
|
| method_name "$=" STRING {
|
47
52
|
result = {
|
48
|
-
val[0].to_sym =>
|
53
|
+
val[0].to_sym => StringRegexpMatcher.new(
|
54
|
+
Regexp.new(Regexp.escape(string_value(val[2])) + "$")
|
55
|
+
)
|
56
|
+
}
|
57
|
+
}
|
58
|
+
| method_name "*=" STRING {
|
59
|
+
result = {
|
60
|
+
val[0].to_sym => StringRegexpMatcher.new(
|
61
|
+
Regexp.new(Regexp.escape(string_value(val[2])))
|
62
|
+
)
|
49
63
|
}
|
50
64
|
}
|
51
65
|
|
@@ -53,6 +67,9 @@ attr : method_name "=" expression { result = { val[0].to_sym => val[2] } }
|
|
53
67
|
# METHOD_NAME tokens, and that "reserved words" will lex as separate kinds of
|
54
68
|
# tokens.
|
55
69
|
method_name : METHOD_NAME
|
70
|
+
| TRUE
|
71
|
+
| FALSE
|
72
|
+
| NIL
|
56
73
|
| ANY
|
57
74
|
| EVEN
|
58
75
|
| ODD
|
@@ -95,6 +112,9 @@ quantifier : "*" { result = [0, nil, 1] }
|
|
95
112
|
literal : SYMBOL { result = LiteralMatcher.new(val[0][1..-1].to_sym) }
|
96
113
|
| INTEGER { result = LiteralMatcher.new(integer_value(val[0])) }
|
97
114
|
| STRING { result = LiteralMatcher.new(string_value(val[0])) }
|
115
|
+
| TRUE { result = LiteralMatcher.new(true) }
|
116
|
+
| FALSE { result = LiteralMatcher.new(false) }
|
117
|
+
| NIL { result = LiteralMatcher.new(nil) }
|
98
118
|
|
99
119
|
any : ANY { result = AnyMatcher.new }
|
100
120
|
|
@@ -166,6 +186,7 @@ SIMPLE_TOKENS = [
|
|
166
186
|
"$=",
|
167
187
|
"[",
|
168
188
|
"]",
|
189
|
+
"*=",
|
169
190
|
"*",
|
170
191
|
"+",
|
171
192
|
"?",
|
@@ -174,6 +195,9 @@ SIMPLE_TOKENS = [
|
|
174
195
|
]
|
175
196
|
|
176
197
|
COMPLEX_TOKENS = [
|
198
|
+
[:TRUE, /^true/],
|
199
|
+
[:FALSE, /^false/],
|
200
|
+
[:NIL, /^nil/],
|
177
201
|
# INTEGER needs to be before METHOD_NAME, otherwise e.g. "+1" would be
|
178
202
|
# recognized as two tokens.
|
179
203
|
[
|
@@ -409,14 +409,14 @@ module Machete::Matchers
|
|
409
409
|
end
|
410
410
|
end
|
411
411
|
|
412
|
-
describe
|
412
|
+
describe StringRegexpMatcher do
|
413
413
|
before :each do
|
414
|
-
@matcher =
|
414
|
+
@matcher = StringRegexpMatcher.new(/abcd/)
|
415
415
|
end
|
416
416
|
|
417
417
|
describe "initialize" do
|
418
418
|
it "sets attributes correctly" do
|
419
|
-
@matcher.
|
419
|
+
@matcher.regexp.should == /abcd/
|
420
420
|
end
|
421
421
|
end
|
422
422
|
|
@@ -426,7 +426,7 @@ module Machete::Matchers
|
|
426
426
|
end
|
427
427
|
|
428
428
|
it "returns true when passed a StartsWithMatcher initialized with the same parameters" do
|
429
|
-
@matcher.should ==
|
429
|
+
@matcher.should == StringRegexpMatcher.new(/abcd/)
|
430
430
|
end
|
431
431
|
|
432
432
|
it "returns false when passed some random object" do
|
@@ -434,23 +434,23 @@ module Machete::Matchers
|
|
434
434
|
end
|
435
435
|
|
436
436
|
it "returns false when passed a subclass of StartsWithMatcher initialized with the same parameters" do
|
437
|
-
class
|
437
|
+
class SubclassedStringRegexpMatcher < StringRegexpMatcher
|
438
438
|
end
|
439
439
|
|
440
|
-
@matcher.should_not ==
|
440
|
+
@matcher.should_not == SubclassedStringRegexpMatcher.new(/abcd/)
|
441
441
|
end
|
442
442
|
|
443
443
|
it "returns false when passed a StartsWithMatcher initialized with different parameters" do
|
444
|
-
@matcher.should_not ==
|
444
|
+
@matcher.should_not == StringRegexpMatcher.new(/efgh/)
|
445
445
|
end
|
446
446
|
end
|
447
447
|
|
448
448
|
describe "matches?" do
|
449
|
-
it "matches a string
|
450
|
-
@matcher.matches?("
|
449
|
+
it "matches a string matching the regexp" do
|
450
|
+
@matcher.matches?("efghabcdijkl").should be_true
|
451
451
|
end
|
452
452
|
|
453
|
-
it "does not match a string not
|
453
|
+
it "does not match a string not matching the regexp" do
|
454
454
|
@matcher.matches?("efghijkl").should be_false
|
455
455
|
end
|
456
456
|
|
@@ -460,57 +460,6 @@ module Machete::Matchers
|
|
460
460
|
end
|
461
461
|
end
|
462
462
|
|
463
|
-
describe EndsWithMatcher do
|
464
|
-
before :each do
|
465
|
-
@matcher = EndsWithMatcher.new("abcd")
|
466
|
-
end
|
467
|
-
|
468
|
-
describe "initialize" do
|
469
|
-
it "sets attributes correctly" do
|
470
|
-
@matcher.suffix.should == "abcd"
|
471
|
-
end
|
472
|
-
end
|
473
|
-
|
474
|
-
describe "==" do
|
475
|
-
it "returns true when passed the same object" do
|
476
|
-
@matcher.should == @matcher
|
477
|
-
end
|
478
|
-
|
479
|
-
it "returns true when passed a EndsWithMatcher initialized with the same parameters" do
|
480
|
-
@matcher.should == EndsWithMatcher.new("abcd")
|
481
|
-
end
|
482
|
-
|
483
|
-
it "returns false when passed some random object" do
|
484
|
-
@matcher.should_not == Object.new
|
485
|
-
end
|
486
|
-
|
487
|
-
it "returns false when passed a subclass of EndsWithMatcher initialized with the same parameters" do
|
488
|
-
class SubclassedEndsWithMatcher < EndsWithMatcher
|
489
|
-
end
|
490
|
-
|
491
|
-
@matcher.should_not == SubclassedEndsWithMatcher.new("abcd")
|
492
|
-
end
|
493
|
-
|
494
|
-
it "returns false when passed a EndsWithMatcher initialized with different parameters" do
|
495
|
-
@matcher.should_not == EndsWithMatcher.new("efgh")
|
496
|
-
end
|
497
|
-
end
|
498
|
-
|
499
|
-
describe "matches?" do
|
500
|
-
it "matches a string ending with the suffix" do
|
501
|
-
@matcher.matches?("efghabcd").should be_true
|
502
|
-
end
|
503
|
-
|
504
|
-
it "does not match a string not ending with the suffix" do
|
505
|
-
@matcher.matches?("ijklefgh").should be_false
|
506
|
-
end
|
507
|
-
|
508
|
-
it "does not match some random object" do
|
509
|
-
@matcher.matches?(Object.new).should be_false
|
510
|
-
end
|
511
|
-
end
|
512
|
-
end
|
513
|
-
|
514
463
|
describe AnyMatcher do
|
515
464
|
before :each do
|
516
465
|
@matcher = AnyMatcher.new
|
data/spec/machete/parser_spec.rb
CHANGED
@@ -74,16 +74,37 @@ module Machete
|
|
74
74
|
it "parses attr" do
|
75
75
|
'Foo<a = 42 | 43>'.should be_parsed_as(NodeMatcher.new(:Foo, :a => @ch4243))
|
76
76
|
'Foo<a ^= "abcd">'.should be_parsed_as(
|
77
|
-
NodeMatcher.new(:Foo, :a =>
|
77
|
+
NodeMatcher.new(:Foo, :a => StringRegexpMatcher.new(/^abcd/))
|
78
|
+
)
|
79
|
+
'Foo<a ^= "[]{}()|-*.\\?+^$ #\t\f\v\n\r">'.should be_parsed_as(
|
80
|
+
NodeMatcher.new(:Foo, :a => StringRegexpMatcher.new(
|
81
|
+
/^\[\]\{\}\(\)\|\-\*\.\\\?\+\^\$\ \#\t\f\v\n\r/
|
82
|
+
))
|
78
83
|
)
|
79
84
|
'Foo<a $= "abcd">'.should be_parsed_as(
|
80
|
-
NodeMatcher.new(:Foo, :a =>
|
85
|
+
NodeMatcher.new(:Foo, :a => StringRegexpMatcher.new(/abcd$/))
|
86
|
+
)
|
87
|
+
'Foo<a $= "[]{}()|-*.\\?+^$ #\t\f\v\n\r">'.should be_parsed_as(
|
88
|
+
NodeMatcher.new(:Foo, :a => StringRegexpMatcher.new(
|
89
|
+
/\[\]\{\}\(\)\|\-\*\.\\\?\+\^\$\ \#\t\f\v\n\r$/
|
90
|
+
))
|
91
|
+
)
|
92
|
+
'Foo<a *= "abcd">'.should be_parsed_as(
|
93
|
+
NodeMatcher.new(:Foo, :a => StringRegexpMatcher.new(/abcd/))
|
94
|
+
)
|
95
|
+
'Foo<a *= "[]{}()|-*.\\?+^$ #\t\f\v\n\r">'.should be_parsed_as(
|
96
|
+
NodeMatcher.new(:Foo, :a => StringRegexpMatcher.new(
|
97
|
+
/\[\]\{\}\(\)\|\-\*\.\\\?\+\^\$\ \#\t\f\v\n\r/
|
98
|
+
))
|
81
99
|
)
|
82
100
|
end
|
83
101
|
|
84
102
|
# Canonical method_name is "a".
|
85
103
|
it "parses method_name" do
|
86
104
|
'Foo<a = 42>'.should be_parsed_as(node_matcher_with_attr(:a))
|
105
|
+
'Foo<true = 42>'.should be_parsed_as(node_matcher_with_attr(:true))
|
106
|
+
'Foo<false = 42>'.should be_parsed_as(node_matcher_with_attr(:false))
|
107
|
+
'Foo<any = 42>'.should be_parsed_as(node_matcher_with_attr(:any))
|
87
108
|
'Foo<any = 42>'.should be_parsed_as(node_matcher_with_attr(:any))
|
88
109
|
'Foo<even = 42>'.should be_parsed_as(node_matcher_with_attr(:even))
|
89
110
|
'Foo<odd = 42>'.should be_parsed_as(node_matcher_with_attr(:odd))
|
@@ -148,6 +169,9 @@ module Machete
|
|
148
169
|
':a'.should be_parsed_as(LiteralMatcher.new(:a))
|
149
170
|
'42'.should be_parsed_as(@i42)
|
150
171
|
'"abcd"'.should be_parsed_as(LiteralMatcher.new("abcd"))
|
172
|
+
'true'.should be_parsed_as(LiteralMatcher.new(true))
|
173
|
+
'false'.should be_parsed_as(LiteralMatcher.new(false))
|
174
|
+
'nil'.should be_parsed_as(LiteralMatcher.new(nil))
|
151
175
|
end
|
152
176
|
|
153
177
|
# Canonical any is "any".
|
@@ -155,6 +179,21 @@ module Machete
|
|
155
179
|
'any'.should be_parsed_as(AnyMatcher.new)
|
156
180
|
end
|
157
181
|
|
182
|
+
# Canonical TRUE is "true".
|
183
|
+
it "parses TRUE" do
|
184
|
+
'true'.should be_parsed_as(LiteralMatcher.new(true))
|
185
|
+
end
|
186
|
+
|
187
|
+
# Canonical FALSE is "false".
|
188
|
+
it "parses FALSE" do
|
189
|
+
'false'.should be_parsed_as(LiteralMatcher.new(false))
|
190
|
+
end
|
191
|
+
|
192
|
+
# Canonical NIL is "nil".
|
193
|
+
it "parses NIL" do
|
194
|
+
'nil'.should be_parsed_as(LiteralMatcher.new(nil))
|
195
|
+
end
|
196
|
+
|
158
197
|
# Canonical INTEGER is "42".
|
159
198
|
it "parses INTEGER" do
|
160
199
|
# Sign
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: machete
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 4
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.4.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- David Majda
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-10-18 00:00:00 +02:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -100,6 +100,7 @@ files:
|
|
100
100
|
- .gitignore
|
101
101
|
- .yardopts
|
102
102
|
- CHANGELOG
|
103
|
+
- Gemfile
|
103
104
|
- LICENSE
|
104
105
|
- README.md
|
105
106
|
- Rakefile
|