%%% -*- mode: erlang -*-
%%% $Id: regexmatch.erlang,v 1.7 2001/01/06 22:35:23 doug Exp $
%%% http://www.bagley.org/~doug/shootout/
-module(regexmatch).
-export([main/0, main/1]).
%% get the program argument, which is how many test iterations to run
main() -> main(['1']).
main([Arg]) ->
Num = list_to_integer(atom_to_list(Arg)),
{ok, Re} = regexp:parse(
"(^|[^0-9\\(])" % preceeding non-digit or bol
"(" % area code
"\\([0-9][0-9][0-9]\\)" % is either 3 digits in parens
"|" % or
"[0-9][0-9][0-9]" % just 3 digits
")" % end of area code
" " % area code is followed by one space
"[0-9][0-9][0-9]" % exchange is 3 digits
"[ -]" % separator is either space or dash
"[0-9][0-9][0-9][0-9]" % last 4 digits
"($|[^0-9])" % must be followed by a non-digit
),
Plist = readlines(),
test(Num, Re, Plist),
halt(0).
test(1, Regexp, Plist) ->
% display output on last iteration
Nums = match_phones(Regexp, Plist),
print_phones(1, Nums),
true;
test(N, Regexp, Plist) ->
match_phones(Regexp, Plist),
test(N-1, Regexp, Plist).
print_phones(Count, [H|T]) ->
[A,E,N] = H,
% A,E,N is a list of the matching sub-expressions, which are:
% Areacode (3 digits), Exchange (3 digits), Number (4 digits)
io:fwrite("~w: (~s) ~s-~s~n", [Count, A,E,N]),
print_phones(Count+1, T);
print_phones(_, []) ->
true.
match_phones(Regexp, List) ->
mapfilter(
fun(String) ->
case regexp:matches(String, Regexp) of
{match, []} -> false;
{match, Matches} -> parse_phone(String, Matches);
_ -> false
end
end,
List).
parse_phone(Str, [H|T]) ->
{Start, Len} = H,
% Numstr is something that looks like a complete phone #
Numstr = string:substr(Str, Start, Len),
case regexp:matches(Numstr, "[0-9][0-9][0-9][0-9]*") of
{match, []} -> false;
{match, Matches} ->
lists:map(fun({Offset, Length}) ->
string:substr(Numstr, Offset, Length) end,
Matches);
_ -> false
end;
parse_phone(Str, []) -> [].
mapfilter(Fun, [H|T]) ->
case Fun(H) of
false -> mapfilter(Fun, T);
New -> [New | mapfilter(Fun, T)]
end;
mapfilter(_, []) -> [].
readlines() ->
Port = open_port({fd, 0, 1}, [eof, {line, 512}]),
readlines_from_stream([], Port).
readlines_from_stream(Lines, Port) ->
receive
{Port, eof} ->
lists:reverse(Lines);
{Port, {_, {_, Line}}} ->
readlines_from_stream([Line|Lines], Port)
end.