def gen_action_tables(t, states)
      t.action_default = states.map { |s| act2actid(s.defact) }
      t.action_table   = []
      t.action_check   = []
      t.action_pointer = []

      entries = []
      states.each do |state|
        if state.action.empty?
          # there is ONLY one default action in this state
          # when the parser sees that the 'action pointer' (or offset) for this
          # state is nil, it will just execute the default action
          t.action_pointer << nil
        else
          # build the action table for this state
          actions = []
          state.action.each do |tok, act|
            actions[tok.ident] = act2actid(act)
          end
          # then store data which will be used when we overlay all the per-state
          # action tables into one big action table
          add_entry(entries, actions, state.ident, t.action_pointer)
        end
      end

      set_table(entries, t.action_table, t.action_check, t.action_pointer)
    end
def gen_action_tables(t, states)
      t.action_default = states.map { |s| act2actid(s.defact) }
      t.action_table   = []
      t.action_check   = []
      t.action_pointer = []

      entries = []
      states.each do |state|
        if state.action.empty?
          # there is ONLY one default action in this state
          # when the parser sees that the 'action pointer' (or offset) for this
          # state is nil, it will just execute the default action
          t.action_pointer << nil
        else
          # build the action table for this state
          actions = []
          state.action.each do |tok, act|
            actions[tok.ident] = act2actid(act)
          end
          # then store data which will be used when we overlay all the per-state
          # action tables into one big action table
          add_entry(entries, actions, state.ident, t.action_pointer)
        end
      end

      set_table(entries, t.action_table, t.action_check, t.action_pointer)
    end
The action and goto tables use a clever trick for compression
 Each state should have its own action table (one per lookahead token)
 Each nonterminal which we can reduce to also has its own goto table
 (with one entry per state which we can perform the reduction from)
 But those tables are very sparse (most entries are nil)

 So, to save space, we OVERLAY all the action tables into one big array
 And same with the goto tables
 We must choose an offset for each state, so its populated entries don't
 collide with the populated entries of any other state
 The chosen offsets go in the 'action_pointer' and 'goto_pointer' arrays
 At runtime, we will retrieve the offset for the current state, add the
 token number of the lookahead token, and index into the action/goto table

 BUT, what if the lookahead token is one which is illegal in this state?
 OR, what if it's legal, but its entry is the default entry (which does
 not explicitly appear in the main action/goto tables)?
 We could blindly hit an entry which belongs to a different state, and go
 off into some random sequence of states
 To prevent this, there are 'check' arrays with the state numbers which
 each action/goto entry belong to
 So before we retrieve an action/goto and use it, we see whether the
 corresponding 'check' number is the current state number