-
Notifications
You must be signed in to change notification settings - Fork 47
/
Copy pathA0$messages.m
60 lines (48 loc) · 2.52 KB
/
A0$messages.m
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
51
52
53
54
55
56
57
58
59
60
Package["SetReplace`"]
(* declareMessage should be loaded before everything else because it is used outside of functions.
(Files are loaded lexicographically.) *)
PackageImport["GeneralUtilities`"]
PackageScope["declareMessage"]
PackageScope["message"]
PackageScope["throw"]
$messageSlotNames = <||>;
Attributes[declareMessage] = {HoldFirst};
declareMessage[messageName_, template_] := Module[{templateObject, argumentNames, namesToIndices},
templateObject = StringTemplate[template];
argumentNames = First /@ Cases[templateObject[[1]], _TemplateSlot];
namesToIndices = AssociationThread[argumentNames -> Range[Length[argumentNames]]];
AssociateTo[$messageSlotNames, Hold[messageName] -> argumentNames];
messageName = StringJoin[Replace[templateObject[[1]],
TemplateSlot[name_] :> "`" <> ToString[namesToIndices[name]] <> "`",
1]];
];
message::messageNotFound = "Message `` could not be found. Messages need to be declared first with declareMessage.";
message::missingArgs = "Arguments `2` missing for message `1`.";
Attributes[message] = {HoldFirst};
message[MessageName[_, None], _] := Null; (* This is helpful for returning unevaluated without printing any messages. *)
message[messageName_, args_ ? AssociationQ] := ModuleScope[
(* Look for the specific message first, e.g., symb::msg. If not found, look for General::msg.
General:: messages should be possible to generate for any symbol. *)
argumentsOrder =
Lookup[$messageSlotNames, Hold[messageName], $messageSlotNames[ReplacePart[Hold[messageName], {1, 1} -> General]]];
If[MissingQ[argumentsOrder],
Message[message::messageNotFound, HoldForm[messageName]]
,
missingArgs = Complement[argumentsOrder, Keys[args]];
If[missingArgs =!= {},
Message[message::missingArgs, HoldForm[messageName], missingArgs];
,
Message[messageName, ##] & @@ Replace[argumentsOrder, args, 1];
];
];
];
(* extraArgs is useful for passing public symbol-specific information such as the public function call. *)
message[head_, failure_ ? FailureQ, extraArgs : _ ? AssociationQ : <||>] := With[{
messageName = failure[[1]],
messageArguments = Join[failure[[2]], extraArgs]},
message[MessageName[head, messageName], messageArguments];
Failure[messageName, messageArguments]
];
message[head_][failure_ ? FailureQ, ___] := message[head, failure];
(* Throws identical value and tag so that the value can be caught with the second argument of Catch *)
throw[exception_] := Throw[exception, exception];