mirror of
https://github.com/MaddyThorson/StrawberryBF.git
synced 2024-11-25 16:18:56 +08:00
JSON IO
This commit is contained in:
parent
c08ada59d1
commit
3f8b016d84
337
src/JSON/JSON.bf
Normal file
337
src/JSON/JSON.bf
Normal file
|
@ -0,0 +1,337 @@
|
|||
/*
|
||||
JSON I/O based on Noel Berry's C# JSON library
|
||||
https://github.com/NoelFB/JSON
|
||||
*/
|
||||
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
namespace Strawberry
|
||||
{
|
||||
public class JSON
|
||||
{
|
||||
public enum Types { Null, Bool, Number, String, Array, Object };
|
||||
|
||||
public Types Type { get; private set; }
|
||||
public bool Bool;
|
||||
public float Number;
|
||||
public String String;
|
||||
|
||||
private List<JSON> array;
|
||||
private Dictionary<String, JSON> children;
|
||||
|
||||
public this()
|
||||
{
|
||||
Type = .Object;
|
||||
children = new Dictionary<String, JSON>();
|
||||
}
|
||||
|
||||
public this(bool value)
|
||||
{
|
||||
Type = .Bool;
|
||||
Bool = value;
|
||||
}
|
||||
|
||||
public this(int value)
|
||||
{
|
||||
Type = .Number;
|
||||
Number = value;
|
||||
}
|
||||
|
||||
public this(float value)
|
||||
{
|
||||
Type = .Number;
|
||||
Number = value;
|
||||
}
|
||||
|
||||
public this(String value)
|
||||
{
|
||||
Type = .String;
|
||||
String = new String(value);
|
||||
}
|
||||
|
||||
public this(params JSON[] value)
|
||||
{
|
||||
Type = .Array;
|
||||
array = new List<JSON>(value.GetEnumerator());
|
||||
}
|
||||
|
||||
private this(Types type)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public ~this()
|
||||
{
|
||||
if (String != null)
|
||||
delete String;
|
||||
|
||||
if (array != null)
|
||||
{
|
||||
for (let a in array)
|
||||
delete a;
|
||||
delete array;
|
||||
}
|
||||
|
||||
if (children != null)
|
||||
{
|
||||
for (let a in children)
|
||||
{
|
||||
delete a.key;
|
||||
delete a.value;
|
||||
}
|
||||
delete children;
|
||||
}
|
||||
}
|
||||
|
||||
// Array
|
||||
|
||||
public JSON this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
Runtime.Assert(Type == .Array);
|
||||
return array[index];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Runtime.Assert(Type == .Array);
|
||||
array[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void ArrayPush(JSON json)
|
||||
{
|
||||
Runtime.Assert(Type == .Array);
|
||||
array.Add(json);
|
||||
}
|
||||
|
||||
public void ArrayRemoveAt(int index)
|
||||
{
|
||||
Runtime.Assert(Type == .Array);
|
||||
let a = array[index];
|
||||
array.RemoveAt(index);
|
||||
delete a;
|
||||
}
|
||||
|
||||
public void ArrayClear()
|
||||
{
|
||||
Runtime.Assert(Type == .Array);
|
||||
for (let a in array)
|
||||
delete a;
|
||||
array.Clear();
|
||||
}
|
||||
|
||||
// Object
|
||||
|
||||
public JSON this[String key]
|
||||
{
|
||||
get
|
||||
{
|
||||
Runtime.Assert(Type == .Object);
|
||||
|
||||
if (children.ContainsKey(key))
|
||||
return children[key];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
Runtime.Assert(Type == .Object);
|
||||
|
||||
//Remove previous child under that key
|
||||
RemoveChild(key);
|
||||
|
||||
//Add new child
|
||||
children.Add(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveChild(String key)
|
||||
{
|
||||
Runtime.Assert(Type == .Object);
|
||||
if (children.ContainsKey(key))
|
||||
delete children.GetAndRemove(key).Value.value;
|
||||
}
|
||||
|
||||
// Operators
|
||||
|
||||
static public implicit operator JSON(bool val)
|
||||
{
|
||||
return new JSON(val);
|
||||
}
|
||||
|
||||
static public implicit operator JSON(int val)
|
||||
{
|
||||
return new JSON(val);
|
||||
}
|
||||
|
||||
static public implicit operator JSON(float val)
|
||||
{
|
||||
return new JSON(val);
|
||||
}
|
||||
|
||||
static public implicit operator JSON(String val)
|
||||
{
|
||||
return new JSON(val);
|
||||
}
|
||||
|
||||
static public implicit operator bool(JSON json)
|
||||
{
|
||||
Runtime.Assert(json.Type == .Bool);
|
||||
return json.Bool;
|
||||
}
|
||||
|
||||
static public implicit operator int(JSON json)
|
||||
{
|
||||
Runtime.Assert(json.Type == .Number);
|
||||
return (int)json.Number;
|
||||
}
|
||||
|
||||
static public implicit operator float(JSON json)
|
||||
{
|
||||
Runtime.Assert(json.Type == .Number);
|
||||
return json.Number;
|
||||
}
|
||||
|
||||
static public implicit operator String(JSON json)
|
||||
{
|
||||
Runtime.Assert(json.Type == .String);
|
||||
return json.String;
|
||||
}
|
||||
|
||||
static public JSON CreateArray()
|
||||
{
|
||||
let json = new JSON(.Array);
|
||||
json.array = new List<JSON>();
|
||||
return json;
|
||||
}
|
||||
|
||||
static public JSON CreateNull()
|
||||
{
|
||||
return new JSON(.Null);
|
||||
}
|
||||
|
||||
// Out
|
||||
|
||||
public void ToString(String strBuffer, int tabLevel)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case .Null:
|
||||
strBuffer.Append("null");
|
||||
break;
|
||||
|
||||
case .Bool:
|
||||
Bool.ToString(strBuffer);
|
||||
break;
|
||||
|
||||
case .Number:
|
||||
Number.ToString(strBuffer);
|
||||
break;
|
||||
|
||||
case .String:
|
||||
strBuffer.Append('"');
|
||||
strBuffer.Append(String);
|
||||
strBuffer.Append('"');
|
||||
break;
|
||||
|
||||
case .Array:
|
||||
strBuffer.Append("[\n");
|
||||
for (int i < array.Count)
|
||||
{
|
||||
for (let t < tabLevel + 1)
|
||||
strBuffer.Append('\t');
|
||||
array[i].ToString(strBuffer, tabLevel + 1);
|
||||
if (i < array.Count - 1)
|
||||
strBuffer.Append(',');
|
||||
strBuffer.Append('\n');
|
||||
}
|
||||
for (let t < tabLevel)
|
||||
strBuffer.Append('\t');
|
||||
strBuffer.Append("]");
|
||||
break;
|
||||
|
||||
case .Object:
|
||||
strBuffer.Append("{\n");
|
||||
int c = 0;
|
||||
for (let o in children)
|
||||
{
|
||||
for (let t < tabLevel + 1)
|
||||
strBuffer.Append('\t');
|
||||
strBuffer.Append('"');
|
||||
strBuffer.Append(o.key);
|
||||
strBuffer.Append("\": ");
|
||||
o.value.ToString(strBuffer, tabLevel + 1);
|
||||
|
||||
c++;
|
||||
if (c < children.Count)
|
||||
strBuffer.Append(',');
|
||||
strBuffer.Append('\n');
|
||||
}
|
||||
for (let t < tabLevel)
|
||||
strBuffer.Append('\t');
|
||||
strBuffer.Append("}");
|
||||
}
|
||||
}
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
ToString(strBuffer, 0);
|
||||
}
|
||||
|
||||
public void ToStream(Stream stream)
|
||||
{
|
||||
let str = scope String();
|
||||
ToString(str);
|
||||
for (let i < str.Length)
|
||||
stream.Write<char8>(str[i]);
|
||||
}
|
||||
|
||||
public void ToFile(String filePath)
|
||||
{
|
||||
if (File.Exists(filePath))
|
||||
File.Delete(filePath);
|
||||
|
||||
FileStream stream = scope FileStream();
|
||||
stream.Create(filePath, .Write);
|
||||
ToStream(stream);
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
// In
|
||||
|
||||
static public JSON FromString(String string)
|
||||
{
|
||||
return (scope JSONReader(string)).JSON;
|
||||
}
|
||||
|
||||
static public JSON FromStream(Stream stream)
|
||||
{
|
||||
String str = scope String();
|
||||
while (stream.Position < stream.Length)
|
||||
{
|
||||
let char = stream.Read<char8>();
|
||||
if (char case .Err)
|
||||
Runtime.FatalError("File read error!");
|
||||
else
|
||||
str.Append(char);
|
||||
}
|
||||
|
||||
return (scope JSONReader(str)).JSON;
|
||||
}
|
||||
|
||||
static public JSON FromFile(String filePath)
|
||||
{
|
||||
FileStream stream = scope FileStream();
|
||||
stream.Open(filePath, .Read);
|
||||
let json = FromStream(stream);
|
||||
stream.Close();
|
||||
|
||||
return json;
|
||||
}
|
||||
}
|
||||
}
|
229
src/JSON/JSONReader.bf
Normal file
229
src/JSON/JSONReader.bf
Normal file
|
@ -0,0 +1,229 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Strawberry
|
||||
{
|
||||
public class JSONReader
|
||||
{
|
||||
private enum Tokens { Key, Null, Bool, Number, String, ObjectStart, ObjectEnd, ArrayStart, ArrayEnd };
|
||||
|
||||
public JSON JSON { get; private set; }
|
||||
|
||||
private int index;
|
||||
private String data;
|
||||
private String buffer = new String() ~ delete _;
|
||||
private bool boolValue;
|
||||
private float numberValue;
|
||||
|
||||
private bool EndOfFile => index >= data.Length;
|
||||
|
||||
public this(String data)
|
||||
{
|
||||
index = 0;
|
||||
this.data = data;
|
||||
|
||||
let t = Read();
|
||||
if (t != .ObjectStart)
|
||||
Runtime.FatalError("Expected {");
|
||||
JSON = ReadObject();
|
||||
}
|
||||
|
||||
public JSON ReadObject()
|
||||
{
|
||||
JSON json = new JSON();
|
||||
String currentKey = null;
|
||||
|
||||
while (!EndOfFile)
|
||||
{
|
||||
let t = Read();
|
||||
|
||||
if (currentKey == null)
|
||||
{
|
||||
if (t == .ObjectEnd)
|
||||
break;
|
||||
else if (t != .Key)
|
||||
Runtime.FatalError("Expected key");
|
||||
|
||||
currentKey = new String(buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case .Null:
|
||||
json[currentKey] = Strawberry.JSON.CreateNull();
|
||||
|
||||
case .Bool:
|
||||
json[currentKey] = boolValue;
|
||||
|
||||
case .Number:
|
||||
json[currentKey] = numberValue;
|
||||
|
||||
case .String:
|
||||
json[currentKey] = buffer;
|
||||
|
||||
case .ObjectStart:
|
||||
json[currentKey] = ReadObject();
|
||||
|
||||
case .ArrayStart:
|
||||
json[currentKey] = ReadArray();
|
||||
|
||||
default:
|
||||
Runtime.FatalError("Unexpected token");
|
||||
}
|
||||
|
||||
currentKey = null;
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
public JSON ReadArray()
|
||||
{
|
||||
JSON json = Strawberry.JSON.CreateArray();
|
||||
|
||||
L: while (!EndOfFile)
|
||||
{
|
||||
let t = Read();
|
||||
switch (t)
|
||||
{
|
||||
case .ArrayEnd:
|
||||
break L;
|
||||
|
||||
case .Null:
|
||||
json.ArrayPush(Strawberry.JSON.CreateNull());
|
||||
|
||||
case .Bool:
|
||||
json.ArrayPush(boolValue);
|
||||
|
||||
case .Number:
|
||||
json.ArrayPush(numberValue);
|
||||
|
||||
case .String:
|
||||
json.ArrayPush(buffer);
|
||||
|
||||
case .ObjectStart:
|
||||
json.ArrayPush(ReadObject());
|
||||
|
||||
case .ArrayStart:
|
||||
json.ArrayPush(ReadArray());
|
||||
|
||||
default:
|
||||
Calc.Log(t);
|
||||
Runtime.FatalError("Unexpected token");
|
||||
}
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
private char8 Step()
|
||||
{
|
||||
if (index < data.Length)
|
||||
return data[index++];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
private char8 Peek()
|
||||
{
|
||||
if (index < data.Length)
|
||||
return data[index];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
private bool IsWhitespace(char8 char)
|
||||
{
|
||||
return char == ' ' || char == '\t' || char == '\n' || char == '\r';
|
||||
}
|
||||
|
||||
private Tokens Read()
|
||||
{
|
||||
bool inComment = false;
|
||||
buffer.Clear();
|
||||
|
||||
while (!EndOfFile)
|
||||
{
|
||||
char8 c = Step();
|
||||
|
||||
//Finish Comments
|
||||
if (inComment)
|
||||
{
|
||||
if (c == '\n' || c == '\r')
|
||||
inComment = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '#':
|
||||
case '/' when Peek() == '/':
|
||||
inComment = true;
|
||||
continue;
|
||||
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\t':
|
||||
case '\r':
|
||||
case ':':
|
||||
case ',':
|
||||
continue;
|
||||
|
||||
case '{':
|
||||
return Tokens.ObjectStart;
|
||||
|
||||
case '}':
|
||||
return Tokens.ObjectEnd;
|
||||
|
||||
case '[':
|
||||
return Tokens.ArrayStart;
|
||||
|
||||
case ']':
|
||||
return Tokens.ArrayEnd;
|
||||
|
||||
case '"':
|
||||
while (!EndOfFile && Peek() != '"')
|
||||
buffer.Append(Step());
|
||||
Step();
|
||||
|
||||
while (IsWhitespace(Peek()))
|
||||
Step();
|
||||
|
||||
if (Peek() == ':')
|
||||
return Tokens.Key;
|
||||
else
|
||||
return Tokens.String;
|
||||
|
||||
default:
|
||||
buffer.Append(c);
|
||||
while (!EndOfFile && !(" \r\n,:{}[]#").Contains(Peek()))
|
||||
buffer.Append(Step());
|
||||
|
||||
if (buffer.Equals("null", .InvariantCultureIgnoreCase))
|
||||
return Tokens.Null;
|
||||
else if (buffer.Equals("true", .InvariantCultureIgnoreCase))
|
||||
{
|
||||
boolValue = true;
|
||||
return Tokens.Bool;
|
||||
}
|
||||
else if (buffer.Equals("false", .InvariantCultureIgnoreCase))
|
||||
{
|
||||
boolValue = false;
|
||||
return Tokens.Bool;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (float.Parse(buffer) case .Err)
|
||||
Calc.Log(buffer);
|
||||
numberValue = float.Parse(buffer);
|
||||
return Tokens.Number;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Runtime.FatalError("Malformed JSON");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user