简单的 ANTLR 预处理器
Simple ANTLR Preprocessor
我正在尝试在 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书中的"Islands in the Stream"章节,XML的例子是有道理的,但是它依赖于不包含两个特定字符的TEXT:
TEXT : ~[<&]+ ;
如果我真的必须这样做,我想我可以排除 # 字符:
TEXT : ~[#]+ ;
但我希望有更好的方法告诉 ANTLR 排除我的预处理器标记,以便它可以将它们与通用代码区分开来。
感谢您的帮助。
使用词法模式将预处理器指令与基本语法的普通文本定义分开。使用 \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 ?
更新 - 将指令的全文合并为单个标记:
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) ;
这样,预处理命令标记是离散的,一个或多个 PTEXT 的序列仅包含预处理标识符。发射 PEOL 似乎是多余的,但不一定是错误的。要演示的解析器规则:
preproc : ifdef | else | endif ;
ifdef : PIFDEF PTEXT+ PEOL ; // the rules are unambiguous
else : PELSE PEOL ; // even without matching the PEOLs
endif : PENDIF PEOL ;
我正在尝试在 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书中的"Islands in the Stream"章节,XML的例子是有道理的,但是它依赖于不包含两个特定字符的TEXT:
TEXT : ~[<&]+ ;
如果我真的必须这样做,我想我可以排除 # 字符:
TEXT : ~[#]+ ;
但我希望有更好的方法告诉 ANTLR 排除我的预处理器标记,以便它可以将它们与通用代码区分开来。
感谢您的帮助。
使用词法模式将预处理器指令与基本语法的普通文本定义分开。使用 \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 ?
更新 - 将指令的全文合并为单个标记:
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) ;
这样,预处理命令标记是离散的,一个或多个 PTEXT 的序列仅包含预处理标识符。发射 PEOL 似乎是多余的,但不一定是错误的。要演示的解析器规则:
preproc : ifdef | else | endif ;
ifdef : PIFDEF PTEXT+ PEOL ; // the rules are unambiguous
else : PELSE PEOL ; // even without matching the PEOLs
endif : PENDIF PEOL ;