#! /usr/local/bin/ruby # rbison v0.0.7, generates a ruby parser class from a bison-like spec file. # Copyright (C) 2000 Jonathan Aseltine # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA require "getoptlong" RBISON_VERSION = '0.0.7' USAGE = <] Options: --debug (-d) Keep temporary files. --help (-h) This usage message. EOF $debug = false begin opts = GetoptLong.new(['--debug', '-d', GetoptLong::NO_ARGUMENT], ['--help', '-h', GetoptLong::NO_ARGUMENT]) opts.each do |opt, arg| case opt when '--debug' $debug = true when '--help' printf($stderr, USAGE); exit(0) end end rescue GetoptLong::InvalidOption printf($stderr, USAGE); exit(1) end ## preprocess so bison won't complain about rubyisms input = File.open(ARGV[0], 'r') ry = input.read() input.close ry.gsub!(%r!/\*!, "{ruby_fslash_asterisk}") ry.gsub!(%r!\*/!, "{ruby_bslash_asterisk}") ry.gsub!(%r!\@!, "{ruby_at}") ry.gsub!(%r!\#(?=\{)!, "{#ruby_pound}") ry.gsub!(%r!(\"[^\"\#]*)(\#+)(?=[^\"\n]*\")!, "\\1{\\2ruby_pound}") ry.gsub!(%r!(\'[^\'\#]*)(\#+)(?=[^\'\n]*\')!, "\\1{\\2ruby_pound}") ry.gsub!(%r!([^\{\#])\#+.*$!, "\\1") ry.gsub!(%r!^\#+.*$!, "") ry.gsub!(%r!\{(\#+)ruby_pound\}!, "\\1") ry.sub!(/^\%\{/, "%{\n/*") ry.sub!(/^\%\}/, "*/\n%}") ry.gsub!(/([^\_\$])\$/, "\\1{ruby_dollar}") ry.gsub!(/_\$/, "$") tmp = File.open("rbison.#{$$}.y", 'w') tmp.write(ry) tmp.close %x{bison rbison.#{$$}.y} File.delete("rbison.#{$$}.y") unless $debug tabc = File.open("rbison.#{$$}.tab.c", 'r') printf(< %d ? %d : YYTRANSLATE[yychar]", $1, $2) next end ## tables if :DEFAULT == s && li =~ /^static .*(yytranslate|yyr1|yyr2|yydefact|yydefgoto|yypact|yypgoto|yycheck|yytable|yyprhs|yyrhs|yyrline|yytname)[^\{]+\{\s*((?:(?:-?\d+|\"[^\"]*\"|NULL),?)+)/o printf(< YYLAST || YYCHECK[yyn] != yychar1 jump = :YYDEFAULT next end ## yyn is what to do for this token type in this state ## negative -> reduce, - yyn is the rule number ## positive -> shift, yyn is the new state ## New state is final state, don't bother to shift, just ## return success ## 0, or most negative number -> error yyn = YYTABLE[yyn] if yyn < 0 if yyn == YYFLAG jump = :YYERRLAB next end yyn = - yyn jump = :YYREDUCE next elsif yyn == 0 jump = :YYERRLAB next end if yyn == YYFINAL return ## accept end ## shift the lookahead token if @yydebug printf(@yyerror, "Shifting token %d (%s), ", yychar, YYTNAME[yychar1]) end ## discard the token being shifted unless it is eof if yychar != YYEOF yychar = YYEMPTY end yyvs.push(yylval) ## count tokens shifted since error; after, three turn off ## error status yyerrstatus -= 1 if yyerrstatus > 0 yystate = yyn jump = :YYNEWSTATE next when :YYDEFAULT yyn = YYDEFACT[yystate] if yyn == 0 jump = :YYERRLAB next else jump = :YYREDUCE next end ## do a reduction. yyn is the number of the rule to reduce with when :YYREDUCE yylen = YYR2[yyn] if yylen > 0 yyval = yyvs[yyvs.size - yylen] end if @yydebug printf(@yyerror, "Reducing via rule %d (line %d), ", yyn, YYRLINE[yyn]) i = YYPRHS[yyn] while YYRHS[i] > 0 printf(@yyerror, "%s ", YYTNAME[YYRHS[i]]) i += 1 end printf(@yyerror, " -> %s\\n", YYTNAME[YYR1[yyn]]) end case yyn EOF next end if :DEFAULT == s && li =~ /^\s*switch \(yyn\)/o s = :CASE next end if :CASE == s && li =~ /^case\s+(\d+):/o printf(< 0 yyss[(yyss.size - yylen) .. (yyss.size - 1)] = [] yyvs[(yyvs.size - yylen) .. (yyvs.size - 1)] = [] end yyvs.push(yyval) if @yydebug printf(@yyerror, "State stack now: %s\\n", yyss.join(' ')) printf(@yyerror, "Value stack now: %s\\n", yyvs.join(' ')) end ## "Shift" the result of the reduction. yyn = YYR1[yyn] yystate = YYPGOTO[yyn - YYNTBASE] + yyss[-1] if yystate >=0 && yystate <= YYLAST && YYCHECK[yystate] == yyss[-1] yystate = YYTABLE[yystate] else yystate = YYDEFGOTO[yyn - YYNTBASE] end jump = :YYNEWSTATE next when :YYERRLAB if yyerrstatus == 0 && @yydebug printf(@yyerror, "Parse error!\\n") end jump = :YYERRLAB1 next when :YYERRLAB1 if yyerrstatus == 3 if yychar == YYEOF raise ParseError, "parse error" end if @yydebug printf(@yyerror, "Discarding token %d (%s).\\n", yychar, YYTNAME[yychar1]) end yychar = YYEMPTY end yyerrstatus = 3 jump = :YYERRHANDLE next when :YYERRPOP if yyvs.empty? raise ParseError, "parse error" end ## don't pop if the state on top of the stack can handle ## the error token yystate = yyss[-1] if YYCHECK[YYPACT[yystate] + YYTERROR] != YYTERROR yyvs.pop yyss.pop if @yydebug printf(@yyerror, "Error: state stack now: %s\\n", yyss.join(' ')) printf(@yyerror, "Error: Value stack now: %s\\n", yyvs.join(' ')) end end jump = :YYERRHANDLE next when :YYERRHANDLE yyn = YYPACT[yystate] if yyn == YYFLAG jump = :YYERRPOP next end yyn += YYTERROR if yyn < 0 || yyn > YYLAST || YYCHECK[yyn] != YYTERROR jump = :YYERRPOP next end yyn = YYTABLE[yyn] if yyn < 0 if yyn == YYFLAG jump = :YYERRPOP next end yyn = -yyn jump = :YYREDUCE next elsif yyn == 0 jump = :YYERRPOP next end if yyn == YYFINAL return ## accept end if @yydebug printf(@yyerror, "Shifting error token, ") end yyvs.push(yylval) yystate = yyn jump = :YYNEWSTATE next end ## case end ## while true end ## yyparse end ## class EOF next end if :DEFAULT == s && li =~ /^yyerrhandle:/o s = :ADDITIONAL_CODE_1 next end if :ADDITIONAL_CODE_1 == s && li =~ /^\#line\s+\d+/o s = :ADDITIONAL_CODE_2 print "## Additional user code\n" next end if :ADDITIONAL_CODE_2 == s printf("%s", li) next end end tabc.close File.delete("rbison.#{$$}.tab.c") unless $debug