From 6fb9640fa9749d59a0635db5c6c5b851676c81f1 Mon Sep 17 00:00:00 2001 From: howsohazard <143410553+howsohazard@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:58:06 -0400 Subject: [PATCH] 21711: Refactors load* and store* opcodes and parameters to be more flexible and consistent, as well as corresponding API, MAJOR (#287) --- docs/index.html | 16 + docs/language.js | 26 +- src/3rd_party/rapidyaml/rapidyaml-0.7.2.hpp | 2 +- src/Amalgam/Amalgam.h | 24 +- src/Amalgam/AmalgamAPI.cpp | 32 +- src/Amalgam/AmalgamMain.cpp | 4 +- src/Amalgam/AmalgamTrace.cpp | 103 +++-- src/Amalgam/AssetManager.cpp | 424 +++++++++--------- src/Amalgam/AssetManager.h | 338 ++++++++------ src/Amalgam/Opcodes.cpp | 7 +- src/Amalgam/Opcodes.h | 10 +- src/Amalgam/amlg_code/full_test.amlg | 39 +- src/Amalgam/entity/Entity.cpp | 4 +- .../entity/EntityExternalInterface.cpp | 50 ++- src/Amalgam/entity/EntityExternalInterface.h | 11 +- src/Amalgam/evaluablenode/EvaluableNode.h | 17 + .../EvaluableNodeTreeManipulation.cpp | 1 - src/Amalgam/interpreter/Interpreter.cpp | 3 +- src/Amalgam/interpreter/Interpreter.h | 2 +- .../InterpreterOpcodesDataTypes.cpp | 47 +- .../InterpreterOpcodesEntityControl.cpp | 174 ++++--- src/Amalgam/string/StringManipulation.cpp | 2 +- test/lib_smoke_test/main.cpp | 6 +- 23 files changed, 748 insertions(+), 594 deletions(-) diff --git a/docs/index.html b/docs/index.html index 1faf8e7f..d9fbd35f 100644 --- a/docs/index.html +++ b/docs/index.html @@ -296,6 +296,7 @@

Amalgam Language Reference

Language Operators
Typical Constructs
System Commands
+ File I/O

Keywords
@@ -401,6 +402,21 @@

System Commands

Additionally, the argument vector passed in on the command line is passed in as the variable argv, with any arguments consumed by the interpreter removed. This includes the standard 0th argument which is the Amalgam script being run. The interpreter path and name are passed in as the variable interpreter.
+ +

File I/O

+ These parameters apply to load and store opcodes and API calls:
+
include_rand_seeds
If true, attempts to include random seeds when storing and loading.

+
escape_resource_name
If true, will escape any characters in the resource or file name that are not universally supported across platforms.

+
escape_contained_resource_names
If true, then for any contained entities and their resource or file paths that extend the base file path, it will escape any characters in the names that are not universally supported across platforms. This only applies to file formats where the entities are not flattened into one file.

+
transactional
If true, attempts to load and store files in a transactional manner, such that any interruption will not corrupt the file. Not applicable to all file types.

+
pretty_print
If true, then any code stored will be pretty printed.

+
sort_keys
If true, then any associative arrays will be sorted by keys.

+
flatten
If true, then will attempt to flatten all contained entities into one executable object and thus one file.

+
parallel_create
If true, will attempt use concurrency to store and load entities in parallel.

+
execute_on_load
If true, will execute the code upon load, which is required when entities are stored using flatten in order to create all of the entity structures.

+ + File formats supported are amlg, json, yaml, csv, and caml; anything not in this list will be loaded as a binary string. Note that loading from a non-'.amlg' extension will only ever provide lists, assocs, numbers, and strings. +

Metadata Files

When attempting to load an asset, whether a .amlg file or another type, the interpreter will look for a file of the same name but with the extension .madm. The .mdam extension stands for metadata of Amalgam. This file consists of simple code of an associative array where the data within is immediate values representing the metadata.
rand_seed
The random seed to apply to the entity.

diff --git a/docs/language.js b/docs/language.js index 1e80fa37..d924f534 100644 --- a/docs/language.js +++ b/docs/language.js @@ -15,7 +15,7 @@ var data = [ "output" : "*", "permissions" : "r", "new value" : "new", - "description" : "Executes system command specified by command. See system commands in later table.", + "description" : "Executes system command specified by command. See the system commands table for further information.", "example" : "(system \"exit\")" }, @@ -1343,42 +1343,34 @@ var data = [ }, { - "parameter" : "load string file_path [bool escape_filename] [string file_type]", + "parameter" : "load string resource_path [string resource_type] [assoc params]", "output" : "*", "permissions" : "r", - "description" : "Loads the data specified by the resource in string. Attempts to load the file type and parse it into appropriate data and evaluate to the corresponding code. The parameter escape_filename defaults to false, but if it is true, it will agressively escape filenames using only alphanumeric characters and the underscore, using underscore as an escape character. If file_type is specified and not null, it will use the file_type specified instead of the extension of the file_path. File formats supported are amlg, json, yaml, csv, and caml; anything not in this list will be loaded as a binary string. Note that loading from a non-'.amlg' extension will only ever provide lists, assocs, numbers, and strings.", + "description" : "Loads the data specified by the resource in string. Attempts to load the file type and parse it into appropriate data and evaluate to the corresponding code. The parameter escape_filename defaults to false, but if it is true, it will agressively escape filenames using only alphanumeric characters and the underscore, using underscore as an escape character. If resource_type is specified and not null, it will use the resource_type specified instead of the extension of the resource_path. File formats supported are amlg, json, yaml, csv, and caml; anything not in this list will be loaded as a binary string. Note that loading from a non-'.amlg' extension will only ever provide lists, assocs, numbers, and strings.", "example" : "(print (load \"my_directory/MyModule.amlg\"))" }, { - "parameter" : "load_entity string file_path [id entity] [bool escape_filename] [bool escape_contained_filenames] [string file_type]", + "parameter" : "load_entity string resource_path [id entity] [string resource_type] [bool persistent] [assoc params]", "output" : "id", "permissions" : "r", - "description" : "Loads an entity specified by the resource in string. Attempts to load the file type and parse it into appropriate data and store it in the entity specified by id, following the same id creation rules as create_entities, except that if no id is specified, it may default to a name based on the resource if available. The parameter escape_filename defaults to false, but if it is true, it will agressively escape filenames using only alphanumeric characters and the underscore, using underscore as an escape character. If escape_contained_filenames is true, which is its default, it will also escape contained entity filenames. If file_type is specified and not null, it will use the file_type specified instead of the extension of the file_path. File formats supported are amlg, json, yaml, csv, and caml; anything not in this list will be loaded as a binary string. Note that loading from a non-'.amlg' extension will only ever provide lists, assocs, numbers, and strings.", + "description" : "Loads an entity specified by the resource in string. Attempts to load the file type and parse it into appropriate data and store it in the entity specified by id, following the same id creation rules as create_entities, except that if no id is specified, it may default to a name based on the resource if available. If persistent is true, default is false, then any modifications to the entity or any entity contained within it will be written out to the resource, so that the memory and persistent storage are synchronized. Options for the file I/O are specified as key-value pairs in params. See File I/O for the file types and related params.", "example" : "(load_entity \"my_directory/MyModule.amlg\" \"MyModule\")" }, { - "parameter" : "load_persistent_entity string file_path [id entity] [bool escape_filename]", - "output" : "id", - "permissions" : "r", - "description" : "Loads an entity specified by the resource in string. Attempts to load the file type and parse it into appropriate data and store it in the entity specified by id, following the same id creation rules as create_entities. Any modifications to the entity or any entity contained within it will be written out to the resource, so that the memory and persistent storage are synchronized. The parameter escape_filename defaults to false, but if it is true, it will agressively escape filenames using only alphanumeric characters and the underscore, using underscore as an escape character. This command will escape contained filenames. The file type of a persisted entity must match the extension of the file of the main entity. File formats supported are amlg, json, yaml, csv, and caml; anything not in this list will be loaded as a binary string. Note that loading from a non-'.amlg' extension will only ever provide lists, assocs, numbers, and strings.\n\nWARNING: Loading the same file as a persistent entity in more than one place will overwrite the file each time either entity is altered, but changes will not be propogated between the entities.", - "example" : "(load_persistent_entity \"my_directory/MyModule.amlg\" \"MyModule\")" - }, - - { - "parameter" : "store string file_path * node [bool escape_filename] [string file_type] [assoc params]", + "parameter" : "store string resource_path * node [string resource_type] [assoc params]", "output" : "bool", "permissions" : "r", - "description" : "Stores the code specified by * to the resource in string. Returns true if successful, false if not. The parameter escape_filename defaults to false, but if it is true, it will agressively escape filenames using only alphanumeric characters and the underscore, using underscore as an escape character. If file_type is specified and not null, it will use the file_type specified instead of the extension of the file_path. File formats supported are amlg, json, yaml, csv, and caml; anything not in this list will be loaded as a binary string. Note that loading from a non-'.amlg' extension will only ever provide lists, assocs, numbers, and strings. If params is specified, it is an assoc that contains key-value pairs describing the format. The key \"sort_keys\" can be used to specify a boolean value, if true, then it will sort the keys, otherwise the default behavior is to emit the keys based on memory layout.", + "description" : "Stores the code specified by * to the resource in string. Returns true if successful, false if not. If resource_type is specified and not null, it will use the resource_type specified instead of the extension of the resource_path. Options for the file I/O are specified as key-value pairs in params. See File I/O for the file types and related params.", "example" : "(store \"my_directory/MyData.amlg\" (list 1 2 3))" }, { - "parameter" : "store_entity string file_path id entity [bool escape_filename] [bool escape_contained_filenames] [string file_type] [assoc params]", + "parameter" : "store_entity string resource_path id entity [string resource_type] [bool persistent] [assoc params]", "output" : "bool", "permissions" : "r", - "description" : "Stores the entity specified by the id to the resource in string. Returns true if successful, false if not. The parameter escape_filename defaults to false, but if it is true, it will agressively escape filenames using only alphanumeric characters and the underscore, using underscore as an escape character. If escape_contained_filenames is true, which is its default, it will also escape contained entity filenames. If file_type is specified and not null, it will use the file_type specified instead of the extension of the file_path. File formats supported are amlg, json, yaml, csv, and caml; anything not in this list will be loaded as a binary string. Note that loading from a non-'.amlg' extension will only ever provide lists, assocs, numbers, and strings. If params is specified, it is an assoc that contains key-value pairs describing the format. The key \"sort_keys\" can be used to specify a boolean value, if true, then it will sort the keys, otherwise the default behavior is to emit the keys based on memory layout. The key \"include_rand_seeds\" can be used when storing caml files to indicate whether random seeds will be stored, which defaults to true. The key \"parallel_create\" can be used when storing caml files to indicate whether creating entities will be performed in parallel when the caml is loaded, which defaults to false.", + "description" : "Stores the entity specified by the id to the resource in string. Returns true if successful, false if not. If resource_type is specified and not null, it will use the resource_type specified instead of the extension of the resource_path. If persistent is true, default is false, then any modifications to the entity or any entity contained within it will be written out to the resource, so that the memory and persistent storage are synchronized. Options for the file I/O are specified as key-value pairs in params. See File I/O for the file types and related params.", "example" : "(store_entity \"my_directory/MyData.amlg\" \"MyData\")" }, diff --git a/src/3rd_party/rapidyaml/rapidyaml-0.7.2.hpp b/src/3rd_party/rapidyaml/rapidyaml-0.7.2.hpp index c611a0bc..775122c3 100644 --- a/src/3rd_party/rapidyaml/rapidyaml-0.7.2.hpp +++ b/src/3rd_party/rapidyaml/rapidyaml-0.7.2.hpp @@ -20201,7 +20201,7 @@ RYML_EXPORT int version_patch(); #endif /* C4_YML_EXPORT_HPP_ */ -#if defined(C4_MSVC) || defined(C4_MINGW) +#if defined(C4_MSVC) || defined(C4_MINGW) || defined(_MSC_VER) //included above: //#include #else diff --git a/src/Amalgam/Amalgam.h b/src/Amalgam/Amalgam.h index 56671ea9..45e98833 100644 --- a/src/Amalgam/Amalgam.h +++ b/src/Amalgam/Amalgam.h @@ -25,36 +25,34 @@ extern "C" }; //loads the entity specified into handle - AMALGAM_EXPORT LoadEntityStatus LoadEntity(char *handle, char *path, bool persistent, bool load_contained_entities, - bool escape_filename, bool escape_contained_filenames, char *write_log_filename, char *print_log_filename); - - //loads the entity specified into handle - //TODO 19512: deprecated - legacy method to support wrappers that can't call LoadEntity returning a LoadEntityStatus yet - AMALGAM_EXPORT bool LoadEntityLegacy(char *handle, char *path, bool persistent, bool load_contained_entities, char *write_log_filename, char *print_log_filename); + AMALGAM_EXPORT LoadEntityStatus LoadEntity(char *handle, char *path, char *file_type, + bool persistent, char *json_file_params, char *write_log_filename, char *print_log_filename); //verifies the entity specified by path. Uses LoadEntityStatus to return any errors and version AMALGAM_EXPORT LoadEntityStatus VerifyEntity(char *path); //clones the entity in handle to clone_handle - //if persistent, then path represents the location it will be persisted to - AMALGAM_EXPORT bool CloneEntity(char *handle, char *clone_handle, char *path, bool persistent, char *write_log_filename, char *print_log_filename); + //if persistent, then path, file_type, and json_file_params represent where and how it will be stored + AMALGAM_EXPORT bool CloneEntity(char *handle, char *clone_handle, char *path, + char *file_type, bool persistent, char *json_file_params, + char *write_log_filename, char *print_log_filename); //stores the entity specified by handle into path - AMALGAM_EXPORT void StoreEntity(char *handle, char *path, bool update_persistence_location = false, bool store_contained_entities = true); + AMALGAM_EXPORT void StoreEntity(char *handle, char *path, char *file_type, bool persistent, char *json_file_params); //executes label on handle - AMALGAM_EXPORT void ExecuteEntity(char *handle, char *label); + AMALGAM_EXPORT void ExecuteEntity(char *handle, char *label); //destroys the entity specified by handle - AMALGAM_EXPORT void DestroyEntity(char *handle); + AMALGAM_EXPORT void DestroyEntity(char *handle); //sets the random seed for the entity specified by handle - AMALGAM_EXPORT bool SetRandomSeed(char *handle, char *rand_seed); + AMALGAM_EXPORT bool SetRandomSeed(char *handle, char *rand_seed); //sets num_entities to the number of entities and allocates an array of string pointers for the handles loaded AMALGAM_EXPORT char **GetEntities(uint64_t *num_entities); - AMALGAM_EXPORT void SetJSONToLabel(char *handle, char *label, char *json); + AMALGAM_EXPORT void SetJSONToLabel(char *handle, char *label, char *json); AMALGAM_EXPORT wchar_t *GetJSONPtrFromLabelWide(char *handle, char *label); AMALGAM_EXPORT char *GetJSONPtrFromLabel(char *handle, char *label); diff --git a/src/Amalgam/AmalgamAPI.cpp b/src/Amalgam/AmalgamAPI.cpp index 61ad13ca..355058ef 100644 --- a/src/Amalgam/AmalgamAPI.cpp +++ b/src/Amalgam/AmalgamAPI.cpp @@ -85,27 +85,19 @@ extern "C" // api methods // ************************************ - LoadEntityStatus LoadEntity(char *handle, char *path, bool persistent, bool load_contained_entities, - bool escape_filename, bool escape_contained_filenames, char *write_log_filename, char *print_log_filename) + LoadEntityStatus LoadEntity(char *handle, char *path, char *file_type, + bool persistent, char *json_file_params, char *write_log_filename, char *print_log_filename) { std::string h(handle); std::string p(path); + std::string ft(file_type); + std::string_view params(json_file_params); std::string wlfname(write_log_filename); std::string plfname(print_log_filename); - auto status = entint.LoadEntity(h, p, persistent, load_contained_entities, escape_filename, escape_contained_filenames, wlfname, plfname); + auto status = entint.LoadEntity(h, p, ft, persistent, params, wlfname, plfname); return ConvertLoadStatusToCStatus(status); } - bool LoadEntityLegacy(char *handle, char *path, bool persistent, bool load_contained_entities, char *write_log_filename, char *print_log_filename) - { - auto status = LoadEntity(handle, path, persistent, load_contained_entities, false, false, write_log_filename, print_log_filename); - - delete[] status.message; - delete[] status.version; - - return status.loaded; - } - LoadEntityStatus VerifyEntity(char *path) { std::string p(path); @@ -113,22 +105,26 @@ extern "C" return ConvertLoadStatusToCStatus(status); } - bool CloneEntity(char *handle, char *clone_handle, char *path, bool persistent, char *write_log_filename, char *print_log_filename) + bool CloneEntity(char *handle, char *clone_handle, char *path, + char *file_type, bool persistent, char *json_file_params, char *write_log_filename, char *print_log_filename) { std::string h(handle); std::string ch(clone_handle); std::string p(path); + std::string ft(file_type); + std::string_view params(json_file_params); std::string wlfname(write_log_filename); std::string plfname(print_log_filename); - return entint.CloneEntity(h, ch, p, persistent, wlfname, plfname); + return entint.CloneEntity(h, ch, p, ft, persistent, params, wlfname, plfname); } - void StoreEntity(char *handle, char *path, bool update_persistence_location, bool store_contained_entities) + void StoreEntity(char *handle, char *path, char *file_type, bool persistent, char *json_file_params) { std::string h(handle); std::string p(path); - - entint.StoreEntity(h, p, update_persistence_location, store_contained_entities); + std::string ft(file_type); + std::string_view params(json_file_params); + entint.StoreEntity(h, p, ft, persistent, params); } void SetJSONToLabel(char *handle, char *label, char *json) diff --git a/src/Amalgam/AmalgamMain.cpp b/src/Amalgam/AmalgamMain.cpp index 58c913fa..44d1ca1a 100644 --- a/src/Amalgam/AmalgamMain.cpp +++ b/src/Amalgam/AmalgamMain.cpp @@ -235,9 +235,9 @@ PLATFORM_MAIN_CONSOLE { //run the standard amlg command line interface EntityExternalInterface::LoadEntityStatus status; + AssetManager::AssetParameters asset_params(amlg_file_to_run, "", true); std::string file_type = ""; - Entity *entity = asset_manager.LoadEntityFromResourcePath(amlg_file_to_run, file_type, - false, true, false, true, random_seed, nullptr, status); + Entity *entity = asset_manager.LoadEntityFromResource(asset_params, false, random_seed, nullptr, status); if(!status.loaded) return 1; diff --git a/src/Amalgam/AmalgamTrace.cpp b/src/Amalgam/AmalgamTrace.cpp index 07043e32..9deec45a 100644 --- a/src/Amalgam/AmalgamTrace.cpp +++ b/src/Amalgam/AmalgamTrace.cpp @@ -33,13 +33,13 @@ int32_t RunAmalgamTrace(std::istream *in_stream, std::ostream *out_stream, std:: std::string label; std::string clone_handle; std::string command; - std::string data; + std::string path; + std::string file_type; + std::string json_payload; std::string persistent; - std::string use_contained; - std::string escape_filename; - std::string escape_contained_filenames; std::string print_listener_path; std::string transaction_listener_path; + std::string rand_seed; std::string response; // program loop @@ -55,36 +55,37 @@ int32_t RunAmalgamTrace(std::istream *in_stream, std::ostream *out_stream, std:: if(command == "LOAD_ENTITY") { std::vector command_tokens = StringManipulation::SplitArgString(input); - if(command_tokens.size() >= 4) + if(command_tokens.size() >= 2) { handle = command_tokens[0]; - data = command_tokens[1]; // path to amlg file - persistent = command_tokens[2]; - use_contained = command_tokens[3]; + path = command_tokens[1]; - if(command_tokens.size() >= 5) - escape_filename = command_tokens[4]; - else - escape_filename = "false"; + if(command_tokens.size() > 2) + file_type = command_tokens[2]; - if(command_tokens.size() >= 6) - escape_contained_filenames = command_tokens[5]; - else - escape_contained_filenames = "true"; + if(command_tokens.size() > 3) + persistent = command_tokens[3]; - if(command_tokens.size() >= 7) - transaction_listener_path = command_tokens[6]; + if(command_tokens.size() > 4) + json_payload = command_tokens[4]; + + if(command_tokens.size() > 5) + transaction_listener_path = command_tokens[5]; else transaction_listener_path = ""; - if(command_tokens.size() >= 8) - print_listener_path = command_tokens[7]; + if(command_tokens.size() > 6) + print_listener_path = command_tokens[6]; else print_listener_path = ""; - std::string new_rand_seed = random_stream.CreateOtherStreamStateViaString("trace"); - auto status = entint.LoadEntity(handle, data, persistent == "true", use_contained == "true", - escape_filename == "true", escape_contained_filenames == "true", transaction_listener_path, print_listener_path, new_rand_seed); + if(command_tokens.size() > 7) + rand_seed = command_tokens[7]; + else + rand_seed = random_stream.CreateOtherStreamStateViaString("trace"); + + auto status = entint.LoadEntity(handle, path, file_type, + persistent == "true", json_payload, transaction_listener_path, print_listener_path, rand_seed); response = status.loaded ? SUCCESS_RESPONSE : FAILURE_RESPONSE; } else @@ -101,23 +102,30 @@ int32_t RunAmalgamTrace(std::istream *in_stream, std::ostream *out_stream, std:: handle = command_tokens[0]; clone_handle = command_tokens[1]; - if(command_tokens.size() >= 3) - data = command_tokens[2]; // path to amlg file + if(command_tokens.size() > 2) + path = command_tokens[2]; - if(command_tokens.size() >= 4) - persistent = command_tokens[3]; + if(command_tokens.size() > 3) + file_type = command_tokens[3]; - if(command_tokens.size() >= 5) - transaction_listener_path = command_tokens[4]; + if(command_tokens.size() > 4) + persistent = command_tokens[4]; + + if(command_tokens.size() > 5) + json_payload = command_tokens[5]; + + if(command_tokens.size() > 6) + transaction_listener_path = command_tokens[6]; else transaction_listener_path = ""; - if(command_tokens.size() >= 6) - print_listener_path = command_tokens[5]; + if(command_tokens.size() > 7) + print_listener_path = command_tokens[7]; else print_listener_path = ""; - bool result = entint.CloneEntity(handle, clone_handle, data, persistent == "true", transaction_listener_path, print_listener_path); + bool result = entint.CloneEntity(handle, clone_handle, path, file_type, + persistent == "true", json_payload, transaction_listener_path, print_listener_path); response = result ? SUCCESS_RESPONSE : FAILURE_RESPONSE; } else @@ -129,14 +137,21 @@ int32_t RunAmalgamTrace(std::istream *in_stream, std::ostream *out_stream, std:: else if(command == "STORE_ENTITY") { std::vector command_tokens = StringManipulation::SplitArgString(input); - if(command_tokens.size() >= 4) + if(command_tokens.size() >= 2) { handle = command_tokens[0]; - data = command_tokens[1]; // path to amlg file - persistent = command_tokens[2]; - use_contained = command_tokens[3]; + path = command_tokens[1]; + + if(command_tokens.size() > 2) + file_type = command_tokens[2]; + + if(command_tokens.size() > 3) + persistent = command_tokens[3]; + + if(command_tokens.size() > 4) + json_payload = command_tokens[4]; - entint.StoreEntity(handle, data, persistent == "true", use_contained == "true"); + entint.StoreEntity(handle, path, file_type, persistent == "true", json_payload); response = SUCCESS_RESPONSE; } else @@ -156,8 +171,8 @@ int32_t RunAmalgamTrace(std::istream *in_stream, std::ostream *out_stream, std:: { handle = StringManipulation::RemoveFirstToken(input); label = StringManipulation::RemoveFirstToken(input); - data = input; // json data - bool result = entint.SetJSONToLabel(handle, label, data); + json_payload = input; // json data + bool result = entint.SetJSONToLabel(handle, label, json_payload); response = result ? SUCCESS_RESPONSE : FAILURE_RESPONSE; } else if(command == "GET_JSON_FROM_LABEL") @@ -170,14 +185,14 @@ int32_t RunAmalgamTrace(std::istream *in_stream, std::ostream *out_stream, std:: { handle = StringManipulation::RemoveFirstToken(input); label = StringManipulation::RemoveFirstToken(input); - data = input; // json data - response = entint.ExecuteEntityJSON(handle, label, data); + json_payload = input; // json data + response = entint.ExecuteEntityJSON(handle, label, json_payload); } else if(command == "SET_RANDOM_SEED") { handle = StringManipulation::RemoveFirstToken(input); - data = input; - bool result = entint.SetRandomSeed(handle, data); + json_payload = input; + bool result = entint.SetRandomSeed(handle, json_payload); response = result ? SUCCESS_RESPONSE : FAILURE_RESPONSE; } else if(command == "VERSION") @@ -218,7 +233,7 @@ int32_t RunAmalgamTrace(std::istream *in_stream, std::ostream *out_stream, std:: } else if(command == "#" || command == "") { - // Commment or blank lines used in execution dumps. + // Comment or blank lines used in execution dumps. } else { diff --git a/src/Amalgam/AssetManager.cpp b/src/Amalgam/AssetManager.cpp index ac8c892b..536d3988 100644 --- a/src/Amalgam/AssetManager.cpp +++ b/src/Amalgam/AssetManager.cpp @@ -19,38 +19,110 @@ AssetManager asset_manager; -EvaluableNodeReference AssetManager::LoadResourcePath(std::string &resource_path, - std::string &resource_base_path, std::string &file_type, EvaluableNodeManager *enm, bool escape_filename, EntityExternalInterface::LoadEntityStatus &status) +AssetManager::AssetParameters::AssetParameters(std::string _resource, std::string file_type, bool is_entity) { - //get file path based on the file loaded - std::string path, file_base, extension; - Platform_SeparatePathFileExtension(resource_path, path, file_base, extension); - resource_base_path = path + file_base; + resource = _resource; + resourceType = file_type; - //escape the string if necessary, otherwise just use the regular one - std::string processed_resource_path; - if(escape_filename) + if(resourceType == "") + { + std::string path, file_base; + Platform_SeparatePathFileExtension(resource, path, file_base, resourceType); + } + + if(resourceType == FILE_EXTENSION_AMLG_METADATA || resourceType == FILE_EXTENSION_AMALGAM) + { + includeRandSeeds = false; + escapeResourceName = false; + escapeContainedResourceNames = true; + transactional = false; + prettyPrint = true; + sortKeys = true; + flatten = false; + parallelCreate = false; + executeOnLoad = false; + } + else if(resourceType == FILE_EXTENSION_JSON || resourceType == FILE_EXTENSION_YAML + || resourceType == FILE_EXTENSION_CSV) { - resource_base_path = path + FilenameEscapeProcessor::SafeEscapeFilename(file_base); - processed_resource_path = resource_base_path + "." + extension; + includeRandSeeds = false; + escapeResourceName = false; + escapeContainedResourceNames = false; + transactional = false; + prettyPrint = false; + sortKeys = true; + flatten = false; + parallelCreate = false; + executeOnLoad = false; + } + else if(resourceType == FILE_EXTENSION_COMPRESSED_AMALGAM_CODE) + { + includeRandSeeds = is_entity; + escapeResourceName = false; + escapeContainedResourceNames = false; + transactional = false; + prettyPrint = false; + sortKeys = false; + flatten = is_entity; + parallelCreate = false; + executeOnLoad = is_entity; } else { - resource_base_path = path + file_base; - processed_resource_path = resource_path; + includeRandSeeds = is_entity; + escapeResourceName = false; + escapeContainedResourceNames = false; + transactional = false; + prettyPrint = false; + sortKeys = false; + flatten = is_entity; + parallelCreate = false; + executeOnLoad = is_entity; } +} - if(file_type.empty()) - file_type = extension; +void AssetManager::AssetParameters::SetParams(EvaluableNode::AssocType ¶ms) +{ + EvaluableNode::GetValueFromMappedChildNodesReference(params, ENBISI_include_rand_seeds, includeRandSeeds); + EvaluableNode::GetValueFromMappedChildNodesReference(params, ENBISI_escape_resource_name, escapeResourceName); + EvaluableNode::GetValueFromMappedChildNodesReference(params, ENBISI_escape_contained_resource_names, escapeContainedResourceNames); + EvaluableNode::GetValueFromMappedChildNodesReference(params, ENBISI_transactional, transactional); + EvaluableNode::GetValueFromMappedChildNodesReference(params, ENBISI_pretty_print, prettyPrint); + EvaluableNode::GetValueFromMappedChildNodesReference(params, ENBISI_sort_keys, sortKeys); + EvaluableNode::GetValueFromMappedChildNodesReference(params, ENBISI_flatten, flatten); + EvaluableNode::GetValueFromMappedChildNodesReference(params, ENBISI_parallel_create, parallelCreate); + EvaluableNode::GetValueFromMappedChildNodesReference(params, ENBISI_execute_on_load, executeOnLoad); +} + +void AssetManager::AssetParameters::UpdateResources() +{ + //get file path based on the file being stored + std::string path, file_base; + Platform_SeparatePathFileExtension(resource, path, file_base, extension); + + //escape the string if necessary, otherwise just use the regular one + if(escapeResourceName) + { + resourceBasePath = path + FilenameEscapeProcessor::SafeEscapeFilename(file_base); + resource = resourceBasePath + "." + extension; + } + else //resource stays the same + { + resourceBasePath = path + file_base; + } +} +EvaluableNodeReference AssetManager::LoadResource(AssetParameters &asset_params, EvaluableNodeManager *enm, + EntityExternalInterface::LoadEntityStatus &status) +{ //load this entity based on file_type - if(file_type == FILE_EXTENSION_AMALGAM || file_type == FILE_EXTENSION_AMLG_METADATA) + if(asset_params.resourceType == FILE_EXTENSION_AMALGAM || asset_params.resourceType == FILE_EXTENSION_AMLG_METADATA) { - auto [code, code_success] = Platform_OpenFileAsString(processed_resource_path); + auto [code, code_success] = Platform_OpenFileAsString(asset_params.resource); if(!code_success) { status.SetStatus(false, code); - if(file_type == FILE_EXTENSION_AMALGAM) + if(asset_params.resourceType == FILE_EXTENSION_AMALGAM) std::cerr << code << std::endl; return EvaluableNodeReference::Null(); } @@ -63,21 +135,27 @@ EvaluableNodeReference AssetManager::LoadResourcePath(std::string &resource_path code.erase(0, 3); } - auto [node, warnings, char_with_error] = Parser::Parse(code, enm, false, &resource_path, debugSources); + auto [node, warnings, char_with_error] = Parser::Parse(code, enm, false, &asset_params.resource, debugSources); for(auto &w : warnings) std::cerr << w << std::endl; return node; } - else if(file_type == FILE_EXTENSION_JSON) - return EvaluableNodeReference(EvaluableNodeJSONTranslation::Load(processed_resource_path, enm, status), true); - else if(file_type == FILE_EXTENSION_YAML) - return EvaluableNodeReference(EvaluableNodeYAMLTranslation::Load(processed_resource_path, enm, status), true); - else if(file_type == FILE_EXTENSION_CSV) - return EvaluableNodeReference(FileSupportCSV::Load(processed_resource_path, enm, status), true); - else if(file_type == FILE_EXTENSION_COMPRESSED_AMALGAM_CODE) + else if(asset_params.resourceType == FILE_EXTENSION_JSON) + { + return EvaluableNodeReference(EvaluableNodeJSONTranslation::Load(asset_params.resource, enm, status), true); + } + else if(asset_params.resourceType == FILE_EXTENSION_YAML) + { + return EvaluableNodeReference(EvaluableNodeYAMLTranslation::Load(asset_params.resource, enm, status), true); + } + else if(asset_params.resourceType == FILE_EXTENSION_CSV) + { + return EvaluableNodeReference(FileSupportCSV::Load(asset_params.resource, enm, status), true); + } + else if(asset_params.resourceType == FILE_EXTENSION_COMPRESSED_AMALGAM_CODE) { BinaryData compressed_data; - auto [error_msg, version, success] = LoadFileToBuffer(processed_resource_path, file_type, compressed_data); + auto [error_msg, version, success] = LoadFileToBuffer(asset_params.resource, asset_params.resourceType, compressed_data); if(!success) { status.SetStatus(false, error_msg, version); @@ -89,7 +167,7 @@ EvaluableNodeReference AssetManager::LoadResourcePath(std::string &resource_path if(strings.size() == 0) return EvaluableNodeReference::Null(); - auto [node, warnings, char_with_error] = Parser::Parse(strings[0], enm, false, &resource_path, debugSources); + auto [node, warnings, char_with_error] = Parser::Parse(strings[0], enm, false, &asset_params.resource, debugSources); for(auto &w : warnings) std::cerr << w << std::endl; return node; @@ -97,7 +175,7 @@ EvaluableNodeReference AssetManager::LoadResourcePath(std::string &resource_path else //just load the file as a string { std::string s; - auto [error_msg, version, success] = LoadFileToBuffer(processed_resource_path, file_type, s); + auto [error_msg, version, success] = LoadFileToBuffer(asset_params.resource, asset_params.resourceType, s); if(success) return EvaluableNodeReference(enm->AllocNode(ENT_STRING, s), true); else @@ -108,31 +186,36 @@ EvaluableNodeReference AssetManager::LoadResourcePath(std::string &resource_path } } -bool AssetManager::StoreResourcePathFromProcessedResourcePaths(EvaluableNode *code, std::string &complete_resource_path, - std::string &file_type, EvaluableNodeManager *enm, bool escape_filename, bool sort_keys) +bool AssetManager::StoreResource(EvaluableNode *code, AssetParameters &asset_params, EvaluableNodeManager *enm) { //store the entity based on file_type - if(file_type == FILE_EXTENSION_AMALGAM || file_type == FILE_EXTENSION_AMLG_METADATA) + if(asset_params.resourceType == FILE_EXTENSION_AMALGAM || asset_params.resourceType == FILE_EXTENSION_AMLG_METADATA) { - std::ofstream outf(complete_resource_path, std::ios::out | std::ios::binary); + std::ofstream outf(asset_params.resource, std::ios::out | std::ios::binary); if(!outf.good()) return false; - std::string code_string = Parser::Unparse(code, enm, true, true, sort_keys); + std::string code_string = Parser::Unparse(code, enm, asset_params.prettyPrint, true, asset_params.sortKeys); outf.write(code_string.c_str(), code_string.size()); outf.close(); return true; } - else if(file_type == FILE_EXTENSION_JSON) - return EvaluableNodeJSONTranslation::Store(code, complete_resource_path, enm, sort_keys); - else if(file_type == FILE_EXTENSION_YAML) - return EvaluableNodeYAMLTranslation::Store(code, complete_resource_path, enm, sort_keys); - else if(file_type == FILE_EXTENSION_CSV) - return FileSupportCSV::Store(code, complete_resource_path, enm); - else if(file_type == FILE_EXTENSION_COMPRESSED_AMALGAM_CODE) + else if(asset_params.resourceType == FILE_EXTENSION_JSON) + { + return EvaluableNodeJSONTranslation::Store(code, asset_params.resource, enm, asset_params.sortKeys); + } + else if(asset_params.resourceType == FILE_EXTENSION_YAML) + { + return EvaluableNodeYAMLTranslation::Store(code, asset_params.resource, enm, asset_params.sortKeys); + } + else if(asset_params.resourceType == FILE_EXTENSION_CSV) { - std::string code_string = Parser::Unparse(code, enm, false, true, sort_keys); + return FileSupportCSV::Store(code, asset_params.resource, enm); + } + else if(asset_params.resourceType == FILE_EXTENSION_COMPRESSED_AMALGAM_CODE) + { + std::string code_string = Parser::Unparse(code, enm, asset_params.prettyPrint, true, asset_params.sortKeys); //transform into format needed for compression CompactHashMap string_map; @@ -140,25 +223,23 @@ bool AssetManager::StoreResourcePathFromProcessedResourcePaths(EvaluableNode *co //compress and store BinaryData compressed_data = CompressStrings(string_map); - return StoreFileFromBuffer(complete_resource_path, file_type, compressed_data); + return StoreFileFromBuffer(asset_params.resource, asset_params.resourceType, compressed_data); } else //binary string { std::string s = EvaluableNode::ToStringPreservingOpcodeType(code); - return StoreFileFromBuffer(complete_resource_path, file_type, s); + return StoreFileFromBuffer(asset_params.resource, asset_params.resourceType, s); } return false; } -Entity *AssetManager::LoadEntityFromResourcePath(std::string &resource_path, std::string &file_type, - bool persistent, bool load_contained_entities, bool escape_filename, bool escape_contained_filenames, +Entity *AssetManager::LoadEntityFromResource(AssetParameters &asset_params, bool persistent, std::string default_random_seed, Interpreter *calling_interpreter, EntityExternalInterface::LoadEntityStatus &status) { - std::string resource_base_path; Entity *new_entity = new Entity(); - EvaluableNodeReference code = LoadResourcePath(resource_path, resource_base_path, file_type, &new_entity->evaluableNodeManager, escape_filename, status); + EvaluableNodeReference code = LoadResource(asset_params, &new_entity->evaluableNodeManager, status); if(!status.loaded) { delete new_entity; @@ -167,7 +248,7 @@ Entity *AssetManager::LoadEntityFromResourcePath(std::string &resource_path, std new_entity->SetRandomState(default_random_seed, true); - if(file_type == FILE_EXTENSION_COMPRESSED_AMALGAM_CODE) + if(asset_params.resourceType == FILE_EXTENSION_COMPRESSED_AMALGAM_CODE) { new_entity->SetRoot(code, true); @@ -183,77 +264,80 @@ Entity *AssetManager::LoadEntityFromResourcePath(std::string &resource_path, std new_entity->SetRoot(code, true); - //load any metadata like random seed - std::string metadata_filename = resource_base_path + "." + FILE_EXTENSION_AMLG_METADATA; - std::string metadata_base_path; - std::string metadata_extension; - EntityExternalInterface::LoadEntityStatus metadata_status; - EvaluableNode *metadata = LoadResourcePath(metadata_filename, metadata_base_path, metadata_extension, &new_entity->evaluableNodeManager, escape_filename, metadata_status); - if(metadata_status.loaded) + if(asset_params.resourceType == FILE_EXTENSION_AMALGAM) { - if(EvaluableNode::IsAssociativeArray(metadata)) + //load any metadata like random seed + AssetParameters metadata_asset_params = asset_params.CreateAssetParametersForAssociatedResource(FILE_EXTENSION_AMLG_METADATA); + EntityExternalInterface::LoadEntityStatus metadata_status; + EvaluableNodeReference metadata = LoadResource(metadata_asset_params, &new_entity->evaluableNodeManager, metadata_status); + if(metadata_status.loaded) { - EvaluableNode **seed = metadata->GetMappedChildNode(GetStringIdFromBuiltInStringId(ENBISI_rand_seed)); - if(seed != nullptr) + if(EvaluableNode::IsAssociativeArray(metadata)) { - default_random_seed = EvaluableNode::ToStringPreservingOpcodeType(*seed); - new_entity->SetRandomState(default_random_seed, true); - } + EvaluableNode **seed = metadata->GetMappedChildNode(GetStringIdFromBuiltInStringId(ENBISI_rand_seed)); + if(seed != nullptr) + { + default_random_seed = EvaluableNode::ToStringPreservingOpcodeType(*seed); + new_entity->SetRandomState(default_random_seed, true); + } - EvaluableNode **version = metadata->GetMappedChildNode(GetStringIdFromBuiltInStringId(ENBISI_version)); - if(version != nullptr) - { - auto [tostr_success, version_str] = EvaluableNode::ToString(*version); - if(tostr_success) + EvaluableNode **version = metadata->GetMappedChildNode(GetStringIdFromBuiltInStringId(ENBISI_version)); + if(version != nullptr) { - auto [error_message, success] = AssetManager::ValidateVersionAgainstAmalgam(version_str); - if(!success) + auto [tostr_success, version_str] = EvaluableNode::ToString(*version); + if(tostr_success) { - status.SetStatus(false, error_message, version_str); - delete new_entity; - return nullptr; + auto [error_message, success] = AssetManager::ValidateVersionAgainstAmalgam(version_str); + if(!success) + { + status.SetStatus(false, error_message, version_str); + delete new_entity; + return nullptr; + } } } } - } - new_entity->evaluableNodeManager.FreeNodeTree(metadata); + new_entity->evaluableNodeManager.FreeNodeTree(metadata); + } } if(persistent) - SetEntityPersistentPath(new_entity, resource_path); + SetEntityPersistence(new_entity, &asset_params); //load contained entities - if(load_contained_entities) + + //iterate over all files in directory + std::string contained_entities_directory = asset_params.resourceBasePath + "/"; + std::vector file_names; + Platform_GetFileNamesOfType(file_names, contained_entities_directory, asset_params.extension); + for(auto &f : file_names) { - //iterate over all files in directory - resource_base_path.append("/"); - std::vector file_names; - Platform_GetFileNamesOfType(file_names, resource_base_path, file_type); - for(auto &f : file_names) - { - std::string ce_path, ce_file_base, ce_extension; - Platform_SeparatePathFileExtension(f, ce_path, ce_file_base, ce_extension); - - std::string entity_name; - if(escape_contained_filenames) - entity_name = FilenameEscapeProcessor::SafeUnescapeFilename(ce_file_base); - else - entity_name = ce_file_base; - - //don't escape filename again because it's already escaped in this loop - std::string default_seed = new_entity->CreateRandomStreamFromStringAndRand(entity_name); - std::string contained_resource_path = resource_base_path + ce_file_base + "." + ce_extension; - Entity *contained_entity = LoadEntityFromResourcePath(contained_resource_path, file_type, - false, true, false, escape_contained_filenames, default_seed, calling_interpreter, status); - if(!status.loaded) - { - delete new_entity; - return nullptr; - } + std::string ce_path, ce_file_base, ce_extension; + Platform_SeparatePathFileExtension(f, ce_path, ce_file_base, ce_extension); + + std::string entity_name; + if(asset_params.escapeContainedResourceNames) + entity_name = FilenameEscapeProcessor::SafeUnescapeFilename(ce_file_base); + else + entity_name = ce_file_base; + + std::string default_seed = new_entity->CreateRandomStreamFromStringAndRand(entity_name); + + std::string ce_resource_base_path = contained_entities_directory + ce_file_base; + AssetParameters ce_asset_params + = asset_params.CreateAssetParametersForContainedResourceByResourceBasePath(ce_resource_base_path); + + Entity *contained_entity = LoadEntityFromResource(ce_asset_params, persistent, + default_seed, calling_interpreter, status); - new_entity->AddContainedEntity(contained_entity, entity_name); + if(!status.loaded) + { + delete new_entity; + return nullptr; } + + new_entity->AddContainedEntity(contained_entity, entity_name); } return new_entity; @@ -267,48 +351,25 @@ void AssetManager::CreateEntity(Entity *entity) #ifdef MULTITHREAD_INTERFACE Concurrency::ReadLock lock(persistentEntitiesMutex); #endif - //early out if no persistent entities - if(persistentEntities.size() == 0) + + Entity *container = entity->GetContainer(); + auto pe_entry = persistentEntities.find(container); + if(pe_entry == end(persistentEntities)) return; + auto &container_asset_params = *pe_entry->second; - Entity *cur = entity->GetContainer(); - std::string slice_path; - std::string filename; - std::string extension = defaultEntityExtension; - std::string traversal_path = ""; - std::string escaped_entity_id = FilenameEscapeProcessor::SafeEscapeFilename(entity->GetId()); - std::string id_suffix = "/" + escaped_entity_id + "." + defaultEntityExtension; - while(cur != nullptr) + //if flattened, then just need to update it or the appropriate container + if(container_asset_params.flatten) { - const auto &pe = persistentEntities.find(cur); - if(pe != end(persistentEntities)) - { - Platform_SeparatePathFileExtension(pe->second, slice_path, filename, extension); - //create contained entity directory in case it doesn't currently exist - std::string new_path = slice_path + filename + traversal_path; - std::error_code ec; - std::filesystem::create_directory(new_path, ec); - - if(!ec) - { - new_path += id_suffix; - StoreEntityToResourcePath(entity, new_path, extension, false, true, false, true, false); - } - else - { - std::cerr << "Could not create directory: " << new_path << std::endl; - } - - } - - //don't need to continue and allocate extra traversal path if already at outermost entity - Entity *cur_container = cur->GetContainer(); - if(cur_container == nullptr) - break; + UpdateEntity(container); + } + else + { + AssetParameters ce_asset_params + = container_asset_params.CreateAssetParametersForContainedResourceByEntityId(entity->GetId()); - escaped_entity_id = FilenameEscapeProcessor::SafeEscapeFilename(cur->GetId()); - traversal_path = "/" + escaped_entity_id + traversal_path; - cur = cur_container; + EnsureEntityToResourceCanContainEntities(container_asset_params); + StoreEntityToResource(entity, ce_asset_params, true, true, false); } } @@ -387,49 +448,32 @@ std::string AssetManager::GetEvaluableNodeSourceFromComments(EvaluableNode *en) void AssetManager::DestroyPersistentEntity(Entity *entity) { - Entity *cur = entity; - std::string slice_path; - std::string filename; - std::string extension; - std::string traversal_path; - std::error_code ec; + auto pe_entry = persistentEntities.find(entity); + if(pe_entry == end(persistentEntities)) + return; + auto &asset_params = *pe_entry->second; - //remove it as a persistent entity if it happened to be a direct one (erase won't do anything if it doesn't exist) - persistentEntities.erase(entity); + //if flattened, then just need to update it or the appropriate container + if(asset_params.flatten) + { + UpdateEntity(entity); + } + else + { + std::error_code ec; - //delete any contained entities that are persistent - for(auto contained_entity : entity->GetContainedEntities()) - DestroyPersistentEntity(contained_entity); + //delete files + std::filesystem::remove(asset_params.resource, ec); + if(ec) + std::cerr << "Could not remove file: " << asset_params.resource << std::endl; - //cover the case if any of this entity's containers were also persisted entities - while(cur != nullptr) - { - const auto &pe = persistentEntities.find(cur); - if(pe != end(persistentEntities)) - { - //get metadata filename - Platform_SeparatePathFileExtension(pe->second, slice_path, filename, extension); - std::string total_filepath = slice_path + filename + traversal_path; - - //delete files - std::filesystem::remove(total_filepath + "." + defaultEntityExtension, ec); - if(ec) - std::cerr << "Could not remove file: " << total_filepath + "." + defaultEntityExtension << std::endl; - - std::filesystem::remove(total_filepath + "." + FILE_EXTENSION_AMLG_METADATA, ec); - if(ec) - std::cerr << "Could not remove file: " << total_filepath + "." + FILE_EXTENSION_AMLG_METADATA << std::endl; - - //remove directory and all contents if it exists (command will fail if it doesn't exist) - std::filesystem::remove_all(total_filepath, ec); - if(ec) - std::cerr << "Could not remove directory: " << total_filepath << std::endl; - } + if(asset_params.resourceType == FILE_EXTENSION_AMALGAM) + std::filesystem::remove(asset_params.resourceBasePath + "." + FILE_EXTENSION_AMLG_METADATA, ec); - std::string escaped_entity_id = FilenameEscapeProcessor::SafeEscapeFilename(cur->GetId()); - traversal_path = "/" + escaped_entity_id + traversal_path; + //remove directory and all contents if it exists + std::filesystem::remove_all(asset_params.resourceBasePath, ec); - cur = cur->GetContainer(); + DeepClearEntityPersistenceRecurse(entity); } } @@ -441,27 +485,3 @@ void AssetManager::RemoveRootPermissions(Entity *entity) SetRootPermission(entity, false); } - -void AssetManager::PreprocessFileNameAndType(std::string &resource_path, - std::string &file_type, bool escape_resource_path, - std::string &resource_base_path, std::string &complete_resource_path) -{ - //get file path based on the file being stored - std::string path, file_base, extension; - Platform_SeparatePathFileExtension(resource_path, path, file_base, extension); - - //escape the string if necessary, otherwise just use the regular one - if(escape_resource_path) - { - resource_base_path = path + FilenameEscapeProcessor::SafeEscapeFilename(file_base); - complete_resource_path = resource_base_path + "." + extension; - } - else - { - resource_base_path = path + file_base; - complete_resource_path = resource_path; - } - - if(file_type.empty()) - file_type = extension; -} diff --git a/src/Amalgam/AssetManager.h b/src/Amalgam/AssetManager.h index d4a52b22..2dfa7085 100644 --- a/src/Amalgam/AssetManager.h +++ b/src/Amalgam/AssetManager.h @@ -34,128 +34,182 @@ class AssetManager : defaultEntityExtension(FILE_EXTENSION_AMALGAM), debugSources(false), debugMinimal(false) { } - //Returns the code to the corresponding entity by resource_path - // sets resource_base_path to the resource path without the extension - //if file_type is not an empty string, it will use the specified file_type instead of the filename's extension - EvaluableNodeReference LoadResourcePath(std::string &resource_path, std::string &resource_base_path, - std::string &file_type, EvaluableNodeManager *enm, bool escape_filename, EntityExternalInterface::LoadEntityStatus &status); - - //Stores the code to the corresponding resource path - // sets resource_base_path to the resource path without the extension, and extension accordingly - //if file_type is not an empty string, it will use the specified file_type instead of the filename's extension - static bool StoreResourcePath(EvaluableNode *code, std::string &resource_path, std::string &resource_base_path, - std::string &file_type, EvaluableNodeManager *enm, bool escape_filename, bool sort_keys) + //parameters that define how an asset is loaded and stored + struct AssetParameters { - std::string complete_resource_path; - PreprocessFileNameAndType(resource_path, file_type, escape_filename, resource_base_path, complete_resource_path); + //initializes defaults for AssetParameters -- should specify whether it is an entity + //_resource specifies the path. if file_type is empty string, then it will + //attempt to extract the file_type from the file extension on the resource + AssetParameters(std::string _resource, std::string file_type, bool is_entity); + + //initializes in a way intended for contained entities for _resource_base_path, will inherit parameters + //but update with the new resource_base_path + inline AssetParameters CreateAssetParametersForContainedResourceByResourceBasePath(std::string _resource_base_path) + { + AssetParameters new_params(*this); + new_params.resourceBasePath = _resource_base_path; + new_params.resource = _resource_base_path + "." + extension; - return StoreResourcePathFromProcessedResourcePaths(code, complete_resource_path, - file_type, enm, escape_filename, sort_keys); - } + //since it is contained, overwrite escapeResourceName + new_params.escapeResourceName = escapeContainedResourceNames; + + return new_params; + } + + //initializes in a way intended for contained entities for the given contained entity_id, will inherit parameters + //but update with the new resource_base_path + inline AssetParameters CreateAssetParametersForContainedResourceByEntityId(const std::string &entity_id) + { + AssetParameters new_params(*this); + if(escapeContainedResourceNames) + { + std::string ce_escaped_filename = FilenameEscapeProcessor::SafeEscapeFilename(entity_id); + new_params.resourceBasePath = resourceBasePath + "/" + ce_escaped_filename; + } + else + { + new_params.resourceBasePath = resourceBasePath + "/" + entity_id; + } - static bool StoreResourcePathFromProcessedResourcePaths(EvaluableNode *code, std::string &complete_resource_path, - std::string &file_type, EvaluableNodeManager *enm, bool escape_filename, bool sort_keys); + new_params.resource = new_params.resourceBasePath + "." + extension; - //Loads an entity, including contained entities, etc. from the resource path specified - //if file_type is not an empty string, it will use the specified file_type instead of the filename's extension + //since it is contained, overwrite escapeResourceName + new_params.escapeResourceName = escapeContainedResourceNames; + + return new_params; + } + + //initializes and returns new asset parameters for a file of the same name but different extension + inline AssetParameters CreateAssetParametersForAssociatedResource(std::string resource_type) + { + AssetParameters new_params(*this); + new_params.resourceType = resource_type; + new_params.resource = resourceBasePath + "." + resource_type; + return new_params; + } + + //sets the parameters + void SetParams(EvaluableNode::AssocType ¶ms); + + //updates resources based on the parameters -- should be called after SetParams + void UpdateResources(); + + std::string resource; + std::string resourceBasePath; + std::string resourceType; + std::string extension; + bool includeRandSeeds; + bool escapeResourceName; + bool escapeContainedResourceNames; + bool transactional; + bool prettyPrint; + bool sortKeys; + bool flatten; + bool parallelCreate; + bool executeOnLoad; + }; + + //Returns the code to the corresponding entity specified by asset_params using enm + //Additionally returns the updated resource_base_path for the file, as well as the status + EvaluableNodeReference LoadResource(AssetParameters &asset_params, EvaluableNodeManager *enm, + EntityExternalInterface::LoadEntityStatus &status); + + //Stores the code to the resource specified in asset_params + bool StoreResource(EvaluableNode *code, AssetParameters &asset_params, EvaluableNodeManager *enm); + + //Loads an entity, including contained entities, etc. from the resource specified // if persistent is true, then it will keep the resource updated based on any calls to UpdateEntity //if the resource does not have a metadata file, will use default_random_seed as its seed - Entity *LoadEntityFromResourcePath(std::string &resource_path, std::string &file_type, bool persistent, - bool load_contained_entities, bool escape_filename, bool escape_contained_filenames, + Entity *LoadEntityFromResource(AssetParameters &asset_params, bool persistent, std::string default_random_seed, Interpreter *calling_interpreter, EntityExternalInterface::LoadEntityStatus &status); - //Stores an entity, including contained entities, etc. from the resource path specified - //if file_type is not an empty string, it will use the specified file_type instead of the filename's extension - // if persistent is true, then it will keep the resource updated based on any calls to UpdateEntity (will not make not persistent if was previously loaded as persistent) + //Stores an entity, including contained entities, etc. from the resource specified + // if update_persistence is true, then it will consider the persistent parameter, otherwise it is ignored + // if persistent is true, then it will keep the resource updated, if false it will clear persistence + // if store_contained_entities is true, then it will also write all contained entities // if all_contained_entities is nullptr, then it will be populated, as read locks are necessary for entities in multithreading //returns true if successful template - bool StoreEntityToResourcePath(Entity *entity, std::string &resource_path, std::string &file_type, - bool update_persistence_location, bool store_contained_entities, - bool escape_filename, bool escape_contained_filenames, bool sort_keys, - bool include_rand_seeds = true, bool parallel_create = false, + bool StoreEntityToResource(Entity *entity, AssetParameters &asset_params, + bool update_persistence, bool persistent, + bool store_contained_entities = true, Entity::EntityReferenceBufferReference *all_contained_entities = nullptr) { if(entity == nullptr) return false; - std::string resource_base_path; - std::string complete_resource_path; - PreprocessFileNameAndType(resource_path, file_type, escape_filename, resource_base_path, complete_resource_path); - Entity::EntityReferenceBufferReference erbr; if(all_contained_entities == nullptr) { - erbr = entity->GetAllDeeplyContainedEntityReferencesGroupedByDepth(); + if(store_contained_entities || asset_params.flatten) + erbr = entity->GetAllDeeplyContainedEntityReferencesGroupedByDepth(); + else + erbr.Clear(); + all_contained_entities = &erbr; } - if(file_type == FILE_EXTENSION_COMPRESSED_AMALGAM_CODE) + if(asset_params.flatten + && (asset_params.resourceType == FILE_EXTENSION_AMALGAM + || asset_params.resourceType == FILE_EXTENSION_COMPRESSED_AMALGAM_CODE)) { EvaluableNodeReference flattened_entity = EntityManipulation::FlattenEntity(&entity->evaluableNodeManager, - entity, *all_contained_entities, include_rand_seeds, parallel_create); + entity, *all_contained_entities, asset_params.includeRandSeeds, asset_params.parallelCreate); - bool all_stored_successfully = AssetManager::StoreResourcePathFromProcessedResourcePaths(flattened_entity, - complete_resource_path, file_type, &entity->evaluableNodeManager, escape_filename, sort_keys); + bool all_stored_successfully = StoreResource(flattened_entity, + asset_params, &entity->evaluableNodeManager); entity->evaluableNodeManager.FreeNodeTreeIfPossible(flattened_entity); return all_stored_successfully; } - bool all_stored_successfully = AssetManager::StoreResourcePathFromProcessedResourcePaths(entity->GetRoot(), - complete_resource_path, file_type, &entity->evaluableNodeManager, escape_filename, sort_keys); + if(!StoreResource(entity->GetRoot(), asset_params, &entity->evaluableNodeManager)) + return false; + + if(asset_params.resourceType == FILE_EXTENSION_AMALGAM) + { + //store any metadata like random seed + AssetParameters metadata_asset_params = asset_params.CreateAssetParametersForAssociatedResource(FILE_EXTENSION_AMLG_METADATA); - //store any metadata like random seed - std::string metadata_filename = resource_base_path + "." + FILE_EXTENSION_AMLG_METADATA; - EvaluableNode en_assoc(ENT_ASSOC); - EvaluableNode en_rand_seed(ENT_STRING, entity->GetRandomState()); - EvaluableNode en_version(ENT_STRING, AMALGAM_VERSION_STRING); - en_assoc.SetMappedChildNode(GetStringIdFromBuiltInStringId(ENBISI_rand_seed), &en_rand_seed); - en_assoc.SetMappedChildNode(GetStringIdFromBuiltInStringId(ENBISI_version), &en_version); + EvaluableNode en_assoc(ENT_ASSOC); + EvaluableNode en_rand_seed(ENT_STRING, entity->GetRandomState()); + EvaluableNode en_version(ENT_STRING, AMALGAM_VERSION_STRING); + en_assoc.SetMappedChildNode(GetStringIdFromBuiltInStringId(ENBISI_rand_seed), &en_rand_seed); + en_assoc.SetMappedChildNode(GetStringIdFromBuiltInStringId(ENBISI_version), &en_version); - std::string metadata_extension = FILE_EXTENSION_AMLG_METADATA; - //don't reescape the path here, since it has already been done - StoreResourcePathFromProcessedResourcePaths(&en_assoc, metadata_filename, metadata_extension, &entity->evaluableNodeManager, false, sort_keys); + StoreResource(&en_assoc, metadata_asset_params, &entity->evaluableNodeManager); + } //store contained entities - if(store_contained_entities && entity->GetContainedEntities().size() > 0) + if(entity->GetContainedEntities().size() > 0) { - std::error_code ec; - //create directory in case it doesn't exist - std::filesystem::create_directories(resource_base_path, ec); - - //return that the directory could not be created - if(ec) + if(!EnsureEntityToResourceCanContainEntities(asset_params)) return false; - //store any contained entities - resource_base_path.append("/"); - for(auto contained_entity : entity->GetContainedEntities()) + //only actually store the contained entities if directed + if(store_contained_entities) { - std::string new_resource_path; - if(escape_contained_filenames) + //store any contained entities + for(auto contained_entity : entity->GetContainedEntities()) { - const std::string &ce_escaped_filename = FilenameEscapeProcessor::SafeEscapeFilename(contained_entity->GetId()); - new_resource_path = resource_base_path + ce_escaped_filename + "." + file_type; + AssetParameters ce_asset_params + = asset_params.CreateAssetParametersForContainedResourceByEntityId(contained_entity->GetId()); + + //don't escape filename again because it's already escaped in this loop + bool stored_successfully = StoreEntityToResource(contained_entity, ce_asset_params, + update_persistence, persistent, true, all_contained_entities); + + if(!stored_successfully) + return false; } - else - new_resource_path = resource_base_path + contained_entity->GetId() + "." + file_type; - - //don't escape filename again because it's already escaped in this loop - bool stored_successfully = StoreEntityToResourcePath(contained_entity, new_resource_path, file_type, false, true, false, - escape_contained_filenames, sort_keys, include_rand_seeds, parallel_create); - if(!stored_successfully) - return false; } } - if(update_persistence_location) - { - std::string new_persist_path = resource_base_path + "." + file_type; - SetEntityPersistentPath(entity, new_persist_path); - } + //update after done using asset_params, just in case it is deleted + if(update_persistence) + SetEntityPersistence(entity, persistent ? &asset_params : nullptr); - return all_stored_successfully; + return true; } //Indicates that the entity has been written to or updated, and so if the asset is persistent, the persistent copy should be updated @@ -163,40 +217,47 @@ class AssetManager void UpdateEntity(Entity *entity, Entity::EntityReferenceBufferReference *all_contained_entities = nullptr) { + if(entity == nullptr) + return; + #ifdef MULTITHREAD_INTERFACE Concurrency::ReadLock lock(persistentEntitiesMutex); #endif - //early out if no persistent entities - if(persistentEntities.size() == 0) - return; - - Entity *cur = entity; - std::string slice_path; - std::string filename; - std::string extension; - std::string traversal_path; - while(cur != nullptr) + //if persistent store only this entity, since only it is getting updated + auto pe_entry = persistentEntities.find(entity); + if(pe_entry != end(persistentEntities)) { - const auto &pe = persistentEntities.find(cur); - if(pe != end(persistentEntities)) + AssetParameters *asset_params = pe_entry->second.get(); + //if the entity is flattened, then need to find top level container entity + //that is persistent and store it out with all its contained entities + if(asset_params->flatten) { - Platform_SeparatePathFileExtension(pe->second, slice_path, filename, extension); - std::string new_path = slice_path + filename + traversal_path + "." + extension; - - //the outermost file is already escaped, but persistent entities must be recursively escaped - StoreEntityToResourcePath(entity, new_path, extension, - false, false, false, true, false, true, false, all_contained_entities); + while(true) + { + Entity *container = entity->GetContainer(); + + if(container == nullptr) + { + StoreEntityToResource(entity, *asset_params, false, true, false, all_contained_entities); + break; + } + + auto container_pe_entry = persistentEntities.find(container); + if(container_pe_entry == end(persistentEntities)) + { + StoreEntityToResource(entity, *asset_params, false, true, false, all_contained_entities); + break; + } + + entity = container; + asset_params = container_pe_entry->second.get(); + } + } + else //just update the individual entity + { + StoreEntityToResource(entity, *asset_params, false, true, false, all_contained_entities); } - - //don't need to continue and allocate extra traversal path if already at outermost entity - Entity *cur_container = cur->GetContainer(); - if(cur_container == nullptr) - break; - - std::string escaped_entity_id = FilenameEscapeProcessor::SafeEscapeFilename(cur->GetId()); - traversal_path = "/" + escaped_entity_id + traversal_path; - cur = cur_container; } } @@ -210,8 +271,7 @@ class AssetManager RemoveRootPermissions(entity); - if(persistentEntities.size() > 0) - DestroyPersistentEntity(entity); + DestroyPersistentEntity(entity); } //sets the entity's root permission to permission @@ -239,18 +299,6 @@ class AssetManager inline bool IsEntityDirectlyPersistent(Entity *entity) { return persistentEntities.find(entity) != end(persistentEntities); } - //sets the entity's persistent path - inline void SetEntityPersistentPath(Entity *entity, std::string &resource_path) - { - #ifdef MULTITHREAD_INTERFACE - Concurrency::WriteLock lock(persistentEntitiesMutex); - #endif - if(resource_path.empty()) - persistentEntities.erase(entity); - else - persistentEntities[entity] = resource_path; - } - inline bool DoesEntityHaveRootPermission(Entity *entity) { if(entity == nullptr) @@ -330,21 +378,57 @@ class AssetManager private: + //sets the entity's persistent path + //if asset_params is null, then it will clear persistence + //assumes persistentEntitiesMutex is locked + inline void SetEntityPersistence(Entity *entity, AssetParameters *asset_params) + { + if(asset_params == nullptr) + { + persistentEntities.erase(entity); + } + else + { + //need to create a copy just in case it is the same location + auto new_asset_params = std::make_unique(*asset_params); + persistentEntities.insert_or_assign(entity, std::move(new_asset_params)); + } + } + + //clears all entity persistence recursively, assumes persistentEntitiesMutex is locked before calling + void DeepClearEntityPersistenceRecurse(Entity *entity) + { + persistentEntities.erase(entity); + + for(auto contained_entity : entity->GetContainedEntities()) + DeepClearEntityPersistenceRecurse(contained_entity); + } + + //creates any directory required to contain entities for asset_params + inline bool EnsureEntityToResourceCanContainEntities(AssetParameters &asset_params) + { + std::error_code ec; + //create directory in case it doesn't exist + std::filesystem::create_directories(asset_params.resourceBasePath, ec); + + //return that the directory could not be created + if(ec) + { + std::cerr << "Error creating directory: " << ec.message() << std::endl; + return false; + } + + return true; + } + //recursively deletes persistent entities void DestroyPersistentEntity(Entity *entity); //recursively removes root permissions void RemoveRootPermissions(Entity *entity); - //using resource_path as the semantically intended path, populates resource_base_path and complete_resource_path, - // and populates file_type if it is unspecified (empty string) - //escapes the resource path if escape_resource_path is true - static void PreprocessFileNameAndType(std::string &resource_path, - std::string &file_type, bool escape_resource_path, - std::string &resource_base_path, std::string &complete_resource_path); - //entities that need changes stored, and the resource paths to store them - CompactHashMap persistentEntities; + FastHashMap> persistentEntities; //entities that have root permissions Entity::EntitySetType rootEntities; diff --git a/src/Amalgam/Opcodes.cpp b/src/Amalgam/Opcodes.cpp index 1bb4fb29..9332f298 100644 --- a/src/Amalgam/Opcodes.cpp +++ b/src/Amalgam/Opcodes.cpp @@ -248,7 +248,6 @@ void StringInternPool::InitializeStaticStrings() EmplaceNodeTypeString(ENT_DESTROY_ENTITIES, "destroy_entities"); EmplaceNodeTypeString(ENT_LOAD, "load"); EmplaceNodeTypeString(ENT_LOAD_ENTITY, "load_entity"); - EmplaceNodeTypeString(ENT_LOAD_PERSISTENT_ENTITY, "load_persistent_entity"); EmplaceNodeTypeString(ENT_STORE, "store"); EmplaceNodeTypeString(ENT_STORE_ENTITY, "store_entity"); EmplaceNodeTypeString(ENT_CONTAINS_ENTITY, "contains_entity"); @@ -322,6 +321,12 @@ void StringInternPool::InitializeStaticStrings() //file storage options EmplaceStaticString(ENBISI_include_rand_seeds, "include_rand_seeds"); EmplaceStaticString(ENBISI_parallel_create, "parallel_create"); + EmplaceStaticString(ENBISI_escape_resource_name, "escape_resource_name"); + EmplaceStaticString(ENBISI_escape_contained_resource_names, "escape_contained_resource_names"); + EmplaceStaticString(ENBISI_transactional, "transactional"); + EmplaceStaticString(ENBISI_pretty_print, "pretty_print"); + EmplaceStaticString(ENBISI_flatten, "flatten"); + EmplaceStaticString(ENBISI_execute_on_load, "execute_on_load"); //substr parameters EmplaceStaticString(ENBISI_all, "all"); diff --git a/src/Amalgam/Opcodes.h b/src/Amalgam/Opcodes.h index 2d1fa06d..70954af3 100644 --- a/src/Amalgam/Opcodes.h +++ b/src/Amalgam/Opcodes.h @@ -229,7 +229,6 @@ enum EvaluableNodeType : uint8_t ENT_DESTROY_ENTITIES, ENT_LOAD, ENT_LOAD_ENTITY, - ENT_LOAD_PERSISTENT_ENTITY, ENT_STORE, ENT_STORE_ENTITY, ENT_CONTAINS_ENTITY, @@ -419,7 +418,7 @@ constexpr OrderedChildNodeType GetInstructionOrderedChildNodeType(EvaluableNodeT case ENT_GET_ENTITY_RAND_SEED: case ENT_GET_ENTITY_ROOT_PERMISSION: case ENT_SET_ENTITY_ROOT_PERMISSION: case ENT_CLONE_ENTITIES: case ENT_MOVE_ENTITIES: - case ENT_LOAD: case ENT_LOAD_ENTITY: case ENT_LOAD_PERSISTENT_ENTITY: + case ENT_LOAD: case ENT_LOAD_ENTITY: case ENT_STORE_ENTITY: case ENT_STORE: case ENT_CONTAINS_ENTITY: return OCNT_POSITION; @@ -536,7 +535,14 @@ enum EvaluableNodeBuiltInStringId //file storage options ENBISI_include_rand_seeds, + ENBISI_escape_resource_name, + ENBISI_escape_contained_resource_names, + ENBISI_transactional, + ENBISI_pretty_print, + //ENBISI_sort_keys -- covered in format below + ENBISI_flatten, ENBISI_parallel_create, + ENBISI_execute_on_load, //substr parameters ENBISI_all, diff --git a/src/Amalgam/amlg_code/full_test.amlg b/src/Amalgam/amlg_code/full_test.amlg index a217ee86..e3230cbb 100644 --- a/src/Amalgam/amlg_code/full_test.amlg +++ b/src/Amalgam/amlg_code/full_test.amlg @@ -5,6 +5,16 @@ (print "--Amalgam Version--\n") (print (system "version") "\n") + ;create directory for storing temporary output + (declare (assoc + rmdir "rmdir /s /q " + rmfile "del /s /q " + slash "\\")) + (if (!= (system "os") "Windows") + (assign (assoc rmdir "rm -r " rmfile "rm " slash "/")) + ) + (system "system" (concat "mkdir amlg_code" slash "test_output")) + (print "--system_time--\n") (declare (assoc start_time (system_time))) @@ -2198,12 +2208,13 @@ (print "--load_entity--\n") (print "load from .amlg:\n") + (load_entity "amlg_code/module_test.amlg" "ModuleTest") (call_entity "ModuleTest" "hello") (print (flatten_entity "ModuleTest")) - (print "--load_persistent_entity--\n") - (load_persistent_entity "amlg_code/persist_module_test.amlg" "PersistModuleTest") + (print "persistent loads\n") + (load_entity "amlg_code/persist_module_test.amlg" "PersistModuleTest" (null) (true)) (assign_to_entities "PersistModuleTest" (assoc a 8)) (assign_to_entities (list "PersistModuleTest" "psm") (assoc a 5)) (create_entities (list "PersistModuleTest" "NewModule") (lambda (associate "a" 1 "b" 2)) ) @@ -2222,13 +2233,13 @@ (create_entities (list "pt" "child" "doublechild2") (list 10 11)) (store_entity "amlg_code/test_output/pt.amlg" "pt") (destroy_entities "pt") - (load_persistent_entity "amlg_code/test_output/pt.amlg" "ptl") + (load_entity "amlg_code/test_output/pt.amlg" "ptl" (null) (true)) (destroy_entities (list "ptl" "child")) ; persistent grandchild test - (print "Load Root:\n" (load_persistent_entity "amlg_code/persistent_tree_test_root.amlg" "PersistTreeRoot") "\n") + (print "Load Root:\n" (load_entity "amlg_code/persistent_tree_test_root.amlg" "PersistTreeRoot" (null) (true)) "\n") (print "Load Inter:\n" (load_entity "amlg_code/persistent_tree_test_inter.amlg" (list "PersistTreeRoot" "PersistTreeInter")) "\n") - (print "Load Leaf:\n" (load_persistent_entity "amlg_code/persistent_tree_test_leaf.amlg" (list "PersistTreeRoot" "PersistTreeInter" "PersistTreeLeaf")) "\n") + (print "Load Leaf:\n" (load_entity "amlg_code/persistent_tree_test_leaf.amlg" (list "PersistTreeRoot" "PersistTreeInter" "PersistTreeLeaf") (null) (true)) "\n") (print "Root contained:\n" (contained_entities "PersistTreeRoot") "\n") (print "Root b:\n" (retrieve_from_entity "PersistTreeRoot" "b") "\n") (print "Inter contained:\n" (contained_entities (list "PersistTreeRoot" "PersistTreeInter")) "\n") @@ -2281,8 +2292,8 @@ (print "contained entities in quackers before file: " (contained_entities "quackerz?") "\n") - (store_entity "amlg_code/test_output/!quackerz.amlg" "quackerz?" (true)) - (load_entity "amlg_code/test_output/!quackerz.amlg" "read-back quackers" (true)) + (store_entity "amlg_code/test_output/!quackerz.amlg" "quackerz?" (null) (false) (assoc escape_filename (true)) ) + (load_entity "amlg_code/test_output/!quackerz.amlg" "read-back quackers" (null) (false) (assoc escape_filename (true)) ) (print "contained entity in quackers2 loaded back from file: " (contained_entities "read-back quackers") "\n") @@ -2315,8 +2326,8 @@ (print "Compression difference: [" (difference_entities "ModuleTest" "ModuleTestDecompressed") "]\n") (print "store to .json in amlg format\n") - (store "amlg_code/test_output/module_test.json" (list (assoc a 3 b 4) (assoc c "c" d (null))) (false) "amlg") - (print (load "amlg_code/test_output/module_test.json" (false) "amlg")) + (store "amlg_code/test_output/module_test.json" (list (assoc a 3 b 4) (assoc c "c" d (null))) "amlg") + (print (load "amlg_code/test_output/module_test.json" "amlg")) (print "store to .json normally\n") (store "amlg_code/test_output/module_test.json" (list (assoc a 3 b 4) (assoc c "c" d (null)))) @@ -4316,15 +4327,15 @@ (print "concurrent entity writes successful: " (= (range 1 1000) (sort concurrent_ent_writes)) "\n") (print "--clean-up test files--\n") - (declare (assoc rmdir "rmdir /s /q " rmfile "del /s /q " slash "\\")) - (if (!= (system "os") "Windows") - (assign (assoc rmdir "rm -r " rmfile "rm " slash "/")) - ) + ;use variables from the start + (print (concat rmdir "amlg_code" slash "test_output") "\n") (system "system" (concat rmdir "amlg_code" slash "test_output")) + (print (concat rmdir "amlg_code" slash "persistent_tree_test_root") "\n") (system "system" (concat rmdir "amlg_code" slash "persistent_tree_test_root")) + (print (concat rmfile "amlg_code" slash "persist_module_test" slash "psm.mdam") "\n") (system "system" (concat rmfile "amlg_code" slash "persist_module_test" slash "psm.mdam")) + (print (concat rmfile "amlg_code" slash "persist_module_test.mdam") "\n") (system "system" (concat rmfile "amlg_code" slash "persist_module_test.mdam")) - (system "system" (concat rmfile "amlg_code" slash "persistent_tree_test_leaf.mdam")) (print "--total execution time--\n") (print (- (system_time) start_time) "\n") diff --git a/src/Amalgam/entity/Entity.cpp b/src/Amalgam/entity/Entity.cpp index 846e02cf..0a288ac9 100644 --- a/src/Amalgam/entity/Entity.cpp +++ b/src/Amalgam/entity/Entity.cpp @@ -628,8 +628,8 @@ StringInternPool::StringID Entity::AddContainedEntity(Entity *t, StringInternPoo { for(auto &wl : *write_listeners) wl->LogCreateEntity(t); - asset_manager.CreateEntity(t); } + asset_manager.CreateEntity(t); return t->idStringId; } @@ -695,8 +695,8 @@ StringInternPool::StringID Entity::AddContainedEntity(Entity *t, std::string id_ { for(auto &wl : *write_listeners) wl->LogCreateEntity(t); - asset_manager.CreateEntity(t); } + asset_manager.CreateEntity(t); return t->idStringId; } diff --git a/src/Amalgam/entity/EntityExternalInterface.cpp b/src/Amalgam/entity/EntityExternalInterface.cpp index 2f8ec480..b2bffdf5 100644 --- a/src/Amalgam/entity/EntityExternalInterface.cpp +++ b/src/Amalgam/entity/EntityExternalInterface.cpp @@ -29,8 +29,9 @@ void EntityExternalInterface::LoadEntityStatus::SetStatus(bool loaded_in, std::s version = std::move(version_in); } -EntityExternalInterface::LoadEntityStatus EntityExternalInterface::LoadEntity(std::string &handle, std::string &path, bool persistent, bool load_contained_entities, - bool escape_filename, bool escape_contained_filenames, std::string &write_log_filename, std::string &print_log_filename, std::string rand_seed) +EntityExternalInterface::LoadEntityStatus EntityExternalInterface::LoadEntity(std::string &handle, std::string &path, + std::string file_type, bool persistent, std::string_view json_file_params, + std::string &write_log_filename, std::string &print_log_filename, std::string rand_seed) { LoadEntityStatus status; @@ -41,9 +42,16 @@ EntityExternalInterface::LoadEntityStatus EntityExternalInterface::LoadEntity(st rand_seed = std::to_string(t); } - std::string file_type = ""; - Entity *entity = asset_manager.LoadEntityFromResourcePath(path, file_type, persistent, load_contained_entities, - escape_filename, escape_contained_filenames, rand_seed, nullptr, status); + AssetManager::AssetParameters asset_params(path, file_type, true); + + EvaluableNodeManager temp_enm; + EvaluableNode *file_params = EvaluableNodeJSONTranslation::JsonToEvaluableNode(&temp_enm, json_file_params); + + if(EvaluableNode::IsAssociativeArray(file_params)) + asset_params.SetParams(file_params->GetMappedChildNodesReference()); + asset_params.UpdateResources(); + + Entity *entity = asset_manager.LoadEntityFromResource(asset_params, persistent, rand_seed, nullptr, status); if(!status.loaded) return status; @@ -82,7 +90,8 @@ EntityExternalInterface::LoadEntityStatus EntityExternalInterface::VerifyEntity( return EntityExternalInterface::LoadEntityStatus(false, "", version); } -bool EntityExternalInterface::CloneEntity(std::string &handle, std::string &cloned_handle, std::string &path, bool persistent, +bool EntityExternalInterface::CloneEntity(std::string &handle, std::string &cloned_handle, std::string &path, + std::string file_type, bool persistent, std::string_view json_file_params, std::string &write_log_filename, std::string &print_log_filename) { auto bundle = FindEntityBundle(handle); @@ -94,6 +103,15 @@ bool EntityExternalInterface::CloneEntity(std::string &handle, std::string &clon Entity *entity = new Entity(bundle->entity); + AssetManager::AssetParameters asset_params(path, file_type, true); + + auto &enm = bundle->entity->evaluableNodeManager; + EvaluableNode *file_params = EvaluableNodeJSONTranslation::JsonToEvaluableNode(&enm, json_file_params); + + if(EvaluableNode::IsAssociativeArray(file_params)) + asset_params.SetParams(file_params->GetMappedChildNodesReference()); + asset_params.UpdateResources(); + PrintListener *pl = nullptr; std::vector wl; @@ -109,20 +127,32 @@ bool EntityExternalInterface::CloneEntity(std::string &handle, std::string &clon AddEntityBundle(cloned_handle, new EntityListenerBundle(entity, wl, pl)); if(persistent) - StoreEntity(cloned_handle, path, true, true); + asset_manager.StoreEntityToResource(entity, asset_params, true, persistent); return true; } -void EntityExternalInterface::StoreEntity(std::string &handle, std::string &path, bool update_persistence_location, bool store_contained_entities) +void EntityExternalInterface::StoreEntity(std::string &handle, std::string &path, + std::string file_type, bool persistent, std::string_view json_file_params) { auto bundle = FindEntityBundle(handle); if(bundle == nullptr || bundle->entity == nullptr) return; - std::string file_type = ""; EntityReadReference entity(bundle->entity); - asset_manager.StoreEntityToResourcePath(entity, path, file_type, update_persistence_location, store_contained_entities, false, true, false); + + AssetManager::AssetParameters asset_params(path, file_type, true); + + auto &enm = bundle->entity->evaluableNodeManager; + EvaluableNode *file_params = EvaluableNodeJSONTranslation::JsonToEvaluableNode(&enm, json_file_params); + + if(EvaluableNode::IsAssociativeArray(file_params)) + asset_params.SetParams(file_params->GetMappedChildNodesReference()); + asset_params.UpdateResources(); + + enm.FreeNodeTree(file_params); + + asset_manager.StoreEntityToResource(entity, asset_params, true, persistent); } void EntityExternalInterface::ExecuteEntity(std::string &handle, std::string &label) diff --git a/src/Amalgam/entity/EntityExternalInterface.h b/src/Amalgam/entity/EntityExternalInterface.h index f8229d11..e7127036 100644 --- a/src/Amalgam/entity/EntityExternalInterface.h +++ b/src/Amalgam/entity/EntityExternalInterface.h @@ -36,15 +36,18 @@ class EntityExternalInterface std::string version; }; - LoadEntityStatus LoadEntity(std::string &handle, std::string &path, bool persistent, bool load_contained_entities, - bool escape_filename, bool escape_contained_filenames, std::string &write_log_filename, std::string &print_log_filename, + LoadEntityStatus LoadEntity(std::string &handle, std::string &path, + std::string file_type, bool persistent, std::string_view json_file_params, + std::string &write_log_filename, std::string &print_log_filename, std::string rand_seed = std::string("")); LoadEntityStatus VerifyEntity(std::string &path); - bool CloneEntity(std::string &handle, std::string &cloned_handle, std::string &path, bool persistent, + bool CloneEntity(std::string &handle, std::string &cloned_handle, std::string &path, + std::string file_type, bool persistent, std::string_view json_file_params, std::string &write_log_filename, std::string &print_log_filename); - void StoreEntity(std::string &handle, std::string &path, bool update_persistence_location, bool store_contained_entities); + void StoreEntity(std::string &handle, std::string &path, + std::string file_type, bool persistent, std::string_view json_file_params); void ExecuteEntity(std::string &handle, std::string &label); diff --git a/src/Amalgam/evaluablenode/EvaluableNode.h b/src/Amalgam/evaluablenode/EvaluableNode.h index ec3af8ee..b42bb3f6 100644 --- a/src/Amalgam/evaluablenode/EvaluableNode.h +++ b/src/Amalgam/evaluablenode/EvaluableNode.h @@ -770,6 +770,23 @@ class EvaluableNode EvaluableNode *EraseMappedChildNode(const StringInternPool::StringID sid); void AppendMappedChildNodes(AssocType &mcn_to_append); + template + static void GetValueFromMappedChildNodesReference(EvaluableNode::AssocType &mcn, EvaluableNodeBuiltInStringId key, T &value) + { + auto found_value = mcn.find(GetStringIdFromBuiltInStringId(key)); + if(found_value != end(mcn)) + { + if constexpr(std::is_same::value) + value = EvaluableNode::IsTrue(found_value->second); + else if constexpr(std::is_same::value) + value = EvaluableNode::ToNumber(found_value->second); + else if constexpr(std::is_same::value) + value = EvaluableNode::ToStringPreservingOpcodeType(found_value->second); + else + value = found_value->second; + } + } + protected: //defined since it is used as a pointer in EvaluableNodeValue struct EvaluableNodeExtendedValue; diff --git a/src/Amalgam/evaluablenode/EvaluableNodeTreeManipulation.cpp b/src/Amalgam/evaluablenode/EvaluableNodeTreeManipulation.cpp index 6382621a..5c50fbb9 100644 --- a/src/Amalgam/evaluablenode/EvaluableNodeTreeManipulation.cpp +++ b/src/Amalgam/evaluablenode/EvaluableNodeTreeManipulation.cpp @@ -2091,7 +2091,6 @@ CompactHashMap EvaluableNodeTreeManipulation::evaluab {ENT_DESTROY_ENTITIES, 0.1}, {ENT_LOAD, 0.01}, {ENT_LOAD_ENTITY, 0.01}, - {ENT_LOAD_PERSISTENT_ENTITY, 0.01}, {ENT_STORE, 0.01}, {ENT_STORE_ENTITY, 0.01}, {ENT_CONTAINS_ENTITY, 0.1}, diff --git a/src/Amalgam/interpreter/Interpreter.cpp b/src/Amalgam/interpreter/Interpreter.cpp index e28dc8d6..f183f744 100644 --- a/src/Amalgam/interpreter/Interpreter.cpp +++ b/src/Amalgam/interpreter/Interpreter.cpp @@ -242,8 +242,7 @@ std::array Interpreter &Interpreter::InterpretNode_ENT_MOVE_ENTITIES, // ENT_MOVE_ENTITIES &Interpreter::InterpretNode_ENT_DESTROY_ENTITIES, // ENT_DESTROY_ENTITIES &Interpreter::InterpretNode_ENT_LOAD, // ENT_LOAD - &Interpreter::InterpretNode_ENT_LOAD_ENTITY_and_LOAD_PERSISTENT_ENTITY, // ENT_LOAD_ENTITY - &Interpreter::InterpretNode_ENT_LOAD_ENTITY_and_LOAD_PERSISTENT_ENTITY, // ENT_LOAD_PERSIST + &Interpreter::InterpretNode_ENT_LOAD_ENTITY, // ENT_LOAD_ENTITY &Interpreter::InterpretNode_ENT_STORE, // ENT_STORE &Interpreter::InterpretNode_ENT_STORE_ENTITY, // ENT_STORE_ENTITY &Interpreter::InterpretNode_ENT_CONTAINS_ENTITY, // ENT_CONTAINS_ENTITY diff --git a/src/Amalgam/interpreter/Interpreter.h b/src/Amalgam/interpreter/Interpreter.h index 35a592c6..9654d022 100644 --- a/src/Amalgam/interpreter/Interpreter.h +++ b/src/Amalgam/interpreter/Interpreter.h @@ -1261,7 +1261,7 @@ class Interpreter EvaluableNodeReference InterpretNode_ENT_MOVE_ENTITIES(EvaluableNode *en, bool immediate_result); EvaluableNodeReference InterpretNode_ENT_DESTROY_ENTITIES(EvaluableNode *en, bool immediate_result); EvaluableNodeReference InterpretNode_ENT_LOAD(EvaluableNode *en, bool immediate_result); - EvaluableNodeReference InterpretNode_ENT_LOAD_ENTITY_and_LOAD_PERSISTENT_ENTITY(EvaluableNode *en, bool immediate_result); + EvaluableNodeReference InterpretNode_ENT_LOAD_ENTITY(EvaluableNode *en, bool immediate_result); EvaluableNodeReference InterpretNode_ENT_STORE(EvaluableNode *en, bool immediate_result); EvaluableNodeReference InterpretNode_ENT_STORE_ENTITY(EvaluableNode *en, bool immediate_result); EvaluableNodeReference InterpretNode_ENT_CONTAINS_ENTITY(EvaluableNode *en, bool immediate_result); diff --git a/src/Amalgam/interpreter/InterpreterOpcodesDataTypes.cpp b/src/Amalgam/interpreter/InterpreterOpcodesDataTypes.cpp index 03850c04..eab58348 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesDataTypes.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesDataTypes.cpp @@ -537,14 +537,8 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_FORMAT(EvaluableNode *en, if(EvaluableNode::IsAssociativeArray(from_params)) { auto &mcn = from_params->GetMappedChildNodesReference(); - - auto found_locale = mcn.find(GetStringIdFromBuiltInStringId(ENBISI_locale)); - if(found_locale != end(mcn) && !EvaluableNode::IsNull(found_locale->second)) - locale = EvaluableNode::ToStringPreservingOpcodeType(found_locale->second); - - auto found_timezone = mcn.find(GetStringIdFromBuiltInStringId(ENBISI_timezone)); - if(found_timezone != end(mcn) && !EvaluableNode::IsNull(found_timezone->second)) - timezone = EvaluableNode::ToStringPreservingOpcodeType(found_timezone->second); + EvaluableNode::GetValueFromMappedChildNodesReference(mcn, ENBISI_locale, locale); + EvaluableNode::GetValueFromMappedChildNodesReference(mcn, ENBISI_timezone, timezone); } use_number = true; @@ -556,10 +550,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_FORMAT(EvaluableNode *en, if(EvaluableNode::IsAssociativeArray(from_params)) { auto &mcn = from_params->GetMappedChildNodesReference(); - - auto found_locale = mcn.find(GetStringIdFromBuiltInStringId(ENBISI_locale)); - if(found_locale != end(mcn) && !EvaluableNode::IsNull(found_locale->second)) - locale = EvaluableNode::ToStringPreservingOpcodeType(found_locale->second); + EvaluableNode::GetValueFromMappedChildNodesReference(mcn, ENBISI_locale, locale); } use_number = true; @@ -617,10 +608,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_FORMAT(EvaluableNode *en, if(EvaluableNode::IsAssociativeArray(to_params)) { auto &mcn = to_params->GetMappedChildNodesReference(); - - auto found_sort_keys = mcn.find(GetStringIdFromBuiltInStringId(ENBISI_sort_keys)); - if(found_sort_keys != end(mcn)) - sort_keys = EvaluableNode::IsTrue(found_sort_keys->second); + EvaluableNode::GetValueFromMappedChildNodesReference(mcn, ENBISI_sort_keys, sort_keys); } string_value = Parser::Unparse(code_value, evaluableNodeManager, false, true, sort_keys); @@ -814,10 +802,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_FORMAT(EvaluableNode *en, if(EvaluableNode::IsAssociativeArray(to_params)) { auto &mcn = to_params->GetMappedChildNodesReference(); - - auto found_sort_keys = mcn.find(GetStringIdFromBuiltInStringId(ENBISI_sort_keys)); - if(found_sort_keys != end(mcn)) - sort_keys = EvaluableNode::IsTrue(found_sort_keys->second); + EvaluableNode::GetValueFromMappedChildNodesReference(mcn, ENBISI_sort_keys, sort_keys); } std::tie(string_value, valid_string_value) = EvaluableNodeJSONTranslation::EvaluableNodeToJson(code_value, sort_keys); @@ -851,10 +836,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_FORMAT(EvaluableNode *en, if(EvaluableNode::IsAssociativeArray(to_params)) { auto &mcn = to_params->GetMappedChildNodesReference(); - - auto found_sort_keys = mcn.find(GetStringIdFromBuiltInStringId(ENBISI_sort_keys)); - if(found_sort_keys != end(mcn)) - sort_keys = EvaluableNode::IsTrue(found_sort_keys->second); + EvaluableNode::GetValueFromMappedChildNodesReference(mcn, ENBISI_sort_keys, sort_keys); } std::tie(string_value, valid_string_value) = EvaluableNodeYAMLTranslation::EvaluableNodeToYaml(code_value, sort_keys); @@ -872,14 +854,8 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_FORMAT(EvaluableNode *en, if(EvaluableNode::IsAssociativeArray(to_params)) { auto &mcn = to_params->GetMappedChildNodesReference(); - - auto found_locale = mcn.find(GetStringIdFromBuiltInStringId(ENBISI_locale)); - if(found_locale != end(mcn) && !EvaluableNode::IsNull(found_locale->second)) - locale = EvaluableNode::ToStringPreservingOpcodeType(found_locale->second); - - auto found_timezone = mcn.find(GetStringIdFromBuiltInStringId(ENBISI_timezone)); - if(found_timezone != end(mcn) && !EvaluableNode::IsNull(found_timezone->second)) - timezone = EvaluableNode::ToStringPreservingOpcodeType(found_timezone->second); + EvaluableNode::GetValueFromMappedChildNodesReference(mcn, ENBISI_locale, locale); + EvaluableNode::GetValueFromMappedChildNodesReference(mcn, ENBISI_timezone, timezone); } double num_secs_from_epoch = 0.0; @@ -896,10 +872,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_FORMAT(EvaluableNode *en, if(EvaluableNode::IsAssociativeArray(to_params)) { auto &mcn = to_params->GetMappedChildNodesReference(); - - auto found_locale = mcn.find(GetStringIdFromBuiltInStringId(ENBISI_locale)); - if(found_locale != end(mcn) && !EvaluableNode::IsNull(found_locale->second)) - locale = EvaluableNode::ToStringPreservingOpcodeType(found_locale->second); + EvaluableNode::GetValueFromMappedChildNodesReference(mcn, ENBISI_locale, locale); } double num_secs_from_midnight = 0.0; @@ -1662,7 +1635,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_SUBSTR(EvaluableNode *en, return retval; } - else if (submatches) + else if(submatches) { EvaluableNodeReference retval(evaluableNodeManager->AllocNode(ENT_LIST), true); diff --git a/src/Amalgam/interpreter/InterpreterOpcodesEntityControl.cpp b/src/Amalgam/interpreter/InterpreterOpcodesEntityControl.cpp index 71e2d9c9..cf94159d 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesEntityControl.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesEntityControl.cpp @@ -597,28 +597,35 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LOAD(EvaluableNode *en, bo if(!asset_manager.DoesEntityHaveRootPermission(curEntity)) return EvaluableNodeReference::Null(); - std::string resource_name = InterpretNodeIntoStringValueEmptyNull(ocn[0]); - if(resource_name.empty()) + std::string path = InterpretNodeIntoStringValueEmptyNull(ocn[0]); + if(path.empty()) return EvaluableNodeReference::Null(); - bool escape_filename = false; - if(ocn.size() >= 2) - escape_filename = InterpretNodeIntoBoolValue(ocn[1], false); - std::string file_type = ""; - if(ocn.size() >= 3) + if(ocn.size() > 1) { - auto [valid, file_type_temp] = InterpretNodeIntoStringValue(ocn[2]); + auto [valid, file_type_temp] = InterpretNodeIntoStringValue(ocn[1]); if(valid) file_type = file_type_temp; } + AssetManager::AssetParameters asset_params(path, file_type, false); + if(ocn.size() > 2) + { + EvaluableNodeReference params = InterpretNodeForImmediateUse(ocn[2]); + + if(EvaluableNode::IsAssociativeArray(params)) + asset_params.SetParams(params->GetMappedChildNodesReference()); + + evaluableNodeManager->FreeNodeTreeIfPossible(params); + } + asset_params.UpdateResources(); + EntityExternalInterface::LoadEntityStatus status; - std::string resource_base_path; - return asset_manager.LoadResourcePath(resource_name, resource_base_path, file_type, evaluableNodeManager, escape_filename, status); + return asset_manager.LoadResource(asset_params, evaluableNodeManager, status); } -EvaluableNodeReference Interpreter::InterpretNode_ENT_LOAD_ENTITY_and_LOAD_PERSISTENT_ENTITY(EvaluableNode *en, bool immediate_result) +EvaluableNodeReference Interpreter::InterpretNode_ENT_LOAD_ENTITY(EvaluableNode *en, bool immediate_result) { auto &ocn = en->GetOrderedChildNodes(); @@ -628,10 +635,34 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LOAD_ENTITY_and_LOAD_PERSI if(!asset_manager.DoesEntityHaveRootPermission(curEntity)) return EvaluableNodeReference::Null(); - std::string resource_name = InterpretNodeIntoStringValueEmptyNull(ocn[0]); - if(resource_name.empty()) + std::string path = InterpretNodeIntoStringValueEmptyNull(ocn[0]); + if(path.empty()) return EvaluableNodeReference::Null(); + std::string file_type = ""; + if(ocn.size() > 2) + { + auto [valid, file_type_temp] = InterpretNodeIntoStringValue(ocn[2]); + if(valid) + file_type = file_type_temp; + } + + bool persistent = false; + if(ocn.size() > 3) + persistent = InterpretNodeIntoBoolValue(ocn[3]); + + AssetManager::AssetParameters asset_params(path, file_type, true); + if(ocn.size() > 4) + { + EvaluableNodeReference params = InterpretNodeForImmediateUse(ocn[4]); + + if(EvaluableNode::IsAssociativeArray(params)) + asset_params.SetParams(params->GetMappedChildNodesReference()); + + evaluableNodeManager->FreeNodeTreeIfPossible(params); + } + asset_params.UpdateResources(); + //get destination if applicable EntityWriteReference destination_entity_parent; StringRef new_entity_id; @@ -641,37 +672,15 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LOAD_ENTITY_and_LOAD_PERSI if(destination_entity_parent == nullptr) return EvaluableNodeReference::Null(); - bool escape_filename = false; - if(ocn.size() >= 3) - escape_filename = InterpretNodeIntoBoolValue(ocn[2], false); - - bool escape_contained_filenames = true; - if(ocn.size() >= 4) - escape_contained_filenames = InterpretNodeIntoBoolValue(ocn[3], true); - - bool persistent = (en->GetType() == ENT_LOAD_PERSISTENT_ENTITY); - if(persistent) - escape_contained_filenames = true; - - //persistent doesn't allow file_type - std::string file_type = ""; - if(!persistent && ocn.size() >= 5) - { - auto [valid, file_type_temp] = InterpretNodeIntoStringValue(ocn[4]); - if(valid) - file_type = file_type_temp; - } - EntityExternalInterface::LoadEntityStatus status; - std::string random_seed = destination_entity_parent->CreateRandomStreamFromStringAndRand(resource_name); + std::string random_seed = destination_entity_parent->CreateRandomStreamFromStringAndRand(asset_params.resource); #ifdef MULTITHREAD_SUPPORT //this interpreter is no longer executing memoryModificationLock.unlock(); #endif - - Entity *loaded_entity = asset_manager.LoadEntityFromResourcePath(resource_name, file_type, - persistent, true, escape_filename, escape_contained_filenames, random_seed, this, status); + + Entity *loaded_entity = asset_manager.LoadEntityFromResource(asset_params, persistent, random_seed, this, status); #ifdef MULTITHREAD_SUPPORT //this interpreter is executing again @@ -705,45 +714,34 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_STORE(EvaluableNode *en, b if(!asset_manager.DoesEntityHaveRootPermission(curEntity)) return EvaluableNodeReference::Null(); - std::string resource_name = InterpretNodeIntoStringValueEmptyNull(ocn[0]); - if(resource_name.empty()) + std::string path = InterpretNodeIntoStringValueEmptyNull(ocn[0]); + if(path.empty()) return EvaluableNodeReference::Null(); auto to_store = InterpretNodeForImmediateUse(ocn[1]); auto node_stack = CreateOpcodeStackStateSaver(to_store); - bool escape_filename = false; - if(ocn.size() >= 3) - escape_filename = InterpretNodeIntoBoolValue(ocn[2], false); - std::string file_type = ""; - if(ocn.size() >= 4) + if(ocn.size() > 2) { - auto [valid, file_type_temp] = InterpretNodeIntoStringValue(ocn[3]); + auto [valid, file_type_temp] = InterpretNodeIntoStringValue(ocn[2]); if(valid) file_type = file_type_temp; } - bool sort_keys = false; - if(ocn.size() >= 5) + AssetManager::AssetParameters asset_params(path, file_type, false); + if(ocn.size() > 3) { - EvaluableNodeReference params = InterpretNodeForImmediateUse(ocn[4]); + EvaluableNodeReference params = InterpretNodeForImmediateUse(ocn[3]); if(EvaluableNode::IsAssociativeArray(params)) - { - auto &mcn = params->GetMappedChildNodesReference(); - - auto found_sort_keys = mcn.find(GetStringIdFromBuiltInStringId(ENBISI_sort_keys)); - if(found_sort_keys != end(mcn)) - sort_keys = EvaluableNode::IsTrue(found_sort_keys->second); - } + asset_params.SetParams(params->GetMappedChildNodesReference()); evaluableNodeManager->FreeNodeTreeIfPossible(params); } + asset_params.UpdateResources(); - std::string resource_base_path; - bool successful_save = asset_manager.StoreResourcePath(to_store, - resource_name, resource_base_path, file_type, evaluableNodeManager, escape_filename, sort_keys); + bool successful_save = asset_manager.StoreResource(to_store, asset_params, evaluableNodeManager); return ReuseOrAllocReturn(to_store, successful_save, immediate_result); } @@ -758,63 +756,53 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_STORE_ENTITY(EvaluableNode if(!asset_manager.DoesEntityHaveRootPermission(curEntity)) return EvaluableNodeReference::Null(); - std::string resource_name = InterpretNodeIntoStringValueEmptyNull(ocn[0]); - if(resource_name.empty()) + std::string path = InterpretNodeIntoStringValueEmptyNull(ocn[0]); + if(path.empty()) return EvaluableNodeReference::Null(); - bool escape_filename = false; - if(ocn.size() >= 3) - escape_filename = InterpretNodeIntoBoolValue(ocn[2], false); - - bool escape_contained_filenames = true; - if(ocn.size() >= 4) - escape_contained_filenames = InterpretNodeIntoBoolValue(ocn[3], true); - std::string file_type = ""; - if(ocn.size() >= 5) + if(ocn.size() > 2) { - auto [valid, file_type_temp] = InterpretNodeIntoStringValue(ocn[4]); + auto [valid, file_type_temp] = InterpretNodeIntoStringValue(ocn[2]); if(valid) file_type = file_type_temp; } - bool sort_keys = false; - bool include_rand_seeds = true; - bool parallel_create = false; - if(ocn.size() >= 6) + bool update_persistence = false; + bool persistent = false; + if(ocn.size() > 3) { - EvaluableNodeReference params = InterpretNodeForImmediateUse(ocn[5]); - - if(EvaluableNode::IsAssociativeArray(params)) + auto persistence_node = InterpretNodeForImmediateUse(ocn[3]); + if(!EvaluableNode::IsNull(persistence_node)) { - auto &mcn = params->GetMappedChildNodesReference(); - - auto found_sort_keys = mcn.find(GetStringIdFromBuiltInStringId(ENBISI_sort_keys)); - if(found_sort_keys != end(mcn)) - sort_keys = EvaluableNode::IsTrue(found_sort_keys->second); + update_persistence = true; + persistent = EvaluableNode::IsTrue(persistence_node); + } + evaluableNodeManager->FreeNodeTreeIfPossible(persistence_node); + } - auto found_include_rand_seeds = mcn.find(GetStringIdFromBuiltInStringId(ENBISI_include_rand_seeds)); - if(found_include_rand_seeds != end(mcn)) - include_rand_seeds = EvaluableNode::IsTrue(found_include_rand_seeds->second); + AssetManager::AssetParameters asset_params(path, file_type, true); + if(ocn.size() > 4) + { + EvaluableNodeReference params = InterpretNodeForImmediateUse(ocn[4]); - auto found_parallel_create = mcn.find(GetStringIdFromBuiltInStringId(ENBISI_parallel_create)); - if(found_parallel_create != end(mcn)) - parallel_create = EvaluableNode::IsTrue(found_parallel_create->second); - } + if(EvaluableNode::IsAssociativeArray(params)) + asset_params.SetParams(params->GetMappedChildNodesReference()); evaluableNodeManager->FreeNodeTreeIfPossible(params); } + asset_params.UpdateResources(); //get the id of the source entity to store. Don't need to keep the reference because it won't be used once the source entity pointer is looked up //retrieve the entity after other parameters to minimize time in locks // and prevent deadlock if one of the params accessed the entity - //StoreEntityToResourcePath will read lock all contained entities appropriately + //StoreEntityToResource will read lock all contained entities appropriately EntityReadReference source_entity = InterpretNodeIntoRelativeSourceEntityReadReference(ocn[1]); if(source_entity == nullptr || source_entity == curEntity) return EvaluableNodeReference::Null(); - bool stored_successfully = asset_manager.StoreEntityToResourcePath(source_entity, resource_name, file_type, - false, true, escape_filename, escape_contained_filenames, sort_keys, include_rand_seeds, parallel_create); + bool stored_successfully = asset_manager.StoreEntityToResource(source_entity, asset_params, + update_persistence, persistent); return AllocReturn(stored_successfully, immediate_result); } diff --git a/src/Amalgam/string/StringManipulation.cpp b/src/Amalgam/string/StringManipulation.cpp index 3c0c365b..4798890e 100644 --- a/src/Amalgam/string/StringManipulation.cpp +++ b/src/Amalgam/string/StringManipulation.cpp @@ -85,7 +85,7 @@ std::vector StringManipulation::SplitArgString(std::string &arg_str { if(arg_string[cur_pos] == '"') { - if (cur_pos > 0 && arg_string[cur_pos - 1] == '\\') + if(cur_pos > 0 && arg_string[cur_pos - 1] == '\\') { //if quotation is backslashed, remove the backslash cur_arg.pop_back(); diff --git a/test/lib_smoke_test/main.cpp b/test/lib_smoke_test/main.cpp index a47b0d2f..320f4b5c 100644 --- a/test/lib_smoke_test/main.cpp +++ b/test/lib_smoke_test/main.cpp @@ -17,9 +17,11 @@ int main(int argc, char* argv[]) // Load+execute+delete entity: char handle[] = "1"; char* file = (argc > 1) ? argv[1] : (char*)"test.amlg"; + char file_type[] = ""; + char json_file_params[] = ""; char write_log[] = ""; char print_log[] = ""; - auto status = LoadEntity(handle, file, false, true, false, false, write_log, print_log); + auto status = LoadEntity(handle, file, file_type, false, json_file_params, write_log, print_log); if(status.loaded) { char label[] = "test"; @@ -29,4 +31,4 @@ int main(int argc, char* argv[]) } return 1; -} \ No newline at end of file +}