Update to version 1.0.0 #3
26 changed files with 1948 additions and 797 deletions
|
@ -4,3 +4,11 @@ FileVersion = 1
|
|||
Name = "Bofa"
|
||||
TargetType = "BeefLib"
|
||||
StartupObject = "Bofa.Program"
|
||||
|
||||
[Platform.Windows]
|
||||
Description = "A parser for Books Object Format A"
|
||||
Company = "Booklordofthedings"
|
||||
Product = "Bofa"
|
||||
Copyright = "Booklordofthedings"
|
||||
FileVersion = "1.0.0"
|
||||
ProductVersion = "1.0.0"
|
||||
|
|
|
@ -3,3 +3,10 @@ Projects = {Bofa = {Path = "."}}
|
|||
|
||||
[Workspace]
|
||||
StartupProject = "Bofa"
|
||||
|
||||
[Configs.Test.Win64]
|
||||
LargeStrings = true
|
||||
|
||||
[Configs.Debug.Win64]
|
||||
LargeStrings = true
|
||||
LargeCollections = true
|
||||
|
|
149
README.md
149
README.md
|
@ -1,2 +1,147 @@
|
|||
# Bofa
|
||||
Books object format a
|
||||
# Bofa - Books Object Format A - 1.0.0
|
||||
Bofa is an object notation for general purpose use in serialization and deserialization.
|
||||
The primary focus is on being human readable, while also being extremely easy to parse
|
||||
This repository implements a Bofa parser in the Beef programming language.
|
||||
|
||||
```
|
||||
# A floating point number
|
||||
n number 125.435
|
||||
l line this is a normal line
|
||||
a Array
|
||||
t text you can
|
||||
- put more text in here
|
||||
t text2 and even
|
||||
- more in here
|
||||
```
|
||||
|
||||
## Index
|
||||
- How to install
|
||||
- Changelog
|
||||
- The Bofa notation
|
||||
- Parser notes
|
||||
- Bofa parser API usage
|
||||
- Potential improvements
|
||||
|
||||
## How to install
|
||||
- Download the repository, either with git, the download zip functionality or the release tab
|
||||
- Create or open your existing Beef project
|
||||
- Right click on the workspace panel
|
||||
- Select "Add Existing Project"
|
||||
- Navigate to the BeefProj.toml file inside the Bofa folder
|
||||
- Right click on the project you want to use Bofa with
|
||||
- Click "Properties" -> "Dependencies"
|
||||
- Check the box that says Bofa
|
||||
- Add the Bofa namespace to the files you want to use Bofa with
|
||||
- Use Bofa
|
||||
|
||||
## Changelog
|
||||
- Version 1.0.0
|
||||
- Initial Version
|
||||
- Fast parsing
|
||||
- Add automated serialization generation
|
||||
|
||||
## The Bofa notation
|
||||
The notation itself is pretty simple.
|
||||
Every line contains one Bofa "Entry", but empty lines and comments are also allowed.
|
||||
A normal Bofa entry has 3 Parts: `{Type} {Name} {Value}`
|
||||
|
||||
The possible values of `{Type}` are:
|
||||
- l - Line - A string (utf8) without a newline character
|
||||
- t - Text - A string (utf8) that may contain newline characters
|
||||
- b - Boolean - A boolean value of either "true" or "false"
|
||||
- i - Integer - A 32bit signed integer
|
||||
- ui - UnsignedInteger - A 32bit unsigned integer
|
||||
- bi - BigInteger - A 64bit signed integer
|
||||
- bui - BigUnsignedInteger - A 64bit unsigned integer
|
||||
- n - Number - A 32bit floating point number
|
||||
- bn - BigNumber - A 64bit floating point number
|
||||
- c - Custom - Any custom type in a string (utf8) representation
|
||||
- o - Object - A Key/Value container for other Bofa entries
|
||||
- a - Array - A container for other Bofa entries, where entries may have identical names
|
||||
|
||||
`{Name}` is simple just a string that does not contain a space or tab character
|
||||
`{Value}` is a string that represents the choosen datatype and can be parsed into the datatype
|
||||
|
||||
Empty lines are full ignored and comments start with a # as the first non whitespace character in the line.
|
||||
|
||||
An entry of type custom needs additional information to work.
|
||||
Instead of `{Type} {Name} {Value}` a custom entry uses `{Type} {Typename} {Name} {Value}` instead.
|
||||
Similarly arrays and objects dont actually need a entry for `{Value}` and just use `{Type} {Name}`.
|
||||
In order to allow multiline text without too many issues the `-` character is used to indicate that the content of this line should be appended
|
||||
to the last line if it was of type text.
|
||||
|
||||
Membership of containers like arrays or objects is indicated via a depth value.
|
||||
In order to indicate depth the start of the line may contain any number of tabs or spaces, the total amount of which indicates the depth of the entry.
|
||||
The exact object it is a member of is always the last defined object at a given depth.
|
||||
```
|
||||
a Array1
|
||||
a Array2
|
||||
a Array3
|
||||
l Line this line is a member of Array3 which is itself contained inside of Array1
|
||||
```
|
||||
Objects have a key uniqueness rules, while Arrays dont have this (Entries of an array still need to have a proper name though, even if its only "-").
|
||||
|
||||
## Parser notes
|
||||
Bofa should always be parsed loosely and to the best of the parsers ability, but never fail completely.
|
||||
Instead it can indicate on which line an error occured and leave the user to figure out wether to stop or to continue.
|
||||
This also enables a parsing pattern where Bofa data can be structured so any parser can read a document from a newer or different parser:
|
||||
```
|
||||
# The following line has a type that was added by a imaginary parser using the Bofa 2.0 Standard
|
||||
rgb color 255:0:255
|
||||
c rgb color 255:0:255
|
||||
```
|
||||
A parser that supports 2.0 should just parse the first entry and ignore the second, as it contains a dublicate name.
|
||||
A parser still on an old version will ignore the first one, since it doesnt know a type named rgb but instead parse the second one correctly
|
||||
|
||||
While the insertion at a depth needs to be done in the order of the document, the parsing and validation of the individual line does not.
|
||||
As such you can theoretically multithread the parsing of each individual line to get some potential speed.
|
||||
|
||||
Different parsers may add different types for their usecases, but single and 2 character typenames should stay reserved for official notation types.
|
||||
|
||||
The entire standard is case sensitive.
|
||||
|
||||
## Bofa parser api usage
|
||||
This shows of an example of how to use the parser api
|
||||
```cs
|
||||
namespace Example;
|
||||
|
||||
using System;
|
||||
using Bofa;
|
||||
|
||||
class ExampleClass
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
String bofaString = """
|
||||
# A floating point number
|
||||
n number 125.435
|
||||
l line this is a normal line
|
||||
a Array
|
||||
t text you can
|
||||
- put more text in here
|
||||
t text2 and even
|
||||
- more in here
|
||||
""";
|
||||
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(bofaString, output, errors);
|
||||
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Potential improvements
|
||||
- There is still alot of speed left on the table
|
||||
- Reduce the amount of allocations
|
||||
- Multithread initial parsing
|
||||
- New useful types
|
||||
- Color
|
||||
- Version
|
||||
- Encrypted raw data
|
||||
- More automatic serialization/deserialization in Corlib
|
||||
- Hashes
|
||||
- Pointers
|
||||
- Tuples
|
242
src/Bofa.bf
Normal file
242
src/Bofa.bf
Normal file
|
@ -0,0 +1,242 @@
|
|||
namespace Bofa;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
class Bofa
|
||||
{
|
||||
|
||||
private String _line = null ~ delete _; //The actual line
|
||||
private Bofa _lastObject = null; //If we are a container we keep track of the last container inside of us
|
||||
|
||||
public StringView Name;
|
||||
public EBofaType Type;
|
||||
public StringView Typename;
|
||||
public BofaValue Value;
|
||||
|
||||
public override void ToString(String strBuffer)
|
||||
{
|
||||
int64 depth = 0;
|
||||
Bofa target = this;
|
||||
List<(int64, Bofa)> toProcess = new .();
|
||||
defer delete toProcess;
|
||||
|
||||
repeat
|
||||
{
|
||||
|
||||
target.ToString(strBuffer, depth);
|
||||
|
||||
if(target.Type == .Array)
|
||||
{
|
||||
depth++;
|
||||
for(var i in target.Value.Array.Reversed)
|
||||
toProcess.Add((depth, i));
|
||||
}
|
||||
else if(target.Type == .Object)
|
||||
{
|
||||
depth++;
|
||||
for(var i in target.Value.Object)
|
||||
toProcess.Add((depth, i.value));
|
||||
}
|
||||
|
||||
if(toProcess.IsEmpty)
|
||||
target = null;
|
||||
else
|
||||
{
|
||||
var t = toProcess.PopBack();
|
||||
depth = t.0;
|
||||
target = t.1;
|
||||
strBuffer.Append('\n');
|
||||
}
|
||||
} while(target != null);
|
||||
}
|
||||
|
||||
private void ToString(String strBuffer, int64 pDepth)
|
||||
{
|
||||
strBuffer.Append(' ', pDepth);
|
||||
switch(Type)
|
||||
{
|
||||
case .Array:
|
||||
strBuffer.Append(scope $"a {Name}");
|
||||
case .BigInteger:
|
||||
strBuffer.Append(scope $"bi {Name} {Value.BigInteger}");
|
||||
case .BigUnsignedInteger:
|
||||
strBuffer.Append(scope $"bui {Name} {Value.BigUnsignedInteger}");
|
||||
case .BigNumber:
|
||||
strBuffer.Append(scope $"bn {Name} {Value.BigNumber:R}");
|
||||
case .Boolean:
|
||||
strBuffer.Append(scope $"b {Name} {Value.Boolean ? "true" : "false"}");
|
||||
case .Custom:
|
||||
strBuffer.Append(scope $"c {Typename} {Name} {Value.Custom}");
|
||||
case .Integer:
|
||||
strBuffer.Append(scope $"i {Name} {Value.Integer}");
|
||||
case .UnsignedInteger:
|
||||
strBuffer.Append(scope $"ui {Name} {Value.UnsignedInteger}");
|
||||
case .Line:
|
||||
strBuffer.Append(scope $"l {Name} {Value.Line}");
|
||||
case .Number:
|
||||
strBuffer.Append(scope $"n {Name} {Value.Number:R}");
|
||||
case .Object:
|
||||
strBuffer.Append(scope $"o {Name}");
|
||||
case .Text:
|
||||
strBuffer.Append(scope $"t {Name} ");
|
||||
for(var i in Value.Text.Split('\n', .None))
|
||||
{
|
||||
strBuffer.Append(i);
|
||||
if(@i.HasMore)
|
||||
{
|
||||
strBuffer.Append('\n');
|
||||
strBuffer.Append(' ', pDepth);
|
||||
strBuffer.Append("- " );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///Deletes this object without filling the stack
|
||||
public void DeepDeletion()
|
||||
{
|
||||
if(this.Type == .Array || this.Type == .Object)
|
||||
{
|
||||
List<Bofa> toDelete = new .();
|
||||
List<Bofa> toProcess = new .();
|
||||
|
||||
toDelete.Add(this);
|
||||
Bofa target = this;
|
||||
repeat
|
||||
{
|
||||
if(target.Type == .Object)
|
||||
{
|
||||
for(var i in target.Value.Object)
|
||||
{
|
||||
toDelete.Add(i.value);
|
||||
toProcess.Add(i.value);
|
||||
}
|
||||
target.Value.Object.Clear();
|
||||
}
|
||||
else if(target.Type == .Array)
|
||||
{
|
||||
for(var i in target.Value.Array)
|
||||
{
|
||||
toDelete.Add(i);
|
||||
toProcess.Add(i);
|
||||
}
|
||||
target.Value.Array.Clear();
|
||||
}
|
||||
|
||||
if(toProcess.Count > 0 && target != null)
|
||||
target = toProcess.PopBack();
|
||||
else
|
||||
target = null;
|
||||
} while(target != null);
|
||||
|
||||
for(var i in toDelete.Reversed)
|
||||
delete i;
|
||||
|
||||
delete toDelete;
|
||||
delete toProcess;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(_line != null)
|
||||
delete _line;
|
||||
|
||||
if(Type == .Text)
|
||||
delete Value.Text;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ~this()
|
||||
{
|
||||
if(Type == .Text)
|
||||
delete Value.Text;
|
||||
else if(Type == .Object)
|
||||
DeleteDictionaryAndValues!(Value.Object);
|
||||
else if(Type == .Array)
|
||||
DeleteContainerAndItems!(Value.Array);
|
||||
}
|
||||
|
||||
public ref Bofa this[StringView view]
|
||||
{
|
||||
[Checked]
|
||||
get
|
||||
{
|
||||
Runtime.Assert(Type == .Object);
|
||||
Runtime.Assert(Value.Object.ContainsKey(view));
|
||||
return ref Value.Object[view];
|
||||
}
|
||||
|
||||
[Unchecked, Inline]
|
||||
get
|
||||
{
|
||||
return ref Value.Object[view];
|
||||
}
|
||||
|
||||
[Checked]
|
||||
set
|
||||
{
|
||||
Runtime.Assert(Type == .Object);
|
||||
Runtime.Assert(!Value.Object.ContainsKey(view));
|
||||
Value.Object[view] = value;
|
||||
}
|
||||
|
||||
[Unchecked, Inline]
|
||||
set
|
||||
{
|
||||
Value.Object[view] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public ref Bofa this[int view]
|
||||
{
|
||||
[Checked]
|
||||
get
|
||||
{
|
||||
Runtime.Assert(Type == .Array);
|
||||
Runtime.Assert(Value.Array.Count > view);
|
||||
return ref Value.Array[view];
|
||||
}
|
||||
|
||||
[Unchecked, Inline]
|
||||
get
|
||||
{
|
||||
return ref Value.Array[view];
|
||||
}
|
||||
|
||||
[Checked]
|
||||
set
|
||||
{
|
||||
Runtime.Assert(Type == .Array);
|
||||
Runtime.Assert(Value.Array.Count > view);
|
||||
Value.Array[view] = value;
|
||||
}
|
||||
|
||||
[Unchecked, Inline]
|
||||
set
|
||||
{
|
||||
Value.Array[view] = value;
|
||||
}
|
||||
}
|
||||
|
||||
///Get the value if the object is a container
|
||||
public Result<Bofa> Get(StringView pKey)
|
||||
{
|
||||
if(Type != .Object)
|
||||
return .Err;
|
||||
if(!Value.Object.ContainsKey(pKey))
|
||||
return .Err;
|
||||
return Value.Object[pKey];
|
||||
}
|
||||
|
||||
///Get the value if the object is a container
|
||||
public Result<Bofa> Get(int pIndex)
|
||||
{
|
||||
if(Type != .Array)
|
||||
return .Err;
|
||||
if(!(Value.Array.Count > pIndex))
|
||||
return .Err;
|
||||
return Value.Array[pIndex];
|
||||
}
|
||||
}
|
|
@ -1,233 +0,0 @@
|
|||
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 .()));
|
||||
}
|
||||
}
|
337
src/BofaParser.bf
Normal file
337
src/BofaParser.bf
Normal file
|
@ -0,0 +1,337 @@
|
|||
namespace Bofa;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Collections;
|
||||
|
||||
using Bofa.Parser;
|
||||
using Bofa;
|
||||
|
||||
class BofaParser
|
||||
{
|
||||
public static void Parse(StringView pToParse, Dictionary<StringView, Bofa> pResult, List<int64> pErrors)
|
||||
{
|
||||
StringStream stream = new .(pToParse, .Reference);
|
||||
defer delete stream;
|
||||
Parse(stream, pResult, pErrors);
|
||||
}
|
||||
|
||||
public static void Parse(Stream pToParse, Dictionary<StringView, Bofa> pResult, List<int64> pErrors)
|
||||
{
|
||||
Bofa last = null;
|
||||
if (pToParse == null || pResult == null)
|
||||
{ //Cannot parse what doesnt exist
|
||||
pErrors.Add(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
StreamReader reader = new .(pToParse);
|
||||
defer delete reader;
|
||||
|
||||
int64 line = 1;
|
||||
while (reader.Peek() case .Ok) //This might technically cause an issue when there is data added to the stream while we where busy processing, but if you hook this up to a network stream you deserve this happening to you
|
||||
{
|
||||
String l = new String();
|
||||
if (reader.ReadLine(l) case .Err)
|
||||
{ //Despite data being here, we cannot read it
|
||||
delete l;
|
||||
pErrors.Add(-2);
|
||||
return;
|
||||
}
|
||||
|
||||
var entry = ParseLine(l, line);
|
||||
if (!(entry.Type == .Bofa || entry.Type == .Text))
|
||||
delete l; //In these cases we have no useable data
|
||||
if (entry.Type == .Empty)
|
||||
{
|
||||
line++;
|
||||
continue;
|
||||
}
|
||||
if (entry.Type == .Error)
|
||||
{
|
||||
if (entry.Data.Bofa != null)
|
||||
delete entry.Data.Bofa;
|
||||
pErrors.Add(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
//If we are on the lowest level we can just add them here
|
||||
if (entry.Depth == 0 && entry.Type == .Bofa)
|
||||
{
|
||||
if (!pResult.ContainsKey(entry.Data.Bofa.Name))
|
||||
{
|
||||
last = entry.Data.Bofa;
|
||||
pResult.Add(entry.Data.Bofa.Name, entry.Data.Bofa);
|
||||
}
|
||||
else
|
||||
{
|
||||
pErrors.Add(entry.Line); //Dublicate name error
|
||||
delete entry.Data.Bofa;
|
||||
}
|
||||
}
|
||||
else if (entry.Depth == 1 && entry.Type == .Text)
|
||||
{
|
||||
if (last.Type == .Text)
|
||||
last.Value.Text.Append(scope $"\n{entry.Data.Text}");
|
||||
else
|
||||
pErrors.Add(entry.Line); //Bad text error
|
||||
delete l;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.Depth--;
|
||||
if (last.[Friend]_Insert(&entry) case .Err)
|
||||
{
|
||||
pErrors.Add(line);
|
||||
delete entry.Data.Bofa;
|
||||
}
|
||||
else if (entry.Type == .Text)
|
||||
delete l;
|
||||
}
|
||||
line++;
|
||||
}
|
||||
return; //Being done normally
|
||||
}
|
||||
|
||||
[Inline]
|
||||
private static ParserEntry ParseLine(String pLine, int64 pLineNumber)
|
||||
{
|
||||
StringView line = pLine;
|
||||
ParserEntry toReturn = .();
|
||||
toReturn.Line = pLineNumber;
|
||||
|
||||
#region Depth
|
||||
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 == '#')
|
||||
{
|
||||
toReturn.Type = .Empty;
|
||||
return toReturn; //Comment
|
||||
}
|
||||
hasContent = true;
|
||||
break;
|
||||
}
|
||||
if (!hasContent)
|
||||
{
|
||||
toReturn.Type = .Empty;
|
||||
return toReturn; //Is empty
|
||||
}
|
||||
line = .(line, depth);
|
||||
toReturn.Depth = depth;
|
||||
#endregion
|
||||
|
||||
#region Type
|
||||
//Get the type of the line
|
||||
let type = NextPart(line);
|
||||
line = type.1;
|
||||
if (type.0 == "") //This should never be reached
|
||||
{
|
||||
Runtime.FatalError("Unreachable code reached");
|
||||
}
|
||||
else if (type.0 == "-")
|
||||
{
|
||||
toReturn.Type = .Text;
|
||||
toReturn.Data.Text = line;
|
||||
toReturn.Depth = depth + 1;
|
||||
return toReturn;
|
||||
}
|
||||
//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 == "")
|
||||
{ //Its of type custom but ends after the custom
|
||||
toReturn.Type = .Error;
|
||||
return toReturn;
|
||||
}
|
||||
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 "ui":
|
||||
typeName = "UnsignedInteger";
|
||||
case "bui":
|
||||
typeName = "BigUnsignedInteger";
|
||||
case "t":
|
||||
typeName = "Text";
|
||||
case "a":
|
||||
typeName = "Array";
|
||||
case "o":
|
||||
typeName = "Object";
|
||||
default:
|
||||
toReturn.Type = .Error;
|
||||
return toReturn; //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 == "")
|
||||
{
|
||||
toReturn.Type = .Error;
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
if (type.0 == "o" || type.0 == "a")
|
||||
{
|
||||
Bofa bofaRes = new Bofa();
|
||||
bofaRes.[Friend]_line = pLine;
|
||||
bofaRes.Name = nameres.0;
|
||||
toReturn.Type = .Bofa;
|
||||
toReturn.Data.Bofa = bofaRes;
|
||||
bofaRes.Typename = typeName;
|
||||
if (type.0 == "o")
|
||||
{
|
||||
bofaRes.Type = .Object;
|
||||
bofaRes.Value.Object = new .();
|
||||
}
|
||||
else
|
||||
{
|
||||
bofaRes.Type = .Array;
|
||||
bofaRes.Value.Array = new .();
|
||||
}
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Value
|
||||
if (line == "")
|
||||
{
|
||||
toReturn.Type = .Error;
|
||||
return toReturn;
|
||||
}
|
||||
Bofa bofaRes = new .();
|
||||
bofaRes.Name = nameres.0;
|
||||
bofaRes.Typename = typeName;
|
||||
|
||||
toReturn.Data.Bofa = bofaRes;
|
||||
bofaRes.[Friend]_line = pLine;
|
||||
|
||||
|
||||
switch (type.0)
|
||||
{
|
||||
case "n":
|
||||
bofaRes.Type = .Number;
|
||||
var result = float.Parse(line);
|
||||
if (result case .Err)
|
||||
{
|
||||
toReturn.Type = .Error;
|
||||
return toReturn;
|
||||
}
|
||||
bofaRes.Value.Number = result.Value;
|
||||
case "b":
|
||||
if (line == "true")
|
||||
bofaRes.Value.Boolean = true;
|
||||
else if (line == "false")
|
||||
bofaRes.Value.Boolean = false;
|
||||
else
|
||||
{
|
||||
toReturn.Type = .Error;
|
||||
return toReturn;
|
||||
}
|
||||
bofaRes.Type = .Boolean;
|
||||
case "l":
|
||||
bofaRes.Value.Line = line;
|
||||
bofaRes.Type = .Line;
|
||||
case "bn":
|
||||
bofaRes.Type = .BigNumber;
|
||||
var result = double.Parse(line);
|
||||
if (result case .Err)
|
||||
{
|
||||
toReturn.Type = .Error;
|
||||
return toReturn;
|
||||
}
|
||||
bofaRes.Value.BigNumber = result.Value;
|
||||
case "i":
|
||||
bofaRes.Type = .Integer;
|
||||
var result = int32.Parse(line);
|
||||
if (result case .Err)
|
||||
{
|
||||
toReturn.Type = .Error;
|
||||
return toReturn;
|
||||
}
|
||||
bofaRes.Value.Integer = result.Value;
|
||||
case "bi":
|
||||
bofaRes.Type = .BigInteger;
|
||||
var result = int64.Parse(line);
|
||||
if (result case .Err)
|
||||
{
|
||||
toReturn.Type = .Error;
|
||||
return toReturn;
|
||||
}
|
||||
case "ui":
|
||||
bofaRes.Type = .UnsignedInteger;
|
||||
var result = uint32.Parse(line);
|
||||
if (result case .Err)
|
||||
{
|
||||
toReturn.Type = .Error;
|
||||
return toReturn;
|
||||
}
|
||||
bofaRes.Value.UnsignedInteger = result.Value;
|
||||
case "bui":
|
||||
bofaRes.Type = .BigUnsignedInteger;
|
||||
var result = uint64.Parse(line);
|
||||
if (result case .Err)
|
||||
{
|
||||
toReturn.Type = .Error;
|
||||
return toReturn;
|
||||
}
|
||||
bofaRes.Value.BigUnsignedInteger = result.Value;
|
||||
case "t":
|
||||
bofaRes.Value.Text = new String(line);
|
||||
bofaRes.Type = .Text;
|
||||
case "c":
|
||||
bofaRes.Value.Custom = line;
|
||||
bofaRes.Type = .Custom;
|
||||
default: //Unknown type
|
||||
toReturn.Type = .Error;
|
||||
return toReturn;
|
||||
}
|
||||
#endregion
|
||||
//If this ever returns something went wrong
|
||||
toReturn.Type = .Bofa;
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
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];
|
||||
}
|
||||
|
||||
}
|
|
@ -1,17 +1,21 @@
|
|||
namespace Bofa;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
[Union]
|
||||
struct BofaValue
|
||||
{
|
||||
public StringView Line;
|
||||
public String Text;
|
||||
public float Number;
|
||||
public double BigNumber;
|
||||
public int32 Integer;
|
||||
public int64 BigInteger;
|
||||
public uint32 UnsignedInteger;
|
||||
public uint64 BigUnsignedInteger;
|
||||
public bool Boolean;
|
||||
public StringView Line;
|
||||
public StringView Custom;
|
||||
public String Text;
|
||||
public Dictionary<StringView, Bofa> Object = null;
|
||||
public List<Bofa> Array = null;
|
||||
public Dictionary<StringView, Bofa> Object;
|
||||
public List<Bofa> Array;
|
||||
}
|
513
src/Extension.bf
Normal file
513
src/Extension.bf
Normal file
|
@ -0,0 +1,513 @@
|
|||
using Bofa.Serialization;
|
||||
using Bofa;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
|
||||
namespace System
|
||||
{
|
||||
static
|
||||
{
|
||||
public static mixin cast<T>(var v)
|
||||
{
|
||||
*(T*)(void*)&v
|
||||
}
|
||||
}
|
||||
|
||||
extension Int : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .Integer;
|
||||
pTarget.Typename = "Integer";
|
||||
pTarget.Value.Integer = (.)Math.Clamp(this, int32.MinValue, int32.MaxValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type != .Integer)
|
||||
return false;
|
||||
this = Math.Clamp(pInput.Value.Integer, int.MaxValue, int.MinValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension Int8 : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .Integer;
|
||||
pTarget.Typename = "Integer";
|
||||
pTarget.Value.Integer = (.)Math.Clamp(this, int8.MinValue, int8.MaxValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type != .Integer)
|
||||
return false;
|
||||
this = (.)Math.Clamp(pInput.Value.Integer, int8.MaxValue, int8.MinValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension Int16 : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .Integer;
|
||||
pTarget.Typename = "Integer";
|
||||
pTarget.Value.Integer = (.)Math.Clamp(this, int16.MinValue, int16.MaxValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type != .Integer)
|
||||
return false;
|
||||
this = (.)Math.Clamp(pInput.Value.Integer, int16.MaxValue, int16.MinValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension Int32 : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .Integer;
|
||||
pTarget.Typename = "Integer";
|
||||
pTarget.Value.Integer = (.)this;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type != .Integer)
|
||||
return false;
|
||||
this = (.)pInput.Value.Integer;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension Int64 : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .BigInteger;
|
||||
pTarget.Typename = "BigInteger";
|
||||
pTarget.Value.BigInteger = (.)this;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type != .BigInteger)
|
||||
return false;
|
||||
this = (.)pInput.Value.BigInteger;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .UnsignedInteger;
|
||||
pTarget.Typename = "UnsignedInteger";
|
||||
pTarget.Value.UnsignedInteger = (.)Math.Clamp(this, uint32.MinValue, uint32.MaxValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type != .UnsignedInteger)
|
||||
return false;
|
||||
this = (.)Math.Clamp(pInput.Value.UnsignedInteger, uint.MaxValue, uint.MinValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt8 : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .UnsignedInteger;
|
||||
pTarget.Typename = "UnsignedInteger";
|
||||
pTarget.Value.UnsignedInteger = (.)Math.Clamp(this, uint8.MinValue, uint8.MaxValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type != .UnsignedInteger)
|
||||
return false;
|
||||
this = (.)Math.Clamp(pInput.Value.UnsignedInteger, uint8.MaxValue, uint8.MinValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt16 : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .UnsignedInteger;
|
||||
pTarget.Typename = "UnsignedInteger";
|
||||
pTarget.Value.UnsignedInteger = (.)Math.Clamp(this, uint16.MinValue, uint16.MaxValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type != .UnsignedInteger)
|
||||
return false;
|
||||
this = (.)Math.Clamp(pInput.Value.UnsignedInteger, uint16.MaxValue, uint16.MinValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt32 : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .UnsignedInteger;
|
||||
pTarget.Typename = "UnsignedInteger";
|
||||
pTarget.Value.UnsignedInteger = (.)Math.Clamp(this, uint32.MinValue, uint32.MaxValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type != .UnsignedInteger)
|
||||
return false;
|
||||
this = (.)Math.Clamp(pInput.Value.UnsignedInteger, uint32.MaxValue, uint32.MinValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt64 : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .BigUnsignedInteger;
|
||||
pTarget.Typename = "BigUnsignedInteger";
|
||||
pTarget.Value.BigUnsignedInteger = (.)Math.Clamp(this, uint64.MinValue, uint64.MaxValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type != .BigUnsignedInteger)
|
||||
return false;
|
||||
this = (.)Math.Clamp(pInput.Value.BigUnsignedInteger, uint64.MaxValue, uint64.MinValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension Char8 : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .Text;
|
||||
pTarget.Typename = "Text";
|
||||
pTarget.Value.Text = new String()..Append(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type == .Text)
|
||||
this = pInput.Value.Text[0];
|
||||
else if(pInput.Type == .Line)
|
||||
this = pInput.Value.Line[0];
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension Char16 : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .Text;
|
||||
pTarget.Typename = "Text";
|
||||
pTarget.Value.Text = new String()..Append(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type == .Text)
|
||||
this = pInput.Value.Text[0];
|
||||
else if(pInput.Type == .Line)
|
||||
this = pInput.Value.Line[0];
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension Char32 : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .Text;
|
||||
pTarget.Typename = "Text";
|
||||
pTarget.Value.Text = new String()..Append(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type == .Text)
|
||||
this = pInput.Value.Text[0];
|
||||
else if(pInput.Type == .Line)
|
||||
this = pInput.Value.Line[0];
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension Float : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .Number;
|
||||
pTarget.Typename = "Number";
|
||||
pTarget.Value.Number = (.)this;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type != .Number)
|
||||
return false;
|
||||
this = (.)pInput.Value.Number;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension Double : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .BigNumber;
|
||||
pTarget.Typename = "BigNumber";
|
||||
pTarget.Value.BigNumber = (.)this;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput) mut
|
||||
{
|
||||
if(pInput.Type != .BigNumber)
|
||||
return false;
|
||||
this = (.)pInput.Value.BigNumber;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension String : IBofaParseable
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
if(this.Contains('\n'))
|
||||
{
|
||||
pTarget.Type = .Text;
|
||||
pTarget.Typename = "Text";
|
||||
pTarget.Value.Text = new .(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
pTarget.Type = .Line;
|
||||
pTarget.Typename = "Line";
|
||||
pTarget.Value.Line = this;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput)
|
||||
{
|
||||
this.Clear();
|
||||
if(pInput.Type == .Line)
|
||||
this.Append(pInput.Value.Line);
|
||||
else if(pInput.Type == .Text)
|
||||
this.Append(pInput.Value.Text);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace System.Collections
|
||||
{
|
||||
extension List<T> : IBofaParseable where T : IBofaParseable where T : new where T : delete where T : class
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .Array;
|
||||
pTarget.Typename = "Array";
|
||||
pTarget.Value.Array = new .(this.Count);
|
||||
for(var i in this)
|
||||
{
|
||||
Bofa toAdd = new .();
|
||||
toAdd.[Friend]_line = new .(@i.Index);
|
||||
toAdd.Name = toAdd.[Friend]_line;
|
||||
if(!i.Serialize(toAdd))
|
||||
{
|
||||
delete toAdd;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
pTarget.Value.Array.Add(toAdd);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput)
|
||||
{
|
||||
if(pInput.Type != .Array)
|
||||
return false;
|
||||
for(var i in pInput.Value.Array)
|
||||
{
|
||||
var toParse = new T();
|
||||
if(!toParse.Deserialize(i))
|
||||
delete toParse;
|
||||
else
|
||||
this.Add((.)toParse);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension List<T> : IBofaParseable where T : IBofaParseable where T : struct where T : new
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .Array;
|
||||
pTarget.Typename = "Array";
|
||||
pTarget.Value.Array = new .(this.Count);
|
||||
for(var i in this)
|
||||
{
|
||||
Bofa toAdd = new .();
|
||||
toAdd.[Friend]_line = new .(@i.Index);
|
||||
toAdd.Name = toAdd.[Friend]_line;
|
||||
if(!i.Serialize(toAdd))
|
||||
{
|
||||
delete toAdd;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
pTarget.Value.Array.Add(toAdd);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput)
|
||||
{
|
||||
if(pInput.Type != .Array)
|
||||
return false;
|
||||
for(var i in pInput.Value.Array)
|
||||
{
|
||||
var toParse = T();
|
||||
if(toParse.Deserialize(i))
|
||||
this.Add((.)toParse);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension Dictionary<TKey, TValue> : IBofaParseable where TKey : String where TValue : IBofaParseable where TValue : new where TValue : delete where TValue : class
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .Object;
|
||||
pTarget.Typename = "Object";
|
||||
pTarget.Value.Object = new .();
|
||||
for(var i in this)
|
||||
{
|
||||
Bofa toAdd = new .();
|
||||
toAdd.[Friend]_line = new .(@i.Key);
|
||||
toAdd.Name = toAdd.[Friend]_line;
|
||||
if(!i.value.Serialize(toAdd))
|
||||
{
|
||||
delete toAdd;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
pTarget.Value.Array.Add(toAdd);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput)
|
||||
{
|
||||
if(pInput.Type != .Object)
|
||||
return false;
|
||||
for(var i in pInput.Value.Object)
|
||||
{
|
||||
var toParse = new TValue();
|
||||
if(toParse.Deserialize(i.value))
|
||||
this.Add(new .(i.key), toParse);
|
||||
else
|
||||
delete toParse;
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
extension Dictionary<TKey, TValue> : IBofaParseable where TKey : String where TValue : IBofaParseable where TValue : new where TValue : struct
|
||||
{
|
||||
public bool Serialize(Bofa.Bofa pTarget)
|
||||
{
|
||||
pTarget.Type = .Object;
|
||||
pTarget.Typename = "Object";
|
||||
pTarget.Value.Object = new .();
|
||||
for(var i in this)
|
||||
{
|
||||
Bofa toAdd = new .();
|
||||
toAdd.[Friend]_line = new .(@i.Key);
|
||||
toAdd.Name = toAdd.[Friend]_line;
|
||||
if(!i.value.Serialize(toAdd))
|
||||
{
|
||||
delete toAdd;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
pTarget.Value.Array.Add(toAdd);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Deserialize(Bofa.Bofa pInput)
|
||||
{
|
||||
if(pInput.Type != .Object)
|
||||
return false;
|
||||
for(var i in pInput.Value.Object)
|
||||
{
|
||||
var toParse = TValue();
|
||||
if(toParse.Deserialize(i.value))
|
||||
this.Add(new .(i.key), toParse);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
80
src/Parser/BofaParserInsert.bf
Normal file
80
src/Parser/BofaParserInsert.bf
Normal file
|
@ -0,0 +1,80 @@
|
|||
namespace Bofa;
|
||||
|
||||
using Bofa.Parser;
|
||||
|
||||
using System;
|
||||
|
||||
extension Bofa
|
||||
{
|
||||
[Inline]
|
||||
private Result<void> _Insert(ParserEntry* pToAdd)
|
||||
{
|
||||
Bofa target = this;
|
||||
while(!(pToAdd.Depth == 0 || (pToAdd.Type == .Text && pToAdd.Depth == 1)))
|
||||
{
|
||||
if(target == null || !(target.Type == .Object || target.Type == .Array))
|
||||
return .Err;
|
||||
|
||||
pToAdd.Depth--;
|
||||
target = target._lastObject;
|
||||
}
|
||||
|
||||
if(pToAdd.Type == .Text && target.Type == .Text)
|
||||
{
|
||||
target.Value.Text.Append(scope $"\n{pToAdd.Data.Text}");
|
||||
return .Ok;
|
||||
}
|
||||
else if(target.Type == .Object)
|
||||
{
|
||||
if(target.Value.Object.ContainsKey(pToAdd.Data.Bofa.Name))
|
||||
return .Err;
|
||||
target.Value.Object.Add(pToAdd.Data.Bofa.Name, pToAdd.Data.Bofa);
|
||||
target._lastObject = pToAdd.Data.Bofa;
|
||||
return .Ok;
|
||||
|
||||
}
|
||||
else if(target.Type == .Array)
|
||||
{
|
||||
target.Value.Array.Add(pToAdd.Data.Bofa);
|
||||
target._lastObject = pToAdd.Data.Bofa;
|
||||
return .Ok;
|
||||
}
|
||||
return .Err;
|
||||
|
||||
/*
|
||||
//See if we can insert and do so
|
||||
if(pToAdd.Depth == 0 || (pToAdd.Type == .Text && pToAdd.Depth == 1))
|
||||
{
|
||||
if(pToAdd.Type == .Text && Type == .Text)
|
||||
{
|
||||
Value.Text.Append(scope $"\n{pToAdd.Data.Text}");
|
||||
return .Ok;
|
||||
}
|
||||
else if(Type == .Object)
|
||||
{
|
||||
if(Value.Object.ContainsKey(pToAdd.Data.Bofa.Name))
|
||||
return .Err;
|
||||
Value.Object.Add(pToAdd.Data.Bofa.Name, pToAdd.Data.Bofa);
|
||||
_lastObject = pToAdd.Data.Bofa;
|
||||
return .Ok;
|
||||
|
||||
}
|
||||
else if(Type == .Array)
|
||||
{
|
||||
Value.Array.Add(pToAdd.Data.Bofa);
|
||||
_lastObject = pToAdd.Data.Bofa;
|
||||
return .Ok;
|
||||
}
|
||||
|
||||
return .Err; //Cannot insert here
|
||||
}
|
||||
|
||||
//Can we even go deeper
|
||||
if(_lastObject == null || !(_lastObject.Type == .Object || _lastObject.Type == .Array))
|
||||
return .Err;
|
||||
|
||||
pToAdd.Depth--;
|
||||
return _lastObject._Insert(pToAdd);
|
||||
*/
|
||||
}
|
||||
}
|
9
src/Parser/EParserEntryType.bf
Normal file
9
src/Parser/EParserEntryType.bf
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Bofa.Parser;
|
||||
|
||||
enum EParserEntryType
|
||||
{
|
||||
Bofa,
|
||||
Text,
|
||||
Empty,
|
||||
Error
|
||||
}
|
11
src/Parser/ParserEntry.bf
Normal file
11
src/Parser/ParserEntry.bf
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Bofa.Parser;
|
||||
|
||||
using System;
|
||||
|
||||
struct ParserEntry
|
||||
{
|
||||
public int64 Line;
|
||||
public int64 Depth = 0;
|
||||
public EParserEntryType Type;
|
||||
public ParserEntryUnion Data;
|
||||
}
|
10
src/Parser/ParserEntryUnion.bf
Normal file
10
src/Parser/ParserEntryUnion.bf
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Bofa.Parser;
|
||||
|
||||
using System;
|
||||
|
||||
[Union]
|
||||
struct ParserEntryUnion
|
||||
{
|
||||
public Bofa Bofa = null;
|
||||
public StringView Text;
|
||||
}
|
13
src/Serialization/BofaIncludeAttribute.bf
Normal file
13
src/Serialization/BofaIncludeAttribute.bf
Normal file
|
@ -0,0 +1,13 @@
|
|||
namespace Bofa.Serialization;
|
||||
|
||||
using System;
|
||||
|
||||
struct BofaIncludeAttribute : Attribute
|
||||
{
|
||||
public StringView Name;
|
||||
|
||||
public this(StringView pName)
|
||||
{
|
||||
Name = pName;
|
||||
}
|
||||
}
|
112
src/Serialization/BofaSerializeAttribute.bf
Normal file
112
src/Serialization/BofaSerializeAttribute.bf
Normal file
|
@ -0,0 +1,112 @@
|
|||
namespace Bofa.Serialization;
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using Bofa;
|
||||
|
||||
[AttributeUsage(.Struct | .Class)]
|
||||
struct BofaSerializeAttribute : Attribute, IOnTypeInit
|
||||
{
|
||||
[Comptime]
|
||||
public void OnTypeInit(Type type, Self* prev)
|
||||
{
|
||||
String serializeString = scope .("public bool Serialize(Bofa b)\n{\n");
|
||||
String deserializeString = scope .("public bool Deserialize(Bofa b)\n{\n");
|
||||
|
||||
serializeString.Append("""
|
||||
b.Type = .Object;
|
||||
b.Value.Object = new .();
|
||||
b.Typename = "Object";
|
||||
Bofa toAdd;
|
||||
|
||||
""");
|
||||
|
||||
deserializeString.Append("""
|
||||
if(b.Type != .Object)
|
||||
return false;
|
||||
|
||||
""");
|
||||
|
||||
|
||||
var fields = type.GetFields();
|
||||
for(var f in fields)
|
||||
{
|
||||
if(!f.IsPublic && !f.HasCustomAttribute<BofaIncludeAttribute>())
|
||||
continue;
|
||||
|
||||
bool hasIFace = false;
|
||||
for(var i in f.FieldType.Interfaces)
|
||||
if(i == typeof(IBofaParseable))
|
||||
hasIFace = true;
|
||||
|
||||
//Hardcoded stuff
|
||||
if(f.FieldType == typeof(int))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(int8))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(int16))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(int32))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(int64))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(uint))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(uint8))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(uint16))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(uint32))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(uint64))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(char8))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(char16))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(char32))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(float))
|
||||
hasIFace = true;
|
||||
else if(f.FieldType == typeof(double))
|
||||
hasIFace = true;
|
||||
|
||||
if(!hasIFace)
|
||||
continue;
|
||||
|
||||
|
||||
StringView name;
|
||||
name = f.Name;
|
||||
if(f.HasCustomAttribute<BofaIncludeAttribute>() && f.GetCustomAttribute<BofaIncludeAttribute>() case .Ok(let attr))
|
||||
name = attr.Name;
|
||||
|
||||
serializeString.Append(scope $"""
|
||||
toAdd = new .();
|
||||
toAdd.Name = "{name}";
|
||||
if(!{f.Name}.Serialize(toAdd))
|
||||
\{
|
||||
delete toAdd;
|
||||
return false;
|
||||
\}
|
||||
else
|
||||
b.Value.Object.Add("{name}", toAdd);
|
||||
""");
|
||||
|
||||
deserializeString.Append(scope $"""
|
||||
if(!b.Value.Object.ContainsKey("{name}"))
|
||||
return false;
|
||||
if(!{f.Name}.Deserialize(b.Value.Object["{name}"]))
|
||||
return false;
|
||||
""");
|
||||
|
||||
}
|
||||
|
||||
|
||||
serializeString.Append("\n\treturn true;\n}\n");
|
||||
deserializeString.Append("\n\treturn true;\n}\n");
|
||||
Compiler.EmitTypeBody(type, serializeString);
|
||||
Compiler.EmitTypeBody(type, deserializeString);
|
||||
Compiler.EmitAddInterface(type, typeof(IBofaParseable));
|
||||
}
|
||||
}
|
11
src/Serialization/IBofaParseable.bf
Normal file
11
src/Serialization/IBofaParseable.bf
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Bofa.Serialization;
|
||||
|
||||
using System;
|
||||
|
||||
interface IBofaParseable
|
||||
{
|
||||
///Serializes the current state of a bofa object, may allocate subobjects to fill
|
||||
public bool Serialize(Bofa pTarget) mut;
|
||||
///Attempt to restore an object from the bofa input state
|
||||
public bool Deserialize(Bofa pInput) mut;
|
||||
}
|
136
src/Testing/Default.bf
Normal file
136
src/Testing/Default.bf
Normal file
|
@ -0,0 +1,136 @@
|
|||
namespace Bofa.Testing;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections;
|
||||
|
||||
class Default
|
||||
{
|
||||
[Test(Name="Parsing all Types")]
|
||||
public static void Parsing_1()
|
||||
{
|
||||
|
||||
String content = """
|
||||
l one line of text
|
||||
b bool true
|
||||
# saoidsaodsad
|
||||
t text goes here
|
||||
- Text addendum
|
||||
n tone 12.5
|
||||
#husdhfiudshfds
|
||||
bn biggie 65645645645.6
|
||||
i int 234345
|
||||
|
||||
|
||||
bi bint 38432847329847329
|
||||
o object
|
||||
b content true
|
||||
a array
|
||||
b content true
|
||||
b content true
|
||||
""";
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(content, output, errors);
|
||||
|
||||
Test.Assert(output.Count == 9);
|
||||
Test.Assert(errors.Count == 0);
|
||||
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
}
|
||||
|
||||
[Test(Name="Parsing Number")]
|
||||
public static void Parsing_2()
|
||||
{
|
||||
|
||||
String content = """
|
||||
n number 1.0
|
||||
n number_1 345
|
||||
bn bignumber 2132432432.56564
|
||||
i integer 21324
|
||||
bi biginteger 565765765765765
|
||||
""";
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(content, output, errors);
|
||||
|
||||
Test.Assert(output.Count == 5);
|
||||
Test.Assert(errors.Count == 0);
|
||||
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
}
|
||||
|
||||
[Test(Name="Parsing Multiline Text")]
|
||||
public static void Parsing_3()
|
||||
{
|
||||
|
||||
String content = """
|
||||
t Multiline text
|
||||
- several lines
|
||||
- can be added to a single thing
|
||||
- this does mean
|
||||
- that stuff has to be appended
|
||||
- but thats fine
|
||||
""";
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(content, output, errors);
|
||||
|
||||
Test.Assert(output.Count == 1);
|
||||
Test.Assert(errors.Count == 0);
|
||||
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
}
|
||||
|
||||
[Test(Name="Parsing Objects")]
|
||||
public static void Parsing_4()
|
||||
{
|
||||
|
||||
String content = """
|
||||
o object
|
||||
n member 1
|
||||
b other true
|
||||
|
||||
|
||||
o obj
|
||||
o deeper
|
||||
o even deeper
|
||||
n member 1
|
||||
""";
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(content, output, errors);
|
||||
|
||||
Test.Assert(output.Count == 2);
|
||||
Test.Assert(errors.Count == 0);
|
||||
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
}
|
||||
|
||||
[Test(Name="Parsing Dublicate Key")]
|
||||
public static void Parsing_5()
|
||||
{
|
||||
|
||||
String content = """
|
||||
n number 1
|
||||
n number 2
|
||||
|
||||
o obj
|
||||
n member 1
|
||||
n member 2
|
||||
""";
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(content, output, errors);
|
||||
|
||||
Test.Assert(output.Count == 2);
|
||||
Test.Assert(errors.Count == 2);
|
||||
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
}
|
||||
}
|
45
src/Testing/Fuzzing.bf
Normal file
45
src/Testing/Fuzzing.bf
Normal file
|
@ -0,0 +1,45 @@
|
|||
namespace Bofa.Testing;
|
||||
|
||||
using Bofa;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
class Fuzzing
|
||||
{
|
||||
[Test(Name = "Fuzzing - 10 * 1000000 random characters")]
|
||||
public static void Fuzzin_Random()
|
||||
{
|
||||
for (int i < 10)
|
||||
{
|
||||
String toParse = new .(10 * 1500);
|
||||
for (int ii < 250000)
|
||||
toParse.Append(cast!<uint8[4]>(gRand.NextI32()));
|
||||
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(toParse, output, errors);
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
delete toParse;
|
||||
}
|
||||
}
|
||||
|
||||
[Test(Name = "Fuzzing - 10 * 10000000 random characters")]
|
||||
public static void Fuzzin_Random_Large()
|
||||
{
|
||||
for (int i < 10)
|
||||
{
|
||||
String toParse = new .(10 * 1500);
|
||||
for (int ii < 2500000)
|
||||
toParse.Append(cast!<uint8[4]>(gRand.NextI32()));
|
||||
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(toParse, output, errors);
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
delete toParse;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
namespace Bofa.Testing.Parsing;
|
||||
|
||||
class T_Bofa
|
||||
{
|
||||
//TODO: Add some testcases that cover multiple lines and insertions
|
||||
//Such as texts, objects and arrays
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
namespace Bofa.Testing.Parsing;
|
||||
|
||||
class T_BofaError
|
||||
{
|
||||
//TODO; add some testcases that fail the parser
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
219
src/Testing/Profiling.bf
Normal file
219
src/Testing/Profiling.bf
Normal file
|
@ -0,0 +1,219 @@
|
|||
namespace Bofa.Testing;
|
||||
|
||||
using Bofa;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
class Profiling
|
||||
{
|
||||
[Test(Name = "Profiling: normal 1000 * 15")]
|
||||
public static void Profiling_Normal()
|
||||
{
|
||||
for (int i < 1000)
|
||||
{
|
||||
|
||||
String toParse = scope .();
|
||||
toParse.Append(scope $"""
|
||||
l one line of text
|
||||
b bool true
|
||||
# saoidsaodsad
|
||||
t text goes here
|
||||
- Text addendum
|
||||
n tone 12.5
|
||||
husdhfiudshfds
|
||||
bn biggie 65645645645.6
|
||||
i int 234345
|
||||
bi bint 38432847329847329
|
||||
o object
|
||||
b content true
|
||||
a array
|
||||
b content true
|
||||
b content true
|
||||
|
||||
""");
|
||||
|
||||
|
||||
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(toParse, output, errors);
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
}
|
||||
}
|
||||
|
||||
[Test(Name = "Profiling: normal 10 * 1500")]
|
||||
public static void Profiling_Normal_Medium()
|
||||
{
|
||||
for (int i < 10)
|
||||
{
|
||||
String toParse = new .(10 * 1500);
|
||||
for (int ii < 100)
|
||||
{
|
||||
toParse.Append(scope $"""
|
||||
l one{ii} line of text
|
||||
b bool{ii} true
|
||||
# saoidsaodsad
|
||||
t text{ii} goes here
|
||||
- Text addendum
|
||||
n tone{ii} 12.5
|
||||
#husdhfiudshfds
|
||||
bn biggie{ii} 65645645645.6
|
||||
i int{ii} 234345
|
||||
|
||||
|
||||
bi bint{ii} 38432847329847329
|
||||
o object{ii}
|
||||
b content true
|
||||
a array{ii}
|
||||
b content true
|
||||
b content true
|
||||
|
||||
""");
|
||||
}
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(toParse, output, errors);
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
delete toParse;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Test(Name = "Profiling: normal 1 * 300000")]
|
||||
public static void Profiling_Normal_Large()
|
||||
{
|
||||
String toParse = new .(10 * 150000);
|
||||
for (int ii < 20000)
|
||||
{
|
||||
toParse.Append(scope $"""
|
||||
l one{ii} line of text
|
||||
b bool{ii} true
|
||||
# saoidsaodsad
|
||||
t text{ii} goes here
|
||||
- Text addendum
|
||||
n tone{ii} 12.5
|
||||
#husdhfiudshfds
|
||||
bn biggie{ii} 65645645645.6
|
||||
i int{ii} 234345
|
||||
|
||||
|
||||
bi bint{ii} 38432847329847329
|
||||
o object{ii}
|
||||
b content true
|
||||
a array{ii}
|
||||
b content true
|
||||
b content true
|
||||
|
||||
""");
|
||||
}
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(toParse, output, errors);
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
delete toParse;
|
||||
}
|
||||
|
||||
[Test(Name = "Profiling: Depth testing 100 * 100")]
|
||||
public static void Profiling_Depth_Testing()
|
||||
{
|
||||
for (int i < 100)
|
||||
{
|
||||
String toParse = scope .();
|
||||
for(int ii < 100)
|
||||
{
|
||||
toParse.Append(' ', ii);
|
||||
toParse.Append(scope $"o obj\n");
|
||||
}
|
||||
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(toParse, output, errors);
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
}
|
||||
}
|
||||
[Test(Name = "Profiling: Depth testing 100 * 1000")]
|
||||
public static void Profiling_Depth_Testing_Medium()
|
||||
{
|
||||
for (int i < 100)
|
||||
{
|
||||
String toParse = scope .();
|
||||
for(int ii < 1000)
|
||||
{
|
||||
toParse.Append(' ', ii);
|
||||
toParse.Append(scope $"o obj\n");
|
||||
}
|
||||
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(toParse, output, errors);
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
}
|
||||
}
|
||||
|
||||
[Test(Name = "Profiling: Depth testing 10 * 5000")]
|
||||
public static void Profiling_Depth_Testing_Large()
|
||||
{
|
||||
for (int i < 10)
|
||||
{
|
||||
String toParse = scope .();
|
||||
for(int ii < 5000)
|
||||
{
|
||||
toParse.Append(' ', ii);
|
||||
toParse.Append(scope $"o obj\n");
|
||||
}
|
||||
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(toParse, output, errors);
|
||||
output["obj"].DeepDeletion();
|
||||
delete output;
|
||||
delete errors;
|
||||
}
|
||||
}
|
||||
|
||||
[Test(Name = "Profiling: Texts 10 * 1000")]
|
||||
public static void Profiling_Texts()
|
||||
{
|
||||
for (int i < 10)
|
||||
{
|
||||
String toParse = new .(10 * 1500);
|
||||
toParse.Append("t text goes here");
|
||||
for (int ii < 1000)
|
||||
toParse.Append("- Addendum");
|
||||
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(toParse, output, errors);
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
delete toParse;
|
||||
}
|
||||
}
|
||||
|
||||
[Test(Name = "Profiling: Large Texts 10 * 100000")]
|
||||
public static void Profiling_Texts_Large()
|
||||
{
|
||||
for (int i < 10)
|
||||
{
|
||||
String toParse = new .(10 * 1500);
|
||||
toParse.Append("t text goes here");
|
||||
for (int ii < 100000)
|
||||
toParse.Append("- Addendum");
|
||||
|
||||
Dictionary<StringView, Bofa> output = new .();
|
||||
List<int64> errors = new .();
|
||||
BofaParser.Parse(toParse, output, errors);
|
||||
DeleteDictionaryAndValues!(output);
|
||||
delete errors;
|
||||
delete toParse;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
27
src/Testing/Serialization.bf
Normal file
27
src/Testing/Serialization.bf
Normal file
|
@ -0,0 +1,27 @@
|
|||
namespace Bofa.Testing;
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
using Bofa;
|
||||
using Bofa.Serialization;
|
||||
|
||||
[BofaSerialize]
|
||||
class Serialization
|
||||
{
|
||||
public int anInteger = 1;
|
||||
public String aString = "Value";
|
||||
public List<String> listOfStrings = new .() ~ DeleteContainerAndItems!(_);
|
||||
|
||||
[Test]
|
||||
public static void Test()
|
||||
{
|
||||
Serialization s = scope .();
|
||||
s.listOfStrings.Add(new .("dfdsfdsfdsf"));
|
||||
Bofa b = scope .();
|
||||
b.Name = "s";
|
||||
s.Serialize(b);
|
||||
|
||||
}
|
||||
|
||||
}
|
78
src/bofa.bf
78
src/bofa.bf
|
@ -1,78 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
|
@ -1,287 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,15 +1,17 @@
|
|||
namespace Bofa;
|
||||
|
||||
enum eBofaType
|
||||
enum EBofaType
|
||||
{
|
||||
Number,
|
||||
Boolean,
|
||||
Line,
|
||||
BigNumber,
|
||||
Integer,
|
||||
BigInteger,
|
||||
Text,
|
||||
Custom,
|
||||
Array,
|
||||
Object
|
||||
Line, //String without \n
|
||||
Text, //String with \n
|
||||
Number, //32bit floating point number
|
||||
BigNumber, //64bit floating point number
|
||||
Integer, //32bit signed integer
|
||||
BigInteger, //64bit signed integer
|
||||
UnsignedInteger, //32bit unsigned integer
|
||||
BigUnsignedInteger, //64bit unsigned integer
|
||||
Boolean, //8bit true or false
|
||||
Object, // Key-Value container
|
||||
Array, //Numeric container
|
||||
Custom //String with a type attached to it
|
||||
}
|
Loading…
Add table
Reference in a new issue