如何在Perl6中实现递归语法

Vad*_*man 10 perl6 raku

我正在尝试使用Perl6语法实现Markdown解析器并且遇到了blockquotes.块引用段落不能嵌套括号来表示,因为它是专门格式化行的列表.但从语义上讲,它是一个嵌套的降价.

基本上,这一切都归结为以下定义:

    token mdBlockquote {
        <mdBQLine>+ {
            my $quoted = [~] $m<mdBQLine>.map: { $_<mdBQLineBody> };
        }
    }
Run Code Online (Sandbox Code Playgroud)

mdBQLine令牌的实际实现与此无关.唯一需要注意的是,mdBQLineBody密钥包含>已经剥离的实际引用行.毕竟,对于一个块:

> # quote1
> quote2
>
> quote3
quote3.1
Run Code Online (Sandbox Code Playgroud)

$quoted标将包含:

# quote1
quote2

quote3
quote3.1
Run Code Online (Sandbox Code Playgroud)

现在,重点是解析上述数据并将其注入Match对象$/.这就是我完全陷入困境的地方.最明显的解决方案:

    token mdBlockquote {
        <mdBQLine>+ {
            my $quoted = [~] $m<mdBQLine>.map: { $_<mdBQLineBody> };
            $<mdBQParsed> = self.parse( $quoted, actions => self.actions );
        }
    }
Run Code Online (Sandbox Code Playgroud)

一次失败有两个原因:第一,$/是一个只读对象; 第二,.parse有效地修改它使得无法向原始树中注入任何东西.

是否有任何解决方案,然后分析解析数据,提取和重新解析blockquotes,重复......?

rai*_*iph 6

在@HåkonHægland评论中稍微扩展一下......

$/ 是一个只读对象...有效地使得无法向原始树中注入任何东西.

不完全的:

  • 从讽刺的角度来说,它$/是一个符号而不是一个对象,无论它是否与一个对象绑定.像P6中的任何其他符号一样,它总是可以自由反弹.(is rw将永远有效.)

  • 但你所指的是任务.赋值的语义由分配给的项决定.如果它们是不是容器的普通对象,那么它们将不支持左值语义,is copy如果您尝试分配它们,则会出现错误.一个$/ := 42目标是在这个意义上是不可改变的.

您可以做的是Cannot modify an immutable ...使用上面的Match方法挂起任意对象的任意数据.(该Match例程调用此方法.make.)这是您在解析树中存储自定义数据的方法.

要访问在解析树/ make对象的给定节点中所做的内容,请在该节点上调用$/(或Match哪个是同义词).

通常,您.made对解析树中较高节点的要求包括为较低级别节点所做的操作.

请尝试以下未经测试的代码,看看你得到了什么,然后评论它是否失败,你无法找到一种方法使它工作,或从那里考虑上面的最后两段考虑,并评论如何它的工作原理:

token mdBlockquote {
    <mdBQLine>+ {
        make .parse: [~] $m<mdBQLine>.map: { $_<mdBQLineBody> };
    }
}
Run Code Online (Sandbox Code Playgroud)


Vad*_*man 5

好的,这是我用过的最终解决方案.语法规则如下所示:

    token mdBlockquote {
        <mdBQLine>+ {
            my $m = $/;
            my $bq-body =  [~] $m<mdBQLine>.map( { $_<mdBQLineBody> } ); 
            $m.make(
                self.WHAT.parse(
                    $bq-body,
                    actions => self.actions.clone,
                )
            );
        }
    }
Run Code Online (Sandbox Code Playgroud)

这里的重要技巧是备份$/,$m因为.parse它将取代它.

Blockquote主体$bq-body在调用之前被预取,.parse因为如果表达式直接作为参数传递,则存在令人困惑的副作用.

.parse被要求self.WHAT避免搞乱当前的语法对象.

此规则最终将$m.ast包含一个Match对象,该对象又包含动作生成的数据.相应的操作方法然后执行以下操作:

    method mdBlockquote ($m) {
        my $bq = self.makeNode( "Blockquote" );
        $bq.push( $m.ast.ast );
        $m.make( $bq );
    }
Run Code Online (Sandbox Code Playgroud)

由于actions对象构建了一个简化的AST,适用于将markdown简单地转换为其他格式,这里发生的是它获取由递归生成的树的brach .parse并将其移植到主树中.

这很好,因为代码支持嵌套的块引用,不需要特殊处理.不好的是,它仍然是很多额外的代码,而是类似的东西:

    token mdBlockquote {
        <mdBQLine>+ $<mdBQBody>={
            my $bq-body =  [~] $<mdBQLine>.map( { $_<mdBQLineBody> } ); 
            self.WHAT.parse(
                $bq-body,
                actions => self.actions.clone,
            );
        }
    }
Run Code Online (Sandbox Code Playgroud)

看起来更好,并且不需要超出正常职责的行动对象干预.