F.A.Q.

Parametry

Reguła może przyjmować parametry a także zwracać wartość (lub wartości). Składnia przedstawiona jest poniżej:

nazwa [parametry_wejsciowe, ...] returns [parametry_wyjściowe, ...] : ... ;

Aby do tak zadeklaroweanych parametrów formalnych się odwołać używamy notacji analogicznej jak dla etykiet - poprzedzając nazwę parametru symbolem $
Poniżej praktyczny przykład:

grammar Par;

r1	:	
	r2 
	| lol=r3[1]  { System.out.println("Zwrocilo sie " + $lol.wyn); }
	;

r2	:	
	'a' r3[2] { System.out.println("Zwrocilo sie " + $r3.wyn); }
	;

r3 [int par] returns [int wyn]	:	
	'b' { 
		if ($par==1) {
			System.out.println("b1");  
			$wyn=$par+2;
		} else { 
			 System.out.println("b2");
			$wyn=$par+3;
		}
	}
	;
	
NL	:	'\r'? '\n' {skip();};
WS	:	(' ' | '\t' | '\n' | '\r') { skip(); };

Wyjątki

ANTLR v 3.1+ obsługuje wyjątki, ale tylko "wybiórczo":

Nic nie stoi zatem na przeszkodzie, by utworzyć własną klasę dziedziczącą po RuntimeException i zrobic z niej użytek :)

grammar Wyjatko;

@members {
	class MyException extends RuntimeException {
		MyException() {
		System.out.println("Ajajaj!");	
		}
	}
}

r1 	:	 r2 
	; /* tu jest średnik należący do reguły r1! */ 
catch [MyException foo] { System.out.println("Catch!"); } /* to też odnosi się do reguły r1!*/ 

r2	 	:
	'a'
	 | 'b' { 
	 	if (1==1) throw new MyException();
	 }
	 ;

Odzyskiwanie kontroli po błędzie

Z błędami należy sobie radzić możliwie selektywnie. Gdy z szeregu wyrażeń jedno jest błędne (np. dzielenie przez zero), następujące po nim wyrażenia powinny zostać prawidłowo przetworzone. W tym celu czasem trzeba w sensowny sposób pozbyć się fragmentu wejścia. Możemy do tego użyć dwóch funkcji: consumeUntil(input, dokąd) oraz input.consume().

Przykładowe konstrukcje zamieszczone są poniżej.

// pozbywamy się całego wejścia do najbliższego leksemu NL oraz leksemu NL
	consumeUntil(input, NL); 
	input.consume();

// pozbywamy się wejścia do najbliższego leksemu PLUS lub MINUS wraz z tym leksemem
	BitSet leksemy = new BitSet();
	leksemy.add(PLUS);
	leksemy.add(MINUS);
	consumeUntil(input, leksemy); 
	input.consume();

Zakresy

Poniżej przedstawiony jest (niepełny) przykład zastosowania bloków i zakresów widoczności.

grammar Skopki;

@header {
        import java.util.HashMap;
}

@members {
        Integer getSymbol (String name) {
                for (int lv=$r2.size()-1; lv >=0; lv--) {
                        if ($r2[lv]::hm.containsKey(name))
                                return $r2[lv]::hm.get(name);
                }
/*              Obsluga wyjatkow prezentowana byla w poprzednim odcinku
                throw new NotFoundException(name);   */
                return -1;
        }

        Integer putSymbol (String name, Integer value) {
                for (int lv=$r2.size()-1; lv >=0; lv--) {
                        if ($r2[lv]::hm.containsKey(name)) {
                                $r2[lv]::hm.put(name, value);
                                return value;
                        }
                }
//              throw new NotFoundException(name);   
                return -1;
        }

        void declareSymbol (String name) {
                if(!$r2::hm.containsKey(name))
                        $r2::hm.put(name, -1);
/*              else
                        throw new RedeclaredException(name);   */
        }
}

r1      :
        r2+;

r2
scope { HashMap<String, Integer> hm; }
@init { $r2::hm=new HashMap<String, Integer>(); }
        :
        '[' { System.out.println("Entering new scope..."); }  
        r3+ 
        ']' { System.out.println("Leaving scope..."); };

r3      :
        r2
        | SLOWO {
                System.out.println(getSymbol($SLOWO.text));
        }
        | DEC SLOWO { declareSymbol($SLOWO.text);}
        | PUT SLOWO {putSymbol($SLOWO.text, $r2::hm.size());};


DEC     :       'Dec';
PUT     :       '>';
SLOWO   :       ('a'..'z')+;
NL      :       '\r'? '\n' { $channel=HIDDEN; };
WS      :       (' ' | '\t' ) { $channel=HIDDEN; };
//WS    :       (' ' | '\t' ) { skip(); };