summaryrefslogtreecommitdiff
path: root/libexec/y2racc
diff options
context:
space:
mode:
Diffstat (limited to 'libexec/y2racc')
-rwxr-xr-xlibexec/y2racc339
1 files changed, 339 insertions, 0 deletions
diff --git a/libexec/y2racc b/libexec/y2racc
new file mode 100755
index 0000000000..38bd3669a2
--- /dev/null
+++ b/libexec/y2racc
@@ -0,0 +1,339 @@
+#!/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 <classname>] [-o <filename>] <input>"
+ parser.on('-o', '--output=FILENAME', 'output file name [<input>.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