/** Finds the next nonblank line, skips past an expected preamble, and reads in a string if there is one, and returns it. * Generates an error otherwise. */ public static string ReadStringWithPreamble(string preamble, IEvolutionState state, StreamReader reader) { DecodeReturn d = CheckPreamble(preamble, state, reader); Decode(d); if (d.Type != DecodeReturn.T_STRING) { state.Output.Fatal("Line " + d.LineNumber + " has no string after preamble '" + preamble + "'\n-->" + d.Data); } return(d.S); }
/// <summary> /// Finds the next nonblank line, then trims the line and checks the preamble. /// Returns a DecodeReturn on the line if successful, else posts a fatal error. /// Sets the DecodeReturn's line number. The DecodeReturn has not yet been decoded. /// You'll need to do that with Code.Decode(...) /// If the "multiline" argument is true, this will append additional lines /// up to the first one that is null or empty (blank). /// Note that this multiline capability provides the means to reconstitute large blocks of data if needed. /// The user must ensure that a blank line (or EOF) marks the end of such blocks. /// </summary> public static DecodeReturn CheckPreamble(string preamble, IEvolutionState state, StreamReader reader, bool multiline) { // BRS : TODO : Added Offset because the reader does not provide "linenumber". Check the actual behavior. var offset = reader.BaseStream.Position; var linenumber = 0; // throw it away later try { // get non-blank line var s = ""; while (s != null && s.Trim().Equals("")) { //linenumber = reader.GetLineNumber(); s = reader.ReadLine(); linenumber++; } // check the preamble if (s == null || !(s = s.Trim()).StartsWith(preamble)) { // uh oh state.Output.Fatal("Stream Offset: " + offset + " Line: " + linenumber + " has bad preamble. Expected '" + preamble + "'. -->" + s); } // At this point we are exactly the same as the single line version if (!multiline) { var d = new DecodeReturn(s, preamble.Length) { LineNumber = linenumber }; return(d); } // multiline has been requested... var sb = new StringBuilder(s); // Aggregate lines up to the first blank line following the preamble while (!String.IsNullOrEmpty(s = reader.ReadLine())) { sb = sb.AppendLine(s); } return(new DecodeReturn(sb.ToString(), preamble.Length) { LineNumber = linenumber }); } catch (IOException e) { state.Output.Fatal("On line " + linenumber + "an IO error occurred:\n\n" + e); return(null); // never happens } }
/// <summary> /// Decodes the next item out of a DecodeReturn and modifies the DecodeReturn to hold the results. /// See DecodeReturn for more explanations about how to interpret the results. /// </summary> public static void Decode(DecodeReturn d) { var dat = d.Data; var x = d.Pos; var len = d.Data.Length; // look for whitespace or ( or ) for (; x < len; x++) { if (!Char.IsWhiteSpace(dat[x]) && dat[x] != '(' && dat[x] != ')') { break; } } // am I at the end of my rope? if (x == len) { d.Type = DecodeReturn.T_ERROR; d.S = "Out of tokens"; return; } // what type am I? switch (dat[x]) { case 't': // boolean (true) if (x + 3 < len && dat[x + 1] == 'r' && dat[x + 2] == 'u' && dat[x + 3] == 'e') { d.Type = DecodeReturn.T_BOOLEAN; d.B = true; d.Pos = x + 4; return; } else { d.Type = DecodeReturn.T_ERROR; d.S = "Expected a (true) boolean"; return; } case 'T': // boolean (true) { d.Type = DecodeReturn.T_BOOLEAN; d.B = true; d.Pos = x + 1; return; } case 'F': // boolean (false) { d.Type = DecodeReturn.T_BOOLEAN; d.B = false; d.Pos = x + 1; return; } case 'f': // float or boolean if (len == 1 || Char.IsWhiteSpace(dat[x + 1]) || dat[x + 1] == ')') // Then we must have a lowercase boolean ("false")? { d.Type = DecodeReturn.T_BOOLEAN; d.B = false; d.Pos = x + 1; return; } if (x + 4 < len && dat[x + 1] == 'a' && dat[x + 2] == 'l' && dat[x + 3] == 's' && dat[x + 4] == 'e') { d.Type = DecodeReturn.T_BOOLEAN; d.B = false; d.Pos = x + 5; return; } else { var readHuman = false; string sf = null; var initial = x + 1; // look for next '|' for (; x < len; x++) { if (dat[x] == '|') { break; } } if (x == initial) { readHuman = true; } if (x >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected a float"; return; } if (!readHuman) { sf = dat.Substring(initial, (x) - (initial)); } x++; // look for next '|' var initial2 = x; // x is now just past first | for (; x < len; x++) { if (dat[x] == '|') { break; } } if (x >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected a float"; return; } if (readHuman) { sf = dat.Substring(initial2, (x) - (initial2)); } float f; try { if (readHuman) { f = Single.Parse(sf); } else { f = (float)BitConverter.Int64BitsToDouble(Int64.Parse(sf)); } } catch (FormatException) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected a float"; return; } d.Type = DecodeReturn.T_FLOAT; d.D = f; d.Pos = x + 1; return; } case 'd': // double { var readHuman = false; string sf = null; var initial = x + 1; // look for next '|' for (; x < len; x++) { if (dat[x] == '|') { break; } } if (x == initial) { readHuman = true; } if (x >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected a double"; return; } if (!readHuman) { sf = dat.Substring(initial, (x) - (initial)); } x++; // look for next '|' var initial2 = x; // x is now just past first | for (; x < len; x++) { if (dat[x] == '|') { break; } } if (x >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected a double"; return; } if (readHuman) { sf = dat.Substring(initial2, (x) - (initial2)); } double f; try { if (readHuman) { f = Double.Parse(sf); } else { f = BitConverter.Int64BitsToDouble(Int64.Parse(sf)); } } catch (FormatException) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected a double"; return; } d.Type = DecodeReturn.T_DOUBLE; d.D = f; d.Pos = x + 1; return; } case 'b': // byte { var initial = x + 1; // look for next '|' for (; x < len; x++) { if (dat[x] == '|') { break; } } if (x >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected a byte"; return; } var sf = dat.Substring(initial, (x) - (initial)); sbyte f; try { f = SByte.Parse(sf); } catch (FormatException) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected a byte"; return; } d.Type = DecodeReturn.T_BYTE; d.L = f; d.Pos = x + 1; return; } case 's': // short { var initial = x + 1; // look for next '|' for (; x < len; x++) { if (dat[x] == '|') { break; } } if (x >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected a short"; return; } var sf = dat.Substring(initial, (x) - (initial)); short f; try { f = Int16.Parse(sf); } catch (FormatException) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected a short"; return; } d.Type = DecodeReturn.T_SHORT; d.L = f; d.Pos = x + 1; return; } case 'i': // int { var initial = x + 1; // look for next '|' for (; x < len; x++) { if (dat[x] == '|') { break; } } if (x >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected an int"; return; } var sf = dat.Substring(initial, (x) - (initial)); int f; try { f = int.Parse(sf); } catch (FormatException) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected an int"; return; } d.Type = DecodeReturn.T_INT; d.L = f; d.Pos = x + 1; return; } case 'l': // long { var initial = x + 1; // look for next '|' for (; x < len; x++) { if (dat[x] == '|') { break; } } if (x >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected a long"; return; } var sf = dat.Substring(initial, (x) - (initial)); long f; try { f = Int64.Parse(sf); } catch (FormatException) { d.Type = DecodeReturn.T_ERROR; d.S = "Expected a long"; return; } d.Type = DecodeReturn.T_LONG; d.L = f; d.Pos = x + 1; return; } case '"': // string { var sb = new StringBuilder(); var inUnicode = false; x++; for (; x < len; x++) { var c = dat[x]; if (c == '"') { // done with the string if (inUnicode) // uh oh { d.Type = DecodeReturn.T_ERROR; d.S = "Forgot to terminate Unicode with a '\\u' in the string"; return; } d.Type = DecodeReturn.T_STRING; d.S = sb.ToString(); d.Pos = x + 1; return; } else if (c == '\\') // escape { x++; if (x >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Unterminated String"; return; } if (dat[x] != 'u' && inUnicode) { d.Type = DecodeReturn.T_ERROR; d.S = "Escape character in Unicode sequence"; return; } switch (dat[x]) { case 'u': inUnicode = !inUnicode; break; case 'b': sb.Append('\b'); break; case 'n': sb.Append('\n'); break; case '"': sb.Append('"'); break; case '\'': sb.Append('\''); break; case 't': sb.Append('\t'); break; case '\\': sb.Append('\\'); break; case '0': sb.Append('\x0000'); break; default: { d.Type = DecodeReturn.T_ERROR; d.S = "Bad escape char in String"; return; } } } else if (inUnicode) { if (x + 3 >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Unterminated String"; return; } try { sb.Append((char)(int.Parse("0x" + c + dat[x + 1] + dat[x + 2] + dat[x + 3]))); ; x += 3; } catch (System.FormatException) { d.Type = DecodeReturn.T_ERROR; d.S = "Bad Unicode in String"; return; } } else { sb.Append(c); } } d.Type = DecodeReturn.T_ERROR; d.S = "Unterminated String"; return; } case '\'': // char { x++; if (x >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Unterminated char"; return; } var c = dat[x]; if (c == '\\') { x++; if (x >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Unterminated char"; return; } switch (dat[x]) { case 'u': if (x + 4 >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Unterminated char"; return; } try { c = (char)(int.Parse("0x" + dat[x + 1] + dat[x + 2] + dat[x + 3] + dat[x + 4])); } catch (System.FormatException) { d.Type = DecodeReturn.T_ERROR; d.S = "Bad Unicode in char"; return; } x += 5; break; case 'b': c = '\b'; x++; break; case 'n': c = '\n'; x++; break; case '"': c = '"'; x++; break; case '\'': c = '\''; x++; break; case 't': c = '\t'; x++; break; case '\\': c = '\\'; x++; break; case '0': c = '\x0000'; x++; break; default: { d.Type = DecodeReturn.T_ERROR; d.S = "Bad escape char in char"; return; } } if (dat[x] != '\'') { d.Type = DecodeReturn.T_ERROR; d.S = "Bad char"; return; } d.Type = DecodeReturn.T_CHAR; d.C = c; d.Pos = x + 1; return; } else { x++; if (x >= len) { d.Type = DecodeReturn.T_ERROR; d.S = "Unterminated char"; return; } if (dat[x] != '\'') { d.Type = DecodeReturn.T_ERROR; d.S = "Bad char"; return; } d.Type = DecodeReturn.T_CHAR; d.C = c; d.Pos = x + 1; return; } } default: d.Type = DecodeReturn.T_ERROR; d.S = "Unknown token"; return; } }