ANTLR(语言识别的另一工具)的简介之二[翻译]

    技术2022-05-11  159

    亲和的ANTLR语法的介绍  

    通过例子来逐渐学习ANTLR是最好的。 一个简单计算器常被用来入门,原因很简单:它简单易懂。这有许多给ANTLR的相似例子和教程,但是我会使用我自己的语言来描述一个计算器。首先我们会创建一些可以直接对简单表达式求值的程序。然后,我会生成树结构,并计算这些树来得到同样的答案。

    当你知道最终你需要将一个字符输入流分解成多个记号时,则好的开始就是去思考一个表达式的文法结构。

    语法向导执行

    语言识别

    我们接受了包含+,-和*的算术表达式,如3+4*5-1,或是可以增强求值顺序的括号表达式,如(3+4)*5。

    全部ANTLR语法都是lexer,语法解析程序和语法树解析程序的子类。既然你应该开始在文法层面思考问题,你因该创建一个文法解析程序的子类。在类的声明之后,你可以使用扩展巴柯斯范式符号指定规则:

    class ExprParser extends Parser;

     

    expr:   mexpr ((PLUS|MINUS) mexpr)*

        ;     

     

    mexpr     

        :   atom (STAR atom)*

        ;   

     

    atom:   INT

        |   LPAREN expr RPAREN

        ;

    lexer遵从相似的模式,它只需要定义一些操作符和空白符。把lexer放进相同的文件,如expr.g,是要做的最容易的事情:

    class ExprLexer extends Lexer;

     

    options {

        k=2; // needed for newline junk

        charVocabulary='/u0000'..'/u007F'; // allow ascii

    }

     

    LPAREN: '(' ;

    RPAREN: ')' ;

    PLUS  : '+' ;

    MINUS : '-' ;

    STAR  : '*' ;

    INT   : ('0'..'9')+ ;

    WS    : ( ' '

            | '/r' '/n'

            | '/n'

            | '/t'

           )

            {$setType(Token.SKIP);}

          ;   

    从这个语法定义文件expr.g生成程序(Java),可以运行ANTLR如下:

    $ java antlr.Tool expr.g

    ANTLR Parser Generator   Version 2.7.2   1989-2003 jGuru.com

    $

    ANTLR 产生什么?

    当发现没有必要完成这个教程时,你可能看到ANTLR在识别程序文件里面生成了什么,并发现这很有启发。ANTLR生成了识别程序,这些程序模拟你通过手工递归下推的语法分析程序来创建的东西;而另一方面,yacc及其朋友在模拟下推自动机的时候生成满是整形数的表。

    ANTLR 将产生下列文件:

    ExprLexer.java

    ExprParser.java

    ExprParserTokenTypes.java

    ExprParserTokenTypes.txt

    如果你看一下里面的内容,比如ExprParser.java,你会看到它为文件expr.g中有解析语法定义的每条规则生成一个方法。比如,mexpr合atom规则的代码看起来类似如下代码:

    public void mexpr() {

      atom();

      while ( LA(1)==STAR ) {

        match(STAR);

        atom();

      }

    }

     

    public void atom() {

      switch ( LA(1) ) { // switch on lookahead token type

        case INT :

          match(INT);

          break;

        case LPAREN :

          match(LPAREN);

          expr();

          match(RPAREN);

          break;

        default :

          // error

      }

    }

    注意到规则定义被翻译成了方法调用,而记号定义则被译成match(TOKEN)函数调用。则关于创建一种语法parser中仅有的难事就是计算前瞻信息。

    记号类别类定义了你的词法分析程序(lexer)和parser所使用到的所有记号类别数字常量:

    // $ANTLR 2.7.2: "expr.g" -> "ExprParser.java"$

     

    public interface ExprParserTokenTypes {

            int EOF = 1;

            int NULL_TREE_LOOKAHEAD = 3;

            int PLUS = 4;

            int MINUS = 5;

            int STAR = 6;

            int INT = 7;

            int LPAREN = 8;

            int RPAREN = 9;

            int WS = 10;

    }

    测试lexer/parser

    为了在实际中使用在ExprParser.java中作为结果的parser,须像如下所示来使用main()函数:

    import antlr.*;

    public class Main {

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

            ExprLexer lexer = new ExprLexer(System.in);

            ExprParser parser = new ExprParser(lexer);

            parser.expr();

        }

    }

    $ java Main

    3+(4*5)

    $

    给错误的输入:

    $ java Main

    3++

    line 1:3: unexpected token: +

    $

    或者

    $ java Main

    3+(4

    line 1:6: expecting RPAREN, found 'null'

    $


    最新回复(0)