2016-03-28 27 views
6

Używam JSON grammar from the antlr4 grammar repository do parsowania plików JSON dla wtyczki edytora. Działa, ale zgłasza nieprawidłowe znaki jeden po drugim. Poniższe wyniki snippet błędy 18 Lexer:Traktuj nieprawidłowe znaki jako jeden token w lexer ANTLR4

{ 
    sometext-without-quotes : 42 
} 

chcę gotować w dół do 1-2 traktując kolejne, nieprawidłowe znaki pojedynczego char tego samego typu jak jeden większy Nieprawidłowy token.

Na podobne pytanie, zwyczaj lexer sugerowano, że kleje „nieznane” elementy do większych tokenów: In antlr4 lexer, How to have a rule that catches all remaining "words" as Unknown token?

Zakładam, że ten omija zwykły raportowanie błędów lexer, które chciałbym unikać, jeśli to możliwe. Czy nie istnieje odpowiednie rozwiązanie dla tego raczej prostego zadania? Wygląda na to, że domyślnie działało w ANTLR3.

Odpowiedz

3

Odpowiedź zawiera link, który podałeś. Nie chcę, aby skopiować oryginalną odpowiedź całkowicie więc spróbuję i parafrazując nieco ...

In antlr4 lexer, How to have a rule that catches all remaining "words" as Unknown token?

Dodaj niewiadomych do lexer, który będzie pasował wielokrotności tych ...

unknowns : Unknown+ ; 
... 
Unknown : . ; 

Dokonano edycji tego posta, aby uwzględnić przypadek, w którym używano leksera i nie używano analizatora składni. Jeśli używasz parsera, nie musisz przesłonić metody nextToken, ponieważ błąd może być przetwarzany w parserze w znacznie czystszy sposób, np. unknowns jest tylko kolejnym typem znacznika, jeśli chodzi o lexer. Lexer przekazuje je do analizatora składni, który może wtedy obsługiwać błędy. Używając parsera normalnie rozpoznaję wszystkie tokeny jako indywidualne tokeny, a następnie w parserze emituję błędy tj. Grupuję je lub nie. Powodem tego jest to, że obsługa błędów odbywa się w jednym miejscu, tzn. Nie znajduje się w lexer i parserze. Ułatwia także pisanie i testowanie leksykonu, tzn. Musi rozpoznawać cały tekst i nigdy nie zawieść na żadnym z wejść utf8. Niektórzy ludzie prawdopodobnie robiliby to inaczej, ale to działało dla mnie z ręcznie napisanymi lexerami w C. Parser jest odpowiedzialny za ustalenie, co jest rzeczywiście poprawne i jak na nim popełnić błąd. Inną korzyścią jest to, że lekser jest dość ogólny i może być ponownie użyty.

Dla Lexer jedyne rozwiązanie ...

Sprawdź odpowiedź na link i spojrzeć na ten komentarz w odpowiedzi ...

... ale mam tylko lexer, bez parserami ...

państwa odpowiedź, że można zastąpić metodę nextToken i zostaje szczegółowo, w jaki sposób to zrobić

@Override 
public Token nextToken() {  

i ważnym elementem w kodzie jest to ...

Token next = super.nextToken(); 

if(next.getType() != Unknown) { 
    return next; 
} 

Kod, który przychodzi po to obsługuje przypadek, w którym można dopasować złe znaki.

+0

Miałem nadzieję na proste rozwiązanie (pro blem jest prosty), ale wygląda na to, że nie ma. Dziękuję za potwierdzenie. – msteiger

2

Co można zrobić, to użyć trybów lexer. W tym celu trzeba było podzielić gramatykę na parser i gramatykę leksykalną. Zacznijmy od gramatyki leksykalnej:

JSONLexer.G4

/** Taken from "The Definitive ANTLR 4 Reference" by Terence Parr */ 

// Derived from http://json.org 
lexer grammar JSONLexer; 

STRING 
    : '"' (ESC | ~ ["\\])* '"' 
    ; 


fragment ESC 
    : '\\' (["\\/bfnrt] | UNICODE) 
    ; 


fragment UNICODE 
    : 'u' HEX HEX HEX HEX 
    ; 


fragment HEX 
    : [0-9a-fA-F] 
    ; 


NUMBER 
    : '-'? INT '.' [0-9] + EXP? | '-'? INT EXP | '-'? INT 
    ; 


fragment INT 
    : '0' | [1-9] [0-9]* 
    ; 

// no leading zeros 

fragment EXP 
    : [Ee] [+\-]? INT 
    ; 

// \- since - means "range" inside [...] 

TRUE : 'true'; 
FALSE : 'false'; 
NULL : 'null'; 

LCURL : '{'; 
RCURL : '}'; 
COL : ':'; 
COMA : ','; 
LBRACK : '['; 
RBRACK : ']'; 

WS 
    : [ \t\n\r] + -> skip 
    ; 

NON_VALID_STRING : . ->pushMode(MODE_ERR); 

mode MODE_ERR; 
WS1 
    : [ \t\n\r] + -> skip 
    ; 
COL1 : ':' ->popMode; 
MY_ERR_TOKEN : ~[':']* ->type(NON_VALID_STRING); 

zasadzie dodałem pewne znaki użyte w części parsera (jak LCURL, COL, COMA etc) i wprowadził NON_VALID_STRING tokena, który jest w zasadzie pierwszy znak, że to nic, że już jest (powinien być) dopasowane. Po wykryciu tego tokena przełączam lexer na tryb MODE_ERR. W tym trybie wracam do trybu domyślnego po wykryciu : (może to być zmienione i może dopracowane, ale serwer tutaj służy :)) lub mówię, że wszystko inne to MY_ERR_TOKEN, do którego przypisuję typ tokena NON_VALID_STRING. Oto co mówi na ten ATNLRWorks kiedy biegnę zinterpretować opcję Lexer z wejściem: ANTLRWorks lexer interpret

Więc s jest NON_VALID_STRING typ i tak jest wszystko inne, aż :. Tak więc, ten sam typ, ale dwa różne tokeny. Jeśli chcesz, aby nie były tego samego typu, po prostu pomiń wywołanie type w gramatyce leksykonu.
Oto gramatyki parser teraz
JSONParser.g4

/** Taken from "The Definitive ANTLR 4 Reference" by Terence Parr */ 

// Derived from http://json.org 
parser grammar JSONParser; 

options { 
    tokenVocab=JSONLexer; 
} 

json 
    : object 
    | array 
    ; 

object 
    : LCURL pair (COMA pair)* RCURL 
    | LCURL RCURL 
    ; 

pair 
    : STRING COL value 
    ; 

array 
    : LBRACK value (COMA value)* RBRACK 
    | LBRACK RBRACK 
    ; 

value 
    : STRING 
    | NUMBER 
    | object 
    | array 
    | TRUE 
    | FALSE 
    | NULL 
    ; 

a jeśli uruchomić platformę testową (robię to z ANTLRworks) dostaniesz jeden błąd (patrz zrzut ekranu) antlworks parser


Możesz także gromadzić błędy leksona, przesłonić wygenerowaną klasę leksykanów, ale zrozumiałem w pytaniu, że to nie jest pożądane lub nie zrozumiałem tej części :)

+0

Tak, masz rację. Dziękuję za szczegółowe wyjaśnienie. Miałem nadzieję na proste rozwiązanie (problem jest prosty), ale wygląda na to, że nie ma. +1 – msteiger