Skip to content

Commit

Permalink
Parameter substitution (Argonne-National-Laboratory#100)
Browse files Browse the repository at this point in the history
  • Loading branch information
dschwen committed Apr 2, 2021
1 parent c56c9ca commit 4b9a347
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 18 deletions.
80 changes: 67 additions & 13 deletions src/parse.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,59 @@

namespace neml {

std::shared_ptr<NEMLModel> parse_string(std::string input)
void recurseSubstitute(rapidxml::xml_node<> * node, std::map<std::string, std::string> substitutions)
{
auto * doc = node->document();

// perform substitution in all attributes
for (rapidxml::xml_attribute<> * attr = node->first_attribute(); attr; attr = attr->next_attribute())
{
std::string value(attr->value());

// find all variables
auto open = value.find("{");
auto close = value.find("}", open);
while (open != std::string::npos)
{
auto var = value.substr(open + 1, close - (open + 1));
auto sub = substitutions.find(var);
if (sub == substitutions.end())
throw UnknownVariableXML(attr, var);
value = value.replace(open, close - open, sub->second);

open = value.find("{", open + 1);
close = value.find("}", open);
}

// were any replacements made?
std::string original_value(attr->value());
if (value != original_value)
{
// allocate XML string data and replace attribute
char * new_value = doc->allocate_string(value.c_str());
auto *new_attr = doc->allocate_attribute(attr->name(), new_value);
node->insert_attribute(attr, new_attr);
node->remove_attribute(attr);
attr = new_attr;
}
}

// iterate over all child nodes
for (rapidxml::xml_node<> * child = node->first_node(); child; child = child->next_sibling())
recurseSubstitute(child, substitutions);
}

std::shared_ptr<NEMLModel> parse_string(std::string input, std::map<std::string, std::string> substitutions)
{
// Parse the string to the rapidxml representation
rapidxml::xml_document<> doc;
doc.parse<0>(&input[0]);

// The model is the root node
const rapidxml::xml_node<> * found = doc.first_node();
rapidxml::xml_node<> * found = doc.first_node();

// substitute {variables} in DOM tree
recurseSubstitute(found, substitutions);

// Get the NEMLObject
std::shared_ptr<NEMLObject> obj = get_object(found);
Expand All @@ -24,17 +69,20 @@ std::shared_ptr<NEMLModel> parse_string(std::string input)
}
}

std::unique_ptr<NEMLModel> parse_string_unique(std::string input, std::string mname)
std::unique_ptr<NEMLModel> parse_string_unique(std::string input, std::string mname, std::map<std::string, std::string> substitutions)
{
// Parse the string to the rapidxml representation
rapidxml::xml_document<> doc;
doc.parse<0>(&input[0]);

// Grab the root node
const rapidxml::xml_node<> * root = doc.first_node();
rapidxml::xml_node<> * root = doc.first_node();

// Find the node with the right name
const rapidxml::xml_node<> * found = root->first_node(mname.c_str());
rapidxml::xml_node<> * found = root->first_node(mname.c_str());

// substitute {variables} in DOM tree
recurseSubstitute(found, substitutions);

// Get the NEMLObject
std::unique_ptr<NEMLObject> obj = get_object_unique(found);
Expand All @@ -49,18 +97,21 @@ std::unique_ptr<NEMLModel> parse_string_unique(std::string input, std::string mn
}
}

std::shared_ptr<NEMLModel> parse_xml(std::string fname, std::string mname)
std::shared_ptr<NEMLModel> parse_xml(std::string fname, std::string mname, std::map<std::string, std::string> substitutions)
{
// Parse the XML file
rapidxml::file <> xmlFile(fname.c_str());
rapidxml::xml_document<> doc;
doc.parse<0>(xmlFile.data());

// Grab the root node
const rapidxml::xml_node<> * root = doc.first_node();
rapidxml::xml_node<> * root = doc.first_node();

// Find the node with the right name
const rapidxml::xml_node<> * found = root->first_node(mname.c_str());
rapidxml::xml_node<> * found = root->first_node(mname.c_str());

// substitute {variables} in DOM tree
recurseSubstitute(found, substitutions);

// Get the NEMLObject
std::shared_ptr<NEMLObject> obj = get_object(found);
Expand All @@ -75,18 +126,21 @@ std::shared_ptr<NEMLModel> parse_xml(std::string fname, std::string mname)
}
}

std::unique_ptr<NEMLModel> parse_xml_unique(std::string fname, std::string mname)
std::unique_ptr<NEMLModel> parse_xml_unique(std::string fname, std::string mname, std::map<std::string, std::string> substitutions)
{
// Parse the XML file
rapidxml::file <> xmlFile(fname.c_str());
rapidxml::xml_document<> doc;
doc.parse<0>(xmlFile.data());

// Grab the root node
const rapidxml::xml_node<> * root = doc.first_node();
rapidxml::xml_node<> * root = doc.first_node();

// Find the node with the right name
const rapidxml::xml_node<> * found = root->first_node(mname.c_str());
rapidxml::xml_node<> * found = root->first_node(mname.c_str());

// substitute {variables} in DOM tree
recurseSubstitute(found, substitutions);

// Get the NEMLObject
std::unique_ptr<NEMLObject> obj = get_object_unique(found);
Expand Down Expand Up @@ -204,7 +258,7 @@ std::vector<std::shared_ptr<NEMLObject>> get_vector_object(

// A somewhat dangerous shortcut -- a list of text values should be
// interpreted as a list of ConstantInterpolates
if ((rapidxml::count_children(const_cast<rapidxml::xml_node<>*>(node))==1) and
if ((rapidxml::count_children(const_cast<rapidxml::xml_node<>*>(node))==1) and
(node->first_node()->type() == rapidxml::node_data)) {
std::vector<double> data = get_vector_double(node);
for (auto v : data) {
Expand Down Expand Up @@ -395,7 +449,7 @@ std::vector<int> split_string_int(std::string sval)

std::string & strip(std::string & s)
{
auto noblank = [](char c) { return !std::isspace(c);};
auto noblank = [](char c) { return !std::isspace(c);};

s.erase(s.begin(), std::find_if(s.begin(), s.end(), noblank));
s.erase(std::find_if(s.rbegin(), s.rend(), noblank).base(), s.end());
Expand Down
36 changes: 31 additions & 5 deletions src/parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@

namespace neml {

/// perform {variable} substitution in DOM tree
void recurseSubstitute(rapidxml::xml_node<> * node, std::map<std::string, std::string> substitutions);

/// Parse from a string to a shared_ptr
std::shared_ptr<NEMLModel> parse_string(std::string input);
std::shared_ptr<NEMLModel> parse_string(std::string input, std::map<std::string, std::string> substitutions = std::map<std::string, std::string>());

/// Parse from a string to a unique_ptr
std::unique_ptr<NEMLModel> parse_string_unique(std::string input, std::string mname);
std::unique_ptr<NEMLModel> parse_string_unique(std::string input, std::string mname, std::map<std::string, std::string> substitutions = std::map<std::string, std::string>());

/// Parse from file to a shared_ptr
NEML_EXPORT std::shared_ptr<NEMLModel> parse_xml(std::string fname, std::string mname);
NEML_EXPORT std::shared_ptr<NEMLModel> parse_xml(std::string fname, std::string mname, std::map<std::string, std::string> substitutions = std::map<std::string, std::string>());

/// Parse from file to a unique_ptr
NEML_EXPORT std::unique_ptr<NEMLModel> parse_xml_unique(std::string fname, std::string mname);
NEML_EXPORT std::unique_ptr<NEMLModel> parse_xml_unique(std::string fname, std::string mname, std::map<std::string, std::string> substitutions = std::map<std::string, std::string>());

/// Extract a NEMLObject from a xml node as a unique_ptr
NEML_EXPORT std::unique_ptr<NEMLObject> get_object_unique(const rapidxml::xml_node<> * node);
Expand All @@ -38,7 +41,7 @@ NEML_EXPORT std::unique_ptr<NEMLObject> get_object_unique(const rapidxml::xml_no
std::shared_ptr<NEMLObject> get_object(const rapidxml::xml_node<> * node);

/// Actually get a valid parameter set from a node
ParameterSet get_parameters(const rapidxml::xml_node<> * node);
ParameterSet get_parameters(const rapidxml::xml_node<> * node);

/// Extract a vector of NEMLObjects from an xml node
std::vector<std::shared_ptr<NEMLObject>> get_vector_object(const rapidxml::xml_node<> * node);
Expand Down Expand Up @@ -201,6 +204,29 @@ class UnregisteredXML: public std::exception {

};

/// An unknown {variable} was found in the XML
class UnknownVariableXML: public std::exception {
public:
UnknownVariableXML(rapidxml::xml_attribute<> *attr, std::string var)
{
message_ = std::string(attr->name()) + " = '" + attr->value() + "'!";
auto parent = attr->parent();
while (parent) {
message_ = std::string(parent->name()) + " -> " + message_;
parent = parent->parent();
}
message_ = "No value provided for variable '" + var + "' in XML attribute\n" + message_;
};

const char * what() const throw ()
{
return message_.c_str();
};

private:
std::string message_;
};

} // namespace neml

#endif // PARSE_H

0 comments on commit 4b9a347

Please sign in to comment.