diff --git a/builtins.go b/builtins.go index 4dc8c89..9d1b950 100644 --- a/builtins.go +++ b/builtins.go @@ -16,7 +16,12 @@ limitations under the License. package jsonnet -import "github.com/google/go-jsonnet/ast" +import ( + "math" + "sort" + + "github.com/google/go-jsonnet/ast" +) // TODO(sbarzowski) Is this the best option? It's the first one that worked for me... //go:generate esc -o std.go -pkg=jsonnet std/std.jsonnet @@ -248,8 +253,81 @@ func builtinType(e *evaluator, xp potentialValue) (value, error) { return makeValueString(x.typename()), nil } +func makeDoubleCheck(e *evaluator, x float64) (value, error) { + if math.IsNaN(x) { + return nil, e.Error("Not a number") + } + if math.IsInf(x, 0) { + return nil, e.Error("Overflow") + } + return makeValueNumber(x), nil +} + +// TODO(sbarzowski) perhaps it is too magical for Go style +func liftNumeric(f func(float64) float64) func(*evaluator, potentialValue) (value, error) { + return func(e *evaluator, xp potentialValue) (value, error) { + x, err := e.evaluateNumber(xp) + if err != nil { + return nil, err + } + return makeDoubleCheck(e, f(x.value)) + } +} + +var builtinSqrt = liftNumeric(math.Sqrt) +var builtinCeil = liftNumeric(math.Ceil) +var builtinFloor = liftNumeric(math.Floor) +var builtinSin = liftNumeric(math.Sin) +var builtinCos = liftNumeric(math.Cos) +var builtinTan = liftNumeric(math.Tan) +var builtinAsin = liftNumeric(math.Asin) +var builtinAcos = liftNumeric(math.Acos) +var builtinAtan = liftNumeric(math.Atan) +var builtinLog = liftNumeric(math.Log) +var builtinExp = liftNumeric(math.Exp) + +func builtinObjectFieldsEx(e *evaluator, objp potentialValue, hiddenp potentialValue) (value, error) { + obj, err := e.evaluateObject(objp) + if err != nil { + return nil, err + } + hidden, err := e.evaluateBoolean(hiddenp) + if err != nil { + return nil, err + } + fields := objectFields(obj, hidden.value) + sort.Strings(fields) + elems := []potentialValue{} + for _, fieldname := range fields { + elems = append(elems, &readyValue{makeValueString(fieldname)}) + } + return makeValueArray(elems), nil +} + +func builtinObjectHasEx(e *evaluator, objp potentialValue, fnamep potentialValue, hiddenp potentialValue) (value, error) { + obj, err := e.evaluateObject(objp) + if err != nil { + return nil, err + } + fname, err := e.evaluateString(fnamep) + if err != nil { + return nil, err + } + hidden, err := e.evaluateBoolean(hiddenp) + if err != nil { + return nil, err + } + for _, fieldname := range objectFields(obj, hidden.value) { + if fieldname == fname.value { + return makeValueBoolean(true), nil + } + } + return makeValueBoolean(false), nil +} + type unaryBuiltin func(*evaluator, potentialValue) (value, error) type binaryBuiltin func(*evaluator, potentialValue, potentialValue) (value, error) +type ternaryBuiltin func(*evaluator, potentialValue, potentialValue, potentialValue) (value, error) type UnaryBuiltin struct { name ast.Identifier @@ -289,6 +367,21 @@ func (b *BinaryBuiltin) Parameters() ast.Identifiers { return b.parameters } +type TernaryBuiltin struct { + name ast.Identifier + function ternaryBuiltin + parameters ast.Identifiers +} + +func (b *TernaryBuiltin) EvalCall(args callArguments, e *evaluator) (value, error) { + // TODO check args + return b.function(getBuiltinEvaluator(e, b.name), args.positional[0], args.positional[1], args.positional[2]) +} + +func (b *TernaryBuiltin) Parameters() ast.Identifiers { + return b.parameters +} + func todoFunc(e *evaluator, x, y potentialValue) (value, error) { return nil, e.Error("not implemented yet") } @@ -341,5 +434,18 @@ var funcBuiltins = map[string]evalCallable{ "length": &UnaryBuiltin{name: "length", function: builtinLength, parameters: ast.Identifiers{"x"}}, "makeArray": &BinaryBuiltin{name: "makeArray", function: builtinMakeArray, parameters: ast.Identifiers{"sz", "func"}}, "primitiveEquals": &BinaryBuiltin{name: "primitiveEquals", function: primitiveEquals, parameters: ast.Identifiers{"sz", "func"}}, + "objectFieldsEx": &BinaryBuiltin{name: "objectFields", function: builtinObjectFieldsEx, parameters: ast.Identifiers{"obj", "hidden"}}, + "objectHasEx": &TernaryBuiltin{name: "objectHasEx", function: builtinObjectHasEx, parameters: ast.Identifiers{"obj", "fname", "hidden"}}, "type": &UnaryBuiltin{name: "type", function: builtinType, parameters: ast.Identifiers{"x"}}, + "ceil": &UnaryBuiltin{name: "ceil", function: builtinCeil, parameters: ast.Identifiers{"x"}}, + "floor": &UnaryBuiltin{name: "floor", function: builtinFloor, parameters: ast.Identifiers{"x"}}, + "sqrt": &UnaryBuiltin{name: "sqrt", function: builtinSqrt, parameters: ast.Identifiers{"x"}}, + "sin": &UnaryBuiltin{name: "sin", function: builtinSin, parameters: ast.Identifiers{"x"}}, + "cos": &UnaryBuiltin{name: "cos", function: builtinCos, parameters: ast.Identifiers{"x"}}, + "tan": &UnaryBuiltin{name: "tan", function: builtinTan, parameters: ast.Identifiers{"x"}}, + "asin": &UnaryBuiltin{name: "asin", function: builtinAsin, parameters: ast.Identifiers{"x"}}, + "acos": &UnaryBuiltin{name: "acos", function: builtinAcos, parameters: ast.Identifiers{"x"}}, + "atan": &UnaryBuiltin{name: "atan", function: builtinAtan, parameters: ast.Identifiers{"x"}}, + "log": &UnaryBuiltin{name: "log", function: builtinLog, parameters: ast.Identifiers{"x"}}, + "exp": &UnaryBuiltin{name: "exp", function: builtinExp, parameters: ast.Identifiers{"x"}}, } diff --git a/testdata/builtinObjectFieldsEx.golden b/testdata/builtinObjectFieldsEx.golden new file mode 100644 index 0000000..7ca3561 --- /dev/null +++ b/testdata/builtinObjectFieldsEx.golden @@ -0,0 +1,5 @@ +[ + "x", + "y", + "z" +] diff --git a/testdata/builtinObjectFieldsEx.input b/testdata/builtinObjectFieldsEx.input new file mode 100644 index 0000000..9286452 --- /dev/null +++ b/testdata/builtinObjectFieldsEx.input @@ -0,0 +1 @@ +std.objectFieldsEx({x: 1, y:: 1, z::: 2}, false) diff --git a/testdata/builtinObjectHasEx.golden b/testdata/builtinObjectHasEx.golden new file mode 100644 index 0000000..27ba77d --- /dev/null +++ b/testdata/builtinObjectHasEx.golden @@ -0,0 +1 @@ +true diff --git a/testdata/builtinObjectHasEx.input b/testdata/builtinObjectHasEx.input new file mode 100644 index 0000000..f79d514 --- /dev/null +++ b/testdata/builtinObjectHasEx.input @@ -0,0 +1 @@ +std.objectHasEx({x: 1, y:: 1, z::: 2}, "x", false) diff --git a/testdata/builtinObjectHasExBadField.golden b/testdata/builtinObjectHasExBadField.golden new file mode 100644 index 0000000..f94e054 --- /dev/null +++ b/testdata/builtinObjectHasExBadField.golden @@ -0,0 +1 @@ +RUNTIME ERROR: Unexpected type number, expected string diff --git a/testdata/builtinObjectHasExBadField.input b/testdata/builtinObjectHasExBadField.input new file mode 100644 index 0000000..50c0ae5 --- /dev/null +++ b/testdata/builtinObjectHasExBadField.input @@ -0,0 +1 @@ +std.objectHasEx({}, 42, false) diff --git a/testdata/builtinObjectHasExBadObject.golden b/testdata/builtinObjectHasExBadObject.golden new file mode 100644 index 0000000..23ae9e6 --- /dev/null +++ b/testdata/builtinObjectHasExBadObject.golden @@ -0,0 +1 @@ +RUNTIME ERROR: Unexpected type number, expected object diff --git a/testdata/builtinObjectHasExBadObject.input b/testdata/builtinObjectHasExBadObject.input new file mode 100644 index 0000000..f6cb50c --- /dev/null +++ b/testdata/builtinObjectHasExBadObject.input @@ -0,0 +1 @@ +std.objectHasEx(42, "x", false) diff --git a/testdata/builtin_acos.golden b/testdata/builtin_acos.golden new file mode 100644 index 0000000..573541a --- /dev/null +++ b/testdata/builtin_acos.golden @@ -0,0 +1 @@ +0 diff --git a/testdata/builtin_acos.input b/testdata/builtin_acos.input new file mode 100644 index 0000000..eefc2fd --- /dev/null +++ b/testdata/builtin_acos.input @@ -0,0 +1 @@ +std.acos(1) diff --git a/testdata/builtin_asin.golden b/testdata/builtin_asin.golden new file mode 100644 index 0000000..53a6e14 --- /dev/null +++ b/testdata/builtin_asin.golden @@ -0,0 +1 @@ +1.5707963267948966 diff --git a/testdata/builtin_asin.input b/testdata/builtin_asin.input new file mode 100644 index 0000000..2ae81b0 --- /dev/null +++ b/testdata/builtin_asin.input @@ -0,0 +1 @@ +std.asin(1) diff --git a/testdata/builtin_atan.golden b/testdata/builtin_atan.golden new file mode 100644 index 0000000..f445930 --- /dev/null +++ b/testdata/builtin_atan.golden @@ -0,0 +1 @@ +0.78539816339744828 diff --git a/testdata/builtin_atan.input b/testdata/builtin_atan.input new file mode 100644 index 0000000..36c2013 --- /dev/null +++ b/testdata/builtin_atan.input @@ -0,0 +1 @@ +std.atan(1) diff --git a/testdata/builtin_ceil.golden b/testdata/builtin_ceil.golden new file mode 100644 index 0000000..00750ed --- /dev/null +++ b/testdata/builtin_ceil.golden @@ -0,0 +1 @@ +3 diff --git a/testdata/builtin_ceil.input b/testdata/builtin_ceil.input new file mode 100644 index 0000000..435394a --- /dev/null +++ b/testdata/builtin_ceil.input @@ -0,0 +1 @@ +std.ceil(2.5) diff --git a/testdata/builtin_cos.golden b/testdata/builtin_cos.golden new file mode 100644 index 0000000..0f6fdf0 --- /dev/null +++ b/testdata/builtin_cos.golden @@ -0,0 +1 @@ +0.54030230586813977 diff --git a/testdata/builtin_cos.input b/testdata/builtin_cos.input new file mode 100644 index 0000000..f926cd3 --- /dev/null +++ b/testdata/builtin_cos.input @@ -0,0 +1 @@ +std.cos(1) diff --git a/testdata/builtin_exp.golden b/testdata/builtin_exp.golden new file mode 100644 index 0000000..2c37450 --- /dev/null +++ b/testdata/builtin_exp.golden @@ -0,0 +1 @@ +162754.79141900392 diff --git a/testdata/builtin_exp.input b/testdata/builtin_exp.input new file mode 100644 index 0000000..a7a400c --- /dev/null +++ b/testdata/builtin_exp.input @@ -0,0 +1 @@ +std.exp(12) diff --git a/testdata/builtin_floor.golden b/testdata/builtin_floor.golden new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/testdata/builtin_floor.golden @@ -0,0 +1 @@ +2 diff --git a/testdata/builtin_floor.input b/testdata/builtin_floor.input new file mode 100644 index 0000000..24d8f61 --- /dev/null +++ b/testdata/builtin_floor.input @@ -0,0 +1 @@ +std.floor(2.5) diff --git a/testdata/builtin_log.golden b/testdata/builtin_log.golden new file mode 100644 index 0000000..e1d4578 --- /dev/null +++ b/testdata/builtin_log.golden @@ -0,0 +1 @@ +3.7376696182833684 diff --git a/testdata/builtin_log.input b/testdata/builtin_log.input new file mode 100644 index 0000000..594799c --- /dev/null +++ b/testdata/builtin_log.input @@ -0,0 +1 @@ +std.log(42) diff --git a/testdata/builtin_sin.golden b/testdata/builtin_sin.golden new file mode 100644 index 0000000..e83a344 --- /dev/null +++ b/testdata/builtin_sin.golden @@ -0,0 +1 @@ +0.8414709848078965 diff --git a/testdata/builtin_sin.input b/testdata/builtin_sin.input new file mode 100644 index 0000000..41e5366 --- /dev/null +++ b/testdata/builtin_sin.input @@ -0,0 +1 @@ +std.sin(1) diff --git a/testdata/builtin_sqrt.golden b/testdata/builtin_sqrt.golden new file mode 100644 index 0000000..0cfbf08 --- /dev/null +++ b/testdata/builtin_sqrt.golden @@ -0,0 +1 @@ +2 diff --git a/testdata/builtin_sqrt.input b/testdata/builtin_sqrt.input new file mode 100644 index 0000000..0e6d28d --- /dev/null +++ b/testdata/builtin_sqrt.input @@ -0,0 +1 @@ +std.sqrt(4) diff --git a/testdata/builtin_tan.golden b/testdata/builtin_tan.golden new file mode 100644 index 0000000..df7ba92 --- /dev/null +++ b/testdata/builtin_tan.golden @@ -0,0 +1 @@ +1.5574077246549021 diff --git a/testdata/builtin_tan.input b/testdata/builtin_tan.input new file mode 100644 index 0000000..c45322d --- /dev/null +++ b/testdata/builtin_tan.input @@ -0,0 +1 @@ +std.tan(1)