parslet icon indicating copy to clipboard operation
parslet copied to clipboard

If you use scope, context cache will hit incorrectly.

Open nak1114 opened this issue 5 years ago • 0 comments

If you use scope, context cache will hit incorrectly. Bug reproduction example:

require 'parslet' 

class Mini < Parslet::Parser
  NewLine="\n"
  root(:root)
  rule(:root) { init >> (exp >> nl).repeat }
  rule(:init) { dynamic{|s,c|c.captures[:indent]=NewLine;str("")} }
  rule(:exp){ (int | block).as(:exp) }

  rule(:space) { match('[ \t\f\v]')}

  rule(:nl){dynamic{|s,c|str(c.captures[:indent])}}

  rule(:int) { match('[0-9]').repeat(1) }

  rule(:block) { str('block').as(:block) >> scope{ indent >> exp >> (nl >>exp).repeat >> dedent }  } 
  rule(:dedent){nl.absent?}
  rule(:indent){(nl >> space.repeat(1) ).capture(:indent)}
end

str=<<EOS
block
  123
  234
  block
    345
    345
  456
567
EOS
p Mini.new.parse(str)
__END__

Correction example:

require 'parslet' 

module Parslet::Atoms
  class Context
    def scope
      captures.push
      @cache = Hash.new { |h, k| h[k] = {} } #<= clear cache when context swithing
      yield
    ensure
      captures.pop
      @cache = Hash.new { |h, k| h[k] = {} } #<= clear cache when context swithing
    end
  end
end

class Mini < Parslet::Parser
  NewLine="\n"
  root(:root)
  rule(:root) { init >> (exp >> nl).repeat }
  rule(:init) { dynamic{|s,c|c.captures[:indent]=NewLine;str("")} }
  rule(:exp){ (int | block).as(:exp) }

  rule(:space) { match('[ \t\f\v]')}

  rule(:nl){dynamic{|s,c|str(c.captures[:indent])}}

  rule(:int) { match('[0-9]').repeat(1) }

  rule(:block) { str('block').as(:block) >> scope{ indent >> exp >> (nl >>exp).repeat >> dedent }  } 
  rule(:dedent){nl.absent?}
  rule(:indent){(nl >> space.repeat(1) ).capture(:indent)}
end

str=<<EOS
block
  123
  234
  block
    345
    345
  456
567
EOS
p Mini.new.parse(str)
__END__

I think this is a bad fix. Uncache only objs that use captures.

nak1114 avatar Mar 21 '19 09:03 nak1114