2015-07-06 3 views
3

Я пытаюсь создать простой препроцессор в ANTLR. Моя грамматика выглядит следующим образом:Простой препроцессор ANTLR

grammar simple_preprocessor; 

ifdef_statement : POUND_IFDEF IDENTIFIER ; 
else_statement : POUND_ELSE ; 
endif_statement : POUND_ENDIF ; 

preprocessor_statement : 
    ifdef_statement 
     code_block 
    else_statement 
     code_block 
    endif_statement 
    ; 

code_file : (preprocessor_statement | code_block)+ EOF ; 

code_block : TEXT ; 

POUND_IFDEF : '#IFDEF'; 
POUND_ELSE : '#ELSE'; 
POUND_ENDIF : '#ENDIF'; 

IDENTIFIER : ID_START ID_CONTINUE* ; 

TEXT : ~[\u000C]+ ; 

fragment ID_START : '_' | [A-Z] | [a-z] ; 
fragment ID_CONTINUE : ID_START | [0-9] ; 

WS : [ \t\r\n\u000C]+ -> channel(HIDDEN) ; 

Затем я анализирую следующее с помощью code_file() правило:

#IFDEF one 
    print "1" 
#ELSE 
    print "2" 
#ENDIF 

Строка дерева выглядит следующим образом:

(code_file (code_block \n#IFDEF one\n print "1"\n#ELSE\n print "2"\n#ENDIF\n) <EOF>) 

Не то, что я хочу , поскольку токены препроцессора обрабатываются как текст и соответствуют правилу code_block.

Я прочитал «Остров в потоке» главы в книге ANTLR, и пример XML имеет смысл, но она опирается на TEXT, не содержащее два специальных символов:

TEXT : ~[<&]+ ; 

Если я действительно должен, Я полагаю, я мог бы исключить символ #:

TEXT : ~[#]+ ; 

Но я надеюсь, что есть лучший способ сказать ANTLR, чтобы исключить мои препроцессора маркеры, чтобы он мог отличить их от общего кода.

Спасибо за любую помощь.

ответ

2

Используйте лексический режим для разделения директив препроцессора из обычного текстового определения вашей базовой грамматики. Используйте \n# и следующий \n как ваш защитный режим.

PStart : '\n#' -> channel(HIDDEN), pushMode(PreProc) ; 

mode PreProc ; 

PIFDEF : 'IFDEF' PTEXT* ; 
PELSE : 'ELSE' ; 
PENDIF : 'ENDIF' ; 
PTEXT : [a-zA-Z0-9_-]+ ; 
PEOL : [\r\n]+  -> channel(HIDDEN), popMode ; 
PWS : [ \t]+  -> channel(HIDDEN) ; 
// maybe PCOMMENT ? 

Update - закрепить полный текст директив на отдельные лексемы:

PIFDEF : 'IFDEF' PTEXT* PEOL -> popMode ; 
PELSE : 'ELSE' PEOL -> popMode ; 
PENDIF : 'ENDIF' PEOL -> popMode ; 

PTEXT : [ \ta-zA-Z0-9_-]+ ; 
PEOL : [\r\n] ; 

Это обычно не направление вы хотите идти - как правило, вы хотите иметь большее разложение, а не Меньше. Например, это может быть лучше при производстве видимых EOL.

mode PreProc ; 

PIFDEF : 'IFDEF' ; 
PELSE : 'ELSE' ; 
PENDIF : 'ENDIF' ; 
PTEXT : [a-zA-Z0-9_-]+ ; 
PEOL : '\r'? '\n' -> popMode ; 
PWS : [ \t]+  -> channel(HIDDEN) ; 
PCMT : '//' ~[\r\n]* -> channel(HIDDEN) ; 

Таким образом, команда маркеры препроцессора дискретны и последовательность из одного или несколько PTEXTs содержит только идентификатор препроцессора. Испускание PEOLs представляется излишним, но не обязательно неправильным. Parser правила для демонстрации:

preproc : ifdef | else | endif ; 
ifdef : PIFDEF PTEXT+ PEOL ; // the rules are unambiguous 
else : PELSE PEOL   ; // even without matching the PEOLs 
endif : PENDIF PEOL   ; 
+0

Спасибо, режимы выглядят как правильный подход. Я пробовал это вчера, но он не работал так, как мне было нужно - скорее всего, я делал неправильно. Спасибо за отправку примера - это будет полезно! – RedTailedHawk

+0

Hi @GRose, ваше определение PTEXT не включает \ r \ n, но что, если вам нужны те внутри текста, содержащиеся в токенах препроцессора? – RedTailedHawk

+0

Спасибо @GRose, я ценю обновление! – RedTailedHawk

Смежные вопросы