source: Common/MatlabModule.cs

Last change on this file was 11, checked in by chronos, 5 years ago
  • Added: Generic classes for loading MATLAB .mdl files and Rational Rose .cat/.sub files.
File size: 19.6 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Globalization;
4using System.Linq;
5using System.Text;
6using System.Text.RegularExpressions;
7using System.IO;
8using System.Drawing;
9using System.Threading.Tasks;
10
11namespace Common
12{
13 public class MatlabModule
14 {
15 public MatlabBlock root;
16
17 public MatlabModule()
18 {
19 root = new MatlabBlock();
20 }
21
22 public void LoadFromFile(string fileName)
23 {
24 Parser parser = new Parser();
25 parser.content = File.ReadAllText(fileName);
26 string itemName = root.ReadName(parser);
27 if (itemName == "") return;
28 MatlabBlock.Parse(parser, out root);
29 if (root != null) root.name = itemName;
30 }
31
32 public void SaveToFile(string fileName)
33 {
34 StringBuilder output = new StringBuilder();
35 root.Compose(output);
36 File.WriteAllText(fileName, output.ToString());
37 }
38 }
39
40 public class MatlabItem
41 {
42 public string name;
43 public MatlabItem parent;
44
45 public virtual string GetAsString(int indent = 0)
46 {
47 return string.Concat(Enumerable.Repeat(" ", indent)) + "Name: " + name + Environment.NewLine;
48 }
49
50 public virtual List<MatlabItem> Find(string text, bool exact = true)
51 {
52 return new List<MatlabItem>();
53 }
54
55 public virtual void Compose(StringBuilder output, int ident = 0)
56 {
57 }
58
59 public string ReadName(Parser parser)
60 {
61 string result = parser.ReadNext();
62 while (parser.CheckNext("."))
63 {
64 result += "." + parser.ReadNext();
65 }
66 return result;
67 }
68 }
69
70 class MatlabString : MatlabItem
71 {
72 public string value;
73 public bool quoted;
74
75 public MatlabString()
76 {
77 value = "";
78 quoted = false;
79 }
80
81 public override string GetAsString(int indent = 0)
82 {
83 return base.GetAsString(indent) +
84 string.Concat(Enumerable.Repeat(" ", indent)) + "Value: " + value + Environment.NewLine;
85 }
86
87 public override List<MatlabItem> Find(string text, bool exact = true)
88 {
89 List<MatlabItem> result = new List<MatlabItem>();
90 if (((text == value) && exact) || (value.Contains(text) && !exact)) result.Add(this);
91 return result;
92 }
93
94 public static bool Parse(Parser parser, out MatlabString itemString)
95 {
96 itemString = new MatlabString();
97 itemString.value = parser.ReadNext();
98 itemString.quoted = parser.elementType == ElementType.String;
99 while (true)
100 {
101 if (parser.CheckNextType(ElementType.String))
102 {
103 itemString.value += parser.ReadNext();
104 }
105 else
106 {
107 break;
108 }
109 }
110 if (itemString.quoted) itemString.value = itemString.value.Unescape();
111 return true;
112 }
113
114 public override void Compose(StringBuilder output, int indent = 0)
115 {
116 output.Append(string.Concat(Enumerable.Repeat(" ", indent)) + name + " ");
117 if (quoted)
118 {
119 var parts = value.SplitInParts(43);
120 int i = 0;
121 foreach (var part in parts)
122 {
123 output.Append("\"" + part.Escape() + "\"");
124 if (i < parts.Count() - 1) output.Append(Environment.NewLine);
125 }
126 }
127 else output.Append(value);
128 output.Append(Environment.NewLine);
129 }
130 }
131
132 class MatlabList : MatlabItem
133 {
134 public List<List<int>> items;
135
136 public MatlabList()
137 {
138 items = new List<List<int>>();
139 items.Add(new List<int>());
140 }
141
142 public static bool Parse(Parser parser, out MatlabList list)
143 {
144 if (!parser.CheckNext("["))
145 {
146 list = null;
147 return false;
148 }
149 list = new MatlabList();
150 parser.Expect("[");
151 int output;
152 if (!parser.CheckNext("]"))
153 {
154 if (int.TryParse(parser.ReadNext(), out output))
155 list.items.Last().Add(output);
156
157 while (parser.CheckNext(",") || parser.CheckNext(";"))
158 {
159 if (parser.CheckNext(";"))
160 {
161 parser.Expect(";");
162 list.items.Add(new List<int>());
163 }
164 else
165 {
166 parser.Expect(",");
167 }
168 if (int.TryParse(parser.ReadNext(), out output))
169 list.items.Last().Add(output);
170 }
171 }
172 parser.Expect("]");
173 return true;
174 }
175
176 public override void Compose(StringBuilder output, int indent = 0)
177 {
178 output.Append(string.Concat(Enumerable.Repeat(" ", indent)) + name + " [");
179 int i = 0;
180 foreach (var item in items)
181 {
182 if (i > 0) output.Append("; ");
183 int j = 0;
184 foreach (var item2 in item)
185 {
186 if (j > 0) output.Append(", ");
187 output.Append(item2);
188 j++;
189 }
190 i++;
191 }
192 output.Append("]" + Environment.NewLine);
193 }
194
195 public override string GetAsString(int indent = 0)
196 {
197 return base.GetAsString(indent) +
198 string.Concat(Enumerable.Repeat(" ", indent)) + "Items: " + string.Join(", ", items) + Environment.NewLine;
199 }
200 }
201
202 public class MatlabBlock : MatlabItem
203 {
204 public List<MatlabItem> items;
205
206 public MatlabBlock()
207 {
208 items = new List<MatlabItem>();
209 }
210
211 public static bool Parse(Parser parser, out MatlabBlock block)
212 {
213 if (!parser.CheckNext("{"))
214 {
215 block = null;
216 return false;
217 }
218 block = new MatlabBlock();
219 parser.Expect("{");
220 MatlabBlock itemBlock;
221 MatlabList itemList;
222 MatlabString itemString;
223 while (!parser.CheckNext("}"))
224 {
225 string itemName = block.ReadName(parser);
226 if (MatlabBlock.Parse(parser, out itemBlock))
227 {
228 itemBlock.name = itemName;
229 itemBlock.parent = block;
230 block.items.Add(itemBlock);
231 }
232 else
233 if (MatlabList.Parse(parser, out itemList))
234 {
235 itemList.name = itemName;
236 itemList.parent = block;
237 block.items.Add(itemList);
238 }
239 else
240 if (MatlabString.Parse(parser, out itemString))
241 {
242 itemString.name = itemName;
243 itemString.parent = block;
244 block.items.Add(itemString);
245 }
246 else
247 throw new Exception("Unsupported block item type.");
248 }
249 parser.Expect("}");
250 return true;
251 }
252
253 public override void Compose(StringBuilder output, int indent = 0)
254 {
255 output.Append(string.Concat(Enumerable.Repeat(" ", indent)) + name + " {" + Environment.NewLine);
256 foreach (var item in items)
257 {
258 item.Compose(output, indent + 1);
259 }
260 output.Append(string.Concat(Enumerable.Repeat(" ", indent)) + "}" + Environment.NewLine);
261 }
262 }
263
264 public enum ElementType { None, String, Ident, Number, SpecialChar, Comment };
265
266 public struct ParserPosition
267 {
268 public int index;
269 public Point position;
270 }
271
272 public struct ParserToken
273 {
274 public string text;
275 public ElementType type;
276 }
277
278 public class ParserState
279 {
280 public ParserPosition currentPosition;
281 public ParserPosition externalPosition;
282 public ParserToken lastToken;
283 }
284
285 public class Parser
286 {
287 public string content;
288 ParserPosition currentPosition;
289 ParserPosition previousPosition;
290 ParserPosition externalPosition;
291 ParserToken lastToken;
292 public ElementType elementType;
293
294 public Parser()
295 {
296 lastToken.type = ElementType.None;
297 Clear();
298 }
299
300 public char ReadChar()
301 {
302 previousPosition = currentPosition;
303 char result = content[currentPosition.index];
304 currentPosition.index++;
305 if (result == '\n')
306 {
307 currentPosition.position.Y++;
308 currentPosition.position.X = 1;
309 }
310 else currentPosition.position.X++;
311 return result;
312 }
313
314 public void Clear()
315 {
316 currentPosition.index = 0;
317 currentPosition.position = new Point(1, 1);
318 previousPosition = currentPosition;
319 externalPosition = currentPosition;
320 }
321
322 public bool IsSpecialChar(char character)
323 {
324 return (character == '{') || (character == '}') || (character == '[') || (character == ']') ||
325 (character == '\r') || (character == ',') || (character == '.') || (character == '$') || (character == ';');
326 }
327
328 public ParserToken ReadNextToken()
329 {
330 string resultString = "";
331 elementType = ElementType.None;
332 bool finished = false;
333 while ((currentPosition.index < content.Length) && !finished)
334 {
335 char character = ReadChar();
336 if (elementType == ElementType.String)
337 {
338 if (character == '\"')
339 {
340 finished = true;
341 break;
342 }
343 else
344 {
345 if (character == '\\')
346 {
347 character = ReadChar();
348 if (character == 'n') resultString += '\n';
349 else if (character == 'r') resultString += '\r';
350 else if (character == 't') resultString += '\t';
351 else resultString += character;
352 }
353 else resultString += character;
354 }
355 }
356 else
357 if (elementType == ElementType.Ident)
358 {
359 if (char.IsLetterOrDigit(character) || (character == '_'))
360 {
361 resultString += character;
362 }
363 else
364 {
365 currentPosition = previousPosition;
366 finished = true;
367 break;
368 }
369 }
370 else
371 if (elementType == ElementType.Number)
372 {
373 if (char.IsNumber(character) || (character == '.'))
374 {
375 resultString += character;
376 }
377 else
378 {
379 currentPosition = previousPosition;
380 finished = true;
381 break;
382 }
383 }
384 else
385 if (elementType == ElementType.Comment)
386 {
387 if ((character == '\n') || (character == '\r'))
388 {
389 elementType = ElementType.None;
390 }
391 }
392 else
393 {
394 if (character == '#')
395 {
396 elementType = ElementType.Comment;
397 } else
398 if ((character == '\r') || (character == '\n'))
399 {
400 // Ignore new lines
401 }
402 else
403 if (IsSpecialChar(character))
404 {
405 elementType = ElementType.SpecialChar;
406 resultString += character;
407 break;
408 }
409 else
410 if (char.IsWhiteSpace(character))
411 {
412 if (resultString == "")
413 {
414 }
415 else
416 {
417 break;
418 }
419 }
420 else
421 if (char.IsLetter(character) || (character == '_') || (character == '@'))
422 {
423 resultString += character;
424 elementType = ElementType.Ident;
425 }
426 else
427 if (char.IsNumber(character) || (character == '-'))
428 {
429 resultString += character;
430 elementType = ElementType.Number;
431 }
432 else
433 if (character == '\"')
434 {
435 elementType = ElementType.String;
436 }
437 else
438 {
439 ErrorMessage("Unsupported character '" + character + "'");
440 }
441 }
442 }
443 ParserToken result;
444 result.text = resultString;
445 result.type = elementType;
446 return result;
447 }
448
449 public string ReadNext()
450 {
451 if (lastToken.type == ElementType.None) lastToken = ReadNextToken();
452 string result = lastToken.text;
453 lastToken.type = ElementType.None;
454 externalPosition = currentPosition;
455 return result;
456 }
457
458 public bool CheckNext(string text)
459 {
460 if (lastToken.type == ElementType.None) lastToken = ReadNextToken();
461 return lastToken.text == text;
462 }
463
464 public bool CheckNextType(ElementType type)
465 {
466 if (lastToken.type == ElementType.None) lastToken = ReadNextToken();
467 return lastToken.type == type;
468 }
469
470 public void Expect(string text)
471 {
472 if (lastToken.type == ElementType.None) lastToken = ReadNextToken();
473 lastToken.type = ElementType.None;
474 externalPosition = currentPosition;
475 if (lastToken.text != text)
476 {
477 ErrorMessage("Expected '" + text + "' but '" + lastToken.text + "' found.");
478 }
479 }
480
481 public void ErrorMessage(string Text)
482 {
483 throw new Exception("Parsing error: " + Text + " (" + externalPosition.position.X.ToString() + "," +
484 externalPosition.position.Y.ToString() + ")");
485 }
486
487 public ParserState GetState()
488 {
489 ParserState result = new ParserState();
490 result.currentPosition = currentPosition;
491 result.externalPosition = externalPosition;
492 result.lastToken = lastToken;
493 return result;
494 }
495
496 public void SetState(ParserState state)
497 {
498 currentPosition = state.currentPosition;
499 externalPosition = state.externalPosition;
500 lastToken = state.lastToken;
501 }
502 }
503
504 static class StringExtensions
505 {
506 public static IEnumerable<String> SplitInParts(this String s, Int32 partLength)
507 {
508 if (s == null)
509 throw new ArgumentNullException("s");
510 if (partLength <= 0)
511 throw new ArgumentException("Part length has to be positive.", "partLength");
512
513 for (var i = 0; i < s.Length; i += partLength)
514 yield return s.Substring(i, Math.Min(partLength, s.Length - i));
515 }
516
517 public static string Unescape(this string txt)
518 {
519 if (string.IsNullOrEmpty(txt)) { return txt; }
520 StringBuilder retval = new StringBuilder(txt.Length);
521 for (int ix = 0; ix < txt.Length;)
522 {
523 int jx = txt.IndexOf('\\', ix);
524 if ((jx < 0) || (jx == txt.Length - 1)) jx = txt.Length;
525 retval.Append(txt, ix, jx - ix);
526 if (jx >= txt.Length) break;
527 switch (txt[jx + 1])
528 {
529 //case '\'': retval.Append('\''); break; // Single quote
530 case '"': retval.Append('"'); break; // Double quote
531 //case '\\': retval.Append('\\'); break; // Don't escape
532 case 'a': retval.Append('\a'); break; // Line feed
533 case 'b': retval.Append('\b'); break; // Backspace
534 case 'f': retval.Append('\f'); break; // Form feed
535 case 'n': retval.Append('\n'); break; // Line feed
536 case 'r': retval.Append('\r'); break; // Carriage return
537 case 't': retval.Append('\t'); break; // Horizontal tab
538 case 'v': retval.Append('\v'); break; // Vertical tab
539 default: // Unrecognized, copy as-is
540 retval.Append('\\').Append(txt[jx + 1]); break;
541 }
542 ix = jx + 2;
543 }
544 return retval.ToString();
545 }
546
547 public static string Escape(this string input)
548 {
549 var literal = new StringBuilder(input.Length + 2);
550 foreach (var c in input)
551 {
552 switch (c)
553 {
554 //case '\'': literal.Append(@"\'"); break;
555 case '\"': literal.Append("\\\""); break;
556 //case '\\': literal.Append(@"\\"); break;
557 case '\0': literal.Append(@"\0"); break;
558 case '\a': literal.Append(@"\a"); break;
559 case '\b': literal.Append(@"\b"); break;
560 case '\f': literal.Append(@"\f"); break;
561 case '\n': literal.Append(@"\n"); break;
562 case '\r': literal.Append(@"\r"); break;
563 case '\t': literal.Append(@"\t"); break;
564 case '\v': literal.Append(@"\v"); break;
565 default:
566 if (Char.GetUnicodeCategory(c) != UnicodeCategory.Control)
567 {
568 literal.Append(c);
569 }
570 else
571 {
572 literal.Append(@"\u");
573 literal.Append(((ushort)c).ToString("x4"));
574 }
575 break;
576 }
577 }
578 return literal.ToString();
579 }
580 }
581}
Note: See TracBrowser for help on using the repository browser.