[m-rev.] For review: initialise default pretty_printer formatters
Ralph Becket
rafe at csse.unimelb.edu.au
Wed Aug 8 12:44:19 AEST 2007
Estimated hours taken: 4
Branches: main
Define pretty_printer formatters for some common standard library types.
Include these formatters in the default pretty_printer formatter_map.
Add a useful function to the pretty_printer interface.
library/array.m:
library/char.m:
library/float.m:
library/int.m:
library/list.m:
library/string.m:
library/tree234.m:
Add <type>_to_doc functions.
library/pretty_printer.m:
Added function format_arg/1.
Initialise the default formatter_map to use the <type>_to_doc
functions.
tests/hard_coded/Mmakefile:
tests/hard_coded/test_pretty_printer_defaults.exp:
tests/hard_coded/test_pretty_printer_defaults.m:
Test case.
Index: library/array.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/array.m,v
retrieving revision 1.156
diff -u -r1.156 array.m
--- library/array.m 12 Jun 2007 07:18:31 -0000 1.156
+++ library/array.m 7 Aug 2007 04:32:25 -0000
@@ -63,6 +63,7 @@
:- import_module list.
:- import_module maybe.
+:- import_module pretty_printer.
:- import_module random.
:- type array(T).
@@ -380,6 +381,10 @@
random.supply, random.supply).
:- mode array.random_permutation(array_di, array_uo, mdi, muo) is det.
+ % Convert an array to a pretty_printer.doc for formatting.
+ %
+:- func array.array_to_doc(array(T)) = pretty_printer.doc.
+
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
@@ -1657,5 +1662,24 @@
array.greatest_index(A) = array.max(A).
+%-----------------------------------------------------------------------------%
+
+array.array_to_doc(A) =
+ indent([str("array(["), array_to_doc_2(0, A), str("])")]).
+
+
+:- func array_to_doc_2(int, array(T)) = doc.
+
+array_to_doc_2(I, A) =
+ ( if I > array.max(A) then
+ str("")
+ else
+ docs([
+ group([nl, format_arg(format(A ^ elem(I)))]),
+ str((if I = array.max(A) then "" else ", ")),
+ format_susp((func) = array_to_doc_2(I + 1, A))
+ ])
+ ).
+
%------------------------------------------------------------------------------%
%------------------------------------------------------------------------------%
Index: library/char.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/char.m,v
retrieving revision 1.58
diff -u -r1.58 char.m
--- library/char.m 30 May 2007 08:16:02 -0000 1.58
+++ library/char.m 7 Aug 2007 04:11:11 -0000
@@ -25,6 +25,7 @@
:- module char.
:- interface.
:- import_module enum.
+:- import_module pretty_printer.
%-----------------------------------------------------------------------------%
@@ -170,12 +171,17 @@
:- func char.det_int_to_digit(int) = char.
:- pred char.det_int_to_digit(int::in, char::out) is det.
+ % Convert a char to a pretty_printer.doc for formatting.
+ %
+:- func char.char_to_doc(char) = pretty_printer.doc.
+
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module require.
+:- import_module term_io.
:- instance enum(character) where [
(to_int(X) = Y :- char.to_int(X, Y)),
@@ -647,3 +653,6 @@
char.det_int_to_digit(N) = C :-
char.det_int_to_digit(N, C).
+
+char.char_to_doc(C) = str(term_io.quoted_char(C)).
+
Index: library/float.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/float.m,v
retrieving revision 1.74
diff -u -r1.74 float.m
--- library/float.m 8 Aug 2007 00:52:40 -0000 1.74
+++ library/float.m 8 Aug 2007 00:54:04 -0000
@@ -51,6 +51,8 @@
:- module float.
:- interface.
+:- import_module pretty_printer.
+
%---------------------------------------------------------------------------%
%
% Arithmetic functions
@@ -216,6 +218,10 @@
%
:- func float.max_exponent = int.
+ % Convert a float to a pretty_printer.doc for formatting.
+ %
+:- func float.float_to_doc(float) = doc.
+
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
@@ -224,6 +230,7 @@
:- import_module exception.
:- import_module int.
:- import_module math.
+:- import_module string.
%
% Header files of mathematical significance.
@@ -842,5 +849,9 @@
%
float.max_exponent = 1024.
+ % Convert a float to a pretty_printer.doc.
+ %
+float.float_to_doc(X) = str(string.float_to_string(X)).
+
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
Index: library/int.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/int.m,v
retrieving revision 1.120
diff -u -r1.120 int.m
--- library/int.m 30 May 2007 08:16:03 -0000 1.120
+++ library/int.m 7 Aug 2007 04:13:27 -0000
@@ -26,6 +26,7 @@
:- import_module array.
:- import_module enum.
+:- import_module pretty_printer.
%-----------------------------------------------------------------------------%
@@ -317,6 +318,10 @@
:- mode int.fold_down2(pred(in, di, uo, di, uo) is det, in, in, di, uo,
di, uo) is det.
+ % Convert an int to a pretty_printer.doc for formatting.
+ %
+:- func int.int_to_doc(int) = pretty_printer.doc.
+
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
@@ -375,6 +380,7 @@
:- import_module exception.
:- import_module math.
+:- import_module string.
%-----------------------------------------------------------------------------%
@@ -803,5 +809,9 @@
).
%-----------------------------------------------------------------------------%
+
+int.int_to_doc(X) = str(string.int_to_string(X)).
+
+%-----------------------------------------------------------------------------%
:- end_module int.
%-----------------------------------------------------------------------------%
Index: library/list.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/list.m,v
retrieving revision 1.165
diff -u -r1.165 list.m
--- library/list.m 14 Jul 2007 02:33:09 -0000 1.165
+++ library/list.m 7 Aug 2007 04:31:48 -0000
@@ -19,6 +19,8 @@
:- module list.
:- interface.
+:- import_module pretty_printer.
+
%-----------------------------------------------------------------------------%
% The definition of the type `list(T)':
@@ -1297,6 +1299,12 @@
:- func list.det_tail(list(T)) = list(T).
%-----------------------------------------------------------------------------%
+
+ % Convert a list to a pretty_printer.doc for formatting.
+ %
+:- func list_to_doc(list(T)) = pretty_printer.doc.
+
+%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
@@ -2570,4 +2578,20 @@
inst_preserving_append(T, L) = NT.
%-----------------------------------------------------------------------------%
+
+list_to_doc(Xs) = indent([str("["), list_to_doc_2(Xs), str("]")]).
+
+
+:- func list_to_doc_2(list(T)) = doc.
+
+list_to_doc_2([]) = str("").
+
+list_to_doc_2([X | Xs]) =
+ docs([
+ group([nl, format_arg(format(X))]),
+ str((if Xs = [] then "" else ", ")),
+ format_susp((func) = list_to_doc_2(Xs))
+ ]).
+
+%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
Index: library/pretty_printer.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/pretty_printer.m,v
retrieving revision 1.1
diff -u -r1.1 pretty_printer.m
--- library/pretty_printer.m 3 Aug 2007 05:18:38 -0000 1.1
+++ library/pretty_printer.m 8 Aug 2007 02:37:11 -0000
@@ -124,6 +124,12 @@
%
:- func format(T) = doc.
+ % format_arg(Doc) has the effect of formatting any term in Doc as though
+ % it were an argument in a Mercury term by enclosing it in parentheses if
+ % necessary.
+ %
+:- func format_arg(doc) = doc.
+
% The pretty-printer limit type, used to control conversion by
% format_univ, format_list, and format_term.
%
@@ -189,8 +195,13 @@
pp_limit :: formatting_limit % Term formatting limit.
).
- % The default formatter_map may also be updated by initialisation goals in
- % various modules.
+ % An initial default formatter_map is provided for the most commonly
+ % used types in the Mercury standard library (array, char, float,
+ % int, map, string, etc.)
+ %
+ % The default formatter_map may also be updated by
+ % users' modules (e.g., in initialisation goals).
+ %
% These defaults are thread local (i.e., changes made by one thread to
% the default formatter_map will not be visible in another thread).
%
@@ -219,13 +230,17 @@
:- implementation.
+:- import_module array. % For array_to_doc.
:- import_module bool.
+:- import_module char. % For char_to_doc.
:- import_module deconstruct.
:- import_module exception.
+:- import_module float. % For float_to_doc.
:- import_module int.
:- import_module map.
:- import_module ops.
:- import_module term_io.
+:- import_module tree234. % For tree234_to_doc.
@@ -303,10 +318,12 @@
%-----------------------------------------------------------------------------%
-:- func set_arg_priority = doc.
-
-set_arg_priority =
- pp_internal(set_op_priority(ops.arg_priority(ops.init_mercury_op_table))).
+format_arg(Doc) =
+ docs([
+ pp_internal(
+ set_op_priority(ops.arg_priority(ops.init_mercury_op_table))),
+ Doc
+ ]).
%-----------------------------------------------------------------------------%
@@ -648,11 +665,11 @@
else
(
Univs = [],
- Doc = group([set_arg_priority, nl, format_univ(Univ)])
+ Doc = format_arg(group([nl, format_univ(Univ)]))
;
Univs = [_ | _],
Doc = docs([
- group([set_arg_priority, nl, format_univ(Univ), Sep]),
+ format_arg(group([nl, format_univ(Univ), Sep])),
format_list(Univs, Sep)
])
)
@@ -821,7 +838,7 @@
%-----------------------------------------------------------------------------%
% Convenience predicates.
-:- mutable(io_formatter_map, formatter_map, new_formatter_map, ground,
+:- mutable(io_formatter_map, formatter_map, initial_formatter_map, ground,
[attach_to_io_state, untrailed, thread_local]).
:- mutable(io_pp_params, pp_params, pp_params(78, 100, triangular(100)),
@@ -859,4 +876,107 @@
format(Stream, Formatters, LineWidth, MaxLines, Limit, Doc, !IO).
%-----------------------------------------------------------------------------%
+
+ % Construct the initial default formatter map. This function
+ % should be extended as more specialised formatters are added
+ % to the standard library modules.
+ %
+:- func initial_formatter_map = formatter_map.
+
+initial_formatter_map = !:Formatters :-
+ !:Formatters = new_formatter_map,
+ set_formatter_sv("builtin", "character", 0, fmt_char, !Formatters),
+ set_formatter_sv("builtin", "float", 0, fmt_float, !Formatters),
+ set_formatter_sv("builtin", "int", 0, fmt_int, !Formatters),
+ set_formatter_sv("builtin", "string", 0, fmt_string, !Formatters),
+ set_formatter_sv("array", "array", 1, fmt_array, !Formatters),
+ set_formatter_sv("list", "list", 1, fmt_list, !Formatters),
+ set_formatter_sv("tree234", "tree234", 2, fmt_tree234, !Formatters).
+
+%-----------------------------------------------------------------------------%
+
+:- pred set_formatter_sv(string::in, string::in, int::in, formatter::in,
+ formatter_map::in, formatter_map::out) is det.
+
+set_formatter_sv(ModuleName, TypeName, Arity, Formatter, FMap0, FMap) :-
+ FMap = set_formatter(ModuleName, TypeName, Arity, Formatter, FMap0).
+
+%-----------------------------------------------------------------------------%
+
+:- func fmt_char(univ, list(type_desc)) = doc.
+
+fmt_char(Univ, _ArgDescs) =
+ ( if Univ = univ(X) then char_to_doc(X) else str("?char?") ).
+
+%-----------------------------------------------------------------------------%
+
+:- func fmt_float(univ, list(type_desc)) = doc.
+
+fmt_float(Univ, _ArgDescs) =
+ ( if Univ = univ(X) then float_to_doc(X) else str("?float?") ).
+
+%-----------------------------------------------------------------------------%
+
+:- func fmt_int(univ, list(type_desc)) = doc.
+
+fmt_int(Univ, _ArgDescs) =
+ ( if Univ = univ(X) then int_to_doc(X) else str("?int?") ).
+
+%-----------------------------------------------------------------------------%
+
+:- func fmt_string(univ, list(type_desc)) = doc.
+
+fmt_string(Univ, _ArgDescs) =
+ ( if Univ = univ(X) then string_to_doc(X) else str("?string?") ).
+
+%-----------------------------------------------------------------------------%
+
+:- func fmt_array(univ, list(type_desc)) = doc.
+
+fmt_array(Univ, ArgDescs) =
+ ( if
+ ArgDescs = [ArgDesc],
+ has_type(_Arg : T, ArgDesc),
+ Value = univ_value(Univ),
+ dynamic_cast(Value, X : array(T))
+ then
+ array_to_doc(X)
+ else
+ str("?array?")
+ ).
+
+%-----------------------------------------------------------------------------%
+
+:- func fmt_list(univ, list(type_desc)) = doc.
+
+fmt_list(Univ, ArgDescs) =
+ ( if
+ ArgDescs = [ArgDesc],
+ has_type(_Arg : T, ArgDesc),
+ Value = univ_value(Univ),
+ dynamic_cast(Value, X : list(T))
+ then
+ list_to_doc(X)
+ else
+ str("?list?")
+ ).
+
+%-----------------------------------------------------------------------------%
+
+:- func fmt_tree234(univ, list(type_desc)) = doc.
+
+fmt_tree234(Univ, ArgDescs) =
+ ( if
+ ArgDescs = [ArgDescA, ArgDescB],
+ has_type(_ArgA : K, ArgDescA),
+ has_type(_ArgB : V, ArgDescB),
+ Value = univ_value(Univ),
+ dynamic_cast(Value, X : tree234(K, V))
+ then
+ tree234_to_doc(X)
+ else
+ str("?tree234?")
+ ).
+
+%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
Index: library/string.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/string.m,v
retrieving revision 1.263
diff -u -r1.263 string.m
--- library/string.m 9 Jul 2007 04:02:18 -0000 1.263
+++ library/string.m 7 Aug 2007 04:35:19 -0000
@@ -39,6 +39,7 @@
:- import_module list.
:- import_module maybe.
:- import_module ops.
+:- import_module pretty_printer.
%-----------------------------------------------------------------------------%
@@ -828,6 +829,10 @@
%
:- func string.word_wrap_separator(string, int, string) = string.
+ % Convert a string to a pretty_printer.doc for formatting.
+ %
+:- func string.string_to_doc(string) = pretty_printer.doc.
+
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
@@ -5363,3 +5368,7 @@
).
%-----------------------------------------------------------------------------%
+
+string.string_to_doc(S) = docs([str("\""), str(S), str("\"")]).
+
+%-----------------------------------------------------------------------------%
Index: library/tree234.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/tree234.m,v
retrieving revision 1.58
diff -u -r1.58 tree234.m
--- library/tree234.m 30 Jul 2007 06:03:07 -0000 1.58
+++ library/tree234.m 7 Aug 2007 03:53:23 -0000
@@ -21,6 +21,7 @@
:- import_module assoc_list.
:- import_module list.
+:- import_module pretty_printer.
:- import_module term.
%-----------------------------------------------------------------------------%
@@ -245,6 +246,13 @@
:- mode tree234.map_foldl2(pred(in, in, out, in, out, in, out) is semidet,
in, out, in, out, in, out) is semidet.
+ % Convert a tree234 into a pretty_printer.doc. A tree mapping
+ % K1 to V1, K2 to V2, ... is formatted as
+ % "map([K1 -> V1, K2 -> V2, ...])". The functor "map" is used
+ % because tree234 values are almost exclusively maps.
+ %
+:- func tree234_to_doc(tree234(K, V)) = pretty_printer.doc.
+
%------------------------------------------------------------------------------%
%------------------------------------------------------------------------------%
@@ -2784,3 +2792,62 @@
tree234.map_values(F, T1) = T2 :-
P = (pred(X::in, Y::in, Z::out) is det :- Z = F(X, Y) ),
tree234.map_values(P, T1, T2).
+
+%-----------------------------------------------------------------------------%
+
+ % The default pretty_printer formatting for key_value_pair will do what
+ % we want.
+ %
+:- type key_value_pair(K, V)
+ ---> (K -> V).
+
+tree234_to_doc(T) =
+ indent([
+ str("map(["),
+ tree234_to_doc_2(tree234_to_lazy_list(T, empty)),
+ str("])")
+ ]).
+
+
+:- func tree234_to_doc_2(lazy_list(K, V)) = doc.
+
+tree234_to_doc_2(empty) = str("").
+
+tree234_to_doc_2(lazy_cons(K, V, Susp)) = Doc :-
+ LL = apply(Susp),
+ (
+ LL = empty,
+ Doc = group([nl, format_arg(format((K -> V)))])
+ ;
+ LL = lazy_cons(_, _, _),
+ Doc = docs([
+ group([nl, format_arg(format((K -> V))), str(", ")]),
+ format_susp((func) = tree234_to_doc_2(LL))
+ ])
+ ).
+
+%-----------------------------------------------------------------------------%
+
+:- type lazy_list(K, V)
+ ---> empty
+ ; lazy_cons(K, V, (func) = lazy_list(K, V)).
+
+
+:- func tree234_to_lazy_list(tree234(K, V), lazy_list(K, V)) = lazy_list(K, V).
+
+tree234_to_lazy_list(empty, LL) = LL.
+
+tree234_to_lazy_list(two(K1, V1, T1, T2), LL) =
+ tree234_to_lazy_list(T1, lazy_cons(K1, V1,
+ (func) = tree234_to_lazy_list(T2, LL))).
+
+tree234_to_lazy_list(three(K1, V1, K2, V2, T1, T2, T3), LL) =
+ tree234_to_lazy_list(T1, lazy_cons(K1, V1,
+ (func) = tree234_to_lazy_list(two(K2, V2, T2, T3), LL))).
+
+tree234_to_lazy_list(four(K1, V1, K2, V2, K3, V3, T1, T2, T3, T4), LL) =
+ tree234_to_lazy_list(T1, lazy_cons(K1, V1,
+ (func) = tree234_to_lazy_list(three(K2, V2, K3, V3, T2, T3, T4), LL))).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
Index: tests/hard_coded/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/tests/hard_coded/Mmakefile,v
retrieving revision 1.328
diff -u -r1.328 Mmakefile
--- tests/hard_coded/Mmakefile 3 Aug 2007 05:18:38 -0000 1.328
+++ tests/hard_coded/Mmakefile 8 Aug 2007 00:44:49 -0000
@@ -220,6 +220,7 @@
test_cord \
test_imported_no_tag \
test_pretty_printer \
+ test_pretty_printer_defaults \
test_promise_impure_implicit \
tim_qual1 \
time_test \
Index: tests/hard_coded/test_pretty_printer_defaults.exp
===================================================================
RCS file: tests/hard_coded/test_pretty_printer_defaults.exp
diff -N tests/hard_coded/test_pretty_printer_defaults.exp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ tests/hard_coded/test_pretty_printer_defaults.exp 8 Aug 2007 02:06:41 -0000
@@ -0,0 +1,21 @@
+two("builtin", two("float", two(0, '<<function>>', empty, empty), two("character", two(0, '<<function>>', empty, empty), empty, empty), three("int", two(0, '<<function>>', empty, empty), "string", two(0, '<<function>>', empty, empty), empty, empty, empty)), two("array", two("array", two(1, '<<function>>', empty, empty), empty, empty), empty, empty), three("list", two("list", two(1, '<<function>>', empty, empty), empty, empty), "tree234", two("tree234", two(2, '<<function>>', empty, empty), empty, empty), empty, empty, empty))
+list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, ...]
+array: array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ..., ...])
+map: map([(1 -> 1), (2 -> 2), (3 -> 3), (4 -> 4), (5 -> 5), (6 -> 6),
+ (7 -> 7), (8 -> 8), (9 -> 9), (10 -> 10), (11 -> 11), (12 -> 12),
+ (13 -> 13), (14 -> 14), (15 -> 15), (16 -> 16), (17 -> 17), (18 -> 18),
+ (19 -> 19), (20 -> 20), (21 -> 21), (22 -> 22), (23 -> 23), (24 -> 24),
+ (25 -> 25), (26 -> 26), (27 -> 27), (28 -> 28), (29 -> 29), (30 -> 30),
+ (31 -> 31), (32 -> 32), (33 -> 33), (34 -> 34), (35 -> 35), (36 -> 36),
+ (37 -> 37), (38 -> 38), (39 -> 39), (40 -> 40), (41 -> 41), (42 -> 42),
+ (43 -> 43), (44 -> 44), (45 -> 45), (46 -> 46), (47 -> 47), (48 -> 48),
+ ..., ...])
+tuple: {1, '2', 3.0, "four"}
+strings: "this is a string"
+ints: 42 -123
+floats: 3.141 -10.0
+chars: ['a', '*', '\n']
Index: tests/hard_coded/test_pretty_printer_defaults.m
===================================================================
RCS file: tests/hard_coded/test_pretty_printer_defaults.m
diff -N tests/hard_coded/test_pretty_printer_defaults.m
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ tests/hard_coded/test_pretty_printer_defaults.m 8 Aug 2007 02:05:31 -0000
@@ -0,0 +1,57 @@
+%-----------------------------------------------------------------------------%
+% test_pretty_printer_defaults.m
+% Ralph Becket <rafe at csse.unimelb.edu.au>
+% Tue Aug 7 15:29:20 EST 2007
+% vim: ft=mercury ts=4 sw=4 et wm=0 tw=0
+%
+% Test the default pretty_printer formatters.
+%
+%-----------------------------------------------------------------------------%
+
+:- module test_pretty_printer_defaults.
+
+:- interface.
+
+:- import_module io.
+
+
+
+:- pred main(io::di, io::uo) is det.
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- import_module array.
+:- import_module char.
+:- import_module int.
+:- import_module list.
+:- import_module map.
+:- import_module pretty_printer.
+
+%-----------------------------------------------------------------------------%
+
+main(!IO) :-
+ L = 1..100,
+ A = array(L),
+ M = map.from_corresponding_lists(L, L) : map(int, int),
+ pretty_printer.get_default_formatter_map(FMap, !IO),
+ io.print(FMap, !IO),
+ io.nl(!IO),
+ pretty_printer.format(
+ docs([
+ str("list: "), format(L), nl,
+ str("array: "), format(A), nl,
+ str("map: "), format(M), nl,
+ str("tuple: "), format({1, '2', 3.0, "four"}), nl,
+ str("strings: "), format("this is a string"), nl,
+ str("ints: "), format(42), str(" "), format(-123), nl,
+ str("floats: "), format(3.141), str(" "), format(-10.0), nl,
+ str("chars: "), format([a, '*', '\n']), nl
+ ]),
+ !IO
+ ).
+
+%-----------------------------------------------------------------------------%
+%-----------------------------------------------------------------------------%
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to: mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions: mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------
More information about the reviews
mailing list