/// <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> /// 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> /// 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> 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 a DateTime </summary> public Timestamp(ParseStream stream) : base("tag:yaml.org,2002:timestamp", NodeType.Timestamp) { int year = 0; int month = 0; int day = 0; int hour = 0; int minutes = 0; int seconds = 0; int ms = 0; try { // Parse year year = ParseNumber(stream, 4); SkipChar(stream, '-'); // Parse month month = ParseNumber(stream, 2); SkipChar(stream, '-'); // Parse day day = ParseNumber(stream, 2); // Additional, the time if (!stream.EOF) { ParseTime(stream, out hour, out minutes, out seconds); } // Additional, milliseconds if (!stream.EOF) { ms = ParseMilliSeconds(stream); } // Additional, the timezone if (!stream.EOF) { timezone = ParseTimezone(stream); } // If there is more, then a format exception if (!stream.EOF) { throw new Exception("More data then excepted"); } content = new DateTime(year, month, day, hour, minutes, seconds, ms); } catch (Exception ex) { throw new ParseException(stream, ex.ToString()); } }
/// <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> Parse a binary node </summary> public Binary(ParseStream stream) : base("tag:yaml.org,2002:binary", NodeType.Binary) { try { content = Parse(stream); } catch (FormatException e) { throw new ParseException(stream, e); } }
/// <summary> /// Parse a Yaml string from a textfile and return a Yaml tree /// </summary> public static Node FromFile(string filename) { // Open YAML file StreamReader reader = File.OpenText(filename); ParseStream parsestream = new ParseStream(reader); // Parse Node node = Parse(parsestream); // Close YAML file reader.Close(); return(node); }
/// <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> Parse a DateTime </summary> public Timestamp(ParseStream stream) : base("tag:yaml.org,2002:timestamp", NodeType.Timestamp) { int year = 0; int month = 0; int day = 0; int hour = 0; int minutes = 0; int seconds = 0; int ms = 0; try { // Parse year year = ParseNumber(stream, 4); SkipChar(stream, '-'); // Parse month month = ParseNumber(stream, 2); SkipChar(stream, '-'); // Parse day day = ParseNumber(stream, 2); // Additional, the time if (!stream.EOF) ParseTime(stream, out hour, out minutes, out seconds); // Additional, milliseconds if (!stream.EOF) ms = ParseMilliSeconds(stream); // Additional, the timezone if (!stream.EOF) timezone = ParseTimezone(stream); // If there is more, then a format exception if (!stream.EOF) throw new Exception("More data then excepted"); content = new DateTime(year, month, day, hour, minutes, seconds, ms); } catch (Exception ex) { throw new ParseException(stream, ex.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 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> /// 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> 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> /// Parses a String surrounded without nothing /// </summary> private string ParseUnQuoted(ParseStream stream) { System.Text.StringBuilder builder = new System.Text.StringBuilder(); while (!stream.EOF) { builder.Append(NextUnescapedChar(stream)); } // Trimming left int count = 0; while (count < builder.Length && (builder[count] == ' ' || builder[count] == '\t')) { count++; } if (count >= 0) { builder.Remove(0, count); } // Trimming right count = 0; while (count < builder.Length && (builder[builder.Length - count - 1] == ' ' || builder[builder.Length - count - 1] == '\t')) { count++; } if (count >= 0) { builder.Remove(builder.Length - count, count); } return(builder.ToString()); }
/// <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> 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 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> /// 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> /// 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 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> 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 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> Constructor </summary> /// <param name="stream"> The parse stream (contains the line number where it went wrong) </param> /// <param name="message"> Info about the exception </param> public ParseException(ParseStream stream, string message) : base("Parse error near line " + stream.CurrentLine + ": " + message) { this.linenr = stream.CurrentLine; }
/// <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> /// 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> 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 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> /// 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 a Yaml string from a textfile and return a Yaml tree /// </summary> public static Node FromFile(string filename) { // Open YAML file StreamReader reader = File.OpenText(filename); ParseStream parsestream = new ParseStream(reader); // Parse Node node = Parse(parsestream); // Close YAML file reader.Close(); return node; }
/// <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> 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 String surrounded without nothing /// </summary> private string ParseUnQuoted(ParseStream stream) { System.Text.StringBuilder builder = new System.Text.StringBuilder(); while (!stream.EOF) builder.Append(NextUnescapedChar(stream)); // Trimming left int count = 0; while (count < builder.Length && (builder[count] == ' ' || builder[count] == '\t')) count++; if (count >= 0) builder.Remove(0, count); // Trimming right count = 0; while (count < builder.Length && (builder[builder.Length - count - 1] == ' ' || builder[builder.Length - count - 1] == '\t')) count++; if (count >= 0) builder.Remove(builder.Length - count, count); return builder.ToString(); }
/// <summary> Constructor </summary> /// <param name="stream"> The parse stream (contains the line number where it went wrong) </param> /// <param name="child"> The exception that is for example throwed again </param> public ParseException(ParseStream stream, Exception child) : base("Parse error near line " + stream.CurrentLine, child) { this.linenr = stream.CurrentLine; }
/// <summary> Parse function </summary> protected static Node Parse(ParseStream stream) { return Parse(stream, true); }
/// <summary> /// Parses a scalar /// <list type="bullet"> /// <item>Integer</item> /// <item>String</item> /// <item>Boolean</item> /// <item>Null</item> /// <item>Timestamp</item> /// <item>Float</item> /// <item>Binary</item> /// </list> /// </summary> /// <remarks> /// Binary is only parsed behind an explicit !!binary tag (in Node.cs) /// </remarks> public static new Scalar Parse(ParseStream stream) { // ----------------- // Parse scalars // ----------------- stream.BuildLookaheadBuffer(); // Try Null #if SUPPORT_NULL_NODES try { Scalar s = new Null(stream); stream.DestroyLookaheadBuffer(); return(s); } catch { } #endif // Try boolean #if SUPPORT_BOOLEAN_NODES stream.RewindLookaheadBuffer(); try { Scalar scalar = new Boolean(stream); stream.DestroyLookaheadBuffer(); return(scalar); } catch { } #endif // Try integer #if SUPPORT_INTEGER_NODES stream.RewindLookaheadBuffer(); try { Scalar scalar = new Integer(stream); stream.DestroyLookaheadBuffer(); return(scalar); } catch { } #endif // Try Float #if SUPPORT_FLOAT_NODES stream.RewindLookaheadBuffer(); try { Scalar scalar = new Float(stream); stream.DestroyLookaheadBuffer(); return(scalar); } catch { } #endif // Try timestamp #if SUPPORT_TIMESTAMP_NODES stream.RewindLookaheadBuffer(); try { Scalar scalar = new Timestamp(stream); stream.DestroyLookaheadBuffer(); return(scalar); } catch { } #endif // Other scalars are strings stream.RewindLookaheadBuffer(); stream.DestroyLookaheadBuffer(); return(new String(stream)); }
/// <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 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> /// 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> 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 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> 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 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> /// 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 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> /// 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> /// 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> /// Parses a scalar /// <list type="bullet"> /// <item>Integer</item> /// <item>String</item> /// <item>Boolean</item> /// <item>Null</item> /// <item>Timestamp</item> /// <item>Float</item> /// <item>Binary</item> /// </list> /// </summary> /// <remarks> /// Binary is only parsed behind an explicit !!binary tag (in Node.cs) /// </remarks> public static new Scalar Parse(ParseStream stream) { // ----------------- // Parse scalars // ----------------- stream.BuildLookaheadBuffer(); // Try Null #if SUPPORT_NULL_NODES try { Scalar s = new Null (stream); stream.DestroyLookaheadBuffer (); return s; } catch { } #endif // Try boolean #if SUPPORT_BOOLEAN_NODES stream.RewindLookaheadBuffer (); try { Scalar scalar = new Boolean (stream); stream.DestroyLookaheadBuffer (); return scalar; } catch { } #endif // Try integer #if SUPPORT_INTEGER_NODES stream.RewindLookaheadBuffer (); try { Scalar scalar = new Integer (stream); stream.DestroyLookaheadBuffer (); return scalar; } catch { } #endif // Try Float #if SUPPORT_FLOAT_NODES stream.RewindLookaheadBuffer (); try { Scalar scalar = new Float (stream); stream.DestroyLookaheadBuffer (); return scalar; } catch { } #endif // Try timestamp #if SUPPORT_TIMESTAMP_NODES stream.RewindLookaheadBuffer (); try { Scalar scalar = new Timestamp (stream); stream.DestroyLookaheadBuffer (); return scalar; } catch { } #endif // Other scalars are strings stream.RewindLookaheadBuffer(); stream.DestroyLookaheadBuffer(); return new String(stream); }
/// <summary> Parse function </summary> protected static Node Parse(ParseStream stream) { return(Parse(stream, true)); }
/// <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); }