From 07fff6435181154d77ae3235a844f2da20d61de6 Mon Sep 17 00:00:00 2001 From: Alexheeeee Date: Tue, 10 Feb 2026 02:26:09 -0500 Subject: [PATCH 1/7] inherits, repeats, and name changes --- README.md | 40 +- examples/yaml_reader.cpp | 67 ++- lattice_files/ex.pals.yaml | 7 +- lattice_files/include.pals.yaml | 6 +- src/yaml_c_wrapper.cpp | 857 ++++++++++++++++++-------------- src/yaml_c_wrapper.h | 90 ++-- tests/test_yaml_c_wrapper.cpp | 837 ++++++++++++++++--------------- 7 files changed, 999 insertions(+), 905 deletions(-) diff --git a/README.md b/README.md index 7b936e5..2062938 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,10 @@ ## Introduction `yaml_c_wrapper.cpp` wraps `YAML::Node` into C objects so they can be part of a shared object library to interface with other languages - +Implemented components of lattice expansion: +include +inherits +repeat ## Usage In pals-cpp, run @@ -66,6 +39,5 @@ To run a specific test, run ctest --test-dir build -R "Test Name" ``` -## Issue -`yaml-cpp`'s cmake only requires cmake version 3.4, which is deprecated. Warnings must -be suppressed to run properly +## To Do +add test cases for lattice expansion diff --git a/examples/yaml_reader.cpp b/examples/yaml_reader.cpp index 9c5333b..548097d 100644 --- a/examples/yaml_reader.cpp +++ b/examples/yaml_reader.cpp @@ -1,24 +1,38 @@ -#include "../src/yaml_c_wrapper.h" #include +#include "../src/yaml_c_wrapper.h" + +// If file name is provided as a command line argument, this will print out the +// expanded contents of the file to the terminal, as well as to expand.pals.yaml. +// Otherwise, it will use the example file ex.pals.yaml -// See ex.pals.yaml for the example lattice file and expand.pals.yaml for the output of this file. +int main(int argc, char* argv[]) { + if (argc != 1) { + char* filename = argv[1]; + std::string path = "../lattice_files/"; + path += filename; + YAMLNodeHandle handle = parse_file(path.c_str()); -int main() { + lattice_expand(handle); + std::cout << "Printing out contents of file: " << filename << std::endl; + std::cout << yaml_to_string(handle) << std::endl; + write_file(handle, "../lattice_files/expand.pals.yaml"); + return 0; + } // reading a lattice from a yaml file - // allow files to be read in through command line, print back out - YAMLNodeHandle handle = yaml_parse_file("../lattice_files/ex.pals.yaml"); - // print out file name that was read in + YAMLNodeHandle handle = parse_file("../lattice_files/ex.pals.yaml"); + std::cout << "Printing out contents of file: " << "ex.pals.yaml" << std::endl; // printing to terminal std::cout << yaml_to_string(handle) << std::endl << std::endl; // type checking // prints "handle is of type sequence: 1", 1 meaning true - std::cout << "handle is of type sequence: " << (yaml_is_sequence(handle)) << "\n"; + std::cout << "handle is of type sequence: " << (is_sequence(handle)) + << "\n"; // accessing sequence - YAMLNodeHandle node = yaml_get_index(handle, 0); + YAMLNodeHandle node = get_index(handle, 0); /* prints - the first element is: + the first element is: thingB: kind: Sextupole */ @@ -26,29 +40,32 @@ int main() { // accessing map // prints "the value at key 'thingB' is: kind: Sextupole" - std::cout << "\nthe value at key 'thingB' is: " << yaml_to_string(yaml_get_key(node, "thingB")) << "\n"; + std::cout << "\nthe value at key 'thingB' is: " + << yaml_to_string(get_key(node, "thingB")) << "\n"; - // creating a new node that's a map - YAMLNodeHandle map = yaml_create_map(); - yaml_set_int(map, "first", 1); + // creating a new node that's a map + YAMLNodeHandle map = create_map(); + set_value_int(map, "apples", 5); // creating a new node that's a sequence - YAMLNodeHandle sequence = yaml_create_sequence(); - yaml_push_string(sequence, "magnet1"); - yaml_push_string(sequence, ""); - YAMLNodeHandle scalar = yaml_create_scalar(); - yaml_set_scalar_string(scalar, "magnet2"); - yaml_set_at_index(sequence, 1, scalar); + YAMLNodeHandle sequence = create_sequence(); + push_string(sequence, "magnet1"); + push_string(sequence, ""); + YAMLNodeHandle scalar = create_scalar(); + set_scalar_string(scalar, "magnet2"); + set_at_index(sequence, 1, scalar); // give sequence a name by putting it in a map: - YAMLNodeHandle magnets = yaml_create_map(); - yaml_set_node(magnets, "magnets", sequence); + YAMLNodeHandle magnets = create_map(); + set_value_node(magnets, "magnets", sequence); // adding new nodes to lattice - yaml_push_node(handle, map); - yaml_push_node(handle, magnets); + push_node(handle, map); + push_node(handle, magnets); - yaml_expand(handle); + // performing lattice expansion + lattice_expand(handle); // writing modified lattice file to expand.pals.yaml - yaml_write_file(handle, "../lattice_files/expand.pals.yaml"); + write_file(handle, "../lattice_files/expand.pals.yaml"); + return 0; } diff --git a/lattice_files/ex.pals.yaml b/lattice_files/ex.pals.yaml index 1261101..644df78 100644 --- a/lattice_files/ex.pals.yaml +++ b/lattice_files/ex.pals.yaml @@ -1,6 +1,5 @@ - thingB: kind: Sextupole - - inj_line: kind: BeamLine multipass: true @@ -16,3 +15,9 @@ - a_subline: # Item a_subline is repeated three times repeat: 3 - include: "include.pals.yaml" +- a_subline: + kind: BeamLine + line: + - a + - b + - c \ No newline at end of file diff --git a/lattice_files/include.pals.yaml b/lattice_files/include.pals.yaml index 84ec631..b9317a2 100644 --- a/lattice_files/include.pals.yaml +++ b/lattice_files/include.pals.yaml @@ -1,4 +1,4 @@ - line: - first: 1 - second: 2 - thrid: 3 \ No newline at end of file + - first: 1 + - second: 2 + - third: 3 \ No newline at end of file diff --git a/src/yaml_c_wrapper.cpp b/src/yaml_c_wrapper.cpp index 5047716..35f310a 100644 --- a/src/yaml_c_wrapper.cpp +++ b/src/yaml_c_wrapper.cpp @@ -1,45 +1,119 @@ #include -#include #include #include +#include +#include #if defined(_WIN32) - #define YAML_API extern "C" __declspec(dllexport) +#define YAML_API extern "C" __declspec(dllexport) #else - #define YAML_API extern "C" __attribute__((visibility("default"))) +#define YAML_API extern "C" __attribute__((visibility("default"))) #endif // ======= LATTICE EXPANSION UTILS -// === REPLACE === -YAML::Node replace_internal(YAML::Node name, std::map* seen) { - std::string str = name.as(); - if (seen->count(str)) { - return YAML::Clone(seen->at(str)); - } else { - return name; +/* +Inserts the elements in `line` into `seq` at `index` a `repeat` number of times. +The value at index will be rewritten by the first inserted element. For example, +if seq = [1,2,3,4,5], line = [a,b], index = 2, repeat = 3, calling the function +will output [1,2,a,b,a,b,a,b,4,5]. +*/ +YAML::Node repeat(YAML::Node line, YAML::Node seq, int index, int repeat) { + YAML::Node exp = YAML::Node(YAML::NodeType::Sequence); + for (int i = 0; i < index; i++) { + exp.push_back(YAML::Clone(seq[i])); + } + for (int i = 0; i < repeat; i++) { + for (int j = 0; j < line.size(); j++) { + exp.push_back(YAML::Clone(line[j])); + } } + for (int i = index + 1; i < seq.size(); i++) { + exp.push_back(YAML::Clone(seq[i])); + } + return exp; +} + +// recursively loops through the node to record all the elements and their +// corresponding parameters +void get_dict_helper(YAML::Node node, std::map* seen) { + if (node.IsSequence()) { + for (int i = 0; i < node.size(); i++) { + get_dict_helper(node[i], seen); + } + } else if (node.IsMap()) { + for (auto ele : node) { + // exclude key words from being stored + if (ele.first.as() == "include" || + ele.first.as() == "inherit" || + (ele.second.IsMap() && ele.second["repeat"])) { + continue; + } + seen->insert( + {ele.first.as(), YAML::Clone(ele.second)}); + get_dict_helper(ele.second, seen); + } + } +} + +std::map* get_dict(YAML::Node root) { + std::map* seen = + new std::map(); + ; + get_dict_helper(root, seen); + return seen; } // === EXPAND === -YAML::Node expand_internal(YAML::Node node, std::map* seen) { +YAML::Node expand_internal(YAML::Node node, + std::map* seen) { if (node.IsSequence()) { + bool modified = true; + while (modified) { + modified = false; + for (int i = 0; i < node.size(); i++) { + if (node[i].IsMap()) { + for (auto ele : node[i]) { + if (ele.second.IsMap() && ele.second["repeat"]) { + YAML::Node line = + (*seen)[ele.first.as()]["line"]; + node = repeat(line, node, i, + ele.second["repeat"].as()); + modified = true; + break; + } + } + if (modified) break; + } + } + } for (int i = 0; i < node.size(); i++) { node[i] = expand_internal(node[i], seen); } return node; - } else if (node.IsScalar()) { - return replace_internal(node, seen); } else if (node.IsMap()) { for (auto ele : node) { // will probably need to add better method of finding files if (ele.first.as() == "include") { - std::string str = ele.second.as(); - std::string filename = str.substr(0, str.length()); - node = YAML::LoadFile("../lattice_files/" + filename); + std::string filename = ele.second.as(); + YAML::Node seq = YAML::Node(YAML::NodeType::Sequence); + YAML::Node file = YAML::Node(YAML::NodeType::Map); + file["file"] = filename; + seq.push_back(file); + seq.push_back( + YAML::LoadFile("../lattice_files/" + filename)[0]); + + ele.first = "included"; + ele.second = seq; break; + } else if (ele.first.as() == "inherit") { + YAML::Node parent = (*seen)[ele.second.as()]; + for (auto ele : parent) { + node[ele.first.as()] = ele.second; + } + ele.first = "inherited"; } - seen->insert({ele.first.as(), ele.second}); - node[ele.first.as()] = expand_internal(ele.second, seen); + node[ele.first.as()] = + expand_internal(ele.second, seen); } return node; } else { @@ -48,398 +122,423 @@ YAML::Node expand_internal(YAML::Node node, std::map* s } extern "C" { - YAML_API typedef void* YAMLNodeHandle; - - // === CREATION/DELETION === - YAML_API YAMLNodeHandle yaml_create_node() { - return new YAML::Node(); - } - - YAML_API YAMLNodeHandle yaml_create_map() { - auto node = new YAML::Node(); - *node = YAML::Node(YAML::NodeType::Map); - return node; - } - - YAML_API YAMLNodeHandle yaml_create_sequence() { - auto node = new YAML::Node(); - *node = YAML::Node(YAML::NodeType::Sequence); - return node; +typedef void* YAMLNodeHandle; + +// === CREATION/DELETION === +YAML_API YAMLNodeHandle create_node() { return new YAML::Node(); } + +YAML_API YAMLNodeHandle create_map() { + auto node = new YAML::Node(); + *node = YAML::Node(YAML::NodeType::Map); + return node; +} + +YAML_API YAMLNodeHandle create_sequence() { + auto node = new YAML::Node(); + *node = YAML::Node(YAML::NodeType::Sequence); + return node; +} + +YAML_API YAMLNodeHandle create_scalar() { + auto node = new YAML::Node(); + *node = YAML::Node(YAML::NodeType::Scalar); + return node; +} + +YAML_API void delete_node(YAMLNodeHandle handle) { + delete static_cast(handle); +} + +// === PARSING === +YAML_API YAMLNodeHandle parse_string(const char* yaml_str) { + try { + return new YAML::Node(YAML::Load(yaml_str)); + } catch (...) { + return nullptr; } - - YAML_API YAMLNodeHandle yaml_create_scalar() { - auto node = new YAML::Node(); - *node = YAML::Node(YAML::NodeType::Scalar); - return node; +} + +YAML_API YAMLNodeHandle parse_file(const char* filename) { + try { + return new YAML::Node(YAML::LoadFile(filename)); + } catch (...) { + return nullptr; } +} - YAML_API void yaml_delete_node(YAMLNodeHandle handle) { - delete static_cast(handle); +// === TYPE CHECKS === +YAML_API bool is_scalar(YAMLNodeHandle handle) { + return static_cast(handle)->IsScalar(); +} + +YAML_API bool is_sequence(YAMLNodeHandle handle) { + return static_cast(handle)->IsSequence(); +} + +YAML_API bool is_map(YAMLNodeHandle handle) { + return static_cast(handle)->IsMap(); +} + +YAML_API bool is_null(YAMLNodeHandle handle) { + return static_cast(handle)->IsNull(); +} + +// === ACCESS === +// equivalent to map[key] +YAML_API YAMLNodeHandle get_key(YAMLNodeHandle handle, const char* key) { + auto node = static_cast(handle); + auto child = (*node)[key]; + if (!child.IsDefined()) { + return nullptr; + } + return new YAML::Node(child); +} + +YAML_API YAMLNodeHandle get_index(YAMLNodeHandle handle, int index) { + auto node = static_cast(handle); + if (index < 0 || index >= node->size()) { + return nullptr; } - - // === PARSING === - YAML_API YAMLNodeHandle yaml_parse(const char* yaml_str) { - try { - return new YAML::Node(YAML::Load(yaml_str)); - } catch (...) { - return nullptr; - } + return new YAML::Node((*node)[index]); +} + +YAML_API bool has_key(YAMLNodeHandle handle, const char* key) { + auto node = static_cast(handle); + return (*node)[key].IsDefined(); +} + +YAML_API int size(YAMLNodeHandle handle) { + return static_cast(handle)->size(); +} + +YAML_API char** get_keys(YAMLNodeHandle handle, int* out_count) { + auto node = static_cast(handle); + if (!node->IsMap()) { + *out_count = 0; + return nullptr; } - - YAML_API YAMLNodeHandle yaml_parse_file(const char* filename) { - try { - return new YAML::Node(YAML::LoadFile(filename)); - } catch (...) { - return nullptr; - } + + std::vector keys; + for (auto it = node->begin(); it != node->end(); ++it) { + keys.push_back(it->first.as()); } - - // === TYPE CHECKS === - YAML_API bool yaml_is_scalar(YAMLNodeHandle handle) { - return static_cast(handle)->IsScalar(); + + *out_count = keys.size(); + char** result = new char*[keys.size()]; + for (size_t i = 0; i < keys.size(); i++) { + result[i] = new char[keys[i].length() + 1]; + strcpy(result[i], keys[i].c_str()); } - - YAML_API bool yaml_is_sequence(YAMLNodeHandle handle) { - return static_cast(handle)->IsSequence(); + return result; +} + +// === CONVERT TO C TYPES (caller must free returned strings) === +YAML_API char* as_string(YAMLNodeHandle handle) { + try { + auto str = static_cast(handle)->as(); + char* result = new char[str.length() + 1]; + strcpy(result, str.c_str()); + return result; + } catch (...) { + return nullptr; } - - YAML_API bool yaml_is_map(YAMLNodeHandle handle) { - return static_cast(handle)->IsMap(); +} + +YAML_API int as_int(YAMLNodeHandle handle) { + try { + return static_cast(handle)->as(); + } catch (...) { + return 0; } - - YAML_API bool yaml_is_null(YAMLNodeHandle handle) { - return static_cast(handle)->IsNull(); +} + +YAML_API double as_float(YAMLNodeHandle handle) { + try { + return static_cast(handle)->as(); + } catch (...) { + return 0.0; } - - // === ACCESS === - YAML_API YAMLNodeHandle yaml_get_key(YAMLNodeHandle handle, const char* key) { - auto node = static_cast(handle); - auto child = (*node)[key]; - if (!child.IsDefined()) { - return nullptr; - } - return new YAML::Node(child); +} + +YAML_API bool as_bool(YAMLNodeHandle handle) { + try { + return static_cast(handle)->as(); + } catch (...) { + return false; } - - YAML_API YAMLNodeHandle yaml_get_index(YAMLNodeHandle handle, int index) { +} + +// === MODIFICATION === +YAML_API void set_value_string(YAMLNodeHandle handle, const char* key, + const char* value) { + auto node = static_cast(handle); + (*node)[key] = value; +} + +YAML_API void set_value_int(YAMLNodeHandle handle, const char* key, int value) { + auto node = static_cast(handle); + (*node)[key] = value; +} + +YAML_API void set_value_float(YAMLNodeHandle handle, const char* key, + double value) { + auto node = static_cast(handle); + (*node)[key] = value; +} + +YAML_API void set_value_bool(YAMLNodeHandle handle, const char* key, + bool value) { + auto node = static_cast(handle); + (*node)[key] = value; +} + +YAML_API void set_value_node(YAMLNodeHandle handle, const char* key, + YAMLNodeHandle value) { + auto node = static_cast(handle); + auto val_node = static_cast(value); + (*node)[key] = *val_node; +} + +YAML_API void set_scalar_string(YAMLNodeHandle handle, const char* value) { + if (handle == nullptr || value == nullptr) return; + auto node = static_cast(handle); + *node = value; +} + +YAML_API void set_scalar_int(YAMLNodeHandle handle, int value) { + if (handle == nullptr) return; + auto node = static_cast(handle); + *node = value; +} + +YAML_API void set_scalar_float(YAMLNodeHandle handle, double value) { + if (handle == nullptr) return; + auto node = static_cast(handle); + *node = value; +} + +YAML_API void set_scalar_bool(YAMLNodeHandle handle, bool value) { + if (handle == nullptr) return; + auto node = static_cast(handle); + *node = value; +} + +// Set node at index for sequences +YAML_API void set_at_index(YAMLNodeHandle handle, int index, + YAMLNodeHandle value) { + auto node = static_cast(handle); + auto val_node = static_cast(value); + (*node)[index] = *val_node; +} + +YAML_API void push_string(YAMLNodeHandle handle, const char* value) { + auto node = static_cast(handle); + node->push_back(value); +} + +YAML_API void push_int(YAMLNodeHandle handle, int value) { + auto node = static_cast(handle); + node->push_back(value); +} + +YAML_API void push_float(YAMLNodeHandle handle, double value) { + auto node = static_cast(handle); + node->push_back(value); +} + +YAML_API void push_node(YAMLNodeHandle handle, YAMLNodeHandle value) { + auto node = static_cast(handle); + auto val_node = static_cast(value); + node->push_back(*val_node); +} + +// === WRITE TO FILE WITH EMITTER === + +YAML_API bool write_file(YAMLNodeHandle handle, const char* filename) { + try { auto node = static_cast(handle); - if (index < 0 || index >= node->size()) { - return nullptr; + + std::ofstream fout(filename); + if (!fout.is_open()) { + return false; } - return new YAML::Node((*node)[index]); - } - - YAML_API bool yaml_has_key(YAMLNodeHandle handle, const char* key) { - auto node = static_cast(handle); - return (*node)[key].IsDefined(); - } - - YAML_API int yaml_size(YAMLNodeHandle handle) { - return static_cast(handle)->size(); + + YAML::Emitter out; + out << *node; + + fout << out.c_str(); + fout.close(); + return true; + } catch (...) { + return false; } +} - YAML_API char** yaml_get_keys(YAMLNodeHandle handle, int* out_count) { +// Write with emitter control (removed SetWrap) +YAML_API bool write_file_formatted(YAMLNodeHandle handle, const char* filename, + int indent, bool flow_maps, bool flow_seqs) { + try { auto node = static_cast(handle); - if (!node->IsMap()) { - *out_count = 0; - return nullptr; - } - - std::vector keys; - for (auto it = node->begin(); it != node->end(); ++it) { - keys.push_back(it->first.as()); - } - - *out_count = keys.size(); - char** result = new char*[keys.size()]; - for (size_t i = 0; i < keys.size(); i++) { - result[i] = new char[keys[i].length() + 1]; - strcpy(result[i], keys[i].c_str()); - } - return result; - } - - // === CONVERT TO C TYPES (caller must free returned strings) === - YAML_API char* yaml_as_string(YAMLNodeHandle handle) { - try { - auto str = static_cast(handle)->as(); - char* result = new char[str.length() + 1]; - strcpy(result, str.c_str()); - return result; - } catch (...) { - return nullptr; - } - } - - YAML_API int yaml_as_int(YAMLNodeHandle handle) { - try { - return static_cast(handle)->as(); - } catch (...) { - return 0; - } - } - - YAML_API double yaml_as_float(YAMLNodeHandle handle) { - try { - return static_cast(handle)->as(); - } catch (...) { - return 0.0; - } - } - - YAML_API bool yaml_as_bool(YAMLNodeHandle handle) { - try { - return static_cast(handle)->as(); - } catch (...) { + + std::ofstream fout(filename); + if (!fout.is_open()) { return false; } - } - - // === MODIFICATION === - YAML_API void yaml_set_string(YAMLNodeHandle handle, const char* key, const char* value) { - auto node = static_cast(handle); - (*node)[key] = value; - } - - YAML_API void yaml_set_int(YAMLNodeHandle handle, const char* key, int value) { - auto node = static_cast(handle); - (*node)[key] = value; - } - - YAML_API void yaml_set_float(YAMLNodeHandle handle, const char* key, double value) { - auto node = static_cast(handle); - (*node)[key] = value; - } - - YAML_API void yaml_set_bool(YAMLNodeHandle handle, const char* key, bool value) { - auto node = static_cast(handle); - (*node)[key] = value; - } - - YAML_API void yaml_set_node(YAMLNodeHandle handle, const char* key, YAMLNodeHandle value) { - auto node = static_cast(handle); - auto val_node = static_cast(value); - (*node)[key] = *val_node; - } - YAML_API void yaml_set_scalar_string(YAMLNodeHandle handle, const char* value) { - if (handle == nullptr || value == nullptr) return; - auto node = static_cast(handle); - *node = value; - } - - YAML_API void yaml_set_scalar_int(YAMLNodeHandle handle, int value) { - if (handle == nullptr) return; - auto node = static_cast(handle); - *node = value; - } - - YAML_API void yaml_set_scalar_float(YAMLNodeHandle handle, double value) { - if (handle == nullptr) return; - auto node = static_cast(handle); - *node = value; - } - - YAML_API void yaml_set_scalar_bool(YAMLNodeHandle handle, bool value) { - if (handle == nullptr) return; - auto node = static_cast(handle); - *node = value; - } - - // Set node at index for sequences - YAML_API void yaml_set_at_index(YAMLNodeHandle handle, int index, YAMLNodeHandle value) { - auto node = static_cast(handle); - auto val_node = static_cast(value); - (*node)[index] = *val_node; - } - - YAML_API void yaml_push_string(YAMLNodeHandle handle, const char* value) { - auto node = static_cast(handle); - node->push_back(value); - } - - YAML_API void yaml_push_int(YAMLNodeHandle handle, int value) { - auto node = static_cast(handle); - node->push_back(value); + YAML::Emitter out; + + // Set formatting options (only valid ones) + out.SetIndent(indent); + out.SetMapFormat(flow_maps ? YAML::Flow : YAML::Block); + out.SetSeqFormat(flow_seqs ? YAML::Flow : YAML::Block); + out.SetBoolFormat(YAML::TrueFalseBool); // true/false instead of yes/no + out.SetNullFormat(YAML::LowerNull); // null instead of ~ + out.SetStringFormat(YAML::Auto); // Auto-detect if quotes needed + + out << *node; + + fout << out.c_str(); + fout.close(); + return true; + } catch (...) { + return false; } - - YAML_API void yaml_push_float(YAMLNodeHandle handle, double value) { +} + +// Get YAML string with emitter +YAML_API char* yaml_emit(YAMLNodeHandle handle, int indent) { + try { auto node = static_cast(handle); - node->push_back(value); + + YAML::Emitter out; + out.SetIndent(indent); + out.SetBoolFormat(YAML::TrueFalseBool); + out.SetNullFormat(YAML::LowerNull); + out << *node; + + std::string str = out.c_str(); + char* result = new char[str.length() + 1]; + strcpy(result, str.c_str()); + return result; + } catch (...) { + return nullptr; } - - YAML_API void yaml_push_node(YAMLNodeHandle handle, YAMLNodeHandle value) { +} + +// Advanced version with all available options +YAML_API bool write_file_advanced( + YAMLNodeHandle handle, const char* filename, int indent, bool flow_maps, + bool flow_seqs, + int bool_format, // 0=YesNo, 1=TrueFalse, 2=OnOff + int null_format, // 0=Tilde (~), 1=Null (null), 2=NULL, 3=Null + int string_format) { // 0=Auto, 1=SingleQuoted, 2=DoubleQuoted, 3=Literal + try { auto node = static_cast(handle); - auto val_node = static_cast(value); - node->push_back(*val_node); - } - // === WRITE TO FILE WITH EMITTER === - - YAML_API bool yaml_write_file(YAMLNodeHandle handle, const char* filename) { - try { - auto node = static_cast(handle); - - std::ofstream fout(filename); - if (!fout.is_open()) { - return false; - } - - YAML::Emitter out; - out << *node; - - fout << out.c_str(); - fout.close(); - return true; - } catch (...) { + std::ofstream fout(filename); + if (!fout.is_open()) { return false; } - } - - // Write with emitter control (removed SetWrap) - YAML_API bool yaml_write_file_formatted(YAMLNodeHandle handle, const char* filename, - int indent, bool flow_maps, bool flow_seqs) { - try { - auto node = static_cast(handle); - - std::ofstream fout(filename); - if (!fout.is_open()) { - return false; - } - - YAML::Emitter out; - - // Set formatting options (only valid ones) - out.SetIndent(indent); - out.SetMapFormat(flow_maps ? YAML::Flow : YAML::Block); - out.SetSeqFormat(flow_seqs ? YAML::Flow : YAML::Block); - out.SetBoolFormat(YAML::TrueFalseBool); // true/false instead of yes/no - out.SetNullFormat(YAML::LowerNull); // null instead of ~ - out.SetStringFormat(YAML::Auto); // Auto-detect if quotes needed - - out << *node; - - fout << out.c_str(); - fout.close(); - return true; - } catch (...) { - return false; - } - } - - // Get YAML string with emitter - YAML_API char* yaml_emit(YAMLNodeHandle handle, int indent) { - try { - auto node = static_cast(handle); - - YAML::Emitter out; - out.SetIndent(indent); - out.SetBoolFormat(YAML::TrueFalseBool); - out.SetNullFormat(YAML::LowerNull); - out << *node; - - std::string str = out.c_str(); - char* result = new char[str.length() + 1]; - strcpy(result, str.c_str()); - return result; - } catch (...) { - return nullptr; - } - } - - // Advanced version with all available options - YAML_API bool yaml_write_file_advanced(YAMLNodeHandle handle, const char* filename, - int indent, - bool flow_maps, - bool flow_seqs, - int bool_format, // 0=YesNo, 1=TrueFalse, 2=OnOff - int null_format, // 0=Tilde (~), 1=Null (null), 2=NULL, 3=Null - int string_format) { // 0=Auto, 1=SingleQuoted, 2=DoubleQuoted, 3=Literal - try { - auto node = static_cast(handle); - - std::ofstream fout(filename); - if (!fout.is_open()) { - return false; - } - - YAML::Emitter out; - out.SetIndent(indent); - out.SetMapFormat(flow_maps ? YAML::Flow : YAML::Block); - out.SetSeqFormat(flow_seqs ? YAML::Flow : YAML::Block); - - // Boolean format - switch(bool_format) { - case 0: out.SetBoolFormat(YAML::YesNoBool); break; - case 1: out.SetBoolFormat(YAML::TrueFalseBool); break; - case 2: out.SetBoolFormat(YAML::OnOffBool); break; - default: out.SetBoolFormat(YAML::TrueFalseBool); - } - - // Null format - switch(null_format) { - case 0: out.SetNullFormat(YAML::TildeNull); break; // ~ - case 1: out.SetNullFormat(YAML::LowerNull); break; // null - case 2: out.SetNullFormat(YAML::UpperNull); break; // NULL - case 3: out.SetNullFormat(YAML::CamelNull); break; // Null - default: out.SetNullFormat(YAML::LowerNull); - } - - // String format - switch(string_format) { - case 0: out.SetStringFormat(YAML::Auto); break; - case 1: out.SetStringFormat(YAML::SingleQuoted); break; - case 2: out.SetStringFormat(YAML::DoubleQuoted); break; - case 3: out.SetStringFormat(YAML::Literal); break; - default: out.SetStringFormat(YAML::Auto); - } - - out << *node; - - fout << out.c_str(); - fout.close(); - return true; - } catch (...) { - return false; + + YAML::Emitter out; + out.SetIndent(indent); + out.SetMapFormat(flow_maps ? YAML::Flow : YAML::Block); + out.SetSeqFormat(flow_seqs ? YAML::Flow : YAML::Block); + + // Boolean format + switch (bool_format) { + case 0: + out.SetBoolFormat(YAML::YesNoBool); + break; + case 1: + out.SetBoolFormat(YAML::TrueFalseBool); + break; + case 2: + out.SetBoolFormat(YAML::OnOffBool); + break; + default: + out.SetBoolFormat(YAML::TrueFalseBool); } - } - - // === UTILITY === - YAML_API char* yaml_to_string(YAMLNodeHandle handle) { - try { - YAML::Emitter out; - out << *static_cast(handle); - std::string str = out.c_str(); - char* result = new char[str.length() + 1]; - strcpy(result, str.c_str()); - return result; - } catch (...) { - return nullptr; + + // Null format + switch (null_format) { + case 0: + out.SetNullFormat(YAML::TildeNull); + break; // ~ + case 1: + out.SetNullFormat(YAML::LowerNull); + break; // null + case 2: + out.SetNullFormat(YAML::UpperNull); + break; // NULL + case 3: + out.SetNullFormat(YAML::CamelNull); + break; // Null + default: + out.SetNullFormat(YAML::LowerNull); } - } - - // Helper to free strings allocated by this library - YAML_API void yaml_free_string(char* str) { - delete[] str; - } - YAML_API void yaml_free_keys(char** keys, int count) { - for (int i = 0; i < count; i++) { - delete[] keys[i]; + // String format + switch (string_format) { + case 0: + out.SetStringFormat(YAML::Auto); + break; + case 1: + out.SetStringFormat(YAML::SingleQuoted); + break; + case 2: + out.SetStringFormat(YAML::DoubleQuoted); + break; + case 3: + out.SetStringFormat(YAML::Literal); + break; + default: + out.SetStringFormat(YAML::Auto); } - delete[] keys; + + out << *node; + + fout << out.c_str(); + fout.close(); + return true; + } catch (...) { + return false; } - - - YAML_API YAMLNodeHandle yaml_clone(YAMLNodeHandle handle) { - return new YAML::Node(YAML::Clone(*static_cast(handle))); +} + +// === UTILITY === +YAML_API char* yaml_to_string(YAMLNodeHandle handle) { + try { + YAML::Emitter out; + out << *static_cast(handle); + std::string str = out.c_str(); + char* result = new char[str.length() + 1]; + strcpy(result, str.c_str()); + return result; + } catch (...) { + return nullptr; } - - // C interface for expand - handles the map internally - YAML_API YAMLNodeHandle yaml_expand(YAMLNodeHandle handle) { - auto node = static_cast(handle); - std::map seen; - YAML::Node result = expand_internal(*node, &seen); - return new YAML::Node(result); +} + +// Helper to free strings allocated by this library +YAML_API void yaml_free_string(char* str) { delete[] str; } + +YAML_API void yaml_free_keys(char** keys, int count) { + for (int i = 0; i < count; i++) { + delete[] keys[i]; } + delete[] keys; +} + +YAML_API YAMLNodeHandle yaml_clone(YAMLNodeHandle handle) { + return new YAML::Node(YAML::Clone(*static_cast(handle))); +} + +// C interface for expand - handles the map internally +YAML_API YAMLNodeHandle lattice_expand(YAMLNodeHandle handle) { + auto node = static_cast(handle); + std::map* seen = get_dict(*node); + YAML::Node result = expand_internal(*node, seen); + return new YAML::Node(result); +} } \ No newline at end of file diff --git a/src/yaml_c_wrapper.h b/src/yaml_c_wrapper.h index 319a9cb..40e7318 100644 --- a/src/yaml_c_wrapper.h +++ b/src/yaml_c_wrapper.h @@ -10,70 +10,72 @@ extern "C" { typedef void* YAMLNodeHandle; // === CREATION === -YAMLNodeHandle yaml_create_node(void); -YAMLNodeHandle yaml_create_map(void); -YAMLNodeHandle yaml_create_sequence(void); -YAMLNodeHandle yaml_create_scalar(); -void yaml_delete_node(YAMLNodeHandle handle); +YAMLNodeHandle create_node(void); +YAMLNodeHandle create_map(void); +YAMLNodeHandle create_sequence(void); +YAMLNodeHandle create_scalar(); +void delete_node(YAMLNodeHandle handle); // === PARSING === -YAMLNodeHandle yaml_parse(const char* yaml_str); -YAMLNodeHandle yaml_parse_file(const char* filename); +YAMLNodeHandle parse_string(const char* yaml_str); +YAMLNodeHandle parse_file(const char* filename); // === TYPE CHECKING === -bool yaml_is_scalar(YAMLNodeHandle handle); -bool yaml_is_sequence(YAMLNodeHandle handle); -bool yaml_is_map(YAMLNodeHandle handle); -bool yaml_is_null(YAMLNodeHandle handle); +bool is_scalar(YAMLNodeHandle handle); +bool is_sequence(YAMLNodeHandle handle); +bool is_map(YAMLNodeHandle handle); +bool is_null(YAMLNodeHandle handle); // === ACCESS === -YAMLNodeHandle yaml_get_key(YAMLNodeHandle handle, const char* key); -YAMLNodeHandle yaml_get_index(YAMLNodeHandle handle, int index); -bool yaml_has_key(YAMLNodeHandle handle, const char* key); -int yaml_size(YAMLNodeHandle handle); +YAMLNodeHandle get_key(YAMLNodeHandle handle, const char* key); +YAMLNodeHandle get_index(YAMLNodeHandle handle, int index); +bool has_key(YAMLNodeHandle handle, const char* key); +int size(YAMLNodeHandle handle); +char** get_keys(YAMLNodeHandle handle, int* out_count); +void yaml_free_keys(char** keys, int count); // === CONVERSION === -char* yaml_as_string(YAMLNodeHandle handle); -int yaml_as_int(YAMLNodeHandle handle); -double yaml_as_float(YAMLNodeHandle handle); -bool yaml_as_bool(YAMLNodeHandle handle); +char* as_string(YAMLNodeHandle handle); +int as_int(YAMLNodeHandle handle); +double as_float(YAMLNodeHandle handle); +bool as_bool(YAMLNodeHandle handle); void yaml_free_string(char* str); // === MODIFICATION === -void yaml_set_string(YAMLNodeHandle handle, const char* key, const char* value); -void yaml_set_int(YAMLNodeHandle handle, const char* key, int value); -void yaml_set_float(YAMLNodeHandle handle, const char* key, double value); -void yaml_set_bool(YAMLNodeHandle handle, const char* key, bool value); -void yaml_set_node(YAMLNodeHandle handle, const char* key, YAMLNodeHandle value); - -void yaml_set_scalar_string(YAMLNodeHandle handle, const char* value); -void yaml_set_scalar_int(YAMLNodeHandle handle, int value); -void yaml_set_scalar_float(YAMLNodeHandle handle, double value); -void yaml_set_scalar_bool(YAMLNodeHandle handle, bool value); - -void yaml_set_at_index(YAMLNodeHandle handle, int index, YAMLNodeHandle value) ; -void yaml_push_string(YAMLNodeHandle handle, const char* value); -void yaml_push_int(YAMLNodeHandle handle, int value); -void yaml_push_float(YAMLNodeHandle handle, double value); +void set_value_string(YAMLNodeHandle handle, const char* key, + const char* value); +void set_value_int(YAMLNodeHandle handle, const char* key, int value); +void set_value_float(YAMLNodeHandle handle, const char* key, double value); +void set_value_bool(YAMLNodeHandle handle, const char* key, bool value); +void set_value_node(YAMLNodeHandle handle, const char* key, + YAMLNodeHandle value); + +void set_scalar_string(YAMLNodeHandle handle, const char* value); +void set_scalar_int(YAMLNodeHandle handle, int value); +void set_scalar_float(YAMLNodeHandle handle, double value); +void set_scalar_bool(YAMLNodeHandle handle, bool value); + +void set_at_index(YAMLNodeHandle handle, int index, YAMLNodeHandle value); +void push_string(YAMLNodeHandle handle, const char* value); +void push_int(YAMLNodeHandle handle, int value); +void push_float(YAMLNodeHandle handle, double value); void yaml_push_bool(YAMLNodeHandle handle, bool value); -void yaml_push_node(YAMLNodeHandle handle, YAMLNodeHandle value); +void push_node(YAMLNodeHandle handle, YAMLNodeHandle value); // === UTILITY === char* yaml_to_string(YAMLNodeHandle handle); char* yaml_emit(YAMLNodeHandle handle, int indent); YAMLNodeHandle yaml_clone(YAMLNodeHandle handle); -YAMLNodeHandle yaml_expand(YAMLNodeHandle handle); +YAMLNodeHandle lattice_expand(YAMLNodeHandle handle); -bool yaml_write_file(YAMLNodeHandle handle, const char* filename); -bool yaml_write_file_formatted(YAMLNodeHandle handle, const char* filename, - int indent, bool flow_maps, bool flow_seqs); - -// === KEY UTILITIES === -char** yaml_get_keys(YAMLNodeHandle handle, int* out_count); -void yaml_free_keys(char** keys, int count); +bool write_file(YAMLNodeHandle handle, const char* filename); +bool write_file_formatted(YAMLNodeHandle handle, const char* filename, + int indent, bool flow_maps, bool flow_seqs); +bool write_file_advanced(YAMLNodeHandle handle, const char* filename, int indent, bool flow_maps, + bool flow_seqs, int bool_format, int null_format, int string_format); #ifdef __cplusplus } #endif -#endif // YAML_C_WRAPPER_H \ No newline at end of file +#endif // YAML_C_WRAPPER_H \ No newline at end of file diff --git a/tests/test_yaml_c_wrapper.cpp b/tests/test_yaml_c_wrapper.cpp index e3e54e9..2b8b669 100644 --- a/tests/test_yaml_c_wrapper.cpp +++ b/tests/test_yaml_c_wrapper.cpp @@ -1,10 +1,11 @@ #define CATCH_CONFIG_MAIN -#include #include -#include "../src/yaml_c_wrapper.h" +#include +#include #include #include -#include + +#include "../src/yaml_c_wrapper.h" using Catch::Approx; @@ -34,30 +35,30 @@ void cleanup_file(const std::string& filename) { TEST_CASE("YAML nodes can be created and deleted", "[creation]") { SECTION("Create empty node") { - YAMLNodeHandle node = yaml_create_node(); + YAMLNodeHandle node = create_node(); REQUIRE(node != nullptr); - yaml_delete_node(node); + delete_node(node); } - + SECTION("Create map node") { - YAMLNodeHandle map = yaml_create_map(); + YAMLNodeHandle map = create_map(); REQUIRE(map != nullptr); - REQUIRE(yaml_is_map(map)); - yaml_delete_node(map); + REQUIRE(is_map(map)); + delete_node(map); } - + SECTION("Create sequence node") { - YAMLNodeHandle seq = yaml_create_sequence(); + YAMLNodeHandle seq = create_sequence(); REQUIRE(seq != nullptr); - REQUIRE(yaml_is_sequence(seq)); - yaml_delete_node(seq); + REQUIRE(is_sequence(seq)); + delete_node(seq); } - + SECTION("Create scalar node") { - YAMLNodeHandle scalar = yaml_create_scalar(); + YAMLNodeHandle scalar = create_scalar(); REQUIRE(scalar != nullptr); - REQUIRE(yaml_is_scalar(scalar)); - yaml_delete_node(scalar); + REQUIRE(is_scalar(scalar)); + delete_node(scalar); } } @@ -68,47 +69,47 @@ TEST_CASE("YAML nodes can be created and deleted", "[creation]") { TEST_CASE("YAML can be parsed from strings", "[parsing]") { SECTION("Parse simple map") { const char* yaml = "key: value"; - YAMLNodeHandle node = yaml_parse(yaml); - + YAMLNodeHandle node = parse_string(yaml); + REQUIRE(node != nullptr); - REQUIRE(yaml_is_map(node)); - REQUIRE(yaml_has_key(node, "key")); - - yaml_delete_node(node); + REQUIRE(is_map(node)); + REQUIRE(has_key(node, "key")); + + delete_node(node); } - + SECTION("Parse sequence") { const char* yaml = "[a, b, c]"; - YAMLNodeHandle node = yaml_parse(yaml); - + YAMLNodeHandle node = parse_string(yaml); + REQUIRE(node != nullptr); - REQUIRE(yaml_is_sequence(node)); - REQUIRE(yaml_size(node) == 3); - - yaml_delete_node(node); + REQUIRE(is_sequence(node)); + REQUIRE(size(node) == 3); + + delete_node(node); } - + SECTION("Parse invalid YAML returns nullptr") { const char* invalid = "invalid: yaml: :"; - YAMLNodeHandle node = yaml_parse(invalid); - + YAMLNodeHandle node = parse_string(invalid); + REQUIRE(node == nullptr); } } TEST_CASE("YAML can be parsed from files", "[parsing][file]") { SECTION("Parse valid file") { - YAMLNodeHandle node = yaml_parse_file("../lattice_files/ex.pals.yaml"); - + YAMLNodeHandle node = parse_file("../lattice_files/ex.pals.yaml"); + REQUIRE(node != nullptr); - REQUIRE(yaml_is_sequence(node)); - REQUIRE(yaml_size(node) >= 2); - - yaml_delete_node(node); + REQUIRE(is_sequence(node)); + REQUIRE(size(node) >= 2); + + delete_node(node); } - + SECTION("Parse non-existent file returns nullptr") { - YAMLNodeHandle node = yaml_parse_file("nonexistent.pals.yaml"); + YAMLNodeHandle node = parse_file("nonexistent.pals.yaml"); REQUIRE(node == nullptr); } } @@ -119,33 +120,33 @@ TEST_CASE("YAML can be parsed from files", "[parsing][file]") { TEST_CASE("Node types can be checked", "[types]") { SECTION("Check scalar type") { - YAMLNodeHandle node = yaml_parse("value"); - REQUIRE(yaml_is_scalar(node)); - REQUIRE_FALSE(yaml_is_map(node)); - REQUIRE_FALSE(yaml_is_sequence(node)); - yaml_delete_node(node); + YAMLNodeHandle node = parse_string("value"); + REQUIRE(is_scalar(node)); + REQUIRE_FALSE(is_map(node)); + REQUIRE_FALSE(is_sequence(node)); + delete_node(node); } - + SECTION("Check map type") { - YAMLNodeHandle node = yaml_parse("key: value"); - REQUIRE(yaml_is_map(node)); - REQUIRE_FALSE(yaml_is_scalar(node)); - REQUIRE_FALSE(yaml_is_sequence(node)); - yaml_delete_node(node); + YAMLNodeHandle node = parse_string("key: value"); + REQUIRE(is_map(node)); + REQUIRE_FALSE(is_scalar(node)); + REQUIRE_FALSE(is_sequence(node)); + delete_node(node); } - + SECTION("Check sequence type") { - YAMLNodeHandle node = yaml_parse("[a, b, c]"); - REQUIRE(yaml_is_sequence(node)); - REQUIRE_FALSE(yaml_is_map(node)); - REQUIRE_FALSE(yaml_is_scalar(node)); - yaml_delete_node(node); + YAMLNodeHandle node = parse_string("[a, b, c]"); + REQUIRE(is_sequence(node)); + REQUIRE_FALSE(is_map(node)); + REQUIRE_FALSE(is_scalar(node)); + delete_node(node); } - + SECTION("Check null type") { - YAMLNodeHandle node = yaml_parse("null"); - REQUIRE(yaml_is_null(node)); - yaml_delete_node(node); + YAMLNodeHandle node = parse_string("null"); + REQUIRE(is_null(node)); + delete_node(node); } } @@ -155,82 +156,80 @@ TEST_CASE("Node types can be checked", "[types]") { TEST_CASE("Map keys can be accessed", "[access][map]") { const char* yaml = "name: test\nvalue: 42"; - YAMLNodeHandle node = yaml_parse(yaml); - + YAMLNodeHandle node = parse_string(yaml); + SECTION("Check key existence") { - REQUIRE(yaml_has_key(node, "name")); - REQUIRE(yaml_has_key(node, "value")); - REQUIRE_FALSE(yaml_has_key(node, "nonexistent")); + REQUIRE(has_key(node, "name")); + REQUIRE(has_key(node, "value")); + REQUIRE_FALSE(has_key(node, "nonexistent")); } - + SECTION("Get key value") { - YAMLNodeHandle name = yaml_get_key(node, "name"); + YAMLNodeHandle name = get_key(node, "name"); REQUIRE(name != nullptr); - - char* str = yaml_as_string(name); + + char* str = as_string(name); REQUIRE(std::string(str) == "test"); - + yaml_free_string(str); - yaml_delete_node(name); + delete_node(name); } - + SECTION("Get non-existent key returns nullptr") { - YAMLNodeHandle missing = yaml_get_key(node, "missing"); + YAMLNodeHandle missing = get_key(node, "missing"); REQUIRE(missing == nullptr); } - + SECTION("Get all keys") { int count; - char** keys = yaml_get_keys(node, &count); - + char** keys = get_keys(node, &count); + REQUIRE(count == 2); REQUIRE(keys != nullptr); - + bool has_name = false, has_value = false; for (int i = 0; i < count; i++) { if (std::string(keys[i]) == "name") has_name = true; if (std::string(keys[i]) == "value") has_value = true; } - + REQUIRE(has_name); REQUIRE(has_value); - + yaml_free_keys(keys, count); } - - yaml_delete_node(node); + + delete_node(node); } TEST_CASE("Sequence indices can be accessed", "[access][sequence]") { const char* yaml = "[apple, banana, cherry]"; - YAMLNodeHandle node = yaml_parse(yaml); - - SECTION("Check size") { - REQUIRE(yaml_size(node) == 3); - } - + YAMLNodeHandle node = parse_string(yaml); + + SECTION("Check size") { REQUIRE(size(node) == 3); } + SECTION("Access valid index") { - YAMLNodeHandle item = yaml_get_index(node, 1); + YAMLNodeHandle item = get_index(node, 1); REQUIRE(item != nullptr); - - char* str = yaml_as_string(item); + + char* str = as_string(item); REQUIRE(std::string(str) == "banana"); - + yaml_free_string(str); - yaml_delete_node(item); + delete_node(item); } - + SECTION("Access out of bounds index returns nullptr") { - YAMLNodeHandle item = yaml_get_index(node, 999); + YAMLNodeHandle item = get_index(node, 999); REQUIRE(item == nullptr); } - + SECTION("Access negative index returns nullptr") { - YAMLNodeHandle item = yaml_get_index(node, -1); + YAMLNodeHandle item = get_index(node, -1); REQUIRE(item == nullptr); } - - yaml_delete_node(node); + + delete_node(node); } // =========================================== @@ -239,53 +238,53 @@ TEST_CASE("Sequence indices can be accessed", "[access][sequence]") { TEST_CASE("Values can be converted to C types", "[conversion]") { SECTION("Convert to string") { - YAMLNodeHandle node = yaml_parse("test_value"); - char* str = yaml_as_string(node); - + YAMLNodeHandle node = parse_string("test_value"); + char* str = as_string(node); + REQUIRE(str != nullptr); REQUIRE(std::string(str) == "test_value"); - + yaml_free_string(str); - yaml_delete_node(node); + delete_node(node); } - + SECTION("Convert to int") { - YAMLNodeHandle node = yaml_parse("42"); - int val = yaml_as_int(node); - + YAMLNodeHandle node = parse_string("42"); + int val = as_int(node); + REQUIRE(val == 42); - - yaml_delete_node(node); + + delete_node(node); } - + SECTION("Convert to float") { - YAMLNodeHandle node = yaml_parse("3.14"); - double val = yaml_as_float(node); - + YAMLNodeHandle node = parse_string("3.14"); + double val = as_float(node); + REQUIRE(val == Approx(3.14)); - - yaml_delete_node(node); + + delete_node(node); } - + SECTION("Convert to bool") { - YAMLNodeHandle node_true = yaml_parse("true"); - YAMLNodeHandle node_false = yaml_parse("false"); - - REQUIRE(yaml_as_bool(node_true) == true); - REQUIRE(yaml_as_bool(node_false) == false); - - yaml_delete_node(node_true); - yaml_delete_node(node_false); - } - + YAMLNodeHandle node_true = parse_string("true"); + YAMLNodeHandle node_false = parse_string("false"); + + REQUIRE(as_bool(node_true) == true); + REQUIRE(as_bool(node_false) == false); + + delete_node(node_true); + delete_node(node_false); + } + SECTION("Invalid conversion returns default") { - YAMLNodeHandle node = yaml_parse("[a, b, c]"); - + YAMLNodeHandle node = parse_string("[a, b, c]"); + // Can't convert sequence to string - char* str = yaml_as_string(node); + char* str = as_string(node); REQUIRE(str == nullptr); - - yaml_delete_node(node); + + delete_node(node); } } @@ -294,62 +293,62 @@ TEST_CASE("Values can be converted to C types", "[conversion]") { // =========================================== TEST_CASE("Map values can be set", "[modification][map]") { - YAMLNodeHandle map = yaml_create_map(); - + YAMLNodeHandle map = create_map(); + SECTION("Set string value") { - yaml_set_string(map, "name", "test"); - REQUIRE(yaml_has_key(map, "name")); - - YAMLNodeHandle value = yaml_get_key(map, "name"); - char* str = yaml_as_string(value); + set_value_string(map, "name", "test"); + REQUIRE(has_key(map, "name")); + + YAMLNodeHandle value = get_key(map, "name"); + char* str = as_string(value); REQUIRE(std::string(str) == "test"); - + yaml_free_string(str); - yaml_delete_node(value); + delete_node(value); } - + SECTION("Set int value") { - yaml_set_int(map, "count", 42); - - YAMLNodeHandle value = yaml_get_key(map, "count"); - REQUIRE(yaml_as_int(value) == 42); - - yaml_delete_node(value); - } - + set_value_int(map, "count", 42); + + YAMLNodeHandle value = get_key(map, "count"); + REQUIRE(as_int(value) == 42); + + delete_node(value); + } + SECTION("Set float value") { - yaml_set_float(map, "pi", 3.14); - - YAMLNodeHandle value = yaml_get_key(map, "pi"); - REQUIRE(yaml_as_float(value) == Catch::Approx(3.14)); - - yaml_delete_node(value); - } - + set_value_float(map, "pi", 3.14); + + YAMLNodeHandle value = get_key(map, "pi"); + REQUIRE(as_float(value) == Catch::Approx(3.14)); + + delete_node(value); + } + SECTION("Set bool value") { - yaml_set_bool(map, "enabled", true); - - YAMLNodeHandle value = yaml_get_key(map, "enabled"); - REQUIRE(yaml_as_bool(value) == true); - - yaml_delete_node(value); - } - + set_value_bool(map, "enabled", true); + + YAMLNodeHandle value = get_key(map, "enabled"); + REQUIRE(as_bool(value) == true); + + delete_node(value); + } + SECTION("Set nested node") { - YAMLNodeHandle nested = yaml_create_map(); - yaml_set_string(nested, "inner", "value"); - - yaml_set_node(map, "nested", nested); - - REQUIRE(yaml_has_key(map, "nested")); - YAMLNodeHandle retrieved = yaml_get_key(map, "nested"); - REQUIRE(yaml_is_map(retrieved)); - - yaml_delete_node(retrieved); - yaml_delete_node(nested); - } - - yaml_delete_node(map); + YAMLNodeHandle nested = create_map(); + set_value_string(nested, "inner", "value"); + + set_value_node(map, "nested", nested); + + REQUIRE(has_key(map, "nested")); + YAMLNodeHandle retrieved = get_key(map, "nested"); + REQUIRE(is_map(retrieved)); + + delete_node(retrieved); + delete_node(nested); + } + + delete_node(map); } // =========================================== @@ -357,81 +356,81 @@ TEST_CASE("Map values can be set", "[modification][map]") { // =========================================== TEST_CASE("Sequence values can be pushed", "[modification][sequence]") { - YAMLNodeHandle seq = yaml_create_sequence(); - + YAMLNodeHandle seq = create_sequence(); + SECTION("Push string values") { - yaml_push_string(seq, "first"); - yaml_push_string(seq, "second"); - - REQUIRE(yaml_size(seq) == 2); - - YAMLNodeHandle item = yaml_get_index(seq, 1); - char* str = yaml_as_string(item); + push_string(seq, "first"); + push_string(seq, "second"); + + REQUIRE(size(seq) == 2); + + YAMLNodeHandle item = get_index(seq, 1); + char* str = as_string(item); REQUIRE(std::string(str) == "second"); - + yaml_free_string(str); - yaml_delete_node(item); + delete_node(item); } - + SECTION("Push int values") { - yaml_push_int(seq, 10); - yaml_push_int(seq, 20); - - REQUIRE(yaml_size(seq) == 2); - - YAMLNodeHandle item = yaml_get_index(seq, 0); - REQUIRE(yaml_as_int(item) == 10); - - yaml_delete_node(item); - } - + push_int(seq, 10); + push_int(seq, 20); + + REQUIRE(size(seq) == 2); + + YAMLNodeHandle item = get_index(seq, 0); + REQUIRE(as_int(item) == 10); + + delete_node(item); + } + SECTION("Push float values") { - yaml_push_float(seq, 1.1); - yaml_push_float(seq, 2.2); - - REQUIRE(yaml_size(seq) == 2); - - YAMLNodeHandle item = yaml_get_index(seq, 1); - REQUIRE(yaml_as_float(item) == Catch::Approx(2.2)); - - yaml_delete_node(item); - } - + push_float(seq, 1.1); + push_float(seq, 2.2); + + REQUIRE(size(seq) == 2); + + YAMLNodeHandle item = get_index(seq, 1); + REQUIRE(as_float(item) == Catch::Approx(2.2)); + + delete_node(item); + } + SECTION("Push node") { - YAMLNodeHandle node = yaml_create_map(); - yaml_set_string(node, "key", "value"); - - yaml_push_node(seq, node); - - REQUIRE(yaml_size(seq) == 1); - - YAMLNodeHandle retrieved = yaml_get_index(seq, 0); - REQUIRE(yaml_is_map(retrieved)); - - yaml_delete_node(retrieved); - yaml_delete_node(node); - } - - yaml_delete_node(seq); + YAMLNodeHandle node = create_map(); + set_value_string(node, "key", "value"); + + push_node(seq, node); + + REQUIRE(size(seq) == 1); + + YAMLNodeHandle retrieved = get_index(seq, 0); + REQUIRE(is_map(retrieved)); + + delete_node(retrieved); + delete_node(node); + } + + delete_node(seq); } TEST_CASE("Sequence values can be set at index", "[modification][sequence]") { - YAMLNodeHandle seq = yaml_parse("[a, b, c]"); - + YAMLNodeHandle seq = parse_string("[a, b, c]"); + SECTION("Set string at index") { - YAMLNodeHandle replacement = yaml_parse("replaced"); - yaml_set_at_index(seq, 1, replacement); - - YAMLNodeHandle item = yaml_get_index(seq, 1); - char* str = yaml_as_string(item); + YAMLNodeHandle replacement = parse_string("replaced"); + set_at_index(seq, 1, replacement); + + YAMLNodeHandle item = get_index(seq, 1); + char* str = as_string(item); REQUIRE(std::string(str) == "replaced"); - + yaml_free_string(str); - yaml_delete_node(item); - yaml_delete_node(replacement); + delete_node(item); + delete_node(replacement); } - - yaml_delete_node(seq); + + delete_node(seq); } // =========================================== @@ -440,41 +439,41 @@ TEST_CASE("Sequence values can be set at index", "[modification][sequence]") { TEST_CASE("Scalar values can be edited directly", "[modification][scalar]") { SECTION("Set scalar string") { - YAMLNodeHandle scalar = yaml_create_scalar(); - yaml_set_scalar_string(scalar, "new_value"); - - char* str = yaml_as_string(scalar); + YAMLNodeHandle scalar = create_scalar(); + set_scalar_string(scalar, "new_value"); + + char* str = as_string(scalar); REQUIRE(std::string(str) == "new_value"); - + yaml_free_string(str); - yaml_delete_node(scalar); + delete_node(scalar); } - + SECTION("Set scalar int") { - YAMLNodeHandle scalar = yaml_create_scalar(); - yaml_set_scalar_int(scalar, 99); - - REQUIRE(yaml_as_int(scalar) == 99); - - yaml_delete_node(scalar); - } - + YAMLNodeHandle scalar = create_scalar(); + set_scalar_int(scalar, 99); + + REQUIRE(as_int(scalar) == 99); + + delete_node(scalar); + } + SECTION("Set scalar float") { - YAMLNodeHandle scalar = yaml_create_scalar(); - yaml_set_scalar_float(scalar, 2.718); - - REQUIRE(yaml_as_float(scalar) == Catch::Approx(2.718)); - - yaml_delete_node(scalar); - } - + YAMLNodeHandle scalar = create_scalar(); + set_scalar_float(scalar, 2.718); + + REQUIRE(as_float(scalar) == Catch::Approx(2.718)); + + delete_node(scalar); + } + SECTION("Set scalar bool") { - YAMLNodeHandle scalar = yaml_create_scalar(); - yaml_set_scalar_bool(scalar, false); - - REQUIRE(yaml_as_bool(scalar) == false); - - yaml_delete_node(scalar); + YAMLNodeHandle scalar = create_scalar(); + set_scalar_bool(scalar, false); + + REQUIRE(as_bool(scalar) == false); + + delete_node(scalar); } } @@ -484,34 +483,34 @@ TEST_CASE("Scalar values can be edited directly", "[modification][scalar]") { TEST_CASE("YAML can be written to files", "[io][file]") { const char* test_file = "test_output.pals.yaml"; - + SECTION("Write simple map") { - YAMLNodeHandle map = yaml_create_map(); - yaml_set_string(map, "test", "value"); - yaml_set_int(map, "count", 5); - - REQUIRE(yaml_write_file(map, test_file)); - + YAMLNodeHandle map = create_map(); + set_value_string(map, "test", "value"); + set_value_int(map, "count", 5); + + REQUIRE(write_file(map, test_file)); + // Read back and verify - YAMLNodeHandle loaded = yaml_parse_file(test_file); + YAMLNodeHandle loaded = parse_file(test_file); REQUIRE(loaded != nullptr); - REQUIRE(yaml_has_key(loaded, "test")); - REQUIRE(yaml_has_key(loaded, "count")); - - yaml_delete_node(map); - yaml_delete_node(loaded); + REQUIRE(has_key(loaded, "test")); + REQUIRE(has_key(loaded, "count")); + + delete_node(map); + delete_node(loaded); cleanup_file(test_file); } - + SECTION("Write with formatting") { - YAMLNodeHandle seq = yaml_parse("[a, b, c]"); - - REQUIRE(yaml_write_file_formatted(seq, test_file, 4, false, true)); - + YAMLNodeHandle seq = parse_string("[a, b, c]"); + + REQUIRE(write_file_formatted(seq, test_file, 4, false, true)); + std::string content = read_file(test_file); - REQUIRE(content.find("[a, b, c]") != std::string::npos); // Flow style - - yaml_delete_node(seq); + REQUIRE(content.find("[a, b, c]") != std::string::npos); // Flow style + + delete_node(seq); cleanup_file(test_file); } } @@ -522,31 +521,31 @@ TEST_CASE("YAML can be written to files", "[io][file]") { TEST_CASE("YAML nodes can be converted to strings", "[conversion][string]") { SECTION("Convert map to string") { - YAMLNodeHandle map = yaml_create_map(); - yaml_set_string(map, "key", "value"); - + YAMLNodeHandle map = create_map(); + set_value_string(map, "key", "value"); + char* str = yaml_to_string(map); REQUIRE(str != nullptr); REQUIRE(std::string(str).find("key") != std::string::npos); REQUIRE(std::string(str).find("value") != std::string::npos); - + yaml_free_string(str); - yaml_delete_node(map); + delete_node(map); } - + SECTION("Emit with custom indent") { - YAMLNodeHandle map = yaml_create_map(); - yaml_set_string(map, "test", "val"); - + YAMLNodeHandle map = create_map(); + set_value_string(map, "test", "val"); + char* str2 = yaml_emit(map, 2); char* str4 = yaml_emit(map, 4); - + REQUIRE(str2 != nullptr); REQUIRE(str4 != nullptr); - + yaml_free_string(str2); yaml_free_string(str4); - yaml_delete_node(map); + delete_node(map); } } @@ -556,46 +555,46 @@ TEST_CASE("YAML nodes can be converted to strings", "[conversion][string]") { TEST_CASE("YAML nodes can be cloned", "[clone]") { SECTION("Clone simple map") { - YAMLNodeHandle original = yaml_create_map(); - yaml_set_string(original, "name", "original"); - + YAMLNodeHandle original = create_map(); + set_value_string(original, "name", "original"); + YAMLNodeHandle clone = yaml_clone(original); REQUIRE(clone != nullptr); - + // Modify clone - yaml_set_string(clone, "name", "modified"); - + set_value_string(clone, "name", "modified"); + // Verify original unchanged - YAMLNodeHandle orig_val = yaml_get_key(original, "name"); - char* orig_str = yaml_as_string(orig_val); + YAMLNodeHandle orig_val = get_key(original, "name"); + char* orig_str = as_string(orig_val); REQUIRE(std::string(orig_str) == "original"); - + // Verify clone changed - YAMLNodeHandle clone_val = yaml_get_key(clone, "name"); - char* clone_str = yaml_as_string(clone_val); + YAMLNodeHandle clone_val = get_key(clone, "name"); + char* clone_str = as_string(clone_val); REQUIRE(std::string(clone_str) == "modified"); - + yaml_free_string(orig_str); yaml_free_string(clone_str); - yaml_delete_node(orig_val); - yaml_delete_node(clone_val); - yaml_delete_node(original); - yaml_delete_node(clone); + delete_node(orig_val); + delete_node(clone_val); + delete_node(original); + delete_node(clone); } - + SECTION("Clone nested structure") { - YAMLNodeHandle original = yaml_parse("outer: {inner: value}"); + YAMLNodeHandle original = parse_string("outer: {inner: value}"); YAMLNodeHandle clone = yaml_clone(original); - + REQUIRE(clone != nullptr); - REQUIRE(yaml_is_map(clone)); - - YAMLNodeHandle outer = yaml_get_key(clone, "outer"); - REQUIRE(yaml_has_key(outer, "inner")); - - yaml_delete_node(outer); - yaml_delete_node(original); - yaml_delete_node(clone); + REQUIRE(is_map(clone)); + + YAMLNodeHandle outer = get_key(clone, "outer"); + REQUIRE(has_key(outer, "inner")); + + delete_node(outer); + delete_node(original); + delete_node(clone); } } @@ -604,74 +603,74 @@ TEST_CASE("YAML nodes can be cloned", "[clone]") { // =========================================== TEST_CASE("ex.pals.yaml has expected structure", "[ex.pals.yaml][structure]") { - YAMLNodeHandle root = yaml_parse_file("../lattice_files/ex.pals.yaml"); + YAMLNodeHandle root = parse_file("../lattice_files/ex.pals.yaml"); REQUIRE(root != nullptr); - + SECTION("Root is a sequence") { - REQUIRE(yaml_is_sequence(root)); - REQUIRE(yaml_size(root) >= 2); + REQUIRE(is_sequence(root)); + REQUIRE(size(root) >= 2); } - + SECTION("First element has thingB") { - YAMLNodeHandle first = yaml_get_index(root, 0); + YAMLNodeHandle first = get_index(root, 0); REQUIRE(first != nullptr); - REQUIRE(yaml_is_map(first)); - REQUIRE(yaml_has_key(first, "thingB")); - - YAMLNodeHandle thingB = yaml_get_key(first, "thingB"); - REQUIRE(yaml_is_map(thingB)); - REQUIRE(yaml_has_key(thingB, "kind")); - - YAMLNodeHandle kind = yaml_get_key(thingB, "kind"); - char* kind_str = yaml_as_string(kind); + REQUIRE(is_map(first)); + REQUIRE(has_key(first, "thingB")); + + YAMLNodeHandle thingB = get_key(first, "thingB"); + REQUIRE(is_map(thingB)); + REQUIRE(has_key(thingB, "kind")); + + YAMLNodeHandle kind = get_key(thingB, "kind"); + char* kind_str = as_string(kind); REQUIRE(std::string(kind_str) == "Sextupole"); - + yaml_free_string(kind_str); - yaml_delete_node(kind); - yaml_delete_node(thingB); - yaml_delete_node(first); + delete_node(kind); + delete_node(thingB); + delete_node(first); } - + SECTION("Second element has inj_line") { - YAMLNodeHandle second = yaml_get_index(root, 1); + YAMLNodeHandle second = get_index(root, 1); REQUIRE(second != nullptr); - REQUIRE(yaml_is_map(second)); - REQUIRE(yaml_has_key(second, "inj_line")); - - YAMLNodeHandle inj_line = yaml_get_key(second, "inj_line"); - REQUIRE(yaml_is_map(inj_line)); - REQUIRE(yaml_has_key(inj_line, "kind")); - REQUIRE(yaml_has_key(inj_line, "multipass")); - REQUIRE(yaml_has_key(inj_line, "line")); - - YAMLNodeHandle kind = yaml_get_key(inj_line, "kind"); - char* kind_str = yaml_as_string(kind); + REQUIRE(is_map(second)); + REQUIRE(has_key(second, "inj_line")); + + YAMLNodeHandle inj_line = get_key(second, "inj_line"); + REQUIRE(is_map(inj_line)); + REQUIRE(has_key(inj_line, "kind")); + REQUIRE(has_key(inj_line, "multipass")); + REQUIRE(has_key(inj_line, "line")); + + YAMLNodeHandle kind = get_key(inj_line, "kind"); + char* kind_str = as_string(kind); REQUIRE(std::string(kind_str) == "BeamLine"); - - YAMLNodeHandle multipass = yaml_get_key(inj_line, "multipass"); - REQUIRE(yaml_as_bool(multipass) == true); - + + YAMLNodeHandle multipass = get_key(inj_line, "multipass"); + REQUIRE(as_bool(multipass) == true); + yaml_free_string(kind_str); - yaml_delete_node(kind); - yaml_delete_node(multipass); - yaml_delete_node(inj_line); - yaml_delete_node(second); + delete_node(kind); + delete_node(multipass); + delete_node(inj_line); + delete_node(second); } - + SECTION("inj_line has line sequence") { - YAMLNodeHandle second = yaml_get_index(root, 1); - YAMLNodeHandle inj_line = yaml_get_key(second, "inj_line"); - YAMLNodeHandle line = yaml_get_key(inj_line, "line"); - - REQUIRE(yaml_is_sequence(line)); - REQUIRE(yaml_size(line) >= 3); - - yaml_delete_node(line); - yaml_delete_node(inj_line); - yaml_delete_node(second); - } - - yaml_delete_node(root); + YAMLNodeHandle second = get_index(root, 1); + YAMLNodeHandle inj_line = get_key(second, "inj_line"); + YAMLNodeHandle line = get_key(inj_line, "line"); + + REQUIRE(is_sequence(line)); + REQUIRE(size(line) >= 3); + + delete_node(line); + delete_node(inj_line); + delete_node(second); + } + + delete_node(root); } // =========================================== @@ -680,33 +679,33 @@ TEST_CASE("ex.pals.yaml has expected structure", "[ex.pals.yaml][structure]") { TEST_CASE("Memory is properly managed", "[memory]") { SECTION("Can safely delete nullptr") { - yaml_delete_node(nullptr); // Should not crash + delete_node(nullptr); // Should not crash } - + SECTION("Multiple operations don't leak") { for (int i = 0; i < 100; i++) { - YAMLNodeHandle node = yaml_create_map(); - yaml_set_int(node, "test", i); - + YAMLNodeHandle node = create_map(); + set_value_int(node, "test", i); + char* str = yaml_to_string(node); yaml_free_string(str); - - yaml_delete_node(node); + + delete_node(node); } // No assertion - just shouldn't crash or leak } - + SECTION("Complex structure cleanup") { - YAMLNodeHandle root = yaml_create_sequence(); - + YAMLNodeHandle root = create_sequence(); + for (int i = 0; i < 10; i++) { - YAMLNodeHandle map = yaml_create_map(); - yaml_set_int(map, "id", i); - yaml_push_node(root, map); - yaml_delete_node(map); // Safe after push_node copies + YAMLNodeHandle map = create_map(); + set_value_int(map, "id", i); + push_node(root, map); + delete_node(map); // Safe after push_node copies } - - yaml_delete_node(root); + + delete_node(root); } } @@ -716,30 +715,30 @@ TEST_CASE("Memory is properly managed", "[memory]") { TEST_CASE("Edge cases are handled correctly", "[edge_cases]") { SECTION("Empty map") { - YAMLNodeHandle map = yaml_create_map(); - REQUIRE(yaml_size(map) == 0); - + YAMLNodeHandle map = create_map(); + REQUIRE(size(map) == 0); + int count; - char** keys = yaml_get_keys(map, &count); + char** keys = get_keys(map, &count); REQUIRE(count == 0); - - yaml_delete_node(map); + + delete_node(map); } - + SECTION("Empty sequence") { - YAMLNodeHandle seq = yaml_create_sequence(); - REQUIRE(yaml_size(seq) == 0); - - YAMLNodeHandle item = yaml_get_index(seq, 0); + YAMLNodeHandle seq = create_sequence(); + REQUIRE(size(seq) == 0); + + YAMLNodeHandle item = get_index(seq, 0); REQUIRE(item == nullptr); - - yaml_delete_node(seq); + + delete_node(seq); } - + SECTION("Set scalar to nullptr is safe") { - YAMLNodeHandle scalar = yaml_create_scalar(); - yaml_set_scalar_string(nullptr, "test"); // Should not crash - yaml_set_scalar_string(scalar, nullptr); // Should not crash - yaml_delete_node(scalar); + YAMLNodeHandle scalar = create_scalar(); + set_scalar_string(nullptr, "test"); // Should not crash + set_scalar_string(scalar, nullptr); // Should not crash + delete_node(scalar); } } From 6da91b746886cbce1cadf8b023f6c9e315006af9 Mon Sep 17 00:00:00 2001 From: Alexheeeee Date: Fri, 13 Feb 2026 03:20:20 -0500 Subject: [PATCH 2/7] original lattice --- CMakeLists.txt | 60 +----------------------------- examples/yaml_reader.cpp | 3 ++ lattice_files/ex.pals.yaml | 4 ++ src/yaml_c_wrapper.cpp | 76 ++++++++++++++++++++++++++++++++++++++ src/yaml_c_wrapper.h | 7 ++++ 5 files changed, 91 insertions(+), 59 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 09796a9..57ae5ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,66 +29,8 @@ add_subdirectory(tests) file(COPY ${CMAKE_SOURCE_DIR}/lattice_files/ DESTINATION ${CMAKE_BINARY_DIR}/lattice_files/) -# if(APPLE) -# # Check for package managers -# execute_process(COMMAND which brew OUTPUT_VARIABLE HOMEBREW_EXISTS OUTPUT_STRIP_TRAILING_WHITESPACE) -# execute_process(COMMAND which port OUTPUT_VARIABLE MACPORTS_EXISTS OUTPUT_STRIP_TRAILING_WHITESPACE) -# execute_process(COMMAND which conda OUTPUT_VARIABLE CONDA_EXISTS OUTPUT_STRIP_TRAILING_WHITESPACE) - -# # Error if both Homebrew and MacPorts exist -# if(HOMEBREW_EXISTS AND MACPORTS_EXISTS) -# message(FATAL_ERROR -# "Both Homebrew and MacPorts detected. This can cause conflicts.\n" -# "Please use only one package manager:\n" -# " - Homebrew found at: ${HOMEBREW_EXISTS}\n" -# " - MacPorts found at: ${MACPORTS_EXISTS}\n" -# "Consider uninstalling one to avoid library conflicts." -# ) -# endif() - -# # Set RPATH based on which package manager is found -# set(BASE_RPATH "@executable_path/../lib;@loader_path") - -# if(HOMEBREW_EXISTS) -# message(STATUS "Using Homebrew package manager") -# # Check if Apple Silicon or Intel -# execute_process( -# COMMAND uname -m -# OUTPUT_VARIABLE ARCH -# OUTPUT_STRIP_TRAILING_WHITESPACE -# ) -# if(ARCH STREQUAL "arm64") -# set(CMAKE_INSTALL_RPATH "${BASE_RPATH};/opt/homebrew/lib") -# list(APPEND CMAKE_PREFIX_PATH "/opt/homebrew") -# else() -# set(CMAKE_INSTALL_RPATH "${BASE_RPATH};/usr/local/lib") -# list(APPEND CMAKE_PREFIX_PATH "/usr/local") -# endif() -# elseif(MACPORTS_EXISTS) -# message(STATUS "Using MacPorts package manager") -# set(CMAKE_INSTALL_RPATH "${BASE_RPATH};/opt/local/lib") -# list(APPEND CMAKE_PREFIX_PATH "/opt/local") -# elseif(CONDA_EXISTS) -# message(STATUS "Using Conda package manager") -# # Get conda prefix -# execute_process( -# COMMAND conda info --base -# OUTPUT_VARIABLE CONDA_PREFIX -# OUTPUT_STRIP_TRAILING_WHITESPACE -# ) -# set(CMAKE_INSTALL_RPATH "${BASE_RPATH};${CONDA_PREFIX}/lib") -# list(APPEND CMAKE_PREFIX_PATH "${CONDA_PREFIX}") -# else() -# message(STATUS "No package manager detected, using default paths") -# set(CMAKE_INSTALL_RPATH "${BASE_RPATH};/usr/local/lib") -# endif() - -# set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -# message(STATUS "RPATH set to: ${CMAKE_INSTALL_RPATH}") -# endif() - add_library(yaml_c_wrapper SHARED src/yaml_c_wrapper.cpp) target_link_libraries(yaml_c_wrapper PUBLIC yaml-cpp) add_executable(yaml_reader examples/yaml_reader.cpp) -target_link_libraries(yaml_reader yaml_c_wrapper) +target_link_libraries(yaml_reader yaml_c_wrapper yaml-cpp) diff --git a/examples/yaml_reader.cpp b/examples/yaml_reader.cpp index 548097d..c2914ef 100644 --- a/examples/yaml_reader.cpp +++ b/examples/yaml_reader.cpp @@ -6,6 +6,9 @@ // Otherwise, it will use the example file ex.pals.yaml int main(int argc, char* argv[]) { + struct lattices lat = get_lattices("ex.pals.yaml"); + std::cout << yaml_to_string(lat.original); + return 0; if (argc != 1) { char* filename = argv[1]; std::string path = "../lattice_files/"; diff --git a/lattice_files/ex.pals.yaml b/lattice_files/ex.pals.yaml index 644df78..f03a80b 100644 --- a/lattice_files/ex.pals.yaml +++ b/lattice_files/ex.pals.yaml @@ -1,5 +1,9 @@ - thingB: kind: Sextupole +- lat1: + kind: Lattice + branches: + - inj_line - inj_line: kind: BeamLine multipass: true diff --git a/src/yaml_c_wrapper.cpp b/src/yaml_c_wrapper.cpp index 35f310a..7aebf91 100644 --- a/src/yaml_c_wrapper.cpp +++ b/src/yaml_c_wrapper.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #if defined(_WIN32) #define YAML_API extern "C" __declspec(dllexport) @@ -10,6 +11,61 @@ #define YAML_API extern "C" __attribute__((visibility("default"))) #endif +struct lattices_int { + YAML::Node original; + YAML::Node included; + YAML::Node expanded; +}; + +void add_to_original(YAML::Node *root, std::string filename) { + YAML::Node node = YAML::Node(YAML::NodeType::Map); + node["path"] = ""; + node["info"] = YAML::LoadFile("../lattice_files/" + filename); + root->push_back(node); +} + +void get_includes(YAML::Node node, std::vector* include_list) { + if (node.IsSequence()) { + for (int i = 0; i < node.size(); i++) { + get_includes(node[i], include_list); + } + } else if (node.IsMap()) { + for (auto ele : node) { + if (ele.first.as() == "include") { + std::string filename = ele.second.as(); + if (std::find(include_list->begin(), include_list->end(), filename) == include_list->end()) { + + include_list->push_back(filename); + YAML::Node child = YAML::LoadFile("../lattice_files/" + filename); + get_includes(child, include_list); + } + } + get_includes(ele.second, include_list); + } + } +} + +YAML::Node original_lattices(std::string filename) { + YAML::Node root = YAML::Node(YAML::NodeType::Sequence); + std::vector include_list; + add_to_original(&root, filename); + get_includes(YAML::LoadFile("../lattice_files/" + filename), &include_list); + for (std::string file : include_list) { + add_to_original(&root, file); + } + return root; +} + +struct lattices_int get_lattices_int(std::string filename) { + struct lattices_int lat; + lat.original = original_lattices(filename); + return lat; +} + +// if file only has one lattice, expand that lattice. if multiple lattices, use (expand) the last one only +// if there's a use statement, expand the lattice specified in the use statement +// need way to specify which lattice to use on command line with -lattice, for example +// only expand stuff in the specified lattice // ======= LATTICE EXPANSION UTILS /* Inserts the elements in `line` into `seq` at `index` a `repeat` number of times. @@ -93,6 +149,12 @@ YAML::Node expand_internal(YAML::Node node, } else if (node.IsMap()) { for (auto ele : node) { // will probably need to add better method of finding files + // seperate out include and expansion + // one struct for og lattice and include files, one for expanded, one for included + // called original, included, expanded + // default print out info from og, print which file was printed + // if people say original and filename, only print out that file + // need support for recursive include if (ele.first.as() == "include") { std::string filename = ele.second.as(); YAML::Node seq = YAML::Node(YAML::NodeType::Sequence); @@ -124,6 +186,20 @@ YAML::Node expand_internal(YAML::Node node, extern "C" { typedef void* YAMLNodeHandle; +struct lattices { + YAMLNodeHandle original; + YAMLNodeHandle included; + YAMLNodeHandle expanded; + }; + +YAML_API struct lattices get_lattices(const char* filename) { + struct lattices lat; + lat.original = static_cast(new YAML::Node(original_lattices(std::string(filename)))); + lat.included = static_cast(new YAML::Node()); + lat.expanded = static_cast(new YAML::Node()); + return lat; + } + // === CREATION/DELETION === YAML_API YAMLNodeHandle create_node() { return new YAML::Node(); } diff --git a/src/yaml_c_wrapper.h b/src/yaml_c_wrapper.h index 40e7318..f61305f 100644 --- a/src/yaml_c_wrapper.h +++ b/src/yaml_c_wrapper.h @@ -8,6 +8,13 @@ extern "C" { #endif typedef void* YAMLNodeHandle; +struct lattices { + YAMLNodeHandle original; + YAMLNodeHandle included; + YAMLNodeHandle expanded; + }; + +struct lattices get_lattices(const char* filename); // === CREATION === YAMLNodeHandle create_node(void); From 4d8e50c353a79d4c7157ff4feeed363895954bdd Mon Sep 17 00:00:00 2001 From: Alexheeeee Date: Fri, 6 Mar 2026 02:05:19 -0500 Subject: [PATCH 3/7] seperated executables, pass by reference --- CMakeLists.txt | 7 +- README.md | 3 + ...yaml_reader.cpp => example_read_write.cpp} | 27 +- lattice_files/ex.pals.yaml | 8 +- lattice_files/include.pals.yaml | 3 +- lattice_files/include2.pals.yaml | 4 + src/get_lattices.cpp | 29 ++ src/yaml_c_wrapper.cpp | 409 ++++++++++++------ src/yaml_c_wrapper.h | 3 +- 9 files changed, 329 insertions(+), 164 deletions(-) rename examples/{yaml_reader.cpp => example_read_write.cpp} (64%) create mode 100644 lattice_files/include2.pals.yaml create mode 100644 src/get_lattices.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 57ae5ee..524c8bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,5 +32,8 @@ file(COPY ${CMAKE_SOURCE_DIR}/lattice_files/ add_library(yaml_c_wrapper SHARED src/yaml_c_wrapper.cpp) target_link_libraries(yaml_c_wrapper PUBLIC yaml-cpp) -add_executable(yaml_reader examples/yaml_reader.cpp) -target_link_libraries(yaml_reader yaml_c_wrapper yaml-cpp) +add_executable(example_read_write examples/example_read_write.cpp) +target_link_libraries(example_read_write yaml_c_wrapper yaml-cpp) + +add_executable(get_lattices src/get_lattices.cpp) +target_link_libraries(get_lattices yaml_c_wrapper yaml-cpp) \ No newline at end of file diff --git a/README.md b/README.md index 2062938..4c9b5ee 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ include inherits repeat +YAML::Nodes are values that act like pointers, so editing a node will cause the tree the node is +contained in to reflect the changes. + ## Usage In pals-cpp, run diff --git a/examples/yaml_reader.cpp b/examples/example_read_write.cpp similarity index 64% rename from examples/yaml_reader.cpp rename to examples/example_read_write.cpp index c2914ef..9813a03 100644 --- a/examples/yaml_reader.cpp +++ b/examples/example_read_write.cpp @@ -1,29 +1,11 @@ #include #include "../src/yaml_c_wrapper.h" - -// If file name is provided as a command line argument, this will print out the -// expanded contents of the file to the terminal, as well as to expand.pals.yaml. -// Otherwise, it will use the example file ex.pals.yaml +#include int main(int argc, char* argv[]) { - struct lattices lat = get_lattices("ex.pals.yaml"); - std::cout << yaml_to_string(lat.original); - return 0; - if (argc != 1) { - char* filename = argv[1]; - std::string path = "../lattice_files/"; - path += filename; - YAMLNodeHandle handle = parse_file(path.c_str()); - - lattice_expand(handle); - std::cout << "Printing out contents of file: " << filename << std::endl; - std::cout << yaml_to_string(handle) << std::endl; - write_file(handle, "../lattice_files/expand.pals.yaml"); - return 0; - } // reading a lattice from a yaml file YAMLNodeHandle handle = parse_file("../lattice_files/ex.pals.yaml"); - std::cout << "Printing out contents of file: " << "ex.pals.yaml" << std::endl; + std::cout << "Output of example_read_write.cpp" << std::endl; // printing to terminal std::cout << yaml_to_string(handle) << std::endl << std::endl; @@ -65,8 +47,9 @@ int main(int argc, char* argv[]) { push_node(handle, map); push_node(handle, magnets); - // performing lattice expansion - lattice_expand(handle); + // getting expanded lattice + struct lattices lat = get_lattices("ex.pals.yaml", "lat1"); + YAMLNodeHandle expanded = lat.expanded; // writing modified lattice file to expand.pals.yaml write_file(handle, "../lattice_files/expand.pals.yaml"); diff --git a/lattice_files/ex.pals.yaml b/lattice_files/ex.pals.yaml index f03a80b..e401e04 100644 --- a/lattice_files/ex.pals.yaml +++ b/lattice_files/ex.pals.yaml @@ -4,6 +4,10 @@ kind: Lattice branches: - inj_line +- lat2: + kind: Lattice + branches: + - inj_line - inj_line: kind: BeamLine multipass: true @@ -24,4 +28,6 @@ line: - a - b - - c \ No newline at end of file + - c +- use: "lat2" +- use: "lat1" \ No newline at end of file diff --git a/lattice_files/include.pals.yaml b/lattice_files/include.pals.yaml index b9317a2..be02bc8 100644 --- a/lattice_files/include.pals.yaml +++ b/lattice_files/include.pals.yaml @@ -1,4 +1,5 @@ - line: - first: 1 - second: 2 - - third: 3 \ No newline at end of file + - third: 3 + - include: "include2.pals.yaml" \ No newline at end of file diff --git a/lattice_files/include2.pals.yaml b/lattice_files/include2.pals.yaml new file mode 100644 index 0000000..2a2119c --- /dev/null +++ b/lattice_files/include2.pals.yaml @@ -0,0 +1,4 @@ +- line: + - fourth: 4 + - fifth: 5 + - sixth: 6 \ No newline at end of file diff --git a/src/get_lattices.cpp b/src/get_lattices.cpp new file mode 100644 index 0000000..8608331 --- /dev/null +++ b/src/get_lattices.cpp @@ -0,0 +1,29 @@ +#include +#include "../src/yaml_c_wrapper.h" + +int main(int argc, char* argv[]) { + std::string file_name = "ex.pals.yaml"; + const char* lattice_name = ""; + if (argc >= 1) { + file_name = argv[0]; + } + if (argc >= 2) { + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "-lat") == 0) { + lattice_name = argv[i+1]; + } + } + } + + struct lattices lat = get_lattices("ex.pals.yaml", lattice_name); + std::cout << "Printing original lattice information: " << std::endl; + std::cout << yaml_to_string(lat.original) << std::endl << "\n\n"; + + // put separating lines here + std::cout << "Printing included lattice information: " << std::endl; + std::cout << yaml_to_string(lat.included) << std::endl << "\n\n"; + + std::cout << "Printing expanded lattice information: " << std::endl; + std::cout << yaml_to_string(lat.expanded) << std::endl; + return 0; +} \ No newline at end of file diff --git a/src/yaml_c_wrapper.cpp b/src/yaml_c_wrapper.cpp index 7aebf91..385eb07 100644 --- a/src/yaml_c_wrapper.cpp +++ b/src/yaml_c_wrapper.cpp @@ -11,178 +11,323 @@ #define YAML_API extern "C" __attribute__((visibility("default"))) #endif +//TODO: add absolute file paths +// for every element, add a field that points to the parent +/* +`original` is a map containing the base lattice as well as any lattices included +in the base lattice. They can be accessed using original[filename]. +`included` is the base lattice but with all included files substituted in. +`expanded` is the base lattice after lattice expansion has been performed. +*/ struct lattices_int { YAML::Node original; YAML::Node included; YAML::Node expanded; }; -void add_to_original(YAML::Node *root, std::string filename) { - YAML::Node node = YAML::Node(YAML::NodeType::Map); - node["path"] = ""; - node["info"] = YAML::LoadFile("../lattice_files/" + filename); - root->push_back(node); +template +std::vector search(YAML::Node root_node, Condition condition) { + std::vector matches; + + if (condition(root_node)) matches.push_back(root_node); + + std::vector child_matches; + if (root_node.IsMap()) { + for (auto ele : root_node) { + child_matches = search(ele.second, condition); + matches.insert(matches.end(), child_matches.begin(), child_matches.end()); + + } + } else if (root_node.IsSequence()) { + for (int i = 0; i < root_node.size(); i++) { + child_matches = search(root_node[i], condition); + matches.insert(matches.end(), child_matches.begin(), child_matches.end()); + } + } + return matches; } -void get_includes(YAML::Node node, std::vector* include_list) { +auto is_kind = [](YAML::Node node, std::string kind_type_string) { + if (node.IsMap()) { + for (auto ele : node) { + if (ele.second.IsMap() && ele.second["kind"].as("") == kind_type_string){ + return true; + } + } + } + return false; + }; + +std::vector search_kind(YAML::Node root, std::string kind_type_string) { + auto condition_wrapper = [kind_type_string](const YAML::Node& node) { + return is_kind(node, kind_type_string); + }; + return search(root, condition_wrapper); +} + +/* +Recursively loops through the node to record all the lattices and beamlines and +their corresponding parameters. +*/ +void get_dict_helper(YAML::Node node, + std::map* element_map) { if (node.IsSequence()) { - for (int i = 0; i < node.size(); i++) { - get_includes(node[i], include_list); + for (size_t i = 0; i < node.size(); i++) { + YAML::Node child = node[i]; + get_dict_helper(child, element_map); } - } else if (node.IsMap()) { + } + else if (node.IsMap()) { for (auto ele : node) { - if (ele.first.as() == "include") { - std::string filename = ele.second.as(); - if (std::find(include_list->begin(), include_list->end(), filename) == include_list->end()) { - - include_list->push_back(filename); - YAML::Node child = YAML::LoadFile("../lattice_files/" + filename); - get_includes(child, include_list); - } + if (ele.second.IsMap() && ele.second["kind"]) { + element_map->insert({ele.first.as(), ele.second}); + } else { + get_dict_helper(ele.second, element_map); } - get_includes(ele.second, include_list); } } } -YAML::Node original_lattices(std::string filename) { - YAML::Node root = YAML::Node(YAML::NodeType::Sequence); - std::vector include_list; - add_to_original(&root, filename); - get_includes(YAML::LoadFile("../lattice_files/" + filename), &include_list); - for (std::string file : include_list) { - add_to_original(&root, file); +/* +Constructs the map from names to lattice elements. The values in the map +are references to lattice elements with a given name, and modifying them +will directly modifty the lattice. Elements in values are stored in the order +that they appear in the lattice file. Use YAML::Clone if only the information +is required. +*/ +std::map* get_dict(YAML::Node root) { + std::map* element_map = new std::map(); + get_dict_helper(root, element_map); + return element_map; +} + +/* +Adds the file contained in `filename` to `original`, which should be lat.original. +The key is the filename and the value is the contents of the file. +*/ +void add_to_original(YAML::Node original, std::string filename) { + if(!original[filename]) { + YAML::Node node = YAML::Node(YAML::NodeType::Map); + node["path"] = ""; + node["info"] = YAML::LoadFile("../lattice_files/" + filename); + original[filename] = node; } - return root; } -struct lattices_int get_lattices_int(std::string filename) { - struct lattices_int lat; - lat.original = original_lattices(filename); - return lat; +/* +Constructs the original lattice. +*/ +YAML::Node original_lattice(std::string filename) { + YAML::Node original = YAML::Node(YAML::NodeType::Map); + + std::vector files_to_process; + files_to_process.push_back(filename); + + auto find_includes = [](const YAML::Node& node) { + return node.IsMap() && node["include"]; + }; + + while (!files_to_process.empty()) { + std::string current_file = files_to_process.back(); + files_to_process.pop_back(); + + if (original[current_file]) { + continue; + } + + add_to_original(original, current_file); + YAML::Node loaded_content = original[current_file]["info"]; + std::vector includes_found = search(loaded_content, find_includes); + for (const auto& inc_node : includes_found) { + std::string inc_fn = inc_node["include"].as(); + if (inc_fn.size() >= 5 && inc_fn.substr(inc_fn.size() - 5) == ".yaml") { + files_to_process.push_back(inc_fn); + } + } + } + return original; } -// if file only has one lattice, expand that lattice. if multiple lattices, use (expand) the last one only -// if there's a use statement, expand the lattice specified in the use statement -// need way to specify which lattice to use on command line with -lattice, for example -// only expand stuff in the specified lattice -// ======= LATTICE EXPANSION UTILS /* -Inserts the elements in `line` into `seq` at `index` a `repeat` number of times. -The value at index will be rewritten by the first inserted element. For example, -if seq = [1,2,3,4,5], line = [a,b], index = 2, repeat = 3, calling the function -will output [1,2,a,b,a,b,a,b,4,5]. +Constructs the included lattice. */ -YAML::Node repeat(YAML::Node line, YAML::Node seq, int index, int repeat) { +YAML::Node included_lattice(std::string filename) { + YAML::Node included = YAML::LoadFile("../lattice_files/" + filename); + std::vector include_files = search(included, [](YAML::Node node) { + return node.IsMap() && node["include"]; + }); + for (int i = 0; i < include_files.size(); i++) { + std::string inc_fn = include_files[i]["include"].as(); + if (inc_fn.size() >= 5 && inc_fn.substr(inc_fn.size() - 5) == ".yaml") { + YAML::Node node(YAML::NodeType::Sequence); + YAML::Node content = included_lattice(inc_fn); + YAML::Node file(YAML::NodeType::Map); + file["file"] = inc_fn; + + node.push_back(file); + node.push_back(content[0]); + + include_files[i].remove("include"); + include_files[i]["included"] = node; + } + } + return included; +} + +/* +Returns a new node with the elements in `line` inserted into `seq` at `index` +a `repeat` number of times. The value at index will be rewritten by the first +inserted element. For example, if seq = [1,2,3,4,5], line = [a,b], index = 2, repeat = 3, +calling the function will output [1,2,a,b,a,b,a,b,4,5]. +*/ +YAML::Node repeat(YAML::Node target_element, YAML::Node seq, int index, int repeat_count) { YAML::Node exp = YAML::Node(YAML::NodeType::Sequence); for (int i = 0; i < index; i++) { exp.push_back(YAML::Clone(seq[i])); } - for (int i = 0; i < repeat; i++) { - for (int j = 0; j < line.size(); j++) { - exp.push_back(YAML::Clone(line[j])); + + YAML::Node inner_content = target_element; + if (inner_content.IsMap() && inner_content["line"]) { + inner_content = inner_content["line"]; + } + + for (int i = 0; i < repeat_count; i++) { + if (inner_content.IsSequence()) { + for (std::size_t j = 0; j < inner_content.size(); j++) { + exp.push_back(YAML::Clone(inner_content[j])); + } + } else { + exp.push_back(YAML::Clone(inner_content)); } } - for (int i = index + 1; i < seq.size(); i++) { + + for (std::size_t i = index + 1; i < seq.size(); i++) { exp.push_back(YAML::Clone(seq[i])); } return exp; } -// recursively loops through the node to record all the elements and their -// corresponding parameters -void get_dict_helper(YAML::Node node, std::map* seen) { - if (node.IsSequence()) { - for (int i = 0; i < node.size(); i++) { - get_dict_helper(node[i], seen); +/* +Performs lattice expansion on the provided `node`. +*/ +YAML::Node expand_internal(YAML::Node node, std::map* elements_map) { + if (node.IsScalar() && elements_map->count(node.as())) { + std::string element_name = node.as(); + YAML::Node wrapped_node = YAML::Node(YAML::NodeType::Map); + + wrapped_node[element_name] = expand_internal(YAML::Clone(elements_map->at(element_name)), elements_map); + return wrapped_node; + } + else if (node.IsSequence()) { + for (std::size_t i = 0; i < node.size(); i++) { + if (node[i].IsMap()) { + for (auto ele : node[i]) { + if (ele.second.IsMap() && ele.second["repeat"]) { + std::string target_name = ele.first.as(); + if (elements_map->count(target_name)) { + YAML::Node target_node = YAML::Clone(elements_map->at(target_name)); + YAML::Node repeated_seq = repeat(target_node, node, i, ele.second["repeat"].as()); + return expand_internal(repeated_seq, elements_map); + } + } + } + } } - } else if (node.IsMap()) { - for (auto ele : node) { - // exclude key words from being stored - if (ele.first.as() == "include" || - ele.first.as() == "inherit" || - (ele.second.IsMap() && ele.second["repeat"])) { - continue; + YAML::Node new_seq = YAML::Node(YAML::NodeType::Sequence); + for (std::size_t i = 0; i < node.size(); i++) { + new_seq.push_back(expand_internal(node[i], elements_map)); + } + return new_seq; + } + else if (node.IsMap()) { + YAML::Node new_map = YAML::Clone(node); + + if (new_map["inherit"]) { + std::string parent_name = new_map["inherit"].as(); + new_map.remove("inherit"); + new_map["inherited"] = parent_name; + + if (elements_map->count(parent_name)) { + YAML::Node parent = elements_map->at(parent_name); + for (auto elep : parent) { + std::string key = elep.first.as(); + if (!new_map[key]) { + new_map[key] = YAML::Clone(elep.second); + } + } } - seen->insert( - {ele.first.as(), YAML::Clone(ele.second)}); - get_dict_helper(ele.second, seen); } - } -} - -std::map* get_dict(YAML::Node root) { - std::map* seen = - new std::map(); - ; - get_dict_helper(root, seen); - return seen; + + YAML::Node final_map = YAML::Node(YAML::NodeType::Map); + for (auto ele : new_map) { + final_map[ele.first.as()] = expand_internal(ele.second, elements_map); + } + return final_map; + } + + return YAML::Clone(node); } -// === EXPAND === -YAML::Node expand_internal(YAML::Node node, - std::map* seen) { - if (node.IsSequence()) { - bool modified = true; - while (modified) { - modified = false; - for (int i = 0; i < node.size(); i++) { - if (node[i].IsMap()) { - for (auto ele : node[i]) { - if (ele.second.IsMap() && ele.second["repeat"]) { - YAML::Node line = - (*seen)[ele.first.as()]["line"]; - node = repeat(line, node, i, - ele.second["repeat"].as()); - modified = true; - break; - } +/* +Performs lattice expansion with the following rules: +1. If name is specified, the lattice in `root` with the given name will be expanded. +2. If no name is specified, the lattice that appears latest in `root` will be expanded. +*/ +void find_and_replace(std::string name, YAML::Node root, std::map* elements_map) { + if (name != "") { + for (std::size_t i = 0; i < root.size(); i++) { + if (root[i].IsMap() && root[i][name]) { + root[i][name] = expand_internal(root[i][name], elements_map); + return; + } + } + } else { + for (int i = root.size() - 1; i >= 0; i--) { + if (root[i].IsMap() && root[i]["use"]) { + std::string target = root[i]["use"].as(); + + for (std::size_t j = 0; j < root.size(); j++) { + if (root[j].IsMap() && root[j][target]) { + root[j][target] = expand_internal(root[j][target], elements_map); + return; } - if (modified) break; } } } - for (int i = 0; i < node.size(); i++) { - node[i] = expand_internal(node[i], seen); - } - return node; - } else if (node.IsMap()) { - for (auto ele : node) { - // will probably need to add better method of finding files - // seperate out include and expansion - // one struct for og lattice and include files, one for expanded, one for included - // called original, included, expanded - // default print out info from og, print which file was printed - // if people say original and filename, only print out that file - // need support for recursive include - if (ele.first.as() == "include") { - std::string filename = ele.second.as(); - YAML::Node seq = YAML::Node(YAML::NodeType::Sequence); - YAML::Node file = YAML::Node(YAML::NodeType::Map); - file["file"] = filename; - seq.push_back(file); - seq.push_back( - YAML::LoadFile("../lattice_files/" + filename)[0]); - - ele.first = "included"; - ele.second = seq; - break; - } else if (ele.first.as() == "inherit") { - YAML::Node parent = (*seen)[ele.second.as()]; - for (auto ele : parent) { - node[ele.first.as()] = ele.second; - } - ele.first = "inherited"; + for (int i = root.size() - 1; i >= 0; i--) { + if (is_kind(root[i], "Lattice")) { + root[i] = expand_internal(root[i], elements_map); + return; } - node[ele.first.as()] = - expand_internal(ele.second, seen); } - return node; - } else { - return node; } } +/* +Constructs the expanded lattice. Expanding has the following priority: +1. If a lattice name is supplied through lattice_name, that lattice will be expanded. +2. If no lattice_name is supplied, the lattice specified by the last use statement +will be expanded. +3. If no lattice_name is supplied and no use statements are present, the lattice +that occurs latest in the file will be expanded. +*/ +YAML::Node expanded_lattice(std::string filename, std::string lattice_name) { + YAML::Node root = YAML::LoadFile("../lattice_files/" + filename); + root = included_lattice(filename); + std::map* elements_map = get_dict(root); + + find_and_replace(lattice_name, root, elements_map); + return root; +} + +struct lattices_int get_lattices_int(const std::string& filename, const std::string& lattice_name = "") { + struct lattices_int lat; + lat.original = original_lattice(filename); + lat.included = included_lattice(filename); + lat.expanded = expanded_lattice(filename, lattice_name); + return lat; +} + extern "C" { typedef void* YAMLNodeHandle; @@ -192,11 +337,11 @@ struct lattices { YAMLNodeHandle expanded; }; -YAML_API struct lattices get_lattices(const char* filename) { +YAML_API struct lattices get_lattices(const char* filename, const char* lattice_name) { struct lattices lat; - lat.original = static_cast(new YAML::Node(original_lattices(std::string(filename)))); - lat.included = static_cast(new YAML::Node()); - lat.expanded = static_cast(new YAML::Node()); + lat.original = static_cast(new YAML::Node(original_lattice(std::string(filename)))); + lat.included = static_cast(new YAML::Node(included_lattice(std::string(filename)))); + lat.expanded = static_cast(new YAML::Node(expanded_lattice(std::string(filename), std::string(lattice_name)))); return lat; } @@ -609,12 +754,4 @@ YAML_API void yaml_free_keys(char** keys, int count) { YAML_API YAMLNodeHandle yaml_clone(YAMLNodeHandle handle) { return new YAML::Node(YAML::Clone(*static_cast(handle))); } - -// C interface for expand - handles the map internally -YAML_API YAMLNodeHandle lattice_expand(YAMLNodeHandle handle) { - auto node = static_cast(handle); - std::map* seen = get_dict(*node); - YAML::Node result = expand_internal(*node, seen); - return new YAML::Node(result); -} } \ No newline at end of file diff --git a/src/yaml_c_wrapper.h b/src/yaml_c_wrapper.h index f61305f..7f58e38 100644 --- a/src/yaml_c_wrapper.h +++ b/src/yaml_c_wrapper.h @@ -14,7 +14,7 @@ struct lattices { YAMLNodeHandle expanded; }; -struct lattices get_lattices(const char* filename); +struct lattices get_lattices(const char* filename, const char* lattice_name); // === CREATION === YAMLNodeHandle create_node(void); @@ -73,7 +73,6 @@ void push_node(YAMLNodeHandle handle, YAMLNodeHandle value); char* yaml_to_string(YAMLNodeHandle handle); char* yaml_emit(YAMLNodeHandle handle, int indent); YAMLNodeHandle yaml_clone(YAMLNodeHandle handle); -YAMLNodeHandle lattice_expand(YAMLNodeHandle handle); bool write_file(YAMLNodeHandle handle, const char* filename); bool write_file_formatted(YAMLNodeHandle handle, const char* filename, From 46300e12dd11272ec613e33a033262ccd00d9eb7 Mon Sep 17 00:00:00 2001 From: Alexheeeee Date: Fri, 6 Mar 2026 02:14:27 -0500 Subject: [PATCH 4/7] typo --- src/yaml_c_wrapper.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/yaml_c_wrapper.cpp b/src/yaml_c_wrapper.cpp index 1884597..d495f17 100644 --- a/src/yaml_c_wrapper.cpp +++ b/src/yaml_c_wrapper.cpp @@ -8,10 +8,8 @@ #if defined(_WIN32) #define YAML_API extern "C" __declspec(dllexport) -#define YAML_API extern "C" __declspec(dllexport) #else #define YAML_API extern "C" __attribute__((visibility("default"))) -#define YAML_API extern "C" __attribute__((visibility("default"))) #endif // TODO: add absolute file paths From 0e7550a4a0ba235a0f4082d9ad74ead6521ed846 Mon Sep 17 00:00:00 2001 From: Alexheeeee Date: Fri, 6 Mar 2026 02:19:36 -0500 Subject: [PATCH 5/7] merge duplicates --- src/yaml_c_wrapper.cpp | 65 ------------------------------------------ 1 file changed, 65 deletions(-) diff --git a/src/yaml_c_wrapper.cpp b/src/yaml_c_wrapper.cpp index d495f17..d5a3ca2 100644 --- a/src/yaml_c_wrapper.cpp +++ b/src/yaml_c_wrapper.cpp @@ -391,23 +391,6 @@ YAML_API void delete_node(YAMLNodeHandle handle) { delete static_cast(handle); } -// === PARSING === -YAML_API YAMLNodeHandle parse_string(const char* yaml_str) { - try { - return new YAML::Node(YAML::Load(yaml_str)); - } catch (...) { - return nullptr; - } -} - -YAML_API YAMLNodeHandle parse_file(const char* filename) { - try { - return new YAML::Node(YAML::LoadFile(filename)); - } catch (...) { - return nullptr; - } -} - // === TYPE CHECKS === YAML_API bool is_scalar(YAMLNodeHandle handle) { return static_cast(handle)->IsScalar(); @@ -452,9 +435,6 @@ YAML_API bool has_key(YAMLNodeHandle handle, const char* key) { YAML_API int size(YAMLNodeHandle handle) { return static_cast(handle)->size(); } -YAML_API void delete_node(YAMLNodeHandle handle) { - delete static_cast(handle); -} // === PARSING === YAML_API YAMLNodeHandle parse_string(const char* yaml_str) { @@ -473,51 +453,6 @@ YAML_API YAMLNodeHandle parse_file(const char* filename) { } } -// === TYPE CHECKS === -YAML_API bool is_scalar(YAMLNodeHandle handle) { - return static_cast(handle)->IsScalar(); -} - -YAML_API bool is_sequence(YAMLNodeHandle handle) { - return static_cast(handle)->IsSequence(); -} - -YAML_API bool is_map(YAMLNodeHandle handle) { - return static_cast(handle)->IsMap(); -} - -YAML_API bool is_null(YAMLNodeHandle handle) { - return static_cast(handle)->IsNull(); -} - -// === ACCESS === -// equivalent to map[key] -YAML_API YAMLNodeHandle get_key(YAMLNodeHandle handle, const char* key) { - auto node = static_cast(handle); - auto child = (*node)[key]; - if (!child.IsDefined()) { - return nullptr; - } - return new YAML::Node(child); -} - -YAML_API YAMLNodeHandle get_index(YAMLNodeHandle handle, int index) { - auto node = static_cast(handle); - if (index < 0 || index >= node->size()) { - return nullptr; - } - return new YAML::Node((*node)[index]); -} - -YAML_API bool has_key(YAMLNodeHandle handle, const char* key) { - auto node = static_cast(handle); - return (*node)[key].IsDefined(); -} - -YAML_API int size(YAMLNodeHandle handle) { - return static_cast(handle)->size(); -} - YAML_API char** get_keys(YAMLNodeHandle handle, int* out_count) { auto node = static_cast(handle); if (!node->IsMap()) { From 249a723dc63d6f42e8283c5c299835af284cf660 Mon Sep 17 00:00:00 2001 From: Alexheeeee Date: Fri, 6 Mar 2026 02:23:46 -0500 Subject: [PATCH 6/7] removed test that depended on old ex.pals.yaml --- tests/test_yaml_c_wrapper.cpp | 75 ----------------------------------- 1 file changed, 75 deletions(-) diff --git a/tests/test_yaml_c_wrapper.cpp b/tests/test_yaml_c_wrapper.cpp index 2b8b669..a2ced7e 100644 --- a/tests/test_yaml_c_wrapper.cpp +++ b/tests/test_yaml_c_wrapper.cpp @@ -598,81 +598,6 @@ TEST_CASE("YAML nodes can be cloned", "[clone]") { } } -// =========================================== -// TEST SUITE: ex.pals.yaml Structure Tests -// =========================================== - -TEST_CASE("ex.pals.yaml has expected structure", "[ex.pals.yaml][structure]") { - YAMLNodeHandle root = parse_file("../lattice_files/ex.pals.yaml"); - REQUIRE(root != nullptr); - - SECTION("Root is a sequence") { - REQUIRE(is_sequence(root)); - REQUIRE(size(root) >= 2); - } - - SECTION("First element has thingB") { - YAMLNodeHandle first = get_index(root, 0); - REQUIRE(first != nullptr); - REQUIRE(is_map(first)); - REQUIRE(has_key(first, "thingB")); - - YAMLNodeHandle thingB = get_key(first, "thingB"); - REQUIRE(is_map(thingB)); - REQUIRE(has_key(thingB, "kind")); - - YAMLNodeHandle kind = get_key(thingB, "kind"); - char* kind_str = as_string(kind); - REQUIRE(std::string(kind_str) == "Sextupole"); - - yaml_free_string(kind_str); - delete_node(kind); - delete_node(thingB); - delete_node(first); - } - - SECTION("Second element has inj_line") { - YAMLNodeHandle second = get_index(root, 1); - REQUIRE(second != nullptr); - REQUIRE(is_map(second)); - REQUIRE(has_key(second, "inj_line")); - - YAMLNodeHandle inj_line = get_key(second, "inj_line"); - REQUIRE(is_map(inj_line)); - REQUIRE(has_key(inj_line, "kind")); - REQUIRE(has_key(inj_line, "multipass")); - REQUIRE(has_key(inj_line, "line")); - - YAMLNodeHandle kind = get_key(inj_line, "kind"); - char* kind_str = as_string(kind); - REQUIRE(std::string(kind_str) == "BeamLine"); - - YAMLNodeHandle multipass = get_key(inj_line, "multipass"); - REQUIRE(as_bool(multipass) == true); - - yaml_free_string(kind_str); - delete_node(kind); - delete_node(multipass); - delete_node(inj_line); - delete_node(second); - } - - SECTION("inj_line has line sequence") { - YAMLNodeHandle second = get_index(root, 1); - YAMLNodeHandle inj_line = get_key(second, "inj_line"); - YAMLNodeHandle line = get_key(inj_line, "line"); - - REQUIRE(is_sequence(line)); - REQUIRE(size(line) >= 3); - - delete_node(line); - delete_node(inj_line); - delete_node(second); - } - - delete_node(root); -} - // =========================================== // TEST SUITE: Memory Safety // =========================================== From 6dfa3240860a7d34290adf855bdb2a2efeece4ad Mon Sep 17 00:00:00 2001 From: Alexheeeee Date: Fri, 6 Mar 2026 02:27:35 -0500 Subject: [PATCH 7/7] missing include --- src/get_lattices.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/get_lattices.cpp b/src/get_lattices.cpp index 8608331..ff2d4c8 100644 --- a/src/get_lattices.cpp +++ b/src/get_lattices.cpp @@ -1,5 +1,6 @@ #include #include "../src/yaml_c_wrapper.h" +#include int main(int argc, char* argv[]) { std::string file_name = "ex.pals.yaml";