Séb*_*nec 6 antlr abstract-syntax-tree function-call
构建AST后,实现树步行器的最佳方法是什么,以便可以按任何顺序定义和调用函数?
例如,这在PHP中有效:
<?php
f(); // function called before it’s defined
function f() {
print 3;
}
?>
Run Code Online (Sandbox Code Playgroud)
我猜不知道必须有第二遍,或树变换,但我找不到任何关于这个主题的有趣内容.这个问题可能不是特定于Antlr的问题,但是如果你能指出一个Antlr如何做到这一点的例子,那就更好了!
是的,你是对的:这是通过AST多次传递完成的.
首先创建一个构建源的AST的语法,然后创建一个树语法,用于迭代树并发现所有已定义的函数.然后,您可以使用另一个树语法来评估脚本,该语法从先前的树语法中获取已发现的函数.
拿来源:
<?php
f(); // function called before it’s defined
function f() {
g();
}
function g() {}
?>
Run Code Online (Sandbox Code Playgroud)
将其解析为以下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();} ;
Run Code Online (Sandbox Code Playgroud)
然后使用从以下树语法生成的"tree-walker"发现声明的函数:
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*)
;
Run Code Online (Sandbox Code Playgroud)
要测试它,创建一个词法分析器和解析器(A),生成"tree-walker"(B),编译所有源文件(C):
// 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
Run Code Online (Sandbox Code Playgroud)
并运行以下主类(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);
}
}
Run Code Online (Sandbox Code Playgroud)
产生以下输出:
Declared functions: [f, g]
Run Code Online (Sandbox Code Playgroud)
当然,这只是如何处理它的一个例子,而不是它如何做得最好.我可以想象(当使用Java来解释脚本时),你不会将声明的函数作为简单的字符串存储在a中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*)
;
Run Code Online (Sandbox Code Playgroud)
使用测试:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
产生异常,因为x()没有在任何地方定义.从源中删除它将导致树行者不会产生异常.
| 归档时间: |
|
| 查看次数: |
5204 次 |
| 最近记录: |