Когда вы используете отладчик грамматики, он позволяет вам точно увидеть, как механизм разбирает строку - сбои являются нормальными и ожидаемыми. Рассматривается, например, совпадение a+b*
со строкой aab
. Вы должны получить два совпадения для «a», после чего произойдет сбой (потому что b
это не так a
), но тогда он будет повторен b
и успешно совпадет.
Это может быть легче увидеть, если вы чередуетесь с ||
(что обеспечивает порядок). Если у тебя есть
token TOP { I have a <fruit> }
token fruit { apple || orange || kiwi }
и вы анализируете предложение «У меня есть киви», вы увидите, что сначала оно совпадает с «у меня есть», затем два сбоя с «яблоком» и «апельсином», и, наконец, с «киви».
Теперь давайте посмотрим на ваш случай:
TOP # Trying to match top (need >1 match of score)
| score # Trying to match score (need >1 match of lc/uc)
| | lc # Trying to match lc
| | * MATCH "a" # lc had a successful match! ("a")
| * MATCH "a " # and as a result so did score! ("a ")
| score # Trying to match score again (because <score>+)
| | lc # Trying to match lc
| | * MATCH "b" # lc had a successful match! ("b")
| * MATCH "b " # and as a result so did score! ("b ")
…………… # …so forth and so on until…
| score # Trying to match score again (because <score>+)
| | uc # Trying to match uc
| | * MATCH "G" # uc had a successful match! ("G")
| * MATCH "G\n" # and as a result, so did score! ("G\n")
| score # Trying to match *score* again (because <score>+)
| * FAIL # failed to match score, because no lc/uc.
|
| # <-------------- At this point, the question is, did TOP match?
| # Remember, TOP is <score>+, so we match TOP if there
| # was at least one <score> token that matched, there was so...
|
* MATCH "a b c d e f g\nA B C D E F G\n" # this is the TOP match
Ошибка здесь нормальная: в какой-то момент у нас закончатся <score>
токены, поэтому ошибка неизбежна. Когда это происходит, механизм грамматики может перейти к тому, что будет после <score>+
вашей грамматики. Поскольку ничего нет, этот сбой на самом деле приводит к совпадению всей строки (потому что TOP
совпадает с неявным /^…$/
).
Кроме того, вы можете рассмотреть возможность перезаписи вашей грамматики с помощью правила, которое автоматически вставляет <.ws> * (если не важно, чтобы это был только один пробел):
grammar test {
rule TOP { <score>+ }
token score {
[
| <uc>
| <lc>
]+
}
token uc { <[A..G]> }
token lc { <[a..g]> }
}
Кроме того, IME, вы можете захотеть добавить протокен для uc / lc, потому что когда он у вас есть, [ <foo> | <bar> ]
вы всегда будете иметь неопределенный один из них, что может сделать его обработку в классе действий немного раздражающей. Вы можете попробовать:
grammar test {
rule TOP { <score> + }
token score { <letter> + }
proto token letter { * }
token letter:uc { <[A..G]> }
token letter:lc { <[a..g]> }
}
$<letter>
всегда будет определяться таким образом.