coldfusion - फलद - छोटे पेड़ के नाम



एक बार व्याकरण पूरा हो जाने पर, एएनटीएलआर v4 ट्री पर चलने का सबसे अच्छा तरीका क्या है? (1)

लक्ष्य

मैं कोल्डफ्यूएशन CFscript के लिए वर्स्कॉपर बनाने के लिए एक प्रोजेक्ट पर काम कर रहा हूं मूल रूप से, इसका मतलब यह है कि स्रोत कोड फाइलों के माध्यम से जांचने के लिए यह सुनिश्चित किया जाए कि डेवलपर्स ने अपने चर को ठीक से var दी है

एएनटीएलआर वी 4 के साथ काम करने के कुछ दिनों के बाद मेरे पास एक व्याकरण है जो GUI दृश्य में एक बहुत अच्छा पेर्स ट्री उत्पन्न करता है। अब, उस पेड़ के उपयोग से मुझे नोड्स को क्रॉल और क्रॉल करने के लिए एक रास्ता चाहिए जो कि प्रोग्रामेटिक रूप से वेरिएबल डिस्क्रिप्शन की तलाश में है और सुनिश्चित करें कि यदि वे फ़ंक्शन के भीतर हैं तो उन्हें उचित स्क्रॉपिंग यदि संभव हो तो मैं इसे व्याकरण फ़ाइल में नहीं बल्कि ऐसा करना चाहता हूं क्योंकि इस विशिष्ट कार्य के साथ भाषा की परिभाषा को मिलाकर आवश्यक होगा।

मैंने क्या कोशिश की है

मेरा नवीनतम प्रयास ParserRuleContext का उपयोग कर रहा था और इसे getPayload() माध्यम से children माध्यम से जाने का प्रयास कर रहा था। getPayLoad() के वर्ग की जांच करने के बाद मुझे या तो एक ParserRuleContext ऑब्जेक्ट या Token ऑब्जेक्ट ParserRuleContext । दुर्भाग्यवश, मैं उस विशिष्ट नोड के लिए वास्तविक नियम प्रकार प्राप्त करने का कोई रास्ता खोज नहीं पा रहा था, केवल पाठ में ही है प्रत्येक नोड के लिए नियम प्रकार आवश्यक है क्योंकि यह मामला है कि क्या वह पाठ नोड एक उपेक्षित अधिकार-हस्त अभिव्यक्ति, एक चर असाइनमेंट या फ़ंक्शन घोषणा है।

प्रशन

  1. मैं एएनटीएलआर के लिए बहुत नया हूँ, क्या यह सही दृष्टिकोण है, या क्या पेड़ को पार करने का एक बेहतर तरीका है?

यहां मेरा नमूना जावा कोड है:

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";
    }
}

https://ffff65535.com


अगर मैं आप थे तो मैन्युअल रूप से इस पर चलना नहीं था I एक लेक्सर और पार्सर बनाने के बाद, CfscriptBaseListener ने एक फाइल भी बनाई होगी जिसे CfscriptBaseListener नाम CfscriptBaseListener है जिसमें आपके सभी पार्सर नियमों के लिए रिक्त विधियां हैं। आप एएनटीएलआर को अपने पेड़ पर चलने दे सकते हैं और एक कस्टम पेड़-श्रोता को संलग्न कर सकते हैं जिसमें आप केवल उन्हीं तरीकों / नियमों को ओवरराइड कर सकते हैं जिनकी आप दिलचस्पी रखते हैं।

आपके मामले में, जब भी कोई नया फ़ंक्शन बनाया जाता है (आपको एक नया दायरा बनाने के लिए) सूचित किया जाता है, और संभवतः आपको चर असाइनमेंट ( variableStatement nonVarVariableStatement और nonVarVariableStatement - nonVarVariableStatement ) में रुचि होगी। आपका श्रोता, चलो कॉल करें, VarListener सभी स्कॉप्स का ट्रैक रखेगा VarListener पेड़ से चलता है।

मैंने 1 नियम को थोड़ी सी बदल दिया था (मैं objectLiteralEntry जोड़ा objectLiteralEntry ):

objectLiteral
    : '{' (objectLiteralEntry (',' objectLiteralEntry)*)? '}'
    ;

objectLiteralEntry
    : Identifier '=' expression
    ;
    

जो निम्न डेमो में जीवन को आसान बनाता है:

VarListener.java

public class VarListener extends CfscriptBaseListener {

    private Stack<Scope> scopes;

    public VarListener() {
        scopes = new Stack<Scope>();
        scopes.push(new Scope(null));
    } 

    @Override
    public void enterVariableStatement(CfscriptParser.VariableStatementContext ctx) {
        String varName = ctx.variableName().getText();
        Scope scope = scopes.peek();
        scope.add(varName);
    }

    @Override
    public void enterNonVarVariableStatement(CfscriptParser.NonVarVariableStatementContext ctx) {
        String varName = ctx.variableName().getText();
        checkVarName(varName);
    }

    @Override
    public void enterObjectLiteralEntry(CfscriptParser.ObjectLiteralEntryContext ctx) {
        String varName = ctx.Identifier().getText();
        checkVarName(varName);
    }

    @Override
    public void enterFunctionDeclaration(CfscriptParser.FunctionDeclarationContext ctx) {
        scopes.push(new Scope(scopes.peek()));
    }

    @Override
    public void exitFunctionDeclaration(CfscriptParser.FunctionDeclarationContext ctx) {
        scopes.pop();        
    }

    private void checkVarName(String varName) {
        Scope scope = scopes.peek();
        if(scope.inScope(varName)) {
            System.out.println("OK   : " + varName);
        }
        else {
            System.out.println("Oops : " + varName);
        }
    }
}

एक Scope ऑब्जेक्ट के रूप में सरल हो सकता है:

Scope.java

class Scope extends HashSet<String> {

    final Scope parent;

    public Scope(Scope parent) {
        this.parent = parent;
    }

    boolean inScope(String varName) {
        if(super.contains(varName)) {
            return true;
        }
        return parent == null ? false : parent.inScope(varName);
    }
}

अब, यह सब परीक्षण करने के लिए, यहां एक छोटा मुख्य वर्ग है:

Main.java

import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.*;

public class Main {

    public static void main(String[] args) throws Exception {

        CfscriptLexer lexer = new CfscriptLexer(new ANTLRFileStream("Test.cfc"));
        CfscriptParser parser = new CfscriptParser(new CommonTokenStream(lexer));
        ParseTree tree = parser.component();
        ParseTreeWalker.DEFAULT.walk(new VarListener(), tree);
    }
}

यदि आप इस Main वर्ग को चलाते हैं, तो निम्नलिखित मुद्रित किया जाएगा:

Oops : testing
Oops : testingagain
OK   : test
Oops : mystuff
Oops : interior
Oops : third
Oops : other
Oops : something

इसमें कोई शक नहीं है, यह वही है जो आप चाहते हैं और मैं शायद कोल्डफ्यूज़न के कुछ नियमों को अपनाना चाहता हूं। लेकिन मुझे लगता है कि यह आपको अपनी समस्या को हल करने के तरीके में कुछ अंतर्दृष्टि देगा मुझे लगता है कि कोड बहुत आत्म व्याख्यात्मक है, लेकिन अगर ऐसा नहीं है, तो स्पष्टीकरण के लिए पूछने में संकोच न करें।

HTH