#ifdef _MSC_VER #include "stdafx.h" #endif #include "cpptempl.h" #include #include #include 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 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(i+1)) ; loop["index0"] = make_data(boost::lexical_cast(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 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) ; } } }