lexing multiline使用antlr4定义语句

时间:2017-01-31 06:18:49

标签: antlr4

我正在尝试编写一个词法分析器来进行预处理,它可以处理多行#define语句。例如,以下输入,其中多行定义被后续空行破坏(但可能包含空格):

aa(bb);
#define XX pqr
#define YY pqr \
    +abc

class p(XX,YY,zz);
endclass

第一步是对输入流进行标记化,对于任何定义,该值将作为一个字符串标记获取。例如,对于YY,我试图将“pqr + abc”作为其字符串值。我写了以下词法分析器来进行标记:

DEF: '#define' -> pushMode(def_mode);
ID: Letter (Letter | DecDigit)* ;
COMMENT : '/*' .*? '*/' -> channel(HIDDEN) ;
LINE_COMMENT : '//' ~('\n'|'\r')* NL  -> channel(HIDDEN);
WS: ( ' ' |'\t' | NL )+     -> channel(HIDDEN) ;
SEMICOLN: ';' ;
COMMA: ',' ;
OB: '(' ;
CB: ')' ;
PLUS: '+' ;
fragment NL : '\r'? '\n' ;
fragment DecDigit: '0'..'9' ;
fragment Letter: 'A'..'Z' | 'a'..'z' | '_' ;
mode def_mode;
    STR2: '\r'? '\n' -> popMode;
    STR1: ~('\n'|'\r')* '\r'? '\n' ;

上面的词法分析器为#define行提供以下标记:

[@10,26:32='#define',<2>,6:0]
[@11,33:42=' YY pqr \\n',<13>,6:7]
[@12,43:51='    +abc\n',<13>,7:0]
[@13,52:52='\n',<12>,8:0]
[@14,53:57='class',<3>,9:0]

只有在#define行后面有一个“空”行时才能获得上述标记。如果该行中有一些空格,即它实际上不是空的,则不退出该模式。当行有空格时,这是标记:

[@10,26:32='#define',<2>,6:0]
[@11,33:42=' YY pqr \\n',<13>,6:7]
[@12,43:51='    +abc\n',<13>,7:0]
[@13,52:55='   \n',<13>,8:0]
[@14,56:74='class p(XX,YY,zz);\n',<13>,9:0]
[@15,75:83='endclass\n',<13>,10:0]
[@16,84:84='\n',<12>,11:0]
[@17,85:84='<EOF>',<-1>,12:0]

此外,词法分析者没有加入这两行。如何解决这些错误?

1 个答案:

答案 0 :(得分:0)

我不是专家(每年一个语法)而不是mode的粉丝,并且不喜欢在词法分析器中进行太多处理。解析器具有更多功能。看看这个:

grammar Question;

/* Parsing preprocessor #define */

program
    :   statement+
    ;

statement
    :   aClass
    |   function
    |   preprocessor
    ;

aClass
    :   'class'
        classDef // classBody
        'endclass'
    ;

classDef
    :   ID '(' list ')' ';'
    ;

function
    :   ID '(' list ')' ';'
    ;

preprocessor
    :   DEFINE ID replacement
        {System.out.println($DEFINE.text + " value=" + $ID.text + " -> replaced by " + $replacement.text);}
    ;

replacement
    :   expr+
    ;

expr
    :   ID
    |   ID '+' ID
    ;

list
    :   ID ( ',' ID )*
    ;

ID  :   LETTER ALPHAMERIC* ;
DEFINE
    :   '#' 'define' ;
COMMENT
    :   '/*' .*? '*/' -> channel(HIDDEN) ;
LINE_COMMENT
    :   '//' ~('\r' | '\n')* -> channel(HIDDEN) ;
WS  :   [ \t\r\n]+ -> channel(HIDDEN) ; // keep spaces in $<rule>.text
//WS  :     [ \t\r\n]+ -> skip ;
CONTINUATION
// if you want to keep the exact value including NL :
//  :   '\\' '\r'? '\n' -> channel(HIDDEN) ;
// to discard the continuation character :
    :   '\\' '\r'? '\n' -> skip ; // ignored as in Ruby, concatenates two lines

fragment LETTER     : [a-zA-Z_] ;
fragment DIGIT      : [0-9] ;
fragment ALPHAMERIC : LETTER | DIGIT ;

输入data.txt

/* function
        call */
aa(bb);
#define XX pqr
#define YY long replacement value
// multiline :
#define ZZ stu \
    +abc

class p(XX,YY,zz);
endclass
#define WW vwx \
    +def

// preceding line contains 10 spaces

$ hexdump -C data.txt 
...
000000b0  2b 64 65 66 0a 20 20 20  20 20 20 20 20 20 20 0a  |+def.          .|
000000c0  2f 2f 20 70 72 65 63 65  64 69 6e 67 20 6c 69 6e  |// preceding lin|
000000d0  65 20 63 6f 6e 74 61 69  6e 73 20 31 30 20 73 70  |e contains 10 sp|
000000e0  61 63 65 73                                       |aces|
000000e4

输出是:

$ grun Question program -tokens data.txt 
[@0,0:26='/* function\n        call */',<COMMENT>,channel=1,1:0]
[@1,27:27='\n',<WS>,channel=1,2:15]
[@2,28:29='aa',<ID>,3:0]
...
[@8,36:42='#define',<DEFINE>,4:0]
[@9,43:43=' ',<WS>,channel=1,4:7]
[@10,44:45='XX',<ID>,4:8]
[@11,46:46=' ',<WS>,channel=1,4:10]
[@12,47:49='pqr',<ID>,4:11]
[@13,50:50='\n',<WS>,channel=1,4:14]
...
#define value=XX -> replaced by pqr
#define value=YY -> replaced by long replacement value
#define value=ZZ -> replaced by stu     +abc
#define value=WW -> replaced by vwx     +def

如果您使用的skipWS,则会pqr+abclongreplacementvaluefunction toggle_visibility() { debugger; var l = document.getElementById('links'); var d = document.getElementById('project'); if (d.className == 'DispayButton') { debugger d.className = ""; l.className += "DispayButton"; } else { debugger l.className = ""; d.className += "DispayButton"; } } 。我将这一点留给你的精明。