fix: support braceless predicates matching Go expr-lang behavior#23
fix: support braceless predicates matching Go expr-lang behavior#23
Conversation
In Go expr-lang, predicates like `filter(arr, # > 2)` work without
braces around the predicate expression. Our PEG parser requires braces
because `#` gets consumed as an `expr` argument before the `predicate`
rule has a chance to match.
This fix detects when the last argument to a function contains `#` and
promotes it to the predicate position at the AST level. The promotion
only triggers when there are 2+ args to avoid incorrectly affecting
nested calls like `len(#)` inside `sortBy(arr, {len(#)})`.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary of ChangesHello @jdx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request enhances the expression language parser to support a more flexible syntax for function predicates. By automatically detecting and promoting braceless predicates containing the Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces support for braceless predicates to align with Go expr-lang behavior, which is a great enhancement for usability. The implementation correctly identifies when to promote an argument to a predicate by checking for the # reference. The logic is sound and well-tested. I have one suggestion to make the code for promoting the argument more concise and readable.
src/ast/node.rs
Outdated
| if predicate.is_none() && args.len() >= 2 { | ||
| if let Some(last) = args.last() { | ||
| if last.contains_hash_ident() { | ||
| let last = args.pop().unwrap(); | ||
| predicate = Some(Box::new(Program { | ||
| lines: Vec::new(), | ||
| expr: last, | ||
| })); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
This block can be made more concise by combining the conditions into a single if statement. The if let is redundant since args.len() >= 2 guarantees args.last() is Some. Using unwrap() is safe here due to the length check.
if predicate.is_none() && args.len() >= 2 && args.last().unwrap().contains_hash_ident() {
let last = args.pop().unwrap();
predicate = Some(Box::new(Program {
lines: Vec::new(),
expr: last,
}));
}- Use a whitelist of predicate-accepting functions (filter, map, sortBy,
etc.) instead of relying on arg count heuristic. This prevents the
promotion from incorrectly firing on nested multi-arg functions like
indexOf("abc", #) inside braced predicates.
- Lower threshold from args.len() >= 2 to >= 1 so pipe syntax works:
`arr | filter(# > 2)` where the array arrives at runtime via pipe.
- Fix contains_hash_ident to recurse into PostfixOperator inner nodes
(Index, Default, Ternary, Pipe) which can contain # references.
- Add tests for pipe + braceless predicates and nested func in predicate.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
0c01dc4 to
a6bebf4
Compare
contains_hash_ident was recursing into Node::Func predicates, but #
inside a function's predicate is bound to that function's iteration —
not a free reference for outer promotion. For example, in
map(arr1, any(arr2, {# > 0})), the inner any's # is bound, so it
should not cause any(...) to be promoted as map's predicate.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…8081) ## Summary - Flutter's Google API returns releases in newest-first order, which causes `mise install flutter` to resolve to an old version (3.22.1) instead of the latest (3.38.x) - Switch from `version_json_path` to `version_expr` with `sortVersions()` to ensure proper ascending semver order - Adds a unit test for the Flutter version_expr parsing Fixes #7863 (comment) Also submitted jdx/expr.rs#23 to fix braceless predicate parsing in expr.rs (Go expr-lang compatibility), which is why this PR uses `{#.channel == "stable"}` with braces. ## Test plan - [x] Unit test `test_parse_flutter_with_version_expr` verifies correct filtering and sorting - [x] All existing tests pass - [x] Linting passes 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk: registry config change and a new unit test; behavior change is limited to how Flutter versions are extracted/sorted from the upstream JSON. > > **Overview** > Fixes Flutter version resolution by switching the registry backend from `version_json_path` to a `version_expr` that filters `stable` releases and applies `sortVersions()` so installs pick the latest stable version. > > Adds a unit test (`test_parse_flutter_with_version_expr`) to validate the expr-based filtering/sorting behavior against Flutter-style JSON. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 9af283d. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…dx#8081) ## Summary - Flutter's Google API returns releases in newest-first order, which causes `mise install flutter` to resolve to an old version (3.22.1) instead of the latest (3.38.x) - Switch from `version_json_path` to `version_expr` with `sortVersions()` to ensure proper ascending semver order - Adds a unit test for the Flutter version_expr parsing Fixes jdx#7863 (comment) Also submitted jdx/expr.rs#23 to fix braceless predicate parsing in expr.rs (Go expr-lang compatibility), which is why this PR uses `{#.channel == "stable"}` with braces. ## Test plan - [x] Unit test `test_parse_flutter_with_version_expr` verifies correct filtering and sorting - [x] All existing tests pass - [x] Linting passes 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Low risk: registry config change and a new unit test; behavior change is limited to how Flutter versions are extracted/sorted from the upstream JSON. > > **Overview** > Fixes Flutter version resolution by switching the registry backend from `version_json_path` to a `version_expr` that filters `stable` releases and applies `sortVersions()` so installs pick the latest stable version. > > Adds a unit test (`test_parse_flutter_with_version_expr`) to validate the expr-based filtering/sorting behavior against Flutter-style JSON. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 9af283d. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Summary
filter(arr, # > 2)work without braces. Our PEG parser requires braces because#gets consumed as anexprargument before thepredicaterule can match.#references and promotes it to the predicate position at the AST level.len(#)insidesortBy(arr, {len(#)}).Test plan
filter(0..9, # % 2 == 0),filter([1, 2, 3], # > 1),map([1, 2, 3], # * 2)sortBytests with{len(#)}still pass (no regression)🤖 Generated with Claude Code
Note
Medium Risk
Changes how function-call ASTs are constructed by reclassifying certain trailing arguments as predicates, which could subtly alter parsing/semantics for edge-case expressions. Scope is limited to a hardcoded set of predicate-accepting functions and is covered by new regression tests.
Overview
Enables Go expr-lang–compatible braceless predicates like
filter(arr, # > 2)and pipe forms likearr | filter(# > 2)by promoting the last argument to a function predicate when it references#and no explicit{...}predicate was parsed.Adds
contains_hash_identtraversal onNode/PostfixOperatorand gates promotion behind a hardcoded allowlist of predicate-capable functions to avoid changing behavior for non-predicate calls and nested#usage; expands tests to cover braceless, piped, and nested-predicate regression cases.Written by Cursor Bugbot for commit 2f34560. This will update automatically on new commits. Configure here.