Repository: goby-lang/goby
Pull request #842 (2020-04-18 14:27:42)

Support define_method and define_singleton_method
This PR implements #839 but also contains other fixes & improvements that are required for the task:
1. Fixes block compilation's `argSet` being nil issue.
2. Fixes `def` statement working incorrectly inside `instance_eval` issue.
3. Fixes singleton method definition gets overridden issue.
4. Reformats Ripper test cases' expectations to improve readability.


Patch:
diff --git a/compiler/bytecode/expression_generation.go b/compiler/bytecode/expression_generation.go
index 69b3b9f..b82e71c 100644
--- a/compiler/bytecode/expression_generation.go
+++ b/compiler/bytecode/expression_generation.go
@@ -74,7 +74,7 @@ func (g *Generator) compileIdentifier(is *InstructionSet, exp *ast.Identifier, s
 
 	// otherwise it's a method call
 	is.define(PutSelf, exp.Line())
-	is.define(Send, exp.Line(), exp.Value, 0, "", &ArgSet{})
+	is.define(Send, exp.Line(), exp.Value, 0, "", initArgSet(0))
 }
 
 func (g *Generator) compileYieldExpression(is *InstructionSet, exp *ast.YieldExpression, scope *scope, table *localTable) {
@@ -93,10 +93,7 @@ func (g *Generator) compileGetBlockExpression(is *InstructionSet, exp *ast.GetBl
 
 func (g *Generator) compileCallExpression(is *InstructionSet, exp *ast.CallExpression, scope *scope, table *localTable) {
 	var blockInfo string
-	argSet := &ArgSet{
-		names: make([]string, len(exp.Arguments)),
-		types: make([]uint8, len(exp.Arguments)),
-	}
+	argSet := initArgSet(len(exp.Arguments))
 
 	// Compile receiver
 	g.compileExpression(is, exp.Receiver, scope, table)
@@ -189,10 +186,14 @@ func (g *Generator) compileBlockArgExpression(index int, exp *ast.CallExpression
 	is.name = fmt.Sprint(index)
 	is.isType = Block
 
-	for i := 0; i < len(exp.BlockArguments); i++ {
-		table.set(exp.BlockArguments[i].Value)
+	argSet := initArgSet(len(exp.BlockArguments))
+
+	for i, arg := range exp.BlockArguments {
+		argSet.setArg(i, arg.Value, NormalArg)
+		table.set(arg.Value)
 	}
 
+	is.argTypes = argSet
 	g.compileCodeBlock(is, exp.Block, scope, table)
 	g.endInstructions(is, exp.Line())
 	g.instructionSets = append(g.instructionSets, is)
@@ -236,14 +237,14 @@ func (g *Generator) compilePrefixExpression(is *InstructionSet, exp *ast.PrefixE
 	switch exp.Operator {
 	case "!":
 		g.compileExpression(is, exp.Right, scope, table)
-		is.define(Send, exp.Line(), exp.Operator, 0, "", &ArgSet{})
+		is.define(Send, exp.Line(), exp.Operator, 0, "", initArgSet(0))
 	case "*":
 		g.compileExpression(is, exp.Right, scope, table)
 		is.define(SplatArray, exp.Line())
 	case "-":
 		is.define(PutObject, exp.Line(), 0)
 		g.compileExpression(is, exp.Right, scope, table)
-		is.define(Send, exp.Line(), exp.Operator, 1, "", &ArgSet{})
+		is.define(Send, exp.Line(), exp.Operator, 1, "", initArgSet(0))
 	case "+":
 		g.compileExpression(is, exp.Right, scope, table)
 	}
diff --git a/compiler/bytecode/instruction.go b/compiler/bytecode/instruction.go
index 16c748d..1cf0bad 100644
--- a/compiler/bytecode/instruction.go
+++ b/compiler/bytecode/instruction.go
@@ -145,6 +145,13 @@ type ArgSet struct {
 	types []uint8
 }
 
+func initArgSet(argCount int) *ArgSet {
+	return &ArgSet{
+		names: make([]string, argCount),
+		types: make([]uint8, argCount),
+	}
+}
+
 // Types are the getter method of *ArgSet's types attribute
 // TODO: needs to change the func to simple public variable
 func (as *ArgSet) Types() []uint8 {
diff --git a/compiler/bytecode/statement_generation.go b/compiler/bytecode/statement_generation.go
index 0314342..475aaed 100644
--- a/compiler/bytecode/statement_generation.go
+++ b/compiler/bytecode/statement_generation.go
@@ -195,10 +195,7 @@ func (g *Generator) compileDefStmt(is *InstructionSet, stmt *ast.DefStatement, s
 	newIS := &InstructionSet{
 		name:   stmt.Name.Value,
 		isType: MethodDef,
-		argTypes: &ArgSet{
-			names: make([]string, len(stmt.Parameters)),
-			types: make([]uint8, len(stmt.Parameters)),
-		},
+		argTypes: initArgSet(len(stmt.Parameters)),
 	}
 
 	for i := 0; i < len(stmt.Parameters); i++ {
diff --git a/vm/class.go b/vm/class.go
index e494e8c..278392d 100644
--- a/vm/class.go
+++ b/vm/class.go
@@ -730,6 +730,31 @@ var builtinModuleCommonClassMethods = []*BuiltinMethodObject{
 			return superClass
 		},
 	},
+	{
+		// Defines an instance method in the receiver.
+		Name: "define_method",
+		Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object {
+			if len(args) != 1 {
+				return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, errors.WrongNumberOfArgument, 1, len(args))
+			}
+
+			err := t.vm.checkArgTypes(args, sourceLine, classes.StringClass)
+
+			if err != nil {
+				return err
+			}
+
+			if blockFrame == nil {
+				return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, "can't define a method without a block")
+			}
+
+			method := &MethodObject{Name: args[0].Value().(string), argc: len(blockFrame.locals), instructionSet: blockFrame.instructionSet, BaseObj: NewBaseObject(t.vm.TopLevelClass(classes.MethodClass))}
+
+			t.vm.defineMethodOn(receiver, method)
+
+			return args[0]
+		},
+	},
 }
 
 // Instance methods -----------------------------------------------------
@@ -897,6 +922,31 @@ var builtinClassCommonInstanceMethods = []*BuiltinMethodObject{
 
 		},
 	},
+	{
+		// Defines a singleton method in the receiver.
+		Name: "define_singleton_method",
+		Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object {
+			if len(args) != 1 {
+				return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, errors.WrongNumberOfArgument, 1, len(args))
+			}
+
+			err := t.vm.checkArgTypes(args, sourceLine, classes.StringClass)
+
+			if err != nil {
+				return err
+			}
+
+			if blockFrame == nil {
+				return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, "can't define a method without a block")
+			}
+
+			method := &MethodObject{Name: args[0].Value().(string), argc: len(blockFrame.locals), instructionSet: blockFrame.instructionSet, BaseObj: NewBaseObject(t.vm.TopLevelClass(classes.MethodClass))}
+
+			t.vm.defineSingletonMethodOn(receiver, method)
+
+			return args[0]
+		},
+	},
 	{
 		// Performs a 'shallow' copy of the receiver object and returns it.
 		// Any arguments are just ignored.
@@ -1771,6 +1821,40 @@ func (vm *VM) createRClass(className string) *RClass {
 	}
 }
 
+func (vm *VM) defineMethodOn(obj Object, method *MethodObject) {
+	switch obj := obj.(type) {
+	case *RClass:
+		obj.Methods.set(method.Name, method)
+	default:
+		if obj.Class().Name == classes.ObjectClass {
+			obj.Class().Methods.set(method.Name, method)
+		} else {
+			vm.findOrCreateSingletonClass(obj).Methods.set(method.Name, method)
+		}
+	}
+}
+
+func (vm *VM) defineSingletonMethodOn(obj Object, method *MethodObject) {
+	switch obj := obj.(type) {
+	case *RClass:
+		obj.SingletonClass().Methods.set(method.Name, method)
+	default:
+		vm.findOrCreateSingletonClass(obj).Methods.set(method.Name, method)
+	}
+}
+
+func (vm *VM) findOrCreateSingletonClass(obj Object) (singletonClass *RClass) {
+	singletonClass = obj.SingletonClass()
+
+	if singletonClass == nil {
+		singletonClass = vm.createRClass(fmt.Sprintf("#<Class:#<%s:%d>>", obj.Class().Name, obj.ID()))
+		singletonClass.isSingleton = true
+		obj.SetSingletonClass(singletonClass)
+	}
+
+	return
+}
+
 func initModuleClass(classClass *RClass) *RClass {
 	moduleClass := &RClass{
 		Name:      classes.ModuleClass,
diff --git a/vm/instruction.go b/vm/instruction.go
index c465127..ad43a1f 100644
--- a/vm/instruction.go
+++ b/vm/instruction.go
@@ -1,7 +1,6 @@
 package vm
 
 import (
-	"fmt"
 	"strings"
 
 	"github.com/goby-lang/goby/compiler/bytecode"
@@ -318,14 +317,7 @@ func init() {
 
 			method := &MethodObject{Name: methodName, argc: argCount, instructionSet: is, BaseObj: NewBaseObject(t.vm.TopLevelClass(classes.MethodClass))}
 
-			v := t.Stack.Pop().Target
-			switch self := v.(type) {
-			case *RClass:
-				self.Methods.set(methodName, method)
-			default:
-				self.Class().Methods.set(methodName, method)
-			}
-
+			t.vm.defineMethodOn(t.Stack.Pop().Target, method)
 		},
 		bytecode.DefSingletonMethod: func(t *Thread, sourceLine int, cf *normalCallFrame, args ...interface{}) {
 			argCount := args[0].(int)
@@ -333,18 +325,7 @@ func init() {
 			is, _ := t.getMethodIS(methodName, cf.FileName())
 			method := &MethodObject{Name: methodName, argc: argCount, instructionSet: is, BaseObj: NewBaseObject(t.vm.TopLevelClass(classes.MethodClass))}
 
-			v := t.Stack.Pop().Target
-
-			switch v := v.(type) {
-			case *RClass:
-				v.SingletonClass().Methods.set(methodName, method)
-			default:
-				singletonClass := t.vm.createRClass(fmt.Sprintf("#<Class:#<%s:%d>>", v.Class().Name, v.ID()))
-				singletonClass.Methods.set(methodName, method)
-				singletonClass.isSingleton = true
-				v.SetSingletonClass(singletonClass)
-			}
-
+			t.vm.defineSingletonMethodOn(t.Stack.Pop().Target, method)
 		},
 		bytecode.DefClass: func(t *Thread, sourceLine int, cf *normalCallFrame, args ...interface{}) {
 			subject := strings.Split(args[0].(string), ":")


Test patch:
diff --git a/native/ripper/ripper_test.go b/native/ripper/ripper_test.go
index 5020031..b71936d 100644
--- a/native/ripper/ripper_test.go
+++ b/native/ripper/ripper_test.go
@@ -5,6 +5,7 @@ import (
 	"testing"
 
 	"github.com/goby-lang/goby/vm"
+	"regexp"
 )
 
 type errorTestCase struct {
@@ -311,29 +312,194 @@ func TestRipperInstruction(t *testing.T) {
 def foo
   10
 end
-  ").to_s`, `[{ arg_types: { names: [], types: [] }, instructions: [{ action: "putobject", line: 0, params: ["10"], source_line: 3 }, { action: "leave", line: 1, params: [], source_line: 2 }], name: "foo", type: "Def" }, { arg_types: { names: [], types: [] }, instructions: [{ action: "putobject", line: 0, params: ["10"], source_line: 3 }, { action: "leave", line: 1, params: [], source_line: 2 }], name: "foo", type: "Def" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 2 }, { action: "putstring", line: 1, params: ["foo"], source_line: 2 }, { action: "def_method", line: 2, params: ["0"], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 2 }, { action: "putstring", line: 1, params: ["foo"], source_line: 2 }, { action: "def_method", line: 2, params: ["0"], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }]`},
+  ").to_s`, `
+[
+  { 
+    arg_types: { names: [], types: [] }, 
+    instructions: [
+      { action: "putobject", line: 0, params: ["10"], source_line: 3 }, 
+      { action: "leave", line: 1, params: [], source_line: 2 }
+    ], 
+    name: "foo", 
+    type: "Def" 
+  }, 
+  {
+     arg_types: { names: [], types: [] }, 
+     instructions: [
+       { action: "putobject", line: 0, params: ["10"], source_line: 3 }, 
+       { action: "leave", line: 1, params: [], source_line: 2 }
+     ], 
+     name: "foo", 
+     type: "Def" 
+  }, 
+  { 
+     instructions: [
+       { action: "putself", line: 0, params: [], source_line: 2 }, 
+       { action: "putstring", line: 1, params: ["foo"], source_line: 2 }, 
+       { action: "def_method", line: 2, params: ["0"], source_line: 2 }, 
+       { action: "leave", line: 3, params: [], source_line: 2 }
+     ], 
+     name: "ProgramStart", 
+     type: "ProgramStart" 
+  }, 
+  { 
+    instructions: [
+      { action: "putself", line: 0, params: [], source_line: 2 }, 
+      { action: "putstring", line: 1, params: ["foo"], source_line: 2 }, 
+      { action: "def_method", line: 2, params: ["0"], source_line: 2 }, 
+      { action: "leave", line: 3, params: [], source_line: 2 }
+    ], 
+    name: "ProgramStart", 
+    type: "ProgramStart" 
+  }
+]
+`,
+	},
 		// Single class definition
 		{`require 'ripper'; Ripper.instruction("
     class Foo
 		end
-		 ").to_s`, `[{ instructions: [{ action: "leave", line: 0, params: [], source_line: 2 }], name: "Foo", type: "DefClass" }, { instructions: [{ action: "leave", line: 0, params: [], source_line: 2 }], name: "Foo", type: "DefClass" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 2 }, { action: "def_class", line: 1, params: ["class:Foo"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 2 }, { action: "def_class", line: 1, params: ["class:Foo"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }]`},
+		 ").to_s`, `
+[
+  { 
+     instructions: [
+       { action: "leave", line: 0, params: [], source_line: 2 }
+     ], 
+     name: "Foo", 
+     type: "DefClass" 
+  }, 
+  { 
+     instructions: [
+       { action: "leave", line: 0, params: [], source_line: 2 }
+     ], 
+     name: "Foo", 
+     type: "DefClass" 
+  }, 
+  { 
+     instructions: [
+       { action: "putself", line: 0, params: [], source_line: 2 }, 
+       { action: "def_class", line: 1, params: ["class:Foo"], source_line: 2 }, 
+       { action: "pop", line: 2, params: [], source_line: 2 }, 
+       { action: "leave", line: 3, params: [], source_line: 2 }
+     ], name: "ProgramStart", type: "ProgramStart" 
+  },
+  { 
+     instructions: [
+       { action: "putself", line: 0, params: [], source_line: 2 }, 
+       { action: "def_class", line: 1, params: ["class:Foo"], source_line: 2 }, 
+       { action: "pop", line: 2, params: [], source_line: 2 }, 
+       { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" 
+  }
+]
+`},
 		// Single method call
 		{`require 'ripper'; Ripper.instruction("
     Array.methods
-		 ").to_s`, `[{ arg_set: { names: [], types: [] }, instructions: [{ action: "getconstant", line: 0, params: ["Array", "false"], source_line: 2 }, { action: "send", line: 1, params: ["methods", "0", "", "&{[] []}"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }, { arg_set: { names: [], types: [] }, instructions: [{ action: "getconstant", line: 0, params: ["Array", "false"], source_line: 2 }, { action: "send", line: 1, params: ["methods", "0", "", "&{[] []}"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }]`},
+		 ").to_s`, `
+[
+  { 
+    arg_set: { names: [], types: [] }, 
+    instructions: [
+      { action: "getconstant", line: 0, params: ["Array", "false"], source_line: 2 }, 
+      { action: "send", line: 1, params: ["methods", "0", "", "&{[] []}"], source_line: 2 }, 
+      { action: "pop", line: 2, params: [], source_line: 2 }, 
+      { action: "leave", line: 3, params: [], source_line: 2 }
+    ], 
+    name: "ProgramStart", 
+    type: "ProgramStart" 
+  }, 
+  { 
+    arg_set: { names: [], types: [] }, 
+    instructions: [
+      { action: "getconstant", line: 0, params: ["Array", "false"], source_line: 2 }, 
+      { action: "send", line: 1, params: ["methods", "0", "", "&{[] []}"], source_line: 2 }, 
+      { action: "pop", line: 2, params: [], source_line: 2 }, 
+      { action: "leave", line: 3, params: [], source_line: 2 }
+    ],
+    name: "ProgramStart", 
+    type: "ProgramStart" 
+  }
+]`},
 		// Single method call with a block
 		{`require 'ripper'; Ripper.instruction("
     10.times do |i| end
-		 ").to_s`, `[{ instructions: [{ action: "leave", line: 0, params: [], source_line: 2 }], name: "0", type: "Block" }, { instructions: [{ action: "leave", line: 0, params: [], source_line: 2 }], name: "0", type: "Block" }, { arg_set: { names: [], types: [] }, instructions: [{ action: "putobject", line: 0, params: ["10"], source_line: 2 }, { action: "send", line: 1, params: ["times", "0", "block:0", "&{[] []}"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }, { arg_set: { names: [], types: [] }, instructions: [{ action: "putobject", line: 0, params: ["10"], source_line: 2 }, { action: "send", line: 1, params: ["times", "0", "block:0", "&{[] []}"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }]`},
+		 ").to_s`, `
+[
+  { 
+		arg_types:{names:["i"],types:[0]},
+    instructions: [
+      { action: "leave", line: 0, params: [], source_line: 2 }
+    ], 
+    name: "0", 
+    type: "Block" 
+  }, 
+  {
+		arg_types:{names:["i"],types:[0]},
+		instructions: [
+		 { action: "leave", line: 0, params: [], source_line: 2 }
+		], 
+		name: "0", 
+		type: "Block" 
+  }, 
+  { 
+     arg_set: { names: [], types: [] }, 
+     instructions: [
+       { action: "putobject", line: 0, params: ["10"], source_line: 2 }, 
+       { action: "send", line: 1, params: ["times", "0", "block:0", "&{[] []}"], source_line: 2 }, 
+       { action: "pop", line: 2, params: [], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }
+     ],
+     name: "ProgramStart", 
+     type: "ProgramStart" 
+  }, 
+  { 
+    arg_set: { names: [], types: [] }, 
+    instructions: [
+      { action: "putobject", line: 0, params: ["10"], source_line: 2 }, 
+      { action: "send", line: 1, params: ["times", "0", "block:0", "&{[] []}"], source_line: 2 }, 
+      { action: "pop", line: 2, params: [], source_line: 2 }, 
+      { action: "leave", line: 3, params: [], source_line: 2 }
+    ], 
+    name: "ProgramStart", 
+    type: "ProgramStart" 
+  }
+]`},
 		// Single module definition
 		{`require 'ripper'; Ripper.instruction("
 		module Bar
     end
-		 ").to_s`, `[{ instructions: [{ action: "leave", line: 0, params: [], source_line: 2 }], name: "Bar", type: "DefClass" }, { instructions: [{ action: "leave", line: 0, params: [], source_line: 2 }], name: "Bar", type: "DefClass" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 2 }, { action: "def_class", line: 1, params: ["module:Bar"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 2 }, { action: "def_class", line: 1, params: ["module:Bar"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }]`},
+		 ").to_s`, `
+[
+  { instructions: [{ action: "leave", line: 0, params: [], source_line: 2 }], name: "Bar", type: "DefClass" }, 
+  { instructions: [{ action: "leave", line: 0, params: [], source_line: 2 }], name: "Bar", type: "DefClass" }, 
+  { 
+    instructions: [
+      { action: "putself", line: 0, params: [], source_line: 2 }, 
+      { action: "def_class", line: 1, params: ["module:Bar"], source_line: 2 }, 
+      { action: "pop", line: 2, params: [], source_line: 2 }, 
+      { action: "leave", line: 3, params: [], source_line: 2 }
+    ], 
+    name: "ProgramStart", 
+    type: "ProgramStart" 
+  },
+  { 
+    instructions: [
+      { action: "putself", line: 0, params: [], source_line: 2 }, 
+      { action: "def_class", line: 1, params: ["module:Bar"], source_line: 2 }, 
+      { action: "pop", line: 2, params: [], source_line: 2 }, 
+      { action: "leave", line: 3, params: [], source_line: 2 }
+    ], 
+    name: "ProgramStart", 
+    type: "ProgramStart" 
+  }
+]`},
 		// Single assignment
 		{`require 'ripper'; Ripper.instruction("
 		 a = 1
-		 ").to_s`, `[{ instructions: [{ action: "putobject", line: 0, params: ["1"], source_line: 2 }, { action: "setlocal", line: 1, params: ["0", "0"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }, { instructions: [{ action: "putobject", line: 0, params: ["1"], source_line: 2 }, { action: "setlocal", line: 1, params: ["0", "0"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }]`},
+		 ").to_s`, `
+[
+  { instructions: [{ action: "putobject", line: 0, params: ["1"], source_line: 2 }, { action: "setlocal", line: 1, params: ["0", "0"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }, 
+  { instructions: [{ action: "putobject", line: 0, params: ["1"], source_line: 2 }, { action: "setlocal", line: 1, params: ["0", "0"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "ProgramStart", type: "ProgramStart" }
+]`},
 		// More complicated cases
 		{`require 'ripper'
 	Ripper.instruction("
@@ -345,7 +511,106 @@ end
 	class Foo < Bar; end
 	class FooBar < Foo; end
 	FooBar.foo
-  ").to_s`, `[{ arg_types: { names: [], types: [] }, instructions: [{ action: "putobject", line: 0, params: ["10"], source_line: 4 }, { action: "leave", line: 1, params: [], source_line: 3 }], name: "foo", type: "Def" }, { arg_types: { names: [], types: [] }, instructions: [{ action: "putobject", line: 0, params: ["10"], source_line: 4 }, { action: "leave", line: 1, params: [], source_line: 3 }], name: "foo", type: "Def" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 3 }, { action: "putstring", line: 1, params: ["foo"], source_line: 3 }, { action: "def_singleton_method", line: 2, params: ["0"], source_line: 3 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "Bar", type: "DefClass" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 3 }, { action: "putstring", line: 1, params: ["foo"], source_line: 3 }, { action: "def_singleton_method", line: 2, params: ["0"], source_line: 3 }, { action: "leave", line: 3, params: [], source_line: 2 }], name: "Bar", type: "DefClass" }, { instructions: [{ action: "leave", line: 0, params: [], source_line: 7 }], name: "Foo", type: "DefClass" }, { instructions: [{ action: "leave", line: 0, params: [], source_line: 7 }], name: "Foo", type: "DefClass" }, { instructions: [{ action: "leave", line: 0, params: [], source_line: 8 }], name: "FooBar", type: "DefClass" }, { instructions: [{ action: "leave", line: 0, params: [], source_line: 8 }], name: "FooBar", type: "DefClass" }, { arg_set: { names: [], types: [] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 2 }, { action: "def_class", line: 1, params: ["class:Bar"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "putself", line: 3, params: [], source_line: 7 }, { action: "getconstant", line: 4, params: ["Bar", "false"], source_line: 7 }, { action: "def_class", line: 5, params: ["class:Foo", "Bar"], source_line: 7 }, { action: "pop", line: 6, params: [], source_line: 7 }, { action: "pop", line: 7, params: [], source_line: 7 }, { action: "putself", line: 8, params: [], source_line: 8 }, { action: "getconstant", line: 9, params: ["Foo", "false"], source_line: 8 }, { action: "def_class", line: 10, params: ["class:FooBar", "Foo"], source_line: 8 }, { action: "pop", line: 11, params: [], source_line: 8 }, { action: "pop", line: 12, params: [], source_line: 8 }, { action: "getconstant", line: 13, params: ["FooBar", "false"], source_line: 9 }, { action: "send", line: 14, params: ["foo", "0", "", "&{[] []}"], source_line: 9 }, { action: "pop", line: 15, params: [], source_line: 9 }, { action: "leave", line: 16, params: [], source_line: 9 }], name: "ProgramStart", type: "ProgramStart" }, { arg_set: { names: [], types: [] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 2 }, { action: "def_class", line: 1, params: ["class:Bar"], source_line: 2 }, { action: "pop", line: 2, params: [], source_line: 2 }, { action: "putself", line: 3, params: [], source_line: 7 }, { action: "getconstant", line: 4, params: ["Bar", "false"], source_line: 7 }, { action: "def_class", line: 5, params: ["class:Foo", "Bar"], source_line: 7 }, { action: "pop", line: 6, params: [], source_line: 7 }, { action: "pop", line: 7, params: [], source_line: 7 }, { action: "putself", line: 8, params: [], source_line: 8 }, { action: "getconstant", line: 9, params: ["Foo", "false"], source_line: 8 }, { action: "def_class", line: 10, params: ["class:FooBar", "Foo"], source_line: 8 }, { action: "pop", line: 11, params: [], source_line: 8 }, { action: "pop", line: 12, params: [], source_line: 8 }, { action: "getconstant", line: 13, params: ["FooBar", "false"], source_line: 9 }, { action: "send", line: 14, params: ["foo", "0", "", "&{[] []}"], source_line: 9 }, { action: "pop", line: 15, params: [], source_line: 9 }, { action: "leave", line: 16, params: [], source_line: 9 }], name: "ProgramStart", type: "ProgramStart" }]`},
+  ").to_s`, `
+[
+  { 
+    arg_types: { names: [], types: [] }, 
+    instructions: [
+      { action: "putobject", line: 0, params: ["10"], source_line: 4 }, 
+      { action: "leave", line: 1, params: [], source_line: 3 }
+    ], 
+    name: "foo", 
+    type: "Def" 
+  }, 
+  { 
+    arg_types: { names: [], types: [] }, 
+    instructions: [
+      { action: "putobject", line: 0, params: ["10"], source_line: 4 }, 
+      { action: "leave", line: 1, params: [], source_line: 3 }
+    ], 
+    name: "foo", 
+    type: "Def" 
+  }, 
+  { 
+    instructions: [
+      { action: "putself", line: 0, params: [], source_line: 3 }, 
+      { action: "putstring", line: 1, params: ["foo"], source_line: 3 }, 
+      { action: "def_singleton_method", line: 2, params: ["0"], source_line: 3 }, 
+      { action: "leave", line: 3, params: [], source_line: 2 }
+    ], 
+    name: "Bar", 
+    type: "DefClass" 
+  }, 
+  { 
+    instructions: [
+      { action: "putself", line: 0, params: [], source_line: 3 }, 
+      { action: "putstring", line: 1, params: ["foo"], source_line: 3 }, 
+      { action: "def_singleton_method", line: 2, params: ["0"], source_line: 3 }, 
+      { action: "leave", line: 3, params: [], source_line: 2 }
+    ], 
+    name: "Bar", 
+    type: "DefClass" 
+  }, 
+  { 
+    instructions: [{ action: "leave", line: 0, params: [], source_line: 7 }], name: "Foo", type: "DefClass" 
+  }, 
+  { 
+    instructions: [{ action: "leave", line: 0, params: [], source_line: 7 }], name: "Foo", type: "DefClass" 
+  }, 
+  { 
+    instructions: [{ action: "leave", line: 0, params: [], source_line: 8 }], name: "FooBar", type: "DefClass" 
+  }, 
+  { 
+    instructions: [{ action: "leave", line: 0, params: [], source_line: 8 }], name: "FooBar", type: "DefClass" 
+  }, 
+  { 
+    arg_set: { names: [], types: [] }, 
+    instructions: [
+      { action: "putself", line: 0, params: [], source_line: 2 }, 
+      { action: "def_class", line: 1, params: ["class:Bar"], source_line: 2 }, 
+      { action: "pop", line: 2, params: [], source_line: 2 }, 
+      { action: "putself", line: 3, params: [], source_line: 7 }, 
+      { action: "getconstant", line: 4, params: ["Bar", "false"], source_line: 7 }, 
+      { action: "def_class", line: 5, params: ["class:Foo", "Bar"], source_line: 7 }, 
+      { action: "pop", line: 6, params: [], source_line: 7 }, 
+      { action: "pop", line: 7, params: [], source_line: 7 }, 
+      { action: "putself", line: 8, params: [], source_line: 8 }, 
+      { action: "getconstant", line: 9, params: ["Foo", "false"], source_line: 8 }, 
+      { action: "def_class", line: 10, params: ["class:FooBar", "Foo"], source_line: 8 }, 
+      { action: "pop", line: 11, params: [], source_line: 8 }, { action: "pop", line: 12, params: [], source_line: 8 }, 
+      { action: "getconstant", line: 13, params: ["FooBar", "false"], source_line: 9 }, 
+      { action: "send", line: 14, params: ["foo", "0", "", "&{[] []}"], source_line: 9 }, 
+      { action: "pop", line: 15, params: [], source_line: 9 }, 
+      { action: "leave", line: 16, params: [], source_line: 9 }
+    ], 
+    name: "ProgramStart", 
+    type: "ProgramStart" 
+  }, 
+  { 
+    arg_set: { names: [], types: [] }, 
+    instructions: [
+      { action: "putself", line: 0, params: [], source_line: 2 }, 
+      { action: "def_class", line: 1, params: ["class:Bar"], source_line: 2 }, 
+      { action: "pop", line: 2, params: [], source_line: 2 }, 
+      { action: "putself", line: 3, params: [], source_line: 7 }, 
+      { action: "getconstant", line: 4, params: ["Bar", "false"], source_line: 7 }, 
+      { action: "def_class", line: 5, params: ["class:Foo", "Bar"], source_line: 7 }, 
+      { action: "pop", line: 6, params: [], source_line: 7 }, 
+      { action: "pop", line: 7, params: [], source_line: 7 }, 
+      { action: "putself", line: 8, params: [], source_line: 8 }, 
+      { action: "getconstant", line: 9, params: ["Foo", "false"], source_line: 8 }, 
+      { action: "def_class", line: 10, params: ["class:FooBar", "Foo"], source_line: 8 }, 
+      { action: "pop", line: 11, params: [], source_line: 8 }, 
+      { action: "pop", line: 12, params: [], source_line: 8 }, 
+      { action: "getconstant", line: 13, params: ["FooBar", "false"], source_line: 9 }, 
+      { action: "send", line: 14, params: ["foo", "0", "", "&{[] []}"], source_line: 9 }, 
+      { action: "pop", line: 15, params: [], source_line: 9 }, 
+	  { action: "leave", line: 16, params: [], source_line: 9 }
+    ],
+	name: "ProgramStart", 
+	type: "ProgramStart" 
+}
+]`},
 		{`require 'ripper'
 Ripper.instruction("
 	def foo(x)
@@ -380,7 +645,979 @@ Ripper.instruction("
 	  end
 	end
 	Baz::Bar.new.bar + a
-").to_s`, `[{ arg_set: { names: [], types: [] }, arg_types: { names: ["x"], types: [0] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 3 }, { action: "getlocal", line: 1, params: ["0", "0"], source_line: 3 }, { action: "putobject", line: 2, params: ["10"], source_line: 3 }, { action: "send", line: 3, params: ["+", "1", "", "&{[] []}"], source_line: 3 }, { action: "invokeblock", line: 4, params: ["1"], source_line: 3 }, { action: "leave", line: 5, params: [], source_line: 2 }], name: "foo", type: "Def" }, { arg_set: { names: [], types: [] }, arg_types: { names: ["x"], types: [0] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 3 }, { action: "getlocal", line: 1, params: ["0", "0"], source_line: 3 }, { action: "putobject", line: 2, params: ["10"], source_line: 3 }, { action: "send", line: 3, params: ["+", "1", "", "&{[] []}"], source_line: 3 }, { action: "invokeblock", line: 4, params: ["1"], source_line: 3 }, { action: "leave", line: 5, params: [], source_line: 2 }], name: "foo", type: "Def" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 7 }, { action: "getlocal", line: 1, params: ["0", "0"], source_line: 7 }, { action: "invokeblock", line: 2, params: ["1"], source_line: 7 }, { action: "leave", line: 3, params: [], source_line: 6 }], name: "0", type: "Block" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 7 }, { action: "getlocal", line: 1, params: ["0", "0"], source_line: 7 }, { action: "invokeblock", line: 2, params: ["1"], source_line: 7 }, { action: "leave", line: 3, params: [], source_line: 6 }], name: "0", type: "Block" }, { arg_set: { names: ["y"], types: [0] }, arg_types: { names: ["y"], types: [0] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 6 }, { action: "getlocal", line: 1, params: ["0", "0"], source_line: 6 }, { action: "send", line: 2, params: ["foo", "1", "block:0", "&{[y] [0]}"], source_line: 6 }, { action: "leave", line: 3, params: [], source_line: 5 }], name: "bar", type: "Def" }, { arg_set: { names: ["y"], types: [0] }, arg_types: { names: ["y"], types: [0] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 6 }, { action: "getlocal", line: 1, params: ["0", "0"], source_line: 6 }, { action: "send", line: 2, params: ["foo", "1", "block:0", "&{[y] [0]}"], source_line: 6 }, { action: "leave", line: 3, params: [], source_line: 5 }], name: "bar", type: "Def" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 12 }, { action: "getlocal", line: 1, params: ["0", "0"], source_line: 12 }, { action: "invokeblock", line: 2, params: ["1"], source_line: 12 }, { action: "leave", line: 3, params: [], source_line: 11 }], name: "1", type: "Block" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 12 }, { action: "getlocal", line: 1, params: ["0", "0"], source_line: 12 }, { action: "invokeblock", line: 2, params: ["1"], source_line: 12 }, { action: "leave", line: 3, params: [], source_line: 11 }], name: "1", type: "Block" }, { arg_set: { names: [""], types: [0] }, arg_types: { names: ["z"], types: [0] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 11 }, { action: "getlocal", line: 1, params: ["0", "0"], source_line: 11 }, { action: "putobject", line: 2, params: ["100"], source_line: 11 }, { action: "send", line: 3, params: ["+", "1", "", "&{[] []}"], source_line: 11 }, { action: "send", line: 4, params: ["bar", "1", "block:1", "&{[] [0]}"], source_line: 11 }, { action: "leave", line: 5, params: [], source_line: 10 }], name: "baz", type: "Def" }, { arg_set: { names: [""], types: [0] }, arg_types: { names: ["z"], types: [0] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 11 }, { action: "getlocal", line: 1, params: ["0", "0"], source_line: 11 }, { action: "putobject", line: 2, params: ["100"], source_line: 11 }, { action: "send", line: 3, params: ["+", "1", "", "&{[] []}"], source_line: 11 }, { action: "send", line: 4, params: ["bar", "1", "block:1", "&{[] [0]}"], source_line: 11 }, { action: "leave", line: 5, params: [], source_line: 10 }], name: "baz", type: "Def" }, { instructions: [{ action: "getlocal", line: 0, params: ["0", "0"], source_line: 17 }, { action: "setlocal", line: 1, params: ["1", "0"], source_line: 17 }, { action: "leave", line: 2, params: [], source_line: 16 }], name: "2", type: "Block" }, { instructions: [{ action: "getlocal", line: 0, params: ["0", "0"], source_line: 17 }, { action: "setlocal", line: 1, params: ["1", "0"], source_line: 17 }, { action: "leave", line: 2, params: [], source_line: 16 }], name: "2", type: "Block" }, { arg_types: { names: [], types: [] }, instructions: [{ action: "putobject", line: 0, params: ["100"], source_line: 23 }, { action: "leave", line: 1, params: [], source_line: 22 }], name: "bar", type: "Def" }, { arg_types: { names: [], types: [] }, instructions: [{ action: "putobject", line: 0, params: ["100"], source_line: 23 }, { action: "leave", line: 1, params: [], source_line: 22 }], name: "bar", type: "Def" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 22 }, { action: "putstring", line: 1, params: ["bar"], source_line: 22 }, { action: "def_method", line: 2, params: ["0"], source_line: 22 }, { action: "leave", line: 3, params: [], source_line: 21 }], name: "Foo", type: "DefClass" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 22 }, { action: "putstring", line: 1, params: ["bar"], source_line: 22 }, { action: "def_method", line: 2, params: ["0"], source_line: 22 }, { action: "leave", line: 3, params: [], source_line: 21 }], name: "Foo", type: "DefClass" }, { arg_set: { names: [], types: [] }, arg_types: { names: [], types: [] }, instructions: [{ action: "getconstant", line: 0, params: ["Foo", "false"], source_line: 29 }, { action: "send", line: 1, params: ["new", "0", "", "&{[] []}"], source_line: 29 }, { action: "send", line: 2, params: ["bar", "0", "", "&{[] []}"], source_line: 29 }, { action: "leave", line: 3, params: [], source_line: 28 }], name: "bar", type: "Def" }, { arg_set: { names: [], types: [] }, arg_types: { names: [], types: [] }, instructions: [{ action: "getconstant", line: 0, params: ["Foo", "false"], source_line: 29 }, { action: "send", line: 1, params: ["new", "0", "", "&{[] []}"], source_line: 29 }, { action: "send", line: 2, params: ["bar", "0", "", "&{[] []}"], source_line: 29 }, { action: "leave", line: 3, params: [], source_line: 28 }], name: "bar", type: "Def" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 28 }, { action: "putstring", line: 1, params: ["bar"], source_line: 28 }, { action: "def_method", line: 2, params: ["0"], source_line: 28 }, { action: "leave", line: 3, params: [], source_line: 27 }], name: "Bar", type: "DefClass" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 28 }, { action: "putstring", line: 1, params: ["bar"], source_line: 28 }, { action: "def_method", line: 2, params: ["0"], source_line: 28 }, { action: "leave", line: 3, params: [], source_line: 27 }], name: "Bar", type: "DefClass" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 27 }, { action: "def_class", line: 1, params: ["class:Bar"], source_line: 27 }, { action: "pop", line: 2, params: [], source_line: 27 }, { action: "leave", line: 3, params: [], source_line: 26 }], name: "Baz", type: "DefClass" }, { instructions: [{ action: "putself", line: 0, params: [], source_line: 27 }, { action: "def_class", line: 1, params: ["class:Bar"], source_line: 27 }, { action: "pop", line: 2, params: [], source_line: 27 }, { action: "leave", line: 3, params: [], source_line: 26 }], name: "Baz", type: "DefClass" }, { arg_set: { names: [], types: [] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 2 }, { action: "putstring", line: 1, params: ["foo"], source_line: 2 }, { action: "def_method", line: 2, params: ["1"], source_line: 2 }, { action: "putself", line: 3, params: [], source_line: 5 }, { action: "putstring", line: 4, params: ["bar"], source_line: 5 }, { action: "def_method", line: 5, params: ["1"], source_line: 5 }, { action: "putself", line: 6, params: [], source_line: 10 }, { action: "putstring", line: 7, params: ["baz"], source_line: 10 }, { action: "def_method", line: 8, params: ["1"], source_line: 10 }, { action: "putobject", line: 9, params: ["0"], source_line: 15 }, { action: "setlocal", line: 10, params: ["0", "0"], source_line: 15 }, { action: "pop", line: 11, params: [], source_line: 15 }, { action: "putself", line: 12, params: [], source_line: 16 }, { action: "putobject", line: 13, params: ["100"], source_line: 16 }, { action: "send", line: 14, params: ["baz", "1", "block:2", "&{[] [0]}"], source_line: 16 }, { action: "pop", line: 15, params: [], source_line: 16 }, { action: "getlocal", line: 16, params: ["0", "0"], source_line: 19 }, { action: "pop", line: 17, params: [], source_line: 19 }, { action: "putself", line: 18, params: [], source_line: 21 }, { action: "def_class", line: 19, params: ["class:Foo"], source_line: 21 }, { action: "pop", line: 20, params: [], source_line: 21 }, { action: "putself", line: 21, params: [], source_line: 26 }, { action: "def_class", line: 22, params: ["module:Baz"], source_line: 26 }, { action: "pop", line: 23, params: [], source_line: 26 }, { action: "getconstant", line: 24, params: ["Baz", "true"], source_line: 33 }, { action: "getconstant", line: 25, params: ["Bar", "false"], source_line: 33 }, { action: "send", line: 26, params: ["new", "0", "", "&{[] []}"], source_line: 33 }, { action: "send", line: 27, params: ["bar", "0", "", "&{[] []}"], source_line: 33 }, { action: "getlocal", line: 28, params: ["0", "0"], source_line: 33 }, { action: "send", line: 29, params: ["+", "1", "", "&{[] []}"], source_line: 33 }, { action: "pop", line: 30, params: [], source_line: 33 }, { action: "leave", line: 31, params: [], source_line: 33 }], name: "ProgramStart", type: "ProgramStart" }, { arg_set: { names: [], types: [] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 2 }, { action: "putstring", line: 1, params: ["foo"], source_line: 2 }, { action: "def_method", line: 2, params: ["1"], source_line: 2 }, { action: "putself", line: 3, params: [], source_line: 5 }, { action: "putstring", line: 4, params: ["bar"], source_line: 5 }, { action: "def_method", line: 5, params: ["1"], source_line: 5 }, { action: "putself", line: 6, params: [], source_line: 10 }, { action: "putstring", line: 7, params: ["baz"], source_line: 10 }, { action: "def_method", line: 8, params: ["1"], source_line: 10 }, { action: "putobject", line: 9, params: ["0"], source_line: 15 }, { action: "setlocal", line: 10, params: ["0", "0"], source_line: 15 }, { action: "pop", line: 11, params: [], source_line: 15 }, { action: "putself", line: 12, params: [], source_line: 16 }, { action: "putobject", line: 13, params: ["100"], source_line: 16 }, { action: "send", line: 14, params: ["baz", "1", "block:2", "&{[] [0]}"], source_line: 16 }, { action: "pop", line: 15, params: [], source_line: 16 }, { action: "getlocal", line: 16, params: ["0", "0"], source_line: 19 }, { action: "pop", line: 17, params: [], source_line: 19 }, { action: "putself", line: 18, params: [], source_line: 21 }, { action: "def_class", line: 19, params: ["class:Foo"], source_line: 21 }, { action: "pop", line: 20, params: [], source_line: 21 }, { action: "putself", line: 21, params: [], source_line: 26 }, { action: "def_class", line: 22, params: ["module:Baz"], source_line: 26 }, { action: "pop", line: 23, params: [], source_line: 26 }, { action: "getconstant", line: 24, params: ["Baz", "true"], source_line: 33 }, { action: "getconstant", line: 25, params: ["Bar", "false"], source_line: 33 }, { action: "send", line: 26, params: ["new", "0", "", "&{[] []}"], source_line: 33 }, { action: "send", line: 27, params: ["bar", "0", "", "&{[] []}"], source_line: 33 }, { action: "getlocal", line: 28, params: ["0", "0"], source_line: 33 }, { action: "send", line: 29, params: ["+", "1", "", "&{[] []}"], source_line: 33 }, { action: "pop", line: 30, params: [], source_line: 33 }, { action: "leave", line: 31, params: [], source_line: 33 }], name: "ProgramStart", type: "ProgramStart" }]`},
+").to_s`, `
+[{
+  arg_set: {
+    names: [],
+    types: []
+  },
+  arg_types: {
+    names: ["x"],
+    types: [0]
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 3
+  }, {
+    action: "getlocal",
+    line: 1,
+    params: ["0", "0"],
+    source_line: 3
+  }, {
+    action: "putobject",
+    line: 2,
+    params: ["10"],
+    source_line: 3
+  }, {
+    action: "send",
+    line: 3,
+    params: ["+", "1", "", "&{[][]}"],
+    source_line: 3
+  }, {
+    action: "invokeblock",
+    line: 4,
+    params: ["1"],
+    source_line: 3
+  }, {
+    action: "leave",
+    line: 5,
+    params: [],
+    source_line: 2
+  }],
+  name: "foo",
+  type: "Def"
+}, {
+  arg_set: {
+    names: [],
+    types: []
+  },
+  arg_types: {
+    names: ["x"],
+    types: [0]
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 3
+  }, {
+    action: "getlocal",
+    line: 1,
+    params: ["0", "0"],
+    source_line: 3
+  }, {
+    action: "putobject",
+    line: 2,
+    params: ["10"],
+    source_line: 3
+  }, {
+    action: "send",
+    line: 3,
+    params: ["+", "1", "", "&{[][]}"],
+    source_line: 3
+  }, {
+    action: "invokeblock",
+    line: 4,
+    params: ["1"],
+    source_line: 3
+  }, {
+    action: "leave",
+    line: 5,
+    params: [],
+    source_line: 2
+  }],
+  name: "foo",
+  type: "Def"
+}, {
+  arg_types: {
+    names: ["f"],
+    types: [0]
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 7
+  }, {
+    action: "getlocal",
+    line: 1,
+    params: ["0", "0"],
+    source_line: 7
+  }, {
+    action: "invokeblock",
+    line: 2,
+    params: ["1"],
+    source_line: 7
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 6
+  }],
+  name: "0",
+  type: "Block"
+}, {
+  arg_types: {
+    names: ["f"],
+    types: [0]
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 7
+  }, {
+    action: "getlocal",
+    line: 1,
+    params: ["0", "0"],
+    source_line: 7
+  }, {
+    action: "invokeblock",
+    line: 2,
+    params: ["1"],
+    source_line: 7
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 6
+  }],
+  name: "0",
+  type: "Block"
+}, {
+  arg_set: {
+    names: ["y"],
+    types: [0]
+  },
+  arg_types: {
+    names: ["y"],
+    types: [0]
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 6
+  }, {
+    action: "getlocal",
+    line: 1,
+    params: ["0", "0"],
+    source_line: 6
+  }, {
+    action: "send",
+    line: 2,
+    params: ["foo", "1", "block:0", "&{[y][0]}"],
+    source_line: 6
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 5
+  }],
+  name: "bar",
+  type: "Def"
+}, {
+  arg_set: {
+    names: ["y"],
+    types: [0]
+  },
+  arg_types: {
+    names: ["y"],
+    types: [0]
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 6
+  }, {
+    action: "getlocal",
+    line: 1,
+    params: ["0", "0"],
+    source_line: 6
+  }, {
+    action: "send",
+    line: 2,
+    params: ["foo", "1", "block:0", "&{[y][0]}"],
+    source_line: 6
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 5
+  }],
+  name: "bar",
+  type: "Def"
+}, {
+  arg_types: {
+    names: ["b"],
+    types: [0]
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 12
+  }, {
+    action: "getlocal",
+    line: 1,
+    params: ["0", "0"],
+    source_line: 12
+  }, {
+    action: "invokeblock",
+    line: 2,
+    params: ["1"],
+    source_line: 12
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 11
+  }],
+  name: "1",
+  type: "Block"
+}, {
+  arg_types: {
+    names: ["b"],
+    types: [0]
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 12
+  }, {
+    action: "getlocal",
+    line: 1,
+    params: ["0", "0"],
+    source_line: 12
+  }, {
+    action: "invokeblock",
+    line: 2,
+    params: ["1"],
+    source_line: 12
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 11
+  }],
+  name: "1",
+  type: "Block"
+}, {
+  arg_set: {
+    names: [""],
+    types: [0]
+  },
+  arg_types: {
+    names: ["z"],
+    types: [0]
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 11
+  }, {
+    action: "getlocal",
+    line: 1,
+    params: ["0", "0"],
+    source_line: 11
+  }, {
+    action: "putobject",
+    line: 2,
+    params: ["100"],
+    source_line: 11
+  }, {
+    action: "send",
+    line: 3,
+    params: ["+", "1", "", "&{[][]}"],
+    source_line: 11
+  }, {
+    action: "send",
+    line: 4,
+    params: ["bar", "1", "block:1", "&{[][0]}"],
+    source_line: 11
+  }, {
+    action: "leave",
+    line: 5,
+    params: [],
+    source_line: 10
+  }],
+  name: "baz",
+  type: "Def"
+}, {
+  arg_set: {
+    names: [""],
+    types: [0]
+  },
+  arg_types: {
+    names: ["z"],
+    types: [0]
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 11
+  }, {
+    action: "getlocal",
+    line: 1,
+    params: ["0", "0"],
+    source_line: 11
+  }, {
+    action: "putobject",
+    line: 2,
+    params: ["100"],
+    source_line: 11
+  }, {
+    action: "send",
+    line: 3,
+    params: ["+", "1", "", "&{[][]}"],
+    source_line: 11
+  }, {
+    action: "send",
+    line: 4,
+    params: ["bar", "1", "block:1", "&{[][0]}"],
+    source_line: 11
+  }, {
+    action: "leave",
+    line: 5,
+    params: [],
+    source_line: 10
+  }],
+  name: "baz",
+  type: "Def"
+}, {
+  arg_types: {
+    names: ["b"],
+    types: [0]
+  },
+  instructions: [{
+    action: "getlocal",
+    line: 0,
+    params: ["0", "0"],
+    source_line: 17
+  }, {
+    action: "setlocal",
+    line: 1,
+    params: ["1", "0"],
+    source_line: 17
+  }, {
+    action: "leave",
+    line: 2,
+    params: [],
+    source_line: 16
+  }],
+  name: "2",
+  type: "Block"
+}, {
+  arg_types: {
+    names: ["b"],
+    types: [0]
+  },
+  instructions: [{
+    action: "getlocal",
+    line: 0,
+    params: ["0", "0"],
+    source_line: 17
+  }, {
+    action: "setlocal",
+    line: 1,
+    params: ["1", "0"],
+    source_line: 17
+  }, {
+    action: "leave",
+    line: 2,
+    params: [],
+    source_line: 16
+  }],
+  name: "2",
+  type: "Block"
+}, {
+  arg_types: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "putobject",
+    line: 0,
+    params: ["100"],
+    source_line: 23
+  }, {
+    action: "leave",
+    line: 1,
+    params: [],
+    source_line: 22
+  }],
+  name: "bar",
+  type: "Def"
+}, {
+  arg_types: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "putobject",
+    line: 0,
+    params: ["100"],
+    source_line: 23
+  }, {
+    action: "leave",
+    line: 1,
+    params: [],
+    source_line: 22
+  }],
+  name: "bar",
+  type: "Def"
+}, {
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 22
+  }, {
+    action: "putstring",
+    line: 1,
+    params: ["bar"],
+    source_line: 22
+  }, {
+    action: "def_method",
+    line: 2,
+    params: ["0"],
+    source_line: 22
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 21
+  }],
+  name: "Foo",
+  type: "DefClass"
+}, {
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 22
+  }, {
+    action: "putstring",
+    line: 1,
+    params: ["bar"],
+    source_line: 22
+  }, {
+    action: "def_method",
+    line: 2,
+    params: ["0"],
+    source_line: 22
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 21
+  }],
+  name: "Foo",
+  type: "DefClass"
+}, {
+  arg_set: {
+    names: [],
+    types: []
+  },
+  arg_types: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "getconstant",
+    line: 0,
+    params: ["Foo", "false"],
+    source_line: 29
+  }, {
+    action: "send",
+    line: 1,
+    params: ["new", "0", "", "&{[][]}"],
+    source_line: 29
+  }, {
+    action: "send",
+    line: 2,
+    params: ["bar", "0", "", "&{[][]}"],
+    source_line: 29
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 28
+  }],
+  name: "bar",
+  type: "Def"
+}, {
+  arg_set: {
+    names: [],
+    types: []
+  },
+  arg_types: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "getconstant",
+    line: 0,
+    params: ["Foo", "false"],
+    source_line: 29
+  }, {
+    action: "send",
+    line: 1,
+    params: ["new", "0", "", "&{[][]}"],
+    source_line: 29
+  }, {
+    action: "send",
+    line: 2,
+    params: ["bar", "0", "", "&{[][]}"],
+    source_line: 29
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 28
+  }],
+  name: "bar",
+  type: "Def"
+}, {
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 28
+  }, {
+    action: "putstring",
+    line: 1,
+    params: ["bar"],
+    source_line: 28
+  }, {
+    action: "def_method",
+    line: 2,
+    params: ["0"],
+    source_line: 28
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 27
+  }],
+  name: "Bar",
+  type: "DefClass"
+}, {
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 28
+  }, {
+    action: "putstring",
+    line: 1,
+    params: ["bar"],
+    source_line: 28
+  }, {
+    action: "def_method",
+    line: 2,
+    params: ["0"],
+    source_line: 28
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 27
+  }],
+  name: "Bar",
+  type: "DefClass"
+}, {
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 27
+  }, {
+    action: "def_class",
+    line: 1,
+    params: ["class:Bar"],
+    source_line: 27
+  }, {
+    action: "pop",
+    line: 2,
+    params: [],
+    source_line: 27
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 26
+  }],
+  name: "Baz",
+  type: "DefClass"
+}, {
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 27
+  }, {
+    action: "def_class",
+    line: 1,
+    params: ["class:Bar"],
+    source_line: 27
+  }, {
+    action: "pop",
+    line: 2,
+    params: [],
+    source_line: 27
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 26
+  }],
+  name: "Baz",
+  type: "DefClass"
+}, {
+  arg_set: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 2
+  }, {
+    action: "putstring",
+    line: 1,
+    params: ["foo"],
+    source_line: 2
+  }, {
+    action: "def_method",
+    line: 2,
+    params: ["1"],
+    source_line: 2
+  }, {
+    action: "putself",
+    line: 3,
+    params: [],
+    source_line: 5
+  }, {
+    action: "putstring",
+    line: 4,
+    params: ["bar"],
+    source_line: 5
+  }, {
+    action: "def_method",
+    line: 5,
+    params: ["1"],
+    source_line: 5
+  }, {
+    action: "putself",
+    line: 6,
+    params: [],
+    source_line: 10
+  }, {
+    action: "putstring",
+    line: 7,
+    params: ["baz"],
+    source_line: 10
+  }, {
+    action: "def_method",
+    line: 8,
+    params: ["1"],
+    source_line: 10
+  }, {
+    action: "putobject",
+    line: 9,
+    params: ["0"],
+    source_line: 15
+  }, {
+    action: "setlocal",
+    line: 10,
+    params: ["0", "0"],
+    source_line: 15
+  }, {
+    action: "pop",
+    line: 11,
+    params: [],
+    source_line: 15
+  }, {
+    action: "putself",
+    line: 12,
+    params: [],
+    source_line: 16
+  }, {
+    action: "putobject",
+    line: 13,
+    params: ["100"],
+    source_line: 16
+  }, {
+    action: "send",
+    line: 14,
+    params: ["baz", "1", "block:2", "&{[][0]}"],
+    source_line: 16
+  }, {
+    action: "pop",
+    line: 15,
+    params: [],
+    source_line: 16
+  }, {
+    action: "getlocal",
+    line: 16,
+    params: ["0", "0"],
+    source_line: 19
+  }, {
+    action: "pop",
+    line: 17,
+    params: [],
+    source_line: 19
+  }, {
+    action: "putself",
+    line: 18,
+    params: [],
+    source_line: 21
+  }, {
+    action: "def_class",
+    line: 19,
+    params: ["class:Foo"],
+    source_line: 21
+  }, {
+    action: "pop",
+    line: 20,
+    params: [],
+    source_line: 21
+  }, {
+    action: "putself",
+    line: 21,
+    params: [],
+    source_line: 26
+  }, {
+    action: "def_class",
+    line: 22,
+    params: ["module:Baz"],
+    source_line: 26
+  }, {
+    action: "pop",
+    line: 23,
+    params: [],
+    source_line: 26
+  }, {
+    action: "getconstant",
+    line: 24,
+    params: ["Baz", "true"],
+    source_line: 33
+  }, {
+    action: "getconstant",
+    line: 25,
+    params: ["Bar", "false"],
+    source_line: 33
+  }, {
+    action: "send",
+    line: 26,
+    params: ["new", "0", "", "&{[][]}"],
+    source_line: 33
+  }, {
+    action: "send",
+    line: 27,
+    params: ["bar", "0", "", "&{[][]}"],
+    source_line: 33
+  }, {
+    action: "getlocal",
+    line: 28,
+    params: ["0", "0"],
+    source_line: 33
+  }, {
+    action: "send",
+    line: 29,
+    params: ["+", "1", "", "&{[][]}"],
+    source_line: 33
+  }, {
+    action: "pop",
+    line: 30,
+    params: [],
+    source_line: 33
+  }, {
+    action: "leave",
+    line: 31,
+    params: [],
+    source_line: 33
+  }],
+  name: "ProgramStart",
+  type: "ProgramStart"
+}, {
+  arg_set: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 2
+  }, {
+    action: "putstring",
+    line: 1,
+    params: ["foo"],
+    source_line: 2
+  }, {
+    action: "def_method",
+    line: 2,
+    params: ["1"],
+    source_line: 2
+  }, {
+    action: "putself",
+    line: 3,
+    params: [],
+    source_line: 5
+  }, {
+    action: "putstring",
+    line: 4,
+    params: ["bar"],
+    source_line: 5
+  }, {
+    action: "def_method",
+    line: 5,
+    params: ["1"],
+    source_line: 5
+  }, {
+    action: "putself",
+    line: 6,
+    params: [],
+    source_line: 10
+  }, {
+    action: "putstring",
+    line: 7,
+    params: ["baz"],
+    source_line: 10
+  }, {
+    action: "def_method",
+    line: 8,
+    params: ["1"],
+    source_line: 10
+  }, {
+    action: "putobject",
+    line: 9,
+    params: ["0"],
+    source_line: 15
+  }, {
+    action: "setlocal",
+    line: 10,
+    params: ["0", "0"],
+    source_line: 15
+  }, {
+    action: "pop",
+    line: 11,
+    params: [],
+    source_line: 15
+  }, {
+    action: "putself",
+    line: 12,
+    params: [],
+    source_line: 16
+  }, {
+    action: "putobject",
+    line: 13,
+    params: ["100"],
+    source_line: 16
+  }, {
+    action: "send",
+    line: 14,
+    params: ["baz", "1", "block:2", "&{[][0]}"],
+    source_line: 16
+  }, {
+    action: "pop",
+    line: 15,
+    params: [],
+    source_line: 16
+  }, {
+    action: "getlocal",
+    line: 16,
+    params: ["0", "0"],
+    source_line: 19
+  }, {
+    action: "pop",
+    line: 17,
+    params: [],
+    source_line: 19
+  }, {
+    action: "putself",
+    line: 18,
+    params: [],
+    source_line: 21
+  }, {
+    action: "def_class",
+    line: 19,
+    params: ["class:Foo"],
+    source_line: 21
+  }, {
+    action: "pop",
+    line: 20,
+    params: [],
+    source_line: 21
+  }, {
+    action: "putself",
+    line: 21,
+    params: [],
+    source_line: 26
+  }, {
+    action: "def_class",
+    line: 22,
+    params: ["module:Baz"],
+    source_line: 26
+  }, {
+    action: "pop",
+    line: 23,
+    params: [],
+    source_line: 26
+  }, {
+    action: "getconstant",
+    line: 24,
+    params: ["Baz", "true"],
+    source_line: 33
+  }, {
+    action: "getconstant",
+    line: 25,
+    params: ["Bar", "false"],
+    source_line: 33
+  }, {
+    action: "send",
+    line: 26,
+    params: ["new", "0", "", "&{[][]}"],
+    source_line: 33
+  }, {
+    action: "send",
+    line: 27,
+    params: ["bar", "0", "", "&{[][]}"],
+    source_line: 33
+  }, {
+    action: "getlocal",
+    line: 28,
+    params: ["0", "0"],
+    source_line: 33
+  }, {
+    action: "send",
+    line: 29,
+    params: ["+", "1", "", "&{[][]}"],
+    source_line: 33
+  }, {
+    action: "pop",
+    line: 30,
+    params: [],
+    source_line: 33
+  }, {
+    action: "leave",
+    line: 31,
+    params: [],
+    source_line: 33
+  }],
+  name: "ProgramStart",
+  type: "ProgramStart"
+}]`},
+
 		{`require 'ripper'
 Ripper.instruction("
 	def bar(block)
@@ -396,11 +1633,354 @@ Ripper.instruction("
 	foo do
 		10
 	end
-").to_s`, `[{ arg_set: { names: [], types: [] }, arg_types: { names: ["block"], types: [0] }, instructions: [{ action: "getlocal", line: 0, params: ["0", "0"], source_line: 3 }, { action: "send", line: 1, params: ["call", "0", "", "&{[] []}"], source_line: 3 }, { action: "getblock", line: 2, params: [], source_line: 3 }, { action: "send", line: 3, params: ["call", "0", "", "&{[] []}"], source_line: 3 }, { action: "send", line: 4, params: ["+", "1", "", "&{[] []}"], source_line: 3 }, { action: "leave", line: 5, params: [], source_line: 2 }], name: "bar", type: "Def" }, { arg_set: { names: [], types: [] }, arg_types: { names: ["block"], types: [0] }, instructions: [{ action: "getlocal", line: 0, params: ["0", "0"], source_line: 3 }, { action: "send", line: 1, params: ["call", "0", "", "&{[] []}"], source_line: 3 }, { action: "getblock", line: 2, params: [], source_line: 3 }, { action: "send", line: 3, params: ["call", "0", "", "&{[] []}"], source_line: 3 }, { action: "send", line: 4, params: ["+", "1", "", "&{[] []}"], source_line: 3 }, { action: "leave", line: 5, params: [], source_line: 2 }], name: "bar", type: "Def" }, { instructions: [{ action: "putobject", line: 0, params: ["20"], source_line: 8 }, { action: "leave", line: 1, params: [], source_line: 7 }], name: "0", type: "Block" }, { instructions: [{ action: "putobject", line: 0, params: ["20"], source_line: 8 }, { action: "leave", line: 1, params: [], source_line: 7 }], name: "0", type: "Block" }, { arg_set: { names: [""], types: [0] }, arg_types: { names: [], types: [] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 7 }, { action: "getblock", line: 1, params: [], source_line: 7 }, { action: "send", line: 2, params: ["bar", "1", "block:0", "&{[] [0]}"], source_line: 7 }, { action: "leave", line: 3, params: [], source_line: 6 }], name: "foo", type: "Def" }, { arg_set: { names: [""], types: [0] }, arg_types: { names: [], types: [] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 7 }, { action: "getblock", line: 1, params: [], source_line: 7 }, { action: "send", line: 2, params: ["bar", "1", "block:0", "&{[] [0]}"], source_line: 7 }, { action: "leave", line: 3, params: [], source_line: 6 }], name: "foo", type: "Def" }, { instructions: [{ action: "putobject", line: 0, params: ["10"], source_line: 13 }, { action: "leave", line: 1, params: [], source_line: 12 }], name: "1", type: "Block" }, { instructions: [{ action: "putobject", line: 0, params: ["10"], source_line: 13 }, { action: "leave", line: 1, params: [], source_line: 12 }], name: "1", type: "Block" }, { arg_set: { names: [], types: [] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 2 }, { action: "putstring", line: 1, params: ["bar"], source_line: 2 }, { action: "def_method", line: 2, params: ["1"], source_line: 2 }, { action: "putself", line: 3, params: [], source_line: 6 }, { action: "putstring", line: 4, params: ["foo"], source_line: 6 }, { action: "def_method", line: 5, params: ["0"], source_line: 6 }, { action: "putself", line: 6, params: [], source_line: 12 }, { action: "send", line: 7, params: ["foo", "0", "block:1", "&{[] []}"], source_line: 12 }, { action: "pop", line: 8, params: [], source_line: 12 }, { action: "leave", line: 9, params: [], source_line: 12 }], name: "ProgramStart", type: "ProgramStart" }, { arg_set: { names: [], types: [] }, instructions: [{ action: "putself", line: 0, params: [], source_line: 2 }, { action: "putstring", line: 1, params: ["bar"], source_line: 2 }, { action: "def_method", line: 2, params: ["1"], source_line: 2 }, { action: "putself", line: 3, params: [], source_line: 6 }, { action: "putstring", line: 4, params: ["foo"], source_line: 6 }, { action: "def_method", line: 5, params: ["0"], source_line: 6 }, { action: "putself", line: 6, params: [], source_line: 12 }, { action: "send", line: 7, params: ["foo", "0", "block:1", "&{[] []}"], source_line: 12 }, { action: "pop", line: 8, params: [], source_line: 12 }, { action: "leave", line: 9, params: [], source_line: 12 }], name: "ProgramStart", type: "ProgramStart" }]`},
+").to_s`, `
+[{
+  arg_set: {
+    names: [],
+    types: []
+  },
+  arg_types: {
+    names: ["block"],
+    types: [0]
+  },
+  instructions: [{
+    action: "getlocal",
+    line: 0,
+    params: ["0", "0"],
+    source_line: 3
+  }, {
+    action: "send",
+    line: 1,
+    params: ["call", "0", "", "&{[][]}"],
+    source_line: 3
+  }, {
+    action: "getblock",
+    line: 2,
+    params: [],
+    source_line: 3
+  }, {
+    action: "send",
+    line: 3,
+    params: ["call", "0", "", "&{[][]}"],
+    source_line: 3
+  }, {
+    action: "send",
+    line: 4,
+    params: ["+", "1", "", "&{[][]}"],
+    source_line: 3
+  }, {
+    action: "leave",
+    line: 5,
+    params: [],
+    source_line: 2
+  }],
+  name: "bar",
+  type: "Def"
+}, {
+  arg_set: {
+    names: [],
+    types: []
+  },
+  arg_types: {
+    names: ["block"],
+    types: [0]
+  },
+  instructions: [{
+    action: "getlocal",
+    line: 0,
+    params: ["0", "0"],
+    source_line: 3
+  }, {
+    action: "send",
+    line: 1,
+    params: ["call", "0", "", "&{[][]}"],
+    source_line: 3
+  }, {
+    action: "getblock",
+    line: 2,
+    params: [],
+    source_line: 3
+  }, {
+    action: "send",
+    line: 3,
+    params: ["call", "0", "", "&{[][]}"],
+    source_line: 3
+  }, {
+    action: "send",
+    line: 4,
+    params: ["+", "1", "", "&{[][]}"],
+    source_line: 3
+  }, {
+    action: "leave",
+    line: 5,
+    params: [],
+    source_line: 2
+  }],
+  name: "bar",
+  type: "Def"
+}, {
+  arg_types: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "putobject",
+    line: 0,
+    params: ["20"],
+    source_line: 8
+  }, {
+    action: "leave",
+    line: 1,
+    params: [],
+    source_line: 7
+  }],
+  name: "0",
+  type: "Block"
+}, {
+  arg_types: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "putobject",
+    line: 0,
+    params: ["20"],
+    source_line: 8
+  }, {
+    action: "leave",
+    line: 1,
+    params: [],
+    source_line: 7
+  }],
+  name: "0",
+  type: "Block"
+}, {
+  arg_set: {
+    names: [""],
+    types: [0]
+  },
+  arg_types: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 7
+  }, {
+    action: "getblock",
+    line: 1,
+    params: [],
+    source_line: 7
+  }, {
+    action: "send",
+    line: 2,
+    params: ["bar", "1", "block:0", "&{[][0]}"],
+    source_line: 7
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 6
+  }],
+  name: "foo",
+  type: "Def"
+}, {
+  arg_set: {
+    names: [""],
+    types: [0]
+  },
+  arg_types: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 7
+  }, {
+    action: "getblock",
+    line: 1,
+    params: [],
+    source_line: 7
+  }, {
+    action: "send",
+    line: 2,
+    params: ["bar", "1", "block:0", "&{[][0]}"],
+    source_line: 7
+  }, {
+    action: "leave",
+    line: 3,
+    params: [],
+    source_line: 6
+  }],
+  name: "foo",
+  type: "Def"
+}, {
+  arg_types: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "putobject",
+    line: 0,
+    params: ["10"],
+    source_line: 13
+  }, {
+    action: "leave",
+    line: 1,
+    params: [],
+    source_line: 12
+  }],
+  name: "1",
+  type: "Block"
+}, {
+  arg_types: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "putobject",
+    line: 0,
+    params: ["10"],
+    source_line: 13
+  }, {
+    action: "leave",
+    line: 1,
+    params: [],
+    source_line: 12
+  }],
+  name: "1",
+  type: "Block"
+}, {
+  arg_set: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 2
+  }, {
+    action: "putstring",
+    line: 1,
+    params: ["bar"],
+    source_line: 2
+  }, {
+    action: "def_method",
+    line: 2,
+    params: ["1"],
+    source_line: 2
+  }, {
+    action: "putself",
+    line: 3,
+    params: [],
+    source_line: 6
+  }, {
+    action: "putstring",
+    line: 4,
+    params: ["foo"],
+    source_line: 6
+  }, {
+    action: "def_method",
+    line: 5,
+    params: ["0"],
+    source_line: 6
+  }, {
+    action: "putself",
+    line: 6,
+    params: [],
+    source_line: 12
+  }, {
+    action: "send",
+    line: 7,
+    params: ["foo", "0", "block:1", "&{[][]}"],
+    source_line: 12
+  }, {
+    action: "pop",
+    line: 8,
+    params: [],
+    source_line: 12
+  }, {
+    action: "leave",
+    line: 9,
+    params: [],
+    source_line: 12
+  }],
+  name: "ProgramStart",
+  type: "ProgramStart"
+}, {
+  arg_set: {
+    names: [],
+    types: []
+  },
+  instructions: [{
+    action: "putself",
+    line: 0,
+    params: [],
+    source_line: 2
+  }, {
+    action: "putstring",
+    line: 1,
+    params: ["bar"],
+    source_line: 2
+  }, {
+    action: "def_method",
+    line: 2,
+    params: ["1"],
+    source_line: 2
+  }, {
+    action: "putself",
+    line: 3,
+    params: [],
+    source_line: 6
+  }, {
+    action: "putstring",
+    line: 4,
+    params: ["foo"],
+    source_line: 6
+  }, {
+    action: "def_method",
+    line: 5,
+    params: ["0"],
+    source_line: 6
+  }, {
+    action: "putself",
+    line: 6,
+    params: [],
+    source_line: 12
+  }, {
+    action: "send",
+    line: 7,
+    params: ["foo", "0", "block:1", "&{[][]}"],
+    source_line: 12
+  }, {
+    action: "pop",
+    line: 8,
+    params: [],
+    source_line: 12
+  }, {
+    action: "leave",
+    line: 9,
+    params: [],
+    source_line: 12
+  }],
+  name: "ProgramStart",
+  type: "ProgramStart"
+}]`},
 	}
 	for i, tt := range tests {
 		evaluated := vm.ExecAndReturn(t, tt.input)
-		vm.VerifyExpected(t, i, evaluated, tt.expected)
+
+		result := removeRedundantSpaces(evaluated.Value().(string))
+		expected := removeRedundantSpaces(tt.expected)
+
+		if result != expected {
+			t.Fatalf("Expect:\n%s. Got:\n%s\nAt case %d", expected, result, i)
+		}
 	}
 }
 
@@ -440,3 +2020,10 @@ func checkErrorMsg(t *testing.T, index int, evaluated Object, expectedErrMsg str
 		t.Fatalf("At test case %d: Expect error message to be:\n  %s. got: \n%s", index, expectedErrMsg, err.Message())
 	}
 }
+
+func removeRedundantSpaces(str string) string {
+	space := regexp.MustCompile(`\s`)
+	newline := regexp.MustCompile(`\n`)
+	str = space.ReplaceAllString(str, "")
+	return newline.ReplaceAllString(str, "")
+}
diff --git a/vm/class_test.go b/vm/class_test.go
index 797e9f6..c017b8b 100644
--- a/vm/class_test.go
+++ b/vm/class_test.go
@@ -556,6 +556,124 @@ func TestGeneralAssignmentByOperation(t *testing.T) {
 
 // Method tests
 
+func TestDefineMethod(t *testing.T) {
+	tests := []struct {
+		input    string
+		expected interface{}
+	}{
+		{`
+		class C
+ 		  ["hi", "hola"].each do |method_name|
+				define_method method_name do
+				end
+			end
+		end
+		C.new.methods.first(2) == ["hi", "hola"]
+		`, true},
+		{`
+		class C
+		  define_method :ten do
+				10
+		  end
+		end
+		C.new.ten
+		`, 10},
+		{`
+		class C
+		  define_method :plus_1 do |num|
+				num + 1
+		  end
+		end
+		C.new.plus_1(1)
+		`, 2},
+		{`
+		Object.define_method :plus_1 do |num|
+			num + 1
+		end
+		plus_1(1)
+		`, 2},
+	}
+	for i, tt := range tests {
+		v := initTestVM()
+		evaluated := v.testEval(t, tt.input, getFilename())
+		VerifyExpected(t, i, evaluated, tt.expected)
+		v.checkCFP(t, i, 0)
+		v.checkSP(t, i, 1)
+	}
+}
+
+func TestDefineMethodFail(t *testing.T) {
+	testsFail := []errorTestCase{
+		{`Object.define_method`, "ArgumentError: Expect 1 argument(s). got: 0", 1},
+		{`Object.define_method :foo`, "ArgumentError: can't define a method without a block", 1},
+	}
+
+	for i, tt := range testsFail {
+		v := initTestVM()
+		evaluated := v.testEval(t, tt.input, getFilename())
+		checkErrorMsg(t, i, evaluated, tt.expected)
+		v.checkCFP(t, i, tt.expectedCFP)
+		v.checkSP(t, i, 1)
+	}
+}
+
+func TestDefineSingletonMethod(t *testing.T) {
+	tests := []struct {
+		input    string
+		expected interface{}
+	}{
+		{`
+		class C
+		end
+		
+		C.define_singleton_method :ten do
+			10
+        end
+		C.ten
+		`, 10},
+		{`
+		s1 = "String"
+		s2 = s1.dup
+		s1.define_singleton_method :ten do
+			10
+		end
+		s2.define_singleton_method :ten do
+			20
+		end
+		s1.ten
+		`, 10},
+		{`
+		class C; end
+		C.define_singleton_method :plus_1 do |num|
+			num + 1
+		end
+		C.plus_1(1)
+		`, 2},
+	}
+	for i, tt := range tests {
+		v := initTestVM()
+		evaluated := v.testEval(t, tt.input, getFilename())
+		VerifyExpected(t, i, evaluated, tt.expected)
+		v.checkCFP(t, i, 0)
+		v.checkSP(t, i, 1)
+	}
+}
+
+func TestDefineSingletonMethodFail(t *testing.T) {
+	testsFail := []errorTestCase{
+		{`Object.define_singleton_method`, "ArgumentError: Expect 1 argument(s). got: 0", 1},
+		{`Object.define_singleton_method :foo`, "ArgumentError: can't define a method without a block", 1},
+	}
+
+	for i, tt := range testsFail {
+		v := initTestVM()
+		evaluated := v.testEval(t, tt.input, getFilename())
+		checkErrorMsg(t, i, evaluated, tt.expected)
+		v.checkCFP(t, i, tt.expectedCFP)
+		v.checkSP(t, i, 1)
+	}
+}
+
 func TestMethodsMethod(t *testing.T) {
 	tests := []struct {
 		input    string
@@ -1702,15 +1820,25 @@ func TestInstanceEvalMethod(t *testing.T) {
 		  @secret
 		end
 `, 99},
+		// below 2 cases test 'def' statement with instance_eval
+		{`
+		string = "String"
+		string.instance_eval do
+		  def reverse
+				self
+		  end
+		end
+		string.reverse
+`, "String"},
 		{`
 		string = "String"
 		string.instance_eval do
-		  def new_method
-			self.reverse
+		  def reverse
+				self
 		  end
 		end
-		string.new_method
-`, "gnirtS"},
+		"Foo".reverse
+`, "ooF"},
 		{`"a".instance_eval`, "a"},
 		{`"a".instance_eval do end`, "a"},
 		{`
diff --git a/vm/statement_test.go b/vm/statement_test.go
index 20d21a7..f8d0cc8 100644
--- a/vm/statement_test.go
+++ b/vm/statement_test.go
@@ -135,70 +135,87 @@ func TestDefStatement(t *testing.T) {
 	}{
 		{`
 		def foo
-		  10
+		 10
 		end
-
+		
 		foo
 		`, 10},
 		{`
 		def foo(x)
-		  x + 10
+		 x + 10
 		end
-
+		
 		foo(10)
 		`, 20},
 		{`
 		def foo(x = 100, y=10)
-		  x + y
+		 x + y
 		end
-
+		
 		foo
 		`, 110},
 		{`
 		def foo(x = 100, y=10)
-		  x + y
+		 x + y
 		end
-
+		
 		foo(20)
 		`, 30},
 		{`
 		def foo(x, y=10)
-		  x + y
+		 x + y
 		end
-
+		
 		foo(100)
 		`, 110},
 		{`
 		def foo(x=10, y=11, z=12)
-		  x + y + z
+		 x + y + z
 		end
-
+		
 		foo(10, 20)
 		`, 42},
 		{`
 		class Foo; end
-
+		
 		def Foo.foo
-		  10
+		 10
 		end
-
+		
 		Foo.foo
 		`, 10},
 		{`
 		class Foo
-		  def ten
-		    10
-		  end
+		 def ten
+		   10
+		 end
 		end
-
+		
 		f1 = Foo.new
 		f2 = Foo.new
-
+		
 		def f1.ten
-		  20
+		 20
 		end
-
+		
 		f2.ten + f1.ten
+		`, 30},
+		{`
+		class Foo
+		end
+
+		f = Foo.new
+
+		def f.ten
+		  10
+		end
+
+		def f.twenty
+			20
+		end
+
+		f.ten + f.twenty
+
 		`, 30},
 		{`
 		a = 1