ゴール
私はColdfusionCFscript用のVarscoperを作成するプロジェクトに取り組んでいます。var
基本的に、これは、開発者が変数を適切に作成したことを確認するためにソースコードファイルをチェックすることを意味します。
ANTLR V4を数日間使用した後、GUIビューで非常に優れた解析ツリーを生成する文法ができました。ここで、そのツリーを使用して、プログラムでノードを上下にクロールして変数宣言を探し、それらが関数内にある場合は適切なスコープを持っていることを確認する方法が必要です。可能であれば、言語の定義をこの特定のタスクと混合する必要があるため、文法ファイルでこれを行わない方がよいでしょう。
私が試したこと
私の最近の試みは、を使用して、を介しParserRuleContext
てそれを通過しようとしたことでした。私のクラスをチェックした後、オブジェクトまたはオブジェクトのいずれかがあります。残念ながら、それを使用すると、特定のノードの実際のルールタイプを取得する方法を見つけることができず、テキストが含まれているだけでした。各ノードのルールタイプは、そのテキストノードが無視される右辺式であるか、変数の割り当てであるか、関数宣言であるかが重要であるため、必要です。children
getPayload()
getPayLoad()
ParserRuleContext
Token
質問
- 私はANTLRに非常に慣れていませんが、これは正しいアプローチですか、それともツリーをトラバースするためのより良い方法がありますか?
これが私のサンプルJavaコードです:
Cfscript.java
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.Trees;
public class Cfscript {
public static void main(String[] args) throws Exception {
ANTLRInputStream input = new ANTLRFileStream(args[0]);
CfscriptLexer lexer = new CfscriptLexer(input);
CommonTokenStream tokens = new CommonTokenStream(lexer);
CfscriptParser parser = new CfscriptParser(tokens);
parser.setBuildParseTree(true);
ParserRuleContext tree = parser.component();
tree.inspect(parser); // show in gui
/*
Recursively go though tree finding function declarations and ensuring all variableDeclarations are varred
but how?
*/
}
}
Cfscript.g4
grammar Cfscript;
component
: 'component' keyValue* '{' componentBody '}'
;
componentBody
: (componentElement)*
;
componentElement
: statement
| functionDeclaration
;
functionDeclaration
: Identifier? Identifier? 'function' Identifier argumentsDefinition '{' functionBody '}'
;
argumentsDefinition
: '(' argumentDefinition (',' argumentDefinition)* ')'
| '()'
;
argumentDefinition
: Identifier? Identifier? argumentName ('=' expression)?
;
argumentName
: Identifier
;
functionBody
: (statement)*
;
statement
: variableStatement
| nonVarVariableStatement
| expressionStatement
;
variableStatement
: 'var' variableName '=' expression ';'
;
nonVarVariableStatement
: variableName '=' expression ';'
;
expressionStatement
: expression ';'
;
expression
: assignmentExpression
| arrayLiteral
| objectLiteral
| StringLiteral
| incrementExpression
| decrementExpression
| 'true'
| 'false'
| Identifier
;
incrementExpression
: variableName '++'
;
decrementExpression
: variableName '--'
;
assignmentExpression
: Identifier (assignmentExpressionSuffix)*
| assignmentExpression (('+'|'-'|'/'|'*') assignmentExpression)+
;
assignmentExpressionSuffix
: '.' assignmentExpression
| ArrayIndex
| ('()' | '(' expression (',' expression)* ')' )
;
methodCall
: Identifier ('()' | '(' expression (',' expression)* ')' )
;
variableName
: Identifier (variableSuffix)*
;
variableSuffix
: ArrayIndex
| '.' variableName
;
arrayLiteral
: '[' expression (',' expression)* ']'
;
objectLiteral
: '{' (Identifier '=' expression (',' Identifier '=' expression)*)? '}'
;
keyValue
: Identifier '=' StringLiteral
;
StringLiteral
: '"' (~('\\'|'"'))* '"'
;
ArrayIndex
: '[' [1-9] [0-9]* ']'
| '[' StringLiteral ']'
;
Identifier
: [a-zA-Z0-9]+
;
WS
: [ \t\r\n]+ -> skip
;
COMMENT
: '/*' .*? '*/' -> skip
;
Test.cfc(テストコードファイル)
component something = "foo" another = "more" persistent = "true" datasource = "#application.env.dsn#" {
var method = something.foo.test1;
testing = something.foo[10];
testingagain = something.foo["this is a test"];
nuts["testing"]++;
blah.test().test3["test"]();
var math = 1 + 2 - blah.test().test4["test"];
var test = something;
var testing = somethingelse;
var testing = {
test = more,
mystuff = {
interior = test
},
third = "third key"
};
other = "Idunno homie";
methodCall(interiorMethod());
public function bar() {
var new = "somebody i used to know";
something = [1, 2, 3];
}
function nuts(required string test1 = "first", string test = "second", test3 = "third") {
}
private boolean function baz() {
var this = "something else";
}
}