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