PEG error "expression cannot fail; following choices cannot be reached"

56 views Asked by At

I tried to group together two cases to reduce code duplication in the grammar:

From

string = _{("'" ~ (string_value) ~ "'") | ("\"" ~ (string_value) ~ "\"") | ("\"" ~ (string_value_escape_1) ~ "\"") | ("'" ~ (string_value_escape_2) ~ "'")}
    string_escape = @{"\\"}
    string_value = @{(!("\""|"\\"|"'") ~ ANY)*}
    string_value_escape_1 = @{((!("\""|"\\") ~ ANY)+ | string_escape ~ ("\"" | "\\"))*}
    string_value_escape_2 = @{((!("'"|"\\") ~ ANY)+ | string_escape ~ ("'" | "\\"))*}

to

string = _{("'" ~ (string_value|string_value_escape_2) ~ "'") | ("\"" ~ (string_value|string_value_escape_1) ~ "\"")}
    string_escape = @{"\\"}
    string_value = @{(!("\""|"\\"|"'") ~ ANY)*}
    string_value_escape_1 = @{((!("\""|"\\") ~ ANY)+ | string_escape ~ ("\"" | "\\"))*}
    string_value_escape_2 = @{((!("'"|"\\") ~ ANY)+ | string_escape ~ ("'" | "\\"))*}

But that caused a build error in what I was sure is a simple grouping:

   = help: message: grammar error
           
            --> 3:20
             |
           3 | string = _{("'" ~ (string_value|string_value_escape_2) ~ "'") | ("\"" ~ (string_value|string_value_escape_1) ~ "\"")}
             |                    ^----------^
             |
             = expression cannot fail; following choices cannot be reached
           
            --> 3:74
             |
           3 | string = _{("'" ~ (string_value|string_value_escape_2) ~ "'") | ("\"" ~ (string_value|string_value_escape_1) ~ "\"")}
             |                                                                          ^----------^
             |
             = expression cannot fail; following choices cannot be reached
1

There are 1 answers

2
rici On BEST ANSWER

string_value can potentially match the empty string (since it's an arbitrary repetition using the Kleene star *). So it can't fail, as the error message says, because no matter where you are in the input, there's always an empty string in front of you.

Thus, (string_value|string_value_escape_2) will never match string_value_escape_2, because that won't be tried until string_value fails.