//
一般的想法是,在其中一个序列或/*
出现之前,一切都是代码(也称为非注释) 。您可以使用如下规则反映这一点:
rule(:code) {
(str('/*').absent? >> str('//').absent? >> any).repeat(1).as(:code)
}
正如我在评论中提到的,字符串存在一个小问题。当注释出现在字符串中时,它显然是字符串的一部分。如果您要从代码中删除注释,那么您将更改此代码的含义。因此,我们必须让解析器知道字符串是什么,其中的任何字符都属于它。另一件事是转义序列。例如"foo \" bar /*baz*/"
,包含文字双引号的字符串实际上会被解析为"foo \"
,然后是一些代码。这当然是需要解决的问题。我编写了一个完整的解析器来处理上述所有情况:
require 'parslet'
class CommentParser < Parslet::Parser
rule(:eof) {
any.absent?
}
rule(:block_comment_text) {
(str('*/').absent? >> any).repeat.as(:comment)
}
rule(:block_comment) {
str('/*') >> block_comment_text >> str('*/')
}
rule(:line_comment_text) {
(str("\n").absent? >> any).repeat.as(:comment)
}
rule(:line_comment) {
str('//') >> line_comment_text >> (str("\n").present? | eof)
}
rule(:string_text) {
(str('"').absent? >> str('\\').maybe >> any).repeat
}
rule(:string) {
str('"') >> string_text >> str('"')
}
rule(:code_without_strings) {
(str('"').absent? >> str('/*').absent? >> str('//').absent? >> any).repeat(1)
}
rule(:code) {
(code_without_strings | string).repeat(1).as(:code)
}
rule(:code_with_comments) {
(code | block_comment | line_comment).repeat
}
root(:code_with_comments)
end
它会解析你的输入
word0
// line comment
word1 // line comment
phrase /* inline comment */ something
/* multiline
comment */
到这个 AST
[{:code=>"\n word0\n "@0},
{:comment=>" line comment"@13},
{:code=>"\n word1 "@26},
{:comment=>" line comment"@37},
{:code=>"\n phrase "@50},
{:comment=>" inline comment "@61},
{:code=>" something \n "@79},
{:comment=>" multiline\n comment "@94},
{:code=>"\n"@116}]
要提取除注释之外的所有内容,您可以执行以下操作:
input = <<-CODE
word0
// line comment
word1 // line comment
phrase /* inline comment */ something
/* multiline
comment */
CODE
ast = CommentParser.new.parse(input)
puts ast.map{|node| node[:code] }.join
这将产生
word0
word1
phrase something