initial commit

This commit is contained in:
Booklordofthedings 2024-05-11 19:11:28 +02:00
commit 06f2f8a12c
14 changed files with 849 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
build/
recovery/
BeefSpace_User.toml

6
BeefProj.toml Normal file
View file

@ -0,0 +1,6 @@
FileVersion = 1
[Project]
Name = "Bofa"
TargetType = "BeefLib"
StartupObject = "Bofa.Program"

5
BeefSpace.toml Normal file
View file

@ -0,0 +1,5 @@
FileVersion = 1
Projects = {Bofa = {Path = "."}}
[Workspace]
StartupProject = "Bofa"

21
LICENSE Normal file
View file

@ -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.

2
README.md Normal file
View file

@ -0,0 +1,2 @@
# Bofa
Books object format a

233
src/BofaBuilder.bf Normal file
View file

@ -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<b> Members;
public this(String name, String type, String typeName, String value, Span<b> 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<b> values;
private List<b> valueList = new .();
public this(params Span<b> 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<b> input)
{
return .(new .(name), "o", null,null,input);
}
public static b Array(StringView name, params Span<b> 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<b> input)
{
return .("-", "o", null, null, input);
}
public static b ArrayA(params Span<b> 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 .()));
}
}

88
src/BofaResult.bf Normal file
View file

@ -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<StringView, Bofa> _contents = new Dictionary<StringView, Bofa>(10); //Storage
private List<Bofa> _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<Bofa> this[StringView pValue]
{
public get {
if(!_contents.ContainsKey(pValue))
return .Err;
return _contents[pValue];
}
}
public Result<Bofa> GetByName(StringView pValue)
{
return this[pValue];
}
}

17
src/BofaValue.bf Normal file
View file

@ -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<StringView, Bofa> Object = null;
public List<Bofa> Array = null;
}

View file

@ -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
}

View file

@ -0,0 +1,6 @@
namespace Bofa.Testing.Parsing;
class T_BofaError
{
//TODO; add some testcases that fail the parser
}

View file

@ -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;
}
}
}

78
src/bofa.bf Normal file
View file

@ -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<Bofa> this[StringView name]
{
public get {
if(Type != .Object)
return .Err;
if(!Value.Object.ContainsKey(name))
return .Err;
return .Ok(Value.Object[name]);
}
}
public Result<Bofa> 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<T>(Result<T> result, T dfault)
{
result case .Err ? dfault : result
}
}

287
src/bofaParser.bf Normal file
View file

@ -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);
}
}
}
}

15
src/eBofaType.bf Normal file
View file

@ -0,0 +1,15 @@
namespace Bofa;
enum eBofaType
{
Number,
Boolean,
Line,
BigNumber,
Integer,
BigInteger,
Text,
Custom,
Array,
Object
}