@@ -156,6 +156,12 @@ pub enum IrExpression<'a> {
156156 /// Derived literal map for pure function bodies.
157157 /// Contains IrExpression values that can include PureFunctionParameter references.
158158 DerivedLiteralMap ( Box < ' a , DerivedLiteralMapExpr < ' a > > ) ,
159+ /// Literal array with IR expression elements.
160+ /// Used to preserve pipe bindings inside array literals during ingest.
161+ LiteralArray ( Box < ' a , IrLiteralArrayExpr < ' a > > ) ,
162+ /// Literal map (object) with IR expression values.
163+ /// Used to preserve pipe bindings inside object literals during ingest.
164+ LiteralMap ( Box < ' a , IrLiteralMapExpr < ' a > > ) ,
159165}
160166
161167impl < ' a > IrExpression < ' a > {
@@ -212,6 +218,10 @@ impl<'a> IrExpression<'a> {
212218 IrExpression :: DerivedLiteralArray ( _) => ExpressionKind :: DerivedLiteralArray ,
213219 // DerivedLiteralMap is a literal map for pure function bodies
214220 IrExpression :: DerivedLiteralMap ( _) => ExpressionKind :: DerivedLiteralMap ,
221+ // LiteralArray is an array literal with IR expression elements
222+ IrExpression :: LiteralArray ( _) => ExpressionKind :: LiteralArray ,
223+ // LiteralMap is an object literal with IR expression values
224+ IrExpression :: LiteralMap ( _) => ExpressionKind :: LiteralMap ,
215225 }
216226 }
217227}
@@ -618,6 +628,34 @@ impl<'a> IrExpression<'a> {
618628 allocator,
619629 ) )
620630 }
631+ IrExpression :: LiteralArray ( e) => {
632+ let mut elements = Vec :: with_capacity_in ( e. elements . len ( ) , allocator) ;
633+ for elem in e. elements . iter ( ) {
634+ elements. push ( elem. clone_in ( allocator) ) ;
635+ }
636+ IrExpression :: LiteralArray ( Box :: new_in (
637+ IrLiteralArrayExpr { elements, source_span : e. source_span } ,
638+ allocator,
639+ ) )
640+ }
641+ IrExpression :: LiteralMap ( e) => {
642+ let mut keys = Vec :: with_capacity_in ( e. keys . len ( ) , allocator) ;
643+ for key in e. keys . iter ( ) {
644+ keys. push ( key. clone ( ) ) ;
645+ }
646+ let mut values = Vec :: with_capacity_in ( e. values . len ( ) , allocator) ;
647+ for value in e. values . iter ( ) {
648+ values. push ( value. clone_in ( allocator) ) ;
649+ }
650+ let mut quoted = Vec :: with_capacity_in ( e. quoted . len ( ) , allocator) ;
651+ for q in e. quoted . iter ( ) {
652+ quoted. push ( * q) ;
653+ }
654+ IrExpression :: LiteralMap ( Box :: new_in (
655+ IrLiteralMapExpr { keys, values, quoted, source_span : e. source_span } ,
656+ allocator,
657+ ) )
658+ }
621659 }
622660 }
623661}
@@ -829,6 +867,30 @@ pub struct DerivedLiteralMapExpr<'a> {
829867 pub source_span : Option < Span > ,
830868}
831869
870+ /// Literal array with IR expression elements.
871+ /// Used during ingest to preserve pipe bindings inside array literals.
872+ #[ derive( Debug ) ]
873+ pub struct IrLiteralArrayExpr < ' a > {
874+ /// Array elements as IR expressions.
875+ pub elements : Vec < ' a , IrExpression < ' a > > ,
876+ /// Source span.
877+ pub source_span : Option < Span > ,
878+ }
879+
880+ /// Literal map (object) with IR expression values.
881+ /// Used during ingest to preserve pipe bindings inside object literals.
882+ #[ derive( Debug ) ]
883+ pub struct IrLiteralMapExpr < ' a > {
884+ /// Map keys (string keys from the original literal map).
885+ pub keys : Vec < ' a , Atom < ' a > > ,
886+ /// Map values as IR expressions.
887+ pub values : Vec < ' a , IrExpression < ' a > > ,
888+ /// Whether each key is quoted.
889+ pub quoted : Vec < ' a , bool > ,
890+ /// Source span.
891+ pub source_span : Option < Span > ,
892+ }
893+
832894/// Pure function expression (memoized computation).
833895#[ derive( Debug ) ]
834896pub struct PureFunctionExpr < ' a > {
@@ -1114,17 +1176,66 @@ pub struct TernaryExpr<'a> {
11141176
11151177/// Binary operators for IR expressions.
11161178///
1117- /// These are used for computed expressions in @for loops.
1179+ /// These are used for computed expressions in @for loops and for preserving
1180+ /// pipes nested in binary expressions like `a ?? (b | pipe)`.
11181181#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
11191182pub enum IrBinaryOperator {
1120- /// Strict equality (===)
1121- Identical ,
1122- /// Strict inequality (!==)
1123- NotIdentical ,
1183+ /// Addition (+)
1184+ Plus ,
11241185 /// Subtraction (-)
11251186 Minus ,
1187+ /// Multiplication (*)
1188+ Multiply ,
1189+ /// Division (/)
1190+ Divide ,
11261191 /// Modulo (%)
11271192 Modulo ,
1193+ /// Exponentiation (**)
1194+ Exponentiation ,
1195+ /// Loose equality (==)
1196+ Equals ,
1197+ /// Loose inequality (!=)
1198+ NotEquals ,
1199+ /// Strict equality (===)
1200+ Identical ,
1201+ /// Strict inequality (!==)
1202+ NotIdentical ,
1203+ /// Less than (<)
1204+ Lower ,
1205+ /// Less than or equal (<=)
1206+ LowerEquals ,
1207+ /// Greater than (>)
1208+ Bigger ,
1209+ /// Greater than or equal (>=)
1210+ BiggerEquals ,
1211+ /// Logical and (&&)
1212+ And ,
1213+ /// Logical or (||)
1214+ Or ,
1215+ /// Nullish coalescing (??)
1216+ NullishCoalesce ,
1217+ /// In operator (in)
1218+ In ,
1219+ /// Assignment (=)
1220+ Assign ,
1221+ /// Addition assignment (+=)
1222+ AdditionAssignment ,
1223+ /// Subtraction assignment (-=)
1224+ SubtractionAssignment ,
1225+ /// Multiplication assignment (*=)
1226+ MultiplicationAssignment ,
1227+ /// Division assignment (/=)
1228+ DivisionAssignment ,
1229+ /// Remainder assignment (%=)
1230+ RemainderAssignment ,
1231+ /// Exponentiation assignment (**=)
1232+ ExponentiationAssignment ,
1233+ /// And assignment (&&=)
1234+ AndAssignment ,
1235+ /// Or assignment (||=)
1236+ OrAssignment ,
1237+ /// Nullish coalescing assignment (??=)
1238+ NullishCoalesceAssignment ,
11281239}
11291240
11301241// ============================================================================
@@ -1246,6 +1357,16 @@ pub fn transform_expressions_in_expression<'a, F>(
12461357 transform_expressions_in_expression ( value, transform, flags) ;
12471358 }
12481359 }
1360+ IrExpression :: LiteralArray ( e) => {
1361+ for elem in e. elements . iter_mut ( ) {
1362+ transform_expressions_in_expression ( elem, transform, flags) ;
1363+ }
1364+ }
1365+ IrExpression :: LiteralMap ( e) => {
1366+ for value in e. values . iter_mut ( ) {
1367+ transform_expressions_in_expression ( value, transform, flags) ;
1368+ }
1369+ }
12491370 IrExpression :: Ternary ( e) => {
12501371 transform_expressions_in_expression ( & mut e. condition , transform, flags) ;
12511372 transform_expressions_in_expression ( & mut e. true_expr , transform, flags) ;
@@ -1389,6 +1510,16 @@ pub fn visit_expressions_in_expression<'a, F>(
13891510 visit_expressions_in_expression ( value, visitor, flags) ;
13901511 }
13911512 }
1513+ IrExpression :: LiteralArray ( e) => {
1514+ for elem in e. elements . iter ( ) {
1515+ visit_expressions_in_expression ( elem, visitor, flags) ;
1516+ }
1517+ }
1518+ IrExpression :: LiteralMap ( e) => {
1519+ for value in e. values . iter ( ) {
1520+ visit_expressions_in_expression ( value, visitor, flags) ;
1521+ }
1522+ }
13921523 IrExpression :: Ternary ( e) => {
13931524 visit_expressions_in_expression ( & e. condition , visitor, flags) ;
13941525 visit_expressions_in_expression ( & e. true_expr , visitor, flags) ;
@@ -1497,19 +1628,169 @@ pub fn transform_expressions_in_update_op<'a, F>(
14971628 UpdateOp :: DeferWhen ( op) => {
14981629 transform_expressions_in_expression ( & mut op. condition , transform, flags) ;
14991630 }
1631+ UpdateOp :: Statement ( op) => {
1632+ // Statement ops may contain WrappedIrNode which wraps IR expressions.
1633+ // We need to transform these expressions so that phases like naming
1634+ // can propagate variable names to ReadVariableExpr inside.
1635+ transform_expressions_in_output_statement ( & mut op. statement , transform, flags) ;
1636+ }
15001637 // Operations without expressions
15011638 UpdateOp :: ListEnd ( _)
15021639 | UpdateOp :: Advance ( _)
15031640 | UpdateOp :: I18nApply ( _)
1504- | UpdateOp :: Animation ( _)
1505- | UpdateOp :: Statement ( _) => {
1506- // Statement ops contain OutputStatement with WrappedNodeExpr which
1507- // wraps IR expressions. These are handled during the reify phase,
1508- // not during the expression transformation phase.
1641+ | UpdateOp :: Animation ( _) => { }
1642+ }
1643+ }
1644+
1645+ /// Transform expressions inside an OutputStatement.
1646+ /// This handles WrappedIrNode expressions that contain IR expressions.
1647+ fn transform_expressions_in_output_statement < ' a , F > (
1648+ stmt : & mut crate :: output:: ast:: OutputStatement < ' a > ,
1649+ transform : & F ,
1650+ flags : VisitorContextFlag ,
1651+ ) where
1652+ F : Fn ( & mut IrExpression < ' a > , VisitorContextFlag ) ,
1653+ {
1654+ use crate :: output:: ast:: OutputStatement ;
1655+
1656+ match stmt {
1657+ OutputStatement :: Expression ( expr_stmt) => {
1658+ transform_expressions_in_output_expression ( & mut expr_stmt. expr , transform, flags) ;
1659+ }
1660+ OutputStatement :: Return ( ret_stmt) => {
1661+ transform_expressions_in_output_expression ( & mut ret_stmt. value , transform, flags) ;
1662+ }
1663+ OutputStatement :: DeclareVar ( decl) => {
1664+ if let Some ( ref mut value) = decl. value {
1665+ transform_expressions_in_output_expression ( value, transform, flags) ;
1666+ }
1667+ }
1668+ OutputStatement :: If ( if_stmt) => {
1669+ transform_expressions_in_output_expression ( & mut if_stmt. condition , transform, flags) ;
1670+ for stmt in if_stmt. true_case . iter_mut ( ) {
1671+ transform_expressions_in_output_statement ( stmt, transform, flags) ;
1672+ }
1673+ for stmt in if_stmt. false_case . iter_mut ( ) {
1674+ transform_expressions_in_output_statement ( stmt, transform, flags) ;
1675+ }
1676+ }
1677+ OutputStatement :: DeclareFunction ( _) => {
1678+ // Function declarations don't contain IrExpressions to transform
15091679 }
15101680 }
15111681}
15121682
1683+ /// Transform expressions inside an OutputExpression.
1684+ /// This handles WrappedIrNode which contains IR expressions.
1685+ fn transform_expressions_in_output_expression < ' a , F > (
1686+ expr : & mut crate :: output:: ast:: OutputExpression < ' a > ,
1687+ transform : & F ,
1688+ flags : VisitorContextFlag ,
1689+ ) where
1690+ F : Fn ( & mut IrExpression < ' a > , VisitorContextFlag ) ,
1691+ {
1692+ use crate :: output:: ast:: OutputExpression ;
1693+
1694+ match expr {
1695+ OutputExpression :: WrappedIrNode ( wrapped) => {
1696+ transform_expressions_in_expression ( & mut wrapped. node , transform, flags) ;
1697+ }
1698+ OutputExpression :: Conditional ( cond) => {
1699+ transform_expressions_in_output_expression ( & mut cond. condition , transform, flags) ;
1700+ transform_expressions_in_output_expression ( & mut cond. true_case , transform, flags) ;
1701+ if let Some ( false_case) = & mut cond. false_case {
1702+ transform_expressions_in_output_expression ( false_case, transform, flags) ;
1703+ }
1704+ }
1705+ OutputExpression :: BinaryOperator ( bin) => {
1706+ transform_expressions_in_output_expression ( & mut bin. lhs , transform, flags) ;
1707+ transform_expressions_in_output_expression ( & mut bin. rhs , transform, flags) ;
1708+ }
1709+ OutputExpression :: UnaryOperator ( un) => {
1710+ transform_expressions_in_output_expression ( & mut un. expr , transform, flags) ;
1711+ }
1712+ OutputExpression :: Not ( not) => {
1713+ transform_expressions_in_output_expression ( & mut not. condition , transform, flags) ;
1714+ }
1715+ OutputExpression :: ReadProp ( member) => {
1716+ transform_expressions_in_output_expression ( & mut member. receiver , transform, flags) ;
1717+ }
1718+ OutputExpression :: ReadKey ( idx) => {
1719+ transform_expressions_in_output_expression ( & mut idx. receiver , transform, flags) ;
1720+ transform_expressions_in_output_expression ( & mut idx. index , transform, flags) ;
1721+ }
1722+ OutputExpression :: TaggedTemplateLiteral ( tagged) => {
1723+ transform_expressions_in_output_expression ( & mut tagged. tag , transform, flags) ;
1724+ for expr in tagged. template . expressions . iter_mut ( ) {
1725+ transform_expressions_in_output_expression ( expr, transform, flags) ;
1726+ }
1727+ }
1728+ OutputExpression :: ArrowFunction ( arrow) => match & mut arrow. body {
1729+ crate :: output:: ast:: ArrowFunctionBody :: Expression ( expr) => {
1730+ transform_expressions_in_output_expression ( expr, transform, flags) ;
1731+ }
1732+ crate :: output:: ast:: ArrowFunctionBody :: Statements ( stmts) => {
1733+ for stmt in stmts. iter_mut ( ) {
1734+ transform_expressions_in_output_statement ( stmt, transform, flags) ;
1735+ }
1736+ }
1737+ } ,
1738+ OutputExpression :: LiteralArray ( arr) => {
1739+ for elem in arr. entries . iter_mut ( ) {
1740+ transform_expressions_in_output_expression ( elem, transform, flags) ;
1741+ }
1742+ }
1743+ OutputExpression :: LiteralMap ( obj) => {
1744+ for entry in obj. entries . iter_mut ( ) {
1745+ transform_expressions_in_output_expression ( & mut entry. value , transform, flags) ;
1746+ if entry. quoted {
1747+ // Key is always a string, no sub-expressions
1748+ }
1749+ }
1750+ }
1751+ OutputExpression :: InvokeFunction ( inv) => {
1752+ transform_expressions_in_output_expression ( & mut inv. fn_expr , transform, flags) ;
1753+ for arg in inv. args . iter_mut ( ) {
1754+ transform_expressions_in_output_expression ( arg, transform, flags) ;
1755+ }
1756+ }
1757+ OutputExpression :: Function ( func) => {
1758+ for stmt in func. statements . iter_mut ( ) {
1759+ transform_expressions_in_output_statement ( stmt, transform, flags) ;
1760+ }
1761+ }
1762+ OutputExpression :: Instantiate ( inst) => {
1763+ transform_expressions_in_output_expression ( & mut inst. class_expr , transform, flags) ;
1764+ for arg in inst. args . iter_mut ( ) {
1765+ transform_expressions_in_output_expression ( arg, transform, flags) ;
1766+ }
1767+ }
1768+ OutputExpression :: Parenthesized ( paren) => {
1769+ transform_expressions_in_output_expression ( & mut paren. expr , transform, flags) ;
1770+ }
1771+ OutputExpression :: Comma ( comma) => {
1772+ for part in comma. parts . iter_mut ( ) {
1773+ transform_expressions_in_output_expression ( part, transform, flags) ;
1774+ }
1775+ }
1776+ OutputExpression :: Typeof ( typeof_expr) => {
1777+ transform_expressions_in_output_expression ( & mut typeof_expr. expr , transform, flags) ;
1778+ }
1779+ OutputExpression :: Void ( void_expr) => {
1780+ transform_expressions_in_output_expression ( & mut void_expr. expr , transform, flags) ;
1781+ }
1782+ // Leaf expressions without sub-expressions
1783+ OutputExpression :: Literal ( _)
1784+ | OutputExpression :: TemplateLiteral ( _)
1785+ | OutputExpression :: RegularExpressionLiteral ( _)
1786+ | OutputExpression :: ReadVar ( _)
1787+ | OutputExpression :: External ( _)
1788+ | OutputExpression :: LocalizedString ( _)
1789+ | OutputExpression :: WrappedNode ( _)
1790+ | OutputExpression :: DynamicImport ( _) => { }
1791+ }
1792+ }
1793+
15131794/// Transform all expressions in a create operation.
15141795pub fn transform_expressions_in_create_op < ' a , F > (
15151796 op : & mut CreateOp < ' a > ,
@@ -2208,6 +2489,14 @@ pub fn vars_used_by_ir_expression(expr: &IrExpression<'_>) -> u32 {
22082489 map. values . iter ( ) . map ( vars_used_by_ir_expression) . sum ( )
22092490 }
22102491
2492+ // LiteralArray: sum of vars used by all elements
2493+ IrExpression :: LiteralArray ( arr) => {
2494+ arr. elements . iter ( ) . map ( vars_used_by_ir_expression) . sum ( )
2495+ }
2496+
2497+ // LiteralMap: sum of vars used by all values
2498+ IrExpression :: LiteralMap ( map) => map. values . iter ( ) . map ( vars_used_by_ir_expression) . sum ( ) ,
2499+
22112500 // Ternary: sum of vars used by all branches
22122501 IrExpression :: Ternary ( ternary) => {
22132502 vars_used_by_ir_expression ( & ternary. condition )
0 commit comments