对于那些在词法分析和解析方面的专家......我试图用 perl 编写一系列程序,这些程序可以出于各种目的解析 IBM 大型机 z/OS JCL,但在方法论上遇到了障碍。我主要遵循 Mark Jason Dominus 在“Higher Order Perl”中提出的词法/解析思想,但有些事情我不太清楚该怎么做。
JCL 有所谓的内联数据,它与“here”文档非常相似。我不太确定如何将这些词法转换为 token 。
内联数据的布局如下:
//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 并立即为第 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 数据,这些行按原样传递,在这种情况下,第 73-80 列不是注释......
那么,有没有人接受这个?我知道会有很多问题来澄清我的需求,我很乐意尽可能多地澄清。
在此先感谢您的帮助...
最佳答案
在这个答案中,我将专注于 heredocs,因为类(class)可以轻松转移到 JCL。
任何支持heredocs 的语言都不是上下文无关的,因此无法使用递归下降等常用技术进行解析。我们需要一种方法来引导词法分析器沿着更曲折的路径前进,但这样做,我们可以保持上下文无关语言的外观。我们需要的只是另一个堆栈。
对于解析器,我们处理对heredocs的介绍<<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 parser通过允许在解析某个标记后触发事件来稍微简化这一点。这些被称为暂停,因为内置的词法分析会暂停片刻以供您接管。这是一个 high-level overview和一个 short blogpost使用 demo code 描述此技术在 Github 上。
关于perl - Lexing/Parsing "here"文档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18703999/