parslet
parslet copied to clipboard
If you use scope, context cache will hit incorrectly.
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.