racc-1.4.14/0000755000004100000410000000000012625454503012547 5ustar www-datawww-dataracc-1.4.14/Rakefile0000644000004100000410000000452312625454503014220 0ustar www-datawww-data# -*- ruby -*- require 'rubygems' require 'hoe' gem 'rake-compiler', '>= 0.4.1' Hoe.plugin :debugging, :doofus, :git, :isolate, :gemspec def java? /java/ === RUBY_PLATFORM end def jruby? Object.const_defined?(:RUBY_ENGINE) and 'jruby' == RUBY_ENGINE end HOE = Hoe.spec 'racc' do developer 'Aaron Patterson', 'aaron@tenderlovemaking.com' license "MIT" self.extra_rdoc_files = Dir['*.rdoc'] self.history_file = 'ChangeLog' self.readme_file = 'README.rdoc' dependency 'rake-compiler', '>= 0.4.1', :developer dependency 'minitest', '~> 4.7', :developer # stick to stdlib's version if java? self.spec_extras[:platform] = 'java' else self.spec_extras[:extensions] = %w[ext/racc/extconf.rb] end self.clean_globs << "lib/#{self.name}/*.{so,bundle,dll,jar}" # from hoe/compiler end def add_file_to_gem relative_path target_path = File.join gem_build_path, relative_path target_dir = File.dirname(target_path) mkdir_p target_dir unless File.directory?(target_dir) rm_f target_path safe_ln relative_path, target_path HOE.spec.files += [relative_path] end def gem_build_path File.join 'pkg', HOE.spec.full_name end file 'lib/racc/parser-text.rb' => ['lib/racc/parser.rb'] do |t| source = 'lib/racc/parser.rb' open(t.name, 'wb') { |io| io.write(<<-eorb) module Racc PARSER_TEXT = <<'__end_of_file__' #{File.read(source)} __end_of_file__ end eorb } end unless jruby? # MRI require "rake/extensiontask" Rake::ExtensionTask.new "cparse", HOE.spec do |ext| ext.lib_dir = File.join 'lib', 'racc' ext.ext_dir = File.join 'ext', 'racc' end task :compile => 'lib/racc/parser-text.rb' # else # JRUBY require "rake/javaextensiontask" Rake::JavaExtensionTask.new("cparse", HOE.spec) do |ext| jruby_home = RbConfig::CONFIG['prefix'] ext.lib_dir = File.join 'lib', 'racc' ext.ext_dir = File.join 'ext', 'racc' # source/target jvm ext.source_version = '1.6' ext.target_version = '1.6' jars = ["#{jruby_home}/lib/jruby.jar"] + FileList['lib/*.jar'] ext.classpath = jars.map { |x| File.expand_path x }.join( ':' ) ext.name = 'cparse-jruby' end task :compile => ['lib/racc/parser-text.rb'] task gem_build_path => [:compile] do add_file_to_gem 'lib/racc/cparse-jruby.jar' end end task :test => :compile Hoe.add_include_dirs('.:lib/racc') racc-1.4.14/bin/0000755000004100000410000000000012625454503013317 5ustar www-datawww-dataracc-1.4.14/bin/y2racc0000755000004100000410000001655212625454503014441 0ustar www-datawww-data#!/usr/local/bin/ruby # # $Id$ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public Lisence version 2.1. # For details of the GNU LGPL, see the file "COPYING". # require 'racc/info' require 'strscan' require 'forwardable' require 'optparse' def main @with_action = true @with_header = false @with_usercode = false cname = 'MyParser' input = nil output = nil parser = OptionParser.new parser.banner = "Usage: #{File.basename($0)} [-Ahu] [-c ] [-o ] " parser.on('-o', '--output=FILENAME', 'output file name [.racc]') {|name| output = name } parser.on('-c', '--classname=NAME', "Name of the parser class. [#{cname}]") {|name| cname = name } parser.on('-A', '--without-action', 'Does not include actions.') { @with_action = false } parser.on('-h', '--with-header', 'Includes header (%{...%}).') { @with_header = true } parser.on('-u', '--with-user-code', 'Includes user code.') { @with_usercode = true } parser.on('--version', 'Prints version and quit.') { puts "y2racc version #{Racc::Version}" exit 0 } parser.on('--copyright', 'Prints copyright and quit.') { puts Racc::Copyright exit 0 } parser.on('--help', 'Prints this message and quit.') { puts parser.help exit 1 } begin parser.parse! rescue OptionParser::ParseError => err $stderr.puts err.message $stderr.puts parser.help exit 1 end if ARGV.empty? $stderr.puts 'no input' exit 1 end if ARGV.size > 1 $stderr.puts 'too many input' exit 1 end input = ARGV[0] begin result = YaccFileParser.parse_file(input) File.open(output || "#{input}.racc", 'w') {|f| convert cname, result, f } rescue SystemCallError => err $stderr.puts err.message exit 1 end end def convert(classname, result, f) init_indent = 'token'.size f.puts %<# Converted from "#{result.filename}" by y2racc version #{Racc::Version}> f.puts f.puts "class #{classname}" unless result.terminals.empty? f.puts f.print 'token' columns = init_indent result.terminals.each do |t| if columns > 60 f.puts f.print ' ' * init_indent columns = init_indent end columns += f.write(" #{t}") end f.puts end unless result.precedence_table.empty? f.puts f.puts 'preclow' result.precedence_table.each do |assoc, toks| f.printf " %-8s %s\n", assoc, toks.join(' ') unless toks.empty? end f.puts 'prechigh' end if result.start f.puts f.puts "start #{@start}" end f.puts f.puts 'rule' texts = @with_action ? result.grammar : result.grammar_without_actions texts.each do |text| f.print text end if @with_header and result.header f.puts f.puts '---- header' f.puts result.header end if @with_usercode and result.usercode f.puts f.puts '---- footer' f.puts result.usercode end end class ParseError < StandardError; end class StringScanner_withlineno def initialize(src) @s = StringScanner.new(src) @lineno = 1 end extend Forwardable def_delegator "@s", :eos? def_delegator "@s", :rest attr_reader :lineno def scan(re) advance_lineno(@s.scan(re)) end def scan_until(re) advance_lineno(@s.scan_until(re)) end def skip(re) str = advance_lineno(@s.scan(re)) str ? str.size : nil end def getch advance_lineno(@s.getch) end private def advance_lineno(str) @lineno += str.count("\n") if str str end end class YaccFileParser Result = Struct.new(:terminals, :precedence_table, :start, :header, :grammar, :usercode, :filename) class Result # reopen def initialize super self.terminals = [] self.precedence_table = [] self.start = nil self.grammar = [] self.header = nil self.usercode = nil self.filename = nil end def grammar_without_actions grammar().map {|text| text[0,1] == '{' ? '{}' : text } end end def YaccFileParser.parse_file(filename) new().parse(File.read(filename), filename) end def parse(src, filename = '-') @result = Result.new @filename = filename @result.filename = filename s = StringScanner_withlineno.new(src) parse_header s parse_grammar s @result end private COMMENT = %r CHAR = /'((?:[^'\\]+|\\.)*)'/ STRING = /"((?:[^"\\]+|\\.)*)"/ def parse_header(s) skip_until_percent s until s.eos? case when t = s.scan(/left/) @result.precedence_table.push ['left', scan_symbols(s)] when t = s.scan(/right/) @result.precedence_table.push ['right', scan_symbols(s)] when t = s.scan(/nonassoc/) @result.precedence_table.push ['nonassoc', scan_symbols(s)] when t = s.scan(/token/) list = scan_symbols(s) list.shift if /\A<(.*)>\z/ =~ list[0] @result.terminals.concat list when t = s.scan(/start/) @result.start = scan_symbols(s)[0] when s.skip(%r<(?: type | union | expect | thong | binary | semantic_parser | pure_parser | no_lines | raw | token_table )\b>x) skip_until_percent s when s.skip(/\{/) # header (%{...%}) str = s.scan_until(/\%\}/) str.chop! str.chop! @result.header = str skip_until_percent s when s.skip(/\%/) # grammar (%%...) return else raise ParseError, "#{@filename}:#{s.lineno}: scan error" end end end def skip_until_percent(s) until s.eos? s.skip /[^\%\/]+/ next if s.skip(COMMENT) return if s.getch == '%' end end def scan_symbols(s) list = [] until s.eos? s.skip /\s+/ if s.skip(COMMENT) ; elsif t = s.scan(CHAR) list.push t elsif t = s.scan(STRING) list.push t elsif s.skip(/\%/) break elsif t = s.scan(/\S+/) list.push t else raise ParseError, "#{@filename}:#{@lineno}: scan error" end end list end def parse_grammar(s) buf = [] until s.eos? if t = s.scan(/[^%'"{\/]+/) buf.push t break if s.eos? end if s.skip(/\{/) buf.push scan_action(s) elsif t = s.scan(/'(?:[^'\\]+|\\.)*'/) then buf.push t elsif t = s.scan(/"(?:[^"\\]+|\\.)*"/) then buf.push t elsif t = s.scan(COMMENT) then buf.push t elsif s.skip(/%prec\b/) then buf.push '=' elsif s.skip(/%%/) @result.usercode = s.rest break else buf.push s.getch end end @result.grammar = buf end def scan_action(s) buf = '{' nest = 1 until s.eos? if t = s.scan(%r<[^/{}'"]+>) buf << t break if s.eos? elsif t = s.scan(COMMENT) buf << t elsif t = s.scan(CHAR) buf << t elsif t = s.scan(STRING) buf << t else c = s.getch buf << c case c when '{' nest += 1 when '}' nest -= 1 return buf if nest == 0 end end end $stderr.puts "warning: unterminated action in #{@filename}" buf end end unless Object.method_defined?(:funcall) class Object alias funcall __send__ end end main racc-1.4.14/bin/racc0000755000004100000410000002043612625454503014162 0ustar www-datawww-data#!/usr/bin/env ruby # # $Id$ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of LGPL, see the file "COPYING". # require 'racc/static' require 'optparse' def main output = nil debug_parser = false make_logfile = false logfilename = nil make_executable = false rubypath = nil embed_runtime = false debug_flags = Racc::DebugFlags.new line_convert = true line_convert_all = false omit_action_call = true superclass = nil check_only = false verbose = false profiler = RaccProfiler.new(false) parser = OptionParser.new parser.banner = "Usage: #{File.basename($0)} [options] " parser.on('-o', '--output-file=PATH', 'output file name [.tab.rb]') {|name| output = name } parser.on('-t', '--debug', 'Outputs debugging parser.') {|fl| debug_parser = fl } parser.on('-g', 'Equivalent to -t (obsolete).') {|fl| $stderr.puts "racc -g is obsolete. Use racc -t instead." if $VERBOSE debug_parser = fl } parser.on('-v', '--verbose', 'Creates .output log file.') {|fl| make_logfile = fl } parser.on('-O', '--log-file=PATH', 'Log file name [.output]') {|path| make_logfile = true logfilename = path } parser.on('-e', '--executable [RUBYPATH]', 'Makes executable parser.') {|path| executable = true rubypath = (path == 'ruby' ? nil : path) } parser.on('-E', '--embedded', "Embeds Racc runtime in output.") { embed_runtime = true } parser.on('--line-convert-all', 'Converts line numbers of user codes.') { line_convert_all = true } parser.on('-l', '--no-line-convert', 'Never convert line numbers.') { line_convert = false line_convert_all = false } parser.on('-a', '--no-omit-actions', 'Never omit actions.') { omit_action_call = false } parser.on('--superclass=CLASSNAME', 'Uses CLASSNAME instead of Racc::Parser.') {|name| superclass = name } parser.on('--runtime=FEATURE', "Uses FEATURE instead of 'racc/parser'") {|feat| runtime = feature } parser.on('-C', '--check-only', 'Checks syntax and quit immediately.') {|fl| check_only = fl } parser.on('-S', '--output-status', 'Outputs internal status time to time.') { verbose = true } parser.on('-P', 'Enables generator profile') { profiler = RaccProfiler.new(true) } parser.on('-D flags', "Flags for Racc debugging (do not use).") {|flags| debug_flags = Racc::DebugFlags.parse_option_string(flags) } #parser.on('--no-extensions', 'Run Racc without any Ruby extension.') { # Racc.const_set :Racc_No_Extentions, true #} parser.on('--version', 'Prints version and quit.') { puts "racc version #{Racc::Version}" exit 0 } parser.on('--runtime-version', 'Prints runtime version and quit.') { printf "racc runtime version %s (rev. %s); %s\n", Racc::Parser::Racc_Runtime_Version, Racc::Parser::Racc_Runtime_Revision, if Racc::Parser.racc_runtime_type == 'ruby' sprintf('ruby core version %s (rev. %s)', Racc::Parser::Racc_Runtime_Core_Version_R, Racc::Parser::Racc_Runtime_Core_Revision_R) else sprintf('c core version %s (rev. %s)', Racc::Parser::Racc_Runtime_Core_Version_C, Racc::Parser::Racc_Runtime_Core_Revision_C) end exit 0 } parser.on('--copyright', 'Prints copyright and quit.') { puts Racc::Copyright exit 0 } parser.on('--help', 'Prints this message and quit.') { puts parser.help exit 1 } begin parser.parse! rescue OptionParser::ParseError => err $stderr.puts err.message $stderr.puts parser.help exit 1 end if ARGV.empty? $stderr.puts 'no input' exit 1 end if ARGV.size > 1 $stderr.puts 'too many input' exit 1 end input = ARGV[0] begin $stderr.puts 'Parsing grammar file...' if verbose result = profiler.section('parse') { parser = Racc::GrammarFileParser.new(debug_flags) parser.parse(File.read(input), File.basename(input)) } if check_only $stderr.puts 'syntax ok' exit 0 end $stderr.puts 'Generating LALR states...' if verbose states = profiler.section('nfa') { Racc::States.new(result.grammar).nfa } $stderr.puts "Resolving #{states.size} states..." if verbose profiler.section('dfa') { states.dfa } $stderr.puts 'Creating parser file...' if verbose params = result.params.dup # Overwrites parameters given by a grammar file with command line options. params.superclass = superclass if superclass params.omit_action_call = true if omit_action_call # From command line option if make_executable params.make_executable = true params.interpreter = rubypath end params.debug_parser = debug_parser params.convert_line = line_convert params.convert_line_all = line_convert_all params.embed_runtime = embed_runtime profiler.section('generation') { generator = Racc::ParserFileGenerator.new(states, params) generator.generate_parser_file(output || make_filename(input, '.tab.rb')) } if make_logfile profiler.section('logging') { $stderr.puts 'Creating log file...' if verbose logfilename ||= make_filename(output || File.basename(input), '.output') File.open(logfilename, 'w') {|f| Racc::LogFileGenerator.new(states, debug_flags).output f } } end if debug_flags.status_logging log_useless states.grammar log_conflict states else report_useless states.grammar report_conflict states end profiler.report rescue Racc::Error, Errno::ENOENT, Errno::EPERM => err raise if $DEBUG or debug_flags.any? lineno = err.message.slice(/\A\d+:/).to_s $stderr.puts "#{File.basename $0}: #{input}:#{lineno} #{err.message.strip}" exit 1 end end def make_filename(path, suffix) path.sub(/(?:\..*?)?\z/, suffix) end def report_conflict(states) if states.should_report_srconflict? $stderr.puts "#{states.n_srconflicts} shift/reduce conflicts" end if states.rrconflict_exist? $stderr.puts "#{states.n_rrconflicts} reduce/reduce conflicts" end end def log_conflict(states) logging('w') {|f| f.puts "ex#{states.grammar.n_expected_srconflicts}" if states.should_report_srconflict? f.puts "sr#{states.n_srconflicts}" end if states.rrconflict_exist? f.puts "rr#{states.n_rrconflicts}" end } end def report_useless(grammar) if grammar.useless_nonterminal_exist? $stderr.puts "#{grammar.n_useless_nonterminals} useless nonterminals" end if grammar.useless_rule_exist? $stderr.puts "#{grammar.n_useless_rules} useless rules" end if grammar.start.useless? $stderr.puts 'fatal: start symbol does not derive any sentence' end end def log_useless(grammar) logging('a') {|f| if grammar.useless_nonterminal_exist? f.puts "un#{grammar.n_useless_nonterminals}" end if grammar.useless_rule_exist? f.puts "ur#{grammar.n_useless_rules}" end } end def logging(mode, &block) File.open("log/#{File.basename(ARGV[0])}", mode, &block) end class RaccProfiler def initialize(really) @really = really @log = [] unless ::Process.respond_to?(:times) # Ruby 1.6 @class = ::Time else @class = ::Process end end def section(name) if @really t1 = @class.times.utime result = yield t2 = @class.times.utime @log.push [name, t2 - t1] result else yield end end def report return unless @really f = $stderr total = cumulative_time() f.puts '--task-----------+--sec------+---%-' @log.each do |name, time| f.printf "%-19s %s %3d%%\n", name, pjust(time,4,4), (time/total*100).to_i end f.puts '-----------------+-----------+-----' f.printf "%-20s%s\n", 'total', pjust(total,4,4) end private def cumulative_time t = @log.inject(0) {|sum, (name, time)| sum + time } t == 0 ? 0.01 : t end def pjust(num, i, j) m = /(\d+)(\.\d+)?/.match(num.to_s) str = m[1].rjust(i) str.concat m[2].ljust(j+1)[0,j+1] if m[2] str end end main racc-1.4.14/bin/racc2y0000755000004100000410000001104712625454503014433 0ustar www-datawww-data#!/usr/local/bin/ruby # # $Id$ # # Copyright (c) 1999-2006 Minero Aoki # # This program is feee software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of the LGPL, see the file "COPYING". # require 'racc/grammarfileparser' require 'racc/info' require 'optparse' def main @with_action = true with_header = false with_inner = false with_footer = false output = nil parser = OptionParser.new parser.banner = "Usage: #{File.basename($0)} [-AHIF] [-oFILENAME] GRAMMARFILE" parser.on('-o', '--output=FILENAME', 'output file name [.yacc]') {|name| output = name } parser.on('-A', '--without-action', 'Does not include actions.') { @with_action = false } parser.on('-H', '--with-header', 'Includes header part.') { with_header = true } parser.on('-I', '--with-inner', 'Includes inner part.') { with_inner = true } parser.on('-F', '--with-footer', 'Includes footer part.') { with_footer = true } parser.on('--version', 'Prints version and quit.') { puts "racc2y version #{Racc::Version}" exit 0 } parser.on('--copyright', 'Prints copyright and quit.') { puts Racc::Copyright exit 0 } parser.on('--help', 'Prints this message and quit.') { puts parser.help exit 1 } begin parser.parse! rescue OptionParser::ParseError => err $stderr.puts err.message $stderr.puts parser.help exit 1 end if ARGV.empty? $stderr.puts "no input file" exit 1 end unless ARGV.size == 1 $stderr.puts "too many inputs" exit 1 end input = ARGV[0] begin result = Racc::GrammarFileParser.parse_file(input) result.grammar.init File.open(output || "#{input}.yacc", 'w') {|f| f.puts "/* generated from #{input} */" if with_header f.puts f.puts '%{' print_user_codes f, result.params.header f.puts '%}' end f.puts print_terminals f, result.grammar f.puts print_precedence_table f, precedence_table(result.grammar) f.puts f.puts '%%' print_grammar f, result.grammar f.puts '%%' if with_inner f.puts '/*---- inner ----*/' print_user_codes f, result.params.inner end if with_footer f.puts '/*---- footer ----*/' print_user_codes f, result.params.footer end } rescue SystemCallError => err $stderr.puts err.message exit 1 end end def print_terminals(f, grammar) init_indent = '%token'.size f.print '%token' columns = init_indent grammar.symboltable.each_terminal do |t| next unless t.terminal? next if t.dummy? next if t == grammar.symboltable.anchor next if t == grammar.symboltable.error unless t.value.kind_of?(String) if columns > 60 f.puts f.print ' ' * init_indent columns = init_indent end columns += f.write(" #{yacc_symbol(t)}") end end f.puts end def precedence_table(grammar) table = [] grammar.symboltable.select {|sym| sym.precedence }.each do |sym| (table[sym.prec] ||= [sym.assoc]).push sym end table.compact end def print_precedence_table(f, table) return if table.empty? f.puts '/* precedance table */' table.each do |syms| assoc = syms.shift f.printf '%%%-8s ', assoc.to_s.downcase f.puts syms.map {|s| yacc_symbol(s) }.join(' ') end f.puts end def print_grammar(f, grammar) prev_target = nil indent = 10 embactions = [] grammar.each do |rule| if rule.target.dummy? embactions.push rule.action unless rule.action.empty? next end if rule.target == prev_target f.print ' ' * indent, '|' else prev_target = rule.target f.printf "\n%-10s:", yacc_symbol(prev_target) end rule.symbols.each do |s| if s.dummy? # target of dummy rule for embedded action f.puts print_action f, embactions.shift, indent f.print ' ' * (indent + 1) else f.print ' ', yacc_symbol(s) end end if rule.specified_prec f.print ' %prec ', yacc_symbol(rule.specified_prec) end f.puts unless rule.action.empty? print_action f, rule.action, indent end end end def print_action(f, action, indent) return unless @with_action f.print ' ' * (indent + 4), "{\n" f.print ' ' * (indent + 6), action.source.text.strip, "\n" f.print ' ' * (indent + 4) , "}\n" end def print_user_codes(f, srcs) return if srcs.empty? srcs.each do |src| f.puts src.text end end def yacc_symbol(s) s.to_s.gsub('"', "'") end main racc-1.4.14/Manifest.txt0000644000004100000410000000361512625454503015063 0ustar www-datawww-dataCOPYING ChangeLog DEPENDS Manifest.txt README.ja.rdoc README.rdoc Rakefile TODO bin/racc bin/racc2y bin/y2racc ext/racc/MANIFEST ext/racc/cparse.c ext/racc/depend ext/racc/extconf.rb ext/racc/com/headius/racc/Cparse.java fastcache/extconf.rb fastcache/fastcache.c lib/racc.rb lib/racc/compat.rb lib/racc/debugflags.rb lib/racc/exception.rb lib/racc/grammar.rb lib/racc/grammarfileparser.rb lib/racc/info.rb lib/racc/iset.rb lib/racc/logfilegenerator.rb lib/racc/parser-text.rb lib/racc/parser.rb lib/racc/parserfilegenerator.rb lib/racc/pre-setup lib/racc/sourcetext.rb lib/racc/state.rb lib/racc/statetransitiontable.rb lib/racc/static.rb misc/dist.sh rdoc/en/NEWS.en.rdoc rdoc/en/grammar.en.rdoc rdoc/ja/NEWS.ja.rdoc rdoc/ja/command.ja.html rdoc/ja/debug.ja.rdoc rdoc/ja/grammar.ja.rdoc rdoc/ja/index.ja.html rdoc/ja/parser.ja.rdoc rdoc/ja/usage.ja.html sample/array.y sample/array2.y sample/calc-ja.y sample/calc.y sample/conflict.y sample/hash.y sample/lalr.y sample/lists.y sample/syntax.y sample/yyerr.y setup.rb tasks/doc.rb tasks/email.rb test/assets/chk.y test/assets/conf.y test/assets/digraph.y test/assets/echk.y test/assets/err.y test/assets/error_recovery.y test/assets/expect.y test/assets/firstline.y test/assets/ichk.y test/assets/intp.y test/assets/mailp.y test/assets/newsyn.y test/assets/noend.y test/assets/nonass.y test/assets/normal.y test/assets/norule.y test/assets/nullbug1.y test/assets/nullbug2.y test/assets/opt.y test/assets/percent.y test/assets/recv.y test/assets/rrconf.y test/assets/scan.y test/assets/syntax.y test/assets/unterm.y test/assets/useless.y test/assets/yyerr.y test/bench.y test/helper.rb test/infini.y test/scandata/brace test/scandata/gvar test/scandata/normal test/scandata/percent test/scandata/slash test/src.intp test/start.y test/test_chk_y.rb test/test_grammar_file_parser.rb test/test_racc_command.rb test/test_scan_y.rb test/testscanner.rb web/racc.en.rhtml web/racc.ja.rhtml racc-1.4.14/fastcache/0000755000004100000410000000000012625454503014470 5ustar www-datawww-dataracc-1.4.14/fastcache/extconf.rb0000644000004100000410000000005312625454503016461 0ustar www-datawww-datarequire 'mkmf' create_makefile 'corecache' racc-1.4.14/fastcache/fastcache.c0000644000004100000410000000722512625454503016563 0ustar www-datawww-data/* $Id$ Copyright (C) 2005 Minero Aoki This program is free software. You can distribute/modify this program under the terms of the GNU LGPL, Lesser General Public Licese version 2.1. */ #include "ruby.h" static VALUE LALRcoreCache; struct item_holder { unsigned long hashval; VALUE core; VALUE state; struct item_holder *next; }; struct lalr_state_cache { struct item_holder **bin; long size; long num; }; static void lalrc_free(struct lalr_state_cache *p) { struct item_holder *tmp; long i; for (i = 0; i < p->size; i++) { while (tmp = p->bin[i]) { p->bin[i] = tmp->next; free(tmp); } } free(p->bin); free(p); } #define INIT_BIN 256 static VALUE lalrc_s_new(VALUE self) { struct lalr_state_cache *cache; cache = ALLOC_N(struct lalr_state_cache, 1); cache->bin = ALLOC_N(struct item_holder*, INIT_BIN); cache->size = INIT_BIN; cache->num = 0; return Data_Wrap_Struct(LALRcoreCache, 0, lalrc_free, cache); } #define GET_LALRC(self, p) Data_Get_Struct(self, struct lalr_state_cache, p) static void lalrc_rehash(struct lalr_state_cache *p) { struct item_holder *top = 0, *tmp = 0; long i; for (i = p->size / 2; i < p->size; i++) { p->bin[i] = 0; } for (i = 0; i < p->size / 2; i++) { if (!p->bin[i]) continue; tmp = p->bin[i]; while (tmp->next) tmp = tmp->next; tmp->next = top; top = p->bin[i]; p->bin[i] = 0; } while (top) { tmp = top; top = tmp->next; tmp->next = 0; i = tmp->hashval % p->size; if (p->bin[i]) { tmp->next = p->bin[i]; p->bin[i] = tmp; } else { p->bin[i] = tmp; } } } static int coreeql(VALUE a, VALUE b) { long i; /* Check_Type(a, T_ARRAY); Check_Type(b, T_ARRAY); */ if (RARRAY(a)->len != RARRAY(b)->len) return 0; for (i = 0; i < RARRAY(a)->len; i++) if (RARRAY(a)->ptr[i] != RARRAY(b)->ptr[i]) return 0; return 1; } static unsigned long hashval(VALUE core) { unsigned long v = 0; long i, j; for (i = 0; i < RARRAY(core)->len; i++) { v *= RARRAY(core)->ptr[i]; v ^= RARRAY(core)->ptr[i]; } return v; } static VALUE lalrc_aref(VALUE self, VALUE core) { struct lalr_state_cache *p; unsigned long v; long i; struct item_holder *ad; /* Check_Type(core, T_ARRAY); */ GET_LALRC(self, p); v = hashval(core); i = v % p->size; ad = p->bin[i]; while (ad) { if (ad->hashval == v) { if (coreeql(core, ad->core)) { return ad->state; } else { printf("."); } } ad = ad->next; } return Qnil; } static VALUE lalrc_add_direct(VALUE self, VALUE core, VALUE state) { struct lalr_state_cache *p; struct item_holder *ad; long i; GET_LALRC(self, p); ad = ALLOC_N(struct item_holder, 1); ad->hashval = hashval(core); ad->core = core; ad->state = state; i = ad->hashval % p->size; ad->next = p->bin[i]; p->bin[i] = ad; p->num++; if ((p->num / p->size) >= 1) { REALLOC_N(p->bin, struct item_holder*, p->size * 2); p->size *= 2; lalrc_rehash(p); } return Qnil; } void Init_corecache(void) { LALRcoreCache = rb_define_class("LALRcoreCache", rb_cObject); rb_define_singleton_method(LALRcoreCache, "new", lalrc_s_new, 0); rb_define_method(LALRcoreCache, "[]", lalrc_aref, 1); rb_define_method(LALRcoreCache, "[]=", lalrc_add_direct, 2); } racc-1.4.14/rdoc/0000755000004100000410000000000012625454503013476 5ustar www-datawww-dataracc-1.4.14/rdoc/ja/0000755000004100000410000000000012625454503014070 5ustar www-datawww-dataracc-1.4.14/rdoc/ja/usage.ja.html0000644000004100000410000004265012625454503016462 0ustar www-datawww-data

Racc の使い方

Racc は文法規則から Ruby で書かれたパーサを生成するパーサジェネレータです。 パーサ生成アルゴリズムには yacc などと同じ LALR(1) を使用しています。

yacc を知っている人は記述法の違いだけわかれば使えると思います。 yacc を知らない人は 拙著『Ruby を 256 倍使うための本 無道編』(青木峰郎著、ASCII) などを一読していただくのがよいかと思います。 他の UNIX コマンドなどとは異なり、 いきなり使うだけで Racc を理解するのはかなり困難です。

Racc とはなにか

Racc は文法を処理するツールです。 文字列はただの文字の列で、コンピュータにとっては意味を持ちません。 しかし人間はその文字の列の中になにか意味を見出すことができます。 コンピュータにもそのようなことを、部分的にでも、させられたら便利でしょう。 Racc はその手伝いをしてくれます。完全な自動化ではありませんが、 人間が全部やるよりも遥かに簡単になります。

Racc が自動化してくれる部分とは、文字列の含む「構造」の処理です。 たとえば Ruby の if 文を考えてみると、次のように定式化できます。

if 条件式 [then]
  文
  :
[elsif 条件式 [then]
  文
  :]
[else
  文
  :]
end

if 文では if という単語が最初になくてはならず、 elsif 節は else 節より前になくてはいけません。 このような配置の関係 (構造) が、Racc が処理する対象です。

一方、Racc で処理できないのはどういうことでしょうか。それは、たとえば if の条件式にあたる部分が「なんであるか」ということです。つまり、条件 式が if の条件だということです。これは、こっちで条件として扱うコードを 書いてやらないといけません。

と言っても、わかりにくいでしょう。こういう抽象的なものは実際にいじって みるのが一番です。

実際の話

実際に Racc をどのように使うかという話をします。Racc には独自のソース コードみたいなものがあって、この中に処理したい「構造」を記述しておきま す。このソースファイルを「文法ファイル」と呼ぶことにしましょう。この文 法ファイルの名前が parse.y と仮定すると、コマンドラインから以下のよう に打ちこめば、その構造を処理するためのクラスを含んだファイルが得られま す。

$ racc parse.y

生成されるファイルはデフォルトでは "ファイル名.tab.rb" です。他の名前 にしたいなら、-o オプションで変更できます。

$ racc parse.y -o myparser.rb

このようにして作ったクラス、またはそのような処理を担当するパート、 のことはパーサ (parser) と呼ぶことになっています。解析するヤツ、 というくらいに適当にとらえてください。

文法ファイルを書く

Racc は文法ファイルから Ruby のクラスを生成するツールだと言いました。 そのクラスは全て Racc::Parser の下位クラスで、名前は文法ファイル中で 指定します。以下、ここに書くべきことが「なんなのか」を説明します。 ここでは内容に重点を置くので、文法ファイル自体の文法の詳細は 文法リファレンスを見てください。

文法

まずは、全体の概形です。

class MyParser
rule

  if_stmt: IF expr then stmt_list elsif else END

  then   : THEN
         |

  elsif  :
         | ELSIF stmt_list

  else   :
         | ELSE stmt_list

  expr   : NUMBER
         | IDENT
         | STRING

  stmt_list : ふにゃふにゃ

end

Ruby スクリプトのように class でパーサクラス名を指定し、rule ... end の間にパーサに解析させたい文法を記述します。

文法は、記号の並びでもって表します。rule ... end の間にあるコロンとバー 以外のもの、if_stmt IF expr then などが全て「記号」です。そしてコロン が日本語で言う「〜は××だ」の「は」みたいなもんで、その左の記号が右の 記号の列と同じものを指す、というふうに定義します。また、バーは「または」 を意味します。それと、単純にコロンの左の記号のことを左辺、右を右辺とも 言います。以下はこちらのほうを使って説明しましょう。

少し注意が必要な点を述べます。まず、then の、バーのあとの定義 (規則) を 見てください。ここには何も書いていないので、これはその通り「無」であっ てもいい、ということを表しています。つまり、then は記号 THEN 一個か、 またはなにもなし(省略する)でよい、ということです。記号 then は実際の Ruby のソースコードにある then とは切り離して考えましょう (それは実は大文字の記号 THEN が表しています)。

さて、そろそろ「記号」というものがなんなのか書きましょう。 ただし順番に話をしないといけないので、まずは聞いていてください。 この文章の最初に、パーサとは文字の列から構造を見出す部分だと言いました。 しかし文字の列からいきなり構造を探すのは面倒なので、実際にはまず 文字の列を単語の列に分割します。その時点でスペースやコメントは捨てて しまい、以降は純粋にプログラムの一部をなす部分だけを相手にします。 たとえば文字列の入力が次のようだったとすると、

if flag then   # item found.
  puts 'ok'
end

単語の列は次のようになります。

if flag then puts 'ok' end

ここで、工夫が必要です。どうやら flag はローカル変数名だと思われますが、 変数名というのは他にもいろいろあります。しかし名前が i だろうが a だろ うが vvvvvvvvvvvv だろうが、「構造」は同じです。つまり同じ扱いをされる べきです。変数 a を書ける場所なら b も書けなくてはいけません。だったら 一時的に同じ名前で読んでもいいじゃん。ということで、この単語の列を以下 のように読みかえましょう。

IF IDENT THEN IDENT STRING END

これが「記号」の列です。パーサではこの記号列のほうを扱い、構造を見付け ていきます。

さらに記号について見ていきましょう。 記号は二種類に分けられます。「左辺にある記号」と「ない記号」です。 左辺にある記号は「非終端」記号と言います。ないほうは「終端」記号と 言います。最初の例では終端記号はすべて大文字、非終端記号は小文字で 書いてあるので、もう一度戻って例の文法を見てください。

なぜこの区分が重要かと言うと、入力の記号列はすべて終端記号だからです。 一方、非終端記号はパーサの中でだけ、終端記号の列から「作りだす」ことに よって始めて存在します。例えば次の規則をもう一度見てください。

  expr   : NUMBER
         | IDENT
         | STRING

expr は NUMBER か IDENT か STRING だと言っています。逆に言うと、 IDENT は expr に「なることができます」。文法上 expr が存在できる 場所に IDENT が来ると、それは expr になります。例えば if の条件式の 部分は expr ですから、ここに IDENT があると expr になります。その ように文法的に「大きい」記号を作っていって、最終的に一個になると、 その入力は文法を満たしていることになります。実際にさっきの入力で 試してみましょう。入力はこうでした。

IF IDENT THEN IDENT STRING END

まず、IDENT が expr になります。

IF expr THEN IDENT STRING END

次に THEN が then になります。

IF expr then IDENT STRING END

IDENT STRING がメソッドコールになります。この定義はさきほどの例には ないですが、実は省略されているんだと考えてください。そしていろいろな 過程を経て、最終的には stmt_list (文のリスト)になります。

IF expr then stmt_list END

elsif と else は省略できる、つまり無から生成できます。

IF expr then stmt_list elsif else END

最後に if_stmt を作ります。

if_stmt

ということでひとつになりました。 つまりこの入力は文法的に正しいということがわかりました。

アクション

ここまでで入力の文法が正しいかどうかを確認する方法はわかりましたが、 これだけではなんにもなりません。最初に説明したように、ここまででは 構造が見えただけで、プログラムは「意味」を理解できません。そしてその 部分は Racc では自動処理できないので、人間が書く、とも言いました。 それを書くのが以下に説明する「アクション」という部分です。

前項で、記号の列がだんだんと大きな単位にまとめられていく過程を見ました。 そのまとめる時に、同時になにかをやらせることができます。それが アクションです。アクションは、文法ファイルで以下のように書きます。

class MyParser
rule

  if_stmt: IF expr then stmt_list elsif else END
             { puts 'if_stmt found' }

  then   : THEN
             { puts 'then found' }
         |
             { puts 'then is omitted' }

  elsif  :
             { puts 'elsif is omitted' }
         | ELSIF stmt_list
             { puts 'elsif found' }

  else   :
             { puts 'else omitted' }
         | ELSE stmt_list
             { puts 'else found' }

  expr   : NUMBER
             { puts 'expr found (NUMBER)' }
         | IDENT
             { puts 'expr found (IDENT)' }
         | STRING
             { puts 'expr found (STRING)' }

  stmt_list : ふにゃふにゃ

end

見てのとおり、規則のあとに { と } で囲んで書きます。 アクションにはだいたい好きなように Ruby スクリプトが書けます。

(この節、未完)


yacc での $$ は Racc ではローカル変数 result で、$1,$2... は配列 valです。 resultval[0] ($1) の値に初期化され、 アクションを抜けたときの result の値が左辺値になります。 Racc ではアクション中の return はアクションから抜けるだけで、 パース自体は終わりません。アクション中からパースを終了するには、 メソッド yyaccept を使ってください。

演算子の優先順位、スタートルールなどの yacc の一般的な機能も用意されて います。ただしこちらも少し文法が違います。

yacc では生成されたコードに直接転写されるコードがありました。 Racc でも同じように、ユーザ指定のコードが書けます。 Racc ではクラスを生成するので、クラス定義の前/中/後の三個所があります。 Racc ではそれを上から順番に header inner footer と呼んでいます。

ユーザが用意すべきコード

パースのエントリポイントとなるメソッドは二つあります。ひとつは do_parseで、こちらはトークンを Parser#next_token から得ます。もうひとつは yyparse で、こちらはスキャナから yield され ることによってトークンを得ます。ユーザ側ではこのどちらか(両方でもいい けど)を起動する簡単なメソッドを inner に書いてください。これらメソッド の引数など、詳しいことはリファレンスを見てください。

どちらのメソッドにも共通なのはトークンの形式です。必ずトークンシンボル とその値の二要素を持つ配列を返すようにします。またスキャンが終了して、 もう送るものがない場合は [false,なにか] を返し てください。これは一回返せば十分です (逆に、yyparse を使 う場合は二回以上 yield してはいけない)。

パーサは別に文字列処理にだけ使われるものではありませんが、実際問題とし て、パーサを作る場面ではたいてい文字列のスキャナとセットで使うことが多 いでしょう。Ruby ならスキャナくらい楽勝で作れますが、高速なスキャナと なると実は難しかったりします。そこで高速なスキャナを作成するためのライ ブラリも作っています。詳しくは 「スキャナを作る」の項を見てください。

Racc には error トークンを使ったエラー回復機能もあります。yacc の yyerror() は Racc では Racc::Parser#on_error で、エラーが起きたトークンとその値、値スタック、の三つの引数をとります。 on_error のデフォルトの実装は例外 Racc::ParseError を発生します。

ユーザがアクション中でパースエラーを発見した場合は、メソッド yyerror を呼べばパーサがエラー回復モードに入ります。 ただしこのとき on_errorは呼ばれません。

パーサを生成する

これだけあればだいたい書けると思います。あとは、最初に示した方法で文法 ファイルを処理し、Ruby スクリプトを得ます。

うまくいけばいいのですが、大きいものだと最初からはうまくいかないでしょ う。racc に -g オプションをつけてコンパイルし、@yydebug を true にする とデバッグ用の出力が得られます。デバッグ出力はパーサの @racc_debug_out に出力されます(デフォルトは stderr)。また、racc に -v オプションをつけ ると、状態遷移表を読みやすい形で出力したファイル(*.output)が得られます。 どちらもデバッグの参考になるでしょう。

作ったパーサを配布する

Racc の生成したパーサは動作時にランタイムルーチンが必要です。 具体的には parser.rb と cparse.so です。 ただし cparse.so は単にパースを高速化するためのライブラリなので 必須ではありません。なくても動きます。

まず Ruby 1.8.0 以降にはこのランタイムが標準添付されているので、 Ruby 1.8 がある環境ならばランタイムについて考慮する必要はありません。 Racc 1.4.x のランタイムと Ruby 1.8 に添付されているランタイムは 完全互換です。

問題は Ruby 1.8 を仮定できない場合です。 Racc をユーザみんなにインストールしてもらうのも一つの手ですが、 これでは不親切です。そこでRacc では回避策を用意しました。

racc に -E オプションをつけてコンパイルすると、 パーサと racc/parser.rb を合体したファイルを出力できます。 これならばファイルは一つだけなので簡単に扱えます。 racc/parser.rb は擬似的に require したような扱いになるので、 この形式のパーサが複数あったとしてもクラスやメソッドが衝突することもありません。 ただし -E を使った場合は cparse.so が使えませんので、 必然的にパーサの速度は落ちます。

おまけ: スキャナを書く

パーサを使うときは、たいてい文字列をトークンに切りわけてくれるスキャナ が必要になります。しかし実は Ruby は文字列の最初からトークンに切りわけ ていくという作業があまり得意ではありません。 正確に言うと、簡単にできるのですが、それなりのオーバーヘッドがかかります。

そのオーバーヘッドを回避しつつ、 手軽にスキャナを作れるように strscan というパッケージを作りました。 Ruby 1.8 以降には標準添付されていますし、 筆者のホームページには 単体パッケージがあります。

racc-1.4.14/rdoc/ja/NEWS.ja.rdoc0000644000004100000410000002326312625454503016114 0ustar www-datawww-data= NEWS === 1.4.6 * バグの修正 * bin/racc -g オプションを -t に改名 * racc/compiler.rb を削除 * '|' が meta rules によって許可 * Ruby 1.8.7 互換性を修正 * Ruby 1.9 互換性を修正 === 1.4.5 (2005-11-21) * [FEATURE CHANGE] --no-extensions オプションを削除 * [fix] racc パッケージのみで -E を使えるように修正 * [fix] --no-omit-actions が動作していなかったのを修正 * setup.rb 3.4.1. === 1.4.4 (2003-10-12) * Ruby 1.8.0 に対応するリリース。本体に変更はなし * -all パッケージに strscan, amstd の同梱するのをやめた * setup.rb 3.2.1 === 1.4.3 (2002-11-14) * [fix] ruby 1.8 の警告を消した === 1.4.2 (2002-01-29) * [new] 新しいオプション --no-extentions === 1.4.1 (2001-12-02) * amstd 非依存になった (ただし -all パッケージへバンドルは継続) * y2racc racc2y を 1.4 対応にした === 1.4.0 (2001-11-30) * ランタイムを Ruby の CVS に入れたのにあわせてマイナーバージョンアップ * RaccParser, RaccScanner → GrammarFileParser, GrammarFileScanner * ハズい typo を修正 (grammer → grammar) === 1.3.12 (2001-11-22) * インストーラのバグを修正 (thanks Tanaka Akira) * アクション中の正規表現や % 文字列、グローバル変数の検出を向上させた === 1.3.11 (2001-08-28) * アクション中の $' $` $/ などを正しくスキャン === 1.3.10 (2001-08-12) * cparse.c のプロトタイプ違いを直した === 1.3.9 (2001-04-07) * Ruby 1.4 に(再び)対応した === 1.3.8 (2001-03-17) * パースエラーの時に記号名も出力するようにした * Racc::Parser#token_to_s === 1.3.7 (2001-02-04) * サンプルを増やした === 1.3.6 (2001-01-22) * cparse がスタティックリンクされても動くようにした === 1.3.5 (2001-01-18) * % 文字列のスキャンがバグってた * 新しい命令 expect === 1.3.4 (2001-01-11) * cparse: シンボルのタイプチェックを入れた * cparse: depend を消した * cparse: rb_iterate 中の GC で落ちるバグを修正 === 1.3.3 (2000-12-25) * ジェネレータに致命的なバグ。1.3.1 から混入 (format.rb) * racc --runtime-version === 1.3.2 (2000-12-21) * -E が失敗するのを直した * 再度 strscan を同梱 (y2racc/racc2y に必要) === 1.3.1 (2000-12-17) * 正規表現の繰り返し指定の上限を動的に決定する (RE_DUP_MAX) * パースルーチンが常に Ruby 版になっていた (消し忘れ) === 1.3.0 (2000-11-30) * スキャナから yield でトークンを渡せるようになった === 1.2.6 (2000-11-28) * class M::C を許した === 1.2.5 (2000-11-20) * オプションに大変動。非互換オプションは -h -f -p -i -n -c -A * ロングオプションをサポート * y2racc, racc2y はデフォルトでアクションを残すようにした === 1.2.4 (2000-09-13) * インストーラとドキュメントを更新 === 1.2.3 (2000-08-14) * 使われない規則と非終端記号を出力 (強力版) * S/R conflict の時 nonassoc で解決するならばエラー === 1.2.2 (2000-08-12) * 内部の変更 === 1.2.1 (2000-08-05) * yacc との変換コマンド racc2y・y2racc を添付 === 1.2.0 (2000-08-02) * 先読みアルゴリズムを bison のものに変更 === 1.1.6 (2000-07-25) * 新たなキーワード options とその引数 no_result_var === 1.1.5 (2000-07-21) * [重要] token を convert に変更 * 「新たな」キーワード token (終端記号の宣言) === 1.1.4 (2000-07-13) * サンプルがバグってた === 1.1.3 (2000-06-30) * 空アクションの呼び出しを省略しないようにするオプション -a === 1.1.2 (2000-06-29) * スキャナで strscan を使わないようにした * ScanError -> Racc::ScanError, ParseError -> Racc::ParseError * エラーメッセージを強化 === 1.1.1 (2000-06-15) * requireミス (thanks Toshさん) * -v をつけるとconflictが報告されなくなっていた === 1.1.0 (2000-06-12) * 新しい 状態遷移表生成アルゴリズム === 1.0.4 (2000-06-04) * S/R conflict がおきると .output 出力で落ちるバグ修正 (Tosh さんの報告) * 使われない非終端記号・規則を表示 === 1.0.3 (2000-06-03) * filter -> collect! === 1.0.2 (2000-05-16) * インストーラをアップデート === 1.0.1 (2000-05-12) * state.rb: 先読みルーチンをちょっとだけ高速化 && 追加デバッグ * コードを整理した。著作権表示全体を全部のファイルにつけた。 * amstd アップデート (1.7.0) === 1.0.0 (2000-05-06) * バージョン 1.0 === 0.14.6 (2000-05-05) * デバッグ出力を詳細にした === 0.14.5 (2000-05-01) * インストーラを ruby 1.4.4 系の新しいパスに対応させた === 0.14.4 (2000-04-09) * パーサの定数を削減(Racc_arg にまとめた) * state 生成を微妙に高速化(コアを文字列に変換) === 0.14.3 (2000-04-04) * cparse の SYM2ID と ID2SYM のチェックを分離 (thanks 小松さん) === 0.14.2 (2000-04-03) * 一行目の class がパースエラーになっていた (thanks 和田さん) * 新しいフラグ racc -V === 0.14.1 (2000-03-31) === 0.14.0 (2000-03-21) * 高速テーブルを実装 * 一時的にファイル名/行番号の変換をやめた(Rubyのバグのため。) === 0.13.1 (2000-03-21) * --version --copyright などがうまく働いてなかった (thanks ふなばさん) === 0.13.0 (2000-03-20) * yyerror/yyerrok/yyaccept を実装 === 0.12.2 (2000-03-19) * -E フラグがバグってた (thanks ふなばさん) === 0.12.1 (2000-03-16) * デフォルトアクションの決め方をちょっと修正(元に戻しただけ) === 0.12.0 (2000-03-15) * 完全な LALR を実装したら遅くなったので SLR も併用するようにした。効果絶大。 === 0.11.3 (2000-03-09) * 状態遷移表生成のバグの修正がまだ甘かった。さらに別のバグもあるようだ。 === 0.11.2 (2000-03-09) * cparse が Symbol に対応できてなかった === 0.11.1 (2000-03-08) * ruby 1.5 の Symbol に対応 * strscan を最新に === 0.11.0 (2000-02-19) * 例外のとき、元のファイルの行番号が出るようにした === 0.10.9 (2000-01-19) * セットアップ方法など細かな変更 === 0.10.8 (2000-01-03) * 忘れてしまったけどたしかインストーラ関係の修正 * (1/17 repacked) ドキュメントの追加と修正 === 0.10.7 (2000-01-03) * setup.rb compile.rb amstd/inst などのバグ修正 === 0.10.6 (1999-12-24) * racc -e ruby でデフォルトパスを使用 * 空のアクションの呼びだしは省略するようにした === 0.10.5 (1999-12-21) * 埋めこみアクションの実装がすさまじくバグってた * setup.rb が inst.rb の変化に追従してなかった * calc.y calc2.y を 0.10 用に修正 === 0.10.4 (1999-12-19) * エラー回復モードを実装 * racc -E で単体で動作するパーサを生成 * Racc は class から module になった === 0.10.3 (1999-12-01) * 埋めこみアクションをサポート * .output の出力内容にバグがあったのを修正 === 0.10.2 (1999-11-27) * ドキュメントの訂正と更新 * libracc.rb を分割 === 0.10.1 (1999-11-19) * C でランタイムを書きなおした * next_token が false を返したらもう読みこまない * アクションがトークンによらず決まるときは next_token を呼ばない * $end 廃止 * LALRactionTable === 0.10.0 (1999-11-06) * next_* を next_token に一本化、peep_token 廃止 * @__debug__ -< @yydebug など変数名を大幅変更 * 文法ファイルの構造が class...rule...end に変わった * コアのコードを一新、高速化 * strscan を併合 * ライブラリを racc/ ディレクトリに移動 === 0.9.5 (1999-10-03) * 0.9.4 の変更がすごくバグってた * $end が通らなかったのを修正 * __show_stack__ の引数が違ってた === 0.9.4 (1999-09-??) * Parser::Reporter をなくしてメソッドに戻した * d.format.rb を再編成 === 0.9.3 (1999-09-03) * racc.rb -> racc === 0.9.2 (1999-06-26) * strscan使用 === 0.9.1 (1999-06-08) * アクション中の正規表現に対応 ( /= にも注意だ) * アクション中の # コメントに対応 === 0.9.0 (1999-06-03) * アクションを { } 形式にした * ユーザーコードを '----' を使う形式にした === 0.8.11 (?) * -g の出力をわかりやすくした === 0.8.10 (?) * アクションからreturnできるようにした === 0.8.9 (1999-03-21) * -g + @__debug__をつかったデバッグメッセージ操作 * エラー発生時のバグを修正 * TOKEN_TO_S_TABLEを付加するようにした === 0.8.8 (1999-03-20) * 100倍程度の高速化 * defaultトークンを加えた * デバッグ用ソースを出力するオプション-gをくわえた * user_initializeを廃止し、普通にinitializeを使えるようにした * parse_initialize/finalize,parseメソッドを廃止 * next_token,next_value,peep_tokenのデフォルトを廃止 * %precと同等の機能を加えた === 0.8.7 (1999-03-01) * 内部構造が大幅に変化 * マニュアルがHTMLになった === 0.8.0 (1999-01-16) * 文法がブロック型に変化 === 0.5.0 (1999-01-07) * 演算子優先順位が実装されたようだ * スタート規則が実装されたようだ * トークン値の置換が実装されたようだ(後に致命的なバグ発見) === 0.1.0 (1999-01-01) * とにかく動くようになった racc-1.4.14/rdoc/ja/grammar.ja.rdoc0000644000004100000410000003426612625454503016773 0ustar www-datawww-data= 規則ファイル文法リファレンス == 文法に関する前バージョンとの非互換 * (1.2.5) ユーザーコードを連結する時、外部ファイルよりも 埋めこんであるコードを先に連結します。 * (1.1.6) 新しいディレクティブ options が追加されました。 * (1.1.5) 予約語 token の意味が変更になりました。 * (0.14) ルールの最後のセミコロンが省略可能になりました。 また、token prechigh などが予約語でなくなりました。 * (10.2) prepare が header に driver が footer になりました。 今はそのままでも使えますが、2.0 からは対応しません。 * (0.10) class に対応する end がなくなりました。 * (0.9) ダサダサのピリオド方式をやめて { と } で囲むようにしました。 == 全体の構造 トップレベルは、規則部とユーザーコード部に分けられます。 ユーザーコード部はクラス定義の後に来なければいけません。 === コメント 文法ファイルには、一部例外を除いて、ほとんどどこにでもコメントを 書くことができます。コメントは、Rubyの #.....(行末) スタイルと、 Cの /*......*/ スタイルを使うことができます。 === 規則部 規則部は以下のような形をしています。 -- class クラス名 [< スーパークラス] [演算子順位] [トークン宣言] [オプション] [expect] [トークンシンボル値おきかえ] [スタート規則] rule 文法記述 -- "クラス名"はここで定義するパーサクラスの名前です。 これはそのままRubyのクラス名になります。 また M::C のように「::」を使った名前を使うと、クラス定義を モジュール M の中にネストさせます。つまり class M::C ならば -- module M class C < Racc::Parser いろいろ end end -- のように出力します。 さらに、Ruby と同じ構文でスーパークラスを指定できます。 ただしこの指定をするとパーサの動作に重大な影響を与えるので、 特に必要がない限り指定してはいけません。これは将来の拡張の ために用意したもので、現在指定する必然性はあまりありません。 === 文法の記述 racc で生成するパーサが理解できる文法を記述します。 文法は、予約語 rule と end の間に、以下のような書式で書きます。 -- トークン: トークンの並び アクション トークン: トークンの並び アクション | トークンの並び アクション | トークンの並び アクション (必要なだけ同じようにつづける) -- アクションは { } で囲みます。アクションでは Ruby の文はほとんど 使えますが、一部だけは非対応です。対応していないものは以下のとおり。 * ヒアドキュメント * =begin ... =end 型コメント * スペースで始まる正規表現 * ごくまれに % の演算。普通に演算子のまわりにスペースを入れていれば問題なし このあたりに関しては完全な対応はまず無理です。あきらめてください。 左辺の値($$)は、オプションによって返し方がかわります。まずデフォルトでは ローカル変数 result (そのデフォルト値は val[0])が 左辺値を表し、アクション ブロックを抜けた時の result の値が左辺値になります。または明示的に return で返した場合もこの値になります。一方、options で no_result_var を指定した 場合、左辺値はアクションブロックの最後の文の値になります (Ruby のメソッドと 同じ)。 どちらの場合でもアクションは省略でき、省略した場合の左辺値は常に val[0] です。 以下に文法記述の全体の例をしめします。 -- rule goal: def ruls source { result = val } def : /* none */ { result = [] } | def startdesig { result[0] = val[1] } | def precrule # これは上の行の続き { result[1] = val[1] } (略) -- アクション内では特別な意味をもった変数がいくつか使えます。 そのような変数を以下に示します。括弧の中は yacc での表記です。 * result ($$) 左辺の値。初期値は val[0] です。 * val ($1,$2,$3…) 右辺の記号の値の配列。Ruby の配列なので当然インデックスはゼロから始まります。 この配列は毎回作られるので自由に変更したり捨てたりして構いません。 * _values (...,$-2,$-1,$0) 値スタック。Racc コアが使っているオブジェクトがそのまま渡されます。 この変数の意味がわかる人以外は絶対に変更してはいけません。 またアクションの特別な形式に、埋めこみアクションというものがあります。 これはトークン列の途中の好きなところに記述することができます。 以下に埋めこみアクションの例を示します。 -- target: A B { puts 'test test' } C D { normal action } -- このように記述すると A B を検出した時点で puts が実行されます。 また、埋めこみアクションはそれ自体が値を持ちます。つまり、以下の例において -- target: A { result = 1 } B { p val[1] } -- 最後にある p val[1] は埋めこみアクションの値 1 を表示します。 B の値ではありません。 意味的には、埋めこみアクションは空の規則を持つ非終端記号を追加することと 全く同じ働きをします。つまり、上の例は次のコードと完全に同じ意味です。 -- target : A nonterm B { p val[1] } nonterm : /* 空の規則 */ { result = 1 } -- === 演算子優先順位 あるトークン上でシフト・還元衝突がおこったとき、そのトークンに 演算子優先順位が設定してあると衝突を解消できる場合があります。 そのようなものとして特に有名なのは数式の演算子と if...else 構文です。 優先順位で解決できる文法は、うまく文法をくみかえてやれば 優先順位なしでも同じ効果を得ることができます。しかしたいていの 場合は優先順位を設定して解決するほうが文法を簡単にできます。 シフト・還元衝突がおこったとき、Racc はまずその規則に順位が設定 されているか調べます。規則の順位は、その規則で一番うしろにある 終端トークンの優先順位です。たとえば -- target: TERM_A nonterm_a TERM_B nonterm_b -- のような規則の順位はTERM_Bの優先順位になります。もしTERM_Bに 優先順位が設定されていなかったら、優先順位で衝突を解決することは できないと判断し、「Shift/Reduce conflict」を報告します。 演算子の優先順位はつぎのように書いて定義します。 -- prechigh nonassoc PLUSPLUS left MULTI DEVIDE left PLUS MINUS right '=' preclow -- prechigh に近い行にあるほど優先順位の高いトークンです。上下をまるごと さかさまにして preclow...prechigh の順番に書くこともできます。left などは必ず行の最初になければいけません。 left right nonassoc はそれぞれ「結合性」を表します。結合性によって、 同じ順位の演算子の規則が衝突した場合にシフト還元のどちらをとるかが 決まります。たとえば -- a - b - c -- が -- (a - b) - c -- になるのが左結合 (left) です。四則演算は普通これです。 一方 -- a - (b - c) -- になるのが右結合 (right) です。代入のクオートは普通 right です。 またこのように演算子が重なるのはエラーである場合、非結合 (nonassoc) です。 C 言語の ++ や単項のマイナスなどがこれにあたります。 ところで、説明したとおり通常は還元する規則の最後のトークンが順位を 決めるのですが、ある規則に限ってそのトークンとは違う順位にしたいことも あります。例えば符号反転のマイナスは引き算のマイナスより順位を高く しないといけません。このような場合 yacc では %prec を使います。 racc ではイコール記号を使って同じことをできます。 -- prechigh nonassoc UMINUS left '*' '/' left '+' '-' preclow (略) exp: exp '*' exp | exp '-' exp | '-' exp = UMINUS # ここだけ順位を上げる -- このように記述すると、'-' exp の規則の順位が UMINUS の順位になります。 こうすることで符号反転の '-' は '*' よりも順位が高くなるので、 意図どおりになります。 === トークン宣言 トークン(終端記号)のつづりを間違えるというのはよくあることですが、 発見するのはなかなか難しいものです。1.1.5 からはトークンを明示的に 宣言することで、宣言にないトークン / 宣言にだけあるトークンに対して 警告が出るようになりました。yacc の %token と似ていますが最大の違いは racc では必須ではなく、しかもエラーにならず警告だけ、という点です。 トークン宣言は以下のように書きます。 -- token A B C D E F G H -- トークンのリストを複数行にわたって書けることに注目してください。 racc では一般に「予約語」は行の先頭に来た時だけ予約語とみなされるので prechigh などもシンボルとして使えます。ただし深淵な理由から end だけは どうやっても予約語になってしまいます。 === オプション racc のコマンドラインオプションの一部をファイル中にデフォルト値 として記述することができます。 -- options オプション オプション … -- 現在ここで使えるのは * omit_action_call 空のアクション呼び出しを省略する * result_var 変数 result を使う です。 それぞれ no_ を頭につけることで意味を反転できます。 === expect 実用になるパーサはたいてい無害な shift/reduce conflict を含みます。 しかし文法ファイルを書いた本人はそれを知っているからいいですが、 ユーザが文法ファイルを処理した時に「conflict」と表示されたら 不安に思うでしょう。そのような場合、以下のように書いておくと shift/reduce conflict のメッセージを抑制できます。 -- expect 3 -- この場合 shift/reduce conflict はぴったり三つでなければいけません。 三つでない場合はやはり表示が出ます (ゼロでも出ます)。 また reduce/reduce conflict の表示は抑制できません。 === トークンシンボル値の変更 トークンシンボルを表す値は、デフォルトでは * 文法中、引用符でかこまれていないもの (RULEとかXENDとか) →その名前の文字列を intern して得られるシンボル (1.4 では Fixnum) * 引用符でかこまれているもの(':'とか'.'とか) →その文字列そのまま となっていますが、たとえば他の形式のスキャナがすでに存在する場合などは、 これにあわせなければならず、このままでは不便です。このような場合には、 convert 節を加えることで、トークンシンボルを表す値を変えることができます。 以下がその例です。 -- convert PLUS 'PlusClass' #→ PlusClass MIN 'MinusClass' #→ MinusClass end -- デフォルトではトークンシンボル PLUS に対してはトークンシンボル値は :PLUS ですが、上のような記述がある場合は PlusClass になります。 変換後の値は false・nil 以外ならなんでも使えます。 変換後の値として文字列を使うときは、次のように引用符を重ねる必要があります。 -- convert PLUS '"plus"' #→ "plus" end -- また、「'」を使っても生成された Ruby のコード上では「"」になるので 注意してください。バックスラッシュによるクオートは有効ですが、バック スラッシュは消えずにそのまま残ります。 -- PLUS '"plus\n"' #→ "plus\n" MIN "\"minus#{val}\"" #→ \"minus#{val}\" -- === スタート規則 パーサをつくるためには、どの規則が「最初の」規則か、ということを Racc におしえて やらなければいけません。それを明示的に書くのがスタート規則です。スタート規則は 次のように書きます。 -- start real_target -- start は行の最初にこなければいけません。このように書くと、ファイルで 一番最初に出てくる real_target の規則をスタート規則として使います。 省略した場合は、ファイルの最初の規則がスタート規則になります。普通は 最初の規則を一番上にかくほうが書きやすく、わかりやすくなりますから、 この記法はあまりつかう必要はないでしょう。 === ユーザーコード部 ユーザーコードは、パーサクラスが書きこまれるファイルに、 アクションの他にもコードを含めたい時に使います。このようなものは 書きこまれる場所に応じて三つ存在し、パーサクラスの定義の前が header、クラスの定義中(の冒頭)が inner、定義の後が footer です。 ユーザコードとして書いたものは全く手を加えずにそのまま連結されます。 ユーザーコード部の書式は以下の通りです。 -- ---- 識別子 ruby の文 ruby の文 ruby の文 ---- 識別子 ruby の文 : -- 行の先頭から四つ以上連続した「-」(マイナス)があるとユーザーコードと みなされます。識別子は一つの単語で、そのあとには「=」以外なら何を 書いてもかまいません。 racc-1.4.14/rdoc/ja/debug.ja.rdoc0000644000004100000410000000407712625454503016430 0ustar www-datawww-data= パーサのデバッグ ここでは、Racc を使っていくうえで遭遇しそうな問題について書きます。 == 文法ファイルがパースエラーになる エラーメッセージに出ている行番号のあたりを見て間違いを 探してください。ブロックを閉じる行でエラーになる場合は、 どこかで開き括弧などを増やしてしまっている可能性が高いです。 == なんたら conflict って言われた 一番ありがちで一番面倒な問題は衝突 (conflict) でしょう。 文法中に衝突があると、racc はコンパイル後に 「5 shift/reduce conflict」のようなメッセージを表示します。 -v をつけると出力される .output ファイルからはさらに詳しい情報が得られます。 それをどう使うか、とかそういうことに関しては、それなりの本を読んでください。 とてもここに書けるような単純な話ではありません。 当然ながら『Ruby を 256 倍使うための本 無道編』(青木峰郎著)がお勧めです。 == パーサは問題なく生成できたけど予想どおりに動かない racc に -g オプションをつけてパーサを出力すると、デバッグ用のコードが 付加されます。ここで、パーサクラスのインスタンス変数 @yydebug を true に しておいてから do_parse/yyparse を呼ぶと、デバッグ用メッセージが出力 されます。パーサが動作する様子が直接見えますので、完全に現在の状態を 把握できます。これを見てどこがおかしいのかわかったらあとは直すだけ。 == next_token に関して いまだ自分でも忘れることが多いのが 「送るトークンが尽きたら [false,なにか] を送る」ということです。 ちなみに Racc 0.10.2 以降では一度 [false,なにか] を受け取ったら それ以上 next_token は呼ばないことが保証されています。 追記: 最近は [false,なにか] ではなく nil でもよいことになった。 racc-1.4.14/rdoc/ja/parser.ja.rdoc0000644000004100000410000001160712625454503016633 0ustar www-datawww-data= class Racc::Parser Racc の生成するパーサはすべて Racc::Parser クラスを継承します。 Racc::Parser クラスにはパース中に使用するメソッドがいくつかあり、 そのようなメソッドをオーバーロードすると、パーサを初期化したり することができます。 == Super Class Object == Constants プリフィクス "Racc_" がついた定数はパーサの予約定数です。 そのような定数は使わないでください。動作不可能になります。 == Instance Methods ここに載っているもののほか、プリフィクス "racc_" および "_racc_" が ついたメソッドはパーサの予約名です。そのようなメソッドは使わないで ください。 : do_parse -> Object パースを開始します。 また、トークンが必要になった時は #next_token を呼び出します。 -- # Example ---- inner def parse @q = [[1,1], [2,2], [3,3], [false, '$']] do_parse end def next_token @q.shift end -- : next_token -> [Symbol, Object] [abstract method] パーサが次のトークンを読みこむ時に使います。 [記号, その値] の形式の配列を返してください。 記号はデフォルトでは * 文法中、引用符でかこまれていないもの → その名前の文字列のシンボル (例えば :ATOM ) * 引用符でかこまれているもの
→ その文字列そのまま (例えば '=' ) で表します。これを変更する方法については、 文法リファレンスを参照してください。 また、もう送るシンボルがなくなったときには [false, なにか] または nil を返してください。 このメソッドは抽象メソッドなので、#do_parse を使う場合は 必ずパーサクラス中で再定義する必要があります。 定義しないままパースを始めると例外 NotImplementedError が 発生します。 : yyparse( receiver, method_id ) パースを開始します。このメソッドでは始めてトークンが 必要になった時点で receiver に対して method_id メソッドを 呼び出してトークンを得ます。 receiver の method_id メソッドはトークンを yield しなければ なりません。形式は #next_token と同じで [記号, 値] です。 つまり、receiver の method_id メソッドの概形は以下のように なるはずです。 -- def method_id until end_of_file : yield 記号, 値 : end end -- 少し注意が必要なのは、method_id が呼び出されるのは始めて トークンが必要になった時点であるということです。method_id メソッドが呼び出されたときは既にパースが進行中なので、 アクション中で使う変数を method_id の冒頭で初期化すると まず失敗します。 トークンの終端を示す [false, なにか] を渡したらそれ以上は yield しないでください。その場合には例外が発生します。 最後に、method_id メソッドからは必ず yield してください。 しない場合は何が起きるかわかりません。 : on_error( error_token_id, error_value, value_stack ) パーサコアが文法エラーを検出すると呼び出します (yacc の yyerror)。 エラーメッセージを出すなり、例外を発生するなりしてください。 このメソッドから正常に戻った場合、パーサはエラー回復モード に移行します。 error_token はパースエラーを起こした記号の内部表現 (整数) です。 #token_to_str で文法ファイル上の文字列表現に直せます。 error_value はその値です。 value_stack はエラーの時点での値スタックです。 value_stack を変更してはいけません。 on_error のデフォルトの実装は例外 ParseError を発生します。 : token_to_str( t ) -> String Racc トークンの内部表現 (整数) を文法ファイル上の記号表現の文字列に変換します。 t が整数でない場合は TypeError を発生します。 t が範囲外の整数だった場合は nil を返します。 : yyerror エラー回復モードに入ります。このとき #on_error は呼ばれません。 アクション以外からは呼び出さないでください。 : yyerrok エラー回復モードから復帰します。 アクション以外からは呼び出さないでください。 : yyaccept すぐに値スタックの先頭の値を返して #do_parse、#yyparse を抜けます。 racc-1.4.14/rdoc/ja/command.ja.html0000644000004100000410000000660712625454503016776 0ustar www-datawww-data

Raccコマンドリファレンス

racc [-ofilename] [--output-file=filename] [-erubypath] [--embedded=rubypath] [-v] [--verbose] [-Ofilename] [--log-file=filename] [-g] [--debug] [-E] [--embedded] [-l] [--no-line-convert] [-c] [--line-convert-all] [-a] [--no-omit-actions] [-C] [--check-only] [-S] [--output-status] [--version] [--copyright] [--help] grammarfile

filename
Raccの文法ファイルを指定します。拡張子には特に制限はありません。
-ooutfile, --output-file=outfile
作成するクラスをかきこむファイル名を指定します。デフォルトは.tab.rbです。
-Ofilename, --log-file=filename
-v オプションをつけた時に生成するログファイルの名前を filename に変更します。 デフォルトは filename.output です。
-erubypath, --executable=rubypath
実行可能ファイルを生成します。rubypathは Ruby 本体のパスです。 rubypathを単に 'ruby' にした時には Racc が動作している Ruby のパスを使用します。
-v, --verbose
ファイル "filename".output に詳細な解析情報を出力します。
-g, --debug
出力するコードにデバッグ用コードを加えます。-g をつけて生成したパーサで @yydebug を true にセットすると、デバッグ用のコードが出力されます。
-g をつけるだけでは何もおこりませんので注意してください。
-E, --embedded
ランタイムルーチンをすべて含んだコードを生成します。 つまり、このオプションをつけて生成したコードは Ruby さえあれば動きます。
-C, --check-only
(文法ファイルの) 文法のチェックだけをして終了します。
-S, --output-status
進行状況を逐一報告します。
-l, --no-line-convert

Ruby では例外が発生した時のファイル名や行番号を表示してくれますが、 Racc の生成したパーサは、デフォルトではこの場合のファイル名・行番号を 文法ファイルでのものに置きかえます。このフラグはその機能をオフにします。

ruby 1.4.3 以前のバージョンではバグのために定数の参照に失敗する 場合があるので、定数参照に関してなにかおかしいことがおこったらこのフラグを 試してみてください。

-c, --line-convert-all
アクションと inner に加え header footer の行番号も変換します。 header と footer がつながっているような場合には使わないでください。
-a, --no-omit-actions
全てのアクションに対応するメソッド定義と呼び出しを行います。 例えアクションが省略されていても空のメソッドを生成します。
--version
Racc のバージョンを出力して終了します。
--copyright
著作権表示を出力して終了します。
--help
オプションの簡単な説明を出力して終了します。
racc-1.4.14/rdoc/ja/index.ja.html0000644000004100000410000000067112625454503016462 0ustar www-datawww-data

Racc ユーザマニュアル

バージョン 1.4 対応

racc-1.4.14/rdoc/en/0000755000004100000410000000000012625454503014100 5ustar www-datawww-dataracc-1.4.14/rdoc/en/NEWS.en.rdoc0000644000004100000410000001450412625454503016132 0ustar www-datawww-data= NEWS === 1.4.6 * Bugfixes * bin/racc -g option renamed to -t * racc/compiler.rb is removed * '|' is allowed with meta rules * Ruby 1.8.7 compatibility fixes * Ruby 1.9 compatibility fixes === 1.4.5 (2005-11-21) * [FEATURE CHANGE] --no-extensions option was removed. * [fix] racc command should not depend on `raccrt' package. * [fix] --no-omit-actions did not work. * setup.rb 3.4.1. === 1.4.4 (2003-10-12) * document changed. * -all packages does not include amstd and strscan. * setup.rb 3.2.1. === 1.4.3 (2002-11-14) * [fix] reduce ruby 1.8 warnings. === 1.4.2 (2002-01-29) * [new] new option --no-extentions === 1.4.1 (2001-12-02) * now Racc does not depend on amstd library. * update y2racc and racc2y for racc 1.4.1 === 1.4.0 (2001-11-30) * minor version up for checking in runtime library into ruby CVS repositry. * RaccParser, RaccScanner -> GrammarFileParser, GrammarFileScanner * modify typo (grammer -> grammar) === 1.3.12 (2001-11-22) * modify installer bug (thanks Tanaka Akira) * enhance regexp/%-strings/gvar detection in action block === 1.3.11 (2001-08-28) * modify scan error on $' $` $/ etc. === 1.3.10 (2001-08-12) * modify prototype missmatch in cparse.c === 1.3.9 (2001-04-07) * support Ruby 1.4 again. === 1.3.8 (2001-03-17) * output symbol name when error * Racc::Parser#token_to_str === 1.3.7 (2001-02-04) * allow nil for EndOfInput (experimental) * more sample grammar files === 1.3.6 (2001-01-22) * modify cparse.so for static link === 1.3.5 (2001-01-18) * %-string scanning was wrong * new directive "expect" === 1.3.4 (2001-01-11) * cparse: add type checks * cparse: rm depend * cparse: does not pass non-VALUE object to rb_iterate() === 1.3.3 (2000-12-25) * critical bug in generator (from 1.3.1) * racc --runtime-version === 1.3.2 (2000-12-21) * bug with racc -E * package strscan togather (again) === 1.3.1 (2000-12-17) * dynamically determine RE_DUP_MAX * ruby version routine was used always === 1.3.0 (2000-11-30) * can yield(sym,val) from scanner (Parser#yyparse) === 1.2.6 (2000-11-28) * class M::C === 1.2.5 (2000-11-20) * big changes in option; -h -f -p -i -n -c -A are incompatible * support long options * y2racc, racc2y leaves actions as default === 1.2.4 (2000-09-13) * updates installer and documents === 1.2.3 (2000-08-14) * output useless rules and nonterminals (version 2) * nonassoc makes error (never shift/reduce) === 1.2.2 (2000-08-12) * internal changes === 1.2.1 (2000-08-05) * racc2y, y2racc === 1.2.0 (2000-08-02) * uses bison's lookahead algorithm === 1.1.6 (2000-07-25) * new keyword "options" and its parameter "no_result_var" === 1.1.5 (2000-07-21) * [IMPORTANT] change keyword "token" to "convert" * NEW keyword "token" for token declearation === 1.1.4 (2000-07-13) * update installer * samples had bugs === 1.1.3 (2000-06-30) * new option -a; does not omit void action call === 1.1.2 (2000-06-29) * now racc does not use strscan.so * ScanError -> Racc::ScanError, ParseError -> Racc::ParseError * more friendly error messages === 1.1.1 (2000-06-15) * require miss * conflicts were not reported with -v === 1.1.0 (2000-06-12) * use other algolithm for generating state table === 1.0.4 (2000-06-04) * S/R conflict & -v flag causes unexpected exception (reported by Tosh) * output useless nonterminals/rules === 1.0.3 (2000-06-03) * use Array#collect! instead of #filter. === 1.0.2 (2000-05-16) * update installer (setup.rb) === 1.0.1 (2000-05-12) * state.rb: faster lookahead & debug lalr code * refine code * update amstd package (1.7.0) === 1.0.0 (2000-05-06) * version 1.0 === 0.14.6 (2000-05-05) * much more debug output === 0.14.5 (2000-05-01) === 0.14.4 (2000-04-09) * Racc_* are included in Racc_arg * faster state generation (a little) === 0.14.3 (2000-04-04) * check both of SYM2ID and ID2SYM (thanks Katsuyuki Komatsu) === 0.14.2 (2000-04-03) * "class" on first line causes parse error (thanks Yoshiki Wada) * new option "racc -V" === 0.14.1 (2000-03-31) === 0.14.0 (2000-03-21) * implement "fast" table (same to bison) * stop line no. conversion temporaliry because of ruby bug === 0.13.1 (2000-03-21) * racc --version --copyright did not work (thanks Tadayoshi Funaba) === 0.13.0 (2000-03-20) * implement yyerror/yyerrok/yyaccept === 0.12.2 (2000-03-19) * -E flag had bug === 0.12.1 (2000-03-16) * modify the way to decide default action === 0.12.0 (2000-03-15) * implement real LALR * use both SLR and LALR to resolve conflicts === 0.11.3 (2000-03-09) * modify lookahead routine again === 0.11.2 (2000-03-09) * bug in lookahead routine * modify cparse.so for Symbol class of ruby 1.5 === 0.11.1 (2000-03-08) * modify for Symbol * update strscan === 0.11.0 (2000-02-19) * if error is occured in action, ruby print line number of grammar file === 0.10.9 (2000-01-19) * change package/setup === 0.10.8 (2000-01-03) * (1-17 re-packed) add/modify documents === 0.10.7 (2000-01-03) * modify setup.rb, compile.rb, amstd/inst. (thanks: Koji Arai) === 0.10.6 (1999-12-24) * racc -e ruby * omit void action call === 0.10.5 (1999-12-21) * critical bug in embedded action implement * bug in setup.rb * modify calc[2].y for 0.10 === 0.10.4 (1999-12-19) * support error recover ('error' token) * can embed runtime by "racc -E" * Racc is module === 0.10.3 (1999-12-01) * support embedded action * modify .output bug === 0.10.2 (1999-11-27) * update document * separate libracc.rb === 0.10.1 (1999-11-19) * rewrite runtime routine in C * once next_token returns [false, *], not call next_token * action is only default, not call next_token * $end is obsolute * LALRactionTable === 0.10.0 (1999-11-06) * next_value, peep_token is obsolute * @__debug__ -> @yydebug * class...rule...end * refine libracc.rb * unify strscan library * *.rb are installed in lib/ruby/VERSION/racc/ === 0.9.5 (1999-10-03) * too few arguments for __show_stack__ * could not scan $end * typo in d.format.rb === 0.9.4 (1999-09-??) === 0.9.3 (1999-09-03) === 0.9.2 (1999-06-26) === 0.9.1 (1999-06-08) === 0.9.0 (1999-06-03) === 0.8.11 (?) === 0.8.10 (?) === 0.8.9 (1999-03-21) === 0.8.8 (1999-03-20) === 0.8.7 (1999-03-01) === 0.8.0 (1999-01-16) === 0.5.0 (1999-01-07) === 0.1.0 (1999-01-01) racc-1.4.14/rdoc/en/grammar.en.rdoc0000644000004100000410000001153312625454503017003 0ustar www-datawww-data= Racc Grammar File Reference == Global Structure == Class Block and User Code Block There's two block on toplevel. one is 'class' block, another is 'user code' block. 'user code' block MUST places after 'class' block. == Comment You can insert comment about all places. Two style comment can be used, Ruby style (#.....) and C style (/*......*/) . == Class Block The class block is formed like this: -- class CLASS_NAME [precedance table] [token declearations] [expected number of S/R conflict] [options] [semantic value convertion] [start rule] rule GRAMMARS -- CLASS_NAME is a name of parser class. This is the name of generating parser class. If CLASS_NAME includes '::', Racc outputs module clause. For example, writing "class M::C" causes creating the code bellow: -- module M class C : : end end -- == Grammar Block The grammar block discripts grammar which is able to be understood by parser. Syntax is: -- (token): (token) (token) (token).... (action) (token): (token) (token) (token).... (action) | (token) (token) (token).... (action) | (token) (token) (token).... (action) -- (action) is an action which is executed when its (token)s are found. (action) is a ruby code block, which is surrounded by braces: -- { print val[0] puts val[1] } -- Note that you cannot use '%' string, here document, '%r' regexp in action. Actions can be omitted. When it is omitted, '' (empty string) is used. A return value of action is a value of left side value ($$). It is value of result, or returned value by "return" statement. Here is an example of whole grammar block. -- rule goal: definition ruls source { result = val } definition: /* none */ { result = [] } | definition startdesig { result[0] = val[1] } | definition precrule # this line continue from upper line { result[1] = val[1] } startdesig: START TOKEN -- You can use following special local variables in action. * result ($$) The value of left-hand side (lhs). A default value is val[0]. * val ($1,$2,$3...) An array of value of right-hand side (rhs). * _values (...$-2,$-1,$0) A stack of values. DO NOT MODIFY this stack unless you know what you are doing. == Operator Precedance This function is equal to '%prec' in yacc. To designate this block: -- prechigh nonassoc '++' left '*' '/' left '+' '-' right '=' preclow -- `right' is yacc's %right, `left' is yacc's %left. `=' + (symbol) means yacc's %prec: -- prechigh nonassoc UMINUS left '*' '/' left '+' '-' preclow rule exp: exp '*' exp | exp '-' exp | '-' exp =UMINUS # equals to "%prec UMINUS" : : -- == expect Racc has bison's "expect" directive. -- # Example class MyParser rule expect 3 : : -- This directive declears "expected" number of shift/reduce conflict. If "expected" number is equal to real number of conflicts, racc does not print confliction warning message. == Declaring Tokens By declaring tokens, you can avoid many meanless bugs. If decleared token does not exist/existing token does not decleared, Racc output warnings. Declearation syntax is: -- token TOKEN_NAME AND_IS_THIS ALSO_THIS_IS AGAIN_AND_AGAIN THIS_IS_LAST -- == Options You can write options for racc command in your racc file. -- options OPTION OPTION ... -- Options are: * omit_action_call omit empty action call or not. * result_var use/does not use local variable "result" You can use 'no_' prefix to invert its meanings. == Converting Token Symbol Token symbols are, as default, * naked token string in racc file (TOK, XFILE, this_is_token, ...) --> symbol (:TOK, :XFILE, :this_is_token, ...) * quoted string (':', '.', '(', ...) --> same string (':', '.', '(', ...) You can change this default by "convert" block. Here is an example: -- convert PLUS 'PlusClass' # We use PlusClass for symbol of `PLUS' MIN 'MinusClass' # We use MinusClass for symbol of `MIN' end -- We can use almost all ruby value can be used by token symbol, except 'false' and 'nil'. These are causes unexpected parse error. If you want to use String as token symbol, special care is required. For example: -- convert class '"cls"' # in code, "cls" PLUS '"plus\n"' # in code, "plus\n" MIN "\"minus#{val}\"" # in code, \"minus#{val}\" end -- == Start Rule '%start' in yacc. This changes start rule. -- start real_target -- This statement will not be used forever, I think. == User Code Block "User Code Block" is a Ruby source code which is copied to output. There are three user code block, "header" "inner" and "footer". Format of user code is like this: -- ---- header ruby statement ruby statement ruby statement ---- inner ruby statement : : -- If four '-' exist on line head, racc treat it as beginning of user code block. A name of user code must be one word. racc-1.4.14/README.rdoc0000644000004100000410000000361012625454503014355 0ustar www-datawww-data= Racc * http://i.loveruby.net/en/projects/racc/ * http://racc.rubyforge.org/ == DESCRIPTION: Racc is a LALR(1) parser generator. It is written in Ruby itself, and generates Ruby program. NOTE: Ruby 1.8.x comes with Racc runtime module. You can run your parsers generated by racc 1.4.x out of the box. == Requirement * Ruby 1.8.x or later. (*) make and C compiler. == Installation gem install: $ gem install racc setup.rb install: Type this in the top directory of the extracted archive: $ ruby setup.rb config $ ruby setup.rb setup ($ su) # ruby setup.rb install You can install Racc into your favorite directory by giving options to setup.rb. e.g. $ ruby setup.rb config --prefix=/usr For details, try "ruby setup.rb --help". If you don't have C Compiler ---------------------------- You can install Racc without C compilers. Type following command in config phase. $ ruby setup.rb config --without-ext == Testing Racc Racc comes with simple calculator. To compile this, on shell: $ racc -o calc calc.y This process costs few seconds (or less). Then type: $ ruby calc ... Does it works? For details of Racc, see HTML documents placed under 'doc.en/' and sample grammer files under 'sample/'. == License Racc is distributed under the terms of the GNU Lesser General Public License version 2. Note that you do NOT need to follow LGPL for your own parser (racc outputs). You can provide those files under any licenses you want. == Bug Reports Any kind of bug reports are welcome. If you find a bug of Racc, please email me. Your grammer file, debug output genereted by "racc -g", are helpful. Minero Aoki aamine@loveruby.net http://i.loveruby.net racc-1.4.14/DEPENDS0000644000004100000410000000007412625454503013555 0ustar www-datawww-dataracc: raccruntime raccruntime: y2racc: strscan racc2y: racc racc-1.4.14/lib/0000755000004100000410000000000012625454503013315 5ustar www-datawww-dataracc-1.4.14/lib/racc.rb0000644000004100000410000000021112625454503014544 0ustar www-datawww-datarequire 'racc/compat' require 'racc/debugflags' require 'racc/grammar' require 'racc/state' require 'racc/exception' require 'racc/info' racc-1.4.14/lib/racc/0000755000004100000410000000000012625454503014225 5ustar www-datawww-dataracc-1.4.14/lib/racc/grammar.rb0000644000004100000410000005433312625454503016210 0ustar www-datawww-data# # $Id: 733f7084ff1dca50994cba55c8a05aba8d82eb31 $ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of the GNU LGPL, see the file "COPYING". # require 'racc/compat' require 'racc/iset' require 'racc/sourcetext' require 'racc/logfilegenerator' require 'racc/exception' require 'forwardable' module Racc class Grammar def initialize(debug_flags = DebugFlags.new) @symboltable = SymbolTable.new @debug_symbol = debug_flags.token @rules = [] # :: [Rule] @start = nil @n_expected_srconflicts = nil @prec_table = [] @prec_table_closed = false @closed = false @states = nil end attr_reader :start attr_reader :symboltable attr_accessor :n_expected_srconflicts def [](x) @rules[x] end def each_rule(&block) @rules.each(&block) end alias each each_rule def each_index(&block) @rules.each_index(&block) end def each_with_index(&block) @rules.each_with_index(&block) end def size @rules.size end def to_s "" end extend Forwardable def_delegator "@symboltable", :each, :each_symbol def_delegator "@symboltable", :each_terminal def_delegator "@symboltable", :each_nonterminal def intern(value, dummy = false) @symboltable.intern(value, dummy) end def symbols @symboltable.symbols end def nonterminal_base @symboltable.nt_base end def useless_nonterminal_exist? n_useless_nonterminals() != 0 end def n_useless_nonterminals @n_useless_nonterminals ||= begin n = 0 @symboltable.each_nonterminal do |sym| n += 1 if sym.useless? end n end end def useless_rule_exist? n_useless_rules() != 0 end def n_useless_rules @n_useless_rules ||= begin n = 0 each do |r| n += 1 if r.useless? end n end end def nfa (@states ||= States.new(self)).nfa end def dfa (@states ||= States.new(self)).dfa end alias states dfa def state_transition_table states().state_transition_table end def parser_class states = states() # cache if $DEBUG srcfilename = caller(1).first.slice(/\A(.*?):/, 1) begin write_log srcfilename + ".output" rescue SystemCallError end report = lambda {|s| $stderr.puts "racc: #{srcfilename}: #{s}" } if states.should_report_srconflict? report["#{states.n_srconflicts} shift/reduce conflicts"] end if states.rrconflict_exist? report["#{states.n_rrconflicts} reduce/reduce conflicts"] end g = states.grammar if g.useless_nonterminal_exist? report["#{g.n_useless_nonterminals} useless nonterminals"] end if g.useless_rule_exist? report["#{g.n_useless_rules} useless rules"] end end states.state_transition_table.parser_class end def write_log(path) File.open(path, 'w') {|f| LogFileGenerator.new(states()).output f } end # # Grammar Definition Interface # def add(rule) raise ArgumentError, "rule added after the Grammar closed" if @closed @rules.push rule end def added?(sym) @rules.detect {|r| r.target == sym } end def start_symbol=(s) raise CompileError, "start symbol set twice'" if @start @start = s end def declare_precedence(assoc, syms) raise CompileError, "precedence table defined twice" if @prec_table_closed @prec_table.push [assoc, syms] end def end_precedence_declaration(reverse) @prec_table_closed = true return if @prec_table.empty? table = reverse ? @prec_table.reverse : @prec_table table.each_with_index do |(assoc, syms), idx| syms.each do |sym| sym.assoc = assoc sym.precedence = idx end end end # # Dynamic Generation Interface # def Grammar.define(&block) env = DefinitionEnv.new env.instance_eval(&block) env.grammar end class DefinitionEnv def initialize @grammar = Grammar.new @seqs = Hash.new(0) @delayed = [] end def grammar flush_delayed @grammar.each do |rule| if rule.specified_prec rule.specified_prec = @grammar.intern(rule.specified_prec) end end @grammar.init @grammar end def precedence_table(&block) env = PrecedenceDefinitionEnv.new(@grammar) env.instance_eval(&block) @grammar.end_precedence_declaration env.reverse end def method_missing(mid, *args, &block) unless mid.to_s[-1,1] == '=' super # raises NoMethodError end target = @grammar.intern(mid.to_s.chop.intern) unless args.size == 1 raise ArgumentError, "too many arguments for #{mid} (#{args.size} for 1)" end _add target, args.first end def _add(target, x) case x when Sym @delayed.each do |rule| rule.replace x, target if rule.target == x end @grammar.symboltable.delete x else x.each_rule do |r| r.target = target @grammar.add r end end flush_delayed end def _delayed_add(rule) @delayed.push rule end def _added?(sym) @grammar.added?(sym) or @delayed.detect {|r| r.target == sym } end def flush_delayed return if @delayed.empty? @delayed.each do |rule| @grammar.add rule end @delayed.clear end def seq(*list, &block) Rule.new(nil, list.map {|x| _intern(x) }, UserAction.proc(block)) end def null(&block) seq(&block) end def action(&block) id = "@#{@seqs["action"] += 1}".intern _delayed_add Rule.new(@grammar.intern(id), [], UserAction.proc(block)) id end alias _ action def option(sym, default = nil, &block) _defmetasyntax("option", _intern(sym), block) {|target| seq() { default } | seq(sym) } end def many(sym, &block) _defmetasyntax("many", _intern(sym), block) {|target| seq() { [] }\ | seq(target, sym) {|list, x| list.push x; list } } end def many1(sym, &block) _defmetasyntax("many1", _intern(sym), block) {|target| seq(sym) {|x| [x] }\ | seq(target, sym) {|list, x| list.push x; list } } end def separated_by(sep, sym, &block) option(separated_by1(sep, sym), [], &block) end def separated_by1(sep, sym, &block) _defmetasyntax("separated_by1", _intern(sym), block) {|target| seq(sym) {|x| [x] }\ | seq(target, sep, sym) {|list, _, x| list.push x; list } } end def _intern(x) case x when Symbol, String @grammar.intern(x) when Racc::Sym x else raise TypeError, "wrong type #{x.class} (expected Symbol/String/Racc::Sym)" end end private def _defmetasyntax(type, id, action, &block) if action idbase = "#{type}@#{id}-#{@seqs[type] += 1}" target = _wrap(idbase, "#{idbase}-core", action) _regist("#{idbase}-core", &block) else target = _regist("#{type}@#{id}", &block) end @grammar.intern(target) end def _regist(target_name) target = target_name.intern unless _added?(@grammar.intern(target)) yield(target).each_rule do |rule| rule.target = @grammar.intern(target) _delayed_add rule end end target end def _wrap(target_name, sym, block) target = target_name.intern _delayed_add Rule.new(@grammar.intern(target), [@grammar.intern(sym.intern)], UserAction.proc(block)) target end end class PrecedenceDefinitionEnv def initialize(g) @grammar = g @prechigh_seen = false @preclow_seen = false @reverse = false end attr_reader :reverse def higher if @prechigh_seen raise CompileError, "prechigh used twice" end @prechigh_seen = true end def lower if @preclow_seen raise CompileError, "preclow used twice" end if @prechigh_seen @reverse = true end @preclow_seen = true end def left(*syms) @grammar.declare_precedence :Left, syms.map {|s| @grammar.intern(s) } end def right(*syms) @grammar.declare_precedence :Right, syms.map {|s| @grammar.intern(s) } end def nonassoc(*syms) @grammar.declare_precedence :Nonassoc, syms.map {|s| @grammar.intern(s)} end end # # Computation # def init return if @closed @closed = true @start ||= @rules.map {|r| r.target }.detect {|sym| not sym.dummy? } raise CompileError, 'no rule in input' if @rules.empty? add_start_rule @rules.freeze fix_ident compute_hash compute_heads determine_terminals compute_nullable_0 @symboltable.fix compute_locate @symboltable.each_nonterminal {|t| compute_expand t } compute_nullable compute_useless end private def add_start_rule r = Rule.new(@symboltable.dummy, [@start, @symboltable.anchor, @symboltable.anchor], UserAction.empty) r.ident = 0 r.hash = 0 r.precedence = nil @rules.unshift r end # Rule#ident # LocationPointer#ident def fix_ident @rules.each_with_index do |rule, idx| rule.ident = idx end end # Rule#hash def compute_hash hash = 4 # size of dummy rule @rules.each do |rule| rule.hash = hash hash += (rule.size + 1) end end # Sym#heads def compute_heads @rules.each do |rule| rule.target.heads.push rule.ptrs[0] end end # Sym#terminal? def determine_terminals @symboltable.each do |s| s.term = s.heads.empty? end end # Sym#self_null? def compute_nullable_0 @symboltable.each do |s| if s.terminal? s.snull = false else s.snull = s.heads.any? {|loc| loc.reduce? } end end end # Sym#locate def compute_locate @rules.each do |rule| t = nil rule.ptrs.each do |ptr| unless ptr.reduce? tok = ptr.dereference tok.locate.push ptr t = tok if tok.terminal? end end rule.precedence = t end end # Sym#expand def compute_expand(t) puts "expand> #{t.to_s}" if @debug_symbol t.expand = _compute_expand(t, ISet.new, []) puts "expand< #{t.to_s}: #{t.expand.to_s}" if @debug_symbol end def _compute_expand(t, set, lock) if tmp = t.expand set.update tmp return set end tok = nil set.update_a t.heads t.heads.each do |ptr| tok = ptr.dereference if tok and tok.nonterminal? unless lock[tok.ident] lock[tok.ident] = true _compute_expand tok, set, lock end end end set end # Sym#nullable?, Rule#nullable? def compute_nullable @rules.each {|r| r.null = false } @symboltable.each {|t| t.null = false } r = @rules.dup s = @symboltable.nonterminals begin rs = r.size ss = s.size check_rules_nullable r check_symbols_nullable s end until rs == r.size and ss == s.size end def check_rules_nullable(rules) rules.delete_if do |rule| rule.null = true rule.symbols.each do |t| unless t.nullable? rule.null = false break end end rule.nullable? end end def check_symbols_nullable(symbols) symbols.delete_if do |sym| sym.heads.each do |ptr| if ptr.rule.nullable? sym.null = true break end end sym.nullable? end end # Sym#useless?, Rule#useless? # FIXME: what means "useless"? def compute_useless @symboltable.each_terminal {|sym| sym.useless = false } @symboltable.each_nonterminal {|sym| sym.useless = true } @rules.each {|rule| rule.useless = true } r = @rules.dup s = @symboltable.nonterminals begin rs = r.size ss = s.size check_rules_useless r check_symbols_useless s end until r.size == rs and s.size == ss end def check_rules_useless(rules) rules.delete_if do |rule| rule.useless = false rule.symbols.each do |sym| if sym.useless? rule.useless = true break end end not rule.useless? end end def check_symbols_useless(s) s.delete_if do |t| t.heads.each do |ptr| unless ptr.rule.useless? t.useless = false break end end not t.useless? end end end # class Grammar class Rule def initialize(target, syms, act) @target = target @symbols = syms @action = act @alternatives = [] @ident = nil @hash = nil @ptrs = nil @precedence = nil @specified_prec = nil @null = nil @useless = nil end attr_accessor :target attr_reader :symbols attr_reader :action def |(x) @alternatives.push x.rule self end def rule self end def each_rule(&block) yield self @alternatives.each(&block) end attr_accessor :ident attr_reader :hash attr_reader :ptrs def hash=(n) @hash = n ptrs = [] @symbols.each_with_index do |sym, idx| ptrs.push LocationPointer.new(self, idx, sym) end ptrs.push LocationPointer.new(self, @symbols.size, nil) @ptrs = ptrs end def precedence @specified_prec || @precedence end def precedence=(sym) @precedence ||= sym end def prec(sym, &block) @specified_prec = sym if block unless @action.empty? raise CompileError, 'both of rule action block and prec block given' end @action = UserAction.proc(block) end self end attr_accessor :specified_prec def nullable?() @null end def null=(n) @null = n end def useless?() @useless end def useless=(u) @useless = u end def inspect "#" end def ==(other) other.kind_of?(Rule) and @ident == other.ident end def [](idx) @symbols[idx] end def size @symbols.size end def empty? @symbols.empty? end def to_s "#" end def accept? if tok = @symbols[-1] tok.anchor? else false end end def each(&block) @symbols.each(&block) end def replace(src, dest) @target = dest @symbols = @symbols.map {|s| s == src ? dest : s } end end # class Rule class UserAction def UserAction.source_text(src) new(src, nil) end def UserAction.proc(pr = nil, &block) if pr and block raise ArgumentError, "both of argument and block given" end new(nil, pr || block) end def UserAction.empty new(nil, nil) end private_class_method :new def initialize(src, proc) @source = src @proc = proc end attr_reader :source attr_reader :proc def source? not @proc end def proc? not @source end def empty? not @proc and not @source end def name "{action type=#{@source || @proc || 'nil'}}" end alias inspect name end class OrMark def initialize(lineno) @lineno = lineno end def name '|' end alias inspect name attr_reader :lineno end class Prec def initialize(symbol, lineno) @symbol = symbol @lineno = lineno end def name "=#{@symbol}" end alias inspect name attr_reader :symbol attr_reader :lineno end # # A set of rule and position in it's RHS. # Note that the number of pointers is more than rule's RHS array, # because pointer points right edge of the final symbol when reducing. # class LocationPointer def initialize(rule, i, sym) @rule = rule @index = i @symbol = sym @ident = @rule.hash + i @reduce = sym.nil? end attr_reader :rule attr_reader :index attr_reader :symbol alias dereference symbol attr_reader :ident alias hash ident attr_reader :reduce alias reduce? reduce def to_s sprintf('(%d,%d %s)', @rule.ident, @index, (reduce?() ? '#' : @symbol.to_s)) end alias inspect to_s def eql?(ot) @hash == ot.hash end alias == eql? def head? @index == 0 end def next @rule.ptrs[@index + 1] or ptr_bug! end alias increment next def before(len) @rule.ptrs[@index - len] or ptr_bug! end private def ptr_bug! raise "racc: fatal: pointer not exist: self: #{to_s}" end end # class LocationPointer class SymbolTable include Enumerable def initialize @symbols = [] # :: [Racc::Sym] @cache = {} # :: {(String|Symbol) => Racc::Sym} @dummy = intern(:$start, true) @anchor = intern(false, true) # Symbol ID = 0 @error = intern(:error, false) # Symbol ID = 1 end attr_reader :dummy attr_reader :anchor attr_reader :error def [](id) @symbols[id] end def intern(val, dummy = false) @cache[val] ||= begin sym = Sym.new(val, dummy) @symbols.push sym sym end end attr_reader :symbols alias to_a symbols def delete(sym) @symbols.delete sym @cache.delete sym.value end attr_reader :nt_base def nt_max @symbols.size end def each(&block) @symbols.each(&block) end def terminals(&block) @symbols[0, @nt_base] end def each_terminal(&block) @terms.each(&block) end def nonterminals @symbols[@nt_base, @symbols.size - @nt_base] end def each_nonterminal(&block) @nterms.each(&block) end def fix terms, nterms = @symbols.partition {|s| s.terminal? } @symbols = terms + nterms @terms = terms @nterms = nterms @nt_base = terms.size fix_ident check_terminals end private def fix_ident @symbols.each_with_index do |t, i| t.ident = i end end def check_terminals return unless @symbols.any? {|s| s.should_terminal? } @anchor.should_terminal @error.should_terminal each_terminal do |t| t.should_terminal if t.string_symbol? end each do |s| s.should_terminal if s.assoc end terminals().reject {|t| t.should_terminal? }.each do |t| raise CompileError, "terminal #{t} not declared as terminal" end nonterminals().select {|n| n.should_terminal? }.each do |n| raise CompileError, "symbol #{n} declared as terminal but is not terminal" end end end # class SymbolTable # Stands terminal and nonterminal symbols. class Sym def initialize(value, dummyp) @ident = nil @value = value @dummyp = dummyp @term = nil @nterm = nil @should_terminal = false @precedence = nil case value when Symbol @to_s = value.to_s @serialized = value.inspect @string = false when String @to_s = value.inspect @serialized = value.dump @string = true when false @to_s = '$end' @serialized = 'false' @string = false when ErrorSymbolValue @to_s = 'error' @serialized = 'Object.new' @string = false else raise ArgumentError, "unknown symbol value: #{value.class}" end @heads = [] @locate = [] @snull = nil @null = nil @expand = nil @useless = nil end class << self def once_writer(nm) nm = nm.id2name module_eval(<<-EOS) def #{nm}=(v) raise 'racc: fatal: @#{nm} != nil' unless @#{nm}.nil? @#{nm} = v end EOS end end once_writer :ident attr_reader :ident alias hash ident attr_reader :value def dummy? @dummyp end def terminal? @term end def nonterminal? @nterm end def term=(t) raise 'racc: fatal: term= called twice' unless @term.nil? @term = t @nterm = !t end def should_terminal @should_terminal = true end def should_terminal? @should_terminal end def string_symbol? @string end def serialize @serialized end attr_writer :serialized attr_accessor :precedence attr_accessor :assoc def to_s @to_s.dup end alias inspect to_s def |(x) rule() | x.rule end def rule Rule.new(nil, [self], UserAction.empty) end # # cache # attr_reader :heads attr_reader :locate def self_null? @snull end once_writer :snull def nullable? @null end def null=(n) @null = n end attr_reader :expand once_writer :expand def useless? @useless end def useless=(f) @useless = f end end # class Sym end # module Racc racc-1.4.14/lib/racc/debugflags.rb0000644000004100000410000000263212625454503016660 0ustar www-datawww-data# # $Id: 74ff4369ce53c7f45cfc2644ce907785104ebf6e $ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of LGPL, see the file "COPYING". # module Racc class DebugFlags def DebugFlags.parse_option_string(s) parse = rule = token = state = la = prec = conf = false s.split(//).each do |ch| case ch when 'p' then parse = true when 'r' then rule = true when 't' then token = true when 's' then state = true when 'l' then la = true when 'c' then prec = true when 'o' then conf = true else raise "unknown debug flag char: #{ch.inspect}" end end new(parse, rule, token, state, la, prec, conf) end def initialize(parse = false, rule = false, token = false, state = false, la = false, prec = false, conf = false) @parse = parse @rule = rule @token = token @state = state @la = la @prec = prec @any = (parse || rule || token || state || la || prec) @status_logging = conf end attr_reader :parse attr_reader :rule attr_reader :token attr_reader :state attr_reader :la attr_reader :prec def any? @any end attr_reader :status_logging end end racc-1.4.14/lib/racc/grammarfileparser.rb0000644000004100000410000003560112625454503020262 0ustar www-datawww-data# # $Id: 5e1871defa15d288d2252e6a76bb2c4cf2119ed3 $ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of the GNU LGPL, see the file "COPYING". # require 'racc' require 'racc/compat' require 'racc/grammar' require 'racc/parserfilegenerator' require 'racc/sourcetext' require 'stringio' module Racc grammar = Grammar.define { g = self g.class = seq(:CLASS, :cname, many(:param), :RULE, :rules, option(:END)) g.cname = seq(:rubyconst) {|name| @result.params.classname = name }\ | seq(:rubyconst, "<", :rubyconst) {|c, _, s| @result.params.classname = c @result.params.superclass = s } g.rubyconst = separated_by1(:colon2, :SYMBOL) {|syms| syms.map {|s| s.to_s }.join('::') } g.colon2 = seq(':', ':') g.param = seq(:CONV, many1(:convdef), :END) {|*| #@grammar.end_convert_block # FIXME }\ | seq(:PRECHIGH, many1(:precdef), :PRECLOW) {|*| @grammar.end_precedence_declaration true }\ | seq(:PRECLOW, many1(:precdef), :PRECHIGH) {|*| @grammar.end_precedence_declaration false }\ | seq(:START, :symbol) {|_, sym| @grammar.start_symbol = sym }\ | seq(:TOKEN, :symbols) {|_, syms| syms.each do |s| s.should_terminal end }\ | seq(:OPTION, :options) {|_, syms| syms.each do |opt| case opt when 'result_var' @result.params.result_var = true when 'no_result_var' @result.params.result_var = false when 'omit_action_call' @result.params.omit_action_call = true when 'no_omit_action_call' @result.params.omit_action_call = false else raise CompileError, "unknown option: #{opt}" end end }\ | seq(:EXPECT, :DIGIT) {|_, num| if @grammar.n_expected_srconflicts raise CompileError, "`expect' seen twice" end @grammar.n_expected_srconflicts = num } g.convdef = seq(:symbol, :STRING) {|sym, code| sym.serialized = code } g.precdef = seq(:LEFT, :symbols) {|_, syms| @grammar.declare_precedence :Left, syms }\ | seq(:RIGHT, :symbols) {|_, syms| @grammar.declare_precedence :Right, syms }\ | seq(:NONASSOC, :symbols) {|_, syms| @grammar.declare_precedence :Nonassoc, syms } g.symbols = seq(:symbol) {|sym| [sym] }\ | seq(:symbols, :symbol) {|list, sym| list.push sym list }\ | seq(:symbols, "|") g.symbol = seq(:SYMBOL) {|sym| @grammar.intern(sym) }\ | seq(:STRING) {|str| @grammar.intern(str) } g.options = many(:SYMBOL) {|syms| syms.map {|s| s.to_s } } g.rules = option(:rules_core) {|list| add_rule_block list unless list.empty? nil } g.rules_core = seq(:symbol) {|sym| [sym] }\ | seq(:rules_core, :rule_item) {|list, i| list.push i list }\ | seq(:rules_core, ';') {|list, *| add_rule_block list unless list.empty? list.clear list }\ | seq(:rules_core, ':') {|list, *| next_target = list.pop add_rule_block list unless list.empty? [next_target] } g.rule_item = seq(:symbol)\ | seq("|") {|*| OrMark.new(@scanner.lineno) }\ | seq("=", :symbol) {|_, sym| Prec.new(sym, @scanner.lineno) }\ | seq(:ACTION) {|src| UserAction.source_text(src) } } GrammarFileParser = grammar.parser_class if grammar.states.srconflict_exist? raise 'Racc boot script fatal: S/R conflict in build' end if grammar.states.rrconflict_exist? raise 'Racc boot script fatal: R/R conflict in build' end class GrammarFileParser # reopen class Result def initialize(grammar) @grammar = grammar @params = ParserFileGenerator::Params.new end attr_reader :grammar attr_reader :params end def GrammarFileParser.parse_file(filename) parse(File.read(filename), filename, 1) end def GrammarFileParser.parse(src, filename = '-', lineno = 1) new().parse(src, filename, lineno) end def initialize(debug_flags = DebugFlags.new) @yydebug = debug_flags.parse end def parse(src, filename = '-', lineno = 1) @filename = filename @lineno = lineno @scanner = GrammarFileScanner.new(src, @filename) @scanner.debug = @yydebug @grammar = Grammar.new @result = Result.new(@grammar) @embedded_action_seq = 0 yyparse @scanner, :yylex parse_user_code @result.grammar.init @result end private def next_token @scanner.scan end def on_error(tok, val, _values) if val.respond_to?(:id2name) v = val.id2name elsif val.kind_of?(String) v = val else v = val.inspect end raise CompileError, "#{location()}: unexpected token '#{v}'" end def location "#{@filename}:#{@lineno - 1 + @scanner.lineno}" end def add_rule_block(list) sprec = nil target = list.shift case target when OrMark, UserAction, Prec raise CompileError, "#{target.lineno}: unexpected symbol #{target.name}" end curr = [] list.each do |i| case i when OrMark add_rule target, curr, sprec curr = [] sprec = nil when Prec raise CompileError, "'=' used twice in one rule" if sprec sprec = i.symbol else curr.push i end end add_rule target, curr, sprec end def add_rule(target, list, sprec) if list.last.kind_of?(UserAction) act = list.pop else act = UserAction.empty end list.map! {|s| s.kind_of?(UserAction) ? embedded_action(s) : s } rule = Rule.new(target, list, act) rule.specified_prec = sprec @grammar.add rule end def embedded_action(act) sym = @grammar.intern("@#{@embedded_action_seq += 1}".intern, true) @grammar.add Rule.new(sym, [], act) sym end # # User Code Block # def parse_user_code line = @scanner.lineno _, *blocks = *@scanner.epilogue.split(/^----/) blocks.each do |block| header, *body = block.lines.to_a label0, pathes = *header.sub(/\A-+/, '').split('=', 2) label = canonical_label(label0) (pathes ? pathes.strip.split(' ') : []).each do |path| add_user_code label, SourceText.new(File.read(path), path, 1) end add_user_code label, SourceText.new(body.join(''), @filename, line + 1) line += (1 + body.size) end end USER_CODE_LABELS = { 'header' => :header, 'prepare' => :header, # obsolete 'inner' => :inner, 'footer' => :footer, 'driver' => :footer # obsolete } def canonical_label(src) label = src.to_s.strip.downcase.slice(/\w+/) unless USER_CODE_LABELS.key?(label) raise CompileError, "unknown user code type: #{label.inspect}" end label end def add_user_code(label, src) @result.params.send(USER_CODE_LABELS[label]).push src end end class GrammarFileScanner def initialize(str, filename = '-') @lines = str.split(/\n|\r\n|\r/) @filename = filename @lineno = -1 @line_head = true @in_rule_blk = false @in_conv_blk = false @in_block = nil @epilogue = '' @debug = false next_line end attr_reader :epilogue def lineno @lineno + 1 end attr_accessor :debug def yylex(&block) unless @debug yylex0(&block) else yylex0 do |sym, tok| $stderr.printf "%7d %-10s %s\n", lineno(), sym.inspect, tok.inspect yield [sym, tok] end end end private def yylex0 begin until @line.empty? @line.sub!(/\A\s+/, '') if /\A\#/ =~ @line break elsif /\A\/\*/ =~ @line skip_comment elsif s = reads(/\A[a-zA-Z_]\w*/) yield [atom_symbol(s), s.intern] elsif s = reads(/\A\d+/) yield [:DIGIT, s.to_i] elsif ch = reads(/\A./) case ch when '"', "'" yield [:STRING, eval(scan_quoted(ch))] when '{' lineno = lineno() yield [:ACTION, SourceText.new(scan_action(), @filename, lineno)] else if ch == '|' @line_head = false end yield [ch, ch] end else end end end while next_line() yield nil end def next_line @lineno += 1 @line = @lines[@lineno] if not @line or /\A----/ =~ @line @epilogue = @lines.join("\n") @lines.clear @line = nil if @in_block @lineno -= 1 scan_error! sprintf('unterminated %s', @in_block) end false else @line.sub!(/(?:\n|\r\n|\r)\z/, '') @line_head = true true end end ReservedWord = { 'right' => :RIGHT, 'left' => :LEFT, 'nonassoc' => :NONASSOC, 'preclow' => :PRECLOW, 'prechigh' => :PRECHIGH, 'token' => :TOKEN, 'convert' => :CONV, 'options' => :OPTION, 'start' => :START, 'expect' => :EXPECT, 'class' => :CLASS, 'rule' => :RULE, 'end' => :END } def atom_symbol(token) if token == 'end' symbol = :END @in_conv_blk = false @in_rule_blk = false else if @line_head and not @in_conv_blk and not @in_rule_blk symbol = ReservedWord[token] || :SYMBOL else symbol = :SYMBOL end case symbol when :RULE then @in_rule_blk = true when :CONV then @in_conv_blk = true end end @line_head = false symbol end def skip_comment @in_block = 'comment' until m = /\*\//.match(@line) next_line end @line = m.post_match @in_block = nil end $raccs_print_type = false def scan_action buf = '' nest = 1 pre = nil @in_block = 'action' begin pre = nil if s = reads(/\A\s+/) # does not set 'pre' buf << s end until @line.empty? if s = reads(/\A[^'"`{}%#\/\$]+/) buf << (pre = s) next end case ch = read(1) when '{' nest += 1 buf << (pre = ch) when '}' nest -= 1 if nest == 0 @in_block = nil return buf end buf << (pre = ch) when '#' # comment buf << ch << @line break when "'", '"', '`' buf << (pre = scan_quoted(ch)) when '%' if literal_head? pre, @line # % string, regexp, array buf << ch case ch = read(1) when /[qQx]/n buf << ch << (pre = scan_quoted(read(1), '%string')) when /wW/n buf << ch << (pre = scan_quoted(read(1), '%array')) when /s/n buf << ch << (pre = scan_quoted(read(1), '%symbol')) when /r/n buf << ch << (pre = scan_quoted(read(1), '%regexp')) when /[a-zA-Z0-9= ]/n # does not include "_" scan_error! "unknown type of % literal '%#{ch}'" else buf << (pre = scan_quoted(ch, '%string')) end else # operator buf << '||op->' if $raccs_print_type buf << (pre = ch) end when '/' if literal_head? pre, @line # regexp buf << (pre = scan_quoted(ch, 'regexp')) else # operator buf << '||op->' if $raccs_print_type buf << (pre = ch) end when '$' # gvar buf << ch << (pre = read(1)) else raise 'racc: fatal: must not happen' end end buf << "\n" end while next_line() raise 'racc: fatal: scan finished before parser finished' end def literal_head?(pre, post) (!pre || /[a-zA-Z_0-9]/n !~ pre[-1,1]) && !post.empty? && /\A[\s\=]/n !~ post end def read(len) s = @line[0, len] @line = @line[len .. -1] s end def reads(re) m = re.match(@line) or return nil @line = m.post_match m[0] end def scan_quoted(left, tag = 'string') buf = left.dup buf = "||#{tag}->" + buf if $raccs_print_type re = get_quoted_re(left) sv, @in_block = @in_block, tag begin if s = reads(re) buf << s break else buf << @line end end while next_line() @in_block = sv buf << "<-#{tag}||" if $raccs_print_type buf end LEFT_TO_RIGHT = { '(' => ')', '{' => '}', '[' => ']', '<' => '>' } CACHE = {} def get_quoted_re(left) term = Regexp.quote(LEFT_TO_RIGHT[left] || left) CACHE[left] ||= /\A[^#{term}\\]*(?:\\.[^\\#{term}]*)*#{term}/ end def scan_error!(msg) raise CompileError, "#{lineno()}: #{msg}" end end end # module Racc racc-1.4.14/lib/racc/compat.rb0000644000004100000410000000121712625454503016036 0ustar www-datawww-data# # $Id: 14fa1118eb3a23e85265e4f7afe2d5a297d69f9c $ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of the GNU LGPL, see the file "COPYING". # unless Object.method_defined?(:__send) class Object alias __send __send__ end end unless Object.method_defined?(:__send!) class Object alias __send! __send__ end end unless Array.method_defined?(:map!) class Array if Array.method_defined?(:collect!) alias map! collect! else alias map! filter end end end racc-1.4.14/lib/racc/exception.rb0000644000004100000410000000060512625454503016551 0ustar www-datawww-data# # $Id: a26517cc7b6a673ce1985c8c75fba88492e1e820 $ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of the GNU LGPL, see the file "COPYING". # module Racc class Error < StandardError; end class CompileError < Error; end end racc-1.4.14/lib/racc/parser.rb0000644000004100000410000004451012625454503016052 0ustar www-datawww-data# # $Id: 2bd697d1146b280d8d3878e21e556ef769484a37 $ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the same terms of ruby. # # As a special exception, when this code is copied by Racc # into a Racc output file, you may use that output file # without restriction. # require 'racc/info' unless defined?(NotImplementedError) NotImplementedError = NotImplementError # :nodoc: end module Racc class ParseError < StandardError; end end unless defined?(::ParseError) ParseError = Racc::ParseError end # Racc is a LALR(1) parser generator. # It is written in Ruby itself, and generates Ruby programs. # # == Command-line Reference # # racc [-ofilename] [--output-file=filename] # [-erubypath] [--embedded=rubypath] # [-v] [--verbose] # [-Ofilename] [--log-file=filename] # [-g] [--debug] # [-E] [--embedded] # [-l] [--no-line-convert] # [-c] [--line-convert-all] # [-a] [--no-omit-actions] # [-C] [--check-only] # [-S] [--output-status] # [--version] [--copyright] [--help] grammarfile # # [+filename+] # Racc grammar file. Any extention is permitted. # [-o+outfile+, --output-file=+outfile+] # A filename for output. default is <+filename+>.tab.rb # [-O+filename+, --log-file=+filename+] # Place logging output in file +filename+. # Default log file name is <+filename+>.output. # [-e+rubypath+, --executable=+rubypath+] # output executable file(mode 755). where +path+ is the Ruby interpreter. # [-v, --verbose] # verbose mode. create +filename+.output file, like yacc's y.output file. # [-g, --debug] # add debug code to parser class. To display debuggin information, # use this '-g' option and set @yydebug true in parser class. # [-E, --embedded] # Output parser which doesn't need runtime files (racc/parser.rb). # [-C, --check-only] # Check syntax of racc grammer file and quit. # [-S, --output-status] # Print messages time to time while compiling. # [-l, --no-line-convert] # turns off line number converting. # [-c, --line-convert-all] # Convert line number of actions, inner, header and footer. # [-a, --no-omit-actions] # Call all actions, even if an action is empty. # [--version] # print Racc version and quit. # [--copyright] # Print copyright and quit. # [--help] # Print usage and quit. # # == Generating Parser Using Racc # # To compile Racc grammar file, simply type: # # $ racc parse.y # # This creates Ruby script file "parse.tab.y". The -o option can change the output filename. # # == Writing A Racc Grammar File # # If you want your own parser, you have to write a grammar file. # A grammar file contains the name of your parser class, grammar for the parser, # user code, and anything else. # When writing a grammar file, yacc's knowledge is helpful. # If you have not used yacc before, Racc is not too difficult. # # Here's an example Racc grammar file. # # class Calcparser # rule # target: exp { print val[0] } # # exp: exp '+' exp # | exp '*' exp # | '(' exp ')' # | NUMBER # end # # Racc grammar files resemble yacc files. # But (of course), this is Ruby code. # yacc's $$ is the 'result', $0, $1... is # an array called 'val', and $-1, $-2... is an array called '_values'. # # See the {Grammar File Reference}[rdoc-ref:lib/racc/rdoc/grammar.en.rdoc] for # more information on grammar files. # # == Parser # # Then you must prepare the parse entry method. There are two types of # parse methods in Racc, Racc::Parser#do_parse and Racc::Parser#yyparse # # Racc::Parser#do_parse is simple. # # It's yyparse() of yacc, and Racc::Parser#next_token is yylex(). # This method must returns an array like [TOKENSYMBOL, ITS_VALUE]. # EOF is [false, false]. # (TOKENSYMBOL is a Ruby symbol (taken from String#intern) by default. # If you want to change this, see the grammar reference. # # Racc::Parser#yyparse is little complicated, but useful. # It does not use Racc::Parser#next_token, instead it gets tokens from any iterator. # # For example, yyparse(obj, :scan) causes # calling +obj#scan+, and you can return tokens by yielding them from +obj#scan+. # # == Debugging # # When debugging, "-v" or/and the "-g" option is helpful. # # "-v" creates verbose log file (.output). # "-g" creates a "Verbose Parser". # Verbose Parser prints the internal status when parsing. # But it's _not_ automatic. # You must use -g option and set +@yydebug+ to +true+ in order to get output. # -g option only creates the verbose parser. # # === Racc reported syntax error. # # Isn't there too many "end"? # grammar of racc file is changed in v0.10. # # Racc does not use '%' mark, while yacc uses huge number of '%' marks.. # # === Racc reported "XXXX conflicts". # # Try "racc -v xxxx.y". # It causes producing racc's internal log file, xxxx.output. # # === Generated parsers does not work correctly # # Try "racc -g xxxx.y". # This command let racc generate "debugging parser". # Then set @yydebug=true in your parser. # It produces a working log of your parser. # # == Re-distributing Racc runtime # # A parser, which is created by Racc, requires the Racc runtime module; # racc/parser.rb. # # Ruby 1.8.x comes with Racc runtime module, # you need NOT distribute Racc runtime files. # # If you want to include the Racc runtime module with your parser. # This can be done by using '-E' option: # # $ racc -E -omyparser.rb myparser.y # # This command creates myparser.rb which `includes' Racc runtime. # Only you must do is to distribute your parser file (myparser.rb). # # Note: parser.rb is LGPL, but your parser is not. # Your own parser is completely yours. module Racc unless defined?(Racc_No_Extentions) Racc_No_Extentions = false # :nodoc: end class Parser Racc_Runtime_Version = ::Racc::VERSION Racc_Runtime_Revision = '$Id: 2bd697d1146b280d8d3878e21e556ef769484a37 $' Racc_Runtime_Core_Version_R = ::Racc::VERSION Racc_Runtime_Core_Revision_R = '$Id: 2bd697d1146b280d8d3878e21e556ef769484a37 $'.split[1] begin if Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE == 'jruby' require 'racc/cparse-jruby.jar' com.headius.racc.Cparse.new.load(JRuby.runtime, false) else require 'racc/cparse' end # Racc_Runtime_Core_Version_C = (defined in extention) Racc_Runtime_Core_Revision_C = Racc_Runtime_Core_Id_C.split[2] unless new.respond_to?(:_racc_do_parse_c, true) raise LoadError, 'old cparse.so' end if Racc_No_Extentions raise LoadError, 'selecting ruby version of racc runtime core' end Racc_Main_Parsing_Routine = :_racc_do_parse_c # :nodoc: Racc_YY_Parse_Method = :_racc_yyparse_c # :nodoc: Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C # :nodoc: Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_C # :nodoc: Racc_Runtime_Type = 'c' # :nodoc: rescue LoadError puts $! puts $!.backtrace Racc_Main_Parsing_Routine = :_racc_do_parse_rb Racc_YY_Parse_Method = :_racc_yyparse_rb Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_R Racc_Runtime_Type = 'ruby' end def Parser.racc_runtime_type # :nodoc: Racc_Runtime_Type end def _racc_setup @yydebug = false unless self.class::Racc_debug_parser @yydebug = false unless defined?(@yydebug) if @yydebug @racc_debug_out = $stderr unless defined?(@racc_debug_out) @racc_debug_out ||= $stderr end arg = self.class::Racc_arg arg[13] = true if arg.size < 14 arg end def _racc_init_sysvars @racc_state = [0] @racc_tstack = [] @racc_vstack = [] @racc_t = nil @racc_val = nil @racc_read_next = true @racc_user_yyerror = false @racc_error_status = 0 end # The entry point of the parser. This method is used with #next_token. # If Racc wants to get token (and its value), calls next_token. # # Example: # def parse # @q = [[1,1], # [2,2], # [3,3], # [false, '$']] # do_parse # end # # def next_token # @q.shift # end def do_parse __send__(Racc_Main_Parsing_Routine, _racc_setup(), false) end # The method to fetch next token. # If you use #do_parse method, you must implement #next_token. # # The format of return value is [TOKEN_SYMBOL, VALUE]. # +token-symbol+ is represented by Ruby's symbol by default, e.g. :IDENT # for 'IDENT'. ";" (String) for ';'. # # The final symbol (End of file) must be false. def next_token raise NotImplementedError, "#{self.class}\#next_token is not defined" end def _racc_do_parse_rb(arg, in_debug) action_table, action_check, action_default, action_pointer, _, _, _, _, _, _, token_table, * = arg _racc_init_sysvars tok = act = i = nil catch(:racc_end_parse) { while true if i = action_pointer[@racc_state[-1]] if @racc_read_next if @racc_t != 0 # not EOF tok, @racc_val = next_token() unless tok # EOF @racc_t = 0 else @racc_t = (token_table[tok] or 1) # error token end racc_read_token(@racc_t, tok, @racc_val) if @yydebug @racc_read_next = false end end i += @racc_t unless i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] act = action_default[@racc_state[-1]] end else act = action_default[@racc_state[-1]] end while act = _racc_evalact(act, arg) ; end end } end # Another entry point for the parser. # If you use this method, you must implement RECEIVER#METHOD_ID method. # # RECEIVER#METHOD_ID is a method to get next token. # It must 'yield' the token, which format is [TOKEN-SYMBOL, VALUE]. def yyparse(recv, mid) __send__(Racc_YY_Parse_Method, recv, mid, _racc_setup(), false) end def _racc_yyparse_rb(recv, mid, arg, c_debug) action_table, action_check, action_default, action_pointer, _, _, _, _, _, _, token_table, * = arg _racc_init_sysvars catch(:racc_end_parse) { until i = action_pointer[@racc_state[-1]] while act = _racc_evalact(action_default[@racc_state[-1]], arg) ; end end recv.__send__(mid) do |tok, val| unless tok @racc_t = 0 else @racc_t = (token_table[tok] or 1) # error token end @racc_val = val @racc_read_next = false i += @racc_t unless i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] act = action_default[@racc_state[-1]] end while act = _racc_evalact(act, arg) ; end while !(i = action_pointer[@racc_state[-1]]) || ! @racc_read_next || @racc_t == 0 # $ unless i and i += @racc_t and i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] act = action_default[@racc_state[-1]] end while act = _racc_evalact(act, arg) ; end end end } end ### ### common ### def _racc_evalact(act, arg) action_table, action_check, _, action_pointer, _, _, _, _, _, _, _, shift_n, reduce_n, * = arg nerr = 0 # tmp if act > 0 and act < shift_n # # shift # if @racc_error_status > 0 @racc_error_status -= 1 unless @racc_t <= 1 # error token or EOF end @racc_vstack.push @racc_val @racc_state.push act @racc_read_next = true if @yydebug @racc_tstack.push @racc_t racc_shift @racc_t, @racc_tstack, @racc_vstack end elsif act < 0 and act > -reduce_n # # reduce # code = catch(:racc_jump) { @racc_state.push _racc_do_reduce(arg, act) false } if code case code when 1 # yyerror @racc_user_yyerror = true # user_yyerror return -reduce_n when 2 # yyaccept return shift_n else raise '[Racc Bug] unknown jump code' end end elsif act == shift_n # # accept # racc_accept if @yydebug throw :racc_end_parse, @racc_vstack[0] elsif act == -reduce_n # # error # case @racc_error_status when 0 unless arg[21] # user_yyerror nerr += 1 on_error @racc_t, @racc_val, @racc_vstack end when 3 if @racc_t == 0 # is $ # We're at EOF, and another error occurred immediately after # attempting auto-recovery throw :racc_end_parse, nil end @racc_read_next = true end @racc_user_yyerror = false @racc_error_status = 3 while true if i = action_pointer[@racc_state[-1]] i += 1 # error token if i >= 0 and (act = action_table[i]) and action_check[i] == @racc_state[-1] break end end throw :racc_end_parse, nil if @racc_state.size <= 1 @racc_state.pop @racc_vstack.pop if @yydebug @racc_tstack.pop racc_e_pop @racc_state, @racc_tstack, @racc_vstack end end return act else raise "[Racc Bug] unknown action #{act.inspect}" end racc_next_state(@racc_state[-1], @racc_state) if @yydebug nil end def _racc_do_reduce(arg, act) _, _, _, _, goto_table, goto_check, goto_default, goto_pointer, nt_base, reduce_table, _, _, _, use_result, * = arg state = @racc_state vstack = @racc_vstack tstack = @racc_tstack i = act * -3 len = reduce_table[i] reduce_to = reduce_table[i+1] method_id = reduce_table[i+2] void_array = [] tmp_t = tstack[-len, len] if @yydebug tmp_v = vstack[-len, len] tstack[-len, len] = void_array if @yydebug vstack[-len, len] = void_array state[-len, len] = void_array # tstack must be updated AFTER method call if use_result vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0]) else vstack.push __send__(method_id, tmp_v, vstack) end tstack.push reduce_to racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug k1 = reduce_to - nt_base if i = goto_pointer[k1] i += state[-1] if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1 return curstate end end goto_default[k1] end # This method is called when a parse error is found. # # ERROR_TOKEN_ID is an internal ID of token which caused error. # You can get string representation of this ID by calling # #token_to_str. # # ERROR_VALUE is a value of error token. # # value_stack is a stack of symbol values. # DO NOT MODIFY this object. # # This method raises ParseError by default. # # If this method returns, parsers enter "error recovering mode". def on_error(t, val, vstack) raise ParseError, sprintf("\nparse error on value %s (%s)", val.inspect, token_to_str(t) || '?') end # Enter error recovering mode. # This method does not call #on_error. def yyerror throw :racc_jump, 1 end # Exit parser. # Return value is Symbol_Value_Stack[0]. def yyaccept throw :racc_jump, 2 end # Leave error recovering mode. def yyerrok @racc_error_status = 0 end # For debugging output def racc_read_token(t, tok, val) @racc_debug_out.print 'read ' @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') ' @racc_debug_out.puts val.inspect @racc_debug_out.puts end def racc_shift(tok, tstack, vstack) @racc_debug_out.puts "shift #{racc_token2str tok}" racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_reduce(toks, sim, tstack, vstack) out = @racc_debug_out out.print 'reduce ' if toks.empty? out.print ' ' else toks.each {|t| out.print ' ', racc_token2str(t) } end out.puts " --> #{racc_token2str(sim)}" racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_accept @racc_debug_out.puts 'accept' @racc_debug_out.puts end def racc_e_pop(state, tstack, vstack) @racc_debug_out.puts 'error recovering mode: pop token' racc_print_states state racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_next_state(curstate, state) @racc_debug_out.puts "goto #{curstate}" racc_print_states state @racc_debug_out.puts end def racc_print_stacks(t, v) out = @racc_debug_out out.print ' [' t.each_index do |i| out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')' end out.puts ' ]' end def racc_print_states(s) out = @racc_debug_out out.print ' [' s.each {|st| out.print ' ', st } out.puts ' ]' end def racc_token2str(tok) self.class::Racc_token_to_s_table[tok] or raise "[Racc Bug] can't convert token #{tok} to string" end # Convert internal ID of token symbol to the string. def token_to_str(t) self.class::Racc_token_to_s_table[t] end end end racc-1.4.14/lib/racc/sourcetext.rb0000644000004100000410000000121012625454503016751 0ustar www-datawww-data# # $Id: 3b2d89d9ada2f5fcb043837dcc5c9631856d5b70 $ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of LGPL, see the file "COPYING". # module Racc class SourceText def initialize(text, filename, lineno) @text = text @filename = filename @lineno = lineno end attr_reader :text attr_reader :filename attr_reader :lineno def to_s "#" end def location "#{@filename}:#{@lineno}" end end end racc-1.4.14/lib/racc/statetransitiontable.rb0000644000004100000410000002002212625454503021011 0ustar www-datawww-data# # $Id: 4c5f4311663b6d03050953d64d6a0e7905ff2216 $ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of LGPL, see the file "COPYING". # require 'racc/parser' unless Object.method_defined?(:funcall) class Object alias funcall __send__ end end module Racc StateTransitionTable = Struct.new(:action_table, :action_check, :action_default, :action_pointer, :goto_table, :goto_check, :goto_default, :goto_pointer, :token_table, :reduce_table, :reduce_n, :shift_n, :nt_base, :token_to_s_table, :use_result_var, :debug_parser) class StateTransitionTable # reopen def StateTransitionTable.generate(states) StateTransitionTableGenerator.new(states).generate end def initialize(states) super() @states = states @grammar = states.grammar self.use_result_var = true self.debug_parser = true end attr_reader :states attr_reader :grammar def parser_class ParserClassGenerator.new(@states).generate end def token_value_table h = {} token_table().each do |sym, i| h[sym.value] = i end h end end class StateTransitionTableGenerator def initialize(states) @states = states @grammar = states.grammar end def generate t = StateTransitionTable.new(@states) gen_action_tables t, @states gen_goto_tables t, @grammar t.token_table = token_table(@grammar) t.reduce_table = reduce_table(@grammar) t.reduce_n = @states.reduce_n t.shift_n = @states.shift_n t.nt_base = @grammar.nonterminal_base t.token_to_s_table = @grammar.symbols.map {|sym| sym.to_s } t end def reduce_table(grammar) t = [0, 0, :racc_error] grammar.each_with_index do |rule, idx| next if idx == 0 t.push rule.size t.push rule.target.ident t.push(if rule.action.empty? # and @params.omit_action_call? then :_reduce_none else "_reduce_#{idx}".intern end) end t end def token_table(grammar) h = {} grammar.symboltable.terminals.each do |t| h[t] = t.ident end h end def gen_action_tables(t, states) t.action_table = yytable = [] t.action_check = yycheck = [] t.action_default = yydefact = [] t.action_pointer = yypact = [] e1 = [] e2 = [] states.each do |state| yydefact.push act2actid(state.defact) if state.action.empty? yypact.push nil next end vector = [] state.action.each do |tok, act| vector[tok.ident] = act2actid(act) end addent e1, vector, state.ident, yypact end set_table e1, e2, yytable, yycheck, yypact end def gen_goto_tables(t, grammar) t.goto_table = yytable2 = [] t.goto_check = yycheck2 = [] t.goto_pointer = yypgoto = [] t.goto_default = yydefgoto = [] e1 = [] e2 = [] grammar.each_nonterminal do |tok| tmp = [] # decide default freq = Array.new(@states.size, 0) @states.each do |state| st = state.goto_table[tok] if st st = st.ident freq[st] += 1 end tmp[state.ident] = st end max = freq.max if max > 1 default = freq.index(max) tmp.map! {|i| default == i ? nil : i } else default = nil end yydefgoto.push default # delete default value tmp.pop until tmp.last or tmp.empty? if tmp.compact.empty? # only default yypgoto.push nil next end addent e1, tmp, (tok.ident - grammar.nonterminal_base), yypgoto end set_table e1, e2, yytable2, yycheck2, yypgoto end def addent(all, arr, chkval, ptr) max = arr.size min = nil arr.each_with_index do |item, idx| if item min ||= idx end end ptr.push(-7777) # mark arr = arr[min...max] all.push [arr, chkval, mkmapexp(arr), min, ptr.size - 1] end n = 2 ** 16 begin Regexp.compile("a{#{n}}") RE_DUP_MAX = n rescue RegexpError n /= 2 retry end def mkmapexp(arr) i = ii = 0 as = arr.size map = '' maxdup = RE_DUP_MAX curr = nil while i < as ii = i + 1 if arr[i] ii += 1 while ii < as and arr[ii] curr = '-' else ii += 1 while ii < as and not arr[ii] curr = '.' end offset = ii - i if offset == 1 map << curr else while offset > maxdup map << "#{curr}{#{maxdup}}" offset -= maxdup end map << "#{curr}{#{offset}}" if offset > 1 end i = ii end Regexp.compile(map, 'n') end def set_table(entries, dummy, tbl, chk, ptr) upper = 0 map = '-' * 10240 # sort long to short entries.sort! {|a,b| b[0].size <=> a[0].size } entries.each do |arr, chkval, expr, min, ptri| if upper + arr.size > map.size map << '-' * (arr.size + 1024) end idx = map.index(expr) ptr[ptri] = idx - min arr.each_with_index do |item, i| if item i += idx tbl[i] = item chk[i] = chkval map[i] = ?o end end upper = idx + arr.size end end def act2actid(act) case act when Shift then act.goto_id when Reduce then -act.ruleid when Accept then @states.shift_n when Error then @states.reduce_n * -1 else raise "racc: fatal: wrong act type #{act.class} in action table" end end end class ParserClassGenerator def initialize(states) @states = states @grammar = states.grammar end def generate table = @states.state_transition_table c = Class.new(::Racc::Parser) c.const_set :Racc_arg, [table.action_table, table.action_check, table.action_default, table.action_pointer, table.goto_table, table.goto_check, table.goto_default, table.goto_pointer, table.nt_base, table.reduce_table, table.token_value_table, table.shift_n, table.reduce_n, false] c.const_set :Racc_token_to_s_table, table.token_to_s_table c.const_set :Racc_debug_parser, true define_actions c c end private def define_actions(c) c.module_eval "def _reduce_none(vals, vstack) vals[0] end" @grammar.each do |rule| if rule.action.empty? c.funcall(:alias_method, "_reduce_#{rule.ident}", :_reduce_none) else c.funcall(:define_method, "_racc_action_#{rule.ident}", &rule.action.proc) c.module_eval(<<-End, __FILE__, __LINE__ + 1) def _reduce_#{rule.ident}(vals, vstack) _racc_action_#{rule.ident}(*vals) end End end end end end end # module Racc racc-1.4.14/lib/racc/logfilegenerator.rb0000644000004100000410000001224312625454503020104 0ustar www-datawww-data# # $Id: a7e9663605afdda065d305b250a9805e3bd3fa70 $ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of the GNU LGPL, see the file "COPYING". # module Racc class LogFileGenerator def initialize(states, debug_flags = DebugFlags.new) @states = states @grammar = states.grammar @debug_flags = debug_flags end def output(out) output_conflict out; out.puts output_useless out; out.puts output_rule out; out.puts output_token out; out.puts output_state out end # # Warnings # def output_conflict(out) @states.each do |state| if state.srconf out.printf "state %d contains %d shift/reduce conflicts\n", state.stateid, state.srconf.size end if state.rrconf out.printf "state %d contains %d reduce/reduce conflicts\n", state.stateid, state.rrconf.size end end end def output_useless(out) @grammar.each do |rl| if rl.useless? out.printf "rule %d (%s) never reduced\n", rl.ident, rl.target.to_s end end @grammar.each_nonterminal do |t| if t.useless? out.printf "useless nonterminal %s\n", t.to_s end end end # # States # def output_state(out) out << "--------- State ---------\n" showall = @debug_flags.la || @debug_flags.state @states.each do |state| out << "\nstate #{state.ident}\n\n" (showall ? state.closure : state.core).each do |ptr| pointer_out(out, ptr) if ptr.rule.ident != 0 or showall end out << "\n" action_out out, state end end def pointer_out(out, ptr) buf = sprintf("%4d) %s :", ptr.rule.ident, ptr.rule.target.to_s) ptr.rule.symbols.each_with_index do |tok, idx| buf << ' _' if idx == ptr.index buf << ' ' << tok.to_s end buf << ' _' if ptr.reduce? out.puts buf end def action_out(f, state) sr = state.srconf && state.srconf.dup rr = state.rrconf && state.rrconf.dup acts = state.action keys = acts.keys keys.sort! {|a,b| a.ident <=> b.ident } [ Shift, Reduce, Error, Accept ].each do |klass| keys.delete_if do |tok| act = acts[tok] if act.kind_of?(klass) outact f, tok, act if sr and c = sr.delete(tok) outsrconf f, c end if rr and c = rr.delete(tok) outrrconf f, c end true else false end end end sr.each {|tok, c| outsrconf f, c } if sr rr.each {|tok, c| outrrconf f, c } if rr act = state.defact if not act.kind_of?(Error) or @debug_flags.any? outact f, '$default', act end f.puts state.goto_table.each do |t, st| if t.nonterminal? f.printf " %-12s go to state %d\n", t.to_s, st.ident end end end def outact(f, t, act) case act when Shift f.printf " %-12s shift, and go to state %d\n", t.to_s, act.goto_id when Reduce f.printf " %-12s reduce using rule %d (%s)\n", t.to_s, act.ruleid, act.rule.target.to_s when Accept f.printf " %-12s accept\n", t.to_s when Error f.printf " %-12s error\n", t.to_s else raise "racc: fatal: wrong act for outact: act=#{act}(#{act.class})" end end def outsrconf(f, confs) confs.each do |c| r = c.reduce f.printf " %-12s [reduce using rule %d (%s)]\n", c.shift.to_s, r.ident, r.target.to_s end end def outrrconf(f, confs) confs.each do |c| r = c.low_prec f.printf " %-12s [reduce using rule %d (%s)]\n", c.token.to_s, r.ident, r.target.to_s end end # # Rules # def output_rule(out) out.print "-------- Grammar --------\n\n" @grammar.each do |rl| if @debug_flags.any? or rl.ident != 0 out.printf "rule %d %s: %s\n", rl.ident, rl.target.to_s, rl.symbols.join(' ') end end end # # Tokens # def output_token(out) out.print "------- Symbols -------\n\n" out.print "**Nonterminals, with rules where they appear\n\n" @grammar.each_nonterminal do |t| tmp = <filename] [--output-file=filename] # [-erubypath] [--embedded=rubypath] # [-v] [--verbose] # [-Ofilename] [--log-file=filename] # [-g] [--debug] # [-E] [--embedded] # [-l] [--no-line-convert] # [-c] [--line-convert-all] # [-a] [--no-omit-actions] # [-C] [--check-only] # [-S] [--output-status] # [--version] [--copyright] [--help] grammarfile # # [+filename+] # Racc grammar file. Any extention is permitted. # [-o+outfile+, --output-file=+outfile+] # A filename for output. default is <+filename+>.tab.rb # [-O+filename+, --log-file=+filename+] # Place logging output in file +filename+. # Default log file name is <+filename+>.output. # [-e+rubypath+, --executable=+rubypath+] # output executable file(mode 755). where +path+ is the Ruby interpreter. # [-v, --verbose] # verbose mode. create +filename+.output file, like yacc's y.output file. # [-g, --debug] # add debug code to parser class. To display debuggin information, # use this '-g' option and set @yydebug true in parser class. # [-E, --embedded] # Output parser which doesn't need runtime files (racc/parser.rb). # [-C, --check-only] # Check syntax of racc grammer file and quit. # [-S, --output-status] # Print messages time to time while compiling. # [-l, --no-line-convert] # turns off line number converting. # [-c, --line-convert-all] # Convert line number of actions, inner, header and footer. # [-a, --no-omit-actions] # Call all actions, even if an action is empty. # [--version] # print Racc version and quit. # [--copyright] # Print copyright and quit. # [--help] # Print usage and quit. # # == Generating Parser Using Racc # # To compile Racc grammar file, simply type: # # $ racc parse.y # # This creates Ruby script file "parse.tab.y". The -o option can change the output filename. # # == Writing A Racc Grammar File # # If you want your own parser, you have to write a grammar file. # A grammar file contains the name of your parser class, grammar for the parser, # user code, and anything else. # When writing a grammar file, yacc's knowledge is helpful. # If you have not used yacc before, Racc is not too difficult. # # Here's an example Racc grammar file. # # class Calcparser # rule # target: exp { print val[0] } # # exp: exp '+' exp # | exp '*' exp # | '(' exp ')' # | NUMBER # end # # Racc grammar files resemble yacc files. # But (of course), this is Ruby code. # yacc's $$ is the 'result', $0, $1... is # an array called 'val', and $-1, $-2... is an array called '_values'. # # See the {Grammar File Reference}[rdoc-ref:lib/racc/rdoc/grammar.en.rdoc] for # more information on grammar files. # # == Parser # # Then you must prepare the parse entry method. There are two types of # parse methods in Racc, Racc::Parser#do_parse and Racc::Parser#yyparse # # Racc::Parser#do_parse is simple. # # It's yyparse() of yacc, and Racc::Parser#next_token is yylex(). # This method must returns an array like [TOKENSYMBOL, ITS_VALUE]. # EOF is [false, false]. # (TOKENSYMBOL is a Ruby symbol (taken from String#intern) by default. # If you want to change this, see the grammar reference. # # Racc::Parser#yyparse is little complicated, but useful. # It does not use Racc::Parser#next_token, instead it gets tokens from any iterator. # # For example, yyparse(obj, :scan) causes # calling +obj#scan+, and you can return tokens by yielding them from +obj#scan+. # # == Debugging # # When debugging, "-v" or/and the "-g" option is helpful. # # "-v" creates verbose log file (.output). # "-g" creates a "Verbose Parser". # Verbose Parser prints the internal status when parsing. # But it's _not_ automatic. # You must use -g option and set +@yydebug+ to +true+ in order to get output. # -g option only creates the verbose parser. # # === Racc reported syntax error. # # Isn't there too many "end"? # grammar of racc file is changed in v0.10. # # Racc does not use '%' mark, while yacc uses huge number of '%' marks.. # # === Racc reported "XXXX conflicts". # # Try "racc -v xxxx.y". # It causes producing racc's internal log file, xxxx.output. # # === Generated parsers does not work correctly # # Try "racc -g xxxx.y". # This command let racc generate "debugging parser". # Then set @yydebug=true in your parser. # It produces a working log of your parser. # # == Re-distributing Racc runtime # # A parser, which is created by Racc, requires the Racc runtime module; # racc/parser.rb. # # Ruby 1.8.x comes with Racc runtime module, # you need NOT distribute Racc runtime files. # # If you want to include the Racc runtime module with your parser. # This can be done by using '-E' option: # # $ racc -E -omyparser.rb myparser.y # # This command creates myparser.rb which `includes' Racc runtime. # Only you must do is to distribute your parser file (myparser.rb). # # Note: parser.rb is LGPL, but your parser is not. # Your own parser is completely yours. module Racc unless defined?(Racc_No_Extentions) Racc_No_Extentions = false # :nodoc: end class Parser Racc_Runtime_Version = ::Racc::VERSION Racc_Runtime_Revision = '$Id: 2bd697d1146b280d8d3878e21e556ef769484a37 $' Racc_Runtime_Core_Version_R = ::Racc::VERSION Racc_Runtime_Core_Revision_R = '$Id: 2bd697d1146b280d8d3878e21e556ef769484a37 $'.split[1] begin if Object.const_defined?(:RUBY_ENGINE) and RUBY_ENGINE == 'jruby' require 'racc/cparse-jruby.jar' com.headius.racc.Cparse.new.load(JRuby.runtime, false) else require 'racc/cparse' end # Racc_Runtime_Core_Version_C = (defined in extention) Racc_Runtime_Core_Revision_C = Racc_Runtime_Core_Id_C.split[2] unless new.respond_to?(:_racc_do_parse_c, true) raise LoadError, 'old cparse.so' end if Racc_No_Extentions raise LoadError, 'selecting ruby version of racc runtime core' end Racc_Main_Parsing_Routine = :_racc_do_parse_c # :nodoc: Racc_YY_Parse_Method = :_racc_yyparse_c # :nodoc: Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_C # :nodoc: Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_C # :nodoc: Racc_Runtime_Type = 'c' # :nodoc: rescue LoadError puts $! puts $!.backtrace Racc_Main_Parsing_Routine = :_racc_do_parse_rb Racc_YY_Parse_Method = :_racc_yyparse_rb Racc_Runtime_Core_Version = Racc_Runtime_Core_Version_R Racc_Runtime_Core_Revision = Racc_Runtime_Core_Revision_R Racc_Runtime_Type = 'ruby' end def Parser.racc_runtime_type # :nodoc: Racc_Runtime_Type end def _racc_setup @yydebug = false unless self.class::Racc_debug_parser @yydebug = false unless defined?(@yydebug) if @yydebug @racc_debug_out = $stderr unless defined?(@racc_debug_out) @racc_debug_out ||= $stderr end arg = self.class::Racc_arg arg[13] = true if arg.size < 14 arg end def _racc_init_sysvars @racc_state = [0] @racc_tstack = [] @racc_vstack = [] @racc_t = nil @racc_val = nil @racc_read_next = true @racc_user_yyerror = false @racc_error_status = 0 end # The entry point of the parser. This method is used with #next_token. # If Racc wants to get token (and its value), calls next_token. # # Example: # def parse # @q = [[1,1], # [2,2], # [3,3], # [false, '$']] # do_parse # end # # def next_token # @q.shift # end def do_parse __send__(Racc_Main_Parsing_Routine, _racc_setup(), false) end # The method to fetch next token. # If you use #do_parse method, you must implement #next_token. # # The format of return value is [TOKEN_SYMBOL, VALUE]. # +token-symbol+ is represented by Ruby's symbol by default, e.g. :IDENT # for 'IDENT'. ";" (String) for ';'. # # The final symbol (End of file) must be false. def next_token raise NotImplementedError, "#{self.class}\#next_token is not defined" end def _racc_do_parse_rb(arg, in_debug) action_table, action_check, action_default, action_pointer, _, _, _, _, _, _, token_table, * = arg _racc_init_sysvars tok = act = i = nil catch(:racc_end_parse) { while true if i = action_pointer[@racc_state[-1]] if @racc_read_next if @racc_t != 0 # not EOF tok, @racc_val = next_token() unless tok # EOF @racc_t = 0 else @racc_t = (token_table[tok] or 1) # error token end racc_read_token(@racc_t, tok, @racc_val) if @yydebug @racc_read_next = false end end i += @racc_t unless i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] act = action_default[@racc_state[-1]] end else act = action_default[@racc_state[-1]] end while act = _racc_evalact(act, arg) ; end end } end # Another entry point for the parser. # If you use this method, you must implement RECEIVER#METHOD_ID method. # # RECEIVER#METHOD_ID is a method to get next token. # It must 'yield' the token, which format is [TOKEN-SYMBOL, VALUE]. def yyparse(recv, mid) __send__(Racc_YY_Parse_Method, recv, mid, _racc_setup(), false) end def _racc_yyparse_rb(recv, mid, arg, c_debug) action_table, action_check, action_default, action_pointer, _, _, _, _, _, _, token_table, * = arg _racc_init_sysvars catch(:racc_end_parse) { until i = action_pointer[@racc_state[-1]] while act = _racc_evalact(action_default[@racc_state[-1]], arg) ; end end recv.__send__(mid) do |tok, val| unless tok @racc_t = 0 else @racc_t = (token_table[tok] or 1) # error token end @racc_val = val @racc_read_next = false i += @racc_t unless i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] act = action_default[@racc_state[-1]] end while act = _racc_evalact(act, arg) ; end while !(i = action_pointer[@racc_state[-1]]) || ! @racc_read_next || @racc_t == 0 # $ unless i and i += @racc_t and i >= 0 and act = action_table[i] and action_check[i] == @racc_state[-1] act = action_default[@racc_state[-1]] end while act = _racc_evalact(act, arg) ; end end end } end ### ### common ### def _racc_evalact(act, arg) action_table, action_check, _, action_pointer, _, _, _, _, _, _, _, shift_n, reduce_n, * = arg nerr = 0 # tmp if act > 0 and act < shift_n # # shift # if @racc_error_status > 0 @racc_error_status -= 1 unless @racc_t <= 1 # error token or EOF end @racc_vstack.push @racc_val @racc_state.push act @racc_read_next = true if @yydebug @racc_tstack.push @racc_t racc_shift @racc_t, @racc_tstack, @racc_vstack end elsif act < 0 and act > -reduce_n # # reduce # code = catch(:racc_jump) { @racc_state.push _racc_do_reduce(arg, act) false } if code case code when 1 # yyerror @racc_user_yyerror = true # user_yyerror return -reduce_n when 2 # yyaccept return shift_n else raise '[Racc Bug] unknown jump code' end end elsif act == shift_n # # accept # racc_accept if @yydebug throw :racc_end_parse, @racc_vstack[0] elsif act == -reduce_n # # error # case @racc_error_status when 0 unless arg[21] # user_yyerror nerr += 1 on_error @racc_t, @racc_val, @racc_vstack end when 3 if @racc_t == 0 # is $ # We're at EOF, and another error occurred immediately after # attempting auto-recovery throw :racc_end_parse, nil end @racc_read_next = true end @racc_user_yyerror = false @racc_error_status = 3 while true if i = action_pointer[@racc_state[-1]] i += 1 # error token if i >= 0 and (act = action_table[i]) and action_check[i] == @racc_state[-1] break end end throw :racc_end_parse, nil if @racc_state.size <= 1 @racc_state.pop @racc_vstack.pop if @yydebug @racc_tstack.pop racc_e_pop @racc_state, @racc_tstack, @racc_vstack end end return act else raise "[Racc Bug] unknown action #{act.inspect}" end racc_next_state(@racc_state[-1], @racc_state) if @yydebug nil end def _racc_do_reduce(arg, act) _, _, _, _, goto_table, goto_check, goto_default, goto_pointer, nt_base, reduce_table, _, _, _, use_result, * = arg state = @racc_state vstack = @racc_vstack tstack = @racc_tstack i = act * -3 len = reduce_table[i] reduce_to = reduce_table[i+1] method_id = reduce_table[i+2] void_array = [] tmp_t = tstack[-len, len] if @yydebug tmp_v = vstack[-len, len] tstack[-len, len] = void_array if @yydebug vstack[-len, len] = void_array state[-len, len] = void_array # tstack must be updated AFTER method call if use_result vstack.push __send__(method_id, tmp_v, vstack, tmp_v[0]) else vstack.push __send__(method_id, tmp_v, vstack) end tstack.push reduce_to racc_reduce(tmp_t, reduce_to, tstack, vstack) if @yydebug k1 = reduce_to - nt_base if i = goto_pointer[k1] i += state[-1] if i >= 0 and (curstate = goto_table[i]) and goto_check[i] == k1 return curstate end end goto_default[k1] end # This method is called when a parse error is found. # # ERROR_TOKEN_ID is an internal ID of token which caused error. # You can get string representation of this ID by calling # #token_to_str. # # ERROR_VALUE is a value of error token. # # value_stack is a stack of symbol values. # DO NOT MODIFY this object. # # This method raises ParseError by default. # # If this method returns, parsers enter "error recovering mode". def on_error(t, val, vstack) raise ParseError, sprintf("\nparse error on value %s (%s)", val.inspect, token_to_str(t) || '?') end # Enter error recovering mode. # This method does not call #on_error. def yyerror throw :racc_jump, 1 end # Exit parser. # Return value is Symbol_Value_Stack[0]. def yyaccept throw :racc_jump, 2 end # Leave error recovering mode. def yyerrok @racc_error_status = 0 end # For debugging output def racc_read_token(t, tok, val) @racc_debug_out.print 'read ' @racc_debug_out.print tok.inspect, '(', racc_token2str(t), ') ' @racc_debug_out.puts val.inspect @racc_debug_out.puts end def racc_shift(tok, tstack, vstack) @racc_debug_out.puts "shift #{racc_token2str tok}" racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_reduce(toks, sim, tstack, vstack) out = @racc_debug_out out.print 'reduce ' if toks.empty? out.print ' ' else toks.each {|t| out.print ' ', racc_token2str(t) } end out.puts " --> #{racc_token2str(sim)}" racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_accept @racc_debug_out.puts 'accept' @racc_debug_out.puts end def racc_e_pop(state, tstack, vstack) @racc_debug_out.puts 'error recovering mode: pop token' racc_print_states state racc_print_stacks tstack, vstack @racc_debug_out.puts end def racc_next_state(curstate, state) @racc_debug_out.puts "goto #{curstate}" racc_print_states state @racc_debug_out.puts end def racc_print_stacks(t, v) out = @racc_debug_out out.print ' [' t.each_index do |i| out.print ' (', racc_token2str(t[i]), ' ', v[i].inspect, ')' end out.puts ' ]' end def racc_print_states(s) out = @racc_debug_out out.print ' [' s.each {|st| out.print ' ', st } out.puts ' ]' end def racc_token2str(tok) self.class::Racc_token_to_s_table[tok] or raise "[Racc Bug] can't convert token #{tok} to string" end # Convert internal ID of token symbol to the string. def token_to_str(t) self.class::Racc_token_to_s_table[t] end end end __end_of_file__ end racc-1.4.14/lib/racc/parserfilegenerator.rb0000644000004100000410000002736512625454503020632 0ustar www-datawww-data# # $Id: f2d2788af2323ada1913f1dad5fea8aae4cc6830 $ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of the GNU LGPL, see the file "COPYING". # require 'enumerator' require 'racc/compat' require 'racc/sourcetext' require 'racc/parser-text' require 'rbconfig' module Racc class ParserFileGenerator class Params def self.bool_attr(name) module_eval(<<-End) def #{name}? @#{name} end def #{name}=(b) @#{name} = b end End end attr_accessor :filename attr_accessor :classname attr_accessor :superclass bool_attr :omit_action_call bool_attr :result_var attr_accessor :header attr_accessor :inner attr_accessor :footer bool_attr :debug_parser bool_attr :convert_line bool_attr :convert_line_all bool_attr :embed_runtime bool_attr :make_executable attr_accessor :interpreter def initialize # Parameters derived from parser self.filename = nil self.classname = nil self.superclass = 'Racc::Parser' self.omit_action_call = true self.result_var = true self.header = [] self.inner = [] self.footer = [] # Parameters derived from command line options self.debug_parser = false self.convert_line = true self.convert_line_all = false self.embed_runtime = false self.make_executable = false self.interpreter = nil end end def initialize(states, params) @states = states @grammar = states.grammar @params = params end def generate_parser string_io = StringIO.new init_line_conversion_system @f = string_io parser_file string_io.rewind string_io.read end def generate_parser_file(destpath) init_line_conversion_system File.open(destpath, 'w') {|f| @f = f parser_file } File.chmod 0755, destpath if @params.make_executable? end private def parser_file shebang @params.interpreter if @params.make_executable? notice line if @params.embed_runtime? embed_library runtime_source() else require 'racc/parser.rb' end header parser_class(@params.classname, @params.superclass) { inner state_transition_table } footer end c = ::RbConfig::CONFIG RUBY_PATH = "#{c['bindir']}/#{c['ruby_install_name']}#{c['EXEEXT']}" def shebang(path) line '#!' + (path == 'ruby' ? RUBY_PATH : path) end def notice line %q[#] line %q[# DO NOT MODIFY!!!!] line %Q[# This file is automatically generated by Racc #{Racc::Version}] line %Q[# from Racc grammer file "#{@params.filename}".] line %q[#] end def runtime_source SourceText.new(::Racc::PARSER_TEXT, 'racc/parser.rb', 1) end def embed_library(src) line %[###### #{src.filename} begin] line %[unless $".index '#{src.filename}'] line %[$".push '#{src.filename}'] put src, @params.convert_line? line %[end] line %[###### #{src.filename} end] end def require(feature) line "require '#{feature}'" end def parser_class(classname, superclass) mods = classname.split('::') classid = mods.pop mods.each do |mod| indent; line "module #{mod}" cref_push mod end indent; line "class #{classid} < #{superclass}" cref_push classid yield cref_pop indent; line "end \# class #{classid}" mods.reverse_each do |mod| indent; line "end \# module #{mod}" cref_pop end end def header @params.header.each do |src| line put src, @params.convert_line_all? end end def inner @params.inner.each do |src| line put src, @params.convert_line? end end def footer @params.footer.each do |src| line put src, @params.convert_line_all? end end # Low Level Routines def put(src, convert_line = false) if convert_line replace_location(src) { @f.puts src.text } else @f.puts src.text end end def line(str = '') @f.puts str end def init_line_conversion_system @cref = [] @used_separator = {} end def cref_push(name) @cref.push name end def cref_pop @cref.pop end def indent @f.print ' ' * @cref.size end def toplevel? @cref.empty? end def replace_location(src) sep = make_separator(src) @f.print 'self.class.' if toplevel? @f.puts "module_eval(<<'#{sep}', '#{src.filename}', #{src.lineno})" yield @f.puts sep end def make_separator(src) sep = unique_separator(src.filename) sep *= 2 while src.text.index(sep) sep end def unique_separator(id) sep = "...end #{id}/module_eval..." while @used_separator.key?(sep) sep.concat sprintf('%02x', rand(255)) end @used_separator[sep] = true sep end # # State Transition Table Serialization # public def put_state_transition_table(f) @f = f state_transition_table end private def state_transition_table table = @states.state_transition_table table.use_result_var = @params.result_var? table.debug_parser = @params.debug_parser? line "##### State transition tables begin ###" line integer_list 'racc_action_table', table.action_table line integer_list 'racc_action_check', table.action_check line integer_list 'racc_action_pointer', table.action_pointer line integer_list 'racc_action_default', table.action_default line integer_list 'racc_goto_table', table.goto_table line integer_list 'racc_goto_check', table.goto_check line integer_list 'racc_goto_pointer', table.goto_pointer line integer_list 'racc_goto_default', table.goto_default line i_i_sym_list 'racc_reduce_table', table.reduce_table line line "racc_reduce_n = #{table.reduce_n}" line line "racc_shift_n = #{table.shift_n}" line sym_int_hash 'racc_token_table', table.token_table line line "racc_nt_base = #{table.nt_base}" line line "racc_use_result_var = #{table.use_result_var}" line @f.print(unindent_auto(<<-End)) Racc_arg = [ racc_action_table, racc_action_check, racc_action_default, racc_action_pointer, racc_goto_table, racc_goto_check, racc_goto_default, racc_goto_pointer, racc_nt_base, racc_reduce_table, racc_token_table, racc_shift_n, racc_reduce_n, racc_use_result_var ] End line string_list 'Racc_token_to_s_table', table.token_to_s_table line line "Racc_debug_parser = #{table.debug_parser}" line line '##### State transition tables end #####' actions end def integer_list(name, table) if table.size > 2000 serialize_integer_list_compressed name, table else serialize_integer_list_std name, table end end def serialize_integer_list_compressed(name, table) # TODO: this can be made a LOT more clean with a simple split/map sep = "\n" nsep = ",\n" buf = '' com = '' ncom = ',' co = com @f.print 'clist = [' table.each do |i| buf << co << i.to_s; co = ncom if buf.size > 66 @f.print sep; sep = nsep @f.print "'", buf, "'" buf = '' co = com end end unless buf.empty? @f.print sep @f.print "'", buf, "'" end line ' ]' @f.print(<<-End) #{name} = arr = ::Array.new(#{table.size}, nil) idx = 0 clist.each do |str| str.split(',', -1).each do |i| arr[idx] = i.to_i unless i.empty? idx += 1 end end End end def serialize_integer_list_std(name, table) sep = '' line "#{name} = [" table.each_slice(10) do |ns| @f.print sep; sep = ",\n" @f.print ns.map {|n| sprintf('%6s', n ? n.to_s : 'nil') }.join(',') end line ' ]' end def i_i_sym_list(name, table) sep = '' line "#{name} = [" table.each_slice(3) do |len, target, mid| @f.print sep; sep = ",\n" @f.printf ' %d, %d, %s', len, target, mid.inspect end line " ]" end def sym_int_hash(name, h) sep = "\n" @f.print "#{name} = {" h.to_a.sort_by {|sym, i| i }.each do |sym, i| @f.print sep; sep = ",\n" @f.printf " %s => %d", sym.serialize, i end line " }" end def string_list(name, list) sep = " " line "#{name} = [" list.each do |s| @f.print sep; sep = ",\n " @f.print s.dump end line ' ]' end def actions @grammar.each do |rule| unless rule.action.source? raise "racc: fatal: cannot generate parser file when any action is a Proc" end end if @params.result_var? decl = ', result' retval = "\n result" default_body = '' else decl = '' retval = '' default_body = 'val[0]' end @grammar.each do |rule| line if rule.action.empty? and @params.omit_action_call? line "# reduce #{rule.ident} omitted" else src0 = rule.action.source || SourceText.new(default_body, __FILE__, 0) if @params.convert_line? src = remove_blank_lines(src0) delim = make_delimiter(src.text) @f.printf unindent_auto(<<-End), module_eval(<<'%s', '%s', %d) def _reduce_%d(val, _values%s) %s%s end %s End delim, src.filename, src.lineno - 1, rule.ident, decl, src.text, retval, delim else src = remove_blank_lines(src0) @f.printf unindent_auto(<<-End), def _reduce_%d(val, _values%s) %s%s end End rule.ident, decl, src.text, retval end end end line @f.printf unindent_auto(<<-'End'), decl def _reduce_none(val, _values%s) val[0] end End line end def remove_blank_lines(src) body = src.text.dup line = src.lineno while body.slice!(/\A[ \t\f]*(?:\n|\r\n|\r)/) line += 1 end SourceText.new(body, src.filename, line) end def make_delimiter(body) delim = '.,.,' while body.index(delim) delim *= 2 end delim end def unindent_auto(str) lines = str.lines.to_a n = minimum_indent(lines) lines.map {|line| detab(line).sub(indent_re(n), '').rstrip + "\n" }.join('') end def minimum_indent(lines) lines.map {|line| n_indent(line) }.min end def n_indent(line) line.slice(/\A\s+/).size end RE_CACHE = {} def indent_re(n) RE_CACHE[n] ||= /\A {#{n}}/ end def detab(str, ts = 8) add = 0 len = nil str.gsub(/\t/) { len = ts - ($`.size + add) % ts add += len - 1 ' ' * len } end end end racc-1.4.14/lib/racc/pre-setup0000644000004100000410000000055112625454503016075 0ustar www-datawww-datadef generate_parser_text_rb(target) return if File.exist?(srcfile(target)) $stderr.puts "generating #{target}..." File.open(target, 'w') {|f| f.puts "module Racc" f.puts " PARSER_TEXT = <<'__end_of_file__'" f.puts File.read(srcfile('parser.rb')) f.puts "__end_of_file__" f.puts "end" } end generate_parser_text_rb 'parser-text.rb' racc-1.4.14/lib/racc/iset.rb0000644000004100000410000000255312625454503015523 0ustar www-datawww-data# # $Id: de638608cfd72d3ed9819d87b65a89ee6a57b589 $ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of the GNU LGPL, see the file "COPYING". # module Racc # An "indexed" set. All items must respond to :ident. class ISet def initialize(a = []) @set = a end attr_reader :set def add(i) @set[i.ident] = i end def [](key) @set[key.ident] end def []=(key, val) @set[key.ident] = val end alias include? [] alias key? [] def update(other) s = @set o = other.set o.each_index do |idx| if t = o[idx] s[idx] = t end end end def update_a(a) s = @set a.each {|i| s[i.ident] = i } end def delete(key) i = @set[key.ident] @set[key.ident] = nil i end def each(&block) @set.compact.each(&block) end def to_a @set.compact end def to_s "[#{@set.compact.join(' ')}]" end alias inspect to_s def size @set.nitems end def empty? @set.nitems == 0 end def clear @set.clear end def dup ISet.new(@set.dup) end end # class ISet end # module Racc racc-1.4.14/lib/racc/state.rb0000644000004100000410000004771512625454503015710 0ustar www-datawww-data# # $Id: 735e622baf286554d4a31d42664ea07b3a17115d $ # # Copyright (c) 1999-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of the GNU LGPL, see the file "COPYING". # require 'racc/iset' require 'racc/statetransitiontable' require 'racc/exception' require 'forwardable' module Racc # A table of LALR states. class States include Enumerable def initialize(grammar, debug_flags = DebugFlags.new) @grammar = grammar @symboltable = grammar.symboltable @d_state = debug_flags.state @d_la = debug_flags.la @d_prec = debug_flags.prec @states = [] @statecache = {} @actions = ActionTable.new(@grammar, self) @nfa_computed = false @dfa_computed = false end attr_reader :grammar attr_reader :actions def size @states.size end def inspect '#' end alias to_s inspect def [](i) @states[i] end def each_state(&block) @states.each(&block) end alias each each_state def each_index(&block) @states.each_index(&block) end extend Forwardable def_delegator "@actions", :shift_n def_delegator "@actions", :reduce_n def_delegator "@actions", :nt_base def should_report_srconflict? srconflict_exist? and (n_srconflicts() != @grammar.n_expected_srconflicts) end def srconflict_exist? n_srconflicts() != 0 end def n_srconflicts @n_srconflicts ||= inject(0) {|sum, st| sum + st.n_srconflicts } end def rrconflict_exist? n_rrconflicts() != 0 end def n_rrconflicts @n_rrconflicts ||= inject(0) {|sum, st| sum + st.n_rrconflicts } end def state_transition_table @state_transition_table ||= StateTransitionTable.generate(self.dfa) end # # NFA (Non-deterministic Finite Automaton) Computation # public def nfa return self if @nfa_computed compute_nfa @nfa_computed = true self end private def compute_nfa @grammar.init # add state 0 core_to_state [ @grammar[0].ptrs[0] ] # generate LALR states cur = 0 @gotos = [] while cur < @states.size generate_states @states[cur] # state is added here cur += 1 end @actions.init end def generate_states(state) puts "dstate: #{state}" if @d_state table = {} state.closure.each do |ptr| if sym = ptr.dereference addsym table, sym, ptr.next end end table.each do |sym, core| puts "dstate: sym=#{sym} ncore=#{core}" if @d_state dest = core_to_state(core.to_a) state.goto_table[sym] = dest id = sym.nonterminal?() ? @gotos.size : nil g = Goto.new(id, sym, state, dest) @gotos.push g if sym.nonterminal? state.gotos[sym] = g puts "dstate: #{state.ident} --#{sym}--> #{dest.ident}" if @d_state # check infinite recursion if state.ident == dest.ident and state.closure.size == 1 raise CompileError, sprintf("Infinite recursion: state %d, with rule %d", state.ident, state.ptrs[0].rule.ident) end end end def addsym(table, sym, ptr) unless s = table[sym] table[sym] = s = ISet.new end s.add ptr end def core_to_state(core) # # convert CORE to a State object. # If matching state does not exist, create it and add to the table. # k = fingerprint(core) unless dest = @statecache[k] # not registered yet dest = State.new(@states.size, core) @states.push dest @statecache[k] = dest puts "core_to_state: create state ID #{dest.ident}" if @d_state else if @d_state puts "core_to_state: dest is cached ID #{dest.ident}" puts "core_to_state: dest core #{dest.core.join(' ')}" end end dest end def fingerprint(arr) arr.map {|i| i.ident }.pack('L*') end # # DFA (Deterministic Finite Automaton) Generation # public def dfa return self if @dfa_computed nfa compute_dfa @dfa_computed = true self end private def compute_dfa la = lookahead() @states.each do |state| state.la = la resolve state end set_accept @states.each do |state| pack state end check_useless end def lookahead # # lookahead algorithm ver.3 -- from bison 1.26 # gotos = @gotos if @d_la puts "\n--- goto ---" gotos.each_with_index {|g, i| print i, ' '; p g } end ### initialize_LA() ### set_goto_map() la_rules = [] @states.each do |state| state.check_la la_rules end ### initialize_F() f = create_tmap(gotos.size) reads = [] edge = [] gotos.each do |goto| goto.to_state.goto_table.each do |t, st| if t.terminal? f[goto.ident] |= (1 << t.ident) elsif t.nullable? edge.push goto.to_state.gotos[t].ident end end if edge.empty? reads.push nil else reads.push edge edge = [] end end digraph f, reads if @d_la puts "\n--- F1 (reads) ---" print_tab gotos, reads, f end ### build_relations() ### compute_FOLLOWS path = nil edge = [] lookback = Array.new(la_rules.size, nil) includes = [] gotos.each do |goto| goto.symbol.heads.each do |ptr| path = record_path(goto.from_state, ptr.rule) lastgoto = path.last st = lastgoto ? lastgoto.to_state : goto.from_state if st.conflict? addrel lookback, st.rruleid(ptr.rule), goto end path.reverse_each do |g| break if g.symbol.terminal? edge.push g.ident break unless g.symbol.nullable? end end if edge.empty? includes.push nil else includes.push edge edge = [] end end includes = transpose(includes) digraph f, includes if @d_la puts "\n--- F2 (includes) ---" print_tab gotos, includes, f end ### compute_lookaheads la = create_tmap(la_rules.size) lookback.each_with_index do |arr, i| if arr arr.each do |g| la[i] |= f[g.ident] end end end if @d_la puts "\n--- LA (lookback) ---" print_tab la_rules, lookback, la end la end def create_tmap(size) Array.new(size, 0) # use Integer as bitmap end def addrel(tbl, i, item) if a = tbl[i] a.push item else tbl[i] = [item] end end def record_path(begst, rule) st = begst path = [] rule.symbols.each do |t| goto = st.gotos[t] path.push goto st = goto.to_state end path end def transpose(rel) new = Array.new(rel.size, nil) rel.each_with_index do |arr, idx| if arr arr.each do |i| addrel new, i, idx end end end new end def digraph(map, relation) n = relation.size index = Array.new(n, nil) vertices = [] @infinity = n + 2 index.each_index do |i| if not index[i] and relation[i] traverse i, index, vertices, map, relation end end end def traverse(i, index, vertices, map, relation) vertices.push i index[i] = height = vertices.size if rp = relation[i] rp.each do |proci| unless index[proci] traverse proci, index, vertices, map, relation end if index[i] > index[proci] # circulative recursion !!! index[i] = index[proci] end map[i] |= map[proci] end end if index[i] == height while true proci = vertices.pop index[proci] = @infinity break if i == proci map[proci] |= map[i] end end end # for debug def print_atab(idx, tab) tab.each_with_index do |i,ii| printf '%-20s', idx[ii].inspect p i end end def print_tab(idx, rel, tab) tab.each_with_index do |bin,i| print i, ' ', idx[i].inspect, ' << '; p rel[i] print ' ' each_t(@symboltable, bin) {|t| print ' ', t } puts end end # for debug def print_tab_i(idx, rel, tab, i) bin = tab[i] print i, ' ', idx[i].inspect, ' << '; p rel[i] print ' ' each_t(@symboltable, bin) {|t| print ' ', t } end # for debug def printb(i) each_t(@symboltable, i) do |t| print t, ' ' end puts end def each_t(tbl, set) 0.upto( set.size ) do |i| (0..7).each do |ii| if set[idx = i * 8 + ii] == 1 yield tbl[idx] end end end end # # resolve # def resolve(state) if state.conflict? resolve_rr state, state.ritems resolve_sr state, state.stokens else if state.rrules.empty? # shift state.stokens.each do |t| state.action[t] = @actions.shift(state.goto_table[t]) end else # reduce state.defact = @actions.reduce(state.rrules[0]) end end end def resolve_rr(state, r) r.each do |item| item.each_la(@symboltable) do |t| act = state.action[t] if act unless act.kind_of?(Reduce) raise "racc: fatal: #{act.class} in action table" end # Cannot resolve R/R conflict (on t). # Reduce with upper rule as default. state.rr_conflict act.rule, item.rule, t else # No conflict. state.action[t] = @actions.reduce(item.rule) end end end end def resolve_sr(state, s) s.each do |stok| goto = state.goto_table[stok] act = state.action[stok] unless act # no conflict state.action[stok] = @actions.shift(goto) else unless act.kind_of?(Reduce) puts 'DEBUG -------------------------------' p stok p act state.action.each do |k,v| print k.inspect, ' ', v.inspect, "\n" end raise "racc: fatal: #{act.class} in action table" end # conflict on stok rtok = act.rule.precedence case do_resolve_sr(stok, rtok) when :Reduce # action is already set when :Shift # overwrite act.decref state.action[stok] = @actions.shift(goto) when :Error act.decref state.action[stok] = @actions.error when :CantResolve # shift as default act.decref state.action[stok] = @actions.shift(goto) state.sr_conflict stok, act.rule end end end end ASSOC = { :Left => :Reduce, :Right => :Shift, :Nonassoc => :Error } def do_resolve_sr(stok, rtok) puts "resolve_sr: s/r conflict: rtok=#{rtok}, stok=#{stok}" if @d_prec unless rtok and rtok.precedence puts "resolve_sr: no prec for #{rtok}(R)" if @d_prec return :CantResolve end rprec = rtok.precedence unless stok and stok.precedence puts "resolve_sr: no prec for #{stok}(S)" if @d_prec return :CantResolve end sprec = stok.precedence ret = if rprec == sprec ASSOC[rtok.assoc] or raise "racc: fatal: #{rtok}.assoc is not Left/Right/Nonassoc" else (rprec > sprec) ? (:Reduce) : (:Shift) end puts "resolve_sr: resolved as #{ret.id2name}" if @d_prec ret end # # complete # def set_accept anch = @symboltable.anchor init_state = @states[0].goto_table[@grammar.start] targ_state = init_state.action[anch].goto_state acc_state = targ_state.action[anch].goto_state acc_state.action.clear acc_state.goto_table.clear acc_state.defact = @actions.accept end def pack(state) ### find most frequently used reduce rule act = state.action arr = Array.new(@grammar.size, 0) act.each do |t, a| arr[a.ruleid] += 1 if a.kind_of?(Reduce) end i = arr.max s = (i > 0) ? arr.index(i) : nil ### set & delete default action if s r = @actions.reduce(s) if not state.defact or state.defact == r act.delete_if {|t, a| a == r } state.defact = r end else state.defact ||= @actions.error end end def check_useless used = [] @actions.each_reduce do |act| if not act or act.refn == 0 act.rule.useless = true else t = act.rule.target used[t.ident] = t end end @symboltable.nt_base.upto(@symboltable.nt_max - 1) do |n| unless used[n] @symboltable[n].useless = true end end end end # class StateTable # A LALR state. class State def initialize(ident, core) @ident = ident @core = core @goto_table = {} @gotos = {} @stokens = nil @ritems = nil @action = {} @defact = nil @rrconf = nil @srconf = nil @closure = make_closure(@core) end attr_reader :ident alias stateid ident alias hash ident attr_reader :core attr_reader :closure attr_reader :goto_table attr_reader :gotos attr_reader :stokens attr_reader :ritems attr_reader :rrules attr_reader :action attr_accessor :defact # default action attr_reader :rrconf attr_reader :srconf def inspect "" end alias to_s inspect def ==(oth) @ident == oth.ident end alias eql? == def make_closure(core) set = ISet.new core.each do |ptr| set.add ptr if t = ptr.dereference and t.nonterminal? set.update_a t.expand end end set.to_a end def check_la(la_rules) @conflict = false s = [] r = [] @closure.each do |ptr| if t = ptr.dereference if t.terminal? s[t.ident] = t if t.ident == 1 # $error @conflict = true end end else r.push ptr.rule end end unless r.empty? if not s.empty? or r.size > 1 @conflict = true end end s.compact! @stokens = s @rrules = r if @conflict @la_rules_i = la_rules.size @la_rules = r.map {|i| i.ident } la_rules.concat r else @la_rules_i = @la_rules = nil end end def conflict? @conflict end def rruleid(rule) if i = @la_rules.index(rule.ident) @la_rules_i + i else puts '/// rruleid' p self p rule p @rrules p @la_rules_i raise 'racc: fatal: cannot get reduce rule id' end end def la=(la) return unless @conflict i = @la_rules_i @ritems = r = [] @rrules.each do |rule| r.push Item.new(rule, la[i]) i += 1 end end def rr_conflict(high, low, ctok) c = RRconflict.new(@ident, high, low, ctok) @rrconf ||= {} if a = @rrconf[ctok] a.push c else @rrconf[ctok] = [c] end end def sr_conflict(shift, reduce) c = SRconflict.new(@ident, shift, reduce) @srconf ||= {} if a = @srconf[shift] a.push c else @srconf[shift] = [c] end end def n_srconflicts @srconf ? @srconf.size : 0 end def n_rrconflicts @rrconf ? @rrconf.size : 0 end end # class State # # Represents a transition on the grammar. # "Real goto" means a transition by nonterminal, # but this class treats also terminal's. # If one is a terminal transition, .ident returns nil. # class Goto def initialize(ident, sym, from, to) @ident = ident @symbol = sym @from_state = from @to_state = to end attr_reader :ident attr_reader :symbol attr_reader :from_state attr_reader :to_state def inspect "(#{@from_state.ident}-#{@symbol}->#{@to_state.ident})" end end # LALR item. A set of rule and its lookahead tokens. class Item def initialize(rule, la) @rule = rule @la = la end attr_reader :rule attr_reader :la def each_la(tbl) la = @la 0.upto(la.size - 1) do |i| (0..7).each do |ii| if la[idx = i * 8 + ii] == 1 yield tbl[idx] end end end end end # The table of LALR actions. Actions are either of # Shift, Reduce, Accept and Error. class ActionTable def initialize(rt, st) @grammar = rt @statetable = st @reduce = [] @shift = [] @accept = nil @error = nil end def init @grammar.each do |rule| @reduce.push Reduce.new(rule) end @statetable.each do |state| @shift.push Shift.new(state) end @accept = Accept.new @error = Error.new end def reduce_n @reduce.size end def reduce(i) case i when Rule then i = i.ident when Integer then ; else raise "racc: fatal: wrong class #{i.class} for reduce" end r = @reduce[i] or raise "racc: fatal: reduce action #{i.inspect} not exist" r.incref r end def each_reduce(&block) @reduce.each(&block) end def shift_n @shift.size end def shift(i) case i when State then i = i.ident when Integer then ; else raise "racc: fatal: wrong class #{i.class} for shift" end @shift[i] or raise "racc: fatal: shift action #{i} does not exist" end def each_shift(&block) @shift.each(&block) end attr_reader :accept attr_reader :error end class Shift def initialize(goto) @goto_state = goto end attr_reader :goto_state def goto_id @goto_state.ident end def inspect "" end end class Reduce def initialize(rule) @rule = rule @refn = 0 end attr_reader :rule attr_reader :refn def ruleid @rule.ident end def inspect "" end def incref @refn += 1 end def decref @refn -= 1 raise 'racc: fatal: act.refn < 0' if @refn < 0 end end class Accept def inspect "" end end class Error def inspect "" end end class SRconflict def initialize(sid, shift, reduce) @stateid = sid @shift = shift @reduce = reduce end attr_reader :stateid attr_reader :shift attr_reader :reduce def to_s sprintf('state %d: S/R conflict rule %d reduce and shift %s', @stateid, @reduce.ruleid, @shift.to_s) end end class RRconflict def initialize(sid, high, low, tok) @stateid = sid @high_prec = high @low_prec = low @token = tok end attr_reader :stateid attr_reader :high_prec attr_reader :low_prec attr_reader :token def to_s sprintf('state %d: R/R conflict with rule %d and %d on %s', @stateid, @high_prec.ident, @low_prec.ident, @token.to_s) end end end racc-1.4.14/lib/racc/static.rb0000644000004100000410000000021112625454503016033 0ustar www-datawww-datarequire 'racc' require 'racc/parser' require 'racc/grammarfileparser' require 'racc/parserfilegenerator' require 'racc/logfilegenerator' racc-1.4.14/metadata.yml0000644000004100000410000001156712625454503015064 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: racc version: !ruby/object:Gem::Version version: 1.4.14 platform: ruby authors: - Aaron Patterson autorequire: bindir: bin cert_chain: [] date: 2015-11-25 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: rdoc requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '4.0' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '4.0' - !ruby/object:Gem::Dependency name: rake-compiler requirement: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 0.4.1 type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 0.4.1 - !ruby/object:Gem::Dependency name: minitest requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '4.7' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '4.7' - !ruby/object:Gem::Dependency name: hoe requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '3.14' type: :development prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '3.14' description: |- Racc is a LALR(1) parser generator. It is written in Ruby itself, and generates Ruby program. NOTE: Ruby 1.8.x comes with Racc runtime module. You can run your parsers generated by racc 1.4.x out of the box. email: - aaron@tenderlovemaking.com executables: - racc - racc2y - y2racc extensions: - ext/racc/extconf.rb extra_rdoc_files: - Manifest.txt - README.ja.rdoc - README.rdoc - rdoc/en/NEWS.en.rdoc - rdoc/en/grammar.en.rdoc - rdoc/ja/NEWS.ja.rdoc - rdoc/ja/debug.ja.rdoc - rdoc/ja/grammar.ja.rdoc - rdoc/ja/parser.ja.rdoc files: - COPYING - ChangeLog - DEPENDS - Manifest.txt - README.ja.rdoc - README.rdoc - Rakefile - TODO - bin/racc - bin/racc2y - bin/y2racc - ext/racc/MANIFEST - ext/racc/com/headius/racc/Cparse.java - ext/racc/cparse.c - ext/racc/depend - ext/racc/extconf.rb - fastcache/extconf.rb - fastcache/fastcache.c - lib/racc.rb - lib/racc/compat.rb - lib/racc/debugflags.rb - lib/racc/exception.rb - lib/racc/grammar.rb - lib/racc/grammarfileparser.rb - lib/racc/info.rb - lib/racc/iset.rb - lib/racc/logfilegenerator.rb - lib/racc/parser-text.rb - lib/racc/parser.rb - lib/racc/parserfilegenerator.rb - lib/racc/pre-setup - lib/racc/sourcetext.rb - lib/racc/state.rb - lib/racc/statetransitiontable.rb - lib/racc/static.rb - misc/dist.sh - rdoc/en/NEWS.en.rdoc - rdoc/en/grammar.en.rdoc - rdoc/ja/NEWS.ja.rdoc - rdoc/ja/command.ja.html - rdoc/ja/debug.ja.rdoc - rdoc/ja/grammar.ja.rdoc - rdoc/ja/index.ja.html - rdoc/ja/parser.ja.rdoc - rdoc/ja/usage.ja.html - sample/array.y - sample/array2.y - sample/calc-ja.y - sample/calc.y - sample/conflict.y - sample/hash.y - sample/lalr.y - sample/lists.y - sample/syntax.y - sample/yyerr.y - setup.rb - tasks/doc.rb - tasks/email.rb - test/assets/chk.y - test/assets/conf.y - test/assets/digraph.y - test/assets/echk.y - test/assets/err.y - test/assets/error_recovery.y - test/assets/expect.y - test/assets/firstline.y - test/assets/ichk.y - test/assets/intp.y - test/assets/mailp.y - test/assets/newsyn.y - test/assets/noend.y - test/assets/nonass.y - test/assets/normal.y - test/assets/norule.y - test/assets/nullbug1.y - test/assets/nullbug2.y - test/assets/opt.y - test/assets/percent.y - test/assets/recv.y - test/assets/rrconf.y - test/assets/scan.y - test/assets/syntax.y - test/assets/unterm.y - test/assets/useless.y - test/assets/yyerr.y - test/bench.y - test/helper.rb - test/infini.y - test/scandata/brace - test/scandata/gvar - test/scandata/normal - test/scandata/percent - test/scandata/slash - test/src.intp - test/start.y - test/test_chk_y.rb - test/test_grammar_file_parser.rb - test/test_racc_command.rb - test/test_scan_y.rb - test/testscanner.rb - web/racc.en.rhtml - web/racc.ja.rhtml homepage: http://i.loveruby.net/en/projects/racc/ licenses: - MIT metadata: {} post_install_message: rdoc_options: - "--main" - README.rdoc require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: rubygems_version: 2.5.0 signing_key: specification_version: 4 summary: Racc is a LALR(1) parser generator test_files: [] racc-1.4.14/test/0000755000004100000410000000000012625454503013526 5ustar www-datawww-dataracc-1.4.14/test/helper.rb0000644000004100000410000000433212625454503015334 0ustar www-datawww-data$VERBOSE = true require 'minitest/autorun' require 'racc/static' require 'fileutils' require 'tempfile' require 'timeout' module Racc class TestCase < MiniTest::Unit::TestCase PROJECT_DIR = File.expand_path(File.join(File.dirname(__FILE__), '..')) TEST_DIR = File.join(PROJECT_DIR, 'test') RACC = File.join(PROJECT_DIR, 'bin', 'racc') OUT_DIR = File.join(TEST_DIR, 'out') TAB_DIR = File.join(TEST_DIR, 'tab') LOG_DIR = File.join(TEST_DIR, 'log') ERR_DIR = File.join(TEST_DIR, 'err') ASSET_DIR = File.join(TEST_DIR, 'assets') INC = [ File.join(PROJECT_DIR, 'lib'), File.join(PROJECT_DIR, 'ext'), ].join(':') def setup [OUT_DIR, TAB_DIR, LOG_DIR, ERR_DIR].each do |dir| FileUtils.mkdir_p(dir) end end def teardown [OUT_DIR, TAB_DIR, LOG_DIR, ERR_DIR].each do |dir| FileUtils.rm_rf(dir) end end def assert_compile asset, args = [] asset = File.basename(asset, '.y') args = ([args].flatten) + [ "#{ASSET_DIR}/#{asset}.y", '-Do', "-O#{OUT_DIR}/#{asset}", "-o#{TAB_DIR}/#{asset}", ] racc "#{args.join(' ')}" end def assert_debugfile asset, ok name = File.basename(asset, '.y') Dir.chdir(TEST_DIR) do File.foreach("log/#{name}.y") do |line| line.strip! case line when /sr/ then assert_equal "sr#{ok[0]}", line when /rr/ then assert_equal "rr#{ok[1]}", line when /un/ then assert_equal "un#{ok[2]}", line when /ur/ then assert_equal "ur#{ok[3]}", line when /ex/ then assert_equal "ex#{ok[4]}", line else raise TestFailed, 'racc outputs unknown debug report???' end end end end def assert_exec file file = File.basename(file, '.y') Dir.chdir(TEST_DIR) do ruby("tab/#{file}") end end def racc arg ruby "-S #{RACC} #{arg}" end def ruby arg Dir.chdir(TEST_DIR) do Tempfile.open 'test' do |io| cmd = "#{ENV['_'] || Gem.ruby} -I #{INC} #{arg} 2>#{io.path}" result = system(cmd) assert(result, io.read) end end end end end racc-1.4.14/test/test_scan_y.rb0000644000004100000410000000276012625454503016373 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) module Racc class TestScanY < TestCase def setup file = File.join(ASSET_DIR, 'scan.y') @debug_flags = Racc::DebugFlags.parse_option_string('o') parser = Racc::GrammarFileParser.new(@debug_flags) @result = parser.parse(File.read(file), File.basename(file)) @states = Racc::States.new(@result.grammar).nfa @states.dfa end def test_compile generator = Racc::ParserFileGenerator.new(@states, @result.params.dup) # it generates valid ruby assert Module.new { self.class_eval(generator.generate_parser) } grammar = @states.grammar assert_equal 0, @states.n_srconflicts assert_equal 0, @states.n_rrconflicts assert_equal 0, grammar.n_useless_nonterminals assert_equal 0, grammar.n_useless_rules assert_nil grammar.n_expected_srconflicts end def test_compile_line_convert params = @result.params.dup params.convert_line_all = true generator = Racc::ParserFileGenerator.new(@states, @result.params.dup) # it generates valid ruby assert Module.new { self.class_eval(generator.generate_parser) } grammar = @states.grammar assert_equal 0, @states.n_srconflicts assert_equal 0, @states.n_rrconflicts assert_equal 0, grammar.n_useless_nonterminals assert_equal 0, grammar.n_useless_rules assert_nil grammar.n_expected_srconflicts end end end racc-1.4.14/test/test_chk_y.rb0000644000004100000410000000305012625454503016205 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) module Racc class TestChkY < TestCase def setup file = File.join(ASSET_DIR, 'chk.y') @debug_flags = Racc::DebugFlags.parse_option_string('o') parser = Racc::GrammarFileParser.new(@debug_flags) @result = parser.parse(File.read(file), File.basename(file)) @states = Racc::States.new(@result.grammar).nfa @states.dfa end def test_compile_chk_y generator = Racc::ParserFileGenerator.new(@states, @result.params.dup) # it generates valid ruby assert Module.new { self.instance_eval(generator.generate_parser, __FILE__, __LINE__) } grammar = @states.grammar assert_equal 0, @states.n_srconflicts assert_equal 0, @states.n_rrconflicts assert_equal 0, grammar.n_useless_nonterminals assert_equal 0, grammar.n_useless_rules assert_nil grammar.n_expected_srconflicts end def test_compile_chk_y_line_convert params = @result.params.dup params.convert_line_all = true generator = Racc::ParserFileGenerator.new(@states, @result.params.dup) # it generates valid ruby assert Module.new { self.instance_eval(generator.generate_parser, __FILE__, __LINE__) } grammar = @states.grammar assert_equal 0, @states.n_srconflicts assert_equal 0, @states.n_rrconflicts assert_equal 0, grammar.n_useless_nonterminals assert_equal 0, grammar.n_useless_rules assert_nil grammar.n_expected_srconflicts end end end racc-1.4.14/test/scandata/0000755000004100000410000000000012625454503015304 5ustar www-datawww-dataracc-1.4.14/test/scandata/percent0000644000004100000410000000065112625454503016671 0ustar www-datawww-data{ 3 % 5 # mod 3%5 # mod 3% 5 # mod i % 5 # mod i%5 # mod i% 5 # mod call %{str} # string call(%{str}) # string %q{string} # string %Q{string} # string %r{string} # string %w(array) # array %x{array} # command string %{string} # string %_string_ # string %/string/ # regexp } racc-1.4.14/test/scandata/slash0000644000004100000410000000035712625454503016346 0ustar www-datawww-data{ # here's many '/'s i = 5/1 # div re = /regex/ # regexp i /= 5 # div result = 5 / 1 # div result = 5/ 1 # div call(/regex/) # regexp call /regex/ # regexp } racc-1.4.14/test/scandata/brace0000644000004100000410000000011012625454503016273 0ustar www-datawww-data{ { } { } { { { { } } } { { { {} } } } {} {} {} } } racc-1.4.14/test/scandata/normal0000644000004100000410000000007312625454503016517 0ustar www-datawww-data{ # comment result = "string".match(/regexp/)[0] } racc-1.4.14/test/scandata/gvar0000644000004100000410000000005212625454503016163 0ustar www-datawww-data{ $' $" $& $-a $/ $\ $( $1 $2 $3 $? $-i } racc-1.4.14/test/testscanner.rb0000644000004100000410000000176212625454503016412 0ustar www-datawww-data# # racc scanner tester # require 'racc/raccs' class ScanError < StandardError; end def testdata( dir, argv ) if argv.empty? then Dir.glob( dir + '/*' ) - Dir.glob( dir + '/*.swp' ) - [ dir + '/CVS' ] else argv.collect {|i| dir + '/' + i } end end if ARGV.delete '--print' then $raccs_print_type = true printonly = true else printonly = false end testdata( File.dirname($0) + '/scandata', ARGV ).each do |file| $stderr.print File.basename(file) + ': ' begin ok = File.read(file) s = Racc::GrammarFileScanner.new( ok ) sym, (val, lineno) = s.scan if printonly then $stderr.puts $stderr.puts val next end val = '{' + val + "}\n" sym == :ACTION or raise ScanError, 'is not action!' val == ok or raise ScanError, "\n>>>\n#{ok}----\n#{val}<<<" $stderr.puts 'ok' rescue => err $stderr.puts 'fail (' + err.type.to_s + ')' $stderr.puts err.message $stderr.puts err.backtrace $stderr.puts end end racc-1.4.14/test/start.y0000644000004100000410000000030312625454503015051 0ustar www-datawww-dataclass S start st rule n: D { result = 'no' } st : A B C n { result = 'ok' } end ---- inner def parse do_parse end ---- footer S.new.parse == 'ok' or raise 'start stmt not worked' racc-1.4.14/test/test_racc_command.rb0000644000004100000410000000705712625454503017531 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) module Racc class TestRaccCommand < TestCase def test_syntax_y assert_compile 'syntax.y', '-v' assert_debugfile 'syntax.y', [0,0,0,0,0] end def test_percent_y assert_compile 'percent.y' assert_debugfile 'percent.y', [] assert_exec 'percent.y' end def test_scan_y assert_compile 'scan.y' assert_debugfile 'scan.y', [] assert_exec 'scan.y' end def test_newsyn_y assert_compile 'newsyn.y' assert_debugfile 'newsyn.y', [] end def test_normal_y assert_compile 'normal.y' assert_debugfile 'normal.y', [] assert_compile 'normal.y', '-vg' assert_debugfile 'normal.y', [] end def test_chk_y assert_compile 'chk.y', '-vg' assert_debugfile 'chk.y', [] assert_exec 'chk.y' assert_compile 'chk.y', '--line-convert-all' assert_debugfile 'chk.y', [] assert_exec 'chk.y' end def test_echk_y assert_compile 'echk.y', '-E' assert_debugfile 'echk.y', [] assert_exec 'echk.y' end def test_err_y assert_compile 'err.y' assert_debugfile 'err.y', [] assert_exec 'err.y' end def test_mailp_y assert_compile 'mailp.y' assert_debugfile 'mailp.y', [] end def test_conf_y assert_compile 'conf.y', '-v' assert_debugfile 'conf.y', [4,1,1,2] end def test_rrconf_y assert_compile 'rrconf.y' assert_debugfile 'rrconf.y', [1,1,0,0] end def test_useless_y assert_compile 'useless.y' assert_debugfile 'useless.y', [0,0,1,2] end def test_opt_y assert_compile 'opt.y' assert_debugfile 'opt.y', [] assert_exec 'opt.y' end def test_yyerr_y assert_compile 'yyerr.y' assert_debugfile 'yyerr.y', [] assert_exec 'yyerr.y' end def test_recv_y assert_compile 'recv.y' assert_debugfile 'recv.y', [5,10,1,4] end def test_ichk_y assert_compile 'ichk.y' assert_debugfile 'ichk.y', [] assert_exec 'ichk.y' end def test_intp_y assert_compile 'intp.y' assert_debugfile 'intp.y', [] assert_exec 'intp.y' end def test_expect_y assert_compile 'expect.y' assert_debugfile 'expect.y', [1,0,0,0,1] end def test_nullbug1_y assert_compile 'nullbug1.y' assert_debugfile 'nullbug1.y', [0,0,0,0] end def test_nullbug2_y assert_compile 'nullbug2.y' assert_debugfile 'nullbug2.y', [0,0,0,0] end def test_firstline_y assert_compile 'firstline.y' assert_debugfile 'firstline.y', [] end def test_nonass_y assert_compile 'nonass.y' assert_debugfile 'nonass.y', [] assert_exec 'nonass.y' end def test_digraph_y assert_compile 'digraph.y' assert_debugfile 'digraph.y', [] assert_exec 'digraph.y' end def test_noend_y assert_compile 'noend.y' assert_debugfile 'noend.y', [] end def test_norule_y assert_raises(MiniTest::Assertion) { assert_compile 'norule.y' } end def test_unterm_y assert_raises(MiniTest::Assertion) { assert_compile 'unterm.y' } end # Regression test for a problem where error recovery at EOF would cause # a Racc-generated parser to go into an infinite loop (on some grammars) def test_error_recovery_y assert_compile 'error_recovery.y' Timeout.timeout(10) do assert_exec 'error_recovery.y' end end end end racc-1.4.14/test/bench.y0000644000004100000410000000071712625454503015004 0ustar www-datawww-dataclass BenchmarkParser rule target: a a a a a a a a a a; a: b b b b b b b b b b; b: c c c c c c c c c c; c: d d d d d d d d d d; d: e e e e e e e e e e; end ---- inner def initialize @old = [ :e, 'e' ] @i = 0 end def next_token return [false, '$'] if @i >= 10_0000 @i += 1 @old end def parse do_parse end ---- footer require 'benchmark' Benchmark.bm do |x| x.report { BenchmarkParser.new.parse } end racc-1.4.14/test/assets/0000755000004100000410000000000012625454503015030 5ustar www-datawww-dataracc-1.4.14/test/assets/recv.y0000644000004100000410000000213712625454503016164 0ustar www-datawww-data# s/r 5, r/r 10 class A rule content: RecvH received ; datetime: day ; msgid: '<' spec '>'; day: | ATOM ',' ; received: recvitem_list recvdatetime ; recvitem_list: | recvitem_list recvitem ; recvitem: by | via | with | for ; by: | BY domain ; via: | VIA ATOM ; with: WITH ATOM ; for: | FOR addr ; recvdatetime: | ';' datetime ; addr: mbox | group ; mboxes: mbox | mboxes ',' mbox ; mbox: spec | routeaddr | phrase routeaddr ; group: phrase ':' mboxes ';' ; routeaddr: '<' route spec '>' | '<' spec '>' ; route: at_domains ':' ; at_domains: '@' domain | at_domains ',' '@' domain ; spec: local '@' domain | local ; local: word | local '.' word ; domain: domword | domain '.' domword ; domword: atom | DOMLIT | DIGIT ; phrase: word | phrase word ; word: atom | QUOTED | DIGIT ; atom: ATOM | FROM | BY | VIA | WITH | ID | FOR ; end racc-1.4.14/test/assets/chk.y0000644000004100000410000000362112625454503015771 0ustar www-datawww-data# # racc tester # class Calcp prechigh left '*' '/' left '+' '-' preclow convert NUMBER 'Number' end rule target : exp | /* none */ { result = 0 } ; exp : exp '+' exp { result += val[2]; @plus = 'plus' } | exp '-' exp { result -= val[2]; @str = "string test" } | exp '*' exp { result *= val[2] } | exp '/' exp { result /= val[2] } | '(' { $emb = true } exp ')' { raise 'must not happen' unless $emb result = val[2] } | '-' NUMBER { result = -val[1] } | NUMBER ; end ----header class Number; end ----inner def parse( src ) $emb = false @plus = nil @str = nil @src = src result = do_parse if @plus raise 'string parse failed' unless @plus == 'plus' end if @str raise 'string parse failed' unless @str == 'string test' end result end def next_token @src.shift end def initialize @yydebug = true end ----footer $parser = Calcp.new $test_number = 1 def chk( src, ans ) result = $parser.parse(src) raise "test #{$test_number} fail" unless result == ans $test_number += 1 end chk( [ [Number, 9], [false, false], [false, false] ], 9 ) chk( [ [Number, 5], ['*', nil], [Number, 1], ['-', nil], [Number, 1], ['*', nil], [Number, 8], [false, false], [false, false] ], -3 ) chk( [ [Number, 5], ['+', nil], [Number, 2], ['-', nil], [Number, 5], ['+', nil], [Number, 2], ['-', nil], [Number, 5], [false, false], [false, false] ], -1 ) chk( [ ['-', nil], [Number, 4], [false, false], [false, false] ], -4 ) chk( [ [Number, 7], ['*', nil], ['(', nil], [Number, 4], ['+', nil], [Number, 3], [')', nil], ['-', nil], [Number, 9], [false, false], [false, false] ], 40 ) racc-1.4.14/test/assets/newsyn.y0000644000004100000410000000056212625454503016550 0ustar www-datawww-data class A preclow left preclow prechigh right left nonassoc token right preclow prechigh right left nonassoc token nonassoc preclow prechigh right left nonassoc token prechigh convert left 'a' right 'b' preclow 'c' nonassoc 'd' preclow 'e' prechigh 'f' end rule left: right nonassoc preclow prechigh right: A B C end racc-1.4.14/test/assets/firstline.y0000644000004100000410000000003412625454503017216 0ustar www-datawww-dataclass T rule a: A B C end racc-1.4.14/test/assets/normal.y0000644000004100000410000000046212625454503016514 0ustar www-datawww-data class Testp convert A '2' B '3' end prechigh left B preclow rule /* comment */ target: A B C nonterminal { action "string" == /regexp/o 1 /= 3 } ; # comment nonterminal: A '+' B = A; /* end */ end ---- driver # driver is old name racc-1.4.14/test/assets/unterm.y0000644000004100000410000000005512625454503016534 0ustar www-datawww-data# unterminated action class A rule a: A { racc-1.4.14/test/assets/nullbug2.y0000644000004100000410000000024612625454503016756 0ustar www-datawww-data# # number of conflicts must be ZERO. # class A rule targ: operation voidhead | variable voidhead : void B void: operation: A variable : A end racc-1.4.14/test/assets/syntax.y0000644000004100000410000000115612625454503016553 0ustar www-datawww-data# # racc syntax checker # class M1::M2::ParserClass < S1::S2::SuperClass token A | B C convert A '5' end prechigh left B preclow start target expect 0 rule target: A B C { print 'abc' } | B C A | C B A { print 'cba' } | cont cont : A c2 B c2 C c2 : C C C C C end ---- inner junk code !!!! kjaljlajrlaolanbla /// %%% (*((( token rule akiurtlajluealjflaj @@@@ end end end end __END__ laieu2o879urkq96ga(Q#*&%Q# #&lkji END q395q?/// liutjqlkr7 racc-1.4.14/test/assets/conf.y0000644000004100000410000000017212625454503016147 0ustar www-datawww-data class A rule a: A c C expr; b: A B; # useless c: A; c: A; expr: expr '+' expr expr: expr '-' expr expr: NUMBER end racc-1.4.14/test/assets/err.y0000644000004100000410000000115012625454503016007 0ustar www-datawww-data class ErrTestp rule target: lines ; lines: line | lines line ; line: A B C D E | error E ; end ---- inner def initialize @yydebug = false @q = [ [:A, 'a'], # [:B, 'b'], [:C, 'c'], [:D, 'd'], [:E, 'e'], [:A, 'a'], [:B, 'b'], [:C, 'c'], [:D, 'd'], [:E, 'e'], [:A, 'a'], [:B, 'b'], # [:C, 'c'], [:D, 'd'], [:E, 'e'], [false, nil] ] end def next_token @q.shift end def on_error( t, val, values ) $stderr.puts "error on token '#{val}'(#{t})" end def parse do_parse end ---- footer p = ErrTestp.new p.parse racc-1.4.14/test/assets/useless.y0000644000004100000410000000011612625454503016703 0ustar www-datawww-data class A token A B C X rule targ : A list B | A B C list: list X end racc-1.4.14/test/assets/intp.y0000644000004100000410000002565712625454503016213 0ustar www-datawww-data# # intp # class Intp::Parser prechigh nonassoc UMINUS left '*' '/' left '+' '-' nonassoc EQ preclow rule program : stmt_list { result = RootNode.new( val[0] ) } stmt_list : { result = [] } | stmt_list stmt EOL { result.push val[1] } | stmt_list EOL stmt : expr | assign | IDENT realprim { result = FuncallNode.new( @fname, val[0][0], val[0][1], [val[1]] ) } | if_stmt | while_stmt | defun if_stmt : IF stmt THEN EOL stmt_list else_stmt END { result = IfNode.new( @fname, val[0][0], val[1], val[4], val[5] ) } else_stmt : ELSE EOL stmt_list { result = val[2] } | { result = nil } while_stmt: WHILE stmt DO EOL stmt_list END { result = WhileNode.new(@fname, val[0][0], val[1], val[4]) } defun : DEF IDENT param EOL stmt_list END { result = DefNode.new(@fname, val[0][0], val[1][1], Function.new(@fname, val[0][0], val[2], val[4])) } param : '(' name_list ')' { result = val[1] } | '(' ')' { result = [] } | { result = [] } name_list : IDENT { result = [ val[0][1] ] } | name_list ',' IDENT { result.push val[2][1] } assign : IDENT '=' expr { result = AssignNode.new(@fname, val[0][0], val[0][1], val[2]) } expr : expr '+' expr { result = FuncallNode.new(@fname, val[0].lineno, '+', [val[0], val[2]]) } | expr '-' expr { result = FuncallNode.new(@fname, val[0].lineno, '-', [val[0], val[2]]) } | expr '*' expr { result = FuncallNode.new(@fname, val[0].lineno, '*', [val[0], val[2]]) } | expr '/' expr { result = FuncallNode.new(@fname, val[0].lineno, '/', [val[0], val[2]]) } | expr EQ expr { result = FuncallNode.new(@fname, val[0].lineno, '==', [val[0], val[2]]) } | primary primary : realprim | '(' expr ')' { result = val[1] } | '-' expr =UMINUS { result = FuncallNode.new(@fname, val[0][0], '-@', [val[1]]) } realprim : IDENT { result = VarRefNode.new(@fname, val[0][0], val[0][1]) } | NUMBER { result = LiteralNode.new(@fname, *val[0]) } | STRING { result = StringNode.new(@fname, *val[0]) } | TRUE { result = LiteralNode.new(@fname, *val[0]) } | FALSE { result = LiteralNode.new(@fname, *val[0]) } | NIL { result = LiteralNode.new(@fname, *val[0]) } | funcall funcall : IDENT '(' args ')' { result = FuncallNode.new(@fname, val[0][0], val[0][1], val[2]) } | IDENT '(' ')' { result = FuncallNode.new(@fname, val[0][0], val[0][1], []) } args : expr { result = val } | args ',' expr { result.push val[2] } end ---- header # # intp/parser.rb # ---- inner def initialize @scope = {} end RESERVED = { 'if' => :IF, 'else' => :ELSE, 'while' => :WHILE, 'then' => :THEN, 'do' => :DO, 'def' => :DEF, 'true' => :TRUE, 'false' => :FALSE, 'nil' => :NIL, 'end' => :END } RESERVED_V = { 'true' => true, 'false' => false, 'nil' => nil } def parse(f, fname) @q = [] @fname = fname lineno = 1 f.each do |line| line.strip! until line.empty? case line when /\A\s+/, /\A\#.*/ ; when /\A[a-zA-Z_]\w*/ word = $& @q.push [(RESERVED[word] || :IDENT), [lineno, RESERVED_V.key?(word) ? RESERVED_V[word] : word.intern]] when /\A\d+/ @q.push [:NUMBER, [lineno, $&.to_i]] when /\A"(?:[^"\\]+|\\.)*"/, /\A'(?:[^'\\]+|\\.)*'/ @q.push [:STRING, [lineno, eval($&)]] when /\A==/ @q.push [:EQ, [lineno, '==']] when /\A./ @q.push [$&, [lineno, $&]] else raise RuntimeError, 'must not happen' end line = $' end @q.push [:EOL, [lineno, nil]] lineno += 1 end @q.push [false, '$'] do_parse end def next_token @q.shift end def on_error(t, v, values) if v line = v[0] v = v[1] else line = 'last' end raise Racc::ParseError, "#{@fname}:#{line}: syntax error on #{v.inspect}" end ---- footer # intp/node.rb module Intp class IntpError < StandardError; end class IntpArgumentError < IntpError; end class Core def initialize @ftab = {} @obj = Object.new @stack = [] @stack.push Frame.new '(toplevel)' end def frame @stack[-1] end def define_function(fname, node) raise IntpError, "function #{fname} defined twice" if @ftab.key?(fname) @ftab[fname] = node end def call_function_or(fname, args) call_intp_function_or(fname, args) { call_ruby_toplevel_or(fname, args) { yield } } end def call_intp_function_or(fname, args) if func = @ftab[fname] frame = Frame.new(fname) @stack.push frame func.call self, frame, args @stack.pop else yield end end def call_ruby_toplevel_or(fname, args) if @obj.respond_to? fname, true @obj.send fname, *args else yield end end end class Frame def initialize(fname) @fname = fname @lvars = {} end attr :fname def lvar?(name) @lvars.key? name end def [](key) @lvars[key] end def []=(key, val) @lvars[key] = val end end class Node def initialize(fname, lineno) @filename = fname @lineno = lineno end attr_reader :filename attr_reader :lineno def exec_list(intp, nodes) v = nil nodes.each {|i| v = i.evaluate(intp) } v end def intp_error!(msg) raise IntpError, "in #{filename}:#{lineno}: #{msg}" end def inspect "#{self.class.name}/#{lineno}" end end class RootNode < Node def initialize(tree) super nil, nil @tree = tree end def evaluate exec_list Core.new, @tree end end class DefNode < Node def initialize(file, lineno, fname, func) super file, lineno @funcname = fname @funcobj = func end def evaluate(intp) intp.define_function @funcname, @funcobj end end class FuncallNode < Node def initialize(file, lineno, func, args) super file, lineno @funcname = func @args = args end def evaluate(intp) args = @args.map {|i| i.evaluate intp } begin intp.call_intp_function_or(@funcname, args) { if args.empty? or not args[0].respond_to?(@funcname) intp.call_ruby_toplevel_or(@funcname, args) { intp_error! "undefined function #{@funcname.id2name}" } else recv = args.shift recv.send @funcname, *args end } rescue IntpArgumentError, ArgumentError intp_error! $!.message end end end class Function < Node def initialize(file, lineno, params, body) super file, lineno @params = params @body = body end def call(intp, frame, args) unless args.size == @params.size raise IntpArgumentError, "wrong # of arg for #{frame.fname}() (#{args.size} for #{@params.size})" end args.each_with_index do |v,i| frame[@params[i]] = v end exec_list intp, @body end end class IfNode < Node def initialize(fname, lineno, cond, tstmt, fstmt) super fname, lineno @condition = cond @tstmt = tstmt @fstmt = fstmt end def evaluate(intp) if @condition.evaluate(intp) exec_list intp, @tstmt else exec_list intp, @fstmt if @fstmt end end end class WhileNode < Node def initialize(fname, lineno, cond, body) super fname, lineno @condition = cond @body = body end def evaluate(intp) while @condition.evaluate(intp) exec_list intp, @body end end end class AssignNode < Node def initialize(fname, lineno, vname, val) super fname, lineno @vname = vname @val = val end def evaluate(intp) intp.frame[@vname] = @val.evaluate(intp) end end class VarRefNode < Node def initialize(fname, lineno, vname) super fname, lineno @vname = vname end def evaluate(intp) if intp.frame.lvar?(@vname) intp.frame[@vname] else intp.call_function_or(@vname, []) { intp_error! "unknown method or local variable #{@vname.id2name}" } end end end class StringNode < Node def initialize(fname, lineno, str) super fname, lineno @val = str end def evaluate(intp) @val.dup end end class LiteralNode < Node def initialize(fname, lineno, val) super fname, lineno @val = val end def evaluate(intp) @val end end end # module Intp begin tree = nil fname = 'src.intp' File.open(fname) {|f| tree = Intp::Parser.new.parse(f, fname) } tree.evaluate rescue Racc::ParseError, Intp::IntpError, Errno::ENOENT raise #### $stderr.puts "#{File.basename $0}: #{$!}" exit 1 end racc-1.4.14/test/assets/norule.y0000644000004100000410000000002212625454503016520 0ustar www-datawww-data class A rule end racc-1.4.14/test/assets/echk.y0000644000004100000410000000322712625454503016140 0ustar www-datawww-data# # racc tester # class Calcp prechigh left '*' '/' left '+' '-' preclow convert NUMBER 'Number' end rule target : exp | /* none */ { result = 0 } ; exp : exp '+' exp { result += val[2]; a = 'plus' } | exp '-' exp { result -= val[2]; "string test" } | exp '*' exp { result *= val[2] } | exp '/' exp { result /= val[2] } | '(' { $emb = true } exp ')' { raise 'must not happen' unless $emb result = val[2] } | '-' NUMBER { result = -val[1] } | NUMBER ; end ----header class Number ; end ----inner def parse( src ) @src = src do_parse end def next_token @src.shift end def initialize @yydebug = true end ----footer $parser = Calcp.new $tidx = 1 def chk( src, ans ) ret = $parser.parse( src ) unless ret == ans then bug! "test #{$tidx} fail" end $tidx += 1 end chk( [ [Number, 9], [false, false], [false, false] ], 9 ) chk( [ [Number, 5], ['*', nil], [Number, 1], ['-', nil], [Number, 1], ['*', nil], [Number, 8], [false, false], [false, false] ], -3 ) chk( [ [Number, 5], ['+', nil], [Number, 2], ['-', nil], [Number, 5], ['+', nil], [Number, 2], ['-', nil], [Number, 5], [false, false], [false, false] ], -1 ) chk( [ ['-', nil], [Number, 4], [false, false], [false, false] ], -4 ) chk( [ [Number, 7], ['*', nil], ['(', nil], [Number, 4], ['+', nil], [Number, 3], [')', nil], ['-', nil], [Number, 9], [false, false], [false, false] ], 40 ) racc-1.4.14/test/assets/rrconf.y0000644000004100000410000000016512625454503016515 0ustar www-datawww-data# 1 s/r conflict and 1 r/r conflict class A rule target: a a : | a list list : | list ITEM end racc-1.4.14/test/assets/error_recovery.y0000644000004100000410000000104112625454503020265 0ustar www-datawww-data# Regression test case for the bug discussed here: # https://github.com/whitequark/parser/issues/93 # In short, a Racc-generated parser could go into an infinite loop when # attempting error recovery at EOF class InfiniteLoop rule stmts: stmt | error stmt stmt: '%' stmt end ---- inner def parse @errors = [] do_parse end def next_token nil end def on_error(error_token, error_value, value_stack) # oh my, an error @errors << [error_token, error_value] end ---- footer InfiniteLoop.new.parseracc-1.4.14/test/assets/nonass.y0000644000004100000410000000065212625454503016526 0ustar www-datawww-data# # nonassoc test # class P preclow nonassoc N left P prechigh rule target : exp exp : exp N exp | exp P exp | T end ---- inner def parse @src = [[:T,'T'], [:N,'N'], [:T,'T'], [:N,'N'], [:T,'T']] do_parse end def next_token @src.shift end ---- footer begin P.new.parse rescue ParseError exit 0 else $stderr.puts 'parse error not raised: nonassoc not work' exit 1 end racc-1.4.14/test/assets/noend.y0000644000004100000410000000004512625454503016324 0ustar www-datawww-dataclass MyParser rule input: A B C end racc-1.4.14/test/assets/nullbug1.y0000644000004100000410000000026112625454503016752 0ustar www-datawww-data# # number of conflicts must be ZERO. # class T rule targ : dummy | a b c dummy : V v V : E e | F f | ; E : ; F : ; end racc-1.4.14/test/assets/expect.y0000644000004100000410000000011012625454503016502 0ustar www-datawww-dataclass E expect 1 rule list: inlist inlist inlist: | A end racc-1.4.14/test/assets/digraph.y0000644000004100000410000000043312625454503016640 0ustar www-datawww-data# ? detect digraph bug class P token A B C D rule target : a b c d a : A | b : B | c : C | d : D | end ---- inner def parse do_parse end def next_token [false, '$'] end ---- footer P.new.parse racc-1.4.14/test/assets/ichk.y0000644000004100000410000000324112625454503016140 0ustar www-datawww-dataclass Calculator prechigh left '*' '/' left '+' '-' preclow convert NUMBER 'Number' end rule target : exp | /* none */ { result = 0 } exp : exp '+' exp { result += val[2]; a = 'plus' } | exp '-' exp { result -= val[2]; a = "string test" } | exp '*' exp { result *= val[2] } | exp '/' exp { result /= val[2] } | '(' { $emb = true } exp ')' { raise 'must not happen' unless $emb result = val[2] } | '-' NUMBER { result = -val[1] } | NUMBER ----header class Number end ----inner def initialize @racc_debug_out = $stdout @yydebug = false end def validate(expected, src) result = parse(src) unless result == expected raise "test #{@test_number} fail" end @test_number += 1 end def parse(src) @src = src @test_number = 1 yyparse self, :scan end def scan(&block) @src.each(&block) end ----footer calc = Calculator.new calc.validate(9, [[Number, 9], nil]) calc.validate(-3, [[Number, 5], ['*', '*'], [Number, 1], ['-', '*'], [Number, 1], ['*', '*'], [Number, 8], nil]) calc.validate(-1, [[Number, 5], ['+', '+'], [Number, 2], ['-', '-'], [Number, 5], ['+', '+'], [Number, 2], ['-', '-'], [Number, 5], nil]) calc.validate(-4, [['-', 'UMINUS'], [Number, 4], nil]) calc.validate(40, [[Number, 7], ['*', '*'], ['(', '('], [Number, 4], ['+', '+'], [Number, 3], [')', ')'], ['-', '-'], [Number, 9], nil]) racc-1.4.14/test/assets/scan.y0000644000004100000410000000251412625454503016150 0ustar www-datawww-dataclass P rule a: A { # comment test # comment test # string @sstring = 'squote string' @dstring = 'dquote string' # regexp @regexp = /some regexp with spaces/ # gvar /regexp/ === 'some regexp matches to this string' @pre_match = $` @matched = $& @post_match = $' @m = $~ # braces @array = [] [1,2,3].each {|i| @array.push i } 3.times { @array.push 10 } } end ---- inner def parse @sstring = @dstring = nil @regexp = nil @pre_match = @matched = @post_match = @m = nil @src = [[:A, 'A'], [false, '$']] do_parse assert_equal 'squote string', @sstring assert_equal 'dquote string', @dstring assert_equal(/some regexp with spaces/, @regexp) assert_equal 'some ', @pre_match assert_equal 'regexp', @matched assert_equal ' matches to this string', @post_match assert_instance_of MatchData, @m end def assert_equal(ok, data) unless ok == data raise "expected <#{ok.inspect}> but is <#{data.inspect}>" end end def assert_instance_of(klass, obj) unless obj.instance_of?(klass) raise "expected #{klass} but is #{obj.class}" end end def next_token @src.shift end ---- footer P.new.parse racc-1.4.14/test/assets/yyerr.y0000644000004100000410000000071512625454503016377 0ustar www-datawww-data# # yyerror/yyerrok/yyaccept test # class A rule target: a b c a: { yyerror raise ArgumentError, "yyerror failed" } | error b: { yyerrok } c: { yyaccept raise ArgumentError, "yyaccept failed" } end ---- inner def parse do_parse end def next_token [false, '$end'] end def on_error( *args ) $stderr.puts "on_error called: args=#{args.inspect}" end ---- footer A.new.parse racc-1.4.14/test/assets/percent.y0000644000004100000410000000127312625454503016665 0ustar www-datawww-dataclass ScannerChecker rule target: A { i = 7 i %= 4 raise 'assert failed' unless i == 3 tmp = %-This is percent string.- raise 'assert failed' unless tmp == 'This is percent string.' a = 5; b = 3 assert_equal(2,(a%b)) #A # assert_equal(2,(a %b)) # is %-string assert_equal(2,(a% b)) #B assert_equal(2,(a % b)) #C } end ---- inner ---- def parse @q = [[:A, 'A'], [false, '$']] do_parse end def next_token @q.shift end def assert_equal( expect, real ) raise "expect #{expect.inspect} but #{real.inspect}" unless expect == real end ---- footer ---- parser = ScannerChecker.new.parse racc-1.4.14/test/assets/mailp.y0000644000004100000410000002273212625454503016332 0ustar www-datawww-data# # mailp for test # class Testp rule content : DateH datetime { @field.date = val[1] } | RecvH received | RetpathH returnpath | MaddrH addrs { @field.addrs.replace val[1] } | SaddrH addr { @field.addr = val[1] } | MmboxH mboxes { @field.addrs.replace val[1] } | SmboxH mbox { @field.addr = val[1] } | MsgidH msgid { @field.msgid = val[1] } | KeyH keys { @field.keys.replace val[1] } | EncH enc | VersionH version | CTypeH ctype | CEncodingH cencode | CDispositionH cdisp | Mbox mbox { mb = val[1] @field.phrase = mb.phrase @field.setroute mb.route @field.local = mb.local @field.domain = mb.domain } | Spec spec { mb = val[1] @field.local = mb.local @field.domain = mb.domain } ; datetime : day DIGIT ATOM DIGIT hour zone # 0 1 2 3 4 5 # day month year { t = Time.gm( val[3].to_i, val[2], val[1].to_i, 0, 0, 0 ) result = (t + val[4] - val[5]).localtime } ; day : /* none */ | ATOM ',' ; hour : DIGIT ':' DIGIT { result = (result.to_i * 60 * 60) + (val[2].to_i * 60) } | DIGIT ':' DIGIT ':' DIGIT { result = (result.to_i * 60 * 60) + (val[2].to_i * 60) + val[4].to_i } ; zone : ATOM { result = ::TMail.zonestr2i( val[0] ) * 60 } ; received : from by via with id for recvdatetime ; from : /* none */ | FROM domain { @field.from = Address.join( val[1] ) } | FROM domain '@' domain { @field.from = Address.join( val[3] ) } | FROM domain DOMLIT { @field.from = Address.join( val[1] ) } ; by : /* none */ | BY domain { @field.by = Address.join( val[1] ) } ; via : /* none */ | VIA ATOM { @field.via = val[1] } ; with : /* none */ | WITH ATOM { @field.with.push val[1] } ; id : /* none */ | ID msgid { @field.msgid = val[1] } | ID ATOM { @field.msgid = val[1] } ; for : /* none */ | FOR addr { @field.for_ = val[1].address } ; recvdatetime : /* none */ | ';' datetime { @field.date = val[1] } ; returnpath: '<' '>' | routeaddr { @field.route.replace result.route @field.addr = result.addr } ; addrs : addr { result = val } | addrs ',' addr { result.push val[2] } ; addr : mbox | group ; mboxes : mbox { result = val } | mboxes ',' mbox { result.push val[2] } ; mbox : spec | routeaddr | phrase routeaddr { val[1].phrase = HFdecoder.decode( result ) result = val[1] } ; group : phrase ':' mboxes ';' { result = AddressGroup.new( result, val[2] ) } # | phrase ':' ';' { result = AddressGroup.new( result ) } ; routeaddr : '<' route spec '>' { result = val[2] result.route = val[1] } | '<' spec '>' { result = val[1] } ; route : at_domains ':' ; at_domains: '@' domain { result = [ val[1] ] } | at_domains ',' '@' domain { result.push val[3] } ; spec : local '@' domain { result = Address.new( val[0], val[2] ) } | local { result = Address.new( result, nil ) } ; local : word { result = val } | local '.' word { result.push val[2] } ; domain : domword { result = val } | domain '.' domword { result.push val[2] } ; domword : atom | DOMLIT | DIGIT ; msgid : '<' spec '>' { val[1] = val[1].addr result = val.join('') } ; phrase : word | phrase word { result << ' ' << val[1] } ; word : atom | QUOTED | DIGIT ; keys : phrase | keys ',' phrase ; enc : word { @field.encrypter = val[0] } | word word { @field.encrypter = val[0] @field.keyword = val[1] } ; version : DIGIT '.' DIGIT { @field.major = val[0].to_i @field.minor = val[2].to_i } ; ctype : TOKEN '/' TOKEN params { @field.main = val[0] @field.sub = val[2] } | TOKEN params { @field.main = val[0] @field.sub = '' } ; params : /* none */ | params ';' TOKEN '=' value { @field.params[ val[2].downcase ] = val[4] } ; value : TOKEN | QUOTED ; cencode : TOKEN { @field.encoding = val[0] } ; cdisp : TOKEN disp_params { @field.disposition = val[0] } ; disp_params : /* none */ | disp_params ';' disp_param ; disp_param: /* none */ | TOKEN '=' value { @field.params[ val[0].downcase ] = val[2] } ; atom : ATOM | FROM | BY | VIA | WITH | ID | FOR ; end ---- header # # mailp for test # require 'tmail/mails' module TMail ---- inner MAILP_DEBUG = false def initialize self.debug = MAILP_DEBUG end def debug=( flag ) @yydebug = flag && Racc_debug_parser @scanner_debug = flag end def debug @yydebug end def Mailp.parse( str, obj, ident ) new.parse( str, obj, ident ) end NATIVE_ROUTINE = { 'TMail::MsgidH' => :msgid_parse, 'TMail::RefH' => :refs_parse } def parse( str, obj, ident ) return if /\A\s*\z/ === str @field = obj if mid = NATIVE_ROUTINE[ obj.type.name ] then send mid, str else unless ident then ident = obj.type.name.split('::')[-1].to_s cmt = [] obj.comments.replace cmt else cmt = nil end @scanner = MailScanner.new( str, ident, cmt ) @scanner.debug = @scanner_debug @first = [ ident.intern, ident ] @pass_array = [nil, nil] do_parse end end private def next_token if @first then ret = @first @first = nil ret else @scanner.scan @pass_array end end def on_error( tok, val, vstack ) raise ParseError, "\nparse error in '#{@field.name}' header, on token #{val.inspect}" end def refs_parse( str ) arr = [] while mdata = ::TMail::MSGID.match( str ) do str = mdata.post_match pre = mdata.pre_match pre.strip! proc_phrase pre, arr unless pre.empty? arr.push mdata.to_s end str.strip! proc_phrase str, arr if not pre or pre.empty? @field.refs.replace arr end def proc_phrase( str, arr ) while mdata = /"([^\\]*(?:\\.[^"\\]*)*)"/.match( str ) do str = mdata.post_match pre = mdata.pre_match pre.strip! arr.push pre unless pre.empty? arr.push mdata[1] end str.strip! arr.push unless str.empty? end def msgid_parse( str ) if mdata = ::TMail::MSGID.match( str ) then @field.msgid = mdata.to_s else raise ParseError, "wrong Message-ID format: #{str}" end end ---- footer end # module TMail mp = TMail::Testp.new mp.parse racc-1.4.14/test/assets/opt.y0000644000004100000410000000333212625454503016025 0ustar www-datawww-data# # check options working # class Calcp prechigh left '*' '/' left '+' '-' preclow convert NUMBER 'Number' end options no_omit_action_call no_result_var rule target : exp | /* none */ { 0 } ; exp : exp '+' exp { chk(val[0] + val[2]) } | exp '-' exp { chk(val[0] - val[2]) } | exp '*' exp { chk(val[0] * val[2]) } | exp '/' exp { chk(val[0] / val[2]) } | '(' { $emb = true } exp ')' { raise 'must not happen' unless $emb val[2] } | '-' NUMBER { -val[1] } | NUMBER ; end ----header class Number; end ----inner def parse( src ) @src = src do_parse end def next_token @src.shift end def initialize @yydebug = true end def chk( i ) # p i i end ----footer $parser = Calcp.new $test_number = 1 def chk( src, ans ) result = $parser.parse(src) raise "test #{$test_number} failed" unless result == ans $test_number += 1 end chk( [ [Number, 9], [false, false], [false, false] ], 9 ) chk( [ [Number, 5], ['*', nil], [Number, 1], ['-', nil], [Number, 1], ['*', nil], [Number, 8], [false, false], [false, false] ], -3 ) chk( [ [Number, 5], ['+', nil], [Number, 2], ['-', nil], [Number, 5], ['+', nil], [Number, 2], ['-', nil], [Number, 5], [false, false], [false, false] ], -1 ) chk( [ ['-', nil], [Number, 4], [false, false], [false, false] ], -4 ) chk( [ [Number, 7], ['*', nil], ['(', nil], [Number, 4], ['+', nil], [Number, 3], [')', nil], ['-', nil], [Number, 9], [false, false], [false, false] ], 40 ) racc-1.4.14/test/src.intp0000644000004100000410000000103012625454503015203 0ustar www-datawww-datadef assert( no, cond ) if cond then else raise( 'assert ' + to_s(no) + ' failed' ) end end assert( 1, concat(concat(concat('str=', 'a'), "b"), 'c') == 'str=abc' ) assert( 2, 'operator' + ' ok' == 'operator ok' ) assert( 3, 1 + 1 == 2 ) assert( 4, 4 * 1 + 10 * 1 == 14 ) if true then assert( 5, true ) else assert( 6, false ) end i = 1 while i == 1 do i = false end assert( 7, i == false ) assert( 8, nil == nil ) def func assert( 9, true ) end func def argfunc( str ) assert( 10, str == 'ok' ) end argfunc 'ok' racc-1.4.14/test/test_grammar_file_parser.rb0000644000004100000410000000064712625454503021122 0ustar www-datawww-datarequire File.expand_path(File.join(File.dirname(__FILE__), 'helper')) module Racc class TestGrammarFileParser < TestCase def test_parse file = File.join(ASSET_DIR, 'yyerr.y') debug_flags = Racc::DebugFlags.parse_option_string('o') assert debug_flags.status_logging parser = Racc::GrammarFileParser.new(debug_flags) parser.parse(File.read(file), File.basename(file)) end end end racc-1.4.14/test/infini.y0000644000004100000410000000004212625454503015170 0ustar www-datawww-data class I rule list: list X end racc-1.4.14/README.ja.rdoc0000644000004100000410000000564212625454503014755 0ustar www-datawww-data= Racc * http://i.loveruby.net/en/projects/racc/ * http://racc.rubyforge.org/ == DESCRIPTION: Racc は LALR(1) パーサジェネレータです。 yacc の Ruby 版に相当します。 NOTE: Ruby 1.8.0 からは Racc のランタイムが標準添付されているので、 Racc で生成したパーサを安心して配布できます。また Ruby 1.6 系に 対応させたい場合は racc -E で生成してください。 == 必要環境 * Ruby 1.8 以降 (*) C コンパイラと make == インストール gem インストール: $ gem install racc setup.rb インストル: パッケージのトップディレクトリで次のように入力してください。 ($ は通常ユーザ、# はルートのプロンプトです) $ ruby setup.rb config $ ruby setup.rb setup ($ su) # ruby setup.rb install これで通常のパスに Racc がインストールされます。自分の好き なディレクトリにインストールしたいときは、setup.rb config に 各種オプションをつけて実行してください。オプションのリストは $ ruby setup.rb --help で見られます。 コンパイラがない場合 -------------------- config を以下のようにすれば、拡張モジュールなしで インストールできます。 $ ruby setup.rb config --without-ext == テスト sample/ 以下にいくつか Racc の文法ファイルのサンプルが用意 してあります。動くのも動かないのもありますが、少なくとも calc-ja.y は動くのでこれを処理してみましょう。Racc をインス トールしたあと $ racc -ocalc.rb calc-ja.y として下さい。処理は一瞬から数秒で終わるので、 $ ruby calc.rb を実行してください。ちゃんと動いてますか? Racc の文法など詳しいことは doc.ja/ ディレクトリ以下の HTML を 見てください。 == ライセンス このパッケージに付属するファイルの著作権は青木峰郎が保持します。 ライセンスは GNU Lesser General Public License (LGPL) version 2 です。ただしユーザが書いた規則ファイルや、Racc がそこから生成し た Ruby スクリプトはその対象外です。好きなライセンスで配布して ください。 == バグなど Racc を使っていてバグらしき現象に遭遇したら、下記のアドレスまで メールをください。作者にはバグを修正する義務はありませんがその 意思はあります。また、そのときはできるだけバグを再現できる文法 ファイルを付けてください。 青木峰郎(あおきみねろう) aamine@loveruby.net http://i.loveruby.net racc-1.4.14/tasks/0000755000004100000410000000000012625454503013674 5ustar www-datawww-dataracc-1.4.14/tasks/doc.rb0000644000004100000410000000045112625454503014766 0ustar www-datawww-datarequire 'rake/rdoctask' Rake::RDocTask.new(:docs) do |rd| rd.main = "README.en.rdoc" rd.rdoc_files.include(SPEC.files.find_all { |file_name| file_name =~ /^(bin|lib|ext)/ || file_name !~ /\// }) title = "#{SPEC.name}-#{SPEC.version} Documentation" rd.options << "-t #{title}" end racc-1.4.14/tasks/email.rb0000644000004100000410000000271212625454503015312 0ustar www-datawww-dataclass EmailTask def initialize language, readme, changelog @language = language @readme = readme @changelog = changelog @languages = { :en => { :release => 'release', :released => 'has been released', }, :ja => { :release => 'リリース', :released => 'はリリースしました', } } define_tasks end private def define_tasks namespace :email do task @language do subject = "#{SPEC.name} #{SPEC.version} #{@languages[@language][:release]}" title = "#{SPEC.name} #{SPEC.version} #{@languages[@language][:released]}!" readme = Hash[*(File.read(@readme).split(/^(=+ .*)$/)[1..-1])] description = readme[readme.keys.find { |x| x =~ /description/i }] description = description.split(/\n\n+/).find { |x| x.length > 0 }.gsub(/^\s*/, '') urls = readme[readme.keys.find { |x| x =~ /#{SPEC.name}/i }] urls = urls.strip.gsub(/\*\s/, '').split(/\n/).map { |s| "* <#{s}>" } File.open("email.#{@language}.txt", "wb") { |file| file.puts(<<-eomail) Subject: [ANN] #{subject} #{title} #{urls.join("\n")} #{description} Changes: #{File.read(@changelog).split(/^(===.*)/)[1..2].join.strip.gsub(/=/, '#')} #{urls.join("\n")} eomail } end end end end EmailTask.new(:en, 'README.en.rdoc', 'doc/en/NEWS.en.rdoc') EmailTask.new(:ja, 'README.ja.rdoc', 'doc/ja/NEWS.ja.rdoc') racc-1.4.14/setup.rb0000644000004100000410000010655712625454503014252 0ustar www-datawww-data# # setup.rb # # Copyright (c) 2000-2006 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # unless Enumerable.method_defined?(:map) # Ruby 1.4.6 module Enumerable alias map collect end end unless File.respond_to?(:read) # Ruby 1.6 def File.read(fname) open(fname) {|f| return f.read } end end unless Errno.const_defined?(:ENOTEMPTY) # Windows? module Errno class ENOTEMPTY # We do not raise this exception, implementation is not needed. end end end def File.binread(fname) open(fname, 'rb') {|f| return f.read } end # for corrupted Windows' stat(2) def File.dir?(path) File.directory?((path[-1,1] == '/') ? path : path + '/') end class ConfigTable include Enumerable def initialize(rbconfig) @rbconfig = rbconfig @items = [] @table = {} # options @install_prefix = nil @config_opt = nil @verbose = true @no_harm = false end attr_accessor :install_prefix attr_accessor :config_opt attr_writer :verbose def verbose? @verbose end attr_writer :no_harm def no_harm? @no_harm end def [](key) lookup(key).resolve(self) end def []=(key, val) lookup(key).set val end def names @items.map {|i| i.name } end def each(&block) @items.each(&block) end def key?(name) @table.key?(name) end def lookup(name) @table[name] or setup_rb_error "no such config item: #{name}" end def add(item) @items.push item @table[item.name] = item end def remove(name) item = lookup(name) @items.delete_if {|i| i.name == name } @table.delete_if {|name, i| i.name == name } item end def load_script(path, inst = nil) if File.file?(path) MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path end end def savefile '.config' end def load_savefile begin File.foreach(savefile()) do |line| k, v = *line.split(/=/, 2) self[k] = v.strip end rescue Errno::ENOENT setup_rb_error $!.message + "\n#{File.basename($0)} config first" end end def save @items.each {|i| i.value } File.open(savefile(), 'w') {|f| @items.each do |i| f.printf "%s=%s\n", i.name, i.value if i.value? and i.value end } end def load_standard_entries standard_entries(@rbconfig).each do |ent| add ent end end def standard_entries(rbconfig) c = rbconfig rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT']) major = c['MAJOR'].to_i minor = c['MINOR'].to_i teeny = c['TEENY'].to_i version = "#{major}.#{minor}" # ruby ver. >= 1.4.4? newpath_p = ((major >= 2) or ((major == 1) and ((minor >= 5) or ((minor == 4) and (teeny >= 4))))) if c['rubylibdir'] # V > 1.6.3 libruby = "#{c['prefix']}/lib/ruby" librubyver = c['rubylibdir'] librubyverarch = c['archdir'] siteruby = c['sitedir'] siterubyver = c['sitelibdir'] siterubyverarch = c['sitearchdir'] elsif newpath_p # 1.4.4 <= V <= 1.6.3 libruby = "#{c['prefix']}/lib/ruby" librubyver = "#{c['prefix']}/lib/ruby/#{version}" librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" siteruby = c['sitedir'] siterubyver = "$siteruby/#{version}" siterubyverarch = "$siterubyver/#{c['arch']}" else # V < 1.4.4 libruby = "#{c['prefix']}/lib/ruby" librubyver = "#{c['prefix']}/lib/ruby/#{version}" librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" siterubyver = siteruby siterubyverarch = "$siterubyver/#{c['arch']}" end parameterize = lambda {|path| path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') } if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } makeprog = arg.sub(/'/, '').split(/=/, 2)[1] else makeprog = 'make' end [ ExecItem.new('installdirs', 'std/site/home', 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ {|val, table| case val when 'std' table['rbdir'] = '$librubyver' table['sodir'] = '$librubyverarch' when 'site' table['rbdir'] = '$siterubyver' table['sodir'] = '$siterubyverarch' when 'home' setup_rb_error '$HOME was not set' unless ENV['HOME'] table['prefix'] = ENV['HOME'] table['rbdir'] = '$libdir/ruby' table['sodir'] = '$libdir/ruby' end }, PathItem.new('prefix', 'path', c['prefix'], 'path prefix of target environment'), PathItem.new('bindir', 'path', parameterize.call(c['bindir']), 'the directory for commands'), PathItem.new('libdir', 'path', parameterize.call(c['libdir']), 'the directory for libraries'), PathItem.new('datadir', 'path', parameterize.call(c['datadir']), 'the directory for shared data'), PathItem.new('mandir', 'path', parameterize.call(c['mandir']), 'the directory for man pages'), PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), 'the directory for system configuration files'), PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), 'the directory for local state data'), PathItem.new('libruby', 'path', libruby, 'the directory for ruby libraries'), PathItem.new('librubyver', 'path', librubyver, 'the directory for standard ruby libraries'), PathItem.new('librubyverarch', 'path', librubyverarch, 'the directory for standard ruby extensions'), PathItem.new('siteruby', 'path', siteruby, 'the directory for version-independent aux ruby libraries'), PathItem.new('siterubyver', 'path', siterubyver, 'the directory for aux ruby libraries'), PathItem.new('siterubyverarch', 'path', siterubyverarch, 'the directory for aux ruby binaries'), PathItem.new('rbdir', 'path', '$siterubyver', 'the directory for ruby scripts'), PathItem.new('sodir', 'path', '$siterubyverarch', 'the directory for ruby extentions'), PathItem.new('rubypath', 'path', rubypath, 'the path to set to #! line'), ProgramItem.new('rubyprog', 'name', rubypath, 'the ruby program using for installation'), ProgramItem.new('makeprog', 'name', makeprog, 'the make program to compile ruby extentions'), SelectItem.new('shebang', 'all/ruby/never', 'ruby', 'shebang line (#!) editing mode'), BoolItem.new('without-ext', 'yes/no', 'no', 'does not compile/install ruby extentions') ] end private :standard_entries def load_multipackage_entries multipackage_entries().each do |ent| add ent end end def multipackage_entries [ PackageSelectionItem.new('with', 'name,name...', '', 'ALL', 'package names that you want to install'), PackageSelectionItem.new('without', 'name,name...', '', 'NONE', 'package names that you do not want to install') ] end private :multipackage_entries ALIASES = { 'std-ruby' => 'librubyver', 'stdruby' => 'librubyver', 'rubylibdir' => 'librubyver', 'archdir' => 'librubyverarch', 'site-ruby-common' => 'siteruby', # For backward compatibility 'site-ruby' => 'siterubyver', # For backward compatibility 'bin-dir' => 'bindir', 'bin-dir' => 'bindir', 'rb-dir' => 'rbdir', 'so-dir' => 'sodir', 'data-dir' => 'datadir', 'ruby-path' => 'rubypath', 'ruby-prog' => 'rubyprog', 'ruby' => 'rubyprog', 'make-prog' => 'makeprog', 'make' => 'makeprog' } def fixup ALIASES.each do |ali, name| @table[ali] = @table[name] end end def options_re /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ end def parse_opt(opt) m = options_re().match(opt) or setup_rb_error "config: unknown option #{opt}" m.to_a[1,2] end def dllext @rbconfig['DLEXT'] end def value_config?(name) lookup(name).value? end class Item def initialize(name, template, default, desc) @name = name.freeze @template = template @value = default @default = default @description = desc end attr_reader :name attr_reader :description attr_accessor :default alias help_default default def help_opt "--#{@name}=#{@template}" end def value? true end def value @value end def resolve(table) @value.gsub(%r<\$([^/]+)>) { table[$1] } end def set(val) @value = check(val) end private def check(val) setup_rb_error "config: --#{name} requires argument" unless val val end end class BoolItem < Item def config_type 'bool' end def help_opt "--#{@name}" end private def check(val) return 'yes' unless val case val when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes' when /\An(o)?\z/i, /\Af(alse)\z/i then 'no' else setup_rb_error "config: --#{@name} accepts only yes/no for argument" end end end class PathItem < Item def config_type 'path' end private def check(path) setup_rb_error "config: --#{@name} requires argument" unless path path[0,1] == '$' ? path : File.expand_path(path) end end class ProgramItem < Item def config_type 'program' end end class SelectItem < Item def initialize(name, selection, default, desc) super @ok = selection.split('/') end def config_type 'select' end private def check(val) unless @ok.include?(val.strip) setup_rb_error "config: use --#{@name}=#{@template} (#{val})" end val.strip end end class ExecItem < Item def initialize(name, selection, desc, &block) super name, selection, nil, desc @ok = selection.split('/') @action = block end def config_type 'exec' end def value? false end def resolve(table) setup_rb_error "$#{name()} wrongly used as option value" end undef set def evaluate(val, table) v = val.strip.downcase unless @ok.include?(v) setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" end @action.call v, table end end class PackageSelectionItem < Item def initialize(name, template, default, help_default, desc) super name, template, default, desc @help_default = help_default end attr_reader :help_default def config_type 'package' end private def check(val) unless File.dir?("packages/#{val}") setup_rb_error "config: no such package: #{val}" end val end end class MetaConfigEnvironment def initialize(config, installer) @config = config @installer = installer end def config_names @config.names end def config?(name) @config.key?(name) end def bool_config?(name) @config.lookup(name).config_type == 'bool' end def path_config?(name) @config.lookup(name).config_type == 'path' end def value_config?(name) @config.lookup(name).config_type != 'exec' end def add_config(item) @config.add item end def add_bool_config(name, default, desc) @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) end def add_path_config(name, default, desc) @config.add PathItem.new(name, 'path', default, desc) end def set_config_default(name, default) @config.lookup(name).default = default end def remove_config(name) @config.remove(name) end # For only multipackage def packages raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer @installer.packages end # For only multipackage def declare_packages(list) raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer @installer.packages = list end end end # class ConfigTable # This module requires: #verbose?, #no_harm? module FileOperations def mkdir_p(dirname, prefix = nil) dirname = prefix + File.expand_path(dirname) if prefix $stderr.puts "mkdir -p #{dirname}" if verbose? return if no_harm? # Does not check '/', it's too abnormal. dirs = File.expand_path(dirname).split(%r<(?=/)>) if /\A[a-z]:\z/i =~ dirs[0] disk = dirs.shift dirs[0] = disk + dirs[0] end dirs.each_index do |idx| path = dirs[0..idx].join('') Dir.mkdir path unless File.dir?(path) end end def rm_f(path) $stderr.puts "rm -f #{path}" if verbose? return if no_harm? force_remove_file path end def rm_rf(path) $stderr.puts "rm -rf #{path}" if verbose? return if no_harm? remove_tree path end def remove_tree(path) if File.symlink?(path) remove_file path elsif File.dir?(path) remove_tree0 path else force_remove_file path end end def remove_tree0(path) Dir.foreach(path) do |ent| next if ent == '.' next if ent == '..' entpath = "#{path}/#{ent}" if File.symlink?(entpath) remove_file entpath elsif File.dir?(entpath) remove_tree0 entpath else force_remove_file entpath end end begin Dir.rmdir path rescue Errno::ENOTEMPTY # directory may not be empty end end def move_file(src, dest) force_remove_file dest begin File.rename src, dest rescue File.open(dest, 'wb') {|f| f.write File.binread(src) } File.chmod File.stat(src).mode, dest File.unlink src end end def force_remove_file(path) begin remove_file path rescue end end def remove_file(path) File.chmod 0777, path File.unlink path end def install(from, dest, mode, prefix = nil) $stderr.puts "install #{from} #{dest}" if verbose? return if no_harm? realdest = prefix ? prefix + File.expand_path(dest) : dest realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) str = File.binread(from) if diff?(str, realdest) verbose_off { rm_f realdest if File.exist?(realdest) } File.open(realdest, 'wb') {|f| f.write str } File.chmod mode, realdest File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| if prefix f.puts realdest.sub(prefix, '') else f.puts realdest end } end end def diff?(new_content, path) return true unless File.exist?(path) new_content != File.binread(path) end def command(*args) $stderr.puts args.join(' ') if verbose? system(*args) or raise RuntimeError, "system(#{args.map{|a| a.inspect }.join(' ')}) failed" end def ruby(*args) command config('rubyprog'), *args end def make(task = nil) command(*[config('makeprog'), task].compact) end def extdir?(dir) File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") end def files_of(dir) Dir.open(dir) {|d| return d.select {|ent| File.file?("#{dir}/#{ent}") } } end DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) def directories_of(dir) Dir.open(dir) {|d| return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT } end end # This module requires: #srcdir_root, #objdir_root, #relpath module HookScriptAPI def get_config(key) @config[key] end alias config get_config # obsolete: use metaconfig to change configuration def set_config(key, val) @config[key] = val end # # srcdir/objdir (works only in the package directory) # def curr_srcdir "#{srcdir_root()}/#{relpath()}" end def curr_objdir "#{objdir_root()}/#{relpath()}" end def srcfile(path) "#{curr_srcdir()}/#{path}" end def srcexist?(path) File.exist?(srcfile(path)) end def srcdirectory?(path) File.dir?(srcfile(path)) end def srcfile?(path) File.file?(srcfile(path)) end def srcentries(path = '.') Dir.open("#{curr_srcdir()}/#{path}") {|d| return d.to_a - %w(. ..) } end def srcfiles(path = '.') srcentries(path).select {|fname| File.file?(File.join(curr_srcdir(), path, fname)) } end def srcdirectories(path = '.') srcentries(path).select {|fname| File.dir?(File.join(curr_srcdir(), path, fname)) } end end class ToplevelInstaller Version = '3.4.1' Copyright = 'Copyright (c) 2000-2006 Minero Aoki' TASKS = [ [ 'all', 'do config, setup, then install' ], [ 'config', 'saves your configurations' ], [ 'show', 'shows current configuration' ], [ 'setup', 'compiles ruby extentions and others' ], [ 'install', 'installs files' ], [ 'test', 'run all tests in test/' ], [ 'clean', "does `make clean' for each extention" ], [ 'distclean',"does `make distclean' for each extention" ] ] def ToplevelInstaller.invoke config = ConfigTable.new(load_rbconfig()) config.load_standard_entries config.load_multipackage_entries if multipackage? config.fixup klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) klass.new(File.dirname($0), config).invoke end def ToplevelInstaller.multipackage? File.dir?(File.dirname($0) + '/packages') end def ToplevelInstaller.load_rbconfig if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } ARGV.delete(arg) load File.expand_path(arg.split(/=/, 2)[1]) $".push 'rbconfig.rb' else require 'rbconfig' end ::Config::CONFIG end def initialize(ardir_root, config) @ardir = File.expand_path(ardir_root) @config = config # cache @valid_task_re = nil end def config(key) @config[key] end def inspect "#<#{self.class} #{__id__()}>" end def invoke run_metaconfigs case task = parsearg_global() when nil, 'all' parsearg_config init_installers exec_config exec_setup exec_install else case task when 'config', 'test' ; when 'clean', 'distclean' @config.load_savefile if File.exist?(@config.savefile) else @config.load_savefile end __send__ "parsearg_#{task}" init_installers __send__ "exec_#{task}" end end def run_metaconfigs @config.load_script "#{@ardir}/metaconfig" end def init_installers @installer = Installer.new(@config, @ardir, File.expand_path('.')) end # # Hook Script API bases # def srcdir_root @ardir end def objdir_root '.' end def relpath '.' end # # Option Parsing # def parsearg_global while arg = ARGV.shift case arg when /\A\w+\z/ setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) return arg when '-q', '--quiet' @config.verbose = false when '--verbose' @config.verbose = true when '--help' print_usage $stdout exit 0 when '--version' puts "#{File.basename($0)} version #{Version}" exit 0 when '--copyright' puts Copyright exit 0 else setup_rb_error "unknown global option '#{arg}'" end end nil end def valid_task?(t) valid_task_re() =~ t end def valid_task_re @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ end def parsearg_no_options unless ARGV.empty? task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1) setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" end end alias parsearg_show parsearg_no_options alias parsearg_setup parsearg_no_options alias parsearg_test parsearg_no_options alias parsearg_clean parsearg_no_options alias parsearg_distclean parsearg_no_options def parsearg_config evalopt = [] set = [] @config.config_opt = [] while i = ARGV.shift if /\A--?\z/ =~ i @config.config_opt = ARGV.dup break end name, value = *@config.parse_opt(i) if @config.value_config?(name) @config[name] = value else evalopt.push [name, value] end set.push name end evalopt.each do |name, value| @config.lookup(name).evaluate value, @config end # Check if configuration is valid set.each do |n| @config[n] if @config.value_config?(n) end end def parsearg_install @config.no_harm = false @config.install_prefix = '' while a = ARGV.shift case a when '--no-harm' @config.no_harm = true when /\A--prefix=/ path = a.split(/=/, 2)[1] path = File.expand_path(path) unless path[0,1] == '/' @config.install_prefix = path else setup_rb_error "install: unknown option #{a}" end end end def print_usage(out) out.puts 'Typical Installation Procedure:' out.puts " $ ruby #{File.basename $0} config" out.puts " $ ruby #{File.basename $0} setup" out.puts " # ruby #{File.basename $0} install (may require root privilege)" out.puts out.puts 'Detailed Usage:' out.puts " ruby #{File.basename $0} " out.puts " ruby #{File.basename $0} [] []" fmt = " %-24s %s\n" out.puts out.puts 'Global options:' out.printf fmt, '-q,--quiet', 'suppress message outputs' out.printf fmt, ' --verbose', 'output messages verbosely' out.printf fmt, ' --help', 'print this message' out.printf fmt, ' --version', 'print version and quit' out.printf fmt, ' --copyright', 'print copyright and quit' out.puts out.puts 'Tasks:' TASKS.each do |name, desc| out.printf fmt, name, desc end fmt = " %-24s %s [%s]\n" out.puts out.puts 'Options for CONFIG or ALL:' @config.each do |item| out.printf fmt, item.help_opt, item.description, item.help_default end out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" out.puts out.puts 'Options for INSTALL:' out.printf fmt, '--no-harm', 'only display what to do if given', 'off' out.printf fmt, '--prefix=path', 'install path prefix', '' out.puts end # # Task Handlers # def exec_config @installer.exec_config @config.save # must be final end def exec_setup @installer.exec_setup end def exec_install @installer.exec_install end def exec_test @installer.exec_test end def exec_show @config.each do |i| printf "%-20s %s\n", i.name, i.value if i.value? end end def exec_clean @installer.exec_clean end def exec_distclean @installer.exec_distclean end end # class ToplevelInstaller class ToplevelInstallerMulti < ToplevelInstaller include FileOperations def initialize(ardir_root, config) super @packages = directories_of("#{@ardir}/packages") raise 'no package exists' if @packages.empty? @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) end def run_metaconfigs @config.load_script "#{@ardir}/metaconfig", self @packages.each do |name| @config.load_script "#{@ardir}/packages/#{name}/metaconfig" end end attr_reader :packages def packages=(list) raise 'package list is empty' if list.empty? list.each do |name| raise "directory packages/#{name} does not exist"\ unless File.dir?("#{@ardir}/packages/#{name}") end @packages = list end def init_installers @installers = {} @packages.each do |pack| @installers[pack] = Installer.new(@config, "#{@ardir}/packages/#{pack}", "packages/#{pack}") end with = extract_selection(config('with')) without = extract_selection(config('without')) @selected = @installers.keys.select {|name| (with.empty? or with.include?(name)) \ and not without.include?(name) } end def extract_selection(list) a = list.split(/,/) a.each do |name| setup_rb_error "no such package: #{name}" unless @installers.key?(name) end a end def print_usage(f) super f.puts 'Inluded packages:' f.puts ' ' + @packages.sort.join(' ') f.puts end # # Task Handlers # def exec_config run_hook 'pre-config' each_selected_installers {|inst| inst.exec_config } run_hook 'post-config' @config.save # must be final end def exec_setup run_hook 'pre-setup' each_selected_installers {|inst| inst.exec_setup } run_hook 'post-setup' end def exec_install run_hook 'pre-install' each_selected_installers {|inst| inst.exec_install } run_hook 'post-install' end def exec_test run_hook 'pre-test' each_selected_installers {|inst| inst.exec_test } run_hook 'post-test' end def exec_clean rm_f @config.savefile run_hook 'pre-clean' each_selected_installers {|inst| inst.exec_clean } run_hook 'post-clean' end def exec_distclean rm_f @config.savefile run_hook 'pre-distclean' each_selected_installers {|inst| inst.exec_distclean } run_hook 'post-distclean' end # # lib # def each_selected_installers Dir.mkdir 'packages' unless File.dir?('packages') @selected.each do |pack| $stderr.puts "Processing the package `#{pack}' ..." if verbose? Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") Dir.chdir "packages/#{pack}" yield @installers[pack] Dir.chdir '../..' end end def run_hook(id) @root_installer.run_hook id end # module FileOperations requires this def verbose? @config.verbose? end # module FileOperations requires this def no_harm? @config.no_harm? end end # class ToplevelInstallerMulti class Installer FILETYPES = %w( bin lib ext data conf man ) include FileOperations include HookScriptAPI def initialize(config, srcroot, objroot) @config = config @srcdir = File.expand_path(srcroot) @objdir = File.expand_path(objroot) @currdir = '.' end def inspect "#<#{self.class} #{File.basename(@srcdir)}>" end def noop(rel) end # # Hook Script API base methods # def srcdir_root @srcdir end def objdir_root @objdir end def relpath @currdir end # # Config Access # # module FileOperations requires this def verbose? @config.verbose? end # module FileOperations requires this def no_harm? @config.no_harm? end def verbose_off begin save, @config.verbose = @config.verbose?, false yield ensure @config.verbose = save end end # # TASK config # def exec_config exec_task_traverse 'config' end alias config_dir_bin noop alias config_dir_lib noop def config_dir_ext(rel) extconf if extdir?(curr_srcdir()) end alias config_dir_data noop alias config_dir_conf noop alias config_dir_man noop def extconf ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt end # # TASK setup # def exec_setup exec_task_traverse 'setup' end def setup_dir_bin(rel) files_of(curr_srcdir()).each do |fname| update_shebang_line "#{curr_srcdir()}/#{fname}" end end alias setup_dir_lib noop def setup_dir_ext(rel) make if extdir?(curr_srcdir()) end alias setup_dir_data noop alias setup_dir_conf noop alias setup_dir_man noop def update_shebang_line(path) return if no_harm? return if config('shebang') == 'never' old = Shebang.load(path) if old $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1 new = new_shebang(old) return if new.to_s == old.to_s else return unless config('shebang') == 'all' new = Shebang.new(config('rubypath')) end $stderr.puts "updating shebang: #{File.basename(path)}" if verbose? open_atomic_writer(path) {|output| File.open(path, 'rb') {|f| f.gets if old # discard output.puts new.to_s output.print f.read } } end def new_shebang(old) if /\Aruby/ =~ File.basename(old.cmd) Shebang.new(config('rubypath'), old.args) elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby' Shebang.new(config('rubypath'), old.args[1..-1]) else return old unless config('shebang') == 'all' Shebang.new(config('rubypath')) end end def open_atomic_writer(path, &block) tmpfile = File.basename(path) + '.tmp' begin File.open(tmpfile, 'wb', &block) File.rename tmpfile, File.basename(path) ensure File.unlink tmpfile if File.exist?(tmpfile) end end class Shebang def Shebang.load(path) line = nil File.open(path) {|f| line = f.gets } return nil unless /\A#!/ =~ line parse(line) end def Shebang.parse(line) cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ') new(cmd, args) end def initialize(cmd, args = []) @cmd = cmd @args = args end attr_reader :cmd attr_reader :args def to_s "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}") end end # # TASK install # def exec_install rm_f 'InstalledFiles' exec_task_traverse 'install' end def install_dir_bin(rel) install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755 end def install_dir_lib(rel) install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644 end def install_dir_ext(rel) return unless extdir?(curr_srcdir()) install_files rubyextentions('.'), "#{config('sodir')}/#{File.dirname(rel)}", 0555 end def install_dir_data(rel) install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 end def install_dir_conf(rel) # FIXME: should not remove current config files # (rename previous file to .old/.org) install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 end def install_dir_man(rel) install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 end def install_files(list, dest, mode) mkdir_p dest, @config.install_prefix list.each do |fname| install fname, dest, mode, @config.install_prefix end end def libfiles glob_reject(%w(*.y *.output), targetfiles()) end def rubyextentions(dir) ents = glob_select("*.#{@config.dllext}", targetfiles()) if ents.empty? setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" end ents end def targetfiles mapdir(existfiles() - hookfiles()) end def mapdir(ents) ents.map {|ent| if File.exist?(ent) then ent # objdir else "#{curr_srcdir()}/#{ent}" # srcdir end } end # picked up many entries from cvs-1.11.1/src/ignore.c JUNK_FILES = %w( core RCSLOG tags TAGS .make.state .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb *~ *.old *.bak *.BAK *.orig *.rej _$* *$ *.org *.in .* ) def existfiles glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) end def hookfiles %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| %w( config setup install clean distclean ).map {|t| sprintf(fmt, t) } }.flatten end def glob_select(pat, ents) re = globs2re([pat]) ents.select {|ent| re =~ ent } end def glob_reject(pats, ents) re = globs2re(pats) ents.reject {|ent| re =~ ent } end GLOB2REGEX = { '.' => '\.', '$' => '\$', '#' => '\#', '*' => '.*' } def globs2re(pats) /\A(?:#{ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') })\z/ end # # TASK test # TESTDIR = 'test' def exec_test unless File.directory?('test') $stderr.puts 'no test in this package' if verbose? return end $stderr.puts 'Running tests...' if verbose? begin require 'test/unit' rescue LoadError setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.' end runner = Test::Unit::AutoRunner.new(true) runner.to_run << TESTDIR runner.run end # # TASK clean # def exec_clean exec_task_traverse 'clean' rm_f @config.savefile rm_f 'InstalledFiles' end alias clean_dir_bin noop alias clean_dir_lib noop alias clean_dir_data noop alias clean_dir_conf noop alias clean_dir_man noop def clean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'clean' if File.file?('Makefile') end # # TASK distclean # def exec_distclean exec_task_traverse 'distclean' rm_f @config.savefile rm_f 'InstalledFiles' end alias distclean_dir_bin noop alias distclean_dir_lib noop def distclean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'distclean' if File.file?('Makefile') end alias distclean_dir_data noop alias distclean_dir_conf noop alias distclean_dir_man noop # # Traversing # def exec_task_traverse(task) run_hook "pre-#{task}" FILETYPES.each do |type| if type == 'ext' and config('without-ext') == 'yes' $stderr.puts 'skipping ext/* by user option' if verbose? next end traverse task, type, "#{task}_dir_#{type}" end run_hook "post-#{task}" end def traverse(task, rel, mid) dive_into(rel) { run_hook "pre-#{task}" __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') directories_of(curr_srcdir()).each do |d| traverse task, "#{rel}/#{d}", mid end run_hook "post-#{task}" } end def dive_into(rel) return unless File.dir?("#{@srcdir}/#{rel}") dir = File.basename(rel) Dir.mkdir dir unless File.dir?(dir) prevdir = Dir.pwd Dir.chdir dir $stderr.puts '---> ' + rel if verbose? @currdir = rel yield Dir.chdir prevdir $stderr.puts '<--- ' + rel if verbose? @currdir = File.dirname(rel) end def run_hook(id) path = [ "#{curr_srcdir()}/#{id}", "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } return unless path $stderr.puts "invoking hook script #{path}" if verbose? begin instance_eval File.read(path), path, 1 rescue raise if $DEBUG setup_rb_error "hook #{path} failed:\n" + $!.message end end end # class Installer class SetupError < StandardError; end def setup_rb_error(msg) raise SetupError, msg end if $0 == __FILE__ begin ToplevelInstaller.invoke rescue SetupError raise if $DEBUG $stderr.puts $!.message $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." exit 1 end end racc-1.4.14/web/0000755000004100000410000000000012625454503013324 5ustar www-datawww-dataracc-1.4.14/web/racc.en.rhtml0000644000004100000410000000224012625454503015703 0ustar www-datawww-data% require 'makefile' % version = Makefile.get_parameter('Makefile', 'version')

Racc

$Id$

Version<%= version %>
TypeParser Generator
FormatRuby script + Ruby extention
Requirementruby (>=1.6)
LicenseLGPL

-- Download (.tar.gz) -- Old Versions -- Online Manual --

Racc (Ruby yACC) is a LALR(1) parser generator for Ruby. Version 1.4.x is stable release.

Parsers generated by Racc requires "Racc Runtime Module". Ruby 1.8.x comes with this runtime. If you want to run your parsers with ruby 1.6.x, use "racc -E" command. For details, see online manual.

Anonymous CVS

You can true latest version of Racc via anonymous CVS. To check out working copy, type:

$ cvs -d :pserver:anonymous@cvs.loveruby.net:/src login
Password: (Just hit [Enter])
$ cvs -d :pserver:anonymous@cvs.loveruby.net:/src co racc
racc-1.4.14/web/racc.ja.rhtml0000644000004100000410000000275412625454503015705 0ustar www-datawww-data% require 'makefile' % version = Makefile.get_parameter('Makefile', 'version')

Racc

$Id$

ǿ<%= version %>
parser generator
ruby script, ruby extention
ɬ״Ķruby (>=1.6)
۾LGPL

Ruby Ѥ LALR(1) ѡͥ졼Ǥ ѡϤʤ˹®ưޤ

Racc ѡư˥󥿥⥸塼뤬ɬפǤ Ruby 1.8 ˤϤΥ󥿥बǽ餫źդƤΤ ͤʤפǤRuby 1.6 оݤˤȤ racc -E ǥѡɬפޤ

ʤRacc 1.4.x Υ󥿥 Ruby 1.8 źդ Racc 󥿥ϡ ɾǤ̯˰㤤ޤ˸ߴޤ

⤦ŪʬϸϤƤϤǤ TODO ϤޤĤޤ¾ˤäƤΤ ʬ礭ѹĤϤޤ

CVS ݥȥ

CVS Ȥ Racc οκǿǤǤޤ ޥɥ饤ǼΤ褦ǤäƤ

$ cvs -d :pserver:anonymous@cvs.loveruby.net:/src login
Password: (EnterǤ)
$ cvs -d :pserver:anonymous@cvs.loveruby.net:/src co racc
racc-1.4.14/TODO0000644000004100000410000000025512625454503013241 0ustar www-datawww-data* check 'error' token handling. * interactive transition table monitor. * support backtracking. * output Ruby extention library? * LL(k)? (But it should not be called Racc) racc-1.4.14/sample/0000755000004100000410000000000012625454503014030 5ustar www-datawww-dataracc-1.4.14/sample/conflict.y0000644000004100000410000000034012625454503016020 0ustar www-datawww-data# $Id$ # # Example of conflicted grammer. # This grammer contains 1 Shift/Reduce conflict and 1 Reduce/Reduce conflict. class A rule target : outer outer : | outer inner inner : | inner ITEM end racc-1.4.14/sample/calc-ja.y0000644000004100000410000000225212625454503015515 0ustar www-datawww-data# $Id$ # # A simple calculator, version 2. # This file contains Japanese characters (encoding=EUC-JP). class Calculator2 prechigh nonassoc UMINUS left '*' '/' left '+' '-' preclow options no_result_var rule target : exp | /* none */ { 0 } exp : exp '+' exp { val[0] + val[2] } | exp '-' exp { val[0] - val[2] } | exp '*' exp { val[0] * val[2] } | exp '/' exp { val[0] / val[2] } | '(' exp ')' { val[1] } | '-' NUMBER =UMINUS { -(val[1]) } | NUMBER end ---- header # $Id$ ---- inner def evaluate(str) @tokens = [] until str.empty? case str when /\A\s+/ ; when /\A\d+/ @tokens.push [:NUMBER, $&.to_i] when /\A.|\n/ s = $& @tokens.push [s, s] end str = $' end @tokens.push [false, '$'] do_parse end def next_token @tokens.shift end ---- footer puts 'Ķ 2 浡' puts 'Q ǽλޤ' calc = Calculator2.new while true print '>>> '; $stdout.flush str = $stdin.gets.strip break if /q/i =~ str begin p calc.evaluate(str) rescue ParseError puts 'parse error' end end racc-1.4.14/sample/syntax.y0000644000004100000410000000124212625454503015547 0ustar www-datawww-data# $Id$ # # Racc syntax checker. This grammer file generates # invalid ruby program, you cannot run this parser. class P token A B C convert A '5' end prechigh left B preclow options omit_action_call start target rule target: A B C { print 'abc' } | B C A | C B A { print 'cba' } | cont cont : A c2 B c2 C c2 : C C C C C end ---- inner junk code !!!! kjaljlajrlaolanbla /// %%% (*((( token rule akiurtlajluealjflaj @@@@ end end end end __END__ laieu2o879urkq96ga(Q#*&%Q# #&lkji END q395q?/// liutjqlkr7 racc-1.4.14/sample/hash.y0000644000004100000410000000205012625454503015142 0ustar www-datawww-data# $Id$ # # Converting Hash-like string into Ruby's Hash. class HashParser options no_result_var rule hash : '{' contents '}' { val[1] } | '{' '}' { Hash.new } # Racc can handle string over 2 bytes. contents: IDENT '=>' IDENT { {val[0] => val[2]} } | contents ',' IDENT '=>' IDENT { val[0][val[2]] = val[4]; val[0] } end ---- inner def parse(str) @str = str yyparse self, :scan end private def scan str = @str until str.empty? case str when /\A\s+/ str = $' when /\A\w+/ yield :IDENT, $& str = $' when /\A=>/ yield '=>', '=>' str = $' else c = str[0,1] yield c, c str = str[1..-1] end end yield false, '$' # is optional from Racc 1.3.7 end ---- footer if $0 == __FILE__ src = < MyName, id => MyIdent } EOS puts 'Parsing (String):' print src puts puts 'Result (Ruby Object):' p HashParser.new.parse(src) end racc-1.4.14/sample/array.y0000644000004100000410000000171312625454503015342 0ustar www-datawww-data# $Id$ # # convert Array-like string into Ruby's Array. class ArrayParser rule array : '[' contents ']' { result = val[1] } | '[' ']' { result = [] } contents: ITEM { result = val } | contents ',' ITEM { result.push val[2] } ---- inner def parse(str) str = str.strip @q = [] until str.empty? case str when /\A\s+/ str = $' when /\A\w+/ @q.push [:ITEM, $&] str = $' else c = str[0,1] @q.push [c, c] str = str[1..-1] end end @q.push [false, '$'] # is optional from Racc 1.3.7 do_parse end def next_token @q.shift end ---- footer if $0 == __FILE__ src = <" item { result = { val[0] => val[2] } } | hash_contents ',' item "=>" item { result[val[2]] = val[4] } racc-1.4.14/sample/lalr.y0000644000004100000410000000022412625454503015152 0ustar www-datawww-data# $Id$ # # This is LALR grammer, and not LL/SLR. class A rule A : L '=' E L : i | R '^' i E : E '+' R | R | '@' L R : i end racc-1.4.14/sample/array2.y0000644000004100000410000000164012625454503015423 0ustar www-datawww-data# $Id$ # # Converting Array-like string into Ruby's Array, version 2. # This grammer uses no_result_var. class ArrayParser2 options no_result_var rule array : '[' contents ']' { val[1] } | '[' ']' { [] } contents: ITEM { val } | contents ',' ITEM { val[0].push val[2]; val[0] } end ---- inner def parse(str) @str = str yyparse self, :scan end def scan str = @str.strip until str.empty? case str when /\A\s+/ str = $' when /\A\w+/ yield :ITEM, $& str = $' else c = str[0,1] yield c, c str = str[1..-1] end end yield false, '$' # is optional from Racc 1.3.7 end def next_token @q.shift end ---- footer if $0 == __FILE__ src = < * lib/racc/grammar.rb (separated_by): last commit was wrong. use optional default return value of #option. Tue Feb 20 18:27:48 2007 Minero Aoki * lib/racc/grammar.rb (separated_by): return [] for empty list. Tue Nov 7 07:13:47 2006 Minero Aoki * lib/racc/grammar.rb (Rule#prec): rule.prec{...} should set action. Tue Nov 7 06:38:57 2006 Minero Aoki * lib/racc/grammar.rb: system call error on writing log file should be ignored. * lib/racc/grammar.rb: never define lvar which have same name with block local variable. * lib/racc/iset.rb: ditto. * lib/racc/logfilegenerator.rb: ditto. * lib/racc/parser.rb: ditto. * lib/racc/state.rb: ditto. * lib/racc/statetransitiontable.rb: ditto. * test/test.rb: racc -c is obsolete, use --line-convert-all. Sun Oct 29 13:27:30 2006 Minero Aoki * lib/racc/grammarfileparser.rb: use String#lines instead of #to_a. * lib/racc/parserfilegenerator.rb: ditto. * lib/racc/compat.rb: provide Object#__send. * lib/racc/compat.rb: provide Object#__send!. * lib/racc/compat.rb: provide String#lines. Thu Aug 24 23:14:16 2006 Minero Aoki * lib/racc/grammar.rb: report conflicts/useless if $DEBUG. * lib/racc/statetransitiontable.rb: remove code for Ruby 1.4 compatibility. Fri Aug 4 01:02:36 2006 Minero Aoki * lib/racc/grammar.rb: #should_terminal should be called in #check_terminals. Fri Aug 4 00:44:56 2006 Minero Aoki * bin/racc: getopts -> optparse. * lib/racc/grammar.rb: value of error symbol is :error. * lib/racc/grammar.rb (check_terminals): string symbols are terminal. * lib/racc/grammarfileparser.rb (add_rule_block): specified-prec did not work. Fri Aug 4 00:29:53 2006 Minero Aoki * lib/racc/parserfilegenerator.rb (serialize_integer_list_compressed): fix typo. Thu Aug 3 22:20:34 2006 Minero Aoki * bin/y2racc: fix filename. Thu Aug 3 21:10:48 2006 Minero Aoki * bin/y2racc: getopts -> optparse. Thu Aug 3 19:35:34 2006 Minero Aoki * setup.rb: updated. Thu Aug 3 19:34:55 2006 Minero Aoki * bin/racc2y: getopts -> optparse. * bin/racc2y: rewrite code for new generator. * lib/racc/grammar.rb (_regist): did not check @delayed rules (it causes registering same dummy rules many times). * lib/racc/grammarfileparser.rb: refactoring: simplify syntax. * lib/racc/grammarfileparser.rb: new method GrammarFileParser.parse. * lib/racc/grammarfileparser.rb: new method GrammarFileParser.parse_file. Sat Jul 29 04:51:42 2006 Minero Aoki * lib/racc/pre-setup: We need not make grammarfileparser.rb. Sat Jul 29 04:30:33 2006 Minero Aoki * lib/racc/grammar.rb: allow '|' operation with meta rules (many, option...). Sat Jul 29 03:17:20 2006 Minero Aoki * lib/racc/grammar.rb (Grammar#parser_class): write log file when $DEBUG=true. * lib/racc/grammar.rb (Grammar.define): run block on a Racc::Grammar::DefinitionEnv object, instead of a Racc::Grammar object. * lib/racc/grammar.rb (DefinitionEnv): new method #null. * lib/racc/grammar.rb (DefinitionEnv): new method #many. * lib/racc/grammar.rb (DefinitionEnv): new method #many1. * lib/racc/grammar.rb (DefinitionEnv): new method #option. * lib/racc/grammar.rb (DefinitionEnv): new method #seperated_by. * lib/racc/grammar.rb (DefinitionEnv): new method #seperated_by1. * lib/racc/grammar.rb (DefinitionEnv): new method #action. Sat Jul 29 03:13:22 2006 Minero Aoki * lib/racc/compat.rb: reduce warning. Sun Jul 16 05:07:12 2006 Minero Aoki * lib/racc/compat.rb: implement Enumerable#each_slice for Ruby 1.8. * lib/racc/parserfilegenerator.rb: better output. * ext/racc/cparse/cparse.c: always use VALUE instead of struct cparse_params. * ext/racc/cparse/cparse.c: mark params->value_v. Thu Jul 6 20:44:48 2006 Minero Aoki * lib/racc/grammar.rb: on-the-fly generator implemented. * lib/racc/generator.rb -> statetransitiontable.rb, parserfilegenerator.rb, logfilegenerator.rb. * lib/racc/statetransitiontable.rb: new file. * lib/racc/parserfilegenerator.rb: new file. * lib/racc/logfilegenerator.rb: new file. * lib/racc/grammarfileparser.rb.in: removed. * lib/racc/grammarfileparser.rb: new file. uses on-the-fly generator. * misc/boot.rb: removed. * lib/racc/static.rb: new file, to import static generator (lib/racc.rb provides dynamic generator). * lib/racc/grammar.rb: grand refactoring. * lib/racc/sourcetext.rb: new method #to_s, #location. * lib/racc/state.rb: compute NFA/DFA on demand. * bin/racc: follow these changes. Thu Jul 6 20:39:42 2006 Minero Aoki * ext/racc/cparse/cparse.so: should mark VALUEs in cparse_params. Tue Jul 4 02:24:27 2006 Minero Aoki * bin/racc: simplify report code. * lib/racc/grammar.rb: introduce new methods for racc command. * lib/racc/states.rb: ditto. * lib/racc/generator.rb: class CodeGenerator -> ParserFileGenerator. * lib/racc/generator.rb: new class ParserFileGenerator::Params. * bin/racc: ditto. * misc/boot.rb: ditto. * lib/racc/grammarfileparser.rb.in: ditto. * lib/racc/grammarfileparser.rb.in: merge grammarfilescanner.rb. * lib/racc/grammarfilescanner.rb: removed. * lib/racc/grammarfileparser.rb.in: parses user code blocks. * lib/racc/usercodeparser.rb: removed. * lib/racc/generator.rb: remove user code parsing code. * lib/racc/grammarfileparser.rb.in: passes user code block by a SourceText object. * lib/racc/generator.rb: ditto. * lib/racc/sourcetext.rb: new file. * lib/racc/generator.rb: introduce DSL to describe file contents. Tue Jul 4 02:15:36 2006 Minero Aoki * lib/racc/debugflags.rb: remove unused class GenerationOptions. Tue Jul 4 02:14:48 2006 Minero Aoki * lib/racc/compat.rb: update coding style. Mon Jul 3 04:34:32 2006 Minero Aoki * lib/racc/compiler.rb: do not export Grammar/SymbolTable/States. * lib/racc/compiler.rb: make a new class for debug flags (Racc::DebugFlags). * lib/racc/compiler.rb: removed. * bin/racc: eliminate Racc::Compiler class. * bin/racc: refactor profiling code. * bin/racc: move file generation code to racc/generator.rb. * misc/boot.rb: does not emulate Racc::Compiler interface. * lib/racc.rb: new file to require whole generator. * lib/racc/grammar.rb: class RuleTable -> Grammar. * lib/racc/grammar.rb: Grammar.new does not acccept a Compiler. * lib/racc/grammar.rb: refactoring. * lib/racc/grammarfileparser.rb.in: GrammarFileParser.new does not accept a Compiler. * lib/racc/grammarfileparser.rb.in: #parser takes more 2 args, a filename and a base line number. * lib/racc/grammarfileparser.rb.in: refactoring. * lib/racc/output.rb -> generate.rb * lib/racc/generate.rb: class Formatter -> CodeGenerator. * lib/racc/generate.rb: CodeGenerator.new does not accept a Compiler. * lib/racc/generate.rb: a CodeGenerator got many parameters via setter method. * lib/racc/generate.rb: class VerboseOutputter -> LogFileGenerator. * lib/racc/generate.rb: LogFileGenerator.new does not accept a Compiler. * lib/racc/generate.rb: refactoring. * lib/racc/state.rb: class StateTable -> States. * lib/racc/state.rb: States.new does not acccept a Compiler. * lib/racc/state.rb: refactoring. * test/test.rb: -Da is obsolete (I forgot what this flag is). * test/test.rb: allow replacing racc via environment variable $RACC. Mon Jul 3 04:18:49 2006 Minero Aoki * Makefile: new task bootstrap-force. Sun Jul 2 19:46:58 2006 Minero Aoki * test/ichk.y: update coding style. Sun Jul 2 19:01:55 2006 Minero Aoki * ext/racc/cparse/cparse.c: must require version.h to get RUBY_VERSION_CODE. Sun Jul 2 18:33:32 2006 Minero Aoki * ext/racc/cparse/cparse.c: do not use rb_iterate to give a block to the method, use rb_block_call instead. [ruby-dev:28445] Mon Jun 19 02:38:18 2006 Minero Aoki * bin/racc: -g option is now -t. -g option is obsolete and is an alias of -t. Mon Jun 19 02:35:59 2006 Minero Aoki * ext/racc/cparse/cparse.c: K&R -> ANSI C. Mon Nov 21 02:37:10 2005 Minero Aoki * version 1.4.5 released. Mon Nov 21 02:31:18 2005 Minero Aoki * bin/racc: shebang line should include file extension. * lib/racc/compat.rb: method removed: bug!. * lib/racc/*.rb: racc compiler should not depend on Racc::ParseError. * lib/racc/*.rb: update copyright year. * lib/racc/*.rb: update coding style. * lib/racc/exception.rb: new file. Mon Nov 21 00:49:18 2005 Minero Aoki * Makefile: remove useless target `import'. * Makefile: generate parser-text.rb. * misc/dist.sh: setup.rb and COPYING is now in repository. * misc/dist.sh: generate parser-text.rb. Mon Nov 21 00:14:21 2005 Minero Aoki * bin/racc: read racc/parser.rb from parser-text.rb. * lib/racc/rubyloader.rb: no longer needed. * lib/racc/pre-setup: new file. * lib/racc/pre-setup: generate parser-text.rb. * lib/racc/pre-setup: generate grammarfileparser.rb. * misc/boot.rb: new method BootstrapCompiler.main. * misc/boot.rb: new method BootstrapCompiler.generate, which is used from pre-setup. Mon Nov 21 00:09:04 2005 Minero Aoki * bin/racc2y: refactoring. * bin/y2racc: refactoring. Sun Nov 20 23:46:42 2005 Minero Aoki * lib/racc/pre-setup: new file. Sun Nov 20 22:46:21 2005 Minero Aoki * COPYING: new file. Sun Nov 20 22:25:15 2005 Minero Aoki * setup.rb: import setup.rb 3.4.1. Thu Sep 29 02:51:56 2005 Minero Aoki * Makefile (clean): invoke `make clean' in ext. Thu Sep 29 02:50:56 2005 Minero Aoki * lib/racc/.cvsignore: removed. Thu Sep 29 02:46:30 2005 Minero Aoki * Makefile: use .makeparams system. * Makefile: unify lib/racc/Makefile. * Makefile: new target lib/racc/grammarfileparser.rb. * lib/racc/Makefile: unified by ./Makefile. * lib/racc/boot: removed (moved under misc). * misc/boot.rb: new file. Thu Sep 29 02:43:30 2005 Minero Aoki * setup.rb: new file. Tue Jul 26 23:37:46 2005 Minero Aoki * bin/racc: --no-omit-actions did not work (This patch is contributed by OHKUBO Takuya). Sun Jan 2 11:48:19 2005 Minero Aoki * lib/racc/grammer.rb (once_writer): bug! needs argument. Mon Feb 16 16:14:16 2004 Minero Aoki * test/echk.y: fix typo. * test/ichk.y: does not use amstd. * test/opt.y: untabify. Mon Feb 16 16:10:46 2004 Minero Aoki * lib/racc/boot: update coding style. * lib/racc/compat.rb: ditto. * lib/racc/compiler.rb: ditto. * lib/racc/grammar.rb: ditto. * lib/racc/grammarfileparser.rb.in: ditto. * lib/racc/grammarfilescanner.rb: ditto. * lib/racc/info.rb: ditto. * lib/racc/iset.rb: ditto. * lib/racc/output.rb: ditto. * lib/racc/parser.rb: ditto. * lib/racc/state.rb: ditto. * lib/racc/usercodeparser.rb: ditto. Mon Feb 16 16:01:34 2004 Minero Aoki * lib/racc/rubyloader.rb: imported rev1.6. Fri Dec 12 01:57:47 2003 Minero Aoki * sample/hash.y: use no_result_var option. * sample/array.y: use latest (my) coding style. * sample/array2.y: ditto. * sample/hash.y: ditto. * sample/lists.y: ditto. Wed Nov 5 19:50:35 2003 Minero Aoki * test/bench.y: remove dependency on amstd. * test/chk.y: ditto. * test/echk.y: ditto. * test/ichk.y: ditto. * test/intp.y: ditto. * test/opt.y: ditto. * test/percent.y: ditto. Wed Nov 5 19:11:15 2003 Minero Aoki * bin/racc (get_options): remove --no-extensions option; racc/parser is preloaded, Racc_No_Extension does not work. Mon Nov 3 22:41:42 2003 Minero Aoki * bin/racc: apply latest coding style. * lib/racc/parser.rb: ditto. * lib/racc/compat.rb: add File.read. Mon Nov 3 21:20:25 2003 Minero Aoki * ext/racc/cparse/cparse.c (parse_main): abort if length of state stack <=1, not ==0. * lib/racc/parser.rb: use <=1, not <2. * ext/racc/cparse/cparse.c: check_*() -> assert_*() * ext/racc/cparse/cparse.c (racc_cparse): define lvar `v' for debugging. * ext/racc/cparse/cparse.c (racc_yyparse): ditto. Mon Nov 3 17:21:55 2003 Minero Aoki * Makefile (all): make cparse.so. Mon Nov 3 17:19:26 2003 Minero Aoki * lib/racc/parser.rb: update version. * ext/racc/cparse/cparse.c: update version. Mon Nov 3 17:19:01 2003 Minero Aoki * Makefile: update version in parser.rb, cparse.c. Sun Oct 12 23:49:58 2003 Minero Aoki * version 1.4.4. Sun Oct 12 23:49:40 2003 Minero Aoki * bin/y2racc: did not work. * bin/y2racc: -u options did not work. Sun Oct 12 23:41:46 2003 Minero Aoki * misc/dist.sh: cd before make. Sun Oct 12 23:38:04 2003 Minero Aoki * Makefile (site): create $siteroot/{ja,en}/man/racc/*.html. Sun Oct 12 23:37:18 2003 Minero Aoki * doc/parser.rrd.m: missing 'j'. Sun Oct 12 23:29:11 2003 Minero Aoki * Makefile: new target `doc'. * Makefile: new target `clean'. * lib/racc/Makefile: new target `clean'. * misc/dist.sh: create documents before pack. Sun Oct 12 23:27:58 2003 Minero Aoki * doc/debug.rd.m: junk char was inserted. * doc/index.html.m: en/ja text were mixed. * doc/parser.rrd.m: add return values. * doc/usage.html.m: fix hyper link. Sun Oct 12 22:57:28 2003 Minero Aoki * doc.en/changes.html, doc.ja/changes.html -> doc/NEWS.rd.m * doc.en/command.html, doc.ja/command.html -> doc/command.html.m * doc.en/debug.html, doc.ja/debug.html -> doc/debug.rd.m * doc.en/grammar.html, doc.ja/grammar.html -> doc/grammar.rd.m * doc.en/index.html, doc.ja/index.html -> doc/index.html.m * doc.en/parser.html, doc.ja/parser.html -> doc/parser.rrd.m * doc.en/usage.html, doc.ja/usage.html -> doc/usage.html.m Sun Oct 12 18:46:21 2003 Minero Aoki * web/racc.ja.html: update descriptions. * web/racc.en.html: ditto. Sun Oct 12 18:43:45 2003 Minero Aoki * misc/dist.sh: remove web/ directory before distribute. Sun Oct 12 18:37:29 2003 Minero Aoki * Makefile: new target `site'. * web/racc.ja.html: new file. * web/racc.en.html: new file. Sun Oct 12 18:30:55 2003 Minero Aoki * misc/dist.sh: forgot to remove tmp comment out. Sun Oct 12 18:12:09 2003 Minero Aoki * lib/racc/info.rb: version 1.4.4. Sun Oct 12 18:11:42 2003 Minero Aoki * Makefile (dist): split out misc/dist.sh. * misc/dist.sh: new file. Sun Oct 12 17:18:47 2003 Minero Aoki * README.en: update documents. * README.ja: ditto. * doc.en/changes.html: ditto. * doc.en/command.html: ditto. * doc.en/debug.html: ditto. * doc.en/grammar.html: ditto. * doc.en/index.html: ditto. * doc.en/parser.html: ditto. * doc.en/usage.html: ditto. * doc.ja/changes.html: ditto. * doc.ja/command.html: ditto. * doc.ja/debug.html: ditto. * doc.ja/index.html: ditto. * doc.ja/parser.html: ditto. * doc.ja/usage.html: ditto. Sun Oct 12 16:24:46 2003 Minero Aoki * sameple/calc-ja.y: simplify. Sun Oct 12 16:24:16 2003 Minero Aoki * misc/y2racc -> bin/y2racc * misc/racc2y -> bin/racc2y Sun Oct 12 15:56:30 2003 Minero Aoki * bin/racc: follow method name change. Sun Oct 12 15:34:14 2003 Minero Aoki * Makefile: new target `test'. * Makefile: missing $datadir. Sun Oct 12 15:33:02 2003 Minero Aoki * README.ja: update description. * README.en: ditto. Sun Oct 12 15:25:23 2003 Minero Aoki * lib/racc/compiler.rb: adjust file names. * lib/racc/grammarfileparser.rb.in: ditto. * lib/racc/grammarfilescanner.rb: ditto. Sun Oct 12 15:24:53 2003 Minero Aoki * Makefile: new file. Sun Oct 12 15:19:57 2003 Minero Aoki * BUGS.en: removed. * BUGS.ja: removed. Sun Oct 12 15:10:38 2003 Minero Aoki * racc -> bin/racc * .cvsignore -> lib/racc/.cvsignore * lib/racc/Makefile: new file. * boot.rb -> lib/racc/boot * compat.rb -> lib/racc/compat.rb * compiler.rb -> lib/racc/compiler.rb * grammar.rb -> lib/racc/grammar.rb * in.raccp.rb -> lib/racc/grammarfileparser.rb.in * raccs.rb -> lib/racc/grammarfilescanner.rb * info.rb -> lib/racc/info.rb * iset.rb -> lib/racc/iset.rb * outpur.rb -> lib/racc/output.rb * parser.rb -> lib/racc/parser.rb * rubyloader.rb -> lib/racc/rubyloader.rb * state.rb -> lib/racc/state.rb * ucodep.rb -> lib/racc/usercodeparser.rb * cparse/MANIFEST -> ext/racc/cparse/MANIFEST * cparse/cparse.c -> ext/racc/cparse/cparse.c * cparse/depend -> ext/racc/cparse/depend * cparse/extconf.rb -> ext/racc/cparse/extconf.rb * cparse/.cvsignore -> ext/racc/cparse/.cvsignore Sun Oct 12 15:10:13 2003 Minero Aoki * test/test.rb: use /bin/rm if exists. Sun Oct 12 14:33:29 2003 Minero Aoki * rubyloader.rb: imported from amstd, rev 1.5. Sun Oct 12 14:24:47 2003 Minero Aoki * boot.rb: reformat only. * compiler.rb: ditto. * grammar.rb: ditto. * in.raccp.rb: ditto. * iset.rb: ditto. * output.rb: ditto. * raccs.rb: ditto. * state.rb: ditto. Sun Oct 12 14:17:22 2003 Minero Aoki * test/test.rb: refactoring. Tue Jun 24 03:14:01 2003 Minero Aoki * ucodep.rb: typo: Grammer -> Grammar Mon May 26 23:06:58 2003 Minero Aoki * compiler.rb: update copyright year. * grammar.rb: ditto. * in.raccp.rb: ditto. * info.rb: ditto. * iset.rb: ditto. * output.rb: ditto. * parser.rb: ditto. * raccs.rb: ditto. * state.rb: ditto. * ucodep.rb: ditto. Sun May 25 13:21:27 2003 Minero Aoki * raccs.rb: update coding style. Fri Nov 15 17:53:12 2002 Minero Aoki * racc: changes style. * parser.rb: ditto. Fri Nov 15 17:11:52 2002 Minero Aoki version 1.4.3. Fri Nov 15 17:08:01 2002 Minero Aoki * boot.rb, compiler.rb, grammar.rb, in.raccp.rb, iset.rb, output.rb, parser.rb, racc, raccs.rb, state.rb, ucodep.rb, misc/racc2y, misc/y2racc: follows (my) latest coding styles. Thu Nov 14 14:39:53 2002 Minero Aoki * raccs.rb: explicit method call for VCALL. Wed Oct 16 15:45:11 2002 Minero Aoki * parser.rb: reformat. Fri Aug 9 18:21:01 2002 Minero Aoki * cparse/cparse.c: use better variable/macro names. Wed Aug 7 08:39:19 2002 Minero Aoki * cparse/cparse.c: goto label requires stmt. Mon Aug 5 21:53:07 2002 Minero Aoki * cparse/cparse.c: grand refine. * cparse/depend: re-added from ruby/ext/racc/cparse. Tue Jun 4 00:15:28 2002 Minero Aoki * boot.rb: allow to omit last 'end'. Mon Jun 3 23:29:45 2002 Minero Aoki * racc (write_table_file): shebang must placed on first line. (reported by Hiroyuki Sato) racc-1.4.14/ext/0000755000004100000410000000000012625454503013347 5ustar www-datawww-dataracc-1.4.14/ext/racc/0000755000004100000410000000000012625454503014257 5ustar www-datawww-dataracc-1.4.14/ext/racc/cparse.c0000644000004100000410000005660712625454503015716 0ustar www-datawww-data/* cparse.c -- Racc Runtime Core Copyright (c) 1999-2006 Minero Aoki This library is free software. You can distribute/modify this program under the same terms of ruby. $originalId: cparse.c,v 1.8 2006/07/06 11:39:46 aamine Exp $ */ #include /* ----------------------------------------------------------------------- Important Constants ----------------------------------------------------------------------- */ #define RACC_VERSION "1.4.14" #define DEFAULT_TOKEN -1 #define ERROR_TOKEN 1 #define FINAL_TOKEN 0 #define vDEFAULT_TOKEN INT2FIX(DEFAULT_TOKEN) #define vERROR_TOKEN INT2FIX(ERROR_TOKEN) #define vFINAL_TOKEN INT2FIX(FINAL_TOKEN) /* ----------------------------------------------------------------------- File Local Variables ----------------------------------------------------------------------- */ static VALUE RaccBug; static VALUE CparseParams; static ID id_yydebug; static ID id_nexttoken; static ID id_onerror; static ID id_noreduce; static ID id_errstatus; static ID id_d_shift; static ID id_d_reduce; static ID id_d_accept; static ID id_d_read_token; static ID id_d_next_state; static ID id_d_e_pop; /* ----------------------------------------------------------------------- Utils ----------------------------------------------------------------------- */ /* For backward compatibility */ #ifndef ID2SYM # define ID2SYM(i) ULONG2NUM(i) #endif #ifndef SYM2ID # define SYM2ID(v) ((ID)NUM2ULONG(v)) #endif #ifndef SYMBOL_P # define SYMBOL_P(v) FIXNUM_P(v) #endif #ifndef LONG2NUM # define LONG2NUM(i) INT2NUM(i) #endif #ifndef HAVE_RB_ARY_SUBSEQ # define rb_ary_subseq(ary, beg, len) rb_ary_new4(len, RARRAY_PTR(ary) + beg) #endif static ID value_to_id _((VALUE v)); static inline long num_to_long _((VALUE n)); static ID value_to_id(VALUE v) { if (! SYMBOL_P(v)) { rb_raise(rb_eTypeError, "not symbol"); } return SYM2ID(v); } static inline long num_to_long(VALUE n) { return NUM2LONG(n); } #define AREF(s, idx) \ ((0 <= idx && idx < RARRAY_LEN(s)) ? rb_ary_entry(s, idx) : Qnil) /* ----------------------------------------------------------------------- Parser Stack Interfaces ----------------------------------------------------------------------- */ static VALUE get_stack_tail _((VALUE stack, long len)); static void cut_stack_tail _((VALUE stack, long len)); static VALUE get_stack_tail(VALUE stack, long len) { if (len < 0) return Qnil; /* system error */ if (len > RARRAY_LEN(stack)) len = RARRAY_LEN(stack); return rb_ary_subseq(stack, RARRAY_LEN(stack) - len, len); } static void cut_stack_tail(VALUE stack, long len) { while (len > 0) { rb_ary_pop(stack); len--; } } #define STACK_INIT_LEN 64 #define NEW_STACK() rb_ary_new2(STACK_INIT_LEN) #define PUSH(s, i) rb_ary_store(s, RARRAY_LEN(s), i) #define POP(s) rb_ary_pop(s) #define LAST_I(s) \ ((RARRAY_LEN(s) > 0) ? rb_ary_entry(s, RARRAY_LEN(s) - 1) : Qnil) #define GET_TAIL(s, len) get_stack_tail(s, len) #define CUT_TAIL(s, len) cut_stack_tail(s, len) /* ----------------------------------------------------------------------- struct cparse_params ----------------------------------------------------------------------- */ struct cparse_params { VALUE value_v; /* VALUE version of this struct */ VALUE parser; /* parser object */ int lex_is_iterator; VALUE lexer; /* scanner object */ ID lexmid; /* name of scanner method (must be an iterator) */ /* State transition tables (immutable) Data structure is from Dragon Book 4.9 */ /* action table */ VALUE action_table; VALUE action_check; VALUE action_default; VALUE action_pointer; /* goto table */ VALUE goto_table; VALUE goto_check; VALUE goto_default; VALUE goto_pointer; long nt_base; /* NonTerminal BASE index */ VALUE reduce_table; /* reduce data table */ VALUE token_table; /* token conversion table */ /* parser stacks and parameters */ VALUE state; long curstate; VALUE vstack; VALUE tstack; VALUE t; long shift_n; long reduce_n; long ruleno; long errstatus; /* nonzero in error recovering mode */ long nerr; /* number of error */ int use_result_var; VALUE retval; /* return value of parser routine */ long fin; /* parse result status */ #define CP_FIN_ACCEPT 1 #define CP_FIN_EOT 2 #define CP_FIN_CANTPOP 3 int debug; /* user level debug */ int sys_debug; /* system level debug */ long i; /* table index */ }; /* ----------------------------------------------------------------------- Parser Main Routines ----------------------------------------------------------------------- */ static VALUE racc_cparse _((VALUE parser, VALUE arg, VALUE sysdebug)); static VALUE racc_yyparse _((VALUE parser, VALUE lexer, VALUE lexmid, VALUE arg, VALUE sysdebug)); static void call_lexer _((struct cparse_params *v)); static VALUE lexer_i _((VALUE block_args, VALUE data, VALUE self)); static VALUE assert_array _((VALUE a)); static long assert_integer _((VALUE n)); static VALUE assert_hash _((VALUE h)); static VALUE initialize_params _((VALUE vparams, VALUE parser, VALUE arg, VALUE lexer, VALUE lexmid)); static void cparse_params_mark _((void *ptr)); static void parse_main _((struct cparse_params *v, VALUE tok, VALUE val, int resume)); static void extract_user_token _((struct cparse_params *v, VALUE block_args, VALUE *tok, VALUE *val)); static void shift _((struct cparse_params* v, long act, VALUE tok, VALUE val)); static int reduce _((struct cparse_params* v, long act)); static VALUE reduce0 _((VALUE block_args, VALUE data, VALUE self)); #ifdef DEBUG # define D_puts(msg) if (v->sys_debug) puts(msg) # define D_printf(fmt,arg) if (v->sys_debug) printf(fmt,arg) #else # define D_puts(msg) # define D_printf(fmt,arg) #endif static VALUE racc_cparse(VALUE parser, VALUE arg, VALUE sysdebug) { volatile VALUE vparams; struct cparse_params *v; vparams = Data_Make_Struct(CparseParams, struct cparse_params, cparse_params_mark, -1, v); D_puts("starting cparse"); v->sys_debug = RTEST(sysdebug); vparams = initialize_params(vparams, parser, arg, Qnil, Qnil); v->lex_is_iterator = Qfalse; parse_main(v, Qnil, Qnil, 0); return v->retval; } static VALUE racc_yyparse(VALUE parser, VALUE lexer, VALUE lexmid, VALUE arg, VALUE sysdebug) { volatile VALUE vparams; struct cparse_params *v; vparams = Data_Make_Struct(CparseParams, struct cparse_params, cparse_params_mark, -1, v); v->sys_debug = RTEST(sysdebug); D_puts("start C yyparse"); vparams = initialize_params(vparams, parser, arg, lexer, lexmid); v->lex_is_iterator = Qtrue; D_puts("params initialized"); parse_main(v, Qnil, Qnil, 0); call_lexer(v); if (!v->fin) { rb_raise(rb_eArgError, "%s() is finished before EndOfToken", rb_id2name(v->lexmid)); } return v->retval; } #ifdef HAVE_RB_BLOCK_CALL static void call_lexer(struct cparse_params *v) { rb_block_call(v->lexer, v->lexmid, 0, NULL, lexer_i, v->value_v); } #else static VALUE lexer_iter(VALUE data) { struct cparse_params *v; Data_Get_Struct(data, struct cparse_params, v); rb_funcall(v->lexer, v->lexmid, 0); return Qnil; } static void call_lexer(struct cparse_params *v) { rb_iterate(lexer_iter, v->value_v, lexer_i, v->value_v); } #endif static VALUE lexer_i(VALUE block_args, VALUE data, VALUE self) { struct cparse_params *v; VALUE tok, val; Data_Get_Struct(data, struct cparse_params, v); if (v->fin) rb_raise(rb_eArgError, "extra token after EndOfToken"); extract_user_token(v, block_args, &tok, &val); parse_main(v, tok, val, 1); if (v->fin && v->fin != CP_FIN_ACCEPT) rb_iter_break(); return Qnil; } static VALUE assert_array(VALUE a) { Check_Type(a, T_ARRAY); return a; } static VALUE assert_hash(VALUE h) { Check_Type(h, T_HASH); return h; } static long assert_integer(VALUE n) { return NUM2LONG(n); } static VALUE initialize_params(VALUE vparams, VALUE parser, VALUE arg, VALUE lexer, VALUE lexmid) { struct cparse_params *v; Data_Get_Struct(vparams, struct cparse_params, v); v->value_v = vparams; v->parser = parser; v->lexer = lexer; if (! NIL_P(lexmid)) v->lexmid = value_to_id(lexmid); v->debug = RTEST(rb_ivar_get(parser, id_yydebug)); Check_Type(arg, T_ARRAY); if (!(13 <= RARRAY_LEN(arg) && RARRAY_LEN(arg) <= 14)) rb_raise(RaccBug, "[Racc Bug] wrong arg.size %ld", RARRAY_LEN(arg)); v->action_table = assert_array (rb_ary_entry(arg, 0)); v->action_check = assert_array (rb_ary_entry(arg, 1)); v->action_default = assert_array (rb_ary_entry(arg, 2)); v->action_pointer = assert_array (rb_ary_entry(arg, 3)); v->goto_table = assert_array (rb_ary_entry(arg, 4)); v->goto_check = assert_array (rb_ary_entry(arg, 5)); v->goto_default = assert_array (rb_ary_entry(arg, 6)); v->goto_pointer = assert_array (rb_ary_entry(arg, 7)); v->nt_base = assert_integer(rb_ary_entry(arg, 8)); v->reduce_table = assert_array (rb_ary_entry(arg, 9)); v->token_table = assert_hash (rb_ary_entry(arg, 10)); v->shift_n = assert_integer(rb_ary_entry(arg, 11)); v->reduce_n = assert_integer(rb_ary_entry(arg, 12)); if (RARRAY_LEN(arg) > 13) { v->use_result_var = RTEST(rb_ary_entry(arg, 13)); } else { v->use_result_var = Qtrue; } v->tstack = v->debug ? NEW_STACK() : Qnil; v->vstack = NEW_STACK(); v->state = NEW_STACK(); v->curstate = 0; PUSH(v->state, INT2FIX(0)); v->t = INT2FIX(FINAL_TOKEN + 1); /* must not init to FINAL_TOKEN */ v->nerr = 0; v->errstatus = 0; rb_ivar_set(parser, id_errstatus, LONG2NUM(v->errstatus)); v->retval = Qnil; v->fin = 0; v->lex_is_iterator = Qfalse; rb_iv_set(parser, "@vstack", v->vstack); if (v->debug) { rb_iv_set(parser, "@tstack", v->tstack); } else { rb_iv_set(parser, "@tstack", Qnil); } return vparams; } static void cparse_params_mark(void *ptr) { struct cparse_params *v = (struct cparse_params*)ptr; rb_gc_mark(v->value_v); rb_gc_mark(v->parser); rb_gc_mark(v->lexer); rb_gc_mark(v->action_table); rb_gc_mark(v->action_check); rb_gc_mark(v->action_default); rb_gc_mark(v->action_pointer); rb_gc_mark(v->goto_table); rb_gc_mark(v->goto_check); rb_gc_mark(v->goto_default); rb_gc_mark(v->goto_pointer); rb_gc_mark(v->reduce_table); rb_gc_mark(v->token_table); rb_gc_mark(v->state); rb_gc_mark(v->vstack); rb_gc_mark(v->tstack); rb_gc_mark(v->t); rb_gc_mark(v->retval); } static void extract_user_token(struct cparse_params *v, VALUE block_args, VALUE *tok, VALUE *val) { if (NIL_P(block_args)) { /* EOF */ *tok = Qfalse; *val = rb_str_new("$", 1); return; } if (TYPE(block_args) != T_ARRAY) { rb_raise(rb_eTypeError, "%s() %s %s (must be Array[2])", v->lex_is_iterator ? rb_id2name(v->lexmid) : "next_token", v->lex_is_iterator ? "yielded" : "returned", rb_class2name(CLASS_OF(block_args))); } if (RARRAY_LEN(block_args) != 2) { rb_raise(rb_eArgError, "%s() %s wrong size of array (%ld for 2)", v->lex_is_iterator ? rb_id2name(v->lexmid) : "next_token", v->lex_is_iterator ? "yielded" : "returned", RARRAY_LEN(block_args)); } *tok = AREF(block_args, 0); *val = AREF(block_args, 1); } #define SHIFT(v,act,tok,val) shift(v,act,tok,val) #define REDUCE(v,act) do {\ switch (reduce(v,act)) { \ case 0: /* normal */ \ break; \ case 1: /* yyerror */ \ goto user_yyerror; \ case 2: /* yyaccept */ \ D_puts("u accept"); \ goto accept; \ default: \ break; \ } \ } while (0) static void parse_main(struct cparse_params *v, VALUE tok, VALUE val, int resume) { long i; /* table index */ long act; /* action type */ VALUE act_value; /* action type, VALUE version */ int read_next = 1; /* true if we need to read next token */ VALUE tmp; if (resume) goto resume; while (1) { D_puts(""); D_puts("---- enter new loop ----"); D_puts(""); D_printf("(act) k1=%ld\n", v->curstate); tmp = AREF(v->action_pointer, v->curstate); if (NIL_P(tmp)) goto notfound; D_puts("(act) pointer[k1] ok"); i = NUM2LONG(tmp); D_printf("read_next=%d\n", read_next); if (read_next && (v->t != vFINAL_TOKEN)) { if (v->lex_is_iterator) { D_puts("resuming..."); if (v->fin) rb_raise(rb_eArgError, "token given after EOF"); v->i = i; /* save i */ return; resume: D_puts("resumed"); i = v->i; /* load i */ } else { D_puts("next_token"); tmp = rb_funcall(v->parser, id_nexttoken, 0); extract_user_token(v, tmp, &tok, &val); } /* convert token */ v->t = rb_hash_aref(v->token_table, tok); if (NIL_P(v->t)) { v->t = vERROR_TOKEN; } D_printf("(act) t(k2)=%ld\n", NUM2LONG(v->t)); if (v->debug) { rb_funcall(v->parser, id_d_read_token, 3, v->t, tok, val); } } read_next = 0; i += NUM2LONG(v->t); D_printf("(act) i=%ld\n", i); if (i < 0) goto notfound; act_value = AREF(v->action_table, i); if (NIL_P(act_value)) goto notfound; act = NUM2LONG(act_value); D_printf("(act) table[i]=%ld\n", act); tmp = AREF(v->action_check, i); if (NIL_P(tmp)) goto notfound; if (NUM2LONG(tmp) != v->curstate) goto notfound; D_printf("(act) check[i]=%ld\n", NUM2LONG(tmp)); D_puts("(act) found"); act_fixed: D_printf("act=%ld\n", act); goto handle_act; notfound: D_puts("(act) not found: use default"); act_value = AREF(v->action_default, v->curstate); act = NUM2LONG(act_value); goto act_fixed; handle_act: if (act > 0 && act < v->shift_n) { D_puts("shift"); if (v->errstatus > 0) { v->errstatus--; rb_ivar_set(v->parser, id_errstatus, LONG2NUM(v->errstatus)); } SHIFT(v, act, v->t, val); read_next = 1; } else if (act < 0 && act > -(v->reduce_n)) { D_puts("reduce"); REDUCE(v, act); } else if (act == -(v->reduce_n)) { goto error; error_recovered: ; /* goto label requires stmt */ } else if (act == v->shift_n) { D_puts("accept"); goto accept; } else { rb_raise(RaccBug, "[Racc Bug] unknown act value %ld", act); } if (v->debug) { rb_funcall(v->parser, id_d_next_state, 2, LONG2NUM(v->curstate), v->state); } } /* not reach */ accept: if (v->debug) rb_funcall(v->parser, id_d_accept, 0); v->retval = rb_ary_entry(v->vstack, 0); v->fin = CP_FIN_ACCEPT; return; error: D_printf("error detected, status=%ld\n", v->errstatus); if (v->errstatus == 0) { v->nerr++; rb_funcall(v->parser, id_onerror, 3, v->t, val, v->vstack); } user_yyerror: if (v->errstatus == 3) { if (v->t == vFINAL_TOKEN) { v->retval = Qfalse; v->fin = CP_FIN_EOT; return; } read_next = 1; } v->errstatus = 3; rb_ivar_set(v->parser, id_errstatus, LONG2NUM(v->errstatus)); /* check if we can shift/reduce error token */ D_printf("(err) k1=%ld\n", v->curstate); D_printf("(err) k2=%d (error)\n", ERROR_TOKEN); while (1) { tmp = AREF(v->action_pointer, v->curstate); if (NIL_P(tmp)) goto error_pop; D_puts("(err) pointer[k1] ok"); i = NUM2LONG(tmp) + ERROR_TOKEN; D_printf("(err) i=%ld\n", i); if (i < 0) goto error_pop; act_value = AREF(v->action_table, i); if (NIL_P(act_value)) { D_puts("(err) table[i] == nil"); goto error_pop; } act = NUM2LONG(act_value); D_printf("(err) table[i]=%ld\n", act); tmp = AREF(v->action_check, i); if (NIL_P(tmp)) { D_puts("(err) check[i] == nil"); goto error_pop; } if (NUM2LONG(tmp) != v->curstate) { D_puts("(err) check[i] != k1"); goto error_pop; } D_puts("(err) found: can handle error token"); break; error_pop: D_puts("(err) act not found: can't handle error token; pop"); if (RARRAY_LEN(v->state) <= 1) { v->retval = Qnil; v->fin = CP_FIN_CANTPOP; return; } POP(v->state); POP(v->vstack); v->curstate = num_to_long(LAST_I(v->state)); if (v->debug) { POP(v->tstack); rb_funcall(v->parser, id_d_e_pop, 3, v->state, v->tstack, v->vstack); } } /* shift/reduce error token */ if (act > 0 && act < v->shift_n) { D_puts("e shift"); SHIFT(v, act, ERROR_TOKEN, val); } else if (act < 0 && act > -(v->reduce_n)) { D_puts("e reduce"); REDUCE(v, act); } else if (act == v->shift_n) { D_puts("e accept"); goto accept; } else { rb_raise(RaccBug, "[Racc Bug] unknown act value %ld", act); } goto error_recovered; } static void shift(struct cparse_params *v, long act, VALUE tok, VALUE val) { PUSH(v->vstack, val); if (v->debug) { PUSH(v->tstack, tok); rb_funcall(v->parser, id_d_shift, 3, tok, v->tstack, v->vstack); } v->curstate = act; PUSH(v->state, LONG2NUM(v->curstate)); } static int reduce(struct cparse_params *v, long act) { VALUE code; v->ruleno = -act * 3; code = rb_catch("racc_jump", reduce0, v->value_v); v->errstatus = num_to_long(rb_ivar_get(v->parser, id_errstatus)); return NUM2INT(code); } static VALUE reduce0(VALUE val, VALUE data, VALUE self) { struct cparse_params *v; VALUE reduce_to, reduce_len, method_id; long len; ID mid; VALUE tmp, tmp_t = Qundef, tmp_v = Qundef; long i, k1, k2; VALUE goto_state; Data_Get_Struct(data, struct cparse_params, v); reduce_len = rb_ary_entry(v->reduce_table, v->ruleno); reduce_to = rb_ary_entry(v->reduce_table, v->ruleno+1); method_id = rb_ary_entry(v->reduce_table, v->ruleno+2); len = NUM2LONG(reduce_len); mid = value_to_id(method_id); /* call action */ if (len == 0) { tmp = Qnil; if (mid != id_noreduce) tmp_v = rb_ary_new(); if (v->debug) tmp_t = rb_ary_new(); } else { if (mid != id_noreduce) { tmp_v = GET_TAIL(v->vstack, len); tmp = rb_ary_entry(tmp_v, 0); } else { tmp = rb_ary_entry(v->vstack, RARRAY_LEN(v->vstack) - len); } CUT_TAIL(v->vstack, len); if (v->debug) { tmp_t = GET_TAIL(v->tstack, len); CUT_TAIL(v->tstack, len); } CUT_TAIL(v->state, len); } if (mid != id_noreduce) { if (v->use_result_var) { tmp = rb_funcall(v->parser, mid, 3, tmp_v, v->vstack, tmp); } else { tmp = rb_funcall(v->parser, mid, 2, tmp_v, v->vstack); } } /* then push result */ PUSH(v->vstack, tmp); if (v->debug) { PUSH(v->tstack, reduce_to); rb_funcall(v->parser, id_d_reduce, 4, tmp_t, reduce_to, v->tstack, v->vstack); } /* calculate transition state */ if (RARRAY_LEN(v->state) == 0) rb_raise(RaccBug, "state stack unexpectedly empty"); k2 = num_to_long(LAST_I(v->state)); k1 = num_to_long(reduce_to) - v->nt_base; D_printf("(goto) k1=%ld\n", k1); D_printf("(goto) k2=%ld\n", k2); tmp = AREF(v->goto_pointer, k1); if (NIL_P(tmp)) goto notfound; i = NUM2LONG(tmp) + k2; D_printf("(goto) i=%ld\n", i); if (i < 0) goto notfound; goto_state = AREF(v->goto_table, i); if (NIL_P(goto_state)) { D_puts("(goto) table[i] == nil"); goto notfound; } D_printf("(goto) table[i]=%ld (goto_state)\n", NUM2LONG(goto_state)); tmp = AREF(v->goto_check, i); if (NIL_P(tmp)) { D_puts("(goto) check[i] == nil"); goto notfound; } if (tmp != LONG2NUM(k1)) { D_puts("(goto) check[i] != table[i]"); goto notfound; } D_printf("(goto) check[i]=%ld\n", NUM2LONG(tmp)); D_puts("(goto) found"); transit: PUSH(v->state, goto_state); v->curstate = NUM2LONG(goto_state); return INT2FIX(0); notfound: D_puts("(goto) not found: use default"); /* overwrite `goto-state' by default value */ goto_state = AREF(v->goto_default, k1); goto transit; } /* ----------------------------------------------------------------------- Ruby Interface ----------------------------------------------------------------------- */ void Init_cparse(void) { VALUE Racc, Parser; ID id_racc = rb_intern("Racc"); if (rb_const_defined(rb_cObject, id_racc)) { Racc = rb_const_get(rb_cObject, id_racc); Parser = rb_const_get_at(Racc, rb_intern("Parser")); } else { Racc = rb_define_module("Racc"); Parser = rb_define_class_under(Racc, "Parser", rb_cObject); } rb_define_private_method(Parser, "_racc_do_parse_c", racc_cparse, 2); rb_define_private_method(Parser, "_racc_yyparse_c", racc_yyparse, 4); rb_define_const(Parser, "Racc_Runtime_Core_Version_C", rb_str_new2(RACC_VERSION)); rb_define_const(Parser, "Racc_Runtime_Core_Id_C", rb_str_new2("$originalId: cparse.c,v 1.8 2006/07/06 11:39:46 aamine Exp $")); CparseParams = rb_define_class_under(Racc, "CparseParams", rb_cObject); RaccBug = rb_eRuntimeError; id_yydebug = rb_intern("@yydebug"); id_nexttoken = rb_intern("next_token"); id_onerror = rb_intern("on_error"); id_noreduce = rb_intern("_reduce_none"); id_errstatus = rb_intern("@racc_error_status"); id_d_shift = rb_intern("racc_shift"); id_d_reduce = rb_intern("racc_reduce"); id_d_accept = rb_intern("racc_accept"); id_d_read_token = rb_intern("racc_read_token"); id_d_next_state = rb_intern("racc_next_state"); id_d_e_pop = rb_intern("racc_e_pop"); } racc-1.4.14/ext/racc/extconf.rb0000644000004100000410000000017512625454503016255 0ustar www-datawww-data# $Id: 1e30abedf4eea155815d1efa5500ec817b10a2ab $ require 'mkmf' have_func('rb_ary_subseq') create_makefile 'racc/cparse' racc-1.4.14/ext/racc/com/0000755000004100000410000000000012625454503015035 5ustar www-datawww-dataracc-1.4.14/ext/racc/com/headius/0000755000004100000410000000000012625454503016457 5ustar www-datawww-dataracc-1.4.14/ext/racc/com/headius/racc/0000755000004100000410000000000012625454503017367 5ustar www-datawww-dataracc-1.4.14/ext/racc/com/headius/racc/Cparse.java0000644000004100000410000010452212625454503021453 0ustar www-datawww-datapackage com.headius.racc; import org.jruby.Ruby; import org.jruby.RubyArray; import org.jruby.RubyBasicObject; import org.jruby.RubyClass; import org.jruby.RubyContinuation; import org.jruby.RubyFixnum; import org.jruby.RubyHash; import org.jruby.RubyModule; import org.jruby.RubyNumeric; import org.jruby.RubyObject; import org.jruby.RubySymbol; import org.jruby.anno.JRubyMethod; import org.jruby.exceptions.JumpException; import org.jruby.internal.runtime.methods.AttrReaderMethod; import org.jruby.internal.runtime.methods.AttrWriterMethod; import org.jruby.internal.runtime.methods.CallConfiguration; import org.jruby.runtime.Arity; import org.jruby.runtime.Block; import org.jruby.runtime.BlockCallback; import org.jruby.runtime.CallBlock19; import org.jruby.runtime.CallSite; import org.jruby.runtime.Helpers; import org.jruby.runtime.MethodIndex; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.load.Library; public class Cparse implements Library { public static final String RACC_VERSION = "1.4.14"; // TODO: parse from Cparse.c public enum TokenType { DEFAULT(-1), FINAL(0), ERROR(1); private final int id; TokenType(int id) { this.id = id; } } private RubyFixnum vDEFAULT_TOKEN; private RubyFixnum vERROR_TOKEN; private RubyFixnum vFINAL_TOKEN; private RubyClass RaccBug; private RubyClass CparseParams; private static final String ID_YYDEBUG = "@yydebug"; private static final String ID_NEXTTOKEN = "next_token"; private static final String ID_ONERROR = "on_error"; private static final String ID_NOREDUCE = "_reduce_none"; private static final String ID_ERRSTATUS = "@racc_error_status"; private static final String ID_D_SHIFT = "racc_shift"; private static final String ID_D_REDUCE = "racc_reduce"; private static final String ID_D_ACCEPT = "racc_accept"; private static final String ID_D_READ_TOKEN = "racc_read_token"; private static final String ID_D_NEXT_STATE = "racc_next_state"; private static final String ID_D_E_POP = "racc_e_pop"; private RubySymbol sym_noreduce; private CallSite call_nexttoken; private CallSite call_onerror; private CallSite call_d_shift; private CallSite call_d_reduce; private CallSite call_d_accept; private CallSite call_d_read_token; private CallSite call_d_next_state; private CallSite call_d_e_pop; private static RubySymbol value_to_id(ThreadContext context, IRubyObject v) { if (!(v instanceof RubySymbol)) { throw context.runtime.newTypeError("not symbol"); } return (RubySymbol)v; } private static int num_to_int(IRubyObject n) { return assert_integer(n); } private static IRubyObject AREF(ThreadContext context, IRubyObject s, int idx) { return ((0 <= idx && idx < ((RubyArray)s).size()) ? ((RubyArray)s).entry(idx) : context.nil); } private static IRubyObject get_stack_tail(ThreadContext context, RubyArray stack, int len) { if (len < 0) return context.nil; int size = stack.size(); len = Math.min(len, size); return stack.subseq(size - len, len); } private static void cut_stack_tail(ThreadContext context, RubyArray stack, int len) { while (len > 0) { stack.pop(context); len--; } } private static final int STACK_INIT_LEN = 64; private static RubyArray NEW_STACK(ThreadContext context) { return context.runtime.newArray(STACK_INIT_LEN); } private static IRubyObject PUSH(RubyArray stack, IRubyObject i) { return stack.append(i); } private static IRubyObject POP(ThreadContext context, RubyArray stack) { return stack.pop(context); } private static IRubyObject LAST_I(ThreadContext context, RubyArray stack) { return stack.size() > 0 ? stack.last() : context.nil; } private static IRubyObject GET_TAIL(ThreadContext context, RubyArray stack, int len) { return get_stack_tail(context, stack, len); } private static void CUT_TAIL(ThreadContext context, RubyArray stack, int len) { cut_stack_tail(context, stack, len); } static final int CP_FIN_ACCEPT = 1; static final int CP_FIN_EOT = 2; static final int CP_FIN_CANTPOP = 3; public class CparseParams extends RubyObject { public CparseParams(Ruby runtime, RubyClass rubyClass) { super(runtime, rubyClass); } public void initialize_params(ThreadContext context, Parser parser, IRubyObject arg, IRubyObject lexer, IRubyObject lexmid) { Ruby runtime = context.runtime; this.parser = parser; this.lexer = lexer; if (!lexmid.isNil()) { this.lexmid = value_to_id(context, lexmid); this.call_lexmid = MethodIndex.getFunctionalCallSite(this.lexmid.toString()); } this.debug = parser.getInstanceVariable(ID_YYDEBUG).isTrue(); RubyArray argAry = arg.convertToArray(); if (!(13 <= argAry.size() && argAry.size() <= 14)) { throw runtime.newRaiseException(RaccBug, "[Racc Bug] wrong arg.size " + argAry.size()); } this.action_table = assert_array(argAry.eltOk(0)); this.action_check = assert_array(argAry.eltOk(1)); this.action_default = assert_array(argAry.eltOk(2)); this.action_pointer = assert_array(argAry.eltOk(3)); this.goto_table = assert_array(argAry.eltOk(4)); this.goto_check = assert_array(argAry.eltOk(5)); this.goto_default = assert_array(argAry.eltOk(6)); this.goto_pointer = assert_array(argAry.eltOk(7)); this.nt_base = assert_integer(argAry.eltOk(8)); this.reduce_table = assert_array(argAry.eltOk(9)); this.token_table = assert_hash(argAry.eltOk(10)); this.shift_n = assert_integer(argAry.eltOk(11)); this.reduce_n = assert_integer(argAry.eltOk(12)); if (argAry.size() > 13) { this.use_result_var = argAry.eltOk(13).isTrue(); } else { this.use_result_var = true; } this.tstack = this.debug ? NEW_STACK(context) : null; this.vstack = NEW_STACK(context); this.state = NEW_STACK(context); this.curstate = 0; PUSH(this.state, RubyFixnum.zero(runtime)); this.t = runtime.newFixnum(TokenType.FINAL.id + 1); // must not init to FINAL_TOKEN this.nerr = 0; this.errstatus = 0; this.parser.setInstanceVariable(ID_ERRSTATUS, runtime.newFixnum(this.errstatus)); this.retval = context.nil; this.fin = 0; this.lex_is_iterator = false; parser.setInstanceVariable("@vstack", this.vstack); if (this.debug) { parser.setInstanceVariable("@tstack", this.tstack); } else { parser.setInstanceVariable("@tstack", context.nil); } } public void extract_user_token(ThreadContext context, IRubyObject block_args, IRubyObject[] tokVal) { if (block_args.isNil()) { /* EOF */ tokVal[0] = context.runtime.getFalse(); tokVal[1] = context.runtime.newString("$"); return; } if (!(block_args instanceof RubyArray)) { throw context.runtime.newTypeError( (lex_is_iterator ? lexmid.asJavaString() : "next_token") + " " + (lex_is_iterator ? "yielded" : "returned") + " " + block_args.getMetaClass().getName() + " (must be Array[2])"); } RubyArray block_args_ary = (RubyArray)block_args; if (block_args_ary.size() != 2) { throw context.runtime.newTypeError( (lex_is_iterator ? lexmid.asJavaString() : "next_token") + " " + (lex_is_iterator ? "yielded" : "returned") + " wrong size of array (" + block_args_ary.size() + " for 2)"); } tokVal[0] = ((RubyArray) block_args).eltOk(0); tokVal[1] = ((RubyArray) block_args).eltOk(1); } private static final int RESUME = 1; private static final int NOTFOUND = 2; private static final int ERROR_RECOVERED = 3; private static final int ERROR = 4; private static final int HANDLE_ACT = 5; private static final int ACT_FIXED = 6; private static final int ACCEPT = 7; private static final int USER_YYERROR = 8; private static final int ERROR_POP = 9; private static final int TRANSIT = 9; private void SHIFT(ThreadContext context, int act, IRubyObject tok, IRubyObject val) { shift(context, act, tok, val); } private int REDUCE(ThreadContext context, int act) { return reduce(context, act); } public void parse_main(ThreadContext context, IRubyObject tok, IRubyObject val, boolean resume) { Ruby runtime = context.runtime; int i = 0; /* table index */ int act = 0; /* action type */ IRubyObject act_value; /* action type, VALUE version */ boolean read_next = true; /* true if we need to read next token */ IRubyObject tmp; int branch = 0; if (resume) { branch = RESUME; } BRANCH: while (true) { switch (branch) { case 0: D_puts(""); D_puts("---- enter new loop ----"); D_puts(""); D_printf("(act) k1=%ld\n", this.curstate); tmp = AREF(context, this.action_pointer, this.curstate); if (tmp.isNil()) {branch = NOTFOUND; continue BRANCH;} D_puts("(act) pointer[k1] ok"); i = assert_integer(tmp); D_printf("read_next=%d\n", read_next); if (read_next && (this.t != vFINAL_TOKEN)) { if (this.lex_is_iterator) { D_puts("resuming..."); if (this.fin != 0) throw runtime.newArgumentError("token given after EOF"); this.i = i; /* save i */ return; // remainder of case duplicated from here for RESUME case //D_puts(this, "resumed"); //i = this.i; /* load i */ } else { D_puts("next_token"); tmp = call_nexttoken.call(context, this.parser, this.parser); IRubyObject[] tokVal = {tok, val}; extract_user_token(context, tmp, tokVal); tok = tokVal[0]; val = tokVal[1]; } /* convert token */ this.t = ((RubyHash)this.token_table).op_aref(context, tok); if (this.t.isNil()) { this.t = vERROR_TOKEN; } D_printf("(act) t(k2)=%ld\n", assert_integer(this.t)); if (this.debug) { call_d_read_token.call(context, this.parser, this.parser, this.t, tok, val); } } // duplicated logic from above for RESUME case case RESUME: if (branch == RESUME) { D_puts("resumed"); i = this.i; /* load i */ /* convert token */ this.t = ((RubyHash)this.token_table).op_aref(context, tok); if (this.t.isNil()) { this.t = vERROR_TOKEN; } D_printf("(act) t(k2)=%ld\n", assert_integer(this.t)); if (this.debug) { call_d_read_token.call(context, this.parser, this.parser, this.t, tok, val); } } read_next = false; i += assert_integer(this.t); D_printf("(act) i=%ld\n", i); if (i < 0) {branch = NOTFOUND; continue BRANCH;} act_value = AREF(context, this.action_table, i); if (act_value.isNil()) {branch = NOTFOUND; continue BRANCH;} act = assert_integer(act_value); D_printf("(act) table[i]=%ld\n", act); tmp = AREF(context, this.action_check, i); if (tmp.isNil()) {branch = NOTFOUND; continue BRANCH;} if (assert_integer(tmp) != this.curstate) {branch = NOTFOUND; continue BRANCH;} D_printf("(act) check[i]=%ld\n", assert_integer(tmp)); D_puts("(act) found"); case ACT_FIXED: D_printf("act=%ld\n", act); branch = HANDLE_ACT; continue BRANCH; case NOTFOUND: D_puts("(act) not found: use default"); act_value = AREF(context, this.action_default, this.curstate); act = assert_integer(act_value); branch = ACT_FIXED; continue BRANCH; case HANDLE_ACT: if (act > 0 && act < this.shift_n) { D_puts("shift"); if (this.errstatus > 0) { this.errstatus--; this.parser.setInstanceVariable(ID_ERRSTATUS, runtime.newFixnum(this.errstatus)); } SHIFT(context, act, this.t, val); read_next = true; } else if (act < 0 && act > -(this.reduce_n)) { D_puts("reduce"); REDUCE(context, act); } else if (act == -(this.reduce_n)) { branch = ERROR; continue BRANCH; } else if (act == this.shift_n) { D_puts("accept"); branch = ACCEPT; continue BRANCH; } else { throw runtime.newRaiseException(RaccBug, "[Cparse Bug] unknown act value " + act); } case ERROR_RECOVERED: if (this.debug) { call_d_next_state.call(context, this.parser, this.parser, runtime.newFixnum(this.curstate), this.state); } branch = 0; continue BRANCH; /* not reach */ case ACCEPT: if (this.debug) call_d_accept.call(context, this.parser, this.parser); this.retval = this.vstack.eltOk(0); this.fin = CP_FIN_ACCEPT; return; case ERROR: D_printf("error detected, status=%ld\n", this.errstatus); if (this.errstatus == 0) { this.nerr++; call_onerror.call(context, this.parser, this.parser, this.t, val, this.vstack); } case USER_YYERROR: if (this.errstatus == 3) { if (this.t == vFINAL_TOKEN) { this.retval = runtime.getFalse(); this.fin = CP_FIN_EOT; return; } read_next = true; } this.errstatus = 3; this.parser.setInstanceVariable(ID_ERRSTATUS, runtime.newFixnum(this.errstatus)); /* check if we can shift/reduce error token */ D_printf("(err) k1=%ld\n", this.curstate); D_printf("(err) k2=%d (error)\n", TokenType.ERROR.id); int branch2 = 0; BRANCH2: while (true) { switch (branch2) { case 0: tmp = AREF(context, this.action_pointer, this.curstate); if (tmp.isNil()) {branch2 = ERROR_POP; continue BRANCH2;} D_puts("(err) pointer[k1] ok"); i = assert_integer(tmp) + TokenType.ERROR.id; D_printf("(err) i=%ld\n", i); if (i < 0) {branch2 = ERROR_POP; continue BRANCH2;} act_value = AREF(context, this.action_table, i); if (act_value.isNil()) { D_puts("(err) table[i] == nil"); branch2 = ERROR_POP; continue BRANCH2; } act = assert_integer(act_value); D_printf("(err) table[i]=%ld\n", act); tmp = AREF(context, this.action_check, i); if (tmp.isNil()) { D_puts("(err) check[i] == nil"); branch2 = ERROR_POP; continue BRANCH2; } if (assert_integer(tmp) != this.curstate) { D_puts("(err) check[i] != k1"); branch2 = ERROR_POP; continue BRANCH2; } D_puts("(err) found: can handle error token"); break BRANCH2; case ERROR_POP: D_puts("(err) act not found: can't handle error token; pop"); if (this.state.size() <= 1) { this.retval = context.nil; this.fin = CP_FIN_CANTPOP; return; } POP(context, this.state); POP(context, this.vstack); this.curstate = assert_integer(LAST_I(context, this.state)); if (this.debug) { POP(context, this.tstack); call_d_e_pop.call(context, this.parser, this.parser, this.state, this.tstack, this.vstack); } } } /* shift/reduce error token */ if (act > 0 && act < this.shift_n) { D_puts("e shift"); SHIFT(context, act, runtime.newFixnum(TokenType.ERROR.id), val); } else if (act < 0 && act > -(this.reduce_n)) { D_puts("e reduce"); REDUCE(context, act); } else if (act == this.shift_n) { D_puts("e accept"); branch = ACCEPT; continue BRANCH; } else { throw runtime.newRaiseException(RaccBug, "[Cparse Bug] unknown act value " + act); } branch = ERROR_RECOVERED; continue BRANCH; } } } private void shift(ThreadContext context, int act, IRubyObject tok, IRubyObject val) { PUSH(vstack, val); if (debug) { PUSH(tstack, tok); call_d_shift.call(context, this.parser, this.parser, tok, tstack, vstack); } curstate = act; PUSH(state, context.runtime.newFixnum(curstate)); } private int reduce(ThreadContext context, int act) { IRubyObject code; ruleno = -act * 3; IRubyObject tag = context.runtime.newSymbol("racc_jump"); RubyContinuation rbContinuation = new RubyContinuation(context.runtime, context.runtime.newSymbol("racc_jump")); try { context.pushCatch(rbContinuation.getContinuation()); code = reduce0(context); errstatus = assert_integer(parser.getInstanceVariable(ID_ERRSTATUS)); } finally { context.popCatch(); } return assert_integer(code); } private IRubyObject reduce0(ThreadContext context) { Ruby runtime = context.runtime; IRubyObject reduce_to, reduce_len, method_id; int len; RubySymbol mid; IRubyObject tmp, tmp_t = RubyBasicObject.UNDEF, tmp_v = RubyBasicObject.UNDEF; int i, k1 = 0, k2; IRubyObject goto_state = context.nil; reduce_len = this.reduce_table.entry(this.ruleno); reduce_to = this.reduce_table.entry(this.ruleno+1); method_id = this.reduce_table.entry(this.ruleno+2); len = assert_integer(reduce_len); mid = value_to_id(context, method_id); int branch = 0; BRANCH: while (true) { switch (branch) { case 0: /* call action */ if (len == 0) { tmp = context.nil; if (mid != sym_noreduce) tmp_v = runtime.newArray(); if (this.debug) tmp_t = runtime.newArray(); } else { if (mid != sym_noreduce) { tmp_v = GET_TAIL(context, this.vstack, len); tmp = ((RubyArray)tmp_v).entry(0); } else { tmp = this.vstack.entry(this.vstack.size() - len); } CUT_TAIL(context, this.vstack, len); if (this.debug) { tmp_t = GET_TAIL(context, this.tstack, len); CUT_TAIL(context, this.tstack, len); } CUT_TAIL(context, this.state, len); } if (mid != sym_noreduce) { if (this.use_result_var) { tmp = Helpers.invoke(context, this.parser, mid.toString(), tmp_v, this.vstack, tmp); } else { tmp = Helpers.invoke(context, this.parser, mid.toString(), tmp_v, this.vstack); } } /* then push result */ PUSH(this.vstack, tmp); if (this.debug) { PUSH(this.tstack, reduce_to); call_d_reduce.call(context, this.parser, this.parser, tmp_t, reduce_to, this.tstack, this.vstack); } /* calculate transition state */ if (state.size() == 0) throw runtime.newRaiseException(RaccBug, "state stack unexpectedly empty"); k2 = assert_integer(LAST_I(context, this.state)); k1 = assert_integer(reduce_to) - this.nt_base; D_printf("(goto) k1=%ld\n", k1); D_printf("(goto) k2=%ld\n", k2); tmp = AREF(context, this.goto_pointer, k1); if (tmp.isNil()) {branch = NOTFOUND; continue BRANCH;} i = assert_integer(tmp) + k2; D_printf("(goto) i=%ld\n", i); if (i < 0) {branch = NOTFOUND; continue BRANCH;} goto_state = AREF(context, this.goto_table, i); if (goto_state.isNil()) { D_puts("(goto) table[i] == nil"); branch = NOTFOUND; continue BRANCH; } D_printf("(goto) table[i]=%ld (goto_state)\n", goto_state.convertToInteger().getLongValue()); tmp = AREF(context, this.goto_check, i); if (tmp.isNil()) { D_puts("(goto) check[i] == nil"); branch = NOTFOUND; continue BRANCH; } if (tmp != runtime.newFixnum(k1)) { D_puts("(goto) check[i] != table[i]"); branch = NOTFOUND; continue BRANCH; } D_printf("(goto) check[i]=%ld\n", tmp.convertToInteger().getLongValue()); D_puts("(goto) found"); case TRANSIT: PUSH(this.state, goto_state); this.curstate = assert_integer(goto_state); return RubyFixnum.zero(runtime); case NOTFOUND: D_puts("(goto) not found: use default"); /* overwrite `goto-state' by default value */ goto_state = AREF(context, this.goto_default, k1); branch = TRANSIT; continue BRANCH; } } } private void D_puts(String msg) { if (sys_debug) { System.out.println(msg); } } private void D_printf(String fmt, long arg) { if (sys_debug) { System.out.println(fmt + ": " + arg); } } private void D_printf(String fmt, boolean arg) { if (sys_debug) { System.out.println(fmt + ": " + arg); } } Parser parser; /* parser object */ boolean lex_is_iterator; IRubyObject lexer; /* scanner object */ RubySymbol lexmid; /* name of scanner method (must be an iterator) */ CallSite call_lexmid; /* call site for scanner method */ /* State transition tables (immutable) Data structure is from Dragon Book 4.9 */ /* action table */ IRubyObject action_table; IRubyObject action_check; IRubyObject action_default; IRubyObject action_pointer; /* goto table */ IRubyObject goto_table; IRubyObject goto_check; IRubyObject goto_default; IRubyObject goto_pointer; int nt_base; /* NonTerminal BASE index */ RubyArray reduce_table; /* reduce data table */ IRubyObject token_table; /* token conversion table */ /* parser stacks and parameters */ RubyArray state; int curstate; RubyArray vstack; RubyArray tstack; IRubyObject t; int shift_n; int reduce_n; int ruleno; int errstatus; /* nonzero in error recovering mode */ int nerr; /* number of error */ boolean use_result_var; IRubyObject retval; /* return IRubyObject of parser routine */ int fin; /* parse result status */ boolean debug; /* user level debug */ boolean sys_debug; /* system level debug */ int i; /* table index */ } private static RubyArray assert_array(IRubyObject a) { return a.convertToArray(); } private static RubyHash assert_hash(IRubyObject h) { return h.convertToHash(); } private static int assert_integer(IRubyObject i) { return (int)i.convertToInteger().getLongValue(); } public class Parser extends RubyObject { public Parser(Ruby runtime, RubyClass rubyClass) { super(runtime, rubyClass); } public static final String Racc_Runtime_Core_Version_C = RACC_VERSION; public static final String Racc_Runtime_Core_Id_C = "$originalId: cparse.c,v 1.8 2006/07/06 11:39:46 aamine Exp $"; @JRubyMethod(name = "_racc_do_parse_c", frame = true) public IRubyObject racc_cparse(ThreadContext context, IRubyObject arg, IRubyObject sysdebug) { CparseParams v = new CparseParams(context.runtime, CparseParams); v.D_puts("starting cparse"); v.sys_debug = sysdebug.isTrue(); v.initialize_params(context, this, arg, context.nil, context.nil); v.lex_is_iterator = false; v.parse_main(context, context.nil, context.nil, false); return v.retval; } @JRubyMethod(name = "_racc_yyparse_c", frame = true, required = 4) public IRubyObject racc_yyparse(ThreadContext context, IRubyObject[] args) { Ruby runtime = context.runtime; CparseParams v = new CparseParams(context.runtime, CparseParams); IRubyObject lexer = args[0], lexmid = args[1], arg = args[2], sysdebug = args[3]; v.sys_debug = sysdebug.isTrue(); v.D_puts("start C yyparse"); v.initialize_params(context, this, arg, lexer, lexmid); v.lex_is_iterator = true; v.D_puts("params initialized"); v.parse_main(context, context.nil, context.nil, false); call_lexer(context, v); if (v.fin == 0) { throw runtime.newArgumentError(v.lexmid + " is finished before EndOfToken"); } return v.retval; } private class LexerUnroll extends RuntimeException { public Throwable fillInStackTrace() { return this; } } private void call_lexer(ThreadContext context, final CparseParams v) { final LexerUnroll lexerUnroll = new LexerUnroll(); try { v.call_lexmid.call(context, v.lexer, v.lexer, CallBlock19.newCallClosure(v, v.getMetaClass(), Arity.ONE_ARGUMENT, new BlockCallback() { @Override public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) { Ruby runtime = context.getRuntime(); if (v.fin != 0) { throw runtime.newArgumentError("extra token after EndOfToken"); } IRubyObject[] tokVal = {null, null}; v.extract_user_token(context, args[0], tokVal); v.parse_main(context, tokVal[0], tokVal[1], true); if (v.fin != 0 && v.fin != CP_FIN_ACCEPT) { throw lexerUnroll; } return context.nil; } }, context)); } catch (LexerUnroll maybeOurs) { if (maybeOurs == lexerUnroll) { return; } } } } public void load(Ruby runtime, boolean wrap) { RubyModule racc = runtime.getOrCreateModule("Racc"); RubyClass parser = racc.defineOrGetClassUnder("Parser", runtime.getObject()); parser.setAllocator(new ObjectAllocator() { @Override public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) { return new Parser(ruby, rubyClass); } }); parser.defineAnnotatedMethods(Parser.class); parser.defineConstant("Racc_Runtime_Core_Version_C", runtime.newString(Parser.Racc_Runtime_Core_Version_C)); parser.defineConstant("Racc_Runtime_Core_Id_C", runtime.newString(Parser.Racc_Runtime_Core_Id_C)); CparseParams = racc.defineClassUnder("CparseParams", runtime.getObject(), new ObjectAllocator() { @Override public IRubyObject allocate(Ruby ruby, RubyClass rubyClass) { return new CparseParams(ruby, rubyClass); } }); RaccBug = runtime.getRuntimeError(); sym_noreduce = runtime.newSymbol(ID_NOREDUCE); call_nexttoken = MethodIndex.getFunctionalCallSite(ID_NEXTTOKEN); call_onerror = MethodIndex.getFunctionalCallSite(ID_ONERROR); call_d_shift = MethodIndex.getFunctionalCallSite(ID_D_SHIFT); call_d_reduce = MethodIndex.getFunctionalCallSite(ID_D_REDUCE); call_d_accept = MethodIndex.getFunctionalCallSite(ID_D_ACCEPT); call_d_read_token = MethodIndex.getFunctionalCallSite(ID_D_READ_TOKEN); call_d_next_state = MethodIndex.getFunctionalCallSite(ID_D_NEXT_STATE); call_d_e_pop = MethodIndex.getFunctionalCallSite(ID_D_E_POP); vDEFAULT_TOKEN = runtime.newFixnum(TokenType.DEFAULT.id); vERROR_TOKEN = runtime.newFixnum(TokenType.ERROR.id); vFINAL_TOKEN = runtime.newFixnum(TokenType.FINAL.id); } } racc-1.4.14/ext/racc/MANIFEST0000644000004100000410000000004412625454503015406 0ustar www-datawww-dataMANIFEST cparse.c depend extconf.rb racc-1.4.14/ext/racc/depend0000644000004100000410000000011312625454503015434 0ustar www-datawww-datacparse.o: cparse.c $(hdrdir)/ruby.h $(topdir)/config.h $(hdrdir)/defines.h racc-1.4.14/COPYING0000644000004100000410000006364412625454503013617 0ustar www-datawww-data GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. ^L Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. ^L GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. ^L Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. ^L 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. ^L 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. ^L 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. ^L 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ^L How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it!