$: << "lib" require 'rpeg' # This is a bootstrapping RPeg grammar. c = RPeg.new c.define :start, [:sequence, [:nonterminal, :spacing], [:nonterminal, :grammar], [:eof], [:code, " @value = @rpeg "] ] c.define :grammar, [:sequence, [:code, " @rpeg = RPeg.new "], [:one_or_more, [:nonterminal, :definition]]] c.define :definition, [:sequence, [:capture, :id, [:nonterminal, :identifier]], [:nonterminal, :BACKARROW], [:capture, :rule, [:nonterminal, :rule]], [:nonterminal, :SEMI], [:code, " @rpeg.define @matches[:id], @matches[:rule] "]] c.define :rule, [:capture, :value, [:nonterminal, :altrule]] c.define :altrule, [:sequence, [:capture, :rule, [:nonterminal, :seqrule]], [:code, " @value = [:choice, @matches[:rule]] "], [:zero_or_more, [:sequence, [:nonterminal, :SLASH], [:capture, :rule2, [:nonterminal, :seqrule]], [:code, " @value << @matches[:rule2] "]]]] c.define :seqrule, [:sequence, [:code, " @value = [:sequence] "], [:one_or_more, [:choice, [:sequence, [:nonterminal, :AND], [:capture, :u1, [:nonterminal, :unaryrule]], [:code, " @value << [:assert, @matches[:u]] "]], [:sequence, [:nonterminal, :NOT], [:capture, :u2, [:nonterminal, :unaryrule]], [:code, " @value << [:assert_not, @matches[:u2]] "]], [:sequence, [:capture, :u3, [:nonterminal, :unaryrule]], [:code, " @value << @matches[:u3] "]]]]] c.define :unaryrule, [:choice, [:sequence, [:capture, :p1, [:nonterminal, :primrule]], [:nonterminal, :QUESTION], [:code, " @value = [:once, @matches[:p1]] "]], [:sequence, [:capture, :p2, [:nonterminal, :primrule]], [:nonterminal, :STAR], [:code, " @value = [:zero_or_more, @matches[:p2]] "]], [:sequence, [:capture, :p3, [:nonterminal, :primrule]], [:nonterminal, :PLUS], [:code, " @value = [:one_or_more, @matches[:p3]] "]], [:sequence, [:capture, :id, [:nonterminal, :identifier]], [:nonterminal, :COLON], [:capture, :p4, [:nonterminal, :primrule]], [:code, " @value = [:capture, @matches[:id], @matches[:p4]] "]], [:capture, :value, [:nonterminal, :primrule]]] c.define :primrule, [:capture, :value, [:choice, [:nonterminal, :nonterminal], [:nonterminal, :stringliteral], [:nonterminal, :charclass], [:nonterminal, :DOT], [:nonterminal, :EOF], [:nonterminal, :EPSILON], [:nonterminal, :codeblock], [:nonterminal, :parens]]] c.define :nonterminal, [:sequence, [:capture, :id, [:nonterminal, :identifier]], [:code, " @value = [:nonterminal, @matches[:id]] "]] c.define :parens, [:sequence, [:nonterminal, :OPEN], [:capture, :value, [:nonterminal, :rule]], [:nonterminal, :CLOSE]] c.define :identifier, [:sequence, [:capture, :value, [:sequence, [:nonterminal, :identstart], [:zero_or_more, [:nonterminal, :identcont]]]], [:nonterminal, :spacing]] c.define :identstart, [:charclass, "A-Za-z_"] c.define :identcont, [:charclass, "A-Za-z0-9_"] c.define :stringliteral, [:sequence, [:literal, '"'], [:capture, :string, [:zero_or_more, [:sequence, [:assert_not, [:literal, '"']], [:nonterminal, :quotedchar]]]], [:literal, '"'], [:nonterminal, :spacing], [:code, " @value = [:literal, RPeg.unescape(@matches[:string])] "]] c.define :charclass, [:sequence, [:literal, '['], [:capture, :class, [:zero_or_more, [:sequence, [:assert_not, [:literal, ']']], [:nonterminal, :quotedchar]]]], [:literal, ']'], [:nonterminal, :spacing], [:code, " @value = [:charclass, RPeg.unescape(@matches[:class])] "]] c.define :quotedchar, [:choice, [:literal, "\\n"], [:literal, "\\r"], [:literal, "\\t"], [:literal, "\\f"], [:literal, "\\\\"], [:literal, "\\'"], [:literal, "\\\""], [:sequence, [:assert_not, [:literal, "\\"]], [:dot]]] c.define :codeblock, [:sequence, [:literal, "{{"], [:capture, :code, [:zero_or_more, [:sequence, [:assert_not, [:literal, "}}"]], [:dot]]]], [:literal, "}}"], [:nonterminal, :spacing], [:code, " @value = [:code, @matches[:code]] "]] c.define :spacing, [:zero_or_more, [:choice, [:charclass, " \t\n\f"], [:nonterminal, :linecomment]]] c.define :linecomment, [:sequence, [:literal, "#"], [:zero_or_more, [:sequence, [:assert_not, [:charclass, "\r\n"]], [:dot]]], [:charclass, "\r\n"]] c.define :BACKARROW, [:sequence, [:literal, "<-"], [:nonterminal, :spacing]] c.define :SLASH, [:sequence, [:literal, "/"], [:nonterminal, :spacing]] c.define :OPEN, [:sequence, [:literal, "("], [:nonterminal, :spacing]] c.define :CLOSE, [:sequence, [:literal, ")"], [:nonterminal, :spacing]] c.define :AND, [:sequence, [:literal, "&"], [:nonterminal, :spacing]] c.define :NOT, [:sequence, [:literal, "!"], [:nonterminal, :spacing]] c.define :QUESTION, [:sequence, [:literal, "?"], [:nonterminal, :spacing]] c.define :STAR, [:sequence, [:literal, "*"], [:nonterminal, :spacing]] c.define :PLUS, [:sequence, [:literal, "+"], [:nonterminal, :spacing]] c.define :COLON, [:sequence, [:literal, ":"]] c.define :SEMI, [:sequence, [:literal, ";"], [:nonterminal, :spacing]] c.define :DOT, [:sequence, [:literal, "."], [:nonterminal, :spacing], [:code, " @value = [:dot] "]] c.define :EOF, [:sequence, [:literal, "%eof"], [:nonterminal, :spacing], [:code, " @value = [:eof] "]] c.define :EPSILON, [:sequence, [:literal, "%epsilon"], [:nonterminal, :spacing], [:code, " @value = [:epsilon] "]] puts "Generating..." # Generate the PEG grammar grammar = c.pretty_print # Generate the Ruby PEG parser rubycode = c.compile2ruby("RPeg::PEGParser < RPeg") # Generate the C PEG parser ccode = c.compile2c("rpegparser") # Self-test interpreter puts "Selftesting interpreter... (this can take a while)" p [:selfparse, (c.parse(grammar).kind_of?(RPeg) rescue false)] selfgrammar = c.value.pretty_print p [:selfgrammar, grammar == selfgrammar] c2 = c.value p [:selfselfparse, (c2.parse(selfgrammar).kind_of?(RPeg) rescue false)] p [:selfselfgrammar, selfgrammar == c2.value.pretty_print] # Self-test Ruby generator puts "Selftesting Ruby generator... (this can take a while)" eval rubycode rp = RPeg::PEGParser.new p [:rubyparse, (rp.parse(grammar).kind_of?(RPeg) rescue false)] p [:rubygrammar, grammar == rp.value.pretty_print] eval rp.value.compile2ruby("PEGParser2 < RPeg") rp2 = PEGParser2.new p [:rubyrubyparse, (rp2.parse(grammar).kind_of?(RPeg) rescue false)] p [:rubyrubygrammar, grammar == rp2.value.pretty_print] # Self-test C generator puts "Selftesting C generator... (development tools needed)" File.open("ext/rpeg_parser/rpegparser.c", "w") { |f| f << "/* rpegparser.c generated #{Time.now} */\n\n" f << ccode } Dir.chdir "ext/rpeg_parser" do system "ruby extconf.rb && make" end begin require 'ext/rpeg_parser/rpegparser' p [:cparse, (Rpegparser.parse(grammar).kind_of?(RPeg) rescue false)] p [:cgrammar, Rpegparser.parse(grammar).pretty_print == grammar] p [:ccgrammar, Rpegparser.parse(grammar).compile2c("rpegparser") == ccode] rescue LoadError puts "failed." end # Save puts "Dumping... " File.open("lib/rpeg.parser.peg", "w") { |f| f << "# rpeg.parser.peg generated #{Time.now}\n\n" f << grammar } File.open("lib/rpeg.parser.rb", "w") { |f| f << "# rpeg.parser.rb generated #{Time.now}\n\n" f << rubycode } puts "done."