【整理】分析cppLexer.g中的多参数的#define实现宏替换的逻辑过程

【背景】

之前折腾:

【记录】将antlr v2的C/C++的preprocess,即cpp.g,转换为antlr v3

期间,后来终于看懂原先的旧的cppLexer.g中,antlr v2版本的lexer,是如何实现,多参数的#define中,宏的替换的逻辑。

现总结如下:

【分析过程】

对于相关部分的代码:

    protected static Map defines = new Hashtable(); // holds the defines
    protected Map defineArgs = new Hashtable(); // holds the args for a macro call
    public void uponEOF() throws TokenStreamException, CharStreamException {
        try {
            selector.pop(); // return to old lexer/stream
            selector.retry();
        } catch (NoSuchElementException e) {
            // return a real EOF if nothing in stack
        }
    }
    ......
    
DIRECTIVE {
    List args = new ArrayList();
    boolean condition = true;
} : '#'
    ......
    | "define" WS defineMacro:RAW_IDENTIFIER
    {
        args.add(""); // first element will hold the macro text
    }
        (
            ( '(' // get arguments if you find them (no spaces before left paren)
                (WS)? defineArg0:RAW_IDENTIFIER (WS)? {args.add(defineArg0.getText());}
                ( COMMA (WS)? defineArg1:RAW_IDENTIFIER (WS)? {args.add(defineArg1.getText());} )*
              ')'
            | ' '|'\t'|'\f'
            )
            ( options{greedy=true;}: ' '|'\t'|'\f' )*
            // store the text verbatim - tokenize when called
            defineText:MACRO_TEXT {args.set(0,defineText.getText());}
        )? '\n' {newline();}
    { if (ifState==1) {
        defines.put( defineMacro.getText(), args );
        $setType(Token.SKIP);
    }}
    ......
    
IDENTIFIER options {testLiterals=true;} {
    List define = new ArrayList();
    List args = new ArrayList();
} :
    identifier:RAW_IDENTIFIER
    {
        // see if this is a macro argument
        define = (List)defineArgs.get(identifier.getText());
        if (_createToken && define==null) {
            // see if this is a macro call
            define = (List)defines.get(identifier.getText());
        }
    }
    ( { (define!=null) && (define.size()>1) }? (WS|COMMENT)?
        // take in arguments if macro call requires them
        '('
        callArg0:EXPR {args.add(callArg0.getText());}
        ( COMMA callArg1:EXPR {args.add(callArg1.getText());} )*
        { args.size()==define.size()-1 }? // better have right amount
        ')'
    | { !((define!=null) && (define.size()>1)) }?
    )
{ if (define!=null) {
    String defineText = (String)define.get(0);
    if (!_createToken) {
        // just substitute text if called from EXPR - no token created
        $setText(defineText);
    } else {
        // create a new lexer to handle the macro text
        cppLexer sublexer = new cppLexer(new DataInputStream(new StringBufferInputStream(defineText)));
        for (int i=0;i<args.size();++i) {
            // treat macro arguments similar to local defines
            List arg = new ArrayList();
            arg.add((String)args.get(i));
            sublexer.defineArgs.put( (String)define.get(1+i), arg );
        }
        selector.push(sublexer);
        // retry in new lexer
        selector.retry();
    }
}};

以如下要处理的代码为例:

#define ADD(A,B,C) A+B+C;

ADD(1,2,3);

其内部执行过程是:

1.代码:

"define" WS defineMacro:RAW_IDENTIFIER

匹配到了ADD,此时:

defineMacro="ADD"

2.代码:

args.add(""); // first element will hold the macro text

的作用是:

将args这个List的第1个元素,即index为0的位置,暂时留空(留作后用,放define的内容)

此时:

args这个List的index为0的位置是空字符串

3.代码:

(WS)? defineArg0:RAW_IDENTIFIER (WS)? {args.add(defineArg0.getText());}

( COMMA (WS)? defineArg1:RAW_IDENTIFIER (WS)? {args.add(defineArg1.getText());} )*

去匹配到了

(A,B,C)

部分,此时是:

defineArg0="A"

defineArg1="B"

defineArg1="C"

List变量args为:

""
"A"
"B"
"C"

4.代码:

// store the text verbatim – tokenize when called

defineText:MACRO_TEXT {args.set(0,defineText.getText());}

作用是,匹配到了:

A+B+C;

此时:

defineText="A+B+C;"

List变量args为:

"A+B+C;"
"A"
"B"
"C"

 

5.代码:

{ if (ifState==1) {

    defines.put( defineMacro.getText(), args );

    $setType(Token.SKIP);

}}

作用是,(此处忽略ifState),将define的内容,和之前已经获得的参数,都保存起来。

此时是:

Map类型的defines,相当于字典类型的变量,内容是:

{

    "ADD"   :   [

                    "A+B+C;",

                    "A",

                    "B",

                    "C"

                ]

}

 

"ADD"

"A+B+C;"

"A"

"B"

"C"

 

6.代码:

IDENTIFIER options {testLiterals=true;} {

    List define = new ArrayList();

    List args = new ArrayList();

} :

作用是,新建局部变量,define和args。

此时是:

局部变量define和args

7.代码:

identifier:RAW_IDENTIFIER

{

    // see if this is a macro argument

    define = (List)defineArgs.get(identifier.getText());

    if (_createToken && define==null) {

        // see if this is a macro call

        define = (List)defines.get(identifier.getText());

    }

}

作用是,匹配到了:

ADD(1,2,3);

中的:

ADD

将ID存为identifier,

将ID这个字符串,拿出来,然后从之前全局的Map类型的defineArgs去尝试取值,

很明显:

  • 如果之前没有定义此ID,那么此处define得到的值就是空null了;
  • 如果之前定义了此ID:此时就是已经定义了ADD,所以可以获得对应的值,即对应的那个List类型的args

此时:

defines

==从Map类型的defineArgs所得到的字典变量中,通过"ADD"所获得对应的那个List类型的args

==

"A+B+C;"
"A"
"B"
"C"

8.代码:

( { (define!=null) && (define.size()>1) }? (WS|COMMENT)?

    // take in arguments if macro call requires them

    ‘(‘

    callArg0:EXPR {args.add(callArg0.getText());}

    ( COMMA callArg1:EXPR {args.add(callArg1.getText());} )*

    { args.size()==define.size()-1 }? // better have right amount

    ‘)’

| { !((define!=null) && (define.size()>1)) }?

)

作用是:

去匹配到了:

ADD(1,2,3);

中的

(1,2,3)

通过判断上述得到的define是否为空,即

define!=null,且define.size()大于1,即对应的List类型的args中超过1个值,即是带参数的define

(否则,如果是不带参数的define,则上述的List的args,就只是只包含单个元素的列表了,其值就只是:

"A+B+C;"

只是define的内容了。)

然后,把对应此处,调用define的地方,以此分析得到调用时所传入的参数,分别赋值给callArg0以及后续的(可能多个的)callArg1,然后都add到args的list中了。

并且,还通过:

args.size()==define.size()-1

去判断,最好是参数个数一致。

即此处是:

(1)(define!=null) && (define.size()>1)

此处就是:

define的确不为空:是包含了4个元素的List

define.size() == 4,的确大于1

(2)

callArg0=1

callArg1=2,callArg1=3

(3)args这个List中的值是:

1
2
3

(4)args.size()==define.size()-1

对应的是:

3 == 4-1

即,此表达式为True

9.代码:

String defineText = (String)define.get(0);

作用是,获得对应的,之前define的index为0的内容,即

"A+B+C;"
"A"
"B"
"C"

的index为0的内容,即:

"A+B+C;"

即,之前define时,define的内容。

此时:

defineText ="A+B+C;"

 

10.代码:

// create a new lexer to handle the macro text

cppLexer sublexer = new cppLexer(new DataInputStream(new StringBufferInputStream(defineText)));

for (int i=0;i<args.size();++i) {

    // treat macro arguments similar to local defines

    List arg = new ArrayList();

    arg.add((String)args.get(i));

    sublexer.defineArgs.put( (String)define.get(1+i), arg );

}

selector.push(sublexer);

// retry in new lexer

selector.retry();

作用是:

新建一个lexer

然后,针对此处的args,即:

1
2
3

去,针对此List的每个值,

先得到其值,比如1,然后再加到arg这个单独新建的List中,此时:

arg是个List,内容为:

1

然后,通过:

sublexer.defineArgs.put( (String)define.get(1+i), arg );

中的

(String)define.get(1+i),

得到对应的,define中的参数的ID,此处即:

"A+B+C;"
"A"
"B"
"C"

中的index为1+1=2,即:

"A"

然后再放到Map类型的defineArgs中,就成了:

{

    "A"   : [

                1

            ]

}

然后后面的for循环中,也是如此逻辑,对应的结果为:

{

    "B"   : [

                2

            ]

}

{

    "C"   : [

                3

            ]

}

如此,就很清晰其用意了:

将,最开始的对于宏的调用:

ADD(1,2,3);

通过此时已有的映射关系(defineArgs):

{

    "ADD"   :   [

                    "A+B+C;",

                    "A",

                    "B",

                    "C"

                ]

}

变成对应的,化解后的,以define中参数为键,以调用处的实际参数为值的键对:

{

    "A"   : [

                1

            ]

}

{

    "B"   : [

                2

            ]

}

{

    "C"   : [

                3

            ]

}

另外,其中的代码:

// create a new lexer to handle the macro text

cppLexer sublexer = new cppLexer(new DataInputStream(new StringBufferInputStream(defineText)));

……

selector.push(sublexer);

// retry in new lexer

selector.retry();

就是之前所分析的,新建一个lexer,并且将相应的内容,让新建的lexer处理。

处理之后,再通过之前的:

public void uponEOF() throws TokenStreamException, CharStreamException {

    try {

        selector.pop(); // return to old lexer/stream

        selector.retry();

    } catch (NoSuchElementException e) {

        // return a real EOF if nothing in stack

    }

}

的逻辑,pop出来,回到当前的lexer,继续后续的处理。

 

【总结】

如此地,化解了一层的键值的匹配关系,即把直接的宏定义ADD化解掉了。

然后生成了,以ADD的参数为键,对应调用处的参数为值的键对关系,保存起来,

然后再去重新调用一个lexer,如此的循环处理,

就可以依次地,把上述的A换成1,B换成2,C换成3了。

 

至此,算是真正了解了,其整个的处理过程和逻辑;



发表评论

电子邮件地址不会被公开。 必填项已用*标注

无觅相关文章插件,快速提升流量