From 9a09ecb86557c3949c884675478137a80a6b87fb Mon Sep 17 00:00:00 2001 From: Lara Aydin Date: Thu, 9 Jan 2020 17:25:47 -0800 Subject: [PATCH] Add a rand() that can handle 0-2 arguments to generate random numbers using math/rand Co-authored-by: Lara Aydin Co-authored-by: Michael Grosser --- vm/class.go | 21 +++++++++++++++++++-- vm/class_test.go | 44 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/vm/class.go b/vm/class.go index 2528004a1..940d9a4ec 100644 --- a/vm/class.go +++ b/vm/class.go @@ -8,6 +8,7 @@ import ( "time" "sort" + "math/rand" "github.com/goby-lang/goby/vm/classes" "github.com/goby-lang/goby/vm/errors" @@ -36,14 +37,14 @@ type RClass struct { var externalClasses = map[string][]ClassLoader{} var externalClassLock sync.Mutex -// RegisterExternalClass will add the given class to the global registry of available classes +// RegisterExternalClass will add the given class to the global registery of available classes func RegisterExternalClass(path string, c ...ClassLoader) { externalClassLock.Lock() externalClasses[path] = c externalClassLock.Unlock() } -// ClassLoader can be registered with a vm so that it can load this library at vm creation +// ClassLoader can be registerd with a vm so that it can load this library at vm creation type ClassLoader = func(*VM) error func buildMethods(m map[string]Method) []*BuiltinMethodObject { @@ -1308,6 +1309,22 @@ var builtinClassCommonInstanceMethods = []*BuiltinMethodObject{ }, }, + { + Name: "rand", + Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object { + aLen := len(args) + switch aLen { + case 0: + return t.vm.initFloatObject(rand.Float64()) + case 1: + return t.vm.InitIntegerObject(rand.Intn(args[0].Value().(int))) + case 2: + return t.vm.InitIntegerObject(rand.Intn(args[1].Value().(int) - args[0].Value().(int) + 1) + args[0].Value().(int)) + default: + return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, errors.WrongNumberOfArgument, 2, aLen) + } + }, + }, { // A predicate class method that returns `true` if the object has an ability to respond to the method, otherwise `false`. // Note that signs like `+` or `?` should be String literal. diff --git a/vm/class_test.go b/vm/class_test.go index 87fda0081..fcd4d0bae 100644 --- a/vm/class_test.go +++ b/vm/class_test.go @@ -985,7 +985,7 @@ func TestInheritsMethodMissingMethod(t *testing.T) { {` class Bar end - + Bar.new.inherits_method_missing? `, false}, @@ -993,7 +993,7 @@ func TestInheritsMethodMissingMethod(t *testing.T) { class Bar inherits_method_missing end - + Bar.new.inherits_method_missing? `, true}, @@ -1132,6 +1132,40 @@ func TestRaiseMethodFail(t *testing.T) { } } +func TestRandMethod(t *testing.T) { + tests := []struct { + input string + expected string + }{ + {`rand.class.name`, "Float"}, + {`rand(1).class.name`, "Integer"}, + {`rand(1, 2).class.name`, "Integer"}, + {`(60 < rand(64, 66)).to_s`, "true"}, + {`(70 > rand(64, 66)).to_s`, "true"}, + } + for i, tt := range tests { + v := initTestVM() + evaluated := v.testEval(t, tt.input, getFilename()) + VerifyExpected(t, i, evaluated, tt.expected) + v.checkCFP(t, i, 0) + v.checkSP(t, i, 1) + } +} + +func TestRandMethodFail(t *testing.T) { + testsFail := []errorTestCase{ + {`rand(1, 10, 100)`, "ArgumentError: Expect 2 argument(s). got: 3", 1}, + } + + for i, tt := range testsFail { + v := initTestVM() + evaluated := v.testEval(t, tt.input, getFilename()) + checkErrorMsg(t, i, evaluated, tt.expected) + v.checkCFP(t, i, tt.expectedCFP) + v.checkSP(t, i, 1) + } +} + func TestRespondToMethod(t *testing.T) { tests := []struct { input string @@ -1642,7 +1676,7 @@ func TestClassSingletonClassMethod(t *testing.T) { `, "#"}, {` module Bar; end - + Bar.singleton_class.name `, "#"}, // Check if this works on non-class objects @@ -1654,12 +1688,12 @@ func TestClassSingletonClassMethod(t *testing.T) { // Below is for testing module inheritance chain {` module Bar; end - + Bar.singleton_class.class.name `, "Class"}, {` module Bar; end - + Bar.singleton_class.superclass.name `, "Module"}, }