difftreelog
Merge commit 'd93ca83fe3668f134c0a705b972e2b52072ebf3d'
in: master
3 files changed
.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:
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth1use 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}crates/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]]"#,