Browse Source
adding cpptemplate library
adding cpptemplate library
Former-commit-id:tempestpy_adaptions8f4d61de7a
[formerly9f307fe358
] Former-commit-id:3f30d88de8
dehnert
8 years ago
6 changed files with 1716 additions and 0 deletions
-
8resources/3rdparty/cpptemplate/LICENSE.txt
-
57resources/3rdparty/cpptemplate/README.rst
-
419resources/3rdparty/cpptemplate/cpptempl.cpp
-
308resources/3rdparty/cpptemplate/cpptempl.h
-
883resources/3rdparty/cpptemplate/cpptempl_test.cpp
-
41resources/3rdparty/cpptemplate/unit_testing.h
@ -0,0 +1,8 @@ |
|||
cpptempl |
|||
Copyright (c) Ryan Ginstrom |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,57 @@ |
|||
cpptempl |
|||
================= |
|||
This is a template engine for C++. |
|||
|
|||
Copyright |
|||
================== |
|||
Author: Ryan Ginstrom |
|||
MIT License |
|||
|
|||
Syntax |
|||
================= |
|||
|
|||
Variables:: |
|||
|
|||
{$variable_name} |
|||
|
|||
Loops:: |
|||
|
|||
{% for person in people %}Name: {$person.name}{% endfor %} |
|||
|
|||
If:: |
|||
|
|||
{% if person.name == "Bob" %}Full name: Robert{% endif %} |
|||
|
|||
|
|||
Usage |
|||
======================= |
|||
|
|||
Define a template:: |
|||
|
|||
string text = "{% if item %}{$item}{% endif %}\n" |
|||
"{% if thing %}{$thing}{% endif %}" ; |
|||
|
|||
Set up data:: |
|||
|
|||
cpptempl::data_map data ; |
|||
data["item"] = "aaa" ; |
|||
data["thing"] = "bbb" ; |
|||
|
|||
Parse the template and data:: |
|||
|
|||
string result = cpptempl::parse(text, data) ; |
|||
|
|||
Lists, nested maps |
|||
======================== |
|||
|
|||
Example:: |
|||
|
|||
cpptempl::data_map person ; |
|||
person["name"] = "Bob" ; |
|||
person["occupation"] = "Plumber" ; |
|||
|
|||
cpptempl::data_map content ; |
|||
content["person"] = person ; |
|||
content["friends"].push_back("Alice") ; |
|||
content["friends"].push_back("Bob") ; |
|||
string result = parse(text, data) ; |
@ -0,0 +1,419 @@ |
|||
#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::wstring& key) { |
|||
return data[key]; |
|||
} |
|||
data_ptr& data_map::operator [](const std::string& key) { |
|||
return data[utf8_to_wide(key)]; |
|||
} |
|||
bool data_map::empty() { |
|||
return data.empty(); |
|||
} |
|||
bool data_map::has(const wstring& key) { |
|||
return data.find(key) != data.end(); |
|||
} |
|||
|
|||
// data_ptr
|
|||
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(utf8_to_wide(data))); |
|||
} |
|||
|
|||
template<> |
|||
void data_ptr::operator = (const std::wstring& 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
|
|||
wstring 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
|
|||
wstring 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(wstring key, data_map &data) |
|||
{ |
|||
// quoted string
|
|||
if (key[0] == L'\"') |
|||
{ |
|||
return make_data(boost::trim_copy_if(key, boost::is_any_of(L"\""))) ; |
|||
} |
|||
// check for dotted notation, i.e [foo.bar]
|
|||
size_t index = key.find(L".") ; |
|||
if (index == wstring::npos) |
|||
{ |
|||
if (!data.has(key)) |
|||
{ |
|||
return make_data(L"{$" + key + L"}") ; |
|||
} |
|||
return data[key] ; |
|||
} |
|||
|
|||
wstring sub_key = key.substr(0, index) ; |
|||
if (!data.has(sub_key)) |
|||
{ |
|||
return make_data(L"{$" + key + L"}") ; |
|||
} |
|||
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::wostream &stream, data_map & ) |
|||
{ |
|||
stream << m_text ; |
|||
} |
|||
|
|||
// TokenVar
|
|||
TokenType TokenVar::gettype() |
|||
{ |
|||
return TOKEN_TYPE_VAR ; |
|||
} |
|||
|
|||
void TokenVar::gettext( std::wostream &stream, data_map &data ) |
|||
{ |
|||
stream << parse_val(m_key, data)->getvalue() ; |
|||
} |
|||
|
|||
// TokenFor
|
|||
TokenFor::TokenFor(wstring expr) |
|||
{ |
|||
std::vector<wstring> 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::wostream &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[L"index"] = make_data(boost::lexical_cast<wstring>(i+1)) ; |
|||
loop[L"index0"] = make_data(boost::lexical_cast<wstring>(i)) ; |
|||
data[L"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::wostream &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( wstring expr, data_map &data ) |
|||
{ |
|||
std::vector<wstring> elements ; |
|||
boost::split(elements, expr, boost::is_space()) ; |
|||
|
|||
if (elements[1] == L"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] == L"==") |
|||
{ |
|||
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 == L"endfor" ? TOKEN_TYPE_ENDFOR : TOKEN_TYPE_ENDIF ; |
|||
} |
|||
|
|||
void TokenEnd::gettext( std::wostream &, data_map &) |
|||
{ |
|||
throw TemplateException("End-of-control statements have no associated text") ; |
|||
} |
|||
|
|||
// gettext
|
|||
// generic helper for getting text from tokens.
|
|||
|
|||
wstring gettext(token_ptr token, data_map &data) |
|||
{ |
|||
std::wostringstream 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(wstring text, token_vector &tokens) |
|||
{ |
|||
while(! text.empty()) |
|||
{ |
|||
size_t pos = text.find(L"{") ; |
|||
if (pos == wstring::npos) |
|||
{ |
|||
if (! text.empty()) |
|||
{ |
|||
tokens.push_back(token_ptr(new TokenText(text))) ; |
|||
} |
|||
return tokens ; |
|||
} |
|||
wstring 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(L"{"))) ; |
|||
return tokens ; |
|||
} |
|||
|
|||
// variable
|
|||
if (text[0] == L'$') |
|||
{ |
|||
pos = text.find(L"}") ; |
|||
if (pos != wstring::npos) |
|||
{ |
|||
tokens.push_back(token_ptr (new TokenVar(text.substr(1, pos-1)))) ; |
|||
text = text.substr(pos+1) ; |
|||
} |
|||
} |
|||
// control statement
|
|||
else if (text[0] == L'%') |
|||
{ |
|||
pos = text.find(L"}") ; |
|||
if (pos != wstring::npos) |
|||
{ |
|||
wstring expression = boost::trim_copy(text.substr(1, pos-2)) ; |
|||
text = text.substr(pos+1) ; |
|||
if (boost::starts_with(expression, L"for")) |
|||
{ |
|||
tokens.push_back(token_ptr (new TokenFor(expression))) ; |
|||
} |
|||
else if (boost::starts_with(expression, L"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(L"{"))) ; |
|||
} |
|||
} |
|||
return tokens ; |
|||
} |
|||
|
|||
/************************************************************************
|
|||
* parse |
|||
* |
|||
* 1. tokenizes template |
|||
* 2. parses tokens into tree |
|||
* 3. resolves template |
|||
* 4. returns converted text |
|||
************************************************************************/ |
|||
wstring parse(wstring templ_text, data_map &data) |
|||
{ |
|||
std::wostringstream stream ; |
|||
parse(stream, templ_text, data) ; |
|||
return stream.str() ; |
|||
} |
|||
std::string parse(std::string templ_text, data_map &data) |
|||
{ |
|||
return wide_to_utf8(parse(utf8_to_wide(templ_text), data)); |
|||
} |
|||
void parse(std::wostream &stream, wstring 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) ; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,308 @@ |
|||
/* |
|||
cpptempl |
|||
================= |
|||
This is a template engine for C++. |
|||
|
|||
Syntax |
|||
================= |
|||
Variables: {$variable_name} |
|||
Loops: {% for person in people %}Name: {$person.name}{% endfor %} |
|||
If: {% for person.name == "Bob" %}Full name: Robert{% endif %} |
|||
|
|||
Copyright |
|||
================== |
|||
Author: Ryan Ginstrom |
|||
MIT License |
|||
|
|||
Usage |
|||
======================= |
|||
wstring text = L"{% if item %}{$item}{% endif %}\n" |
|||
L"{% if thing %}{$thing}{% endif %}" ; |
|||
cpptempl::data_map data ; |
|||
data[L"item"] = cpptempl::make_data(L"aaa") ; |
|||
data[L"thing"] = cpptempl::make_data(L"bbb") ; |
|||
|
|||
wstring result = cpptempl::parse(text, data) ; |
|||
|
|||
Handy Functions |
|||
======================== |
|||
make_data() : Feed it a string, data_map, or data_list to create a data entry. |
|||
Example: |
|||
data_map person ; |
|||
person[L"name"] = make_data(L"Bob") ; |
|||
person[L"occupation"] = make_data(L"Plumber") ; |
|||
data_map data ; |
|||
data[L"person"] = make_data(person) ; |
|||
wstring result = parse(templ_text, data) ; |
|||
|
|||
*/ |
|||
#pragma once |
|||
|
|||
#ifdef _WIN32 |
|||
#pragma warning( disable : 4996 ) // 'std::copy': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators' |
|||
#pragma warning( disable : 4512 ) // 'std::copy': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators' |
|||
#endif |
|||
|
|||
#include <string> |
|||
#include <vector> |
|||
#include <map> |
|||
#include <boost/shared_ptr.hpp> |
|||
#ifndef _MSC_VER |
|||
#include <boost/locale.hpp> |
|||
#else |
|||
#include <boost/scoped_array.hpp> |
|||
#include "windows.h" |
|||
#include "winnls.h" // unicode-multibyte conversion |
|||
#endif |
|||
#include <boost/unordered_map.hpp> |
|||
#include <boost/lexical_cast.hpp> |
|||
|
|||
#include <iostream> |
|||
|
|||
namespace cpptempl |
|||
{ |
|||
using std::wstring ; |
|||
// various typedefs |
|||
|
|||
class data_ptr; |
|||
typedef std::vector<data_ptr> data_list ; |
|||
|
|||
class data_map { |
|||
public: |
|||
data_ptr& operator [](const std::wstring& key); |
|||
data_ptr& operator [](const std::string& key); |
|||
bool empty(); |
|||
bool has(const wstring& key); |
|||
private: |
|||
boost::unordered_map<wstring, data_ptr> data; |
|||
}; |
|||
|
|||
// data classes |
|||
class Data ; |
|||
class DataValue ; |
|||
class DataList ; |
|||
class DataMap ; |
|||
|
|||
class data_ptr { |
|||
public: |
|||
data_ptr() {} |
|||
template<typename T> data_ptr(const T& data) { |
|||
this->operator =(data); |
|||
} |
|||
data_ptr(DataValue* data) : ptr(data) {} |
|||
data_ptr(DataList* data) : ptr(data) {} |
|||
data_ptr(DataMap* data) : ptr(data) {} |
|||
data_ptr(const data_ptr& data) { |
|||
ptr = data.ptr; |
|||
} |
|||
template<typename T> void operator = (const T& data); |
|||
void push_back(const data_ptr& data); |
|||
virtual ~data_ptr() {} |
|||
Data* operator ->() { |
|||
return ptr.get(); |
|||
} |
|||
private: |
|||
boost::shared_ptr<Data> ptr; |
|||
}; |
|||
|
|||
template<> inline void data_ptr::operator = (const data_ptr& data); |
|||
template<> void data_ptr::operator = (const std::string& data); |
|||
template<> void data_ptr::operator = (const std::wstring& data); |
|||
template<> void data_ptr::operator = (const data_map& data); |
|||
template<typename T> |
|||
void data_ptr::operator = (const T& data) { |
|||
#ifndef _MSC_VER |
|||
std::wstring data_str = boost::lexical_cast<std::wstring>(data); |
|||
#else |
|||
|
|||
#endif |
|||
this->operator =(data_str); |
|||
} |
|||
|
|||
// convenience functions for recoding utf8 string to wstring and back |
|||
inline std::wstring utf8_to_wide(const std::string& text) { |
|||
#ifndef _MSC_VER |
|||
return boost::locale::conv::to_utf<wchar_t>(text, "UTF-8"); |
|||
#else |
|||
// Calculate the required length of the buffer |
|||
const size_t len_needed = ::MultiByteToWideChar(CP_UTF8, 0, text.c_str(), (UINT)(text.length()) , NULL, 0 ); |
|||
boost::scoped_array<wchar_t> buff(new wchar_t[len_needed+1]) ; |
|||
const size_t num_copied = ::MultiByteToWideChar(CP_UTF8, 0, text.c_str(), text.size(), buff.get(), len_needed+1) ; |
|||
return std::wstring(buff.get(), num_copied) ; |
|||
#endif |
|||
} |
|||
|
|||
inline std::string wide_to_utf8(const std::wstring& text) { |
|||
#ifndef _MSC_VER |
|||
return boost::locale::conv::from_utf<>(text, "UTF-8"); |
|||
#else |
|||
const size_t len_needed = ::WideCharToMultiByte(CP_UTF8, 0, text.c_str(), (UINT)(text.length()) , NULL, 0, NULL, NULL) ; |
|||
boost::scoped_array<char> buff(new char[len_needed+1]) ; |
|||
const size_t num_copied = ::WideCharToMultiByte(CP_UTF8, 0, text.c_str(), (UINT)(text.length()) , buff.get(), len_needed+1, NULL, NULL) ; |
|||
return std::string(buff.get(), num_copied) ; |
|||
#endif |
|||
} |
|||
|
|||
// token classes |
|||
class Token ; |
|||
typedef boost::shared_ptr<Token> token_ptr ; |
|||
typedef std::vector<token_ptr> token_vector ; |
|||
|
|||
// Custom exception class for library errors |
|||
class TemplateException : public std::exception |
|||
{ |
|||
public: |
|||
TemplateException(std::string reason) : m_reason(reason){} |
|||
~TemplateException() throw() {} |
|||
const char* what() throw() { |
|||
return m_reason.c_str(); |
|||
} |
|||
private: |
|||
std::string m_reason; |
|||
}; |
|||
|
|||
// Data types used in templates |
|||
class Data |
|||
{ |
|||
public: |
|||
virtual bool empty() = 0 ; |
|||
virtual wstring getvalue(); |
|||
virtual data_list& getlist(); |
|||
virtual data_map& getmap() ; |
|||
}; |
|||
|
|||
class DataValue : public Data |
|||
{ |
|||
wstring m_value ; |
|||
public: |
|||
DataValue(wstring value) : m_value(value){} |
|||
wstring getvalue(); |
|||
bool empty(); |
|||
}; |
|||
|
|||
class DataList : public Data |
|||
{ |
|||
data_list m_items ; |
|||
public: |
|||
DataList(const data_list &items) : m_items(items){} |
|||
data_list& getlist() ; |
|||
bool empty(); |
|||
}; |
|||
|
|||
class DataMap : public Data |
|||
{ |
|||
data_map m_items ; |
|||
public: |
|||
DataMap(const data_map &items) : m_items(items){} |
|||
data_map& getmap(); |
|||
bool empty(); |
|||
}; |
|||
|
|||
// convenience functions for making data objects |
|||
inline data_ptr make_data(wstring val) |
|||
{ |
|||
return data_ptr(new DataValue(val)) ; |
|||
} |
|||
inline data_ptr make_data(data_list &val) |
|||
{ |
|||
return data_ptr(new DataList(val)) ; |
|||
} |
|||
inline data_ptr make_data(data_map &val) |
|||
{ |
|||
return data_ptr(new DataMap(val)) ; |
|||
} |
|||
// get a data value from a data map |
|||
// e.g. foo.bar => data["foo"]["bar"] |
|||
data_ptr parse_val(wstring key, data_map &data) ; |
|||
|
|||
typedef enum |
|||
{ |
|||
TOKEN_TYPE_NONE, |
|||
TOKEN_TYPE_TEXT, |
|||
TOKEN_TYPE_VAR, |
|||
TOKEN_TYPE_IF, |
|||
TOKEN_TYPE_FOR, |
|||
TOKEN_TYPE_ENDIF, |
|||
TOKEN_TYPE_ENDFOR, |
|||
} TokenType; |
|||
|
|||
// Template tokens |
|||
// base class for all token types |
|||
class Token |
|||
{ |
|||
public: |
|||
virtual TokenType gettype() = 0 ; |
|||
virtual void gettext(std::wostream &stream, data_map &data) = 0 ; |
|||
virtual void set_children(token_vector &children); |
|||
virtual token_vector & get_children(); |
|||
}; |
|||
|
|||
// normal text |
|||
class TokenText : public Token |
|||
{ |
|||
wstring m_text ; |
|||
public: |
|||
TokenText(wstring text) : m_text(text){} |
|||
TokenType gettype(); |
|||
void gettext(std::wostream &stream, data_map &data); |
|||
}; |
|||
|
|||
// variable |
|||
class TokenVar : public Token |
|||
{ |
|||
wstring m_key ; |
|||
public: |
|||
TokenVar(wstring key) : m_key(key){} |
|||
TokenType gettype(); |
|||
void gettext(std::wostream &stream, data_map &data); |
|||
}; |
|||
|
|||
// for block |
|||
class TokenFor : public Token |
|||
{ |
|||
public: |
|||
wstring m_key ; |
|||
wstring m_val ; |
|||
token_vector m_children ; |
|||
TokenFor(wstring expr); |
|||
TokenType gettype(); |
|||
void gettext(std::wostream &stream, data_map &data); |
|||
void set_children(token_vector &children); |
|||
token_vector &get_children(); |
|||
}; |
|||
|
|||
// if block |
|||
class TokenIf : public Token |
|||
{ |
|||
public: |
|||
wstring m_expr ; |
|||
token_vector m_children ; |
|||
TokenIf(wstring expr) : m_expr(expr){} |
|||
TokenType gettype(); |
|||
void gettext(std::wostream &stream, data_map &data); |
|||
bool is_true(wstring expr, data_map &data); |
|||
void set_children(token_vector &children); |
|||
token_vector &get_children(); |
|||
}; |
|||
|
|||
// end of block |
|||
class TokenEnd : public Token // end of control block |
|||
{ |
|||
wstring m_type ; |
|||
public: |
|||
TokenEnd(wstring text) : m_type(text){} |
|||
TokenType gettype(); |
|||
void gettext(std::wostream &stream, data_map &data); |
|||
}; |
|||
|
|||
wstring gettext(token_ptr token, data_map &data) ; |
|||
|
|||
void parse_tree(token_vector &tokens, token_vector &tree, TokenType until=TOKEN_TYPE_NONE) ; |
|||
token_vector & tokenize(wstring text, token_vector &tokens) ; |
|||
|
|||
// The big daddy. Pass in the template and data, |
|||
// and get out a completed doc. |
|||
void parse(std::wostream &stream, wstring templ_text, data_map &data) ; |
|||
wstring parse(wstring templ_text, data_map &data); |
|||
std::string parse(std::string templ_text, data_map &data); |
|||
} |
@ -0,0 +1,883 @@ |
|||
|
|||
#include "cpptempl.h"
|
|||
|
|||
#ifdef UNIT_TEST
|
|||
|
|||
#include <boost/test/unit_test.hpp>
|
|||
|
|||
#ifndef BOOST_TEST_MODULE
|
|||
#define BOOST_TEST_MODULE cpptemplTests
|
|||
#endif
|
|||
|
|||
#pragma warning( disable : 4996 ) // doesn't like wcstombs
|
|||
|
|||
#include "unit_testing.h"
|
|||
|
|||
using namespace std ; |
|||
|
|||
BOOST_AUTO_TEST_SUITE( TestCppData ) |
|||
|
|||
using namespace cpptempl ; |
|||
|
|||
// DataMap
|
|||
BOOST_AUTO_TEST_CASE(test_DataMap_getvalue) |
|||
{ |
|||
data_map items ; |
|||
data_ptr data(new DataMap(items)) ; |
|||
BOOST_CHECK_THROW( data->getvalue(), TemplateException ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_DataMap_getlist_throws) |
|||
{ |
|||
data_map items ; |
|||
data_ptr data(new DataMap(items)) ; |
|||
|
|||
BOOST_CHECK_THROW( data->getlist(), TemplateException ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_DataMap_getitem_throws) |
|||
{ |
|||
data_map items ; |
|||
items[L"key"] = data_ptr(new DataValue(L"foo")) ; |
|||
data_ptr data(new DataMap(items)) ; |
|||
|
|||
BOOST_CHECK_EQUAL( data->getmap()[L"key"]->getvalue(), L"foo" ) ; |
|||
} |
|||
// DataList
|
|||
BOOST_AUTO_TEST_CASE(test_DataList_getvalue) |
|||
{ |
|||
data_list items ; |
|||
data_ptr data(new DataList(items)) ; |
|||
|
|||
BOOST_CHECK_THROW( data->getvalue(), TemplateException ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_DataList_getlist_throws) |
|||
{ |
|||
data_list items ; |
|||
items.push_back(make_data(L"bar")) ; |
|||
data_ptr data(new DataList(items)) ; |
|||
|
|||
BOOST_CHECK_EQUAL( data->getlist().size(), 1u ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_DataList_getitem_throws) |
|||
{ |
|||
data_list items ; |
|||
data_ptr data(new DataList(items)) ; |
|||
|
|||
BOOST_CHECK_THROW( data->getmap(), TemplateException ) ; |
|||
} |
|||
// DataValue
|
|||
BOOST_AUTO_TEST_CASE(test_DataValue_getvalue) |
|||
{ |
|||
data_ptr data(new DataValue(L"foo")) ; |
|||
|
|||
BOOST_CHECK_EQUAL( data->getvalue(), L"foo" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_DataValue_getlist_throws) |
|||
{ |
|||
data_ptr data(new DataValue(L"foo")) ; |
|||
|
|||
BOOST_CHECK_THROW( data->getlist(), TemplateException ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_DataValue_getitem_throws) |
|||
{ |
|||
data_ptr data(new DataValue(L"foo")) ; |
|||
|
|||
BOOST_CHECK_THROW( data->getmap(), TemplateException ) ; |
|||
} |
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
|
|||
|
|||
BOOST_AUTO_TEST_SUITE( TestCppParseVal ) |
|||
|
|||
using namespace cpptempl ; |
|||
BOOST_AUTO_TEST_CASE(test_quoted) |
|||
{ |
|||
data_map data ; |
|||
data[L"foo"] = make_data(L"bar") ; |
|||
data_ptr value = parse_val(L"\"foo\"", data) ; |
|||
|
|||
BOOST_CHECK_EQUAL( value->getvalue(), L"foo" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_value) |
|||
{ |
|||
data_map data ; |
|||
data[L"foo"] = make_data(L"bar") ; |
|||
data_ptr value = parse_val(L"foo", data) ; |
|||
|
|||
BOOST_CHECK_EQUAL( value->getvalue(), L"bar" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_not_found) |
|||
{ |
|||
data_map data ; |
|||
data[L"foo"] = make_data(L"bar") ; |
|||
data_ptr value = parse_val(L"kettle", data) ; |
|||
|
|||
BOOST_CHECK_EQUAL( value->getvalue(), L"{$kettle}" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_not_found_dotted) |
|||
{ |
|||
data_map data ; |
|||
data[L"foo"] = make_data(L"bar") ; |
|||
data_ptr value = parse_val(L"kettle.black", data) ; |
|||
|
|||
BOOST_CHECK_EQUAL( value->getvalue(), L"{$kettle.black}" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_my_ax) |
|||
{ |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"my ax") ; |
|||
BOOST_CHECK_EQUAL( parse_val(L"item", data)->getvalue(), L"my ax" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_list) |
|||
{ |
|||
data_map data ; |
|||
data_list items ; |
|||
items.push_back(make_data(L"bar")) ; |
|||
data[L"foo"] = data_ptr(new DataList(items)) ; |
|||
data_ptr value = parse_val(L"foo", data) ; |
|||
|
|||
BOOST_CHECK_EQUAL( value->getlist().size(), 1u ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_dotted) |
|||
{ |
|||
data_map data ; |
|||
data_map subdata ; |
|||
subdata[L"b"] = data_ptr(new DataValue(L"c")) ; |
|||
data[L"a"] = data_ptr(new DataMap(subdata)) ; |
|||
data_ptr value = parse_val(L"a.b", data) ; |
|||
|
|||
BOOST_CHECK_EQUAL( value->getvalue(), L"c" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_double_dotted) |
|||
{ |
|||
data_map data ; |
|||
data_map sub_data ; |
|||
data_map sub_sub_data ; |
|||
sub_sub_data[L"c"] = data_ptr(new DataValue(L"d")) ; |
|||
sub_data[L"b"] = data_ptr(new DataMap(sub_sub_data)) ; |
|||
data[L"a"] = data_ptr(new DataMap(sub_data)) ; |
|||
data_ptr value = parse_val(L"a.b.c", data) ; |
|||
|
|||
BOOST_CHECK_EQUAL( value->getvalue(), L"d" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_dotted_to_list) |
|||
{ |
|||
data_list friends ; |
|||
friends.push_back(make_data(L"Bob")) ; |
|||
data_map person ; |
|||
person[L"friends"] = make_data(friends) ; |
|||
data_map data ; |
|||
data[L"person"] = make_data(person) ; |
|||
data_ptr value = parse_val(L"person.friends", data) ; |
|||
|
|||
BOOST_CHECK_EQUAL( value->getlist().size(), 1u ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_dotted_to_dict_list) |
|||
{ |
|||
data_map bob ; |
|||
bob[L"name"] = make_data(L"Bob") ; |
|||
data_map betty ; |
|||
betty[L"name"] = make_data(L"Betty") ; |
|||
data_list friends ; |
|||
friends.push_back(make_data(bob)) ; |
|||
friends.push_back(make_data(betty)) ; |
|||
data_map person ; |
|||
person[L"friends"] = make_data(friends) ; |
|||
data_map data ; |
|||
data[L"person"] = make_data(person) ; |
|||
data_ptr value = parse_val(L"person.friends", data) ; |
|||
|
|||
BOOST_CHECK_EQUAL( value->getlist()[0]->getmap()[L"name"]->getvalue(), L"Bob" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
|
|||
BOOST_AUTO_TEST_SUITE( TestCppToken ) |
|||
|
|||
using namespace cpptempl ; |
|||
|
|||
// TokenVar
|
|||
BOOST_AUTO_TEST_CASE(TestTokenVarType) |
|||
{ |
|||
TokenVar token(L"foo") ; |
|||
BOOST_CHECK_EQUAL( token.gettype(), TOKEN_TYPE_VAR ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenVar) |
|||
{ |
|||
token_ptr token(new TokenVar(L"foo")) ; |
|||
data_map data ; |
|||
data[L"foo"] = make_data(L"bar") ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"bar" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenVarCantHaveChildren) |
|||
{ |
|||
TokenVar token(L"foo") ; |
|||
token_vector children ; |
|||
BOOST_CHECK_THROW(token.set_children(children), TemplateException) ; |
|||
} |
|||
// TokenText
|
|||
BOOST_AUTO_TEST_CASE(TestTokenTextType) |
|||
{ |
|||
TokenText token(L"foo") ; |
|||
BOOST_CHECK_EQUAL( token.gettype(), TOKEN_TYPE_TEXT ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenText) |
|||
{ |
|||
token_ptr token(new TokenText(L"foo")) ; |
|||
data_map data ; |
|||
data[L"foo"] = make_data(L"bar") ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"foo" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenTextCantHaveChildrenSet) |
|||
{ |
|||
TokenText token(L"foo") ; |
|||
token_vector children ; |
|||
BOOST_CHECK_THROW(token.set_children(children), TemplateException) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenTextCantHaveChildrenGet) |
|||
{ |
|||
TokenText token(L"foo") ; |
|||
token_vector children ; |
|||
BOOST_CHECK_THROW(token.get_children(), TemplateException) ; |
|||
} |
|||
// TokenFor
|
|||
BOOST_AUTO_TEST_CASE(TestTokenForBadSyntax) |
|||
{ |
|||
BOOST_CHECK_THROW(TokenFor token(L"foo"), TemplateException ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenForType) |
|||
{ |
|||
TokenFor token(L"for item in items") ; |
|||
BOOST_CHECK_EQUAL( token.gettype(), TOKEN_TYPE_FOR ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenForTextEmpty) |
|||
{ |
|||
token_ptr token(new TokenFor(L"for item in items")) ; |
|||
data_map data ; |
|||
data_list items ; |
|||
items.push_back(make_data(L"first")); |
|||
data[L"items"] = make_data(items) ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenForTextOneVar) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenVar(L"item"))) ; |
|||
token_ptr token(new TokenFor(L"for item in items")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data_list items ; |
|||
items.push_back(make_data(L"first ")); |
|||
items.push_back(make_data(L"second ")); |
|||
data[L"items"] = make_data(items) ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"first second " ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenForTextOneVarLoop) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenVar(L"loop.index"))) ; |
|||
token_ptr token(new TokenFor(L"for item in items")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data_list items ; |
|||
items.push_back(make_data(L"first ")); |
|||
items.push_back(make_data(L"second ")); |
|||
data[L"items"] = make_data(items) ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"12" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenForLoopTextVar) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenVar(L"loop.index"))) ; |
|||
children.push_back(token_ptr(new TokenText(L". "))) ; |
|||
children.push_back(token_ptr(new TokenVar(L"item"))) ; |
|||
children.push_back(token_ptr(new TokenText(L" "))) ; |
|||
token_ptr token(new TokenFor(L"for item in items")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data_list items ; |
|||
items.push_back(make_data(L"first")); |
|||
items.push_back(make_data(L"second")); |
|||
data[L"items"] = make_data(items) ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"1. first 2. second " ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenForLoopTextVarDottedKeyAndVal) |
|||
{ |
|||
TokenFor token(L"for friend in person.friends") ; |
|||
BOOST_CHECK_EQUAL( token.m_key, L"person.friends" ) ; |
|||
BOOST_CHECK_EQUAL( token.m_val, L"friend" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenForLoopTextVarDotted) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenVar(L"loop.index"))) ; |
|||
children.push_back(token_ptr(new TokenText(L". "))) ; |
|||
children.push_back(token_ptr(new TokenVar(L"friend.name"))) ; |
|||
children.push_back(token_ptr(new TokenText(L" "))) ; |
|||
token_ptr token(new TokenFor(L"for friend in person.friends")) ; |
|||
token->set_children(children) ; |
|||
|
|||
data_map bob ; |
|||
bob[L"name"] = make_data(L"Bob") ; |
|||
data_map betty ; |
|||
betty[L"name"] = make_data(L"Betty") ; |
|||
data_list friends ; |
|||
friends.push_back(make_data(bob)) ; |
|||
friends.push_back(make_data(betty)) ; |
|||
data_map person ; |
|||
person[L"friends"] = make_data(friends) ; |
|||
data_map data ; |
|||
data[L"person"] = make_data(person) ; |
|||
|
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"1. Bob 2. Betty " ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenForTextOneText) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenText(L"{--}"))) ; |
|||
token_ptr token(new TokenFor(L"for item in items")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data_list items ; |
|||
items.push_back(make_data(L"first ")); |
|||
items.push_back(make_data(L"second ")); |
|||
data[L"items"] = make_data(items) ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"{--}{--}" ) ; |
|||
} |
|||
|
|||
//////////////////////////////////////////////////////////////////////////
|
|||
// TokenIf
|
|||
//////////////////////////////////////////////////////////////////////////
|
|||
|
|||
BOOST_AUTO_TEST_CASE(TestTokenIfType) |
|||
{ |
|||
TokenIf token(L"if items") ; |
|||
BOOST_CHECK_EQUAL( token.gettype(), TOKEN_TYPE_IF ) ; |
|||
} |
|||
// if not empty
|
|||
BOOST_AUTO_TEST_CASE(TestTokenIfTrueText) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenText(L"{--}"))) ; |
|||
token_ptr token(new TokenIf(L"if item")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"foo") ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"{--}" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenIfTrueVar) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenVar(L"item"))) ; |
|||
token_ptr token(new TokenIf(L"if item")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"foo") ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"foo" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenIfFalse) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenText(L"{--}"))) ; |
|||
token_ptr token(new TokenIf(L"if item")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"") ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"") ; |
|||
} |
|||
|
|||
|
|||
|
|||
// ==
|
|||
BOOST_AUTO_TEST_CASE(TestTokenIfEqualsTrue) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenVar(L"item"))) ; |
|||
token_ptr token(new TokenIf(L"if item == \"foo\"")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"foo") ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"foo" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenIfEqualsFalse) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenVar(L"item"))) ; |
|||
token_ptr token(new TokenIf(L"if item == \"bar\"")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"foo") ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenIfEqualsTwoVarsTrue) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenVar(L"item"))) ; |
|||
token_ptr token(new TokenIf(L"if item == foo")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"x") ; |
|||
data[L"foo"] = make_data(L"x") ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"x" ) ; |
|||
} |
|||
|
|||
// !=
|
|||
BOOST_AUTO_TEST_CASE(TestTokenIfNotEqualsTrue) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenVar(L"item"))) ; |
|||
token_ptr token(new TokenIf(L"if item != \"foo\"")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"foo") ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenIfNotEqualsFalse) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenVar(L"item"))) ; |
|||
token_ptr token(new TokenIf(L"if item != \"bar\"")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"foo") ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"foo" ) ; |
|||
} |
|||
|
|||
// not
|
|||
BOOST_AUTO_TEST_CASE(TestTokenIfNotTrueText) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenText(L"{--}"))) ; |
|||
token_ptr token(new TokenIf(L"if not item")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"foo") ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"") ; |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(TestTokenIfNotFalseText) |
|||
{ |
|||
token_vector children ; |
|||
children.push_back(token_ptr(new TokenText(L"{--}"))) ; |
|||
token_ptr token(new TokenIf(L"if not item")) ; |
|||
token->set_children(children) ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"") ; |
|||
BOOST_CHECK_EQUAL( gettext(token, data), L"{--}") ; |
|||
} |
|||
|
|||
// TokenEnd
|
|||
BOOST_AUTO_TEST_CASE(TestTokenEndFor) |
|||
{ |
|||
TokenEnd token(L"endfor") ; |
|||
BOOST_CHECK_EQUAL( token.gettype(), TOKEN_TYPE_ENDFOR ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenEndIf) |
|||
{ |
|||
TokenEnd token(L"endif") ; |
|||
BOOST_CHECK_EQUAL( token.gettype(), TOKEN_TYPE_ENDIF ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(TestTokenEndIfCantHaveChildren) |
|||
{ |
|||
TokenEnd token(L"endif") ; |
|||
token_vector children ; |
|||
BOOST_CHECK_THROW(token.set_children(children), TemplateException) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_throws_on_gettext) |
|||
{ |
|||
data_map data ; |
|||
token_ptr token(new TokenEnd(L"endif")) ; |
|||
|
|||
BOOST_CHECK_THROW(gettext(token, data), TemplateException) ; |
|||
} |
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
|
|||
BOOST_AUTO_TEST_SUITE( TestCppTokenize ) |
|||
|
|||
using namespace cpptempl ; |
|||
|
|||
BOOST_AUTO_TEST_CASE(test_empty) |
|||
{ |
|||
wstring text = L"" ; |
|||
token_vector tokens ; |
|||
tokenize(text, tokens) ; |
|||
|
|||
BOOST_CHECK_EQUAL( 0u, tokens.size() ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_text_only) |
|||
{ |
|||
wstring text = L"blah blah blah" ; |
|||
token_vector tokens ; |
|||
tokenize(text, tokens) ; |
|||
data_map data ; |
|||
|
|||
BOOST_CHECK_EQUAL( 1u, tokens.size() ) ; |
|||
BOOST_CHECK_EQUAL( gettext(tokens[0], data), L"blah blah blah" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_brackets_no_var) |
|||
{ |
|||
wstring text = L"{foo}" ; |
|||
token_vector tokens ; |
|||
tokenize(text, tokens) ; |
|||
data_map data ; |
|||
|
|||
BOOST_CHECK_EQUAL( 2u, tokens.size() ) ; |
|||
BOOST_CHECK_EQUAL( gettext(tokens[0], data), L"{" ) ; |
|||
BOOST_CHECK_EQUAL( gettext(tokens[1], data), L"foo}" ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_ends_with_bracket) |
|||
{ |
|||
wstring text = L"blah blah blah{" ; |
|||
token_vector tokens ; |
|||
tokenize(text, tokens) ; |
|||
data_map data ; |
|||
|
|||
BOOST_CHECK_EQUAL( 2u, tokens.size() ) ; |
|||
BOOST_CHECK_EQUAL( gettext(tokens[0], data), L"blah blah blah" ) ; |
|||
BOOST_CHECK_EQUAL( gettext(tokens[1], data), L"{" ) ; |
|||
} |
|||
// var
|
|||
BOOST_AUTO_TEST_CASE(test_var) |
|||
{ |
|||
wstring text = L"{$foo}" ; |
|||
token_vector tokens ; |
|||
tokenize(text, tokens) ; |
|||
data_map data ; |
|||
data[L"foo"] = make_data(L"bar") ; |
|||
|
|||
BOOST_CHECK_EQUAL( 1u, tokens.size() ) ; |
|||
BOOST_CHECK_EQUAL( gettext(tokens[0], data), L"bar" ) ; |
|||
} |
|||
// for
|
|||
BOOST_AUTO_TEST_CASE(test_for) |
|||
{ |
|||
wstring text = L"{% for item in items %}" ; |
|||
token_vector tokens ; |
|||
tokenize(text, tokens) ; |
|||
|
|||
BOOST_CHECK_EQUAL( 1u, tokens.size() ) ; |
|||
BOOST_CHECK_EQUAL( tokens[0]->gettype(), TOKEN_TYPE_FOR ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_for_full) |
|||
{ |
|||
wstring text = L"{% for item in items %}{$item}{% endfor %}" ; |
|||
token_vector tokens ; |
|||
tokenize(text, tokens) ; |
|||
|
|||
BOOST_CHECK_EQUAL( 3u, tokens.size() ) ; |
|||
BOOST_CHECK_EQUAL( tokens[0]->gettype(), TOKEN_TYPE_FOR ) ; |
|||
BOOST_CHECK_EQUAL( tokens[1]->gettype(), TOKEN_TYPE_VAR ) ; |
|||
BOOST_CHECK_EQUAL( tokens[2]->gettype(), TOKEN_TYPE_ENDFOR ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_for_full_with_text) |
|||
{ |
|||
wstring text = L"{% for item in items %}*{$item}*{% endfor %}" ; |
|||
token_vector tokens ; |
|||
tokenize(text, tokens) ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"my ax") ; |
|||
|
|||
BOOST_CHECK_EQUAL( 5u, tokens.size() ) ; |
|||
BOOST_CHECK_EQUAL( tokens[0]->gettype(), TOKEN_TYPE_FOR ) ; |
|||
BOOST_CHECK_EQUAL( gettext(tokens[1], data), L"*" ) ; |
|||
BOOST_CHECK_EQUAL( tokens[2]->gettype(), TOKEN_TYPE_VAR ) ; |
|||
BOOST_CHECK_EQUAL( gettext(tokens[2], data), L"my ax" ) ; |
|||
BOOST_CHECK_EQUAL( gettext(tokens[3], data), L"*" ) ; |
|||
BOOST_CHECK_EQUAL( tokens[4]->gettype(), TOKEN_TYPE_ENDFOR ) ; |
|||
} |
|||
// if
|
|||
BOOST_AUTO_TEST_CASE(test_if) |
|||
{ |
|||
wstring text = L"{% if foo %}" ; |
|||
token_vector tokens ; |
|||
tokenize(text, tokens) ; |
|||
|
|||
BOOST_CHECK_EQUAL( 1u, tokens.size() ) ; |
|||
BOOST_CHECK_EQUAL( tokens[0]->gettype(), TOKEN_TYPE_IF ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_if_full) |
|||
{ |
|||
wstring text = L"{% if item %}{$item}{% endif %}" ; |
|||
token_vector tokens ; |
|||
tokenize(text, tokens) ; |
|||
|
|||
BOOST_CHECK_EQUAL( 3u, tokens.size() ) ; |
|||
BOOST_CHECK_EQUAL( tokens[0]->gettype(), TOKEN_TYPE_IF ) ; |
|||
BOOST_CHECK_EQUAL( tokens[1]->gettype(), TOKEN_TYPE_VAR ) ; |
|||
BOOST_CHECK_EQUAL( tokens[2]->gettype(), TOKEN_TYPE_ENDIF ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_if_full_with_text) |
|||
{ |
|||
wstring text = L"{% if item %}{{$item}}{% endif %}" ; |
|||
token_vector tokens ; |
|||
tokenize(text, tokens) ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"my ax") ; |
|||
|
|||
BOOST_CHECK_EQUAL( 5u, tokens.size() ) ; |
|||
BOOST_CHECK_EQUAL( tokens[0]->gettype(), TOKEN_TYPE_IF ) ; |
|||
BOOST_CHECK_EQUAL( gettext(tokens[1], data), L"{" ) ; |
|||
BOOST_CHECK_EQUAL( tokens[2]->gettype(), TOKEN_TYPE_VAR ) ; |
|||
BOOST_CHECK_EQUAL( gettext(tokens[2], data), L"my ax" ) ; |
|||
BOOST_CHECK_EQUAL( gettext(tokens[3], data), L"}" ) ; |
|||
BOOST_CHECK_EQUAL( tokens[4]->gettype(), TOKEN_TYPE_ENDIF ) ; |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
|
|||
|
|||
BOOST_AUTO_TEST_SUITE( test_parse_tree ) |
|||
|
|||
using namespace cpptempl ; |
|||
|
|||
token_ptr make_tt(wstring text) |
|||
{ |
|||
return token_ptr(new TokenText(text)) ; |
|||
} |
|||
token_ptr make_for(wstring text) |
|||
{ |
|||
return token_ptr(new TokenFor(text)) ; |
|||
} |
|||
token_ptr make_if(wstring text) |
|||
{ |
|||
return token_ptr(new TokenIf(text)) ; |
|||
} |
|||
token_ptr make_endfor() |
|||
{ |
|||
return token_ptr(new TokenEnd(L"endfor")) ; |
|||
} |
|||
token_ptr make_endif() |
|||
{ |
|||
return token_ptr(new TokenEnd(L"endif")) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_empty) |
|||
{ |
|||
token_vector tokens ; |
|||
token_vector tree ; |
|||
parse_tree(tokens, tree) ; |
|||
BOOST_CHECK_EQUAL( 0u, tree.size() ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_one) |
|||
{ |
|||
token_vector tokens ; |
|||
tokens.push_back(make_tt(L"foo")) ; |
|||
token_vector tree ; |
|||
parse_tree(tokens, tree) ; |
|||
BOOST_CHECK_EQUAL( 1u, tree.size() ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_for) |
|||
{ |
|||
token_vector tokens ; |
|||
tokens.push_back(make_for(L"for item in items")) ; |
|||
tokens.push_back(make_tt(L"foo")) ; |
|||
tokens.push_back(make_endfor()) ; |
|||
token_vector tree ; |
|||
parse_tree(tokens, tree) ; |
|||
BOOST_CHECK_EQUAL( 1u, tree.size() ) ; |
|||
BOOST_CHECK_EQUAL( 1u, tree[0]->get_children().size()) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_if) |
|||
{ |
|||
token_vector tokens ; |
|||
tokens.push_back(make_if(L"if insane")) ; |
|||
tokens.push_back(make_tt(L"foo")) ; |
|||
tokens.push_back(make_endif()) ; |
|||
token_vector tree ; |
|||
parse_tree(tokens, tree) ; |
|||
BOOST_CHECK_EQUAL( 1u, tree.size() ) ; |
|||
BOOST_CHECK_EQUAL( 1u, tree[0]->get_children().size()) ; |
|||
} |
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
|
|||
BOOST_AUTO_TEST_SUITE(TestCppParse) |
|||
|
|||
using namespace cpptempl ; |
|||
|
|||
BOOST_AUTO_TEST_CASE(test_empty) |
|||
{ |
|||
wstring text = L"" ; |
|||
data_map data ; |
|||
wstring actual = parse(text, data) ; |
|||
wstring expected = L"" ; |
|||
BOOST_CHECK_EQUAL( expected, actual ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_no_vars) |
|||
{ |
|||
wstring text = L"foo" ; |
|||
data_map data ; |
|||
wstring actual = parse(text, data) ; |
|||
wstring expected = L"foo" ; |
|||
BOOST_CHECK_EQUAL( expected, actual ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_var) |
|||
{ |
|||
wstring text = L"{$foo}" ; |
|||
data_map data ; |
|||
data[L"foo"] = make_data(L"bar") ; |
|||
wstring actual = parse(text, data) ; |
|||
wstring expected = L"bar" ; |
|||
BOOST_CHECK_EQUAL( expected, actual ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_var_surrounded) |
|||
{ |
|||
wstring text = L"aaa{$foo}bbb" ; |
|||
data_map data ; |
|||
data[L"foo"] = make_data(L"---") ; |
|||
wstring actual = parse(text, data) ; |
|||
wstring expected = L"aaa---bbb" ; |
|||
BOOST_CHECK_EQUAL( expected, actual ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_for) |
|||
{ |
|||
wstring text = L"{% for item in items %}{$item}{% endfor %}" ; |
|||
data_map data ; |
|||
data_list items ; |
|||
items.push_back(make_data(L"0")) ; |
|||
items.push_back(make_data(L"1")) ; |
|||
data[L"items"] = make_data(items) ; |
|||
wstring actual = parse(text, data) ; |
|||
wstring expected = L"01" ; |
|||
BOOST_CHECK_EQUAL( expected, actual ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_if_false) |
|||
{ |
|||
wstring text = L"{% if item %}{$item}{% endif %}" ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"") ; |
|||
wstring actual = parse(text, data) ; |
|||
wstring expected = L"" ; |
|||
BOOST_CHECK_EQUAL( expected, actual ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_if_true) |
|||
{ |
|||
wstring text = L"{% if item %}{$item}{% endif %}" ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"foo") ; |
|||
wstring actual = parse(text, data) ; |
|||
wstring expected = L"foo" ; |
|||
BOOST_CHECK_EQUAL( expected, actual ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_nested_for) |
|||
{ |
|||
wstring text = L"{% for item in items %}{% for thing in things %}{$item}{$thing}{% endfor %}{% endfor %}" ; |
|||
data_map data ; |
|||
data_list items ; |
|||
items.push_back(make_data(L"0")) ; |
|||
items.push_back(make_data(L"1")) ; |
|||
data[L"items"] = make_data(items) ; |
|||
data_list things ; |
|||
things.push_back(make_data(L"a")) ; |
|||
things.push_back(make_data(L"b")) ; |
|||
data[L"things"] = make_data(things) ; |
|||
wstring actual = parse(text, data) ; |
|||
wstring expected = L"0a0b1a1b" ; |
|||
BOOST_CHECK_EQUAL( expected, actual ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_nested_if_false) |
|||
{ |
|||
wstring text = L"{% if item %}{% if thing %}{$item}{$thing}{% endif %}{% endif %}" ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"aaa") ; |
|||
data[L"thing"] = make_data(L"") ; |
|||
wstring actual = parse(text, data) ; |
|||
wstring expected = L"" ; |
|||
BOOST_CHECK_EQUAL( expected, actual ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_nested_if_true) |
|||
{ |
|||
wstring text = L"{% if item %}{% if thing %}{$item}{$thing}{% endif %}{% endif %}" ; |
|||
data_map data ; |
|||
data[L"item"] = make_data(L"aaa") ; |
|||
data[L"thing"] = make_data(L"bbb") ; |
|||
wstring actual = parse(text, data) ; |
|||
wstring expected = L"aaabbb" ; |
|||
BOOST_CHECK_EQUAL( expected, actual ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_usage_example) |
|||
{ |
|||
wstring text = L"{% if item %}{$item}{% endif %}\n" |
|||
L"{% if thing %}{$thing}{% endif %}" ; |
|||
cpptempl::data_map data ; |
|||
data[L"item"] = cpptempl::make_data(L"aaa") ; |
|||
data[L"thing"] = cpptempl::make_data(L"bbb") ; |
|||
|
|||
wstring result = cpptempl::parse(text, data) ; |
|||
|
|||
wstring expected = L"aaa\nbbb" ; |
|||
BOOST_CHECK_EQUAL( result, expected ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_syntax_if) |
|||
{ |
|||
wstring text = L"{% if person.name == \"Bob\" %}Full name: Robert{% endif %}" ; |
|||
data_map person ; |
|||
person[L"name"] = make_data(L"Bob") ; |
|||
person[L"occupation"] = make_data(L"Plumber") ; |
|||
data_map data ; |
|||
data[L"person"] = make_data(person) ; |
|||
|
|||
wstring result = cpptempl::parse(text, data) ; |
|||
|
|||
wstring expected = L"Full name: Robert" ; |
|||
BOOST_CHECK_EQUAL( result, expected ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_syntax_dotted) |
|||
{ |
|||
wstring text = L"{% for friend in person.friends %}" |
|||
L"{$loop.index}. {$friend.name} " |
|||
L"{% endfor %}" ; |
|||
|
|||
data_map bob ; |
|||
bob[L"name"] = make_data(L"Bob") ; |
|||
data_map betty ; |
|||
betty[L"name"] = make_data(L"Betty") ; |
|||
data_list friends ; |
|||
friends.push_back(make_data(bob)) ; |
|||
friends.push_back(make_data(betty)) ; |
|||
data_map person ; |
|||
person[L"friends"] = make_data(friends) ; |
|||
data_map data ; |
|||
data[L"person"] = make_data(person) ; |
|||
|
|||
wstring result = cpptempl::parse(text, data) ; |
|||
|
|||
wstring expected = L"1. Bob 2. Betty " ; |
|||
BOOST_CHECK_EQUAL( result, expected ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_example_okinawa) |
|||
{ |
|||
// The text template
|
|||
wstring text = L"I heart {$place}!" ; |
|||
// Data to feed the template engine
|
|||
cpptempl::data_map data ; |
|||
// {$place} => Okinawa
|
|||
data[L"place"] = cpptempl::make_data(L"Okinawa"); |
|||
// parse the template with the supplied data dictionary
|
|||
wstring result = cpptempl::parse(text, data) ; |
|||
|
|||
wstring expected = L"I heart Okinawa!" ; |
|||
BOOST_CHECK_EQUAL( result, expected ) ; |
|||
} |
|||
BOOST_AUTO_TEST_CASE(test_example_ul) |
|||
{ |
|||
wstring text = L"<h3>Locations</h3><ul>" |
|||
L"{% for place in places %}" |
|||
L"<li>{$place}</li>" |
|||
L"{% endfor %}" |
|||
L"</ul>" ; |
|||
|
|||
// Create the list of items
|
|||
cpptempl::data_list places; |
|||
places.push_back(cpptempl::make_data(L"Okinawa")); |
|||
places.push_back(cpptempl::make_data(L"San Francisco")); |
|||
// Now set this in the data map
|
|||
cpptempl::data_map data ; |
|||
data[L"places"] = cpptempl::make_data(places); |
|||
// parse the template with the supplied data dictionary
|
|||
wstring result = cpptempl::parse(text, data) ; |
|||
wstring expected = L"<h3>Locations</h3><ul>" |
|||
L"<li>Okinawa</li>" |
|||
L"<li>San Francisco</li>" |
|||
L"</ul>" ; |
|||
BOOST_CHECK_EQUAL(result, expected) ; |
|||
} |
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
|
|||
#endif
|
@ -0,0 +1,41 @@ |
|||
#pragma once |
|||
|
|||
#include <iostream> |
|||
#include <ostream> |
|||
#include <sstream> |
|||
#ifndef _MSC_VER |
|||
#include <boost/locale.hpp> |
|||
#else |
|||
#include <boost/scoped_array.hpp> |
|||
#include "windows.h" |
|||
#include "winnls.h" // unicode-multibyte conversion |
|||
#endif |
|||
|
|||
inline std::string wide2utf8(const std::wstring& text) { |
|||
#ifndef _MSC_VER |
|||
return boost::locale::conv::to_utf<wchar_t>(text, "UTF-8"); |
|||
#else |
|||
const size_t len_needed = ::WideCharToMultiByte(CP_UTF8, 0, text.c_str(), (UINT)(text.length()) , NULL, 0, NULL, NULL) ; |
|||
boost::scoped_array<char> buff(new char[len_needed+1]) ; |
|||
const size_t num_copied = ::WideCharToMultiByte(CP_UTF8, 0, text.c_str(), (UINT)(text.length()) , buff.get(), len_needed+1, NULL, NULL) ; |
|||
return std::string(buff.get(), num_copied) ; |
|||
#endif |
|||
} |
|||
|
|||
namespace std { |
|||
|
|||
inline ostream& operator<<(ostream& out, const wchar_t* value) |
|||
{ |
|||
wstring text(value) ; |
|||
out << wide2utf8(text); |
|||
return out; |
|||
} |
|||
|
|||
inline ostream& operator<<(ostream& out, const wstring& value) |
|||
{ |
|||
out << wide2utf8(value); |
|||
return out; |
|||
} |
|||
} |
|||
|
|||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue