Created
August 1, 2011 08:04
-
-
Save vic/1117774 to your computer and use it in GitHub Desktop.
Parsing the Fancy Language with KPeg.
This file contains hidden or 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_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 | |
| } |
Author
Almost, I'm adding tuple support right now. Yeah, I saw manveru's parser,
but I'm trying a complete parser rewrite, I'm almost done with it (only
missing regex and hash literals) am currently trying to compile
examples/hello_world.fy with it. I'll be pushing into a branch soon.
On Mon, Aug 1, 2011 at 4:23 AM, bakkdoor < ***@***.***>wrote:
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 ***@***.*** wrote it -
afaik it's not done yet): https://github.com/bakkdoor/fancy/tree/kpeg
##
Reply to this email directly or view it on GitHub:
https://gist.github.com/1117774
##
vic
Quaerendo invenietis.
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
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