/* * This file is a part of EZC -- Easy templating in C++ * and is distributed under the (new) BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2007-2011, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name Tomasz Sowa nor the names of contributors to this * project may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef headerfile_ezc_generator #define headerfile_ezc_generator #include "pattern.h" #include "functions.h" #include #include #include namespace Ezc { /* StreamType we use only method write(const wchar_t * str, size_t len) from the stream */ template class Generator { public: Generator(); Generator(const Generator & n); Generator & operator=(const Generator & n); ~Generator(); void SetMax(int max_items_, int max_for_items_); // recognizing some special characters in text patterns (item_text in Patterns) // \r will be a carriage return (13) // \n will be a new line (10) // \t will be a tabulator (9) // \s will be a space // \\ will be one '\' // if the second character is different from those shown above the first slash will be printed // so "\x" gives "\x" // default: false void RecognizeSpecialChars(bool spec); // trimming white characters (at the beginning and at the end of an item_text) // (special char \s if enabled is not treated as a white character here) // default: false void TrimWhite(bool trim); // skipping new line characters (from the whole string in an item_text) // but you can use a new line character written as "\n" (if special chars are turn on) // default: false void SkipNewLine(bool skip); // default: 20 void SetMaxFilters(size_t new_len); // the main methods for generating void Generate(StreamType & o, Pattern & p, Functions & f); // this method is used only if the pattern has cached functions void Generate(StreamType & o, Pattern & p); private: StreamType * output_stream; Pattern * pattern; Functions * functions; // temporary error messages std::wstring temp_msg; bool break_generating; int current_item; int max_items; int max_for_items; bool special_chars; bool trim_white; bool skip_new_line; size_t filter_index; size_t filter_size; // we have to use a pointers table because standard streams such // as std::wostringstream are not copyable std::vector filter_tab; const StreamType empty_stream; bool is_used; // an empty string for info objects // when there is no any parameters const std::wstring empty; // a stack item for 'for' statements // currently only one item (iteration) struct StackItem { int iter; }; // a stack for 'for' statements std::vector stack_tab; void ResizeFilterTab(); void ClearFilterTab(); void ClearStream(StreamType & str); bool Find(const Item::Function & item_fun, typename Functions::Function ** function); void Call(typename Functions::Function * function, FunInfo & info); void Call(typename Functions::Function * function, std::vector & params, StreamType & out_stream, const StreamType & in_stream, bool * info_res = 0); bool Call(Item::Function & function, bool * info_res = 0, typename Functions::Function ** pfun = 0); void Call(typename Functions::Function * function, std::vector & params, bool * info_res = 0); wchar_t CreateSpecialChar(wchar_t c); const wchar_t * PrintSpecialChar(const wchar_t * start, const wchar_t * end); void PrintSpecialText(const wchar_t * start, const wchar_t * end); void PrintNormalText(const wchar_t * start, const wchar_t * end); void TrimWhite(const wchar_t *& start, const wchar_t *& end); void SkipWhite(const wchar_t *& str); int StrToInt(const wchar_t * str, const wchar_t ** str_end); void CreateMsg(StreamType & out, const wchar_t * type, const wchar_t * arg = 0); void CreateMsg(const wchar_t * type, const wchar_t * arg = 0); void CreateMsg(const std::wstring & type, const std::wstring & arg); void CreateMsg(const std::wstring & type); void CreateUnknownMsg(const std::wstring & fun); void MakeTextIf_go(Item & item, bool result); bool MakeTextIfindexnumber(Item & item, bool & result); void MakeTextIf(Item & item); void MakeTextIfno(Item & item); void MakeTextIfany(Item & item); void MakeTextIfone(Item & item); void MakeTextIfanyno(Item & item); void MakeTextIfoneno(Item & item); void MakeTextIfindex(Item & item); void MakeTextForLoop(Item & item, typename Functions::Function * function); void MakeTextFor(Item & item); void MakeItemText(Item & item); void MakeTextContainer(Item & item); void MakeTextNormal(Item & item); void MakeTextIs(Item & item); void MakeTextIsno(Item & item); void MakeTextDefine(Item & item); void MakeTextFilter(Item & item); void MakeText(Item & item); void Generate(StreamType * o, Pattern * p, Functions * f); }; // class Generator template Generator::Generator() { output_stream = 0; pattern = 0; functions = 0; max_items = 50000; max_for_items = 5000; filter_size = 20; special_chars = false; trim_white = false; skip_new_line = false; is_used = false; } template Generator::Generator(const Generator & n) { operator=(n); } template Generator & Generator::operator=(const Generator & n) { output_stream = n.output_stream; pattern = n.pattern; functions = n.functions; max_items = n.max_items; max_for_items = n.max_for_items; filter_size = n.filter_size; special_chars = n.special_chars; trim_white = n.trim_white; skip_new_line = n.skip_new_line; // don't copy filter tab filter_tab.clear(); // don't copy 'is_used' flag is_used = false; return *this; } template Generator::~Generator() { ClearFilterTab(); } template void Generator::ResizeFilterTab() { if( filter_tab.size() != filter_size ) { if( filter_tab.size() < filter_size ) { for(size_t i=filter_tab.size() ; i void Generator::ClearFilterTab() { for(size_t i=0 ; i void Generator::ClearStream(StreamType & str) { #ifdef EZC_HAS_SPECIAL_STREAM str.Clear(); #else str.str(L""); #endif } template void Generator::SetMax(int max_items_, int max_for_items_) { max_items = max_items_; max_for_items = max_for_items_; } template void Generator::RecognizeSpecialChars(bool spec) { special_chars = spec; } template void Generator::TrimWhite(bool trim) { trim_white = trim; } template void Generator::SkipNewLine(bool skip) { skip_new_line = skip; } template void Generator::SetMaxFilters(size_t new_len) { // the table will be resized when Generate() method is called filter_size = new_len; } template void Generator::Generate(StreamType * o, Pattern * p, Functions * f) { if( is_used ) { if( o ) CreateMsg(*o, L"generator busy"); return; } output_stream = o; pattern = p; functions = f; break_generating = false; current_item = 0; stack_tab.clear(); ResizeFilterTab(); filter_index = 0; if( output_stream && pattern ) { try { is_used = true; MakeText( pattern->item_root ); is_used = false; } catch(...) { is_used = false; throw; } } } template void Generator::Generate(StreamType & o, Pattern & p, Functions & f) { Generate(&o, &p, &f); } template void Generator::Generate(StreamType & o, Pattern & p) { Generate(&o, &p, 0); } template bool Generator::Find(const Item::Function & item_fun, typename Functions::Function ** function) { if( !functions ) { if( item_fun.fun_cache ) { *function = reinterpret_cast::Function*>(item_fun.fun_cache); return true; } else { CreateUnknownMsg(item_fun.name); return false; } } if( !functions->Find(item_fun.name, function) ) { CreateUnknownMsg(item_fun.name); return false; } return true; } template void Generator::Call(typename Functions::Function * function, FunInfo & info) { info.Clear(); if( stack_tab.empty() ) info.iter = 0; else info.iter = stack_tab.back().iter; if( function->type == Functions::function ) (function->user_function)(info); else info.res = !function->variable.empty(); } template void Generator::Call(typename Functions::Function * function, std::vector & params, StreamType & out_stream, const StreamType & in_stream, bool * info_res) { if( params.empty() ) { FunInfo info(out_stream, params, empty, in_stream); Call(function, info); if( info_res ) *info_res = info.res; } else { FunInfo info(out_stream, params, params[0], in_stream); Call(function, info); if( info_res ) *info_res = info.res; } } // return: true if a function or variable was found and called template bool Generator::Call(Item::Function & item_fun, bool * info_res, typename Functions::Function ** pfun) { typename Functions::Function * fun; if( !Find(item_fun, &fun) ) return false; Call(fun, item_fun.params, *output_stream, empty_stream, info_res); if( pfun ) *pfun = fun; return true; } // it calls the function pointed by pfun template void Generator::Call(typename Functions::Function * function, std::vector & params, bool * info_res) { Call(function, params, *output_stream, empty_stream, info_res); } template wchar_t Generator::CreateSpecialChar(wchar_t c) { wchar_t res = 0; if( c == 'r' ) res = '\r'; else if( c == 'n' ) res = '\n'; else if( c == 't' ) res = '\t'; else if( c == 's' ) res = ' '; else if( c == '\\' ) res = '\\'; return res; } // a special character is precedeed by a slash '\' // if the special character is unknown the first slash is printing // so "\t" gives one character of code 9 // and "\x" gives "\x" template const wchar_t * Generator::PrintSpecialChar(const wchar_t * start, const wchar_t * end) { wchar_t special = 0; if( *start == '\\' && (start+1) != end ) special = CreateSpecialChar(*(start+1)); if( special ) { output_stream->write(&special, 1); start += 2; } else { if( !skip_new_line || *start != 10 ) output_stream->write(start, 1); start += 1; } return start; } template void Generator::PrintSpecialText(const wchar_t * start, const wchar_t * end) { while( start != end ) { const wchar_t * end2 = start; // looking for a first new line character or a special char // (for new line only if skip_new_line is true) while( end2 != end && *end2 != '\\' && (!skip_new_line || *end2 != 10) ) end2 += 1; // printing the first part of the text if( start != end2 ) output_stream->write(start, end2 - start); start = end2; // skipping one or more new line characters or special chars // (new lines only if skip_new_line is true) while( start != end && (*start == '\\' || (skip_new_line && *start == 10)) ) start = PrintSpecialChar(start, end); } } template void Generator::PrintNormalText(const wchar_t * start, const wchar_t * end) { if( skip_new_line ) { while( start != end ) { const wchar_t * end2 = start; // looking for a first new line character while( end2 != end && *end2 != 10 ) end2 += 1; // printing the first part of the text (until the new line) if( start != end2 ) output_stream->write(start, end2 - start); start = end2; // skipping one or more new line characters while( start != end && *start == 10 ) start += 1; } } else { if( start != end ) output_stream->write(start, end-start); } } template void Generator::TrimWhite(const wchar_t *& start, const wchar_t *& end) { while( start != end && pattern->IsWhite(*start) ) ++start; while( start != end && pattern->IsWhite(*(end-1)) ) --end; } template void Generator::SkipWhite(const wchar_t *& str) { while( pattern->IsWhite(*str) ) str += 1; } template int Generator::StrToInt(const wchar_t * str, const wchar_t ** str_end) { int res = 0; SkipWhite(str); // overflow is not checked while( *str>='0' && *str<='9' ) { res *= 10; res += *str - '0'; str += 1; } SkipWhite(str); *str_end = str; return res; } template void Generator::CreateMsg(StreamType & out, const wchar_t * type, const wchar_t * arg) { pattern->CreateMsg(temp_msg, type, arg); out.write(temp_msg.c_str(), temp_msg.size()); } template void Generator::CreateMsg(const wchar_t * type, const wchar_t * arg) { CreateMsg(*output_stream, type, arg); } template void Generator::CreateMsg(const std::wstring & type, const std::wstring & arg) { CreateMsg(type.c_str(), arg.c_str()); } template void Generator::CreateMsg(const std::wstring & type) { CreateMsg(type.c_str()); } template void Generator::CreateUnknownMsg(const std::wstring & fun) { CreateMsg(L"unknown function", fun.c_str()); } template void Generator::MakeItemText(Item & item) { const wchar_t * start = item.text.c_str(); const wchar_t * end = item.text.c_str() + item.text.size(); if( trim_white ) TrimWhite(start, end); if( special_chars ) PrintSpecialText(start, end); else PrintNormalText(start, end); } template void Generator::MakeTextContainer(Item & item) { std::vector::iterator i = item.item_tab.begin(); for( ; i != item.item_tab.end() && !break_generating ; ++i ) MakeText(**i); } template void Generator::MakeTextNormal(Item & item) { if( item.functions.size() != 1 ) return; typename Functions::Function * pfun; bool called = Call(item.functions[0], 0, &pfun); if( called && pfun->type == Functions::variable ) (*output_stream) << pfun->variable; } template void Generator::MakeTextIf_go(Item & item, bool result) { if( result ) { if( item.item_tab.size() > 0 ) MakeText( *item.item_tab[0] ); } else { // second element can be (or not -- it's from [else]) if( item.item_tab.size() > 1 ) MakeText( *item.item_tab[1] ); } } template void Generator::MakeTextIf(Item & item) { bool info_res; if( item.functions.size() != 1 ) return; if( !Call(item.functions[0], &info_res) ) return; MakeTextIf_go(item, info_res); } template void Generator::MakeTextIfno(Item & item) { bool info_res; if( item.functions.size() != 1 ) return; if( !Call(item.functions[0], &info_res) ) return; MakeTextIf_go(item, !info_res); } template void Generator::MakeTextIfany(Item & item) { std::vector::iterator d = item.functions.begin(); unsigned how_many_true = 0; bool info_res; for( ; d != item.functions.end() ; ++d ) { if( !Call(*d, &info_res) ) return; if( info_res ) ++how_many_true; } MakeTextIf_go(item, how_many_true == item.functions.size()); } template void Generator::MakeTextIfone(Item & item) { std::vector::iterator d = item.functions.begin(); unsigned how_many_true = 0; bool info_res; for( ; d != item.functions.end() ; ++d ) { if( Call(*d, &info_res) && info_res ) { // there is no sense to go through all functions ++how_many_true; break; } } MakeTextIf_go(item, how_many_true > 0); } template void Generator::MakeTextIfanyno(Item & item) { std::vector::iterator d = item.functions.begin(); unsigned how_many_true = 0; bool info_res; for( ; d != item.functions.end() ; ++d ) { if( !Call(*d, &info_res) ) return; if( info_res ) ++how_many_true; } MakeTextIf_go(item, how_many_true == 0); } template void Generator::MakeTextIfoneno(Item & item) { std::vector::iterator d = item.functions.begin(); unsigned how_many_false = 0; bool info_res; for( ; d != item.functions.end() ; ++d ) { if( Call(*d, &info_res) && !info_res ) ++how_many_false; } MakeTextIf_go(item, how_many_false > 0); } template void Generator::MakeTextIs(Item & item) { bool info_res1, info_res2; if( item.functions.size() != 2 ) return; if( !Call(item.functions[0], &info_res1) ) return; if( !Call(item.functions[1], &info_res2) ) return; MakeTextIf_go(item, info_res1 == info_res2); } template void Generator::MakeTextIsno(Item & item) { bool info_res1, info_res2; if( item.functions.size() != 2 ) return; if( !Call(item.functions[0], &info_res1) ) return; if( !Call(item.functions[1], &info_res2) ) return; MakeTextIf_go(item, info_res1 != info_res2); } template bool Generator::MakeTextIfindexnumber(Item & item, bool & result) { if( item.functions.size() != 1 || stack_tab.empty() ) return false; const wchar_t * number_text = item.functions[0].name.c_str(); const wchar_t * last_char; int number = StrToInt(number_text, &last_char); if( *last_char != '\0' ) { CreateMsg(L"if-index: syntax error"); return false; } result = (stack_tab.back().iter == number); return true; } template void Generator::MakeTextIfindex(Item & item) { if( item.functions.size() != 1 ) return; bool result = false; if( !stack_tab.empty() ) { if( item.functions[0].name == L"odd" ) { result = (stack_tab.back().iter & 1) == 1; } else if( item.functions[0].name == L"even" ) { result = (stack_tab.back().iter & 1) == 0; } else if( item.functions[0].name == L"first" ) { result = stack_tab.back().iter == 0; } else { if( !MakeTextIfindexnumber(item, result) ) return; } } MakeTextIf_go(item, result); } template void Generator::MakeTextForLoop(Item & item, typename Functions::Function * function) { bool info_res; for( ; !break_generating ; stack_tab.back().iter += 1 ) { if( stack_tab.back().iter >= max_for_items ) { CreateMsg(item.functions[0].name.c_str(), L"function exceeded a limit for a [for] statement"); break; } Call(function, item.functions[0].params, &info_res); if( !info_res ) break; if( !item.item_tab.empty() ) MakeText( *item.item_tab[0] ); // should be only one item - item_container } } template void Generator::MakeTextFor(Item & item) { if( item.functions.size() != 1 ) return; typename Functions::Function * function; if( !Find(item.functions[0], &function) ) return; StackItem s; s.iter = 0; stack_tab.push_back(s); MakeTextForLoop(item, function); stack_tab.erase(--stack_tab.end()); } template void Generator::MakeTextDefine(Item & item) { if( !functions ) { CreateMsg(L"[def] statement is not available when used without functions set"); return; } if( item.functions.size() == 1 ) { // inserting a new variable if( item.functions[0].params.size() == 1 ) functions->Insert(item.functions[0].name, item.functions[0].params[0]); } else if( item.functions.size() == 2 ) { typename Functions::Function * function; if( Find(item.functions[1], &function) ) { if( function->type == Functions::function ) { // inserting a new function functions->Insert(item.functions[0].name, function->user_function); } else { // inserting a new variable (the value is copied) functions->Insert(item.functions[0].name, function->variable); } } } } template void Generator::MakeTextFilter(Item & item) { if( item.functions.size() != 1 ) return; typename Functions::Function * function; if( !Find(item.functions[0], &function) ) return; if( filter_index >= filter_tab.size() ) { CreateMsg(L"Generator exceeded allowed number of filters"); return; } StreamType * old_stream = output_stream; output_stream = filter_tab[filter_index]; ClearStream(*output_stream); filter_index += 1; if( !item.item_tab.empty() ) MakeText( *item.item_tab[0] ); // should be only one item - item_container Call(function, item.functions[0].params, *old_stream, *output_stream); output_stream = old_stream; filter_index -= 1; } template void Generator::MakeText(Item & item) { if( break_generating ) return; if( ++current_item > max_items ) { break_generating = true; CreateMsg(L"Generator exceeded allowed number of elements"); return; } if ( item.type == Item::item_text ) MakeItemText(item); else if( item.type == Item::item_container )MakeTextContainer(item); else if( item.type == Item::item_normal ) MakeTextNormal(item); else if( item.type == Item::item_if ) MakeTextIf(item); else if( item.type == Item::item_ifno ) MakeTextIfno(item); else if( item.type == Item::item_ifany ) MakeTextIfany(item); else if( item.type == Item::item_ifone ) MakeTextIfone(item); else if( item.type == Item::item_ifanyno ) MakeTextIfanyno(item); else if( item.type == Item::item_ifoneno ) MakeTextIfoneno(item); else if( item.type == Item::item_ifindex ) MakeTextIfindex(item); else if( item.type == Item::item_is ) MakeTextIs(item); else if( item.type == Item::item_isno ) MakeTextIsno(item); else if( item.type == Item::item_for ) MakeTextFor(item); else if( item.type == Item::item_def ) MakeTextDefine(item); else if( item.type == Item::item_filter ) MakeTextFilter(item); else if( item.type == Item::item_err ) CreateMsg(L"a wrong directive"); } } // namespace Ezc #endif