using System; using System.Text; using System.Collections.Generic; using sharp.extensions; using System.Globalization; namespace sharp.json { public class ByteParser { private static ASCIIEncoding encoding = new ASCIIEncoding(); static System.Tuple[] escapeCharacters = { System.Tuple.Create('\\','\\'), System.Tuple.Create('/','/'), System.Tuple.Create('"','"'), System.Tuple.Create('b','\b'), System.Tuple.Create('f','\f'), System.Tuple.Create('n','\n'), System.Tuple.Create('r','\r'), System.Tuple.Create('t','\t'), }; static List numberScan = new List(new char[] { '-', '0', '1', '2', '3', '4', '5', '7', '8', '9', '.','E','e' }); char[] buffer; int position; public ByteParser(string source) { this.buffer = source.ToCharArray(); this.position = 0; } public char Peek(){ //if (position >= buffer.Length){ // return 0; //} return buffer[position]; } public string Peek(int len){ return new String(buffer.Segment(position,len).Extend(len)); } public char Read(){ if (position < buffer.Length){ return buffer[position++]; } throw new Exception("End of buffer reached. No more characters available!"); } public char[] Read(int len){ if ((position) < buffer.Length){ char[] r = buffer.Segment(position, len).Extend(len); position += r.Length; return r; } throw new Exception("End of buffer reached. No more characters available!"); } private Int64 parseInt64(){ Int64 t = 0; char ch = Read(); bool neg = false; if (ch == '-'){ neg = true; ch = Read(); } if (!ch.isDigit()){ throw new FormatException("JSON: Number format error"); } while (true){ t *= 10; t += (int)(ch - '0'); if (!Peek().isDigit()){ break; } ch = Read(); } if (neg){ t *= -1; } return t; } public JSON parseNumber(){ List digits = new List(); do { char n = (char)Peek(); if (!numberScan.Contains(n)){ break; } digits.Add(n); Read(); } while (true); string sv = new String(digits.ToArray()); if ((sv.IndexOf(".") > 0)||(sv.IndexOf("E") > 0)||(sv.IndexOf("e") > 0)) { return new JSONNumber(double.Parse(sv,CultureInfo.InvariantCulture)); } else { return new JSONNumber(Int64.Parse(sv)); } //Int64 i64 = parseInt64(); //if (Peek() == '.'){ // Read(); // Int64 dec = parseInt64(); // int lg10 = (int)Math.Log10(dec); // i64 = i64 * lg10; // if (i64 < 0){ // i64 -= dec; // } else { // i64 += dec; // } // return new JSONNumber(((double)i64) / (double)lg10 ); //} else { // return new JSONNumber(i64); //} } public JSON parseObject(){ if (Read() != '{'){ throw new FormatException("parser error: JSON Object should begin with '{'"); } JSONObject o = new JSONObject(); scanWhitespace(); if (Peek() == '}'){ return o; } else { while (true){ string name = parseStringInternal(); scanWhitespace(); if (Read() != ':'){ throw new FormatException(String.Format("parser error: expected ':' but got '{0}'",buffer[position-1])); } scanWhitespace(); o[name] = parseValue(); scanWhitespace(); char n = Read(); if (n == '}'){ break; } if (n == ','){ scanWhitespace(); continue; } throw new FormatException("parser error: expected ',' or '}' but got '"+n+"'"); } } return o; } public JSON parseArray(){ if (Read() != '['){ throw new FormatException("parser error: JSON array should begin with '['"); } JSONArray o = new JSONArray(); scanWhitespace(); if (Peek() == ']'){ return o; } else { while (true){ o.Add(parseValue()); scanWhitespace(); char n = Read(); if (n == ']'){ break; } if (n == ','){ scanWhitespace(); continue; } throw new FormatException(String.Format("parser error: expected ',' or ']' but got '{0}'",n)); } } return o; } private string parseStringInternal(){ StringBuilder sb = new StringBuilder(); if (Read() != '"'){ throw new FormatException(String.Format("JSON string must start with '\"' but got '{0}'",buffer[position-1])); } while (Peek() != '"'){ char ch = Read(); sb.Append(ch); if (ch == '\\') { ch = Read(); sb.Append(ch); } } Read(); return sb.ToString(); } public JSON parseString(){ return new JSONString(parseStringInternal()); } public JSON parseValue(){ scanWhitespace(); char peek = Peek(); switch (peek){ case '{': return parseObject(); case '[': return parseArray(); case '"': return parseString(); } if ((peek >= '0' && peek <= '9') || (peek == '-')){ return parseNumber(); } String p = Peek(4).ToLower(); if (p.Equals("true")){ position+=4; return JSONSpecial.True; } if (p.Equals("null")){ position+=4; return JSONSpecial.Null; } p = Peek(5).ToLower(); if (p.Equals("false")){ position+=5; return JSONSpecial.False; } throw new FormatException(String.Format("Could not parse source at character '{0}' at position {1}",Peek(),position)); } public JSON parse(){ return parseValue(); } private int findNext(char c){ int p = position; while (p < buffer.Length){ if (buffer[p] == c){ return p - position; } } return -1; } private int scanWhitespace(){ while (position < buffer.Length){ if (buffer[position] > 0x20){ return position; } position++; } return position; } } }