class Parslet::Expression::Treetop
  class Parser < Parslet::Parser
    root(:expression)
    
    rule(:expression) { alternatives }
    
    # alternative 'a' / 'b'
    rule(:alternatives) {
      (simple >> (spaced('/') >> simple).repeat).as(:alt)
    }
    
    # sequence by simple concatenation 'a' 'b'
    rule(:simple) { occurrence.repeat(1).as(:seq) }
    
    # occurrence modifiers
    rule(:occurrence) {
      atom.as(:repetition) >> spaced('*').as(:sign) |
      atom.as(:repetition) >> spaced('+').as(:sign) |
      atom.as(:repetition) >> repetition_spec |
      
      atom.as(:maybe) >> spaced('?') | 
      atom
    }
        
    rule(:atom) { 
      spaced('(') >> expression.as(:unwrap) >> spaced(')') |
      dot |
      string |
      char_class
    }
    
    # a character class
    rule(:char_class) {
      (str('[') >>
        (str('\\') >> any |
        str(']').absent? >> any).repeat(1) >>
      str(']')).as(:match) >> space?
    }
    
    # anything at all
    rule(:dot) { spaced('.').as(:any) }
    
    # recognizing strings
    rule(:string) {
      str('\'') >> 
      (
        (str('\\') >> any) |
        (str("'").absent? >> any)
      ).repeat.as(:string) >> 
      str('\'') >> space?
    }
    
    # repetition specification like {1, 2}
    rule(:repetition_spec) {
      spaced('{') >> 
        integer.maybe.as(:min) >> spaced(',') >> 
        integer.maybe.as(:max) >> spaced('}')
    }
    rule(:integer) {
      match['0-9'].repeat(1)
    }
    
    # whitespace handling
    rule(:space) { match("\s").repeat(1) }
    rule(:space?) { space.maybe }
    
    def spaced(str)
      str(str) >> space?
    end
  end
  
  class Transform < Parslet::Transform
    
    rule(:repetition => simple(:rep), :sign => simple(:sign)) { 
      min = sign=='+' ? 1 : 0
      Parslet::Atoms::Repetition.new(rep, min, nil) }
    rule(:repetition => simple(:rep), :min => simple(:min), :max => simple(:max)) { 
      Parslet::Atoms::Repetition.new(rep, 
        Integer(min || 0), 
        max && Integer(max) || nil) }
      
    rule(:alt => subtree(:alt))       { Parslet::Atoms::Alternative.new(*alt) }
    rule(:seq => sequence(:s))        { Parslet::Atoms::Sequence.new(*s) }
    rule(:unwrap => simple(:u))       { u }
    rule(:maybe => simple(:m))        { |d| d[:m].maybe }
    rule(:string => simple(:s))       { Parslet::Atoms::Str.new(s) }
    rule(:match => simple(:m))        { Parslet::Atoms::Re.new(m) }
    rule(:any => simple(:a))          { Parslet::Atoms::Re.new('.') }
  end
  
end