/// <summary> /// Parse the time (hours, minutes, seconds) /// </summary> private void ParseTime(ParseStream stream, out int hour, out int minutes, out int seconds) { if (stream.Char == 't' || stream.Char == 'T') { stream.Next(true); } else { SkipWhitespace(stream); } // Parse hour // Note: A hour can be represented by one or two digits. string hulp = ""; while (stream.Char >= '0' && stream.Char <= '9' && !stream.EOF && hulp.Length <= 2) { hulp += stream.Char; stream.Next(true); } hour = Int32.Parse(hulp); SkipChar(stream, ':'); // Parse minutes minutes = ParseNumber(stream, 2); SkipChar(stream, ':'); // Parse seconds seconds = ParseNumber(stream, 2); }
/// <summary> /// Parses a String surrounded with single quotes /// </summary> private string ParseSingleQuoted(ParseStream stream) { System.Text.StringBuilder builder = new System.Text.StringBuilder(); // Start literal parsing stream.StartLiteral(); // Skip ''' stream.Next(true); while (!stream.EOF) { if (stream.Char == '\'') { stream.Next(); // Escaped single quote if (stream.Char == '\'') { builder.Append(stream.Char); } // End of string else { break; } } else { builder.Append(stream.Char); } stream.Next(); // Skip \' if (stream.EOF) { stream.StopLiteral(); throw new ParseException(stream, "Single quoted string not closed"); } } // Stop literal parsing stream.StopLiteral(); return(builder.ToString()); }
/// <summary> Parses a binairy node. </summary> /// <remarks> /// This is not an efficient method. First the stream is placed /// in a string. And after that the string is converted in a byte[]. /// If there is a fault in the binairy string then that will only be detected /// after reading the whole stream and after coneverting. /// </remarks> public static new byte [] Parse(ParseStream stream) { bool quoted = false; bool block = false; System.Text.StringBuilder input = new System.Text.StringBuilder(); if (stream.EOF) { throw new ParseException(stream, "Empty node"); } // Detect block scalar stream.SkipSpaces(); if (stream.Char == '|') { block = true; stream.Next(); stream.SkipSpaces(); } while (!stream.EOF) { // Detect quotes if (stream.Char == '\"') { if (quoted) { break; //End of stream } else { quoted = true; //Start of quoted stream } } // Detect and ignore newline char's else if (!(stream.Char == '\n' && block)) { input.Append(stream.Char); } stream.Next(); } //Console.WriteLine("convert [" + input.ToString() + "]"); return(System.Convert.FromBase64String(input.ToString())); }
/// <summary> /// Test for the value's nan, NaN and NAN in the stream. If /// found then it is placed inside the content. /// There is no more data excepted behind it /// </summary> private void ParseNaN(ParseStream stream) { // Read the first 8 chars char [] chars = new char [8]; int length = 0; for (int i = 0; i < chars.Length && !stream.EOF; i++) { chars [i] = stream.Char; length++; stream.Next(true); } // Compare if (length == 3) { string s = "" + chars [0] + chars [1] + chars [2]; if (s == "NAN" || s == "NaN" || s == "nan") { content = double.NaN; return; } } throw new ParseException(stream, "No valid NaN"); }
/// <summary> Hexadecimal string </summary> private static long Parse16(ParseStream stream, short sign) { uint output = 0; while (!stream.EOF) { // 0 .. 9 if (stream.Char >= '0' && stream.Char <= '9') { output = (output * 16) + (uint)(stream.Char - '0'); OverflowTest(output, sign); } // a .. f else if (stream.Char >= 'a' && stream.Char <= 'f') { output = (output * 16) + (uint)(stream.Char - 'a') + 10; OverflowTest(output, sign); } // A .. F else if (stream.Char >= 'A' && stream.Char <= 'F') { output = (output * 16) + (uint)(stream.Char - 'A') + 10; OverflowTest(output, sign); } // Ignore underscores, other chars are not allowed else if (stream.Char != '_') { throw new Exception("Unknown char in base 16"); } stream.Next(); } return((long)(sign * output)); }
/// <summary> Parses a string with a given base (maximum 10) </summary> /// <remarks> /// This is not completly correct. For base10 the first char may not be a '_' /// The other bases allow this... /// </remarks> private static long ParseBase(ParseStream stream, uint basis, short sign) { // Base must be <= 10 if (basis > 10) { throw new Exception("Base to large. Maximum 10"); } ulong output = 0; char max = (char)((basis - 1) + (int)'0'); // Parse while (!stream.EOF) { // Decimal if (stream.Char >= '0' && stream.Char <= max) { output = (output * basis) + (uint)(stream.Char - '0'); OverflowTest(output, sign); } // Ignore underscores, but other chars are not allowed // see remarks else if (stream.Char != '_') { throw new Exception("Unknown char in base " + basis); } stream.Next(); } return(sign * (long)output); }
/// <summary> /// Skips the spaces * and tabs * in the current stream /// </summary> private void SkipWhitespace(ParseStream stream) { while ((stream.Char == ' ' || stream.Char == '\t') && !stream.EOF) { stream.Next(true); } }
/// <summary> /// Parse the milliseconds /// </summary> private int ParseMilliSeconds(ParseStream stream) { int ms = 0; // Look for fraction if (stream.Char == '.') { stream.Next(true); // Parse fraction, can consists of an // unlimited sequence of numbers, we only // look to the first three (max 1000) int count = 0; while (stream.Char >= '0' && stream.Char <= '9' && count < 3 && !stream.EOF) { ms *= 10; ms += stream.Char - '0'; stream.Next(true); count++; } if (count == 1) { ms *= 100; } if (count == 2) { ms *= 10; } if (count == 3) { ms *= 1; } // Ignore the rest while (stream.Char >= '0' && stream.Char <= '9' && !stream.EOF) { stream.Next(true); } } return(ms); }
/// <summary> Parse float </summary> public Float(ParseStream stream) : base("tag:yaml.org,2002:float", NodeType.Float) { // NaN if (stream.Char == '.') { stream.Next(true); ParseNaN(stream); } else { // By default positief int sign = 1; // Negative sign if (stream.Char == '-') { sign = -1; stream.Next(true); } // Positive sign else if (stream.Char == '+') { stream.Next(true); } // Test for inf, Inf and INF if (!stream.EOF && stream.Char == '.') { stream.Next(true); ParseInf(stream, sign); } // Parse the numbers else if (!stream.EOF) { ParseNumber(stream, sign); } else { throw new ParseException(stream, "No valid float, no data behind the sign"); } } }
/// <summary> /// Skips a specified char, and throws an exception when /// another char was found. /// </summary> private void SkipChar(ParseStream stream, char toSkip) { if (stream.Char == toSkip) { stream.Next(true); } else { throw new Exception("Unexpected character"); } }
/// <summary> Parses the exponential part of the float </summary> private static long ParseScient(ParseStream stream) { ulong output = 0; short sign; if (stream.Char == '-') { sign = -1; } else if (stream.Char == '+') { sign = 1; } else { throw new ParseException(stream, "Excepted + or - for the exponential part"); } stream.Next(true); while (!stream.EOF) { if (stream.Char >= '0' && stream.Char <= '9') { output = (10 * output) + (uint)stream.Char - '0'; } else { throw new ParseException(stream, "Unexepted char in exponential part: >" + stream.Char + "<"); } stream.Next(true); } return(sign * (long)output); }
/// <summary> /// Parses a String surrounded with double quotes /// </summary> private string ParseDoubleQuoted(ParseStream stream) { System.Text.StringBuilder builder = new System.Text.StringBuilder(); // Skip '"' stream.Next(); // Stop at " stream.StopAt(new char [] { '\"' }); while (!stream.EOF) { if (stream.Char == '\n') { builder.Append(' '); stream.Next(); } else { builder.Append(NextUnescapedChar(stream)); } } // Don't stop at " stream.DontStop(); // Skip '"' if (stream.Char != '\"') { throw new ParseException(stream, "Double quoted string not closed"); } else { stream.Next(true); } return(builder.ToString()); }
/// <summary> Parse a null node </summary> public Null(ParseStream stream) : base("tag:yaml.org,2002:null", NodeType.Null) { // An empty string is a valid null node if (stream.EOF) { return; } else { // Read the first 4 chars char [] chars = new char [8]; int length = 0; for (int i = 0; i < chars.Length && !stream.EOF; i++) { chars [i] = stream.Char; length++; stream.Next(); } // Compare if (length == 1) { string s = "" + chars [0]; // Canonical notation if (s == "~") { return; } } if (length == 4) { string s = "" + chars [0] + chars [1] + chars [2] + chars [3]; // null, Null, NULL if (s == "NULL" || s == "Null" || s == "null") { return; } } throw new ParseException(stream, "Not NULL"); } }
/// <summary> /// Parse an integer /// </summary> /// <param name="length"> /// The number of characters that the integer is expected to be. /// </param> /// <param name="stream"> The stream that will be parsed </param> private int ParseNumber(ParseStream stream, int length) { System.Text.StringBuilder hulp = new System.Text.StringBuilder(); int i; for (i = 0; i < length && !stream.EOF; i++) { hulp.Append(stream.Char); stream.Next(true); } if (i == length) { return(Int32.Parse(hulp.ToString())); } else { throw new Exception("Can't parse number"); } }
/// <summary> Parse a null node </summary> public Null(ParseStream stream) : base("tag:yaml.org,2002:null", NodeType.Null) { // An empty string is a valid null node if (stream.EOF) return; else { // Read the first 4 chars char [] chars = new char [8]; int length = 0; for (int i = 0; i < chars.Length && ! stream.EOF; i ++) { chars [i] = stream.Char; length ++; stream.Next (); } // Compare if (length == 1) { string s = "" + chars [0]; // Canonical notation if (s == "~") return; } if (length == 4) { string s = "" + chars [0] + chars [1] + chars [2] + chars [3]; // null, Null, NULL if (s == "NULL" || s == "Null" || s == "null") return; } throw new ParseException (stream, "Not NULL"); } }
/// <summary> /// Test for the value's inf, Inf and INF in the stream. If /// found then it 'merged' with the sign and placed in the content. /// There is no more data excepted behind it. /// </summary> private void ParseInf(ParseStream stream, int sign) { // Read the first 8 chars char [] chars = new char [8]; int length = 0; for (int i = 0; i < chars.Length && !stream.EOF; i++) { chars [i] = stream.Char; length++; stream.Next(true); } // Compare if (length == 3) { string s = "" + chars [0] + chars [1] + chars [2]; if (s == "INF" || s == "Inf" || s == "inf") { if (sign < 0) { content = double.NegativeInfinity; } else { content = double.PositiveInfinity; } return; } } throw new ParseException(stream, "No valid infinity"); }
/// <summary> Internal parse method </summary> /// <param name="parseImplicitMappings"> /// Avoids ethernal loops while parsing implicit mappings. Implicit mappings are /// not rocognized by a leading character. So while trying to parse the key of /// something we think that could be a mapping, we're sure that if it is a mapping, /// the key of this implicit mapping is not a mapping itself. /// /// NOTE: Implicit mapping still belong to unstable code and require the UNSTABLE and /// IMPLICIT_MAPPINGS preprocessor flags. /// </param> /// <param name="stream"></param> protected static Node Parse (ParseStream stream, bool parseImplicitMappings) { // ---------------- // Skip Whitespace // ---------------- if (! stream.EOF) { // Move the firstindentation pointer after the whitespaces of this line stream.SkipSpaces (); while (stream.Char == '\n' && ! stream.EOF) { // Skip newline and next whitespaces stream.Next (); stream.SkipSpaces (); } } // ----------------- // No remaining chars (Null/empty stream) // ----------------- if (stream.EOF) return new Null (); // ----------------- // Explicit type // ----------------- #if SUPPORT_EXPLICIT_TYPES stream.BuildLookaheadBuffer (); char a = '\0', b = '\0'; a = stream.Char; stream.Next (); b = stream.Char; stream.Next (); // Starting with !! if (a == '!' && b == '!' && ! stream.EOF) { stream.DestroyLookaheadBuffer (); // Read the tagname string tag = ""; while (stream.Char != ' ' && stream.Char != '\n' && ! stream.EOF) { tag += stream.Char; stream.Next (); } // Skip Whitespace if (! stream.EOF) { stream.SkipSpaces (); while (stream.Char == '\n' && ! stream.EOF) { stream.Next (); stream.SkipSpaces (); } } // Parse Node n; switch (tag) { // Mappings and sequences // NOTE: // - sets are mappings without values // - Ordered maps are ordered sequence of key: value // pairs without duplicates. // - Pairs are ordered sequence of key: value pairs // allowing duplicates. // TODO: Create new datatypes for omap and pairs // derived from sequence with a extra duplicate // checking. case "seq": n = new Sequence (stream); break; case "map": n = new Mapping (stream); break; case "set": n = new Mapping (stream); break; case "omap": n = new Sequence (stream); break; case "pairs": n = new Sequence (stream); break; // Scalars // // TODO: do we have to move this to Scalar.cs // in order to get the following working: // // !!str "...": "..." // !!str "...": "..." case "timestamp": n = new Timestamp (stream); break; case "binary": n = new Binary (stream); break; case "null": n = new Null (stream); break; case "float": n = new Float (stream); break; case "int": n = new Integer (stream); break; case "bool": n = new Boolean (stream); break; case "str": n = new String (stream); break; // Unknown data type default: throw new Exception ("Incorrect tag '!!" + tag + "'"); } return n; } else { stream.RewindLookaheadBuffer (); stream.DestroyLookaheadBuffer (); } #endif // ----------------- // Sequence // ----------------- if (stream.Char == '-' || stream.Char == '[') return new Sequence (stream); // ----------------- // Mapping // ----------------- if (stream.Char == '?' || stream.Char == '{') return new Mapping (stream); // ----------------- // Try implicit mapping // ----------------- // This are mappings which are not preceded by a question // mark. The keys have to be scalars. #if (UNSTABLE && SUPPORT_IMPLICIT_MAPPINGS) // NOTE: This code can't be included in Mapping.cs // because of the way we are using to rewind the buffer. Node key, val; if (parseImplicitMappings) { // First Key/value pair stream.BuildLookaheadBuffer (); stream.StopAt (new char [] {':'}); // Keys of implicit mappings can't be sequences, or other mappings // just look for scalars key = Scalar.Parse (stream, false); stream.DontStop (); Console.WriteLine ("key: " + key); // Followed by a colon, so this is a real mapping if (stream.Char == ':') { stream.DestroyLookaheadBuffer (); Mapping mapping = new Mapping (); // Skip colon and spaces stream.Next (); stream.SkipSpaces (); // Parse the value Console.Write ("using buffer: " + stream.UsingBuffer ()); stream.Indent (); Console.Write ("using buffer: " + stream.UsingBuffer ()); // val = Parse (stream, false); Console.Write ("<<"); while (!stream.EOF) {Console.Write (stream.Char);stream.Next (true);} Console.Write (">>"); val = new String (stream); Console.Write ("using buffer: " + stream.UsingBuffer ()); stream.UnIndent (); Console.Write ("using buffer: " + stream.UsingBuffer ()); Console.Write ("<<"); while (!stream.EOF) {Console.Write (stream.Char);stream.Next (true);} Console.Write (">>"); Console.WriteLine ("val: " + val); mapping.AddMappingNode (key, val); // Skip possible newline // NOTE: this can't be done by the drop-newline // method since this is not the end of a block while (stream.Char == '\n') stream.Next (true); // Other key/value pairs while (! stream.EOF) { stream.StopAt (new char [] {':'} ); stream.Indent (); key = Scalar.Parse (stream); stream.UnIndent (); stream.DontStop (); Console.WriteLine ("key 2: " + key); if (stream.Char == ':') { // Skip colon and spaces stream.Next (); stream.SkipSpaces (); // Parse the value stream.Indent (); val = Parse (stream); stream.UnIndent (); Console.WriteLine ("val 2: " + val); mapping.AddMappingNode (key, val); } else // TODO: Is this an error? { // NOTE: We can't recover from this error, // the last buffer has been destroyed, so // rewinding is impossible. throw new ParseException (stream, "Implicit mapping without value node"); } // Skip possible newline while (stream.Char == '\n') stream.Next (); } return mapping; } stream.RewindLookaheadBuffer (); stream.DestroyLookaheadBuffer (); } #endif // ----------------- // No known data structure, assume this is a scalar // ----------------- Scalar scalar = Scalar.Parse (stream); // Skip trash while (! stream.EOF) stream.Next (); return scalar; }
/// <summary> Parse a string </summary> public String(ParseStream stream) : base("tag:yaml.org,2002:str", NodeType.String) { // set flags for folded or block scalar if (stream.Char == '>') // TODO: '+' and '-' chomp chars { folded = true; } else if (stream.Char == '|') { block = true; } if (block || folded) { stream.Next(); stream.SkipSpaces(); } // ----------------- // Folded Scalar // ----------------- if (folded) { System.Text.StringBuilder builder = new System.Text.StringBuilder(); // First line (the \n after the first line is always ignored, // not replaced with a whitespace) while (!stream.EOF && stream.Char != '\n') { builder.Append(stream.Char); stream.Next(); } // Skip the first newline stream.Next(); // Next lines (newlines will be replaced by spaces in folded scalars) while (!stream.EOF) { if (stream.Char == '\n') { builder.Append(' '); } else { builder.Append(stream.Char); } stream.Next(true); } content = builder.ToString(); } // ----------------- // Block Scalar (verbatim block without folding) // ----------------- else if (block) { /* * Console.Write(">>"); * while (! stream.EOF) * { * Console.Write (stream.Char); * stream.Next(); * } * Console.Write("<<"); * // */ System.Text.StringBuilder builder = new System.Text.StringBuilder(); while (!stream.EOF) { builder.Append(stream.Char); stream.Next(true); } content = builder.ToString(); } // String between double quotes if (stream.Char == '\"') { content = ParseDoubleQuoted(stream); } // Single quoted string else if (stream.Char == '\'') { content = ParseSingleQuoted(stream); } // String without quotes else { content = ParseUnQuoted(stream); } }
/// <summary> /// Reads a character from the stream, unescapes it, /// and moves to the next character. /// </summary> private char NextUnescapedChar(ParseStream stream) { char c = stream.Char; // If escaped if (c == '\\') { // Never stop, every special character // looses its meaning behind a backslash. stream.StopAt(new Char [] { }); stream.Next(true); c = stream.Char; // ASCII null if (c == '0') { c = '\0'; } // ASCII bell else if (c == 'a') { c = (char)0x7; } // ASCII backspace else if (c == 'b') { c = (char)0x8; } // ASCII horizontal tab else if (c == 't') { c = (char)0x9; } // ASCII newline else if (c == 'n') { c = (char)0xA; } // ASCII vertical tab else if (c == 'v') { c = (char)0xB; } // ASCII form feed else if (c == 'f') { c = (char)0xC; } // ASCII carriage return else if (c == 'r') { c = (char)0xD; } // ASCII escape else if (c == 'e') { c = (char)0x1D; } // Unicode next line else if (c == 'N') { c = (char)0x85; } // Unicode non breaking space else if (c == '_') { c = (char)0xA0; } // TODO larger unicode characters // Unicode line separator // else if (c == 'L') c = (char) 0x20282028; // 8 bit hexadecimal else if (c == 'x') { int c_int = (char)0; for (int i = 0; i < 2; i++) { c_int *= 16; stream.Next(); char d = stream.Char; if (d >= '0' && d <= '9') { c_int += d - '0'; } else if (d >= 'a' && d <= 'f') { c_int += d - 'a'; } else if (d >= 'A' && d <= 'F') { c_int += d - 'A'; } else { stream.DontStop(); throw new ParseException(stream, "Invalid escape sequence"); } } c = (char)c_int; } stream.Next(true); // Restore last stop settings stream.DontStop(); } else { stream.Next(true); } return(c); }
/// <summary> /// Parse the milliseconds /// </summary> private int ParseMilliSeconds(ParseStream stream) { int ms = 0; // Look for fraction if (stream.Char == '.') { stream.Next (true); // Parse fraction, can consists of an // unlimited sequence of numbers, we only // look to the first three (max 1000) int count = 0; while (stream.Char >= '0' && stream.Char <= '9' && count < 3 && ! stream.EOF) { ms *= 10; ms += stream.Char - '0'; stream.Next (true); count ++; } if (count == 1) ms *= 100; if (count == 2) ms *= 10; if (count == 3) ms *= 1; // Ignore the rest while (stream.Char >= '0' && stream.Char <= '9' && ! stream.EOF) stream.Next (true); } return ms; }
/// <summary> Parse boolean </summary> public Boolean(ParseStream stream) : base("tag:yaml.org,2002:bool", NodeType.Boolean) { // Read the first 8 chars char [] chars = new char [8]; int length = 0; for (int i = 0; i < chars.Length && !stream.EOF; i++) { chars [i] = stream.Char; length++; stream.Next(true); } // Compare if (length == 1) { if (chars[0] == 'Y' || chars[0] == 'y') { content = true; return; } if (chars[0] == 'N' || chars[0] == 'n') { content = false; return; } } if (length == 2) { string s = "" + chars [0] + chars [1]; if (s == "ON" || s == "On" || s == "on") { content = true; return; } if (s == "NO" || s == "No" || s == "no") { content = false; return; } } if (length == 3) { string s = "" + chars [0] + chars [1] + chars [2]; if (s == "YES" || s == "Yes" || s == "yes") { content = true; return; } if (s == "OFF" || s == "Off" || s == "off") { content = false; return; } } if (length == 4) { string s = "" + chars [0] + chars [1] + chars [2] + chars [3]; if (s == "TRUE" || s == "True" || s == "true") { content = true; return; } } if (length == 5) { string s = "" + chars [0] + chars [1] + chars [2] + chars [3] + chars[4]; if (s == "FALSE" || s == "False" || s == "false") { content = false; return; } } // No boolean throw new Exception("No valid boolean"); }
/// <summary> Parse boolean </summary> public Boolean(ParseStream stream) : base("tag:yaml.org,2002:bool", NodeType.Boolean) { // Read the first 8 chars char [] chars = new char [8]; int length = 0; for (int i = 0; i < chars.Length && ! stream.EOF; i ++) { chars [i] = stream.Char; length ++; stream.Next (true); } // Compare if (length == 1) { if (chars[0] == 'Y' || chars[0] == 'y') { content = true; return; } if (chars[0] == 'N' || chars[0] == 'n') { content = false; return; } } if (length == 2) { string s = "" + chars [0] + chars [1]; if (s == "ON" || s == "On" || s == "on") { content = true; return; } if (s == "NO" || s == "No" || s == "no") { content = false; return; } } if (length == 3) { string s = "" + chars [0] + chars [1] + chars [2]; if (s == "YES" || s == "Yes" || s == "yes") { content = true; return; } if (s == "OFF" || s == "Off" || s == "off") { content = false; return; } } if (length == 4) { string s = "" + chars [0] + chars [1] + chars [2] + chars [3]; if (s == "TRUE" || s == "True" || s == "true") { content = true; return; } } if (length == 5) { string s = "" + chars [0] + chars [1] + chars [2] + chars [3] + chars[4]; if (s == "FALSE" || s == "False" || s == "false") { content = false; return; } } // No boolean throw new Exception ("No valid boolean"); }
/// <summary> Parse an integer </summary> public Integer(ParseStream stream) : base("tag:yaml.org,2002:int", NodeType.Integer) { short sign = 1; // Positive sign by default // Negative sign if (stream.Char == '-') { sign = -1; stream.Next(); } // Positive sign else if (stream.Char == '+') { stream.Next(); } try { // Determine base if (stream.Char == '0') { stream.Next(); // Base 2 if (stream.Char == 'b') { stream.Next(); content = ParseBase(stream, 2, sign); return; } // Base 16 else if (stream.Char == 'x') { stream.Next(); content = Parse16(stream, sign); return; } // Base 8 else { content = ParseBase(stream, 8, sign); return; } } // Other base stream.BuildLookaheadBuffer(); // First, try to parse with base 10 try { content = ParseBase(stream, 10, sign); stream.DestroyLookaheadBuffer(); return; } catch { } // If not parseable with base 10, then try base 60 stream.RewindLookaheadBuffer(); stream.DestroyLookaheadBuffer(); content = Parse60(stream, sign); } catch (Exception ex) { throw new ParseException(stream, ex.ToString()); } }
/// <summary> /// Parses a String surrounded with double quotes /// </summary> private string ParseDoubleQuoted(ParseStream stream) { System.Text.StringBuilder builder = new System.Text.StringBuilder (); // Skip '"' stream.Next (); // Stop at " stream.StopAt (new char [] {'\"'} ); while (! stream.EOF) { if (stream.Char == '\n') { builder.Append (' '); stream.Next (); } else builder.Append (NextUnescapedChar (stream)); } // Don't stop at " stream.DontStop (); // Skip '"' if (stream.Char != '\"') throw new ParseException (stream, "Double quoted string not closed"); else stream.Next (true); return builder.ToString(); }
/// <summary> Parses a string with a given base (maximum 10) </summary> /// <remarks> /// This is not completly correct. For base10 the first char may not be a '_' /// The other bases allow this... /// </remarks> private static long ParseBase(ParseStream stream, uint basis, short sign) { // Base must be <= 10 if (basis > 10) throw new Exception ("Base to large. Maximum 10"); ulong output = 0; char max = (char) ((basis - 1) + (int) '0'); // Parse while (! stream.EOF) { // Decimal if (stream.Char >= '0' && stream.Char <= max) { output = (output * basis) + (uint) (stream.Char - '0'); OverflowTest (output, sign); } // Ignore underscores, but other chars are not allowed // see remarks else if (stream.Char != '_') throw new Exception ("Unknown char in base " + basis); stream.Next (); } return sign * (long) output; }
/// <summary> Parse an integer </summary> public Integer(ParseStream stream) : base("tag:yaml.org,2002:int", NodeType.Integer) { short sign = 1; // Positive sign by default // Negative sign if (stream.Char == '-') { sign = -1; stream.Next (); } // Positive sign else if (stream.Char == '+') stream.Next (); try { // Determine base if (stream.Char == '0') { stream.Next (); // Base 2 if (stream.Char == 'b') { stream.Next (); content = ParseBase (stream, 2, sign); return; } // Base 16 else if (stream.Char == 'x') { stream.Next (); content = Parse16 (stream, sign); return; } // Base 8 else { content = ParseBase (stream, 8, sign); return; } } // Other base stream.BuildLookaheadBuffer (); // First, try to parse with base 10 try { content = ParseBase (stream, 10, sign); stream.DestroyLookaheadBuffer (); return; } catch { } // If not parseable with base 10, then try base 60 stream.RewindLookaheadBuffer (); stream.DestroyLookaheadBuffer (); content = Parse60 (stream, sign); } catch (Exception ex) { throw new ParseException (stream, ex.ToString ()); } }
/// <summary> Parse a sequence </summary> public Sequence(ParseStream stream) : base("tag:yaml.org,2002:seq", NodeType.Sequence) { // Is this really a sequence? if (stream.Char == '-') { // Parse recursively do { // Override the parent's stop chars, never stop stream.StopAt(new char [] { }); // Skip over '-' stream.Next(); // Parse recursively stream.Indent(); AddNode(Parse(stream)); stream.UnIndent(); // Re-accept the parent's stop chars stream.DontStop(); }while (!stream.EOF && stream.Char == '-'); } // Or inline Sequence else if (stream.Char == '[') { // Override the parent's stop chars, never stop stream.StopAt(new char [] { }); // Skip '[' stream.Next(); do { stream.StopAt(new char [] { ']', ',' }); stream.Indent(); AddNode(Parse(stream, false)); stream.UnIndent(); stream.DontStop(); // Skip ',' if (stream.Char != ']' && stream.Char != ',') { stream.DontStop(); throw new ParseException(stream, "Comma expected in inline sequence"); } if (stream.Char == ',') { stream.Next(); stream.SkipSpaces(); } }while (!stream.EOF && stream.Char != ']'); // Re-accept the parent's stop chars stream.DontStop(); // Skip ']' if (stream.Char == ']') { stream.Next(true); } else { throw new ParseException(stream, "Inline sequence not closed"); } } // Throw an exception when not else { throw new Exception("This is not a sequence"); } }
/// <summary> /// Parse the time (hours, minutes, seconds) /// </summary> private void ParseTime(ParseStream stream, out int hour, out int minutes, out int seconds) { if (stream.Char == 't' || stream.Char == 'T') stream.Next (true); else SkipWhitespace (stream); // Parse hour // Note: A hour can be represented by one or two digits. string hulp = ""; while (stream.Char >= '0' && stream.Char <= '9' && ! stream.EOF && hulp.Length <= 2) { hulp += stream.Char; stream.Next (true); } hour = Int32.Parse (hulp); SkipChar (stream, ':'); // Parse minutes minutes = ParseNumber (stream, 2); SkipChar (stream, ':'); // Parse seconds seconds = ParseNumber (stream, 2); }
/// <summary> /// Parse an integer /// </summary> /// <param name="length"> /// The number of characters that the integer is expected to be. /// </param> /// <param name="stream"> The stream that will be parsed </param> private int ParseNumber(ParseStream stream, int length) { System.Text.StringBuilder hulp = new System.Text.StringBuilder (); int i; for (i = 0; i < length && !stream.EOF; i++) { hulp.Append (stream.Char); stream.Next (true); } if (i == length) return Int32.Parse (hulp.ToString ()); else throw new Exception ("Can't parse number"); }
/// <summary> Internal parse method </summary> /// <param name="parseImplicitMappings"> /// Avoids ethernal loops while parsing implicit mappings. Implicit mappings are /// not rocognized by a leading character. So while trying to parse the key of /// something we think that could be a mapping, we're sure that if it is a mapping, /// the key of this implicit mapping is not a mapping itself. /// /// NOTE: Implicit mapping still belong to unstable code and require the UNSTABLE and /// IMPLICIT_MAPPINGS preprocessor flags. /// </param> /// <param name="stream"></param> protected static Node Parse(ParseStream stream, bool parseImplicitMappings) { // ---------------- // Skip Whitespace // ---------------- if (!stream.EOF) { // Move the firstindentation pointer after the whitespaces of this line stream.SkipSpaces(); while (stream.Char == '\n' && !stream.EOF) { // Skip newline and next whitespaces stream.Next(); stream.SkipSpaces(); } } // ----------------- // No remaining chars (Null/empty stream) // ----------------- if (stream.EOF) { return(new Null()); } // ----------------- // Explicit type // ----------------- #if SUPPORT_EXPLICIT_TYPES stream.BuildLookaheadBuffer(); char a = '\0', b = '\0'; a = stream.Char; stream.Next(); b = stream.Char; stream.Next(); // Starting with !! if (a == '!' && b == '!' && !stream.EOF) { stream.DestroyLookaheadBuffer(); // Read the tagname string tag = ""; while (stream.Char != ' ' && stream.Char != '\n' && !stream.EOF) { tag += stream.Char; stream.Next(); } // Skip Whitespace if (!stream.EOF) { stream.SkipSpaces(); while (stream.Char == '\n' && !stream.EOF) { stream.Next(); stream.SkipSpaces(); } } // Parse Node n; switch (tag) { // Mappings and sequences // NOTE: // - sets are mappings without values // - Ordered maps are ordered sequence of key: value // pairs without duplicates. // - Pairs are ordered sequence of key: value pairs // allowing duplicates. // TODO: Create new datatypes for omap and pairs // derived from sequence with a extra duplicate // checking. case "seq": n = new Sequence(stream); break; case "map": n = new Mapping(stream); break; case "set": n = new Mapping(stream); break; case "omap": n = new Sequence(stream); break; case "pairs": n = new Sequence(stream); break; // Scalars // // TODO: do we have to move this to Scalar.cs // in order to get the following working: // // !!str "...": "..." // !!str "...": "..." case "timestamp": n = new Timestamp(stream); break; case "binary": n = new Binary(stream); break; case "null": n = new Null(stream); break; case "float": n = new Float(stream); break; case "int": n = new Integer(stream); break; case "bool": n = new Boolean(stream); break; case "str": n = new String(stream); break; // Unknown data type default: throw new Exception("Incorrect tag '!!" + tag + "'"); } return(n); } else { stream.RewindLookaheadBuffer(); stream.DestroyLookaheadBuffer(); } #endif // ----------------- // Sequence // ----------------- if (stream.Char == '-' || stream.Char == '[') { return(new Sequence(stream)); } // ----------------- // Mapping // ----------------- if (stream.Char == '?' || stream.Char == '{') { return(new Mapping(stream)); } // ----------------- // Try implicit mapping // ----------------- // This are mappings which are not preceded by a question // mark. The keys have to be scalars. #if (UNSTABLE && SUPPORT_IMPLICIT_MAPPINGS) // NOTE: This code can't be included in Mapping.cs // because of the way we are using to rewind the buffer. Node key, val; if (parseImplicitMappings) { // First Key/value pair stream.BuildLookaheadBuffer(); stream.StopAt(new char [] { ':' }); // Keys of implicit mappings can't be sequences, or other mappings // just look for scalars key = Scalar.Parse(stream, false); stream.DontStop(); Console.WriteLine("key: " + key); // Followed by a colon, so this is a real mapping if (stream.Char == ':') { stream.DestroyLookaheadBuffer(); Mapping mapping = new Mapping(); // Skip colon and spaces stream.Next(); stream.SkipSpaces(); // Parse the value Console.Write("using buffer: " + stream.UsingBuffer()); stream.Indent(); Console.Write("using buffer: " + stream.UsingBuffer()); // val = Parse (stream, false); Console.Write("<<"); while (!stream.EOF) { Console.Write(stream.Char); stream.Next(true); } Console.Write(">>"); val = new String(stream); Console.Write("using buffer: " + stream.UsingBuffer()); stream.UnIndent(); Console.Write("using buffer: " + stream.UsingBuffer()); Console.Write("<<"); while (!stream.EOF) { Console.Write(stream.Char); stream.Next(true); } Console.Write(">>"); Console.WriteLine("val: " + val); mapping.AddMappingNode(key, val); // Skip possible newline // NOTE: this can't be done by the drop-newline // method since this is not the end of a block while (stream.Char == '\n') { stream.Next(true); } // Other key/value pairs while (!stream.EOF) { stream.StopAt(new char [] { ':' }); stream.Indent(); key = Scalar.Parse(stream); stream.UnIndent(); stream.DontStop(); Console.WriteLine("key 2: " + key); if (stream.Char == ':') { // Skip colon and spaces stream.Next(); stream.SkipSpaces(); // Parse the value stream.Indent(); val = Parse(stream); stream.UnIndent(); Console.WriteLine("val 2: " + val); mapping.AddMappingNode(key, val); } else // TODO: Is this an error? { // NOTE: We can't recover from this error, // the last buffer has been destroyed, so // rewinding is impossible. throw new ParseException(stream, "Implicit mapping without value node"); } // Skip possible newline while (stream.Char == '\n') { stream.Next(); } } return(mapping); } stream.RewindLookaheadBuffer(); stream.DestroyLookaheadBuffer(); } #endif // ----------------- // No known data structure, assume this is a scalar // ----------------- Scalar scalar = Scalar.Parse(stream); // Skip trash while (!stream.EOF) { stream.Next(); } return(scalar); }
/// <summary> /// Parses a String surrounded with single quotes /// </summary> private string ParseSingleQuoted(ParseStream stream) { System.Text.StringBuilder builder = new System.Text.StringBuilder (); // Start literal parsing stream.StartLiteral (); // Skip ''' stream.Next (true); while (! stream.EOF) { if (stream.Char == '\'') { stream.Next (); // Escaped single quote if (stream.Char == '\'') builder.Append (stream.Char); // End of string else break; } else builder.Append (stream.Char); stream.Next (); // Skip \' if (stream.EOF) { stream.StopLiteral (); throw new ParseException (stream, "Single quoted string not closed"); } } // Stop literal parsing stream.StopLiteral (); return builder.ToString(); }
/// <summary> Parses a string with base 60, without sign </summary> private static long Parse60(ParseStream stream, short sign) { ulong output = 0; // Parse ulong part = 0; bool firstPart = true; // Only the first part can be larger then 59 while (!stream.EOF) { // Decimal if (stream.Char >= '0' && stream.Char <= '9') { part = (part * 10) + (uint)(stream.Char - '0'); } // New part else if (stream.Char == ':') { // Only the first part can be largen then 60 if (!firstPart) { if (part >= 60) { throw new Exception("Part of base 60 scalar is too large (max. 59)"); } else { firstPart = false; } } output = (output * 60) + part; OverflowTest(output, sign); part = 0; } // Ignore underscores, other chars are not allowed else if (stream.Char != '_') { throw new Exception("Unknown char in base 16"); } stream.Next(); } // Add last part to the output if (!firstPart) { if (part >= 60) { throw new Exception( "Part of base 60 scalar is too large (max. 59)"); } else { firstPart = false; } } output = (output * 60) + part; OverflowTest(output, sign); return(sign * (long)output); }
/// <summary> Parse a string </summary> public String(ParseStream stream) : base("tag:yaml.org,2002:str", NodeType.String) { // set flags for folded or block scalar if (stream.Char == '>') // TODO: '+' and '-' chomp chars folded = true; else if (stream.Char == '|') block = true; if (block || folded) { stream.Next (); stream.SkipSpaces (); } // ----------------- // Folded Scalar // ----------------- if (folded) { System.Text.StringBuilder builder = new System.Text.StringBuilder (); // First line (the \n after the first line is always ignored, // not replaced with a whitespace) while (! stream.EOF && stream.Char != '\n') { builder.Append (stream.Char); stream.Next (); } // Skip the first newline stream.Next (); // Next lines (newlines will be replaced by spaces in folded scalars) while (! stream.EOF) { if (stream.Char == '\n') builder.Append (' '); else builder.Append (stream.Char); stream.Next (true); } content = builder.ToString (); } // ----------------- // Block Scalar (verbatim block without folding) // ----------------- else if (block) { /* Console.Write(">>"); while (! stream.EOF) { Console.Write (stream.Char); stream.Next(); } Console.Write("<<"); // */ System.Text.StringBuilder builder = new System.Text.StringBuilder (); while (! stream.EOF) { builder.Append (stream.Char); stream.Next (true); } content = builder.ToString (); } // String between double quotes if (stream.Char == '\"') content = ParseDoubleQuoted (stream); // Single quoted string else if (stream.Char == '\'') content = ParseSingleQuoted (stream); // String without quotes else content = ParseUnQuoted (stream); }
/// <summary> Parse a mapping </summary> public Mapping(ParseStream stream) : base("tag:yaml.org,2002:map", NodeType.Mapping) { // Mapping with eplicit key, (implicit mappings are threaded // in Node.cs) if (stream.Char == '?') { // Parse recursively do { Node key, val; // Skip over '?' stream.Next(); stream.SkipSpaces(); // Parse recursively. The false param avoids // looking recursively for implicit mappings. stream.StopAt(new char [] { ':' }); stream.Indent(); key = Parse(stream, false); stream.UnIndent(); stream.DontStop(); // Parse recursively. The false param avoids // looking for implit nodes if (stream.Char == ':') { // Skip over ':' stream.Next(); stream.SkipSpaces(); // Parse recursively stream.Indent(); val = Parse(stream); stream.UnIndent(); } else { val = new Null(); } AddMappingNode(key, val); // Skip possible newline // NOTE: this can't be done by the drop-newline // method since this is not the end of a block if (stream.Char == '\n') { stream.Next(); } }while (!stream.EOF && stream.Char == '?'); } // Inline mapping else if (stream.Char == '{') { // Override the parent's stop chars, never stop stream.StopAt(new char [] { }); // Skip '{' stream.Next(); do { Node key, val; // Skip '?' // (NOTE: it's not obligated to use this '?', // especially because this is an inline mapping) if (stream.Char == '?') { stream.Next(); stream.SkipSpaces(); } // Parse recursively the key stream.StopAt(new char [] { ':', ',', '}' }); stream.Indent(); key = Parse(stream, false); stream.UnIndent(); stream.DontStop(); // Value if (stream.Char == ':') { // Skip colon stream.Next(); stream.SkipSpaces(); // Parse recursively the value stream.StopAt(new char [] { '}', ',' }); stream.Indent(); val = Parse(stream, false); stream.UnIndent(); stream.DontStop(); } else { val = new Null(); } AddMappingNode(key, val); // Skip comma (key sepatator) if (stream.Char != '}' && stream.Char != ',') { stream.DontStop(); throw new ParseException(stream, "Comma expected in inline sequence"); } if (stream.Char == ',') { stream.Next(); stream.SkipSpaces(); } }while (!stream.EOF && stream.Char != '}'); // Re-accept the parent's stop chars stream.DontStop(); // Skip '}' if (stream.Char == '}') { stream.Next(); } else { throw new ParseException(stream, "Inline mapping not closed"); } } }
/// <summary> Parses a string with base 60, without sign </summary> private static long Parse60(ParseStream stream, short sign) { ulong output = 0; // Parse ulong part = 0; bool firstPart = true; // Only the first part can be larger then 59 while (! stream.EOF) { // Decimal if (stream.Char >= '0' && stream.Char <= '9') part = (part * 10) + (uint) (stream.Char - '0'); // New part else if (stream.Char == ':') { // Only the first part can be largen then 60 if ( ! firstPart) if (part >= 60) throw new Exception ("Part of base 60 scalar is too large (max. 59)"); else firstPart = false; output = (output * 60) + part; OverflowTest(output, sign); part = 0; } // Ignore underscores, other chars are not allowed else if (stream.Char != '_') throw new Exception ("Unknown char in base 16"); stream.Next (); } // Add last part to the output if (!firstPart) if (part >= 60) throw new Exception ( "Part of base 60 scalar is too large (max. 59)"); else firstPart = false; output = (output * 60) + part; OverflowTest (output, sign); return sign * (long) output; }
/// <summary> Parse a mapping </summary> public Mapping(ParseStream stream) : base("tag:yaml.org,2002:map", NodeType.Mapping) { // Mapping with eplicit key, (implicit mappings are threaded // in Node.cs) if (stream.Char == '?') { // Parse recursively do { Node key, val; // Skip over '?' stream.Next (); stream.SkipSpaces (); // Parse recursively. The false param avoids // looking recursively for implicit mappings. stream.StopAt (new char [] {':'}); stream.Indent (); key = Parse (stream, false); stream.UnIndent (); stream.DontStop (); // Parse recursively. The false param avoids // looking for implit nodes if (stream.Char == ':') { // Skip over ':' stream.Next (); stream.SkipSpaces (); // Parse recursively stream.Indent (); val = Parse (stream); stream.UnIndent (); } else val = new Null (); AddMappingNode (key, val); // Skip possible newline // NOTE: this can't be done by the drop-newline // method since this is not the end of a block if (stream.Char == '\n') stream.Next (); } while ( ! stream.EOF && stream.Char == '?'); } // Inline mapping else if (stream.Char == '{') { // Override the parent's stop chars, never stop stream.StopAt (new char [] { }); // Skip '{' stream.Next (); do { Node key, val; // Skip '?' // (NOTE: it's not obligated to use this '?', // especially because this is an inline mapping) if (stream.Char == '?') { stream.Next (); stream.SkipSpaces (); } // Parse recursively the key stream.StopAt (new char [] {':', ',', '}'}); stream.Indent (); key = Parse (stream, false); stream.UnIndent (); stream.DontStop (); // Value if (stream.Char == ':') { // Skip colon stream.Next (); stream.SkipSpaces (); // Parse recursively the value stream.StopAt (new char [] {'}', ','}); stream.Indent (); val = Parse (stream, false); stream.UnIndent (); stream.DontStop (); } else val = new Null (); AddMappingNode (key, val); // Skip comma (key sepatator) if (stream.Char != '}' && stream.Char != ',') { stream.DontStop (); throw new ParseException (stream, "Comma expected in inline sequence"); } if (stream.Char == ',') { stream.Next (); stream.SkipSpaces (); } } while ( ! stream.EOF && stream.Char != '}' ); // Re-accept the parent's stop chars stream.DontStop (); // Skip '}' if (stream.Char == '}') stream.Next (); else throw new ParseException (stream, "Inline mapping not closed"); } }
/// <summary> Hexadecimal string </summary> private static long Parse16(ParseStream stream, short sign) { uint output = 0; while (! stream.EOF) { // 0 .. 9 if (stream.Char >= '0' && stream.Char <= '9') { output = (output * 16) + (uint) (stream.Char - '0'); OverflowTest (output, sign); } // a .. f else if (stream.Char >= 'a' && stream.Char <= 'f') { output = (output * 16) + (uint) (stream.Char - 'a') + 10; OverflowTest (output, sign); } // A .. F else if (stream.Char >= 'A' && stream.Char <= 'F') { output = (output * 16) + (uint) (stream.Char - 'A') + 10; OverflowTest(output, sign); } // Ignore underscores, other chars are not allowed else if (stream.Char != '_') throw new Exception ("Unknown char in base 16"); stream.Next (); } return (long) (sign * output); }
/// <summary> /// Parse the time zone /// </summary> private double ParseTimezone(ParseStream stream) { double timezone = 0; SkipWhitespace (stream); // Timezone = UTC, use by default 0 if (stream.Char == 'Z') stream.Next (true); else { // Find the sign of the offset int sign = 0; if (stream.Char == '-') sign = -1; else if (stream.Char == '+') sign = +1; else throw new Exception ("Invalid time zone: " + "unexpected character"); // Read next char and test for more chars stream.Next (true); if (stream.EOF) throw new Exception ("Invalid time zone"); // Parse hour offset // Note: A hour can be represented by one or two digits. string hulp = ""; while (stream.Char >= '0' && stream.Char <= '9' && !stream.EOF && hulp.Length <= 2) { hulp += (stream.Char); stream.Next (true); } timezone = sign * Double.Parse (hulp); // Parse the minutes of the timezone // when there is still more to parse if ( ! stream.EOF) { SkipChar (stream, ':'); int temp = ParseNumber (stream, 2); timezone += (temp / 60.0); } } return timezone; }
/// <summary> /// Parse the time zone /// </summary> private double ParseTimezone(ParseStream stream) { double timezone = 0; SkipWhitespace(stream); // Timezone = UTC, use by default 0 if (stream.Char == 'Z') { stream.Next(true); } else { // Find the sign of the offset int sign = 0; if (stream.Char == '-') { sign = -1; } else if (stream.Char == '+') { sign = +1; } else { throw new Exception("Invalid time zone: " + "unexpected character"); } // Read next char and test for more chars stream.Next(true); if (stream.EOF) { throw new Exception("Invalid time zone"); } // Parse hour offset // Note: A hour can be represented by one or two digits. string hulp = ""; while (stream.Char >= '0' && stream.Char <= '9' && !stream.EOF && hulp.Length <= 2) { hulp += (stream.Char); stream.Next(true); } timezone = sign * Double.Parse(hulp); // Parse the minutes of the timezone // when there is still more to parse if (!stream.EOF) { SkipChar(stream, ':'); int temp = ParseNumber(stream, 2); timezone += (temp / 60.0); } } return(timezone); }
/// <summary> /// If it is not Infinity or NaN, then parse as a number /// </summary> private void ParseNumber(ParseStream stream, int sign) { bool base60 = false; // Base 60 with ':' bool afterDecimal = false; // Before or after the decimal point double factor = 0.1; double part; // If base60 different parts, else output value // Set sign content = sign >= 0 ? 1 : -1; // First char must 0-9 if (stream.Char >= '0' && stream.Char <= '9') { part = (uint)(stream.Char - '0'); stream.Next(true); } else { throw new ParseException(stream, "No valid float: Invalid first character of float: " + stream.Char); } while (!stream.EOF) { // Decimal if (stream.Char >= '0' && stream.Char <= '9') { if (afterDecimal) { part += (uint)(stream.Char - '0') * factor; factor *= 0.1; } else { part = (part * 10) + (uint)(stream.Char - '0'); } } // Base60 detected else if (stream.Char == ':') { if (!base60) // First time { content *= part; // Multiply to get sign part = 0; base60 = true; // We are now sure base 60 } else { if (part > 59) { throw new ParseException(stream, "Part of base 60 can't be larger then 59"); } content = (60 * content) + part; part = 0; } } // If first '.', then after decimal, else it is ignored if not in Base60 else if ((!base60 || (base60 && !afterDecimal)) && stream.Char == '.') { afterDecimal = true; } // Determine scientific notation else if ((stream.Char == 'E' || stream.Char == 'e') && !base60) { stream.Next(true); content *= Math.Pow(10, ParseScient(stream)); } // Ignore underscores if before the decimal point, special case if base 60 else if ((!afterDecimal || (afterDecimal && base60)) && stream.Char != '_') { throw new ParseException(stream, "Unknown char"); } stream.Next(true); } // Add last part of base to content if (base60) { content = (60 * content) + part; } else { content *= part; // Multiply to get sign } }
/// <summary> /// Skips a specified char, and throws an exception when /// another char was found. /// </summary> private void SkipChar(ParseStream stream, char toSkip) { if (stream.Char == toSkip) stream.Next (true); else throw new Exception ("Unexpected character"); }
/// <summary> /// Skips the spaces * and tabs * in the current stream /// </summary> private void SkipWhitespace(ParseStream stream) { while ((stream.Char == ' ' || stream.Char == '\t') && ! stream.EOF) stream.Next (true); }
/// <summary> /// Reads a character from the stream, unescapes it, /// and moves to the next character. /// </summary> private char NextUnescapedChar(ParseStream stream) { char c = stream.Char; // If escaped if (c == '\\') { // Never stop, every special character // looses its meaning behind a backslash. stream.StopAt (new Char [] { }); stream.Next (true); c = stream.Char; // ASCII null if (c == '0') c = '\0'; // ASCII bell else if (c == 'a') c = (char) 0x7; // ASCII backspace else if (c == 'b') c = (char) 0x8; // ASCII horizontal tab else if (c == 't') c = (char) 0x9; // ASCII newline else if (c == 'n') c = (char) 0xA; // ASCII vertical tab else if (c == 'v') c = (char) 0xB; // ASCII form feed else if (c == 'f') c = (char) 0xC; // ASCII carriage return else if (c == 'r') c = (char) 0xD; // ASCII escape else if (c == 'e') c = (char) 0x1D; // Unicode next line else if (c == 'N') c = (char) 0x85; // Unicode non breaking space else if (c == '_') c = (char) 0xA0; // TODO larger unicode characters // Unicode line separator // else if (c == 'L') c = (char) 0x20282028; // 8 bit hexadecimal else if (c == 'x') { int c_int = (char) 0; for (int i = 0; i < 2; i ++) { c_int *= 16; stream.Next (); char d = stream.Char; if (d >= '0' && d <= '9') c_int += d - '0'; else if (d >= 'a' && d <= 'f') c_int += d - 'a'; else if (d >= 'A' && d <= 'F') c_int += d - 'A'; else { stream.DontStop (); throw new ParseException (stream, "Invalid escape sequence"); } } c = (char) c_int; } stream.Next (true); // Restore last stop settings stream.DontStop (); } else stream.Next (true); return c; }
/// <summary> Parse a sequence </summary> public Sequence(ParseStream stream) : base("tag:yaml.org,2002:seq", NodeType.Sequence) { // Is this really a sequence? if (stream.Char == '-') { // Parse recursively do { // Override the parent's stop chars, never stop stream.StopAt (new char [] { } ); // Skip over '-' stream.Next (); // Parse recursively stream.Indent (); AddNode (Parse (stream)); stream.UnIndent (); // Re-accept the parent's stop chars stream.DontStop (); } while ( ! stream.EOF && stream.Char == '-' ); } // Or inline Sequence else if (stream.Char == '[') { // Override the parent's stop chars, never stop stream.StopAt (new char [] { }); // Skip '[' stream.Next (); do { stream.StopAt (new char [] {']', ','}); stream.Indent (); AddNode (Parse (stream, false)); stream.UnIndent (); stream.DontStop (); // Skip ',' if (stream.Char != ']' && stream.Char != ',') { stream.DontStop (); throw new ParseException (stream, "Comma expected in inline sequence"); } if (stream.Char == ',') { stream.Next (); stream.SkipSpaces (); } } while ( ! stream.EOF && stream.Char != ']'); // Re-accept the parent's stop chars stream.DontStop (); // Skip ']' if (stream.Char == ']') stream.Next (true); else throw new ParseException (stream, "Inline sequence not closed"); } // Throw an exception when not else throw new Exception ("This is not a sequence"); }
/// <summary> Parses a binairy node. </summary> /// <remarks> /// This is not an efficient method. First the stream is placed /// in a string. And after that the string is converted in a byte[]. /// If there is a fault in the binairy string then that will only be detected /// after reading the whole stream and after coneverting. /// </remarks> public static new byte[] Parse(ParseStream stream) { bool quoted = false; bool block = false; System.Text.StringBuilder input = new System.Text.StringBuilder(); if (stream.EOF) throw new ParseException (stream, "Empty node"); // Detect block scalar stream.SkipSpaces (); if (stream.Char == '|') { block = true; stream.Next (); stream.SkipSpaces (); } while ( ! stream.EOF) { // Detect quotes if (stream.Char == '\"') if (quoted) break; //End of stream else quoted = true; //Start of quoted stream // Detect and ignore newline char's else if (!(stream.Char == '\n' && block)) input.Append( stream.Char ); stream.Next (); } //Console.WriteLine("convert [" + input.ToString() + "]"); return System.Convert.FromBase64String (input.ToString ()); }