忙しいなかやる気を持続させるために、まめにBlogに書き込んでします。いま一度、字句解析のコードを修正前後で比較します。文字の集合型で表している部分を、文字列に直します。元のプログラムはASCIIで#0...#255の範囲を想定しています。Unicode対応するときに、集合型から文字列に置き換えるだけでよいのか、検証していきます。
正規表現で「以下の文字を含まない」とか「改行以外の文字」を表すときに、Unicodeだとうまく集合型に収まらないのでなんらか工夫が必要だと認識しました。
SASプログラムとログを解析するユーティリティを開発するための備忘録です。This is a memo to develop utility that analyzes the SAS log.
function charStr(c: char): string;
begin
case c of
#0..#31, (* nonprintable characters *)
#127..#255: Result := '#' + intStr(Ord(c));
'''': Result := '''''''''';
else Result := '''' + c + '''';
end;
end(*charStr*);
procedure writecc(var f: Text; cc: CClass);
(* print the given character class *)
const
MaxChar = #255;
var
c1, c2: char;
col: integer;
tag: string;
Quit: boolean;
begin
Write(f, '[ ');
col := 0;
c1 := chr(0);
Quit := False;
while not Quit do
begin
if c1 in cc then
begin
if col > 0 then
begin
Write(f, ',');
Inc(col);
end;
if col > 40 then
{ insert line break }
begin
writeln(f);
Write(f, ' ': 12);
col := 0;
end;
c2 := c1;
while (c2 < MaxChar) and (succ(c2) in cc) do
c2 := succ(c2);
if c1 = c2 then
tag := charStr(c1)
else if c2 = succ(c1) then
tag := charStr(c1) + ',' + charStr(c2)
else
tag := charStr(c1) + '..' + charStr(c2);
Write(f, tag);
col := col + length(tag);
c1 := c2;
end;
Quit := c1 = MaxChar;
if not Quit then
c1 := Succ(c1);
end; { of while }
Write(f, ' ]');
end(*writecc*);
引数ccで渡された集合型の文字を、印字可能なものならばそのまま出力、印字できない文字ならば数値化しています。
文字の範囲を0から255のコードを前提とした実装です。ASCIIで連続したコード値であれば、'A'...'Z'の形式で出力します。type (* String and character class pointers: *) StrPtr = PString; CClass = set of char; CClassPtr = ^CClass;
type YYTRec = record
cc : set of char;
s : Integer;
end;
type YYTRec = record
cc : String;
s : Integer;
end;
function libname(dsn)
{
i = index(dsn, ".");
if (i > 0) {
tmp = substr(dsn, 1, i - 1);
}
else {
tmp = "WORK";
}
return toupper(tmp);
}
function memname(dsn)
{
i = index(dsn, ".");
if (i > 0) {
tmp = substr(dsn, i + 1, length(dsn));
}
else {
tmp = dsn;
}
return toupper(tmp);
}
function ilog(path, dsn)
{
printf("R,%s,%s,%d,%s,%d\n", path, dsn, FNR, FILENAME, NR);
return 1;
}
function olog(path, dsn)
{
printf("W,%s,%s,%d,%s,%d\n", path, dsn, FNR, FILENAME, NR);
return 1;
}
/NOTE: ライブラリ参照名/ && /を次のように割り当てました。/ {
libref = $3;
getline;
while ($1 != "物理名") {
getline;
}
lib[libref] = $3;
}
$1 ~ /NOTE:/ && $2 ~ /データセット/ && $NF ~ /オブザベーションを読み込みました。/ {
dsn = $3;
libref = libname(dsn);
mem = memname(dsn);
if (libref != "WORK" && libref != "SASHELP") {
dir = lib[libref];
if (dir != "") {
tmp = sprintf("%s/%s.sas7bdat", dir, tolower(mem));
status = ilog(tmp, dsn);
}
else {
printf("ERROR %s\n", dsn);
exit;
}
}
}
$1 ~ /NOTE:/ && $2 ~ /データセット/ && $NF ~ /変数です。/ {
dsn = $3;
libref = libname(dsn);
mem = memname(dsn);
if (libref != "WORK" && libref != "SASHELP") {
dir = lib[libref];
if (dir != "") {
tmp = sprintf("%s/%s.sas7bdat", dir, tolower(mem));
status = olog(tmp, dsn);
}
else {
printf("ERROR %s\n", dsn);
exit;
}
}
}
$1 ~ /MPRINT\([A-Z0-9_]+\):/ && $2 ~ /^set$/ {
match($3, /([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+/);
if (RLENGTH > 0) {
dsn = substr($3, RSTART, RLENGTH);
libref = libname(dsn);
mem = memname(dsn);
if (libref != "WORK" && libref != "SASHELP") {
dir = lib[libref];
if (dir != "") {
tmp = sprintf("%s/%s.sas7bdat", dir, tolower(mem));
status = ilog(tmp, dsn);
}
else {
printf("ERROR %s\n", dsn);
exit;
}
}
}
}
END {
print "_EOF_";
}
/* *++ * import io log-file *-- */ data iods; infile "c:\temp\iods.csv" dsd firstobs=2; attrib RW length=$1; attrib FILENAME length=$64; attrib DSN length=$32; attrib FNR length=8; attrib LOG length=$61; attrib NR length=8; input rw filename dsn fnr log nr; run; /* *++ * input data-set *-- */ proc sort data=iods out=ids; by filename nr rw; run; data ids; set ids; by filename nr rw; if first.filename and rw eq 'R' then output; run; proc sort data=ids; by nr; run; /* *++ * output data-set *-- */ proc sort data=iods out=ods; where rw eq 'W'; by filename nr; run; data ods; set ods; by filename nr; if last.filename then output; run; proc sort data=ods; by nr; run; /* *++ * make compare procedure statement *-- */ data _null_; set ods; attrib comment length=$256; attrib libstat length=$256; attrib titstat length=$256; attrib comproc length=$256; attrib mem length=$32; basedir = "/sumdata/bk02"; comment = "/* dsn=" || compress(upcase(dsn)) || " logfile=" || compress(log) || " line=" || compress(put(fnr, best.)) || " */"; put comment; i = 1; imax = length(filename); pos = 0; do i=1 to imax; if substr(filename, i, 1) eq '/' then pos = i; end; put "options nocenter;"; libstat = "libname tmp1 '" || compress(basedir) || substr(filename, 1, pos-1) || "' access=readonly;"; put libstat; libstat = "libname tmp2 '" || substr(filename, 1, pos-1) || "' access=readonly;"; put libstat; titstat = "title 'dsn=" || compress(upcase(dsn)) || " log=" || compress(log) || "';"; put titstat; mem = substr(dsn, index(dsn, ".")+1); comproc = "proc compare base=tmp1." || compress(mem) || " compare=tmp2." || compress(mem) || " maxprint=5;"; put comproc; put "run;"; put "title;"; put ; run;