【问题标题】:Don't read specific token at a given point不要在给定点读取特定令牌
【发布时间】:2019-04-29 14:59:40
【问题描述】:

在我的语法文件中,我希望 ANTLR 将我的输入读取为 2 个标记而不是 1 个。 在我的源文件中,我有值

12345.name

词法分析器使用

12345.

作为浮动令牌。在源文件的这个特定点,我希望 ANTLR 将其读取为

  • 12345 (INT)
  • 。 (点)
  • 姓名(姓名)

有没有办法告诉 ANTLR 它应该在某个给定点忽略 FLOAT-Types?

这是我当前的 .g4 文件:

grammar Quest;
import Lua;

@header {
package dev.codeflush.m2qc.antlr;
}

/*
prefixed everything with "m2" to avoid nameclashes
*/

m2QuestFile
    : m2Define* m2Quest* EOF
    ;

m2Define
    : 'define' NAME m2DefineValue
    ;

m2DefineValue
    : ~('\r\n' | '\r' | '\n')
    ;

m2Quest
    : 'quest' NAME 'begin' m2State* 'end'
    ;

m2State
    : 'state' NAME 'begin' (m2TriggerBlock | m2Function)* 'end'
    ;

m2TriggerBlock
    : 'when' m2Trigger ('or' m2Trigger)* ('with' exp)? 'begin' block 'end'
    ;

m2Function
    : 'function' NAME funcbody
    ;

m2Trigger
    : m2TriggerTarget DOT m2TriggerEvent DOT m2TriggerSubEvent DOT m2TriggerArgument
    | m2TriggerTarget DOT m2TriggerEvent DOT m2TriggerArgument
    | m2TriggerTarget DOT m2TriggerEvent
    | m2TriggerEvent
    ;

m2TriggerTarget
    : NAME
    | INT
    | NORMALSTRING
    ;

/*
not complete
*/
m2TriggerEvent
    : 'button'
    | 'enter'
    | 'info'
    | 'item_informer'
    | 'kill'
    | 'leave'
    | 'letter'
    | 'levelup'
    | 'login'
    | 'logout'
    | 'unmount'
    | 'target'
    | 'chat'
    | 'timer'
    | 'server_timer'
    ;

m2TriggerSubEvent
    : 'click'
    | 'chat'
    | 'arrive'
    ;

m2TriggerArgument
    : exp
    ;

DOT
    : '.'
    ;

我正在使用 https://github.com/antlr/grammars-v4/blob/master/lua/Lua.g4 的 Lua 语法

我当前的示例输入文件如下所示:

quest test begin
    state start begin
        when kill begin
        end

        when "12345".kill begin
        end

        when 12345.kill begin
        end
    end
end

前两个按预期工作,但第三个没有(因为词法分析器将 '12345.' 作为一个 FLOAT-Token 读取)

【问题讨论】:

  • 是否可以选择在全局浮点文字的点后要求至少一位数字?
  • @sepp2k 是的,这也适用于我。谢谢你:)

标签: antlr antlr4


【解决方案1】:

我的语法 where I wanted to issue multiple tokens (2 actually) for a single match 在特定条件下也有类似的需求(这里:当一个点直接跟在一个标识符后面时,包括一个关键字)。

// Special rule that should also match all keywords if they are directly preceded by a dot.
// Hence it's defined before all keywords.
// Here we make use of the ability in our base lexer to emit multiple tokens with a single rule.
DOT_IDENTIFIER:
    DOT_SYMBOL LETTER_WHEN_UNQUOTED_NO_DIGIT LETTER_WHEN_UNQUOTED* { emitDot(); } -> type(IDENTIFIER)
;

需要helper function 来发出额外的令牌:

/**
 * Puts a DOT token onto the pending token list.
 */
void MySQLBaseLexer::emitDot() {
  _pendingTokens.emplace_back(_factory->create({this, _input}, MySQLLexer::DOT_SYMBOL, _text, channel,
                                               tokenStartCharIndex, tokenStartCharIndex, tokenStartLine,
                                               tokenStartCharPositionInLine));
  ++tokenStartCharIndex;
}

这又需要对令牌生成进行自定义处理。您必须在令牌流中 override nextToken 方法,在返回下一个真实令牌之前考虑待处理令牌列表。

/**
 * Allow a grammar rule to emit as many tokens as it needs.
 */
std::unique_ptr<antlr4::Token> MySQLBaseLexer::nextToken() {
  // First respond with pending tokens to the next token request, if there are any.
  if (!_pendingTokens.empty()) {
    auto pending = std::move(_pendingTokens.front());
    _pendingTokens.pop_front();
    return pending;
  }

  // Let the main lexer class run the next token recognition.
  // This might create additional tokens again.
  auto next = Lexer::nextToken();
  if (!_pendingTokens.empty()) {
    auto pending = std::move(_pendingTokens.front());
    _pendingTokens.pop_front();
    _pendingTokens.push_back(std::move(next));
    return pending;
  }
  return next;
}

请记住:词法分析器规则仍会发布自己的令牌(我在这里设置为 IDENTIFIER),这意味着您只需发布额外的令牌。

【讨论】:

  • 谢谢,我会尽快尝试并接受您的回答,如果它适用于我的情况。因为这是一个私人项目,所以我可能需要几天的时间才能最终重新获得它。
  • 我可以成功地为我的项目实施您的示例并相应地接受您的回答。但是,我设法通过简单地从 Lua 语法中删除 FLOAT 令牌并将其替换为解析器规则来解决我的问题,因为我真的不需要那个特定的令牌来实现我的目的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-20
  • 2017-10-03
  • 2013-10-22
  • 2017-06-10
相关资源
最近更新 更多