Skip to content

Instantly share code, notes, and snippets.

@JoshCheek
Created June 23, 2017 15:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JoshCheek/f29207ad5d1e1ac706f281857dfa27f7 to your computer and use it in GitHub Desktop.
Save JoshCheek/f29207ad5d1e1ac706f281857dfa27f7 to your computer and use it in GitHub Desktop.
Ruby parsing to AST and compiling to MRI's VM instructions
code = <<-RUBY
class A
def whatevz
if 1
2
else
3
end
end
end
A.new {}.whatevz
RUBY
# Parsing to an ast
# If you wanted to do this yourself, then start w/ an existing parser
# eg treetop http://cjheath.github.io/treetop/
# https://github.com/JoshCheek/cln/blob/7c34f5219ba011ebc611353e2db99c94b8fbffc2/cln#L590
# https://github.com/JoshCheek/day-dream-in-Ruby/blob/06053303907917bf695e6d1bdf0b0fb6cd5d4f3e/lib/ddir/ddir.treetop
# NOTE: you can't recurse on the left hand side
# Then write a parser combinator
# https://vimeo.com/147468546 <-- my video where you can watch how the parser state changes over time
# https://github.com/JoshCheek/ruby_slippers_for_the_cobblers_children/tree/6a4794de5763370bcdc044f9add2116883e6970d/interpreter/defs/experiments/success-indentation-based-interactive-debugging
require 'parser/Ruby22'
ast = Parser::Ruby22.parse(code)
ast.type # => :begin
ast.children[1].children
# => [s(:block,
# s(:send,
# s(:const, nil, :A), :new),
# s(:args), nil),
# :whatevz]
ast
# => s(:begin,
# s(:class,
# s(:const, nil, :A), nil,
# s(:def, :whatevz,
# s(:args),
# s(:if,
# s(:int, 1),
# s(:int, 2),
# s(:int, 3)))),
# s(:send,
# s(:block,
# s(:send,
# s(:const, nil, :A), :new),
# s(:args), nil), :whatevz))
# What does MRI do?
RubyVM::InstructionSequence.compile(code).disasm.lines.reject { |l| l =~ /\btrace\b/ }.join
# => "== disasm: #<ISeq:<compiled>@<compiled>>================================\n" +
# "== catch table\n" +
# "| catch type: break st: 0012 ed: 0023 sp: 0000 cont: 0023\n" +
# "|------------------------------------------------------------------------\n" +
# "0002 putspecialobject 3\n" +
# "0004 putnil \n" +
# "0005 defineclass :A, <class:A>, 0\n" +
# "0009 pop \n" +
# "0012 getinlinecache 19, <is:0>\n" +
# "0015 getconstant :A\n" +
# "0017 setinlinecache <is:0>\n" +
# "0019 send <callinfo!mid:new, argc:0>, <callcache>, block in <compiled>\n" +
# "0023 opt_send_without_block <callinfo!mid:whatevz, argc:0, ARGS_SIMPLE>, <callcache>\n" +
# "0026 leave \n" +
# "== disasm: #<ISeq:<class:A>@<compiled>>=================================\n" +
# "0004 putspecialobject 1\n" +
# "0006 putobject :whatevz\n" +
# "0008 putiseq whatevz\n" +
# "0010 opt_send_without_block <callinfo!mid:core#define_method, argc:2, ARGS_SIMPLE>, <callcache>\n" +
# "0015 leave ( 2)\n" +
# "== disasm: #<ISeq:whatevz@<compiled>>===================================\n" +
# "0006 putobject 2\n" +
# "0008 jump 14 ( 3)\n" +
# "0012 putobject 3\n" +
# "0016 leave ( 6)\n" +
# "== disasm: #<ISeq:block in <compiled>@<compiled>>=======================\n" +
# "== catch table\n" +
# "| catch type: redo st: 0002 ed: 0003 sp: 0000 cont: 0002\n" +
# "| catch type: next st: 0002 ed: 0003 sp: 0000 cont: 0003\n" +
# "|------------------------------------------------------------------------\n" +
# "0002 putnil \n" +
# "0005 leave \n"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment