grammar 0.5 → 0.8
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/benchmark/json.benchmark.rb +355 -0
- data/benchmark/json.grammar.rb +56 -0
- data/benchmark/json.grammar0_5.rb +57 -0
- data/benchmark/json.ll1.rb +155 -0
- data/benchmark/json.peggy.rb +174 -0
- data/benchmark/json.re.rb +81 -0
- data/lib/grammar.rb +212 -639
- data/lib/grammar/ruby.rb +606 -0
- data/lib/grammar/ruby/code.rb +1030 -0
- data/lib/grammar/ruby0.rb +521 -0
- data/lib/grammar/ruby2cext.rb +19 -0
- data/lib/grammar/rubycall.rb +21 -0
- data/test/advanced.rb +105 -0
- data/test/atoms.rb +77 -0
- data/test/basic.rb +32 -0
- data/test/composite.rb +147 -0
- data/test/molecules.rb +125 -0
- data/test/test_demo.rb +200 -0
- data/test/test_ruby.rb +30 -0
- data/test/test_ruby0.rb +30 -0
- data/test/test_ruby2cext.rb +30 -0
- data/test/test_rubycall.rb +30 -0
- metadata +45 -28
- data/samples/fact.tcl +0 -12
- data/samples/infix2postfix.rb +0 -114
- data/samples/tcl.rb +0 -163
- data/samples/test.infix +0 -4
- data/test/test_grammar.rb +0 -274
    
        data/samples/tcl.rb
    DELETED
    
    | @@ -1,163 +0,0 @@ | |
| 1 | 
            -
            #!/bin/env ruby
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require 'rubygems'
         | 
| 4 | 
            -
            require 'cursor/io'
         | 
| 5 | 
            -
            require 'cursor/indexed'
         | 
| 6 | 
            -
            require 'grammar'
         | 
| 7 | 
            -
            require 'duck'
         | 
| 8 | 
            -
            require 'set'
         | 
| 9 | 
            -
             | 
| 10 | 
            -
            class Tcl
         | 
| 11 | 
            -
             | 
| 12 | 
            -
            def initialize
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                @variables = {}
         | 
| 15 | 
            -
                @procs = {}
         | 
| 16 | 
            -
             | 
| 17 | 
            -
                space = Grammar::Element[Set[?\ ,?\t].duck!(:==,:include?)].discard
         | 
| 18 | 
            -
                newline = Grammar::Element[?\n]
         | 
| 19 | 
            -
                command_separator = newline|Grammar::Element[?;]
         | 
| 20 | 
            -
                hex = Grammar::Element[(
         | 
| 21 | 
            -
                    Set[]+(?0..?9)+(?a..?f)+(?A..?F)
         | 
| 22 | 
            -
                ).duck!(:==,:include?)]
         | 
| 23 | 
            -
                octal = Grammar::Element[(?0..?7).duck!(:==,:===)]
         | 
| 24 | 
            -
                alphanum = Grammar::Element[(
         | 
| 25 | 
            -
                    Set[?_]+(?0..?9)+(?a..?z)+(?A..?Z)
         | 
| 26 | 
            -
                ).duck!(:==,:include?)]
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                backslashed = Grammar::Element[?\\].discard+(
         | 
| 29 | 
            -
                    Grammar::Element[?a].filter { "\a" } |
         | 
| 30 | 
            -
                    Grammar::Element[?b].filter { "\b" } |
         | 
| 31 | 
            -
                    Grammar::Element[?f].filter { "\f" } |
         | 
| 32 | 
            -
                    Grammar::Element[?n].filter { "\n" } |
         | 
| 33 | 
            -
                    Grammar::Element[?r].filter { "\r" } |
         | 
| 34 | 
            -
                    Grammar::Element[?t].filter { "\t" } |
         | 
| 35 | 
            -
                    Grammar::Element[?v].filter { "\v" } |
         | 
| 36 | 
            -
                    Grammar::Element[?x].discard+hex.list1.filter { |n|
         | 
| 37 | 
            -
                        eval(%Q("\\x#{n}"))
         | 
| 38 | 
            -
                    } |
         | 
| 39 | 
            -
                    Grammar::Element[?u].discard+hex.list1 { |n| # don't know what to do with unicode
         | 
| 40 | 
            -
                        eval(%Q("\\x#{n}"))
         | 
| 41 | 
            -
                    } |
         | 
| 42 | 
            -
                    octal.list1.filter { |n|
         | 
| 43 | 
            -
                        eval(%Q("\\#{n}"))
         | 
| 44 | 
            -
                    } |
         | 
| 45 | 
            -
                    (newline.discard+space.list0).filter { " " } |
         | 
| 46 | 
            -
                    Grammar::ANY
         | 
| 47 | 
            -
                )
         | 
| 48 | 
            -
                
         | 
| 49 | 
            -
                braced_element = Grammar.new { |braced_element|
         | 
| 50 | 
            -
                    Grammar::Element[?\{]+braced_element.list0(Grammar::Element[?\}]) |
         | 
| 51 | 
            -
                    Grammar::Element[?\\].discard+(
         | 
| 52 | 
            -
                        (newline.discard+space.list0).filter { " " } |
         | 
| 53 | 
            -
                        Grammar::NULL.filter { "\\" }
         | 
| 54 | 
            -
                    ) |
         | 
| 55 | 
            -
                    newline |
         | 
| 56 | 
            -
                    Grammar::ANY
         | 
| 57 | 
            -
                }
         | 
| 58 | 
            -
                braced = Grammar::Element[?\{].discard+braced_element.list0(Grammar::Element[?\}].discard)
         | 
| 59 | 
            -
                
         | 
| 60 | 
            -
                element = Grammar.new
         | 
| 61 | 
            -
                
         | 
| 62 | 
            -
                varname = (
         | 
| 63 | 
            -
                    alphanum |
         | 
| 64 | 
            -
                    Grammar::Element[?\:]*(2..+1.0/0)
         | 
| 65 | 
            -
                ).list1
         | 
| 66 | 
            -
                index = Grammar::Element[?\(]+element.list1(Grammar::Element[?\)])
         | 
| 67 | 
            -
                variable = (Grammar::Element[?\$].discard + (
         | 
| 68 | 
            -
                    varname.group(String) + index.group(String).optional |
         | 
| 69 | 
            -
                    braced.group(String)
         | 
| 70 | 
            -
                )).filter { |var| @variables[var.to_s.to_sym].to_s }
         | 
| 71 | 
            -
                
         | 
| 72 | 
            -
                quoted = Grammar::Element[?\"].discard+element.list1(Grammar::Element[?\"].discard)
         | 
| 73 | 
            -
                comment = (Grammar::Element[?\#]+Grammar::ANY.list0(newline)).discard
         | 
| 74 | 
            -
                
         | 
| 75 | 
            -
                commander = lambda { |terminator|
         | 
| 76 | 
            -
                    word_terminator = space.list1|+command_separator|+terminator
         | 
| 77 | 
            -
                    word = ((braced|quoted)+word_terminator|element.list1(word_terminator)).
         | 
| 78 | 
            -
                        group(String).filter {|x,t| t.concat(x)}
         | 
| 79 | 
            -
                    command = space.list0 + (
         | 
| 80 | 
            -
                        comment |
         | 
| 81 | 
            -
                        word.list0(command_separator.discard|+terminator).
         | 
| 82 | 
            -
                            filter(Array) { |com,ret|
         | 
| 83 | 
            -
                                com.empty? ? ret : ret.replace(send(*com).to_s)
         | 
| 84 | 
            -
                            }
         | 
| 85 | 
            -
                    )
         | 
| 86 | 
            -
                    command.list0(terminator.discard)
         | 
| 87 | 
            -
                }
         | 
| 88 | 
            -
                
         | 
| 89 | 
            -
                bracketed = Grammar::Element[?[].discard+commander[Grammar::Element[?]]]
         | 
| 90 | 
            -
                
         | 
| 91 | 
            -
                @interpreter = commander[Grammar::EOF]
         | 
| 92 | 
            -
             | 
| 93 | 
            -
                element << (backslashed | bracketed | variable | newline | Grammar::ANY)
         | 
| 94 | 
            -
             | 
| 95 | 
            -
            end
         | 
| 96 | 
            -
             | 
| 97 | 
            -
            def interpret(cursor)
         | 
| 98 | 
            -
                @interpreter.scan(cursor,"",false)
         | 
| 99 | 
            -
            end
         | 
| 100 | 
            -
             | 
| 101 | 
            -
            def method_missing(name,*args)
         | 
| 102 | 
            -
                vars,body = *@procs[name]
         | 
| 103 | 
            -
                return "<#{name}#{args.inspect}>" if !body
         | 
| 104 | 
            -
                variables = @variables
         | 
| 105 | 
            -
                @variables = {}
         | 
| 106 | 
            -
                vars.zip(args).each { |var,val| @variables[var.to_sym] = val }
         | 
| 107 | 
            -
                ret = interpret(body.to_cursor)
         | 
| 108 | 
            -
                @variables = variables
         | 
| 109 | 
            -
                ret
         | 
| 110 | 
            -
            end
         | 
| 111 | 
            -
             | 
| 112 | 
            -
            def proc(name,args,body)
         | 
| 113 | 
            -
                @procs[name.to_sym] = [args,body]
         | 
| 114 | 
            -
                ""
         | 
| 115 | 
            -
            end
         | 
| 116 | 
            -
             | 
| 117 | 
            -
            def set(name,value)
         | 
| 118 | 
            -
                @variables[name.to_sym] = value
         | 
| 119 | 
            -
            end
         | 
| 120 | 
            -
             | 
| 121 | 
            -
            def if(condition,body)
         | 
| 122 | 
            -
                # should really use expr to get condition
         | 
| 123 | 
            -
                unless %w(0 false no off).include?(condition.to_s)
         | 
| 124 | 
            -
                    interpret(body.to_cursor)
         | 
| 125 | 
            -
                end
         | 
| 126 | 
            -
                # need to handle elsif and else
         | 
| 127 | 
            -
                ""
         | 
| 128 | 
            -
            end
         | 
| 129 | 
            -
             | 
| 130 | 
            -
            def sum(*values)
         | 
| 131 | 
            -
                values.inject(0) { |sum, v| sum + eval(v) }
         | 
| 132 | 
            -
            end
         | 
| 133 | 
            -
             | 
| 134 | 
            -
            def product(*values)
         | 
| 135 | 
            -
                values.inject(1) { |sum, v| sum * eval(v) }
         | 
| 136 | 
            -
            end
         | 
| 137 | 
            -
             | 
| 138 | 
            -
            def subtract(a,b)
         | 
| 139 | 
            -
                eval(a)-eval(b)
         | 
| 140 | 
            -
            end
         | 
| 141 | 
            -
             | 
| 142 | 
            -
            def divide(a,b)
         | 
| 143 | 
            -
                eval(a)/eval(b)
         | 
| 144 | 
            -
            end
         | 
| 145 | 
            -
             | 
| 146 | 
            -
            def puts(str)
         | 
| 147 | 
            -
                $stdout.print(str,"\n")
         | 
| 148 | 
            -
                ""
         | 
| 149 | 
            -
            end
         | 
| 150 | 
            -
             | 
| 151 | 
            -
            def concat(*args)
         | 
| 152 | 
            -
                args.inject("") { |concatenated, arg| concatenated.concat(arg) }
         | 
| 153 | 
            -
            end
         | 
| 154 | 
            -
             | 
| 155 | 
            -
            end
         | 
| 156 | 
            -
             | 
| 157 | 
            -
            if $0==__FILE__
         | 
| 158 | 
            -
                result = Tcl.new.interpret($stdin.to_cursor)
         | 
| 159 | 
            -
                p result
         | 
| 160 | 
            -
                result
         | 
| 161 | 
            -
            end
         | 
| 162 | 
            -
             | 
| 163 | 
            -
             | 
    
        data/samples/test.infix
    DELETED
    
    
    
        data/test/test_grammar.rb
    DELETED
    
    | @@ -1,274 +0,0 @@ | |
| 1 | 
            -
            #!/bin/env ruby
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            require 'rubygems'
         | 
| 4 | 
            -
            require 'test/unit'
         | 
| 5 | 
            -
            require 'test/unit/collector'
         | 
| 6 | 
            -
            require 'cursor/indexed'
         | 
| 7 | 
            -
            require 'grammar'
         | 
| 8 | 
            -
             | 
| 9 | 
            -
            module Test   
         | 
| 10 | 
            -
              module Unit
         | 
| 11 | 
            -
                class AutoRunner
         | 
| 12 | 
            -
                  alias_method(:_options_,:options)
         | 
| 13 | 
            -
                  def options
         | 
| 14 | 
            -
                    @options = _options_
         | 
| 15 | 
            -
                    @options.on('-i', '--iterations=NUMBER', Float,
         | 
| 16 | 
            -
                           "Randomly run tests for a number of iterations.") do |iterations|
         | 
| 17 | 
            -
                        $random_iterations = iterations
         | 
| 18 | 
            -
                      end
         | 
| 19 | 
            -
                    @options.on('-s', '--seed=NUMBER', Integer,
         | 
| 20 | 
            -
                           "Random seed.") do |seed|
         | 
| 21 | 
            -
                        $random_seed = seed.nonzero?
         | 
| 22 | 
            -
                      end
         | 
| 23 | 
            -
                    @options
         | 
| 24 | 
            -
                  end
         | 
| 25 | 
            -
                  alias_method(:_run_,:run)
         | 
| 26 | 
            -
                  def run
         | 
| 27 | 
            -
                    $output_level = @output_level
         | 
| 28 | 
            -
                    $random_seed ||= (srand;srand)
         | 
| 29 | 
            -
                    srand($random_seed)
         | 
| 30 | 
            -
                    _run_
         | 
| 31 | 
            -
                  end
         | 
| 32 | 
            -
                end
         | 
| 33 | 
            -
                module Collector
         | 
| 34 | 
            -
                  alias_method(:_add_suite_,:add_suite)
         | 
| 35 | 
            -
                  def add_suite(destination, suite)
         | 
| 36 | 
            -
                    _add_suite_(destination, suite)
         | 
| 37 | 
            -
                    if $random_iterations
         | 
| 38 | 
            -
                      (class << suite.tests;self;end).class_eval {
         | 
| 39 | 
            -
                        def each
         | 
| 40 | 
            -
                          n = size
         | 
| 41 | 
            -
                          ($random_iterations*n).to_i.times {
         | 
| 42 | 
            -
                            yield(slice(rand(n)))
         | 
| 43 | 
            -
                          }
         | 
| 44 | 
            -
                        end
         | 
| 45 | 
            -
                      }
         | 
| 46 | 
            -
                    end
         | 
| 47 | 
            -
                    destination
         | 
| 48 | 
            -
                  end
         | 
| 49 | 
            -
                end
         | 
| 50 | 
            -
                class TestSuite
         | 
| 51 | 
            -
                  def run(result, &progress_block)
         | 
| 52 | 
            -
                    yield(STARTED, name)
         | 
| 53 | 
            -
                    catch(:stop_suite) {
         | 
| 54 | 
            -
                      @tests.each { |test|
         | 
| 55 | 
            -
                        catch(:invalid_test) {
         | 
| 56 | 
            -
                          test.run(result, &progress_block)
         | 
| 57 | 
            -
                        }
         | 
| 58 | 
            -
                      }
         | 
| 59 | 
            -
                    }
         | 
| 60 | 
            -
                    yield(FINISHED, name)
         | 
| 61 | 
            -
                  end
         | 
| 62 | 
            -
                end
         | 
| 63 | 
            -
                class RandomTestCase < TestCase
         | 
| 64 | 
            -
                  def self.suite
         | 
| 65 | 
            -
                    suite = super
         | 
| 66 | 
            -
                    puts("random_seed: #{$random_seed}") if !suite.tests.empty? and $output_level>=UI::NORMAL
         | 
| 67 | 
            -
                    suite
         | 
| 68 | 
            -
                  end
         | 
| 69 | 
            -
                  undef_method(:default_test) # so that RandomTestCase is empty
         | 
| 70 | 
            -
                  def teardown
         | 
| 71 | 
            -
                    if not passed?
         | 
| 72 | 
            -
                      puts("\nrandom_seed: #{$random_seed}")
         | 
| 73 | 
            -
                      throw(:stop_suite)
         | 
| 74 | 
            -
                    end
         | 
| 75 | 
            -
                  end
         | 
| 76 | 
            -
                end
         | 
| 77 | 
            -
              end
         | 
| 78 | 
            -
            end
         | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
            class Grammar
         | 
| 82 | 
            -
             | 
| 83 | 
            -
            class Test < ::Test::Unit::RandomTestCase
         | 
| 84 | 
            -
             | 
| 85 | 
            -
            class Grammars
         | 
| 86 | 
            -
                include ::Test::Unit::Assertions
         | 
| 87 | 
            -
                def initialize
         | 
| 88 | 
            -
                    @grammar = []
         | 
| 89 | 
            -
                    @match = []
         | 
| 90 | 
            -
                    @parsed = []
         | 
| 91 | 
            -
                    @mismatch = []
         | 
| 92 | 
            -
                    @partial_match = []
         | 
| 93 | 
            -
                end
         | 
| 94 | 
            -
                def get
         | 
| 95 | 
            -
                    i = rand(@grammar.size.nonzero? || throw(:invalid_test))
         | 
| 96 | 
            -
                    return @grammar[i],@match[i],@parsed[i],@mismatch[i],@partial_match[i]
         | 
| 97 | 
            -
                end
         | 
| 98 | 
            -
                def add(grammar,match,parsed,mismatch=nil,partial_match=nil)
         | 
| 99 | 
            -
                    puts("#{grammar.inspect} #{match.inspect} #{parsed.inspect} #{mismatch.inspect} #{partial_match.inspect}") if
         | 
| 100 | 
            -
                        $output_level>=::Test::Unit::UI::VERBOSE
         | 
| 101 | 
            -
                    match.size.times { |i|
         | 
| 102 | 
            -
                        assert_equal(parsed[i],grammar.scan(match[i].to_cursor))
         | 
| 103 | 
            -
                    }
         | 
| 104 | 
            -
                    if mismatch
         | 
| 105 | 
            -
                        assert_raise(Grammar::Error){p grammar.scan(mismatch.to_cursor)}
         | 
| 106 | 
            -
                        assert_equal(false,grammar.scan(mismatch.to_cursor,[],true))
         | 
| 107 | 
            -
                    end
         | 
| 108 | 
            -
                    if partial_match
         | 
| 109 | 
            -
                        assert_raise(Grammar::Error){grammar.scan(partial_match.to_cursor)}
         | 
| 110 | 
            -
                    end
         | 
| 111 | 
            -
                    @grammar << grammar
         | 
| 112 | 
            -
                    @match << match
         | 
| 113 | 
            -
                    @parsed << parsed
         | 
| 114 | 
            -
                    @mismatch << mismatch
         | 
| 115 | 
            -
                    @partial_match << partial_match
         | 
| 116 | 
            -
                    nil
         | 
| 117 | 
            -
                end
         | 
| 118 | 
            -
            end
         | 
| 119 | 
            -
             | 
| 120 | 
            -
            def self.suite
         | 
| 121 | 
            -
                suite = super
         | 
| 122 | 
            -
                self.plant
         | 
| 123 | 
            -
                suite
         | 
| 124 | 
            -
            end
         | 
| 125 | 
            -
             | 
| 126 | 
            -
            def self.plant
         | 
| 127 | 
            -
                @@grammars = Grammars.new
         | 
| 128 | 
            -
            end
         | 
| 129 | 
            -
             | 
| 130 | 
            -
            def test_Sequence
         | 
| 131 | 
            -
                partial = rand(2)==1
         | 
| 132 | 
            -
                value = ["a","bc"][rand(2)]
         | 
| 133 | 
            -
                match = value.dup
         | 
| 134 | 
            -
                if partial
         | 
| 135 | 
            -
                    match = [match,match[0,1+rand(value.size)]]
         | 
| 136 | 
            -
                else
         | 
| 137 | 
            -
                    match = [match]
         | 
| 138 | 
            -
                end
         | 
| 139 | 
            -
                parsed = match.map{|s|s.unpack("C*")}
         | 
| 140 | 
            -
                @@grammars.add(Grammar::Sequence.new(value,partial),match,parsed,"")
         | 
| 141 | 
            -
            end
         | 
| 142 | 
            -
             | 
| 143 | 
            -
            def test_Element
         | 
| 144 | 
            -
                value = [?a,?b][rand(2)]
         | 
| 145 | 
            -
                match = ["" << value]
         | 
| 146 | 
            -
                parsed = [[value]]
         | 
| 147 | 
            -
                @@grammars.add(Grammar::Element.new(value),match,parsed,"")
         | 
| 148 | 
            -
            end
         | 
| 149 | 
            -
             | 
| 150 | 
            -
            def test_NULL
         | 
| 151 | 
            -
                @@grammars.add(Grammar::NULL,[""],[[]])
         | 
| 152 | 
            -
            end
         | 
| 153 | 
            -
             | 
| 154 | 
            -
            def test_or
         | 
| 155 | 
            -
                grammar1,match1,parsed1,mismatch1,partial1 = @@grammars.get
         | 
| 156 | 
            -
                grammar2,match2,parsed2,mismatch2,partial2 = @@grammars.get
         | 
| 157 | 
            -
                i = rand(match1.size)
         | 
| 158 | 
            -
                match1 = match1[i]
         | 
| 159 | 
            -
                parsed1 = parsed1[i]
         | 
| 160 | 
            -
                i = rand(match2.size)
         | 
| 161 | 
            -
                match2 = match2[i]
         | 
| 162 | 
            -
                parsed2 = parsed2[i]
         | 
| 163 | 
            -
                begin
         | 
| 164 | 
            -
                    # match2 shouldn't match grammar1
         | 
| 165 | 
            -
                    grammar1.scan(match2.to_cursor,[],true) and throw(:invalid_test)
         | 
| 166 | 
            -
                rescue Grammar::Error
         | 
| 167 | 
            -
                    throw(:invalid_test) # partial match
         | 
| 168 | 
            -
                end
         | 
| 169 | 
            -
                @@grammars.add(
         | 
| 170 | 
            -
                    grammar1|grammar2,
         | 
| 171 | 
            -
                    [match1,match2],
         | 
| 172 | 
            -
                    [parsed1,parsed2],
         | 
| 173 | 
            -
                    mismatch1==mismatch2 ? mismatch1 : nil,
         | 
| 174 | 
            -
                    partial1==partial2 ? partial1 : nil
         | 
| 175 | 
            -
                )
         | 
| 176 | 
            -
            end
         | 
| 177 | 
            -
             | 
| 178 | 
            -
            def test_plus
         | 
| 179 | 
            -
                grammar1,match1,parsed1,mismatch1,partial1 = @@grammars.get
         | 
| 180 | 
            -
                grammar2,match2,parsed2,mismatch2,partial2 = @@grammars.get
         | 
| 181 | 
            -
                i = rand(match1.size)
         | 
| 182 | 
            -
                match1 = match1[i]
         | 
| 183 | 
            -
                parsed1 = parsed1[i]
         | 
| 184 | 
            -
                i = rand(match2.size)
         | 
| 185 | 
            -
                match2 = match2[i]
         | 
| 186 | 
            -
                parsed2 = parsed2[i]
         | 
| 187 | 
            -
                # grammar1 shouldn't eat into match2
         | 
| 188 | 
            -
                begin
         | 
| 189 | 
            -
                    grammar1.scan((match1+match2).to_cursor)==parsed1 or
         | 
| 190 | 
            -
                    throw(:invalid_test)
         | 
| 191 | 
            -
                rescue Grammar::Error
         | 
| 192 | 
            -
                    throw(:invalid_test)
         | 
| 193 | 
            -
                end
         | 
| 194 | 
            -
                @@grammars.add(
         | 
| 195 | 
            -
                    grammar1+grammar2,
         | 
| 196 | 
            -
                    [match1+match2],
         | 
| 197 | 
            -
                    [parsed1+parsed2],
         | 
| 198 | 
            -
                    mismatch1,
         | 
| 199 | 
            -
                    partial1 || (mismatch2 && match1+mismatch2)
         | 
| 200 | 
            -
                )
         | 
| 201 | 
            -
            end
         | 
| 202 | 
            -
             | 
| 203 | 
            -
            def test_Grammar
         | 
| 204 | 
            -
                grammar1,match1,parsed1,mismatch1,partial1 = @@grammars.get
         | 
| 205 | 
            -
                grammar2,match2,parsed2,mismatch2,partial2 = @@grammars.get
         | 
| 206 | 
            -
                i = rand(match1.size)
         | 
| 207 | 
            -
                match1 = match1[i]
         | 
| 208 | 
            -
                parsed1 = parsed1[i]
         | 
| 209 | 
            -
                i = rand(match2.size)
         | 
| 210 | 
            -
                match2 = match2[i]
         | 
| 211 | 
            -
                parsed2 = parsed2[i]
         | 
| 212 | 
            -
                !match1.empty? or throw(:invalid_test)
         | 
| 213 | 
            -
                begin
         | 
| 214 | 
            -
                    (grammar1.scan((match1+match1).to_cursor)==parsed1 and
         | 
| 215 | 
            -
                    grammar1.scan((match1+match2).to_cursor)==parsed1 and
         | 
| 216 | 
            -
                    grammar2.scan((match2+match2).to_cursor)==parsed2) or
         | 
| 217 | 
            -
                    throw(:invalid_test)
         | 
| 218 | 
            -
                    grammar1.scan(match2.to_cursor,[],true) and throw(:invalid_test)
         | 
| 219 | 
            -
                rescue Grammar::Error
         | 
| 220 | 
            -
                    throw(:invalid_test)
         | 
| 221 | 
            -
                end
         | 
| 222 | 
            -
                grammar = Grammar.new
         | 
| 223 | 
            -
                grammar << grammar1+(grammar|Grammar::NULL)+grammar2
         | 
| 224 | 
            -
                @@grammars.add(
         | 
| 225 | 
            -
                    grammar,
         | 
| 226 | 
            -
                    [match1+match2,match1+match1+match1+match2+match2+match2],
         | 
| 227 | 
            -
                    [parsed1+parsed2,parsed1+parsed1+parsed1+parsed2+parsed2+parsed2],
         | 
| 228 | 
            -
                    mismatch1,
         | 
| 229 | 
            -
                    partial1
         | 
| 230 | 
            -
                )
         | 
| 231 | 
            -
            end
         | 
| 232 | 
            -
             | 
| 233 | 
            -
            def test_times
         | 
| 234 | 
            -
                grammar1,match1,parsed1,mismatch1,partial1 = @@grammars.get
         | 
| 235 | 
            -
                begin
         | 
| 236 | 
            -
                    grammar1.scan("".to_cursor,[],true) and throw(:invalid_test)
         | 
| 237 | 
            -
                rescue Grammar::Error
         | 
| 238 | 
            -
                    throw(:invalid_test)
         | 
| 239 | 
            -
                end
         | 
| 240 | 
            -
                min = rand(2)
         | 
| 241 | 
            -
                diff = rand(4)
         | 
| 242 | 
            -
                match = ""
         | 
| 243 | 
            -
                parsed = []
         | 
| 244 | 
            -
                match0 = nil
         | 
| 245 | 
            -
                parsed0 = nil
         | 
| 246 | 
            -
                (min+rand(diff+1)).times {
         | 
| 247 | 
            -
                    i = rand(match1.size)
         | 
| 248 | 
            -
                    match.concat(match1[i])
         | 
| 249 | 
            -
                    parsed.concat(parsed1[i])
         | 
| 250 | 
            -
                    if match0
         | 
| 251 | 
            -
                        begin
         | 
| 252 | 
            -
                            grammar1.scan((match0+match1[i]).to_cursor)==parsed0 or
         | 
| 253 | 
            -
                            throw(:invalid_test)
         | 
| 254 | 
            -
                        rescue Grammar::Error
         | 
| 255 | 
            -
                            throw(:invalid_test)
         | 
| 256 | 
            -
                        end
         | 
| 257 | 
            -
                    end
         | 
| 258 | 
            -
                    match0 = match1[i]
         | 
| 259 | 
            -
                    parsed0 = parsed1[i]
         | 
| 260 | 
            -
                }
         | 
| 261 | 
            -
                diff = 1.0/0.0 if diff==3
         | 
| 262 | 
            -
                multiplier = (diff.zero? && rand(2).zero?) ? min : (min..(min+diff))
         | 
| 263 | 
            -
                @@grammars.add(
         | 
| 264 | 
            -
                    grammar1*multiplier,
         | 
| 265 | 
            -
                    [match],
         | 
| 266 | 
            -
                    [parsed],
         | 
| 267 | 
            -
                    min.zero? ? nil : mismatch1,
         | 
| 268 | 
            -
                    min.zero? ? nil : (partial1 || (min>1 && match1[rand(match1.size)]))
         | 
| 269 | 
            -
                )
         | 
| 270 | 
            -
            end
         | 
| 271 | 
            -
             | 
| 272 | 
            -
            end
         | 
| 273 | 
            -
            end
         | 
| 274 | 
            -
             |