You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							411 lines
						
					
					
						
							9.8 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							411 lines
						
					
					
						
							9.8 KiB
						
					
					
				| #ifdef _MSC_VER | |
| #include "stdafx.h" | |
| #endif | |
|  | |
| #include "cpptempl.h" | |
|  | |
| #include <sstream> | |
| #include <boost/algorithm/string.hpp> | |
| #include <boost/lexical_cast.hpp> | |
|  | |
| namespace cpptempl | |
| { | |
| 	////////////////////////////////////////////////////////////////////////// | |
| 	// Data classes | |
| 	////////////////////////////////////////////////////////////////////////// | |
|  | |
| 	// data_map | |
| 	data_ptr& data_map::operator [](const std::string& key) { | |
| 		return data[key]; | |
| 	} | |
| 	bool data_map::empty() { | |
| 		return data.empty(); | |
| 	} | |
| 	bool data_map::has(const std::string& key) { | |
| 		return data.find(key) != data.end(); | |
| 	} | |
| 
 | |
| 	// data_ptr | |
|     data_ptr::data_ptr(DataValue* data) : ptr(data) {} | |
|     data_ptr::data_ptr(DataList* data) : ptr(data) {} | |
|     data_ptr::data_ptr(DataMap* data) : ptr(data) {} | |
|      | |
| 	template<> | |
| 	inline void data_ptr::operator = (const data_ptr& data) { | |
| 		ptr = data.ptr; | |
| 	} | |
| 
 | |
| 	template<> | |
| 	void data_ptr::operator = (const std::string& data) { | |
| 		ptr.reset(new DataValue(data)); | |
| 	} | |
| 
 | |
| 	template<> | |
| 	void data_ptr::operator = (const data_map& data) { | |
| 		ptr.reset(new DataMap(data)); | |
| 	} | |
| 
 | |
| 	void data_ptr::push_back(const data_ptr& data) { | |
| 		if (!ptr) { | |
| 			ptr.reset(new DataList(data_list())); | |
| 		} | |
| 		data_list& list = ptr->getlist(); | |
| 		list.push_back(data); | |
| 	} | |
| 
 | |
| 	// base data | |
|     std::string Data::getvalue() | |
| 	{ | |
| 		throw TemplateException("Data item is not a value") ; | |
| 	} | |
| 
 | |
| 	data_list& Data::getlist() | |
| 	{ | |
| 		throw TemplateException("Data item is not a list") ; | |
| 	} | |
| 	data_map& Data::getmap() | |
| 	{ | |
| 		throw TemplateException("Data item is not a dictionary") ; | |
| 	} | |
| 	// data value | |
|     std::string DataValue::getvalue() | |
| 	{ | |
| 		return m_value ; | |
| 	} | |
| 	bool DataValue::empty() | |
| 	{ | |
| 		return m_value.empty(); | |
| 	} | |
| 	// data list | |
| 	data_list& DataList::getlist() | |
| 	{ | |
| 		return m_items ; | |
| 	} | |
| 
 | |
| 	bool DataList::empty() | |
| 	{ | |
| 		return m_items.empty(); | |
| 	} | |
| 	// data map | |
| 	data_map& DataMap:: getmap() | |
| 	{ | |
| 		return m_items ; | |
| 	} | |
| 	bool DataMap::empty() | |
| 	{ | |
| 		return m_items.empty(); | |
| 	} | |
| 	////////////////////////////////////////////////////////////////////////// | |
| 	// parse_val | |
| 	////////////////////////////////////////////////////////////////////////// | |
| 	data_ptr parse_val(std::string key, data_map &data) | |
| 	{ | |
| 		// quoted string | |
| 		if (key[0] == '\"') | |
| 		{ | |
| 			return make_data(boost::trim_copy_if(key, boost::is_any_of("\""))) ; | |
| 		} | |
| 		// check for dotted notation, i.e [foo.bar] | |
| 		size_t index = key.find(".") ; | |
| 		if (index == std::string::npos) | |
| 		{ | |
| 			if (!data.has(key)) | |
| 			{ | |
| 				return make_data("{$" + key + "}") ; | |
| 			} | |
| 			return data[key] ; | |
| 		} | |
| 
 | |
|         std::string sub_key = key.substr(0, index) ; | |
| 		if (!data.has(sub_key)) | |
| 		{ | |
| 			return make_data("{$" + key + "}") ; | |
| 		} | |
| 		data_ptr item = data[sub_key] ; | |
| 		return parse_val(key.substr(index+1), item->getmap()) ; | |
| 	} | |
| 
 | |
| 	////////////////////////////////////////////////////////////////////////// | |
| 	// Token classes | |
| 	////////////////////////////////////////////////////////////////////////// | |
|  | |
| 	// defaults, overridden by subclasses with children | |
| 	void Token::set_children( token_vector & ) | |
| 	{ | |
| 		throw TemplateException("This token type cannot have children") ; | |
| 	} | |
| 
 | |
| 	token_vector & Token::get_children() | |
| 	{ | |
| 		throw TemplateException("This token type cannot have children") ; | |
| 	} | |
| 
 | |
| 	// TokenText | |
| 	TokenType TokenText::gettype() | |
| 	{ | |
| 		return TOKEN_TYPE_TEXT ; | |
| 	} | |
| 
 | |
| 	void TokenText::gettext( std::ostream &stream, data_map & ) | |
| 	{ | |
| 		stream << m_text ; | |
| 	} | |
| 
 | |
| 	// TokenVar | |
| 	TokenType TokenVar::gettype() | |
| 	{ | |
| 		return TOKEN_TYPE_VAR ; | |
| 	} | |
| 
 | |
| 	void TokenVar::gettext( std::ostream &stream, data_map &data ) | |
| 	{ | |
| 		stream << parse_val(m_key, data)->getvalue() ; | |
| 	} | |
| 
 | |
| 	// TokenFor | |
| 	TokenFor::TokenFor(std::string expr) | |
| 	{ | |
| 		std::vector<std::string> elements ; | |
| 		boost::split(elements, expr, boost::is_space()) ; | |
| 		if (elements.size() != 4u) | |
| 		{ | |
| 			throw TemplateException("Invalid syntax in for statement") ; | |
| 		} | |
| 		m_val = elements[1] ; | |
| 		m_key = elements[3] ; | |
| 	} | |
| 
 | |
| 	TokenType TokenFor::gettype() | |
| 	{ | |
| 		return TOKEN_TYPE_FOR ; | |
| 	} | |
| 
 | |
| 	void TokenFor::gettext( std::ostream &stream, data_map &data ) | |
| 	{ | |
| 		data_ptr value = parse_val(m_key, data) ; | |
| 		data_list &items = value->getlist() ; | |
| 		for (size_t i = 0 ; i < items.size() ; ++i) | |
| 		{ | |
| 			data_map loop ; | |
| 			loop["index"] = make_data(boost::lexical_cast<std::string>(i+1)) ; | |
| 			loop["index0"] = make_data(boost::lexical_cast<std::string>(i)) ; | |
| 			data["loop"] = make_data(loop); | |
| 			data[m_val] = items[i] ; | |
| 			for(size_t j = 0 ; j < m_children.size() ; ++j) | |
| 			{ | |
| 				m_children[j]->gettext(stream, data) ; | |
| 			} | |
| 		} | |
| 	} | |
| 
 | |
| 	void TokenFor::set_children( token_vector &children ) | |
| 	{ | |
| 		m_children.assign(children.begin(), children.end()) ; | |
| 	} | |
| 
 | |
| 	token_vector & TokenFor::get_children() | |
| 	{ | |
| 		return m_children; | |
| 	} | |
| 
 | |
| 	// TokenIf | |
| 	TokenType TokenIf::gettype() | |
| 	{ | |
| 		return TOKEN_TYPE_IF ; | |
| 	} | |
| 
 | |
| 	void TokenIf::gettext( std::ostream &stream, data_map &data ) | |
| 	{ | |
| 		if (is_true(m_expr, data)) | |
| 		{ | |
| 			for(size_t j = 0 ; j < m_children.size() ; ++j) | |
| 			{ | |
| 				m_children[j]->gettext(stream, data) ; | |
| 			} | |
| 		} | |
| 	} | |
| 
 | |
| 	bool TokenIf::is_true( std::string expr, data_map &data ) | |
| 	{ | |
| 		std::vector<std::string> elements ; | |
| 		boost::split(elements, expr, boost::is_space()) ; | |
| 
 | |
| 		if (elements[1] == "not") | |
| 		{ | |
| 			return parse_val(elements[2], data)->empty() ; | |
| 		} | |
| 		if (elements.size() == 2) | |
| 		{ | |
| 			return ! parse_val(elements[1], data)->empty() ; | |
| 		} | |
| 		data_ptr lhs = parse_val(elements[1], data) ; | |
| 		data_ptr rhs = parse_val(elements[3], data) ; | |
| 		if (elements[2] == "==") | |
| 		{ | |
| 			return lhs->getvalue() == rhs->getvalue() ; | |
| 		} | |
| 		return lhs->getvalue() != rhs->getvalue() ; | |
| 	} | |
| 
 | |
| 	void TokenIf::set_children( token_vector &children ) | |
| 	{ | |
| 		m_children.assign(children.begin(), children.end()) ; | |
| 	} | |
| 
 | |
| 	token_vector & TokenIf::get_children() | |
| 	{ | |
| 		return m_children; | |
| 	} | |
| 
 | |
| 	// TokenEnd | |
| 	TokenType TokenEnd::gettype() | |
| 	{ | |
| 		return m_type == "endfor" ? TOKEN_TYPE_ENDFOR : TOKEN_TYPE_ENDIF ; | |
| 	} | |
| 
 | |
| 	void TokenEnd::gettext( std::ostream &, data_map &) | |
| 	{ | |
| 		throw TemplateException("End-of-control statements have no associated text") ; | |
| 	} | |
| 
 | |
| 	// gettext | |
| 	// generic helper for getting text from tokens. | |
|  | |
|     std::string gettext(token_ptr token, data_map &data) | |
| 	{ | |
| 		std::ostringstream stream ; | |
| 		token->gettext(stream, data) ; | |
| 		return stream.str() ; | |
| 	} | |
| 	////////////////////////////////////////////////////////////////////////// | |
| 	// parse_tree | |
| 	// recursively parses list of tokens into a tree | |
| 	////////////////////////////////////////////////////////////////////////// | |
| 	void parse_tree(token_vector &tokens, token_vector &tree, TokenType until) | |
| 	{ | |
| 		while(! tokens.empty()) | |
| 		{ | |
| 			// 'pops' first item off list | |
| 			token_ptr token = tokens[0] ; | |
| 			tokens.erase(tokens.begin()) ; | |
| 
 | |
| 			if (token->gettype() == TOKEN_TYPE_FOR) | |
| 			{ | |
| 				token_vector children ; | |
| 				parse_tree(tokens, children, TOKEN_TYPE_ENDFOR) ; | |
| 				token->set_children(children) ; | |
| 			} | |
| 			else if (token->gettype() == TOKEN_TYPE_IF) | |
| 			{ | |
| 				token_vector children ; | |
| 				parse_tree(tokens, children, TOKEN_TYPE_ENDIF) ; | |
| 				token->set_children(children) ; | |
| 			} | |
| 			else if (token->gettype() == until) | |
| 			{ | |
| 				return ; | |
| 			} | |
| 			tree.push_back(token) ; | |
| 		} | |
| 	} | |
| 	////////////////////////////////////////////////////////////////////////// | |
| 	// tokenize | |
| 	// parses a template into tokens (text, for, if, variable) | |
| 	////////////////////////////////////////////////////////////////////////// | |
| 	token_vector & tokenize(std::string text, token_vector &tokens) | |
| 	{ | |
| 		while(! text.empty()) | |
| 		{ | |
| 			size_t pos = text.find("{") ; | |
| 			if (pos == std::string::npos) | |
| 			{ | |
| 				if (! text.empty()) | |
| 				{ | |
| 					tokens.push_back(token_ptr(new TokenText(text))) ; | |
| 				} | |
| 				return tokens ; | |
| 			} | |
|             std::string pre_text = text.substr(0, pos) ; | |
| 			if (! pre_text.empty()) | |
| 			{ | |
| 				tokens.push_back(token_ptr(new TokenText(pre_text))) ; | |
| 			} | |
| 			text = text.substr(pos+1) ; | |
| 			if (text.empty()) | |
| 			{ | |
| 				tokens.push_back(token_ptr(new TokenText("{"))) ; | |
| 				return tokens ; | |
| 			} | |
| 
 | |
| 			// variable | |
| 			if (text[0] == '$') | |
| 			{ | |
| 				pos = text.find("}") ; | |
| 				if (pos != std::string::npos) | |
| 				{ | |
| 					tokens.push_back(token_ptr (new TokenVar(text.substr(1, pos-1)))) ; | |
| 					text = text.substr(pos+1) ; | |
| 				} | |
| 			} | |
| 			// control statement | |
| 			else if (text[0] == '%') | |
| 			{ | |
| 				pos = text.find("}") ; | |
| 				if (pos != std::string::npos) | |
| 				{ | |
|                     std::string expression = boost::trim_copy(text.substr(1, pos-2)) ; | |
| 					text = text.substr(pos+1) ; | |
| 					if (boost::starts_with(expression, "for")) | |
| 					{ | |
| 						tokens.push_back(token_ptr (new TokenFor(expression))) ; | |
| 					} | |
| 					else if (boost::starts_with(expression, "if")) | |
| 					{ | |
| 						tokens.push_back(token_ptr (new TokenIf(expression))) ; | |
| 					} | |
| 					else | |
| 					{ | |
| 						tokens.push_back(token_ptr (new TokenEnd(boost::trim_copy(expression)))) ; | |
| 					} | |
| 				} | |
| 			} | |
| 			else | |
| 			{ | |
| 				tokens.push_back(token_ptr(new TokenText("{"))) ; | |
| 			} | |
| 		} | |
| 		return tokens ; | |
| 	} | |
| 
 | |
| 	/************************************************************************ | |
| 	* parse | |
| 	* | |
| 	*  1. tokenizes template | |
| 	*  2. parses tokens into tree | |
| 	*  3. resolves template | |
| 	*  4. returns converted text | |
| 	************************************************************************/ | |
|     std::string parse(std::string templ_text, data_map &data) | |
| 	{ | |
| 		std::ostringstream stream ; | |
| 		parse(stream, templ_text, data) ; | |
| 		return stream.str() ; | |
| 	} | |
| 	void parse(std::ostream &stream, std::string templ_text, data_map &data) | |
| 	{ | |
| 		token_vector tokens ; | |
| 		tokenize(templ_text, tokens) ; | |
| 		token_vector tree ; | |
| 		parse_tree(tokens, tree) ; | |
| 
 | |
| 		for (size_t i = 0 ; i < tree.size() ; ++i) | |
| 		{ | |
| 			// Recursively calls gettext on each node in the tree. | |
| 			// gettext returns the appropriate text for that node. | |
| 			// for text, itself; | |
| 			// for variable, substitution; | |
| 			// for control statement, recursively gets kids | |
| 			tree[i]->gettext(stream, data) ; | |
| 		} | |
| 	} | |
| }
 |