Files
mercury/samples/ultra_sub.m
Julien Fischer 9c96387183 Fix formatting of sample programs.
samples/Mmakefile:
    Add the beer program to list of targets.

samples/*.m:
    Convert (C->T;E) to (if C then T else E).

    Delete trailing whitespace.

    Use predmode syntax instead of separate pred and mode
    declarations.

samples/interpreter.m:
    Fix up one predicate that had some clauses that used
    DCGs and others that used state variables.

    Don't use the name 'IO' for something that isn't the I/O state.

samples/diff/*.m:
samples/c_interface/c_calls_mercury/mercury_main.m:
samples/c_interface/short_example.m:
    Delete trailing whitespace.
2015-12-14 16:06:13 +11:00

179 lines
5.3 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
%
% file: ultra_sub.m
% author: conway.
%
% This source file is hereby placed in the public domain. -conway (the author).
%
% 'ultra_sub' is an extended version of zs' 'sub' command. The idea is that
% it takes a pattern, a template and some strings, and matches the strings
% against the pattern, binding some variables in the process. Then it
% substitutes the variables in the template for the bindings from the pattern.
%
% usage: ultra_sub <pattern> <template> [strings...]
%
% Variables in the pattern and template are represented by capital letters
% (unfortunately limiting the number of variables to 26 ). Real capital letters
% should be preceded by a \.
% Variables in the template that do not occur in the pattern are treated as
% real capital letters.
%
% eg
% $ ultra_sub 1X2Y3Z 7Z8Y9X 1a2b3c 1foo2bar3baz
% 7c8b9a
% 7baz8bar9foo
%
% Strings that do not match the pattern are ignored.
%
%------------------------------------------------------------------------------%
:- module ultra_sub.
:- interface.
:- import_module io.
:- pred main(io::di, io::uo) is det.
%------------------------------------------------------------------------------%
%------------------------------------------------------------------------------%
:- implementation.
:- import_module char.
:- import_module list.
:- import_module map.
:- import_module string.
%------------------------------------------------------------------------------%
main(!IO) :-
% I really should add some options for switching whether
% capitals or backslashed things are variables.
io.command_line_arguments(Args, !IO),
( if
Args = [Pattern0, Template0 | Rest]
then
string.to_char_list(Pattern0, Pattern),
string.to_char_list(Template0, Template),
process_args(Rest, Pattern, Template, !IO)
else
io.write_string(
"usage: ultra_sub template pattern [strings]\n", !IO)
).
%------------------------------------------------------------------------------%
:- pred process_args(list(string)::in, list(char)::in, list(char)::in,
io::di, io::uo) is det.
process_args([], _Pattern, _Template, !IO).
process_args([Str | Strs], Pattern, Template, !IO) :-
( if
string.to_char_list(Str, Chars),
map.init(Match0),
match(Pattern, Chars, Match0, Match)
then
% If the string matches, then apply the substitution.
sub(Template, Match, ResultChars),
string.from_char_list(ResultChars, Result),
io.write_string(Result, !IO),
io.write_string("\n", !IO)
else
true
),
process_args(Strs, Pattern, Template, !IO).
%------------------------------------------------------------------------------%
:- pred match(list(char)::in, list(char)::in, map(char, list(char))::in,
map(char, list(char))::out) is semidet.
match([], [], Match, Match).
match([T | Ts], Chars, !Match) :-
( if
char.is_upper(T)
then
% Match against a variable.
match_2(T, Chars, [], Ts, !Match)
else if
T = ('\\') % don't you love ISO compliant syntax :-(
then
Ts = [T1 | Ts1],
Chars = [T1 | Chars1],
match(Ts1, Chars1, !Match)
else
Chars = [T | Chars1],
match(Ts, Chars1, !Match)
).
:- pred match_2(char::in, list(char)::in, list(char)::in, list(char)::in,
map(char, list(char))::in, map(char, list(char))::out) is semidet.
match_2(X, Chars, Tail, Ts, !Match) :-
( if
% Have we bound X? Does it match an earlier binding?
map.search(!.Match, X, Chars)
then
true
else
map.set(X, Chars, !Match)
),
( if
% Try and match the remainder of the pattern.
match(Ts, Tail, !Match)
then
true
else
% If the match failed, then try binding less of the string to X.
remove_last(Chars, Chars1, C),
match_2(X, Chars1, [C | Tail], Ts, !Match)
).
%------------------------------------------------------------------------------%
:- pred remove_last(list(char)::in, list(char)::out, char::out) is semidet.
remove_last([X | Xs], Ys, Z) :-
remove_last_2(X, Xs, Ys, Z).
:- pred remove_last_2(char::in, list(char)::in, list(char)::out, char::out)
is det.
remove_last_2(X, [], [], X).
remove_last_2(X, [Y | Ys], [X | Zs], W) :-
remove_last_2(Y, Ys, Zs, W).
%------------------------------------------------------------------------------%
:- pred sub(list(char)::in, map(char, list(char))::in, list(char)::out) is det.
sub([], _Match, []).
sub([C | Cs], Match, Result) :-
( if
char.is_upper(C),
map.search(Match, C, Chars)
then
sub(Cs, Match, Result0),
list.append(Chars, Result0, Result)
else if
C = ('\\')
then
( if Cs = [C1 | Cs1] then
sub(Cs1, Match, Result0),
Result = [C1 | Result0]
else
sub(Cs, Match, Result0),
Result = Result0
)
else
sub(Cs, Match, Result0),
Result = [C | Result0]
).
%------------------------------------------------------------------------------%
:- end_module ultra_sub.
%------------------------------------------------------------------------------%