是的,你说得对:这是在一次以上的AST传球中完成的。
  
  
   首先创建一个生成源AST的语法,然后创建一个用于遍历树并发现所有已定义函数的树语法。然后,可以使用另一个树语法对脚本求值,该语法从上一个树语法中获取所发现的函数。
  
  
  
  
   获取源:
  
  <?php
f(); // function called before itâs defined
function f() {
  g();
}
function g() {}
?>
  
   它被解析为以下AST:
  
  
    
  
  
  
  
  
  grammar PHPMin;
options { 
  output=AST; 
}
tokens {
  SCRIPT; F_CALL; F_DECL; F_BODY;
}
parse
  :  script EOF -> script
  ;
script
  :  '<?php' atom* '?>' -> ^(SCRIPT atom*)
  ;
atom
  :  functionCall
  |  functionDecl
  ;
functionCall
  :  Identifier '(' ')' ';' -> ^(F_CALL Identifier)
  ;
functionDecl
  :  'function' Identifier '(' ')' '{' functionBody '}' -> ^(F_DECL Identifier functionBody)
  ;
functionBody
  :  functionCall* -> ^(F_BODY functionCall*)
  ;
Identifier  : ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')* ;
LineComment : '//' ~('\r' | '\n')* ('\r'? '\n' | EOF){skip();} ;
Space       : (' ' | '\t' | '\r' | '\n'){skip();} ;
  
   然后使用从以下树语法生成的“树行者”来发现声明的函数:
  
  tree grammar PHPMinFunctionWalker;
options {
    tokenVocab=PHPMin;
    ASTLabelType=CommonTree;
}
@members {
    java.util.Set<String> declared = new java.util.HashSet<String>();
}
discover
  :  script
  ;
script
  :  ^(SCRIPT atom*)
  ;
atom
  :  functionCall
  |  functionDecl
  ;
functionCall
  :  ^(F_CALL Identifier)
  ;
functionDecl
  :  ^(F_DECL Identifier functionBody) {declared.add($Identifier.text);}
  ;
functionBody
  :  ^(F_BODY functionCall*)
  ;
  
  
  // A
java -cp antlr-3.2.jar org.antlr.Tool PHPMin.g
// B 
java -cp antlr-3.2.jar org.antlr.Tool PHPMinFunctionWalker.g
// C
javac -cp antlr-3.2.jar *.java
// D     
java -cp .:antlr-3.2.jar Main    // *nix 
java -cp .;antlr-3.2.jar Main    // Windows
  
   并运行以下主类(D):
  
  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 source = "<?php                                          \n" + 
                        "f(); // function called before itâs defined    \n" + 
                        "function f() {                                 \n" + 
                        "  g();                                         \n" + 
                        "}                                              \n" + 
                        "function g() {}                                \n" + 
                        "?>                                             \n";
        // create a lexer and parser for the source
        ANTLRStringStream in = new ANTLRStringStream(source);
        PHPMinLexer lexer = new PHPMinLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        PHPMinParser parser = new PHPMinParser(tokens);
        PHPMinParser.parse_return returnValue = parser.parse();
        CommonTree tree = (CommonTree)returnValue.getTree();
        // create a tree walker to discover all declared functions
        CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
        nodes.setTokenStream(tokens);
        PHPMinFunctionWalker functions = new PHPMinFunctionWalker(nodes);
        functions.discover();
        System.out.println("Declared functions: "+functions.declared);
    }
}
  
   产生以下输出:
  
  Declared functions: [f, g]
  
   当然,这只是一个如何处理的例子,而不是如何做得最好。我可以想象(当使用Java解释脚本时),您不会将声明的函数作为简单字符串存储在
   
    Set<String>
   
   ,而是作为
   
    Map<String, CommonTree>
   
   轻松获取函数的根并在调用时对其求值。
  
  
   进一步阅读:
   
    http://www.antlr.org/wiki/display/ANTLR3/Simple+tree-based+interpeter
   
  
  
  
  
   
    编辑
   
  
  
  
  tree grammar PHPMinValidateWalker;
options {
    tokenVocab=PHPMin;
    ASTLabelType=CommonTree;
}
@members {
    java.util.Set<String> declared = new java.util.HashSet<String>();
}
validate
  :  script
  ;
script
  :  ^(SCRIPT atom*)
  ;
atom
  :  functionCall
  |  functionDecl
  ;
functionCall
  :  ^(F_CALL Identifier) 
     {
       if(!declared.contains($Identifier.text)) {
         throw new RuntimeException("no such function: " +  $Identifier.text);
       }
     }
  ;
functionDecl
  :  ^(F_DECL Identifier functionBody)
  ;
functionBody
  :  ^(F_BODY functionCall*)
  ;
  
   使用测试:
  
  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 source = "<?php                                          \n" + 
                        "f(); // function called before itâs defined    \n" + 
                        "function f() {                                 \n" + 
                        "  g();                                         \n" + 
                        "  x();                                         \n" + 
                        "}                                              \n" + 
                        "function g() {}                                \n" + 
                        "?>                                             \n";
        // create a lexer and parser for the source
        ANTLRStringStream in = new ANTLRStringStream(source);
        PHPMinLexer lexer = new PHPMinLexer(in);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        PHPMinParser parser = new PHPMinParser(tokens);
        PHPMinParser.parse_return returnValue = parser.parse();
        CommonTree tree = (CommonTree)returnValue.getTree();
        // create a tree walker to discover all declared functions
        CommonTreeNodeStream nodes = new CommonTreeNodeStream(tree);
        nodes.setTokenStream(tokens);
        PHPMinFunctionWalker functions = new PHPMinFunctionWalker(nodes);
        functions.discover();
        System.out.println("Declared functions: "+functions.declared);
        // PHPMinValidateWalker
        nodes = new CommonTreeNodeStream(tree);
        nodes.setTokenStream(tokens);
        PHPMinValidateWalker validator = new PHPMinValidateWalker(nodes);
        validator.declared = functions.declared;
        validator.validate();
    }
}
  
   产生异常,因为
   
    x()
   
   在任何地方都没有定义。从源代码中删除它将导致树行者不产生异常。