让我们考虑一下这段不属于任何已知语言的代码:
foo() {
bar() {
bar();
}
baz() {
// Content baz
qux() {
// Content qux
}
}
}
我想通过调用子例程来迭代处理每个function
,子例程接收:函数名,参数,缩进级别和内容。
到目前为止,我已经写过:
#!/usr/bin/env perl
use 5.010;
$_ = do {local $/; <>};
s/([{}])/$1.($1 eq '{'?++$i:$i--).$1/eg;
parse($_);
sub parse {
local $_ = shift;
while (/(?<name>\w+)\s*\((?<args>.*?)\)\s*\{(\d)\{(?<content>.*?)\}(?<level>\3)\}/gs) {
parse($+{content});
process($+{content}, $+{args}, $+{level}, $+{content});
}
}
sub process {
my ($name, $args, $level, $content) = @_;
#...
}
棘手的想法是使用缩进编号替换每个匹配的大括号{
。所以这个:
{
{
}
}
将成为这个:
{1{
{2{
}2}
}1}
它允许轻松编写简单的解析正则表达式:
qr/
\w+ # name
\s* \(.*?\) # arguments
\s* \{(\d)\{ # opening brace
.*? # content
\s* \}\1\} # closing brace
/x
如果没有这个技巧我怎么能重写呢?
请注意,{1{
的选择可以是{(1)
,{1-
,{[1]
甚至{1✈
答案 0 :(得分:2)
您可以尝试使用递归正则表达式。例如:
/(\{(?:[^{}]++|(?1))*\})/
将匹配一组平衡的大括号。有关更多信息,请参阅
答案 1 :(得分:2)
使用递归正则表达式时,您将很难做到这一点,因为您只能获取每个捕获组的最后一个值,并且会丢失所有干预值。此任务更适合像Parse::RecDescent
这样的解析器:
use strict;
use warnings;
use 5.010;
use Parse::RecDescent;
sub process {
my ($name, $args, $depth) = @_;
say "$depth - $name($args)";
}
my $grammar = q{
{ my $indent = 0; }
startrule : expression(s /;/)
expression : function_call
| function_def[$indent]
function_call : identifier '(' arglist ')' ';'
function_def : identifier '(' arglist ')' '{' expression[ $arg[0]++ ](s?) '}'
{ main::process( $item{identifier}, join(',', @$item{arglist}), $arg[0] ) }
arglist : identifier(s? /,/)
identifier : /\w+/
};
# Tell parser to ignore spaces and C99 one-line comments
my $skip_spaces_and_comments = qr{ (?: \s+ | // .*?$ )* }mxs;
$Parse::RecDescent::skip = $skip_spaces_and_comments;
my $parser = Parse::RecDescent->new($grammar) or die 'Bad grammar';
my $text = do { local $/; <DATA> };
defined $parser->startrule($text) or die 'Failed to parse';
__DATA__
foo() {
bar() {
bar();
}
baz() {
// Content baz
qux() {
// Content qux
}
}
}
2 - bar()
1 - qux()
2 - baz()
3 - foo()
请注意,深度是反转的(1是嵌套最多的),这不会返回函数定义的内容,但它应该让您开始。