forked from ln-dotnet/ln.json
275 lines
5.3 KiB
C#
275 lines
5.3 KiB
C#
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<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'),
|
|
};
|
|
|
|
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<char,char> 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;
|
|
}
|
|
|
|
}
|
|
}
|