我正在尝试使用 Perl6 语法实现一个 Markdown 解析器,但遇到了块引号。块引用段落不能用嵌套大括号表示,因为它是特定格式的行的列表。但从语义上讲,它是一个嵌套的降价。
基本上,这一切都归结为以下定义:
token mdBlockquote {
<mdBQLine>+ {
my $quoted = [~] $m<mdBQLine>.map: { $_<mdBQLineBody> };
}
}
mdBQLine
令牌的实际实现在这里无关紧要。唯一需要注意的是,mdBQLineBody
键包含实际引用的行,>
已被剥离。毕竟,对于一个块:
> # quote1
> quote2
>
> quote3
quote3.1
$quoted
标量将包含:
# quote1
quote2
quote3
quote3.1
现在,重点是将上述数据解析并注入回Match
对象$/
。这就是我完全不知道的地方。最明显的解决方案:
token mdBlockquote {
<mdBQLine>+ {
my $quoted = [~] $m<mdBQLine>.map: { $_<mdBQLineBody> };
$<mdBQParsed> = self.parse( $quoted, actions => self.actions );
}
}
同时失败的原因有两个:首先,$/
是只读对象;其次,.parse
有效地修改了它,使其无法将任何内容注入原始树中。
是否有任何解决方案,然后对解析的数据进行后分析,提取和重新解析块引用,重复...?
稍微扩展一下@HåkonH ægland的评论...
$/
是只读对象...有效地使无法将任何东西注入原始树。
差一点:
-
从学究上讲,
$/
是一个符号,而不是一个对象,无论它是否绑定到一个符号上。如果它是一个参数(并且没有用is rw
或is copy
声明),那么它是只读的,但除此之外它可以自由回弹,例如。$/ := 42
. -
但你指的是对键的分配。赋值的语义由要分配到的项确定。如果它们是不是容器的普通对象,则它们将不支持左值语义,并且如果您尝试分配给它们,则会收到
Cannot modify an immutable ...
错误。从这个意义上说,Match
对象是不可变的。
您可以做的是通过在其上使用.make
方法将任意数据挂在任何Match
对象上。(make
例程在$/
上调用此方法。这就是在解析树中存储自定义数据的方式。
要访问在解析树/Match
对象的给定节点中制作的内容,请在该节点上调用.made
(或.ast
同义词)。
通常,您为解析树中的较高节点make
的内容包括为较低级别的节点所做的内容。
请尝试以下未经测试的代码,看看你得到了什么,然后评论它是否惨败并且你无法找到让它工作的方法,或者从那里构建,考虑到上面的最后两段,并评论它是如何工作的:
token mdBlockquote {
<mdBQLine>+ {
make .parse: [~] $m<mdBQLine>.map: { $_<mdBQLineBody> };
}
}
好的,这是我使用的最终解决方案。语法规则如下所示:
token mdBlockquote {
<mdBQLine>+ {
my $m = $/;
my $bq-body = [~] $m<mdBQLine>.map( { $_<mdBQLineBody> } );
$m.make(
self.WHAT.parse(
$bq-body,
actions => self.actions.clone,
)
);
}
}
这里的重要技巧是在$m
中备份$/
,因为.parse
将替换它。
在调用 Blockquote 正文之前,Blockquote 正文被预取到$bq-body
.parse
因为如果表达式直接作为参数传递,则会产生令人困惑的副作用。
.parse
在self.WHAT
上调用以避免弄乱当前语法对象。
此规则最终将生成包含Match
对象的$m.ast
,而该对象又将包含操作生成的数据。然后,相应的操作方法执行以下操作:
method mdBlockquote ($m) {
my $bq = self.makeNode( "Blockquote" );
$bq.push( $m.ast.ast );
$m.make( $bq );
}
由于 actions 对象构建了一个简化的 AST,适合将 markdown 简单转换为其他格式,因此这里发生的情况是获取由递归.parse
生成的该树的凹凸并将其移植到主树中。
这很好,因为代码支持开箱即用的嵌套块引用,不需要特殊处理。不好的是它仍然是很多额外的代码,而像这样:
token mdBlockquote {
<mdBQLine>+ $<mdBQBody>={
my $bq-body = [~] $<mdBQLine>.map( { $_<mdBQLineBody> } );
self.WHAT.parse(
$bq-body,
actions => self.actions.clone,
);
}
}
WHOULD看起来更好,并且不需要超出其正常职责的行动对象干预。 😀