Antlr4 语义谓词匹配用户定义的分隔符
Antlr4 semantic predicate to match user-defined delimiters
我有一个解析器,它接受两种不同类型的预定义 HTML-template 标签。一种以 "end*" 明确终止,另一种则不是,例如:
{% for ... %}
An explicitly-terminated tag
{% endfor %}
{% assign x = 'my implicitly-terminated tag' %}
这很好用,但我还需要允许用户从 C# 代码定义他自己的标签,所以我需要 Antlr4 来匹配这些示例:
{% mycustomtag %}
...
{% endmycustomtag %}
{% myunterminatedtag %}
我尝试使用 C# 语义谓词来匹配以 {% [USERDEFINED] %}
开头和结束标记 {% end[USERDEFINED] %}
的任何内容,如下所示:
tag: // ...
| custom_blocktag
| custom_tag
// ...
// an explicitly-terminated tag
custom_blocktag: TAGSTART custom_block_start_tag customtagblock_expr* TAGEND custom_blocktag_block TAGSTART custom_block_end_tag TAGEND { _localctx.custom_block_end_tag().GetText().Equals("end" + _localctx.custom_block_start_tag().GetText()) }?;
// an implicitly-terminated tag
custom_tag: TAGSTART tagname customtag_expr* TAGEND ;
不幸的是,只有当我在明确终止的标签之前没有出现隐式终止的标签时,这才能正常工作,但如果它以相反的顺序出现,它就会失败。
失败并出现错误:
{% xyz \"Test\" %}{% abc \"hello\"%}...{% endabc %}
但是,这工作正常:
{% abc \"hello\"%}...{% endabc %}{% xyz \"Test\" %}
据我了解,如果我希望语义谓词阻止匹配成功(而不是匹配规则然后失败并产生错误),我将需要左侧的语义谓词。但是,如果语义谓词在左侧,则它不会有任何值---所以我不确定如何进行。
有没有一种方法可以编写解析器规则,以便我可以定义这两种情况?
正如您所描述的问题,可以从任何一个标记确定的唯一句法确定性是配对集的结束标记的名称以 'end' 开头。开始标签出现在结束标签之前是真正的语义关联('for' -> 'endfor' 关系可用于确认关联,但在句法上并没有真正帮助)。
最好的通用方法是在解析器中处理句法问题,在解析树遍历器中处理语义问题。在这里,初步检查每个标签并构建 table 开始和结束标签关联很容易。
因此,只需在解析器中识别标签,而无需尝试限定为开始、结束或单例。
tag: TBEG
( id expression // assign etc
| expression // for etc
| id // endfor etc
)
TEND // { processTag($tag); } // alternate solution
;
实际上,您可以通过向标记规则添加操作来完全在解析器中获得相同的结果。此操作将创建标签并将遇到的标签添加到标签 table。添加结束命名标签时,先前的标签将被标记为开始标签。
如果您打算实施其他助行器(可能需要实施标签表达式),最好再添加一个以预先限定标签。
我有一个解析器,它接受两种不同类型的预定义 HTML-template 标签。一种以 "end*" 明确终止,另一种则不是,例如:
{% for ... %}
An explicitly-terminated tag
{% endfor %}
{% assign x = 'my implicitly-terminated tag' %}
这很好用,但我还需要允许用户从 C# 代码定义他自己的标签,所以我需要 Antlr4 来匹配这些示例:
{% mycustomtag %}
...
{% endmycustomtag %}
{% myunterminatedtag %}
我尝试使用 C# 语义谓词来匹配以 {% [USERDEFINED] %}
开头和结束标记 {% end[USERDEFINED] %}
的任何内容,如下所示:
tag: // ...
| custom_blocktag
| custom_tag
// ...
// an explicitly-terminated tag
custom_blocktag: TAGSTART custom_block_start_tag customtagblock_expr* TAGEND custom_blocktag_block TAGSTART custom_block_end_tag TAGEND { _localctx.custom_block_end_tag().GetText().Equals("end" + _localctx.custom_block_start_tag().GetText()) }?;
// an implicitly-terminated tag
custom_tag: TAGSTART tagname customtag_expr* TAGEND ;
不幸的是,只有当我在明确终止的标签之前没有出现隐式终止的标签时,这才能正常工作,但如果它以相反的顺序出现,它就会失败。
失败并出现错误:
{% xyz \"Test\" %}{% abc \"hello\"%}...{% endabc %}
但是,这工作正常:
{% abc \"hello\"%}...{% endabc %}{% xyz \"Test\" %}
据我了解,如果我希望语义谓词阻止匹配成功(而不是匹配规则然后失败并产生错误),我将需要左侧的语义谓词。但是,如果语义谓词在左侧,则它不会有任何值---所以我不确定如何进行。
有没有一种方法可以编写解析器规则,以便我可以定义这两种情况?
正如您所描述的问题,可以从任何一个标记确定的唯一句法确定性是配对集的结束标记的名称以 'end' 开头。开始标签出现在结束标签之前是真正的语义关联('for' -> 'endfor' 关系可用于确认关联,但在句法上并没有真正帮助)。
最好的通用方法是在解析器中处理句法问题,在解析树遍历器中处理语义问题。在这里,初步检查每个标签并构建 table 开始和结束标签关联很容易。
因此,只需在解析器中识别标签,而无需尝试限定为开始、结束或单例。
tag: TBEG
( id expression // assign etc
| expression // for etc
| id // endfor etc
)
TEND // { processTag($tag); } // alternate solution
;
实际上,您可以通过向标记规则添加操作来完全在解析器中获得相同的结果。此操作将创建标签并将遇到的标签添加到标签 table。添加结束命名标签时,先前的标签将被标记为开始标签。
如果您打算实施其他助行器(可能需要实施标签表达式),最好再添加一个以预先限定标签。