【问题】
目标是:
希望实现,对于包含宏依赖的内容,也可以正确替换。
即:
#define A B #define B C A |
可以替换为C,而不是B。
其中的一种变体是,对于:
#define PV_UPPER_RANGE_VALUE position_upper_range_value #define GET_DEV_VAR_VALUE(a,b) get_dev_var_value(a,0,b) GET_DEV_VAR_VALUE(" Upper travel range: \n ", PV_UPPER_RANGE_VALUE); |
希望可以替换为:
| get_dev_var_value(" Upper travel range: \n ", 0, position_upper_range_value); |
但是现在只能实现替换为:
| get_dev_var_value(" Upper travel range: \n ",0, PV_UPPER_RANGE_VALUE); |
目前语法代码为:
grammar preprocess;
//lexer grammar preprocess;
options{
language=Java;
output = AST;
}
......
COMMENT
: ('//' ~('\n'|'\r')* '\r'? '\n') {skip();}
| ('/*' ( options {greedy=false;} : . )* '*/') {skip();}
;
// and lexer rule
INCLUDE : '#include' (WS)? f=STRING
{
String name = f.getText();
name = name.substring(1,name.length()-1);
try {
// save current lexer's state
SaveStruct ss = new SaveStruct(input);
//SaveStruct ss = new SaveStruct(input, globalDefineMap, globalDefineArgsMap);
includes.push(ss);
// switch on new input stream
setCharStream(new ANTLRFileStream(name));
reset();
} catch(Exception fnf) { throw new Error("Cannot open file " + name); }
};
/*
fragment
NON_CR_LF : ~('\r'|'\n');
fragment
TAB_SPACE
: (' ' | '\t');
*/
//DIRECTIVE : ('#define' WS* defineMacro=ID WS* defineText=STRING)
//DIRECTIVE : ('#define' WS* defineMacro=ID WS* defineText=( NON_CR_LF+ | (NON_CR_LF* (TAB_SPACE+ '\\' '\r'? '\n' NON_CR_LF+)*) ) )
fragment
//MACRO_TEXT : ( (('\\'){skip();System.out.println("skip line tail back slash");} '\r'? '\n')
//MACRO_TEXT : ( ('\\'{$channel=HIDDEN;System.out.println("set back slash to hidden");} '\r'? '\n')
//MACRO_TEXT : ( (('\\'){setText("");System.out.println("set back slash to empty");} '\r'? '\n')
MACRO_TEXT : (('\\' '\r'? '\n') | (~('\r'|'\n')))*;
//MACRO_TEXT : (('\\' '\r'? '\n') | (~('\n')))*;
//MACRO_TEXT : (('\\' '\n') | (~('\n')))*;
//MACRO_TEXT : (('\\' '\n') | (~('\n' | '\r')))*;
//MACRO_TEXT : ( ('\\' '\r'? '\n') | (~('\r'|'\n')))* -> ( ('\r'? '\n') | (~('\r'|'\n')))*;
//MACRO_TEXT : (('\\'{setText("");} '\r'? '\n') | (~('\r'|'\n')))*;
/*
MACRO_TEXT : ((('\\' '\r'? '\n') | (~('\r'|'\n')))*)
{
String origMultiLineStr = getText();
String newMultiLineStr = origMultiLineStr.replace("\\", "");
setText(newMultiLineStr);
};
*/
//MACRO_TEXT : ( (('\\' '\r'? '\n')=>('\r' '\n')) | (~('\r'|'\n')))*;
DIRECTIVE
@init{
List args = new ArrayList();
boolean condition = true;
String arg0Text = "";
String arg1Text = "";
String definedContent = "";
String defineId = "";
}
@after{
//SETTEXT(GETTEXT()->substring(GETTEXT(),1,GETTEXT()->len-1))
//String processedTokenStr = GETTEXT();
//String processedTokenStr = state.text;
//String processedTokenStr = getText();
//String processedTokenStr = this.getText();
//System.out.println("after process, whole token string is" + processedTokenStr);
}
: ('#define' WS* defineMacro=RAW_IDENTIFIER_CONTAIN_POINT
{
args.add(""); // first element will hold the macro text
}
(
( '(' // get arguments if you find them (no spaces before left paren)
(WS)? (defineArg0=RAW_IDENTIFIER_CONTAIN_POINT (WS)? {arg0Text = defineArg0.getText(); args.add(arg0Text);})?
( ',' (WS)? defineArg1=RAW_IDENTIFIER_CONTAIN_POINT (WS)? {arg1Text = defineArg1.getText(); args.add(arg1Text);} )*
')'
| ' '|'\t'|'\f'
)
( options{greedy=true;}: ' '|'\t'|'\f' )*
// store the text verbatim - tokenize when called
macroText=MACRO_TEXT
{
definedContent = macroText.getText();
definedContent = definedContent.replace("\\", ""); // remove mutile line define last's '\'
args.set(0, definedContent);
}
)? '\r'? '\n'
{
defineId = defineMacro.getText();
globalDefineMap.put(defineId, args );
skip();
// //process the define content, to check whether it contain the previous define
// //if yes, then process it
// // save current lexer's state
// SaveStruct ss = new SaveStruct(input);
// //SaveStruct ss = new SaveStruct(input, globalDefineMap, globalDefineArgsMap);
// includes.push(ss);
// // switch on new input stream
// setCharStream(new ANTLRStringStream(definedContent));
// reset();
// // isReplacingDefineContent = true;
}
)
/*
{
//process the define content, to check whether it contain the previous define
//if yes, then process it
// save current lexer's state
SaveStruct ss = new SaveStruct(input);
includes.push(ss);
// switch on new input stream
setCharStream(new ANTLRStringStream(definedContent));
reset();
//after replacement
//update the define map -> replace to the replaced text
String processedDefineContent = macroText.getText();
args.set(0, processedDefineContent);
globalDefineMap.put(defineId, args );
skip();
}
*/;
IDENTIFIER
@init{
List define = new ArrayList();
List foundArgs = new ArrayList();
String callArg0Text = "";
String callArg1Text = "";
}
@after{
//String curCallParaText = getText();
// if(foundArgs.size() == 0)
// {
// //remove () if no para
// setText("");
// }
} :
identifier=RAW_IDENTIFIER_CONTAIN_POINT
{
String IdText = (String)identifier.getText();
// see if this is a macro argument
define = (List)globalDefineArgsMap.get(IdText);
// while(define != null)
// {
// //if define not null, then find recursively to get para's real value
// String firstParaValue = (String)define.get(0);
// if(globalDefineArgsMap.containsKey(firstParaValue))
// {
// define = (List)globalDefineArgsMap.get(firstParaValue);
// }
// else
// {
// break;
// }
// }
if (define==null) {
// see if this is a macro call
define = (List)globalDefineMap.get(IdText);
//System.out.println("normal define call=" + IdText);
}
else
{
//is define args replacement
//isReplacingDefineContent = true;
//System.out.println("normal define args call=" + (String)define.get(0));
}
}
( {(define!=null) && (define.size()>1)}?=> (WS|COMMENT)?
// take in arguments if macro call requires them
'('
callArg0=EXPR
{
callArg0Text = callArg0.getText();
//foundArgs.add(callArg0Text);
//maybe whitespace, so need trim here
if(!callArg0Text.isEmpty() && !callArg0Text.trim().isEmpty())
{
foundArgs.add(callArg0Text);
}
}
( COMMA callArg1=EXPR
{
callArg1Text = callArg1.getText();
//foundArgs.add(callArg1Text);
//maybe whitespace, so need trim here
if(!callArg1Text.isEmpty() && !callArg1Text.trim().isEmpty())
{
foundArgs.add(callArg1Text);
}
}
)*
{ foundArgs.size()==define.size()-1 }? // better have right amount
//must add WS support here, otherwise not support none-para but use whitespace within (), such as: ( )
')'
| {(define!=null) && (define.size()==1)}?=> '(' WS* ')'
| {!((define!=null) && (define.size()>1))}?=>
)
{
if (define!=null) {
String curText = getText();
String defineText = (String)define.get(0);
if (define.size()==1) {
//only have one value in list
//-> the defineText is the define para content, or is no-para define replacement
//-> just need replace directly
setText(defineText);
} else {
//add new dict pair: (para, call value)
for (int i=0;i<foundArgs.size();++i) {
// treat macro arguments similar to local defines
List arg = new ArrayList();
arg.add((String)foundArgs.get(i));
globalDefineArgsMap.put( (String)define.get(1+i), arg );
}
// save current lexer's state
SaveStruct ss = new SaveStruct(input);
//SaveStruct ss = new SaveStruct(input, globalDefineMap, globalDefineArgsMap);
includes.push(ss);
// switch on new input stream
setCharStream(new ANTLRStringStream(defineText));
reset();
}
}
};
fragment RAW_IDENTIFIER_CONTAIN_POINT : RAW_IDENTIFIER (POINT RAW_IDENTIFIER)?;
fragment RAW_IDENTIFIER : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')* ;
......
fragment EXPR // allow just about anything without being ambiguous
: (WS)? (NUMBER|IDENTIFIER)?
(
( LEFT EXPR ( COMMA EXPR )* RIGHT
| STRING
| OPERATOR // quotes, COMMA, LEFT, and RIGHT not in here
)
EXPR
)?
;
......生成lexer代码为:
// $ANTLR start "IDENTIFIER"
public final void mIDENTIFIER() throws RecognitionException {
try {
int _type = IDENTIFIER;
int _channel = DEFAULT_TOKEN_CHANNEL;
CommonToken identifier=null;
CommonToken callArg0=null;
CommonToken callArg1=null;
List define = new ArrayList();
List foundArgs = new ArrayList();
String callArg0Text = "";
String callArg1Text = "";
// D:\\DevRoot\\IndustrialMobileAutomation\\HandheldDataSetter\\ANTLR\\projects\\v1.5\\HartEddlParser_local_TFS\\preprocess\\remove_comment\\preprocess.g:231:3: (identifier= RAW_IDENTIFIER_CONTAIN_POINT ({...}? => ( WS | COMMENT )? '(' callArg0= EXPR ( COMMA callArg1= EXPR )* {...}? ')' |{...}? => '(' ( WS )* ')' |{...}? =>) )
// D:\\DevRoot\\IndustrialMobileAutomation\\HandheldDataSetter\\ANTLR\\projects\\v1.5\\HartEddlParser_local_TFS\\preprocess\\remove_comment\\preprocess.g:232:5: identifier= RAW_IDENTIFIER_CONTAIN_POINT ({...}? => ( WS | COMMENT )? '(' callArg0= EXPR ( COMMA callArg1= EXPR )* {...}? ')' |{...}? => '(' ( WS )* ')' |{...}? =>)
{
int identifierStart467 = getCharIndex();
int identifierStartLine467 = getLine();
int identifierStartCharPos467 = getCharPositionInLine();
mRAW_IDENTIFIER_CONTAIN_POINT();
identifier = new CommonToken(input, Token.INVALID_TOKEN_TYPE, Token.DEFAULT_CHANNEL, identifierStart467, getCharIndex()-1);
identifier.setLine(identifierStartLine467);
identifier.setCharPositionInLine(identifierStartCharPos467);
String IdText = (String)identifier.getText();
// see if this is a macro argument
define = (List)globalDefineArgsMap.get(IdText);
// while(define != null)
// {
// //if define not null, then find recursively to get para's real value
// String firstParaValue = (String)define.get(0);
// if(globalDefineArgsMap.containsKey(firstParaValue))
// {
// define = (List)globalDefineArgsMap.get(firstParaValue);
// }
// else
// {
// break;
// }
// }
if (define==null) {
// see if this is a macro call
define = (List)globalDefineMap.get(IdText);
//System.out.println("normal define call=" + IdText);
}
else
{
//is define args replacement
//isReplacingDefineContent = true;
//System.out.println("normal define args call=" + (String)define.get(0));
}
......
match('(');
int callArg0Start520 = getCharIndex();
int callArg0StartLine520 = getLine();
int callArg0StartCharPos520 = getCharPositionInLine();
mEXPR();
callArg0 = new CommonToken(input, Token.INVALID_TOKEN_TYPE, Token.DEFAULT_CHANNEL, callArg0Start520, getCharIndex()-1);
callArg0.setLine(callArg0StartLine520);
callArg0.setCharPositionInLine(callArg0StartCharPos520);
callArg0Text = callArg0.getText();
//foundArgs.add(callArg0Text);
//maybe whitespace, so need trim here
if(!callArg0Text.isEmpty() && !callArg0Text.trim().isEmpty())
{
foundArgs.add(callArg0Text);
}
然后经过调试发现,对于:
拿到了GET_DEV_VAR_VALUE中的PV_UPPER_RANGE_VALUE,已经去调用了对应的
mEXPR();
然后可以将PV_UPPER_RANGE_VALUE替换为想要的position_upper_range_value了,但是后面的:
callArg0Text = callArg0.getText();
依然得到的是PV_UPPER_RANGE_VALUE
即,对于:
// $ANTLR start "EXPR"
public final void mEXPR() throws RecognitionException {
try {
......
case 2 :
// D:\\DevRoot\\IndustrialMobileAutomation\\HandheldDataSetter\\ANTLR\\projects\\v1.5\\HartEddlParser_local_TFS\\preprocess\\remove_comment\\preprocess.g:345:21: IDENTIFIER
{
mIDENTIFIER();
}
break;
}中的mEXPR,其中调用的mIDENTIFIER();中的setText,根本就没生效。
无法返回被setText替换后的内容。
即问题转化为:
在递归嵌套调用的token中的setText,无法将替换后的值,传递给最上层,setText不工作,无效。
【解决过程】
1.参考:
ANTLR replace tokens in a recursive manner
说是将token改为rule,就可以了。
但是,我此处,不希望用到rule,还是希望用token本身。
2.待续。。。
【总结】