山东大学软件工程应用与实践——PIG代码分析(六)

2021SC@SDUSC

总览

本篇为对QueryParserDriver类进行分析的第二篇,主要是对
parse生成逻辑执行计划的过程进行分析。

代码分析

parse

生成逻辑执行计划具体依靠的是LogicalPlanGenerator。 LogicalPlanGenerator.g是一个树分析器文件,antlr生成LogicalPlanGenerator.java文件,实现org.antlr.runtime.tree.TreeParser接口,会对QueryParser.g对应的抽象语法树进行语义处理,用来生成逻辑执行计划。每次调用pigServer.registerQuery方法,注册一个查询语句,Pig都会启动解析、验证步骤,然后调用LogicalPlanGenerator.query()方法,生成该条语句对应的逻辑执行子计划。直到调用pigServer.store方法,才会生成一个完整的逻辑执行计划,触发下一阶段操作。

public LogicalPlan parse(String query) throws ParserException {
    
    
        LogicalPlan plan = null;

        ScriptState ss = ScriptState.get();
        CommonTokenStream tokenStream = tokenize(query, ss.getFileName());

        Tree ast = parse( tokenStream );
        ast = expandMacro( ast );

        try{
    
    
            ast = validateAst( ast );
            applyRegisters(ast);

            LogicalPlanGenerator planGenerator =
                new LogicalPlanGenerator( new CommonTreeNodeStream( ast ), pigContext, scope, fileNameMap );
            planGenerator.query();

            checkError( planGenerator );

            plan = planGenerator.getLogicalPlan();
            operators = planGenerator.getOperators();
            lastRel = planGenerator.getLastRel();
        } catch(RecognitionException ex) {
    
    
            throw new ParserException( ex );
        } catch(Exception ex) {
    
    
            throw new ParserException( ex.getMessage(), ex );
        }

        return plan;
    }

不同于上述方法,下面的方法生成的是抽象语法树,该抽象语法树记录的是命令语句的各个部分的命令,当在源程序语法分析工作时,是在相应程序设计语言的语法规则指导下进行的。语法规则描述了该语言的各种语法成分的组成结构,通常可以用所谓的前后文无关文法或与之等价的Backus-Naur范式(BNF)将一个程序设计语言的语法规则确切的描述出来。所以需要抽象语法数的建立。

static Tree parse(CommonTokenStream tokens) throws ParserException {
    
    
        QueryParser parser = QueryParserUtils.createParser(tokens);

        QueryParser.query_return result = null;
        try {
    
    
            result = parser.query();
        } catch (RecognitionException e) {
    
    
            String msg = parser.getErrorHeader(e) + " "
                    + parser.getErrorMessage(e, parser.getTokenNames());
            SourceLocation location = new SourceLocation(null, e.line,e.charPositionInLine);
            throw new ParserException(msg, location);
        } catch(RuntimeException ex) {
    
    
            throw new ParserException( ex.getMessage() );
        }

        Tree ast = (Tree) result.getTree();
        checkError(parser);

        return ast;
    }

applyRegisters

首先判断该抽象语法树的文本是否为register,如果是则首先读取位于子路径0的地址,如果子节点有5个,则在注册时将第三个节点存储的记录为脚本语言,第五个记录为命名域,如果没有5个则直接登记,不标注命名域以及脚本语言;如果不是register则依次登记各个节点。

    private void applyRegisters(Tree t) throws ExecException, ParserException {
    
    
        if (t.getText().equalsIgnoreCase(REGISTER_DEF)) {
    
    
            String path = t.getChild(0).getText();
            path = path.substring(1, path.length()-1);
            try {
    
    
                if (t.getChildCount() == 5) {
    
    
                    new RegisterResolver(getPigServer()).parseRegister(path, t.getChild(2).getText(), t.getChild(4).getText());
                } else {
    
    
                    new RegisterResolver(getPigServer()).parseRegister(path, null, null);
                }
            } catch (IOException ioe) {
    
    
                throw new ParserException(ioe.getMessage());
            }
        } else {
    
    
            for (int i = 0; i < t.getChildCount(); i++) {
    
    
                applyRegisters(t.getChild(i));
            }
        }
    }

tokenize

读取指令以及其来源,然后通过查询词法分析器对此法进行分析得到对应结果。

 static CommonTokenStream tokenize(String query, String source)
            throws ParserException {
    
    
        CharStream input;
        try {
    
    
            input = new QueryParserStringStream(query, source);
        } catch (IOException ex) {
    
    
            throw new ParserException("Unexpected IOException: "
                    + ex.getMessage());
        }
        QueryLexer lexer = new QueryLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        checkError(lexer);
        return tokens;
    }

总结

本次代码分析分析了逻辑执行计划的生成计划以及生成效果,同时了解了对抽象语法树其中节点的解释读取。

猜你喜欢

转载自blog.csdn.net/qq_45822693/article/details/121234592