【问题标题】:Antlr check return statements in scopeAntlr 检查范围内的返回语句
【发布时间】:2017-10-28 19:14:05
【问题描述】:

我想知道如何在语法分析期间检查所有路径是否在函数中都有返回。所以说我在 Lexer 中有以下内容

RETURN: 'return';
PRINT: 'print';
IF:'if';
ELSE: 'else';
THEN:'then';
PLUS: '+';
MINUS:'-';
EQUALS: '==';
DIGIT: '0'..'9';
OPEN:'{';
CLOSE:'}';
STRING: [a..zA..Z]+;
SEMICOLON: ';';

和解析器

function: STRING OPEN statement CLOSE
statement: RETURN expr | PRINT expr | IF expr THEN statement ELSE statement | statement SEMICOLON statement;
expr: DIGIT| expr PLUS expr | expr MINUS expr | expr EQUALS expr;

我的问题是一个有效的函数应该有一个 return 语句,后面什么都没有。 所以一个有效的是

test { return 2+2 }

test{ if 2 == 2 then return 2 else return 3 }

返回后代码无法访问的地方是无效的。例如。

test{return 2; print 3}

我将如何检查 return 语句后没有任何内容?

我的主要 java 方法看起来像这样:

MyLexer mylexer = new MyLexer(new ANTLRInputStream(System.in));
CommonTokenStream toks = new CommonTokenStream(mylexer);
MyParser parser = new MyParser(tokens);
ParseTree parseTree = parser.program();

【问题讨论】:

  • 无法通过语法强制执行此操作。您可以在语义操作中检查它,但通常根本不会在解析器中检查它,而是在语义分析阶段。
  • 非常感谢
  • @sepp2k:你为什么不能用语法来强制执行呢?这是一个简单的句法特征。
  • @rici 不,你说得对,我没有好好考虑。它仍然会导致大量重复(出现在函数末尾的块与没有出现的块不同),所以我仍然建议事后检查。

标签: java compiler-construction antlr antlr4


【解决方案1】:

我不是专家,直到今天才使用错误报告,但如果您只是想要一个错误列表,您可以按照The Definitive ANTLR 4 Reference 的第 9 章执行以下操作。

文件Question.g4

grammar Question;

/* Detecting invalid input after a return statement. */

question
@init {System.out.println("Question last update 1302");}
    :   function+ EOF
    ;

function
    :   STRING OPEN statement_block CLOSE
    ;

statement_block
    :   statement* if_last? return_statement
    ;

statement
    :   PRINT expr
    |   IF expr THEN statement ELSE statement
    |   statement SEMICOLON statement
    ;

if_last
    :   IF expr THEN statement_block ELSE statement*
    ;

return_statement
    :   RETURN expr
    ;

expr
    :   DIGIT
    |   expr PLUS expr
    |   expr MINUS expr
    |   expr EQUALS expr
    ;

CLOSE  : '}' ;
ELSE   : 'else' ;
EQUALS : '==' ;
IF     : 'if' ;
MINUS  : '-' ;
OPEN   : '{' ;
PLUS   : '+' ;
PRINT  : 'print' ;
RETURN : 'return' ;
THEN   : 'then' ;

DIGIT     : [0-9] ;
STRING    : [a-zA-Z]+ ;
SEMICOLON : ';' ;

WS  : [ \r\n\t] -> channel(HIDDEN) ;

文件MyListener.java

public class MyListener extends QuestionBaseListener {
    QuestionParser parser;
    public MyListener(QuestionParser parser) { this.parser = parser; }

    public void exitFunction(QuestionParser.FunctionContext ctx) {
        System.out.println(">>> in MyListener for function");
        System.out.println(parser.getTokenStream().getText(ctx));
    }
}

文件test.java

import org.antlr.v4.runtime.*;

import org.antlr.v4.runtime.tree.*;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.util.*;

public class test {
    public static class UnderlineListener extends BaseErrorListener {
        public void syntaxError(Recognizer<?, ?> recognizer,
                                Object offendingSymbol,
                                int line, int charPositionInLine,
                                String msg,
                                RecognitionException e)
        {
            System.err.println("line " + line + ":" + charPositionInLine + " " + msg);
            underlineError(recognizer,(Token)offendingSymbol,
                line, charPositionInLine);
        }

        protected void underlineError(Recognizer recognizer,
                                      Token offendingToken, int line,
                                      int charPositionInLine) {
            CommonTokenStream tokens =
                (CommonTokenStream)recognizer.getInputStream();
            String input = tokens.getTokenSource().getInputStream().toString();
            String[] lines = input.split("\n");
            String errorLine = lines[line - 1];
            System.err.println(errorLine);
            for (int i=0; i<charPositionInLine; i++) System.err.print(" ");
            int start = offendingToken.getStartIndex();
            int stop = offendingToken.getStopIndex();
            if ( start>=0 && stop>=0 ) {
                for (int i=start; i<=stop; i++) System.err.print("^");
            }
            System.err.println();
        }
    }

    public static void main(String[] args) throws IOException {
        ANTLRInputStream input = new ANTLRFileStream(args[0]);
        QuestionLexer lexer = new QuestionLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        QuestionParser parser = new QuestionParser(tokens);
        parser.removeErrorListeners(); // remove ConsoleErrorListener
        parser.addErrorListener(new UnderlineListener()); // add ours
        ParseTree tree = parser.question();
        System.out.println("---parsing ended");
        ParseTreeWalker walker = new ParseTreeWalker();
        MyListener my_listener = new MyListener(parser);
        System.out.println(">>>> about to walk");
        walker.walk(my_listener, tree);
    }
}

文件t.text

test { return 2+2 }
test { if 2 == 2 then return 2 else return 3 }
test { if 2 == 2 then print 2 else print 3 return 4 }
test {return 2; print 3}
test { if 2 == 2 then return 2; abc else return 3; def }
test { if 2 == 2 then if 6 then print 6 else print 3 else if 5 then
print 1; print 2 else print 3 return 4 }

执行:

$ java test t.text 
Question last update 1302
line 4:14 mismatched input ';' expecting {'}', '==', '-', '+'}
test {return 2; print 3}
              ^
line 5:30 mismatched input ';' expecting {'else', '==', '-', '+'}
test { if 2 == 2 then return 2; abc else return 3; def }
                              ^
line 5:49 mismatched input ';' expecting {'}', '==', '-', '+'}
test { if 2 == 2 then return 2; abc else return 3; def }
                                                 ^
---parsing ended
>>>> about to walk
>>> in MyListener for function
test { return 2+2 }
>>> in MyListener for function
test { if 2 == 2 then return 2 else return 3 }
>>> in MyListener for function
test { if 2 == 2 then print 2 else print 3 return 4 }
>>> in MyListener for function
test {return 2; print 3}
>>> in MyListener for function
test { if 2 == 2 then return 2; abc else return 3; def }
>>> in MyListener for function
test { if 2 == 2 then if 6 then print 6 else print 3 else if 5 then print 1; print 2 else print 3 return 4 }

【讨论】:

  • 这要求所有 if-blocks 在最后都有一个 return 语句,即使 if 语句不是函数中的最后一个语句。如果if 的两种情况都有一个return 语句并且if 之后有一个语句,它也不会产生错误。要在语法中实际解决这个问题,您需要针对出现在函数末尾和不出现的 if 的不同规则。您还需要考虑到ifs,不是在最后,可能会在其中一种情况下返回,但不是两种情况(至少这是明智的规则以及它在其他语言中的工作方式)。
  • @sepp2k 对,针对一个案例进行了更新。如果他对此解决方案感兴趣,我会让 OP 提供所有案例。但正如您在第一条评论中建议的那样,最好在语义分析阶段进行。
猜你喜欢
  • 2013-10-02
  • 2019-06-24
  • 1970-01-01
  • 2013-01-04
  • 2012-06-09
  • 1970-01-01
  • 2020-01-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多