Skip to content

Instantly share code, notes, and snippets.

@duck2
Created June 23, 2019 03:15
Show Gist options
  • Save duck2/ad7fe7bb29962d9ffd5017ffad0a20c1 to your computer and use it in GitHub Desktop.
Save duck2/ad7fe7bb29962d9ffd5017ffad0a20c1 to your computer and use it in GitHub Desktop.
#include <cstring>
#include <memory>
#include <iostream>
#include <string>
#include <vector>
#include "pugixml.hpp"
struct t_hello;
struct t_hello {
std::string greeting;
std::vector<std::string> name_list;
};
std::unique_ptr<t_hello> hello;
void read_t_hello(pugi::xml_node &root, t_hello &out);
void read(const char *filename){
pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(filename);
if(!result){
throw std::runtime_error("Could not load XML file " + std::string(filename) + ".");
}
pugi::xml_node node = doc.first_child();
for(; node; node = node.next_sibling()){
if(std::strcmp(node.name(), "hello") == 0){
hello = std::unique_ptr<t_hello>(new t_hello);
read_t_hello(node, *hello);
}
else throw std::runtime_error("Invalid root-level element " + std::string(node.name()));
}
}
void read_t_hello(pugi::xml_node &root, t_hello &out){
pugi::xml_node node = root.first_child();
const char *in = node.name();
int state = 2;
while(1){
switch(state){
case 0:
if(strcmp(in, "name") == 0){
state = 0;
out.name_list.push_back(node.text().get());
}
else if(strcmp(in, "end of input") == 0){
goto accept;
}
else throw std::runtime_error("expected <name> or end of input, found " + std::string(in));
break;
case 1:
if(strcmp(in, "name") == 0){
state = 0;
out.name_list.push_back(node.text().get());
}
else throw std::runtime_error("expected <name>, found " + std::string(in));
break;
case 2:
if(strcmp(in, "greeting") == 0){
state = 1;
out.greeting = node.text().get();
}
else throw std::runtime_error("expected <greeting>, found " + std::string(in));
break;
}
node = node.next_sibling();
if(node) in = node.name();
else in = "end of input";
}
accept:
return;
}
int main(){
read("hello.xml");
std::cout << hello->greeting << "\n";
for(auto name : hello->name_list){
std::cout << "\t" << name << "\n";
}
}
@mithro
Copy link

mithro commented Jun 28, 2019

Is it possible to use enums for the state values and provide better names for them?

@duck2
Copy link
Author

duck2 commented Jun 28, 2019

Something like this?

enum class t_hello_state {GET_NAME_OR_END, GET_NAME, GET_GREETING};
void read_t_hello(pugi::xml_node &root, t_hello &out){
	pugi::xml_node node = root.first_child();
	const char *in = node.name();
	t_hello_state state = t_hello_state::GET_GREETING;
	while(1){
		switch(state){
		case t_hello_state::GET_NAME_OR_END:
			if(strcmp(in, "name") == 0){
				state = t_hello_state::GET_NAME_OR_END;
				out.name_list.push_back(node.text().get());
			}
			else if(strcmp(in, "end of input") == 0){
				goto accept;
			}
			else throw std::runtime_error("expected <name> or end of input, found " + std::string(in));
			break;
		case t_hello_state::GET_NAME:
			if(strcmp(in, "name") == 0){
				state = t_hello_state::GET_NAME_OR_END;
				out.name_list.push_back(node.text().get());
			}
			else throw std::runtime_error("expected <name>, found " + std::string(in));
			break;
		case t_hello_state::GET_GREETING:
			if(strcmp(in, "greeting") == 0){
				state = t_hello_state::GET_NAME;
				out.greeting = node.text().get();
			}
			else throw std::runtime_error("expected <greeting>, found " + std::string(in));
			break;
		}
		node = node.next_sibling();
		if(node) in = node.name();
		else in = "end of input";
	}
accept:
	return;
}

@mithro
Copy link

mithro commented Jun 28, 2019

Much better!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment