Skip to content

Commit

Permalink
Merge pull request #118 from CertainLach/feature/upstream-std-sync
Browse files Browse the repository at this point in the history
feat: sync upstream stdlib changes
  • Loading branch information
CertainLach authored Jul 17, 2023
2 parents e0d3ba2 + 397e1ed commit 47e40b2
Show file tree
Hide file tree
Showing 11 changed files with 418 additions and 175 deletions.
374 changes: 214 additions & 160 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions crates/jrsonnet-stdlib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ serde = "1.0"

# std.md5
md5 = "0.7.0"
# std.sha1
sha1 = "0.10.5"
# std.sha256, std.sha512
sha2 = "0.10.6"
# std.sha3
sha3 = "0.10.8"
# std.base64
base64 = "0.21.0"
# std.parseJson
Expand Down
49 changes: 48 additions & 1 deletion crates/jrsonnet-stdlib/src/arrays.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
#![allow(non_snake_case)]

use jrsonnet_evaluator::{
error::{ErrorKind::RuntimeError, Result},
function::{builtin, FuncVal},
throw,
typed::{BoundedI32, BoundedUsize, Either2, NativeFn, Typed},
val::{equals, ArrValue, IndexableVal, StrValue},
Either, IStr, Val,
Either, IStr, Thunk, Val,
};

pub(crate) fn eval_on_empty(on_empty: Option<Thunk<Val>>) -> Result<Val> {
if let Some(on_empty) = on_empty {
on_empty.evaluate()
} else {
throw!("expected non-empty array")
}
}

#[builtin]
pub fn builtin_make_array(sz: BoundedI32<0, { i32::MAX }>, func: FuncVal) -> Result<ArrValue> {
if *sz == 0 {
Expand Down Expand Up @@ -220,6 +230,11 @@ pub fn builtin_member(arr: IndexableVal, x: Val) -> Result<bool> {
}
}

#[builtin]
pub fn builtin_contains(arr: IndexableVal, elem: Val) -> Result<bool> {
builtin_member(arr, elem)
}

#[builtin]
pub fn builtin_count(arr: ArrValue, x: Val) -> Result<usize> {
let mut count = 0;
Expand All @@ -230,3 +245,35 @@ pub fn builtin_count(arr: ArrValue, x: Val) -> Result<usize> {
}
Ok(count)
}

#[builtin]
pub fn builtin_avg(arr: Vec<f64>, onEmpty: Option<Thunk<Val>>) -> Result<Val> {
if arr.is_empty() {
return eval_on_empty(onEmpty);
}
Ok(Val::Num(arr.iter().sum::<f64>() / (arr.len() as f64)))
}

#[builtin]
pub fn builtin_remove_at(
arr: ArrValue,
index: usize,
) -> Result<ArrValue> {
let newArrLeft = arr.clone().slice(None, Some(index), None);
let newArrRight = arr.slice(Some(index + 1), None, None);

Ok(ArrValue::extended(
newArrLeft.unwrap_or(ArrValue::empty()),
newArrRight.unwrap_or(ArrValue::empty()))
)
}

#[builtin]
pub fn builtin_remove(arr: ArrValue, elem: Val) -> Result<ArrValue> {
for (index, item) in arr.iter().enumerate() {
if equals(&item?, &elem)? {
return builtin_remove_at(arr.clone(), index)
}
}
Ok(arr)
}
12 changes: 12 additions & 0 deletions crates/jrsonnet-stdlib/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,15 @@ pub fn builtin_sha512(s: IStr) -> String {
use sha2::digest::Digest;
format!("{:x}", sha2::Sha512::digest(s.as_bytes()))
}

#[builtin]
pub fn builtin_sha1(s: IStr) -> String {
use sha1::digest::Digest;
format!("{:x}", sha1::Sha1::digest(s.as_bytes()))
}

#[builtin]
pub fn builtin_sha3(s: IStr) -> String {
use sha3::digest::Digest;
format!("{:x}", sha3::Sha3_512::digest(s.as_bytes()))
}
17 changes: 17 additions & 0 deletions crates/jrsonnet-stdlib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,11 @@ pub fn stdlib_uncached(settings: Rc<RefCell<Settings>>) -> ObjValue {
("any", builtin_any::INST),
("all", builtin_all::INST),
("member", builtin_member::INST),
("contains", builtin_member::INST),
("count", builtin_count::INST),
("avg", builtin_avg::INST),
("removeAt", builtin_remove_at::INST),
("remove", builtin_remove::INST),
// Math
("abs", builtin_abs::INST),
("sign", builtin_sign::INST),
Expand All @@ -102,20 +106,30 @@ pub fn stdlib_uncached(settings: Rc<RefCell<Settings>>) -> ObjValue {
("exp", builtin_exp::INST),
("mantissa", builtin_mantissa::INST),
("exponent", builtin_exponent::INST),
("round", builtin_round::INST),
("isEven", builtin_is_even::INST),
("isOdd", builtin_is_odd::INST),
("isInteger", builtin_is_integer::INST),
("isDecimal", builtin_is_decimal::INST),
// Operator
("mod", builtin_mod::INST),
("primitiveEquals", builtin_primitive_equals::INST),
("equals", builtin_equals::INST),
("xor", builtin_xor::INST),
("xnor", builtin_xnor::INST),
("format", builtin_format::INST),
// Sort
("sort", builtin_sort::INST),
("uniq", builtin_uniq::INST),
("set", builtin_set::INST),
("minArray", builtin_min_array::INST),
("maxArray", builtin_max_array::INST),
// Hash
("md5", builtin_md5::INST),
("sha1", builtin_sha1::INST),
("sha256", builtin_sha256::INST),
("sha512", builtin_sha512::INST),
("sha3", builtin_sha3::INST),
// Encoding
("encodeUTF8", builtin_encode_utf8::INST),
("decodeUTF8", builtin_decode_utf8::INST),
Expand All @@ -125,6 +139,7 @@ pub fn stdlib_uncached(settings: Rc<RefCell<Settings>>) -> ObjValue {
// Objects
("objectFieldsEx", builtin_object_fields_ex::INST),
("objectHasEx", builtin_object_has_ex::INST),
("objectRemoveKey", builtin_object_remove_key::INST),
// Manifest
("escapeStringJson", builtin_escape_string_json::INST),
("manifestJsonEx", builtin_manifest_json_ex::INST),
Expand All @@ -138,6 +153,8 @@ pub fn stdlib_uncached(settings: Rc<RefCell<Settings>>) -> ObjValue {
("substr", builtin_substr::INST),
("char", builtin_char::INST),
("strReplace", builtin_str_replace::INST),
("isEmpty", builtin_is_empty::INST),
("equalsIgnoreCase", builtin_equals_ignore_case::INST),
("splitLimit", builtin_splitlimit::INST),
("asciiUpper", builtin_ascii_upper::INST),
("asciiLower", builtin_ascii_lower::INST),
Expand Down
25 changes: 25 additions & 0 deletions crates/jrsonnet-stdlib/src/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,28 @@ pub fn builtin_mantissa(x: f64) -> f64 {
pub fn builtin_exponent(x: f64) -> i16 {
frexp(x).1
}

#[builtin]
pub fn builtin_round(x: f64) -> f64 {
x.round()
}

#[builtin]
pub fn builtin_is_even(x: f64) -> bool {
builtin_round(x) % 2.0 == 0.0
}

#[builtin]
pub fn builtin_is_odd(x: f64) -> bool {
builtin_round(x) % 2.0 == 1.0
}

#[builtin]
pub fn builtin_is_integer(x: f64) -> bool {
builtin_round(x) == x
}

#[builtin]
pub fn builtin_is_decimal(x: f64) -> bool {
builtin_round(x) != x
}
16 changes: 15 additions & 1 deletion crates/jrsonnet-stdlib/src/objects.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use jrsonnet_evaluator::{
function::builtin,
val::{StrValue, Val},
IStr, ObjValue,
IStr, ObjValue, ObjValueBuilder,
};


#[builtin]
pub fn builtin_object_fields_ex(
obj: ObjValue,
Expand All @@ -27,3 +28,16 @@ pub fn builtin_object_fields_ex(
pub fn builtin_object_has_ex(obj: ObjValue, fname: IStr, hidden: bool) -> bool {
obj.has_field_ex(fname, hidden)
}

#[builtin]
pub fn builtin_object_remove_key(obj: ObjValue, key: IStr) -> ObjValue {
let mut new_obj = ObjValueBuilder::with_capacity(obj.len() - 1);
for (k, v) in obj.iter() {
if k == key {
continue
}
new_obj.member(k).value_unchecked(v.unwrap())
}

new_obj.build()
}
5 changes: 5 additions & 0 deletions crates/jrsonnet-stdlib/src/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ pub fn builtin_xor(x: bool, y: bool) -> bool {
x ^ y
}

#[builtin]
pub fn builtin_xnor(x: bool, y: bool) -> bool {
x == y
}

#[builtin]
pub fn builtin_format(str: IStr, vals: Val) -> Result<String> {
std_format(&str, vals)
Expand Down
75 changes: 62 additions & 13 deletions crates/jrsonnet-stdlib/src/sort.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(non_snake_case)]

use std::cmp::Ordering;

use jrsonnet_evaluator::{
Expand All @@ -9,6 +11,9 @@ use jrsonnet_evaluator::{
Thunk, Val,
};
use jrsonnet_gcmodule::Cc;
use jrsonnet_parser::BinaryOpType;

use crate::eval_on_empty;

#[derive(Copy, Clone)]
enum SortKeyType {
Expand Down Expand Up @@ -64,15 +69,13 @@ fn sort_identity(mut values: Vec<Val>) -> Result<Vec<Val>> {
let mut err = None;
// evaluate_compare_op will never return equal on types, which are different from
// jsonnet perspective
values.sort_unstable_by(|a, b| {
match evaluate_compare_op(a, b, jrsonnet_parser::BinaryOpType::Lt) {
Ok(ord) => ord,
Err(e) if err.is_none() => {
let _ = err.insert(e);
Ordering::Equal
}
Err(_) => Ordering::Equal,
values.sort_unstable_by(|a, b| match evaluate_compare_op(a, b, BinaryOpType::Lt) {
Ok(ord) => ord,
Err(e) if err.is_none() => {
let _ = err.insert(e);
Ordering::Equal
}
Err(_) => Ordering::Equal,
});
if let Some(err) = err {
return Err(err);
Expand Down Expand Up @@ -105,16 +108,16 @@ fn sort_keyf(values: ArrValue, keyf: FuncVal) -> Result<Vec<Thunk<Val>>> {
let mut err = None;
// evaluate_compare_op will never return equal on types, which are different from
// jsonnet perspective
vk.sort_by(|(_a, ak), (_b, bk)| {
match evaluate_compare_op(ak, bk, jrsonnet_parser::BinaryOpType::Lt) {
vk.sort_by(
|(_a, ak), (_b, bk)| match evaluate_compare_op(ak, bk, BinaryOpType::Lt) {
Ok(ord) => ord,
Err(e) if err.is_none() => {
let _ = err.insert(e);
Ordering::Equal
}
Err(_) => Ordering::Equal,
}
});
},
);
if let Some(err) = err {
return Err(err);
}
Expand All @@ -138,7 +141,6 @@ pub fn sort(values: ArrValue, key_getter: FuncVal) -> Result<ArrValue> {
}

#[builtin]
#[allow(non_snake_case)]
pub fn builtin_sort(arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {
super::sort::sort(arr, keyF.unwrap_or_else(FuncVal::identity))
}
Expand Down Expand Up @@ -206,3 +208,50 @@ pub fn builtin_set(arr: ArrValue, keyF: Option<FuncVal>) -> Result<ArrValue> {
Ok(ArrValue::lazy(Cc::new(arr)))
}
}


fn eval_keyf(val: Val, key_f: &Option<FuncVal>) -> Result<Val> {
if let Some(key_f) = key_f {
key_f.evaluate_simple(&(val,), false)
} else {
Ok(val)
}
}

fn array_top1(arr: ArrValue, key_f: Option<FuncVal>, ordering: Ordering) -> Result<Val> {
let mut iter = arr.iter();
let mut min = iter.next().expect("not empty")?;
let mut min_key = eval_keyf(min.clone(), &key_f)?;
for item in iter {
let cur = item?;
let cur_key = eval_keyf(cur.clone(), &key_f)?;
if evaluate_compare_op(&cur_key, &min_key, BinaryOpType::Lt)? == ordering {
min = cur;
min_key = cur_key;
}
}
Ok(min)
}

#[builtin]
pub fn builtin_min_array(
arr: ArrValue,
keyF: Option<FuncVal>,
onEmpty: Option<Thunk<Val>>,
) -> Result<Val> {
if arr.is_empty() {
return eval_on_empty(onEmpty);
}
array_top1(arr, keyF, Ordering::Less)
}
#[builtin]
pub fn builtin_max_array(
arr: ArrValue,
keyF: Option<FuncVal>,
onEmpty: Option<Thunk<Val>>,
) -> Result<Val> {
if arr.is_empty() {
return eval_on_empty(onEmpty);
}
array_top1(arr, keyF, Ordering::Greater)
}
6 changes: 6 additions & 0 deletions crates/jrsonnet-stdlib/src/std.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,12 @@
objectValuesAll(o)::
[o[k] for k in std.objectFieldsAll(o)],

objectKeysValues(o)::
[{ key: k, value: o[k] } for k in std.objectFields(o)],

objectKeysValuesAll(o)::
[{ key: k, value: o[k] } for k in std.objectFieldsAll(o)],

resolvePath(f, r)::
local arr = std.split(f, '/');
std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]),
Expand Down
10 changes: 10 additions & 0 deletions crates/jrsonnet-stdlib/src/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ pub fn builtin_str_replace(str: String, from: IStr, to: IStr) -> String {
str.replace(&from as &str, &to as &str)
}

#[builtin]
pub fn builtin_is_empty(str: String) -> bool {
str.is_empty()
}

#[builtin]
pub fn builtin_equals_ignore_case(x: String, y: String) -> bool {
x.to_ascii_lowercase() == y.to_ascii_lowercase()
}

#[builtin]
pub fn builtin_splitlimit(str: IStr, c: IStr, maxsplits: Either![usize, M1]) -> ArrValue {
use Either2::*;
Expand Down

0 comments on commit 47e40b2

Please sign in to comment.