mirror of
				https://github.com/MaddyThorson/StrawberryBF.git
				synced 2025-11-04 01:41:33 +08:00 
			
		
		
		
	JSON IO
This commit is contained in:
		
							
								
								
									
										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");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user