git.delta.rocks / jrsonnet / refs/commits / cead2c336a71

difftreelog

Merge commit 'd93ca83fe3668f134c0a705b972e2b52072ebf3d'

Yaroslav Bolyukin2021-05-23parents: #b148741 #d93ca83.patch.diff
in: master

3 files changed

modified.github/workflows/release.ymldiffbeforeafterboth
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -53,38 +53,98 @@
     needs: [test]
     strategy:
       matrix:
-        os: [ubuntu-latest, macOS-latest, windows-latest]
+        target:
+          # Tier 1
+          - aarch64-unknown-linux-gnu
+          - i686-pc-windows-gnu
+          - i686-pc-windows-msvc
+          - i686-unknown-linux-gnu
+          - x86_64-apple-darwin
+          - x86_64-pc-windows-gnu
+          - x86_64-pc-windows-msvc
+          - x86_64-unknown-linux-gnu
+
+          # Other
+          - x86_64-unknown-linux-musl
         include:
-          - os: ubuntu-latest
-            rust: stable
-            target: x86_64-unknown-linux-musl
+          - target: aarch64-unknown-linux-gnu
+            os: ubuntu-latest
             bin: jrsonnet
-            name: jrsonnet-linux-amd64
-          - os: windows-latest
-            rust: stable
-            target: x86_64-pc-windows-msvc
+            name: jrsonnet-linux-gnu-aarch64
+          - target: i686-pc-windows-gnu
+            os: windows-latest
             bin: jrsonnet.exe
-            name: jrsonnet-windows-amd64.exe
-          - os: macOS-latest
-            rust: stable
-            target: x86_64-apple-darwin
+            name: jrsonnet-windows-gnu-i686.exe
+          - target: i686-pc-windows-msvc
+            os: windows-latest
+            bin: jrsonnet.exe
+            name: jrsonnet-windows-msvc-i686.exe
+          - target: i686-unknown-linux-gnu
+            os: ubuntu-latest
             bin: jrsonnet
+            name: jrsonnet-linux-gnu-i686
+          - target: x86_64-apple-darwin
+            os: macOS-latest
+            bin: jrsonnet
             name: jrsonnet-darwin-amd64
+          - target: x86_64-pc-windows-gnu
+            os: windows-latest
+            bin: jrsonnet.exe
+            name: jrsonnet-windows-gnu-amd64.exe
+          - target: x86_64-pc-windows-msvc
+            os: windows-latest
+            bin: jrsonnet.exe
+            name: jrsonnet-windows-msvc-amd64.exe
+          - target: x86_64-unknown-linux-gnu
+            os: ubuntu-latest
+            bin: jrsonnet
+            name: jrsonnet-linux-gnu-amd64
+
+          - target: x86_64-unknown-linux-musl
+            os: ubuntu-latest
+            bin: jrsonnet
+            name: jrsonnet-linux-musl-amd64
     runs-on: ${{ matrix.os }}
     steps:
       - name: Install stable toolchain
         uses: actions-rs/toolchain@v1
         with:
-          toolchain: ${{ matrix.rust }}
+          toolchain: stable
           override: true
           target: ${{ matrix.target }}
+
       - name: Checkout
         uses: actions/checkout@v2
+
+      - name: Linux x86 cross compiler
+        if: ${{ matrix.target == 'i686-unknown-linux-gnu' }}
+        run: sudo apt install gcc-multilib
+
+      - name: Windows x86 cross compiler
+        if: ${{ matrix.target == 'i686-pc-windows-gnu' }}
+        uses: egor-tensin/setup-mingw@v2
+        with:
+          platform: x86
+
+      - name: ARM cross compiler
+        if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }}
+        uses: actions-rs/cargo@v1
+        with:
+          command: install
+          args: cross
+
+      - name: Run ARM build
+        if: ${{ matrix.target == 'aarch64-unknown-linux-gnu' }}
+        shell: bash
+        run: cross --bin=jrsonnet --release --target ${{ matrix.target }}
+
       - name: Run build
+        if: ${{ matrix.target != 'aarch64-unknown-linux-gnu' }}
         uses: actions-rs/cargo@v1
         with:
           command: build
           args: --bin=jrsonnet --release --target ${{ matrix.target }}
+
       - name: Package
         shell: bash
         run: |
@@ -93,8 +153,10 @@
 
           cp ${{ matrix.bin }} ../../../${{ matrix.name }}
           cd -
+
       - name: Generate SHA-256
         run: shasum -a 256 ${{ matrix.name }} > ${{ matrix.name }}.sha256
+
       - name: Publish
         uses: softprops/action-gh-release@v1
         with:
modifiedcrates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth
before · crates/jrsonnet-evaluator/src/builtin/mod.rs
1use crate::{2	equals,3	error::{Error::*, Result},4	parse_args, primitive_equals, push, throw, with_state, ArrValue, Context, FuncVal, LazyVal,5	Val,6};7use format::{format_arr, format_obj};8use jrsonnet_interner::IStr;9use jrsonnet_parser::{ArgsDesc, BinaryOpType, ExprLocation};10use jrsonnet_types::ty;11use std::{collections::HashMap, path::PathBuf, rc::Rc};1213pub mod stdlib;14pub use stdlib::*;1516use self::manifest::{escape_string_json, manifest_json_ex, ManifestJsonOptions, ManifestType};1718pub mod format;19pub mod manifest;20pub mod sort;2122fn std_format(str: IStr, vals: Val) -> Result<Val> {23	push(24		Some(&ExprLocation(Rc::from(PathBuf::from("std.jsonnet")), 0, 0)),25		|| format!("std.format of {}", str),26		|| {27			Ok(match vals {28				Val::Arr(vals) => Val::Str(format_arr(&str, &vals.evaluated()?)?.into()),29				Val::Obj(obj) => Val::Str(format_obj(&str, &obj)?.into()),30				o => Val::Str(format_arr(&str, &[o])?.into()),31			})32		},33	)34}3536type Builtin = fn(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val>;3738type BuiltinsType = HashMap<Box<str>, Builtin>;3940thread_local! {41	static BUILTINS: BuiltinsType = {42		[43			("length".into(), builtin_length as Builtin),44			("type".into(), builtin_type),45			("makeArray".into(), builtin_make_array),46			("codepoint".into(), builtin_codepoint),47			("objectFieldsEx".into(), builtin_object_fields_ex),48			("objectHasEx".into(), builtin_object_has_ex),49			("slice".into(), builtin_slice),50			("primitiveEquals".into(), builtin_primitive_equals),51			("equals".into(), builtin_equals),52			("modulo".into(), builtin_modulo),53			("mod".into(), builtin_mod),54			("floor".into(), builtin_floor),55			("log".into(), builtin_log),56			("pow".into(), builtin_pow),57			("extVar".into(), builtin_ext_var),58			("native".into(), builtin_native),59			("filter".into(), builtin_filter),60			("map".into(), builtin_map),61			("foldl".into(), builtin_foldl),62			("foldr".into(), builtin_foldr),63			("sortImpl".into(), builtin_sort_impl),64			("format".into(), builtin_format),65			("range".into(), builtin_range),66			("char".into(), builtin_char),67			("encodeUTF8".into(), builtin_encode_utf8),68			("md5".into(), builtin_md5),69			("base64".into(), builtin_base64),70			("trace".into(), builtin_trace),71			("join".into(), builtin_join),72			("escapeStringJson".into(), builtin_escape_string_json),73			("manifestJsonEx".into(), builtin_manifest_json_ex),74			("reverse".into(), builtin_reverse),75			("id".into(), builtin_id),76			("strReplace".into(), builtin_str_replace),77		].iter().cloned().collect()78	};79}8081fn builtin_length(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {82	parse_args!(context, "length", args, 1, [83		0, x: ty!((string | object | array));84	], {85		Ok(match x {86			Val::Str(n) => Val::Num(n.chars().count() as f64),87			Val::Arr(a) => Val::Num(a.len() as f64),88			Val::Obj(o) => Val::Num(89				o.fields_visibility()90					.into_iter()91					.filter(|(_k, v)| *v)92					.count() as f64,93			),94			_ => unreachable!(),95		})96	})97}9899fn builtin_type(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {100	parse_args!(context, "type", args, 1, [101		0, x: ty!(any);102	], {103		Ok(Val::Str(x.value_type().name().into()))104	})105}106107fn builtin_make_array(108	context: Context,109	_loc: Option<&ExprLocation>,110	args: &ArgsDesc,111) -> Result<Val> {112	parse_args!(context, "makeArray", args, 2, [113		0, sz: ty!(BoundedNumber<(Some(0.0)), (None)>) => Val::Num;114		1, func: ty!(function) => Val::Func;115	], {116		let mut out = Vec::with_capacity(sz as usize);117		for i in 0..sz as usize {118			out.push(LazyVal::new_resolved(func.evaluate_values(119				context.clone(),120				&[Val::Num(i as f64)]121			)?))122		}123		Ok(Val::Arr(out.into()))124	})125}126127fn builtin_codepoint(128	context: Context,129	_loc: Option<&ExprLocation>,130	args: &ArgsDesc,131) -> Result<Val> {132	parse_args!(context, "codepoint", args, 1, [133		0, str: ty!(char) => Val::Str;134	], {135		Ok(Val::Num(str.chars().next().unwrap() as u32 as f64))136	})137}138139fn builtin_object_fields_ex(140	context: Context,141	_loc: Option<&ExprLocation>,142	args: &ArgsDesc,143) -> Result<Val> {144	parse_args!(context, "objectFieldsEx", args, 2, [145		0, obj: ty!(object) => Val::Obj;146		1, inc_hidden: ty!(boolean) => Val::Bool;147	], {148		let out = obj.fields_ex(inc_hidden);149		Ok(Val::Arr(out.into_iter().map(Val::Str).collect::<Vec<_>>().into()))150	})151}152153fn builtin_object_has_ex(154	context: Context,155	_loc: Option<&ExprLocation>,156	args: &ArgsDesc,157) -> Result<Val> {158	parse_args!(context, "objectHasEx", args, 3, [159		0, obj: ty!(object) => Val::Obj;160		1, f: ty!(string) => Val::Str;161		2, inc_hidden: ty!(boolean) => Val::Bool;162	], {163		Ok(Val::Bool(obj.has_field_ex(f, inc_hidden)))164	})165}166167// faster168fn builtin_slice(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {169	parse_args!(context, "slice", args, 4, [170		0, indexable: ty!((string | array));171		1, index: ty!((number | null));172		2, end: ty!((number | null));173		3, step: ty!((number | null));174	], {175		let index = match index {176			Val::Num(v) => v as usize,177			Val::Null => 0,178			_ => unreachable!(),179		};180		let end = match end {181			Val::Num(v) => v as usize,182			Val::Null => match &indexable {183				Val::Str(s) => s.chars().count(),184				Val::Arr(v) => v.len(),185				_ => unreachable!()186			},187			_ => unreachable!()188		};189		let step = match step {190			Val::Num(v) => v as usize,191			Val::Null => 1,192			_ => unreachable!()193		};194		match &indexable {195			Val::Str(s) => {196				Ok(Val::Str((s.chars().skip(index).take(end-index).step_by(step).collect::<String>()).into()))197			}198			Val::Arr(arr) => {199				Ok(Val::Arr((arr.iter().skip(index).take(end-index).step_by(step).collect::<Result<Vec<Val>>>()?).into()))200			}201			_ => unreachable!()202		}203	})204}205206// faster207fn builtin_primitive_equals(208	context: Context,209	_loc: Option<&ExprLocation>,210	args: &ArgsDesc,211) -> Result<Val> {212	parse_args!(context, "primitiveEquals", args, 2, [213		0, a: ty!(any);214		1, b: ty!(any);215	], {216		Ok(Val::Bool(primitive_equals(&a, &b)?))217	})218}219220// faster221fn builtin_equals(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {222	parse_args!(context, "equals", args, 2, [223		0, a: ty!(any);224		1, b: ty!(any);225	], {226		Ok(Val::Bool(equals(&a, &b)?))227	})228}229230fn builtin_modulo(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {231	parse_args!(context, "modulo", args, 2, [232		0, a: ty!(number) => Val::Num;233		1, b: ty!(number) => Val::Num;234	], {235		Ok(Val::Num(a % b))236	})237}238239fn builtin_mod(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {240	parse_args!(context, "mod", args, 2, [241		0, a: ty!((number | string));242		1, b: ty!(any);243	], {244		match (a, b) {245			(Val::Num(a), Val::Num(b)) => Ok(Val::Num(a % b)),246			(Val::Str(str), vals) => std_format(str, vals),247			(a, b) => throw!(BinaryOperatorDoesNotOperateOnValues(BinaryOpType::Mod, a.value_type(), b.value_type()))248		}249	})250}251252fn builtin_floor(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {253	parse_args!(context, "floor", args, 1, [254		0, x: ty!(number) => Val::Num;255	], {256		Ok(Val::Num(x.floor()))257	})258}259260fn builtin_log(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {261	parse_args!(context, "log", args, 1, [262		0, n: ty!(number) => Val::Num;263	], {264		Ok(Val::Num(n.ln()))265	})266}267268fn builtin_pow(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {269	parse_args!(context, "pow", args, 2, [270		0, x: ty!(number) => Val::Num;271		1, n: ty!(number) => Val::Num;272	], {273		Ok(Val::Num(x.powf(n)))274	})275}276277fn builtin_ext_var(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {278	parse_args!(context, "extVar", args, 1, [279		0, x: ty!(string) => Val::Str;280	], {281		Ok(with_state(|s| s.settings().ext_vars.get(&x).cloned()).ok_or(UndefinedExternalVariable(x))?)282	})283}284285fn builtin_native(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {286	parse_args!(context, "native", args, 1, [287		0, x: ty!(string) => Val::Str;288	], {289		Ok(with_state(|s| s.settings().ext_natives.get(&x).cloned()).map(|v| Val::Func(Rc::new(FuncVal::NativeExt(x.clone(), v)))).ok_or(UndefinedExternalFunction(x))?)290	})291}292293fn builtin_filter(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {294	parse_args!(context, "filter", args, 2, [295		0, func: ty!(function) => Val::Func;296		1, arr: ty!(array) => Val::Arr;297	], {298		Ok(Val::Arr(arr.filter(|val| func299			.evaluate_values(context.clone(), &[val.clone()])?300			.try_cast_bool("filter predicate"))?))301	})302}303304fn builtin_map(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {305	parse_args!(context, "map", args, 2, [306		0, func: ty!(function) => Val::Func;307		1, arr: ty!(array) => Val::Arr;308	], {309		Ok(Val::Arr(arr.map(|val| func310			.evaluate_values(context.clone(), &[val]))?))311	})312}313314fn builtin_foldl(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {315	parse_args!(context, "foldl", args, 3, [316		0, func: ty!(function) => Val::Func;317		1, arr: ty!(array) => Val::Arr;318		2, init: ty!(any);319	], {320		let mut acc = init;321		for i in arr.iter() {322			acc = func.evaluate_values(context.clone(), &[acc, i?])?;323		}324		Ok(acc)325	})326}327328fn builtin_foldr(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {329	parse_args!(context, "foldr", args, 3, [330		0, func: ty!(function) => Val::Func;331		1, arr: ty!(array) => Val::Arr;332		2, init: ty!(any);333	], {334		let mut acc = init;335		for i in arr.iter().rev() {336			acc = func.evaluate_values(context.clone(), &[acc, i?])?;337		}338		Ok(acc)339	})340}341342#[allow(non_snake_case)]343fn builtin_sort_impl(344	context: Context,345	_loc: Option<&ExprLocation>,346	args: &ArgsDesc,347) -> Result<Val> {348	parse_args!(context, "sort", args, 2, [349		0, arr: ty!(array) => Val::Arr;350		1, keyF: ty!(function) => Val::Func;351	], {352		if arr.len() <= 1 {353			return Ok(Val::Arr(arr))354		}355		Ok(Val::Arr(ArrValue::Eager(sort::sort(context, arr.evaluated()?, &keyF)?)))356	})357}358359// faster360fn builtin_format(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {361	parse_args!(context, "format", args, 2, [362		0, str: ty!(string) => Val::Str;363		1, vals: ty!(any)364	], {365		std_format(str, vals)366	})367}368369fn builtin_range(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {370	parse_args!(context, "range", args, 2, [371		0, from: ty!(number) => Val::Num;372		1, to: ty!(number) => Val::Num;373	], {374		if to < from {375			return Ok(Val::Arr(ArrValue::new_eager()))376		}377		let mut out = Vec::with_capacity((1+to as usize-from as usize).max(0));378		for i in from as usize..=to as usize {379			out.push(Val::Num(i as f64));380		}381		Ok(Val::Arr(out.into()))382	})383}384385fn builtin_char(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {386	parse_args!(context, "char", args, 1, [387		0, n: ty!(number) => Val::Num;388	], {389		let mut out = String::new();390		out.push(std::char::from_u32(n as u32).ok_or_else(||391			InvalidUnicodeCodepointGot(n as u32)392		)?);393		Ok(Val::Str(out.into()))394	})395}396397fn builtin_encode_utf8(398	context: Context,399	_loc: Option<&ExprLocation>,400	args: &ArgsDesc,401) -> Result<Val> {402	parse_args!(context, "encodeUTF8", args, 1, [403		0, str: ty!(string) => Val::Str;404	], {405		Ok(Val::Arr((str.bytes().map(|b| Val::Num(b as f64)).collect::<Vec<Val>>()).into()))406	})407}408409fn builtin_md5(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {410	parse_args!(context, "md5", args, 1, [411		0, str: ty!(string) => Val::Str;412	], {413		Ok(Val::Str(format!("{:x}", md5::compute(&str.as_bytes())).into()))414	})415}416417fn builtin_trace(context: Context, loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {418	parse_args!(context, "trace", args, 2, [419		0, str: ty!(string) => Val::Str;420		1, rest: ty!(any);421	], {422		eprint!("TRACE:");423		if let Some(loc) = loc {424			with_state(|s|{425				let locs = s.map_source_locations(&loc.0, &[loc.1]);426				eprint!(" {}:{}", loc.0.file_name().unwrap().to_str().unwrap(), locs[0].line);427			});428		}429		eprintln!(" {}", str);430		Ok(rest)431	})432}433434fn builtin_base64(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {435	parse_args!(context, "base64", args, 1, [436		0, input: ty!((string | (Array<number>)));437	], {438		Ok(Val::Str(match input {439			Val::Str(s) => {440				base64::encode(s.bytes().collect::<Vec<_>>()).into()441			},442			Val::Arr(a) => {443				base64::encode(a.iter().map(|v| {444					Ok(v?.unwrap_num()? as u8)445				}).collect::<Result<Vec<_>>>()?).into()446			},447			_ => unreachable!()448		}))449	})450}451452fn builtin_join(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {453	parse_args!(context, "join", args, 2, [454		0, sep: ty!((string | array));455		1, arr: ty!(array) => Val::Arr;456	], {457		Ok(match sep {458			Val::Arr(joiner_items) => {459				let mut out = Vec::new();460461				let mut first = true;462				for item in arr.iter() {463					let item = item?.clone();464					if let Val::Arr(items) = item {465						if !first {466							out.reserve(joiner_items.len());467							// TODO: extend468							for item in joiner_items.iter() {469								out.push(item?);470							}471						}472						first = false;473						out.reserve(items.len());474						// TODO: extend475						for item in items.iter() {476							out.push(item?);477						}478					} else {479						throw!(RuntimeError("in std.join all items should be arrays".into()));480					}481				}482483				Val::Arr(out.into())484			},485			Val::Str(sep) => {486				let mut out = String::new();487488				let mut first = true;489				for item in arr.iter() {490					let item = item?.clone();491					if let Val::Str(item) = item {492						if !first {493							out += &sep;494						}495						first = false;496						out += &item;497					} else {498						throw!(RuntimeError("in std.join all items should be strings".into()));499					}500				}501502				Val::Str(out.into())503			},504			_ => unreachable!()505		})506	})507}508509// faster510fn builtin_escape_string_json(511	context: Context,512	_loc: Option<&ExprLocation>,513	args: &ArgsDesc,514) -> Result<Val> {515	parse_args!(context, "escapeStringJson", args, 1, [516		0, str_: ty!(string) => Val::Str;517	], {518		Ok(Val::Str(escape_string_json(&str_).into()))519	})520}521522// faster523fn builtin_manifest_json_ex(524	context: Context,525	_loc: Option<&ExprLocation>,526	args: &ArgsDesc,527) -> Result<Val> {528	parse_args!(context, "manifestJsonEx", args, 2, [529		0, value: ty!(any);530		1, indent: ty!(string) => Val::Str;531	], {532		Ok(Val::Str(manifest_json_ex(&value, &ManifestJsonOptions {533			padding: &indent,534			mtype: ManifestType::Std,535		})?.into()))536	})537}538539// faster540fn builtin_reverse(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {541	parse_args!(context, "reverse", args, 1, [542		0, value: ty!(array) => Val::Arr;543	], {544		Ok(Val::Arr(value.reversed()))545	})546}547548fn builtin_id(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {549	parse_args!(context, "id", args, 1, [550		0, v: ty!(any);551	], {552		Ok(v)553	})554}555556// faster557fn builtin_str_replace(558	context: Context,559	_loc: Option<&ExprLocation>,560	args: &ArgsDesc,561) -> Result<Val> {562	parse_args!(context, "strReplace", args, 3, [563		0, str: ty!(string) => Val::Str;564		1, from: ty!(string) => Val::Str;565		2, to: ty!(string) => Val::Str;566	], {567		let mut out = String::new();568		let mut last_idx = 0;569		while let Some(idx) = (&str[last_idx..]).find(&from as &str) {570			out.push_str(&str[last_idx..last_idx+idx]);571			out.push_str(&to);572			last_idx += idx + from.len();573		}574		if last_idx == 0 {575			return Ok(Val::Str(str))576		}577		out.push_str(&str[last_idx..]);578		Ok(Val::Str(out.into()))579	})580}581582pub fn call_builtin(583	context: Context,584	loc: Option<&ExprLocation>,585	name: &str,586	args: &ArgsDesc,587) -> Result<Val> {588	BUILTINS589		.with(|builtins| builtins.get(name).copied())590		.ok_or_else(|| IntrinsicNotFound(name.into()))?(context, loc, args)591}
modifiedcrates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth
--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -809,6 +809,21 @@
 	}
 
 	#[test]
+	fn parse_json() {
+		assert_json!(
+			r#"std.parseJson('{"a": -1,"b": 1,"c": 3.141,"d": []}')"#,
+			r#"{"a": -1,"b": 1,"c": 3.141,"d": []}"#
+		);
+		// TODO: this should in fact fail as is no proper JSON syntax
+		assert_json!(
+			r#"std.parseJson("{a:-1, b:1, c:3.141, d:[]}")"#,
+			r#"{"a": -1,"b": 1,"c": 3.141,"d": []}"#
+		);
+		// TODO: this is also no valid JSON
+		assert_json!(r#"std.parseJson('local x = 2; x * x')"#, r#"4"#);
+	}
+
+	#[test]
 	fn test() {
 		assert_json!(
 			r#"[[a, b] for a in [1,2,3] for b in [4,5,6]]"#,