@@ -520,21 +520,25 @@ impl<'a> GrammarParser<'a> {
520520
521521 /// Parse an action block
522522 fn parse_action ( & mut self ) -> ParseResult < ( ActionIR , String ) > {
523- // Parse return type path
524- let return_type = self . parse_type_path ( ) ?;
523+ // Parse first identifier/ type path
524+ let first_path = self . parse_type_path ( ) ?;
525525 self . skip_ws ( ) ;
526526
527- // Check for simple pass-through
527+ // Check for simple pass-through: -> binding (no '{' follows)
528+ // If the first_path is a simple identifier (no ::) and no '{' follows,
529+ // it's a pass-through action
528530 if !self . check_str ( "{" ) {
529- // Simple pass-through: -> binding
530- if let Some ( c) = self . peek_char ( ) {
531- if c. is_ascii_alphabetic ( ) || c == '_' {
532- let binding = self . parse_identifier ( ) ?;
533- return Ok ( ( ActionIR :: PassThrough { binding } , return_type) ) ;
534- }
531+ // Check if it's a simple identifier (no :: in path) - that's pass-through
532+ if !first_path. contains ( "::" ) {
533+ // No '::' means it's just a binding name, not a type path
534+ return Ok ( ( ActionIR :: PassThrough { binding : first_path. clone ( ) } , first_path) ) ;
535535 }
536+ // If it has ::, then there should be a { with fields
537+ return Err ( self . error ( & format ! ( "Expected '{{' after type path '{}'" , first_path) ) ) ;
536538 }
537539
540+ let return_type = first_path;
541+
538542 // Full action block
539543 self . expect_char ( '{' ) ?;
540544 self . skip_ws_and_comments ( ) ;
@@ -934,7 +938,7 @@ impl<'a> GrammarParser<'a> {
934938 }
935939 }
936940
937- /// Parse argument list (comma-separated expressions)
941+ /// Parse argument list (comma-separated expressions, allows trailing comma )
938942 fn parse_arg_list ( & mut self ) -> ParseResult < Vec < ExprIR > > {
939943 let mut args = Vec :: new ( ) ;
940944 self . skip_ws ( ) ;
@@ -950,6 +954,10 @@ impl<'a> GrammarParser<'a> {
950954 if self . peek_char ( ) == Some ( ',' ) {
951955 self . advance ( ) ;
952956 self . skip_ws ( ) ;
957+ // Check for trailing comma (next is ) or ])
958+ if self . peek_char ( ) == Some ( ')' ) || self . peek_char ( ) == Some ( ']' ) {
959+ break ;
960+ }
953961 args. push ( self . parse_expr ( ) ?) ;
954962 } else {
955963 break ;
@@ -1456,6 +1464,128 @@ mod tests {
14561464 }
14571465 }
14581466
1467+ #[ test]
1468+ fn test_parse_nested_struct_in_list ( ) {
1469+ let input = r#"
1470+ @language { name: "Test", version: "1.0" }
1471+
1472+ test_rule = { "test" }
1473+ -> Foo::Bar {
1474+ args: [X::Y { f: v }],
1475+ }
1476+ "# ;
1477+
1478+ match parse_grammar ( input) {
1479+ Ok ( grammar) => {
1480+ println ! ( "OK: {} rules" , grammar. rules. len( ) ) ;
1481+ let rule = grammar. rules . get ( "test_rule" ) . unwrap ( ) ;
1482+ println ! ( "Rule action: {:?}" , rule. action) ;
1483+ }
1484+ Err ( e) => {
1485+ panic ! ( "Failed to parse: {:?}" , e) ;
1486+ }
1487+ }
1488+ }
1489+
1490+ #[ test]
1491+ fn test_parse_box_new_call ( ) {
1492+ let input = r#"
1493+ @language { name: "Test", version: "1.0" }
1494+
1495+ test_rule = { "test" }
1496+ -> Foo::Bar {
1497+ callee: Box::new(X::Y { name: n }),
1498+ }
1499+ "# ;
1500+
1501+ match parse_grammar ( input) {
1502+ Ok ( grammar) => {
1503+ println ! ( "OK: {} rules" , grammar. rules. len( ) ) ;
1504+ let rule = grammar. rules . get ( "test_rule" ) . unwrap ( ) ;
1505+ println ! ( "Rule action: {:?}" , rule. action) ;
1506+ }
1507+ Err ( e) => {
1508+ panic ! ( "Failed to parse: {:?}" , e) ;
1509+ }
1510+ }
1511+ }
1512+
1513+ #[ test]
1514+ fn test_parse_nested_array_in_struct ( ) {
1515+ // Simplified test case - struct in array in struct
1516+ let input = r#"
1517+ @language { name: "Test", version: "1.0" }
1518+
1519+ test = { "test" }
1520+ -> A::B {
1521+ outer: C::D {
1522+ inner: [E::F { x: v }],
1523+ },
1524+ }
1525+ "# ;
1526+
1527+ match parse_grammar ( input) {
1528+ Ok ( grammar) => {
1529+ println ! ( "OK: {} rules" , grammar. rules. len( ) ) ;
1530+ let rule = grammar. rules . get ( "test" ) . unwrap ( ) ;
1531+ println ! ( "Rule action: {:?}" , rule. action) ;
1532+ }
1533+ Err ( e) => {
1534+ panic ! ( "Failed to parse: {:?}" , e) ;
1535+ }
1536+ }
1537+ }
1538+
1539+ #[ test]
1540+ fn test_parse_array_with_function_call ( ) {
1541+ // Test array containing struct with function call (no trailing comma)
1542+ let input = r#"
1543+ @language { name: "Test", version: "1.0" }
1544+
1545+ test = { "test" }
1546+ -> A::B {
1547+ args: [
1548+ C::D { x: intern(v) }
1549+ ],
1550+ }
1551+ "# ;
1552+
1553+ match parse_grammar ( input) {
1554+ Ok ( grammar) => {
1555+ println ! ( "OK: {} rules" , grammar. rules. len( ) ) ;
1556+ let rule = grammar. rules . get ( "test" ) . unwrap ( ) ;
1557+ println ! ( "Rule action: {:?}" , rule. action) ;
1558+ }
1559+ Err ( e) => {
1560+ panic ! ( "Failed to parse: {:?}" , e) ;
1561+ }
1562+ }
1563+ }
1564+
1565+ #[ test]
1566+ fn test_parse_array_with_trailing_comma ( ) {
1567+ // Test array with trailing comma
1568+ let input = r#"
1569+ @language { name: "Test", version: "1.0" }
1570+
1571+ test = { "test" }
1572+ -> A::B {
1573+ args: [a, b,],
1574+ }
1575+ "# ;
1576+
1577+ match parse_grammar ( input) {
1578+ Ok ( grammar) => {
1579+ println ! ( "OK: {} rules" , grammar. rules. len( ) ) ;
1580+ let rule = grammar. rules . get ( "test" ) . unwrap ( ) ;
1581+ println ! ( "Rule action: {:?}" , rule. action) ;
1582+ }
1583+ Err ( e) => {
1584+ panic ! ( "Failed to parse trailing comma: {:?}" , e) ;
1585+ }
1586+ }
1587+ }
1588+
14591589 #[ test]
14601590 fn test_parse_imagepipe_grammar ( ) {
14611591 let input = include_str ! ( "../../../../examples/imagepipe/imagepipe.zyn" ) ;
@@ -1471,7 +1601,7 @@ mod tests {
14711601
14721602 // Verify key metadata
14731603 assert_eq ! ( grammar. metadata. name, "ImagePipe" ) ;
1474- assert_eq ! ( grammar. metadata. version, "1 .0" ) ;
1604+ assert_eq ! ( grammar. metadata. version, "2 .0" ) ;
14751605
14761606 // Verify key rules exist
14771607 assert ! ( grammar. rules. contains_key( "program" ) ) ;
0 commit comments