citrus 1.6.0 → 1.7.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/README +30 -7
 - data/examples/calc.citrus +2 -0
 - data/examples/calc.rb +3 -0
 - data/examples/ip.citrus +82 -0
 - data/examples/ip.rb +75 -0
 - data/extras/citrus.vim +1 -1
 - data/lib/citrus.rb +16 -8
 - data/lib/citrus/file.rb +24 -26
 - data/test/file_test.rb +11 -4
 - data/test/rule_test.rb +7 -0
 - metadata +5 -3
 
    
        data/README
    CHANGED
    
    | 
         @@ -10,7 +10,7 @@ elegance and expressiveness of the language with the simplicity and power of 
     | 
|
| 
       10 
10 
     | 
    
         
             
            parsing expressions.
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
      
 13 
     | 
    
         
            +
            = Installation
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         
             
            Via RubyGems:
         
     | 
| 
         @@ -24,7 +24,7 @@ From a local copy: 
     | 
|
| 
       24 
24 
     | 
    
         
             
              $ rake package && sudo rake install
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
      
 27 
     | 
    
         
            +
            = Background
         
     | 
| 
       28 
28 
     | 
    
         | 
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
            In order to be able to use Citrus effectively, you must first understand the
         
     | 
| 
         @@ -99,7 +99,7 @@ available to them including the text of the match, its position in the input, 
     | 
|
| 
       99 
99 
     | 
    
         
             
            and any submatches.
         
     | 
| 
       100 
100 
     | 
    
         | 
| 
       101 
101 
     | 
    
         | 
| 
       102 
     | 
    
         
            -
             
     | 
| 
      
 102 
     | 
    
         
            +
            = Syntax
         
     | 
| 
       103 
103 
     | 
    
         | 
| 
       104 
104 
     | 
    
         | 
| 
       105 
105 
     | 
    
         
             
            The most straightforward way to compose a Citrus grammar is to use Citrus' own
         
     | 
| 
         @@ -168,7 +168,7 @@ Note that any operator binds more tightly than the bar. 
     | 
|
| 
       168 
168 
     | 
    
         
             
            == Super
         
     | 
| 
       169 
169 
     | 
    
         | 
| 
       170 
170 
     | 
    
         
             
            When including a grammar inside another, all rules in the child that have the
         
     | 
| 
       171 
     | 
    
         
            -
            same name as a rule in the parent also have access to the super keyword to
         
     | 
| 
      
 171 
     | 
    
         
            +
            same name as a rule in the parent also have access to the "super" keyword to
         
     | 
| 
       172 
172 
     | 
    
         
             
            invoke the parent rule.
         
     | 
| 
       173 
173 
     | 
    
         | 
| 
       174 
174 
     | 
    
         
             
            == Labels
         
     | 
| 
         @@ -181,8 +181,31 @@ immediately preceding any expression. 
     | 
|
| 
       181 
181 
     | 
    
         
             
                              # expression may be referred to as "chars"
         
     | 
| 
       182 
182 
     | 
    
         
             
                              # in a block method
         
     | 
| 
       183 
183 
     | 
    
         | 
| 
      
 184 
     | 
    
         
            +
            == Precedence
         
     | 
| 
       184 
185 
     | 
    
         | 
| 
       185 
     | 
    
         
            -
             
     | 
| 
      
 186 
     | 
    
         
            +
            The following table contains a list of all operators and their precedence. A
         
     | 
| 
      
 187 
     | 
    
         
            +
            higher level of precedence indicates tighter binding.
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
            Operator    | Level of Precedence   | Name
         
     | 
| 
      
 190 
     | 
    
         
            +
            ----------------------------------------------------------------
         
     | 
| 
      
 191 
     | 
    
         
            +
            ''          | 6                     | Literal string
         
     | 
| 
      
 192 
     | 
    
         
            +
            ""          | 6                     | Literal string
         
     | 
| 
      
 193 
     | 
    
         
            +
            []          | 6                     | Character class
         
     | 
| 
      
 194 
     | 
    
         
            +
            .           | 6                     | Any character (dot)
         
     | 
| 
      
 195 
     | 
    
         
            +
            //          | 6                     | Regular expression
         
     | 
| 
      
 196 
     | 
    
         
            +
            ()          | 6                     | Grouping
         
     | 
| 
      
 197 
     | 
    
         
            +
            *           | 5                     | Repetition (arbitrary)
         
     | 
| 
      
 198 
     | 
    
         
            +
            +           | 5                     | Repetition (one or more)
         
     | 
| 
      
 199 
     | 
    
         
            +
            ?           | 5                     | Repetition (zero or one)
         
     | 
| 
      
 200 
     | 
    
         
            +
            &           | 4                     | And predicate
         
     | 
| 
      
 201 
     | 
    
         
            +
            !           | 4                     | Not predicate
         
     | 
| 
      
 202 
     | 
    
         
            +
            :           | 4                     | Label
         
     | 
| 
      
 203 
     | 
    
         
            +
            <>, {}      | 3                     | Extension
         
     | 
| 
      
 204 
     | 
    
         
            +
            e1 e2       | 2                     | Sequence
         
     | 
| 
      
 205 
     | 
    
         
            +
            e1 | e2     | 1                     | Ordered choice
         
     | 
| 
      
 206 
     | 
    
         
            +
             
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
            = Example
         
     | 
| 
       186 
209 
     | 
    
         | 
| 
       187 
210 
     | 
    
         | 
| 
       188 
211 
     | 
    
         
             
            Below is an example of a simple grammar that is able to parse strings of
         
     | 
| 
         @@ -300,7 +323,7 @@ Take a look at examples/calc.citrus for an example of a calculator that is able 
     | 
|
| 
       300 
323 
     | 
    
         
             
            to parse and evaluate more complex mathematical expressions.
         
     | 
| 
       301 
324 
     | 
    
         | 
| 
       302 
325 
     | 
    
         | 
| 
       303 
     | 
    
         
            -
             
     | 
| 
      
 326 
     | 
    
         
            +
            = Links
         
     | 
| 
       304 
327 
     | 
    
         | 
| 
       305 
328 
     | 
    
         | 
| 
       306 
329 
     | 
    
         
             
            http://mjijackson.com/citrus
         
     | 
| 
         @@ -309,7 +332,7 @@ http://en.wikipedia.org/wiki/Parsing_expression_grammar 
     | 
|
| 
       309 
332 
     | 
    
         
             
            http://treetop.rubyforge.org/index.html
         
     | 
| 
       310 
333 
     | 
    
         | 
| 
       311 
334 
     | 
    
         | 
| 
       312 
     | 
    
         
            -
             
     | 
| 
      
 335 
     | 
    
         
            +
            = License
         
     | 
| 
       313 
336 
     | 
    
         | 
| 
       314 
337 
     | 
    
         | 
| 
       315 
338 
     | 
    
         
             
            Copyright 2010 Michael Jackson
         
     | 
    
        data/examples/calc.citrus
    CHANGED
    
    | 
         @@ -1,6 +1,8 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # A grammar for mathematical formulas that apply the basic four operations to
         
     | 
| 
       2 
2 
     | 
    
         
             
            # non-negative numbers (integers and floats), respecting operator precedence and
         
     | 
| 
       3 
3 
     | 
    
         
             
            # ignoring whitespace.
         
     | 
| 
      
 4 
     | 
    
         
            +
            #
         
     | 
| 
      
 5 
     | 
    
         
            +
            # An identical grammar that is written using pure Ruby can be found in calc.rb.
         
     | 
| 
       4 
6 
     | 
    
         
             
            grammar Calc
         
     | 
| 
       5 
7 
     | 
    
         
             
              rule term
         
     | 
| 
       6 
8 
     | 
    
         
             
                additive | factor
         
     | 
    
        data/examples/calc.rb
    CHANGED
    
    | 
         @@ -3,6 +3,9 @@ require 'citrus' 
     | 
|
| 
       3 
3 
     | 
    
         
             
            # A grammar for mathematical formulas that apply the basic four operations to
         
     | 
| 
       4 
4 
     | 
    
         
             
            # non-negative numbers (integers and floats), respecting operator precedence and
         
     | 
| 
       5 
5 
     | 
    
         
             
            # ignoring whitespace.
         
     | 
| 
      
 6 
     | 
    
         
            +
            #
         
     | 
| 
      
 7 
     | 
    
         
            +
            # An identical grammar that is written using Citrus' own grammar syntax can be
         
     | 
| 
      
 8 
     | 
    
         
            +
            # found in calc.citrus.
         
     | 
| 
       6 
9 
     | 
    
         
             
            grammar :Calc do
         
     | 
| 
       7 
10 
     | 
    
         
             
              rule :term do
         
     | 
| 
       8 
11 
     | 
    
         
             
                any(:additive, :factor)
         
     | 
    
        data/examples/ip.citrus
    ADDED
    
    | 
         @@ -0,0 +1,82 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # The grammars in this file conform to the ABNF given in Appendix A of RFC 3986
         
     | 
| 
      
 2 
     | 
    
         
            +
            # Uniform Resource Identifier (URI): Generic Syntax.
         
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            # See http://tools.ietf.org/html/rfc3986#appendix-A for more information.
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            grammar IPv4Address
         
     | 
| 
      
 7 
     | 
    
         
            +
              # A host identified by an IPv4 literal address is represented in
         
     | 
| 
      
 8 
     | 
    
         
            +
              # dotted-decimal notation (a sequence of four decimal numbers in the
         
     | 
| 
      
 9 
     | 
    
         
            +
              # range 0 to 255, separated by "."), as described in [RFC1123] by
         
     | 
| 
      
 10 
     | 
    
         
            +
              # reference to [RFC0952].  Note that other forms of dotted notation may
         
     | 
| 
      
 11 
     | 
    
         
            +
              # be interpreted on some platforms, as described in Section 7.4, but
         
     | 
| 
      
 12 
     | 
    
         
            +
              # only the dotted-decimal form of four octets is allowed by this
         
     | 
| 
      
 13 
     | 
    
         
            +
              # grammar.
         
     | 
| 
      
 14 
     | 
    
         
            +
              rule IPv4address
         
     | 
| 
      
 15 
     | 
    
         
            +
                (dec-octet '.' dec-octet '.' dec-octet '.' dec-octet) {
         
     | 
| 
      
 16 
     | 
    
         
            +
                  def version; 4 end
         
     | 
| 
      
 17 
     | 
    
         
            +
                }
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
              rule dec-octet
         
     | 
| 
      
 21 
     | 
    
         
            +
                  '25' [0-5]        # 250-255
         
     | 
| 
      
 22 
     | 
    
         
            +
                | '2' [0-4] DIGIT   # 200-249
         
     | 
| 
      
 23 
     | 
    
         
            +
                | '1' DIGIT DIGIT   # 100-199
         
     | 
| 
      
 24 
     | 
    
         
            +
                | [1-9] DIGIT       # 10-99
         
     | 
| 
      
 25 
     | 
    
         
            +
                | DIGIT             # 0-9
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
              rule DIGIT
         
     | 
| 
      
 29 
     | 
    
         
            +
                [0-9]
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            grammar IPv6Address
         
     | 
| 
      
 34 
     | 
    
         
            +
              include IPv4Address
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              # A 128-bit IPv6 address is divided into eight 16-bit pieces. Each
         
     | 
| 
      
 37 
     | 
    
         
            +
              # piece is represented numerically in case-insensitive hexadecimal,
         
     | 
| 
      
 38 
     | 
    
         
            +
              # using one to four hexadecimal digits (leading zeroes are permitted).
         
     | 
| 
      
 39 
     | 
    
         
            +
              # The eight encoded pieces are given most-significant first, separated
         
     | 
| 
      
 40 
     | 
    
         
            +
              # by colon characters. Optionally, the least-significant two pieces
         
     | 
| 
      
 41 
     | 
    
         
            +
              # may instead be represented in IPv4 address textual format. A
         
     | 
| 
      
 42 
     | 
    
         
            +
              # sequence of one or more consecutive zero-valued 16-bit pieces within
         
     | 
| 
      
 43 
     | 
    
         
            +
              # the address may be elided, omitting all their digits and leaving
         
     | 
| 
      
 44 
     | 
    
         
            +
              # exactly two consecutive colons in their place to mark the elision.
         
     | 
| 
      
 45 
     | 
    
         
            +
              rule IPv6address
         
     | 
| 
      
 46 
     | 
    
         
            +
                (                         (h16 ":")6*6 ls32
         
     | 
| 
      
 47 
     | 
    
         
            +
                |                    "::" (h16 ":")5*5 ls32
         
     | 
| 
      
 48 
     | 
    
         
            +
                |  h16             ? "::" (h16 ":")4*4 ls32
         
     | 
| 
      
 49 
     | 
    
         
            +
                | (h16 (":" h16)*1)? "::" (h16 ":")3*3 ls32
         
     | 
| 
      
 50 
     | 
    
         
            +
                | (h16 (":" h16)*2)? "::" (h16 ":")2*2 ls32
         
     | 
| 
      
 51 
     | 
    
         
            +
                | (h16 (":" h16)*3)? "::"  h16 ":"     ls32
         
     | 
| 
      
 52 
     | 
    
         
            +
                | (h16 (":" h16)*4)? "::"              ls32
         
     | 
| 
      
 53 
     | 
    
         
            +
                | (h16 (":" h16)*5)? "::"              h16
         
     | 
| 
      
 54 
     | 
    
         
            +
                | (h16 (":" h16)*6)? "::"
         
     | 
| 
      
 55 
     | 
    
         
            +
                ) {
         
     | 
| 
      
 56 
     | 
    
         
            +
                  def version; 6 end
         
     | 
| 
      
 57 
     | 
    
         
            +
                }
         
     | 
| 
      
 58 
     | 
    
         
            +
              end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
              # 16-bit address represented in hexadecimal.
         
     | 
| 
      
 61 
     | 
    
         
            +
              rule h16
         
     | 
| 
      
 62 
     | 
    
         
            +
                HEXDIG 1*4
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
              # Least-significant 32 bits of address.
         
     | 
| 
      
 66 
     | 
    
         
            +
              rule ls32
         
     | 
| 
      
 67 
     | 
    
         
            +
                (h16 ":" h16) | IPv4address
         
     | 
| 
      
 68 
     | 
    
         
            +
              end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
              rule HEXDIG
         
     | 
| 
      
 71 
     | 
    
         
            +
                DIGIT | [a-fA-F] # Hexadecimal should be case-insensitive.
         
     | 
| 
      
 72 
     | 
    
         
            +
              end
         
     | 
| 
      
 73 
     | 
    
         
            +
            end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
            grammar IPAddress
         
     | 
| 
      
 76 
     | 
    
         
            +
              include IPv4Address
         
     | 
| 
      
 77 
     | 
    
         
            +
              include IPv6Address
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
              rule IPaddress
         
     | 
| 
      
 80 
     | 
    
         
            +
                IPv4address | IPv6address
         
     | 
| 
      
 81 
     | 
    
         
            +
              end
         
     | 
| 
      
 82 
     | 
    
         
            +
            end
         
     | 
    
        data/examples/ip.rb
    ADDED
    
    | 
         @@ -0,0 +1,75 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'citrus'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # This file contains a small suite of tests for the grammars found in ip.citrus.
         
     | 
| 
      
 4 
     | 
    
         
            +
            # If this file is run directly (i.e. using `ruby ip.rb') the tests will run.
         
     | 
| 
      
 5 
     | 
    
         
            +
            # Otherwise, this file may be required by another that needs access to the IP
         
     | 
| 
      
 6 
     | 
    
         
            +
            # address grammars just as any other file would be.
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            # Load and evaluate the grammars contained in ip.citrus into the global
         
     | 
| 
      
 9 
     | 
    
         
            +
            # namespace.
         
     | 
| 
      
 10 
     | 
    
         
            +
            Citrus.load(File.expand_path('../ip.citrus', __FILE__))
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            if $0 == __FILE__
         
     | 
| 
      
 13 
     | 
    
         
            +
              require 'test/unit'
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              class IPAddressTest < Test::Unit::TestCase
         
     | 
| 
      
 16 
     | 
    
         
            +
                def test_dec_octet
         
     | 
| 
      
 17 
     | 
    
         
            +
                  match = IPv4Address.parse('0', :root => :'dec-octet')
         
     | 
| 
      
 18 
     | 
    
         
            +
                  assert(match)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  match = IPv4Address.parse('255', :root => :'dec-octet')
         
     | 
| 
      
 21 
     | 
    
         
            +
                  assert(match)
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def test_hexdig
         
     | 
| 
      
 25 
     | 
    
         
            +
                  match = IPv6Address.parse('0', :root => :HEXDIG)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  assert(match)
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  match = IPv6Address.parse('A', :root => :HEXDIG)
         
     | 
| 
      
 29 
     | 
    
         
            +
                  assert(match)
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def test_v4
         
     | 
| 
      
 33 
     | 
    
         
            +
                  match = IPv4Address.parse('0.0.0.0')
         
     | 
| 
      
 34 
     | 
    
         
            +
                  assert(match)
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  match = IPv4Address.parse('255.255.255.255')
         
     | 
| 
      
 37 
     | 
    
         
            +
                  assert(match)
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  assert_raise Citrus::ParseError do
         
     | 
| 
      
 40 
     | 
    
         
            +
                    IPv4Address.parse('255.255.255')
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def test_v6
         
     | 
| 
      
 45 
     | 
    
         
            +
                  match = IPv6Address.parse('1:2:3:4:5:6:7:8')
         
     | 
| 
      
 46 
     | 
    
         
            +
                  assert(match)
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  match = IPv6Address.parse('12AD:34FC:A453:1922::')
         
     | 
| 
      
 49 
     | 
    
         
            +
                  assert(match)
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  match = IPv6Address.parse('12AD::34FC')
         
     | 
| 
      
 52 
     | 
    
         
            +
                  assert(match)
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  match = IPv6Address.parse('12AD::')
         
     | 
| 
      
 55 
     | 
    
         
            +
                  assert(match)
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  match = IPv6Address.parse('::')
         
     | 
| 
      
 58 
     | 
    
         
            +
                  assert(match)
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  assert_raise Citrus::ParseError do
         
     | 
| 
      
 61 
     | 
    
         
            +
                    IPv6Address.parse('1:2')
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                def test_all
         
     | 
| 
      
 66 
     | 
    
         
            +
                  match = IPAddress.parse('1.2.3.4')
         
     | 
| 
      
 67 
     | 
    
         
            +
                  assert(match)
         
     | 
| 
      
 68 
     | 
    
         
            +
                  assert_equal(4, match.version)
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                  match = IPAddress.parse('1:2:3:4::')
         
     | 
| 
      
 71 
     | 
    
         
            +
                  assert(match)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  assert_equal(6, match.version)
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
              end
         
     | 
| 
      
 75 
     | 
    
         
            +
            end
         
     | 
    
        data/extras/citrus.vim
    CHANGED
    
    | 
         @@ -16,7 +16,7 @@ syn case match 
     | 
|
| 
       16 
16 
     | 
    
         
             
            syn match ctDoubleColon "::" contained
         
     | 
| 
       17 
17 
     | 
    
         
             
            syn match ctConstant "\u\w*" contained
         
     | 
| 
       18 
18 
     | 
    
         
             
            syn match ctModule "\(\(::\)\?\u\w*\)\+" contains=ctDoubleColon,ctConstant contained
         
     | 
| 
       19 
     | 
    
         
            -
            syn match ctVariable "[a-zA- 
     | 
| 
      
 19 
     | 
    
         
            +
            syn match ctVariable "\a[a-zA-Z0-9_-]*" contained
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
            " Comments
         
     | 
| 
       22 
22 
     | 
    
         
             
            syn match ctComment "#.*" contains=@Spell
         
     | 
    
        data/lib/citrus.rb
    CHANGED
    
    | 
         @@ -4,7 +4,7 @@ 
     | 
|
| 
       4 
4 
     | 
    
         
             
            #
         
     | 
| 
       5 
5 
     | 
    
         
             
            # http://mjijackson.com/citrus
         
     | 
| 
       6 
6 
     | 
    
         
             
            module Citrus
         
     | 
| 
       7 
     | 
    
         
            -
              VERSION = [1,  
     | 
| 
      
 7 
     | 
    
         
            +
              VERSION = [1, 7, 0]
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
              Infinity = 1.0 / 0
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
         @@ -68,7 +68,7 @@ module Citrus 
     | 
|
| 
       68 
68 
     | 
    
         
             
                # created with this method may be assigned a name by being assigned to some
         
     | 
| 
       69 
69 
     | 
    
         
             
                # constant, e.g.:
         
     | 
| 
       70 
70 
     | 
    
         
             
                #
         
     | 
| 
       71 
     | 
    
         
            -
                #     Calc = Grammar.new {}
         
     | 
| 
      
 71 
     | 
    
         
            +
                #     Calc = Citrus::Grammar.new {}
         
     | 
| 
       72 
72 
     | 
    
         
             
                #
         
     | 
| 
       73 
73 
     | 
    
         
             
                def self.new(&block)
         
     | 
| 
       74 
74 
     | 
    
         
             
                  mod = Module.new { include Grammar }
         
     | 
| 
         @@ -143,8 +143,8 @@ module Citrus 
     | 
|
| 
       143 
143 
     | 
    
         
             
                end
         
     | 
| 
       144 
144 
     | 
    
         | 
| 
       145 
145 
     | 
    
         
             
                # Gets/sets the rule with the given +name+. If +obj+ is given the rule
         
     | 
| 
       146 
     | 
    
         
            -
                # will be set to the value of +obj+ passed through Rule# 
     | 
| 
       147 
     | 
    
         
            -
                #  
     | 
| 
      
 146 
     | 
    
         
            +
                # will be set to the value of +obj+ passed through Rule#new. If a block is
         
     | 
| 
      
 147 
     | 
    
         
            +
                # given, its return value will be used for the value of +obj+.
         
     | 
| 
       148 
148 
     | 
    
         
             
                #
         
     | 
| 
       149 
149 
     | 
    
         
             
                # It is important to note that this method will also check any included
         
     | 
| 
       150 
150 
     | 
    
         
             
                # grammars for a rule with the given +name+ if one cannot be found in this
         
     | 
| 
         @@ -156,7 +156,7 @@ module Citrus 
     | 
|
| 
       156 
156 
     | 
    
         
             
                  if obj
         
     | 
| 
       157 
157 
     | 
    
         
             
                    rule_names << sym unless has_rule?(sym)
         
     | 
| 
       158 
158 
     | 
    
         | 
| 
       159 
     | 
    
         
            -
                    rule = Rule. 
     | 
| 
      
 159 
     | 
    
         
            +
                    rule = Rule.new(obj)
         
     | 
| 
       160 
160 
     | 
    
         
             
                    rule.name = name
         
     | 
| 
       161 
161 
     | 
    
         
             
                    setup_super(rule, name)
         
     | 
| 
       162 
162 
     | 
    
         
             
                    rule.grammar = self
         
     | 
| 
         @@ -239,7 +239,7 @@ module Citrus 
     | 
|
| 
       239 
239 
     | 
    
         
             
                # the given +rule+. A block may also be given that will be used to create
         
     | 
| 
       240 
240 
     | 
    
         
             
                # an anonymous module. See Rule#ext=.
         
     | 
| 
       241 
241 
     | 
    
         
             
                def ext(rule, mod=nil)
         
     | 
| 
       242 
     | 
    
         
            -
                  rule = Rule. 
     | 
| 
      
 242 
     | 
    
         
            +
                  rule = Rule.new(rule)
         
     | 
| 
       243 
243 
     | 
    
         
             
                  mod = Proc.new if block_given?
         
     | 
| 
       244 
244 
     | 
    
         
             
                  rule.extension = mod if mod
         
     | 
| 
       245 
245 
     | 
    
         
             
                  rule
         
     | 
| 
         @@ -342,7 +342,7 @@ module Citrus 
     | 
|
| 
       342 
342 
     | 
    
         
             
              # Input during parsing.
         
     | 
| 
       343 
343 
     | 
    
         
             
              module Rule
         
     | 
| 
       344 
344 
     | 
    
         
             
                # Returns a new Rule object depending on the type of object given.
         
     | 
| 
       345 
     | 
    
         
            -
                def self. 
     | 
| 
      
 345 
     | 
    
         
            +
                def self.new(obj)
         
     | 
| 
       346 
346 
     | 
    
         
             
                  case obj
         
     | 
| 
       347 
347 
     | 
    
         
             
                  when Rule     then obj
         
     | 
| 
       348 
348 
     | 
    
         
             
                  when Symbol   then Alias.new(obj)
         
     | 
| 
         @@ -356,6 +356,14 @@ module Citrus 
     | 
|
| 
       356 
356 
     | 
    
         
             
                  end
         
     | 
| 
       357 
357 
     | 
    
         
             
                end
         
     | 
| 
       358 
358 
     | 
    
         | 
| 
      
 359 
     | 
    
         
            +
                # Creates a new rule object from the given expression.
         
     | 
| 
      
 360 
     | 
    
         
            +
                #
         
     | 
| 
      
 361 
     | 
    
         
            +
                #     Citrus::Rule.create('"a" | "b"')
         
     | 
| 
      
 362 
     | 
    
         
            +
                #
         
     | 
| 
      
 363 
     | 
    
         
            +
                def self.create(expr)
         
     | 
| 
      
 364 
     | 
    
         
            +
                  File.parse(expr, :root => :rule_body).value
         
     | 
| 
      
 365 
     | 
    
         
            +
                end
         
     | 
| 
      
 366 
     | 
    
         
            +
             
     | 
| 
       359 
367 
     | 
    
         
             
                @unique_id = 0
         
     | 
| 
       360 
368 
     | 
    
         | 
| 
       361 
369 
     | 
    
         
             
                # Generates a new rule id.
         
     | 
| 
         @@ -587,7 +595,7 @@ module Citrus 
     | 
|
| 
       587 
595 
     | 
    
         
             
                include Rule
         
     | 
| 
       588 
596 
     | 
    
         | 
| 
       589 
597 
     | 
    
         
             
                def initialize(rules=[])
         
     | 
| 
       590 
     | 
    
         
            -
                  @rules = rules.map {|r| Rule. 
     | 
| 
      
 598 
     | 
    
         
            +
                  @rules = rules.map {|r| Rule.new(r) }
         
     | 
| 
       591 
599 
     | 
    
         
             
                end
         
     | 
| 
       592 
600 
     | 
    
         | 
| 
       593 
601 
     | 
    
         
             
                # An array of the actual Rule objects this rule uses to match.
         
     | 
    
        data/lib/citrus/file.rb
    CHANGED
    
    | 
         @@ -75,7 +75,7 @@ module Citrus 
     | 
|
| 
       75 
75 
     | 
    
         
             
                end
         
     | 
| 
       76 
76 
     | 
    
         | 
| 
       77 
77 
     | 
    
         
             
                rule :sequence do
         
     | 
| 
       78 
     | 
    
         
            -
                  zero_or_more(: 
     | 
| 
      
 78 
     | 
    
         
            +
                  zero_or_more(:appendix) {
         
     | 
| 
       79 
79 
     | 
    
         
             
                    def values
         
     | 
| 
       80 
80 
     | 
    
         
             
                      matches.map {|m| m.value }
         
     | 
| 
       81 
81 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -86,44 +86,44 @@ module Citrus 
     | 
|
| 
       86 
86 
     | 
    
         
             
                  }
         
     | 
| 
       87 
87 
     | 
    
         
             
                end
         
     | 
| 
       88 
88 
     | 
    
         | 
| 
       89 
     | 
    
         
            -
                rule : 
     | 
| 
       90 
     | 
    
         
            -
                  all( 
     | 
| 
      
 89 
     | 
    
         
            +
                rule :appendix do
         
     | 
| 
      
 90 
     | 
    
         
            +
                  all(:prefix, zero_or_one(:extension)) {
         
     | 
| 
       91 
91 
     | 
    
         
             
                    def value
         
     | 
| 
       92 
     | 
    
         
            -
                      rule =  
     | 
| 
       93 
     | 
    
         
            -
                       
     | 
| 
       94 
     | 
    
         
            -
                      rule =  
     | 
| 
      
 92 
     | 
    
         
            +
                      rule = prefix.value
         
     | 
| 
      
 93 
     | 
    
         
            +
                      extension = matches[1].first
         
     | 
| 
      
 94 
     | 
    
         
            +
                      rule.extension = extension.value if extension
         
     | 
| 
       95 
95 
     | 
    
         
             
                      rule
         
     | 
| 
       96 
96 
     | 
    
         
             
                    end
         
     | 
| 
       97 
97 
     | 
    
         
             
                  }
         
     | 
| 
       98 
98 
     | 
    
         
             
                end
         
     | 
| 
       99 
99 
     | 
    
         | 
| 
       100 
     | 
    
         
            -
                rule : 
     | 
| 
       101 
     | 
    
         
            -
                  all(: 
     | 
| 
      
 100 
     | 
    
         
            +
                rule :prefix do
         
     | 
| 
      
 101 
     | 
    
         
            +
                  all(zero_or_one(:predicate), :suffix) {
         
     | 
| 
       102 
102 
     | 
    
         
             
                    def value
         
     | 
| 
       103 
103 
     | 
    
         
             
                      rule = suffix.value
         
     | 
| 
       104 
     | 
    
         
            -
                       
     | 
| 
       105 
     | 
    
         
            -
                       
     | 
| 
      
 104 
     | 
    
         
            +
                      predicate = matches[0].first
         
     | 
| 
      
 105 
     | 
    
         
            +
                      rule = predicate.wrap(rule) if predicate
         
     | 
| 
       106 
106 
     | 
    
         
             
                      rule
         
     | 
| 
       107 
107 
     | 
    
         
             
                    end
         
     | 
| 
       108 
108 
     | 
    
         
             
                  }
         
     | 
| 
       109 
109 
     | 
    
         
             
                end
         
     | 
| 
       110 
110 
     | 
    
         | 
| 
       111 
111 
     | 
    
         
             
                rule :suffix do
         
     | 
| 
       112 
     | 
    
         
            -
                  all(:primary, zero_or_one(: 
     | 
| 
      
 112 
     | 
    
         
            +
                  all(:primary, zero_or_one(:repeat)) {
         
     | 
| 
       113 
113 
     | 
    
         
             
                    def value
         
     | 
| 
       114 
114 
     | 
    
         
             
                      rule = primary.value
         
     | 
| 
       115 
     | 
    
         
            -
                       
     | 
| 
       116 
     | 
    
         
            -
                      rule =  
     | 
| 
      
 115 
     | 
    
         
            +
                      repeat = matches[1].first
         
     | 
| 
      
 116 
     | 
    
         
            +
                      rule = repeat.wrap(rule) if repeat
         
     | 
| 
       117 
117 
     | 
    
         
             
                      rule
         
     | 
| 
       118 
118 
     | 
    
         
             
                    end
         
     | 
| 
       119 
119 
     | 
    
         
             
                  }
         
     | 
| 
       120 
120 
     | 
    
         
             
                end
         
     | 
| 
       121 
121 
     | 
    
         | 
| 
       122 
122 
     | 
    
         
             
                rule :primary do
         
     | 
| 
       123 
     | 
    
         
            -
                  any(:super, :alias, : 
     | 
| 
      
 123 
     | 
    
         
            +
                  any(:super, :alias, :grouping, :terminal)
         
     | 
| 
       124 
124 
     | 
    
         
             
                end
         
     | 
| 
       125 
125 
     | 
    
         | 
| 
       126 
     | 
    
         
            -
                rule : 
     | 
| 
      
 126 
     | 
    
         
            +
                rule :grouping do
         
     | 
| 
       127 
127 
     | 
    
         
             
                  all(:lparen, :rule_body, :rparen) {
         
     | 
| 
       128 
128 
     | 
    
         
             
                    def value
         
     | 
| 
       129 
129 
     | 
    
         
             
                      rule_body.value
         
     | 
| 
         @@ -157,8 +157,10 @@ module Citrus 
     | 
|
| 
       157 
157 
     | 
    
         
             
                  }
         
     | 
| 
       158 
158 
     | 
    
         
             
                end
         
     | 
| 
       159 
159 
     | 
    
         | 
| 
      
 160 
     | 
    
         
            +
                # Rule names may contain letters, numbers, underscores, and dashes. They
         
     | 
| 
      
 161 
     | 
    
         
            +
                # MUST start with a letter.
         
     | 
| 
       160 
162 
     | 
    
         
             
                rule :rule_name do
         
     | 
| 
       161 
     | 
    
         
            -
                  all(/[a-zA- 
     | 
| 
      
 163 
     | 
    
         
            +
                  all(/[a-zA-Z][a-zA-Z0-9_-]*/, :space) {
         
     | 
| 
       162 
164 
     | 
    
         
             
                    def value
         
     | 
| 
       163 
165 
     | 
    
         
             
                      first.text
         
     | 
| 
       164 
166 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -184,7 +186,7 @@ module Citrus 
     | 
|
| 
       184 
186 
     | 
    
         
             
                rule :terminal do
         
     | 
| 
       185 
187 
     | 
    
         
             
                  any(:quoted_string, :character_class, :anything_symbol, :regular_expression) {
         
     | 
| 
       186 
188 
     | 
    
         
             
                    def value
         
     | 
| 
       187 
     | 
    
         
            -
                      Rule. 
     | 
| 
      
 189 
     | 
    
         
            +
                      Rule.new(super)
         
     | 
| 
       188 
190 
     | 
    
         
             
                    end
         
     | 
| 
       189 
191 
     | 
    
         
             
                  }
         
     | 
| 
       190 
192 
     | 
    
         
             
                end
         
     | 
| 
         @@ -221,7 +223,7 @@ module Citrus 
     | 
|
| 
       221 
223 
     | 
    
         
             
                  }
         
     | 
| 
       222 
224 
     | 
    
         
             
                end
         
     | 
| 
       223 
225 
     | 
    
         | 
| 
       224 
     | 
    
         
            -
                rule : 
     | 
| 
      
 226 
     | 
    
         
            +
                rule :predicate do
         
     | 
| 
       225 
227 
     | 
    
         
             
                  any(:and, :not, :label)
         
     | 
| 
       226 
228 
     | 
    
         
             
                end
         
     | 
| 
       227 
229 
     | 
    
         | 
| 
         @@ -254,11 +256,7 @@ module Citrus 
     | 
|
| 
       254 
256 
     | 
    
         
             
                end
         
     | 
| 
       255 
257 
     | 
    
         | 
| 
       256 
258 
     | 
    
         
             
                rule :extension do
         
     | 
| 
       257 
     | 
    
         
            -
                  any(:tag, :block) 
     | 
| 
       258 
     | 
    
         
            -
                    def apply(rule)
         
     | 
| 
       259 
     | 
    
         
            -
                      rule.extension = value
         
     | 
| 
       260 
     | 
    
         
            -
                    end
         
     | 
| 
       261 
     | 
    
         
            -
                  }
         
     | 
| 
      
 259 
     | 
    
         
            +
                  any(:tag, :block)
         
     | 
| 
       262 
260 
     | 
    
         
             
                end
         
     | 
| 
       263 
261 
     | 
    
         | 
| 
       264 
262 
     | 
    
         
             
                rule :tag do
         
     | 
| 
         @@ -277,8 +275,8 @@ module Citrus 
     | 
|
| 
       277 
275 
     | 
    
         
             
                  }
         
     | 
| 
       278 
276 
     | 
    
         
             
                end
         
     | 
| 
       279 
277 
     | 
    
         | 
| 
       280 
     | 
    
         
            -
                rule : 
     | 
| 
       281 
     | 
    
         
            -
                  any(:question, :plus, : 
     | 
| 
      
 278 
     | 
    
         
            +
                rule :repeat do
         
     | 
| 
      
 279 
     | 
    
         
            +
                  any(:question, :plus, :star_quantity) {
         
     | 
| 
       282 
280 
     | 
    
         
             
                    def wrap(rule)
         
     | 
| 
       283 
281 
     | 
    
         
             
                      Repeat.new(min, max, rule)
         
     | 
| 
       284 
282 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -299,7 +297,7 @@ module Citrus 
     | 
|
| 
       299 
297 
     | 
    
         
             
                  }
         
     | 
| 
       300 
298 
     | 
    
         
             
                end
         
     | 
| 
       301 
299 
     | 
    
         | 
| 
       302 
     | 
    
         
            -
                rule : 
     | 
| 
      
 300 
     | 
    
         
            +
                rule :star_quantity do
         
     | 
| 
       303 
301 
     | 
    
         
             
                  all(/[0-9]*/, '*', /[0-9]*/, :space) {
         
     | 
| 
       304 
302 
     | 
    
         
             
                    def min
         
     | 
| 
       305 
303 
     | 
    
         
             
                      matches[0] == '' ? 0 : matches[0].text.to_i
         
     | 
    
        data/test/file_test.rb
    CHANGED
    
    | 
         @@ -105,6 +105,13 @@ class CitrusFileTest < Test::Unit::TestCase 
     | 
|
| 
       105 
105 
     | 
    
         
             
                assert_kind_of(Rule, match.value)
         
     | 
| 
       106 
106 
     | 
    
         
             
                assert_instance_of(Repeat, match.value)
         
     | 
| 
       107 
107 
     | 
    
         | 
| 
      
 108 
     | 
    
         
            +
                match = grammar.parse('"a"*')
         
     | 
| 
      
 109 
     | 
    
         
            +
                assert(match)
         
     | 
| 
      
 110 
     | 
    
         
            +
                assert_kind_of(Rule, match.value)
         
     | 
| 
      
 111 
     | 
    
         
            +
                assert_instance_of(Repeat, match.value)
         
     | 
| 
      
 112 
     | 
    
         
            +
                assert_equal(0, match.value.min)
         
     | 
| 
      
 113 
     | 
    
         
            +
                assert_equal(Infinity, match.value.max)
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
       108 
115 
     | 
    
         
             
                match = grammar.parse('("a" "b")*')
         
     | 
| 
       109 
116 
     | 
    
         
             
                assert(match)
         
     | 
| 
       110 
117 
     | 
    
         
             
                assert_kind_of(Rule, match.value)
         
     | 
| 
         @@ -469,8 +476,8 @@ class CitrusFileTest < Test::Unit::TestCase 
     | 
|
| 
       469 
476 
     | 
    
         
             
                assert_equal(/a/i, match.value)
         
     | 
| 
       470 
477 
     | 
    
         
             
              end
         
     | 
| 
       471 
478 
     | 
    
         | 
| 
       472 
     | 
    
         
            -
              def  
     | 
| 
       473 
     | 
    
         
            -
                grammar = file(: 
     | 
| 
      
 479 
     | 
    
         
            +
              def test_predicate
         
     | 
| 
      
 480 
     | 
    
         
            +
                grammar = file(:predicate)
         
     | 
| 
       474 
481 
     | 
    
         | 
| 
       475 
482 
     | 
    
         
             
                match = grammar.parse('&')
         
     | 
| 
       476 
483 
     | 
    
         
             
                assert(match)
         
     | 
| 
         @@ -570,8 +577,8 @@ class CitrusFileTest < Test::Unit::TestCase 
     | 
|
| 
       570 
577 
     | 
    
         
             
                assert(match.value)
         
     | 
| 
       571 
578 
     | 
    
         
             
              end
         
     | 
| 
       572 
579 
     | 
    
         | 
| 
       573 
     | 
    
         
            -
              def  
     | 
| 
       574 
     | 
    
         
            -
                grammar = file(: 
     | 
| 
      
 580 
     | 
    
         
            +
              def test_repeat
         
     | 
| 
      
 581 
     | 
    
         
            +
                grammar = file(:repeat)
         
     | 
| 
       575 
582 
     | 
    
         | 
| 
       576 
583 
     | 
    
         
             
                match = grammar.parse('?')
         
     | 
| 
       577 
584 
     | 
    
         
             
                assert(match)
         
     | 
    
        data/test/rule_test.rb
    CHANGED
    
    | 
         @@ -20,6 +20,13 @@ class RuleTest < Test::Unit::TestCase 
     | 
|
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
              NumericModule = Module.new(&NumericProc)
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
      
 23 
     | 
    
         
            +
              def test_create
         
     | 
| 
      
 24 
     | 
    
         
            +
                rule = Rule.create('"a"')
         
     | 
| 
      
 25 
     | 
    
         
            +
                assert(rule)
         
     | 
| 
      
 26 
     | 
    
         
            +
                match = rule.match(input('a'))
         
     | 
| 
      
 27 
     | 
    
         
            +
                assert(match)
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
       23 
30 
     | 
    
         
             
              def test_match_module
         
     | 
| 
       24 
31 
     | 
    
         
             
                rule = EqualRule.new('a')
         
     | 
| 
       25 
32 
     | 
    
         
             
                rule.extension = MatchModule
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version 
     | 
|
| 
       4 
4 
     | 
    
         
             
              prerelease: false
         
     | 
| 
       5 
5 
     | 
    
         
             
              segments: 
         
     | 
| 
       6 
6 
     | 
    
         
             
              - 1
         
     | 
| 
       7 
     | 
    
         
            -
              -  
     | 
| 
      
 7 
     | 
    
         
            +
              - 7
         
     | 
| 
       8 
8 
     | 
    
         
             
              - 0
         
     | 
| 
       9 
     | 
    
         
            -
              version: 1. 
     | 
| 
      
 9 
     | 
    
         
            +
              version: 1.7.0
         
     | 
| 
       10 
10 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       11 
11 
     | 
    
         
             
            authors: 
         
     | 
| 
       12 
12 
     | 
    
         
             
            - Michael Jackson
         
     | 
| 
         @@ -14,7 +14,7 @@ autorequire: 
     | 
|
| 
       14 
14 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       15 
15 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
            date: 2010-08-17 00:00:00 - 
     | 
| 
      
 17 
     | 
    
         
            +
            date: 2010-08-17 00:00:00 -05:00
         
     | 
| 
       18 
18 
     | 
    
         
             
            default_executable: 
         
     | 
| 
       19 
19 
     | 
    
         
             
            dependencies: 
         
     | 
| 
       20 
20 
     | 
    
         
             
            - !ruby/object:Gem::Dependency 
         
     | 
| 
         @@ -63,6 +63,8 @@ files: 
     | 
|
| 
       63 
63 
     | 
    
         
             
            - doc/syntax.rdoc
         
     | 
| 
       64 
64 
     | 
    
         
             
            - examples/calc.citrus
         
     | 
| 
       65 
65 
     | 
    
         
             
            - examples/calc.rb
         
     | 
| 
      
 66 
     | 
    
         
            +
            - examples/ip.citrus
         
     | 
| 
      
 67 
     | 
    
         
            +
            - examples/ip.rb
         
     | 
| 
       66 
68 
     | 
    
         
             
            - extras/citrus.vim
         
     | 
| 
       67 
69 
     | 
    
         
             
            - lib/citrus/debug.rb
         
     | 
| 
       68 
70 
     | 
    
         
             
            - lib/citrus/file.rb
         
     |