在LaTeX中写论文时,我有这个.bib file用于参考管理:
@article{garg2017patch,
title={Patch testing in patients with suspected cosmetic dermatitis: A retrospective study},
author={Garg, Taru and Agarwal, Soumya and Chander, Ram and Singh, Aashim and Yadav, Pravesh},
journal={Journal of Cosmetic Dermatology},
year={2017},
publisher={Wiley Online Library}
}
@article{hauso2008neuroendocrine,
title={Neuroendocrine tumor epidemiology},
author={Hauso, Oyvind and Gustafsson, Bjorn I and Kidd, Mark and Waldum, Helge L and Drozdov, Ignat and Chan, Anthony KC and Modlin, Irvin M},
journal={Cancer},
volume={113},
number={10},
pages={2655--2664},
year={2008},
publisher={Wiley Online Library}
}
@article{siperstein1997laparoscopic,
title={Laparoscopic thermal ablation of hepatic neuroendocrine tumor metastases},
author={Siperstein, Allan E and Rogers, Stanley J and Hansen, Paul D and Gitomirsky, Alexis},
journal={Surgery},
volume={122},
number={6},
pages={1147--1155},
year={1997},
publisher={Elsevier}
}
如果有人想知道什么是bib文件,你可以找到详细的here。
我想用Perl 6解析这个以提取密钥和标题,如下所示:
garg2017patch: Patch testing in patients with suspected cosmetic dermatitis: A retrospective study
hauso2008neuroendocrine: Neuroendocrine tumor epidemiology
siperstein1997laparoscopic: Laparoscopic thermal ablation of hepatic neuroendocrine tumor metastases
你可以帮助我这样做,可能有两种方式:
这个答案旨在成为两者:
"$_[0]: $_[1]\n" .put
for (slurp 'derm.bib')
~~ m:g/ '@article{' (<-[,]>+) ',' \s+ 'title={' ~ '}' (<-[}]>+) /
(Qazxswpoi。)
我决定从熟悉P6的开发人员开始写几分钟就可以完成你在问题中指定的简单任务,如果他们不太关心新手的可读性。
我不打算提供解释。它只是做到了这一点。如果你是P6新手,它可能是压倒性的。如果是这样,请阅读我的其余答案 - 它需要更慢并且有全面的评论。也许回到这里看看其余的阅读后是否更有意义。
Run this code at glot.io
这几乎与“单一语句(高级用户)”代码完全相同 - 分为四个语句而不是一个语句。我可以让它更紧密地复制代码的第一个版本,但我做了一些改变,我将解释。我这样做是为了更清楚地表明P6故意将其功能作为可扩展且可重构的连续体,因此可以混合使用,并且呃匹配最适合给定用例的任何功能。
my \input = slurp 'derm.bib' ;
my \pattern = rule { '@article{' ( <-[,]>+ ) ','
'title={' ~ '}' ( <-[}]>+ ) }
my \articles = input.match: pattern, :global ;
for articles -> $/ { print "$0: $1\n\n" }
Perls以他们的印记而闻名。在P6中,如果你不需要它们,你可以“削减”它们。 Perls也以简洁的做事方式而闻名。 my \input = slurp 'derm.bib' ;
一次性完整地读取文件。
slurp
Perl 6模式通常称为regex或my \pattern = rule { '@article{' ( <-[,]>+ ) ','
'title={' ~ '}' ( <-[}]>+ ) }
。有几种类型的正则表达式/规则。模式语言是一样的;不同类型只是指示匹配引擎修改它处理给定模式的方式。
一个正则表达式/规则类型是P6相当于经典正则表达式。这些是用Rules或/.../
声明的。开放的“超级用户”代码中的正则表达式是这些正则表达式之一。它们的区别在于它们在必要时回溯,就像经典正则表达式一样。
没有必要回溯以匹配regex {...}
格式。除非您需要回溯,否则最好考虑使用其他规则类型之一。我已切换到使用关键字.bib
声明的规则。
用rule
声明的规则与用rule
(或regex
)声明的规则相同,除了A)它不回溯和B)它将其模式中的空格解释为对应于输入中的可能空格。你是否发现我在/.../
之前立刻放弃了\s+
?那是因为'title={'
会自动处理这个问题。
另一个区别是我写道:
rule
代替:
'title={' ~ '}' ( ... )
即在支架之后移动与支架之间的钻头匹配的图案,并在支架之间放置一个'title={' ( ... ) '}'
。它们匹配相同的整体模式。我本可以在高级用户~
模式中编写任何一种方式,并且在本节的/.../
模式中使用任何一种方式。但我希望这一部分更加面向“最佳实践”。我将推迟对这种差异的完整解释以及这种模式的所有其他细节,直到下面的“围兜”语法部分的解释。
rule
此行使用早期“超级用户”版本中使用的my \articles = input.match: pattern, :global ;
例程的方法形式。
m
与:global
相同。我可以用两种版本的方式编写它。
如果要搜索匹配的整个字符串,找到尽可能多的匹配项,而不仅仅是第一个匹配项,则在调用:g
方法(或:global
例程)时将:g
(或.match
)添加到参数列表中。然后该方法(或m
例程)返回m
列表而不是一个。在这种情况下,我们将得到三个,对应于输入文件中的三篇文章。
Match
objects
根据for articles -> $/ { print "$0: $1\n\n" }
,“P6 doc on $/是匹配变量......所以通常包含Match类型的对象。”它还提供了一些其他便利,我们在这里利用这些便利之一,如下所述。
$/
循环连续地将每个匹配对象(对应于您的示例文件中由语法成功解析的每篇文章)绑定到for
块内的符号$/
。
该模式包含两对括号。这些生成“位置捕获”。整个Match对象通过for
(postfix Positional subscripting)提供对其两个Positional捕获的访问。因此,在[]
区块内,for
和$/[0]
可以访问给定文章的两个位置捕获。但$/[1]
和$0
也是如此 - 因为标准P6将后面这些符号别名化为$1
和$/[0]
以方便您使用。
还在我这儿?
这个答案的后半部分构建并彻底解释了基于语法的方法。阅读它可以提供对上述解决方案的进一步了解。
但首先...
我想用Perl 6解析这个。任何人都可以帮忙吗?
P6可能使编写解析器比使用其他工具更乏味。但不那么乏味仍然乏味。 P6解析目前很慢。
在大多数情况下,当你想解析除了最简单的文件格式之外的任何东西时,实际的答案 - 特别是几十年前的众所周知的格式 - 是找到并使用现有的解析器。
您可以从$/[1]
开始,希望找到一个公开共享的“围兜”解析模块。一个纯粹的Perl 6或一个P6包装在非P6库周围。但在撰写本文时,“围兜”并没有匹配。
几乎可以肯定的是,“bib”解析C库已经可用了。它可能是最快的解决方案。你也可以在自己的P6代码中轻松优雅地使用打包为C lib的外部解析库,即使你不知道C.如果search for 'bib' on modules.perl6.org解释太多或太少,请考虑NativeCall并询问无论您需要或想要什么NativeCall帮助。
如果C lib不适合特定用例,那么您仍然可以通过visiting the freenode IRC channel #perl6使用Perl 5,Python,Ruby,Lua等编写的包。只需安装Perl 5,Python或任何你想要的包;确保它使用其他语言运行;安装适当的语言适配器;然后their Inline::*
language adapters包含导出的P6函数,类,对象,值等。
Perl 5适配器是最成熟的,所以我将以此为例。假设您使用Perl 5的Text :: BibTex软件包,现在希望将Perl 6与Perl 5中现有的Text :: BibTeX :: BibFormat模块一起使用。首先,设置Perl 5软件包,因为它们应该是自己的README软件包等。然后,在Perl 6中,写下类似的东西:
use the package and its features as if it were a P6 package
第一行是如何告诉P6您希望加载P5模块。 (除非use Text::BibTeX::BibFormat:from<Perl5>;
...
@blocks = $entry.format;
已经安装并正常工作,否则它将无效。但是如果你使用的是流行的Rakudo Perl 6软件包,那应该是。如果没有,你应该至少拥有模块安装程序Inline::Perl5
,这样你就可以运行zef
了。)
最后一行只是来自zef install Inline::Perl5
的@blocks = $entry->format;
线的机械P6平移。
好。足够“无聊”的实用建议。现在让我们尝试为您的问题中的示例创建一个足够好的P6解析器。
the SYNOPSIS of the Perl 5 Text::BibTeX::BibFormat
有了这个语法,我们现在可以编写如下内容:
# use Grammar::Tracer;
grammar bib {
rule TOP { <article>* }
rule article { '@article{' $<id>=<-[,]>+ ','
<kv-pairs>
'}'
}
rule kv-pairs { <kv-pair>* % ',' }
rule kv-pair { $<key>=\w* '={' ~ '}' $<value>=<-[}]>* }
}
生成与早期“超级用户”和“基本Perl 6”解决方案完全相同的输出 - 但使用语法/解析器方法。
die "Maybe use Grammar::Tracer?" unless bib.parsefile: 'derm.bib';
for $<article> { say .<id> ~ ': ' ~ .<kv-pairs><kv-pair>[0]<value> ~ "\n" }
如果解析失败,则返回值为# use Grammar::Tracer;
。 P6不会告诉你它到底有多远。你将无法理解你的解析失败的原因。
如果你没有Nil
,那么,当你的语法失败时,a better option (?)来帮助调试(如果你还没有安装它,首先安装它)。
use Grammar::Tracer
grammar bib {
关键字就像grammar
,但语法不仅可以包含像往常一样命名的class
s,还可以包含method
s,regex
s和token
s。
rule
除非另有说明,否则解析例程首先调用名为 rule TOP {
的rule
(或token
,regex
或method
)。
作为一个经验法则,如果你不知道你是否应该使用TOP
,rule
,regex
或token
进行一些解析,请使用method
。 (与token
模式不同,regex
s不会回溯,因此它们可以消除由于回溯导致不必要地缓慢运行的风险。)
但在这种情况下,我使用了token
。像rule
模式一样,token
s也避免回溯。但另外,它们在模式中的任何原子之后采用空白以自然的方式显着。这通常适用于解析树的顶部。 (令牌和偶尔的正则表达式通常适用于叶子。)
rule
规则末尾的空格意味着语法将匹配输入末尾的任何数量的空格。
rule TOP { <article>* }
在此语法中调用另一个命名规则(或令牌/正则表达式/方法)。
因为看起来每个bib文件应该允许任意数量的文章,我在<article>
的末尾添加了*
(zero or more quantifier)。
<article>*
我有时会排除类似 rule article { '@article{' $<id>=<-[,]>+ ','
<kv-pairs>
'}'
}
看起来的方式。我试着在这里这样做。
typical input是字符类的P6语法,如传统正则表达式语法中的<[...]>
。它更强大,但现在你需要知道的是[...]
中的-
表示否定,即与<-[,]>
语法中的^
相同。所以[^,]
尝试一个或多个角色的匹配,其中没有一个是<-[,]>+
。
,
告诉P6尝试匹配$<id>=<-[,]>+
右边的量化原子(即=
位)并将结果存储在当前Match对象中的关键<-[,]>+
。后者将挂在解析树的一个分支上;我们将准确到达以后的位置。
'id'
此正则表达式代码说明了几个方便的P6正则表达式功能之一。它表示你想要匹配由逗号分隔的零个或多个 rule kv-pairs { <kv-pair>* % ',' }
s。
(更详细地说,kv-pair
要求左侧的量化原子的匹配由右侧的原子分开。)
%
regex infix operator
这里的新位是 rule kv-pair { $<key>=\w* '={' ~ '}' $<value>=<-[}]>* }
。这是另一个方便的正则表达式功能。 '={' ~ '}'
解析分隔结构(在这种情况下,一个用regex Tilde operator开启器和={
更接近)与分隔符之间的位与匹配器右侧的量化正则表达式原子匹配。这带来了几个好处,但主要的是错误消息可以更清楚。
最后一行(}
)中的$<article>
和.<id>
等位是指存储在解析树中的Match对象,该对象是从成功的解析生成并返回的。
回到语法的顶部:
for $<article> { say .<id> ~ ':' ~ .<kv-pairs><kv-pair>[0]<value> ~ "\n" }
如果解析成功,则返回单个“TOP”级别Match对象,即对应于解析树顶部的对象。 (它也可以通过变量 rule TOP {
在解析方法调用之后立即用于代码。)
但是在解析的最终返回发生之前,将生成许多其他Match对象,代表整个解析的子部分,并将其添加到解析树中。将Match对象添加到解析树是通过将单个生成的Match对象或它们的列表分配给“父”Match对象的$/
或Positional元素来完成的,如下所述。
Associative
像 rule TOP { <article>* }
这样的规则调用有两个效果。首先,P6尝试匹配规则。其次,如果匹配,P6会生成相应的Match对象并将其添加到解析树中。
如果成功匹配的模式只是<article>
而不是<article>
,那么只会尝试一次匹配,并且只生成一个值,即单个Match对象,并将其添加到解析树中。
但模式是<article>*
,而不仅仅是<article>*
。所以P6试图多次匹配<article>
规则。如果它至少匹配一次,则它生成并存储一个或多个Match对象的相应列表。 (有关更详细的说明,请参阅article
。)
因此,匹配对象列表被分配给TOP级匹配对象的my answer to "How do I access the captures within a match?"键。 (如果匹配的正则表达式只是'article'
而不是<article>
,那么匹配将导致只有一个Match对象被分配给<article>*
键而不是它们的列表。)
所以现在我将尝试解释最后一行代码的'article'
部分,它是:
$<article>
for $<article> { say .<id> ~ ': ' ~ .<kv-pairs><kv-pair>[0]<value> ~ "\n" }
是$<article>
的缩写。
根据qazxsw poi,“qazxsw poi是匹配变量。它存储最后一个正则表达式匹配的结果,因此通常包含Match类型的对象。”。
在我们的案例中,最后一个正则表达式匹配是来自$/.<article>
语法的P6 doc on $/规则。
所以$/
是解析返回的TOP级Match对象的TOP
键下的值。此值是3个“文章”级别匹配对象的列表。
bib
$<article>
正则表达式依次包含任务左侧的'article'
。这对应于将Match对象分配给添加到文章级别Match对象的新 rule article { '@article{' $<id>=<-[,]>+ ','
键。
希望这已经足够了(也许太多了!)我现在可以解释最后一行代码了,它再一次是:
article
$<id>
迭代在解析期间生成的3个Match对象(对应于输入中的3篇文章)的列表,并存储在TOP级Match对象的'id'
键下。
(此迭代自动将这三个子Match对象中的每一个分配给for $<article> { say .<id> ~ ': ' ~ .<kv-pairs><kv-pair>[0]<value> ~ "\n" }
,又称为“it”或“the topic”,然后,在每次赋值后,执行块中的代码(for
)。块中的代码通常会引用,无论是明示还是暗示,都与'article'
合作。)
块中的$_
位等同于{ ... }
,即它隐含地指$_
。正如刚刚解释的那样,.<id>
是$_.<id>
级别匹配对象,这次正在围绕$_
循环进行处理。 $_
位表示article
返回存储在for
级别Match对象的<id>
键下的Match对象。
最后,.<id>
位引用存储在Match对象的'id'
键下的Match对象,该对象存储为Match对象列表的第一个(第0个)元素,该对象存储在对应于article
规则的.<kv-pairs><kv-pair>[0]<value>
规则的'value'
键下。存储在kv-pair
级别Match对象的kv-pairs
键下。
唷!
好像以上所有都不够,我还需要提一件事。
解析树强烈反映了语法的隐式树结构。但是,由于解析而获得此结构有时不方便 - 可能需要不同的树结构,可能是更简单的树,也许是一些非树数据结构。
当自动结果不合适时,用于从解析中准确生成所需内容的主要机制是使用'kv-pairs'
。 (这可以在规则内部的代码块中使用,也可以在与语法分开的article
中使用。)
反过来,make的主要用例是生成挂在解析树上的Action classes节点。
最后,这些稀疏树的主要用例是存储AST。