use strict; my $match = &ret_match_any( "0 OBS", "AT LEAST", "EXTRANEOUS", "CARTESIAN", "CLOSING", "CONVERT", "DIVISION BY ZERO", "DOES NOT EXIST", "DUE TO LOOPING", "END OF MACRO", "ENDING EXECUTION", "ERROR", "ERRORABEND", "ERRORCHECK=STRICT", "EXCEED", "HANGING", "HAS 0 OBSERVATIONS", "ILLEGAL", "INCOMPLETE", "INVALID", "LOST CARD", "MATHEMAT", "MERGE STATEMENT", "MISSING", "MULTIPLE", "NOT FOUND", "NOT RESOLVED", "OBS=0", "REFERENCE", "REPEAT", "SAS CAMPUS DRIVE", "SAS SET OPTION OBS=0", "SAS WENT", "SHIFTED", "STOP", "TOO SMALL", "UNBALANCED", "UNCLOSED", "UNREF", "UNRESOLVED", "WARNING" ); while(<>) { if ($_ =~ $match) { print "line $., file $ARGV, problem $1\n$_\n"; } } # Takes a list of strings and returns an RE that matches any. sub ret_match_any { my $match_str = &trie_strs(map quotemeta, @_); return qr /($match_str)/; } # Takes a list of escaped strings and returns a single string # suitable for building a match in an efficient way. # Works recursively by grouping strings that share one character sub trie_strs { unless (@_) { return (); } my %rest; foreach my $str (@_) { if (length($str)) { my $chr = substr($str, 0, 1); if ("\\" eq $chr) { $chr = substr($str, 0, 2); push @{$rest{$chr}}, substr($str, 2); } else { push @{$rest{$chr}}, substr($str, 1); } } else { $rest{''} = ['']; } } my @to_join; foreach my $chr (keys %rest) { my $list_ref = $rest{$chr}; if (1 < @$list_ref) { push @to_join, $chr . &trie_strs(@$list_ref); } else { push @to_join, $chr . $list_ref->[0]; } } if (1 < @to_join) { return '(?:' . (join '|', @to_join) . ')'; } else { return $to_join[0]; } }