class PostProcessor def initialize(source, out=STDOUT) @out = out @lines = File.readlines(source) @index = -1 @case_bodies = {} @max_case_number = -1 end # Read/Unread with ability to push back one line for a single lookahead def read @index += 1 line = @last ? @last : @lines[@index] @last = nil line end def unread(line) @index -= 1 @last = line end def end_of_actions?(line) return line =~ %r{^//\s*ACTIONS_END} end def translate while (line = read) if line =~ %r{^//\s*ACTIONS_BEGIN} translate_actions elsif line =~ %r{^//\s*ACTION_BODIES} generate_action_body_methods else @out.puts line end end end def generate_action_body_methods if RIPPER @out.puts "static RipperParserState[] states = new RipperParserState[#{@max_case_number+1}];" else @out.puts "static ParserState[] states = new ParserState[#{@max_case_number+1}];" end @out.puts "static {"; @case_bodies.each do |state, code_body| if RIPPER generate_ripper_action_body_method(state, code_body) else generate_action_body_method(state, code_body) end end @out.puts "}"; end def generate_ripper_action_body_method(state, code_body) @out.puts "states[#{state}] = new RipperParserState() {" @out.puts " @Override public Object execute(RipperParser p, Object yyVal, Object[] yyVals, int yyTop) {" code_body.each { |line| @out.puts line } @out.puts " return yyVal;" @out.puts " }" @out.puts "};" end def generate_action_body_method(state, code_body) @out.puts "states[#{state}] = new ParserState() {" @out.puts " @Override public Object execute(ParserSupport support, RubyYaccLexer lexer, Object yyVal, Object[] yyVals, int yyTop) {" code_body.each { |line| @out.puts line } @out.puts " return yyVal;" @out.puts " }" @out.puts "};" end def translate_actions count = 1 while (translate_action) count += 1 end end # Assumptions: # 1. no break; in our code. A bit weak, but this is highly specialized code. # 2. All productions will have a line containing only { (with optional comment) # 3. All productions will end with a line containly only } followed by break in ass 1. def translate_action line = read return false if end_of_actions?(line) || line !~ /case\s+(\d+):/ case_number = $1.to_i line = read return false if line !~ /line\s+(\d+)/ line_number = $1 # Extra boiler plate '{' that we do not need line = read return false if line !~ /^\s*\{\s*(\/\*.*\*\/)?$/ @max_case_number = case_number if case_number > @max_case_number label = "case#{case_number}_line#{line_number}" body = [] last_line = nil while (line = read) if line =~ /^\s*\}\s*$/ # Extra trailing boiler plate next_line = read if next_line =~ /break;/ break else body << line unread next_line end else body << line end end @case_bodies[case_number] = body true end end if ARGV[0] =~ /Ripper/ RIPPER = true else RIPPER = false end PostProcessor.new(ARGV.shift).translate