difftreelog
perf make flatMap builtin
in: master
2 files changed
crates/jrsonnet-evaluator/src/builtin/mod.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/builtin/mod.rs
+++ b/crates/jrsonnet-evaluator/src/builtin/mod.rs
@@ -92,6 +92,7 @@
("native".into(), builtin_native),
("filter".into(), builtin_filter),
("map".into(), builtin_map),
+ ("flatMap".into(), builtin_flatmap),
("foldl".into(), builtin_foldl),
("foldr".into(), builtin_foldr),
("sortImpl".into(), builtin_sort_impl),
@@ -331,6 +332,40 @@
})
}
+fn builtin_flatmap(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
+ parse_args!(context, "flatMap", args, 2, [
+ 0, func: ty!(function) => Val::Func;
+ 1, arr: ty!((array | string));
+ ], {
+ match arr {
+ Val::Str(s) => {
+ let mut out = String::new();
+ for c in s.chars() {
+ match func.evaluate_values(context.clone(), &[Val::Str(c.to_string().into())])? {
+ Val::Str(o) => out.push_str(&o),
+ _ => throw!(RuntimeError("in std.join all items should be strings".into())),
+ };
+ }
+ Ok(Val::Str(out.into()))
+ },
+ Val::Arr(a) => {
+ let mut out = Vec::new();
+ for el in a.iter() {
+ let el = el?;
+ match func.evaluate_values(context.clone(), &[el])? {
+ Val::Arr(o) => for oe in o.iter() {
+ out.push(oe?)
+ },
+ _ => throw!(RuntimeError("in std.join all items should be arrays".into())),
+ };
+ }
+ Ok(Val::Arr(out.into()))
+ },
+ _ => unreachable!(),
+ }
+ })
+}
+
fn builtin_foldl(context: Context, _loc: Option<&ExprLocation>, args: &ArgsDesc) -> Result<Val> {
parse_args!(context, "foldl", args, 3, [
0, func: ty!(function) => Val::Func;
crates/jrsonnet-stdlib/src/std.jsonnetdiffbeforeafterboth1{2 local std = self,3 local id = std.id,45 # Those functions aren't normally located in stdlib6 length:: $intrinsic(length),7 type:: $intrinsic(type),8 makeArray:: $intrinsic(makeArray),9 codepoint:: $intrinsic(codepoint),10 objectFieldsEx:: $intrinsic(objectFieldsEx),11 objectHasEx:: $intrinsic(objectHasEx),12 primitiveEquals:: $intrinsic(primitiveEquals),13 modulo:: $intrinsic(modulo),14 floor:: $intrinsic(floor),15 log:: $intrinsic(log),16 pow:: $intrinsic(pow),17 extVar:: $intrinsic(extVar),18 native:: $intrinsic(native),19 filter:: $intrinsic(filter),20 char:: $intrinsic(char),21 encodeUTF8:: $intrinsic(encodeUTF8),22 md5:: $intrinsic(md5),23 trace:: $intrinsic(trace),24 id:: $intrinsic(id),25 parseJson:: $intrinsic(parseJson),2627 isString(v):: std.type(v) == 'string',28 isNumber(v):: std.type(v) == 'number',29 isBoolean(v):: std.type(v) == 'boolean',30 isObject(v):: std.type(v) == 'object',31 isArray(v):: std.type(v) == 'array',32 isFunction(v):: std.type(v) == 'function',3334 toString(a)::35 if std.type(a) == 'string' then a else '' + a,3637 substr(str, from, len)::38 assert std.isString(str) : 'substr first parameter should be a string, got ' + std.type(str);39 assert std.isNumber(from) : 'substr second parameter should be a string, got ' + std.type(from);40 assert std.isNumber(len) : 'substr third parameter should be a string, got ' + std.type(len);41 assert len >= 0 : 'substr third parameter should be greater than zero, got ' + len;42 std.join('', std.makeArray(std.max(0, std.min(len, std.length(str) - from)), function(i) str[i + from])),4344 startsWith(a, b)::45 if std.length(a) < std.length(b) then46 false47 else48 std.substr(a, 0, std.length(b)) == b,4950 endsWith(a, b)::51 if std.length(a) < std.length(b) then52 false53 else54 std.substr(a, std.length(a) - std.length(b), std.length(b)) == b,5556 lstripChars(str, chars)::57 if std.length(str) > 0 && std.member(chars, str[0]) then58 std.lstripChars(str[1:], chars)59 else60 str,6162 rstripChars(str, chars)::63 local len = std.length(str);64 if len > 0 && std.member(chars, str[len - 1]) then65 std.rstripChars(str[:len - 1], chars)66 else67 str,6869 stripChars(str, chars)::70 std.lstripChars(std.rstripChars(str, chars), chars),7172 stringChars(str)::73 std.makeArray(std.length(str), function(i) str[i]),7475 local parse_nat(str, base) =76 assert base > 0 && base <= 16 : 'integer base %d invalid' % base;77 // These codepoints are in ascending order:78 local zero_code = std.codepoint('0');79 local upper_a_code = std.codepoint('A');80 local lower_a_code = std.codepoint('a');81 local addDigit(aggregate, char) =82 local code = std.codepoint(char);83 local digit = if code >= lower_a_code then84 code - lower_a_code + 1085 else if code >= upper_a_code then86 code - upper_a_code + 1087 else88 code - zero_code;89 assert digit >= 0 && digit < base : '%s is not a base %d integer' % [str, base];90 base * aggregate + digit;91 std.foldl(addDigit, std.stringChars(str), 0),9293 parseInt(str)::94 assert std.isString(str) : 'Expected string, got ' + std.type(str);95 assert std.length(str) > 0 && str != '-' : 'Not an integer: "%s"' % [str];96 if str[0] == '-' then97 -parse_nat(str[1:], 10)98 else99 parse_nat(str, 10),100101 parseOctal(str)::102 assert std.isString(str) : 'Expected string, got ' + std.type(str);103 assert std.length(str) > 0 : 'Not an octal number: ""';104 parse_nat(str, 8),105106 parseHex(str)::107 assert std.isString(str) : 'Expected string, got ' + std.type(str);108 assert std.length(str) > 0 : 'Not hexadecimal: ""';109 parse_nat(str, 16),110111 split(str, c)::112 assert std.isString(str) : 'std.split first parameter should be a string, got ' + std.type(str);113 assert std.isString(c) : 'std.split second parameter should be a string, got ' + std.type(c);114 assert std.length(c) == 1 : 'std.split second parameter should have length 1, got ' + std.length(c);115 std.splitLimit(str, c, -1),116117 splitLimit(str, c, maxsplits)::118 assert std.isString(str) : 'std.splitLimit first parameter should be a string, got ' + std.type(str);119 assert std.isString(c) : 'std.splitLimit second parameter should be a string, got ' + std.type(c);120 assert std.length(c) == 1 : 'std.splitLimit second parameter should have length 1, got ' + std.length(c);121 assert std.isNumber(maxsplits) : 'std.splitLimit third parameter should be a number, got ' + std.type(maxsplits);122 local aux(str, delim, i, arr, v) =123 local c = str[i];124 local i2 = i + 1;125 if i >= std.length(str) then126 arr + [v]127 else if c == delim && (maxsplits == -1 || std.length(arr) < maxsplits) then128 aux(str, delim, i2, arr + [v], '') tailstrict129 else130 aux(str, delim, i2, arr, v + c) tailstrict;131 aux(str, c, 0, [], ''),132133 strReplace:: $intrinsic(strReplace),134135 asciiUpper(str)::136 local cp = std.codepoint;137 local up_letter(c) = if cp(c) >= 97 && cp(c) < 123 then138 std.char(cp(c) - 32)139 else140 c;141 std.join('', std.map(up_letter, std.stringChars(str))),142143 asciiLower(str)::144 local cp = std.codepoint;145 local down_letter(c) = if cp(c) >= 65 && cp(c) < 91 then146 std.char(cp(c) + 32)147 else148 c;149 std.join('', std.map(down_letter, std.stringChars(str))),150151 range:: $intrinsic(range),152153 repeat(what, count)::154 local joiner =155 if std.isString(what) then ''156 else if std.isArray(what) then []157 else error 'std.repeat first argument must be an array or a string';158 std.join(joiner, std.makeArray(count, function(i) what)),159160 slice:: $intrinsic(slice),161162 member(arr, x)::163 if std.isArray(arr) then164 std.count(arr, x) > 0165 else if std.isString(arr) then166 std.length(std.findSubstr(x, arr)) > 0167 else error 'std.member first argument must be an array or a string',168169 count(arr, x):: std.length(std.filter(function(v) v == x, arr)),170171 mod:: $intrinsic(mod),172173 map:: $intrinsic(map),174175 mapWithIndex(func, arr)::176 if !std.isFunction(func) then177 error ('std.mapWithIndex first param must be function, got ' + std.type(func))178 else if !std.isArray(arr) && !std.isString(arr) then179 error ('std.mapWithIndex second param must be array, got ' + std.type(arr))180 else181 std.makeArray(std.length(arr), function(i) func(i, arr[i])),182183 mapWithKey(func, obj)::184 if !std.isFunction(func) then185 error ('std.mapWithKey first param must be function, got ' + std.type(func))186 else if !std.isObject(obj) then187 error ('std.mapWithKey second param must be object, got ' + std.type(obj))188 else189 { [k]: func(k, obj[k]) for k in std.objectFields(obj) },190191 flatMap(func, arr)::192 if !std.isFunction(func) then193 error ('std.flatMap first param must be function, got ' + std.type(func))194 else if std.isArray(arr) then195 std.flattenArrays(std.makeArray(std.length(arr), function(i) func(arr[i])))196 else if std.isString(arr) then197 std.join('', std.makeArray(std.length(arr), function(i) func(arr[i])))198 else error ('std.flatMap second param must be array / string, got ' + std.type(arr)),199200 join:: $intrinsic(join),201202 lines(arr)::203 std.join('\n', arr + ['']),204205 deepJoin(arr)::206 if std.isString(arr) then207 arr208 else if std.isArray(arr) then209 std.join('', [std.deepJoin(x) for x in arr])210 else211 error 'Expected string or array, got %s' % std.type(arr),212213214 format:: $intrinsic(format),215216 foldr:: $intrinsic(foldr),217218 foldl:: $intrinsic(foldl),219220 filterMap(filter_func, map_func, arr)::221 if !std.isFunction(filter_func) then222 error ('std.filterMap first param must be function, got ' + std.type(filter_func))223 else if !std.isFunction(map_func) then224 error ('std.filterMap second param must be function, got ' + std.type(map_func))225 else if !std.isArray(arr) then226 error ('std.filterMap third param must be array, got ' + std.type(arr))227 else228 std.map(map_func, std.filter(filter_func, arr)),229230 assertEqual(a, b)::231 if a == b then232 true233 else234 error 'Assertion failed. ' + a + ' != ' + b,235236 abs(n)::237 if !std.isNumber(n) then238 error 'std.abs expected number, got ' + std.type(n)239 else240 if n > 0 then n else -n,241242 sign(n)::243 if !std.isNumber(n) then244 error 'std.sign expected number, got ' + std.type(n)245 else246 if n > 0 then247 1248 else if n < 0 then249 -1250 else 0,251252 max(a, b)::253 if !std.isNumber(a) then254 error 'std.max first param expected number, got ' + std.type(a)255 else if !std.isNumber(b) then256 error 'std.max second param expected number, got ' + std.type(b)257 else258 if a > b then a else b,259260 min(a, b)::261 if !std.isNumber(a) then262 error 'std.min first param expected number, got ' + std.type(a)263 else if !std.isNumber(b) then264 error 'std.min second param expected number, got ' + std.type(b)265 else266 if a < b then a else b,267268 clamp(x, minVal, maxVal)::269 if x < minVal then minVal270 else if x > maxVal then maxVal271 else x,272273 flattenArrays(arrs)::274 std.foldl(function(a, b) a + b, arrs, []),275276 manifestIni(ini)::277 local body_lines(body) =278 std.join([], [279 local value_or_values = body[k];280 if std.isArray(value_or_values) then281 ['%s = %s' % [k, value] for value in value_or_values]282 else283 ['%s = %s' % [k, value_or_values]]284285 for k in std.objectFields(body)286 ]);287288 local section_lines(sname, sbody) = ['[%s]' % [sname]] + body_lines(sbody),289 main_body = if std.objectHas(ini, 'main') then body_lines(ini.main) else [],290 all_sections = [291 section_lines(k, ini.sections[k])292 for k in std.objectFields(ini.sections)293 ];294 std.join('\n', main_body + std.flattenArrays(all_sections) + ['']),295296 manifestToml(value):: std.manifestTomlEx(value, ' '),297298 manifestTomlEx(value, indent)::299 local300 escapeStringToml = std.escapeStringJson,301 escapeKeyToml(key) =302 local bare_allowed = std.set(std.stringChars('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-'));303 if std.setUnion(std.set(std.stringChars(key)), bare_allowed) == bare_allowed then key else escapeStringToml(key),304 isTableArray(v) = std.isArray(v) && std.length(v) > 0 && std.foldl(function(a, b) a && std.isObject(b), v, true),305 isSection(v) = std.isObject(v) || isTableArray(v),306 renderValue(v, indexedPath, inline, cindent) =307 if v == true then308 'true'309 else if v == false then310 'false'311 else if v == null then312 error 'Tried to manifest "null" at ' + indexedPath313 else if std.isNumber(v) then314 '' + v315 else if std.isString(v) then316 escapeStringToml(v)317 else if std.isFunction(v) then318 error 'Tried to manifest function at ' + indexedPath319 else if std.isArray(v) then320 if std.length(v) == 0 then321 '[]'322 else323 local range = std.range(0, std.length(v) - 1);324 local new_indent = if inline then '' else cindent + indent;325 local separator = if inline then ' ' else '\n';326 local lines = ['[' + separator]327 + std.join([',' + separator],328 [329 [new_indent + renderValue(v[i], indexedPath + [i], true, '')]330 for i in range331 ])332 + [separator + (if inline then '' else cindent) + ']'];333 std.join('', lines)334 else if std.isObject(v) then335 local lines = ['{ ']336 + std.join([', '],337 [338 [escapeKeyToml(k) + ' = ' + renderValue(v[k], indexedPath + [k], true, '')]339 for k in std.objectFields(v)340 ])341 + [' }'];342 std.join('', lines),343 renderTableInternal(v, path, indexedPath, cindent) =344 local kvp = std.flattenArrays([345 [cindent + escapeKeyToml(k) + ' = ' + renderValue(v[k], indexedPath + [k], false, cindent)]346 for k in std.objectFields(v)347 if !isSection(v[k])348 ]);349 local sections = [std.join('\n', kvp)] + [350 (351 if std.isObject(v[k]) then352 renderTable(v[k], path + [k], indexedPath + [k], cindent)353 else354 renderTableArray(v[k], path + [k], indexedPath + [k], cindent)355 )356 for k in std.objectFields(v)357 if isSection(v[k])358 ];359 std.join('\n\n', sections),360 renderTable(v, path, indexedPath, cindent) =361 cindent + '[' + std.join('.', std.map(escapeKeyToml, path)) + ']'362 + (if v == {} then '' else '\n')363 + renderTableInternal(v, path, indexedPath, cindent + indent),364 renderTableArray(v, path, indexedPath, cindent) =365 local range = std.range(0, std.length(v) - 1);366 local sections = [367 (cindent + '[[' + std.join('.', std.map(escapeKeyToml, path)) + ']]'368 + (if v[i] == {} then '' else '\n')369 + renderTableInternal(v[i], path, indexedPath + [i], cindent + indent))370 for i in range371 ];372 std.join('\n\n', sections);373 if std.isObject(value) then374 renderTableInternal(value, [], [], '')375 else376 error 'TOML body must be an object. Got ' + std.type(value),377378 escapeStringJson:: $intrinsic(escapeStringJson),379380 escapeStringPython(str)::381 std.escapeStringJson(str),382383 escapeStringBash(str_)::384 local str = std.toString(str_);385 local trans(ch) =386 if ch == "'" then387 "'\"'\"'"388 else389 ch;390 "'%s'" % std.join('', [trans(ch) for ch in std.stringChars(str)]),391392 escapeStringDollars(str_)::393 local str = std.toString(str_);394 local trans(ch) =395 if ch == '$' then396 '$$'397 else398 ch;399 std.foldl(function(a, b) a + trans(b), std.stringChars(str), ''),400401 manifestJson(value):: std.manifestJsonEx(value, ' '),402403 manifestJsonEx:: $intrinsic(manifestJsonEx),404405 manifestYamlDoc(value, indent_array_in_object=false)::406 local aux(v, path, cindent) =407 if v == true then408 'true'409 else if v == false then410 'false'411 else if v == null then412 'null'413 else if std.isNumber(v) then414 '' + v415 else if std.isString(v) then416 local len = std.length(v);417 if len == 0 then418 '""'419 else if v[len - 1] == '\n' then420 local split = std.split(v, '\n');421 std.join('\n' + cindent + ' ', ['|'] + split[0:std.length(split) - 1])422 else423 std.escapeStringJson(v)424 else if std.isFunction(v) then425 error 'Tried to manifest function at ' + path426 else if std.isArray(v) then427 if std.length(v) == 0 then428 '[]'429 else430 local params(value) =431 if std.isArray(value) && std.length(value) > 0 then {432 // While we could avoid the new line, it yields YAML that is433 // hard to read, e.g.:434 // - - - 1435 // - 2436 // - - 3437 // - 4438 new_indent: cindent + ' ',439 space: '\n' + self.new_indent,440 } else if std.isObject(value) && std.length(value) > 0 then {441 new_indent: cindent + ' ',442 // In this case we can start on the same line as the - because the indentation443 // matches up then. The converse is not true, because fields are not always444 // 1 character long.445 space: ' ',446 } else {447 // In this case, new_indent is only used in the case of multi-line strings.448 new_indent: cindent,449 space: ' ',450 };451 local range = std.range(0, std.length(v) - 1);452 local parts = [453 '-' + param.space + aux(v[i], path + [i], param.new_indent)454 for i in range455 for param in [params(v[i])]456 ];457 std.join('\n' + cindent, parts)458 else if std.isObject(v) then459 if std.length(v) == 0 then460 '{}'461 else462 local params(value) =463 if std.isArray(value) && std.length(value) > 0 then {464 // Not indenting allows e.g.465 // ports:466 // - 80467 // instead of468 // ports:469 // - 80470 new_indent: if indent_array_in_object then cindent + ' ' else cindent,471 space: '\n' + self.new_indent,472 } else if std.isObject(value) && std.length(value) > 0 then {473 new_indent: cindent + ' ',474 space: '\n' + self.new_indent,475 } else {476 // In this case, new_indent is only used in the case of multi-line strings.477 new_indent: cindent,478 space: ' ',479 };480 local lines = [481 std.escapeStringJson(k) + ':' + param.space + aux(v[k], path + [k], param.new_indent)482 for k in std.objectFields(v)483 for param in [params(v[k])]484 ];485 std.join('\n' + cindent, lines);486 aux(value, [], ''),487488 manifestYamlStream(value, indent_array_in_object=false, c_document_end=true)::489 if !std.isArray(value) then490 error 'manifestYamlStream only takes arrays, got ' + std.type(value)491 else492 '---\n' + std.join(493 '\n---\n', [std.manifestYamlDoc(e, indent_array_in_object) for e in value]494 ) + if c_document_end then '\n...\n' else '\n',495496497 manifestPython(v)::498 if std.isObject(v) then499 local fields = [500 '%s: %s' % [std.escapeStringPython(k), std.manifestPython(v[k])]501 for k in std.objectFields(v)502 ];503 '{%s}' % [std.join(', ', fields)]504 else if std.isArray(v) then505 '[%s]' % [std.join(', ', [std.manifestPython(v2) for v2 in v])]506 else if std.isString(v) then507 '%s' % [std.escapeStringPython(v)]508 else if std.isFunction(v) then509 error 'cannot manifest function'510 else if std.isNumber(v) then511 std.toString(v)512 else if v == true then513 'True'514 else if v == false then515 'False'516 else if v == null then517 'None',518519 manifestPythonVars(conf)::520 local vars = ['%s = %s' % [k, std.manifestPython(conf[k])] for k in std.objectFields(conf)];521 std.join('\n', vars + ['']),522523 manifestXmlJsonml(value)::524 if !std.isArray(value) then525 error 'Expected a JSONML value (an array), got %s' % std.type(value)526 else527 local aux(v) =528 if std.isString(v) then529 v530 else531 local tag = v[0];532 local has_attrs = std.length(v) > 1 && std.isObject(v[1]);533 local attrs = if has_attrs then v[1] else {};534 local children = if has_attrs then v[2:] else v[1:];535 local attrs_str =536 std.join('', [' %s="%s"' % [k, attrs[k]] for k in std.objectFields(attrs)]);537 std.deepJoin(['<', tag, attrs_str, '>', [aux(x) for x in children], '</', tag, '>']);538539 aux(value),540541 local base64_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',542 local base64_inv = { [base64_table[i]]: i for i in std.range(0, 63) },543544 base64:: $intrinsic(base64),545546 base64DecodeBytes(str)::547 if std.length(str) % 4 != 0 then548 error 'Not a base64 encoded string "%s"' % str549 else550 local aux(str, i, r) =551 if i >= std.length(str) then552 r553 else554 // all 6 bits of i, 2 MSB of i+1555 local n1 = [base64_inv[str[i]] << 2 | (base64_inv[str[i + 1]] >> 4)];556 // 4 LSB of i+1, 4MSB of i+2557 local n2 =558 if str[i + 2] == '=' then []559 else [(base64_inv[str[i + 1]] & 15) << 4 | (base64_inv[str[i + 2]] >> 2)];560 // 2 LSB of i+2, all 6 bits of i+3561 local n3 =562 if str[i + 3] == '=' then []563 else [(base64_inv[str[i + 2]] & 3) << 6 | base64_inv[str[i + 3]]];564 aux(str, i + 4, r + n1 + n2 + n3) tailstrict;565 aux(str, 0, []),566567 base64Decode(str)::568 local bytes = std.base64DecodeBytes(str);569 std.join('', std.map(function(b) std.char(b), bytes)),570571 reverse:: $intrinsic(reverse),572573 sortImpl:: $intrinsic(sortImpl),574575 sort(arr, keyF=id)::576 std.sortImpl(arr, keyF),577578 uniq(arr, keyF=id)::579 local f(a, b) =580 if std.length(a) == 0 then581 [b]582 else if keyF(a[std.length(a) - 1]) == keyF(b) then583 a584 else585 a + [b];586 std.foldl(f, arr, []),587588 set(arr, keyF=id)::589 std.uniq(std.sort(arr, keyF), keyF),590591 setMember(x, arr, keyF=id)::592 // TODO(dcunnin): Binary chop for O(log n) complexity593 std.length(std.setInter([x], arr, keyF)) > 0,594595 setUnion(a, b, keyF=id)::596 // NOTE: order matters, values in `a` win597 local aux(a, b, i, j, acc) =598 if i >= std.length(a) then599 acc + b[j:]600 else if j >= std.length(b) then601 acc + a[i:]602 else603 local ak = keyF(a[i]);604 local bk = keyF(b[j]);605 if ak == bk then606 aux(a, b, i + 1, j + 1, acc + [a[i]]) tailstrict607 else if ak < bk then608 aux(a, b, i + 1, j, acc + [a[i]]) tailstrict609 else610 aux(a, b, i, j + 1, acc + [b[j]]) tailstrict;611 aux(a, b, 0, 0, []),612613 setInter(a, b, keyF=id)::614 local aux(a, b, i, j, acc) =615 if i >= std.length(a) || j >= std.length(b) then616 acc617 else618 if keyF(a[i]) == keyF(b[j]) then619 aux(a, b, i + 1, j + 1, acc + [a[i]]) tailstrict620 else if keyF(a[i]) < keyF(b[j]) then621 aux(a, b, i + 1, j, acc) tailstrict622 else623 aux(a, b, i, j + 1, acc) tailstrict;624 aux(a, b, 0, 0, []) tailstrict,625626 setDiff(a, b, keyF=id)::627 local aux(a, b, i, j, acc) =628 if i >= std.length(a) then629 acc630 else if j >= std.length(b) then631 acc + a[i:]632 else633 if keyF(a[i]) == keyF(b[j]) then634 aux(a, b, i + 1, j + 1, acc) tailstrict635 else if keyF(a[i]) < keyF(b[j]) then636 aux(a, b, i + 1, j, acc + [a[i]]) tailstrict637 else638 aux(a, b, i, j + 1, acc) tailstrict;639 aux(a, b, 0, 0, []) tailstrict,640641 mergePatch(target, patch)::642 if std.isObject(patch) then643 local target_object =644 if std.isObject(target) then target else {};645646 local target_fields =647 if std.isObject(target_object) then std.objectFields(target_object) else [];648649 local null_fields = [k for k in std.objectFields(patch) if patch[k] == null];650 local both_fields = std.setUnion(target_fields, std.objectFields(patch));651652 {653 [k]:654 if !std.objectHas(patch, k) then655 target_object[k]656 else if !std.objectHas(target_object, k) then657 std.mergePatch(null, patch[k]) tailstrict658 else659 std.mergePatch(target_object[k], patch[k]) tailstrict660 for k in std.setDiff(both_fields, null_fields)661 }662 else663 patch,664665 objectFields(o)::666 std.objectFieldsEx(o, false),667668 objectFieldsAll(o)::669 std.objectFieldsEx(o, true),670671 objectHas(o, f)::672 std.objectHasEx(o, f, false),673674 objectHasAll(o, f)::675 std.objectHasEx(o, f, true),676677 objectValues(o)::678 [o[k] for k in std.objectFields(o)],679680 objectValuesAll(o)::681 [o[k] for k in std.objectFieldsAll(o)],682683 equals:: $intrinsic(equals),684685 resolvePath(f, r)::686 local arr = std.split(f, '/');687 std.join('/', std.makeArray(std.length(arr) - 1, function(i) arr[i]) + [r]),688689 prune(a)::690 local isContent(b) =691 if b == null then692 false693 else if std.isArray(b) then694 std.length(b) > 0695 else if std.isObject(b) then696 std.length(b) > 0697 else698 true;699 if std.isArray(a) then700 [std.prune(x) for x in a if isContent($.prune(x))]701 else if std.isObject(a) then {702 [x]: $.prune(a[x])703 for x in std.objectFields(a)704 if isContent(std.prune(a[x]))705 } else706 a,707708 findSubstr(pat, str)::709 if !std.isString(pat) then710 error 'findSubstr first parameter should be a string, got ' + std.type(pat)711 else if !std.isString(str) then712 error 'findSubstr second parameter should be a string, got ' + std.type(str)713 else714 local pat_len = std.length(pat);715 local str_len = std.length(str);716 if pat_len == 0 || str_len == 0 || pat_len > str_len then717 []718 else719 std.filter(function(i) str[i:i + pat_len] == pat, std.range(0, str_len - pat_len)),720721 find(value, arr)::722 if !std.isArray(arr) then723 error 'find second parameter should be an array, got ' + std.type(arr)724 else725 std.filter(function(i) arr[i] == value, std.range(0, std.length(arr) - 1)),726}