This commit is contained in:
Matt Thorson 2020-05-16 22:15:03 -07:00
parent c08ada59d1
commit 3f8b016d84
2 changed files with 566 additions and 0 deletions

337
src/JSON/JSON.bf Normal file
View 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
View 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");
}
}
}