高级属性文法的说明和使用
lexer 和 parse 的 options 用法
antlr4 允许在生成的语法分析器中,通过一些 named actions 的操作,以自定义的方式插入一些代码片段。这些 action 操作是与特定平台相关的,比如 c++ 和 java 的就不一样。通用的几个操作如下
- @parser::header
- @parser::members
- @lexer::header
- @lexer::members
@parser::header
和 @lexer::header
中的内容,将会出现在所有生成的文件的第一行,所以一般通过这种方式,来添加 license 或者 版本信息非常合适。如下所示
@lexer::header {
/* This is a test for lexer::header */
}
在 Lexer 词法文件中加入上述的 named action,生成的所有 Lexer 的 h 和 cpp 文件的最上面,就会出现如下的注释信息
/* This is a test for lexer::header */
// Generated from MathLexer.g4 by ANTLR 4.7.1
parser 也是同样。
@parser::members
和 @lexer::members
是在 lexer 或者 parser 的类中,添加 public 属性的成员,比如语法 3-1
parser grammar MathParser;
options {
tokenVocab=MathLexer;
}
@parser::members {
std::string filename;
}
compileUnit
: expr EOF
;
expr
: LEFT_PAREN expr RIGHT_PAREN # parenExpr
| op=(ADD | SUB) expr # unaryExpr
| left=expr op=(MUL | DIV) right=expr # mulDivExpr
| left=expr op=(ADD | SUB) right=expr # addSubExpr
| func=ID LEFT_PAREN expr (COMMA expr)? RIGHT_PAREN # funcExpr
| NUM # numExpr
;
在 parser 中添加了 filename 这个 public 属性的成员,在生成的 Parser 的 h 头文件的类定义中,会将 filename 作为 public 成员添加到类中
class MathParser : public antlr4::Parser {
public:
...
MathParser(antlr4::TokenStream *input);
~MathParser();
virtual std::string getGrammarFileName() const override;
virtual const antlr4::atn::ATN& getATN() const override { return _atn; };
virtual const std::vector<std::string>& getTokenNames() const override { return _tokenNames; }; // deprecated: use vocabulary instead.
virtual const std::vector<std::string>& getRuleNames() const override;
virtual antlr4::dfa::Vocabulary& getVocabulary() const override;
std::string filename;
class CompileUnitContext;
class ExprContext;
...
};
另外,在 antlr4 的 c++ 平台中,还额外支持一些其他的 actions 的操作。在词法中,支持
- @lexer::preinclude - 在生成的 lexer 的 h 头文件或者 cpp 源文件的第一个
#include
之前,插入 action 中需要插入的#include
的头文件信息。这通常用于那些必须出现在最前面的头文件或者系统库方面的头文件 - @lexer::postinclude - 在生成的 lexer 的头文件或者源文件的最后一个
#include
之后,但是在类定义之前,插入 action 中需要插入的#include
的头文件信息,有一些头文件的包含关系必须放在最后,笔者之前遇到过 plog 就出现过这种情况 - @lexer::context - 在生成的 lexer 的头文件中,插入在类申明之前,一本可以是需要预先定义的类型或者类定义,或者 typedef 等。
比如在 MathLexer.g4
中插入
@lexer::postinclude {
#include <string>
#include <vector>
}
@lexer::context {
typedef std::vector<string> Strings;
}
那么在程成的 MathLexer.h
头文件中,会插入如下代码片段
#include <string>
#include <vector>
typedef std::vector<string> Strings;
class MathLexer : public antlr4::Lexer { ...
- @lexer::declarations - 在生成的 lexer 的头文件中,在类的定义中的 private 属性部分,添加 action 中所需要插入的内容,比如
@lexer::declarations {
int private_mem;
}
在 MathLexer.h
的 private 部分就会插入
int private_mem;
这个成员。当然,取巧一点,适用于 c++ 的语法规则,可以在 action 中插入的代码片段中,加上属性规则,比如
@lexer::declarations {
public:
int private_mem;
}
这样插入的代码片段,虽然在 private 下面插入的,但是因为指定了 public 属性&