对于那些是lexing和解析专家的人......我试图在perl中编写一系列程序来解析IBM大型机z/OS JCL用于各种目的,但是我在方法论上遇到了障碍.我主要遵循Mark Jason Dominus在"高阶Perl"中提出的lexing/parsing意识形态,但有些事情我不知道该怎么做.
JCL具有所谓的内联数据,这与"此处"文档非常相似.我不太确定如何把它们变成令牌.
内联数据的布局如下:
//DDNAME DD *
this is the inline data
this is some more inline data
/*
...
Run Code Online (Sandbox Code Playgroud)
通常,"DD"之后的"*"表示后续行是内联数据本身,由"/*"或下一个有效JCL记录(前两列中以"//"开头)终止.
更高级,内联数据可能如下所示:
//DDNAME DD *,DLM=ZZ
//THIS LOOKS LIKE JCL BUT IT'S ACTUALLY DATA
//MORE DATA MASQUERADING AS JCL
ZZ
...
Run Code Online (Sandbox Code Playgroud)
有时,内联数据本身就是JCL(可能被抽到一个程序或内部读者,无论如何).
但这就是问题所在.在JCL中,记录是80个字节,长度固定.第72栏(第73-80栏)的所有内容都是"评论".同样,跟随有效JCL的空白之后的所有内容同样是评论.由于我希望在我的程序中操作JCL并将其吐出来,我想捕获注释以便我可以保留它们.
所以,这是内联数据中的内联注释的示例:
//DDNAME DD *,DLM=ZZ THIS IS A COMMENT COL73DAT
data
...
ZZ
...more JCL
Run Code Online (Sandbox Code Playgroud)
我原本以为我可以让我最顶级的词法分析器拉入JCL系列并立即为cols 1-72创建一个非令牌,然后为第73列注释创建一个令牌(['COL73COMMENT',$ 1]),如果任何.然后,这将向下游传递给下一个迭代器/标记生成器,其中包含cols 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
Run Code Online (Sandbox Code Playgroud)
令牌化器/解析器如何知道标记化") - >重新格式化();" 然后仍按原样获取以下记录?对于内联JCL数据,这些行按原样传递,在这种情况下,cols 73-80不是注释...
那么,对此有何看法?我知道会有很多问题澄清我的需求,我很乐意尽可能多地澄清.
在此先感谢任何帮助......
amo*_*mon 14
在这个答案中,我将专注于heredocs,因为课程可以很容易地转移到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;
}
Run Code Online (Sandbox Code Playgroud)
输出:
STRING 0:
body 1
STRING 1:
body 2
STRING 2:
body 3
Run Code Online (Sandbox Code Playgroud)
该马尔巴分析器通过允许触发事件,一旦某个令牌解析这简化了一下.这些被称为暂停,因为内置的lexing暂停片刻让你接管.这是一个高级概述和一个简短的博客文章,用Github 上的演示代码描述这种技术.