我正在尝试编写一个词法分析器来进行预处理,它可以处理多行#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]
此外,词法分析者没有加入这两行。如何解决这些错误?
答案 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
如果您使用的skip
版WS
,则会pqr+abc
,longreplacementvalue
。function 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";
}
}
。我将这一点留给你的精明。