static List <MiniYamlNode> FromLines(string[] lines, string filename) { var levels = new List <List <MiniYamlNode> >(); levels.Add(new List <MiniYamlNode>()); var lineNo = 0; foreach (var ll in lines) { var line = ll; ++lineNo; if (line.Contains('#')) { line = line.Substring(0, line.IndexOf('#')).TrimEnd(' ', '\t'); } var t = line.TrimStart(' ', '\t'); if (t.Length == 0) { continue; } var level = line.Length - t.Length; var location = new MiniYamlNode.SourceLocation { Filename = filename, Line = lineNo }; if (levels.Count <= level) { throw new YamlException("Bad indent in miniyaml at {0}".F(location)); } while (levels.Count > level + 1) { levels.RemoveAt(levels.Count - 1); } var d = new List <MiniYamlNode>(); var rhs = SplitAtColon(ref t); levels[level].Add(new MiniYamlNode(t, rhs, d, location)); levels.Add(d); } return(levels[0]); }
static List <MiniYamlNode> FromLines(IEnumerable <string> lines, string filename, bool discardCommentsAndWhitespace, Dictionary <string, string> stringPool) { if (stringPool == null) { stringPool = new Dictionary <string, string>(); } var levels = new List <List <MiniYamlNode> >(); levels.Add(new List <MiniYamlNode>()); var lineNo = 0; foreach (var ll in lines) { var line = ll; ++lineNo; var keyStart = 0; var level = 0; var spaces = 0; var textStart = false; string key = null; string value = null; string comment = null; var location = new MiniYamlNode.SourceLocation { Filename = filename, Line = lineNo }; if (line.Length > 0) { var currChar = line[keyStart]; while (!(currChar == '\n' || currChar == '\r') && keyStart < line.Length && !textStart) { currChar = line[keyStart]; switch (currChar) { case ' ': spaces++; if (spaces >= SpacesPerLevel) { spaces = 0; level++; } keyStart++; break; case '\t': level++; keyStart++; break; default: textStart = true; break; } } if (levels.Count <= level) { throw new YamlException("Bad indent in miniyaml at {0}".F(location)); } while (levels.Count > level + 1) { levels.RemoveAt(levels.Count - 1); } // Extract key, value, comment from line as `<key>: <value>#<comment>` // The # character is allowed in the value if escaped (\#). // Leading and trailing whitespace is always trimmed from keys. // Leading and trailing whitespace is trimmed from values unless they // are marked with leading or trailing backslashes var keyLength = line.Length - keyStart; var valueStart = -1; var valueLength = 0; var commentStart = -1; for (var i = 0; i < line.Length; i++) { if (valueStart < 0 && line[i] == ':') { valueStart = i + 1; keyLength = i - keyStart; valueLength = line.Length - i - 1; } if (commentStart < 0 && line[i] == '#' && (i == 0 || line[i - 1] != '\\')) { commentStart = i + 1; if (commentStart <= keyLength) { keyLength = i - keyStart; } else { valueLength = i - valueStart; } break; } } if (keyLength > 0) { key = line.Substring(keyStart, keyLength).Trim(); } if (valueStart >= 0) { var trimmed = line.Substring(valueStart, valueLength).Trim(); if (trimmed.Length > 0) { value = trimmed; } } if (commentStart >= 0 && !discardCommentsAndWhitespace) { comment = line.Substring(commentStart); } // Remove leading/trailing whitespace guards if (value != null && value.Length > 1) { var trimLeading = value[0] == '\\' && (value[1] == ' ' || value[1] == '\t') ? 1 : 0; var trimTrailing = value[value.Length - 1] == '\\' && (value[value.Length - 2] == ' ' || value[value.Length - 2] == '\t') ? 1 : 0; if (trimLeading + trimTrailing > 0) { value = value.Substring(trimLeading, value.Length - trimLeading - trimTrailing); } } // Remove escape characters from # if (value != null && value.IndexOf('#') != -1) { value = value.Replace("\\#", "#"); } } if (key != null || !discardCommentsAndWhitespace) { key = key == null ? null : stringPool.GetOrAdd(key, key); value = value == null ? null : stringPool.GetOrAdd(value, value); comment = comment == null ? null : stringPool.GetOrAdd(comment, comment); var nodes = new List <MiniYamlNode>(); levels[level].Add(new MiniYamlNode(key, value, comment, nodes, location)); levels.Add(nodes); } } foreach (var nodes in levels) { nodes.TrimExcess(); } return(levels[0]); }
static List <MiniYamlNode> FromLines(IEnumerable <string> lines, string filename) { var levels = new List <List <MiniYamlNode> >(); levels.Add(new List <MiniYamlNode>()); var lineNo = 0; foreach (var ll in lines) { var line = ll; ++lineNo; var commentIndex = line.IndexOf('#'); if (commentIndex != -1) { line = line.Substring(0, commentIndex).TrimEnd(' ', '\t'); } if (line.Length == 0) { continue; } var charPosition = 0; var level = 0; var spaces = 0; var textStart = false; var currChar = line[charPosition]; while (!(currChar == '\n' || currChar == '\r') && charPosition < line.Length && !textStart) { currChar = line[charPosition]; switch (currChar) { case ' ': spaces++; if (spaces >= SpacesPerLevel) { spaces = 0; level++; } charPosition++; break; case '\t': level++; charPosition++; break; default: textStart = true; break; } } var realText = line.Substring(charPosition); if (realText.Length == 0) { continue; } var location = new MiniYamlNode.SourceLocation { Filename = filename, Line = lineNo }; if (levels.Count <= level) { throw new YamlException("Bad indent in miniyaml at {0}".F(location)); } while (levels.Count > level + 1) { levels.RemoveAt(levels.Count - 1); } var d = new List <MiniYamlNode>(); var rhs = SplitAtColon(ref realText); levels[level].Add(new MiniYamlNode(realText, rhs, d, location)); levels.Add(d); } return(levels[0]); }
static List <MiniYamlNode> FromLines(IEnumerable <ReadOnlyMemory <char> > lines, string filename, bool discardCommentsAndWhitespace, Dictionary <string, string> stringPool) { if (stringPool == null) { stringPool = new Dictionary <string, string>(); } var levels = new List <List <MiniYamlNode> >(); levels.Add(new List <MiniYamlNode>()); var lineNo = 0; foreach (var ll in lines) { var line = ll.Span; ++lineNo; var keyStart = 0; var level = 0; var spaces = 0; var textStart = false; ReadOnlySpan <char> key = default; ReadOnlySpan <char> value = default; ReadOnlySpan <char> comment = default; var location = new MiniYamlNode.SourceLocation { Filename = filename, Line = lineNo }; if (line.Length > 0) { var currChar = line[keyStart]; while (!(currChar == '\n' || currChar == '\r') && keyStart < line.Length && !textStart) { currChar = line[keyStart]; switch (currChar) { case ' ': spaces++; if (spaces >= SpacesPerLevel) { spaces = 0; level++; } keyStart++; break; case '\t': level++; keyStart++; break; default: textStart = true; break; } } if (levels.Count <= level) { throw new YamlException($"Bad indent in miniyaml at {location}"); } while (levels.Count > level + 1) { levels[levels.Count - 1].TrimExcess(); levels.RemoveAt(levels.Count - 1); } // Extract key, value, comment from line as `<key>: <value>#<comment>` // The # character is allowed in the value if escaped (\#). // Leading and trailing whitespace is always trimmed from keys. // Leading and trailing whitespace is trimmed from values unless they // are marked with leading or trailing backslashes var keyLength = line.Length - keyStart; var valueStart = -1; var valueLength = 0; var commentStart = -1; for (var i = 0; i < line.Length; i++) { if (valueStart < 0 && line[i] == ':') { valueStart = i + 1; keyLength = i - keyStart; valueLength = line.Length - i - 1; } if (commentStart < 0 && line[i] == '#' && (i == 0 || line[i - 1] != '\\')) { commentStart = i + 1; if (commentStart <= keyLength) { keyLength = i - keyStart; } else { valueLength = i - valueStart; } break; } } if (keyLength > 0) { key = line.Slice(keyStart, keyLength).Trim(); } if (valueStart >= 0) { var trimmed = line.Slice(valueStart, valueLength).Trim(); if (trimmed.Length > 0) { value = trimmed; } } if (commentStart >= 0 && !discardCommentsAndWhitespace) { comment = line.Slice(commentStart); } if (value.Length > 1) { // Remove leading/trailing whitespace guards var trimLeading = value[0] == '\\' && (value[1] == ' ' || value[1] == '\t') ? 1 : 0; var trimTrailing = value[value.Length - 1] == '\\' && (value[value.Length - 2] == ' ' || value[value.Length - 2] == '\t') ? 1 : 0; if (trimLeading + trimTrailing > 0) { value = value.Slice(trimLeading, value.Length - trimLeading - trimTrailing); } // Remove escape characters from # if (value.Contains("\\#", StringComparison.Ordinal)) { value = value.ToString().Replace("\\#", "#"); } } } if (!key.IsEmpty || !discardCommentsAndWhitespace) { var keyString = key.IsEmpty ? null : key.ToString(); var valueString = value.IsEmpty ? null : value.ToString(); // Note: We need to support empty comments here to ensure that empty comments // (i.e. a lone # at the end of a line) can be correctly re-serialized var commentString = comment == default ? null : comment.ToString(); keyString = keyString == null ? null : stringPool.GetOrAdd(keyString, keyString); valueString = valueString == null ? null : stringPool.GetOrAdd(valueString, valueString); commentString = commentString == null ? null : stringPool.GetOrAdd(commentString, commentString); var nodes = new List <MiniYamlNode>(); levels[level].Add(new MiniYamlNode(keyString, valueString, commentString, nodes, location)); levels.Add(nodes); } } foreach (var nodes in levels) { nodes.TrimExcess(); } return(levels[0]); }