From 06f2f8a12cc5a3c62cb516380273e53c9272871d Mon Sep 17 00:00:00 2001 From: Booklordofthedings Date: Sat, 11 May 2024 19:11:28 +0200 Subject: [PATCH] initial commit --- .gitignore | 3 + BeefProj.toml | 6 + BeefSpace.toml | 5 + LICENSE | 21 ++ README.md | 2 + src/BofaBuilder.bf | 233 ++++++++++++++++++++++ src/BofaResult.bf | 88 +++++++++ src/BofaValue.bf | 17 ++ src/Testing/Parsing/T_Bofa.bf | 7 + src/Testing/Parsing/T_BofaError.bf | 6 + src/Testing/Parsing/T_SingleLine.bf | 81 ++++++++ src/bofa.bf | 78 ++++++++ src/bofaParser.bf | 287 ++++++++++++++++++++++++++++ src/eBofaType.bf | 15 ++ 14 files changed, 849 insertions(+) create mode 100644 .gitignore create mode 100644 BeefProj.toml create mode 100644 BeefSpace.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/BofaBuilder.bf create mode 100644 src/BofaResult.bf create mode 100644 src/BofaValue.bf create mode 100644 src/Testing/Parsing/T_Bofa.bf create mode 100644 src/Testing/Parsing/T_BofaError.bf create mode 100644 src/Testing/Parsing/T_SingleLine.bf create mode 100644 src/bofa.bf create mode 100644 src/bofaParser.bf create mode 100644 src/eBofaType.bf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2308b79 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +recovery/ +BeefSpace_User.toml \ No newline at end of file diff --git a/BeefProj.toml b/BeefProj.toml new file mode 100644 index 0000000..0d15fdc --- /dev/null +++ b/BeefProj.toml @@ -0,0 +1,6 @@ +FileVersion = 1 + +[Project] +Name = "Bofa" +TargetType = "BeefLib" +StartupObject = "Bofa.Program" diff --git a/BeefSpace.toml b/BeefSpace.toml new file mode 100644 index 0000000..0bb5a2b --- /dev/null +++ b/BeefSpace.toml @@ -0,0 +1,5 @@ +FileVersion = 1 +Projects = {Bofa = {Path = "."}} + +[Workspace] +StartupProject = "Bofa" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d8a8fd1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Jannis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3979052 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# Bofa +Books object format a diff --git a/src/BofaBuilder.bf b/src/BofaBuilder.bf new file mode 100644 index 0000000..841954e --- /dev/null +++ b/src/BofaBuilder.bf @@ -0,0 +1,233 @@ +namespace Bofa.Builder; +using System; +using System.Collections; +typealias bb = BofaBuilder; +class BofaBuilder +{ + /* + Allows users to easily build a bofa file via code + name, typename, typeindicator, value + + This is really fucking slow, because we are allocating so much + In a sensible application you wouldnd call this all that much anyways, so I dont care + */ + public struct b + { + public String Name; //This is always dynamic + public String Type; //Never dynamic + public String TypeName; //Only dynamic on custom types + public String Value; //Dynamic on everything that isnt a array or object + public Span Members; + + public this(String name, String type, String typeName, String value, Span members) + { + Name = name; + Type = type; + TypeName = typeName; + Value = value; + Members = members; + } + + public void Cleanup() + { + delete Name; + if(Type == "c") + delete TypeName; + + if(Type != "a" && Type != "o") + delete Value; + + for(let e in Members) + { + e.Cleanup(); + } + } + + public void ToString(String toAppend, uint32 depth) + { + String toAdd = scope .(scope $"{Type} "); + toAdd.PadLeft(depth+toAdd.Length,' '); + if(Type == "c") + toAdd.Append(scope $"{TypeName} "); + toAdd.Append(scope $"{Name} "); + //Every single + if(Type != "a" && Type != "o") //In this case we can just normally return + { + + if(Type != "t") + toAdd.Append(scope $"{Value}"); + else + { + var en = Value.Split('\n'); + toAdd.Append(en.GetNext()); + + for(var e in en) + { + toAdd.Append('\n'); + String n = scope .(); + n.PadLeft(depth+1, ' '); + n.Append("- "); + n.Append(e); + toAdd.Append(n); + } + } + + + } + toAdd.Append('\n'); + if(Type == "a" || Type == "o") + { + for(var b in Members) + { + b.ToString(toAdd,depth+1); + } + } + toAppend.Append(toAdd); + } + } + + private Span values; + private List valueList = new .(); + public this(params Span input) + { + values = input; + } + + + public ~this() + { + for(var a in values) + a.Cleanup(); + + for(var a in valueList) + a.Cleanup(); + delete valueList; + } + + public override void ToString(String strBuffer) + { + for(var a in values) + { + a.ToString(strBuffer, 0); + } + for(var a in valueList) + { + a.ToString(strBuffer, 0); + } + if(strBuffer[strBuffer.Length-1] == '\n') + strBuffer.RemoveFromEnd(1); //There are probably better ways to do this but I dont care + } + + ///Adds a single entry after the creation + public void Add(b pToAdd) + { + valueList.Add(pToAdd); + } + + public static b Num(StringView name, float number) + { + return .(new .(name), "n", null, new .(number.ToString(.. scope .())),null); + } + + public static b Bool(StringView name, bool bool) + { + return .(new .(name), "b", null, new .(bool.ToString(.. scope .())),null); + } + + public static b Line(StringView name, StringView line) + { + return .(new .(name), "l", null, new .(line),null); + } + + public static b BigNum(StringView name, double bigNum) + { + return .(new .(name), "bn", null, new .(bigNum.ToString(.. scope .())),null); + } + + public static b Int(StringView name, int32 number) + { + return .(new .(name), "i", null, new .(number.ToString(.. scope .())),null); + } + + public static b BigInt(StringView name, int64 number) + { + return .(new .(name), "bi", null, new .(number.ToString(.. scope .())),null); + } + + public static b Text(StringView name, StringView text) + { + return .(new .(name),"t", null, new .(text),null); + } + + public static b Custom(StringView name, StringView type, StringView value) + { + return .(new .(name), "c",new .(type), new .(value), null); + } + + public static b Object(StringView name, params Span input) + { + return .(new .(name), "o", null,null,input); + } + + public static b Array(StringView name, params Span input) + { + return .(new .(name), "a", null,new .(""),input); + } + + + //Array functions, that dont take a name + public static b NumA(float value) + { + return Num("-", value); + } + public static b BoolA(bool value) + { + return Bool("-", value); + } + public static b LineA(StringView value) + { + return Line("-", value); + } + public static b BigNumA(double value) + { + return BigNum("-", value); + } + public static b IntA(int32 value) + { + return Int("-", value); + } + public static b BigIntA(int64 value) + { + return BigInt("-", value); + } + public static b TextA(StringView value) + { + return Text("-", value); + } + public static b CustomA(StringView type, StringView value) + { + return Custom("-", type, value); + } + public static b ObjectA(params Span input) + { + return .("-", "o", null, null, input); + } + public static b ArrayA(params Span input) + { + return .("-", "a", null, null, input); + } + + [Test] + public static void Test() + { + BofaBuilder builder = scope .( + bb.Num("Number",123), + bb.Custom("farbe", "color", "255 0 255"), + bb.Text("Text", "multi \nLine"), + bb.Object( "obj", + bb.Num("SubObject", 123) + ) + ); + Console.WriteLine(builder.ToString(.. scope .())); + } +} \ No newline at end of file diff --git a/src/BofaResult.bf b/src/BofaResult.bf new file mode 100644 index 0000000..ca5a8e2 --- /dev/null +++ b/src/BofaResult.bf @@ -0,0 +1,88 @@ +namespace Bofa; +using System; +using System.Collections; +class BofaResult +{ + /* + BofaResult contains the result of an interaction with the Bofa Parser Library + If you parse a String the parser will return a BofaResult Object + Querying an existing Bofa Result will also return a sub BofaResult + This exist in order to keep the bofa data objects clean and instead manage access via this class + It also contains information about wether any one query was sucessfull + */ + private bool _isResult = false; //If this is true we dont delete any actual object data + private String _data = null; //The backing data, needs to be cleared on destruction if set + private Dictionary _contents = new Dictionary(10); //Storage + private List _result = null; + private Bofa _lastObject = null; //Keeps track of the last object we have added + + + public bool OK { //Keeps track of wether the parsing failed at some point + public get; + private set; + } = true; + + public ~this() + { + + if(_result != null) + delete _result; + + if(_data != null) + { + delete _data; + for(let i in _contents) + { + delete i.value; + } + } //We only delete values on the core object in order to not have error with dumb shit + + delete _contents; + } + + private bool Insert(BofaParser.lineParserResult pResult) + { //This is guaranteed to be a valid pResult, because no one would ever call this from somewhere else with different params + //DONT DO THIS ^ + + if(pResult.Bofa.0 > 0 && (_lastObject == null || (_lastObject.Type != .Object && _lastObject.Type != .Array && _lastObject.Type != .Text))) + return false; + + if(pResult.Bofa.0 > 0) + if(pResult.IsBofa) + return _lastObject.[Friend]Insert(pResult.Bofa.0-1, pResult.Bofa.1); + else + return _lastObject.[Friend]Insert(pResult.Text.0-1, null, pResult.Text.1); + //Okay so we really need to add it in on this layer + + if(!pResult.IsBofa) + { //For text entries on the current layer + if(_lastObject.Type != .Text) + return false; + _lastObject.Value.Text.Append(scope $"\n{pResult.Text.1}"); + return true; + } + + let toAdd = pResult.Bofa.1; + if(_contents.ContainsKey(toAdd.Name)) + return false; //Dublicate name error + _contents.Add(toAdd.Name, toAdd); + _lastObject = toAdd; + return true; + } + + ///Interface functions go here + public Result this[StringView pValue] + { + public get { + if(!_contents.ContainsKey(pValue)) + return .Err; + return _contents[pValue]; + } + } + + public Result GetByName(StringView pValue) + { + return this[pValue]; + } + +} \ No newline at end of file diff --git a/src/BofaValue.bf b/src/BofaValue.bf new file mode 100644 index 0000000..ae1e8f8 --- /dev/null +++ b/src/BofaValue.bf @@ -0,0 +1,17 @@ +namespace Bofa; +using System; +using System.Collections; +[Union] +struct BofaValue +{ + public float Number; + public double BigNumber; + public int32 Integer; + public int64 BigInteger; + public bool Boolean; + public StringView Line; + public StringView Custom; + public String Text; + public Dictionary Object = null; + public List Array = null; +} \ No newline at end of file diff --git a/src/Testing/Parsing/T_Bofa.bf b/src/Testing/Parsing/T_Bofa.bf new file mode 100644 index 0000000..fef00e0 --- /dev/null +++ b/src/Testing/Parsing/T_Bofa.bf @@ -0,0 +1,7 @@ +namespace Bofa.Testing.Parsing; + +class T_Bofa +{ + //TODO: Add some testcases that cover multiple lines and insertions + //Such as texts, objects and arrays +} \ No newline at end of file diff --git a/src/Testing/Parsing/T_BofaError.bf b/src/Testing/Parsing/T_BofaError.bf new file mode 100644 index 0000000..597759b --- /dev/null +++ b/src/Testing/Parsing/T_BofaError.bf @@ -0,0 +1,6 @@ +namespace Bofa.Testing.Parsing; + +class T_BofaError +{ + //TODO; add some testcases that fail the parser +} \ No newline at end of file diff --git a/src/Testing/Parsing/T_SingleLine.bf b/src/Testing/Parsing/T_SingleLine.bf new file mode 100644 index 0000000..2db3731 --- /dev/null +++ b/src/Testing/Parsing/T_SingleLine.bf @@ -0,0 +1,81 @@ +namespace Bofa.Testing.Parsing; +using System; +using Bofa; +using Bofa.Builder; +class T_SingleLine +{ + /* + This class checks the validity of any single line bofa object + */ + + [Test] //Any line that should be parsed as empty + public static void EmptyLine() + { + StringView[] lineArr = scope .( + "", + " ", + "\t", + " ", + "#asdasdasdasd", + " # asdasdasdasd", + "#" + ); + + BofaParser p = .(); + for(let e in lineArr) + { + var r = p.[Friend]parseLine(e); + if(!r.IsEmpty) + Runtime.FatalError(); + } + } + + [Test] //Some lines that should be parsed as text + public static void Text() + { + StringView[] lineArr = scope .( + "- text", //Depth 0 + " - text", //Depth 1 + " - TEasdasdas iodfjoidfiosduiofsd", //Depth 2 + " - dadsadasdasdasfjrdfiofjiofdsghjniodfgdf" //Depth 3 + ); + BofaParser p = .(); + for(int i < lineArr.Count) + { + var r = p.[Friend]parseLine(lineArr[i]); + if(!r.Ok || r.IsEmpty) + Runtime.FatalError(); + + if(r.Text.0 != i+1) + Runtime.FatalError(); + } + } + + [Test] //Doing some parsing + public static void Parse() + { + StringView[] lineArr = scope .( + "n float 1223", + "n float 1.232", + "b bool true", + "b bool false", + "l line text goes here", + "bn BigNumber 3242343242354543", + "i integer 12423423", + "bi BigInteger 344234323454365", + "t text sdfsdgdfgdfgdf", + "t text ", + "c color redd 255 0 0", + "a array", + "o object" + ); + BofaParser p = .(); + for(var e in lineArr) + { + var r = p.[Friend]parseLine(e); + if(!r.Ok || r.IsEmpty || !r.IsBofa) + Runtime.FatalError(); + delete r.Bofa.1; + } + } +} \ No newline at end of file diff --git a/src/bofa.bf b/src/bofa.bf new file mode 100644 index 0000000..62a7191 --- /dev/null +++ b/src/bofa.bf @@ -0,0 +1,78 @@ +namespace Bofa; +using System; +class Bofa +{ + public eBofaType Type; + public StringView TypeName; + public StringView Name; + public BofaValue Value; + + private Bofa LastObject = null; + + public ~this() + { + + if(Type == .Text) + delete Value.Text; + else if(Type == .Object && Value.Object != null) + DeleteDictionaryAndValues!(Value.Object); + else if(Type == .Array && Value.Array != null) + DeleteContainerAndItems!(Value.Array); + } + + private bool Insert(uint32 depth, Bofa toInsert, StringView toAdd = "") + { + if(depth > 0 && (LastObject == null || (LastObject.Type != .Array && LastObject.Type != .Object && LastObject.Type != .Text))) + return false; + + if(depth > 0) + { + return LastObject.Insert(depth-1, toInsert, toAdd); + } + //Actually insert it now + switch(Type) + { + case .Text: + Value.Text.Append(scope $"\n{toAdd}"); + case .Array: + Value.Array.Add(toInsert); + case .Object: + if(Value.Object.ContainsKey(toInsert.Name)) + return false; //Error + Value.Object.Add(toInsert.Name, toInsert); + default: + return false; + } + LastObject = toInsert; + return true; + } + + + //Finding specific entries easier + public Result this[StringView name] + { + public get { + if(Type != .Object) + return .Err; + if(!Value.Object.ContainsKey(name)) + return .Err; + return .Ok(Value.Object[name]); + } + } + + public Result this[uint32 number] + { + public get { + if(Type != .Array) + return .Err; + if(Value.Array.Count <= number) + return .Err; + return Value.Array[number]; + } + } + + public static mixin GetValueOrDefault(Result result, T dfault) + { + result case .Err ? dfault : result + } +} \ No newline at end of file diff --git a/src/bofaParser.bf b/src/bofaParser.bf new file mode 100644 index 0000000..fa932c8 --- /dev/null +++ b/src/bofaParser.bf @@ -0,0 +1,287 @@ +namespace Bofa; +using System; +struct BofaParser +{ + public BofaResult Parse(StringView pLine) + { + BofaResult toReturn = new .(); + toReturn.[Friend]_data = new String(pLine); + var enumerator = toReturn.[Friend]_data.Split('\n'); + for(let e in enumerator) + { //Loop through each line + var res = parseLine(e); //Handle the result and do the inserting + if(res.IsEmpty) + continue; + if(!res.Ok) + { + toReturn.[Friend]OK = false; + return toReturn; + } + var result = toReturn.[Friend]Insert(res); + if(result == false) + { + toReturn.[Friend]OK = false; + if(res.IsBofa) + delete res.Bofa.1; + return toReturn; + } + } + return toReturn; + } + + + ///Atttempts to parse a single line into a bofa object and returns its depth + private lineParserResult parseLine(StringView pLine) + { + StringView line = pLine; //So that we can work on it + +#region Depth + //Get the depth of the line + uint32 depth = 0; + var hasContent = false; //In order to check wether it just ran out, or wether we actually have content + for(var c in line) + { + if(c == ' ' || c == ' ') + { + depth++; + continue; + } + else if(c == '#') + return .(); //Comment + hasContent = true; + break; + } + if(!hasContent) + return .(); //Is empty + line = .(line, depth); +#endregion + +#region Type + //Get the type of the line + let type = NextPart(line); + line = type.1; + if(type.0 == "") //Shouldnt be empty + return .(); + else if(type.0 == "-") + return .(type.1, depth+1); + //We have now assured, that the object we are handling is seemingly a normal one + StringView typeName; + switch(type.0) + { + case "c": + let tnameres = NextPart(line); + if(tnameres.0 == "") + return .(false); + line = tnameres.1; + typeName = tnameres.0; + case "n": + typeName = "Number"; + case "b": + typeName = "Boolean"; + case "l": + typeName = "Line"; + case "bn": + typeName = "BigNumber"; + case "i": + typeName = "Integer"; + case "bi": + typeName = "BigInteger"; + case "t": + typeName = "Text"; + case "a": + typeName = "Array"; + case "o": + typeName = "Object"; + default: + return .(false); //Unsupported type error + } +#endregion + +#region Name + //Get the name and return if its a array or object + let nameres = NextPart(line); + line = nameres.1; + if(nameres.0 == "") + return .(false); + + if(type.0 == "o" || type.0 == "a") + { + Bofa toReturn = new Bofa(); + toReturn.Name = nameres.0; + toReturn.TypeName = typeName; + if(type.0 == "o") + { + toReturn.Type = .Object; + toReturn.Value.Object = new .(); + return .(toReturn, depth); + } + toReturn.Type = .Array; + toReturn.Value.Array = new .(); + return .(toReturn, depth); + } +#endregion + +#region Value + if(line == "") + return .(false); + Bofa toReturn = new .(); + toReturn.Name = nameres.0; + toReturn.TypeName = typeName; + switch(type.0) + { + case "n": + toReturn.Type = .Number; + var result = float.Parse(line); + if(result case .Err) + { + delete toReturn; + return .(false); + } + toReturn.Value.Number = result.Value; + case "b": + if(line == "true") + toReturn.Value.Boolean = true; + else if(line == "false") + toReturn.Value.Boolean = false; + else + { + delete toReturn; + return .(false); + } + toReturn.Type = .Boolean; + case "l": + toReturn.Value.Line = line; + toReturn.Type = .Line; + case "bn": + toReturn.Type = .BigNumber; + var result = double.Parse(line); + if(result case .Err) + { + delete toReturn; + return .(false); + } + toReturn.Value.BigNumber = result.Value; + case "i": + toReturn.Type = .Integer; + var result = int32.Parse(line); + if(result case .Err) + { + delete toReturn; + return .(false); + } + toReturn.Value.Integer = result.Value; + case "bi": + toReturn.Type = .BigInteger; + var result = int64.Parse(line); + if(result case .Err) + { + delete toReturn; + return .(false); + } + toReturn.Value.BigInteger = result.Value; + case "t": + toReturn.Value.Text = new .(line); + toReturn.Type = .Text; + case "c": + toReturn.Value.Custom = line; + toReturn.Type = .Custom; + default: + delete toReturn; + return .(false); + } +#endregion + //If this ever returns something went wrong + return .(toReturn,depth); + + } + + + + private static (StringView, StringView) NextPart(StringView pLine) + { + int i = 0; + for(var c in pLine) + { + if(c == ' ') + { + if(@c.GetNext() case .Ok(let val)) + { + i++; + } + break; + } + i++; + } + return (.(pLine, 0, (i < pLine.Length) ? i-1 : i), .(pLine, i)); + } + + public struct lineParserResult + { //I hate that this is necessary to handle parseLine, but its just so much data that needs to be moved + private bool ok = true; + private bool isEmpty = false; + private uint32 depth = 0; + + private bool isBofa = true; //Wether this is a bofa or a string extension + private StringView text = ""; + private Bofa bofa = null; + + public this(bool pIsEmpty = true) + { //Either for errors or incase its an empty line + //the difference is that empty lines should be skipped, while an error would + if(pIsEmpty) + isEmpty = true; + else + ok = false; + } + + public this(StringView pText, uint32 pDepth = 0) + { //For text addendums + depth = pDepth; + isBofa = false; + text = pText; + } + + public this(Bofa pBofa, uint32 pDepth = 0) + { //For normal bofa objects + depth = pDepth; + bofa = pBofa; + } + + public bool Ok + { + public get { + return ok; + } + } + + public bool IsEmpty + { + public get { + return isEmpty; + } + } + + public bool IsBofa + { + public get { + return isBofa; + } + } + + public (uint32, Bofa) Bofa + { + public get { + return (depth, bofa); + } + } + + public (uint32, StringView) Text + { + public get { + return (depth, text); + } + } + + } + +} \ No newline at end of file diff --git a/src/eBofaType.bf b/src/eBofaType.bf new file mode 100644 index 0000000..7bc06e9 --- /dev/null +++ b/src/eBofaType.bf @@ -0,0 +1,15 @@ +namespace Bofa; + +enum eBofaType +{ + Number, + Boolean, + Line, + BigNumber, + Integer, + BigInteger, + Text, + Custom, + Array, + Object +} \ No newline at end of file