Working on Prefab Parser updates

This commit is contained in:
2023-03-29 11:28:04 -07:00
parent 846d881b03
commit ec8ec5bbb4
16 changed files with 470 additions and 356 deletions

View File

@ -13,6 +13,9 @@ target_sources(prefabtool
${DAWN_SHARED_SOURCES}
${DAWN_TOOL_SOURCES}
PrefabTool.cpp
PrefabParser.cpp
PrefabRegistry.cpp
PrefabComponentParser.cpp
)
# Includes

View File

@ -0,0 +1,10 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PrefabParser.hpp"
namespace Dawn {
}

View File

@ -0,0 +1,132 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PrefabComponentParser.hpp"
using namespace Dawn;
std::vector<std::string> PrefabComponentParser::getRequiredAttributes() {
return { };
}
std::map<std::string, std::string> PrefabComponentParser::getOptionalAttributes() {
return { { "ref", "" } };
}
int32_t PrefabComponentParser::onParse(
Xml *node,
std::map<std::string, std::string> values,
struct PrefabComponent *out,
std::string *error
) {
// Check if values contains key "ref" and has a string of size more than 0
if(values.find("ref") != values.end() && values["ref"].size() > 0) {
out->ref = values["ref"];
}
// Get the ruleset.
auto ruleset = out->registry->getRuleset(node->node);
if(ruleset.name.size() == 0) {
*error = "Unknown prefab node type: " + node->node;
return 1;
}
out->include = ruleset.include;
out->type = node->node;
// Define parser types here.
auto rawParser = [&](std::string v){
return v;
};
auto stringParser = [&](std::string v){
return "\"" + v + "\"";
};
auto floatParser = [&](std::string v){
v = stringTrim(v);
// Make sure number contains decimal
if(v.find(".") == std::string::npos) {
v += ".0";
}
// Make sure number contains a number before the decimal
if(v.find(".") == 0) {
v = "0" + v;
}
// Make sure ends with f
if(v.find("f") == std::string::npos) {
v += "f";
}
return v;
};
auto vec2Parser = [&](std::string v) {
// Split string by comma into two strings that we pass into float
auto split = stringSplit(v, ",");
if(split.size() != 2) {
*error = "Invalid vec2 value: " + v;
return std::string("");
}
return std::string(
"glm::vec2(" + floatParser(split[0]) + ", " + floatParser(split[1]) + ")"
);
};
auto vec3Parser = [&](std::string v) {
// Split string by comma into two strings that we pass into float
auto split = stringSplit(v, ",");
if(split.size() != 3) {
*error = "Invalid vec3 value: " + v;
return std::string("");
}
return std::string(
"glm::vec3(" + floatParser(split[0]) + ", " + floatParser(split[1]) + ", " + floatParser(split[2]) + ")"
);
};
auto colorParser = rawParser;
// Iterate all the optional attributes and store within the out if present
auto itOptional = ruleset.optional.begin();
while(itOptional != ruleset.optional.end()) {
// Determine the parser to use
auto name = itOptional->first;
auto type = itOptional->second;
std::function<std::string(std::string)> parser = rawParser;
if(type.find("string") != std::string::npos) {
parser = stringParser;
} else if(type.find("float") != std::string::npos) {
parser = floatParser;
} else if(type.find("Color") != std::string::npos) {
parser = colorParser;
} else if(type.find("vec2") != std::string::npos) {
parser = vec2Parser;
} else if(type.find("vec3") != std::string::npos) {
parser = vec3Parser;
} else if(type.find("*") == (type.size() - 1)) {
type = type.substr(0, type.size() - 1);
parser = rawParser;
} else if(name[0] == '*') {
name = name.substr(1, name.size());
parser = rawParser;
} else {
*error = "Unkown parser for type: " + type + "::" + name;
return 1;
}
if(node->attributes.find(name) != node->attributes.end()) {
auto raw = node->attributes[name];
out->values[name] = parser(raw);
if(error->size() != 0) return 1;
}
++itOptional;
}
return 0;
}

View File

@ -0,0 +1,30 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PrefabRegistry.hpp"
namespace Dawn {
struct PrefabComponent {
struct PrefabRegistry *registry;
std::string include;
std::string type;
std::map<std::string, std::string> values;
std::string ref;
};
class PrefabComponentParser : public XmlParser<struct PrefabComponent> {
protected:
std::vector<std::string> getRequiredAttributes() override;
std::map<std::string, std::string> getOptionalAttributes() override;
int32_t onParse(
Xml *node,
std::map<std::string, std::string> values,
struct PrefabComponent *out,
std::string *error
) override;
};
}

View File

@ -0,0 +1,44 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "PrefabParser.hpp"
using namespace Dawn;
std::vector<std::string> PrefabParser::getRequiredAttributes() {
return { "name", "type" };
}
std::map<std::string, std::string> PrefabParser::getOptionalAttributes() {
return { };
}
int32_t PrefabParser::onParse(
Xml *node,
std::map<std::string, std::string> values,
struct Prefab *out,
std::string *error
) {
out->name = values["name"];
out->type = values["type"];
auto itChildren = node->children.begin();
while(itChildren != node->children.end()) {
// Parse child nodes, they may be components or not
auto c = *itChildren;
if(c->node == "child") {
std::cout << "Child detected" << std::endl;
} else {
struct PrefabComponent component;
component.registry = out->registry;
auto ret = (PrefabComponentParser()).parse(*itChildren, &component, error);
if(ret != 0) return ret;
out->components.push_back(component);
}
++itChildren;
}
return 0;
}

View File

@ -0,0 +1,28 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PrefabComponentParser.hpp"
namespace Dawn {
struct Prefab {
struct PrefabRegistry *registry;
std::string name;
std::string type;
std::vector<struct PrefabComponent> components;
};
class PrefabParser : public XmlParser<struct Prefab> {
protected:
std::vector<std::string> getRequiredAttributes() override;
std::map<std::string, std::string> getOptionalAttributes() override;
int32_t onParse(
Xml *node,
std::map<std::string, std::string> values,
struct Prefab *out,
std::string *error
) override;
};
}

View File

@ -0,0 +1,115 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "PrefabRegistry.hpp"
using namespace Dawn;
struct PrefabComponentParserRuleset PrefabRegistry::getRuleset(std::string type) {
std::vector<std::string> pathsToScan;
pathsToScan.push_back(this->sources);
auto it = pathsToScan.begin();
while(it != pathsToScan.end()) {
auto str = *it;
pathsToScan.erase(it);
Directory dir = Directory(str);
auto children = dir.readDirectory();
auto itChildren = children.begin();
while(itChildren != children.end()) {
if(itChildren->second == DIRECTORY_CHILD_TYPE_DIRECTORY) {
pathsToScan.push_back(str + "/" + itChildren->first);
++itChildren;
continue;
}
if(itChildren->first != type+".hpp") {
++itChildren;
continue;
}
// Load ruleset
std::string data;
File file(str + "/" + itChildren->first);
if(!file.readString(&data)) {
std::cout << "Failed to read ruleset file!" << std::endl;
return { .name = "" };
}
// Begin scanning contentsr
// std::string include = this->sources;
auto toRemove = File::normalizeSlashes(this->sources + FILE_PATH_SEP);
auto includePath = file.filename.substr(toRemove.size(), file.filename.size() - toRemove.size());
// Now locate the first subdir since we don't want to include the root path (e.g. dawn, dawnrose, etc)
auto firstSlash = includePath.find(FILE_PATH_SEP);
if(firstSlash != std::string::npos) {
includePath = includePath.substr(firstSlash + 1, includePath.size() - firstSlash - 1);
}
struct PrefabComponentParserRuleset ruleset;
// Replace all file seps with slashes
size_t pos = 0;
while ((pos = includePath.find(FILE_PATH_SEP, pos)) != std::string::npos) {
includePath.replace(pos, 1, "/");
pos += 1;
}
ruleset.include = includePath;
ruleset.name = type;
// Find each instance of "@optional" when it's used within a comment
// e.g. // @optional or /* @optional */ in the string data.
std::regex regex("^\\s*(?:\\/\\/|\\/\\*){1}\\s*\\@optional\\s*(?:\\*\\/)?\\s*$", std::regex_constants::ECMAScript);
std::sregex_iterator it(data.begin(), data.end(), regex);
std::sregex_iterator end;
while(it != end) {
// Find the next ";"
auto endPos = data.find(";", it->position() + it->length());
if(endPos == std::string::npos) {
std::cout << "Failed to find end of line for optional attribute!" << std::endl;
return { .name = "" };
}
// Go backwards see if there's an equals sign after the match but before endPos
auto equalsPos = data.rfind("=", endPos);
// If there's an equals sign, we search backwards from there,
// otherwise we search backwards from the ;
auto varStart = it->position() + it->length() + 1;
size_t lastSearchPos = (
(equalsPos == std::string::npos || equalsPos <= varStart) ?
endPos :
equalsPos
);
// Now we have our string
auto varLength = lastSearchPos - varStart;
auto variableString = data.substr(varStart, varLength);
// Now (should) be able to extract the type;
std::regex regex2("^\\s*(?:[\\S]+<)?([\\w*:_\\s]+)(?:[\\S]+)? (\\**[\\w]+)\\s*$", std::regex_constants::ECMAScript);
std::smatch match;
if(!std::regex_search(variableString, match, regex2)) {
std::cout << "Failed to extract type and name from variable string! " << variableString << std::endl;
return { .name = "" };
}
// Now we have our type and name
auto type = match[1].str();
auto name = match[2].str();
ruleset.optional[name] = type;
++it;
}
return ruleset;
++itChildren;
}
it = pathsToScan.begin();
};
return { .name = "" };
}

View File

@ -0,0 +1,32 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/DawnTool.hpp"
#include "util/Directory.hpp"
#include "util/XmlParser.hpp"
#include "util/CodeGen.hpp"
#include "util/string.hpp"
#include <regex>
namespace Dawn {
struct PrefabComponentParserRuleset {
std::string name;
std::string include;
std::map<std::string, std::string> optional;
};
struct PrefabRegistry {
std::string sources;
/**
* Gets a prefab component ruleset for a specific scene item component type.
*
* @param type Scene Item Component Type to get ruleset for.
* @return The found ruleset, or an empty ruleset if not found.
*/
struct PrefabComponentParserRuleset getRuleset(std::string type);
};
}

View File

@ -7,109 +7,6 @@
using namespace Dawn;
struct PrefabComponentParserRuleset PrefabRegistry::getRuleset(std::string type) {
std::vector<std::string> pathsToScan;
pathsToScan.push_back(this->sources);
auto it = pathsToScan.begin();
while(it != pathsToScan.end()) {
auto str = *it;
pathsToScan.erase(it);
Directory dir = Directory(str);
auto children = dir.readDirectory();
auto itChildren = children.begin();
while(itChildren != children.end()) {
if(itChildren->second == DIRECTORY_CHILD_TYPE_DIRECTORY) {
pathsToScan.push_back(str + "/" + itChildren->first);
} else {
if(itChildren->first == type+".hpp") {
// Load ruleset
std::string data;
File file(str + "/" + itChildren->first);
if(!file.readString(&data)) {
std::cout << "Failed to read ruleset file!" << file.filename << std::endl;
} else {
// Begin scanning contentsr
// std::string include = this->sources;
auto toRemove = File::normalizeSlashes(this->sources + FILE_PATH_SEP);
auto includePath = file.filename.substr(toRemove.size(), file.filename.size() - toRemove.size());
// Now locate the first subdir since we don't want to include the root path (e.g. dawn, dawnrose, etc)
auto firstSlash = includePath.find(FILE_PATH_SEP);
if(firstSlash != std::string::npos) {
includePath = includePath.substr(firstSlash + 1, includePath.size() - firstSlash - 1);
}
struct PrefabComponentParserRuleset ruleset;
// Replace all file seps with slashes
size_t pos = 0;
while ((pos = includePath.find(FILE_PATH_SEP, pos)) != std::string::npos) {
includePath.replace(pos, 1, "/");
pos += 1;
}
ruleset.include = includePath;
ruleset.name = type;
// Find each instance of "@optional" when it's used within a comment
// e.g. // @optional or /* @optional */ in the string data.
std::regex regex("^(\\s)*(\\/\\/|\\/\\*\\s*)(\\@optional)(\\s*\\*\\/)?(\\s)$", std::regex_constants::ECMAScript);
std::sregex_iterator it(data.begin(), data.end(), regex);
std::sregex_iterator end;
while(it != end) {
// Find the next ";"
auto endPos = data.find(";", it->position() + it->length());
if(endPos == std::string::npos) {
std::cout << "Failed to find end of line for optional attribute!" << std::endl;
return { .name = "" };
}
// Go backwards see if there's an equals sign after the match but before endPos
auto equalsPos = data.rfind("=", endPos);
// If there's an equals sign, we search backwards from there,
// otherwise we search backwards from the ;
auto varStart = it->position() + it->length() + 1;
size_t lastSearchPos = (
(equalsPos == std::string::npos || equalsPos <= varStart) ?
endPos :
equalsPos
);
// Now we have our string
auto varLength = lastSearchPos - varStart;
auto variableString = data.substr(varStart, varLength);
// Now (should) be able to extract the type;
std::regex regex2("^\\s*(?:[\\S]+<)?([\\w*:_\\s]+)(?:[\\S]+)? (\\**[\\w]+)\\s*$", std::regex_constants::ECMAScript);
std::smatch match;
if(!std::regex_search(variableString, match, regex2)) {
std::cout << "Failed to extract type and name from variable string! " << variableString << std::endl;
return { .name = "" };
}
// Now we have our type and name
auto type = match[1].str();
auto name = match[2].str();
ruleset.optional[name] = type;
++it;
}
return ruleset;
}
}
}
++itChildren;
}
it = pathsToScan.begin();
};
return { .name = "" };
}
//
std::vector<std::string> PrefabTool::getRequiredFlags() {
return { "input", "output", "sources" };
}
@ -173,133 +70,6 @@ int32_t PrefabTool::start() {
//
std::vector<std::string> PrefabParser::getRequiredAttributes() {
return { "name", "type" };
}
std::map<std::string, std::string> PrefabParser::getOptionalAttributes() {
return { };
}
int32_t PrefabParser::onParse(
Xml *node,
std::map<std::string, std::string> values,
struct Prefab *out,
std::string *error
) {
out->name = values["name"];
out->type = values["type"];
auto itChildren = node->children.begin();
while(itChildren != node->children.end()) {
// Parse children as components
struct PrefabComponent component;
component.registry = out->registry;
auto ret = (PrefabComponentParser()).parse(*itChildren, &component, error);
if(ret != 0) return ret;
out->components.push_back(component);
++itChildren;
}
return 0;
}
//
std::vector<std::string> PrefabComponentParser::getRequiredAttributes() {
return { };
}
std::map<std::string, std::string> PrefabComponentParser::getOptionalAttributes() {
return { { "ref", "" } };
}
int32_t PrefabComponentParser::onParse(
Xml *node,
std::map<std::string, std::string> values,
struct PrefabComponent *out,
std::string *error
) {
// Check if values contains key "ref" and has a string of size more than 0
if(values.find("ref") != values.end() && values["ref"].size() > 0) {
out->ref = values["ref"];
}
// Get the ruleset.
auto ruleset = out->registry->getRuleset(node->node);
if(ruleset.name.size() == 0) {
*error = "Unknown prefab node type: " + node->node;
return 1;
}
out->include = ruleset.include;
out->type = node->node;
// Define parser types here.
auto rawParser = [&](std::string v){
return v;
};
auto stringParser = [&](std::string v){
return "\"" + v + "\"";
};
auto floatParser = [&](std::string v){
// Make sure number contains decimal
if(v.find(".") == std::string::npos) {
v += ".0";
}
// Make sure number contains a number before the decimal
if(v.find(".") == 0) {
v = "0" + v;
}
// Make sure ends with f
if(v.find("f") == std::string::npos) {
v += "f";
}
return v;
};
auto colorParser = rawParser;
// Iterate all the optional attributes and store within the out if present
auto itOptional = ruleset.optional.begin();
while(itOptional != ruleset.optional.end()) {
// Determine the parser to use
auto name = itOptional->first;
auto type = itOptional->second;
std::function<std::string(std::string)> parser = rawParser;
if(type.find("string") != std::string::npos) {
parser = stringParser;
} else if(type.find("float") != std::string::npos) {
parser = floatParser;
} else if(type.find("Color") != std::string::npos) {
parser = colorParser;
} else if(type.find("*") == (type.size() - 1)) {
type = type.substr(0, type.size() - 1);
parser = rawParser;
} else if(name[0] == '*') {
name = name.substr(1, name.size());
parser = rawParser;
} else {
*error = "Unkown parser for type: " + type + "::" + name;
return 1;
}
if(node->attributes.find(name) != node->attributes.end()) {
auto raw = node->attributes[name];
out->values[name] = parser(raw);
}
++itOptional;
}
return 0;
}
//
void PrefabGen::generate(
std::vector<std::string> *out,
struct Prefab *info,
@ -335,7 +105,7 @@ void PrefabGen::generate(
if(c.ref.size() > 0) {
init = false;
name = c.ref;
line(&classInfo.publicProperties, c.type + " *" + c.ref + ";", "");
line(&classInfo.publicProperties, c.type + " *" + c.ref + " = nullptr;", "");
}
// Initialize

View File

@ -4,46 +4,9 @@
// https://opensource.org/licenses/MIT
#pragma once
#include "util/DawnTool.hpp"
#include "util/Directory.hpp"
#include "util/XmlParser.hpp"
#include "util/CodeGen.hpp"
#include <regex>
#include "PrefabParser.hpp"
namespace Dawn {
enum PrefabcComponentParserRulesetType {
STRIN
};
struct PrefabComponentParserRuleset {
std::string name;
std::string include;
std::map<std::string, std::string> optional;
};
struct PrefabRegistry {
std::string sources;
struct PrefabComponentParserRuleset getRuleset(std::string type);
};
struct PrefabComponent {
struct PrefabRegistry *registry;
std::string include;
std::string type;
std::map<std::string, std::string> values;
std::string ref;
};
struct Prefab {
struct PrefabRegistry *registry;
std::string name;
std::string type;
std::vector<struct PrefabComponent> components;
};
class PrefabTool : public DawnTool {
protected:
std::vector<std::string> getRequiredFlags() override;
@ -53,30 +16,6 @@ namespace Dawn {
int32_t start();
};
class PrefabParser : public XmlParser<struct Prefab> {
protected:
std::vector<std::string> getRequiredAttributes() override;
std::map<std::string, std::string> getOptionalAttributes() override;
int32_t onParse(
Xml *node,
std::map<std::string, std::string> values,
struct Prefab *out,
std::string *error
) override;
};
class PrefabComponentParser : public XmlParser<struct PrefabComponent> {
protected:
std::vector<std::string> getRequiredAttributes() override;
std::map<std::string, std::string> getOptionalAttributes() override;
int32_t onParse(
Xml *node,
std::map<std::string, std::string> values,
struct PrefabComponent *out,
std::string *error
) override;
};
class PrefabGen : public CodeGen {
public:
static void generate(