using System; using System.Text; using System.Collections.Generic; using sharp.extensions; 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'), }; 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)); } 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); 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(){ 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 '{0}'"); } } 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(); if (ch == '\\'){ ch = Read(); foreach (System.Tuple repl in escapeCharacters){ if (repl.Item1 == ch){ sb.Append(repl.Item2); break; } } if (ch == 'u'){ char[] hex = Read(4); sb.Append("_"); Console.WriteLine("JSON WARNING: UNICODE ESCAPE SEQUENCES ARE NOT IMPLEMENTED YET"); } } else { 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; } } }