package org.libll;
import java.util.ArrayList; import java.util.ArrayDeque;
import org.libll.DriverConfig;
import org.jruby.Ruby; import org.jruby.RubyModule; import org.jruby.RubyClass; import org.jruby.RubyObject; import org.jruby.RubyArray; import org.jruby.RubySymbol; import org.jruby.RubyFixnum;
import org.jruby.anno.JRubyClass; import org.jruby.anno.JRubyMethod; import org.jruby.runtime.Arity; import org.jruby.runtime.Helpers; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.BlockCallback; import org.jruby.runtime.Block; import org.jruby.runtime.CallBlock19; import org.jruby.runtime.builtin.IRubyObject;
@JRubyClass(name=“LL::Driver”, parent=“Object”) public class Driver extends RubyObject {
private static long T_EOF = -1; private static long T_RULE = 0; private static long T_TERMINAL = 1; private static long T_EPSILON = 2; private static long T_ACTION = 3; private static long T_STAR = 4; private static long T_PLUS = 5; private static long T_ADD_VALUE_STACK = 6; private static long T_APPEND_VALUE_STACK = 7; private static long T_QUESTION = 8; /** * The current Ruby runtime. */ private Ruby runtime; /** * The driver configuration. */ private DriverConfig config; /** * Sets up the class in the Ruby runtime. */ public static void load(Ruby runtime) { RubyModule ll = (RubyModule) runtime.getModule("LL"); RubyClass driver = ll.defineClassUnder( "Driver", runtime.getObject(), ALLOCATOR ); driver.defineAnnotatedMethods(Driver.class); } private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klass) { return new org.libll.Driver(runtime, klass); } }; /** * @param runtime The current Ruby runtime. * @param klass The Driver class. */ public Driver(Ruby runtime, RubyClass klass) { super(runtime, klass); this.runtime = runtime; this.config = (DriverConfig) klass.getConstant("CONFIG"); } /** * The main parsing loop of the driver. */ @JRubyMethod public IRubyObject parse(ThreadContext context) { final ArrayDeque<Long> stack = new ArrayDeque<Long>(); final ArrayDeque<IRubyObject> value_stack = new ArrayDeque<IRubyObject>(); final Driver self = this; // EOF stack.push(this.T_EOF); stack.push(this.T_EOF); // Start rule ArrayList<Long> start_row = self.config.rules.get(0); for ( int index = 0; index < start_row.size(); index++ ) { stack.push(start_row.get(index)); } BlockCallback callback = new BlockCallback() { public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block) { RubyArray token = (RubyArray) args[0]; IRubyObject type = token.entry(0); IRubyObject value = token.entry(1); while ( true ) { if ( stack.size() == 0 ) { IRubyObject[] error_args = { RubyFixnum.newFixnum(self.runtime, -1), RubyFixnum.newFixnum(self.runtime, -1), type, value }; self.callMethod(context, "parser_error", error_args); } Long stack_value = stack.pop(); Long stack_type = stack.pop(); Long token_id = (long) 0; if ( self.config.terminals.containsKey(type) ) { token_id = self.config.terminals.get(type); } // A rule or the "+" operator if ( stack_type == self.T_RULE || stack_type == self.T_PLUS ) { Long production_i = self.config.table .get(stack_value.intValue()) .get(token_id.intValue()); if ( production_i == self.T_EOF ) { IRubyObject[] error_args = { RubyFixnum.newFixnum(self.runtime, stack_type), RubyFixnum.newFixnum(self.runtime, stack_value), type, value }; self.callMethod(context, "parser_error", error_args); } else { // Append a "*" operator for all following // occurrences as they are optional if ( stack_type == self.T_PLUS ) { stack.push(self.T_STAR); stack.push(stack_value); stack.push(self.T_APPEND_VALUE_STACK); stack.push(Long.valueOf(0)); } ArrayList<Long> row = self.config.rules .get(production_i.intValue()); for ( int index = 0; index < row.size(); index++ ) { stack.push(row.get(index)); } } } // "*" operator else if ( stack_type == self.T_STAR ) { Long production_i = self.config.table .get(stack_value.intValue()) .get(token_id.intValue()); if ( production_i != self.T_EOF ) { stack.push(self.T_STAR); stack.push(stack_value); stack.push(self.T_APPEND_VALUE_STACK); stack.push(Long.valueOf(0)); ArrayList<Long> row = self.config.rules .get(production_i.intValue()); for ( int index = 0; index < row.size(); index++ ) { stack.push(row.get(index)); } } } // "?" operator else if ( stack_type == self.T_QUESTION ) { Long production_i = self.config.table .get(stack_value.intValue()) .get(token_id.intValue()); if ( production_i == self.T_EOF ) { value_stack.push(context.nil); } else { ArrayList<Long> row = self.config.rules .get(production_i.intValue()); for ( int index = 0; index < row.size(); index++ ) { stack.push(row.get(index)); } } } // Adds a new array to the value stack that can be used to // group operator values together else if ( stack_type == self.T_ADD_VALUE_STACK ) { RubyArray operator_buffer = self.runtime.newArray(); value_stack.push(operator_buffer); } // Appends the last value on the value stack to the operator // buffer that preceeds it. else if ( stack_type == self.T_APPEND_VALUE_STACK ) { IRubyObject last_value = value_stack.pop(); RubyArray operator_buffer = (RubyArray) value_stack.peek(); operator_buffer.append(last_value); } // Terminal else if ( stack_type == self.T_TERMINAL ) { if ( stack_value == token_id ) { value_stack.push(value); break; } else { IRubyObject[] error_args = { RubyFixnum.newFixnum(self.runtime, stack_type), RubyFixnum.newFixnum(self.runtime, stack_value), type, value }; self.callMethod(context, "parser_error", error_args); } } // Action else if ( stack_type == self.T_ACTION ) { String method = self.config.action_names .get(stack_value.intValue()) .toString(); long num_args = (long) self.config.action_arg_amounts .get(stack_value.intValue()); RubyArray action_args = self.runtime.newArray(); if ( num_args > (long) value_stack.size() ) { num_args = (long) value_stack.size(); } while ( (num_args--) > 0 ) { if ( value_stack.size() > 0 ) { action_args.store(num_args, value_stack.pop()); } } value_stack.push( self.callMethod(context, method, action_args) ); } else if ( stack_type == self.T_EOF ) { break; } } return context.nil; } }; Helpers.invoke( context, this, "each_token", CallBlock19.newCallClosure( this, this.metaClass, Arity.NO_ARGUMENTS, callback, context ) ); if ( value_stack.isEmpty() ) { return context.nil; } else { return value_stack.pop(); } }
}