C 如何输出使用ANTLR构建的AST?

C 如何输出使用ANTLR构建的AST?,c,antlr,static-analysis,abstract-syntax-tree,C,Antlr,Static Analysis,Abstract Syntax Tree,我正在为C做一个静态分析器。 我已经使用ANTLR完成了lexer和解析器,其中生成了Java代码 ANTLR是否通过选项{output=AST;}自动为我们构建AST?还是我必须自己做这棵树?如果有,那么如何吐出该AST上的节点 我目前认为AST上的节点将用于生成SSA,然后进行数据流分析,以生成静态分析器。我走对了吗 拉斐尔写道: antlr是否通过选项{output=AST;}自动为我们构建AST?还是我必须自己做这棵树?如果有,那么如何吐出该AST上的节点 不,解析器不知道每个解析器规则

我正在为C做一个静态分析器。 我已经使用ANTLR完成了lexer和解析器,其中生成了Java代码

ANTLR是否通过
选项{output=AST;}
自动为我们构建AST?还是我必须自己做这棵树?如果有,那么如何吐出该AST上的节点

我目前认为AST上的节点将用于生成SSA,然后进行数据流分析,以生成静态分析器。我走对了吗

拉斐尔写道:

antlr是否通过选项{output=AST;}自动为我们构建AST?还是我必须自己做这棵树?如果有,那么如何吐出该AST上的节点

不,解析器不知道每个解析器规则的根和叶是什么,因此您需要做的不仅仅是在语法中放入
options{output=AST;}

例如,当使用语法生成的解析器解析源代码时,“true&&(false | | true&&(true | | false))”:

grammar ASTDemo;

options { 
  output=AST; 
}

parse
  :  orExp
  ;

orExp
  :  andExp ('||' andExp)*
  ;

andExp
  :  atom ('&&' atom)*
  ;

atom
  :  'true'
  |  'false'
  |  '(' orExp ')'
  ;

// ignore white space characters
Space
  :  (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;}
  ;
将生成以下解析树:

(即,仅仅是一个扁平的一维标记列表)

您需要告诉ANTLR语法中哪些标记成为根、叶,或者干脆不在树中

创建AST的方法有两种:

  • 使用如下所示的重写规则:
    foo:abcd->^(dab)
    ,其中
    foo
    是与令牌
    a B C D
    匹配的解析器规则。所以
    ->
    之后的所有内容都是实际的重写规则。如您所见,重写规则中没有使用标记
    C
    ,这意味着AST中省略了该标记。直接放置在
    ^(
    后面的令牌将成为树的根
  • 在解析器规则中的令牌之后使用树运算符
    ^
    ,其中
    ^
    将使令牌成为根,而
    将从树中删除令牌。
    foo:abcd->^(dab);
    的等价物是
    foo:abc!D^;
  • foo:abcd->^(dab);
    foo:abcc!D^;
    都将生成以下AST:

    现在,您可以将语法改写如下:

    grammar ASTDemo;
    
    options { 
      output=AST; 
    }
    
    parse
      :  orExp
      ;
    
    orExp
      :  andExp ('||'^ andExp)* // Make `||` root
      ;
    
    andExp
      :  atom ('&&'^ atom)* // Make `&&` root
      ;
    
    atom
      :  'true'
      |  'false'
      |  '(' orExp ')' -> orExp // Just a single token, no need to do `^(...)`, 
                                // we're removing the parenthesis. Note that
                                // `'('! orExp ')'!` will do exactly the same.
      ;
    
    // ignore white space characters
    Space
      :  (' ' | '\t' | '\r' | '\n') {$channel=HIDDEN;}
      ;
    
    它将从源代码“true&&(false | | true&&(true | | false))”创建以下AST:

    相关ANTLR wiki链接:

    拉斐尔写道:

    我目前认为AST上的节点将用于制作SSA,然后进行数据流分析,以制作静态分析器。我走的路对吗

    我从来没有做过这样的事情,但在我看来,你首先需要的是来自源代码的AST,所以是的,我想你的方法是正确的!:)

    编辑 以下是如何使用生成的lexer和parser:

    import org.antlr.runtime.*;
    import org.antlr.runtime.tree.*;
    import org.antlr.stringtemplate.*;
    
    public class Main {
      public static void main(String[] args) throws Exception {
        String src = "true && (false || true && (true || false))";
        ASTDemoLexer lexer = new ASTDemoLexer(new ANTLRStringStream(src));
        ASTDemoParser parser = new ASTDemoParser(new CommonTokenStream(lexer));
        CommonTree tree = (CommonTree)parser.parse().getTree();
        DOTTreeGenerator gen = new DOTTreeGenerator();
        StringTemplate st = gen.toDOT(tree);
        System.out.println(st);
      }
    }
    

    谢谢你的回答,但我仍然不知道如何决定哪些标记应该是根和叶。有什么建议吗?@Raphael,你觉得最有意义的就说吧。很自然,括号、分号等可以很容易地从树中删除,运算符成为根。
    while
    语句的根可能是关键字
    'while'
    ,并且可能有两个子项:
    expression
    statementBlock
    (这是零个或多个
    语句
    ),在表达式计算结果为true时执行。再说一次:对你来说最有意义的事情:你就是那个要“走”AST并做所有艰苦工作的人。祝你好运@Samuel,请参见我的编辑(这是您想要的)。为什么示例中的解析树是扁平的?无法从平面解析树推断解析过程。@Malinda它们通常通过(我已更新链接)提供