/* * 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-2010, 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 namespace Ezc { template class Generator { public: Generator(); Generator(StreamType & o, Pattern & p, Functions & f); void Set(StreamType & o, Pattern & p, Functions & f); void Set(StreamType & o); void Set(Pattern & p); void Set(Functions & f); 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 '\' // 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); void Generate(); 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; // an empty string for info objects // when there is no any parameters const std::wstring empty; void PutChar(wchar_t z, StreamType & out); void PutStringGeneric(const wchar_t * start, size_t len, StreamType & out); // !! tu bedzie chyba problem z kompilacja // kiedy typ strumienia to bedzie std::ostream // chwilowo zostawiam jak jest /* void PutString(const wchar_t * start, const wchar_t * end, std::ostream & out) { PutStringGeneric(start, end-start, out); } void PutString(const wchar_t * start, const wchar_t * end, std::iostream & out) { PutStringGeneric(start, end-start, out); } void PutString(const wchar_t * start, const wchar_t * end, std::ofstream & out) { PutStringGeneric(start, end-start, out); } void PutString(const wchar_t * start, const wchar_t * end, std::fstream & out) { PutStringGeneric(start, end-start, out); } void PutString(const wchar_t * start, const wchar_t * end, std::ostringstream & out) { PutStringGeneric(start, end-start, out); } void PutString(const wchar_t * start, const wchar_t * end, std::stringstream & out) { PutStringGeneric(start, end-start, out); } */ void PutString(const std::wstring & str, StreamType & out) { PutString(str.c_str(), str.c_str() + str.size(), out); } void PutString(const wchar_t * start, const wchar_t * end, StreamType & out) { out.write(start, end-start); } bool Find(const std::wstring & key, typename Functions::Function ** function); void Call(typename Functions::Function * function, FunInfo & info); bool Call(const std::wstring & name, FunInfo & info, typename Functions::Function ** pfun = 0); bool Call(typename Item::Function & function, bool * info_res = 0, typename Functions::Function ** pfun = 0); void CallUserFunction(typename Functions::Function * function, FunInfo & info); void CallVariable(typename Functions::Function * function, FunInfo & info); wchar_t CreateSpecialChar(wchar_t c); 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 & o, const wchar_t * type, const wchar_t * arg = 0); void CreateMsg(StreamType & o, const std::wstring & type, const std::wstring & arg); void CreateMsg(StreamType & o, const std::wstring & type); void MakeTextIf_go(Item & item, bool result); bool MakeTextIfindexnumber(Item & item, typename Functions::Function * function, 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 MakeText(Item & item); }; // class Generator template void Generator::PutChar(wchar_t z, StreamType & out) { typedef typename StreamType::char_type CharType; out << static_cast(z); } /* template void Generator::PutString(const wchar_t * start, const wchar_t * end, StreamType & out) { out.write(start, end-start); } template void Generator::PutString(const std::wstring & str, StreamType & out) { PutString(str.c_str(), str.c_str() + str.size(), out); } */ template void Generator::PutStringGeneric(const wchar_t * start, size_t len, StreamType & out) { typedef typename StreamType::char_type CharType; for(size_t i=0 ; i(start[i]); } template Generator::Generator() { output_stream = 0; pattern = 0; functions = 0; max_items = 50000; max_for_items = 5000; special_chars = false; trim_white = false; skip_new_line = false; } template Generator::Generator(StreamType & o, Pattern & p, Functions & f) { output_stream = &o; pattern = &p; functions = &f; max_items = 50000; max_for_items = 5000; special_chars = false; trim_white = false; skip_new_line = false; } template void Generator::Set(StreamType & o, Pattern & p, Functions & f) { output_stream = &o; pattern = &p; functions = &f; } template void Generator::Set(StreamType & o) { output_stream = &o; } template void Generator::Set(Pattern & p) { pattern = &p; } template void Generator::Set(Functions & f) { functions = &f; } 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::Generate() { if( !output_stream || !pattern || !functions ) return; break_generating = false; current_item = 0; MakeText( pattern->item_root ); } template bool Generator::Find(const std::wstring & key, typename Functions::Function ** function) { if( !functions->Find(key, function) ) { CreateMsg(*output_stream, L"can't find", key.c_str() ); return false; } return true; } template void Generator::CallUserFunction(typename Functions::Function * function, FunInfo & info) { if( function->is_running ) { // recurrences are not allowed CreateMsg(*output_stream, L"the function is being executed"); return; } function->is_running = true; (function->user_function)(info); function->is_running = false; } template void Generator::CallVariable(typename Functions::Function * function, FunInfo & info) { info.res = !function->variable.empty(); } template void Generator::Call(typename Functions::Function * function, FunInfo & info) { info.Clear(); info.iter = function->iter; if( function->type == Functions::function ) CallUserFunction(function, info); else CallVariable(function, info); } // return: true if a function or variable was found and called template bool Generator::Call(const std::wstring & name, FunInfo & info, typename Functions::Function ** pfun) { typename Functions::Function * function; if( Find(name, &function) ) { Call(function, info); if( pfun ) *pfun = function; return true; } return false; } // return: true if a function or variable was found and called template bool Generator::Call(typename Item::Function & function, bool * info_res, typename Functions::Function ** pfun) { bool called; if( function.params.empty() ) { FunInfo info(*output_stream, function.params, empty); called = Call(function.name, info, pfun); if( info_res ) *info_res = info.res; } else { FunInfo info(*output_stream, function.params, function.params[0]); called = Call(function.name, info, pfun); if( info_res ) *info_res = info.res; } return called; } 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; } template void Generator::PrintSpecialText(const wchar_t * start, const wchar_t * end) { wchar_t special; while( start != end ) { special = 0; if( *start == '\\' && (start+1) != end ) special = CreateSpecialChar(*(start+1)); if( special ) { *output_stream << special; start += 2; } else { if( !(skip_new_line && *start == 10) ) *output_stream << *start; start += 1; } } } template void Generator::PrintNormalText(const wchar_t * start, const wchar_t * end) { if( skip_new_line ) { for( ; start != end ; ++start) { if( *start != 10 ) PutChar(*start, *output_stream); } } else { if( start != end ) PutString(start, end, *output_stream); } } 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 & o, const wchar_t * type, const wchar_t * arg) { pattern->CreateMsg(temp_msg, type, arg); PutString(temp_msg, o); temp_msg.clear(); } template void Generator::CreateMsg(StreamType & o, const std::wstring & type, const std::wstring & arg) { CreateMsg(o, type.c_str(), arg.c_str()); } template void Generator::CreateMsg(StreamType & o, const std::wstring & type) { CreateMsg(o, type.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 ) PutString(pfun->variable, *output_stream); } 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, typename Functions::Function * function, bool & result) { if( item.functions.size() != 2 ) return false; const wchar_t * number_text = item.functions[1].name.c_str(); const wchar_t * last_char; int number = StrToInt(number_text, &last_char); if( *last_char != '\0' ) { CreateMsg(*output_stream, L"if-index: syntax error"); return false; } result = (function->iter == number); return true; } template void Generator::MakeTextIfindex(Item & item) { if( item.functions.size() != 2 ) return; // we actually don't call a function (or variable) here // but only reading the iterator typename Functions::Function * function; if( !Find(item.functions[0].name, &function) ) return; bool result = false; if( item.functions[1].name == L"odd" ) { result = (function->iter & 1) == 1; } else if( item.functions[1].name == L"even" ) { result = (function->iter & 1) == 0; } else if( item.functions[1].name == L"first" ) { result = function->iter == 0; } else { if( !MakeTextIfindexnumber(item, function, result) ) return; } MakeTextIf_go(item, result); } template void Generator::MakeTextForLoop(Item & item, typename Functions::Function * function) { bool info_res; for( ; !break_generating ; function->iter += 1 ) { if( function->iter >= max_for_items ) { CreateMsg(*output_stream, item.functions[0].name.c_str(), L"function exceeded a limit for a [for] statement"); break; } Call(item.functions[0], &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].name, &function) ) return; if( function->is_for ) { CreateMsg(*output_stream, item.functions[0].name.c_str(), L"this function is already used in a [for] statement"); return; } function->is_for = true; function->iter = 0; MakeTextForLoop(item, function); function->is_for = false; function->iter = 0; } template void Generator::MakeTextDefine(Item & item) { 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].name, &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::MakeText(Item & item) { if( break_generating ) return; if( ++current_item > max_items ) { break_generating = true; CreateMsg(*output_stream, 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_err ) CreateMsg(*output_stream, L"a wrong directive"); } } // namespace Ezc #endif