/// <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 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 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> 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> /// 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> /// 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> /// 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> 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> 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); }