第三章:上下文无关文法进阶

高级属性文法的说明和使用

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
    ;

(语法 3-1)

在 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 属性&

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猫步旅人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值