BytecodeHelpers < BasicDecorators {
## # Bytecode Generation Helper Methods var debug_mode: false var overall_fail: new_label g: self # For consistency puts_string: |string| g.push_rubinius; g.push_literal(string); g.send(:puts, 1, true); g.pop inspect_top: g.dup_top; g.push_rubinius; g.swap; g.send(:p, 1, true); g.pop set_subject: g.set_local (0) push_subject: g.push_local (0) set_idx: g.set_local (1) push_idx: g.push_local (1) set_captures: g.set_ivar (:"@captures") push_captures: g.push_ivar (:"@captures") set_memo_for: |name| g.set_ivar (:"@memo_for_"name"") push_memo_for: |name| g.push_ivar (:"@memo_for_"name"") pop_to_set_idx: g.set_idx; g.pop push_temp_captures: g.push_captures; g.make_array(0); g.set_captures; g.pop pop_to_reject_captures: g.set_captures; g.pop pop_to_accept_captures: g.push_captures; g.send(:concat, 1); g.set_captures; g.pop push_subject_at_idx: g.push_subject; g.push_idx; g.send(:at, 1) push_subject_chr_at_idx: g.push_subject; g.push_idx; g.send(:chr_at, 1) push_new_table: g.push_rubinius; g.find_const(:LookupTable); g.send(:new, 0) # Set the memo ivar to a new hash if it doesn't already exist memo_or_eq_new_hash: |name| { memo_exists_label = g.new_label g.push_memo_for(name) g.goto_if_true(memo_exists_label) g.push_new_table g.set_memo_for(name) g.pop # ivar memo_exists_label.set! } # Copy the top two values into the memo ivar for |name| at the current idx. copy_result_to_memo: |name| { g.dup_top g.push_captures g.make_array(2) # [result_idx, result_captures] g.push_memo_for(name) g.swap # [result_idx, result_captures] g.push_idx # idx g.swap # TODO: use rotate g.send(:"[]=", 2) g.pop } # Goto the given |label| and push the result values if a memoized result # exists for the given |name| at the current idx. goto_if_memo_for_idx: |name, label| { skip_label = g.new_label g.push_memo_for(name) g.push_idx g.send(:"[]", 1) g.dup_top # dup the result to retain after goto test g.goto_if_nil(skip_label) # Shift result_idx onto the stack and swap with array g.shift_array; g.swap # Shift result_captures, set to @captures, and pop g.shift_array; g.set_captures; g.pop g.pop # pop the empty containing array g.goto(label) skip_label.set! g.pop } # Increment the idx local by the current number at the top of the stack # Saves the new idx to @highest_idx ivar if it is highest increment_idx: { g.push_idx g.send(:"+", 1) g.set_idx g.dup_top g.push_ivar(:"@highest_idx") g.send(:">", 1) not_highest_idx_label = g.new_label g.goto_if_false(not_highest_idx_label) g.set_ivar(:"@highest_idx") not_highest_idx_label.set! g.pop } # Begin a compiled parser method rule_start: { # 2 argument: (subject, idx) g.required_args = 2 g.total_args = 2 g.splat_index = null g.local_count = 2 g.local_names = [:subject, :idx] g.debug_mode && ( g.push_idx; g.inspect_top; g.pop g.puts_string("trying.. "g.name"") ) } # Wrap up a compiled parser method rule_finish: { overall_done = g.new_label # end g.push_idx g.debug_mode && ( g.push_idx; g.inspect_top; g.pop g.puts_string("SUCCESS! "g.name"") ) g.goto(overall_done) # fail g.overall_fail.set! g.push_nil g.debug_mode && ( g.push_idx; g.inspect_top; g.pop g.puts_string("failure: "g.name"") ) overall_done.set! g.ret g.close } push_literal_array: |ary| ary.each |lit| { g.push_literal_or_array(lit) }; make_array(ary.size) push_literal_or_array: |item| item.is_a?(Array) &? push_literal_array(item) ?? push_literal(item) capture: |*metadata| { g.push_captures g.push_idx g.push_literal_array(metadata) g.make_array(2) g.send(:push, 1) g.pop }
}