Skip to content

Commit

Permalink
Multiple items and spreads in array comprehensions
Browse files Browse the repository at this point in the history
Fixes #439
  • Loading branch information
edemaine committed Dec 23, 2024
1 parent b637ae6 commit b4a8b52
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 3 deletions.
13 changes: 12 additions & 1 deletion civet.dev/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1818,7 +1818,7 @@ for key: keyof typeof object, value in object
### Loop Expressions
If needed, loops automatically assemble an Array of the last value
If needed, loops automatically assemble an array of the last value
within the body of the loop for each completed iteration.
<Playground>
Expand Down Expand Up @@ -1850,6 +1850,17 @@ so you cannot use `return` inside such a loop,
nor can you `break` or `continue` any outer loop.
:::
You can also accumulate multiple items and/or spreads:
<Playground>
function flatJoin<T>(list: T[][], sep: T): T[]
for sublist, i of list
if i
sep, ...sublist
else
...sublist
</Playground>
If you don't specify a body, `for` loops list the item being iterated over:
<Playground>
Expand Down
28 changes: 27 additions & 1 deletion source/parser.hera
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,30 @@ CommaExpression
if($2.length == 0) return $1
return $0

# Variation on CommaExpression that allows ... spreads,
# which we allow in statements for the sake of expressionized iterations
CommaExpressionSpread
AssignmentExpressionSpread ( CommaDelimiter AssignmentExpressionSpread )* ->
if($2.length == 0) return $1
return $0

AssignmentExpressionSpread
_? DotDotDot AssignmentExpression:expression ->
return {
type: "SpreadElement",
children: $0,
expression,
names: expression.names,
}
AssignmentExpression:expression ( _? DotDotDot )? ->
if (!$2) return $1
return {
type: "SpreadElement",
children: [ ...$2, $1 ],
expression,
names: expression.names,
}

# https://262.ecma-international.org/#prod-Arguments
Arguments
ExplicitArguments
Expand Down Expand Up @@ -5397,7 +5421,9 @@ CommaExpressionStatement
# NOTE: semi-colons are being handled elsewhere
# NOTE: Shouldn't need negative lookahead if shadowed in the proper order
# NOTE: CommaExpression allows , operator
CommaExpression ->
# NOTE: CommaExpressionSpread allows ... spreads,
# which are useful within expressionized iterations
CommaExpressionSpread ->
// Wrap object literal with parens to disambiguate from block statements.
// Also wrap nameless functions from `->` expressions with parens
// as needed in JS.
Expand Down
2 changes: 1 addition & 1 deletion source/parser/lib.civet
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ function makeExpressionStatement(expression: ASTNode): ASTNode
if Array.isArray(expression) and expression[1]?[0]?[0]?[1]?.token is "," // CommaExpression
[
makeExpressionStatement expression[0]
expression[1].map ([comma, exp]) => // CommaDelimiter AssignmentExpression
expression[1].map [comma, exp] => // CommaDelimiter AssignmentExpression
[comma, makeExpressionStatement exp]
]
else if expression?.type is "ObjectExpression" or
Expand Down
55 changes: 55 additions & 0 deletions test/for.civet
Original file line number Diff line number Diff line change
Expand Up @@ -1527,3 +1527,58 @@ describe "for", ->
---
if (!(()=>{let results=true;for (const f of facesHit) {results = false; break}return results})()) { return false }
"""

describe "spreads", ->
testCase """
comma in body
---
function concatPairs(pairs)
for [x, y] of pairs
x, y
---
function concatPairs(pairs) {
const results=[];for (const [x, y] of pairs) {
results.push(x, y)
};return results;
}
"""

testCase """
spread in body
---
function concat(arrays)
for array of arrays
...array
---
function concat(arrays) {
const results=[];for (const array of arrays) {
results.push(...array)
};return results;
}
"""

testCase """
reverse spread in body
---
function concat(arrays)
for array of arrays
array ...
---
function concat(arrays) {
const results=[];for (const array of arrays) {
results.push( ...array)
};return results;
}
"""

testCase """
multiple spreads in body
---
result :=
for item of items
item.pre, ...item.mid, item.post, ... item.tail
---
const results=[];for (const item of items) {
results.push(item.pre, ...item.mid, item.post, ... item.tail)
};const result =results
"""

0 comments on commit b4a8b52

Please sign in to comment.