From ec62aa4e8eca0335b6db94db3280b7673ab65257 Mon Sep 17 00:00:00 2001 From: Eleanor McHugh Date: Sat, 28 Nov 2009 01:53:20 +0000 Subject: [PATCH] Removed dependency on khash library by moving all hashes to Go's map type. Also expanded remaining macros so the codebase is now much more verbose, ready for refactoring. --- README.rdoc | 1 - vm/array.go | 72 +++++++------ vm/block.go | 47 ++++---- vm/call.go | 33 +++--- vm/class.go | 66 ++++++------ vm/compiler.go | 87 +++++++++++++-- vm/error.go | 57 +++++----- vm/grammar.go | 245 ++++++++++++++++++++--------------------- vm/hash.go | 52 ++++----- vm/internal.go | 64 ----------- vm/kernel.go | 41 ++++--- vm/number.go | 25 +++-- vm/object.go | 46 ++++---- vm/primitive.go | 13 ++- vm/proc.go | 1 - vm/range.go | 11 +- vm/regexp.go | 21 ++-- vm/string.go | 46 ++++---- vm/tr.go | 102 ++++++------------ vm/vendor/khash.go | 264 --------------------------------------------- vm/vm.go | 148 ++++++++++++++++--------- 21 files changed, 591 insertions(+), 851 deletions(-) delete mode 100644 vm/internal.go delete mode 100644 vm/vendor/khash.go diff --git a/README.rdoc b/README.rdoc index ccebccf..c0d7c25 100644 --- a/README.rdoc +++ b/README.rdoc @@ -41,7 +41,6 @@ Inspired by: * tinypy, http://tinypy.org * potion, http://github.com/why/potion -kvec.h and khash.h (c) 2008, by Attractive Chaos GC Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers PCRE Copyright (c) 1997-2008 University of Cambridge released under BSD license diff --git a/vm/array.go b/vm/array.go index 75ef460..26019d4 100644 --- a/vm/array.go +++ b/vm/array.go @@ -1,62 +1,72 @@ import ( "tr"; - "internal"; "container/vector"; ) type Array struct { type TR_T; class RubyObject; - ivars *map[string] RubyObject; - kv *Vector; + ivars map[string] RubyObject; + values Vector; } -func newArray(vm *RubyVM) RubyObject { - return Array{type: TR_T_Array, class: vm.classes[TR_T_Array], ivars: kh_init(RubyObject), kv: Vector.New(0)}; +func (vm *RubyVM) newArray() RubyObject { + return Array{type: TR_T_Array, class: vm.classes[TR_T_Array], ivars: make(map[string] RubyObject), values: Vector.New(0)}; } // Uses variadic ... parameter which replaces the mechanism used by stdarg.h -func newArray2(vm *RubyVM, argc int, ...) RubyObject { - a := newArray(vm); +func (vm *RubyVM) newArray2(args []RubyObject) RubyObject { + a := vm.newArray(); va_list argp; - int i; va_start(argp, argc); - for (i = 0; i < argc; ++i) a.kv.Push(va_arg(argp, *RubyObject)); + for i := 0; i < argc; ++i { a.Push(va_arg(argp, *RubyObject)); } va_end(argp); return a; } -func newArray3(vm *RubyVM, argc int, items []RubyObject) RubyObject { - a := newArray(vm); - for i := 0; i < argc; ++i { a.kv.Push(items[i]) }; +func (vm *RubyVM) newArray3(argc int, items []RubyObject) RubyObject { + a := vm.newArray(); + for i := 0; i < argc; ++i { a.Push(items[i]) }; return a; } -func (self *Array) push(vm *RubyVM, x *RubyObject) *RubyObject { - self.kv.Push(x); +func (self *Array) Push(x *RubyObject) *RubyObject { + self.values.Push(x); return x; } -func (self *Array) at2index(vm *RubyVM, at *RubyObject) int { - int i = TR_FIX2INT(at); - if (i < 0) i = self.kv.Len() + i; +func (self *Array) at2index(at *RubyObject) int { + i := TR_FIX2INT(at); + if i < 0 { i = self.kv.Len() + i; } return i; } -func (self *Array) at(vm *RubyVM, at *RubyObject) *RubyObject { +func (self *Array) At(at *RubyObject) *RubyObject { i := self.at2index(vm, at); - if i < 0 || i >= self.kv.Len() { return TR_NIL; } - return self.kv.At(i); + if i < 0 || i >= self.values.Len() { + return TR_NIL; + } else { + return self.At(i); + } } func (self *Array) set(vm *RubyVM, at, x *RubyObject) *RubyObject { i := self.at2index(vm, at); switch { - case i < 0: tr_raise(IndexError, "index %d out of array", i); - case i < self.Len(): self.Set(at, x); - case i > self.Len(): self.AppendVector(Vector.New(at - self.Len())); - fallthrough; - case i == self.Len(): self.Push(x); + case i < 0: + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cIndexError, tr_sprintf(vm, "index %d out of array", i)); + return TR_UNDEF; + + case i < self.Len(): + self.Set(at, x); + + case i > self.Len(): + self.AppendVector(Vector.New(at - self.Len())); + fallthrough; + + case i == self.Len(): + self.Push(x); } return x; } @@ -66,10 +76,10 @@ func (self *Array) length(vm *RubyVM) RubyObject { } void TrArray_init(vm *RubyVM) { - c := vm.classes[TR_T_Array] = Object_const_set(vm, vm.self, tr_intern(Array), newClass(vm, tr_intern(Array), vm.classes[TR_T_Object])); - tr_def(c, "length", TrArray_length, 0); - tr_def(c, "size", TrArray_length, 0); - tr_def(c, "<<", TrArray_push, 1); - tr_def(c, "[]", TrArray_at, 1); - tr_def(c, "[]=", TrArray_set, 2); + c := vm.classes[TR_T_Array] = Object_const_set(vm, vm.self, TrSymbol_new(vm, Array), newClass(vm, TrSymbol_new(vm, Array), vm.classes[TR_T_Object])); + c.add_method(vm, TrSymbol_new(vm, "length"), newMethod(vm, (TrFunc *)TrArray_length, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "size"), newMethod(vm, (TrFunc *)TrArray_length, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "<<"), newMethod(vm, (TrFunc *)Push, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "[]"), newMethod(vm, (TrFunc *)TrArray_at, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "[]="), newMethod(vm, (TrFunc *)TrArray_set, TR_NIL, 2)); } \ No newline at end of file diff --git a/vm/block.go b/vm/block.go index 22378bd..31afe53 100644 --- a/vm/block.go +++ b/vm/block.go @@ -1,45 +1,44 @@ import ( "tr"; "opcode"; - "internal"; "fmt"; "container/vector"; ) type Block struct { // static - k *Vector; - strings *StringVector; - locals *Vector; - upvals *Vector; - code *Vector; - defaults *IntVector; + k Vector; + strings StringVector; + locals Vector; + upvals Vector; + code Vector; + defaults Vector; blocks []Block; regc int; argc int; arg_splat int; - filename *RubyObject; + filename RubyObject; line int; parent *Block; // dynamic - sites CallSiteVector; + sites Vector; } - -func newBlock(compiler *Compiler, parent *Block) *Block { - block = new(Block); - block.defaults = IntVector.New(0) - block.strings = StringVector.New(0) - block.filename = compiler.filename; - block.line = 1; - block.regc = 0; - block.argc = 0; - block.parent = parent; - block.k = Vector.New(0); - block.code = Vector.New(0); - block.locals = Vector.New(0); - block.sites = Vector.New(0); - return block; +func (compiler *Compiler) newBlock(parent *Block) *Block { + return Block{ parent: parent, + k: Vector.New(0), + strings: StringVector.new(0), + locals: Vector.new(0), + upvals: Vector.new(0), + code: Vector.new(0), + defaults: Vector.new(0), + sites: Vector.new(0), + blocks: Vector.new(0), + regc: 0, + argc: 0, + line: 1, + filename: compiler.filename, + } } func (b *Block) dump(vm *RubyVM, level int) RubyObject { diff --git a/vm/call.go b/vm/call.go index f58bfd1..b0f4b72 100644 --- a/vm/call.go +++ b/vm/call.go @@ -4,18 +4,6 @@ import ( "bytes"; ) -type Frame struct { - closure *Closure; - method *Method; // current called method - stack *RubyObject; - upvals *RubyObject; - self *RubyObject; - class *RubyObject; - filename *RubyObject; - line size_t; - previous *Frame; -} - func (self *Method) call(vm *RubyVM, receiver *RubyObject, argc int, args []RubyObject, splat int, closure *Closure) RubyObject { if TR_IMMEDIATE(receiver) { receiver_class := vm.classes[Object_type(vm, receiver)]; @@ -25,9 +13,13 @@ func (self *Method) call(vm *RubyVM, receiver *RubyObject, argc int, args []Ruby // push a frame vm.cf++; - if vm.cf >= TR_MAX_FRAMES { tr_raise(SystemStackError, "Stack overflow"); } + if vm.cf >= TR_MAX_FRAMES { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cSystemStackError, tr_sprintf(vm, "Stack overflow")); + return TR_UNDEF; + } - frame := Frame{previous: vm.frame, method: nil, filename: nil, line: 0, self: receiver, class: receiver_class, closure: closure} + frame := newFrame(receiver, receiver_class, closure); if vm.cf == 0 { vm.top_frame = frame; } vm.frame = frame; vm.throw_reason = vm.throw_value = 0; @@ -42,7 +34,7 @@ func (self *Method) call(vm *RubyVM, receiver *RubyObject, argc int, args []Ruby splatedn := splated.kv.Len(); new_args := make([]OBJ, argc) memcpy(new_args, args, sizeof(OBJ) * (argc - 1)); - memcpy(new_args + argc - 1, &splated.kv.At(0), sizeof(OBJ) * splatedn); + memcpy(new_args + argc - 1, &splated.values.At(0), sizeof(OBJ) * splatedn); argc += splatedn-1; args = new_args; } @@ -50,7 +42,11 @@ func (self *Method) call(vm *RubyVM, receiver *RubyObject, argc int, args []Ruby if (m.arity == -1) { result := function(vm, receiver, argc, args); } else { - if method.arity != argc { tr_raise(ArgumentError, "Expected %d arguments, got %d.", frame.method.arity, argc); } + if method.arity != argc { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cArgumentError, tr_sprintf(vm, "Expected %d arguments, got %d.", frame.method.arity, argc)); + return TR_UNDEF; + } switch argc { case 0: result := function(vm, receiver); case 1: result := function(vm, receiver, args[0]); @@ -63,7 +59,10 @@ func (self *Method) call(vm *RubyVM, receiver *RubyObject, argc int, args []Ruby case 8: result := function(vm, receiver, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); case 9: result := function(vm, receiver, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); case 10: result := function(vm, receiver, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); - default: tr_raise(ArgumentError, "Too much arguments: %d, max is %d for now.", argc, 10); + default: + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cArgumentError, tr_sprintf(vm, "Too many arguments: %d, max is %d for now.", argc, 10)); + return TR_UNDEF; } } diff --git a/vm/class.go b/vm/class.go index d6128e8..86fbfd1 100644 --- a/vm/class.go +++ b/vm/class.go @@ -1,12 +1,11 @@ import ( "tr"; - "internal"; ) type Method struct { type TR_T; class *RubyObject; - ivars *map[string] RubyObject; + ivars map[string] RubyObject; func *TrFunc; data *RubyObject; name *RubyObject; @@ -16,34 +15,29 @@ type Method struct { type Module struct { type TR_T; class *RubyObject; - ivars *map[string] *RubyObject; + ivars map[string] RubyObject; name *RubyObject; super *RubyObject; methods map[string] RubyObject; - meta:1 int; + meta bool; } -type Class struct { - module Module; +func (vm *RubyVM) newModule(name *RubyObject) RubyObject { + return Module{type: TR_T_Module, class: vm.classes[TR_T_Module], ivars: make(map[string] RubyObject), name: name, methods: make(map[string] RubyObject)}; } - -/* included module proxy */ - -func newIModule(vm *RubyVM, module, super *RubyObject) RubyObject { +func (vm *RubyVM) newIncludedModule(module, super *RubyObject) RubyObject { if !module.(Class) && !module.(Module) { vm.throw_reason = TR_THROW_EXCEPTION; vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected " + module)); return TR_UNDEF; } m := Class *(module); - return Module{type: TR_T_Module, class: vm.classes[TR_T_Module], ivars: kh_init(RubyObject), name: m.name, methods: m.methods, super: super}; + return Module{type: TR_T_Module, class: vm.classes[TR_T_Module], ivars: make(map[string] RubyObject), name: m.name, methods: m.methods, super: super}; } -/* module */ - -func newModule(vm *RubyVM, name *RubyObject) RubyObject { - return Module{type: TR_T_Module, class: vm.classes[TR_T_Module], ivars: kh_init(RubyObject), name: name, methods: kh_init(RubyObject), meta: false}; +type Class struct { + module Module; } func (self *Module) instance_method(vm *RubyVM, name *RubyObject) RubyObject { @@ -54,7 +48,7 @@ func (self *Module) instance_method(vm *RubyVM, name *RubyObject) RubyObject { } class := Class *(self); while (class) { - if method := TR_KH_GET(class.methods, name) { return method; } + if method := class.methods[name] { return method; } class = Class *(class).super; } return TR_NIL; @@ -67,8 +61,8 @@ func (self *Module) add_method(vm *RubyVM, name, method *RubyObject) RubyObject return TR_UNDEF; } m := Class *(self); - TR_KH_SET(m.methods, name, method); - Method *(method).name = name; + m.methods[name] = method; + method.name = name; return method; } @@ -83,7 +77,7 @@ func (self *Module) include(vm *RubyVM, module *RubyObject) RubyObject { return TR_UNDEF; } class := Class *(self); - class.super = newIModule(vm, module, class.super); + class.super = vm.newIncludedModule(module, class.super); return module; } @@ -97,18 +91,18 @@ func (self *Module) name(vm *RubyVM) RubyObject { } func TrModule_init(vm *RubyVM) { - c := vm.classes[TR_T_Module] = Object_const_set(vm, vm.self, tr_intern(Module), newClass(vm, tr_intern(Module), vm.classes[TR_T_Object])); - tr_def(c, "name", TrModule_name, 0); - tr_def(c, "include", TrModule_include, 1); - tr_def(c, "instance_method", TrModule_instance_method, 1); - tr_def(c, "alias_method", TrModule_alias_method, 2); - tr_def(c, "to_s", TrModule_name, 0); + c := vm.classes[TR_T_Module] = Object_const_set(vm, vm.self, TrSymbol_new(vm, Module), newClass(vm, TrSymbol_new(vm, Module), vm.classes[TR_T_Object])); + c.add_method(vm, TrSymbol_new(vm, "name"), newMethod(vm, (TrFunc *)TrModule_name, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "include"), newMethod(vm, (TrFunc *)TrModule_include, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "instance_method"), newMethod(vm, (TrFunc *)TrModule_instance_method, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "alias_method"), newMethod(vm, (TrFunc *)TrModule_instance_method, TR_NIL, 2)); + c.add_method(vm, TrSymbol_new(vm, "to_s"), newMethod(vm, (TrFunc *)TrModule_name, TR_NIL, 0)); } /* class */ func newClass(vm *RubyVM, name, super *RubyObject) RubyObject { - c := Class{type: TR_T_Class, class: vm.classes[TR_T_Class], ivars: kh_init(RubyObject), name: name, methods: kh_init(RubyObject), meta: false}; + c := Class{type: TR_T_Class, class: vm.classes[TR_T_Class], ivars: make(map[string] RubyObject), name: name, methods: make(map[string] RubyObject), meta: false}; if !super.(Class) && !super.(Module) { vm.throw_reason = TR_THROW_EXCEPTION; vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected " + super)); @@ -121,7 +115,7 @@ func newClass(vm *RubyVM, name, super *RubyObject) RubyObject { } func (self *Class) allocate(vm *RubyVM) RubyObject { - return Object{type: TR_T_Object, class: vm.classes[TR_T_Object], ivars: kh_init(RubyObject), class: self}; + return Object{type: TR_T_Object, class: vm.classes[TR_T_Object], ivars: make(map[string] RubyObject), class: self}; } func (self *Class) superclass(vm *RubyVM) RubyObject { @@ -143,9 +137,9 @@ func (self *Class) superclass(vm *RubyVM) RubyObject { } func TrClass_init(vm *RubyVM) { - c := vm.classes[TR_T_Class] = Object_const_set(vm, vm.self, tr_intern(Class), newClass(vm, tr_intern(Class), vm.classes[TR_T_Module])); - tr_def(c, "superclass", TrClass_superclass, 0); - tr_def(c, "allocate", TrClass_allocate, 0); + c := vm.classes[TR_T_Class] = Object_const_set(vm, vm.self, TrSymbol_new(vm, Class), newClass(vm, TrSymbol_new(vm, Class), vm.classes[TR_T_Module])); + c.add_method(vm, TrSymbol_new(vm, "superclass"), newMethod(vm, (TrFunc *)TrClass_superclass, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "allocate"), newMethod(vm, (TrFunc *)TrClass_allocate, TR_NIL, 0)); } /* metaclass */ @@ -168,7 +162,7 @@ func newMetaClass(vm *RubyVM, super *RubyObject) RubyObject { vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected " + name)); return TR_UNDEF; } - name := tr_intern(name.ptr); /* symbolize */ + name := TrSymbol_new(vm, name.ptr); /* symbolize */ mc = newClass(vm, name, 0); mc.super = super; mc.meta = 1; @@ -178,7 +172,7 @@ func newMetaClass(vm *RubyVM, super *RubyObject) RubyObject { /* method */ func newMethod(vm *RubyVM, function *TrFunc, data *RubyObject, arity int) RubyObject { - return Method{type: TR_T_Method, class: vm.classes[TR_T_Method], ivars: kh_init(RubyObject), func: function, data: data, arity: arity}; + return Method{type: TR_T_Method, class: vm.classes[TR_T_Method], ivars: make(map[string] RubyObject), func: function, data: data, arity: arity}; } func (self *Method) name(vm *RubyVM) RubyObject { return ((Method *) self).name; } @@ -201,8 +195,8 @@ func (self *Method) dump(vm *RubyVM) RubyObject { } func TrMethod_init(vm *RubyVM) { - c := vm.classes[TR_T_Method] = Object_const_set(vm, vm.self, tr_intern(Method), newClass(vm, tr_intern(Method), vm.classes[TR_T_Object])); - tr_def(c, "name", TrMethod_name, 0); - tr_def(c, "arity", TrMethod_arity, 0); - tr_def(c, "dump", TrMethod_dump, 0); + c := vm.classes[TR_T_Method] = Object_const_set(vm, vm.self, TrSymbol_new(vm, Method), newClass(vm, TrSymbol_new(vm, Method), vm.classes[TR_T_Object])); + c.add_method(vm, TrSymbol_new(vm, "name"), newMethod(vm, (TrFunc *)TrMethod_name, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "arity"), newMethod(vm, (TrFunc *)TrMethod_arity, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "dump"), newMethod(vm, (TrFunc *)TrMethod_dump, TR_NIL, 0)); } \ No newline at end of file diff --git a/vm/compiler.go b/vm/compiler.go index e56d06e..69485c4 100644 --- a/vm/compiler.go +++ b/vm/compiler.go @@ -1,19 +1,64 @@ import ( "tr"; "opcode"; - "internal"; ) // ast node type ASTNode struct { type TR_T; class *RubyObject; - ivars *map[string] RubyObject; + ivars map[string] RubyObject; ntype int; args [3]RubyObject; line size_t; } +// types of nodes in the AST built by the parser +const ( + NODE_ROOT = iota; + NODE_BLOCK; + NODE_VALUE; + NODE_STRING; + NODE_ASSIGN; + NODE_ARG; + NODE_SEND; + NODE_MSG; + NODE_IF; + NODE_UNLESS; + NODE_AND; + NODE_OR; + NODE_WHILE; + NODE_UNTIL; + NODE_BOOL; + NODE_NIL; + NODE_SELF; + NODE_LEAVE; + NODE_RETURN; + NODE_BREAK; + NODE_YIELD; + NODE_DEF; + NODE_METHOD; + NODE_PARAM; + NODE_CLASS; + NODE_MODULE; + NODE_CONST; + NODE_SETCONST; + NODE_ARRAY; + NODE_HASH; + NODE_RANGE; + NODE_GETIVAR; + NODE_SETIVAR; + NODE_GETCVAR; + NODE_SETCVAR; + NODE_GETGLOBAL; + NODE_SETGLOBAL; + NODE_ADD; + NODE_SUB; + NODE_LT; + NODE_NEG; + NODE_NOT; +) + func newASTNode(vm *RubyVM, type int, a, b, c *RubyObject, line size_t) RubyObject { return ASTNode{ntype: type, type: TR_T_NODE, args: {a, b, c}, line: line} } @@ -33,7 +78,7 @@ func newCompiler(vm *RubyVM, filename *string) Compiler * { compiler := new(Compiler); compiler.line = 1; compiler.vm = vm; - compiler.block = newBlock(compiler, 0); + compiler.block = compiler.newBlock(nil); compiler.reg = 0; compiler.node = TR_NIL; compiler.filename = TrString_new2(vm, filename); @@ -101,7 +146,11 @@ func (self *ASTNode) compile(vm *RubyVM, c *Compiler, b *Block, reg int) RubyObj if reg >= b.regc { b.regc = reg + 1; } index++; } - if start_reg != reg { tr_raise(SyntaxError, "Can't create local variable inside Array") } + if start_reg != reg { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cSyntaxError, tr_sprintf(vm, "Can't create local variable inside Array")); + return TR_UNDEF; + } } b.code.Push(MachineOp{OpCode: TR_OP_NEWARRAY, A: reg, B: size}); @@ -120,7 +169,11 @@ func (self *ASTNode) compile(vm *RubyVM, c *Compiler, b *Block, reg int) RubyObj if reg >= b.regc { b.regc = reg + 1; } index++; } - if start_reg != reg { tr_raise(SyntaxError, "Can't create local variable inside Hash") } + if start_reg != reg { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cSyntaxError, tr_sprintf(vm, "Can't create local variable inside Hash")); + return TR_UNDEF; + } } b.code.Push(MachineOp{OpCode: TR_OP_NEWHASH, A: reg, B: size / 2}); @@ -131,7 +184,11 @@ func (self *ASTNode) compile(vm *RubyVM, c *Compiler, b *Block, reg int) RubyObj if next_reg >= b.regc { b.regc = next_reg + 1; } self.args[1].compile(vm, c, b, next_reg); if next_reg >= b.regc { b.regc = next_reg + 1; } - if start_reg != reg { tr_raise(SyntaxError, "Can't create local variable inside Range") } + if start_reg != reg { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cSyntaxError, tr_sprintf(vm, "Can't create local variable inside Range")); + return TR_UNDEF; + } b.code.Push(MachineOp{OpCode: TR_OP_NEWRANGE, A: reg, B: next_reg, C: self.args[2]}); case NODE_ASSIGN: @@ -217,14 +274,18 @@ func (self *ASTNode) compile(vm *RubyVM, c *Compiler, b *Block, reg int) RubyObj if argument.args[1] { argc |= 1 } // splat index++; } - if start_reg != reg { tr_raise(SyntaxError, "Can't create local variable inside arguments") } + if start_reg != reg { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cSyntaxError, tr_sprintf(vm, "Can't create local variable inside arguments")); + return TR_UNDEF; + } } // block blki := 0; blk := nil; if (self.args[2]) { - blk := newBlock(c, b); + blk := c.newBlock(b); blkn := self.args[2]; blki = b.blocks.Len() + 1; blk.argc = 0; @@ -377,14 +438,18 @@ func (self *ASTNode) compile(vm *RubyVM, c *Compiler, b *Block, reg int) RubyObj if reg >= b.regc { b.regc = reg + 1; } index++; } - if start_reg != reg { tr_raise(SyntaxError, "Can't create local variable inside yield") } + if start_reg != reg { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cSyntaxError, tr_sprintf(vm, "Can't create local variable inside yield")); + return TR_UNDEF; + } } b.code.Push(MachineOp{OpCode: TR_OP_YIELD, A: reg, B:argc}); case NODE_DEF: { method := self.args[0]; assert(method.ntype == NODE_METHOD); - blk := newBlock(c, 0); + blk := c.newBlock(nil); blki := b.blocks.Len(); blk_reg := 0; b.blocks.Push(blk); @@ -427,7 +492,7 @@ func (self *ASTNode) compile(vm *RubyVM, c *Compiler, b *Block, reg int) RubyObj } case NODE_CLASS, NODE_MODULE: - blk := newBlock(c, 0); + blk := c.newBlock(nil); blki := b.blocks.Len(); b.blocks.Push(blk); reg = 0; diff --git a/vm/error.go b/vm/error.go index f38f510..d81dd72 100644 --- a/vm/error.go +++ b/vm/error.go @@ -1,7 +1,5 @@ import "tr" -/* Error management stuff */ - /* Exception NoMemoryError ScriptError @@ -33,8 +31,8 @@ import "tr" func TrException_new(vm *RubyVM, class, message *RubyObject) RubyObject { e := Object_alloc(vm, class); - tr_setivar(e, "@message", message); - tr_setivar(e, "@backtrace", TR_NIL); + e.ivars[TrSymbol_new(vm, "@message"] = message; + e.ivars[TrSymbol_new(vm, "@backtrace"] = TR_NIL; return e; } @@ -59,15 +57,15 @@ func TrException_iexception(vm *RubyVM, self *RubyObject, argc int, argv []RubyO } func TrException_message(vm *RubyVM, self *RubyObject) RubyObject { - return tr_getivar(self, "@message"); + return self.ivars[TrSymbol_new(vm, "@message")] || TR_NIL; } func TrException_backtrace(vm *RubyVM, self *RubyObject) RubyObject { - return tr_getivar(self, "@backtrace"); + return self.ivars[TrSymbol_new(vm, "@backtrace")] || TR_NIL; } func TrException_set_backtrace(vm *RubyVM, self, backtrace *RubyObject) RubyObject { - return tr_setivar(self, "@backtrace", backtrace); + self.ivars[TrSymbol_new(vm, "@backtrace")] = backtrace; } func TrException_default_handler(vm *RubyVM, exception *RubyObject) RubyObject { @@ -82,9 +80,8 @@ func TrException_default_handler(vm *RubyVM, exception *RubyObject) RubyObject { vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected " + X)); return TR_UNDEF; } - msg := tr_getivar(exception, "@message"); - backtrace := tr_getivar(exception, "@backtrace"); - + msg := exception.ivars[TrSymbol_new(vm, "@message")] || TR_NIL; + backtrace := exception.ivars[TrSymbol_new(vm, "@backtrace")] || TR_NIL; if !exception_class.name.(String) && !exception_class.name.(Symbol) { vm.throw_reason = TR_THROW_EXCEPTION; vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected " + exception_class.name)); @@ -111,24 +108,24 @@ func TrException_default_handler(vm *RubyVM, exception *RubyObject) RubyObject { } func TrError_init(vm *RubyVM) { - c := vm.cException = tr_defclass("Exception", 0); - tr_metadef(c, "exception", TrException_cexception, -1); - tr_def(c, "exception", TrException_iexception, -1); - tr_def(c, "backtrace", TrException_backtrace, 0); - tr_def(c, "message", TrException_message, 0); - tr_def(c, "to_s", TrException_message, 0); - - vm.cScriptError = tr_defclass("ScriptError", vm.cException); - vm.cSyntaxError = tr_defclass("SyntaxError", vm.cScriptError); - vm.cStandardError = tr_defclass("StandardError", vm.cException); - vm.cArgumentError = tr_defclass("ArgumentError", vm.cStandardError); - vm.cRegexpError = tr_defclass("RegexpError", vm.cStandardError); - vm.cRuntimeError = tr_defclass("RuntimeError", vm.cStandardError); - vm.cTypeError = tr_defclass("TypeError", vm.cStandardError); - vm.cSystemCallError = tr_defclass("SystemCallError", vm.cStandardError); - vm.cIndexError = tr_defclass("IndexError", vm.cStandardError); - vm.cLocalJumpError = tr_defclass("LocalJumpError", vm.cStandardError); - vm.cSystemStackError = tr_defclass("SystemStackError", vm.cStandardError); - vm.cNameError = tr_defclass("NameError", vm.cStandardError); - vm.cNoMethodError = tr_defclass("NoMethodError", vm.cNameError); + c := vm.cException = Object_const_set(vm, vm.self, TrSymbol_new(vm, "Exception"), newClass(vm, TrSymbol_new(vm, "Exception"), 0)); + Object_add_singleton_method(vm, c, TrSymbol_new(vm, "exception"), newMethod(vm, (TrFunc *)TrException_cexception, TR_NIL, -1)); + c.add_method(vm, TrSymbol_new(vm, "exception"), newMethod(vm, (TrFunc *)TrException_iexception, TR_NIL, -1)); + c.add_method(vm, TrSymbol_new(vm, "backtrace"), newMethod(vm, (TrFunc *)TrException_backtrace, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "message"), newMethod(vm, (TrFunc *)TrException_message, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "to_s"), newMethod(vm, (TrFunc *)TrException_message, TR_NIL, 0)); + + vm.cScriptError = Object_const_set(vm, vm.self, TrSymbol_new(vm, "ScriptError"), newClass(vm, TrSymbol_new(vm, "ScriptError"), vm.cException)); + vm.cSyntaxError = Object_const_set(vm, vm.self, TrSymbol_new(vm, "SyntaxError"), newClass(vm, TrSymbol_new(vm, "SyntaxError"), vm.cScriptError)); + vm.cStandardError = Object_const_set(vm, vm.self, TrSymbol_new(vm, "StandardError"), newClass(vm, TrSymbol_new(vm, "StandardError"), vm.cException)); + vm.cArgumentError = Object_const_set(vm, vm.self, TrSymbol_new(vm, "ArgumentError"), newClass(vm, TrSymbol_new(vm, "ArgumentError"), vm.cStandardError)); + vm.cRegexpError = Object_const_set(vm, vm.self, TrSymbol_new(vm, "RegexpError"), newClass(vm, TrSymbol_new(vm, "RegexpError"), vm.cStandardError)); + vm.cRuntimeError = Object_const_set(vm, vm.self, TrSymbol_new(vm, "RuntimeError"), newClass(vm, TrSymbol_new(vm, "RuntimeError"), vm.cStandardError)); + vm.cTypeError = Object_const_set(vm, vm.self, TrSymbol_new(vm, "TypeError"), newClass(vm, TrSymbol_new(vm, "TypeError"), vm.cStandardError)); + vm.cSystemCallError = Object_const_set(vm, vm.self, TrSymbol_new(vm, "SystemCallError"), newClass(vm, TrSymbol_new(vm, "SystemCallError"), vm.cStandardError)); + vm.cIndexError = Object_const_set(vm, vm.self, TrSymbol_new(vm, "IndexError"), newClass(vm, TrSymbol_new(vm, "IndexError"), vm.cStandardError)); + vm.cLocalJumpError = Object_const_set(vm, vm.self, TrSymbol_new(vm, "LocalJumpError"), newClass(vm, TrSymbol_new(vm, "LocalJumpError"), vm.cStandardError)); + vm.cSystemStackError = Object_const_set(vm, vm.self, TrSymbol_new(vm, "SystemStackError"), newClass(vm, TrSymbol_new(vm, "SystemStackError"), vm.cStandardError)); + vm.cNameError = Object_const_set(vm, vm.self, TrSymbol_new(vm, "NameError"), newClass(vm, TrSymbol_new(vm, "NameError"), vm.cStandardError)); + vm.cNoMethodError = Object_const_set(vm, vm.self, TrSymbol_new(vm, "NoMethodError"), newClass(vm, TrSymbol_new(vm, "NoMethodError"), vm.cNameError)); } \ No newline at end of file diff --git a/vm/grammar.go b/vm/grammar.go index bd9d625..fd8e4c8 100644 --- a/vm/grammar.go +++ b/vm/grammar.go @@ -1,7 +1,6 @@ %{ #include #include "tr.h" -#include "internal.h" /*#define YY_DEBUG 1*/ @@ -10,39 +9,38 @@ charbuf *string; sbuf *string; -size_t nbuf; -Compiler *compiler; - -#define YY_INPUT(buf, result, max_size) { \ - int yyc; \ - if (charbuf && *charbuf != '\0') \ - yyc= *charbuf++; \ - else \ - yyc= EOF; \ - result= (EOF == yyc) ? 0 : (*(buf)= yyc, 1); \ +nbuf size_t; +compiler *Compiler; + +#define YY_INPUT(buf, result, max_size) { \ + yyc int; \ + if charbuf && *charbuf != '\0' { \ + yyc= *charbuf++; \ + } else { \ + yyc= EOF; \ + } \ + if EOF == yyc { \ + result := 0; \ + } else { \ + (*(buf)= yyc, 1); \ + } \ } -/* TODO grow buffer */ -#define STRING_MAX 4096 -#define STRING_START sbuf = make([]byte, STRING_MAX); nbuf = 0 -#define STRING_PUSH(P,L) \ - assert(nbuf + (L) < 4096); \ - memcpy(sbuf + nbuf, P, sizeof(char) * L); \ - nbuf += (L) - +// TODO grow buffer +#define STRING_START sbuf = make([]byte, 4096); nbuf = 0 %} -Root = s:Stmts EOF { compiler.node = NODE(ROOT, s) } +Root = s:Stmts EOF { compiler.node = newASTNode(compiler.vm, NODE_ROOT, s, 0, 0, compiler.line) } Stmts = SEP* - - head:Stmt Comment? { head = NODES(head) } - ( SEP - tail:Stmt Comment? { PUSH_NODE(head, tail) } + - head:Stmt Comment? { head = compiler.vm.newArray2(1, head) } + ( SEP - tail:Stmt Comment? { head.Push(tail) } | SEP - Comment )* SEP? { $$ = head } - | SEP+ { $$ = NODES_N(0) } + | SEP+ { $$ = compiler.vm.newArray2(0) } OptStmts = Stmts - | - SEP? { $$ = NODES_N(0) } + | - SEP? { $$ = compiler.vm.newArray2(0) } Stmt = While | Until @@ -69,16 +67,16 @@ Comment = - '#' (!EOL .)* Call = { block = rcv = 0 } ( rcv:Value '.' - )? ( rmsg:Message '.' { rcv = NODE2(SEND, rcv, rmsg) } + )? ( rmsg:Message '.' { rcv = newASTNode(compiler.vm, NODE_SEND, rcv, rmsg, 0, compiler.line) } )* msg:Message - - block:Block? { $$ = NODE3(SEND, rcv, msg, block) } + - block:Block? { $$ = newASTNode(compiler.vm, NODE_SEND, rcv, msg, block, compiler.line) } # TODO refactor head part w/ Call maybe eh? AsgnCall = { rcv = 0 } ( rcv:Value '.' - )? ( rmsg:Message '.' { rcv = NODE2(SEND, rcv, rmsg) } + )? ( rmsg:Message '.' { rcv = newASTNode(compiler.vm, NODE_SEND, rcv, rmsg, 0, compiler.line) } )* msg:ID - asg:ASSIGN - - val:Stmt { vm = RubyVM *(yyvm); $$ = NODE2(SEND, rcv, NODE2(MSG, SYMCAT(msg, asg), NODES(NODE(ARG, val)))) } + - val:Stmt { vm = RubyVM *(yyvm); $$ = newASTNode(compiler.vm, NODE_SEND, rcv, newASTNode(compiler.vm, NODE_MSG, TrSymbol_new(vm, TrString *(msg).ptr, TrString *(asg).ptr), compiler.vm.newArray2(1, newASTNode(compiler.vm, NODE_ARG, val, 0, 0, compiler.line)), 0, compiler.line), 0, compiler.line) } Receiver = ( { rcv = 0 } rcv:Call @@ -86,142 +84,147 @@ Receiver = ( { rcv = 0 } ) { $$ = rcv } SpecCall = rcv:Receiver '[' args:Args ']' - - ASSIGN - val:Stmt { PUSH_NODE(args, NODE(ARG, val)); $$ = NODE2(SEND, rcv, NODE2(MSG, TrSymbol_new(yyvm, "[]="), args)) } - | rcv:Receiver '[' args:Args ']' { $$ = NODE2(SEND, rcv, NODE2(MSG, TrSymbol_new(yyvm, "[]"), args)) } + - ASSIGN - val:Stmt { args.Push(newASTNode(compiler.vm, NODE_ARG, val, 0, 0, compiler.line)); $$ = newASTNode(compiler.vm, NODE_SEND, rcv, newASTNode(compiler.vm, NODE_MSG, TrSymbol_new(yyvm, "[]="), args, 0, compiler.line), 0, compiler.line) } + | rcv:Receiver '[' args:Args ']' { $$ = newASTNode(compiler.vm, NODE_SEND, rcv, newASTNode(compiler.vm, NODE_MSG, TrSymbol_new(yyvm, "[]"), args, 0, compiler.line), 0, compiler.line) } BinOp = ( rcv:SpecCall | rcv:Receiver ) - ( - '&&' - arg:Expr { $$ = NODE2(AND, rcv, arg) } - | '||' - arg:Expr { $$ = NODE2(OR, rcv, arg) } - | '+' - arg:Expr { $$ = NODE2(ADD, rcv, arg) } - | '-' - arg:Expr { $$ = NODE2(SUB, rcv, arg) } - | '<' - arg:Expr { $$ = NODE2(LT, rcv, arg) } - | op:BINOP - arg:Expr { $$ = NODE2(SEND, rcv, NODE2(MSG, op, NODES(NODE(ARG, arg)))) } + '&&' - arg:Expr { $$ = newASTNode(compiler.vm, NODE_AND, rcv, arg, 0, compiler.line) } + | '||' - arg:Expr { $$ = newASTNode(compiler.vm, NODE_OR, rcv, arg, 0, compiler.line) } + | '+' - arg:Expr { $$ = newASTNode(compiler.vm, NODE_ADD, rcv, arg, 0, compiler.line) } + | '-' - arg:Expr { $$ = newASTNode(compiler.vm, NODE_SUB, rcv, arg, 0, compiler.line) } + | '<' - arg:Expr { $$ = newASTNode(compiler.vm, NODE_LT, rcv, arg, 0, compiler.line) } + | op:BINOP - arg:Expr { $$ = newASTNode(compiler.vm, NODE_SEND, rcv, newASTNode(compiler.vm, NODE_MSG, op, compiler.vm.newArray2(1, newASTNode(compiler.vm, NODE_ARG, arg, 0, 0, compiler.line)), 0, compiler.line), 0, compiler.line) } ) -UnaryOp = '-' rcv:Expr { $$ = NODE(NEG, rcv) } - | '!' rcv:Expr { $$ = NODE(NOT, rcv) } +UnaryOp = '-' rcv:Expr { $$ = newASTNode(compiler.vm, NODE_NEG, rcv, 0, 0, compiler.line) } + | '!' rcv:Expr { $$ = newASTNode(compiler.vm, NODE_NOT, rcv, 0, 0, compiler.line) } Message = name:ID { args = 0 } ( '(' args:Args? ')' | SPACE args:Args - )? { $$ = NODE2(MSG, name, args) } + )? { $$ = newASTNode(compiler.vm, NODE_MSG, name, args, 0, compiler.line) } -Args = - head:Expr - { head = NODES(NODE(ARG, head)) } - ( ',' - tail:Expr - { PUSH_NODE(head, NODE(ARG, tail)) } - )* ( ',' - '*' splat:Expr - { PUSH_NODE(head, NODE2(ARG, splat, 1)) } +Args = - head:Expr - { head = compiler.vm.newArray2(1, newASTNode(compiler.vm, NODE_ARG, head, 0, 0, compiler.line)) } + ( ',' - tail:Expr - { head.Push(newASTNode(compiler.vm, NODE_ARG, tail, 0, 0, compiler.line)) } + )* ( ',' - '*' splat:Expr - { head.Push(newASTNode(compiler.vm, NODE_ARG, splat, 1, 0, compiler.line)) } )? { $$ = head } - | - '*' splat:Expr - { $$ = NODES(NODE2(ARG, splat, 1)) } + | - '*' splat:Expr - { $$ = compiler.vm.newArray2(1, newASTNode(compiler.vm, NODE_ARG, splat, 1, 0, compiler.line)) } Block = 'do' SEP - body:OptStmts - - 'end' { $$ = NODE(BLOCK, body) } + 'end' { $$ = newASTNode(compiler.vm, NODE_BLOCK, body, 0, 0, compiler.line) } | 'do' - '|' params:Params '|' SEP - body:OptStmts - - 'end' { $$ = NODE2(BLOCK, body, params) } + 'end' { $$ = newASTNode(compiler.vm, NODE_BLOCK, body, params, 0, compiler.line) } # FIXME this might hang the parser and is very slow. # Clash with Hash for sure. - #| '{' - body:OptStmts - '}' { $$ = NODE(BLOCK, body) } + #| '{' - body:OptStmts - '}' { $$ = newASTNode(compiler.vm, NODE_BLOCK, body, 0, 0, compiler.line) } #| '{' - '|' params:Params '|' - # - body:OptStmts - '}' { $$ = NODE2(BLOCK, body, params) } + # - body:OptStmts - '}' { $$ = newASTNode(compiler.vm, NODE_BLOCK, body, params, 0, compiler.line) } -Assign = name:ID - ASSIGN - val:Stmt { $$ = NODE2(ASSIGN, name, val) } - | name:CONST - ASSIGN - val:Stmt { $$ = NODE2(SETCONST, name, val) } - | name:IVAR - ASSIGN - val:Stmt { $$ = NODE2(SETIVAR, name, val) } - | name:CVAR - ASSIGN - val:Stmt { $$ = NODE2(SETCVAR, name, val) } - | name:GLOBAL - ASSIGN - val:Stmt { $$ = NODE2(SETGLOBAL, name, val) } +Assign = name:ID - ASSIGN - val:Stmt { $$ = newASTNode(compiler.vm, NODE_ASSIGN, name, val, 0, compiler.line) } + | name:CONST - ASSIGN - val:Stmt { $$ = newASTNode(compiler.vm, NODE_SETCONST, name, val, 0, compiler.line) } + | name:IVAR - ASSIGN - val:Stmt { $$ = newASTNode(compiler.vm, NODE_SETIVAR, name, val, 0, compiler.line) } + | name:CVAR - ASSIGN - val:Stmt { $$ = newASTNode(compiler.vm, NODE_SETCVAR, name, val, 0, compiler.line) } + | name:GLOBAL - ASSIGN - val:Stmt { $$ = newASTNode(compiler.vm, NODE_SETGLOBAL, name, val, 0, compiler.line) } While = 'while' SPACE cond:Expr SEP body:Stmts - - 'end' { $$ = NODE2(WHILE, cond, body) } + 'end' { $$ = newASTNode(compiler.vm, NODE_WHILE, cond, body, 0, compiler.line) } Until = 'until' SPACE cond:Expr SEP body:Stmts - - 'end' { $$ = NODE2(UNTIL, cond, body) } + 'end' { $$ = newASTNode(compiler.vm, NODE_UNTIL, cond, body, 0, compiler.line) } If = 'if' SPACE cond:Expr SEP { else_body = 0 } body:Stmts - else_body:Else? - 'end' { $$ = NODE3(IF, cond, body, else_body) } - | body:Expr - 'if' - cond:Expr { $$ = NODE2(IF, cond, NODES(body)) } + 'end' { $$ = newASTNode(compiler.vm, NODE_IF, cond, body, else_body, compiler.line) } + | body:Expr - 'if' - cond:Expr { $$ = newASTNode(compiler.vm, NODE_IF, cond, compiler.vm.newArray2(1, body), 0, compiler.line) } Unless = 'unless' SPACE cond:Expr SEP { else_body = 0 } body:Stmts - else_body:Else? - 'end' { $$ = NODE3(UNLESS, cond, body, else_body) } + 'end' { $$ = newASTNode(compiler.vm, NODE_UNLESS, cond, body, else_body, compiler.line) } | body:Expr - - 'unless' - cond:Expr { $$ = NODE2(UNLESS, cond, NODES(body)) } + 'unless' - cond:Expr { $$ = newASTNode(compiler.vm, NODE_UNLESS, cond, compiler.vm.newArray2(1, body), 0, compiler.line) } Else = 'else' SEP - body:Stmts - { $$ = body } -Method = rcv:ID '.' name:METHOD { $$ = NODE2(METHOD, NODE2(SEND, 0, NODE(MSG, rcv)), name) } - | rcv:Value '.' name:METHOD { $$ = NODE2(METHOD, rcv, name) } - | name:METHOD { $$ = NODE2(METHOD, 0, name) } +Method = rcv:ID '.' name:METHOD { $$ = newASTNode(compiler.vm, NODE_METHOD, newASTNode(compiler.vm, NODE_SEND, 0, newASTNode(compiler.vm, NODE_MSG, rcv, 0, 0, compiler.line), 0, compiler.line), name, 0, compiler.line) } + | rcv:Value '.' name:METHOD { $$ = newASTNode(compiler.vm, NODE_METHOD, rcv, name, 0, compiler.line) } + | name:METHOD { $$ = newASTNode(compiler.vm, NODE_METHOD, 0, name, 0, compiler.line) } Def = 'def' SPACE method:Method { params = 0 } (- '(' params:Params? ')')? SEP body:OptStmts - - 'end' { $$ = NODE3(DEF, method, params ? params : NODES_N(0), body) } - -Params = head:Param { head = NODES(head) } - ( ',' tail:Param { PUSH_NODE(head, tail) } + 'end' { if params > 0 { + $$ := newASTNode(compiler.vm, NODE_DEF, method, params, body, compiler.line); + } else { + $$ := newASTNode(compiler.vm, NODE_DEF, method, compiler.vm.newArray2(0), body, compiler.line); + } + } + +Params = head:Param { head = compiler.vm.newArray2(1, head) } + ( ',' tail:Param { head.Push(tail) } )* { $$ = head } -Param = - name:ID - '=' - def:Expr { $$ = NODE3(PARAM, name, 0, def) } - | - name:ID - { $$ = NODE(PARAM, name) } - | - '*' name:ID - { $$ = NODE2(PARAM, name, 1) } +Param = - name:ID - '=' - def:Expr { $$ = newASTNode(compiler.vm, NODE_PARAM, name, 0, def, compiler.line) } + | - name:ID - { $$ = newASTNode(compiler.vm, NODE_PARAM, name, 0, 0, compiler.line) } + | - '*' name:ID - { $$ = newASTNode(compiler.vm, NODE_PARAM, name, 1, 0, compiler.line) } Class = 'class' SPACE name:CONST { super = 0 } (- '<' - super:CONST)? SEP body:OptStmts - - 'end' { $$ = NODE3(CLASS, name, super, body) } + 'end' { $$ = newASTNode(compiler.vm, NODE_CLASS, name, super, body, compiler.line) } Module = 'module' SPACE name:CONST SEP body:OptStmts - - 'end' { $$ = NODE3(MODULE, name, 0, body) } - -Range = s:Receiver - '..' - e:Expr { $$ = NODE3(RANGE, s, e, 0) } - | s:Receiver - '...' - e:Expr { $$ = NODE3(RANGE, s, e, 1) } - -Yield = 'yield' SPACE args:AryItems { $$ = NODE(YIELD, args) } - | 'yield' '(' args:AryItems ')' { $$ = NODE(YIELD, args) } - | 'yield' { $$ = NODE(YIELD, NODES_N(0)) } - -Return = 'return' SPACE arg:Expr - !',' { $$ = NODE(RETURN, arg) } - | 'return' '(' arg:Expr ')' - !','{ $$ = NODE(RETURN, arg) } - | 'return' SPACE args:AryItems { $$ = NODE(RETURN, NODE(ARRAY, args)) } - | 'return' '(' args:AryItems ')' { $$ = NODE(RETURN, NODE(ARRAY, args)) } - | 'return' { $$ = NODE(RETURN, 0) } - -Break = 'break' { $$ = NODE(BREAK, 0) } - -Value = v:NUMBER { $$ = NODE(VALUE, v) } - | v:SYMBOL { $$ = NODE(VALUE, v) } - | v:REGEXP { $$ = NODE(VALUE, v) } - | v:STRING1 { $$ = NODE(STRING, v) } - | v:STRING2 { $$ = NODE(STRING, v) } - | v:CONST { $$ = NODE(CONST, v) } - | 'nil' { $$ = NODE(NIL, 0) } - | 'true' { $$ = NODE(BOOL, TR_TRUE) } - | 'false' { $$ = NODE(BOOL, TR_FALSE) } - | 'self' { $$ = NODE(SELF, 0) } - | name:IVAR { $$ = NODE(GETIVAR, name) } - | name:CVAR { $$ = NODE(GETCVAR, name) } - | name:GLOBAL { $$ = NODE(GETGLOBAL, name) } # TODO - | '[' - ']' { $$ = NODE(ARRAY, NODES_N(0)) } - | '[' - items:AryItems - ']' { $$ = NODE(ARRAY, items) } - | '{' - '}' { $$ = NODE(HASH, NODES_N(0)) } - | '{' - items:HashItems - '}' { $$ = NODE(HASH, items) } + 'end' { $$ = newASTNode(compiler.vm, NODE_MODULE, name, 0, body, compiler.line) } + +Range = s:Receiver - '..' - e:Expr { $$ = newASTNode(compiler.vm, NODE_RANGE, s, e, 0, compiler.line) } + | s:Receiver - '...' - e:Expr { $$ = newASTNode(compiler.vm, NODE_RANGE, s, e, 1, compiler.line) } + +Yield = 'yield' SPACE args:AryItems { $$ = newASTNode(compiler.vm, NODE_YIELD, args, 0, 0, compiler.line) } + | 'yield' '(' args:AryItems ')' { $$ = newASTNode(compiler.vm, NODE_YIELD, args, 0, 0, compiler.line) } + | 'yield' { $$ = newASTNode(compiler.vm, NODE_YIELD, compiler.vm.newArray2(0), 0, 0, compiler.line) } + +Return = 'return' SPACE arg:Expr - !',' { $$ = newASTNode(compiler.vm, NODE_RETURN, arg, 0, 0, compiler.line) } + | 'return' '(' arg:Expr ')' - !','{ $$ = newASTNode(compiler.vm, NODE_RETURN, arg, 0, 0, compiler.line) } + | 'return' SPACE args:AryItems { $$ = newASTNode(compiler.vm, NODE_RETURN, newASTNode(compiler.vm, NODE_ARRAY, args, 0, 0, compiler.line), 0, 0, compiler.line) } + | 'return' '(' args:AryItems ')' { $$ = newASTNode(compiler.vm, NODE_RETURN, newASTNode(compiler.vm, NODE_ARRAY, args, 0, 0, compiler.line), 0, 0, compiler.line) } + | 'return' { $$ = newASTNode(compiler.vm, NODE_RETURN, 0, 0, 0, compiler.line) } + +Break = 'break' { $$ = newASTNode(compiler.vm, NODE_BREAK, 0, 0, 0, compiler.line) } + +Value = v:NUMBER { $$ = newASTNode(compiler.vm, NODE_VALUE, v, 0, 0, compiler.line) } + | v:SYMBOL { $$ = newASTNode(compiler.vm, NODE_VALUE, v, 0, 0, compiler.line) } + | v:REGEXP { $$ = newASTNode(compiler.vm, NODE_VALUE, v, 0, 0, compiler.line) } + | v:STRING1 { $$ = newASTNode(compiler.vm, NODE_STRING, v, 0, 0, compiler.line) } + | v:STRING2 { $$ = newASTNode(compiler.vm, NODE_STRING, v, 0, 0, compiler.line) } + | v:CONST { $$ = newASTNode(compiler.vm, NODE_CONST, v, 0, 0, compiler.line) } + | 'nil' { $$ = newASTNode(compiler.vm, NODE_NIL, 0, 0, 0, compiler.line) } + | 'true' { $$ = newASTNode(compiler.vm, NODE_BOOL, TR_TRUE, 0, 0, compiler.line) } + | 'false' { $$ = newASTNode(compiler.vm, NODE_BOOL, TR_FALSE, 0, 0, compiler.line) } + | 'self' { $$ = newASTNode(compiler.vm, NODE_SELF, 0, 0, 0, compiler.line) } + | name:IVAR { $$ = newASTNode(compiler.vm, NODE_GETIVAR, name, 0, 0, compiler.line) } + | name:CVAR { $$ = newASTNode(compiler.vm, NODE_GETCVAR, name, 0, 0, compiler.line) } + | name:GLOBAL { $$ = newASTNode(compiler.vm, NODE_GETGLOBAL, name, 0, 0, compiler.line) } # TODO + | '[' - ']' { $$ = newASTNode(compiler.vm, NODE_ARRAY, compiler.vm.newArray2(0), 0, 0, compiler.line) } + | '[' - items:AryItems - ']' { $$ = newASTNode(compiler.vm, NODE_ARRAY, items, 0, 0, compiler.line) } + | '{' - '}' { $$ = newASTNode(compiler.vm, NODE_HASH, compiler.vm.newArray2(0), 0, 0, compiler.line) } + | '{' - items:HashItems - '}' { $$ = newASTNode(compiler.vm, NODE_HASH, items, 0, 0, compiler.line) } | '(' - Expr - ')' -AryItems = - head:Expr - { head = NODES(head) } - ( ',' - tail:Expr - { PUSH_NODE(head, tail) } +AryItems = - head:Expr - { head = compiler.vm.newArray2(1, head) } + ( ',' - tail:Expr - { head.Push(tail) } )* { $$ = head } -HashItems = head:Expr - '=>' - val:Expr { head = NODES_N(2, head, val) } - ( - ',' - key:Expr - { PUSH_NODE(head, key) } - '=>' - val:Expr { PUSH_NODE(head, val) } +HashItems = head:Expr - '=>' - val:Expr { head = compiler.vm.newArray2(2, head, val) } + ( - ',' - key:Expr - { head.Push(key) } + '=>' - val:Expr { head.Push(val) } )* { $$ = head } KEYWORD = 'while' | 'until' | 'do' | 'end' | @@ -254,29 +257,29 @@ SYMBOL = ':' < (NAME | KEYWORD) > { $$ = TrSymbol_new(yyvm, yytext) } STRING1 = '\'' { STRING_START } ( - '\\\'' { STRING_PUSH("'", 1) } - | < [^\'] > { STRING_PUSH(yytext, yyleng) } + '\\\'' { assert(nbuf + 1 < 4096); memcpy(sbuf + nbuf, "'", sizeof(char) * 1); nbuf += 1; } + | < [^\'] > { assert(nbuf + yyleng < 4096); memcpy(sbuf + nbuf, yytext, sizeof(char) * yyleng); nbuf += yyleng; } )* '\'' { $$ = TrString_new2(yyvm, sbuf) } -ESC_CHAR = '\\n' { STRING_PUSH("\n", 1) } - | '\\b' { STRING_PUSH("\b", 1) } - | '\\f' { STRING_PUSH("\f", 1) } - | '\\r' { STRING_PUSH("\r", 1) } - | '\\t' { STRING_PUSH("\t", 1) } - | '\\\"' { STRING_PUSH("\"", 1) } - | '\\\\' { STRING_PUSH("\\", 1) } +ESC_CHAR = '\\n' { assert(nbuf + 1 < 4096); memcpy(sbuf + nbuf, "\n", sizeof(char) * 1); nbuf += 1; } + | '\\b' { assert(nbuf + 1 < 4096); memcpy(sbuf + nbuf, "\b", sizeof(char) * 1); nbuf += 1; } + | '\\f' { assert(nbuf + 1 < 4096); memcpy(sbuf + nbuf, "\f", sizeof(char) * 1); nbuf += 1; } + | '\\r' { assert(nbuf + 1 < 4096); memcpy(sbuf + nbuf, "\r", sizeof(char) * 1); nbuf += 1; } + | '\\t' { assert(nbuf + 1 < 4096); memcpy(sbuf + nbuf, "\t", sizeof(char) * 1); nbuf += 1; } + | '\\\"' { assert(nbuf + 1 < 4096); memcpy(sbuf + nbuf, "\"", sizeof(char) * 1); nbuf += 1; } + | '\\\\' { assert(nbuf + 1 < 4096); memcpy(sbuf + nbuf, "\\", sizeof(char) * 1); nbuf += 1; } STRING2 = '"' { STRING_START } ( ESC_CHAR - | < [^\"] > { STRING_PUSH(yytext, yyleng) } #" for higlighting + | < [^\"] > { assert(nbuf + yyleng < 4096); memcpy(sbuf + nbuf, yytext, sizeof(char) * yyleng); nbuf += yyleng; } #" for higlighting )* '"' { $$ = TrString_new2(yyvm, sbuf) } REGEXP = '/' { STRING_START } ( ESC_CHAR - | < [^/] > { STRING_PUSH(yytext, yyleng) } + | < [^/] > { assert(nbuf + yyleng < 4096); memcpy(sbuf + nbuf, yytext, sizeof(char) * yyleng); nbuf += yyleng; } )* '/' { $$ = TrRegexp_new(yyvm, sbuf, 0) } @@ -315,7 +318,9 @@ RubyObject yyerror() { vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected " + msg)); return TR_UNDEF; } - tr_raise(SyntaxError, msg.ptr); + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cSyntaxError, tr_sprintf(vm, msg.ptr)); + return TR_UNDEF; } /* Compiles code to a Block. diff --git a/vm/hash.go b/vm/hash.go index 1a1add4..0f084bc 100644 --- a/vm/hash.go +++ b/vm/hash.go @@ -1,21 +1,17 @@ import( "tr"; - "internal"; ) func TrHash_new(vm *RubyVM) RubyObject { - return Hash{type: TR_T_Hash, class: vm.classes[TR_T_Hash], ivars: kh_init(RubyObject), kh: kh_init(RubyObject)}; + return Hash{type: TR_T_Hash, class: vm.classes[TR_T_Hash], ivars: make(map[string] RubyObject), hash: make(map[string] RubyObject)}; } func TrHash_new2(vm *RubyVM, n size_t, items []RubyObject) RubyObject { - h := TrHash_new(vm); - i size_t; - ret int; - for (i = 0; i < n; i+=2) { - k := kh_put(RubyObject, h.kh, items[i], &ret); - kh_value(h.kh, k) = items[i+1]; - } - return h; + hash := TrHash_new(vm); + for i := 0; i < n; i += 2) { + hash.hash[items[i]] = items[i + 1]; + } + return h; } func TrHash_size(vm *RubyVM, self *RubyObject) RubyObject { @@ -24,8 +20,7 @@ func TrHash_size(vm *RubyVM, self *RubyObject) RubyObject { vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected Hash")); return TR_UNDEF; } - h := TrHash *(self); - return TR_INT2FIX(kh_size(h.kh)); + return TR_INT2FIX(len(self.hash)); } // TODO use Object#hash as the key @@ -35,10 +30,7 @@ func TrHash_get(vm *RubyVM, self, key *RubyObject) RubyObject { vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected Hash")); return TR_UNDEF; } - h := TrHash *(self); - k := kh_get(RubyObject, h.kh, key); - if (k != kh_end(h.kh)) return kh_value(h.kh, k); - return TR_NIL; + return self.hash[key] || TR_NIL; } func TrHash_set(vm *RubyVM, self, key, value *RubyObject) RubyObject { @@ -47,11 +39,7 @@ func TrHash_set(vm *RubyVM, self, key, value *RubyObject) RubyObject { vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected Hash")); return TR_UNDEF; } - h := TrHash *(self); - ret int; - k := kh_put(RubyObject, h.kh, key, &ret); - if (!ret) kh_del(RubyObject, h.kh, k); - kh_value(h.kh, k) = value; + self.hash[key] = value; return value; } @@ -61,21 +49,19 @@ func TrHash_delete(vm *RubyVM, self, key *RubyObject) RubyObject { vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected Hash")); return TR_UNDEF; } - h := TrHash *(self); - k := kh_get(RubyObject, h.kh, key); - if (k != kh_end(h.kh)) { - value := kh_value(h.kh, k); - kh_del(RubyObject, h.kh, k); + if value, ok := self.hash[key]; ok { + hash[key] = 0, false; // deletes the value from the map return value; + } else { + return TR_NIL; } - return TR_NIL; } func TrHash_init(vm *RubyVM) { - c := vm.classes[TR_T_Hash] = Object_const_set(vm, vm.self, tr_intern(Hash), newClass(vm, tr_intern(Hash), vm.classes[TR_T_Object])); - tr_def(c, "length", TrHash_size, 0); - tr_def(c, "size", TrHash_size, 0); - tr_def(c, "[]", TrHash_get, 1); - tr_def(c, "[]=", TrHash_set, 2); - tr_def(c, "delete", TrHash_delete, 1); + c := vm.classes[TR_T_Hash] = Object_const_set(vm, vm.self, TrSymbol_new(vm, Hash), newClass(vm, TrSymbol_new(vm, Hash), vm.classes[TR_T_Object])); + c.add_method(vm, TrSymbol_new(vm, "length"), newMethod(vm, (TrFunc *)TrHash_size, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "size"), newMethod(vm, (TrFunc *)TrHash_size, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "[]"), newMethod(vm, (TrFunc *)TrHash_get, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "[]="), newMethod(vm, (TrFunc *)TrHash_set, TR_NIL, 2)); + c.add_method(vm, TrSymbol_new(vm, "delete"), newMethod(vm, (TrFunc *)TrHash_delete, TR_NIL, 1)); } \ No newline at end of file diff --git a/vm/internal.go b/vm/internal.go deleted file mode 100644 index 57fb3f2..0000000 --- a/vm/internal.go +++ /dev/null @@ -1,64 +0,0 @@ -// ast building macros -#define NODE(T,A) newASTNode(compiler.vm, NODE_##T, (A), 0, 0, compiler.line) -#define NODE2(T,A,B) newASTNode(compiler.vm, NODE_##T, (A), (B), 0, compiler.line) -#define NODE3(T,A,B,C) newASTNode(compiler.vm, NODE_##T, (A), (B), (C), compiler.line) -#define NODES(I) newArray2(compiler.vm, 1, (I)) -#define NODES_N(N,...) newArray2(compiler.vm, (N), ##__VA_ARGS__) -#define PUSH_NODE(A,N) (A).kv.Push(N) -#define SYMCAT(A,B) tr_intern(strcat(((TrString*)(A)).ptr, ((TrString*)(B)).ptr)) - -/* This provides the compiler about branch hints, so it - keeps the normal case fast. Stolen from Rubinius. */ -#ifdef __GNUC__ -#define likely(x) __builtin_expect((long int)(x),1) -#define unlikely(x) __builtin_expect((long int)(x),0) -#else -#define likely(x) x -#define unlikely(x) x -#endif - -/* types of nodes in the AST built by the parser */ -const ( - NODE_ROOT = iota; - NODE_BLOCK; - NODE_VALUE; - NODE_STRING; - NODE_ASSIGN; - NODE_ARG; - NODE_SEND; - NODE_MSG; - NODE_IF; - NODE_UNLESS; - NODE_AND; - NODE_OR; - NODE_WHILE; - NODE_UNTIL; - NODE_BOOL; - NODE_NIL; - NODE_SELF; - NODE_LEAVE; - NODE_RETURN; - NODE_BREAK; - NODE_YIELD; - NODE_DEF; - NODE_METHOD; - NODE_PARAM; - NODE_CLASS; - NODE_MODULE; - NODE_CONST; - NODE_SETCONST; - NODE_ARRAY; - NODE_HASH; - NODE_RANGE; - NODE_GETIVAR; - NODE_SETIVAR; - NODE_GETCVAR; - NODE_SETCVAR; - NODE_GETGLOBAL; - NODE_SETGLOBAL; - NODE_ADD; - NODE_SUB; - NODE_LT; - NODE_NEG; - NODE_NOT; -) \ No newline at end of file diff --git a/vm/kernel.go b/vm/kernel.go index 6d2ce04..025138b 100644 --- a/vm/kernel.go +++ b/vm/kernel.go @@ -1,18 +1,17 @@ import( "tr"; - "internal"; ) func TrBinding_new(vm *RubyVM, frame *Frame) RubyObject { - return Binding{type: TR_T_Binding, class: vm.classes[TR_T_Binding], ivars: kh_init(RubyObject), frame: frame}; + return Binding{type: TR_T_Binding, class: vm.classes[TR_T_Binding], ivars: make(map[string] RubyObject), frame: frame}; } func TrBinding_init(vm *RubyVM) { - vm.classes[TR_T_Binding] = Object_const_set(vm, vm.self, tr_intern(Binding), newClass(vm, tr_intern(Binding), vm.classes[TR_T_Object])); + vm.classes[TR_T_Binding] = Object_const_set(vm, vm.self, TrSymbol_new(vm, Binding), newClass(vm, TrSymbol_new(vm, Binding), vm.classes[TR_T_Object])); } func TrKernel_puts(vm *RubyVM, self *RubyObject, argc int, argv []RubyObject) RubyObject { - object_as_string := tr_send2(object, "to_s"); + object_as_string := Object_send(vm, object, 1, { TrSymbol_new(vm, "to_s") }); if !object_as_string.(String) && !object_as_string.(Symbol) { vm.throw_reason = TR_THROW_EXCEPTION; vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected " + object_as_string)); @@ -27,8 +26,16 @@ func TrKernel_binding(vm *RubyVM, self RubyObject) RubyObject { } func TrKernel_eval(vm *RubyVM, self *RubyObject, argc int, argv []RubyObject) RubyObject { - if argc < 1 { tr_raise(ArgumentError, "string argument required") } - if argc > 4 { tr_raise(ArgumentError, "Too much arguments") } + if argc < 1 { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cArgumentError, tr_sprintf(vm, "string argument required")); + return TR_UNDEF; + } + if argc > 4 { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cArgumentError, tr_sprintf(vm, "Too many arguments")); + return TR_UNDEF; + } code_string := argv[0]; if argc > 1 && argv[1] { if !argv[1].(Binding) { @@ -79,20 +86,22 @@ func TrKernel_raise(vm *RubyVM, self *RubyObject, argc int, argv []RubyObject) R e := TR_NIL; switch (argc) { case 0: - e = tr_getglobal("$!"); + e = vm.globals[TrSymbol_new(vm, "$!")] || TR_NIL; case 1: if argv[0].(String) { e = TrException_new(vm, vm.cRuntimeError, argv[0]); } else { - e = tr_send2(argv[0], "exception"); + e = Object_send(vm, argv[0], 1, { TrSymbol_new(vm, "exception") }); } case 2: - e = tr_send2(argv[0], "exception", argv[1]); + e = Object_send(vm, argv[0], 1, { TrSymbol_new(vm, "exception") }); default: - tr_raise(ArgumentError, "wrong number of arguments (%d for 2)", argc); + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cArgumentError, tr_sprintf(vm, "wrong number of arguments (%d for 2)", argc)); + return TR_UNDEF; } TrException_set_backtrace(vm, e, vm.backtrace()); vm.throw_reason = TR_THROW_EXCEPTION; @@ -101,11 +110,11 @@ func TrKernel_raise(vm *RubyVM, self *RubyObject, argc int, argv []RubyObject) R } func TrKernel_init(vm *RubyVM) { - m := tr_defmodule("Kernel"); + m := Object_const_set(vm, vm.self, TrSymbol_new(vm, "Kernel"), vm.newModule(TrSymbol_new(vm, "Kernel"))); vm.classes[TR_T_Object].include(vm, m); - tr_def(m, "puts", TrKernel_puts, -1); - tr_def(m, "eval", TrKernel_eval, -1); - tr_def(m, "load", TrKernel_load, 1); - tr_def(m, "binding", TrKernel_binding, 0); - tr_def(m, "raise", TrKernel_raise, -1); + c.add_method(vm, TrSymbol_new(vm, "puts"), newMethod(vm, (TrFunc *)TrKernel_puts, TR_NIL, -1)); + c.add_method(vm, TrSymbol_new(vm, "eval"), newMethod(vm, (TrFunc *)TrKernel_eval, TR_NIL, -1)); + c.add_method(vm, TrSymbol_new(vm, "load"), newMethod(vm, (TrFunc *)TrKernel_load, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "binding"), newMethod(vm, (TrFunc *)TrKernel_binding, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "raise"), newMethod(vm, (TrFunc *)TrKernel_raise, TR_NIL, -1)); } \ No newline at end of file diff --git a/vm/number.go b/vm/number.go index 5be96da..a456d8e 100644 --- a/vm/number.go +++ b/vm/number.go @@ -1,6 +1,5 @@ import( "tr"; - "internal"; ) func TrFixnum_add(vm *RunyVM, self, other *RubyObject) RubyObject { @@ -72,16 +71,16 @@ func TrFixnum_to_s(vm *RubyVM, self *RubyObject) RubyObject { } void TrFixnum_init(vm *RubyVM) { - c := vm.classes[TR_T_Fixnum] = Object_const_set(vm, vm.self, tr_intern(Fixnum), newClass(vm, tr_intern(Fixnum), vm.classes[TR_T_Object])); - tr_def(c, "+", TrFixnum_add, 1); - tr_def(c, "-", TrFixnum_sub, 1); - tr_def(c, "*", TrFixnum_mul, 1); - tr_def(c, "/", TrFixnum_div, 1); - tr_def(c, "==", TrFixnum_eq, 1); - tr_def(c, "!=", TrFixnum_eq, 1); - tr_def(c, "<", TrFixnum_lt, 1); - tr_def(c, "<=", TrFixnum_le, 1); - tr_def(c, ">", TrFixnum_gt, 1); - tr_def(c, ">=", TrFixnum_ge, 1); - tr_def(c, "to_s", TrFixnum_to_s, 0); + c := vm.classes[TR_T_Fixnum] = Object_const_set(vm, vm.self, TrSymbol_new(vm, Fixnum), newClass(vm, TrSymbol_new(vm, Fixnum), vm.classes[TR_T_Object])); + c.add_method(vm, TrSymbol_new(vm, "+"), newMethod(vm, (TrFunc *)TrFixnum_add, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "-"), newMethod(vm, (TrFunc *)TrFixnum_sub, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "*"), newMethod(vm, (TrFunc *)TrFixnum_mul, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "/"), newMethod(vm, (TrFunc *)TrFixnum_div, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "=="), newMethod(vm, (TrFunc *)TrFixnum_eq, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "!="), newMethod(vm, (TrFunc *)TrFixnum_eq, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "<"), newMethod(vm, (TrFunc *)TrFixnum_lt, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "<="), newMethod(vm, (TrFunc *)TrFixnum_le, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, ">"), newMethod(vm, (TrFunc *)TrFixnum_gt, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, ">="), newMethod(vm, (TrFunc *)TrFixnum_ge, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "to_s"), newMethod(vm, (TrFunc *)TrFixnum_to_s, TR_NIL, 0)); } \ No newline at end of file diff --git a/vm/object.go b/vm/object.go index 6ff543b..264c772 100644 --- a/vm/object.go +++ b/vm/object.go @@ -1,17 +1,16 @@ import ( "tr"; - "internal"; "call"; ) type Object struct { type TR_T; class *RubyObject; - ivars *map[string] *RubyObject; + ivars map[string] RubyObject; } func Object_alloc(vm *RubyVM, class *RubyObject) RubyObject { - return Object{type: TR_T_Object, class: vm.classes[TR_T_Object], ivars: kh_init(RubyObject), class: class}; + return Object{type: TR_T_Object, class: vm.classes[TR_T_Object], ivars: make(map[string] RubyObject), class: class}; } func Object_type(vm *RubyVM, obj *RubyObject) int { @@ -40,14 +39,20 @@ func Object_method_missing(vm *RubyVM, self *RubyObject, argc int, argv []RubyOb vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected " + argv[0])); return TR_UNDEF; } - tr_raise(NoMethodError, "Method not found: `%s'", argv[0].ptr); + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cNoMethodError, tr_sprintf(vm, "Method not found: `%s'", argv[0].ptr)); + return TR_UNDEF; } func Object_send(vm *RubyVM, self *RubyObject, argc int, argv []RubyObject) RubyObject { - if argc == 0 { tr_raise(ArgumentError, "wrong number of arguments (%d for 1)", argc); } + if argc == 0 { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cArgumentError, tr_sprintf(vm, "wrong number of arguments (%d for 1)", argc)); + return TR_UNDEF; + } method := Object_method(vm, self, argv[0]); if method == TR_NIL { - method = Object_method(vm, self, tr_intern("method_missing")); + method = Object_method(vm, self, TrSymbol_new(vm, "method_missing")); return method.call(vm, self, argc, argv, 0, 0); } else { return method.call(vm, self, argc-1, argv+1, 0, 0); @@ -56,16 +61,11 @@ func Object_send(vm *RubyVM, self *RubyObject, argc int, argv []RubyObject) Ruby // TODO respect namespace func Object_const_get(vm *RubyVM, self, name *RubyObject) RubyObject { - k := kh_get(RubyObject, vm.consts, name); - if (k != kh_end(vm.consts)) return kh_value(vm.consts, k); - return TR_NIL; + return vm.consts[name] || TR_NIL; } func Object_const_set(vm *RubyVM, self, name, value *RubyObject) RubyObject { - int ret; - k := kh_put(RubyObject, vm.consts, name, &ret); - if (!ret) kh_del(RubyObject, vm.consts, k); - kh_value(vm.consts, k) = value; + vm.consts[name] = value; return value; } @@ -117,7 +117,7 @@ func Object_instance_eval(vm *RubyVM, self, code *RubyObject) RubyObject { } func Object_inspect(vm *RubyVM, self *RubyObject) RubyObject { - class_name := tr_send2(tr_send2(self, "class"), "name") + class_name := Object_send(vm, Object_send(vm, self, 1, { TrSymbol_new(vm, "class") }), 1, { TrSymbol_new(vm, "name") }); if !class_name.(String) && !class_name.(Symbol) { vm.throw_reason = TR_THROW_EXCEPTION; vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected " + class_name)); @@ -127,17 +127,17 @@ func Object_inspect(vm *RubyVM, self *RubyObject) RubyObject { } func Object_preinit(vm *RubyVM) { - return vm.classes[TR_T_Object] = Object_const_set(vm, vm.self, tr_intern(Object), newClass(vm, tr_intern(Object), vm.classes[TR_T_Object])); + return vm.classes[TR_T_Object] = Object_const_set(vm, vm.self, TrSymbol_new(vm, Object), newClass(vm, TrSymbol_new(vm, Object), vm.classes[TR_T_Object])); } func Object_init(vm *RubyVM) { c := vm.classes[TR_T_Object]; - tr_def(c, "class", Object_class, 0); - tr_def(c, "method", Object_method, 1); - tr_def(c, "method_missing", Object_method_missing, -1); - tr_def(c, "send", Object_send, -1); - tr_def(c, "object_id", Object_object_id, 0); - tr_def(c, "instance_eval", Object_instance_eval, 1); - tr_def(c, "to_s", Object_inspect, 0); - tr_def(c, "inspect", Object_inspect, 0); + c.add_method(vm, TrSymbol_new(vm, "class"), newMethod(vm, (TrFunc *)Object_class, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "method"), newMethod(vm, (TrFunc *)Object_method, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "method_missing"), newMethod(vm, (TrFunc *)Object_method_missing, TR_NIL, -1)); + c.add_method(vm, TrSymbol_new(vm, "send"), newMethod(vm, (TrFunc *)Object_send, TR_NIL, -1)); + c.add_method(vm, TrSymbol_new(vm, "object_id"), newMethod(vm, (TrFunc *)Object_object_id, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "instance_eval"), newMethod(vm, (TrFunc *)Object_instance_eval, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "to_s"), newMethod(vm, (TrFunc *)Object_inspect, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "inspect"), newMethod(vm, (TrFunc *)Object_inspect, TR_NIL, 0)); } \ No newline at end of file diff --git a/vm/primitive.go b/vm/primitive.go index a95ddaa..02e6031 100644 --- a/vm/primitive.go +++ b/vm/primitive.go @@ -1,6 +1,5 @@ import ( "tr"; - "internal"; ) func TrNil_to_s(vm *RubyVM, self *RubyObject) RubyObject { @@ -16,10 +15,10 @@ func TrFalse_to_s(vm *RubyVM, self *RubyObject) RubyObject { } func TrPrimitive_init(vm *RubyVM) { - nilc := vm.classes[TR_T_NilClass] = Object_const_set(vm, vm.self, tr_intern(NilClass), newClass(vm, tr_intern(NilClass), vm.classes[TR_T_Object])); - truec := vm.classes[TR_T_TrueClass] = Object_const_set(vm, vm.self, tr_intern(TrueClass), newClass(vm, tr_intern(TrueClass), vm.classes[TR_T_Object])); - falsec := vm.classes[TR_T_FalseClass] = Object_const_set(vm, vm.self, tr_intern(FalseClass), newClass(vm, tr_intern(FalseClass), vm.classes[TR_T_Object])); - tr_def(nilc, "to_s", TrNil_to_s, 0); - tr_def(truec, "to_s", TrTrue_to_s, 0); - tr_def(falsec, "to_s", TrFalse_to_s, 0); + nilc := vm.classes[TR_T_NilClass] = Object_const_set(vm, vm.self, TrSymbol_new(vm, NilClass), newClass(vm, TrSymbol_new(vm, NilClass), vm.classes[TR_T_Object])); + truec := vm.classes[TR_T_TrueClass] = Object_const_set(vm, vm.self, TrSymbol_new(vm, TrueClass), newClass(vm, TrSymbol_new(vm, TrueClass), vm.classes[TR_T_Object])); + falsec := vm.classes[TR_T_FalseClass] = Object_const_set(vm, vm.self, TrSymbol_new(vm, FalseClass), newClass(vm, TrSymbol_new(vm, FalseClass), vm.classes[TR_T_Object])); + nilc.add_method(vm, TrSymbol_new(vm, "to_s"), newMethod(vm, (TrFunc *)TrNil_to_s, TR_NIL, 0)); + truec.add_method(vm, TrSymbol_new(vm, "to_s"), newMethod(vm, (TrFunc *)TrTrue_to_s, TR_NIL, 0)); + falsec.add_method(vm, TrSymbol_new(vm, "to_s"), newMethod(vm, (TrFunc *)TrFalse_to_s, TR_NIL, 0)); } \ No newline at end of file diff --git a/vm/proc.go b/vm/proc.go index b0962e2..0938620 100644 --- a/vm/proc.go +++ b/vm/proc.go @@ -1,6 +1,5 @@ import ( "tr"; - "internal"; ) type Closure struct { diff --git a/vm/range.go b/vm/range.go index b6b7e30..14f9022 100644 --- a/vm/range.go +++ b/vm/range.go @@ -1,10 +1,9 @@ import ( "tr"; - "internal"; ) func TrRange_new(vm *RubyVM, first, last *RubyObject, exclusive int) RubyObject { - return Range{type: TR_T_Range, class: vm.classes[TR_T_Range], ivars: kh_init(RubyObject), first: first, last: last, exclusive: exclusive}; + return Range{type: TR_T_Range, class: vm.classes[TR_T_Range], ivars: make(map[string] RubyObject), first: first, last: last, exclusive: exclusive}; } func TrRange_first(vm *RubyVM, self *RubyObject) RubyObject { @@ -40,8 +39,8 @@ func TrRange_exclude_end(vm *RubyVM, self *RubyObject) RubyObject { } func TrRange_init(vm *RubyVM) { - c := vm.classes[TR_T_Range] = Object_const_set(vm, vm.self, tr_intern(Range), newClass(vm, tr_intern(Range), vm.classes[TR_T_Object])); - tr_def(c, "first", TrRange_first, 0); - tr_def(c, "last", TrRange_last, 0); - tr_def(c, "exclude_end?", TrRange_exclude_end, 0); + c := vm.classes[TR_T_Range] = Object_const_set(vm, vm.self, TrSymbol_new(vm, Range), newClass(vm, TrSymbol_new(vm, Range), vm.classes[TR_T_Object])); + c.add_method(vm, TrSymbol_new(vm, "first"), newMethod(vm, (TrFunc *)TrRange_first, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "last"), newMethod(vm, (TrFunc *)TrRange_last, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "exclude_end?"), newMethod(vm, (TrFunc *)TrRange_exclude_end, TR_NIL, 0)); } \ No newline at end of file diff --git a/vm/regexp.go b/vm/regexp.go index 03592d1..7a63e15 100644 --- a/vm/regexp.go +++ b/vm/regexp.go @@ -1,7 +1,6 @@ import ( "pcre"; "tr"; - "internal"; ) // Loosely based on http://vcs.pcre.org/viewvc/code/trunk/pcredemo.c @@ -9,7 +8,7 @@ import ( // Translate this to use Go's stdlib regexp package func TrRegexp_new(vm *RubyVM, pattern *string, options int) RubyObject { - r := Regexp{type: TR_T_Regexp, class: vm.classes[TR_T_Regexp], ivars: kh_init(RubyObject)}; + r := Regexp{type: TR_T_Regexp, class: vm.classes[TR_T_Regexp], ivars: make(map[string] RubyObject)}; error *string; erroffset int; @@ -22,7 +21,9 @@ func TrRegexp_new(vm *RubyVM, pattern *string, options int) RubyObject { if (r.re == nil) { TrRegex_free(vm, r); - tr_raise(RegexpError, "compilation failed at offset %d: %s", erroffset, error); + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cRegexpError, tr_sprintf(vm, "compilation failed at offset %d: %s", erroffset, error)); + return TR_UNDEF; } return r; } @@ -68,16 +69,18 @@ func TrRegexp_match(vm *RubyVM, self, str *RubyObject) RubyObject { if (rc == 0) { rc = OVECCOUNT/3; - tr_raise(RegexpError, "Too much matches, only %d supported for now", rc - 1); + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cRegexpError, tr_sprintf(vm, "Too many matches, only %d supported for now", rc - 1)); + return TR_UNDEF; } // TODO should create a MatchData object - data := newArray(vm); + data := vm.newArray(); i int; for (i = 0; i < rc; i++) { substring_start := subject + ovector[2*i]; substring_length := ovector[2*i+1] - ovector[2*i]; - data.kv.Push(TrString_new(vm, substring_start, substring_length)); + data.Push(TrString_new(vm, substring_start, substring_length)); } return data; } @@ -88,7 +91,7 @@ func TrRegex_free(vm *RubyVM, self *RubyObject) { } func TrRegexp_init(vm *RubyVM) { - c := vm.classes[TR_T_Regexp] = Object_const_set(vm, vm.self, tr_intern(Regexp), newClass(vm, tr_intern(Regexp), vm.classes[TR_T_Object])); - tr_metadef(c, "new", TrRegexp_compile, 1); - tr_def(c, "match", TrRegexp_match, 1); + c := vm.classes[TR_T_Regexp] = Object_const_set(vm, vm.self, TrSymbol_new(vm, Regexp), newClass(vm, TrSymbol_new(vm, Regexp), vm.classes[TR_T_Object])); + Object_add_singleton_method(vm, c, TrSymbol_new(vm, "new"), newMethod(vm, (TrFunc *)TrRegexp_compile, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "match"), newMethod(vm, (TrFunc *)TrRegexp_match, TR_NIL, 1)); } \ No newline at end of file diff --git a/vm/string.go b/vm/string.go index 170d1e4..e9899f6 100644 --- a/vm/string.go +++ b/vm/string.go @@ -6,31 +6,23 @@ import ( "bytes"; "fmt"; "tr"; - "internal"; ) // symbol -func TrSymbol_lookup(vm *RubyVM, str string) RubyObject { - symbols := vm.symbols; - k := kh_get(str, symbols, str); - if (k != kh_end(symbols)) return kh_value(symbols, k); - return TR_NIL; +func TrSymbol_lookup(vm *RubyVM, name string) RubyObject { + return vm.symbols[name] || TR_NIL; } -func TrSymbol_add(vm *RubyVM, str *string, id *RubyObject) { - ret int; - symbols := vm.symbols; - k := kh_put(str, symbols, str, &ret); - if (!ret) kh_del(str, symbols, k); - kh_value(symbols, k) = id; +func TrSymbol_add(vm *RubyVM, name *string, id *RubyObject) { + vm.symbols[name] = id; } func TrSymbol_new(vm *RubyVM, str *string) RubyObject { id := TrSymbol_lookup(vm, str); if (!id) { - s := Symbol{type: TR_T_Symbol, class: vm.classes[TR_T_Symbol], ivars: kh_init(RubyObject), len: strlen(str), ptr: make([]byte, s.len + 1), interned: true}; + s := Symbol{type: TR_T_Symbol, class: vm.classes[TR_T_Symbol], ivars: make(map[string] RubyObject), len: strlen(str), ptr: make([]byte, s.len + 1), interned: true}; bytes.Copy(s.ptr, str[0:s.len - 1]); s.ptr[s.len] = '\0'; id := s; @@ -49,8 +41,8 @@ func TrSymbol_to_s(vm *RubyVM, self *RubyObject) RubyObject { } func TrSymbol_init(vm *RubyVM) { - c := vm.classes[TR_T_Symbol] = Object_const_set(vm, vm.self, tr_intern(Symbol), newClass(vm, tr_intern(Symbol), vm.classes[TR_T_Object])); - tr_def(c, "to_s", TrSymbol_to_s, 0); + c := vm.classes[TR_T_Symbol] = Object_const_set(vm, vm.self, TrSymbol_new(vm, Symbol), newClass(vm, TrSymbol_new(vm, Symbol), vm.classes[TR_T_Object])); + c.add_method(vm, TrSymbol_new(vm, "to_s"), newMethod(vm, (TrFunc *)TrSymbol_to_s, TR_NIL, 0)); } // string @@ -69,7 +61,7 @@ func TrString_size(vm *RubyVM, self) RubyObject { } func TrString_new(vm *RubyVM, str *string, len size_t) RubyObject { - s := String{type: TR_T_String, class: vm.classes[TR_T_String], ivars: kh_init(RubyObject), len: len, ptr: make([]byte, s.len + 1)}; + s := String{type: TR_T_String, class: vm.classes[TR_T_String], ivars: make(map[string] RubyObject), len: len, ptr: make([]byte, s.len + 1)}; bytes.Copy(s.ptr, str[0:s.len - 1]); s.ptr[s.len] = '\0'; return s; @@ -80,7 +72,7 @@ func TrString_new2(vm *RubyVM, str *string) RubyObject { } func TrString_new3(vm *RubyVM, len size_t) RubyObject { - s := String{type: TR_T_String, class: vm.classes[TR_T_String], ivars: kh_init(RubyObject), len: len, ptr: make([]byte, s.len + 1)}; + s := String{type: TR_T_String, class: vm.classes[TR_T_String], ivars: make(map[string] RubyObject), len: len, ptr: make([]byte, s.len + 1)}; s.ptr[s.len] = '\0' return s; } @@ -161,7 +153,7 @@ func TrString_to_sym(vm *RubyVM, self *RubyObject) RubyObject { vm.throw_value = TrException_new(vm, vm.cTypeError, TrString_new2(vm, "Expected " + self)); return TR_UNDEF; } - return tr_intern(self.ptr); + return TrSymbol_new(vm, self.ptr); } // Uses variadic ... parameter which replaces the mechanism used by stdarg.h @@ -179,13 +171,13 @@ func tr_sprintf(vm *RubyVM, fmt *string, args ...) RubyObject { } func TrString_init(vm *RubyVM) { - c := vm.classes[TR_T_String] = Object_const_set(vm, vm.self, tr_intern(String), newClass(vm, tr_intern(String), vm.classes[TR_T_Object])); - tr_def(c, "to_s", TrString_to_s, 0); - tr_def(c, "to_sym", TrString_to_sym, 0); - tr_def(c, "size", TrString_size, 0); - tr_def(c, "replace", TrString_replace, 1); - tr_def(c, "substring", TrString_substring, 2); - tr_def(c, "+", TrString_add, 1); - tr_def(c, "<<", TrString_push, 1); - tr_def(c, "<=>", TrString_cmp, 1); + c := vm.classes[TR_T_String] = Object_const_set(vm, vm.self, TrSymbol_new(vm, String), newClass(vm, TrSymbol_new(vm, String), vm.classes[TR_T_Object])); + c.add_method(vm, TrSymbol_new(vm, "to_s"), newMethod(vm, (TrFunc *)TrString_to_s, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "to_sym"), newMethod(vm, (TrFunc *)TrString_to_sym, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "size"), newMethod(vm, (TrFunc *)TrString_size, TR_NIL, 0)); + c.add_method(vm, TrSymbol_new(vm, "replace"), newMethod(vm, (TrFunc *)TrString_replace, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "substring"), newMethod(vm, (TrFunc *)ToString_substring, TR_NIL, 2)); + c.add_method(vm, TrSymbol_new(vm, "+"), newMethod(vm, (TrFunc *)TrString_add, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "<<"), newMethod(vm, (TrFunc *)TrString_push, TR_NIL, 1)); + c.add_method(vm, TrSymbol_new(vm, "<=>"), newMethod(vm, (TrFunc *)TrString_cmp, TR_NIL, 1)); } \ No newline at end of file diff --git a/vm/tr.go b/vm/tr.go index 0e6c12b..dff633a 100644 --- a/vm/tr.go +++ b/vm/tr.go @@ -7,10 +7,11 @@ #include #include -import( - "config"; - "vendor/khash"; -) +#ifdef __unix__ + #include +#else + #include "freegetopt/getopt.h" +#endif const ( TR_VERSION "0.0"; @@ -20,29 +21,6 @@ const ( /* allocation macros */ #define TR_REALLOC GC_realloc -/* raw hash macros */ -#define TR_KH_GET(KH,K) ({ \ - key := (K); \ - hash := (KH); \ - k := kh_get(RubyObject, hash, key); \ - k == kh_end(hash) ? TR_NIL : kh_value(hash, k); \ -}) -#define TR_KH_SET(KH,K,V) ({ \ - key := (K); \ - hash := (KH); \ - int ret; \ - k := kh_put(RubyObject, hash, key, &ret); \ - kh_value(hash, k) = (V); \ -}) -#define TR_KH_EACH(H,I,V,B) ({ \ - khiter_t __k##V; \ - for (__k##V = kh_begin(H); __k##V != kh_end(H); ++__k##V) \ - if (kh_exist((H), __k##V)) { \ - V := kh_value((H), __k##V); \ - B \ - } \ - }) - /* immediate values macros */ #define TR_IMMEDIATE(X) (X==TR_NIL || X==TR_TRUE || X==TR_FALSE || X==TR_UNDEF || TR_IS_FIX(X)) #define TR_IS_FIX(F) ((F) & 1) @@ -53,30 +31,6 @@ const ( #define TR_TRUE OBJ(4) #define TR_UNDEF OBJ(6) -/* API macros */ -#define tr_getivar(O,N) TR_KH_GET(Object*(O).ivars, tr_intern(N)) -#define tr_setivar(O,N,V) TR_KH_SET(Object*(O).ivars, tr_intern(N), V) -#define tr_getglobal(N) TR_KH_GET(vm.globals, tr_intern(N)) -#define tr_setglobal(N,V) TR_KH_SET(vm.globals, tr_intern(N), V) -#define tr_intern(S) TrSymbol_new(vm, (S)) -#define tr_raise(T,M,...) { \ - vm.throw_reason = TR_THROW_EXCEPTION; \ - vm.throw_value = TrException_new(vm, vm.c##T, tr_sprintf(vm, (M), ##__VA_ARGS__)); \ - return TR_UNDEF; \ -} - -#define tr_raise_errno(M) tr_raise(SystemCallError, "%s: %s", strerror(errno), (M)) -#define tr_def(C,N,F,A) (C).add_method(vm, tr_intern(N), newMethod(vm, (TrFunc *)(F), TR_NIL, (A))) -#define tr_metadef(O,N,F,A) Object_add_singleton_method(vm, (O), tr_intern(N), newMethod(vm, (TrFunc *)(F), TR_NIL, (A))) -#define tr_defclass(N,S) Object_const_set(vm, vm.self, tr_intern(N), newClass(vm, tr_intern(N), S)) -#define tr_defmodule(N) Object_const_set(vm, vm.self, tr_intern(N), newModule(vm, tr_intern(N))) - -#define tr_send(R,MSG,...) ({ \ - __argv[] := { (MSG), ##__VA_ARGS__ }; \ - Object_send(vm, R, sizeof(__argv)/sizeof(RubyObject), __argv); \ -}) -#define tr_send2(R,STR,...) tr_send((R), tr_intern(STR), ##__VA_ARGS__) - typedef unsigned long OBJ; typedef unsigned char u8; @@ -92,11 +46,31 @@ typedef enum { TR_T_MAX /* keep last */ } TR_T; -typedef enum { - TR_THROW_EXCEPTION, - TR_THROW_RETURN, - TR_THROW_BREAK -} TR_THROW_REASON; +const( + TR_T_Object = iota; + TR_T_Module; + TR_T_Class; + TR_T_Method; + TR_T_Binding; + TR_T_Symbol; + TR_T_String; + TR_T_Fixnum; + TR_T_Range; + TR_T_Regexp; + TR_T_NilClass; + TR_T_TrueClass; + TR_T_FalseClass, + TR_T_Array; + TR_T_Hash; + TR_T_Node; + TR_T_MAX; // keep last +) + +const( + TR_THROW_EXCEPTION = iota; + TR_THROW_RETURN; + TR_THROW_BREAK; +) type TrCallSite struct { class *RubyObject; @@ -123,7 +97,7 @@ type TrBinding struct { type TrString struct { type TR_T; class *RubyObject; - ivars *map[string] *RubyObject; + ivars map[string] *RubyObject; ptr *char; len size_t; interned bool; @@ -133,7 +107,7 @@ type TrSymbol TrString type TrRange struct { type TR_T; class *RubyObject; - ivars *map[string] *RubyObject; + ivars map[string] *RubyObject; first, last *RubyObject; exclusive int; } @@ -141,23 +115,17 @@ type TrRange struct { type TrHash struct { type TR_T; class *RubyObject; - ivars *map[string] *RubyObject; - kh *map[string] *RubyObject; + ivars map[string] RubyObject; + hash map[string] RubyObject; } type TrRegexp struct { type TR_T; class *RubyObject; - ivars *map[string] RubyObject; + ivars map[string] RubyObject; re *pcre; } -#ifdef __unix__ - #include -#else - #include "freegetopt/getopt.h" -#endif - func usage() { fmt.println("usage: tinyrb [options] [file]"); fmt.println("options:"; diff --git a/vm/vendor/khash.go b/vm/vendor/khash.go deleted file mode 100644 index 7caf5a5..0000000 --- a/vm/vendor/khash.go +++ /dev/null @@ -1,264 +0,0 @@ -/* The MIT License - - Copyright (c) 2008, by Attractive Chaos - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -/* - An example: - -#include "khash.h" -KHASH_MAP_INIT_INT(32, char) -int main() { - int ret, is_missing; - khiter_t k; - khash_t(32) *h = kh_init(32); - k = kh_put(32, h, 5, &ret); - if (!ret) kh_del(32, h, k); - kh_value(h, k) = 10; - k = kh_get(32, h, 10); - is_missing = (k == kh_end(h)); - k = kh_get(32, h, 5); - kh_del(32, h, k); - for (k = kh_begin(h); k != kh_end(h); ++k) - if (kh_exist(h, k)) kh_value(h, k) = 1; - kh_destroy(32, h); - return 0; -} -*/ - -#define AC_VERSION_KHASH_H "0.2.2" - -#include -#include -#include - -typedef uint32_t khint_t; -typedef khint_t khiter_t; - -#define __ac_HASH_PRIME_SIZE 32 -static const uint32_t __ac_prime_list[__ac_HASH_PRIME_SIZE] = -{ - 0ul, 3ul, 11ul, 23ul, 53ul, - 97ul, 193ul, 389ul, 769ul, 1543ul, - 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, - 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, - 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, - 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul, - 3221225473ul, 4294967291ul -}; - -#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) -#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) -#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) -#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) -#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) -#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) -#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) - -static const double __ac_HASH_UPPER = 0.77; - -#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ - typedef struct { \ - khint_t n_buckets, size, n_occupied, upper_bound; \ - uint32_t *flags; \ - khkey_t *keys; \ - khval_t *vals; \ - } kh_##name##_t; \ - static inline kh_##name##_t *kh_init_##name() { \ - return kh_##name##_t{}; \ - } \ - static inline void kh_destroy_##name(kh_##name##_t *h) \ - { \ - if (h) { \ - } \ - } \ - static inline void kh_clear_##name(kh_##name##_t *h) \ - { \ - if (h && h.flags) { \ - memset(h.flags, 0xaa, ((h.n_buckets>>4) + 1) * sizeof(uint32_t)); \ - h.size = h.n_occupied = 0; \ - } \ - } \ - static inline khint_t kh_get_##name(kh_##name##_t *h, khkey_t key) \ - { \ - if (h.n_buckets) { \ - khint_t inc, k, i, last; \ - k = __hash_func(key); i = k % h.n_buckets; \ - inc = 1 + k % (h.n_buckets - 1); last = i; \ - while (!__ac_isempty(h.flags, i) && (__ac_isdel(h.flags, i) || !__hash_equal(h.keys[i], key))) { \ - if (i + inc >= h.n_buckets) i = i + inc - h.n_buckets; \ - else i += inc; \ - if (i == last) return h.n_buckets; \ - } \ - return __ac_iseither(h.flags, i)? h.n_buckets : i; \ - } else return 0; \ - } \ - static inline void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ - { \ - uint32_t *new_flags = 0; \ - khint_t j = 1; \ - { \ - khint_t t = __ac_HASH_PRIME_SIZE - 1; \ - while (__ac_prime_list[t] > new_n_buckets) --t; \ - new_n_buckets = __ac_prime_list[t+1]; \ - if (h.size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; \ - else { \ - new_flags = make([]uint32,(new_n_buckets >> 4) + 1); \ - memset(new_flags, 0xaa, ((new_n_buckets>>4) + 1) * sizeof(uint32_t)); \ - if (h.n_buckets < new_n_buckets) { \ - h.keys = (khkey_t*)TR_REALLOC(h.keys, new_n_buckets * sizeof(khkey_t)); \ - if (kh_is_map) \ - h.vals = (khval_t*)TR_REALLOC(h.vals, new_n_buckets * sizeof(khval_t)); \ - } \ - } \ - } \ - if (j) { \ - for (j = 0; j != h.n_buckets; ++j) { \ - if (__ac_iseither(h.flags, j) == 0) { \ - khkey_t key = h.keys[j]; \ - khval_t val; \ - if (kh_is_map) val = h.vals[j]; \ - __ac_set_isdel_true(h.flags, j); \ - while (1) { \ - khint_t inc, k, i; \ - k = __hash_func(key); \ - i = k % new_n_buckets; \ - inc = 1 + k % (new_n_buckets - 1); \ - while (!__ac_isempty(new_flags, i)) { \ - if (i + inc >= new_n_buckets) i = i + inc - new_n_buckets; \ - else i += inc; \ - } \ - __ac_set_isempty_false(new_flags, i); \ - if (i < h.n_buckets && __ac_iseither(h.flags, i) == 0) { \ - { khkey_t tmp = h.keys[i]; h.keys[i] = key; key = tmp; } \ - if (kh_is_map) { khval_t tmp = h.vals[i]; h.vals[i] = val; val = tmp; } \ - __ac_set_isdel_true(h.flags, i); \ - } else { \ - h.keys[i] = key; \ - if (kh_is_map) h.vals[i] = val; \ - break; \ - } \ - } \ - } \ - } \ - if (h.n_buckets > new_n_buckets) { \ - h.keys = (khkey_t*)TR_REALLOC(h.keys, new_n_buckets * sizeof(khkey_t)); \ - if (kh_is_map) \ - h.vals = (khval_t*)TR_REALLOC(h.vals, new_n_buckets * sizeof(khval_t)); \ - } \ - h.flags = new_flags; \ - h.n_buckets = new_n_buckets; \ - h.n_occupied = h.size; \ - h.upper_bound = (khint_t)(h.n_buckets * __ac_HASH_UPPER + 0.5); \ - } \ - } \ - static inline khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ - { \ - khint_t x; \ - if (h.n_occupied >= h.upper_bound) { \ - if (h.n_buckets > (h.size<<1)) kh_resize_##name(h, h.n_buckets - 1); \ - else kh_resize_##name(h, h.n_buckets + 1); \ - } \ - { \ - khint_t inc, k, i, site, last; \ - x = site = h.n_buckets; k = __hash_func(key); i = k % h.n_buckets; \ - if (__ac_isempty(h.flags, i)) x = i; \ - else { \ - inc = 1 + k % (h.n_buckets - 1); last = i; \ - while (!__ac_isempty(h.flags, i) && (__ac_isdel(h.flags, i) || !__hash_equal(h.keys[i], key))) { \ - if (__ac_isdel(h.flags, i)) site = i; \ - if (i + inc >= h.n_buckets) i = i + inc - h.n_buckets; \ - else i += inc; \ - if (i == last) { x = site; break; } \ - } \ - if (x == h.n_buckets) { \ - if (__ac_isempty(h.flags, i) && site != h.n_buckets) x = site; \ - else x = i; \ - } \ - } \ - } \ - if (__ac_isempty(h.flags, x)) { \ - h.keys[x] = key; \ - __ac_set_isboth_false(h.flags, x); \ - ++h.size; ++h.n_occupied; \ - *ret = 1; \ - } else if (__ac_isdel(h.flags, x)) { \ - h.keys[x] = key; \ - __ac_set_isboth_false(h.flags, x); \ - ++h.size; \ - *ret = 2; \ - } else *ret = 0; \ - return x; \ - } \ - static inline void kh_del_##name(kh_##name##_t *h, khint_t x) \ - { \ - if (x != h.n_buckets && !__ac_iseither(h.flags, x)) { \ - __ac_set_isdel_true(h.flags, x); \ - --h.size; \ - } \ - } - -/* --- BEGIN OF HASH FUNCTIONS --- */ - -#define kh_int_hash_func(key) (uint32_t)(key) -#define kh_int_hash_equal(a, b) (a == b) -#define kh_int64_hash_func(key) (uint32_t)((key)>>33^(key)^(key)<<11) -#define kh_int64_hash_equal(a, b) (a == b) -static inline khint_t __ac_X31_hash_string(s *string) -{ - khint_t h = *s; - if (h) for (++s ; *s; ++s) h = (h << 5) - h + *s; - return h; -} -#define kh_str_hash_func(key) __ac_X31_hash_string(key) -#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) - -/* --- END OF HASH FUNCTIONS --- */ - -/* Other necessary macros... */ - -#define khash_t(name) kh_##name##_t - -#define kh_init(name) kh_init_##name() -#define kh_destroy(name, h) kh_destroy_##name(h) -#define kh_clear(name, h) kh_clear_##name(h) -#define kh_resize(name, h, s) kh_resize_##name(h, s) -#define kh_put(name, h, k, r) kh_put_##name(h, k, r) -#define kh_get(name, h, k) kh_get_##name(h, k) -#define kh_del(name, h, k) kh_del_##name(h, k) - -#define kh_exist(h, x) (!__ac_iseither((h).flags, (x))) -#define kh_key(h, x) ((h).keys[x]) -#define kh_val(h, x) ((h).vals[x]) -#define kh_value(h, x) ((h).vals[x]) -#define kh_begin(h) (khint_t)(0) -#define kh_end(h) ((h).n_buckets) -#define kh_size(h) ((h).size) -#define kh_n_buckets(h) ((h).n_buckets) - -/* More conenient interfaces */ - -#define KHASH_MAP_INIT_INT(name, khval_t) \ - KHASH_INIT(name, uint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) - -#define KHASH_MAP_INIT_INT64(name, khval_t) \ - KHASH_INIT(name, uint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) - -type kh_cstr_t *string; -#define KHASH_SET_INIT_STR(name) \ - KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) - -#define KHASH_MAP_INIT_STR(name, khval_t) \ - KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) \ No newline at end of file diff --git a/vm/vm.go b/vm/vm.go index b0eb821..4a2a0b2 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -9,7 +9,6 @@ import ( "bytes"; "tr"; "opcode"; - "internal"; "call"; ) @@ -17,6 +16,22 @@ type RubyObject interface { } +type Frame struct { + closure *Closure; + method *Method; // current called method + stack *RubyObject; + upvals *RubyObject; + self *RubyObject; + class *RubyObject; + filename *RubyObject; + line size_t; + previous *Frame; +} + +func (vm *RubyVM) newFrame(self, class, closure *RubyObject) Frame { + return Frame{ closure: closure, self: self, class: class, previous: vm.frame } +} + type RubyVM struct { symbols *map[string] string; globals *map[string] *RubyObject; @@ -79,7 +94,7 @@ func (vm *RubyVM) lookup(block *Block, receiver, msg *RubyObject, ip *MachineOP) s.method = method; s.message = msg; if method == TR_NIL { - s.method = Object_method(vm, receiver, tr_intern("method_missing")); + s.method = Object_method(vm, receiver, TrSymbol_new(vm, "method_missing")); s.method_missing = 1; } @@ -104,7 +119,7 @@ func (vm *RubyVM) defclass(name *RubyObject, block *Block, module int, super *Ru if !mod { // new module/class if module { - mod := newModule(vm, name); + mod := vm.newModule(name); } else { mod := newClass(vm, name, super ? super : vm.classes[TR_T_Object]); } @@ -114,9 +129,13 @@ func (vm *RubyVM) defclass(name *RubyObject, block *Block, module int, super *Ru // push a frame vm.cf++; - if vm.cf >= TR_MAX_FRAMES { tr_raise(SystemStackError, "Stack overflow"); } + if vm.cf >= TR_MAX_FRAMES { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cSystemStackError, tr_sprintf(vm, "Stack overflow")); + return TR_UNDEF; + } - frame := Frame{previous: vm.frame, method: nil, filename: nil, line: 0, self: mod, class: mod, closure: nil} + frame := newFrame(mod, mod, nil); if vm.cf == 0 { vm.top_frame = frame; } vm.frame = frame; vm.throw_reason = vm.throw_value = 0; @@ -133,7 +152,11 @@ func (vm *RubyVM) defclass(name *RubyObject, block *Block, module int, super *Ru func (vm *RubyVM) interpret_method(self *RubyObject, args []RubyObject) RubyObject { assert(vm.frame.method); block := Block *(vm.frame.method.data); - if args.Len() != block.argc { tr_raise(ArgumentError, "wrong number of arguments (%d for %lu)", args.Len(), block.argc); } + if args.Len() != block.argc { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cArgumentError, tr_sprintf(vm, "wrong number of arguments (%d for %lu)", args.Len(), block.argc)); + return TR_UNDEF; + } return vm.interpret(vm, vm.frame, block, 0, args, 0); } @@ -141,8 +164,16 @@ func (vm *RubyVM) interpret_method_with_defaults(self *RubyObject, args []RubyOb assert(vm.frame.method); block := Block *(vm.frame.method.data); req_argc := block.argc - block.defaults.Len(); - if args.Len() < req_argc { tr_raise(ArgumentError, "wrong number of arguments (%d for %d)", args.Len(), req_argc); } - if args.Len() > block.argc { tr_raise(ArgumentError, "wrong number of arguments (%d for %lu)", args.Len(), b.argc); } + if args.Len() < req_argc { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cArgumentError, tr_sprintf(vm, "wrong number of arguments (%d for %d)", args.Len(), req_argc)); + return TR_UNDEF; + } + if args.Len() > block.argc { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cArgumentError, tr_sprintf(vm, "wrong number of arguments (%d for %lu)", args.Len(), b.argc)); + return TR_UNDEF; + } // index in defaults table or -1 for none if (i := args.Len() - req_argc - 1) < 0 { return vm.interpret(vm.frame, block, 0, args, 0); @@ -156,8 +187,12 @@ func (vm *RubyVM) interpret_method_with_splat(self *RubyObject, args []RubyObjec block := Block *(vm.frame.method.data); // TODO support defaults assert(block.defaults.Len() == 0 && "defaults with splat not supported for now"); - if args.Len() < b.argc - 1 { tr_raise(ArgumentError, "wrong number of arguments (%d for %lu)", args.Len(), block.argc - 1); } - argv[block.argc - 1] = newArray3(vm, args.Len() - block.argc + 1, &argv[block.argc - 1]); + if args.Len() < b.argc - 1 { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cArgumentError, tr_sprintf(vm, "wrong number of arguments (%d for %lu)", args.Len(), block.argc -1)); + return TR_UNDEF; + } + argv[block.argc - 1] = vm.newArray3(args.Len() - block.argc + 1, &argv[block.argc - 1]); return vm.interpret(vm.frame, block, 0, args[0:block.argc - 1], 0); } @@ -179,15 +214,23 @@ func (vm *RubyVM) defmethod(frame *Frame, name *RubyObject, block *Block, meta b func (vm *RubyVM) yield(frame *Frame, args []RubyObject) RubyObject { closure := frame.closure; - if !closure { tr_raise(LocalJumpError, "no block given"); } + if !closure { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cLocalJumpError, tr_sprintf(vm, "no block given")); + return TR_UNDEF; + } // push a frame vm.cf++; - if vm.cf >= TR_MAX_FRAMES { tr_raise(SystemStackError, "Stack overflow"); } + if vm.cf >= TR_MAX_FRAMES { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cSystemStackError, tr_sprintf(vm, "Stack overflow")); + return TR_UNDEF; + } - new_frame := Frame{previous: vm.frame, method: nil, filename: nil, line: 0, self: closure.self, class: closure.class, closure: closure.parent} - if vm.cf == 0 { vm.top_frame = new_frame; } - vm.frame = new_frame; + closed_frame := newFrame(closure.self, closure.class, closure.parent); + if vm.cf == 0 { vm.top_frame = closed_frame; } + vm.frame = closed_frame; vm.throw_reason = vm.throw_value = 0; // execute BODY inside the frame @@ -245,7 +288,7 @@ func (vm *RubyVM) TrVM_interpret(frame *Frame, block *Block, start, args []RubyO stack[i.A] = i.B case TR_OP_NEWARRAY: - stack[i.A] = newArray3(vm, i.B, &stack[i.A + 1]) + stack[i.A] = vm.newArray3(i.B, &stack[i.A + 1]) case TR_OP_NEWHASH: stack[i.A] = TrHash_new2(vm, i.B, &stack[i.A + 1]) @@ -275,16 +318,16 @@ func (vm *RubyVM) TrVM_interpret(frame *Frame, block *Block, start, args []RubyO stack[i.A] = *(upvals[i.B].value) case TR_OP_SETIVAR: - TR_KH_SET(Object *(frame.self).ivars, k[i.Get_Bx()], stack[i.A]) + frame.self.ivars[k[i.Get_Bx()]] = stack[i.A]; case TR_OP_GETIVAR: - stack[i.A] = TR_KH_GET(Object *(frame.self).ivars, k[i.Get_Bx()]) + stack[i.A] = frame.self.ivars[k[i.Get_Bx()]]; case TR_OP_SETCVAR: - TR_KH_SET(Object *(frame.class).ivars, k[i.Get_Bx()], stack[i.A]) + frame.class.ivars[k[i.Get_Bx()]] = stack[i.A]; case TR_OP_GETCVAR: - stack[i.A] = TR_KH_GET(Object*(frame.class).ivars, k[i.Get_Bx()]) + stack[i.A] := frame.class.ivars[k[i.Get_Bx]] || TR_NIL; case TR_OP_SETCONST: Object_const_set(vm, frame.self, k[i.Get_Bx()], stack[i.A]) @@ -293,11 +336,11 @@ func (vm *RubyVM) TrVM_interpret(frame *Frame, block *Block, start, args []RubyO stack[i.A] = Object_const_get(vm, frame.self, k[i.Get_Bx()]) case TR_OP_SETGLOBAL: - TR_KH_SET(vm.globals, k[i.Get_Bx()], stack[i.A]) + vm.globals[k[i.Get_Bx()]] = stack[i.A]; case TR_OP_GETGLOBAL: - stack[i.A] = TR_KH_GET(vm.globals, k[i.Get_Bx()]) - + stack[i.A] = vm.globals[k[i.Get_Bx()]] || TR_NIL; + // method calling case TR_OP_LOOKUP: if RubyObject(call = TrCallSite *(vm.lookup(block, stack[i.A], k[i.Get_Bx()], ip))) == TR_UNDEF { return TR_UNDEF; } @@ -406,7 +449,7 @@ func (vm *RubyVM) TrVM_interpret(frame *Frame, block *Block, start, args []RubyO if stack[i.A] == TR_NIL || stack[i.A] == TR_FALSE { ip += i.Get_sBx(); } // arithmetic optimizations - // TODO cache lookup in tr_send and force send if method was redefined + // TODO cache lookup and force send if method was redefined case TR_OP_ADD: if i.B & (1 << (SIZE_B - 1) { rb := k[i.B & ~0x100] @@ -423,7 +466,7 @@ func (vm *RubyVM) TrVM_interpret(frame *Frame, block *Block, start, args []RubyO if TR_IS_FIX(rb) { stack[i.A] = TR_INT2FIX(TR_FIX2INT(rb) + TR_FIX2INT(rc)) } else { - stack[i.A] = tr_send(rb, vm.sADD, rc) + stack[i.A] = Object_send(vm, rb, 2, { vm.sADD, rc }); } case TR_OP_SUB: @@ -442,7 +485,7 @@ func (vm *RubyVM) TrVM_interpret(frame *Frame, block *Block, start, args []RubyO if TR_IS_FIX(rb) { stack[i.A] = TR_INT2FIX(TR_FIX2INT(rb) - TR_FIX2INT(rc)) } else { - stack[i.A] = tr_send(rb, vm.sSUB, rc) + stack[i.A] = Object_send(vm, rb, 2, { vm.sSUB, rc }); } case TR_OP_LT: @@ -466,7 +509,7 @@ func (vm *RubyVM) TrVM_interpret(frame *Frame, block *Block, start, args []RubyO } } else { - stack[i.A] = tr_send(rb, vm.sLT, rc) + stack[i.A] = Object_send(vm, rb, 2, { vm.sLT, rc } ); } case TR_OP_NEG: @@ -483,7 +526,7 @@ func (vm *RubyVM) TrVM_interpret(frame *Frame, block *Block, start, args []RubyO } else { rc := stack[i.C] } - stack[i.A] = tr_send(rb, vm.sNEG, rc) + stack[i.A] = Object_send(vm, rb, 2, { vm.sNEG, rc }); } case TR_OP_NOT: @@ -512,7 +555,7 @@ func (vm *RubyVM) TrVM_interpret(frame *Frame, block *Block, start, args []RubyO /* returns the backtrace of the current call frames */ func (vm *RubyVM) backtrace() RubyObject { - backtrace := newArray(vm); + backtrace := vm.newArray(); if vm.frame { // skip a frame since it's the one doing the raising frame := vm.frame.previous; @@ -539,7 +582,7 @@ func (vm *RubyVM) backtrace() RubyObject { } else { context := tr_sprintf(vm, "\tfrom %s:%lu", filename, f.line); } - backtrace.kv.Push(context); + backtrace.Push(context); frame = frame.previous; } } @@ -562,24 +605,27 @@ func (vm *RubyVM) eval(code *string, filename *string) RubyObject { func (vm *RubyVM) load(filename *string) RubyObject { stats = new(stat); - if stat(filename, &stats) == -1 { tr_raise_errno(filename); } - - if fp := fopen(filename, "rb") { - s := make([]byte, stats.st_size + 1); - if fread(s, 1, stats.st_size, fp) == stats.st_size { return vm.eval(s, filename); } - tr_raise_errno(filename); - } else { - tr_raise_errno(filename); + if stat(filename, &stats) != -1 { + if fp := fopen(filename, "rb") { + s := make([]byte, stats.st_size + 1); + if fread(s, 1, stats.st_size, fp) == stats.st_size { return vm.eval(s, filename); } + } } - return TR_NIL; + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cSystemCallError, tr_sprintf(vm, "%s: %s", strerror(errno), filename); + return TR_UNDEF; } func (vm *RubyVM) run(block *Block, self, class *RubyObject, args []RubyObject) RubyObject { // push a frame vm.cf++; - if vm.cf >= TR_MAX_FRAMES { tr_raise(SystemStackError, "Stack overflow"); } + if vm.cf >= TR_MAX_FRAMES { + vm.throw_reason = TR_THROW_EXCEPTION; + vm.throw_value = TrException_new(vm, vm.cSystemStackError, tr_sprintf(vm, "Stack overflow"); + return TR_UNDEF; + } - frame := Frame{self: self, method: nil, class: class, previous: vm.frame, closure: false, filename: nil, line: 0}; + frame := newFrame(self, class, nil); if vm.cf == 0 { vm.top_frame = frame; } vm.frame = frame; vm.throw_reason = vm.throw_value = 0; @@ -594,9 +640,9 @@ func (vm *RubyVM) run(block *Block, self, class *RubyObject, args []RubyObject) func newRubyVM() *RubyVM { vm := new(RubyVM); - vm.symbols = kh_init(str); - vm.globals = kh_init(RubyObject); - vm.consts = kh_init(RubyObject); + vm.symbols = make(map[string] string); + vm.globals = make(map[string] RubyObject); + vm.consts = make(map[string] RubyObject); vm.debug = 0; // bootstrap core classes, order is important here, so careful, mkay? @@ -621,8 +667,8 @@ func newRubyVM() *RubyVM { objectc.class = newMetaClass(vm, objectc.class); // Some symbols are created before Object, so make sure all have proper class. - TR_KH_EACH(vm.symbols, i, sym, { Object*(sym).class = RubyObject(symbolc); }); - + for key, value := range vm.symbols { value.class = RubyObject(symbolc); } + // bootstrap rest of core classes, order is no longer important here Object_init(vm); TrError_init(vm); @@ -640,11 +686,11 @@ func newRubyVM() *RubyVM { vm.cf = -1; // cache some commonly used values - vm.sADD = tr_intern("+"); - vm.sSUB = tr_intern("-"); - vm.sLT = tr_intern("<"); - vm.sNEG = tr_intern("@-"); - vm.sNOT = tr_intern("!"); + vm.sADD = TrSymbol_new(vm, "+"); + vm.sSUB = TrSymbol_new(vm, "-"); + vm.sLT = TrSymbol_new(vm, "<"); + vm.sNEG = TrSymbol_new(vm, "@-"); + vm.sNOT = TrSymbol_new(vm, "!"); if vm.load("lb/boot.rb") == TR_UNDEF && vm.throw_reason == TR_THROW_EXCEPTION { TrException_default_handler(vm, vm.throw_value));