Skip to content

Instantly share code, notes, and snippets.

@vic
Created August 1, 2011 08:04
Show Gist options
  • Select an option

  • Save vic/1117774 to your computer and use it in GitHub Desktop.

Select an option

Save vic/1117774 to your computer and use it in GitHub Desktop.
Parsing the Fancy Language with KPeg.
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift File.expand_path('../../kpeg/lib', __FILE__)
require 'kpeg'
class Fancy
G = KPeg::Grammar.new
KPeg.compile DATA.read, "Parser", self
def Parser.sexp(node)
if Parser::Node === node
sexp = []
sexp << node.name if node.name
sexp.push *node.args.map { |a| sexp(a) }
sexp
else
node
end
end
def Parser.parse(str)
parser = Parser.new(str)
if parser.parse
require 'pp'
PP.pp(sexp(parser.result))
else
parser.show_error
end
end
if ARGV.empty?
print self,": "
line = nil
Parser.parse(line.chomp) or print(self, ": ") while line = gets
else
ARGV.map do |a|
if File.exists?(a) && a.end_with?(".rb")
require File.expand_path(a)
else
print "<< ", a, "\n"
Parser.parse(File.exists?(a) && File.read(a) || a)
end
end
end
end
__END__
root = - body:b - {b}
space = " " | "\t"
nl = "\n" | "\r\n" | ";"
comment = "#" /.*?$/
- = (comment | space | nl)*
p = &. ~position
body = body:b - chain:c { b.args.push c; b }
| chain:c ~node(c.pos, :body, c)
chain = chain:c space+ "." ~node(p, :chain, c)
| chain:c space+ message:m { c.args.push m; c }
| p:p value:v ~node(p, :chain, v)
args = args:a space* "," - body:c { a + [c] }
| body:c {[c]}
message = p:p oper:o - value:v ~node(p, :message, o, v)
| p:p "[" - args?:a - "]" ~node(p, "[]", *Array(a))
| collon:c {c}
| p:p identifier:i ~node(p, :message, i)
collon = collon:c space+ identifier:i ":" space+ - value:v {c.args.push i, v; c}
| p:p identifier:i ":" space+ - value:v ~node(p, :message, i, v)
block = p:p "|" block_params:a "|" - curly:b ~node(p, :block, a, b)
| curly:b ~node(p, :block, [], b)
curly = p:p "{" - body:b - "}" {b}
block_params = block_params:b space+ ident:i {b + [i]}
| ident:i {[i]}
value = "(" - body:v - ")" {v}
| "$" space+ chain:c {c}
| classdef | singdef | methdef | match | trycatch
| literal | block | message | special | constant | identifier
trycatch = p:p "try" space+ curly:t space+ (catches:c space+)? finally:f
~node(p, :try, *[t,c,f].flatten.compact)
| p:p "try" space+ curly:t space+ catches:c
~node(p, :try, *[t,c].flatten)
finally = p:p "finally" space+ curly:f ~node(p, :finally, f)
catche = p:p "catch" space+ value:v space+ "=>" space+ ident:i space+ curly:b
~node(p, :catch, b, v, i)
| p:p "catch" space+ value:v space+ curly:b
~node(p, :catch, b, v)
| p:p "catch" space+ curly:b
~node(p, :catch, b)
catches = catches:s space+ catche:c {s+[c]}
| catche:c {[c]}
match = p:p "match" space+ value:v space+ "{" - match_cases:c - "}"
~node(p, :match, v, *c.flatten)
match_cases = match_cases:s - match_case:c {s + [c]}
| match_case:c {[c]}
match_case = "case" space+ "_" space+ "->" - body:b {[nil, b]}
| "case" space+ value:v space+ "->" - body:b {[v, b]}
classdef = p:p "class" space+ constant:c space+ (":" constant:s space+)? curly:b
~node(p, :class, c, s, b)
methdef = p:p "def" space+ params:m space+ curly:b ~node(p, :method, m, b)
singdef = p:p "def" space+ value:v space+ params:m space+ curly:b
~node(p, :singleton, v, m, b)
params = p:p "[]" space+ param:a ~node(p, "[]", a)
| selectors:s {s}
| p:p ident:i ~node(p, i)
param = p:p ident:i space+ "(" body:b ")" ~node(p, :param, i, b)
| p:p ident:i ~node(p, :param, i)
selectors = selectors:s space+ ident:i ":" space+ param:a {s.args.push i, a; s}
| p:p ident:i ":" space+ param:a ~node(p, i, a)
const = </[A-Z][a-zA-Z0-9_]*/> {text}
ident = !&("case"|"class"|"def"|"match"|"try")
<"@"? "@"? /[a-z_][a-zA-Z0-9_]*[\?\!]?/> {text}
oper = </[?!=*\/^><%&~+-]+/ | "||" /[?!=*\/^><%&~+_-]*/ > {text}
special = p:p <"__FILE__" | "__LINE__"> ~node(p, :special, text)
constant = constant:c space+ const:o { c.args.push o; c }
| p:p const:c ~node(p, :const, c)
identifier = p:p ident:i ~node(p, :ident, i)
literal = float | fixnum | str | symbol
float = p:p dec:n "." dec:f ~node(p, :float, (n+"."+f).to_f)
fixnum = p:p (hexadec | binary | octal | decimal):n ~node(p, :fixnum, n)
digits(d) = < d+ ("_" d+)* > { text.gsub('_', '') }
dec = digits(&/[0-9]/):d {d}
oct = "0" /[oO]/? digits(&/[0-7]/):d {d}
hex = "0" /[xX]/ digits(&/[0-9a-fA-F]/):d {d}
bin = "0" /[bB]/ digits(&/[0-1]/):d {d}
hexadec = hex:d {d.to_i(16)}
binary = bin:d {d.to_i(2)}
octal = oct:d {d.to_i(8)}
decimal = dec:d {d.to_i(10)}
symbol = p:p "'" (str | sym):i ~node(p, :symbol, i)
sym = <(ident | oper | const | ":" | "[]")+> {text}
str = (mstr | sstr)
sstr = p:p "\"" sstr_inner*:b "\"" ~node(p, :text, b)
sstr_inner = p:p "#" left_brace:l - body?:b - right_brace(l) ~node(p, l.join, b)
| < ("\\\"" | &!("\"" | "#" left_brace) .)+ > {text}
mstr = p:p "\"\"\"" mstr_inner*:b "\"\"\"" ~node(p, :text, b)
-
mstr_inner = p:p "#" left_brace:l - body?:b - right_brace(l) ~node(p, l.join, b)
| < ("\\\"\"\"" | !&("\"\"\"" | "#" left_brace) . | . &("\"\"\""))+ > {text}
brace = < . > &{ brace(text) } { brace(text) }
left_brace = <brace:b> &{ text == b.first} { b }
right_brace(l) = <brace:b> &{ text == l.last } { l }
%% {
class Position
attr_reader :line, :column
def initialize(line, column)
@line, @column = line, column
end
end
class Node
attr_reader :pos, :name, :args
def initialize(pos, name, *args)
@pos, @name, @args = pos, name, args
end
end
def node(pos, name, *args)
Node.new(pos, name, *args)
end
def position(line = current_line, column = current_column)
Position.new(line, column)
end
BRACES_ALIST = [
['(', ')'],
['{', '}'],
['[', ']'],
]
def braces
@braces ||= BRACES_ALIST.dup
end
def brace(text)
braces.assoc(text) || braces.rassoc(text)
end
}
@bakkdoor
Copy link
Copy Markdown

bakkdoor commented Aug 1, 2011

Does this fully work? I'd like to switch to kpeg, so that would be awesome. There's also an incomplete kpeg parser for Fancy here (@manveru wrote it - afaik it's not done yet): https://github.com/bakkdoor/fancy/tree/kpeg

@vic
Copy link
Copy Markdown
Author

vic commented Aug 1, 2011 via email

@bakkdoor
Copy link
Copy Markdown

bakkdoor commented Aug 1, 2011

Awesome :D Can't wait to get rid of the bison parser :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment