2012-06-29 37 views
5

Mam konwerter bbcode -> html, który reaguje na zdarzenie change w obszarze tekstowym. Obecnie odbywa się to za pomocą serii wyrażeń regularnych i istnieje wiele patologicznych przypadków. Zawsze chciałem wyostrzyć ołówek w tej gramatyce, ale nie chciałem zagłębiać się w golenie. Ale ... niedawno dowiedziałem się o pegjs, co wydaje się całkiem kompletną implementacją generacji parserów PEG. Mam większość gramatyki określonej, ale teraz zastanawiam się, czy jest to odpowiednie użycie pełnowymiarowego parsera.Korzystanie z PEG Parser do BBCode Parsing: pegjs lub ... co?

Moje konkretne pytania:

  1. Ponieważ mój wniosek opiera się na tłumaczenia, co mogę do HTML i pozostawiając resztę w postaci czystego tekstu, czy wdrożenie BBcode użyciu parser, które mogą nie działać na błąd składni ma sens ? Na przykład: [url=/foo/bar]click me![/url] z pewnością odniosą sukces po wprowadzeniu nawiasu zamykającego na znaczniku zamykającym. Ale co zobaczy użytkownik w międzyczasie? W przypadku wyrażenia regularnego mogę po prostu zignorować niezgodne elementy i traktować je jako zwykły tekst do celów podglądu. Z formalną gramatyką nie wiem, czy jest to możliwe, ponieważ polegam na tworzeniu kodu HTML z drzewa parsowania, a co nie, parsowanie jest ... co?

  2. Nie jestem pewien, gdzie należy dokonać transformacji. W formalnym parserze opartym na lex/yacc będę miał pliki nagłówkowe i symbole, które oznaczają typ węzła. W pegjs otrzymuję tablice zagnieżdżone z tekstem węzła. Mogę wyemitować przetłumaczony kod jako działanie parsera generowanego przez pegjs, ale wydaje się, że jest to zapach kodu łączący parser i emiter. Jednakże, jeśli zadzwonię PEG.parse.parse(), wrócę mniej więcej tak:

[ 
     [ 
      "[", 
      "img", 
      "", 
      [ 
      "/", 
      "f", 
      "o", 
      "o", 
      "/", 
      "b", 
      "a", 
      "r" 
      ], 
      "", 
      "]" 
     ], 
     [ 
      "[/", 
      "img", 
      "]" 
     ] 
    ]

podane gramatyki jak:

document 
    = (open_tag/close_tag/new_line/text)* 

open_tag 
    = ("[" tag_name "="? tag_data? tag_attributes? "]") 


close_tag 
    = ("[/" tag_name "]") 

text 
    = non_tag+ 

non_tag 
    = [\n\[\]] 

new_line 
    = ("\r\n"/"\n") 

mam abbreviating gramatykę, oczywiście, ale wpadnij na pomysł. Tak więc, jeśli zauważysz, nie ma informacji kontekstowych w tablicy tablic, która mówi mi, jaki rodzaj węzła mam i mam zamiar zrobić porównania ciągów ponownie, nawet jeśli parser już to zrobił. Oczekuję, że możliwe jest zdefiniowanie wywołań zwrotnych i użycie akcji w celu ich uruchomienia podczas analizy, ale w Internecie jest niewiele informacji na temat tego, jak można to zrobić.

Czy szczerzę złe drzewo? Czy powinienem wrócić do skanowania regex i zapomnieć o parsowaniu?

Dzięki

+0

Steve, twoje pytanie jest bardzo interesujące (+1), po prostu chcę zrobić to samo w rozszerzeniu: parsowanie BBCode w textarea (niestety jest to format, którego wciąż używa forum) i tworzenie "live" "podgląd z wpisanego tekstu za pomocą PEG.js lub cokolwiek innego oprócz wyrażeń regularnych. Czy udało Ci się stworzyć gramatykę dla parsera BBCode? Czy nie możesz podzielić się swoim rozwiązaniem za pośrednictwem GitHub lub cokolwiek innego? To bardzo mi pomogło. Bardzo dziękuję z góry! – Sk8erPeter

+0

Użyłem [parsera bbcode patorjk] (https://github.com/patorjk/Extendible-BBCode-Parser). Działa świetnie i można go dostosować do własnych potrzeb, jeśli masz specjalne tagi. –

+0

Dzięki, ja już widziałem tę bibliotekę, ale używa wyrażeń regularnych, których chciałem uniknąć, ponieważ teoretycznie, parsowanie BBCode za pomocą wyrażeń regularnych nie może być wykonane bez błędów ([»» link] (http: // kore- nordmann.de/blog/do_NOT_parse_using_regexp.html)) w niektórych przypadkach, np. kiedy zagnieżdżam je w sobie nawzajem itd. Dlatego chciałem to zrobić za pomocą analizowania formalizmu ekspresji gramatycznej. Czy nie próbowałeś ulepszyć gramatyki, którą zacząłeś? :) Nie możesz podzielić się z tym podstawą? :) – Sk8erPeter

Odpowiedz

2

Jeśli chodzi o pierwsze pytanie mam tosay że podgląd na żywo będzie trudne. Problemy, które wskazałeś w związku z tym, że analizator składni nie rozumie, że dane wejściowe to "praca w toku", są poprawne. Peg.js informuje, w którym momencie jest błąd, więc może mógłbyś wziąć te informacje i wrócić kilka słów i ponownie przeanalizować lub jeśli brakuje tagu końcowego, spróbuj dodać go na końcu.

Druga część twojego pytania jest łatwiejsza, ale twoja gramatyka nie będzie tak ładnie wyglądać później. W zasadzie to, co zrobić, to umieścić wywołania zwrotne na każdej reguły, tak na przykład

text 
    = text:non_tag+ { 
    // we captured the text in an array and can manipulate it now 
    return text.join(""); 
    } 

W tej chwili trzeba napisać te wywołania zwrotne inline w gramatyce. W tej chwili robię wiele z tych rzeczy w pracy, więc mogę poprosić peg.js o wycofanie, żeby to naprawić. Ale nie jestem pewien, kiedy znajdę czas, aby to zrobić.

1

Spróbuj czegoś takiego jak ta zasada zastępowania. Jesteś na dobrej drodze; po prostu musisz powiedzieć, aby zebrać wyniki.

tekst = wynik: non_tag + {return result.join ("'); }

3

Pierwsze pytanie (gramatyka dla niekompletnych tekstów):

Możesz dodać

incomplete_tag = ("[" tag_name "="? tag_data? tag_attributes?) 
//       the closing bracket is omitted ---^ 

poopen_tag i zmienić document obejmuje niepełny znacznik na końcu. Sztuczka polega na tym, że udostępniasz analizator składni wszystkie potrzebne produkcje do analizy, ale ważne są one najpierw. Możesz wtedy zignorować incomplete_tag podczas podglądu na żywo.

Drugie pytanie (jak obejmować działania):

piszesz tak zwanych działania po wyrażeniach. Akcja to kod JavaScript zawarty w nawiasach klamrowych i jest dozwolony po wyrażeniu pegjs, i. mi. także w trakcie produkcji!

W praktyce działania takie jak { return result.join("") } są prawie zawsze konieczne, ponieważ pegjs dzieli się na pojedyncze znaki. Można również zwrócić skomplikowane tablice zagnieżdżone. Dlatego zazwyczaj piszę funkcje pomocnicze w inicjalizatorze pegjs na czele gramatyki, aby zachować małe akcje. Jeśli wybierzesz nazwy funkcji uważnie, akcja jest samodzielna.

Aby zobaczyć, zobacz PEG for Python style indentation. Zastrzeżenie: to jest moja odpowiedź.