forked from ln-dotnet/ln.json
289 lines
5.6 KiB
C#
289 lines
5.6 KiB
C#
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<char,char>[] 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<char> numberScan = new List<char>(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<char> digits = new List<char>();
|
|
|
|
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;
|
|
}
|
|
|
|
}
|
|
}
|