Created
June 23, 2017 15:16
-
-
Save JoshCheek/f29207ad5d1e1ac706f281857dfa27f7 to your computer and use it in GitHub Desktop.
Ruby parsing to AST and compiling to MRI's VM instructions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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