From 1e6cdc03c145a433bb9d430788f988a8a84e411c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Sun, 23 Jun 2024 14:38:38 +0200 Subject: [PATCH 01/75] doc: mention an additional Rust implementation (#1156) --- doc/ref/bindings.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/ref/bindings.html b/doc/ref/bindings.html index 48b5eb61e..0a8f96f85 100644 --- a/doc/ref/bindings.html +++ b/doc/ref/bindings.html @@ -219,8 +219,9 @@

Unofficial Third Party APIs

Ruby
  • - Rust but please first consider the - native port + Rust but please first consider + one of the native ports (jrsonnet + or rsjsonnet)
  • Haskell native port From 03a3a3968bc67a949257235659985f19339bc878 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20S=C3=A1nchez=20Mu=C3=B1oz?= Date: Sun, 23 Jun 2024 14:46:29 +0200 Subject: [PATCH 02/75] Bind the standard library to a `$std` variable and use it in desugared expressions. (#1158) This keeps redefining `std` from breaking expressions that desugar to calls to standard library functions by redefining `std`. --- core/desugarer.cpp | 18 +++++++++++++----- doc/ref/spec.html | 20 ++++++++++---------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/core/desugarer.cpp b/core/desugarer.cpp index 5fd9acfd5..6c38eb913 100644 --- a/core/desugarer.cpp +++ b/core/desugarer.cpp @@ -100,6 +100,7 @@ static constexpr char STD_CODE[] = { */ class Desugarer { Allocator *alloc; + bool isStdlib; template T *make(Args &&... args) @@ -139,7 +140,10 @@ class Desugarer { Var *std(void) { - return var(id(U"std")); + // In most places, there is a "$std" variable inserted by + // the desugarer. On the standard library itself there isn't, + // so use "std" instead. + return var(id(isStdlib ? U"std" : U"$std")); } Local::Bind bind(const Identifier *id, AST *body) @@ -219,7 +223,7 @@ class Desugarer { } public: - Desugarer(Allocator *alloc) : alloc(alloc) {} + Desugarer(Allocator *alloc, bool isStdlib = false) : alloc(alloc), isStdlib(isStdlib) {} void desugarParams(ArgParams ¶ms, unsigned obj_level) { @@ -999,13 +1003,17 @@ class Desugarer { make(E, line_end, body))); } - // local std = (std.jsonnet stuff); ast - ast = make(ast->location, EF, singleBind(id(U"std"), std_obj), ast); + // local $std = (std.jsonnet stuff); std = $std; ast + // The standard library is bound to $std, which cannot be overriden, + // so redefining std won't break expressions that desugar to calls + // to standard library functions. + ast = make(ast->location, EF, singleBind(id(U"std"), std()), ast); + ast = make(ast->location, EF, singleBind(id(U"$std"), std_obj), ast); } }; DesugaredObject *makeStdlibAST(Allocator *alloc, std::string filename) { - Desugarer desugarer(alloc); + Desugarer desugarer(alloc, true); return desugarer.stdlibAST(filename); } diff --git a/doc/ref/spec.html b/doc/ref/spec.html index f0221df4d..9cfd6c01b 100644 --- a/doc/ref/spec.html +++ b/doc/ref/spec.html @@ -1125,7 +1125,7 @@

    Desugaring

    \[ - desugar(e) = desugar_{expr}(\local{\texttt{std} = e_{std}}{e}, false) + desugar(e) = desugar_{expr}(\local{\texttt{\$std} = e_{std}}{\local{\texttt{std} = \texttt{\$std}}{e}}, false) \]
    @@ -1287,7 +1287,7 @@

    Desugaring

    \[ desugar_{expr}(e[e':e'':e'''], b) = - desugar_{expr}(\texttt{std.slice}(e, e', e'', e'''), b) + desugar_{expr}(\texttt{\$std.slice}(e, e', e'', e'''), b) \]
    @@ -1318,27 +1318,27 @@

    Desugaring

    \[ - desugar_{expr}(e \mathop{==} e', b) = desugar_{expr}(\texttt{std.equals}(e, e'), b) + desugar_{expr}(e \mathop{==} e', b) = desugar_{expr}(\texttt{\$std.equals}(e, e'), b) \]
    \[ - desugar_{expr}(e \mathop{\%} e', b) = desugar_{expr}(\texttt{std.mod}(e, e'), b) + desugar_{expr}(e \mathop{\%} e', b) = desugar_{expr}(\texttt{\$std.mod}(e, e'), b) \]
    \[ desugar_{expr}(e \mathop{\texttt{in}} e', b) = - desugar_{expr}(\texttt{std.objectHasEx}(e', e, \texttt{true}), b) + desugar_{expr}(\texttt{\$std.objectHasEx}(e', e, \texttt{true}), b) \]
    \[ desugar_{expr}(e \mathop{\texttt{in}} \texttt{super}, b) = - desugar_{expr}(\texttt{std.objectHasEx}(\texttt{super}, e, \texttt{true}), b) + desugar_{expr}(\texttt{\$std.objectHasEx}(\texttt{super}, e, \texttt{true}), b) \]
    @@ -1484,8 +1484,8 @@

    Desugaring

    \hspace{10mm}\textrm{Let }arr, i\textrm{ fresh} \\ \hspace{10mm}desugar_{expr}( \local{arr = e'}{ - \texttt{std.join}(\\\hspace{20mm}[\ ], \texttt{std.makeArray}( - \texttt{std.length}(arr), + \texttt{\$std.join}(\\\hspace{20mm}[\ ], \texttt{\$std.makeArray}( + \texttt{\$std.length}(arr), \function{i}{\local{x = arr[i]}{desugar_{arrcomp}(e, compspec, b)}} )) }, @@ -1499,8 +1499,8 @@

    Desugaring

    \hspace{10mm}\textrm{Let }arr, i\textrm{ fresh} \\ \hspace{10mm}desugar_{expr}( \local{arr = e'}{ - \texttt{std.join}(\\\hspace{20mm}[\ ], \texttt{std.makeArray}( - \texttt{std.length}(arr), + \texttt{\$std.join}(\\\hspace{20mm}[\ ], \texttt{\$std.makeArray}( + \texttt{\$std.length}(arr), \function{i}{\local{x = arr[i]}{[e]}} )) }, From 913281d203578bb394995bacc792f2576371e06c Mon Sep 17 00:00:00 2001 From: Pat Riehecky <3534830+jcpunk@users.noreply.github.com> Date: Sun, 23 Jun 2024 07:47:36 -0500 Subject: [PATCH 03/75] fix: remove deprecated declarations in python bindings (#1159) Fixes: https://github.com/google/jsonnet/issues/1032 --- python/_jsonnet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/_jsonnet.c b/python/_jsonnet.c index 5acd4aa1a..6bca5c7e9 100644 --- a/python/_jsonnet.c +++ b/python/_jsonnet.c @@ -186,7 +186,7 @@ static struct JsonnetJsonValue *cpython_native_callback( } // Call python function. - result = PyEval_CallObject(ctx->callback, arglist); + result = PyObject_CallObject(ctx->callback, arglist); Py_DECREF(arglist); if (result == NULL) { @@ -226,7 +226,7 @@ static int cpython_import_callback(void *ctx_, const char *base, const char *rel PyEval_RestoreThread(*ctx->py_thread); arglist = Py_BuildValue("(s, s)", base, rel); - result = PyEval_CallObject(ctx->callback, arglist); + result = PyObject_CallObject(ctx->callback, arglist); Py_DECREF(arglist); if (result == NULL) { From ec1e961061632bbb3a96f657ea79322bc14bcc87 Mon Sep 17 00:00:00 2001 From: Oleg Parashchenko Date: Mon, 14 Oct 2024 05:28:07 +0200 Subject: [PATCH 04/75] Add link to npmjs wasm module. --- doc/ref/bindings.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/ref/bindings.html b/doc/ref/bindings.html index 0a8f96f85..030c6ab5b 100644 --- a/doc/ref/bindings.html +++ b/doc/ref/bindings.html @@ -210,7 +210,10 @@

    Unofficial Third Party APIs

    LuaJIT (FFI)
  • - Node.js + Node.js node-jsonnet: C++ version compiled to JS code +
  • +
  • + Node.js tplfa-jsonnet: Golang version compiled to WASM code
  • PHP From dfde9c68ac5e8fce022b66ef4d3b5046e320b39d Mon Sep 17 00:00:00 2001 From: Oleg Parashchenko Date: Mon, 14 Oct 2024 05:41:50 +0200 Subject: [PATCH 05/75] Restart the workflow From 30ac79dac6b2bac66c950911af57305be06624cf Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Mon, 9 Dec 2024 23:44:22 +0100 Subject: [PATCH 06/75] Load py_runtime/py_runtime_pair from rules_python --- MODULE.bazel | 1 + platform_defs/BUILD | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/MODULE.bazel b/MODULE.bazel index 94ac2641a..423af815a 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,6 +1,7 @@ module(name = "jsonnet", version = "0.0.0") bazel_dep(name = "googletest", version = "1.11.0", repo_name = "com_google_googletest") +bazel_dep(name = "rules_python", version = "0.40.0") build_defs = use_extension("//tools/build_defs:extensions.bzl", "build_defs") use_repo( diff --git a/platform_defs/BUILD b/platform_defs/BUILD index 930d7735d..d2a365772 100644 --- a/platform_defs/BUILD +++ b/platform_defs/BUILD @@ -1,4 +1,4 @@ -load("@bazel_tools//tools/python:toolchain.bzl", "py_runtime_pair") +load("@rules_python//python:defs.bzl", "py_runtime", "py_runtime_pair") py_runtime( name = "python3", From f96c01ae3877bc9f75d7bcb111b1ee8d78c97548 Mon Sep 17 00:00:00 2001 From: Morten Mjelva Date: Mon, 9 Dec 2024 23:47:05 +0100 Subject: [PATCH 07/75] Load py_library/py_binary from rules_python as well --- python/BUILD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/BUILD b/python/BUILD index 12936ad26..089066c0c 100644 --- a/python/BUILD +++ b/python/BUILD @@ -1,3 +1,5 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + cc_binary( name = "_jsonnet.so", srcs = ["_jsonnet.c"], From 4ab62a1cae9439b40c3f7a926607901ea7ffd980 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Fri, 17 Jan 2025 09:52:38 +0000 Subject: [PATCH 08/75] chore: update Python Bazel toolchain reference to use @rules_python See rules_python docs: https://rules-python.readthedocs.io/en/latest/toolchains.html# --- platform_defs/BUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform_defs/BUILD b/platform_defs/BUILD index d2a365772..f4bc72237 100644 --- a/platform_defs/BUILD +++ b/platform_defs/BUILD @@ -16,6 +16,6 @@ py_runtime_pair( toolchain( name = "default_python3_toolchain", toolchain = ":just_python3", - toolchain_type = "@bazel_tools//tools/python:toolchain_type", + toolchain_type = "@rules_python//python:toolchain_type", ) From 6542f410f9ac6836818313d0a0bf318634d2d830 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 18 Jan 2025 10:49:51 +0000 Subject: [PATCH 09/75] chore: reformat Bazel files using buildifier --- MODULE.bazel | 5 +++- WORKSPACE | 6 ++--- cpp/testdata/BUILD | 6 ++--- platform_defs/BUILD | 3 +-- python/BUILD | 2 +- stdlib/BUILD | 2 +- third_party/json/BUILD | 2 +- third_party/md5/BUILD | 4 ++-- tools/build_defs/extensions.bzl | 8 +++---- tools/build_defs/python_repo.bzl | 41 ++++++++++++++++---------------- 10 files changed, 41 insertions(+), 38 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 423af815a..2be900140 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,4 +1,7 @@ -module(name = "jsonnet", version = "0.0.0") +module( + name = "jsonnet", + version = "0.0.0", +) bazel_dep(name = "googletest", version = "1.11.0", repo_name = "com_google_googletest") bazel_dep(name = "rules_python", version = "0.40.0") diff --git a/WORKSPACE b/WORKSPACE index 97dfb640e..f10f2bb9a 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -11,9 +11,9 @@ git_repository( git_repository( name = "com_google_googletest", - remote = "https://github.com/google/googletest.git", # If updating googletest version, also update CMakeLists.txt.in. - commit = "2fe3bd994b3189899d93f1d5a881e725e046fdc2", # release: release-1.8.1 + commit = "2fe3bd994b3189899d93f1d5a881e725e046fdc2", # release: release-1.8.1 + remote = "https://github.com/google/googletest.git", shallow_since = "1535728917 -0400", ) @@ -22,5 +22,5 @@ register_toolchains("//platform_defs:default_python3_toolchain") # This allows building C++ against python3 headers. load("//tools/build_defs:python_repo.bzl", "python_headers") -python_headers(name = "default_python3_headers") +python_headers(name = "default_python3_headers") diff --git a/cpp/testdata/BUILD b/cpp/testdata/BUILD index b504c2c20..7f02498b9 100644 --- a/cpp/testdata/BUILD +++ b/cpp/testdata/BUILD @@ -1,11 +1,11 @@ -package(default_visibility = ["//visibility:public"]) - load( "@io_bazel_rules_jsonnet//jsonnet:jsonnet.bzl", - "jsonnet_to_json_test", "jsonnet_library", + "jsonnet_to_json_test", ) +package(default_visibility = ["//visibility:public"]) + jsonnet_to_json_test( name = "example_test", src = "example.jsonnet", diff --git a/platform_defs/BUILD b/platform_defs/BUILD index f4bc72237..2017b1f2b 100644 --- a/platform_defs/BUILD +++ b/platform_defs/BUILD @@ -3,8 +3,8 @@ load("@rules_python//python:defs.bzl", "py_runtime", "py_runtime_pair") py_runtime( name = "python3", interpreter_path = "/usr/bin/python3", - stub_shebang = "#!/usr/bin/env python3", python_version = "PY3", + stub_shebang = "#!/usr/bin/env python3", ) py_runtime_pair( @@ -18,4 +18,3 @@ toolchain( toolchain = ":just_python3", toolchain_type = "@rules_python//python:toolchain_type", ) - diff --git a/python/BUILD b/python/BUILD index 089066c0c..b648b15c6 100644 --- a/python/BUILD +++ b/python/BUILD @@ -23,8 +23,8 @@ py_test( srcs = ["_jsonnet_test.py"], data = [ "testdata/basic_check.jsonnet", - "testdata/binary1230123.bin", "testdata/binary123.bin", + "testdata/binary1230123.bin", "testdata/trivial.jsonnet", "testdata/trivial_no_eol.jsonnet", ], diff --git a/stdlib/BUILD b/stdlib/BUILD index 06d4ad634..519ca91be 100644 --- a/stdlib/BUILD +++ b/stdlib/BUILD @@ -7,8 +7,8 @@ filegroup( cc_library( name = "std", - hdrs = ["std.jsonnet.h"], srcs = [":gen-std-jsonnet-h"], + hdrs = ["std.jsonnet.h"], includes = ["."], linkstatic = 1, ) diff --git a/third_party/json/BUILD b/third_party/json/BUILD index 93a9710dd..896d2d1bc 100644 --- a/third_party/json/BUILD +++ b/third_party/json/BUILD @@ -1,4 +1,5 @@ licenses(["permissive"]) + package(default_visibility = ["//visibility:public"]) cc_library( @@ -10,4 +11,3 @@ cc_library( ], includes = ["."], ) - diff --git a/third_party/md5/BUILD b/third_party/md5/BUILD index 56103677d..cba3d668a 100644 --- a/third_party/md5/BUILD +++ b/third_party/md5/BUILD @@ -1,4 +1,5 @@ licenses(["permissive"]) + package(default_visibility = ["//visibility:public"]) cc_library( @@ -9,7 +10,6 @@ cc_library( hdrs = [ "md5.h", ], - linkopts = ["-lm"], includes = ["."], + linkopts = ["-lm"], ) - diff --git a/tools/build_defs/extensions.bzl b/tools/build_defs/extensions.bzl index 0a5aa700e..00e35c1c3 100644 --- a/tools/build_defs/extensions.bzl +++ b/tools/build_defs/extensions.bzl @@ -3,10 +3,10 @@ load(":python_repo.bzl", "python_headers") def _impl(repository_ctx): git_repository( - name = "io_bazel_rules_jsonnet", - commit = "ad2b4204157ddcf7919e8bd210f607f8a801aa7f", - remote = "https://github.com/bazelbuild/rules_jsonnet.git", - shallow_since = "1556260764 +0200", + name = "io_bazel_rules_jsonnet", + commit = "ad2b4204157ddcf7919e8bd210f607f8a801aa7f", + remote = "https://github.com/bazelbuild/rules_jsonnet.git", + shallow_since = "1556260764 +0200", ) python_headers(name = "default_python3_headers") diff --git a/tools/build_defs/python_repo.bzl b/tools/build_defs/python_repo.bzl index 38d8b5f60..8e789e137 100644 --- a/tools/build_defs/python_repo.bzl +++ b/tools/build_defs/python_repo.bzl @@ -14,26 +14,27 @@ cc_library( """ def _impl(repository_ctx): - rctx = repository_ctx - if "/" in rctx.attr.path or "\\" in rctx.attr.path: - # Canonicalize the path - realpath = rctx.path(rctx.attr.path) - else: - # Find it in $PATH - realpath = rctx.which(rctx.attr.path) - rctx.symlink(realpath, "bin/python") - include_path = rctx.execute([ - realpath, "-c", "import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())", - ]) - if include_path.return_code != 0: - fail("Failed to locate Python headers:\n" + include_path.stderr) - rctx.symlink(include_path.stdout.strip(), "include") - rctx.file( - "WORKSPACE", - 'workspace(name = "{}")\n'.format(rctx.name), - ) - rctx.file("BUILD", build_file_contents) - + rctx = repository_ctx + if "/" in rctx.attr.path or "\\" in rctx.attr.path: + # Canonicalize the path + realpath = rctx.path(rctx.attr.path) + else: + # Find it in $PATH + realpath = rctx.which(rctx.attr.path) + rctx.symlink(realpath, "bin/python") + include_path = rctx.execute([ + realpath, + "-c", + "import distutils.sysconfig; print(distutils.sysconfig.get_python_inc())", + ]) + if include_path.return_code != 0: + fail("Failed to locate Python headers:\n" + include_path.stderr) + rctx.symlink(include_path.stdout.strip(), "include") + rctx.file( + "WORKSPACE", + 'workspace(name = "{}")\n'.format(rctx.name), + ) + rctx.file("BUILD", build_file_contents) python_headers = repository_rule( implementation = _impl, From 3aa1fabe0e3fe30aab2afb71157274d1752f1822 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 18 Jan 2025 13:44:58 +0000 Subject: [PATCH 10/75] chore: define a local jsonnet_json_golden_test rule instead of using rules_jsonnet Although rules_jsonnet is the appropriate Bazel module for external Jsonnet users to use for their Jsonnet build actions, using it internally within the jsonnet repo itself is actually a little difficult as it creates a mutually recursive dependency. Since the only thing it's being used for is the jsonnet_json_test rule, and we have scripts to runs such tests anyway, it's relatively easy to define our own jsonnet_json_golden_test rule and avoid the recursive dependency. --- cpp/testdata/BUILD | 25 +++------ test_suite/BUILD | 8 +++ tools/build_defs/golden_test.bzl | 87 ++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 19 deletions(-) create mode 100644 test_suite/BUILD create mode 100644 tools/build_defs/golden_test.bzl diff --git a/cpp/testdata/BUILD b/cpp/testdata/BUILD index 7f02498b9..c019693b1 100644 --- a/cpp/testdata/BUILD +++ b/cpp/testdata/BUILD @@ -1,40 +1,27 @@ -load( - "@io_bazel_rules_jsonnet//jsonnet:jsonnet.bzl", - "jsonnet_library", - "jsonnet_to_json_test", -) +load("//tools/build_defs:golden_test.bzl", "jsonnet_json_golden_test") package(default_visibility = ["//visibility:public"]) -jsonnet_to_json_test( +jsonnet_json_golden_test( name = "example_test", src = "example.jsonnet", golden = "example_golden.json", ) -jsonnet_to_json_test( +jsonnet_json_golden_test( name = "importing_test", src = "importing.jsonnet", + data = ["example.jsonnet"], golden = "importing_golden.json", - imports = ["."], - deps = [":testlib"], ) -jsonnet_to_json_test( +jsonnet_json_golden_test( name = "invalid_test", src = "invalid.jsonnet", - error = 1, + expect_error = True, golden = "invalid.out", ) -jsonnet_library( - name = "testlib", - srcs = [ - "example.jsonnet", - "importing.jsonnet", - ], -) - filegroup( name = "testdata", srcs = [ diff --git a/test_suite/BUILD b/test_suite/BUILD new file mode 100644 index 000000000..12c893782 --- /dev/null +++ b/test_suite/BUILD @@ -0,0 +1,8 @@ +package(default_visibility = ["//visibility:private"]) + +# Export the tests.source so it can be used by Bazel tests elsewhere in the repo. +sh_library( + name = "tests_sh_lib", + srcs = ["tests.source"], + visibility = ["//tools/build_defs:__pkg__"], +) diff --git a/tools/build_defs/golden_test.bzl b/tools/build_defs/golden_test.bzl new file mode 100644 index 000000000..71e3da541 --- /dev/null +++ b/tools/build_defs/golden_test.bzl @@ -0,0 +1,87 @@ +"""Bazel rules for running golden tests.""" + +def _gen_test_script(ctx): + return """\ +#!/bin/bash + +set -uo pipefail + +# We don't run 'init' or 'deinit' from tests.source, as these +# just create & remove a TMP dir and Bazel deals with that for us; +# instead we just need to ensure that the path for the Bazel +# managed temp dir is available in TMP_DIR. + +TMP_DIR="$TEST_TMPDIR" +VERBOSE=true +DISABLE_ERROR_TESTS= +SUMMARY_ONLY= + +source ./test_suite/tests.source + +GOLDEN_OUTPUT=$(cat '{golden_path}') + +test_eval '{jsonnet_path}' '{input_path}' '{expected_exit_code}' "$GOLDEN_OUTPUT" '{golden_kind}' + +if [ $FAILED -eq 0 ] ; then + echo "$0: All $EXECUTED test scripts pass." + exit 0 +else + echo "$0: FAILED: $FAILED / $EXECUTED" + exit 1 +fi +""".format( + jsonnet_path = ctx.executable.jsonnet.short_path, + input_path = ctx.file.src.short_path, + expected_exit_code = 1 if ctx.attr.expect_error else 0, + golden_path = ctx.file.golden.short_path, + golden_kind = "PLAIN", + ) + +def _jsonnet_json_golden_test_impl(ctx): + test_script = ctx.actions.declare_file(ctx.label.name) + ctx.actions.write( + output = test_script, + is_executable = True, + content = _gen_test_script(ctx), + ) + return DefaultInfo( + executable = test_script, + runfiles = ctx.runfiles( + transitive_files = ctx.attr._test_sh_lib.files, + files = [ + ctx.executable.jsonnet, + ctx.file.src, + ctx.file.golden, + ] + ctx.files.data, + ), + ) + +jsonnet_json_golden_test = rule( + implementation = _jsonnet_json_golden_test_impl, + test = True, + attrs = { + "src": attr.label( + mandatory = True, + allow_single_file = True, + ), + "data": attr.label_list(allow_files = True), + "golden": attr.label( + mandatory = True, + allow_single_file = True, + ), + "jsonnet": attr.label( + default = "//cmd:jsonnet", + executable = True, + cfg = "exec", + ), + "expect_error": attr.bool( + doc = "If True, the golden file is the expected stderr output from jsonnet", + ), + "canonicalize_golden": attr.bool( + doc = "If True, the golden file will be reformatted prior to comparing against the jsonnet output", + ), + "_test_sh_lib": attr.label( + default = "//test_suite:tests_sh_lib", + ), + }, +) From 6c31c582f49cf01c94afec02b7f7201c2f903fa8 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 18 Jan 2025 13:48:20 +0000 Subject: [PATCH 11/75] chore: remove the now-unnecessary dependency on rules_jsonnet --- MODULE.bazel | 1 - WORKSPACE | 7 ------- tools/build_defs/extensions.bzl | 9 +-------- 3 files changed, 1 insertion(+), 16 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 2be900140..7cdeaab8c 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -10,7 +10,6 @@ build_defs = use_extension("//tools/build_defs:extensions.bzl", "build_defs") use_repo( build_defs, "default_python3_headers", - "io_bazel_rules_jsonnet", ) register_toolchains("//platform_defs:default_python3_toolchain") diff --git a/WORKSPACE b/WORKSPACE index f10f2bb9a..fdbbb1883 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -2,13 +2,6 @@ workspace(name = "jsonnet") load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") -git_repository( - name = "io_bazel_rules_jsonnet", - commit = "ad2b4204157ddcf7919e8bd210f607f8a801aa7f", - remote = "https://github.com/bazelbuild/rules_jsonnet.git", - shallow_since = "1556260764 +0200", -) - git_repository( name = "com_google_googletest", # If updating googletest version, also update CMakeLists.txt.in. diff --git a/tools/build_defs/extensions.bzl b/tools/build_defs/extensions.bzl index 00e35c1c3..5437fb8f8 100644 --- a/tools/build_defs/extensions.bzl +++ b/tools/build_defs/extensions.bzl @@ -1,14 +1,7 @@ -load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") +# load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load(":python_repo.bzl", "python_headers") def _impl(repository_ctx): - git_repository( - name = "io_bazel_rules_jsonnet", - commit = "ad2b4204157ddcf7919e8bd210f607f8a801aa7f", - remote = "https://github.com/bazelbuild/rules_jsonnet.git", - shallow_since = "1556260764 +0200", - ) - python_headers(name = "default_python3_headers") build_defs = module_extension( From 88e7354a6cf48a22fb5eb85f658ec40837cf8814 Mon Sep 17 00:00:00 2001 From: Daniel Pozo Escalona Date: Sat, 18 Jan 2025 15:29:53 +0100 Subject: [PATCH 12/75] Document jpathdir option in Python binding (#1150) --- doc/ref/bindings.html | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/ref/bindings.html b/doc/ref/bindings.html index 030c6ab5b..7993aeab3 100644 --- a/doc/ref/bindings.html +++ b/doc/ref/bindings.html @@ -128,6 +128,7 @@

    Python API

    Keyword arguments to these functions are used to control the virtual machine. They are:

      +
    • jpathdir   (string or list of strings)
    • max_stack   (number)
    • gc_min_objects   (number)
    • gc_growth_trigger   (number)
    • From a02a615def105c861fb02ef8bc5d89c9c5a3c25b Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 18 Jan 2025 16:13:44 +0000 Subject: [PATCH 13/75] chore: enable more triggers for the CI checks This is mostly experimental; I'm hoping to be able to trigger checks on some existing PRs... --- .github/workflows/build_and_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index b7c08484a..367347a36 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -2,6 +2,7 @@ name: Build and Test on: pull_request: + types: [opened, reopened, synchronize, ready_for_review, review_requested] branches-ignore: - 'gh-pages' push: From 2b0122c180d89f077a98e2f7e4b608c298522478 Mon Sep 17 00:00:00 2001 From: Richard Barnes Date: Sat, 18 Jan 2025 11:19:29 -0500 Subject: [PATCH 14/75] Make fallthroughs explicit and enable warnings around this (#1161) --- CMakeLists.txt | 4 ++-- core/desugarer.cpp | 2 +- core/vm.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 213539449..84e554b6a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,8 +36,8 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${GLOBAL_OUTPUT_PATH}) if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") set(OPT "-O3") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -Wextra -pedantic -std=c99 -O3 ${OPT}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -Woverloaded-virtual -pedantic -std=c++17 -fPIC ${OPT}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -Wextra -Wimplicit-fallthrough -pedantic -std=c99 -O3 ${OPT}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -Wall -Wextra -Wimplicit-fallthrough -Woverloaded-virtual -pedantic -std=c++17 -fPIC ${OPT}") else() # TODO: Windows support. message(FATAL_ERROR "Compiler ${CMAKE_CXX_COMPILER_ID} not supported") diff --git a/core/desugarer.cpp b/core/desugarer.cpp index 6c38eb913..611c1545e 100644 --- a/core/desugarer.cpp +++ b/core/desugarer.cpp @@ -732,7 +732,7 @@ class Desugarer { } break; case BOP_MANIFEST_UNEQUAL: invert = true; - /* fallthrough */ + [[fallthrough]]; case BOP_MANIFEST_EQUAL: { ast_ = equals(ast->location, ast->left, ast->right); if (invert) diff --git a/core/vm.cpp b/core/vm.cpp index 0e21c82aa..7cc0a8655 100644 --- a/core/vm.cpp +++ b/core/vm.cpp @@ -2390,7 +2390,7 @@ class Interpreter { stack.top().val2 = scratch; stack.top().kind = FRAME_BINARY_OP; } - // Falls through. + [[fallthrough]]; case FRAME_BINARY_OP: { const auto &ast = *static_cast(f.ast); const Value &lhs = stack.top().val; From 4487bfb8ba7ed7a3d91d79d4a1aa2205e7839558 Mon Sep 17 00:00:00 2001 From: Robert <766670+rben01@users.noreply.github.com> Date: Sat, 18 Jan 2025 11:38:28 -0500 Subject: [PATCH 15/75] Added constant pi and several math functions (#1187) Add constant pi and several math functions Added: * pi (up to IEEE 64-bit binary float precision) * deg2rad, rad2deg * atan2 (builtin using C/C++ std library) * hypot (builtin using C/C++ std library) * log2, log10 --- core/desugarer.cpp | 4 ++- core/vm.cpp | 16 +++++++++ doc/_stdlib_gen/stdlib-content.jsonnet | 9 +++++ doc/ref/spec.html | 3 ++ doc/ref/stdlib.html | 5 +++ stdlib/std.jsonnet | 9 +++++ test_suite/stdlib.jsonnet | 48 +++++++++++++++++--------- 7 files changed, 77 insertions(+), 17 deletions(-) diff --git a/core/desugarer.cpp b/core/desugarer.cpp index 611c1545e..3ed2b453e 100644 --- a/core/desugarer.cpp +++ b/core/desugarer.cpp @@ -36,7 +36,7 @@ struct BuiltinDecl { std::vector params; }; -static unsigned long max_builtin = 38; +static unsigned long max_builtin = 40; BuiltinDecl jsonnet_builtin_decl(unsigned long builtin) { switch (builtin) { @@ -79,6 +79,8 @@ BuiltinDecl jsonnet_builtin_decl(unsigned long builtin) case 36: return {U"parseYaml", {U"str"}}; case 37: return {U"encodeUTF8", {U"str"}}; case 38: return {U"decodeUTF8", {U"arr"}}; + case 39: return {U"atan2", {U"y", U"x"}}; + case 40: return {U"hypot", {U"a", U"b"}}; default: std::cerr << "INTERNAL ERROR: Unrecognized builtin function: " << builtin << std::endl; std::abort(); diff --git a/core/vm.cpp b/core/vm.cpp index 7cc0a8655..27629fd67 100644 --- a/core/vm.cpp +++ b/core/vm.cpp @@ -937,6 +937,8 @@ class Interpreter { builtins["parseYaml"] = &Interpreter::builtinParseYaml; builtins["encodeUTF8"] = &Interpreter::builtinEncodeUTF8; builtins["decodeUTF8"] = &Interpreter::builtinDecodeUTF8; + builtins["atan2"] = &Interpreter::builtinAtan2; + builtins["hypot"] = &Interpreter::builtinHypot; DesugaredObject *stdlib = makeStdlibAST(alloc, "__internal__"); jsonnet_static_analysis(stdlib); @@ -1099,6 +1101,20 @@ class Interpreter { return nullptr; } + const AST *builtinAtan2(const LocationRange &loc, const std::vector &args) + { + validateBuiltinArgs(loc, "atan2", args, {Value::NUMBER, Value::NUMBER}); + scratch = makeNumberCheck(loc, std::atan2(args[0].v.d, args[1].v.d)); + return nullptr; + } + + const AST *builtinHypot(const LocationRange &loc, const std::vector &args) + { + validateBuiltinArgs(loc, "hypot", args, {Value::NUMBER, Value::NUMBER}); + scratch = makeNumberCheck(loc, std::hypot(args[0].v.d, args[1].v.d)); + return nullptr; + } + const AST *builtinType(const LocationRange &, const std::vector &args) { switch (args[0].t) { diff --git a/doc/_stdlib_gen/stdlib-content.jsonnet b/doc/_stdlib_gen/stdlib-content.jsonnet index c36e1c5a8..27b1d63e2 100644 --- a/doc/_stdlib_gen/stdlib-content.jsonnet +++ b/doc/_stdlib_gen/stdlib-content.jsonnet @@ -98,6 +98,8 @@ local html = import 'html.libsonnet';
        std.pow(x, n)
        std.exp(x)
        std.log(x)
      +
        std.log2(x)
      +
        std.log10(x)
        std.exponent(x)
        std.mantissa(x)
        std.floor(x)
      @@ -109,12 +111,19 @@ local html = import 'html.libsonnet';
        std.asin(x)
        std.acos(x)
        std.atan(x)
      +
        std.atan2(y, x)
      +
        std.deg2rad(x)
      +
        std.rad2deg(x)
      +
        std.hypot(a, b)
        std.round(x)
        std.isEven(x)
        std.isOdd(x)
        std.isInteger(x)
        std.isDecimal(x)
    +

    + The constant std.pi is also available. +

    The function std.mod(a, b) is what the % operator is desugared to. It performs modulo arithmetic if the left hand side is a number, or if the left hand side is a string, diff --git a/doc/ref/spec.html b/doc/ref/spec.html index 9cfd6c01b..a9d7b852e 100644 --- a/doc/ref/spec.html +++ b/doc/ref/spec.html @@ -2825,7 +2825,10 @@

    Execution

    std.asin(x), std.acos(x), std.atan(x), + std.atan2(y, x), std.log(x), + std.log2(x), + std.log10(x), std.exp(x), std.mantissa(x), std.exponent(x) and diff --git a/doc/ref/stdlib.html b/doc/ref/stdlib.html index dc18e227d..79703bbbb 100644 --- a/doc/ref/stdlib.html +++ b/doc/ref/stdlib.html @@ -234,6 +234,8 @@

      std.pow(x, n)
      std.exp(x)
      std.log(x)
    +
      std.log2(x)
    +
      std.log10(x)
      std.exponent(x)
      std.mantissa(x)
      std.floor(x)
    @@ -245,6 +247,9 @@

      std.asin(x)
      std.acos(x)
      std.atan(x)
    +
      std.atan2(y, x)
    +
      std.deg2rad(x)
    +
      std.rad2deg(x)
      std.round(x)
      std.isEven(x)
      std.isOdd(x)
    diff --git a/stdlib/std.jsonnet b/stdlib/std.jsonnet index fccbe26d5..ec7ab8f4d 100644 --- a/stdlib/std.jsonnet +++ b/stdlib/std.jsonnet @@ -261,6 +261,15 @@ limitations under the License. else error 'Operator % cannot be used on types ' + std.type(a) + ' and ' + std.type(b) + '.', + // this is the most precision that will fit in a f64 + pi:: 3.14159265358979311600, + + deg2rad(x):: x * std.pi / 180, + rad2deg(x):: x * 180 / std.pi, + + log2(x):: std.log(x) / std.log(2), + log10(x):: std.log(x) / std.log(10), + map(func, arr):: if !std.isFunction(func) then error ('std.map first param must be function, got ' + std.type(func)) diff --git a/test_suite/stdlib.jsonnet b/test_suite/stdlib.jsonnet index fc1161ed5..1ea325c68 100644 --- a/test_suite/stdlib.jsonnet +++ b/test_suite/stdlib.jsonnet @@ -56,31 +56,47 @@ std.assertEqual(std.abs(33), 33) && std.assertEqual(std.abs(-33), 33) && std.assertEqual(std.abs(0), 0) && -// Ordinary (non-test) code can define pi as 2*std.acos(0) -local pi = 3.14159265359; - -assertClose(std.sin(0.0 * pi), 0) && -assertClose(std.sin(0.5 * pi), 1) && -assertClose(std.sin(1.0 * pi), 0) && -assertClose(std.sin(1.5 * pi), -1) && -assertClose(std.sin(2.0 * pi), 0) && -assertClose(std.cos(0.0 * pi), 1) && -assertClose(std.cos(0.5 * pi), 0) && -assertClose(std.cos(1.0 * pi), -1) && -assertClose(std.cos(1.5 * pi), 0) && -assertClose(std.cos(2.0 * pi), 1) && +assertClose(std.sin(0.0 * std.pi), 0) && +assertClose(std.sin(0.5 * std.pi), 1) && +assertClose(std.sin(1.0 * std.pi), 0) && +assertClose(std.sin(1.5 * std.pi), -1) && +assertClose(std.sin(2.0 * std.pi), 0) && +assertClose(std.cos(0.0 * std.pi), 1) && +assertClose(std.cos(0.5 * std.pi), 0) && +assertClose(std.cos(1.0 * std.pi), -1) && +assertClose(std.cos(1.5 * std.pi), 0) && +assertClose(std.cos(2.0 * std.pi), 1) && assertClose(std.tan(0), 0) && -assertClose(std.tan(0.25 * pi), 1) && +assertClose(std.tan(0.25 * std.pi), 1) && assertClose(std.asin(0), 0) && assertClose(std.acos(1), 0) && -assertClose(std.asin(1), 0.5 * pi) && -assertClose(std.acos(0), 0.5 * pi) && +assertClose(std.asin(1), 0.5 * std.pi) && +assertClose(std.acos(0), 0.5 * std.pi) && assertClose(std.atan(0), 0) && +assertClose(std.atan2(1, 1), std.pi / 4) && +assertClose(std.atan2(-1, 1), -std.pi / 4) && +assertClose(std.atan2(1.2, -3.8), 2.835713782184941) && // arbitrary, done on a calculator +assertClose(std.deg2rad(0), 0) && +assertClose(std.deg2rad(45), std.pi / 4) && +assertClose(std.deg2rad(90), std.pi / 2) && +assertClose(std.deg2rad(172), 3.0019663134302466) && // arbitrary, done on a calculator +assertClose(std.rad2deg(std.pi / 4), 45) && +assertClose(std.rad2deg(std.pi / 2), 90) && +assertClose(std.rad2deg(3.0019663134302466), 172) && // arbitrary, done on a calculator +assertClose(std.hypot(3, 4), 5) && +assertClose(std.hypot(5, 12), 13) && +assertClose(std.hypot(1, 1), std.sqrt(2)) && assertClose(std.log(std.exp(5)), 5) && assertClose(std.mantissa(1), 0.5) && assertClose(std.exponent(1), 1) && assertClose(std.mantissa(128), 0.5) && assertClose(std.exponent(128), 8) && +assertClose(std.log2(std.pow(2, -5)), -5) && +assertClose(std.log2(std.pow(2, 0)), 0) && +assertClose(std.log2(std.pow(2, std.pi)), std.pi) && +assertClose(std.log10(std.pow(10, -5)), -5) && +assertClose(std.log10(std.pow(10, 0)), 0) && +assertClose(std.log10(std.pow(10, std.pi)), std.pi) && std.assertEqual(std.clamp(-3, 0, 5), 0) && std.assertEqual(std.clamp(4, 0, 5), 4) && From 92b7747a1f61ebd09c9d013364a8f6de77732e0f Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sun, 19 Jan 2025 14:29:05 +0000 Subject: [PATCH 16/75] fix decode_utf8 for codepoints >= U+010000 Fixes #1181. Add unit test cases to cover UTF-8 decode/encode. --- CMakeLists.txt | 2 +- core/BUILD | 9 ++++ core/CMakeLists.txt | 3 ++ core/unicode.h | 2 +- core/unicode_test.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 core/unicode_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 84e554b6a..77331f6ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,6 +133,6 @@ if (BUILD_TESTS) # target runs tests without building them. add_custom_target(run_tests COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS libjsonnet_test libjsonnet_test_file libjsonnet_test_snippet - jsonnet parser_test lexer_test libjsonnet++_test libjsonnet_test_locale + jsonnet unicode_test parser_test lexer_test libjsonnet++_test libjsonnet_test_locale ) endif() diff --git a/core/BUILD b/core/BUILD index 62b4459f5..c84744ad3 100644 --- a/core/BUILD +++ b/core/BUILD @@ -69,3 +69,12 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "unicode_test", + srcs = ["unicode_test.cpp"], + deps = [ + ":libjsonnet", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 048addb24..8ab37775c 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -87,6 +87,9 @@ function(add_test_executable test_name) endfunction() if (BUILD_TESTS) + add_test_executable(unicode_test) + add_test(unicode_test ${GLOBAL_OUTPUT_PATH}/unicode_test) + add_test_executable(lexer_test) add_test(lexer_test ${GLOBAL_OUTPUT_PATH}/lexer_test) diff --git a/core/unicode.h b/core/unicode.h index 56484cc2d..b28fcd4b7 100644 --- a/core/unicode.h +++ b/core/unicode.h @@ -114,7 +114,7 @@ static inline char32_t decode_utf8(const std::string &str, size_t &i) if ((c3 & 0xC0) != 0x80) { return JSONNET_CODEPOINT_ERROR; } - return ((c0 & 0x7) << 24ul) | ((c1 & 0x3F) << 12ul) | ((c2 & 0x3F) << 6) | (c3 & 0x3F); + return ((c0 & 0x7) << 18ul) | ((c1 & 0x3F) << 12ul) | ((c2 & 0x3F) << 6) | (c3 & 0x3F); } else { return JSONNET_CODEPOINT_ERROR; } diff --git a/core/unicode_test.cpp b/core/unicode_test.cpp new file mode 100644 index 000000000..c34365ee9 --- /dev/null +++ b/core/unicode_test.cpp @@ -0,0 +1,114 @@ +/* +Copyright 2025 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include +#include +#include +#include +#include "unicode.h" +#include "gtest/gtest.h" + +namespace jsonnet::internal { +namespace { + +void testEncodeDecode(char32_t codepoint, const std::string &expect_utf8) { + std::string buffer; + size_t len = encode_utf8(codepoint, buffer); + EXPECT_EQ(len, expect_utf8.size()); + EXPECT_EQ(buffer, expect_utf8); + + size_t at = 0; + char32_t decoded = decode_utf8(expect_utf8, at); + EXPECT_EQ(decoded, codepoint); + EXPECT_EQ(at, expect_utf8.size() - 1); +} + +TEST(Unicode, TestUTF8) +{ + // ASCII encodes as itself. + testEncodeDecode(0x00, std::string("\x00", 1)); + testEncodeDecode(0x41, "A"); + testEncodeDecode(0x7f, "\x7f"); + + testEncodeDecode(0x80, "\xc2\x80"); + testEncodeDecode(0x100, "\xc4\x80"); + testEncodeDecode(0x7ff, "\xdf\xbf"); + + testEncodeDecode(0x800, "\xe0\xa0\x80"); + testEncodeDecode(0x1482, "\xe1\x92\x82"); + testEncodeDecode(0xffff, "\xef\xbf\xbf"); + + testEncodeDecode(0x010000, "\xf0\x90\x80\x80"); + testEncodeDecode(0x01f600, "\xf0\x9f\x98\x80"); // U+1F600 "Grinning Face" + testEncodeDecode(0x0f057e, "\xf3\xb0\x95\xbe"); // U+F057E Private use area character + testEncodeDecode(0x10ffff, "\xf4\x8f\xbf\xbf"); +} + +TEST(Unicode, TestUTF8RejectBad) +{ + const auto test_cases = std::array{ + "\x80", // Continuation byte without leading byte + "\xa0", // Continuation byte without leading byte + "\xbf", // Continuation byte without leading byte + "\xc0", // Leading byte for 2-byte sequence (missing tail) + "\xe0", // Leading byte for 3-byte sequence (missing tail) + "\xf0", // Leading byte for 4-byte sequence (missing tail) + "\xf8\x83\x83\x83", // Invalid leading byte + "\xe0\x80", // Leading byte for 3-byte sequence (missing tail) + "\xf0\x80", // Leading byte for 4-byte sequence (missing tail) + "\xf0\x80\x80", // Leading byte for 4-byte sequence (missing tail) + "\xc0\xcf", // Leading byte for 2-byte sequence (incorrect tail) + "\xe0\xcf", // Leading byte for 3-byte sequence (incorrect tail) + "\xf0\xcf", // Leading byte for 4-byte sequence (incorrect tail) + "\xe0\xcf\x80", // Leading byte for 3-byte sequence (incorrect tail) + "\xf0\xcf\x80", // Leading byte for 4-byte sequence (incorrect tail) + "\xe0\x80\xcf", // Leading byte for 3-byte sequence (incorrect tail) + "\xf0\x80\xcf", // Leading byte for 4-byte sequence (incorrect tail) + "\xf0\x80\x80\xcf", // Leading byte for 4-byte sequence (incorrect tail) + "\xf0\x80\xcf\x80", // Leading byte for 4-byte sequence (incorrect tail) + "\xf0\xcf\x80\x80", // Leading byte for 4-byte sequence (incorrect tail) + }; + for (size_t i = 0; i < test_cases.size(); ++i) { + const auto str = test_cases[i]; + size_t at = 0; + char32_t c = decode_utf8(str, at); + + EXPECT_EQ(c, JSONNET_CODEPOINT_ERROR) << "expect decode to reject. case " << i << std::endl; + } +} + +TEST(Unicode, TestUTF8RoundTripExhaustive) +{ + // Encode every Unicode code-point as UTF-8 and verify that + // it decodes to the same value. + std::string buffer; + for (int x = 0; x < JSONNET_CODEPOINT_MAX; ++x) { + if (x == JSONNET_CODEPOINT_ERROR) { + continue; + } + buffer.clear(); + encode_utf8(x, buffer); + + size_t at = 0; + char32_t y = decode_utf8(buffer, at); + EXPECT_NE(y, JSONNET_CODEPOINT_ERROR) << "UTF-8 roundtrip failed for codepoint " << x << " decode rejects" << std::endl; + EXPECT_EQ(x, y) << "UTF-8 roundtrip failed for codepoint " << x << " converts to " << y << std::endl; + EXPECT_EQ(at, buffer.size() - 1) << "UTF-8 roundtrip failed for codepoint " << x << " decodes incorrect length" << std::endl; + } +} + +} // namespace +} // namespace jsonnet::internal From 5a4e8e34cc1fe841bdebb983646b9b9ae8fa8ca4 Mon Sep 17 00:00:00 2001 From: Tim Vergenz Date: Mon, 20 Jan 2025 14:25:51 -0500 Subject: [PATCH 17/75] feat: add `|||-` chomped text block syntax (#1175) * feat: add |||- chomped text block syntax Resolves #289 * revert MathJax change The optable in the MathJax code is not related to Jsonnet's syntax, and should not have `|||-` added. --------- Co-authored-by: John Bartholomew --- core/formatter.cpp | 9 +++++++- core/lexer.cpp | 11 ++++++++++ doc/js/codemirror-mode-jsonnet.js | 2 +- doc/ref/spec.html | 22 +++++++++++-------- test_suite/text_block.jsonnet | 27 ++++++++++++++++++++++++ test_suite/text_block.jsonnet.fmt.golden | 26 +++++++++++++++++++++++ 6 files changed, 86 insertions(+), 11 deletions(-) diff --git a/core/formatter.cpp b/core/formatter.cpp index cad3369c1..ac6d4b1e3 100644 --- a/core/formatter.cpp +++ b/core/formatter.cpp @@ -500,7 +500,11 @@ class Unparser { o << encode_utf8(ast->value); o << "'"; } else if (ast->tokenKind == LiteralString::BLOCK) { - o << "|||\n"; + o << "|||"; + if (ast->value.back() != U'\n') { + o << "-"; + } + o << "\n"; if (ast->value.c_str()[0] != U'\n') o << ast->blockIndent; for (const char32_t *cp = ast->value.c_str(); *cp != U'\0'; ++cp) { @@ -513,6 +517,9 @@ class Unparser { o << ast->blockIndent; } } + if (ast->value.back() != U'\n') { + o << "\n"; + } o << ast->blockTermIndent << "|||"; } else if (ast->tokenKind == LiteralString::VERBATIM_DOUBLE) { o << "@\""; diff --git a/core/lexer.cpp b/core/lexer.cpp index 31599f4d7..10319875c 100644 --- a/core/lexer.cpp +++ b/core/lexer.cpp @@ -704,6 +704,13 @@ Tokens jsonnet_lex(const std::string &filename, const char *input) // Text block if (*c == '|' && *(c + 1) == '|' && *(c + 2) == '|') { c += 3; // Skip the "|||". + + bool chomp_trailing_nl = false; + if (*c == '-') { + chomp_trailing_nl = true; + c++; + } + while (is_horz_ws(*c)) ++c; // Chomp whitespace at end of line. if (*c != '\n') { auto msg = "text block syntax requires new line after |||."; @@ -762,6 +769,10 @@ Tokens jsonnet_lex(const std::string &filename, const char *input) c += 3; // Leave after the last | data = block.str(); kind = Token::STRING_BLOCK; + if (chomp_trailing_nl) { + assert(data.back() == '\n'); + data.pop_back(); + } break; // Out of the while loop. } } diff --git a/doc/js/codemirror-mode-jsonnet.js b/doc/js/codemirror-mode-jsonnet.js index b0b826bbe..ec8649746 100644 --- a/doc/js/codemirror-mode-jsonnet.js +++ b/doc/js/codemirror-mode-jsonnet.js @@ -176,7 +176,7 @@ } // Enter text block. - if (stream.match(/\|\|\|/)) { + if (stream.match(/\|\|\|-?/)) { state.textBlock = true; state.textBlockIndent = null; return "string"; diff --git a/doc/ref/spec.html b/doc/ref/spec.html index a9d7b852e..e21350545 100644 --- a/doc/ref/spec.html +++ b/doc/ref/spec.html @@ -168,15 +168,19 @@

    Lexing

    subsequent non-quoted '

  • - Text block, beginning with |||, followed by optional whitespace and a - new-line. The next non-empty line must be prefixed with some non-zero length - whitespace W. The block ends at the first subsequent line that is non-empty - and does not begin with W, and it is an error if this line does not contain - some optional whitespace followed by |||. The content of the string is - the concatenation of all the lines between the two |||, which either - begin with W (in which case that prefix is stripped) or they are empty lines - (in which case they remain as empty lines). The line ending style in the file is - preserved in the string. This form cannot be used in import statements. + Text block, beginning with ||| followed by an optional + -, then optional whitespace and a new-line. The next non-empty + line must be prefixed with some non-zero length whitespace W. The + block ends at the first subsequent line that is non-empty and does not begin + with W, and it is an error if this line does not contain some + optional whitespace followed by |||. The content of the string + is the concatenation of all the lines between the two |||, + which either begin with W (in which case that prefix is stripped) or + they are empty lines (in which case they remain as empty lines). The line + ending style in the file is preserved in the string. If the beginning + ||| was followed by - then the final new-line is + stripped from the resulting string. This form cannot be used in + import statements.
  • diff --git a/test_suite/text_block.jsonnet b/test_suite/text_block.jsonnet index b57eb4971..db958f848 100644 --- a/test_suite/text_block.jsonnet +++ b/test_suite/text_block.jsonnet @@ -58,6 +58,33 @@ local bash_mixed = ||| std.assertEqual(bash_golden, bash_mixed) && +// Chomp trailing newline +local str1 = |||- + foo bar baz +|||; + +std.assertEqual(str1, "foo bar baz") && + + +// Chomp just one trailing newline +local str1 = |||- + foo bar baz + +|||; + +std.assertEqual(str1, "foo bar baz\n") && + + +// Concatenate chomped blocks +local str1 = |||- + foo bar baz +||| + " " + |||- + biz buzz +|||; + +std.assertEqual(str1, "foo bar baz biz buzz") && + + // More indent local str1 = ||| text diff --git a/test_suite/text_block.jsonnet.fmt.golden b/test_suite/text_block.jsonnet.fmt.golden index 7c7ae6e25..ac9d41334 100644 --- a/test_suite/text_block.jsonnet.fmt.golden +++ b/test_suite/text_block.jsonnet.fmt.golden @@ -58,6 +58,32 @@ local bash_mixed = ||| std.assertEqual(bash_golden, bash_mixed) && +// Chomp trailing newline +local str1 = |||- + foo bar baz +|||; + +std.assertEqual(str1, 'foo bar baz') && + + +// Chomp just one trailing newline +local str1 = ||| + foo bar baz +|||; + +std.assertEqual(str1, 'foo bar baz\n') && + + +// Concatenate chomped blocks +local str1 = |||- + foo bar baz +||| + ' ' + |||- + biz buzz +|||; + +std.assertEqual(str1, 'foo bar baz biz buzz') && + + // More indent local str1 = ||| text From 5408df29610f7c61e570d62c334f0bc41d817c93 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Tue, 18 Feb 2025 21:06:45 +0000 Subject: [PATCH 18/75] chore: jsonnetfmt the jsonnet source files in doc/ directory --- doc/_stdlib_gen/stdlib.jsonnet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/_stdlib_gen/stdlib.jsonnet b/doc/_stdlib_gen/stdlib.jsonnet index 00841cf87..33d3da129 100644 --- a/doc/_stdlib_gen/stdlib.jsonnet +++ b/doc/_stdlib_gen/stdlib.jsonnet @@ -73,7 +73,7 @@ local group(group_spec, prefix) = ]; local stdlibPage = [ - in_panel(html.h1({id: 'standard_library'}, 'Standard Library')), + in_panel(html.h1({ id: 'standard_library' }, 'Standard Library')), '', in_panel(content.intro), '', From 02cb454879ef0c558e0427b23eae89d2d7e10378 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Tue, 18 Feb 2025 21:09:01 +0000 Subject: [PATCH 19/75] chore: update generated stdlib reference html --- doc/ref/stdlib.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/ref/stdlib.html b/doc/ref/stdlib.html index 79703bbbb..5236bc4a3 100644 --- a/doc/ref/stdlib.html +++ b/doc/ref/stdlib.html @@ -250,12 +250,16 @@

    + +

    + The constant std.pi is also available. +

    The function std.mod(a, b) is what the % operator is desugared to. It performs modulo arithmetic if the left hand side is a number, or if the left hand side is a string, From 8f48de6a4802f3f861df505027e867eed7fbf02e Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Tue, 18 Feb 2025 21:10:14 +0000 Subject: [PATCH 20/75] docs: fix formatting of examples for std.manifest* functions fixes #1205 --- doc/_stdlib_gen/stdlib-content.jsonnet | 31 +++-- doc/_stdlib_gen/stdlib.jsonnet | 5 +- doc/ref/stdlib.html | 160 ++++++++++++++++++------- 3 files changed, 138 insertions(+), 58 deletions(-) diff --git a/doc/_stdlib_gen/stdlib-content.jsonnet b/doc/_stdlib_gen/stdlib-content.jsonnet index 27b1d63e2..c2090e7a9 100644 --- a/doc/_stdlib_gen/stdlib-content.jsonnet +++ b/doc/_stdlib_gen/stdlib-content.jsonnet @@ -1,5 +1,14 @@ local html = import 'html.libsonnet'; +local exampleDocMultiline(mid, ex) = + html.spaceless([ + html.p({}, 'Example:'), + html.pre({}, ex.input), + html.p({}, mid), + html.pre({}, ex.output), + ]) +; + { intro: html.paragraphs([ ||| @@ -772,7 +781,7 @@ local html = import 'html.libsonnet'; |||), ], examples: [ - { + exampleDocMultiline('Yields a string containing this JSON:', { input: ||| std.manifestJsonEx( { @@ -796,8 +805,8 @@ local html = import 'html.libsonnet'; y: { a: 1, b: 2, c: [1, 2] }, }, ' ' ), - }, - { + }), + exampleDocMultiline('Yields a string containing this JSON:', { input: ||| std.manifestJsonEx( { @@ -812,7 +821,7 @@ local html = import 'html.libsonnet'; y: { a: 1, b: [1, 2] }, }, '', ' ', ' : ' ), - }, + }), ], }, { @@ -824,7 +833,7 @@ local html = import 'html.libsonnet'; it calls std.manifestJsonEx with a 4-space indent: |||, examples: [ - { + exampleDocMultiline('Yields a string containing this JSON:', { input: ||| std.manifestJson( { @@ -848,7 +857,7 @@ local html = import 'html.libsonnet'; y: { a: 1, b: 2, c: [1, 2] }, } ), - }, + }), ], }, { @@ -860,7 +869,7 @@ local html = import 'html.libsonnet'; it calls std.manifestJsonEx: |||, examples: [ - { + exampleDocMultiline('Yields a string containing this JSON:', { input: ||| std.manifestJsonMinified( { @@ -884,7 +893,7 @@ local html = import 'html.libsonnet'; y: { a: 1, b: 2, c: [1, 2] }, } ), - }, + }), ], }, { @@ -1055,7 +1064,7 @@ local html = import 'html.libsonnet'; one or more whitespaces that are used for indentation: |||, examples: [ - { + exampleDocMultiline('Yields a string containing this TOML file:', { input: ||| std.manifestTomlEx({ key1: "value", @@ -1094,7 +1103,7 @@ local html = import 'html.libsonnet'; ], }, ' ') ), - }, + }), ], }, ], @@ -1602,7 +1611,7 @@ local html = import 'html.libsonnet'; params: ['o'], availableSince: '0.20.0', description: ||| - Returns an array of objects from the given object, each object having two fields: + Returns an array of objects from the given object, each object having two fields: key (string) and value (object). Does not include hidden fields. |||, }, diff --git a/doc/_stdlib_gen/stdlib.jsonnet b/doc/_stdlib_gen/stdlib.jsonnet index 33d3da129..c2a7b8ede 100644 --- a/doc/_stdlib_gen/stdlib.jsonnet +++ b/doc/_stdlib_gen/stdlib.jsonnet @@ -15,7 +15,10 @@ local exampleDoc(ex) = else html.spaceless([html.code({}, ex.input), ' yields ', html.code({}, manifestJsonSingleLine(ex.output))]) ; - html.p({}, html.spaceless(['Example: ', exRep, '.'])) + if std.objectHas(ex, 'type') then + ex + else + html.p({}, html.spaceless(['Example: ', exRep, '.'])) ; local hgroup(body) = html.div({ class: 'hgroup' }, body); diff --git a/doc/ref/stdlib.html b/doc/ref/stdlib.html index 5236bc4a3..dae270f3c 100644 --- a/doc/ref/stdlib.html +++ b/doc/ref/stdlib.html @@ -1549,20 +1549,42 @@

    of an object field:

    - Example: std.manifestJsonEx( - { - x: [1, 2, 3, true, false, null, - "string\nstring"], - y: { a: 1, b: 2, c: [1, 2] }, - }, " ") yields "{\n \"x\": [\n 1,\n 2,\n 3,\n true,\n false,\n null,\n \"string\\nstring\"\n ],\n \"y\": {\n \"a\": 1,\n \"b\": 2,\n \"c\": [\n 1,\n 2\n ]\n }\n}". -

    + Example: +

    std.manifestJsonEx(
    +{
    +    x: [1, 2, 3, true, false, null,
    +        "string\nstring"],
    +    y: { a: 1, b: 2, c: [1, 2] },
    +}, "    ")

    + Yields a string containing this JSON: +

    {
    +    "x": [
    +        1,
    +        2,
    +        3,
    +        true,
    +        false,
    +        null,
    +        "string\nstring"
    +    ],
    +    "y": {
    +        "a": 1,
    +        "b": 2,
    +        "c": [
    +            1,
    +            2
    +        ]
    +    }
    +}

    - Example: std.manifestJsonEx( - { - x: [1, 2, "string\nstring"], - y: { a: 1, b: [1, 2] }, - }, "", " ", " : ") yields "{ \"x\" : [ 1, 2, \"string\\nstring\" ], \"y\" : { \"a\" : 1, \"b\" : [ 1, 2 ] } }". -

    + Example: +

    std.manifestJsonEx(
    +{
    +  x: [1, 2, "string\nstring"],
    +  y: { a: 1, b: [1, 2] },
    +}, "", " ", " : ")

    + Yields a string containing this JSON: +

    { "x" : [ 1, 2, "string\nstring" ], "y" : { "a" : 1, "b" : [ 1, 2 ] } }
    @@ -1591,13 +1613,33 @@

    it calls std.manifestJsonEx with a 4-space indent:

    - Example: std.manifestJson( - { - x: [1, 2, 3, true, false, null, - "string\nstring"], - y: { a: 1, b: 2, c: [1, 2] }, - }) yields "{\n \"x\": [\n 1,\n 2,\n 3,\n true,\n false,\n null,\n \"string\\nstring\"\n ],\n \"y\": {\n \"a\": 1,\n \"b\": 2,\n \"c\": [\n 1,\n 2\n ]\n }\n}". -

    + Example: +

    std.manifestJson(
    +{
    +    x: [1, 2, 3, true, false, null,
    +        "string\nstring"],
    +    y: { a: 1, b: 2, c: [1, 2] },
    +})

    + Yields a string containing this JSON: +

    {
    +    "x": [
    +        1,
    +        2,
    +        3,
    +        true,
    +        false,
    +        null,
    +        "string\nstring"
    +    ],
    +    "y": {
    +        "a": 1,
    +        "b": 2,
    +        "c": [
    +            1,
    +            2
    +        ]
    +    }
    +}
    @@ -1626,13 +1668,15 @@

    it calls std.manifestJsonEx:

    - Example: std.manifestJsonMinified( - { - x: [1, 2, 3, true, false, null, - "string\nstring"], - y: { a: 1, b: 2, c: [1, 2] }, - }) yields "{\"x\":[1,2,3,true,false,null,\"string\\nstring\"],\"y\":{\"a\":1,\"b\":2,\"c\":[1,2]}}". -

    + Example: +

    std.manifestJsonMinified(
    +{
    +    x: [1, 2, 3, true, false, null,
    +        "string\nstring"],
    +    y: { a: 1, b: 2, c: [1, 2] },
    +})

    + Yields a string containing this JSON: +

    {"x":[1,2,3,true,false,null,"string\nstring"],"y":{"a":1,"b":2,"c":[1,2]}}
    @@ -1844,24 +1888,48 @@

    one or more whitespaces that are used for indentation:

    - Example: std.manifestTomlEx({ - key1: "value", - key2: 1, - section: { - a: 1, - b: "str", - c: false, - d: [1, "s", [2, 3]], - subsection: { - k: "v", - }, - }, - sectionArray: [ - { k: "v1", v: 123 }, - { k: "v2", c: "value2" }, - ], - }, " ") yields "key1 = \"value\"\nkey2 = 1\n\n[section]\n a = 1\n b = \"str\"\n c = false\n d = [\n 1,\n \"s\",\n [ 2, 3 ]\n ]\n\n [section.subsection]\n k = \"v\"\n\n[[sectionArray]]\n k = \"v1\"\n v = 123\n\n[[sectionArray]]\n c = \"value2\"\n k = \"v2\"". -

    + Example: +

    std.manifestTomlEx({
    +  key1: "value",
    +  key2: 1,
    +  section: {
    +    a: 1,
    +    b: "str",
    +    c: false,
    +    d: [1, "s", [2, 3]],
    +    subsection: {
    +      k: "v",
    +    },
    +  },
    +  sectionArray: [
    +    { k: "v1", v: 123 },
    +    { k: "v2", c: "value2" },
    +  ],
    +}, "  ")

    + Yields a string containing this TOML file: +

    key1 = "value"
    +key2 = 1
    +
    +[section]
    +  a = 1
    +  b = "str"
    +  c = false
    +  d = [
    +    1,
    +    "s",
    +    [ 2, 3 ]
    +  ]
    +
    +  [section.subsection]
    +    k = "v"
    +
    +[[sectionArray]]
    +  k = "v1"
    +  v = 123
    +
    +[[sectionArray]]
    +  c = "value2"
    +  k = "v2"
    @@ -3083,7 +3151,7 @@

    - Returns an array of objects from the given object, each object having two fields: + Returns an array of objects from the given object, each object having two fields: key (string) and value (object). Does not include hidden fields.

    From 25323cce127ee0b308527e8d3b2cb15502fe6821 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Fri, 21 Feb 2025 22:03:18 +0000 Subject: [PATCH 21/75] ci: trigger release workflow manually not on push-to-tag --- .github/workflows/release.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d497b3e48..3b4d773be 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,10 +15,7 @@ # Cut a release whenever a new tag is pushed to the repo. name: Release -on: - push: - tags: - - "*.*.*" +on: [workflow_dispatch] jobs: build: From 4a983cd2ae16cd12f8ae3983350221723d908671 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Fri, 21 Feb 2025 23:31:26 +0000 Subject: [PATCH 22/75] ci: rename Python build and publish workflow --- .github/workflows/{build_python_wheel.yml => publish-python.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{build_python_wheel.yml => publish-python.yml} (100%) diff --git a/.github/workflows/build_python_wheel.yml b/.github/workflows/publish-python.yml similarity index 100% rename from .github/workflows/build_python_wheel.yml rename to .github/workflows/publish-python.yml From 0566bd76becfd63cce17c7d819a427e0ef8e946b Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Fri, 21 Feb 2025 22:03:36 +0000 Subject: [PATCH 23/75] ci: initial Python build-and-publish workflow Manually triggered. Only set up to publish to Test PyPI for now. --- .github/workflows/publish-python.yml | 88 ++++++++++++++++++++++++---- pyproject.toml | 3 + 2 files changed, 81 insertions(+), 10 deletions(-) create mode 100644 pyproject.toml diff --git a/.github/workflows/publish-python.yml b/.github/workflows/publish-python.yml index d0d1fb613..6ded6307b 100644 --- a/.github/workflows/publish-python.yml +++ b/.github/workflows/publish-python.yml @@ -1,21 +1,56 @@ -name: Build Python Wheel +name: Build and Publish Python Package + +# Be very careful granting extra permissions here, as this workflow uses third party actions. +# Prefer to pin to exact commits for third-party actions. I'm making an exception for actions +# maintained by GitHub itself. # For now, just trigger this workflow manually. -on: [workflow_dispatch] +on: + workflow_dispatch: + inputs: + upload_to_pypi: + description: "Upload generate package files to PyPI" + required: true + type: boolean + use_prod_pypi: + description: "Use production PyPI not Test PyPI" + required: false + type: boolean jobs: + build_sdist: + name: Build Python sdist + runs-on: ubuntu-24.04 + permissions: + contents: read + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.13" + cache: "pip" + + - run: pip install 'build==1.2.2' + + - name: Build sdist + run: python3 -m build --sdist + + - uses: actions/upload-artifact@v4 + with: + name: sdist + path: ./dist/*.gz + if-no-files-found: error + build_wheels: name: Build wheels on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: - #os: [ubuntu-20.04, windows-latest, macos-latest] - os: [ubuntu-20.04, windows-latest] + #os: [ubuntu-22.04, windows-latest, macos-latest] + os: [ubuntu-22.04, windows-latest] - # We're using third party actions (not just the official GitHub-maintained actions), - # so it's very important to minimise the ability for this workflow to do anything dangerous. - # Grant it only read access to the repository content (which is public anyway); it shouldn't - # need anything else. permissions: contents: read @@ -23,7 +58,7 @@ jobs: - uses: actions/checkout@v4 - name: Build wheels - uses: pypa/cibuildwheel@v2.16.5 + uses: pypa/cibuildwheel@ee63bf16da6cddfb925f542f2c7b59ad50e93969 # v2.22.0 env: # Skip PyPy, 32-bit Windows, 32-bit Linux, and CPython before 3.9. # See https://cibuildwheel.readthedocs.io/en/stable/options/#examples_1 @@ -33,5 +68,38 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + name: cibw-wheels-${{ matrix.os }} path: ./wheelhouse/*.whl + if-no-files-found: error + + upload_to_test_pypi: + name: Upload packages to TEST PyPI + needs: [build_sdist, build_wheels] + runs-on: ubuntu-24.04 + if: "${{ inputs.upload_to_pypi && !inputs.use_prod_pypi }}" + permissions: + contents: read + id-token: write # Needed for PyPI Trusted Publishing + environment: + name: testpypi + url: https://test.pypi.org/p/jsonnet + steps: + - uses: actions/download-artifact@v4 + with: + path: cibw-wheels + pattern: cibw-wheels-* + - uses: actions/download-artifact@v4 + with: + path: sdist + name: sdist + - name: Flatten wheels to one directory + run: | + mkdir dist + find cibw-wheels/ -type f -name '*.whl' -exec mv '{}' ./dist/ ';' + find sdist/ -type f -name '*.gz' -exec mv '{}' ./dist/ ';' + - name: Publish to TEST PyPI + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 + with: + verbose: true + print-hash: true + repository-url: https://test.pypi.org/legacy/ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..fed528d4a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" From 05ac3b564abcdc4f1dcc54a45ffa1a440ad6ce42 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 22 Feb 2025 00:08:39 +0000 Subject: [PATCH 24/75] fix Python build version extraction from libjsonnet.h --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 914b90a37..200dc0ab3 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,9 @@ def get_version(): """ Parses the version out of libjsonnet.h """ - rx = re.compile(r'^\s*#\s*define\s+LIB_JSONNET_VERSION\s+"v([0-9.]+)"\s*$') + rx = re.compile( + r'^\s*#\s*define\s+LIB_JSONNET_VERSION\s+"v([0-9.]+(?:-?[a-z][a-z0-9]*)?)"\s*$' + ) with open(os.path.join(DIR, "include/libjsonnet.h")) as f: for line in f: m = rx.match(line) From 55743c3b595abf57ccdbbfd9f8ae11f93a632e8b Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 22 Feb 2025 00:13:36 +0000 Subject: [PATCH 25/75] ci: build and test on Ubuntu 22.04 runner; 20.04 is going away very soon --- .github/workflows/build_and_test.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 367347a36..0518ded4b 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -4,10 +4,10 @@ on: pull_request: types: [opened, reopened, synchronize, ready_for_review, review_requested] branches-ignore: - - 'gh-pages' + - "gh-pages" push: branches: - - 'master' + - "master" tags: - v* @@ -17,7 +17,7 @@ permissions: jobs: check_code_format: name: Check code formatting - runs-on: 'ubuntu-20.04' + runs-on: "ubuntu-22.04" # The code currently doesn't pass formatting. # Enable this workflow job once it does, to detect regressions. if: false @@ -29,7 +29,7 @@ jobs: plain_makefile: name: Build and test using plain Makefile - runs-on: 'ubuntu-20.04' + runs-on: "ubuntu-22.04" steps: - uses: actions/checkout@v4 - name: Build (make all) @@ -41,7 +41,7 @@ jobs: python_module: name: Build and test Python module - runs-on: 'ubuntu-20.04' + runs-on: "ubuntu-22.04" steps: - uses: actions/checkout@v4 - name: Build @@ -53,7 +53,7 @@ jobs: cmake_build: name: Build and test using CMake - runs-on: 'ubuntu-20.04' + runs-on: "ubuntu-22.04" steps: - uses: actions/checkout@v4 - name: Configure (cmake) @@ -70,7 +70,7 @@ jobs: bazel_build: name: Build and test using Bazel - runs-on: 'ubuntu-20.04' + runs-on: "ubuntu-22.04" steps: - uses: actions/checkout@v4 - name: Build From 3325b4b03e03d6e967480d1efb1983bbe78e9b67 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 22 Feb 2025 00:57:26 +0000 Subject: [PATCH 26/75] ci: update the release workflow to do a few extra things This updates to a newer version of the release helper action, and makes some other adjustments: - The release is created as a draft. - Release notes are appended. - The jsonnet version is extracted directly from the libjsonnet.h header, so that it can get version even when running the workflow from an experimental branch. - If the workflow is not run at a tag, or the tag doesn't match the actual jsonnet version as declared in the header, then the generated archive will have a (partial) commit sha included in its name, so that it can be distinguished and identified as non-canonical. --- ...archive_and_notes.sh => create_archive.sh} | 29 ++++++++++++------ .github/workflows/release.yml | 30 +++++++++++++------ 2 files changed, 41 insertions(+), 18 deletions(-) rename .github/workflows/{create_archive_and_notes.sh => create_archive.sh} (56%) diff --git a/.github/workflows/create_archive_and_notes.sh b/.github/workflows/create_archive.sh similarity index 56% rename from .github/workflows/create_archive_and_notes.sh rename to .github/workflows/create_archive.sh index 460ea3f74..cb7a03992 100755 --- a/.github/workflows/create_archive_and_notes.sh +++ b/.github/workflows/create_archive.sh @@ -17,14 +17,25 @@ set -o errexit -o nounset -o pipefail # Set by GH actions, see # https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables -TAG=${GITHUB_REF_NAME} +JSONNET_VERSION="$(grep -Ee '^\s*#\s*define\s+LIB_JSONNET_VERSION\s+"[^"]+"\s*$' include/libjsonnet.h | sed -E -e 's/[^"]+"([^"]+)".*/\1/')" + +VERSION_SUFFIX= +if [[ "${GITHUB_REF_TYPE}" != 'tag' || "${GITHUB_REF_NAME}" != "${JSONNET_VERSION}" ]]; then + >&2 echo 'WARNING: Jsonnet library version in header does not match release ref. Adding commit suffix.' + VERSION_SUFFIX="-${GITHUB_SHA:0:9}" +fi + # A prefix is added to better match the GitHub generated archives. -PREFIX="jsonnet-${TAG}" -ARCHIVE="jsonnet-$TAG.tar.gz" -git archive --format=tar --prefix=${PREFIX}/ ${TAG} | gzip > $ARCHIVE -SHA=$(shasum -a 256 $ARCHIVE | awk '{print $1}') +PREFIX="jsonnet-${JSONNET_VERSION}${VERSION_SUFFIX}" +ARCHIVE="jsonnet-${JSONNET_VERSION}${VERSION_SUFFIX}.tar.gz" +git archive --format=tar --prefix=${PREFIX}/ "${GITHUB_SHA}" | gzip > "$ARCHIVE" +ARCHIVE_SHA=$(shasum -a 256 "$ARCHIVE" | awk '{print $1}') + +echo "archive_sha256=${ARCHIVE_SHA}" >> "$GITHUB_OUTPUT" +echo "jsonnet_version=${JSONNET_VERSION}" >> "$GITHUB_OUTPUT" +echo "jsonnet_version_permanent=${JSONNET_VERSION}${VERSION_SUFFIX}" >> "$GITHUB_OUTPUT" -cat > release_notes.txt << EOF +cat > bazel_dep_release_notes.txt << EOF ### Importing Jsonnet in a project that uses Bazel #### Using Bzlmod with Bazel 6 @@ -32,7 +43,7 @@ cat > release_notes.txt << EOF Add to your \`MODULE.bazel\` file: \`\`\`starlark -bazel_dep(name = "jsonnet", version = "${TAG}") +bazel_dep(name = "jsonnet", version = "${JSONNET_VERSION}") \`\`\` #### Using WORKSPACE @@ -44,9 +55,9 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") http_archive( name = "jsonnet", - sha256 = "${SHA}", + sha256 = "${ARCHIVE_SHA}", strip_prefix = "${PREFIX}", - url = "https://github.com/google/jsonnet/releases/download/${TAG}/jsonnet-${TAG}.tar.gz", + url = "https://github.com/google/jsonnet/releases/download/${JSONNET_VERSION}/${ARCHIVE}", ) \`\`\` diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3b4d773be..ba3965fdb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,21 +15,33 @@ # Cut a release whenever a new tag is pushed to the repo. name: Release -on: [workflow_dispatch] +on: + workflow_dispatch: + inputs: + prerelease: + description: If set, publish this as a release candidate / prerelease version. + type: boolean + required: true jobs: build: - runs-on: ubuntu-latest + permissions: + contents: write + runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v2 - - name: Create release archive and notes - run: .github/workflows/create_archive_and_notes.sh + uses: actions/checkout@v4 + - name: Create release archive + id: create_archive + run: .github/workflows/create_archive.sh - name: Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1 with: - # Use GH feature to populate the changelog automatically - generate_release_notes: true - body_path: release_notes.txt + name: ${{ steps.create_archive.outputs.jsonnet_version }} + tag_name: ${{ steps.create_archive.outputs.jsonnet_version }} + prerelease: ${{ inputs.prerelease }} + draft: true fail_on_unmatched_files: true + body_path: bazel_dep_release_notes.txt + append_body: true files: jsonnet-*.tar.gz From 162652994aefbebf4d4a763f88e553d483a015da Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 22 Feb 2025 01:35:58 +0000 Subject: [PATCH 27/75] ci: add support for publishing to real (not test) PyPI --- .github/workflows/publish-python.yml | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/.github/workflows/publish-python.yml b/.github/workflows/publish-python.yml index 6ded6307b..4f2b27a28 100644 --- a/.github/workflows/publish-python.yml +++ b/.github/workflows/publish-python.yml @@ -72,6 +72,36 @@ jobs: path: ./wheelhouse/*.whl if-no-files-found: error + upload_to_prod_pypi: + name: Upload packages to PyPI + needs: [build_sdist, build_wheels] + runs-on: ubuntu-24.04 + if: "${{ inputs.upload_to_pypi && inputs.use_prod_pypi && startsWith(github.ref, 'refs/tags') }}" + permissions: + contents: read + id-token: write # Needed for PyPI Trusted Publishing + environment: + name: pypi + url: https://pypi.org/p/jsonnet + steps: + - uses: actions/download-artifact@v4 + with: + path: cibw-wheels + pattern: cibw-wheels-* + - uses: actions/download-artifact@v4 + with: + path: sdist + name: sdist + - name: Flatten wheels to one directory + run: | + mkdir dist + find cibw-wheels/ -type f -name '*.whl' -exec mv '{}' ./dist/ ';' + find sdist/ -type f -name '*.gz' -exec mv '{}' ./dist/ ';' + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 + with: + print-hash: true + upload_to_test_pypi: name: Upload packages to TEST PyPI needs: [build_sdist, build_wheels] From fcead4955b1c800204ec3fa0dd94307030b1f4b6 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 22 Feb 2025 15:03:50 +0000 Subject: [PATCH 28/75] add explicit std::array<> template args to support C++14 build Not all the build systems make it easy to specify the C++ standard to use (somehow, Bazel doesn't). C++14 doesn't support deducing the template arguments for std::array. But it seems like there's only one place that needs to be changed to make a C++14 build work ok. --- core/unicode_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/unicode_test.cpp b/core/unicode_test.cpp index c34365ee9..c16bd2b5a 100644 --- a/core/unicode_test.cpp +++ b/core/unicode_test.cpp @@ -59,7 +59,7 @@ TEST(Unicode, TestUTF8) TEST(Unicode, TestUTF8RejectBad) { - const auto test_cases = std::array{ + const auto test_cases = std::array{ "\x80", // Continuation byte without leading byte "\xa0", // Continuation byte without leading byte "\xbf", // Continuation byte without leading byte From 2eccc0ae7f799fab12bb6759c606116599c8190a Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 22 Feb 2025 15:37:28 +0000 Subject: [PATCH 29/75] ci: allow building from prepare-release without adding git hash suffix Gradually trying to work out a reasonable release workflow. Something like: - Update version number etc on branch "prepare-release" - Trigger release workflow: This generates a source archive and a draft GitHub release object. The GitHub release has a propsed tag that comes from the version in libjsonnet.h. - Trigger Python publish workflow: This generates Python packages, then waits for approval before publishing to PyPI. - Fill in release notes in GitHub. Do any last minute checks. Consider actually downloading the release artifacts and checking them locally. - Approve the Python publish workflow. - Publish the GitHub release (which will create the version tag) - Fast-forward master to the new version. It's a little ugly, but the benefits here are: - All the final build artifacts are generated before any publishing so they can be checked (manually or otherwise). - Release notes can be edited in GitHub. - The tag is created at publish time. --- .github/workflows/create_archive.sh | 36 ++++------------------------- .github/workflows/release.yml | 2 -- 2 files changed, 5 insertions(+), 33 deletions(-) diff --git a/.github/workflows/create_archive.sh b/.github/workflows/create_archive.sh index cb7a03992..5e0da8177 100755 --- a/.github/workflows/create_archive.sh +++ b/.github/workflows/create_archive.sh @@ -15,12 +15,14 @@ set -o errexit -o nounset -o pipefail -# Set by GH actions, see -# https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables JSONNET_VERSION="$(grep -Ee '^\s*#\s*define\s+LIB_JSONNET_VERSION\s+"[^"]+"\s*$' include/libjsonnet.h | sed -E -e 's/[^"]+"([^"]+)".*/\1/')" +# GITHUB_REF is set by GH actions, see +# https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables + VERSION_SUFFIX= -if [[ "${GITHUB_REF_TYPE}" != 'tag' || "${GITHUB_REF_NAME}" != "${JSONNET_VERSION}" ]]; then +if [[ ( "${GITHUB_REF_TYPE}" != 'branch' || "${GITHUB_REF_NAME}" != "prepare-release" ) && + ( "${GITHUB_REF_TYPE}" != 'tag' || "${GITHUB_REF_NAME}" != "${JSONNET_VERSION}" ) ]]; then >&2 echo 'WARNING: Jsonnet library version in header does not match release ref. Adding commit suffix.' VERSION_SUFFIX="-${GITHUB_SHA:0:9}" fi @@ -34,31 +36,3 @@ ARCHIVE_SHA=$(shasum -a 256 "$ARCHIVE" | awk '{print $1}') echo "archive_sha256=${ARCHIVE_SHA}" >> "$GITHUB_OUTPUT" echo "jsonnet_version=${JSONNET_VERSION}" >> "$GITHUB_OUTPUT" echo "jsonnet_version_permanent=${JSONNET_VERSION}${VERSION_SUFFIX}" >> "$GITHUB_OUTPUT" - -cat > bazel_dep_release_notes.txt << EOF -### Importing Jsonnet in a project that uses Bazel - -#### Using Bzlmod with Bazel 6 - -Add to your \`MODULE.bazel\` file: - -\`\`\`starlark -bazel_dep(name = "jsonnet", version = "${JSONNET_VERSION}") -\`\`\` - -#### Using WORKSPACE - -Paste this snippet into your \`WORKSPACE\` file: - -\`\`\`starlark -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -http_archive( - name = "jsonnet", - sha256 = "${ARCHIVE_SHA}", - strip_prefix = "${PREFIX}", - url = "https://github.com/google/jsonnet/releases/download/${JSONNET_VERSION}/${ARCHIVE}", -) -\`\`\` - -EOF diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ba3965fdb..b8ba3fbf2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,6 +42,4 @@ jobs: prerelease: ${{ inputs.prerelease }} draft: true fail_on_unmatched_files: true - body_path: bazel_dep_release_notes.txt - append_body: true files: jsonnet-*.tar.gz From 94a21f1eafd39376291e9dca7d15fe9cb4191b2d Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 22 Feb 2025 15:45:31 +0000 Subject: [PATCH 30/75] ci: run build_and_test workflow on pushes to prepare-release --- .github/workflows/build_and_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 0518ded4b..9af18e3e0 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -8,6 +8,7 @@ on: push: branches: - "master" + - "prepare-release" tags: - v* From 061e7abef095c24dbf82bdec4d7a00ed3d3cf39d Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 22 Feb 2025 16:26:27 +0000 Subject: [PATCH 31/75] ci: restructure publish-python to reduce duplication Instead of having two mostly-identical jobs for publishing to PyPI (one for prod one for test), have one job and take an environment selector as a workflow input. --- .github/workflows/publish-python.yml | 53 +++++++--------------------- 1 file changed, 13 insertions(+), 40 deletions(-) diff --git a/.github/workflows/publish-python.yml b/.github/workflows/publish-python.yml index 4f2b27a28..49bd7862b 100644 --- a/.github/workflows/publish-python.yml +++ b/.github/workflows/publish-python.yml @@ -12,10 +12,13 @@ on: description: "Upload generate package files to PyPI" required: true type: boolean - use_prod_pypi: - description: "Use production PyPI not Test PyPI" - required: false - type: boolean + pypi_environment: + description: "Which PyPI instance to publish to" + required: true + type: choice + options: + - testpypi + - pypi jobs: build_sdist: @@ -72,17 +75,17 @@ jobs: path: ./wheelhouse/*.whl if-no-files-found: error - upload_to_prod_pypi: - name: Upload packages to PyPI + upload_to_pypi: + name: "Upload packages to PyPI" needs: [build_sdist, build_wheels] runs-on: ubuntu-24.04 - if: "${{ inputs.upload_to_pypi && inputs.use_prod_pypi && startsWith(github.ref, 'refs/tags') }}" + if: "${{ inputs.upload_to_pypi }}" permissions: contents: read id-token: write # Needed for PyPI Trusted Publishing environment: - name: pypi - url: https://pypi.org/p/jsonnet + name: "${{ inputs.pypi_environment }}" + url: "${{ inputs.pypi_environment == 'pypi' && 'https://pypi.org/p/jsonnet' || 'https://test.pypi.org/p/jsonnet' }}" steps: - uses: actions/download-artifact@v4 with: @@ -98,38 +101,8 @@ jobs: find cibw-wheels/ -type f -name '*.whl' -exec mv '{}' ./dist/ ';' find sdist/ -type f -name '*.gz' -exec mv '{}' ./dist/ ';' - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 - with: - print-hash: true - - upload_to_test_pypi: - name: Upload packages to TEST PyPI - needs: [build_sdist, build_wheels] - runs-on: ubuntu-24.04 - if: "${{ inputs.upload_to_pypi && !inputs.use_prod_pypi }}" - permissions: - contents: read - id-token: write # Needed for PyPI Trusted Publishing - environment: - name: testpypi - url: https://test.pypi.org/p/jsonnet - steps: - - uses: actions/download-artifact@v4 - with: - path: cibw-wheels - pattern: cibw-wheels-* - - uses: actions/download-artifact@v4 - with: - path: sdist - name: sdist - - name: Flatten wheels to one directory - run: | - mkdir dist - find cibw-wheels/ -type f -name '*.whl' -exec mv '{}' ./dist/ ';' - find sdist/ -type f -name '*.gz' -exec mv '{}' ./dist/ ';' - - name: Publish to TEST PyPI uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 with: verbose: true print-hash: true - repository-url: https://test.pypi.org/legacy/ + repository-url: "${{ inputs.pypi_environment == 'testpypi' && 'https://test.pypi.org/legacy/' || '' }}" From 2c3b51491a67ab7b24aecfc23fa3f73f68135129 Mon Sep 17 00:00:00 2001 From: John Bartholomew Date: Sat, 22 Feb 2025 14:33:29 +0000 Subject: [PATCH 32/75] release: prepare to release v0.21.0-rc1 This should be followed shortly by v0.21.0, ideally with no further changes. But since this is the first release for nearly 2 years it seems prudent to be a little cautious and publish an actual release candidate first. --- MODULE.bazel | 2 +- cpp/CMakeLists.txt | 2 +- doc/_stdlib_gen/stdlib-content.jsonnet | 26 +- doc/ref/stdlib.html | 3749 --------------------- include/libjsonnet.h | 6 +- test_cmd/fmt_help.golden.stdout | 2 +- test_cmd/fmt_help.golden.stdout.golang | 2 +- test_cmd/fmt_no_args.golden.stderr | 2 +- test_cmd/fmt_no_args.golden.stderr.golang | 2 +- test_cmd/fmt_version1.golden.stdout | 2 +- test_cmd/fmt_version2.golden.stdout | 2 +- test_cmd/help.golden.stdout.cpp | 2 +- test_cmd/help.golden.stdout.golang | 2 +- test_cmd/no_args.golden.stderr.cpp | 2 +- test_cmd/no_args.golden.stderr.golang | 2 +- test_cmd/version1.golden.stdout.cpp | 2 +- test_cmd/version1.golden.stdout.golang | 2 +- test_cmd/version2.golden.stdout.cpp | 2 +- test_cmd/version2.golden.stdout.golang | 2 +- 19 files changed, 32 insertions(+), 3781 deletions(-) diff --git a/MODULE.bazel b/MODULE.bazel index 7cdeaab8c..cef573755 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,6 +1,6 @@ module( name = "jsonnet", - version = "0.0.0", + version = "0.21.0-rc1", ) bazel_dep(name = "googletest", version = "1.11.0", repo_name = "com_google_googletest") diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 500f65106..8e730fc3c 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -15,7 +15,7 @@ target_link_libraries(libjsonnet++ libjsonnet) # CMake prepends CMAKE_SHARED_LIBRARY_PREFIX to shared libraries, so without # this step the output would be |liblibjsonnet|. set_target_properties(libjsonnet++ PROPERTIES OUTPUT_NAME jsonnet++ - VERSION "0.20.0" + VERSION "0.21.0-rc1" SOVERSION "0" PUBLIC_HEADER "${LIB_HEADER}") install(TARGETS libjsonnet++ diff --git a/doc/_stdlib_gen/stdlib-content.jsonnet b/doc/_stdlib_gen/stdlib-content.jsonnet index c2090e7a9..316d4d7b1 100644 --- a/doc/_stdlib_gen/stdlib-content.jsonnet +++ b/doc/_stdlib_gen/stdlib-content.jsonnet @@ -400,7 +400,7 @@ local exampleDocMultiline(mid, ex) = { name: 'trim', params: ['str'], - availableSince: 'upcoming', + availableSince: '0.21.0', description: ||| Returns a copy of string after eliminating leading and trailing whitespaces. |||, @@ -408,7 +408,7 @@ local exampleDocMultiline(mid, ex) = { name: 'equalsIgnoreCase', params: ['str1', 'str2'], - availableSince: 'upcoming', + availableSince: '0.21.0', description: ||| Returns true if the the given str1 is equal to str2 by doing case insensitive comparison, false otherwise. |||, @@ -1347,7 +1347,7 @@ local exampleDocMultiline(mid, ex) = { name: 'flattenDeepArray', params: ['value'], - availableSince: 'upcoming', + availableSince: '0.21.0', description: ||| Concatenate an array containing values and arrays into a single flattened array. |||, @@ -1433,7 +1433,7 @@ local exampleDocMultiline(mid, ex) = { name: 'minArray', params: ['arr', 'keyF', 'onEmpty'], - availableSince: 'upcoming', + availableSince: '0.21.0', description: html.paragraphs([ ||| Return the min of all element in arr. @@ -1443,7 +1443,7 @@ local exampleDocMultiline(mid, ex) = { name: 'maxArray', params: ['arr', 'keyF', 'onEmpty'], - availableSince: 'upcoming', + availableSince: '0.21.0', description: html.paragraphs([ ||| Return the max of all element in arr. @@ -1453,7 +1453,7 @@ local exampleDocMultiline(mid, ex) = { name: 'contains', params: ['arr', 'elem'], - availableSince: 'upcoming', + availableSince: '0.21.0', description: html.paragraphs([ ||| Return true if given elem is present in arr, false otherwise. @@ -1473,7 +1473,7 @@ local exampleDocMultiline(mid, ex) = { name: 'remove', params: ['arr', 'elem'], - availableSince: 'upcoming', + availableSince: '0.21.0', description: html.paragraphs([ ||| Remove first occurrence of elem from arr. @@ -1483,7 +1483,7 @@ local exampleDocMultiline(mid, ex) = { name: 'removeAt', params: ['arr', 'idx'], - availableSince: 'upcoming', + availableSince: '0.21.0', description: html.paragraphs([ ||| Remove element at idx index from arr. @@ -1650,7 +1650,7 @@ local exampleDocMultiline(mid, ex) = { name: 'objectRemoveKey', params: ['obj', 'key'], - availableSince: 'upcoming', + availableSince: '0.21.0', description: ||| Returns a new object after removing the given key from object. |||, @@ -1717,7 +1717,7 @@ local exampleDocMultiline(mid, ex) = { name: 'sha1', params: ['s'], - availableSince: 'upcoming', + availableSince: '0.21.0', description: [ html.p({}, ||| Encodes the given value into an SHA1 string. @@ -1730,7 +1730,7 @@ local exampleDocMultiline(mid, ex) = { name: 'sha256', params: ['s'], - availableSince: 'upcoming', + availableSince: '0.21.0', description: [ html.p({}, ||| Encodes the given value into an SHA256 string. @@ -1743,7 +1743,7 @@ local exampleDocMultiline(mid, ex) = { name: 'sha512', params: ['s'], - availableSince: 'upcoming', + availableSince: '0.21.0', description: [ html.p({}, ||| Encodes the given value into an SHA512 string. @@ -1756,7 +1756,7 @@ local exampleDocMultiline(mid, ex) = { name: 'sha3', params: ['s'], - availableSince: 'upcoming', + availableSince: '0.21.0', description: [ html.p({}, ||| Encodes the given value into an SHA3 string. diff --git a/doc/ref/stdlib.html b/doc/ref/stdlib.html index dae270f3c..e69de29bb 100644 --- a/doc/ref/stdlib.html +++ b/doc/ref/stdlib.html @@ -1,3749 +0,0 @@ ---- -# AUTOGENERATED FILE. DO NOT EDIT BY HAND! -layout: default -title: Standard Library ---- - -
    -
    -
    -

    - Standard Library -

    -
    -
    -
    -
    - -
    -
    -
    -

    - This page describes the functions available in Jsonnet's standard library, i.e. the object - implicitly bound to the std variable. Some of the standard library functions - can be implemented in Jsonnet. Their code can be found in the std.jsonnet file. - The behavior of some of the other functions, i.e. the ones that expose extra functionality - not otherwise available to programmers, is described formally in the specification. -

    -

    - The standard library is implicitly added to all Jsonnet programs by enclosing them in a - local construct. For example, if the program given by the user is {x: "foo"}, - then the actual code executed would be local std = { ... }; {x: "foo"}. The - functions in the standard library are all hidden fields of the std object. -

    -

    - Note: Some of these functions marked available since v0.10.0 were actually available earlier. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - External Variables -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.extVar(x) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - If an external variable with the given name was defined, return its value. Otherwise, raise an error. -

    - -
    -
    -
    -
    - - -
    -
    -
    -

    - Types and Reflection -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.thisFile -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Note that this is a field. It contains the current Jsonnet filename as a string. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.type(x) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Return a string that indicates the type of the value. The possible return values are: - "array", "boolean", "function", "null", "number", "object", and "string". -

    -

    - The following functions are also available and return a boolean: - std.isArray(v), std.isBoolean(v), std.isFunction(v), - std.isNumber(v), std.isObject(v), and - std.isString(v). -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.length(x) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Depending on the type of the value given, either returns the number of elements in the - array, the number of codepoints in the string, the number of parameters in the function, or - the number of fields in the object. Raises an error if given a primitive value, i.e. - null, true or false. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.prune(a) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Recursively remove all "empty" members of a. "Empty" is defined as zero - length `arrays`, zero length `objects`, or `null` values. - The argument a may have any type. -

    - -
    -
    -
    -
    - - -
    -
    -
    -

    - Mathematical Utilities -

    -
    -
    -
    -
    -
    -
    -
    -

    - The following mathematical functions are available: -

    -
      -
        std.abs(n)
      -
        std.sign(n)
      -
        std.max(a, b)
      -
        std.min(a, b)
      -
        std.pow(x, n)
      -
        std.exp(x)
      -
        std.log(x)
      -
        std.log2(x)
      -
        std.log10(x)
      -
        std.exponent(x)
      -
        std.mantissa(x)
      -
        std.floor(x)
      -
        std.ceil(x)
      -
        std.sqrt(x)
      -
        std.sin(x)
      -
        std.cos(x)
      -
        std.tan(x)
      -
        std.asin(x)
      -
        std.acos(x)
      -
        std.atan(x)
      -
        std.atan2(y, x)
      -
        std.deg2rad(x)
      -
        std.rad2deg(x)
      -
        std.hypot(a, b)
      -
        std.round(x)
      -
        std.isEven(x)
      -
        std.isOdd(x)
      -
        std.isInteger(x)
      -
        std.isDecimal(x)
      -
    -

    - The constant std.pi is also available. -

    -

    - The function std.mod(a, b) is what the % operator is desugared to. It performs - modulo arithmetic if the left hand side is a number, or if the left hand side is a string, - it does Python-style string formatting with std.format(). -

    -

    - The functions std.isEven(x) and std.isOdd(x) use integral part of a - floating number to test for even or odd. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.clamp(x, minVal, maxVal) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.15.0. - -

    -

    - Clamp a value to fit within the range [minVal, maxVal]. - Equivalent to std.max(minVal, std.min(x, maxVal)). -

    -

    - Example: std.clamp(-3, 0, 5) yields 0. -

    -

    - Example: std.clamp(4, 0, 5) yields 4. -

    -

    - Example: std.clamp(7, 0, 5) yields 5. -

    -
    -
    -
    -
    - - -
    -
    -
    -

    - Assertions and Debugging -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.assertEqual(a, b) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Ensure that a == b. Returns true or throws an error message. -

    - -
    -
    -
    -
    - - -
    -
    -
    -

    - String Manipulation -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.toString(a) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Convert the given argument to a string. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.codepoint(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Returns the positive integer representing the unicode codepoint of the character in the - given single-character string. This function is the inverse of std.char(n). -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.char(n) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Returns a string of length one whose only unicode codepoint has integer id n. - This function is the inverse of std.codepoint(str). -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.substr(str, from, len) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Returns a string that is the part of s that starts at offset from - and is len codepoints long. If the string s is shorter than - from+len, the suffix starting at position from will be returned. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.findSubstr(pat, str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Returns an array that contains the indexes of all occurrences of pat in - str. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.startsWith(a, b) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Returns whether the string a is prefixed by the string b. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.endsWith(a, b) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Returns whether the string a is suffixed by the string b. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.stripChars(str, chars) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.15.0. - -

    -

    - Removes characters chars from the beginning and from the end of str. -

    -

    - Example: std.stripChars(" test test test ", " ") yields "test test test". -

    -

    - Example: std.stripChars("aaabbbbcccc", "ac") yields "bbbb". -

    -

    - Example: std.stripChars("cacabbbbaacc", "ac") yields "bbbb". -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.lstripChars(str, chars) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.15.0. - -

    -

    - Removes characters chars from the beginning of str. -

    -

    - Example: std.lstripChars(" test test test ", " ") yields "test test test ". -

    -

    - Example: std.lstripChars("aaabbbbcccc", "ac") yields "bbbbcccc". -

    -

    - Example: std.lstripChars("cacabbbbaacc", "ac") yields "bbbbaacc". -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.rstripChars(str, chars) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.15.0. - -

    -

    - Removes characters chars from the end of str. -

    -

    - Example: std.rstripChars(" test test test ", " ") yields " test test test". -

    -

    - Example: std.rstripChars("aaabbbbcccc", "ac") yields "aaabbbb". -

    -

    - Example: std.rstripChars("cacabbbbaacc", "ac") yields "cacabbbb". -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.split(str, c) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Split the string str into an array of strings, divided by the string - c. -

    -

    - Note: Versions up to and including 0.18.0 require c to be a single character. -

    -

    - Example: std.split("foo/_bar", "/_") yields [ "foo", "bar" ]. -

    -

    - Example: std.split("/_foo/_bar", "/_") yields [ "", "foo", "bar" ]. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.splitLimit(str, c, maxsplits) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - As std.split(str, c) but will stop after maxsplits splits, thereby the largest - array it will return has length maxsplits + 1. A limit of -1 means unlimited. -

    -

    - Note: Versions up to and including 0.18.0 require c to be a single character. -

    -

    - Example: std.splitLimit("foo/_bar", "/_", 1) yields [ "foo", "bar" ]. -

    -

    - Example: std.splitLimit("/_foo/_bar", "/_", 1) yields [ "", "foo/_bar" ]. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.splitLimitR(str, c, maxsplits) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.19.0. - -

    -

    - As std.splitLimit(str, c, maxsplits) but will split from right to left. -

    -

    - Example: std.splitLimitR("/_foo/_bar", "/_", 1) yields [ "/_foo", "bar" ]. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.strReplace(str, from, to) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Returns a copy of the string in which all occurrences of string from have been - replaced with string to. -

    -

    - Example: std.strReplace('I like to skate with my skateboard', 'skate', 'surf') yields "I like to surf with my surfboard". -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.isEmpty(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.20.0. - -

    -

    - Returns true if the given string is of zero length. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.trim(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available in upcoming release. - -

    -

    - Returns a copy of string after eliminating leading and trailing whitespaces. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.equalsIgnoreCase(str1, str2) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available in upcoming release. - -

    -

    - Returns true if the the given str1 is equal to str2 by doing case insensitive comparison, false otherwise. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.asciiUpper(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Returns a copy of the string in which all ASCII letters are capitalized. -

    -

    - Example: std.asciiUpper('100 Cats!') yields "100 CATS!". -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.asciiLower(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Returns a copy of the string in which all ASCII letters are lower cased. -

    -

    - Example: std.asciiLower('100 Cats!') yields "100 cats!". -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.stringChars(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Split the string str into an array of strings, each containing a single - codepoint. -

    -

    - Example: std.stringChars("foo") yields [ "f", "o", "o" ]. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.format(str, vals) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Format the string str using the values in vals. The values can be - an array, an object, or in other cases are treated as if they were provided in a singleton - array. The string formatting follows the same rules as - Python. The % operator can be used as a shorthand for this function. -

    -

    - Example: std.format("Hello %03d", 12) yields "Hello 012". -

    -

    - Example: "Hello %03d" % 12 yields "Hello 012". -

    -

    - Example: "Hello %s, age %d" % ["Foo", 25] yields "Hello Foo, age 25". -

    -

    - Example: "Hello %(name)s, age %(age)d" % {age: 25, name: "Foo"} yields "Hello Foo, age 25". -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.escapeStringBash(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Wrap str in single quotes, and escape any single quotes within str - by changing them to a sequence '"'"'. This allows injection of arbitrary strings - as arguments of commands in bash scripts. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.escapeStringDollars(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Convert $ to $$ in str. This allows injection of arbitrary strings into - systems that use $ for string interpolation (like Terraform). -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.escapeStringJson(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Convert str to allow it to be embedded in a JSON representation, within a - string. This adds quotes, escapes backslashes, and escapes unprintable characters. -

    -

    - Example: local description = "Multiline\nc:\\path"; - "{name: %s}" % std.escapeStringJson(description) yields "{name: \"Multiline\\nc:\\\\path\"}". -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.escapeStringPython(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Convert str to allow it to be embedded in Python. This is an alias for - std.escapeStringJson. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.escapeStringXml(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Convert str to allow it to be embedded in XML (or HTML). The following replacements are made: -

    -        {
    -          "<": "&lt;",
    -          ">": "&gt;",
    -          "&": "&amp;",
    -          "\"": "&quot;",
    -          "'": "&apos;",
    -        }
    -        
    -

    - -
    -
    -
    -
    - - -
    -
    -
    -

    - Parsing -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.parseInt(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Parses a signed decimal integer from the input string. -

    -

    - Example: std.parseInt("123") yields 123. -

    -

    - Example: std.parseInt("-123") yields -123. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.parseOctal(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Parses an unsigned octal integer from the input string. Initial zeroes are tolerated. -

    -

    - Example: std.parseOctal("755") yields 493. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.parseHex(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Parses an unsigned hexadecimal integer, from the input string. Case insensitive. -

    -

    - Example: std.parseHex("ff") yields 255. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.parseJson(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.13.0. - -

    -

    - Parses a JSON string. -

    -

    - Example: std.parseJson('{"foo": "bar"}') yields { "foo": "bar" }. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.parseYaml(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.18.0. - -

    -

    - Parses a YAML string. This is provided as a "best-effort" mechanism and should not be relied on to provide - a fully standards compliant YAML parser. YAML is a superset of JSON, consequently "downcasting" or - manifestation of YAML into JSON or Jsonnet values will only succeed when using the subset of YAML that is - compatible with JSON. The parser does not support YAML documents with scalar values at the root. The - root node of a YAML document must start with either a YAML sequence or map to be successfully parsed. -

    -

    - Example: std.parseYaml('foo: bar') yields { "foo": "bar" }. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.encodeUTF8(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.13.0. - -

    -

    - Encode a string using UTF8. Returns an array of numbers - representing bytes. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.decodeUTF8(arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.13.0. - -

    -

    - Decode an array of numbers representing bytes using UTF8. - Returns a string. -

    - -
    -
    -
    -
    - - -
    -
    -
    -

    - Manifestation -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.manifestIni(ini) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Convert the given structure to a string in INI format. This - allows using Jsonnet's - object model to build a configuration to be consumed by an application expecting an INI - file. The data is in the form of a set of sections, each containing a key/value mapping. - These examples should make it clear: -

    -
    {
    -    main: { a: "1", b: "2" },
    -    sections: {
    -        s1: {x: "11", y: "22", z: "33"},
    -        s2: {p: "yes", q: ""},
    -        empty: {},
    -    }
    -}
    -

    - Yields a string containing this INI file: -

    -
    a = 1
    -b = 2
    -[empty]
    -[s1]
    -x = 11
    -y = 22
    -z = 33
    -[s2]
    -p = yes
    -q =
    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.manifestPython(v) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Convert the given value to a JSON-like form that is compatible with Python. The chief - differences are True / False / None instead of true / false / null. -

    -
    {
    -    b: ["foo", "bar"],
    -    c: true,
    -    d: null,
    -    e: { f1: false, f2: 42 },
    -}
    -

    - Yields a string containing Python code like: -

    -
    {
    -    "b": ["foo", "bar"],
    -    "c": True,
    -    "d": None,
    -    "e": {"f1": False, "f2": 42}
    -}
    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.manifestPythonVars(conf) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Convert the given object to a JSON-like form that is compatible with Python. The key - difference to std.manifestPython is that the top level is represented as a list - of Python global variables. -

    -
    {
    -    b: ["foo", "bar"],
    -    c: true,
    -    d: null,
    -    e: { f1: false, f2: 42 },
    -}
    -

    - Yields a string containing this Python code: -

    -
    b = ["foo", "bar"]
    -c = True
    -d = None
    -e = {"f1": False, "f2": 42}
    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.manifestJsonEx(value, indent, newline, key_val_sep) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Convert the given object to a JSON form. indent is a string containing - one or more whitespaces that are used for indentation. newline is - by default \n and is inserted where a newline would normally be used - to break long lines. key_val_sep is used to separate the key and value - of an object field: -

    -

    - Example: -

    std.manifestJsonEx(
    -{
    -    x: [1, 2, 3, true, false, null,
    -        "string\nstring"],
    -    y: { a: 1, b: 2, c: [1, 2] },
    -}, "    ")

    - Yields a string containing this JSON: -

    {
    -    "x": [
    -        1,
    -        2,
    -        3,
    -        true,
    -        false,
    -        null,
    -        "string\nstring"
    -    ],
    -    "y": {
    -        "a": 1,
    -        "b": 2,
    -        "c": [
    -            1,
    -            2
    -        ]
    -    }
    -}
    -

    - Example: -

    std.manifestJsonEx(
    -{
    -  x: [1, 2, "string\nstring"],
    -  y: { a: 1, b: [1, 2] },
    -}, "", " ", " : ")

    - Yields a string containing this JSON: -

    { "x" : [ 1, 2, "string\nstring" ], "y" : { "a" : 1, "b" : [ 1, 2 ] } }
    -
    -
    -
    -
    - -
    -
    -
    -

    - std.manifestJson(value) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Convert the given object to a JSON form. Under the covers, - it calls std.manifestJsonEx with a 4-space indent: -

    -

    - Example: -

    std.manifestJson(
    -{
    -    x: [1, 2, 3, true, false, null,
    -        "string\nstring"],
    -    y: { a: 1, b: 2, c: [1, 2] },
    -})

    - Yields a string containing this JSON: -

    {
    -    "x": [
    -        1,
    -        2,
    -        3,
    -        true,
    -        false,
    -        null,
    -        "string\nstring"
    -    ],
    -    "y": {
    -        "a": 1,
    -        "b": 2,
    -        "c": [
    -            1,
    -            2
    -        ]
    -    }
    -}
    -
    -
    -
    -
    - -
    -
    -
    -

    - std.manifestJsonMinified(value) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.18.0. - -

    -

    - Convert the given object to a minified JSON form. Under the covers, - it calls std.manifestJsonEx: -

    -

    - Example: -

    std.manifestJsonMinified(
    -{
    -    x: [1, 2, 3, true, false, null,
    -        "string\nstring"],
    -    y: { a: 1, b: 2, c: [1, 2] },
    -})

    - Yields a string containing this JSON: -

    {"x":[1,2,3,true,false,null,"string\nstring"],"y":{"a":1,"b":2,"c":[1,2]}}
    -
    -
    -
    -
    - -
    -
    -
    -

    - std.manifestYamlDoc(value, indent_array_in_object=false, quote_keys=true) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Convert the given value to a YAML form. Note that std.manifestJson could also - be used for this purpose, because any JSON is also valid YAML. But this function will - produce more canonical-looking YAML. -

    -
    std.manifestYamlDoc(
    -  {
    -      x: [1, 2, 3, true, false, null,
    -          "string\nstring\n"],
    -      y: { a: 1, b: 2, c: [1, 2] },
    -  },
    -  indent_array_in_object=false)
    -

    - Yields a string containing this YAML: -

    -
    "x":
    -  - 1
    -  - 2
    -  - 3
    -  - true
    -  - false
    -  - null
    -  - |
    -      string
    -      string
    -"y":
    -  "a": 1
    -  "b": 2
    -  "c":
    -      - 1
    -      - 2
    -

    - The indent_array_in_object param adds additional indentation which some people - may find easier to read. -

    -

    - The quote_keys parameter controls whether YAML identifiers are always quoted - or only when necessary. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.manifestYamlStream(value, indent_array_in_object=false, c_document_end=false, quote_keys=true) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Given an array of values, emit a YAML "stream", which is a sequence of documents separated - by --- and ending with .... -

    -
    std.manifestYamlStream(
    -  ['a', 1, []],
    -  indent_array_in_object=false,
    -  c_document_end=true)
    -

    - Yields this string: -

    -
    ---
    -"a"
    ----
    -1
    ----
    -[]
    -...
    -

    - The indent_array_in_object and quote_keys params are the - same as in manifestYamlDoc. -

    -

    - The c_document_end param adds the optional terminating .... -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.manifestXmlJsonml(value) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Convert the given JsonML-encoded value to a string - containing the XML. -

    -
    std.manifestXmlJsonml([
    -    'svg', { height: 100, width: 100 },
    -    [
    -        'circle', {
    -        cx: 50, cy: 50, r: 40,
    -        stroke: 'black', 'stroke-width': 3,
    -        fill: 'red',
    -        }
    -    ],
    -])
    -

    - Yields a string containing this XML (all on one line): -

    -
    <svg height="100" width="100">
    -    <circle cx="50" cy="50" fill="red" r="40"
    -    stroke="black" stroke-width="3"></circle>;
    -</svg>;
    -

    - Which represents the following image: -

    - - - Sorry, your browser does not support inline SVG. - -

    - JsonML is designed to preserve "mixed-mode content" (i.e., textual data outside of or next - to elements). This includes the whitespace needed to avoid having all the XML on one line, - which is meaningful in XML. In order to have whitespace in the XML output, it must be - present in the JsonML input: -

    -
    std.manifestXmlJsonml([
    -    'svg',
    -    { height: 100, width: 100 },
    -    '\n  ',
    -    [
    -        'circle',
    -        {
    -        cx: 50, cy: 50, r: 40, stroke: 'black',
    -        'stroke-width': 3, fill: 'red',
    -        }
    -    ],
    -    '\n',
    -])
    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.manifestTomlEx(toml, indent) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.18.0. - -

    -

    - Convert the given object to a TOML form. indent is a string containing - one or more whitespaces that are used for indentation: -

    -

    - Example: -

    std.manifestTomlEx({
    -  key1: "value",
    -  key2: 1,
    -  section: {
    -    a: 1,
    -    b: "str",
    -    c: false,
    -    d: [1, "s", [2, 3]],
    -    subsection: {
    -      k: "v",
    -    },
    -  },
    -  sectionArray: [
    -    { k: "v1", v: 123 },
    -    { k: "v2", c: "value2" },
    -  ],
    -}, "  ")

    - Yields a string containing this TOML file: -

    key1 = "value"
    -key2 = 1
    -
    -[section]
    -  a = 1
    -  b = "str"
    -  c = false
    -  d = [
    -    1,
    -    "s",
    -    [ 2, 3 ]
    -  ]
    -
    -  [section.subsection]
    -    k = "v"
    -
    -[[sectionArray]]
    -  k = "v1"
    -  v = 123
    -
    -[[sectionArray]]
    -  c = "value2"
    -  k = "v2"
    -
    -
    -
    -
    - - -
    -
    -
    -

    - Arrays -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.makeArray(sz, func) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Create a new array of sz elements by calling func(i) to initialize - each element. Func is expected to be a function that takes a single parameter, the index of - the element it should initialize. -

    -

    - Example: std.makeArray(3,function(x) x * x) yields [ 0, 1, 4 ]. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.member(arr, x) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.15.0. - -

    -

    - Returns whether x occurs in arr. - Argument arr may be an array or a string. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.count(arr, x) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Return the number of times that x occurs in arr. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.find(value, arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Returns an array that contains the indexes of all occurrences of value in - arr. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.map(func, arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Apply the given function to every element of the array to form a new array. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.mapWithIndex(func, arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Similar to map above, but it also passes to the function the element's - index in the array. The function func is expected to take the index as the - first parameter and the element as the second. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.filterMap(filter_func, map_func, arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - It first filters, then maps the given array, using the two functions provided. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.flatMap(func, arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Apply the given function to every element of arr to form a new array then flatten the result. - The argument arr must be an array or a string. If arr is an array, function func must return an array. - If arr is a string, function func must return an string. -

    -

    - The std.flatMap function can be thought of as a generalized std.map, - with each element mapped to 0, 1 or more elements. -

    -

    - Example: std.flatMap(function(x) [x, x], [1, 2, 3]) yields [ 1, 1, 2, 2, 3, 3 ]. -

    -

    - Example: std.flatMap(function(x) if x == 2 then [] else [x], [1, 2, 3]) yields [ 1, 3 ]. -

    -

    - Example: std.flatMap(function(x) if x == 2 then [] else [x * 3, x * 2], [1, 2, 3]) yields [ 3, 2, 9, 6 ]. -

    -

    - Example: std.flatMap(function(x) x+x, "foo") yields "ffoooo". -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.filter(func, arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Return a new array containing all the elements of arr for which the - func function returns true. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.foldl(func, arr, init) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Classic foldl function. Calls the function on the result of the previous function call and - each array element, or init in the case of the initial element. Traverses the - array from left to right. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.foldr(func, arr, init) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Classic foldr function. Calls the function on the result of the previous function call and - each array element, or init in the case of the initial element. Traverses the - array from right to left. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.range(from, to) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Return an array of ascending numbers between the two limits, inclusively. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.repeat(what, count) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.15.0. - -

    -

    - Repeats an array or a string what a number of times specified by an integer count. -

    -

    - Example: std.repeat([1, 2, 3], 3) yields [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ]. -

    -

    - Example: std.repeat("blah", 2) yields "blahblah". -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.slice(indexable, index, end, step) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Selects the elements of an array or a string from index to end with step and returns an array or a string respectively. -

    -

    - Note that it's recommended to use dedicated slicing syntax both for arrays and strings (e.g. arr[0:4:1] instead of std.slice(arr, 0, 4, 1)). -

    -

    - Example: std.slice([1, 2, 3, 4, 5, 6], 0, 4, 1) yields [ 1, 2, 3, 4 ]. -

    -

    - Example: std.slice([1, 2, 3, 4, 5, 6], 1, 6, 2) yields [ 2, 4, 6 ]. -

    -

    - Example: std.slice("jsonnet", 0, 4, 1) yields "json". -

    -

    - Example: std.slice("jsonnet", -3, null, null) yields "net". -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.join(sep, arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - If sep is a string, then arr must be an array of strings, in which - case they are concatenated with sep used as a delimiter. If sep - is an array, then arr must be an array of arrays, in which case the arrays are - concatenated in the same way, to produce a single array. -

    -

    - Example: std.join(".", ["www", "google", "com"]) yields "www.google.com". -

    -

    - Example: std.join([9, 9], [[1], [2, 3]]) yields [ 1, 9, 9, 2, 3 ]. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.lines(arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Concatenate an array of strings into a text file with newline characters after each string. - This is suitable for constructing bash scripts and the like. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.flattenArrays(arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Concatenate an array of arrays into a single array. -

    -

    - Example: std.flattenArrays([[1, 2], [3, 4], [[5, 6], [7, 8]]]) yields [ 1, 2, 3, 4, [ 5, 6 ], [ 7, 8 ] ]. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.flattenDeepArray(value) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available in upcoming release. - -

    -

    - Concatenate an array containing values and arrays into a single flattened array. -

    -

    - Example: std.flattenDeepArray([[1, 2], [], [3, [4]], [[5, 6, [null]], [7, 8]]]) yields [ 1, 2, 3, 4, 5, 6, null, 7, 8 ]. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.reverse(arrs) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.13.0. - -

    -

    - Reverses an array. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.sort(arr, keyF=id) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Sorts the array using the <= operator. -

    -

    - Optional argument keyF is a single argument function used to extract comparison key from each array element. - Default value is identity function keyF=function(x) x. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.uniq(arr, keyF=id) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Removes successive duplicates. When given a sorted array, removes all duplicates. -

    -

    - Optional argument keyF is a single argument function used to extract comparison key from each array element. - Default value is identity function keyF=function(x) x. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.all(arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.19.0. - -

    -

    - Return true if all elements of arr is true, false otherwise. all([]) evaluates to true. -

    -

    - It's an error if 1) arr is not an array, or 2) arr contains non-boolean values. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.any(arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.19.0. - -

    -

    - Return true if any element of arr is true, false otherwise. any([]) evaluates to false. -

    -

    - It's an error if 1) arr is not an array, or 2) arr contains non-boolean values. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.sum(arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.20.0. - -

    -

    - Return sum of all element in arr. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.minArray(arr, keyF, onEmpty) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available in upcoming release. - -

    -

    - Return the min of all element in arr. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.maxArray(arr, keyF, onEmpty) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available in upcoming release. - -

    -

    - Return the max of all element in arr. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.contains(arr, elem) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available in upcoming release. - -

    -

    - Return true if given elem is present in arr, false otherwise. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.avg(arr) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.20.0. - -

    -

    - Return average of all element in arr. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.remove(arr, elem) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available in upcoming release. - -

    -

    - Remove first occurrence of elem from arr. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.removeAt(arr, idx) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available in upcoming release. - -

    -

    - Remove element at idx index from arr. -

    - -
    -
    -
    -
    - - -
    -
    -
    -

    - Sets -

    -
    -
    -
    -
    -
    -
    -
    -

    - Sets are represented as ordered arrays without duplicates. -

    -

    - Note that the std.set* functions rely on the uniqueness and ordering - on arrays passed to them to work. This can be guaranteed by using std.set(arr). - If that is not the case, the functions will quietly return non-meaningful results. -

    -

    - All set.set* functions accept keyF function of one argument, which can be - used to extract key to use from each element. All Set operations then use extracted key for the purpose - of identifying uniqueness. Default value is identity function local id = function(x) x. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.set(arr, keyF=id) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Shortcut for std.uniq(std.sort(arr)). -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.setInter(a, b, keyF=id) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Set intersection operation (values in both a and b). -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.setUnion(a, b, keyF=id) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Set union operation (values in any of a or b). Note that + on sets will simply - concatenate - the arrays, possibly forming an array that is not a set (due to not being ordered without - duplicates). -

    -

    - Example: std.setUnion([1, 2], [2, 3]) yields [ 1, 2, 3 ]. -

    -

    - Example: std.setUnion([{n:"A", v:1}, {n:"B"}], [{n:"A", v: 9999}, {n:"C"}], keyF=function(x) x.n) yields [ { "n": "A", "v": 1 }, { "n": "B" }, { "n": "C" } ]. -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.setDiff(a, b, keyF=id) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Set difference operation (values in a but not b). -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.setMember(x, arr, keyF=id) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Returns true if x is a member of array, otherwise false. -

    - -
    -
    -
    -
    - - -
    -
    -
    -

    - Objects -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.get(o, f, default=null, inc_hidden=true) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.18.0. - -

    -

    - Returns the object's field if it exists or default value otherwise. - inc_hidden controls whether to include hidden fields. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.objectHas(o, f) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Returns true if the given object has the field (given as a string), otherwise - false. Raises an error if the arguments are not object and string - respectively. Returns false if the field is hidden. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.objectFields(o) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Returns an array of strings, each element being a field from the given object. Does not include - hidden fields. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.objectValues(o) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.17.0. - -

    -

    - Returns an array of the values in the given object. Does not include hidden fields. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.objectKeysValues(o) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.20.0. - -

    -

    - Returns an array of objects from the given object, each object having two fields: - key (string) and value (object). Does not include hidden fields. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.objectHasAll(o, f) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - As std.objectHas but also includes hidden fields. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.objectFieldsAll(o) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - As std.objectFields but also includes hidden fields. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.objectValuesAll(o) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.17.0. - -

    -

    - As std.objectValues but also includes hidden fields. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.objectKeysValuesAll(o) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.20.0. - -

    -

    - As std.objectKeysValues but also includes hidden fields. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.objectRemoveKey(obj, key) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available in upcoming release. - -

    -

    - Returns a new object after removing the given key from object. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.mapWithKey(func, obj) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Apply the given function to all fields of the given object, also passing - the field name. The function func is expected to take the - field name as the first parameter and the field value as the second. -

    - -
    -
    -
    -
    - - -
    -
    -
    -

    - Encoding -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.base64(input) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Encodes the given value into a base64 string. The encoding sequence is A-Za-z0-9+/ with - = - to pad the output to a multiple of 4 characters. The value can be a string or an array of - numbers, but the codepoints / numbers must be in the 0 to 255 range. The resulting string - has no line breaks. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.base64DecodeBytes(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Decodes the given base64 string into an array of bytes (number values). Currently assumes - the input string has no linebreaks and is padded to a multiple of 4 (with the = character). - In other words, it consumes the output of std.base64(). -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.base64Decode(str) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Deprecated, use std.base64DecodeBytes and decode the string explicitly (e.g. with std.decodeUTF8) instead. -

    -

    - Behaves like std.base64DecodeBytes() except returns a naively encoded string instead of an array of bytes. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.md5(s) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Encodes the given value into an MD5 string. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.sha1(s) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available in upcoming release. - -

    -

    - Encodes the given value into an SHA1 string. -

    -

    - This function is only available in Go version of jsonnet. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.sha256(s) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available in upcoming release. - -

    -

    - Encodes the given value into an SHA256 string. -

    -

    - This function is only available in Go version of jsonnet. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.sha512(s) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available in upcoming release. - -

    -

    - Encodes the given value into an SHA512 string. -

    -

    - This function is only available in Go version of jsonnet. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.sha3(s) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available in upcoming release. - -

    -

    - Encodes the given value into an SHA3 string. -

    -

    - This function is only available in Go version of jsonnet. -

    - -
    -
    -
    -
    - - -
    -
    -
    -

    - Booleans -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.xor(x, y) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.20.0. - -

    -

    - Returns the xor of the two given booleans. -

    - -
    -
    -
    -
    - -
    -
    -
    -

    - std.xnor(x, y) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.20.0. - -

    -

    - Returns the xnor of the two given booleans. -

    - -
    -
    -
    -
    - - -
    -
    -
    -

    - JSON Merge Patch -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.mergePatch(target, patch) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.10.0. - -

    -

    - Applies patch to target - according to RFC7396 -

    - -
    -
    -
    -
    - - -
    -
    -
    -

    - Debugging -

    -
    -
    -
    -
    - -
    -
    -
    -

    - std.trace(str, rest) -

    -
    -
    -
    -
    -
    -
    -
    -

    - - Available since version 0.11.0. - -

    -

    - Outputs the given string str to stderr and - returns rest as the result. -

    -

    - Example: -

    -

    -

    local conditionalReturn(cond, in1, in2) =
    -  if (cond) then
    -      std.trace('cond is true returning '
    -              + std.toString(in1), in1)
    -  else
    -      std.trace('cond is false returning '
    -              + std.toString(in2), in2);
    -
    -{
    -    a: conditionalReturn(true, { b: true }, { c: false }),
    -}
    -

    -

    - Prints: -

    -

    -

    TRACE: test.jsonnet:3 cond is true returning {"b": true}
    -{
    -    "a": {
    -        "b": true
    -    }
    -}
    -

    - -
    -
    -
    -
    - - diff --git a/include/libjsonnet.h b/include/libjsonnet.h index e86f1662f..15d6f822c 100644 --- a/include/libjsonnet.h +++ b/include/libjsonnet.h @@ -24,14 +24,14 @@ limitations under the License. * of using the library. */ -/** The version string of th Jsonnet interpreter. +/** The version string of the Jsonnet interpreter. * * This is currently grepped out of this file by setup.py, Makefile, and CMakeLists.txt so be aware * of that when making changes. * - * If this isn't the sae as jsonnet_version() then you've got a mismatched binary / header. + * If this isn't the same as jsonnet_version() then you've got a mismatched binary / header. */ -#define LIB_JSONNET_VERSION "v0.20.0" +#define LIB_JSONNET_VERSION "v0.21.0-rc1" /** Return the version string of the Jsonnet interpreter. Conforms to semantic versioning * https://semver.org/ If this does not match LIB_JSONNET_VERSION then there is a mismatch between diff --git a/test_cmd/fmt_help.golden.stdout b/test_cmd/fmt_help.golden.stdout index 652f276bc..33b1ca597 100644 --- a/test_cmd/fmt_help.golden.stdout +++ b/test_cmd/fmt_help.golden.stdout @@ -1,4 +1,4 @@ -Jsonnet reformatter v0.20.0 +Jsonnet reformatter v0.21.0-rc1 jsonnetfmt {