设计规则的两种不同方式(检测像##<Text>##这样的标记)

Two Different Ways of Designing a Rule (Detecting Tokens Like ##<Text>##)

我正在为基于 markdown 的语言设计语法,但没有上下文感知。

例如,我想检测像## ## 这样的标记。

我发现了两种不同的设计规则的方法,但我不太确定哪种方法是最好的方法。

第一种方式:定义更复杂的标记和简单的规则。

fragment
HEAD
    : '#'
    ;

fragment
HEADING_TEXT
    : (~[#]|'\#')+?
    ;

SUBHEADLINE
    : HEAD HEAD HEADING_TEXT HEAD HEAD
    ;

subheadline
    : SUBHEADLINE
    ;

由于片段 HEAD 和 HEADING_TEXT 会到达解析器。我在 IntelliJ 中制作原型,解析效果很好。并且错误消息显示类似 "missing SUBHEADLINE" 什么对主应用程序有用(我想我可以轻松地将这些错误更改为人类可读的错误)。

第二种方法:更简单的标记和更复杂的解析器规则。

HEAD
    : '#'
    ;

HEADING_TEXT
    : (~[#]|'\#')+?
    ;

subheadline
    : HEAD HEAD HEADING_TEXT HEAD HEAD
    ;

也很好用。错误更具体,可能不太适合将它们转换为人类可读的错误。

但我总体上不确定我应该遵循哪种方法以及为什么?!在这种情况下,更复杂的标记更容易编写,因为不会像普通编程语言那样包含任何复杂的规则。但感觉这不是正确的做法。

两种方式都有自己的行为,这取决于你需要什么来决定使用什么。以您所做的方式在词法分析器中定义副标题不允许 skipped/hidden 之间的标记。 '#',这可能是您想要的。在解析器中这样做允许例如# /*acomment*/headline## 这可能不是预期的行为。此外,我会将严格属于同一类的事物合并为一个规则。例如,您的第二个变体中的 HEADING_TEXT 可能会匹配您希望以不同方式匹配的输入。而是完全按照语言指示定义副标题:

SUBHEADING: '##' .*? '##';

这比你的更简单的变体更简洁,同时仍然不允许在标记之间跳过输入。