对于那些是词法分析专家的人来说...我试图用perl编写一系列程序,这些程序可以出于各种目的解析出IBM大型机z / OS JCL,但是却在方法论上遇到了障碍。我主要遵循Mark Jason Dominus在“高级Perl”中提出的词汇化/解析思想,但是有些事情我还不太清楚该怎么做。

JCL具有所谓的内联数据,与“这里”文档非常相似。我不太确定如何将这些词分解成令牌。

内联数据的布局如下:

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


按照惯例,“ DD”之后的“ *”表示以下行是内联数据本身,以“ / *”或下一个有效的JCL记录(在前两列中以“ //”开头)结尾。

更高级的是,内联数据可能如下所示:

//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行,并立即为cols 1-72创建一个非令牌,然后为第73列注释创建一个令牌(['COL73COMMENT',$ 1]),如果任何。然后,这会将一串cols 1-72文本后跟col73令牌向下游传递到下一个迭代器/令牌器。

但是,我将如何从那里下游获取内联数据?最初,我认为最顶端的标记化程序可以寻找“ DD \ *(,DLM =(\ S *))”(或类似名称),然后继续从提要迭代器中提取记录,直到到达定界符为止或有效的JCL启动器(“ //”)。

但是您可能会在这里看到问题...我不能有2个最顶部的标记生成器...寻找COL73注释的标记生成器必须位于顶部,或者获取内联数据的标记生成器必须位于顶部。

我想自从看到Perl解析器以来,同样面临挑战

<<DELIM

不一定是行的末尾,其后是here文档数据。毕竟,您可能会看到像以下的perl:

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


分词器/解析器如何知道对“)-> reformat();”进行分词。然后仍按原样获取以下记录?对于内联JCL数据,这些行将按原样传递,在这种情况下,cols 73-80不是注释...

那么,对此有何建议?我知道会有很多问题可以阐明我的需求,并且很高兴根据需要澄清所有问题。

预先感谢您的任何帮助...

最佳答案

在这个答案中,我将专注于heredocs,因为这些课程可以轻松地转移到JCL。

任何支持heredocs的语言都不是上下文无关的,因此无法使用诸如递归下降之类的通用技术进行解析。我们需要一种方法来引导词法分析器沿更复杂的路径前进,但是这样做可以保持上下文无关语言的外观。我们需要的是另一个堆栈。

对于解析器,我们将heredocs <<END的介绍视为字符串文字。但是词法分析器必须扩展为执行以下操作:


遇到Heredoc简介时,它将终止符添加到堆栈中。
遇到换行符时,heredoc的正文将被词法化,直到堆栈为空。之后,恢复正常解析。


请注意适当地更新行号。

在手写的组合解析器/词法分析器中,可以这样实现:

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 parser通过允许在解析特定令牌后触发事件来简化此操作。这些称为暂停,因为内置词法暂停片刻让您接管。这是在Github上使用high-level overview描述此技术的short blogpostdemo code

10-07 21:32