LotSizingDataModel.Instance 2.0.1
Lot-sizing instance representation, descriptors and problem characterization.
Loading...
Searching...
No Matches
UniversalNotationParser.cs
Go to the documentation of this file.
3
5
6/// <summary>
7/// Parses and canonicalizes notation scheme version 1.
8/// </summary>
9public sealed class UniversalNotationParser
10{
12 string text,
13 string? schemeVersion = null)
14 {
15 if (string.IsNullOrWhiteSpace(text))
16 {
17 throw new FormatException(
18 "Universal notation cannot be empty.");
19 }
20
21 string version =
22 schemeVersion ??
24
26 {
27 throw new NotSupportedException(
28 $"Universal notation scheme version '{version}' " +
29 "is not supported.");
30 }
31
32 string[] sections =
33 text.Split(
34 '|',
35 StringSplitOptions.TrimEntries);
36
37 if (sections.Length != 3)
38 {
39 throw new FormatException(
40 "Universal notation must contain exactly three " +
41 "alpha | beta | gamma sections.");
42 }
43
45 ParseAlpha(sections[0]),
46 ParseBeta(sections[1]),
47 ParseGamma(sections[2]),
48 version);
49 }
50
51 public string Canonicalize(
52 string text,
53 string? schemeVersion = null)
54 {
55 return Parse(text, schemeVersion).Render();
56 }
57
58 private static UniversalNotationAlpha ParseAlpha(
59 string section)
60 {
61 string[] tokens =
62 section.Split(
63 ',',
64 StringSplitOptions.RemoveEmptyEntries |
65 StringSplitOptions.TrimEntries);
66
67 UniversalItemCardinality cardinality =
69
72
73 ProductStructureType productStructureType =
75
76 UniversalNetworkNotation? network = null;
77
78 foreach (string token in tokens)
79 {
80 if (token == "1")
81 {
82 cardinality =
84 }
85 else if (
86 token.Equals(
87 "m",
88 StringComparison.OrdinalIgnoreCase))
89 {
90 cardinality =
92 }
93 else if (token == "?")
94 {
95 cardinality =
97 }
98 else if (
99 token.Equals(
100 "SL",
101 StringComparison.OrdinalIgnoreCase))
102 {
103 level =
104 UniversalProblemLevel.SingleLevel;
105
106 productStructureType =
107 ProductStructureType.IndependentItems;
108 }
109 else if (
110 token.StartsWith(
111 "ML:",
112 StringComparison.OrdinalIgnoreCase))
113 {
114 level =
115 UniversalProblemLevel.MultiLevel;
116
117 productStructureType =
118 UniversalNotationTokenCatalog
119 .ParseProductStructureCode(
120 token[3..]);
121 }
122 else if (
123 token.Equals(
124 "Level:?",
125 StringComparison.OrdinalIgnoreCase))
126 {
127 level =
128 UniversalProblemLevel.Unknown;
129 }
130 else if (
131 token.StartsWith(
132 "Net:",
133 StringComparison.OrdinalIgnoreCase))
134 {
135 if (network is not null)
136 {
137 throw new FormatException(
138 "Alpha section contains more than one network token.");
139 }
140
141 network = ParseNetwork(token);
142 }
143 else
144 {
145 throw new FormatException(
146 $"Unknown alpha token '{token}'.");
147 }
148 }
149
150 if (network is null)
151 {
152 throw new FormatException(
153 "Alpha section must contain a network token.");
154 }
155
156 return new UniversalNotationAlpha
157 {
158 ItemCardinality = cardinality,
159 ProblemLevel = level,
160 ProductStructureType = productStructureType,
161 Network = network
162 };
163 }
164
165 private static UniversalNetworkNotation ParseNetwork(
166 string token)
167 {
168 string working = token.Trim();
169
170 NetworkCouplingType coupling;
171 SupplyNetworkTopologyType forwardTopology;
172 SupplyNetworkTopologyType? reverseTopology = null;
173 string modifiersText = string.Empty;
174
175 if (
176 working.StartsWith(
177 "Net:CL(",
178 StringComparison.OrdinalIgnoreCase))
179 {
180 int closeIndex = working.IndexOf(')');
181
182 if (closeIndex < 0)
183 {
184 throw new FormatException(
185 "Closed-loop network token is missing ')'.");
186 }
187
188 string core =
189 working.Substring(
190 "Net:CL(".Length,
191 closeIndex - "Net:CL(".Length);
192
193 string[] parts =
194 core.Split(
195 ';',
196 StringSplitOptions.TrimEntries);
197
198 if (parts.Length != 2 ||
199 !parts[0].StartsWith(
200 "F:",
201 StringComparison.OrdinalIgnoreCase) ||
202 !parts[1].StartsWith(
203 "R:",
204 StringComparison.OrdinalIgnoreCase))
205 {
206 throw new FormatException(
207 "Closed-loop network must use " +
208 "Net:CL(F:<topology>;R:<topology>).");
209 }
210
211 coupling = NetworkCouplingType.ClosedLoop;
212
213 forwardTopology =
214 UniversalNotationTokenCatalog
215 .ParseNetworkTopologyCode(
216 parts[0][2..]);
217
218 reverseTopology =
219 UniversalNotationTokenCatalog
220 .ParseNetworkTopologyCode(
221 parts[1][2..]);
222
223 if (closeIndex + 1 < working.Length)
224 {
225 if (working[closeIndex + 1] != ':')
226 {
227 throw new FormatException(
228 "Invalid network modifier separator.");
229 }
230
231 modifiersText =
232 working[(closeIndex + 2)..];
233 }
234 }
235 else
236 {
237 string[] parts =
238 working.Split(
239 ':',
240 StringSplitOptions.RemoveEmptyEntries |
241 StringSplitOptions.TrimEntries);
242
243 if (parts.Length < 2 ||
244 !parts[0].Equals(
245 "Net",
246 StringComparison.OrdinalIgnoreCase))
247 {
248 throw new FormatException(
249 $"Invalid network token '{token}'.");
250 }
251
252 int modifierStart;
253
254 if (
255 parts.Length >= 3 &&
256 parts[1].Equals(
257 "R",
258 StringComparison.OrdinalIgnoreCase))
259 {
260 coupling = NetworkCouplingType.ReverseOnly;
261
262 reverseTopology =
263 UniversalNotationTokenCatalog
264 .ParseNetworkTopologyCode(
265 parts[2]);
266
267 forwardTopology =
269
270 modifierStart = 3;
271 }
272 else
273 {
274 coupling = NetworkCouplingType.ForwardOnly;
275
276 forwardTopology =
277 UniversalNotationTokenCatalog
278 .ParseNetworkTopologyCode(
279 parts[1]);
280
281 modifierStart = 2;
282 }
283
284 modifiersText =
285 string.Join(
286 ":",
287 parts.Skip(modifierStart));
288 }
289
290 int? echelonCount = null;
291 bool hasCycles = false;
292 bool hasMultiSourcing = false;
293 bool hasTransshipment = false;
294
295 if (!string.IsNullOrWhiteSpace(modifiersText))
296 {
297 foreach (
298 string modifier
299 in modifiersText.Split(
300 ':',
301 StringSplitOptions.RemoveEmptyEntries |
302 StringSplitOptions.TrimEntries))
303 {
304 if (
305 modifier.StartsWith(
306 "E",
307 StringComparison.OrdinalIgnoreCase))
308 {
309 if (!int.TryParse(
310 modifier[1..],
311 out int parsedEchelon) ||
312 parsedEchelon < 0)
313 {
314 throw new FormatException(
315 $"Invalid echelon modifier '{modifier}'.");
316 }
317
318 echelonCount = parsedEchelon;
319 }
320 else if (
321 modifier.Equals(
322 "CY",
323 StringComparison.OrdinalIgnoreCase))
324 {
325 hasCycles = true;
326 }
327 else if (
328 modifier.Equals(
329 "MS",
330 StringComparison.OrdinalIgnoreCase))
331 {
332 hasMultiSourcing = true;
333 }
334 else if (
335 modifier.Equals(
336 "TS",
337 StringComparison.OrdinalIgnoreCase))
338 {
339 hasTransshipment = true;
340 }
341 else
342 {
343 throw new FormatException(
344 $"Unknown network modifier '{modifier}'.");
345 }
346 }
347 }
348
349 return new UniversalNetworkNotation
350 {
351 Coupling = coupling,
352 ForwardTopology = forwardTopology,
353 ReverseTopology = reverseTopology,
354 EchelonCount = echelonCount,
355 HasCycles = hasCycles,
356 HasMultiSourcing = hasMultiSourcing,
357 HasTransshipment = hasTransshipment
358 };
359 }
360
361 private static UniversalNotationBeta ParseBeta(
362 string section)
363 {
364 if (
365 section.Equals(
366 "None",
367 StringComparison.OrdinalIgnoreCase))
368 {
369 return new UniversalNotationBeta();
370 }
371
372 string[] tokens =
373 section.Split(
374 ',',
375 StringSplitOptions.RemoveEmptyEntries |
376 StringSplitOptions.TrimEntries);
377
378 var features =
379 new List<UniversalNotationFeature>();
380
381 var temporalQualifiers =
382 new List<UniversalTemporalQualifier>();
383
384 var semanticConditions =
385 new List<UniversalSemanticCondition>();
386
387 foreach (string token in tokens)
388 {
389 if (
390 UniversalNotationTokenCatalog
391 .TryParseTemporalQualifier(
392 token,
393 out UniversalTemporalQualifier? qualifier))
394 {
395 temporalQualifiers.Add(
396 qualifier!);
397
398 continue;
399 }
400
401 if (
402 UniversalNotationTokenCatalog
403 .TryParseSemanticCondition(
404 token,
405 out UniversalSemanticCondition semanticCondition))
406 {
407 semanticConditions.Add(
408 semanticCondition);
409
410 continue;
411 }
412
413 if (!UniversalNotationTokenCatalog.TryParseFeature(
414 token,
415 out UniversalNotationFeature feature))
416 {
417 throw new FormatException(
418 $"Unknown beta token '{token}'.");
419 }
420
421 features.Add(feature);
422 }
423
424 return new UniversalNotationBeta(
425 features,
426 temporalQualifiers,
427 semanticConditions);
428 }
429
430 private static UniversalNotationGamma ParseGamma(
431 string section)
432 {
433 UniversalObjectiveKind objective =
434 section.Trim().ToUpperInvariant() switch
435 {
436 "OBJ:ECON" =>
437 UniversalObjectiveKind.Economic,
438 "OBJ:MULTI" =>
439 UniversalObjectiveKind.MultipleObjectives,
440 "OBJ:FIN" =>
441 UniversalObjectiveKind.Financial,
442 "OBJ:SUST" =>
443 UniversalObjectiveKind.Sustainability,
444 "OBJ:SERVICE" =>
445 UniversalObjectiveKind.ServiceLevel,
446 "OBJ:?" =>
448 _ => throw new FormatException(
449 $"Unknown gamma token '{section.Trim()}'.")
450 };
451
452 return new UniversalNotationGamma
453 {
454 Objective = objective
455 };
456 }
457}
Typed abstract syntax tree for the versioned alpha | beta | gamma scheme.
Typed alpha-field representation of the physical network.
Alpha field: structural/environment characteristics.
Parses and canonicalizes notation scheme version 1.
string Canonicalize(string text, string? schemeVersion=null)
UniversalLotSizingNotation Parse(string text, string? schemeVersion=null)
Identifies the versioned LotSizingDataModel universal notation scheme.
ProductStructureType
Identifies the structural category of a product bill-of-materials graph.
NetworkCouplingType
Describes coupling between forward and reverse physical networks.
SupplyNetworkTopologyType
Classifies the directed physical topology of a supply-flow network.
UniversalSemanticCondition
Generic derived semantic conditions that may be required by a universal problem specification.
UniversalObjectiveKind
Objective family represented in the gamma field of notation v1.
UniversalNotationFeature
Feature vocabulary represented in the beta field of notation v1. Enum order is the canonical renderin...
UniversalProblemLevel
Canonical BOM-level token.
record UniversalTemporalQualifier
Qualifies one universal model-parameter family with a generic temporal pattern.
UniversalItemCardinality
Canonical item-cardinality token.