Allow trailing commas in parser
4bb2cff33178f88bc59dcaaf2955ab2602d3ed7ef7432f4e1bc954f460d96be6
1 parent
c8b1ee9e
lib/std/lang/parser.rad
+8 -11
| 816 | 816 | let mut args = ast::nodeSlice(p.arena, 4); |
|
| 817 | 817 | ||
| 818 | 818 | if kind == ast::Builtin::SliceOf { |
|
| 819 | 819 | // Parse comma-separated expressions until closing paren. |
|
| 820 | 820 | // Argument count validation is done in semantic analysis. |
|
| 821 | - | if p.current.kind != scanner::TokenKind::RParen { |
|
| 821 | + | while not check(p, scanner::TokenKind::RParen) { |
|
| 822 | 822 | args.append(try parseExpr(p), p.allocator); |
|
| 823 | - | while p.current.kind == scanner::TokenKind::Comma { |
|
| 824 | - | advance(p); |
|
| 825 | - | args.append(try parseExpr(p), p.allocator); |
|
| 823 | + | if not consume(p, scanner::TokenKind::Comma) { |
|
| 824 | + | break; |
|
| 826 | 825 | } |
|
| 827 | 826 | } |
|
| 828 | 827 | } else { |
|
| 829 | 828 | args.append(try parseType(p), p.allocator); |
|
| 830 | 829 | } |
| 1902 | 1901 | throws (ParseError) |
|
| 1903 | 1902 | { |
|
| 1904 | 1903 | try expect(p, scanner::TokenKind::LParen, "expected `(` after function name"); |
|
| 1905 | 1904 | let mut params = ast::nodeSlice(p.arena, 8); |
|
| 1906 | 1905 | ||
| 1907 | - | if not check(p, scanner::TokenKind::RParen) { |
|
| 1908 | - | loop { |
|
| 1909 | - | let param = try parseFnParam(p); |
|
| 1910 | - | params.append(param, p.allocator); |
|
| 1906 | + | while not check(p, scanner::TokenKind::RParen) { |
|
| 1907 | + | let param = try parseFnParam(p); |
|
| 1908 | + | params.append(param, p.allocator); |
|
| 1911 | 1909 | ||
| 1912 | - | if not consume(p, scanner::TokenKind::Comma) { |
|
| 1913 | - | break; |
|
| 1914 | - | } |
|
| 1910 | + | if not consume(p, scanner::TokenKind::Comma) { |
|
| 1911 | + | break; |
|
| 1915 | 1912 | } |
|
| 1916 | 1913 | } |
|
| 1917 | 1914 | try expect(p, scanner::TokenKind::RParen, "expected `)` after function parameters"); |
|
| 1918 | 1915 | ||
| 1919 | 1916 | let mut returnType: ?*ast::Node = nil; |
lib/std/lang/parser/tests.rad
+49 -0
| 2663 | 2663 | ||
| 2664 | 2664 | try expectIdent(inner.thenExpr, "b"); |
|
| 2665 | 2665 | try expectIdent(inner.condition, "y"); |
|
| 2666 | 2666 | try expectIdent(inner.elseExpr, "c"); |
|
| 2667 | 2667 | } |
|
| 2668 | + | ||
| 2669 | + | /// Test that trailing commas are allowed in all comma-separated lists. |
|
| 2670 | + | @test fn testTrailingCommas() throws (testing::TestError) { |
|
| 2671 | + | // Function call arguments. |
|
| 2672 | + | let call = try! parseExprStr("f(1, 2, 3,)"); |
|
| 2673 | + | let case ast::NodeValue::Call(c) = call.value else throw testing::TestError::Failed; |
|
| 2674 | + | try testing::expect(c.args.len == 3); |
|
| 2675 | + | ||
| 2676 | + | // Function parameters. |
|
| 2677 | + | let fnNode = try! parseStmtStr("fn add(x: i32, y: i32,) {}"); |
|
| 2678 | + | let case ast::NodeValue::FnDecl(fnDecl) = fnNode.value else throw testing::TestError::Failed; |
|
| 2679 | + | try testing::expect(fnDecl.sig.params.len == 2); |
|
| 2680 | + | ||
| 2681 | + | // Record declarations. |
|
| 2682 | + | let recNode = try! parseStmtStr("record R { x: i32, y: bool, }"); |
|
| 2683 | + | let case ast::NodeValue::RecordDecl(recDecl) = recNode.value else throw testing::TestError::Failed; |
|
| 2684 | + | try testing::expect(recDecl.fields.len == 2); |
|
| 2685 | + | ||
| 2686 | + | // Tuple record declarations. |
|
| 2687 | + | let tupNode = try! parseStmtStr("record R(i32, bool,);"); |
|
| 2688 | + | let case ast::NodeValue::RecordDecl(tupDecl) = tupNode.value else throw testing::TestError::Failed; |
|
| 2689 | + | try testing::expect(tupDecl.fields.len == 2); |
|
| 2690 | + | ||
| 2691 | + | // Record literals. |
|
| 2692 | + | let litNode = try! parseExprStr("Point { x: 1, y: 2, }"); |
|
| 2693 | + | let case ast::NodeValue::RecordLit(lit) = litNode.value else throw testing::TestError::Failed; |
|
| 2694 | + | try testing::expect(lit.fields.len == 2); |
|
| 2695 | + | ||
| 2696 | + | // Union declarations. |
|
| 2697 | + | let unionNode = try! parseStmtStr("union Color { Red, Green, Blue, }"); |
|
| 2698 | + | let case ast::NodeValue::UnionDecl(unionDecl) = unionNode.value else throw testing::TestError::Failed; |
|
| 2699 | + | try testing::expect(unionDecl.variants.len == 3); |
|
| 2700 | + | ||
| 2701 | + | // Array literals. |
|
| 2702 | + | let arrNode = try! parseExprStr("[1, 2, 3,]"); |
|
| 2703 | + | let case ast::NodeValue::ArrayLit(items) = arrNode.value else throw testing::TestError::Failed; |
|
| 2704 | + | try testing::expect(items.len == 3); |
|
| 2705 | + | ||
| 2706 | + | // Function type parameters. |
|
| 2707 | + | let fnType = try! parseTypeStr("fn (i32, bool,) -> void"); |
|
| 2708 | + | let case ast::NodeValue::TypeSig(sigValue) = fnType.value else throw testing::TestError::Failed; |
|
| 2709 | + | let case ast::TypeSig::Fn(sig) = sigValue else throw testing::TestError::Failed; |
|
| 2710 | + | try testing::expect(sig.params.len == 2); |
|
| 2711 | + | ||
| 2712 | + | // Throws lists. |
|
| 2713 | + | let throwsNode = try! parseStmtStr("fn handle() throws (Error, Other,) {}"); |
|
| 2714 | + | let case ast::NodeValue::FnDecl(throwsDecl) = throwsNode.value else throw testing::TestError::Failed; |
|
| 2715 | + | try testing::expect(throwsDecl.sig.throwList.len == 2); |
|
| 2716 | + | } |