0.3.0
Bug fixes
$..[*]/$..[0]/$..[1:3]no longer return duplicates when descending into maps whose values are lists. (collect_all_listswas double-counting list values found inside maps.)- Infix
!is now rejected by the parser instead of crashing the evaluator.!only participates as a prefix operator (!@.x,!(...)); using it as a binary operator used to bypass the parser and crash inside a non-existent binaryKernel.!function. - Built-in functions are crash-safe.
min()/max()on[],sum()/avg()on non-numeric or empty data,concat()on a map, andlength()on unsupported types all returnnil(or0for sum) instead of raising. - Division and modulo by zero in filters no longer crash. They propagate a "Nothing" marker that the filter treats as no match.
- Missing keys are distinguishable from explicit
nullinside filters.@.missing == nullnow returnsfalse;@.missing != nullreturnstrue. Two missing paths still compare equal (Nothing == Nothing). Top-level$.missingstill returnsnilfor backward compatibility. - Mixed-type ordering comparisons return
falseinstead of leaking BEAM term-order (e.g."hello" > 10is no longer truthy in filters). - Parser errors include line numbers and the unparsed remainder when the parser only partially consumes input.
- Parser accepts non-binary input gracefully — returns
{:error, _}rather thanFunctionClauseError.
Performance
- Array slicing is now linear-time. A
$[::2]slice on a 100,000-element list went from ~9 s to ~3 ms (~3,000× faster).$[::-1]went from ~18 s to ~5 ms. - Single-index access avoids tuple allocation, so root-reference filters like
$[?(@.value > $[0].value)]are ~25× faster and use ~50× less memory. Enum.reduce+ reverse replaces++appends in multi-key bracket access and in the comparison-chain grouping helper, removing quadratic behavior on larger inputs.collect_all_listsis no longer recursive into already-collected lists, which also speeds up$..[…]queries.
New features
- Full RFC 9535 escape sequences in single- and double-quoted strings:
\n,\t,\r,\b,\f,\/,\\,\',\", and\uXXXX. Unknown escapes pass through unchanged so regex patterns can be embedded inline. - Unicode member names in dot notation:
$.屬性,$.имя, etc. Previously these required bracket notation. - Scientific-notation float literals:
1.5e2,-1e-3,2E+2,-0.123e2. count()built-in (postfix and prefix). Returns the length of a list or the size of a map.- Prefix-style function calls in filters:
length(@),count(@.x),sum(@.nums),min(@.scores),max(@.scores),avg(@.values),concat(@.parts),match(@.s, "regex"),search(@.s, "regex"). match()andsearch()regex built-ins.match()requires the whole string to match (RFC 9535 semantics);search()matches anywhere. Invalid regex patterns returnfalseinstead of raising.JSONPathEx.evaluate!/2— raises on parse failure and returns the evaluation result directly.
Code quality
- Removed dead
min: 1option passes toNimbleParsec.repeat/3(the option was silently ignored). - Dropped
unwrap_and_tagfrom punctuation combinators that are then wrapped inignore([,],(,),,,:,',"). - Removed unused
is_list(result)branch from multi-key bracket access on lists (get/2on a list always returns a list). - Improved internal sentinels (
@missing) are namespaced (:"$$__jsonpath_ex_missing__$$") to avoid collision with user-supplied atoms.
Test suite
- 116 → 206 tests, including:
test/jsonpath_ex/regression_test.exs— one test per fixed bug, asserting the corrected behavior.test/jsonpath_ex/feature_test.exs— coverage for every new feature.test/jsonpath_ex/stress_test.exs—@tag :performancedeep/wide stress cases (run withmix test --include performance).
Benchmarks
- New
bench/directory with Benchee suites for parsing, evaluation, slicing, filters, and recursion. Run any of them withmix run bench/<file>.exs.
0.2.0
Initial public release on Hex.