machete 0.4.0 → 0.5.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 +2 -0
 - data/CHANGELOG +13 -0
 - data/Gemfile +4 -0
 - data/README.md +85 -22
 - data/Rakefile +2 -2
 - data/VERSION +1 -1
 - data/lib/machete.rb +87 -43
 - data/lib/machete/matchers.rb +24 -14
 - data/lib/machete/parser.y +99 -26
 - data/machete.gemspec +2 -2
 - data/spec/machete/matchers_spec.rb +69 -11
 - data/spec/machete/parser_spec.rb +183 -68
 - data/spec/machete_spec.rb +44 -16
 - metadata +17 -15
 
    
        data/.gitignore
    CHANGED
    
    
    
        data/CHANGELOG
    CHANGED
    
    | 
         @@ -1,5 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            0.5.0 (2012-08-19)
         
     | 
| 
      
 2 
     | 
    
         
            +
            ------------------
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            * Support for matching symbols using "^=", "$=" and "*=" operators.
         
     | 
| 
      
 5 
     | 
    
         
            +
            * Support for regexp literals and matching regexps using "*=" operator.
         
     | 
| 
      
 6 
     | 
    
         
            +
            * Extended symbol grammar to cover symbols denoting instance and class variable
         
     | 
| 
      
 7 
     | 
    
         
            +
              names (:@foo, :@@bar).
         
     | 
| 
      
 8 
     | 
    
         
            +
            * Matchete#matches? and Machete#find accept the pattern also in compiled
         
     | 
| 
      
 9 
     | 
    
         
            +
              form (instance of Machete:Matchers::Matcher class).
         
     | 
| 
      
 10 
     | 
    
         
            +
            * Works in Rubinius 1.9 mode.
         
     | 
| 
      
 11 
     | 
    
         
            +
            * Internal code improvements and fixes.
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
       1 
13 
     | 
    
         
             
            0.4.0 (2011-10-18)
         
     | 
| 
       2 
14 
     | 
    
         
             
            ------------------
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
       3 
16 
     | 
    
         
             
            * Support for "true", "false" and "nil" literals.
         
     | 
| 
       4 
17 
     | 
    
         
             
            * New "*=" operator matching part of a string.
         
     | 
| 
       5 
18 
     | 
    
         
             
            * Bundler support.
         
     | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,12 +1,16 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            Machete
         
     | 
| 
       2 
2 
     | 
    
         
             
            =======
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
     | 
    
         
            -
            Machete is a simple tool for matching Rubinius AST nodes against patterns. You 
     | 
| 
      
 4 
     | 
    
         
            +
            Machete is a simple tool for matching Rubinius AST nodes against patterns. You
         
     | 
| 
      
 5 
     | 
    
         
            +
            can use it if you are writing any kind of tool that processes Ruby code and
         
     | 
| 
      
 6 
     | 
    
         
            +
            needs to do some work on specific types of nodes, needs to find patterns in the
         
     | 
| 
      
 7 
     | 
    
         
            +
            code, etc.
         
     | 
| 
       5 
8 
     | 
    
         | 
| 
       6 
9 
     | 
    
         
             
            Installation
         
     | 
| 
       7 
10 
     | 
    
         
             
            ------------
         
     | 
| 
       8 
11 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
            You need to install [Rubinius](http://rubini.us/) 
     | 
| 
      
 12 
     | 
    
         
            +
            You need to install current development version of [Rubinius](http://rubini.us/)
         
     | 
| 
      
 13 
     | 
    
         
            +
            first. You can then install Machete:
         
     | 
| 
       10 
14 
     | 
    
         | 
| 
       11 
15 
     | 
    
         
             
                $ gem install machete
         
     | 
| 
       12 
16 
     | 
    
         | 
| 
         @@ -17,19 +21,21 @@ First, require the library: 
     | 
|
| 
       17 
21 
     | 
    
         | 
| 
       18 
22 
     | 
    
         
             
                require "machete"
         
     | 
| 
       19 
23 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
            You can now use one of two methods Machete offers: `Machete.matches?` and 
     | 
| 
      
 24 
     | 
    
         
            +
            You can now use one of two methods Machete offers: `Machete.matches?` and
         
     | 
| 
      
 25 
     | 
    
         
            +
            `Machete.find`.
         
     | 
| 
       21 
26 
     | 
    
         | 
| 
       22 
27 
     | 
    
         
             
            The `Machete.matches?` method matches a Rubinus AST node against a pattern:
         
     | 
| 
       23 
28 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
                Machete.matches?('foo.bar'.to_ast, 'Send< 
     | 
| 
      
 29 
     | 
    
         
            +
                Machete.matches?('foo.bar'.to_ast, 'Send<name = :bar>')
         
     | 
| 
       25 
30 
     | 
    
         
             
                # => true
         
     | 
| 
       26 
31 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
                Machete.matches?('42'.to_ast, 'Send< 
     | 
| 
      
 32 
     | 
    
         
            +
                Machete.matches?('42'.to_ast, 'Send<name = :bar>')
         
     | 
| 
       28 
33 
     | 
    
         
             
                # => false
         
     | 
| 
       29 
34 
     | 
    
         | 
| 
       30 
35 
     | 
    
         
             
            (See below for pattern syntax description.)
         
     | 
| 
       31 
36 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
            The `Machete.find` method finds all nodes in a Rubinius AST tree matching a 
     | 
| 
      
 37 
     | 
    
         
            +
            The `Machete.find` method finds all nodes in a Rubinius AST tree matching a
         
     | 
| 
      
 38 
     | 
    
         
            +
            pattern:
         
     | 
| 
       33 
39 
     | 
    
         | 
| 
       34 
40 
     | 
    
         
             
                Machete.find('42 + 43 + 44'.to_ast, 'FixnumLiteral')
         
     | 
| 
       35 
41 
     | 
    
         
             
                # => [
         
     | 
| 
         @@ -38,12 +44,24 @@ The `Machete.find` method finds all nodes in a Rubinius AST tree matching a patt 
     | 
|
| 
       38 
44 
     | 
    
         
             
                #      #<Rubinius::AST::FixnumLiteral:0x10c0 @value=42 @line=1>
         
     | 
| 
       39 
45 
     | 
    
         
             
                #    ]
         
     | 
| 
       40 
46 
     | 
    
         | 
| 
      
 47 
     | 
    
         
            +
            Both `Machete.matches?` and `Machete.find` also accept patterns in their
         
     | 
| 
      
 48 
     | 
    
         
            +
            compiled form (instance of `Machete::Matchers::Matcher`):
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                Machete.matches?(
         
     | 
| 
      
 51 
     | 
    
         
            +
                  'foo.bar'.to_ast,
         
     | 
| 
      
 52 
     | 
    
         
            +
                  Machete::Matchers::NodeMatcher.new("Send",
         
     | 
| 
      
 53 
     | 
    
         
            +
                    :name => Machete::Matchers::LiteralMatcher.new(:bar)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  )
         
     | 
| 
      
 55 
     | 
    
         
            +
                )
         
     | 
| 
      
 56 
     | 
    
         
            +
                # => true
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
       41 
58 
     | 
    
         
             
            Pattern Syntax
         
     | 
| 
       42 
59 
     | 
    
         
             
            --------------
         
     | 
| 
       43 
60 
     | 
    
         | 
| 
       44 
61 
     | 
    
         
             
            ### Basics
         
     | 
| 
       45 
62 
     | 
    
         | 
| 
       46 
     | 
    
         
            -
            Rubinius AST consists of instances of classes that represent various types of 
     | 
| 
      
 63 
     | 
    
         
            +
            Rubinius AST consists of instances of classes that represent various types of
         
     | 
| 
      
 64 
     | 
    
         
            +
            nodes:
         
     | 
| 
       47 
65 
     | 
    
         | 
| 
       48 
66 
     | 
    
         
             
                '42'.to_ast     # => #<Rubinius::AST::FixnumLiteral:0xf28 @value=42 @line=1>
         
     | 
| 
       49 
67 
     | 
    
         
             
                '"abcd"'.to_ast # => #<Rubinius::AST::StringLiteral:0xf60 @line=1 @string="abcd">
         
     | 
| 
         @@ -58,25 +76,31 @@ To specify multiple alternatives, use the choice operator: 
     | 
|
| 
       58 
76 
     | 
    
         
             
                Machete.matches?('42'.to_ast,     'FixnumLiteral | StringLiteral') # => true
         
     | 
| 
       59 
77 
     | 
    
         
             
                Machete.matches?('"abcd"'.to_ast, 'FixnumLiteral | StringLiteral') # => true
         
     | 
| 
       60 
78 
     | 
    
         | 
| 
       61 
     | 
    
         
            -
            If you don't care about the node type at all, use the `any` keyword (this is 
     | 
| 
      
 79 
     | 
    
         
            +
            If you don't care about the node type at all, use the `any` keyword (this is
         
     | 
| 
      
 80 
     | 
    
         
            +
            most useful when matching arrays — see below):
         
     | 
| 
       62 
81 
     | 
    
         | 
| 
       63 
82 
     | 
    
         
             
                Machete.matches?('42'.to_ast,     'any') # => true
         
     | 
| 
       64 
83 
     | 
    
         
             
                Machete.matches?('"abcd"'.to_ast, 'any') # => true
         
     | 
| 
       65 
84 
     | 
    
         | 
| 
       66 
85 
     | 
    
         
             
            ### Node Attributes
         
     | 
| 
       67 
86 
     | 
    
         | 
| 
       68 
     | 
    
         
            -
            If you want to match a specific attribute of a node, specify its value inside 
     | 
| 
      
 87 
     | 
    
         
            +
            If you want to match a specific attribute of a node, specify its value inside
         
     | 
| 
      
 88 
     | 
    
         
            +
            `<...>` right after the node name:
         
     | 
| 
       69 
89 
     | 
    
         | 
| 
       70 
90 
     | 
    
         
             
                Machete.matches?('42'.to_ast, 'FixnumLiteral<value = 42>') # => true
         
     | 
| 
       71 
91 
     | 
    
         
             
                Machete.matches?('45'.to_ast, 'FixnumLiteral<value = 42>') # => false
         
     | 
| 
       72 
92 
     | 
    
         | 
| 
       73 
     | 
    
         
            -
            The attribute value can be ` 
     | 
| 
      
 93 
     | 
    
         
            +
            The attribute value can be `nil`, `true`, `false`, integer, symbol, string,
         
     | 
| 
      
 94 
     | 
    
         
            +
            regexp, array or other pattern. The last option means you can easily match
         
     | 
| 
      
 95 
     | 
    
         
            +
            nested nodes recursively. You can also specify multiple attributes:
         
     | 
| 
       74 
96 
     | 
    
         | 
| 
       75 
97 
     | 
    
         
             
                Machete.matches?('foo.bar'.to_ast, 'Send<receiver = Send<receiver = Self, name = :foo>, name = :bar>') # => true
         
     | 
| 
       76 
98 
     | 
    
         | 
| 
       77 
     | 
    
         
            -
            #### String Attributes
         
     | 
| 
      
 99 
     | 
    
         
            +
            #### String And Symbol Attributes
         
     | 
| 
       78 
100 
     | 
    
         | 
| 
       79 
     | 
    
         
            -
            When matching string attributes values, you don't have to do a whole-string 
     | 
| 
      
 101 
     | 
    
         
            +
            When matching string attributes values, you don't have to do a whole-string
         
     | 
| 
      
 102 
     | 
    
         
            +
            match using the `=` operator. You can also match the beginning, the end or a
         
     | 
| 
      
 103 
     | 
    
         
            +
            part of a string attribute value using the `^=`, `$=` and `*=` operators:
         
     | 
| 
       80 
104 
     | 
    
         | 
| 
       81 
105 
     | 
    
         
             
                Machete.matches?('"abcd"'.to_ast, 'StringLiteral<string ^= "ab">') # => true
         
     | 
| 
       82 
106 
     | 
    
         
             
                Machete.matches?('"efgh"'.to_ast, 'StringLiteral<string ^= "ab">') # => false
         
     | 
| 
         @@ -85,9 +109,31 @@ When matching string attributes values, you don't have to do a whole-string matc 
     | 
|
| 
       85 
109 
     | 
    
         
             
                Machete.matches?('"abcd"'.to_ast, 'StringLiteral<string *= "bc">') # => true
         
     | 
| 
       86 
110 
     | 
    
         
             
                Machete.matches?('"efgh"'.to_ast, 'StringLiteral<string *= "bc">') # => false
         
     | 
| 
       87 
111 
     | 
    
         | 
| 
      
 112 
     | 
    
         
            +
            Match symbol attributes works in the same way:
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
                Machete.matches?(':abcd'.to_ast, 'SymbolLiteral<value ^= :ab>') # => true
         
     | 
| 
      
 115 
     | 
    
         
            +
                Machete.matches?(':efgh'.to_ast, 'SymbolLiteral<value ^= :ab>') # => false
         
     | 
| 
      
 116 
     | 
    
         
            +
                Machete.matches?(':abcd'.to_ast, 'SymbolLiteral<value $= :cd>') # => true
         
     | 
| 
      
 117 
     | 
    
         
            +
                Machete.matches?(':efgh'.to_ast, 'SymbolLiteral<value $= :cd>') # => false
         
     | 
| 
      
 118 
     | 
    
         
            +
                Machete.matches?(':abcd'.to_ast, 'SymbolLiteral<value *= :bc>') # => true
         
     | 
| 
      
 119 
     | 
    
         
            +
                Machete.matches?(':efgh'.to_ast, 'SymbolLiteral<value *= :bc>') # => false
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
            In addition, you can match string and symbol attributes using regular
         
     | 
| 
      
 122 
     | 
    
         
            +
            expressions together with the `*=` operator:
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                Machete.matches?('"abcd"'.to_ast, 'StringLiteral<string *= /bc/>') # => true
         
     | 
| 
      
 125 
     | 
    
         
            +
                Machete.matches?('"efgh"'.to_ast, 'StringLiteral<string *= /bc/>') # => false
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                Machete.matches?(':abcd'.to_ast, 'SymbolLiteral<value *= /bc/>') # => true
         
     | 
| 
      
 128 
     | 
    
         
            +
                Machete.matches?(':efgh'.to_ast, 'SymbolLiteral<value *= /bc/>') # => false
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
            The regular expressions can take the `i`, `m` and `x` options with the same
         
     | 
| 
      
 131 
     | 
    
         
            +
            semantics as in Ruby.
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
       88 
133 
     | 
    
         
             
            #### Array Attributes
         
     | 
| 
       89 
134 
     | 
    
         | 
| 
       90 
     | 
    
         
            -
            When matching array attribute values, the simplest way is to specify the array 
     | 
| 
      
 135 
     | 
    
         
            +
            When matching array attribute values, the simplest way is to specify the array
         
     | 
| 
      
 136 
     | 
    
         
            +
            elements exactly. They will be matched one-by-one.
         
     | 
| 
       91 
137 
     | 
    
         | 
| 
       92 
138 
     | 
    
         
             
                Machete.matches?('[1, 2]'.to_ast, 'ArrayLiteral<body = [FixnumLiteral<value = 1>, FixnumLiteral<value = 2>]>') # => true
         
     | 
| 
       93 
139 
     | 
    
         | 
| 
         @@ -96,7 +142,9 @@ If you don't care about the node type of some array elements, you can use `any`: 
     | 
|
| 
       96 
142 
     | 
    
         
             
                Machete.matches?('[1, 2]'.to_ast,      'ArrayLiteral<body = [any, FixnumLiteral<value = 2>]>') # => true
         
     | 
| 
       97 
143 
     | 
    
         
             
                Machete.matches?('["abcd", 2]'.to_ast, 'ArrayLiteral<body = [any, FixnumLiteral<value = 2>]>') # => true
         
     | 
| 
       98 
144 
     | 
    
         | 
| 
       99 
     | 
    
         
            -
            The best thing about array matching is that you can use quantifiers for 
     | 
| 
      
 145 
     | 
    
         
            +
            The best thing about array matching is that you can use quantifiers for
         
     | 
| 
      
 146 
     | 
    
         
            +
            elements: `*`, `+`, `?`, `{n}`, `{n,}`, `{,n}`, `{m,n}`. Their meaning is the
         
     | 
| 
      
 147 
     | 
    
         
            +
            same as in Perl-like regular expressions:
         
     | 
| 
       100 
148 
     | 
    
         | 
| 
       101 
149 
     | 
    
         
             
                Machete.matches?('[2]'.to_ast,          'ArrayLiteral<body = [any*, FixnumLiteral<value = 2>]>') # => true
         
     | 
| 
       102 
150 
     | 
    
         
             
                Machete.matches?('[1, 2]'.to_ast,       'ArrayLiteral<body = [any*, FixnumLiteral<value = 2>]>') # => true
         
     | 
| 
         @@ -126,7 +174,8 @@ The best thing about array matching is that you can use quantifiers for elements 
     | 
|
| 
       126 
174 
     | 
    
         
             
                Machete.matches?('[1, 1, 2]'.to_ast,    'ArrayLiteral<body = [any{1,2}, FixnumLiteral<value = 2>]>') # => true
         
     | 
| 
       127 
175 
     | 
    
         
             
                Machete.matches?('[1, 1, 1, 2]'.to_ast, 'ArrayLiteral<body = [any{1,2}, FixnumLiteral<value = 2>]>') # => false
         
     | 
| 
       128 
176 
     | 
    
         | 
| 
       129 
     | 
    
         
            -
            There are also two unusual quantifiers: `{even}` and `{odd}`. They specify that 
     | 
| 
      
 177 
     | 
    
         
            +
            There are also two unusual quantifiers: `{even}` and `{odd}`. They specify that
         
     | 
| 
      
 178 
     | 
    
         
            +
            the quantified expression must repeat even or odd number of times:
         
     | 
| 
       130 
179 
     | 
    
         | 
| 
       131 
180 
     | 
    
         
             
                Machete.matches?('[1, 2]'.to_ast,       'ArrayLiteral<body = [any{even}, FixnumLiteral<value = 2>]>') # => false
         
     | 
| 
       132 
181 
     | 
    
         
             
                Machete.matches?('[1, 1, 2]'.to_ast,    'ArrayLiteral<body = [any{even}, FixnumLiteral<value = 2>]>') # => true
         
     | 
| 
         @@ -134,26 +183,40 @@ There are also two unusual quantifiers: `{even}` and `{odd}`. They specify that 
     | 
|
| 
       134 
183 
     | 
    
         
             
                Machete.matches?('[1, 2]'.to_ast,       'ArrayLiteral<body = [any{odd}, FixnumLiteral<value = 2>]>') # => true
         
     | 
| 
       135 
184 
     | 
    
         
             
                Machete.matches?('[1, 1, 2]'.to_ast,    'ArrayLiteral<body = [any{odd}, FixnumLiteral<value = 2>]>') # => false
         
     | 
| 
       136 
185 
     | 
    
         | 
| 
       137 
     | 
    
         
            -
            These quantifiers are best used when matching hashes containing a specific key 
     | 
| 
      
 186 
     | 
    
         
            +
            These quantifiers are best used when matching hashes containing a specific key
         
     | 
| 
      
 187 
     | 
    
         
            +
            or value. This is because in Rubinius AST both hash keys and values are
         
     | 
| 
      
 188 
     | 
    
         
            +
            flattened into one array and the only thing distinguishing them is even or odd
         
     | 
| 
      
 189 
     | 
    
         
            +
            position.
         
     | 
| 
       138 
190 
     | 
    
         | 
| 
       139 
191 
     | 
    
         
             
            ### More Information
         
     | 
| 
       140 
192 
     | 
    
         | 
| 
       141 
     | 
    
         
            -
            For more details about the syntax see the `lib/machete/parser.y` file which 
     | 
| 
      
 193 
     | 
    
         
            +
            For more details about the syntax see the `lib/machete/parser.y` file which
         
     | 
| 
      
 194 
     | 
    
         
            +
            contains the pattern parser.
         
     | 
| 
       142 
195 
     | 
    
         | 
| 
       143 
196 
     | 
    
         
             
            FAQ
         
     | 
| 
       144 
197 
     | 
    
         
             
            ---
         
     | 
| 
       145 
198 
     | 
    
         | 
| 
       146 
     | 
    
         
            -
            **Why did you chose Rubinius AST as a base? Aren't there other tools for Ruby 
     | 
| 
      
 199 
     | 
    
         
            +
            **Why did you chose Rubinius AST as a base? Aren't there other tools for Ruby
         
     | 
| 
      
 200 
     | 
    
         
            +
            parsing which are not VM-specific?**
         
     | 
| 
       147 
201 
     | 
    
         | 
| 
       148 
202 
     | 
    
         
             
            There are three other tools which were considered but each has its issues:
         
     | 
| 
       149 
203 
     | 
    
         | 
| 
       150 
     | 
    
         
            -
            * [parse_tree](http://parsetree.rubyforge.org/) — unmaintained and unsupported 
     | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
       152 
     | 
    
         
            -
            * [ 
     | 
| 
      
 204 
     | 
    
         
            +
            * [parse_tree](http://parsetree.rubyforge.org/) — unmaintained and unsupported
         
     | 
| 
      
 205 
     | 
    
         
            +
              for 1.9
         
     | 
| 
      
 206 
     | 
    
         
            +
            * [ruby_parser](http://parsetree.rubyforge.org/) — sometimes reports wrong line
         
     | 
| 
      
 207 
     | 
    
         
            +
               numbers for the nodes (this is a killer for some use cases)
         
     | 
| 
      
 208 
     | 
    
         
            +
            * [Ripper](http://rubyforge.org/projects/ripper/) — usable but the generated AST
         
     | 
| 
      
 209 
     | 
    
         
            +
              is too low level (the patterns would be too complex and low-level)
         
     | 
| 
       153 
210 
     | 
    
         | 
| 
       154 
211 
     | 
    
         
             
            Rubinius AST is also by far the easiest to work with.
         
     | 
| 
       155 
212 
     | 
    
         | 
| 
      
 213 
     | 
    
         
            +
            Compatibility
         
     | 
| 
      
 214 
     | 
    
         
            +
            -------------
         
     | 
| 
      
 215 
     | 
    
         
            +
             
     | 
| 
      
 216 
     | 
    
         
            +
            Machete is compatible with both the 1.8 and 1.9 mode of Rubinius.
         
     | 
| 
      
 217 
     | 
    
         
            +
             
     | 
| 
       156 
218 
     | 
    
         
             
            Acknowledgement
         
     | 
| 
       157 
219 
     | 
    
         
             
            ---------------
         
     | 
| 
       158 
220 
     | 
    
         | 
| 
       159 
     | 
    
         
            -
            The general idea and inspiration for the pattern syntax was taken form Python's 
     | 
| 
      
 221 
     | 
    
         
            +
            The general idea and inspiration for the pattern syntax was taken form Python's
         
     | 
| 
      
 222 
     | 
    
         
            +
            [2to3](http://docs.python.org/library/2to3.html) tool.
         
     | 
    
        data/Rakefile
    CHANGED
    
    | 
         @@ -5,8 +5,8 @@ desc "Generate the expression parser" 
     | 
|
| 
       5 
5 
     | 
    
         
             
            task :parser do
         
     | 
| 
       6 
6 
     | 
    
         
             
              source = "lib/machete/parser.y"
         
     | 
| 
       7 
7 
     | 
    
         
             
              target = "lib/machete/parser.rb"
         
     | 
| 
       8 
     | 
    
         
            -
              unless uptodate?(target, source)
         
     | 
| 
       9 
     | 
    
         
            -
                 
     | 
| 
      
 8 
     | 
    
         
            +
              unless uptodate?(target, [source])
         
     | 
| 
      
 9 
     | 
    
         
            +
                sh "racc -o #{target} #{source}"
         
     | 
| 
       10 
10 
     | 
    
         
             
              end
         
     | 
| 
       11 
11 
     | 
    
         
             
            end
         
     | 
| 
       12 
12 
     | 
    
         | 
    
        data/VERSION
    CHANGED
    
    | 
         @@ -1 +1 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            0. 
     | 
| 
      
 1 
     | 
    
         
            +
            0.5.0
         
     | 
    
        data/lib/machete.rb
    CHANGED
    
    | 
         @@ -3,53 +3,97 @@ require File.expand_path(File.dirname(__FILE__) + "/machete/parser") 
     | 
|
| 
       3 
3 
     | 
    
         
             
            require File.expand_path(File.dirname(__FILE__) + "/machete/version")
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            module Machete
         
     | 
| 
       6 
     | 
    
         
            -
               
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                 
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
      
 6 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 7 
     | 
    
         
            +
                # Matches a Rubinius AST node against a pattern.
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # @param [Rubinius::AST::Node] node node to match
         
     | 
| 
      
 10 
     | 
    
         
            +
                # @param [String, Machete::Matchers::Matcher] pattern pattern to match the
         
     | 
| 
      
 11 
     | 
    
         
            +
                #   node against, either as a string (see {file:README.md} for syntax
         
     | 
| 
      
 12 
     | 
    
         
            +
                #   description) or in compiled form
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                # @example Test using a string pattern
         
     | 
| 
      
 15 
     | 
    
         
            +
                #   Machete.matches?('foo.bar'.to_ast, 'Send<name = :bar>')
         
     | 
| 
      
 16 
     | 
    
         
            +
                #   # => true
         
     | 
| 
      
 17 
     | 
    
         
            +
                #
         
     | 
| 
      
 18 
     | 
    
         
            +
                #   Machete.matches?('42'.to_ast, 'Send<name = :bar>')
         
     | 
| 
      
 19 
     | 
    
         
            +
                #   # => false
         
     | 
| 
      
 20 
     | 
    
         
            +
                #
         
     | 
| 
      
 21 
     | 
    
         
            +
                # @example Test using a compiled pattern
         
     | 
| 
      
 22 
     | 
    
         
            +
                #   Machete.matches?(
         
     | 
| 
      
 23 
     | 
    
         
            +
                #     'foo.bar'.to_ast,
         
     | 
| 
      
 24 
     | 
    
         
            +
                #     Machete::Matchers::NodeMatcher.new("Send",
         
     | 
| 
      
 25 
     | 
    
         
            +
                #       :name => Machete::Matchers::LiteralMatcher.new(:bar)
         
     | 
| 
      
 26 
     | 
    
         
            +
                #     )
         
     | 
| 
      
 27 
     | 
    
         
            +
                #   )
         
     | 
| 
      
 28 
     | 
    
         
            +
                #   # => true
         
     | 
| 
      
 29 
     | 
    
         
            +
                #
         
     | 
| 
      
 30 
     | 
    
         
            +
                #   Machete.matches?(
         
     | 
| 
      
 31 
     | 
    
         
            +
                #     '42'.to_ast,
         
     | 
| 
      
 32 
     | 
    
         
            +
                #     Machete::Matchers::NodeMatcher.new("Send",
         
     | 
| 
      
 33 
     | 
    
         
            +
                #       :name => Machete::Matchers::LiteralMatcher.new(:bar)
         
     | 
| 
      
 34 
     | 
    
         
            +
                #     )
         
     | 
| 
      
 35 
     | 
    
         
            +
                #   )
         
     | 
| 
      
 36 
     | 
    
         
            +
                #   # => false
         
     | 
| 
      
 37 
     | 
    
         
            +
                #
         
     | 
| 
      
 38 
     | 
    
         
            +
                # @return [Boolean] +true+ if the node matches the pattern, +false+
         
     | 
| 
      
 39 
     | 
    
         
            +
                #   otherwise
         
     | 
| 
      
 40 
     | 
    
         
            +
                #
         
     | 
| 
      
 41 
     | 
    
         
            +
                # @raise [Matchete::Parser::SyntaxError] if the pattern is invalid
         
     | 
| 
      
 42 
     | 
    
         
            +
                def matches?(node, pattern)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  compiled_pattern(pattern).matches?(node)
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                # Finds all nodes in a Rubinius AST matching a pattern.
         
     | 
| 
      
 47 
     | 
    
         
            +
                #
         
     | 
| 
      
 48 
     | 
    
         
            +
                # @param [Rubinius::AST::Node] ast tree to search
         
     | 
| 
      
 49 
     | 
    
         
            +
                # @param [String, Machete::Matchers::Matcher] pattern pattern to match the
         
     | 
| 
      
 50 
     | 
    
         
            +
                #   nodes against, either as a string (see {file:README.md} for syntax
         
     | 
| 
      
 51 
     | 
    
         
            +
                #   description) or in compiled form
         
     | 
| 
      
 52 
     | 
    
         
            +
                #
         
     | 
| 
      
 53 
     | 
    
         
            +
                # @example Search using a string pattern
         
     | 
| 
      
 54 
     | 
    
         
            +
                #   Machete.find('42 + 43 + 44'.to_ast, 'FixnumLiteral')
         
     | 
| 
      
 55 
     | 
    
         
            +
                #   # => [
         
     | 
| 
      
 56 
     | 
    
         
            +
                #   #      #<Rubinius::AST::FixnumLiteral:0x10b0 @value=44 @line=1>,
         
     | 
| 
      
 57 
     | 
    
         
            +
                #   #      #<Rubinius::AST::FixnumLiteral:0x10b8 @value=43 @line=1>,
         
     | 
| 
      
 58 
     | 
    
         
            +
                #   #      #<Rubinius::AST::FixnumLiteral:0x10c0 @value=42 @line=1>
         
     | 
| 
      
 59 
     | 
    
         
            +
                #   #    ]
         
     | 
| 
      
 60 
     | 
    
         
            +
                #
         
     | 
| 
      
 61 
     | 
    
         
            +
                # @example Search using a compiled pattern
         
     | 
| 
      
 62 
     | 
    
         
            +
                #   Machete.find(
         
     | 
| 
      
 63 
     | 
    
         
            +
                #     '42 + 43 + 44'.to_ast,
         
     | 
| 
      
 64 
     | 
    
         
            +
                #     Machete::Matchers::NodeMatcher.new("FixnumLiteral")
         
     | 
| 
      
 65 
     | 
    
         
            +
                #   )
         
     | 
| 
      
 66 
     | 
    
         
            +
                #   # => [
         
     | 
| 
      
 67 
     | 
    
         
            +
                #   #      #<Rubinius::AST::FixnumLiteral:0x10b0 @value=44 @line=1>,
         
     | 
| 
      
 68 
     | 
    
         
            +
                #   #      #<Rubinius::AST::FixnumLiteral:0x10b8 @value=43 @line=1>,
         
     | 
| 
      
 69 
     | 
    
         
            +
                #   #      #<Rubinius::AST::FixnumLiteral:0x10c0 @value=42 @line=1>
         
     | 
| 
      
 70 
     | 
    
         
            +
                #   #    ]
         
     | 
| 
      
 71 
     | 
    
         
            +
                #
         
     | 
| 
      
 72 
     | 
    
         
            +
                # @return [Array] list of matching nodes (in unspecified order)
         
     | 
| 
      
 73 
     | 
    
         
            +
                #
         
     | 
| 
      
 74 
     | 
    
         
            +
                # @raise [Matchete::Parser::SyntaxError] if the pattern is invalid
         
     | 
| 
      
 75 
     | 
    
         
            +
                def find(ast, pattern)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  matcher = compiled_pattern(pattern)
         
     | 
| 
       25 
77 
     | 
    
         | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
              # @param [Rubinius::AST::Node] ast tree to search
         
     | 
| 
       29 
     | 
    
         
            -
              # @param [String] pattern pattern to match the nodes against (see {file:README.md} for syntax description)
         
     | 
| 
       30 
     | 
    
         
            -
              #
         
     | 
| 
       31 
     | 
    
         
            -
              # @example
         
     | 
| 
       32 
     | 
    
         
            -
              #   Machete.find('42 + 43 + 44'.to_ast, 'FixnumLiteral')
         
     | 
| 
       33 
     | 
    
         
            -
              #   # => [
         
     | 
| 
       34 
     | 
    
         
            -
              #   #      #<Rubinius::AST::FixnumLiteral:0x10b0 @value=44 @line=1>,
         
     | 
| 
       35 
     | 
    
         
            -
              #   #      #<Rubinius::AST::FixnumLiteral:0x10b8 @value=43 @line=1>,
         
     | 
| 
       36 
     | 
    
         
            -
              #   #      #<Rubinius::AST::FixnumLiteral:0x10c0 @value=42 @line=1>
         
     | 
| 
       37 
     | 
    
         
            -
              #   #    ]
         
     | 
| 
       38 
     | 
    
         
            -
              #
         
     | 
| 
       39 
     | 
    
         
            -
              # @return [Array] list of matching nodes (in unspecified order)
         
     | 
| 
       40 
     | 
    
         
            -
              #
         
     | 
| 
       41 
     | 
    
         
            -
              # @raise [Matchete::Parser::SyntaxError] if the pattern is invalid
         
     | 
| 
       42 
     | 
    
         
            -
              def self.find(ast, pattern)
         
     | 
| 
       43 
     | 
    
         
            -
                matcher = Parser.new.parse(pattern)
         
     | 
| 
      
 78 
     | 
    
         
            +
                  result = []
         
     | 
| 
      
 79 
     | 
    
         
            +
                  result << ast if matcher.matches?(ast)
         
     | 
| 
       44 
80 
     | 
    
         | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
      
 81 
     | 
    
         
            +
                  ast.walk(true) do |dummy, node|
         
     | 
| 
      
 82 
     | 
    
         
            +
                    result << node if matcher.matches?(node)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    true
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
       47 
85 
     | 
    
         | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
                  result << node if matcher.matches?(node)
         
     | 
| 
       50 
     | 
    
         
            -
                  true
         
     | 
| 
      
 86 
     | 
    
         
            +
                  result
         
     | 
| 
       51 
87 
     | 
    
         
             
                end
         
     | 
| 
       52 
88 
     | 
    
         | 
| 
       53 
     | 
    
         
            -
                 
     | 
| 
      
 89 
     | 
    
         
            +
                private
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                def compiled_pattern(pattern)
         
     | 
| 
      
 92 
     | 
    
         
            +
                  if pattern.is_a?(String)
         
     | 
| 
      
 93 
     | 
    
         
            +
                    Parser.new.parse(pattern)
         
     | 
| 
      
 94 
     | 
    
         
            +
                  else
         
     | 
| 
      
 95 
     | 
    
         
            +
                    pattern
         
     | 
| 
      
 96 
     | 
    
         
            +
                  end
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
       54 
98 
     | 
    
         
             
              end
         
     | 
| 
       55 
99 
     | 
    
         
             
            end
         
     | 
    
        data/lib/machete/matchers.rb
    CHANGED
    
    | 
         @@ -1,7 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Machete
         
     | 
| 
       2 
     | 
    
         
            -
              # @private
         
     | 
| 
       3 
2 
     | 
    
         
             
              module Matchers
         
     | 
| 
       4 
     | 
    
         
            -
                # @private
         
     | 
| 
       5 
3 
     | 
    
         
             
                class Quantifier
         
     | 
| 
       6 
4 
     | 
    
         
             
                  # :min should be always set, :max can be nil (meaning infinity)
         
     | 
| 
       7 
5 
     | 
    
         
             
                  attr_reader :matcher, :min, :max, :step
         
     | 
| 
         @@ -19,8 +17,10 @@ module Machete 
     | 
|
| 
       19 
17 
     | 
    
         
             
                  end
         
     | 
| 
       20 
18 
     | 
    
         
             
                end
         
     | 
| 
       21 
19 
     | 
    
         | 
| 
       22 
     | 
    
         
            -
                 
     | 
| 
       23 
     | 
    
         
            -
                 
     | 
| 
      
 20 
     | 
    
         
            +
                class Matcher
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                class ChoiceMatcher < Matcher
         
     | 
| 
       24 
24 
     | 
    
         
             
                  attr_reader :alternatives
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
                  def initialize(alternatives)
         
     | 
| 
         @@ -36,8 +36,7 @@ module Machete 
     | 
|
| 
       36 
36 
     | 
    
         
             
                  end
         
     | 
| 
       37 
37 
     | 
    
         
             
                end
         
     | 
| 
       38 
38 
     | 
    
         | 
| 
       39 
     | 
    
         
            -
                 
     | 
| 
       40 
     | 
    
         
            -
                class NodeMatcher
         
     | 
| 
      
 39 
     | 
    
         
            +
                class NodeMatcher < Matcher
         
     | 
| 
       41 
40 
     | 
    
         
             
                  attr_reader :class_name, :attrs
         
     | 
| 
       42 
41 
     | 
    
         | 
| 
       43 
42 
     | 
    
         
             
                  def initialize(class_name, attrs = {})
         
     | 
| 
         @@ -56,8 +55,7 @@ module Machete 
     | 
|
| 
       56 
55 
     | 
    
         
             
                  end
         
     | 
| 
       57 
56 
     | 
    
         
             
                end
         
     | 
| 
       58 
57 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
                 
     | 
| 
       60 
     | 
    
         
            -
                class ArrayMatcher
         
     | 
| 
      
 58 
     | 
    
         
            +
                class ArrayMatcher < Matcher
         
     | 
| 
       61 
59 
     | 
    
         
             
                  attr_reader :items
         
     | 
| 
       62 
60 
     | 
    
         | 
| 
       63 
61 
     | 
    
         
             
                  def initialize(items)
         
     | 
| 
         @@ -119,8 +117,7 @@ module Machete 
     | 
|
| 
       119 
117 
     | 
    
         
             
                  end
         
     | 
| 
       120 
118 
     | 
    
         
             
                end
         
     | 
| 
       121 
119 
     | 
    
         | 
| 
       122 
     | 
    
         
            -
                 
     | 
| 
       123 
     | 
    
         
            -
                class LiteralMatcher
         
     | 
| 
      
 120 
     | 
    
         
            +
                class LiteralMatcher < Matcher
         
     | 
| 
       124 
121 
     | 
    
         
             
                  attr_reader :literal
         
     | 
| 
       125 
122 
     | 
    
         | 
| 
       126 
123 
     | 
    
         
             
                  def initialize(literal)
         
     | 
| 
         @@ -136,8 +133,7 @@ module Machete 
     | 
|
| 
       136 
133 
     | 
    
         
             
                  end
         
     | 
| 
       137 
134 
     | 
    
         
             
                end
         
     | 
| 
       138 
135 
     | 
    
         | 
| 
       139 
     | 
    
         
            -
                 
     | 
| 
       140 
     | 
    
         
            -
                class StringRegexpMatcher
         
     | 
| 
      
 136 
     | 
    
         
            +
                class RegexpMatcher < Matcher
         
     | 
| 
       141 
137 
     | 
    
         
             
                  attr_reader :regexp
         
     | 
| 
       142 
138 
     | 
    
         | 
| 
       143 
139 
     | 
    
         
             
                  def initialize(regexp)
         
     | 
| 
         @@ -147,14 +143,28 @@ module Machete 
     | 
|
| 
       147 
143 
     | 
    
         
             
                  def ==(other)
         
     | 
| 
       148 
144 
     | 
    
         
             
                    other.instance_of?(self.class) && @regexp == other.regexp
         
     | 
| 
       149 
145 
     | 
    
         
             
                  end
         
     | 
| 
      
 146 
     | 
    
         
            +
                end
         
     | 
| 
       150 
147 
     | 
    
         | 
| 
      
 148 
     | 
    
         
            +
                class SymbolRegexpMatcher < RegexpMatcher
         
     | 
| 
      
 149 
     | 
    
         
            +
                  def matches?(node)
         
     | 
| 
      
 150 
     | 
    
         
            +
                    node.is_a?(Symbol) && node.to_s =~ @regexp
         
     | 
| 
      
 151 
     | 
    
         
            +
                  end
         
     | 
| 
      
 152 
     | 
    
         
            +
                end
         
     | 
| 
      
 153 
     | 
    
         
            +
             
     | 
| 
      
 154 
     | 
    
         
            +
                class StringRegexpMatcher < RegexpMatcher
         
     | 
| 
       151 
155 
     | 
    
         
             
                  def matches?(node)
         
     | 
| 
       152 
156 
     | 
    
         
             
                    node.is_a?(String) && node =~ @regexp
         
     | 
| 
       153 
157 
     | 
    
         
             
                  end
         
     | 
| 
       154 
158 
     | 
    
         
             
                end
         
     | 
| 
       155 
159 
     | 
    
         | 
| 
       156 
     | 
    
         
            -
                 
     | 
| 
       157 
     | 
    
         
            -
             
     | 
| 
      
 160 
     | 
    
         
            +
                class IndifferentRegexpMatcher < RegexpMatcher
         
     | 
| 
      
 161 
     | 
    
         
            +
                  def matches?(node)
         
     | 
| 
      
 162 
     | 
    
         
            +
                    (node.is_a?(Symbol) && node.to_s =~ @regexp) ||
         
     | 
| 
      
 163 
     | 
    
         
            +
                    (node.is_a?(String) && node =~ @regexp)
         
     | 
| 
      
 164 
     | 
    
         
            +
                  end
         
     | 
| 
      
 165 
     | 
    
         
            +
                end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                class AnyMatcher < Matcher
         
     | 
| 
       158 
168 
     | 
    
         
             
                  def ==(other)
         
     | 
| 
       159 
169 
     | 
    
         
             
                    other.instance_of?(self.class)
         
     | 
| 
       160 
170 
     | 
    
         
             
                  end
         
     |