0

我使用 ANTLR 制作了自己的语言。语法是完整的,但我想制作一个验证输入的 tree walker。但这不起作用。当我(不小心)在 tree walker 中使用 output=AST 时,它编译时没有错误,但它不接受任何输入。(它说 org.antlr.runtime.tree.CommonTree 不能转换为 tools.ANode)。但我认为 output=AST 不能在 tree walker 中使用。但是,它不会编译。它给出了一个

internal error: /.../src/AwesomeChecker.g : java.lang.IllegalStateException: java.lang.NullPointerException
org.deved.antlride.runtime.AntlrErrorListener$DynamicToken.invokeMethod(AntlrErrorListener.java:59)
org.deved.antlride.runtime.AntlrErrorListener$DynamicToken.getLine(AntlrErrorListener.java:64)
org.deved.antlride.runtime.AntlrErrorListener.report(AntlrErrorListener.java:131)
org.deved.antlride.runtime.AntlrErrorListener.message(AntlrErrorListener.java:115)
org.deved.antlride.runtime.AntlrErrorListener.warning(AntlrErrorListener.java:99)
org.antlr.tool.ErrorManager.grammarWarning(ErrorManager.java:742)
org.antlr.tool.ErrorManager.grammarWarning(ErrorManager.java:757)
org.antlr.tool.Grammar.parseAndBuildAST(Grammar.java:655)
org.antlr.Tool.getRootGrammar(Tool.java:626)
org.antlr.Tool.process(Tool.java:459)
org.deved.antlride.runtime.Tool2.main(Tool2.java:24)

当我尝试使用命令行编译时,它说我应该添加 output=AST。我的第一个想法是编译器看到一些只能在树解析器中使用的符号,但我找不到任何“非法”符号。

你能帮我么?

检查器的语法是:

//AwesomeChecker.g: ANTLR (context) checker voor Awesome++

tree grammar AwesomeChecker;

options
{
    tokenVocab = Awesome;       //import tokens from Awesome.tokens
    ASTLabelType = ANode;       //AST nodes are of type ANode
}

//Alter code generation so catch-clauses get replaced with this action.
//This disables ANTLR error handling: Exceptions are propagated upwards.
@rulecatch
{
    catch (RecognitionException re) 
    {
        throw re;
    }
}

@header
{
    import tools.*;
    import java.util.List;
    import java.util.ArrayList; 
}

@members
{
    //symbol table voor de identifiers
    private SymbolTable ST      = new SymbolTable();
    //voor de AST types
    private Checker checker     = new Checker();

    private void enter(ANode id, boolean isVar) throws AwesomeException {
        try {
            IdEntry ie = new IdEntry(id, isVar);
            this.ST.enter(id.getText(), ie);
        }
        catch (SymbolTableException e)  {
            throw new AwesomeException(id, "Al gedeclareerd");
        }
    }
}

start
    : program
    ;

program
        @init                   {   this.ST.openScope();    }
    :   ^(PROGRAM statement+)   {   this.ST.closeScope();   }
    ;

statement
    :   op=IDENTIFIER e=assignment_statement
            {
                checker.checkTypes($op, $e.start, op, checker.ANY_TYPE);
            }
    |   output_stat
    |   input_stat
    |   if_statement[false]
    |   do_while_statement
    |   declaration
    ;

assignment_statement
    : (op=BECOMES^ e=expr)+
            {
                op.setExprType($e.start.getExprType());
            }
    ;

declaration
    :   ^(DECL t=type c=CONST? id=IDENTIFIER (e=expr)?)
            {
                boolean isVar = (c == null); 
                int type = id.setExprType($t.start.getExprType());
                if(e == null) {

                } else {            
                    checker.checkType($e.start, type);
                }
                this.enter(id, isVar);
            }
    ;

type
    :   t=BOOLEAN
            {$t.setExprType(checker.BOOL_TYPE);}
    |   t=INTEGER
            {$t.setExprType(checker.INT_TYPE);}
    |   t=CHAR
        {$t.setExprType(checker.CHAR_TYPE);}
    ;

statements_block
        @init               {   this.ST.openScope();    }
    :   statement*
        {this.ST.closeScope();}
    ;

expr
    :   ^(op=COMPOUND (statement)* e=expr)
                {
                    op.setExprType($e.start.getExprType());
                }
    |   ^(op = (PLUS | MINUS | MULTIPLY)    e1=expr e2=expr)    
                            {   
                                checker.checkTypes($e1.start, $e2.start, op, checker.INT_TYPE); 
                                op.setExprType(checker.INT_TYPE);   
                            }
    |   ^(op = (DIVIDE | MODULUS)   e1=expr e2=expr)    
                            {   
                                checker.checkTypes($e1.start, $e2.start, op, checker.INT_TYPE);
                                if ($e2.text.equals("0"))
                                {
                                    throw new AwesomeException($e2.start, "Cannot divide by zero");
                                }
                                op.setExprType(checker.INT_TYPE);   
                            }
    |   ^(op = (SMALLER | SMALLEREQUAL | GREATEREQUAL | GREATER) e1=expr e2=expr)   
                            {   
                                checker.checkTypes($e1.start, $e2.start, op, checker.INT_TYPE); 
                                op.setExprType(checker.BOOL_TYPE);  
                            }
    |   ^(op = (EQUAL | NOTEQUAL) e1=expr e2=expr)  
                            {   
                                checker.checkTypes($e1.start, $e2.start, op, checker.ANY_TYPE); 
                                op.setExprType(checker.BOOL_TYPE);  
                            }
    |   ^(op = (AND | OR) e1=expr e2=expr)  
                            {   
                                checker.checkTypes($e1.start, $e2.start, op, checker.BOOL_TYPE);
                                op.setExprType(checker.BOOL_TYPE);  
                            }
    |   ^(op = UNARYEXPR (PLUS | MINUS) e1=expr)        
                            {   
                                checker.checkType($e1.start, checker.INT_TYPE);     
                                op.setExprType(checker.INT_TYPE);   
                            } 
    |   ^(op = UNARYEXPR NOTEQUAL e1=expr)      
                            {   
                                checker.checkType($e1.start, checker.BOOL_TYPE);    
                                op.setExprType(checker.BOOL_TYPE);  
                            }
    ;

input_stat
    :   ^(op=INPUT v=varlist)
                            {   
                                int type = $v.start.getExprType();
                                op.setExprType(type);
                            }
    ;

output_stat
    :   ^(op=OUTPUT v=varlist)
                            {
                                int type = $v.start.getExprType();
                                op.setExprType(type);
                            }
    ;

if_statement[boolean isExpr]
    :   ^(op=IF cond=expr (statements_block|op1=operand) (ELSE (statements_block|op2=operand))?)
            {
                checker.checkType($cond.start, checker.BOOL_TYPE);
                if(isExpr) {
                    int type = checker.checkTypes($op1.start, $op2.start, op, $op1.start.getExprType());
                    op.setExprType(type);
                } else {
                    op.setExprType(checker.VOID_TYPE);
                }
            }
    ;

do_while_statement
    :   ^(op=DO statements_block WHILE cond=expr)
        {
            checker.checkType($cond.start, checker.BOOL_TYPE);
            op.setExprType(checker.VOID_TYPE);
        }
    ;

varlist
    :   expr (COMMA expr)*
    ;

operand
    :   IDENTIFIER
    |   literal
    |   expr
    ;

literal
    :   NUMBER
    |   CHARLITERAL
    |   boolliteral  
    ;

boolliteral
        :    TRUE | FALSE
        ;

语法.g 是:

grammar Awesome;

options {
    k=1;                                // LL(1) - do not use LL(*)
    language=Java;                      // target language is Java (= default)
    output=AST;                         // build an AST
}

tokens {
    COLON       =   ':'     ;
    COMMA       =   ','     ;
    SEMICOLON   =   ';'     ;
    LPAREN      =   '('     ;
    RPAREN      =   ')'     ;
    LBRACKET    =   '{'     ;
    RBRACKET    =   '}'     ;

    // keyword for AST
    DECL        =   'decl'  ;
    UNARYEXPR   =   'unexpr';
    COMPOUND    =   'compound';
    ASSIGNMENT  =   'ASSIGNMENT';

    // operators
    BECOMES     =   '='     ;
    PLUS        =   '+'     ;
    MINUS       =   '-'     ;
    MULTIPLY    =   '*'     ;
    DIVIDE      =   '/'     ;
    MODULUS     =   '%'     ;
    EXCLAMATION =   '!'     ;

    // comparators
    AND             =   'AND'   ;
    OR              =   'OR'    ;
    SMALLER         =   '<'     ;
    SMALLEREQUAL    =   '<='    ;
    GREATER         =   '>'     ;
    GREATEREQUAL    =   '>='    ;
    EQUAL           =   '=='    ;
    NOTEQUAL        =   '!='    ;  

    // keywords
    PROGRAM     =   'program'   ;
    RETURN      =   'return'   ;
    VAR         =   'var'       ;
    CONST       =   'const'     ;
    INPUT       =   'input'     ;
    OUTPUT      =   'output'     ;
    IF          =   'if'        ;
    THEN        =   'then'      ;
    ELSE        =   'else'      ;
    DO          =   'do'        ;
    WHILE       =   'while'     ;

    // types
    INTEGER     =   'int'   ;
    BOOLEAN     =   'bool'  ;
    CHAR        =   'char'  ;
    STRING      =   'string';

    TRUE        =   'true';
    FALSE       =   'false';

}

// Parser rules
start
    : program
    ;

program
    :   (statement)+ EOF
            ->  ^(PROGRAM statement+)
    ;

statement
    :   IDENTIFIER assignment_statement SEMICOLON!
    |   output_stat SEMICOLON!
    |   input_stat SEMICOLON!
    |   if_statement
    |   do_while_statement
    |   declaration SEMICOLON!
    ;

declaration
    :   type CONST? IDENTIFIER (BECOMES expr)?
        -> ^(DECL type CONST? IDENTIFIER (BECOMES expr)?)
    ;

type
    :   BOOLEAN
    |   INTEGER
    |   CHAR
    |   STRING
    ;

statements_block
    :   LBRACKET statement* RBRACKET
    ;

assignment_statement
    : (BECOMES^ expr)+
    ;

expr
    :   expr_or
    |   expr_compound
    |   if_statement
    ;

expr_unary
    :   operand
    |   PLUS operand -> ^(UNARYEXPR PLUS operand)
    |   MINUS operand -> ^(UNARYEXPR MINUS operand)
    |   EXCLAMATION operand -> ^(UNARYEXPR EXCLAMATION operand)
    ;

expr_multiply
    :   expr_unary ((MULTIPLY | DIVIDE | MODULUS)^ expr_unary)*
    ;

expr_plus
    :   expr_multiply ((PLUS | MINUS)^ expr_multiply)*
    ;

expr_compare
    :   expr_plus ((SMALLER | SMALLEREQUAL | GREATEREQUAL | GREATER | EQUAL | NOTEQUAL)^ expr_plus)*
    ;

expr_and
    :   expr_compare (AND^ expr_compare)* 
    ;

expr_or
    :   expr_and (OR^ expr_and)* 
    ;

expr_compound
    :   LBRACKET (statement)* RETURN expr SEMICOLON RBRACKET
        -> ^(COMPOUND (statement)* expr)
    ;

do_while_statement
    :   DO^ statements_block WHILE expr
    ;

if_statement
    :   IF^ expr (statements_block|operand) (ELSE (statements_block|operand))?
    ;

input_stat
    :   INPUT^ LPAREN! varlist RPAREN!
    ;

output_stat
    :   OUTPUT^ LPAREN! varlist RPAREN!
    ;

varlist
    :   expr (COMMA expr)*
    ;

operand
    :   IDENTIFIER
    |   literal
    |   LPAREN! expr RPAREN!
    ;

literal
    :   NUMBER
    |   CHARLITERAL
    |   boolliteral  
    ;

boolliteral
        :    TRUE | FALSE
        ;

// Lexer rules

IDENTIFIER
    :   LETTER (LETTER | DIGIT)*
    ;

CHARLITERAL
    :   '\'' LETTER '\''
    ;

NUMBER
    :   DIGIT+
    ;

COMMENT
    :   '[' .* ']' 
            { $channel=HIDDEN; }
    ;

WS
    :   (' ' | '\t' | '\f' | '\r' | '\n')+
            { $channel=HIDDEN; }
    ;

fragment DIGIT  :   ('0'..'9') ;
fragment LOWER  :   ('a'..'z') ;
fragment UPPER  :   ('A'..'Z') ;
fragment LETTER :   LOWER | UPPER ;

// EOF

编辑:我发现

assignment_statement
        : (op=BECOMES^ e=expr)+

是错误,应该是

assignment_statement
        : (op=BECOMES e=expr)+

它编译没有错误!:D 谢谢你的帮助!

4

2 回答 2

1

我找到

assignment_statement
        : (op=BECOMES^ e=expr)+

是错误,应该是

assignment_statement
        : (op=BECOMES e=expr)+

我没有更改语法文件选项中的任何内容。

它现在编译没有错误!:D 谢谢你的帮助!

于 2012-06-28T12:34:52.990 回答
0

但我认为output=AST不能用于树行者。

当然可以。这个想法是您的第一个语法将输入文本转换为节点树,然后树语法执行一些其他操作。如果您正在制作一个编译器,那么可能会在解析和代码生成阶段之间使用 tree walker。您的输出类型必须是 AST,以便流程的下一阶段可以完成它的工作......即使您实际上没有对树进行任何更改或对其进行任何其他操作。

您的问题是树语法中的这一行:

ASTLabelType = ANode

在树语法中,它期望传入的 AST 具有该类型的节点,但您的第一个语法不会创建该类型的节点。它创建默认的 CommonTree 节点。所以只需在第一个语法中设置同一行。

ASTLabelType什至在官方参考书中也没有看到很多文档,但是在 antlr wiki的“树构造”页面底部附近有一些关于自定义节点类型的注释。

于 2012-06-28T10:23:00.273 回答