feat: arrow functions support#957
Open
michaeldk wants to merge 22 commits intotwigjs:masterfrom
Open
Conversation
…ion in Twig expressions
… in Twig expressions
…ts using arrow predicates
…ow function support
…ith arrow function support
…ns and preserve original array
…ue in arrays and objects using arrow functions
…ngle-element arrays
…issing variables and async filters
…ing async filters in arrow functions
…field using an arrow comparator
…eturning Twig.Promise in arrow function
…d joining bound variable
Author
|
Any idea if this could get discussed anytime soon? @willrowe |
Collaborator
|
@michaeldk unfortunately, this is a very tricky feature to implement. |
Author
|
@willrowe Any issue with the one I am suggesting here? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Arrow function support
Fixes #652
Arrow function support matching the Twig template language specification, with five filters that consume them:
filter,map,reduce,sort, andfind.{% for product in products|filter(p => p.stock > 10) %} {{ product.name }} {% endfor %} {{ items|map(v => v.name)|join(', ') }} {{ totals|reduce((carry, v) => carry + v, 0) }} {{ items|sort((a, b) => a.price - b.price) }} {{ users|find(u => u.id == target_id) }}All forms from the Twig spec are supported:
v => v > 3(v) => v > 3(value, key) => key ~ ': ' ~ valuev => v > min_thresholditems|map(v => v.children|filter(c => c.active))items|filter(v => v > 0)|map(v => v * 2)|sort((a, b) => a - b)How it works
Arrow functions are handled by a pre-compilation pass inserted between tokenize and compile:
=>is registered as a token type (arrowOperator) so the tokenizer doesn't crash on itpreprocessArrowsscans the flat token array, finds=>tokens, extracts parameter names (backward look), collects body tokens (forward look with depth tracking), recursively handles nested arrows, sub-compiles the body into an RPN stack, and replaces the entire span with a singlearrowFunctiontokenarrowFunctionas a single atomic token — no special cases neededparseParamsas an opaque object, landing asparams[0]in the filter functionevaluateArrow, which creates a scoped context (outer context + arrow params), clones the body tokens (sinceparsemutates tokens), and evaluates viaparseAsyncThe shunting-yard loop is untouched. No existing handler's
compileorparsemethod is modified. The only change to an existing function is one line added toTwig.expression.compileto callpreprocessArrows.Async support
All filters fully support async expressions inside arrow bodies via
renderAsync(), addressing the concern raised by @ericmorand in #874.filter,map,find: useTwig.Promise.all— handles sync and async transparentlyreduce: usesTwig.async.forEachfor sequential iteration (each step depends on previous carry)sort: tries nativeArray.sortwith sync evaluation first (zero overhead for the common case); if an async expression is detected, falls back to an async merge sort that chains comparisons throughTwig.Promise