shepmaster / fuzzy-pickles

A Rust parser to power Strata Rust
Apache License 2.0
7 stars 2 forks source link

Expression ambiguity of ranges #126

Closed dtolnay closed 5 years ago

dtolnay commented 5 years ago

Following up on https://github.com/dtolnay/rust-quiz/issues/2#issuecomment-478978696:

Constructs like for i in 0.. {} will greedily match the block as the second part of the range

This is not the case in rustc. For example try:

fn main() {
    for i in 0.. {
        break;
    }
}

There is one bit of state that determines whether an open curly marks the beginning of the loop body and it is the same bit for struct literals as for range rhs.

shepmaster commented 5 years ago

This might just be a poor wording of the comment. That should be read closer to

without handling this somehow, the parser infrastructure would greedily match

For what it's worth, FP parses your example code as (slightly cleaned up):

File {
    items: [
        Attributed {
            extent: Extent(0, 53),
            attributes: [],
            value: Function(
                Function {
                    extent: Extent(0, 53),
                    header: FunctionHeader {
                        extent: Extent(0, 9),
                        visibility: None,
                        qualifiers: FunctionQualifiers {
                            extent: Extent(0, 0),
                            is_default: None,
                            is_const: None,
                            is_unsafe: None,
                            is_extern: None,
                            abi: None, 
                        },
                        name: Ident {
                            extent: Extent(3, 7)
                        },
                        generics: None,
                        arguments: [],
                        return_type: None,
                        wheres: [], 
                    },
                    body: Block {
                        extent: Extent(10, 53),
                        statements: [],
                        expression: Some(
                            Attributed {
                                extent: Extent(16, 51),
                                attributes: [],
                                value: ForLoop(
                                    ForLoop {
                                        extent: Extent(16, 51),
                                        label: None,
                                        pattern: Pattern {
                                            extent: Extent(20, 21),
                                            name: None,
                                            kind: Ident(
                                                PatternIdent {
                                                    extent: Extent(20, 21),
                                                    is_ref: None,
                                                    is_mut: None,
                                                    ident: PathedIdent {
                                                        extent: Extent(20, 21),
                                                        components: [
                                                            PathComponent {
                                                                extent: Extent(20, 21),
                                                                ident: Ident {
                                                                    extent: Extent(20, 21)
                                                                },
                                                                turbofish: None, 
                                                            }
                                                        ], 
                                                    },
                                                    tuple: None, 
                                                }
                                            ), 
                                        },
                                        iter: Attributed {
                                            extent: Extent(25, 28),
                                            attributes: [],
                                            value: Range(
                                                Range {
                                                    extent: Extent(25, 28),
                                                    lhs: Some(
                                                        Attributed {
                                                            extent: Extent(25, 26),
                                                            attributes: [],
                                                            value: Number(
                                                                Number {
                                                                    extent: Extent(25, 26),
                                                                    is_negative: None,
                                                                    value: Decimal(
                                                                        NumberDecimal {
                                                                            extent: Extent(25, 26),
                                                                            decimal: Extent(25, 26),
                                                                            fraction: None,
                                                                            exponent: None,
                                                                            suffix: None
                                                                        }
                                                                    ), 
                                                                }
                                                            ), 
                                                        }
                                                    ),
                                                    rhs: None, 
                                                }
                                            ), 
                                        },
                                        body: Block {
                                            extent: Extent(29, 51),
                                            statements: [
                                                Expression(
                                                    Attributed {
                                                        extent: Extent(39, 44),
                                                        attributes: [],
                                                        value: Break(
                                                            Break {
                                                                extent: Extent(39, 44),
                                                                label: None,
                                                                value: None, 
                                                            }
                                                        ), 
                                                    }
                                                )
                                            ],
                                            expression: None, 
                                        }, 
                                    }
                                ), 
                            }
                        ), 
                    }, 
                }
            ), 
        }
    ], 
}
dtolnay commented 5 years ago

Ah good call, not sure how I misread this. That syntax tree looks correct and the comment makes sense. :slightly_smiling_face:

I am still curious why fuzzy-pickles uses the three ambiguity choices where Syn has a bool -- I will take a look in the code here and try to see if there is a case that Syn is handling incorrectly.

shepmaster commented 5 years ago

Nevermind, I'm dumb and forgot the x in part.


a case that Syn is handling incorrectly

It does seem unlikely based on how widespread Syn's usage is, but:

fn main() -> Result<(), syn::Error> {
    let a: syn::Expr = syn::parse_str("for 0.. {}")?;
    println!("{:?}", a);

    let a: syn::Expr = syn::parse_str("for {0}.. {}")?;
    println!("{:?}", a);

    Ok(())
}
Error: Error { start_span: Span, end_span: Span, message: "expected one of: literal, identifier, `::`, `<`, `self`, `Self`, `super`, `extern`, `crate`" }
[dependencies]
syn = { version = "0.15.30", features = ["parsing", "extra-traits", "full"] }