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 CHANGED
@@ -2,3 +2,4 @@ lib/machete/parser.rb
2
2
  doc/
3
3
  .yardoc
4
4
  *.rbc
5
+ Gemfile.lock
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ 0.4.0 (2011-10-18)
2
+ ------------------
3
+ * Support for "true", "false" and "nil" literals.
4
+ * New "*=" operator matching part of a string.
5
+ * Bundler support.
6
+
1
7
  0.3.0 (2011-09-27)
2
8
  ------------------
3
9
 
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
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 an integer, string, symbol, array or other pattern. The last option means you can easily match nested nodes recursively. You can also specify multiple attributes:
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 or the end of a string attribute value using the `^=` or `$=` operators:
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.3.0
1
+ 0.4.0
@@ -137,36 +137,19 @@ module Machete
137
137
  end
138
138
 
139
139
  # @private
140
- class StartsWithMatcher
141
- attr_reader :prefix
140
+ class StringRegexpMatcher
141
+ attr_reader :regexp
142
142
 
143
- def initialize(prefix)
144
- @prefix = prefix
143
+ def initialize(regexp)
144
+ @regexp = regexp
145
145
  end
146
146
 
147
147
  def ==(other)
148
- other.instance_of?(self.class) && @prefix == other.prefix
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.start_with?(@prefix)
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
 
@@ -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 => StartsWithMatcher.new(string_value(val[2]))
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 => EndsWithMatcher.new(string_value(val[2]))
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 StartsWithMatcher do
412
+ describe StringRegexpMatcher do
413
413
  before :each do
414
- @matcher = StartsWithMatcher.new("abcd")
414
+ @matcher = StringRegexpMatcher.new(/abcd/)
415
415
  end
416
416
 
417
417
  describe "initialize" do
418
418
  it "sets attributes correctly" do
419
- @matcher.prefix.should == "abcd"
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 == StartsWithMatcher.new("abcd")
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 SubclassedStartsWithMatcher < StartsWithMatcher
437
+ class SubclassedStringRegexpMatcher < StringRegexpMatcher
438
438
  end
439
439
 
440
- @matcher.should_not == SubclassedStartsWithMatcher.new("abcd")
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 == StartsWithMatcher.new("efgh")
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 starting with the prefix" do
450
- @matcher.matches?("abcdefgh").should be_true
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 starting with the prefix" do
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
@@ -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 => StartsWithMatcher.new("abcd"))
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 => EndsWithMatcher.new("abcd"))
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: 19
4
+ hash: 15
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 3
8
+ - 4
9
9
  - 0
10
- version: 0.3.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-09-27 00:00:00 +02:00
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