Repository: goby-lang/goby (go)

Issue:
Support for Array.new with an argument
We were trying to do `Array.new(n)` to get an array with n elements, but it's not supported. 



Pull request description:
Support Array.new(args)
This PR supports multiple ways to initialize an Array:

```ruby
Array.new(3) #=> [nil, nil, nil]
Array.new(3, true) #=> [true, true, true]
Array.new(3) do |i|
  i * 2
end #=> [0, 2, 4]
```

Closes #831 

Patch:
diff --git a/vm/array.go b/vm/array.go
index e4902ca..774ecb9 100644
--- a/vm/array.go
+++ b/vm/array.go
@@ -25,7 +25,45 @@ var builtinArrayClassMethods = []*BuiltinMethodObject{
 	{
 		Name: "new",
 		Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object {
-			return t.vm.InitNoMethodError(sourceLine, "new", receiver)
+			if len(args) > 2 {
+				return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, errors.WrongNumberOfArgumentRange, 1, 2, len(args))
+			}
+
+			if len(args) >= 1 {
+				n, ok := args[0].(*IntegerObject)
+
+				if !ok {
+					return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, errors.WrongArgumentTypeFormat, "Integer", args[0].Class().Name)
+				}
+
+				if n.value < 0 {
+					return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, "Negative Array Size")
+				}
+
+				elems := make([]Object, n.value)
+
+				if blockFrame != nil && !blockIsEmpty(blockFrame) {
+					for i := range elems {
+						elems[i] = t.builtinMethodYield(blockFrame, t.vm.InitIntegerObject(i)).Target
+					}
+				} else {
+					var elem Object
+
+					if len(args) == 2 {
+						elem = args[1]
+					} else {
+						elem = NULL
+					}
+
+					for i := 0; i < n.value; i++ {
+						elems[i] = elem
+					}
+				}
+
+				return t.vm.InitArrayObject(elems)
+			}
+
+			return t.vm.InitArrayObject([]Object{})
 		},
 	},
 }


Test patch:
diff --git a/vm/array_test.go b/vm/array_test.go
index ab6cf66..039a451 100644
--- a/vm/array_test.go
+++ b/vm/array_test.go
@@ -4,6 +4,59 @@ import (
 	"testing"
 )
 
+func TestArrayInitialization(t *testing.T) {
+	tests := []struct {
+		input    string
+		expected []interface{}
+	}{
+
+		{`
+			Array.new
+		`, []interface{}{}},
+		{`
+			Array.new(3)
+		`, []interface{}{nil, nil, nil}},
+		{`
+			Array.new(3, true)
+		`, []interface{}{true, true, true}},
+		{`
+			Array.new(3) do |i|
+             i * 2
+			end
+		`, []interface{}{0, 2, 4}},
+	}
+
+	for i, tt := range tests {
+		vm := initTestVM()
+		evaluated := vm.testEval(t, tt.input, getFilename())
+		verifyArrayObject(t, i, evaluated, tt.expected)
+		vm.checkCFP(t, i, 0)
+		vm.checkSP(t, i, 1)
+	}
+}
+
+func TestArrayInitializationFail(t *testing.T) {
+	testsFail := []errorTestCase{
+		{`
+			Array.new(1, 2, 3)
+		`, "ArgumentError: Expect 1 to 2 argument(s). got: 3", 1},
+		{`
+			Array.new("foo")
+		`, "ArgumentError: Expect argument to be Integer. got: String", 1},
+		{`
+			Array.new(-1)
+		`, "ArgumentError: Negative Array Size", 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 TestArrayClassSuperclass(t *testing.T) {
 	tests := []struct {
 		input    string
diff --git a/vm/error_test.go b/vm/error_test.go
index 302f617..2648729 100644
--- a/vm/error_test.go
+++ b/vm/error_test.go
@@ -199,7 +199,6 @@ func TestNoMethodErrorOnNew(t *testing.T) {
 		{`String.new`, "NoMethodError: Undefined Method 'new' for String", 1},
 		{`Integer.new`, "NoMethodError: Undefined Method 'new' for Integer", 1},
 		{`Hash.new`, "NoMethodError: Undefined Method 'new' for Hash", 1},
-		{`Array.new`, "NoMethodError: Undefined Method 'new' for Array", 1},
 		{`Boolean.new`, "NoMethodError: Undefined Method 'new' for Boolean", 1},
 		{`Null.new`, "NoMethodError: Undefined Method 'new' for Null", 1},
 	}