mirror of
https://github.com/MontFerret/ferret.git
synced 2025-08-13 19:52:52 +02:00
Bugfix/#295 arithmetic operators (#298)
* Some work * Updated Add operator * Updated Subtract operator * Updated Subtract operator tests * Added tests for multiplication * Added division * Updated the rest of operators
This commit is contained in:
@@ -2,6 +2,7 @@ package operators
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
@@ -88,9 +89,62 @@ func Not(left, _ core.Value) core.Value {
|
|||||||
return values.True
|
return values.True
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ToNumberOrString(input core.Value) core.Value {
|
||||||
|
switch input.Type() {
|
||||||
|
case types.Int, types.Float, types.String:
|
||||||
|
return input
|
||||||
|
default:
|
||||||
|
return values.ToInt(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToNumberOnly(input core.Value) core.Value {
|
||||||
|
switch input.Type() {
|
||||||
|
case types.Int, types.Float:
|
||||||
|
return input
|
||||||
|
case types.String:
|
||||||
|
if strings.Contains(input.String(), ".") {
|
||||||
|
return values.ToFloat(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.ToInt(input)
|
||||||
|
case types.Array:
|
||||||
|
arr := input.(*values.Array)
|
||||||
|
length := arr.Length()
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
return values.ZeroInt
|
||||||
|
}
|
||||||
|
|
||||||
|
i := values.ZeroInt
|
||||||
|
f := values.ZeroFloat
|
||||||
|
|
||||||
|
for y := values.Int(0); y < length; y++ {
|
||||||
|
out := ToNumberOnly(arr.Get(y))
|
||||||
|
|
||||||
|
if out.Type() == types.Int {
|
||||||
|
i += out.(values.Int)
|
||||||
|
} else {
|
||||||
|
f += out.(values.Float)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if f == 0 {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
return values.Float(i) + f
|
||||||
|
default:
|
||||||
|
return values.ToInt(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Adds numbers
|
// Adds numbers
|
||||||
// Concats strings
|
// Concatenates strings
|
||||||
func Add(left, right core.Value) core.Value {
|
func Add(inputL, inputR core.Value) core.Value {
|
||||||
|
left := ToNumberOrString(inputL)
|
||||||
|
right := ToNumberOrString(inputR)
|
||||||
|
|
||||||
if left.Type() == types.Int {
|
if left.Type() == types.Int {
|
||||||
if right.Type() == types.Int {
|
if right.Type() == types.Int {
|
||||||
l := left.(values.Int)
|
l := left.(values.Int)
|
||||||
@@ -126,7 +180,10 @@ func Add(left, right core.Value) core.Value {
|
|||||||
return values.NewString(left.String() + right.String())
|
return values.NewString(left.String() + right.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func Subtract(left, right core.Value) core.Value {
|
func Subtract(inputL, inputR core.Value) core.Value {
|
||||||
|
left := ToNumberOnly(inputL)
|
||||||
|
right := ToNumberOnly(inputR)
|
||||||
|
|
||||||
if left.Type() == types.Int {
|
if left.Type() == types.Int {
|
||||||
if right.Type() == types.Int {
|
if right.Type() == types.Int {
|
||||||
l := left.(values.Int)
|
l := left.(values.Int)
|
||||||
@@ -162,7 +219,10 @@ func Subtract(left, right core.Value) core.Value {
|
|||||||
return values.ZeroInt
|
return values.ZeroInt
|
||||||
}
|
}
|
||||||
|
|
||||||
func Multiply(left, right core.Value) core.Value {
|
func Multiply(inputL, inputR core.Value) core.Value {
|
||||||
|
left := ToNumberOnly(inputL)
|
||||||
|
right := ToNumberOnly(inputR)
|
||||||
|
|
||||||
if left.Type() == types.Int {
|
if left.Type() == types.Int {
|
||||||
if right.Type() == types.Int {
|
if right.Type() == types.Int {
|
||||||
l := left.(values.Int)
|
l := left.(values.Int)
|
||||||
@@ -198,20 +258,31 @@ func Multiply(left, right core.Value) core.Value {
|
|||||||
return values.ZeroInt
|
return values.ZeroInt
|
||||||
}
|
}
|
||||||
|
|
||||||
func Divide(left, right core.Value) core.Value {
|
func Divide(inputL, inputR core.Value) core.Value {
|
||||||
|
left := ToNumberOnly(inputL)
|
||||||
|
right := ToNumberOnly(inputR)
|
||||||
|
|
||||||
if left.Type() == types.Int {
|
if left.Type() == types.Int {
|
||||||
if right.Type() == types.Int {
|
if right.Type() == types.Int {
|
||||||
l := left.(values.Int)
|
l := values.Float(left.(values.Int))
|
||||||
r := right.(values.Int)
|
r := values.Float(right.(values.Int))
|
||||||
|
|
||||||
|
if r == 0.0 {
|
||||||
|
panic("divide by zero")
|
||||||
|
}
|
||||||
|
|
||||||
return l / r
|
return l / r
|
||||||
}
|
}
|
||||||
|
|
||||||
if right.Type() == types.Float {
|
if right.Type() == types.Float {
|
||||||
l := left.(values.Int)
|
l := values.Float(left.(values.Int))
|
||||||
r := right.(values.Float)
|
r := right.(values.Float)
|
||||||
|
|
||||||
return values.Float(l) / r
|
if r == 0.0 {
|
||||||
|
panic("divide by zero")
|
||||||
|
}
|
||||||
|
|
||||||
|
return l / r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,21 +291,32 @@ func Divide(left, right core.Value) core.Value {
|
|||||||
l := left.(values.Float)
|
l := left.(values.Float)
|
||||||
r := right.(values.Float)
|
r := right.(values.Float)
|
||||||
|
|
||||||
|
if r == 0.0 {
|
||||||
|
panic("divide by zero")
|
||||||
|
}
|
||||||
|
|
||||||
return l / r
|
return l / r
|
||||||
}
|
}
|
||||||
|
|
||||||
if right.Type() == types.Int {
|
if right.Type() == types.Int {
|
||||||
l := left.(values.Float)
|
l := left.(values.Float)
|
||||||
r := right.(values.Int)
|
r := values.Float(right.(values.Int))
|
||||||
|
|
||||||
return l / values.Float(r)
|
if r == 0.0 {
|
||||||
|
panic("divide by zero")
|
||||||
|
}
|
||||||
|
|
||||||
|
return l / r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return values.ZeroInt
|
return values.ZeroInt
|
||||||
}
|
}
|
||||||
|
|
||||||
func Modulus(left, right core.Value) core.Value {
|
func Modulus(inputL, inputR core.Value) core.Value {
|
||||||
|
left := ToNumberOnly(inputL)
|
||||||
|
right := ToNumberOnly(inputR)
|
||||||
|
|
||||||
if left.Type() == types.Int {
|
if left.Type() == types.Int {
|
||||||
if right.Type() == types.Int {
|
if right.Type() == types.Int {
|
||||||
l := left.(values.Int)
|
l := left.(values.Int)
|
||||||
@@ -270,7 +352,9 @@ func Modulus(left, right core.Value) core.Value {
|
|||||||
return values.ZeroInt
|
return values.ZeroInt
|
||||||
}
|
}
|
||||||
|
|
||||||
func Increment(left, _ core.Value) core.Value {
|
func Increment(inputL, _ core.Value) core.Value {
|
||||||
|
left := ToNumberOnly(inputL)
|
||||||
|
|
||||||
if left.Type() == types.Int {
|
if left.Type() == types.Int {
|
||||||
l := left.(values.Int)
|
l := left.(values.Int)
|
||||||
|
|
||||||
@@ -286,7 +370,9 @@ func Increment(left, _ core.Value) core.Value {
|
|||||||
return values.None
|
return values.None
|
||||||
}
|
}
|
||||||
|
|
||||||
func Decrement(left, _ core.Value) core.Value {
|
func Decrement(inputL, _ core.Value) core.Value {
|
||||||
|
left := ToNumberOnly(inputL)
|
||||||
|
|
||||||
if left.Type() == types.Int {
|
if left.Type() == types.Int {
|
||||||
l := left.(values.Int)
|
l := left.(values.Int)
|
||||||
|
|
||||||
@@ -303,31 +389,27 @@ func Decrement(left, _ core.Value) core.Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Negative(value, _ core.Value) core.Value {
|
func Negative(value, _ core.Value) core.Value {
|
||||||
err := core.ValidateType(value, types.Int, types.Float)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return values.ZeroInt
|
|
||||||
}
|
|
||||||
|
|
||||||
if value.Type() == types.Int {
|
if value.Type() == types.Int {
|
||||||
return -value.(values.Int)
|
return -value.(values.Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if value.Type() == types.Float {
|
||||||
return -value.(values.Float)
|
return -value.(values.Float)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
func Positive(value, _ core.Value) core.Value {
|
func Positive(value, _ core.Value) core.Value {
|
||||||
err := core.ValidateType(value, types.Int, types.Float)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return values.ZeroInt
|
|
||||||
}
|
|
||||||
|
|
||||||
if value.Type() == types.Int {
|
if value.Type() == types.Int {
|
||||||
return +value.(values.Int)
|
return +value.(values.Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if value.Type() == types.Float {
|
||||||
return +value.(values.Float)
|
return +value.(values.Float)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToBoolean(value, _ core.Value) core.Value {
|
func ToBoolean(value, _ core.Value) core.Value {
|
||||||
|
1022
pkg/runtime/expressions/operators/operator_test.go
Normal file
1022
pkg/runtime/expressions/operators/operator_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,8 @@ import (
|
|||||||
|
|
||||||
type Float float64
|
type Float float64
|
||||||
|
|
||||||
|
var NaN = Float(math.NaN())
|
||||||
|
|
||||||
const ZeroFloat = Float(0.0)
|
const ZeroFloat = Float(0.0)
|
||||||
|
|
||||||
func NewFloat(input float64) Float {
|
func NewFloat(input float64) Float {
|
||||||
@@ -80,7 +82,7 @@ func (t Float) Type() core.Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t Float) String() string {
|
func (t Float) String() string {
|
||||||
return fmt.Sprintf("%f", t)
|
return fmt.Sprintf("%v", float64(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t Float) Compare(other core.Value) int64 {
|
func (t Float) Compare(other core.Value) int64 {
|
||||||
|
@@ -235,58 +235,116 @@ func ToBoolean(input core.Value) core.Value {
|
|||||||
switch input.Type() {
|
switch input.Type() {
|
||||||
case types.Boolean:
|
case types.Boolean:
|
||||||
return input
|
return input
|
||||||
case types.None:
|
|
||||||
return False
|
|
||||||
case types.String:
|
case types.String:
|
||||||
return NewBoolean(input.String() != "")
|
return NewBoolean(input.(String) != "")
|
||||||
case types.Int:
|
case types.Int:
|
||||||
return NewBoolean(input.(Int) != 0)
|
return NewBoolean(input.(Int) != 0)
|
||||||
case types.Float:
|
case types.Float:
|
||||||
return NewBoolean(input.(Float) != 0)
|
return NewBoolean(input.(Float) != 0)
|
||||||
|
case types.DateTime:
|
||||||
|
return NewBoolean(!input.(DateTime).IsZero())
|
||||||
|
case types.None:
|
||||||
|
return False
|
||||||
default:
|
default:
|
||||||
return True
|
return True
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToFloat(input core.Value) (Float, error) {
|
func ToFloat(input core.Value) Float {
|
||||||
switch val := input.(type) {
|
switch val := input.(type) {
|
||||||
case Float:
|
case Float:
|
||||||
return val, nil
|
return val
|
||||||
case Int:
|
case Int:
|
||||||
return Float(val), nil
|
return Float(val)
|
||||||
case String:
|
case String:
|
||||||
i, err := strconv.ParseFloat(string(val), 64)
|
i, err := strconv.ParseFloat(string(val), 64)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ZeroFloat, err
|
return ZeroFloat
|
||||||
}
|
}
|
||||||
|
|
||||||
return Float(i), nil
|
return Float(i)
|
||||||
|
case Boolean:
|
||||||
|
if val {
|
||||||
|
return Float(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Float(0)
|
||||||
|
case DateTime:
|
||||||
|
dt := input.(DateTime)
|
||||||
|
|
||||||
|
if dt.IsZero() {
|
||||||
|
return ZeroFloat
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewFloat(float64(dt.Unix()))
|
||||||
|
case *Array:
|
||||||
|
length := val.Length()
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
return ZeroFloat
|
||||||
|
}
|
||||||
|
|
||||||
|
res := ZeroFloat
|
||||||
|
|
||||||
|
for i := Int(0); i < length; i++ {
|
||||||
|
res += ToFloat(val.Get(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
default:
|
default:
|
||||||
return ZeroFloat, core.TypeError(input.Type(), types.Int, types.Float, types.String)
|
return ZeroFloat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToInt(input core.Value) (Int, error) {
|
func ToInt(input core.Value) Int {
|
||||||
switch val := input.(type) {
|
switch val := input.(type) {
|
||||||
case Int:
|
case Int:
|
||||||
return val, nil
|
return val
|
||||||
case Float:
|
case Float:
|
||||||
return Int(val), nil
|
return Int(val)
|
||||||
case String:
|
case String:
|
||||||
i, err := strconv.ParseInt(string(val), 10, 64)
|
i, err := strconv.ParseInt(string(val), 10, 64)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ZeroInt, err
|
return ZeroInt
|
||||||
}
|
}
|
||||||
|
|
||||||
return Int(i), nil
|
return Int(i)
|
||||||
|
case Boolean:
|
||||||
|
if val {
|
||||||
|
return Int(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Int(0)
|
||||||
|
case DateTime:
|
||||||
|
dt := input.(DateTime)
|
||||||
|
|
||||||
|
if dt.IsZero() {
|
||||||
|
return ZeroInt
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewInt(int(dt.Unix()))
|
||||||
|
case *Array:
|
||||||
|
length := val.Length()
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
return ZeroInt
|
||||||
|
}
|
||||||
|
|
||||||
|
res := ZeroInt
|
||||||
|
|
||||||
|
for i := Int(0); i < length; i++ {
|
||||||
|
res += ToInt(val.Get(i))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
default:
|
default:
|
||||||
return ZeroInt, core.TypeError(input.Type(), types.Int, types.Float, types.String)
|
return ZeroInt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToArray(ctx context.Context, input core.Value) (core.Value, error) {
|
func ToArray(ctx context.Context, input core.Value) core.Value {
|
||||||
switch value := input.(type) {
|
switch value := input.(type) {
|
||||||
case Boolean,
|
case Boolean,
|
||||||
Int,
|
Int,
|
||||||
@@ -294,9 +352,9 @@ func ToArray(ctx context.Context, input core.Value) (core.Value, error) {
|
|||||||
String,
|
String,
|
||||||
DateTime:
|
DateTime:
|
||||||
|
|
||||||
return NewArrayWith(value), nil
|
return NewArrayWith(value)
|
||||||
case *Array:
|
case *Array:
|
||||||
return value.Copy(), nil
|
return value.Copy()
|
||||||
case *Object:
|
case *Object:
|
||||||
arr := NewArray(int(value.Length()))
|
arr := NewArray(int(value.Length()))
|
||||||
|
|
||||||
@@ -306,12 +364,12 @@ func ToArray(ctx context.Context, input core.Value) (core.Value, error) {
|
|||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
return arr, nil
|
return arr
|
||||||
case core.Iterable:
|
case core.Iterable:
|
||||||
iterator, err := value.Iterate(ctx)
|
iterator, err := value.Iterate(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return None, err
|
return None
|
||||||
}
|
}
|
||||||
|
|
||||||
arr := NewArray(10)
|
arr := NewArray(10)
|
||||||
@@ -320,7 +378,7 @@ func ToArray(ctx context.Context, input core.Value) (core.Value, error) {
|
|||||||
val, _, err := iterator.Next(ctx)
|
val, _, err := iterator.Next(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return None, err
|
return None
|
||||||
}
|
}
|
||||||
|
|
||||||
if val == None {
|
if val == None {
|
||||||
@@ -330,9 +388,9 @@ func ToArray(ctx context.Context, input core.Value) (core.Value, error) {
|
|||||||
arr.Push(val)
|
arr.Push(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
return arr, nil
|
return arr
|
||||||
default:
|
default:
|
||||||
return NewArray(0), nil
|
return NewArray(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,3 +430,9 @@ func MapHash(input map[string]core.Value) uint64 {
|
|||||||
|
|
||||||
return h.Sum64()
|
return h.Sum64()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsNumber(input core.Value) Boolean {
|
||||||
|
t := input.Type()
|
||||||
|
|
||||||
|
return t == types.Int || t == types.Float
|
||||||
|
}
|
||||||
|
@@ -225,41 +225,58 @@ func TestHelpers(t *testing.T) {
|
|||||||
Convey("ToFloat", func() {
|
Convey("ToFloat", func() {
|
||||||
Convey("Should convert Int", func() {
|
Convey("Should convert Int", func() {
|
||||||
input := values.NewInt(100)
|
input := values.NewInt(100)
|
||||||
output, err := values.ToFloat(input)
|
output := values.ToFloat(input)
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(output, ShouldEqual, values.NewFloat(100))
|
So(output, ShouldEqual, values.NewFloat(100))
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should convert Float", func() {
|
Convey("Should convert Float", func() {
|
||||||
input := values.NewFloat(100)
|
input := values.NewFloat(100)
|
||||||
output, err := values.ToFloat(input)
|
output := values.ToFloat(input)
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(output, ShouldEqual, values.NewFloat(100))
|
So(output, ShouldEqual, values.NewFloat(100))
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should convert String", func() {
|
Convey("Should convert String", func() {
|
||||||
input := values.NewString("100.1")
|
input := values.NewString("100.1")
|
||||||
output, err := values.ToFloat(input)
|
output := values.ToFloat(input)
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(output, ShouldEqual, values.NewFloat(100.1))
|
So(output, ShouldEqual, values.NewFloat(100.1))
|
||||||
|
|
||||||
|
output2 := values.ToFloat(values.NewString("foobar"))
|
||||||
|
So(output2, ShouldEqual, values.ZeroFloat)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Should convert Boolean", func() {
|
||||||
|
So(values.ToFloat(values.True), ShouldEqual, values.NewFloat(1))
|
||||||
|
So(values.ToFloat(values.False), ShouldEqual, values.NewFloat(0))
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Should convert Array with single item", func() {
|
||||||
|
So(values.ToFloat(values.NewArrayWith(values.NewFloat(1))), ShouldEqual, values.NewFloat(1))
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Should convert Array with multiple items", func() {
|
||||||
|
arg := values.NewArrayWith(values.NewFloat(1), values.NewFloat(1))
|
||||||
|
|
||||||
|
So(values.ToFloat(arg), ShouldEqual, values.NewFloat(2))
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Should convert DateTime", func() {
|
||||||
|
dt := values.NewCurrentDateTime()
|
||||||
|
ts := dt.Time.Unix()
|
||||||
|
|
||||||
|
So(values.ToFloat(dt), ShouldEqual, values.NewFloat(float64(ts)))
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should NOT convert other types", func() {
|
Convey("Should NOT convert other types", func() {
|
||||||
inputs := []core.Value{
|
inputs := []core.Value{
|
||||||
values.NewBoolean(true),
|
|
||||||
values.NewCurrentDateTime(),
|
|
||||||
values.NewArray(1),
|
|
||||||
values.NewObject(),
|
values.NewObject(),
|
||||||
values.NewBinary([]byte("")),
|
values.NewBinary([]byte("")),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, input := range inputs {
|
for _, input := range inputs {
|
||||||
_, err := values.ToFloat(input)
|
So(values.ToFloat(input), ShouldEqual, values.ZeroFloat)
|
||||||
|
|
||||||
So(err, ShouldNotBeNil)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -267,41 +284,58 @@ func TestHelpers(t *testing.T) {
|
|||||||
Convey("ToInt", func() {
|
Convey("ToInt", func() {
|
||||||
Convey("Should convert Int", func() {
|
Convey("Should convert Int", func() {
|
||||||
input := values.NewInt(100)
|
input := values.NewInt(100)
|
||||||
output, err := values.ToInt(input)
|
output := values.ToInt(input)
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(output, ShouldEqual, values.NewInt(100))
|
So(output, ShouldEqual, values.NewInt(100))
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should convert Float", func() {
|
Convey("Should convert Float", func() {
|
||||||
input := values.NewFloat(100.1)
|
input := values.NewFloat(100.1)
|
||||||
output, err := values.ToInt(input)
|
output := values.ToInt(input)
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(output, ShouldEqual, values.NewInt(100))
|
So(output, ShouldEqual, values.NewInt(100))
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should convert String", func() {
|
Convey("Should convert String", func() {
|
||||||
input := values.NewString("100")
|
input := values.NewString("100")
|
||||||
output, err := values.ToInt(input)
|
output := values.ToInt(input)
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(output, ShouldEqual, values.NewInt(100))
|
So(output, ShouldEqual, values.NewInt(100))
|
||||||
|
|
||||||
|
output2 := values.ToInt(values.NewString("foobar"))
|
||||||
|
So(output2, ShouldEqual, values.ZeroInt)
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Should convert Boolean", func() {
|
||||||
|
So(values.ToInt(values.True), ShouldEqual, values.NewInt(1))
|
||||||
|
So(values.ToInt(values.False), ShouldEqual, values.NewInt(0))
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Should convert Array with single item", func() {
|
||||||
|
So(values.ToInt(values.NewArrayWith(values.NewFloat(1))), ShouldEqual, values.NewInt(1))
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Should convert Array with multiple items", func() {
|
||||||
|
arg := values.NewArrayWith(values.NewFloat(1), values.NewFloat(1))
|
||||||
|
|
||||||
|
So(values.ToInt(arg), ShouldEqual, values.NewFloat(2))
|
||||||
|
})
|
||||||
|
|
||||||
|
Convey("Should convert DateTime", func() {
|
||||||
|
dt := values.NewCurrentDateTime()
|
||||||
|
ts := dt.Time.Unix()
|
||||||
|
|
||||||
|
So(values.ToInt(dt), ShouldEqual, values.NewInt(int(ts)))
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("Should NOT convert other types", func() {
|
Convey("Should NOT convert other types", func() {
|
||||||
inputs := []core.Value{
|
inputs := []core.Value{
|
||||||
values.NewBoolean(true),
|
|
||||||
values.NewCurrentDateTime(),
|
|
||||||
values.NewArray(1),
|
|
||||||
values.NewObject(),
|
values.NewObject(),
|
||||||
values.NewBinary([]byte("")),
|
values.NewBinary([]byte("")),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, input := range inputs {
|
for _, input := range inputs {
|
||||||
_, err := values.ToInt(input)
|
So(values.ToInt(input), ShouldEqual, values.ZeroInt)
|
||||||
|
|
||||||
So(err, ShouldNotBeNil)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -338,10 +372,9 @@ func TestHelpers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, pairs := range inputs {
|
for _, pairs := range inputs {
|
||||||
actual, err := values.ToArray(context.Background(), pairs[0])
|
actual := values.ToArray(context.Background(), pairs[0])
|
||||||
expected := pairs[1]
|
expected := pairs[1]
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(actual.Compare(expected), ShouldEqual, 0)
|
So(actual.Compare(expected), ShouldEqual, 0)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -357,9 +390,7 @@ func TestHelpers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input := values.NewArrayWith(vals...)
|
input := values.NewArrayWith(vals...)
|
||||||
output, err := values.ToArray(context.Background(), input)
|
output := values.ToArray(context.Background(), input)
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
arr := output.(*values.Array)
|
arr := output.(*values.Array)
|
||||||
|
|
||||||
@@ -383,9 +414,7 @@ func TestHelpers(t *testing.T) {
|
|||||||
values.NewObjectProperty("qaz", values.NewObject()),
|
values.NewObjectProperty("qaz", values.NewObject()),
|
||||||
)
|
)
|
||||||
|
|
||||||
output, err := values.ToArray(context.Background(), input)
|
output := values.ToArray(context.Background(), input)
|
||||||
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
arr := output.(*values.Array).Sort()
|
arr := output.(*values.Array).Sort()
|
||||||
|
|
||||||
|
@@ -38,17 +38,8 @@ func MouseMoveXY(ctx context.Context, args ...core.Value) (core.Value, error) {
|
|||||||
return values.None, err
|
return values.None, err
|
||||||
}
|
}
|
||||||
|
|
||||||
x, err := values.ToFloat(args[0])
|
x := values.ToFloat(args[0])
|
||||||
|
y := values.ToFloat(args[1])
|
||||||
if err != nil {
|
|
||||||
return values.None, err
|
|
||||||
}
|
|
||||||
|
|
||||||
y, err := values.ToFloat(args[1])
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return values.None, err
|
|
||||||
}
|
|
||||||
|
|
||||||
doc := args[0].(drivers.HTMLDocument)
|
doc := args[0].(drivers.HTMLDocument)
|
||||||
|
|
||||||
|
@@ -38,17 +38,8 @@ func ScrollXY(ctx context.Context, args ...core.Value) (core.Value, error) {
|
|||||||
return values.None, err
|
return values.None, err
|
||||||
}
|
}
|
||||||
|
|
||||||
x, err := values.ToFloat(args[1])
|
x := values.ToFloat(args[1])
|
||||||
|
y := values.ToFloat(args[2])
|
||||||
if err != nil {
|
|
||||||
return values.None, err
|
|
||||||
}
|
|
||||||
|
|
||||||
y, err := values.ToFloat(args[2])
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return values.None, err
|
|
||||||
}
|
|
||||||
|
|
||||||
doc := args[0].(drivers.HTMLDocument)
|
doc := args[0].(drivers.HTMLDocument)
|
||||||
|
|
||||||
|
@@ -26,22 +26,10 @@ func Rand(_ context.Context, args ...core.Value) (core.Value, error) {
|
|||||||
var max float64
|
var max float64
|
||||||
var min float64
|
var min float64
|
||||||
|
|
||||||
arg1, err := values.ToFloat(args[0])
|
max = float64(values.ToFloat(args[0]))
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return values.None, err
|
|
||||||
}
|
|
||||||
|
|
||||||
max = float64(arg1)
|
|
||||||
|
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
arg2, err := values.ToFloat(args[1])
|
min = float64(values.ToFloat(args[1]))
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return values.None, err
|
|
||||||
}
|
|
||||||
|
|
||||||
min = float64(arg2)
|
|
||||||
} else {
|
} else {
|
||||||
max, min = core.NumberBoundaries(max)
|
max, min = core.NumberBoundaries(max)
|
||||||
}
|
}
|
||||||
|
@@ -20,5 +20,5 @@ func ToArray(ctx context.Context, args ...core.Value) (core.Value, error) {
|
|||||||
return values.None, err
|
return values.None, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return values.ToArray(ctx, args[0])
|
return values.ToArray(ctx, args[0]), nil
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,6 @@ import (
|
|||||||
|
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToBool takes an input value of any type and converts it into the appropriate boolean value.
|
// ToBool takes an input value of any type and converts it into the appropriate boolean value.
|
||||||
@@ -24,44 +23,5 @@ func ToBool(_ context.Context, args ...core.Value) (core.Value, error) {
|
|||||||
return values.None, err
|
return values.None, err
|
||||||
}
|
}
|
||||||
|
|
||||||
arg := args[0]
|
return values.ToBoolean(args[0]), nil
|
||||||
|
|
||||||
switch arg.Type() {
|
|
||||||
case types.Boolean:
|
|
||||||
return arg, nil
|
|
||||||
case types.Int:
|
|
||||||
val := arg.(values.Int)
|
|
||||||
|
|
||||||
if val != 0 {
|
|
||||||
return values.True, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.False, nil
|
|
||||||
case types.Float:
|
|
||||||
val := arg.(values.Float)
|
|
||||||
|
|
||||||
if val != 0 {
|
|
||||||
return values.True, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.False, nil
|
|
||||||
case types.String:
|
|
||||||
if arg.String() != "" {
|
|
||||||
return values.True, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.False, nil
|
|
||||||
case types.DateTime:
|
|
||||||
val := arg.(values.DateTime)
|
|
||||||
|
|
||||||
if !val.IsZero() {
|
|
||||||
return values.True, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.False, nil
|
|
||||||
case types.None:
|
|
||||||
return values.False, nil
|
|
||||||
default:
|
|
||||||
return values.True, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -2,11 +2,9 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToFloat takes an input value of any type and convert it into a float value.
|
// ToFloat takes an input value of any type and convert it into a float value.
|
||||||
@@ -20,67 +18,12 @@ import (
|
|||||||
// An empty array is converted to 0, an array with one member is converted into the result of TO_NUMBER() for its sole member.
|
// An empty array is converted to 0, an array with one member is converted into the result of TO_NUMBER() for its sole member.
|
||||||
// An array with two or more members is converted to the number 0.
|
// An array with two or more members is converted to the number 0.
|
||||||
// An object / HTML node is converted to the number 0.
|
// An object / HTML node is converted to the number 0.
|
||||||
func ToFloat(ctx context.Context, args ...core.Value) (core.Value, error) {
|
func ToFloat(_ context.Context, args ...core.Value) (core.Value, error) {
|
||||||
err := core.ValidateArgs(args, 1, 1)
|
err := core.ValidateArgs(args, 1, 1)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return values.None, err
|
return values.None, err
|
||||||
}
|
}
|
||||||
|
|
||||||
arg := args[0]
|
return values.ToFloat(args[0]), nil
|
||||||
|
|
||||||
switch arg.Type() {
|
|
||||||
case types.Boolean:
|
|
||||||
val := arg.(values.Boolean)
|
|
||||||
|
|
||||||
if val {
|
|
||||||
return values.NewFloat(1), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.ZeroFloat, nil
|
|
||||||
case types.Int:
|
|
||||||
val := arg.(values.Int)
|
|
||||||
|
|
||||||
return values.Float(val), nil
|
|
||||||
case types.Float:
|
|
||||||
return arg, nil
|
|
||||||
case types.String:
|
|
||||||
str := arg.String()
|
|
||||||
|
|
||||||
if str == "" {
|
|
||||||
return values.ZeroFloat, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
num, err := strconv.ParseFloat(str, 64)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return values.ZeroFloat, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.NewFloat(num), nil
|
|
||||||
case types.DateTime:
|
|
||||||
val := arg.(values.DateTime)
|
|
||||||
|
|
||||||
if val.IsZero() {
|
|
||||||
return values.ZeroFloat, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.NewFloat(float64(val.Unix())), nil
|
|
||||||
case types.None:
|
|
||||||
return values.ZeroFloat, nil
|
|
||||||
case types.Array:
|
|
||||||
val := arg.(*values.Array)
|
|
||||||
|
|
||||||
if val.Length() == 0 {
|
|
||||||
return values.ZeroFloat, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if val.Length() == 1 {
|
|
||||||
return ToFloat(ctx, val.Get(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.ZeroFloat, nil
|
|
||||||
default:
|
|
||||||
return values.ZeroFloat, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -2,11 +2,9 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/core"
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values"
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
||||||
"github.com/MontFerret/ferret/pkg/runtime/values/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToInt takes an input value of any type and convert it into an integer value.
|
// ToInt takes an input value of any type and convert it into an integer value.
|
||||||
@@ -20,67 +18,12 @@ import (
|
|||||||
// An empty array is converted to 0, an array with one member is converted into the result of TO_NUMBER() for its sole member.
|
// An empty array is converted to 0, an array with one member is converted into the result of TO_NUMBER() for its sole member.
|
||||||
// An array with two or more members is converted to the number 0.
|
// An array with two or more members is converted to the number 0.
|
||||||
// An object / HTML node is converted to the number 0.
|
// An object / HTML node is converted to the number 0.
|
||||||
func ToInt(ctx context.Context, args ...core.Value) (core.Value, error) {
|
func ToInt(_ context.Context, args ...core.Value) (core.Value, error) {
|
||||||
err := core.ValidateArgs(args, 1, 1)
|
err := core.ValidateArgs(args, 1, 1)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return values.None, err
|
return values.None, err
|
||||||
}
|
}
|
||||||
|
|
||||||
arg := args[0]
|
return values.ToInt(args[0]), nil
|
||||||
|
|
||||||
switch arg.Type() {
|
|
||||||
case types.Boolean:
|
|
||||||
val := arg.(values.Boolean)
|
|
||||||
|
|
||||||
if val {
|
|
||||||
return values.NewInt(1), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.ZeroInt, nil
|
|
||||||
case types.Int:
|
|
||||||
return arg, nil
|
|
||||||
case types.Float:
|
|
||||||
val := arg.(values.Float)
|
|
||||||
|
|
||||||
return values.Int(val), nil
|
|
||||||
case types.String:
|
|
||||||
str := arg.String()
|
|
||||||
|
|
||||||
if str == "" {
|
|
||||||
return values.ZeroInt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
num, err := strconv.Atoi(str)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return values.ZeroInt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.NewInt(num), nil
|
|
||||||
case types.DateTime:
|
|
||||||
val := arg.(values.DateTime)
|
|
||||||
|
|
||||||
if val.IsZero() {
|
|
||||||
return values.ZeroInt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.NewInt(int(val.Unix())), nil
|
|
||||||
case types.None:
|
|
||||||
return values.ZeroInt, nil
|
|
||||||
case types.Array:
|
|
||||||
val := arg.(*values.Array)
|
|
||||||
|
|
||||||
if val.Length() == 0 {
|
|
||||||
return values.ZeroInt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if val.Length() == 1 {
|
|
||||||
return ToInt(ctx, val.Get(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
return values.ZeroInt, nil
|
|
||||||
default:
|
|
||||||
return values.ZeroInt, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -17,11 +17,7 @@ func Wait(_ context.Context, args ...core.Value) (core.Value, error) {
|
|||||||
return values.None, nil
|
return values.None, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
arg, err := values.ToInt(args[0])
|
arg := values.ToInt(args[0])
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return values.None, err
|
|
||||||
}
|
|
||||||
|
|
||||||
time.Sleep(time.Millisecond * time.Duration(arg))
|
time.Sleep(time.Millisecond * time.Duration(arg))
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user