Skip to content

Instantly share code, notes, and snippets.

@darkhelmet
Last active December 24, 2015 16:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save darkhelmet/6831945 to your computer and use it in GitHub Desktop.
Save darkhelmet/6831945 to your computer and use it in GitHub Desktop.
weird kpeg behaviour
%% 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) }
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
>> 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">>
>>
@darkhelmet
Copy link
Author

Only parsing both lines does what I would think would be the correct thing. Both runs parsing single lines result in left and right holding the same object, which is the ast pair object.

@darkhelmet
Copy link
Author

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.

@darkhelmet
Copy link
Author

If I remove !marvel from the grammar, I get nil/Pair for "thor and loki" but parsing only "batman and robin" is still wrong.

@evanphx
Copy link

evanphx commented Oct 4, 2013

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