alecthomas / participle

A parser library for Go
MIT License
3.45k stars 186 forks source link

Very weird bug using otherwise OK parsers in Union #389

Closed oguimbal closed 7 months ago

oguimbal commented 7 months ago

Hi !

I'm a bit new to Participle, but I've been writting parsers for a while, and I'm falling a bit short on why the last test fails to parse

func TestBrokenUnion(t *testing.T) {

    // ==========================================
    // ==== check that "named" parser works =====
    // ==========================================

    type Named struct {
        Name string `@(Ident ('.' Ident)?)`
    }

    namedParser := participle.MustBuild[Named]()

    // parses a named type with dot ✅
    named, err := namedParser.ParseString("", "Abc.def")
    require.NoError(t, err)
    require.Equal(t, &Named{Name: "Abc.def"}, named)

    // parses a named type without dot ✅
    named, err = namedParser.ParseString("", "Abc")
    require.NoError(t, err)
    require.Equal(t, &Named{Name: "Abc"}, named)

    // ===========================================
    // ==== use this type parser in an union =====
    // ===========================================

    type AnyType interface{}
    type ArrayOf struct {
        Of *Named `@@ '[' ']'`
    }
    typeParser := participle.MustBuild[AnyType](
        // union includes the "named" parser, which works
        participle.Union[AnyType](&ArrayOf{}, &Named{}),
    )

    // parses an array type without dot ✅
    typ, err := typeParser.ParseString("", "Abc[]")
    require.NoError(t, err)
    require.Equal(t, &ArrayOf{Of: &Named{Name: "Abc"}}, *typ)

    // parses an array type with dot ✅
    typ, err = typeParser.ParseString("", "Abc.def[]")
    require.NoError(t, err)
    require.Equal(t, &ArrayOf{Of: &Named{Name: "Abc.def"}}, *typ)

    // parses non array type without dot ✅
    typ, err = typeParser.ParseString("", "Abc")
    require.NoError(t, err)
    require.Equal(t, &Named{Name: "Abc"}, *typ)

    // parses non array type with dot
    //   ❌  unexpected token "<EOF>" (expected "[" "]")
    //   👉  why hasnt it fallback on "named" parser, as the array did not match ?
    //    (like it did on the test above)
    typ, err = typeParser.ParseString("", "Abc.def")
    require.NoError(t, err)
    require.Equal(t, &Named{Name: "Abc.def"}, *typ)
}

Any idea ?

Thanks

alecthomas commented 7 months ago

I can't really parse this as-is. Can you paste a link to a failing example on https://play.golang.org that illustrates the issue?

oguimbal commented 7 months ago

sure, here it is https://go.dev/play/p/tais4cSPVOz

oguimbal commented 7 months ago

Hi @alecthomas, any way I can help ? If you point me in the right direction I might try a PR

alecthomas commented 7 months ago

You need to configure Participle to use enough lookahead that it will be able to backtrack if a branch fails. In this case participle.UseLookahead(4) seems to work.

oguimbal commented 7 months ago

Oh thanks !