ghostwheel 0.0.1
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/Rakefile +46 -0
- data/lib/ghost_wheel.rb +28 -0
- data/lib/ghost_wheel/build_parser.rb +11 -0
- data/lib/ghost_wheel/errors.rb +9 -0
- data/lib/ghost_wheel/expression.rb +22 -0
- data/lib/ghost_wheel/expression/alternation.rb +25 -0
- data/lib/ghost_wheel/expression/empty.rb +15 -0
- data/lib/ghost_wheel/expression/end_of_file.rb +19 -0
- data/lib/ghost_wheel/expression/literal.rb +31 -0
- data/lib/ghost_wheel/expression/look_ahead.rb +33 -0
- data/lib/ghost_wheel/expression/optional.rb +26 -0
- data/lib/ghost_wheel/expression/query.rb +32 -0
- data/lib/ghost_wheel/expression/repetition.rb +44 -0
- data/lib/ghost_wheel/expression/rule.rb +24 -0
- data/lib/ghost_wheel/expression/sequence.rb +30 -0
- data/lib/ghost_wheel/expression/transform.rb +30 -0
- data/lib/ghost_wheel/parse_results.rb +9 -0
- data/lib/ghost_wheel/parser.rb +71 -0
- data/lib/ghost_wheel/parser_builder/ghost_wheel.rb +100 -0
- data/lib/ghost_wheel/parser_builder/ruby.rb +175 -0
- data/lib/ghost_wheel/scanner.rb +42 -0
- data/setup.rb +1360 -0
- data/test/dsl/tc_build_parser.rb +29 -0
- data/test/dsl/tc_ghost_wheel_dsl.rb +143 -0
- data/test/dsl/tc_ruby_dsl.rb +227 -0
- data/test/example/tc_json_core.rb +95 -0
- data/test/example/tc_json_ghost_wheel.rb +48 -0
- data/test/example/tc_json_ruby.rb +81 -0
- data/test/helpers/ghost_wheel_namespace.rb +24 -0
- data/test/helpers/json_tests.rb +63 -0
- data/test/helpers/parse_helpers.rb +83 -0
- data/test/parser/tc_alternation_expression.rb +27 -0
- data/test/parser/tc_empty_expression.rb +15 -0
- data/test/parser/tc_end_of_file_expression.rb +27 -0
- data/test/parser/tc_literal_expression.rb +55 -0
- data/test/parser/tc_look_ahead_expression.rb +41 -0
- data/test/parser/tc_memoization.rb +31 -0
- data/test/parser/tc_optional_expression.rb +31 -0
- data/test/parser/tc_parser.rb +78 -0
- data/test/parser/tc_query_expression.rb +40 -0
- data/test/parser/tc_repetition_expression.rb +76 -0
- data/test/parser/tc_rule_expression.rb +32 -0
- data/test/parser/tc_scanning.rb +192 -0
- data/test/parser/tc_sequence_expression.rb +42 -0
- data/test/parser/tc_transform_expression.rb +77 -0
- data/test/ts_all.rb +7 -0
- data/test/ts_dsl.rb +8 -0
- data/test/ts_example.rb +7 -0
- data/test/ts_parser.rb +19 -0
- metadata +94 -0
    
        data/Rakefile
    ADDED
    
    | @@ -0,0 +1,46 @@ | |
| 1 | 
            +
            require "rake/testtask"
         | 
| 2 | 
            +
            require "rake/gempackagetask"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            DIR     = File.dirname(__FILE__)
         | 
| 5 | 
            +
            LIB     = File.join(DIR, "lib", "ghost_wheel.rb")
         | 
| 6 | 
            +
            VERSION = File.read(LIB)[/^\s*VERSION\s*=\s*(['"])(\d\.\d\.\d)\1/, 2]
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            task :default => [:test]
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            Rake::TestTask.new do |test|
         | 
| 11 | 
            +
            	test.libs       << "test"
         | 
| 12 | 
            +
            	test.test_files =  %w[test/ts_all.rb]
         | 
| 13 | 
            +
            	test.verbose    =  true
         | 
| 14 | 
            +
            end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            GEM_SPEC = Gem::Specification.new do |spec|
         | 
| 17 | 
            +
              spec.name              =  "ghostwheel"
         | 
| 18 | 
            +
              spec.version           =  VERSION
         | 
| 19 | 
            +
              spec.platform          =  Gem::Platform::RUBY
         | 
| 20 | 
            +
              spec.summary           =  "Ghost Wheel is a pure Ruby parser generator."
         | 
| 21 | 
            +
              spec.files             =  Dir.glob("{lib,test}/**/*.rb").
         | 
| 22 | 
            +
                                            delete_if { |item| item.include?(".svn") } +
         | 
| 23 | 
            +
                                            ["Rakefile", "setup.rb"]
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              spec.test_suite_file   =  "test/ts_all.rb"
         | 
| 26 | 
            +
              # spec.has_rdoc          =  true
         | 
| 27 | 
            +
              # spec.extra_rdoc_files  =  %w{README INSTALL TODO CHANGELOG LICENSE}
         | 
| 28 | 
            +
              # spec.rdoc_options      << '--title' << 'Ghost Wheel Documentation' <<
         | 
| 29 | 
            +
              #                           '--main'  << 'README'
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              spec.require_path      =  'lib'
         | 
| 32 | 
            +
             | 
| 33 | 
            +
              spec.author            =  "James Edward Gray II"
         | 
| 34 | 
            +
              spec.email             =  "james@grayproductions.net"
         | 
| 35 | 
            +
              spec.rubyforge_project =  "ghostwheel"
         | 
| 36 | 
            +
              spec.homepage          =  "http://ghostwheel.rubyforge.org"
         | 
| 37 | 
            +
              spec.description       =  <<END_DESC
         | 
| 38 | 
            +
            Ghost Wheel is a packrat parser generator handling scanning and parsing of
         | 
| 39 | 
            +
            context-free grammars. Parsers can be built using an EBNF-like syntax or a with
         | 
| 40 | 
            +
            a Ruby DSL.
         | 
| 41 | 
            +
            END_DESC
         | 
| 42 | 
            +
            end
         | 
| 43 | 
            +
            Rake::GemPackageTask.new(GEM_SPEC) do |pkg|
         | 
| 44 | 
            +
              pkg.need_zip = true
         | 
| 45 | 
            +
              pkg.need_tar = true
         | 
| 46 | 
            +
            end
         | 
    
        data/lib/ghost_wheel.rb
    ADDED
    
    | @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby -w
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "ghost_wheel/errors"
         | 
| 4 | 
            +
            require "ghost_wheel/parse_results"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require "ghost_wheel/scanner"
         | 
| 7 | 
            +
            require "ghost_wheel/parser"
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            require "ghost_wheel/expression"
         | 
| 10 | 
            +
            require "ghost_wheel/expression/literal"
         | 
| 11 | 
            +
            require "ghost_wheel/expression/sequence"
         | 
| 12 | 
            +
            require "ghost_wheel/expression/alternation"
         | 
| 13 | 
            +
            require "ghost_wheel/expression/empty"
         | 
| 14 | 
            +
            require "ghost_wheel/expression/optional"
         | 
| 15 | 
            +
            require "ghost_wheel/expression/repetition"
         | 
| 16 | 
            +
            require "ghost_wheel/expression/look_ahead"
         | 
| 17 | 
            +
            require "ghost_wheel/expression/end_of_file"
         | 
| 18 | 
            +
            require "ghost_wheel/expression/query"
         | 
| 19 | 
            +
            require "ghost_wheel/expression/transform"
         | 
| 20 | 
            +
            require "ghost_wheel/expression/rule"
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            require "ghost_wheel/build_parser"
         | 
| 23 | 
            +
            require "ghost_wheel/parser_builder/ruby"
         | 
| 24 | 
            +
            require "ghost_wheel/parser_builder/ghost_wheel"
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            module GhostWheel
         | 
| 27 | 
            +
              VERSION = "0.0.1"
         | 
| 28 | 
            +
            end
         | 
| @@ -0,0 +1,9 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby -w
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GhostWheel
         | 
| 4 | 
            +
              class StackUnderflowError < RuntimeError; end
         | 
| 5 | 
            +
              class EmptyRuleError      < RuntimeError; end
         | 
| 6 | 
            +
              class ParseError          < RuntimeError; end
         | 
| 7 | 
            +
              class EmptyParseError     < ParseError;   end
         | 
| 8 | 
            +
              class FailedParseError    < ParseError;   end
         | 
| 9 | 
            +
            end
         | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby -w
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GhostWheel
         | 
| 4 | 
            +
              class Expression
         | 
| 5 | 
            +
                def parse(scanner, cache = Hash.new)
         | 
| 6 | 
            +
                  cache_key = "#{object_id}:#{scanner.pos}"
         | 
| 7 | 
            +
                  cache_hit = cache[cache_key]
         | 
| 8 | 
            +
                  if cache_hit.nil?
         | 
| 9 | 
            +
                    result           = uncached_parse(scanner, cache)
         | 
| 10 | 
            +
                    cache[cache_key] = [result, scanner.pos]
         | 
| 11 | 
            +
                    result
         | 
| 12 | 
            +
                  else
         | 
| 13 | 
            +
                    scanner.pos = cache_hit.last
         | 
| 14 | 
            +
                    cache_hit.first
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
                
         | 
| 18 | 
            +
                def ==(other)
         | 
| 19 | 
            +
                  self.class == other.class
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
              end
         | 
| 22 | 
            +
            end
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby -w
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GhostWheel
         | 
| 4 | 
            +
              class Expression
         | 
| 5 | 
            +
                class Alternation < Expression
         | 
| 6 | 
            +
                  def initialize(*expressions)
         | 
| 7 | 
            +
                    @expressions = expressions
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
                  
         | 
| 10 | 
            +
                  attr_reader :expressions
         | 
| 11 | 
            +
                
         | 
| 12 | 
            +
                  def uncached_parse(scanner, cache)
         | 
| 13 | 
            +
                    @expressions.each do |expression|
         | 
| 14 | 
            +
                      result = expression.parse(scanner, cache)
         | 
| 15 | 
            +
                      return result unless result.is_a? FailedParseResult
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
                    FailedParseResult.instance
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def ==(other)
         | 
| 21 | 
            +
                    super and @expressions == other.expressions
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby -w
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require "singleton"
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module GhostWheel
         | 
| 6 | 
            +
              class Expression
         | 
| 7 | 
            +
                class EndOfFile < Expression
         | 
| 8 | 
            +
                  include Singleton
         | 
| 9 | 
            +
                  
         | 
| 10 | 
            +
                  def uncached_parse(scanner, cache)
         | 
| 11 | 
            +
                    if scanner.eos?
         | 
| 12 | 
            +
                      EmptyParseResult.instance
         | 
| 13 | 
            +
                    else
         | 
| 14 | 
            +
                      FailedParseResult.instance
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
| @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby -w
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GhostWheel
         | 
| 4 | 
            +
              class Expression
         | 
| 5 | 
            +
                class Literal < Expression
         | 
| 6 | 
            +
                  def initialize(literal, skip = false)
         | 
| 7 | 
            +
                    @literal = case literal
         | 
| 8 | 
            +
                               when Regexp then literal
         | 
| 9 | 
            +
                               when Fixnum then /#{Regexp.escape(literal.chr)}/
         | 
| 10 | 
            +
                               else             /#{Regexp.escape(literal.to_s)}/
         | 
| 11 | 
            +
                               end
         | 
| 12 | 
            +
                    @skip = skip
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                  
         | 
| 15 | 
            +
                  attr_reader  :literal, :skip
         | 
| 16 | 
            +
                  alias_method :skip?,   :skip
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def uncached_parse(scanner, cache)
         | 
| 19 | 
            +
                    if scanner.scan(@literal)
         | 
| 20 | 
            +
                      @skip ? EmptyParseResult.instance : ParseResult.new(scanner.matched)
         | 
| 21 | 
            +
                    else
         | 
| 22 | 
            +
                      FailedParseResult.instance
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                  
         | 
| 26 | 
            +
                  def ==(other)
         | 
| 27 | 
            +
                    super and @literal.to_s == other.literal.to_s and @skip == other.skip
         | 
| 28 | 
            +
                  end
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby -w
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GhostWheel
         | 
| 4 | 
            +
              class Expression
         | 
| 5 | 
            +
                class LookAhead < Expression
         | 
| 6 | 
            +
                  def initialize(expression, negate = false)
         | 
| 7 | 
            +
                    @expression = expression
         | 
| 8 | 
            +
                    @negate     = negate
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
                  
         | 
| 11 | 
            +
                  attr_reader  :expression, :negate
         | 
| 12 | 
            +
                  alias_method :negate?,    :negate
         | 
| 13 | 
            +
                
         | 
| 14 | 
            +
                  def uncached_parse(scanner, cache)
         | 
| 15 | 
            +
                    matched = nil
         | 
| 16 | 
            +
                    scanner.transaction do
         | 
| 17 | 
            +
                      matched = @expression.parse(scanner, cache)
         | 
| 18 | 
            +
                      scanner.abort
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
                  
         | 
| 21 | 
            +
                    if matched.is_a? FailedParseResult
         | 
| 22 | 
            +
                      @negate ? EmptyParseResult.instance : matched
         | 
| 23 | 
            +
                    else
         | 
| 24 | 
            +
                      @negate ? FailedParseResult.instance : EmptyParseResult.instance
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
                  end
         | 
| 27 | 
            +
                  
         | 
| 28 | 
            +
                  def ==(other)
         | 
| 29 | 
            +
                    super and @expression == other.expression and @negate == other.negate
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby -w
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GhostWheel
         | 
| 4 | 
            +
              class Expression
         | 
| 5 | 
            +
                class Optional < Expression
         | 
| 6 | 
            +
                  def initialize(expression)
         | 
| 7 | 
            +
                    @expression = expression
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
                  
         | 
| 10 | 
            +
                  attr_reader :expression
         | 
| 11 | 
            +
                
         | 
| 12 | 
            +
                  def uncached_parse(scanner, cache)
         | 
| 13 | 
            +
                    result = @expression.parse(scanner, cache)
         | 
| 14 | 
            +
                    if result.is_a? FailedParseResult
         | 
| 15 | 
            +
                      EmptyParseResult.instance
         | 
| 16 | 
            +
                    else
         | 
| 17 | 
            +
                      result
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  def ==(other)
         | 
| 22 | 
            +
                    super and @expression == other.expression
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
            end
         | 
| @@ -0,0 +1,32 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby -w
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GhostWheel
         | 
| 4 | 
            +
              class Expression
         | 
| 5 | 
            +
                class Query < Expression
         | 
| 6 | 
            +
                  def initialize(expression, &query)
         | 
| 7 | 
            +
                    @expression = expression
         | 
| 8 | 
            +
                    @query      = query
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
                  
         | 
| 11 | 
            +
                  attr_reader :expression, :query
         | 
| 12 | 
            +
                  
         | 
| 13 | 
            +
                  def uncached_parse(scanner, cache)
         | 
| 14 | 
            +
                    result = @expression.parse(scanner, cache)
         | 
| 15 | 
            +
                    if result.is_a? FailedParseResult
         | 
| 16 | 
            +
                      result
         | 
| 17 | 
            +
                    else
         | 
| 18 | 
            +
                      value = result.is_a?(ParseResult) ? result.value : result
         | 
| 19 | 
            +
                      if @query[value]
         | 
| 20 | 
            +
                        result
         | 
| 21 | 
            +
                      else
         | 
| 22 | 
            +
                        FailedParseResult.instance
         | 
| 23 | 
            +
                      end
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                  
         | 
| 27 | 
            +
                  def ==(other)
         | 
| 28 | 
            +
                    super and @expression == other.expression and @query == other.query
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
              end
         | 
| 32 | 
            +
            end
         | 
| @@ -0,0 +1,44 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby -w
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GhostWheel
         | 
| 4 | 
            +
              class Expression
         | 
| 5 | 
            +
                class Repetition < Expression
         | 
| 6 | 
            +
                  def initialize(expression, minimum_count = 0, maximum_count = 1.0 / 0.0)
         | 
| 7 | 
            +
                    @expression    = expression
         | 
| 8 | 
            +
                    @minimum_count = minimum_count
         | 
| 9 | 
            +
                    @maximum_count = maximum_count
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
                  
         | 
| 12 | 
            +
                  attr_reader :expression, :minimum_count, :maximum_count
         | 
| 13 | 
            +
                
         | 
| 14 | 
            +
                  def uncached_parse(scanner, cache)
         | 
| 15 | 
            +
                    scanner.transaction do
         | 
| 16 | 
            +
                      parsed = ParseResult.new(Array.new)
         | 
| 17 | 
            +
                      while parsed.value.size < @maximum_count
         | 
| 18 | 
            +
                        case result = @expression.parse(scanner, cache)
         | 
| 19 | 
            +
                        when FailedParseResult then break
         | 
| 20 | 
            +
                        when ParseResult       then parsed.value << result.value
         | 
| 21 | 
            +
                        end
         | 
| 22 | 
            +
                      end
         | 
| 23 | 
            +
                    
         | 
| 24 | 
            +
                      if parsed.value.size >= @minimum_count
         | 
| 25 | 
            +
                        if parsed.value.empty?
         | 
| 26 | 
            +
                          EmptyParseResult.instance
         | 
| 27 | 
            +
                        else
         | 
| 28 | 
            +
                          parsed
         | 
| 29 | 
            +
                        end
         | 
| 30 | 
            +
                      else
         | 
| 31 | 
            +
                        scanner.abort(FailedParseResult.instance)
         | 
| 32 | 
            +
                      end
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def ==(other)
         | 
| 37 | 
            +
                    super                                 and
         | 
| 38 | 
            +
                    @expression    == other.expression    and
         | 
| 39 | 
            +
                    @minimum_count == other.minimum_count and
         | 
| 40 | 
            +
                    @maximum_count == other.maximum_count
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
            end
         | 
| @@ -0,0 +1,24 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby -w
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GhostWheel
         | 
| 4 | 
            +
              class Expression
         | 
| 5 | 
            +
                class Rule < Expression
         | 
| 6 | 
            +
                  def initialize(expression = nil)
         | 
| 7 | 
            +
                    @expression = expression
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
                
         | 
| 10 | 
            +
                  attr_accessor :expression
         | 
| 11 | 
            +
                
         | 
| 12 | 
            +
                  def uncached_parse(scanner, cache)
         | 
| 13 | 
            +
                    if @expression.nil?
         | 
| 14 | 
            +
                      raise EmptyRuleError, "You failed to set an expression for this rule."
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
                    @expression.parse(scanner, cache)
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  def ==(other)
         | 
| 20 | 
            +
                    super and @expression == other.expression
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby -w
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GhostWheel
         | 
| 4 | 
            +
              class Expression
         | 
| 5 | 
            +
                class Sequence < Expression
         | 
| 6 | 
            +
                  def initialize(*expressions)
         | 
| 7 | 
            +
                    @expressions = expressions
         | 
| 8 | 
            +
                  end
         | 
| 9 | 
            +
                  
         | 
| 10 | 
            +
                  attr_reader :expressions
         | 
| 11 | 
            +
                
         | 
| 12 | 
            +
                  def uncached_parse(scanner, cache)
         | 
| 13 | 
            +
                    scanner.transaction do
         | 
| 14 | 
            +
                      parsed = ParseResult.new(Array.new)
         | 
| 15 | 
            +
                      @expressions.each do |expression|
         | 
| 16 | 
            +
                        case result = expression.parse(scanner, cache)
         | 
| 17 | 
            +
                        when FailedParseResult then scanner.abort(result)
         | 
| 18 | 
            +
                        when ParseResult       then parsed.value << result.value
         | 
| 19 | 
            +
                        end
         | 
| 20 | 
            +
                      end
         | 
| 21 | 
            +
                      parsed
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def ==(other)
         | 
| 26 | 
            +
                    super and @expressions == other.expressions
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            #!/usr/bin/env ruby -w
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module GhostWheel
         | 
| 4 | 
            +
              class Expression
         | 
| 5 | 
            +
                class Transform < Expression
         | 
| 6 | 
            +
                  def initialize(expression, &transformer)
         | 
| 7 | 
            +
                    @expression  = expression
         | 
| 8 | 
            +
                    @transformer = transformer
         | 
| 9 | 
            +
                  end
         | 
| 10 | 
            +
                  
         | 
| 11 | 
            +
                  attr_reader :expression, :transformer
         | 
| 12 | 
            +
                
         | 
| 13 | 
            +
                  def uncached_parse(scanner, cache)
         | 
| 14 | 
            +
                    result = @expression.parse(scanner, cache)
         | 
| 15 | 
            +
                    if result.is_a? FailedParseResult
         | 
| 16 | 
            +
                      result
         | 
| 17 | 
            +
                    else
         | 
| 18 | 
            +
                      value = result.is_a?(ParseResult) ? result.value : result
         | 
| 19 | 
            +
                      ParseResult.new(@transformer[value])
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                  
         | 
| 23 | 
            +
                  def ==(other)
         | 
| 24 | 
            +
                    super                            and
         | 
| 25 | 
            +
                    @expression  == other.expression and
         | 
| 26 | 
            +
                    @transformer == other.transformer
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         |