@@ -291,6 +291,9 @@ pub enum Expr {
291291 /// Typed hole: ?hole
292292 Hole { span : Span , name : Ident } ,
293293
294+ /// Wildcard: _ (anonymous argument, NOT a typed hole)
295+ Wildcard { span : Span } ,
296+
294297 /// Array literal: [1, 2, 3]
295298 Array { span : Span , elements : Vec < Expr > } ,
296299
@@ -580,6 +583,7 @@ impl Expr {
580583 | Expr :: RecordUpdate { span, .. }
581584 | Expr :: TypeAnnotation { span, .. }
582585 | Expr :: Hole { span, .. }
586+ | Expr :: Wildcard { span, .. }
583587 | Expr :: Array { span, .. }
584588 | Expr :: Negate { span, .. }
585589 | Expr :: AsPattern { span, .. }
@@ -1697,6 +1701,10 @@ impl Converter {
16971701 // --- Expression conversion ---
16981702
16991703 fn convert_expr ( & mut self , expr : & cst:: Expr ) -> Expr {
1704+ stacker:: maybe_grow ( 32 * 1024 , 2 * 1024 * 1024 , || self . convert_expr_impl ( expr) )
1705+ }
1706+
1707+ fn convert_expr_impl ( & mut self , expr : & cst:: Expr ) -> Expr {
17001708 match expr {
17011709 cst:: Expr :: Var { span, name } => Expr :: Var {
17021710 span : * span,
@@ -1931,10 +1939,87 @@ impl Converter {
19311939 . map ( |f| self . convert_record_field ( f) )
19321940 . collect ( ) ,
19331941 } ,
1934- cst:: Expr :: RecordAccess { span, expr, field } => Expr :: RecordAccess {
1935- span : * span,
1936- expr : Box :: new ( self . convert_expr ( expr) ) ,
1937- field : field. clone ( ) ,
1942+ cst:: Expr :: RecordAccess { span, expr, field } => {
1943+ // _.x (record accessor section) → \$_arg -> $_arg.x
1944+ if Self :: is_wildcard ( expr) {
1945+ let param_name = intern ( "$_arg" ) ;
1946+ let mut scope = HashMap :: new ( ) ;
1947+ scope. insert ( param_name, * span) ;
1948+ self . local_scopes . push ( scope) ;
1949+ let param_expr = Expr :: Var {
1950+ span : expr. span ( ) ,
1951+ name : QualifiedIdent {
1952+ module : None ,
1953+ name : param_name,
1954+ } ,
1955+ definition_site : DefinitionSite :: Local ( * span) ,
1956+ } ;
1957+ let body = Expr :: RecordAccess {
1958+ span : * span,
1959+ expr : Box :: new ( param_expr) ,
1960+ field : field. clone ( ) ,
1961+ } ;
1962+ self . local_scopes . pop ( ) ;
1963+ Expr :: Lambda {
1964+ span : * span,
1965+ binders : vec ! [ Binder :: Var {
1966+ span: * span,
1967+ name: cst:: Spanned {
1968+ span: * span,
1969+ value: param_name,
1970+ } ,
1971+ } ] ,
1972+ body : Box :: new ( body) ,
1973+ }
1974+ } else {
1975+ // Handle chained accessor on wildcard: _.x.y → \$_arg -> $_arg.x.y
1976+ let mut chain = vec ! [ field. clone( ) ] ;
1977+ let mut inner = expr. as_ref ( ) ;
1978+ while let cst:: Expr :: RecordAccess { expr : e, field : f, .. } = inner {
1979+ chain. push ( f. clone ( ) ) ;
1980+ inner = e. as_ref ( ) ;
1981+ }
1982+ if Self :: is_wildcard ( inner) {
1983+ let param_name = intern ( "$_arg" ) ;
1984+ let mut scope = HashMap :: new ( ) ;
1985+ scope. insert ( param_name, * span) ;
1986+ self . local_scopes . push ( scope) ;
1987+ let mut body = Expr :: Var {
1988+ span : inner. span ( ) ,
1989+ name : QualifiedIdent {
1990+ module : None ,
1991+ name : param_name,
1992+ } ,
1993+ definition_site : DefinitionSite :: Local ( * span) ,
1994+ } ;
1995+ // Apply fields in reverse (innermost first): _.x.y → ($__arg).x.y
1996+ for f in chain. iter ( ) . rev ( ) {
1997+ body = Expr :: RecordAccess {
1998+ span : * span,
1999+ expr : Box :: new ( body) ,
2000+ field : f. clone ( ) ,
2001+ } ;
2002+ }
2003+ self . local_scopes . pop ( ) ;
2004+ Expr :: Lambda {
2005+ span : * span,
2006+ binders : vec ! [ Binder :: Var {
2007+ span: * span,
2008+ name: cst:: Spanned {
2009+ span: * span,
2010+ value: param_name,
2011+ } ,
2012+ } ] ,
2013+ body : Box :: new ( body) ,
2014+ }
2015+ } else {
2016+ Expr :: RecordAccess {
2017+ span : * span,
2018+ expr : Box :: new ( self . convert_expr ( expr) ) ,
2019+ field : field. clone ( ) ,
2020+ }
2021+ }
2022+ }
19382023 } ,
19392024 cst:: Expr :: RecordUpdate {
19402025 span,
@@ -1975,9 +2060,8 @@ impl Converter {
19752060 span : * span,
19762061 name : * name,
19772062 } ,
1978- cst:: Expr :: Wildcard { span } => Expr :: Hole {
2063+ cst:: Expr :: Wildcard { span } => Expr :: Wildcard {
19792064 span : * span,
1980- name : intern ( "_" ) ,
19812065 } ,
19822066 cst:: Expr :: Array { span, elements } => Expr :: Array {
19832067 span : * span,
@@ -2204,8 +2288,7 @@ impl Converter {
22042288 return result;
22052289 }
22062290
2207- let wildcard_sym = interner:: intern ( "_" ) ;
2208- // Destructure App(App(op, left), right) to check for holes
2291+ // Destructure App(App(op, left), right) to check for wildcard sections
22092292 if let Expr :: App {
22102293 span : outer_span,
22112294 func : outer_func,
@@ -2218,10 +2301,8 @@ impl Converter {
22182301 arg : left_arg,
22192302 } = * outer_func
22202303 {
2221- let left_is_hole =
2222- matches ! ( & * left_arg, Expr :: Hole { name, .. } if * name == wildcard_sym) ;
2223- let right_is_hole =
2224- matches ! ( & * right_arg, Expr :: Hole { name, .. } if * name == wildcard_sym) ;
2304+ let left_is_hole = matches ! ( & * left_arg, Expr :: Wildcard { .. } ) ;
2305+ let right_is_hole = matches ! ( & * right_arg, Expr :: Wildcard { .. } ) ;
22252306 if left_is_hole || right_is_hole {
22262307 // Valid section after rebalancing — desugar to lambda
22272308 let param_name = interner:: intern ( "$_arg" ) ;
@@ -2278,10 +2359,7 @@ impl Converter {
22782359 // but emit error for safety
22792360 self . errors
22802361 . push ( TypeError :: IncorrectAnonymousArgument { span } ) ;
2281- output. pop ( ) . unwrap_or ( Expr :: Hole {
2282- span,
2283- name : wildcard_sym,
2284- } )
2362+ output. pop ( ) . unwrap_or ( Expr :: Wildcard { span } )
22852363 }
22862364
22872365 fn build_op_app (
@@ -2363,6 +2441,10 @@ impl Converter {
23632441 // --- Type expression conversion ---
23642442
23652443 fn convert_type_expr ( & mut self , ty : & cst:: TypeExpr ) -> TypeExpr {
2444+ stacker:: maybe_grow ( 32 * 1024 , 2 * 1024 * 1024 , || self . convert_type_expr_impl ( ty) )
2445+ }
2446+
2447+ fn convert_type_expr_impl ( & mut self , ty : & cst:: TypeExpr ) -> TypeExpr {
23662448 match ty {
23672449 cst:: TypeExpr :: Var { span, name } => TypeExpr :: Var {
23682450 span : * span,
@@ -2610,6 +2692,10 @@ impl Converter {
26102692 // --- Binder conversion ---
26112693
26122694 fn convert_binder ( & mut self , binder : & cst:: Binder ) -> Binder {
2695+ stacker:: maybe_grow ( 32 * 1024 , 2 * 1024 * 1024 , || self . convert_binder_impl ( binder) )
2696+ }
2697+
2698+ fn convert_binder_impl ( & mut self , binder : & cst:: Binder ) -> Binder {
26132699 match binder {
26142700 cst:: Binder :: Wildcard { span } => Binder :: Wildcard { span : * span } ,
26152701 cst:: Binder :: Var { span, name } => Binder :: Var {
0 commit comments