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.
328 lines
9.1 KiB
328 lines
9.1 KiB
/*
|
|
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>
|
|
#include <utf8.h>
|
|
#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
|
|
std::wstring result;
|
|
if (sizeof(wchar_t) == 2) {
|
|
utf8::utf8to16(text.begin(), text.end(), std::back_inserter(result));
|
|
} else {
|
|
assert(sizeof(wchar_t) == 4);
|
|
utf8::utf8to32(text.begin(), text.end(), std::back_inserter(result));
|
|
}
|
|
return result;
|
|
//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
|
|
std::string result;
|
|
if (sizeof(wchar_t) == 2) {
|
|
utf8::utf16to8(text.begin(), text.end(), std::back_inserter(result));
|
|
} else {
|
|
assert(sizeof(wchar_t) == 4);
|
|
utf8::utf32to8(text.begin(), text.end(), std::back_inserter(result));
|
|
}
|
|
return result;
|
|
//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() const throw() {
|
|
return m_reason.c_str();
|
|
}
|
|
private:
|
|
std::string m_reason;
|
|
};
|
|
|
|
// Data types used in templates
|
|
class Data
|
|
{
|
|
public:
|
|
virtual ~Data() {
|
|
// Intentionally left empty.
|
|
}
|
|
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);
|
|
}
|