17

私は単純な文法を perl 正規表現で変更しようとしています (これは本番環境での使用を意図したものではなく、エディターのヒント/補完を提供するための簡単な分析にすぎないことに注意してください)。例えば、

my $GRAMMAR = qr{(?(DEFINE)
  (?<expr> \( (?&expr) \) | (?&number) | (?&var) | (?&expr) (?&op) (?&expr) )
  (?<number> \d++ )
  (?<var> [a-z]++ )
  (?<op> [-+*/] )
)}x;

これを次のように実行できるようにしたい

$expr =~ /$GRAMMAR(?&expr)/;

次に、すべての変数名にアクセスします。ただし、perlreによると、

再帰内で一致したキャプチャ グループは、再帰が戻った後はアクセスできないことに注意してください。そのため、キャプチャ グループの追加レイヤーが必要です。したがって、$+{NAME} は定義されますが、$+{NAME_PAT} は定義されません。

したがって、明らかにこれは不可能です。ブロックを使用して(?{ code })変数名をハッシュに保存することもできますが、これはバックトラッキングを尊重しません (つまり、変数が過去にバックトラックされても、代入の副作用は持続します)。

再帰的な一致を含め、特定の名前付きキャプチャ グループによってすべてをキャプチャする方法はありますか? それとも、個々のピースを手動で掘り下げる (したがって、すべてのパターンを複製する) 必要がありますか?

4

2 に答える 2

9

キャプチャとバックトラッキングの機構を追加する必要があることは、 Regexp::Grammarsが対処する欠点の 1 つです。

ただし、質問の文法はleft-recursiveであり、Perl 正規表現も再帰降下パーサーも解析しません。

文法をRegexp::Grammarsに適合させ、左再帰を因数分解すると、

my $EXPR = do {
  use Regexp::Grammars;
  qr{
    ^ <Expr> $

    <rule: Expr>        <Term> <ExprTail>
               |        <Term>

    <rule: Term>        <Number>
               |        <Var>
               |        \( <MATCH=Expr> \)

    <rule: ExprTail>    <Op> <Expr>

    <token: Op>         \+ | \- | \* | \/

    <token: Number>     \d++

    <token: Var>        [a-z]++
  }x;
};

この単純な文法は、Please Excuse My Dear Aunt Sally ではなく、すべての演算子に同等の優先順位を与えることに注意してください。

すべての変数名を抽出したいので、次のように AST をたどることができます。

sub all_variables {
  my($root,$var) = @_;

  $var ||= {};
  ++$var->{ $root->{Var} } if exists $root->{Var};
  all_variables($_, $var) for grep ref $_, values %$root;

  wantarray ? keys %$var : [ keys %$var ];
}

結果を印刷します

if ("(a + (b - c))" =~ $EXPR) {
  print "[$_]\n" for sort +all_variables \%/;
}
else {
  print "no match\n";
}

Varもう 1 つの方法は、正常に解析された変数の名前を記録するルールの自動アクションをインストールすることです。

package JustTheVarsMaam;

sub new { bless {}, shift }

sub Var {
  my($self,$result) = @_;
  ++$self->{VARS}{$result};
  $result;
}

sub all_variables { keys %{ $_[0]->{VARS} } }

1;

これを次のように呼び出します

my $vars = JustTheVarsMaam->new;
if ("(a + (b - c))" =~ $EXPR->with_actions($vars)) {
  print "[$_]\n" for sort $vars->all_variables;
}
else {
  print "no match\n";
}

いずれにせよ、出力は

[a]
[b]
[ハ]
于 2013-07-12T01:28:08.780 に答える