From 5452864c21cf496f0ab6c3878b4ad9e6b43253ab Mon Sep 17 00:00:00 2001 From: Steven Deutsch Date: Sat, 17 Mar 2018 18:58:58 -0500 Subject: [PATCH 1/6] Add Float# abs, ceil, floor --- vm/float.go | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ vm/float_test.go | 56 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) diff --git a/vm/float.go b/vm/float.go index 6c7cb5a0b..6a243f264 100644 --- a/vm/float.go +++ b/vm/float.go @@ -320,6 +320,70 @@ var builtinFloatInstanceMethods = []*BuiltinMethodObject{ }, }, + { + // Returns the Float as a positive value. + // + // ```Ruby + // -3.14.abs # => 3.14 + // 3.14.abs # => 3.14 + // ``` + // @return [Float] + Name: "abs", + Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object { + if len(args) != 0 { + return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, "Expect 0 argument. got=%v", strconv.Itoa(len(args))) + } + r := receiver.(*FloatObject) + result := math.Abs(r.value) + return t.vm.initFloatObject(result) + }, + }, + { + // Returns the smallest Integer greater than or equal to self. + // + // ```Ruby + // 1.2.ceil # => 2 + // 2.ceil # => 2 + // -1.2.ceil # => -1 + // -2.ceil # => -2 + // ``` + // @return [Integer] + Name: "ceil", + Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object { + // TODO: Make ceil accept arguments + if len(args) != 0 { + return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, "Expect 0 argument. got=%v", strconv.Itoa(len(args))) + } + r := receiver.(*FloatObject) + result := math.Ceil(r.value) + newInt := t.vm.InitIntegerObject(int(result)) + newInt.flag = i + return newInt + }, + }, + { + // Returns the largest Integer less than or equal to self. + // + // ```Ruby + // 1.2.floor # => 1 + // 2.0.floor # => 2 + // -1.2.floor # => -2 + // -2.0.floor # => -2 + // ``` + // @return [Integer] + Name: "floor", + Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object { + // TODO: Make floor accept arguments + if len(args) != 0 { + return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, "Expect 0 argument. got=%v", strconv.Itoa(len(args))) + } + r := receiver.(*FloatObject) + result := math.Floor(r.value) + newInt := t.vm.InitIntegerObject(int(result)) + newInt.flag = i + return newInt + }, + }, } // Internal functions =================================================== diff --git a/vm/float_test.go b/vm/float_test.go index b82b2e37b..d6b12acf1 100644 --- a/vm/float_test.go +++ b/vm/float_test.go @@ -337,6 +337,23 @@ func TestFloatNumberOfDigit(t *testing.T) { v.checkSP(t, i, 1) } } +func TestFloatAbs(t *testing.T) { + tests := []struct { + input string + expected interface{} + }{ + {"34.56.abs", 34.56}, + {"-34.56.abs", 34.56}, + } + + 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 TestFloatMinusZero(t *testing.T) { tests := []struct { @@ -369,6 +386,26 @@ func TestFloatMinusZero(t *testing.T) { } } +func TestFloatCeil(t *testing.T) { + tests := []struct { + input string + expected interface{} + }{ + {"1.2.ceil", 2}, + {"2.0.ceil", 2}, + {"-1.2.ceil", -1}, + {"-2.0.ceil", -2}, + } + + for i, tt := range tests { + v := initTestVM() + evaluated := v.testEval(t, tt.input, getFilename()) + VerifyExpected(t, i, evaluated, tt.expected) + v.checkCFP(t, i, 0) + v.checkSP(t, i, 1) + } +} + func TestFloatDupMethod(t *testing.T) { tests := []struct { input string @@ -385,3 +422,22 @@ func TestFloatDupMethod(t *testing.T) { v.checkSP(t, i, 1) } } +func TestFloatFloor(t *testing.T) { + tests := []struct { + input string + expected interface{} + }{ + {"1.2.floor", 1}, + {"2.0.floor", 2}, + {"-1.2.floor", -2}, + {"-2.0.floor", -2}, + } + + for i, tt := range tests { + v := initTestVM() + evaluated := v.testEval(t, tt.input, getFilename()) + VerifyExpected(t, i, evaluated, tt.expected) + v.checkCFP(t, i, 0) + v.checkSP(t, i, 1) + } +} From bf8554c6cd5f4fe0500079b715a894def00cf0f2 Mon Sep 17 00:00:00 2001 From: Steven Deutsch Date: Mon, 26 Mar 2018 04:04:28 -0500 Subject: [PATCH 2/6] Add Float#zero? --- vm/float.go | 32 ++++++++++++++++++++++++-------- vm/float_test.go | 18 ++++++++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/vm/float.go b/vm/float.go index 6a243f264..f098d59fd 100644 --- a/vm/float.go +++ b/vm/float.go @@ -317,15 +317,14 @@ var builtinFloatInstanceMethods = []*BuiltinMethodObject{ Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object { r := receiver.(*FloatObject) return t.vm.initGoObject(&r.value) - }, }, { // Returns the Float as a positive value. // // ```Ruby - // -3.14.abs # => 3.14 - // 3.14.abs # => 3.14 + // -34.56.abs # => 34.56 + // 34.56.abs # => 34.56 // ``` // @return [Float] Name: "abs", @@ -342,10 +341,10 @@ var builtinFloatInstanceMethods = []*BuiltinMethodObject{ // Returns the smallest Integer greater than or equal to self. // // ```Ruby - // 1.2.ceil # => 2 - // 2.ceil # => 2 + // 1.2.ceil # => 2 + // 2.ceil # => 2 // -1.2.ceil # => -1 - // -2.ceil # => -2 + // -2.ceil # => -2 // ``` // @return [Integer] Name: "ceil", @@ -365,8 +364,8 @@ var builtinFloatInstanceMethods = []*BuiltinMethodObject{ // Returns the largest Integer less than or equal to self. // // ```Ruby - // 1.2.floor # => 1 - // 2.0.floor # => 2 + // 1.2.floor # => 1 + // 2.0.floor # => 2 // -1.2.floor # => -2 // -2.0.floor # => -2 // ``` @@ -384,6 +383,23 @@ var builtinFloatInstanceMethods = []*BuiltinMethodObject{ return newInt }, }, + { + // Returns true if Float is equal to 0.0 + // + // ```Ruby + // 0.0.zero? # => true + // 1.0.zero? # => false + // ``` + // @return [Boolean] + Name: "zero?", + Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object { + if len(args) != 0 { + return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, "Expect 0 argument. got=%v", strconv.Itoa(len(args))) + } + r := receiver.(*FloatObject) + return toBooleanObject(r.value == 0.0) + }, + }, } // Internal functions =================================================== diff --git a/vm/float_test.go b/vm/float_test.go index d6b12acf1..5421ae343 100644 --- a/vm/float_test.go +++ b/vm/float_test.go @@ -441,3 +441,21 @@ func TestFloatFloor(t *testing.T) { v.checkSP(t, i, 1) } } + +func TestZero(t *testing.T) { + tests := []struct { + input string + expected interface{} + }{ + {"0.0.zero?", true}, + {"1.0.zero?", false}, + } + + 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) + } +} From afc4d178c66511023bc7cfa1543e5126ad63993e Mon Sep 17 00:00:00 2001 From: Steven Deutsch Date: Mon, 26 Mar 2018 04:14:28 -0500 Subject: [PATCH 3/6] Add Float#positive? --- vm/float.go | 18 ++++++++++++++++++ vm/float_test.go | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/vm/float.go b/vm/float.go index f098d59fd..634fcf34e 100644 --- a/vm/float.go +++ b/vm/float.go @@ -400,6 +400,24 @@ var builtinFloatInstanceMethods = []*BuiltinMethodObject{ return toBooleanObject(r.value == 0.0) }, }, + { + // Returns true if Float is larger than 0.0 + // + // ```Ruby + // -1.0.positive? # => false + // 0.0.positive? # => false + // 1.0.positive? # => true + // ``` + // @return [Boolean] + Name: "positive?", + Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object { + if len(args) != 0 { + return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, "Expect 0 argument. got=%v", strconv.Itoa(len(args))) + } + r := receiver.(*FloatObject) + return toBooleanObject(r.value > 0.0) + }, + }, } // Internal functions =================================================== diff --git a/vm/float_test.go b/vm/float_test.go index 5421ae343..f3c6cce66 100644 --- a/vm/float_test.go +++ b/vm/float_test.go @@ -459,3 +459,22 @@ func TestZero(t *testing.T) { v.checkSP(t, i, 1) } } + +func TestPositive(t *testing.T) { + tests := []struct { + input string + expected interface{} + }{ + {"-1.0.positive?", false}, + {"0.0.positive?", false}, + {"1.0.positive?", 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) + } +} From 53c79cf33517a202769ed404c56600cb69db96ba Mon Sep 17 00:00:00 2001 From: Steven Deutsch Date: Mon, 26 Mar 2018 04:22:37 -0500 Subject: [PATCH 4/6] Add Float#negative? --- vm/float.go | 18 ++++++++++++++++++ vm/float_test.go | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/vm/float.go b/vm/float.go index 634fcf34e..b0519fce3 100644 --- a/vm/float.go +++ b/vm/float.go @@ -418,6 +418,24 @@ var builtinFloatInstanceMethods = []*BuiltinMethodObject{ return toBooleanObject(r.value > 0.0) }, }, + { + // Returns true if Float is less than 0.0 + // + // ```Ruby + // -1.0.negative? # => true + // 0.0.negative? # => false + // 1.0.negative? # => false + // ``` + // @return [Boolean] + Name: "negative?", + Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object { + if len(args) != 0 { + return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, "Expect 0 argument. got=%v", strconv.Itoa(len(args))) + } + r := receiver.(*FloatObject) + return toBooleanObject(r.value < 0.0) + }, + }, } // Internal functions =================================================== diff --git a/vm/float_test.go b/vm/float_test.go index f3c6cce66..2df182da0 100644 --- a/vm/float_test.go +++ b/vm/float_test.go @@ -478,3 +478,22 @@ func TestPositive(t *testing.T) { v.checkSP(t, i, 1) } } + +func TestNegative(t *testing.T) { + tests := []struct { + input string + expected interface{} + }{ + {"-1.0.negative?", true}, + {"0.0.negative?", false}, + {"1.0.negative?", false}, + } + + 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) + } +} From 77e3229ecf53a6eaebde45c18f4e6c6d3ea7905c Mon Sep 17 00:00:00 2001 From: st0012 Date: Thu, 10 May 2018 11:51:16 +0800 Subject: [PATCH 5/6] Reorganize float api tests. --- vm/float_test.go | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/vm/float_test.go b/vm/float_test.go index 2df182da0..c25abbb20 100644 --- a/vm/float_test.go +++ b/vm/float_test.go @@ -337,6 +337,8 @@ func TestFloatNumberOfDigit(t *testing.T) { v.checkSP(t, i, 1) } } +// API tests + func TestFloatAbs(t *testing.T) { tests := []struct { input string @@ -442,13 +444,14 @@ func TestFloatFloor(t *testing.T) { } } -func TestZero(t *testing.T) { +func TestFloatNegative(t *testing.T) { tests := []struct { input string expected interface{} }{ - {"0.0.zero?", true}, - {"1.0.zero?", false}, + {"-1.0.negative?", true}, + {"0.0.negative?", false}, + {"1.0.negative?", false}, } for i, tt := range tests { @@ -460,7 +463,7 @@ func TestZero(t *testing.T) { } } -func TestPositive(t *testing.T) { +func TestFloatPositive(t *testing.T) { tests := []struct { input string expected interface{} @@ -479,14 +482,13 @@ func TestPositive(t *testing.T) { } } -func TestNegative(t *testing.T) { +func TestFloatZero(t *testing.T) { tests := []struct { input string expected interface{} }{ - {"-1.0.negative?", true}, - {"0.0.negative?", false}, - {"1.0.negative?", false}, + {"0.0.zero?", true}, + {"1.0.zero?", false}, } for i, tt := range tests { @@ -497,3 +499,4 @@ func TestNegative(t *testing.T) { v.checkSP(t, i, 1) } } + From 86f94a0fef639bd57f992ac90bc4e6d8cb9cf940 Mon Sep 17 00:00:00 2001 From: st0012 Date: Thu, 10 May 2018 12:09:31 +0800 Subject: [PATCH 6/6] Add Float#round method. --- vm/float.go | 34 ++++++++++++++++++++++++++++++++++ vm/float_test.go | 26 +++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/vm/float.go b/vm/float.go index b0519fce3..8bd4392dc 100644 --- a/vm/float.go +++ b/vm/float.go @@ -436,6 +436,40 @@ var builtinFloatInstanceMethods = []*BuiltinMethodObject{ return toBooleanObject(r.value < 0.0) }, }, + { + // Rounds float to a given precision in decimal digits (default 0 digits) + // + // ```Ruby + // 1.115.round # => 1 + // 1.115.round(1) # => 1.1 + // 1.115.round(2) # => 1.12 + // -1.115.round # => -1 + // -1.115.round(1) # => -1.1 + // -1.115.round(2) # => -1.12 + // ``` + // @return [Integer] + Name: "round", + Fn: func(receiver Object, sourceLine int, t *Thread, args []Object, blockFrame *normalCallFrame) Object { + var precision int + + if len(args) > 1 { + return t.vm.InitErrorObject(errors.ArgumentError, sourceLine, "Expect 0 or 1 argument. got=%v", strconv.Itoa(len(args))) + } else if len(args) == 1 { + int, ok := args[0].(*IntegerObject) + + if !ok { + return t.vm.InitErrorObject(errors.TypeError, sourceLine, errors.WrongArgumentTypeFormat, classes.IntegerClass, args[0].Class().Name) + } + + precision = int.value + } + + f := receiver.(*FloatObject).floatValue() + n := math.Pow10(precision) + + return t.vm.initFloatObject(math.Round(f*n) / n) + }, + }, } // Internal functions =================================================== diff --git a/vm/float_test.go b/vm/float_test.go index c25abbb20..1c3e0d7fc 100644 --- a/vm/float_test.go +++ b/vm/float_test.go @@ -337,6 +337,7 @@ func TestFloatNumberOfDigit(t *testing.T) { v.checkSP(t, i, 1) } } + // API tests func TestFloatAbs(t *testing.T) { @@ -482,6 +483,30 @@ func TestFloatPositive(t *testing.T) { } } +func TestFloatRound(t *testing.T) { + tests := []struct { + input string + expected interface{} + }{ + {"1.115.round", 1.0}, + {"1.115.round(1)", 1.1}, + {"1.115.round(2)", 1.12}, + {"-1.115.round", -1.0}, + {"-1.115.round(1)", -1.1}, + {"-1.115.round(2)", -1.12}, + {"1.115.round(-1)", 0.0}, + {"-1.115.round(-1)", 0.0}, + } + + 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 TestFloatZero(t *testing.T) { tests := []struct { input string @@ -499,4 +524,3 @@ func TestFloatZero(t *testing.T) { v.checkSP(t, i, 1) } } -