15

字句解析と構文解析の専門家のために...私は、さまざまな目的で IBM メインフレーム z/OS JCL を解析する一連のプログラムを perl で作成しようとしていますが、方法論の障害にぶつかっています。私は主に、Mark Jason Dominus による「Higher Order Perl」で提示された字句解析/構文解析のイデオロギーに従っていますが、どうすればよいかよくわからないことがいくつかあります。

JCL にはインライン データと呼ばれるものがあり、これは「ヒア」ドキュメントと非常によく似ています。これらをトークンに変換する方法がよくわかりません。

インライン データのレイアウトは次のとおりです。

//DDNAME   DD *
this is the inline data
this is some more inline data
/*
...

通常、「DD」の後の「*」は、後続の行がインライン データ自体であり、「/*」または次の有効な JCL レコード (最初の 2 列が「//」で始まる) で終了することを意味します。

より高度なインライン データは、次のように表示されます。

//DDNAME   DD *,DLM=ZZ
//THIS LOOKS LIKE JCL BUT IT'S ACTUALLY DATA
//MORE DATA MASQUERADING AS JCL
ZZ
...

インライン データ自体が JCL である場合もあります (おそらく、プログラムや内部リーダーなどに送り込まれるためです)。

しかし、ここに問題があります。JCL では、レコードは固定長の 80 バイトです。72 列目以降 (73 ~ 80 列目) はすべて「コメント」です。同様に、有効な JCL に続く空白の後に続くものはすべて、同様にコメントです。私は自分のプログラムで JCL を操作して吐き出そうとしているので、コメントを取り込んで保存できるようにしたいと考えています。

インライン データの場合のインライン コメントの例を次に示します。

//DDNAME   DD *,DLM=ZZ THIS IS A COMMENT                                COL73DAT
data
...
ZZ
...more JCL

私は当初、最上位のレクサーで JCL の行を取得し、すぐに列 1 ~ 72 の非トークンを作成し、次に列 73 コメントのトークン (['COL73COMMENT',$1]) を作成できると考えていました。どれか。これにより、下流の次のイテレーター/トークナイザーに、列 1 から 72 のテキストの文字列とそれに続く col73 トークンが渡されます。

しかし、そこから下流でインライン データを取得するにはどうすればよいでしょうか。私は当初、最上位のトークナイザーが "DD \*(,DLM=(\S*))" (など) を探し、フィード イテレーターから区切り文字に到達するまでレコードを取得し続けることができると考えていました。または有効な JCL スターター (「//」)。

しかし、ここで問題が発生する可能性があります...最上位のトークナイザーを 2 つ持つことはできません...COL73 コメントを検索するトークナイザーが最上位にある必要があるか、インライン データを取得するトークナイザーが最上位にある必要があります。

perl パーサーにも同じ課題があると思います。

<<DELIM

必ずしも行末ではなく、その後にヒアドキュメントデータが続きます。結局のところ、perl は次のように表示されます。

my $this=$obj->ingest(<<DELIM)->reformat();
inline here document data
more data
DELIM

トークナイザー/パーサーは、「)->reformat();」をトークン化する方法をどのように認識しますか? それでも、次のレコードをそのまま取得しますか? インライン JCL データの場合、これらの行はそのまま渡されます。その場合、列 73 から 80 はコメントではありません...

それで、これに賛成する人はいますか?私のニーズを明確にするためにたくさんの質問があることは承知しており、必要なだけ明確にしていきます.

助けてくれてありがとう...

4

2 に答える 2

14

この回答では、レッスンを JCL に簡単に転送できるため、ヒアドキュメントに集中します。

ヒアドキュメントをサポートする言語はすべて文脈自由ではないため、再帰的降下などの一般的な手法では解析できません。よりねじれたパスに沿ってレクサーをガイドする方法が必要ですが、そうすることで、コンテキストフリー言語の外観を維持できます。必要なのは別のスタックだけです。

パーサーでは、ヒアドキュメントの紹介を<<END文字列リテラルとして扱います。ただし、次のことを行うには、レクサーを拡張する必要があります。

  • ヒアドキュメントの導入が検出されると、ターミネータがスタックに追加されます。
  • 改行が検出されると、スタックが空になるまで、ヒアドキュメントの本体が字句解析されます。その後、通常の解析が再開されます。

行番号を適切に更新するように注意してください。

手書きの複合パーサー/レクサーでは、これは次のように実装できます。

use strict; use warnings; use 5.010;

my $s = <<'INPUT-END'; pos($s) = 0;
<<A <<B
body 1
A
body 2
B
<<C
body 3
C
INPUT-END

my @strs;
push @strs, parse_line() while pos($s) < length($s);
for my $i (0 .. $#strs) {
  say "STRING $i:";
  say $strs[$i];
}

sub parse_line {
  my @strings;
  my @heredocs;

  $s =~ /\G\s+/gc;

  # get the markers
  while ($s =~ /\G<<(\w+)/gc) {
    push @strings, '';
    push @heredocs, [ \$strings[-1], $1 ];
    $s =~ /\G[^\S\n]+/gc;  # spaces that are no newlines
  }

  # lex the EOL
  $s =~ /\G\n/gc or die "Newline expected";

  # process the deferred heredocs:
  while (my $heredoc = shift @heredocs) {
    my ($placeholder, $marker) = @$heredoc;
    $s =~ /\G(.*\n)$marker\n/sgc or die "Heredoc <<$marker expected";
    $$placeholder = $1;
  }

  return @strings;
}

出力:

STRING 0:
body 1

STRING 1:
body 2

STRING 2:
body 3

Marpa パーサーは、特定のトークンが解析されるとイベントがトリガーされるようにすることで、これを少し簡素化します。これらは一時停止と呼ばれます。これは、組み込みの字句解析が一時停止して、ユーザーが引き継ぐためです。以下は、ハイレベルな概要と、Githubのデモ コードを使用してこの手法を説明する短いブログ投稿です。

于 2013-09-09T19:16:03.587 に答える
0

どうやってこれを解決することにしたのか疑問に思っている人のために、私がしたことは次のとおりです。

私のメインの字句解析ルーチンは、テキストの全行を送り出すイテレータを受け入れます (これは、ファイル、文字列など、必要なものから取得できます)。ルーチンはそれを使用して別のイテレータを作成します。これは列 72 の後の「コメント」の行を調べ、「メインライン」トークンとそれに続く「col72」トークンとして返します。次に、この反復子を使用してさらに別の反復子を作成します。この反復子は、col72 トークンを変更せずに渡しますが、メインライン トークンを取り、それらをアトミック トークン (STRING、NUMBER、COMMA、NEWLINE など) に変換します。

しかし、ここに核心があります...字句解析ルーチンにはまだ元の反復があります...したがって、「ここに」ドキュメントがあることを示すトークンを受け取ると、NEWLINEトークンに到達するまでトークンの処理を続けます(実際のテキスト行) を作成し、元のイテレータを使用してヒア ドキュメント データを取り出します。そのイテレーターはアトミック トークン イテレーターにフィードするため、そこからプルすると、それらの行がアトミック化されなくなります。

説明のために、反復子をホースのようなものと考えてください。最初のホースはメインの反復子です。これに col72 イテレータ ホースを取り付け、さらにアトミック トークナイザ ホースを取り付けます。文字の流れが最初のホースに入ると、霧化されたトークンが 3 番目のホースの端から出てきます。しかし、2 ウェイ ノズルを最初のホースに取り付けて、その出力が代替ノズルから出てくるようにし、そのデータが 2 番目のホース (したがって 3 番目のホース) に入らないようにすることができます。別のノズルを介してデータを転送し終わったら、それをオフにすると、データは 2 番目と 3 番目のホースを介して再び流れ始めます。

簡単です。

于 2013-09-18T15:08:38.770 に答える