Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit df9f622

Browse files
committed
Test out disassembler
1 parent 154e75f commit df9f622

File tree

2 files changed

+166
-51
lines changed

2 files changed

+166
-51
lines changed

lib/syntax_tree/yarv.rb

Lines changed: 120 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,65 @@ def serialize(insn)
802802
# This class is responsible for taking a compiled instruction sequence and
803803
# walking through it to generate equivalent Ruby code.
804804
class Disassembler
805+
module DSL
806+
def Args(parts)
807+
Args.new(parts: parts, location: Location.default)
808+
end
809+
810+
def ArgParen(arguments)
811+
ArgParen.new(arguments: arguments, location: Location.default)
812+
end
813+
814+
def Assign(target, value)
815+
Assign.new(target: target, value: value, location: Location.default)
816+
end
817+
818+
def Binary(left, operator, right)
819+
Binary.new(left: left, operator: operator, right: right, location: Location.default)
820+
end
821+
822+
def CallNode(receiver, operator, message, arguments)
823+
CallNode.new(receiver: receiver, operator: operator, message: message, arguments: arguments, location: Location.default)
824+
end
825+
826+
def FloatLiteral(value)
827+
FloatLiteral.new(value: value, location: Location.default)
828+
end
829+
830+
def Ident(value)
831+
Ident.new(value: value, location: Location.default)
832+
end
833+
834+
def Int(value)
835+
Int.new(value: value, location: Location.default)
836+
end
837+
838+
def Period(value)
839+
Period.new(value: value, location: Location.default)
840+
end
841+
842+
def Program(statements)
843+
Program.new(statements: statements, location: Location.default)
844+
end
845+
846+
def ReturnNode(arguments)
847+
ReturnNode.new(arguments: arguments, location: Location.default)
848+
end
849+
850+
def Statements(body)
851+
Statements.new(nil, body: body, location: Location.default)
852+
end
853+
854+
def VarField(value)
855+
VarField.new(value: value, location: Location.default)
856+
end
857+
858+
def VarRef(value)
859+
VarRef.new(value: value, location: Location.default)
860+
end
861+
end
862+
863+
include DSL
805864
attr_reader :iseq
806865

807866
def initialize(iseq)
@@ -812,78 +871,88 @@ def to_ruby
812871
stack = []
813872

814873
iseq.insns.each do |insn|
874+
# skip line numbers and events
875+
next unless insn.is_a?(Array)
876+
815877
case insn[0]
816878
when :getlocal_WC_0
817-
value = iseq.local_table.locals[insn[1]].name.to_s
818-
stack << VarRef.new(
819-
value: Ident.new(value: value, location: Location.default),
820-
location: Location.default
821-
)
879+
stack << VarRef(Ident(local_name(insn[1], 0)))
822880
when :leave
823-
stack << ReturnNode.new(
824-
arguments:
825-
Args.new(parts: [stack.pop], location: Location.default),
826-
location: Location.default
827-
)
881+
stack << ReturnNode(Args([stack.pop]))
882+
when :opt_and
883+
left, right = stack.pop(2)
884+
stack << Binary(left, :&, right)
885+
when :opt_div
886+
left, right = stack.pop(2)
887+
stack << Binary(left, :/, right)
888+
when :opt_eq
889+
left, right = stack.pop(2)
890+
stack << Binary(left, :==, right)
891+
when :opt_ge
892+
left, right = stack.pop(2)
893+
stack << Binary(left, :>=, right)
894+
when :opt_gt
895+
left, right = stack.pop(2)
896+
stack << Binary(left, :>, right)
897+
when :opt_le
898+
left, right = stack.pop(2)
899+
stack << Binary(left, :<=, right)
900+
when :opt_lt
901+
left, right = stack.pop(2)
902+
stack << Binary(left, :<, right)
903+
when :opt_ltlt
904+
left, right = stack.pop(2)
905+
stack << Binary(left, :<<, right)
906+
when :opt_minus
907+
left, right = stack.pop(2)
908+
stack << Binary(left, :-, right)
909+
when :opt_mod
910+
left, right = stack.pop(2)
911+
stack << Binary(left, :%, right)
828912
when :opt_mult
829913
left, right = stack.pop(2)
830-
stack << Binary.new(
831-
left: left,
832-
operator: :*,
833-
right: right,
834-
location: Location.default
835-
)
914+
stack << Binary(left, :*, right)
915+
when :opt_neq
916+
left, right = stack.pop(2)
917+
stack << Binary(left, :"!=", right)
918+
when :opt_or
919+
left, right = stack.pop(2)
920+
stack << Binary(left, :|, right)
836921
when :opt_plus
837922
left, right = stack.pop(2)
838-
stack << Binary.new(
839-
left: left,
840-
operator: :+,
841-
right: right,
842-
location: Location.default
843-
)
923+
stack << Binary(left, :+, right)
924+
when :opt_send_without_block
925+
receiver, *arguments = stack.pop(insn[1][:orig_argc] + 1)
926+
stack << CallNode(receiver, Period("."), Ident(insn[1][:mid]), ArgParen(Args(arguments)))
844927
when :putobject
845928
case insn[1]
846929
when Float
847-
stack << FloatLiteral.new(
848-
value: insn[1].inspect,
849-
location: Location.default
850-
)
930+
stack << FloatLiteral(insn[1].inspect)
851931
when Integer
852-
stack << Int.new(
853-
value: insn[1].inspect,
854-
location: Location.default
855-
)
856-
when Rational
857-
stack << RationalLiteral.new(
858-
value: insn[1].inspect,
859-
location: Location.default
860-
)
932+
stack << Int(insn[1].inspect)
861933
else
862934
raise "Unknown object type: #{insn[1].class.name}"
863935
end
936+
when :putobject_INT2FIX_0_
937+
stack << Int("0")
864938
when :putobject_INT2FIX_1_
865-
stack << Int.new(value: "1", location: Location.default)
939+
stack << Int("1")
866940
when :setlocal_WC_0
867-
target =
868-
VarField.new(
869-
value:
870-
Ident.new(
871-
value: iseq.local_table.locals[insn[1]].name.to_s,
872-
location: Location.default
873-
),
874-
location: Location.default
875-
)
876-
stack << Assign.new(
877-
target: target,
878-
value: stack.pop,
879-
location: Location.default
880-
)
941+
stack << Assign(VarField(Ident(local_name(insn[1], 0))), stack.pop)
881942
else
882943
raise "Unknown instruction #{insn[0]}"
883944
end
884945
end
885946

886-
Statements.new(nil, body: stack, location: Location.default)
947+
Program(Statements(stack))
948+
end
949+
950+
private
951+
952+
def local_name(index, level)
953+
current = iseq
954+
level.times { current = current.parent_iseq }
955+
current.local_table.locals[index].name.to_s
887956
end
888957
end
889958

test/yarv_test.rb

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# frozen_string_literal: true
2+
3+
return if !defined?(RubyVM::InstructionSequence) || RUBY_VERSION < "3.1"
4+
require_relative "test_helper"
5+
6+
module SyntaxTree
7+
class YARVTest < Minitest::Test
8+
CASES = {
9+
"0" => "return 0\n",
10+
"1" => "return 1\n",
11+
"2" => "return 2\n",
12+
"1.0" => "return 1.0\n",
13+
"1 + 2" => "return 1 + 2\n",
14+
"1 - 2" => "return 1 - 2\n",
15+
"1 * 2" => "return 1 * 2\n",
16+
"1 / 2" => "return 1 / 2\n",
17+
"1 % 2" => "return 1 % 2\n",
18+
"1 < 2" => "return 1 < 2\n",
19+
"1 <= 2" => "return 1 <= 2\n",
20+
"1 > 2" => "return 1 > 2\n",
21+
"1 >= 2" => "return 1 >= 2\n",
22+
"1 == 2" => "return 1 == 2\n",
23+
"1 != 2" => "return 1 != 2\n",
24+
"1 & 2" => "return 1 & 2\n",
25+
"1 | 2" => "return 1 | 2\n",
26+
"1 << 2" => "return 1 << 2\n",
27+
"1 >> 2" => "return 1.>>(2)\n",
28+
"1 ** 2" => "return 1.**(2)\n",
29+
"a = 1; a" => "a = 1\nreturn a\n",
30+
}.freeze
31+
32+
CASES.each do |source, expected|
33+
define_method("test_disassemble_#{source}") do
34+
assert_disassembles(expected, source)
35+
end
36+
end
37+
38+
private
39+
40+
def assert_disassembles(expected, source)
41+
iseq = SyntaxTree.parse(source).accept(Compiler.new)
42+
actual = Formatter.format(source, YARV::Disassembler.new(iseq).to_ruby)
43+
assert_equal expected, actual
44+
end
45+
end
46+
end

0 commit comments

Comments
 (0)