我编写了一个简单的解析器来尝试了解 ANTLR4 如何在具有匹配标签的 HTML 风格语言上工作。例如,如果有一个标签
<p> ... </p>
我认为它应该与一个规则相匹配:
p: '<' 'p' CLASS_NAME? '>' tag_block '<' '/' 'p' '>' ;
这看起来相当繁重,因为如果每个规则可以包含其他所有内容,我必须以这种方式手动编写每个规则。是否有一种通用的方法来编写规则,即对于可以包含其他标签的每种标签,前后的尖括号必须匹配?或者我只需为每一个都写一下:
p: '<' 'p' CLASS_NAME? '>' tag_block '<' '/' 'p' '>' ;
div: '<' 'div' CLASS_NAME? '>' tag_block '<' '/' 'div' '>' ;
canvas: '<' 'canvas' CLASS_NAME? '>' tag_block '<' '/' 'canvas' '>' ;
是否可以编写一个通用的复合标签,例如:
compoundtag: '<' ('p' | 'div' | 'canvas') CLASS_NAME? '>' tag_block '<' '/' ( MATCHING EARLIER NAME???) '>'
我确信我犯了更多错误,所以这里是完整的语法文件:
grammar simple_grail;
prog: tag_block ;
tag_block: (tag)*;
/* root definition of any tag in Grail */
tag:
p | div | canvas | rect | button | txt | textlit;
p: '<' 'p' CLASS_NAME? '>' tag_block '<' '/' 'p' '>' ;
div: '<' 'div' CLASS_NAME? '>' tag_block '<' '/' 'div' '>' ;
canvas: '<' 'canvas' CLASS_NAME? '>' tag_block '<' '/' 'canvas' '>' ;
rect:
'<' 'rect' CLASS_NAME?
'x' ASSIGN_COORD 'y' ASSIGN_COORD 'w' ASSIGN_COORD 'h' ASSIGN_COORD
'/>' ;
button:
'<' 'button' (CLASS_NAME)? (NAME)? 'text' ASSIGN_TEXT '/>' ;
txt:
'<' 'text' 'x' ASSIGN_COORD 'y' ASSIGN_COORD '/>' ;
textlit:
TEXT?
;
CLASS_NAME: [a-zA-Z_] [a-zA-Z_0-9]* ;
ASSIGN_COORD: '=' [0-9]+ ;
name: 'name' '=' NAME ;
NAME: [A-Za-z_][A-Za-z_0-9]* ;
ASSIGN_TEXT: '=' ('"' [^"]* '"' | '\'' [^']* '\'');
TEXT: [^<]+ ;
我想解析第一个小程序:
<p>text goes here</p>
我希望 textlit 应该与文本匹配,对于像 p 这样的文本周围的标签,我应该覆盖 exitTextlit() 方法和 exitP() 来决定要做什么。如果我截取了 EnterP(),那么里面还没有文本。是对的吗?
有一个警告说我可以匹配空字符串,并且我知道如果我跳过空格会产生错误,但我不明白程序应该如何匹配:
<p></p>
如果textlit不能为空。
(至少)有两件事出了问题:
textlit
不应匹配空字符串,因为textlit
正在重复tag_block: (tag)*;
[^<]+
(匹配一个或多个字符'^'
或'<'
),而是执行以下操作:(~[<]+
匹配除 之外的一个或多个字符'<'
)当您修复这些点时,您将看到为输入创建以下标记
<p>text goes here</p>
:正如你所看到的,
p>text goes here
这是不正确的。解决这个问题的一种方法是使用词法模式。您可以查看 ANTLR 存储库中的 HTML 语法:https://github.com/antlr/grammars-v4/tree/master/html我总是只通过一个小的 Java 类进行测试,但如果您使用
TestRig
,请确保您正在使用最近生成的解析器类并编译它们。1. 生成新类
2.编译生成的类
3. 运行
TestRig
这会产生错误:
鉴于
TEXT
匹配太多,这是预期的。