2012-02-22 4 views
5

Rozważmy przykład parsera jak ten:Kombinatory parsera: czy repsep umożliwia śledzenie wstecz?

object TestParser extends RegexParsers { 
    override protected val whiteSpace = """[ \t]*""".r 

    def eol = """(\r?\n)+""".r 
    def item = "[a-zA-Z][a-zA-Z0-9-]*".r 
    def list = "items:" ~> rep1sep(item,",") 
    def constraints = "exclude:" ~> item 

    def itemsDefinition = (rep1sep(list, eol) ~ repsep(constraints,eol)) 
} 

Gdy próbuję analizować to wejście (bez dwóch wierszy zawiera wykluczyć działa OK):

items: item1, item2, item3, item3, item4 
items: item2, item3, item3, item5, item4  
items: item4, item5, item6, item10  
items: item1, item2, item3 
exclude: item1 
exclude: item2 

mi się następujący błąd:

[5.5] failure: `items:' expected but `e' found 

     exclude: item1 

    ^

Problem jest oczywisty w tej linii:

def itemsDefinition = (rep1sep(list, eol) ~ repsep(constraints,eol)) 

Co jest powodem, dlaczego to nie działa. Czy ma to coś wspólnego z cofaniem? Jakie mam alternatywy, aby to działało?

+0

Jeśli ktoś ma propozycję lepszego tytułu pytania, proszę dać mi znać. Nie jestem pewien, czy to ma sens ... – PrimosK

Odpowiedz

5

Będziesz potrzebować EOL pomiędzy listami i swoimi ograniczeniami

(rep1sep(list, eol) <~ eol) ~ repsep(constraint,eol) 

ukończeniu odpowiedź:

gramatykę określić EOL jako separator pomiędzy listami, nie terminator. Przyjmuje dane wejściowe, gdzie pierwszy exclude pojawia się tuż po ostatnim item3 (z białą spacją, ale nie nową linią).

Po osiągnięciu przez parser niechcianego eol, szuka on items i zamiast tego znajduje excludes. Który daje wyświetlony komunikat o błędzie. Następnie parser rzeczywiście cofa się do poprzedniej nowej linii. Rozważa możliwość, że część list się tam zatrzymuje i szuka wykluczeń. Ale jeśli znajdzie zamiast tego eol. Tak inny możliwy komunikat o błędzie będzie "excludes expected, eol found", która w tym przypadku byłby bardziej pomocny

Kiedy istnieje możliwość wyboru w gramatyce, a nie oddział powiedzie, parser zwraca błąd z pozycji najdalszy, który jest normalnie właściwa strategia. Załóżmy, że gramatyka pozwala na "if" lub "for", a dane wejściowe to "if !!!". W przypadku gałęzi if błąd będzie podobny do "(" expected, "!" found. W gałęzi for wiadomość będzie miała postać "for expected, if found". Oczywiście komunikat z gałęzi if, który pojawia się na drugim tokenie, jest znacznie lepszy niż komunikat z gałęzi for, na pierwszym tokenie i nie ma znaczenia.

w kwestii separator/terminator, można rozważyć:

  • separator (; w Pascalu): repsep(item, separator)
  • Terminator (; w C): rep(item <~ terminator)
  • elastyczny: repsep(item, separator) <~ separator?

to ostatnie pozwoliłoby na pojedynczy separator po żadnych elementach. Jeśli jest to niepożądane, być może (rep1sep(item, separator) <~ separator?)?.

+0

Wow .. Świetnie! Ale dlaczego tak działa? – PrimosK

+0

Przypuszczam, że ponieważ "sep" są między "list" a nie po każdym powtórzeniu. Tak więc parser nie może "opuścić" pierwszego 'rep1sep' i oczekuje" listy "po każdym" eol ". – paradigmatic

+0

Świetna odpowiedź !! TY – PrimosK