Last active
December 24, 2015 16:59
-
-
Save darkhelmet/6831945 to your computer and use it in GitHub Desktop.
weird kpeg behaviour
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% name = Heroes | |
%% { | |
attr_accessor :result | |
def self.run(text) | |
parser = new(text) | |
return parser.result if parser.parse | |
raise ParseError, parser.failure_info | |
end | |
} | |
%% ast-location = ::Heroes::AST | |
%% pair = ast Pair(left, right) | |
cr = "\r" | |
lf = "\n" | |
newline = cr? lf | |
not_newline = !newline . | |
space = " " | "\t" | |
sp = space+ | |
rest = < not_newline* > { text } | |
and = "and" | |
batman_and_robin = < "batman" sp and sp "robin" > ~pair("batman", "robin") | |
thor_and_loki = < "thor" sp and sp "loki" > ~pair("thor", "loki") | |
marvel = thor_and_loki | |
dc = !marvel batman_and_robin | |
root = dc:first? newline? marvel:second? { @result = ::Heroes::AST::Pair.new(first, second) } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'kpeg/compiled_parser' | |
class Heroes < KPeg::CompiledParser | |
attr_accessor :result | |
def self.run(text) | |
parser = new(text) | |
return parser.result if parser.parse | |
raise ParseError, parser.failure_info | |
end | |
# :stopdoc: | |
module ::Heroes::AST | |
class Node; end | |
class Pair < Node | |
def initialize(left, right) | |
@left = left | |
@right = right | |
end | |
attr_reader :left | |
attr_reader :right | |
end | |
end | |
module ::Heroes::ASTConstruction | |
def pair(left, right) | |
::Heroes::AST::Pair.new(left, right) | |
end | |
end | |
include ::Heroes::ASTConstruction | |
# cr = "\r" | |
def _cr | |
_tmp = match_string("\r") | |
set_failed_rule :_cr unless _tmp | |
return _tmp | |
end | |
# lf = "\n" | |
def _lf | |
_tmp = match_string("\n") | |
set_failed_rule :_lf unless _tmp | |
return _tmp | |
end | |
# newline = cr? lf | |
def _newline | |
_save = self.pos | |
while true # sequence | |
_save1 = self.pos | |
_tmp = apply(:_cr) | |
unless _tmp | |
_tmp = true | |
self.pos = _save1 | |
end | |
unless _tmp | |
self.pos = _save | |
break | |
end | |
_tmp = apply(:_lf) | |
unless _tmp | |
self.pos = _save | |
end | |
break | |
end # end sequence | |
set_failed_rule :_newline unless _tmp | |
return _tmp | |
end | |
# not_newline = !newline . | |
def _not_newline | |
_save = self.pos | |
while true # sequence | |
_save1 = self.pos | |
_tmp = apply(:_newline) | |
_tmp = _tmp ? nil : true | |
self.pos = _save1 | |
unless _tmp | |
self.pos = _save | |
break | |
end | |
_tmp = get_byte | |
unless _tmp | |
self.pos = _save | |
end | |
break | |
end # end sequence | |
set_failed_rule :_not_newline unless _tmp | |
return _tmp | |
end | |
# space = (" " | "\t") | |
def _space | |
_save = self.pos | |
while true # choice | |
_tmp = match_string(" ") | |
break if _tmp | |
self.pos = _save | |
_tmp = match_string("\t") | |
break if _tmp | |
self.pos = _save | |
break | |
end # end choice | |
set_failed_rule :_space unless _tmp | |
return _tmp | |
end | |
# sp = space+ | |
def _sp | |
_save = self.pos | |
_tmp = apply(:_space) | |
if _tmp | |
while true | |
_tmp = apply(:_space) | |
break unless _tmp | |
end | |
_tmp = true | |
else | |
self.pos = _save | |
end | |
set_failed_rule :_sp unless _tmp | |
return _tmp | |
end | |
# rest = < not_newline* > { text } | |
def _rest | |
_save = self.pos | |
while true # sequence | |
_text_start = self.pos | |
while true | |
_tmp = apply(:_not_newline) | |
break unless _tmp | |
end | |
_tmp = true | |
if _tmp | |
text = get_text(_text_start) | |
end | |
unless _tmp | |
self.pos = _save | |
break | |
end | |
@result = begin; text ; end | |
_tmp = true | |
unless _tmp | |
self.pos = _save | |
end | |
break | |
end # end sequence | |
set_failed_rule :_rest unless _tmp | |
return _tmp | |
end | |
# and = "and" | |
def _and | |
_tmp = match_string("and") | |
set_failed_rule :_and unless _tmp | |
return _tmp | |
end | |
# batman_and_robin = < "batman" sp and sp "robin" > {pair("batman", "robin")} | |
def _batman_and_robin | |
_save = self.pos | |
while true # sequence | |
_text_start = self.pos | |
_save1 = self.pos | |
while true # sequence | |
_tmp = match_string("batman") | |
unless _tmp | |
self.pos = _save1 | |
break | |
end | |
_tmp = apply(:_sp) | |
unless _tmp | |
self.pos = _save1 | |
break | |
end | |
_tmp = apply(:_and) | |
unless _tmp | |
self.pos = _save1 | |
break | |
end | |
_tmp = apply(:_sp) | |
unless _tmp | |
self.pos = _save1 | |
break | |
end | |
_tmp = match_string("robin") | |
unless _tmp | |
self.pos = _save1 | |
end | |
break | |
end # end sequence | |
if _tmp | |
text = get_text(_text_start) | |
end | |
unless _tmp | |
self.pos = _save | |
break | |
end | |
@result = begin; pair("batman", "robin"); end | |
_tmp = true | |
unless _tmp | |
self.pos = _save | |
end | |
break | |
end # end sequence | |
set_failed_rule :_batman_and_robin unless _tmp | |
return _tmp | |
end | |
# thor_and_loki = < "thor" sp and sp "loki" > {pair("thor", "loki")} | |
def _thor_and_loki | |
_save = self.pos | |
while true # sequence | |
_text_start = self.pos | |
_save1 = self.pos | |
while true # sequence | |
_tmp = match_string("thor") | |
unless _tmp | |
self.pos = _save1 | |
break | |
end | |
_tmp = apply(:_sp) | |
unless _tmp | |
self.pos = _save1 | |
break | |
end | |
_tmp = apply(:_and) | |
unless _tmp | |
self.pos = _save1 | |
break | |
end | |
_tmp = apply(:_sp) | |
unless _tmp | |
self.pos = _save1 | |
break | |
end | |
_tmp = match_string("loki") | |
unless _tmp | |
self.pos = _save1 | |
end | |
break | |
end # end sequence | |
if _tmp | |
text = get_text(_text_start) | |
end | |
unless _tmp | |
self.pos = _save | |
break | |
end | |
@result = begin; pair("thor", "loki"); end | |
_tmp = true | |
unless _tmp | |
self.pos = _save | |
end | |
break | |
end # end sequence | |
set_failed_rule :_thor_and_loki unless _tmp | |
return _tmp | |
end | |
# marvel = thor_and_loki | |
def _marvel | |
_tmp = apply(:_thor_and_loki) | |
set_failed_rule :_marvel unless _tmp | |
return _tmp | |
end | |
# dc = !marvel batman_and_robin | |
def _dc | |
_save = self.pos | |
while true # sequence | |
_save1 = self.pos | |
_tmp = apply(:_marvel) | |
_tmp = _tmp ? nil : true | |
self.pos = _save1 | |
unless _tmp | |
self.pos = _save | |
break | |
end | |
_tmp = apply(:_batman_and_robin) | |
unless _tmp | |
self.pos = _save | |
end | |
break | |
end # end sequence | |
set_failed_rule :_dc unless _tmp | |
return _tmp | |
end | |
# root = dc:first? newline? marvel:second? { @result = ::Heroes::AST::Pair.new(first, second) } | |
def _root | |
_save = self.pos | |
while true # sequence | |
_save1 = self.pos | |
_tmp = apply(:_dc) | |
first = @result | |
unless _tmp | |
_tmp = true | |
self.pos = _save1 | |
end | |
unless _tmp | |
self.pos = _save | |
break | |
end | |
_save2 = self.pos | |
_tmp = apply(:_newline) | |
unless _tmp | |
_tmp = true | |
self.pos = _save2 | |
end | |
unless _tmp | |
self.pos = _save | |
break | |
end | |
_save3 = self.pos | |
_tmp = apply(:_marvel) | |
second = @result | |
unless _tmp | |
_tmp = true | |
self.pos = _save3 | |
end | |
unless _tmp | |
self.pos = _save | |
break | |
end | |
@result = begin; @result = ::Heroes::AST::Pair.new(first, second) ; end | |
_tmp = true | |
unless _tmp | |
self.pos = _save | |
end | |
break | |
end # end sequence | |
set_failed_rule :_root unless _tmp | |
return _tmp | |
end | |
Rules = {} | |
Rules[:_cr] = rule_info("cr", "\"\\r\"") | |
Rules[:_lf] = rule_info("lf", "\"\\n\"") | |
Rules[:_newline] = rule_info("newline", "cr? lf") | |
Rules[:_not_newline] = rule_info("not_newline", "!newline .") | |
Rules[:_space] = rule_info("space", "(\" \" | \"\\t\")") | |
Rules[:_sp] = rule_info("sp", "space+") | |
Rules[:_rest] = rule_info("rest", "< not_newline* > { text }") | |
Rules[:_and] = rule_info("and", "\"and\"") | |
Rules[:_batman_and_robin] = rule_info("batman_and_robin", "< \"batman\" sp and sp \"robin\" > {pair(\"batman\", \"robin\")}") | |
Rules[:_thor_and_loki] = rule_info("thor_and_loki", "< \"thor\" sp and sp \"loki\" > {pair(\"thor\", \"loki\")}") | |
Rules[:_marvel] = rule_info("marvel", "thor_and_loki") | |
Rules[:_dc] = rule_info("dc", "!marvel batman_and_robin") | |
Rules[:_root] = rule_info("root", "dc:first? newline? marvel:second? { @result = ::Heroes::AST::Pair.new(first, second) }") | |
# :startdoc: | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>> load 'heroes.rb' | |
true | |
>> Heroes.run('batman and robin') | |
#<Heroes::AST::Pair:0x00000110b80978 @left=#<Heroes::AST::Pair:0x00000110b80bd0 @left="batman", @right="robin">, @right=#<Heroes::AST::Pair:0x00000110b80bd0 @left="batman", @right="robin">> | |
>> Heroes.run("batman and robin\nthor and loki") | |
#<Heroes::AST::Pair:0x00000110ba9d00 @left=#<Heroes::AST::Pair:0x00000110baa3b8 @left="batman", @right="robin">, @right=#<Heroes::AST::Pair:0x00000110ba9d28 @left="thor", @right="loki">> | |
>> Heroes.run("thor and loki") | |
#<Heroes::AST::Pair:0x00000110ba54a8 @left=#<Heroes::AST::Pair:0x00000110ba5660 @left="thor", @right="loki">, @right=#<Heroes::AST::Pair:0x00000110ba5660 @left="thor", @right="loki">> | |
>> |
I would expect either the left
or right
to be nil
in the event that either marvel
or dc
were not parsed, but it grabs it anyway.
If I remove !marvel
from the grammar, I get nil
/Pair
for "thor and loki"
but parsing only "batman and robin"
is still wrong.
You want root = dc?:first newline? marvel?:second { @result = ::Heroes::AST::Pair.new(first, second) }
(note the change in ? position) It's a bug that it accepts dc:first?
actually.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Only parsing both lines does what I would think would be the correct thing. Both runs parsing single lines result in
left
andright
holding the same object, which is the ast pair object.