using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.IO; using System.Drawing; using System.Threading.Tasks; namespace Common { public class MatlabModule { public MatlabBlock root; public MatlabModule() { root = new MatlabBlock(); } public void LoadFromFile(string fileName) { Parser parser = new Parser(); parser.content = File.ReadAllText(fileName); string itemName = root.ReadName(parser); if (itemName == "") return; MatlabBlock.Parse(parser, out root); if (root != null) root.name = itemName; } public void SaveToFile(string fileName) { StringBuilder output = new StringBuilder(); root.Compose(output); File.WriteAllText(fileName, output.ToString()); } } public class MatlabItem { public string name; public MatlabItem parent; public virtual string GetAsString(int indent = 0) { return string.Concat(Enumerable.Repeat(" ", indent)) + "Name: " + name + Environment.NewLine; } public virtual List Find(string text, bool exact = true) { return new List(); } public virtual void Compose(StringBuilder output, int ident = 0) { } public string ReadName(Parser parser) { string result = parser.ReadNext(); while (parser.CheckNext(".")) { result += "." + parser.ReadNext(); } return result; } } class MatlabString : MatlabItem { public string value; public bool quoted; public MatlabString() { value = ""; quoted = false; } public override string GetAsString(int indent = 0) { return base.GetAsString(indent) + string.Concat(Enumerable.Repeat(" ", indent)) + "Value: " + value + Environment.NewLine; } public override List Find(string text, bool exact = true) { List result = new List(); if (((text == value) && exact) || (value.Contains(text) && !exact)) result.Add(this); return result; } public static bool Parse(Parser parser, out MatlabString itemString) { itemString = new MatlabString(); itemString.value = parser.ReadNext(); itemString.quoted = parser.elementType == ElementType.String; while (true) { if (parser.CheckNextType(ElementType.String)) { itemString.value += parser.ReadNext(); } else { break; } } if (itemString.quoted) itemString.value = itemString.value.Unescape(); return true; } public override void Compose(StringBuilder output, int indent = 0) { output.Append(string.Concat(Enumerable.Repeat(" ", indent)) + name + " "); if (quoted) { var parts = value.SplitInParts(43); int i = 0; foreach (var part in parts) { output.Append("\"" + part.Escape() + "\""); if (i < parts.Count() - 1) output.Append(Environment.NewLine); } } else output.Append(value); output.Append(Environment.NewLine); } } class MatlabList : MatlabItem { public List> items; public MatlabList() { items = new List>(); items.Add(new List()); } public static bool Parse(Parser parser, out MatlabList list) { if (!parser.CheckNext("[")) { list = null; return false; } list = new MatlabList(); parser.Expect("["); int output; if (!parser.CheckNext("]")) { if (int.TryParse(parser.ReadNext(), out output)) list.items.Last().Add(output); while (parser.CheckNext(",") || parser.CheckNext(";")) { if (parser.CheckNext(";")) { parser.Expect(";"); list.items.Add(new List()); } else { parser.Expect(","); } if (int.TryParse(parser.ReadNext(), out output)) list.items.Last().Add(output); } } parser.Expect("]"); return true; } public override void Compose(StringBuilder output, int indent = 0) { output.Append(string.Concat(Enumerable.Repeat(" ", indent)) + name + " ["); int i = 0; foreach (var item in items) { if (i > 0) output.Append("; "); int j = 0; foreach (var item2 in item) { if (j > 0) output.Append(", "); output.Append(item2); j++; } i++; } output.Append("]" + Environment.NewLine); } public override string GetAsString(int indent = 0) { return base.GetAsString(indent) + string.Concat(Enumerable.Repeat(" ", indent)) + "Items: " + string.Join(", ", items) + Environment.NewLine; } } public class MatlabBlock : MatlabItem { public List items; public MatlabBlock() { items = new List(); } public static bool Parse(Parser parser, out MatlabBlock block) { if (!parser.CheckNext("{")) { block = null; return false; } block = new MatlabBlock(); parser.Expect("{"); MatlabBlock itemBlock; MatlabList itemList; MatlabString itemString; while (!parser.CheckNext("}")) { string itemName = block.ReadName(parser); if (MatlabBlock.Parse(parser, out itemBlock)) { itemBlock.name = itemName; itemBlock.parent = block; block.items.Add(itemBlock); } else if (MatlabList.Parse(parser, out itemList)) { itemList.name = itemName; itemList.parent = block; block.items.Add(itemList); } else if (MatlabString.Parse(parser, out itemString)) { itemString.name = itemName; itemString.parent = block; block.items.Add(itemString); } else throw new Exception("Unsupported block item type."); } parser.Expect("}"); return true; } public override void Compose(StringBuilder output, int indent = 0) { output.Append(string.Concat(Enumerable.Repeat(" ", indent)) + name + " {" + Environment.NewLine); foreach (var item in items) { item.Compose(output, indent + 1); } output.Append(string.Concat(Enumerable.Repeat(" ", indent)) + "}" + Environment.NewLine); } } public enum ElementType { None, String, Ident, Number, SpecialChar, Comment }; public struct ParserPosition { public int index; public Point position; } public struct ParserToken { public string text; public ElementType type; } public class ParserState { public ParserPosition currentPosition; public ParserPosition externalPosition; public ParserToken lastToken; } public class Parser { public string content; ParserPosition currentPosition; ParserPosition previousPosition; ParserPosition externalPosition; ParserToken lastToken; public ElementType elementType; public Parser() { lastToken.type = ElementType.None; Clear(); } public char ReadChar() { previousPosition = currentPosition; char result = content[currentPosition.index]; currentPosition.index++; if (result == '\n') { currentPosition.position.Y++; currentPosition.position.X = 1; } else currentPosition.position.X++; return result; } public void Clear() { currentPosition.index = 0; currentPosition.position = new Point(1, 1); previousPosition = currentPosition; externalPosition = currentPosition; } public bool IsSpecialChar(char character) { return (character == '{') || (character == '}') || (character == '[') || (character == ']') || (character == '\r') || (character == ',') || (character == '.') || (character == '$') || (character == ';'); } public ParserToken ReadNextToken() { string resultString = ""; elementType = ElementType.None; bool finished = false; while ((currentPosition.index < content.Length) && !finished) { char character = ReadChar(); if (elementType == ElementType.String) { if (character == '\"') { finished = true; break; } else { if (character == '\\') { character = ReadChar(); if (character == 'n') resultString += '\n'; else if (character == 'r') resultString += '\r'; else if (character == 't') resultString += '\t'; else resultString += character; } else resultString += character; } } else if (elementType == ElementType.Ident) { if (char.IsLetterOrDigit(character) || (character == '_')) { resultString += character; } else { currentPosition = previousPosition; finished = true; break; } } else if (elementType == ElementType.Number) { if (char.IsNumber(character) || (character == '.')) { resultString += character; } else { currentPosition = previousPosition; finished = true; break; } } else if (elementType == ElementType.Comment) { if ((character == '\n') || (character == '\r')) { elementType = ElementType.None; } } else { if (character == '#') { elementType = ElementType.Comment; } else if ((character == '\r') || (character == '\n')) { // Ignore new lines } else if (IsSpecialChar(character)) { elementType = ElementType.SpecialChar; resultString += character; break; } else if (char.IsWhiteSpace(character)) { if (resultString == "") { } else { break; } } else if (char.IsLetter(character) || (character == '_') || (character == '@')) { resultString += character; elementType = ElementType.Ident; } else if (char.IsNumber(character) || (character == '-')) { resultString += character; elementType = ElementType.Number; } else if (character == '\"') { elementType = ElementType.String; } else { ErrorMessage("Unsupported character '" + character + "'"); } } } ParserToken result; result.text = resultString; result.type = elementType; return result; } public string ReadNext() { if (lastToken.type == ElementType.None) lastToken = ReadNextToken(); string result = lastToken.text; lastToken.type = ElementType.None; externalPosition = currentPosition; return result; } public bool CheckNext(string text) { if (lastToken.type == ElementType.None) lastToken = ReadNextToken(); return lastToken.text == text; } public bool CheckNextType(ElementType type) { if (lastToken.type == ElementType.None) lastToken = ReadNextToken(); return lastToken.type == type; } public void Expect(string text) { if (lastToken.type == ElementType.None) lastToken = ReadNextToken(); lastToken.type = ElementType.None; externalPosition = currentPosition; if (lastToken.text != text) { ErrorMessage("Expected '" + text + "' but '" + lastToken.text + "' found."); } } public void ErrorMessage(string Text) { throw new Exception("Parsing error: " + Text + " (" + externalPosition.position.X.ToString() + "," + externalPosition.position.Y.ToString() + ")"); } public ParserState GetState() { ParserState result = new ParserState(); result.currentPosition = currentPosition; result.externalPosition = externalPosition; result.lastToken = lastToken; return result; } public void SetState(ParserState state) { currentPosition = state.currentPosition; externalPosition = state.externalPosition; lastToken = state.lastToken; } } static class StringExtensions { public static IEnumerable SplitInParts(this String s, Int32 partLength) { if (s == null) throw new ArgumentNullException("s"); if (partLength <= 0) throw new ArgumentException("Part length has to be positive.", "partLength"); for (var i = 0; i < s.Length; i += partLength) yield return s.Substring(i, Math.Min(partLength, s.Length - i)); } public static string Unescape(this string txt) { if (string.IsNullOrEmpty(txt)) { return txt; } StringBuilder retval = new StringBuilder(txt.Length); for (int ix = 0; ix < txt.Length;) { int jx = txt.IndexOf('\\', ix); if ((jx < 0) || (jx == txt.Length - 1)) jx = txt.Length; retval.Append(txt, ix, jx - ix); if (jx >= txt.Length) break; switch (txt[jx + 1]) { //case '\'': retval.Append('\''); break; // Single quote case '"': retval.Append('"'); break; // Double quote //case '\\': retval.Append('\\'); break; // Don't escape case 'a': retval.Append('\a'); break; // Line feed case 'b': retval.Append('\b'); break; // Backspace case 'f': retval.Append('\f'); break; // Form feed case 'n': retval.Append('\n'); break; // Line feed case 'r': retval.Append('\r'); break; // Carriage return case 't': retval.Append('\t'); break; // Horizontal tab case 'v': retval.Append('\v'); break; // Vertical tab default: // Unrecognized, copy as-is retval.Append('\\').Append(txt[jx + 1]); break; } ix = jx + 2; } return retval.ToString(); } public static string Escape(this string input) { var literal = new StringBuilder(input.Length + 2); foreach (var c in input) { switch (c) { //case '\'': literal.Append(@"\'"); break; case '\"': literal.Append("\\\""); break; //case '\\': literal.Append(@"\\"); break; case '\0': literal.Append(@"\0"); break; case '\a': literal.Append(@"\a"); break; case '\b': literal.Append(@"\b"); break; case '\f': literal.Append(@"\f"); break; case '\n': literal.Append(@"\n"); break; case '\r': literal.Append(@"\r"); break; case '\t': literal.Append(@"\t"); break; case '\v': literal.Append(@"\v"); break; default: if (Char.GetUnicodeCategory(c) != UnicodeCategory.Control) { literal.Append(c); } else { literal.Append(@"\u"); literal.Append(((ushort)c).ToString("x4")); } break; } } return literal.ToString(); } } }