/* * This file is a part of EZC -- Easy templating in C++ library * and is distributed under the BSD 3-Clause licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2007-2015, 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 "blocks.h" #include "pattern.h" #include "functions.h" #include "objects.h" #include "outstreams.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 SetPattern(Pattern & pattern); void SetBlocks(Blocks & blocks); void SetFunctions(Functions & functions); void SetObjects(Objects & objects); void SetMax(size_t max_items_, size_t 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); // default stack size: 300 void SetStackSize(size_t new_stack_size); // set whether or not we can use cache for functions or blocks // true by default void CanUseCache(bool can_use_cache); // set whether or not we can use variables: [def ...] statement // true by default void CanUseVars(bool can_use_variables); // the main methods for generating void Generate(StreamType & out); void Generate(StreamType & out, OutStreams & out_streams); void Generate(OutStreams & out_streams); void SetCommentary(const char * com_start, const char * com_stop); void SetCommentary(const std::string & com_start, const std::string & com_stop); void SetCommentary(const wchar_t * com_start, const wchar_t * com_stop); void SetCommentary(const std::wstring & com_start, const std::wstring & com_stop); private: // variables set typedef std::map Vars; Vars vars; struct BlockStack { std::vector args; StreamType * out_stream; bool was_return; }; std::vector block_stack_tab; size_t block_stack_index; size_t block_stack_size; // current output stream (can be null) // at the beginning it is pointing to the main stream (to the StreamType argument passsed to Generate method) StreamType * output_stream; Pattern * ppattern; Blocks * pblocks; Functions * pfunctions; Objects * pobjects; // pointer to the output streams map (can be null) // output stream will be created when [ezc out "stream_name"] statement is found OutStreams * output_stream_map; // temporary error messages std::wstring temp_msg; bool break_generating; size_t current_item; size_t max_items; size_t max_for_items; bool special_chars; bool trim_white; bool skip_new_line; size_t ezc_out_stack_index; size_t ezc_out_stack_size; size_t stack_index; size_t stack_size; 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; std::vector ezc_out_stack_tab; const StreamType empty_stream; // used in [0] [1] [2] when there is no such argument defined std::wstring empty_argument; // temporary streams used in [if..] [for...] or [def ...] // or if output_stream is null and an ezc function should be called StreamType stream_temp1, stream_temp_define; // last result from a user function (FunInfo::res) bool last_res; // true if this Generator is working now bool is_generator_working; // true if the Generator is working with [for ...], [if ...] etc. bool is_generating_for; bool is_generating_if; bool is_generating_normal; bool is_generating_filter; // an empty string for FunInfo objects // when there is no any parameters const std::wstring empty; // a stack for [for] statements std::vector stack_tab; std::wstring commentary_start, commentary_stop; bool can_use_vars; bool can_find_in_cache; void ResizeStreamStack(std::vector & stream_tab, size_t stream_tab_max_size); void ResizeFilterTab(); void ResizeStack(); void ResizeBlockStack(); void ResizeEzcOutStack(); void ClearStreamStack(std::vector & stream_tab); void ClearFilterTab(); void ClearForStack(); void ClearBlockStack(); void ClearEzcOutTab(); void ClearStream(StreamType & str); void RemoveStackFunData(Stack & sitem); bool ConvertToBool(const std::wstring & str); template CharType ToLower(CharType c); bool CheckBlockArgument(Item::Function & item_fun, std::wstring ** variable); bool FindInCache(Item::Function & item_fun, BaseObj ** base_obj, int * method_index, typename Functions::UserFunction ** function, Item ** item_block); bool FindInFunctionsAndBlocks(const std::wstring & name, BaseObj ** base_obj, int * method_index, typename Functions::UserFunction ** function, Item ** item_block); bool FindInVariables(const std::wstring & name, std::wstring ** variable); bool Find(Item::Function & item_fun, BaseObj ** base_obj, int * method_index, typename Functions::UserFunction ** function, Item ** item_block, std::wstring ** variable); void CallFunction(typename Functions::UserFunction * function, FunInfo & info); void CallFunction(typename Functions::UserFunction * function, std::vector & parameters, StreamType & out_stream, const StreamType & in_stream); bool CallBlock(Item & item_block, std::vector & parameters, StreamType & out_stream); void CallObject(BaseObj * base_obj, int method_index, FunInfo & info); void CallObject(BaseObj * base_obj, int method_index, std::vector & parameters, StreamType & out_stream, const StreamType & in_stream); bool Call(Item::Function & item_fun, StreamType & out_stream, bool clear_out_stream, const StreamType & in_stream); bool Call(Item::Function & item_fun); 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); bool IsWhite(wchar_t c); void TrimWhite(const wchar_t *& start, const wchar_t *& end); void SkipWhite(const wchar_t *& str); size_t StrToSize(const wchar_t * str, const wchar_t ** str_end = 0); void CopyTmpStreamToOutputStreams(Item::Function & fun, StreamType & ezc_out_tmp_stream, StreamType & previous_stream); void CreateMsg(std::wstring & out, const wchar_t * type, const wchar_t * arg = 0); void CreateMsg(StreamType & stream, 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); bool LimitAchieved(); void MakeTextIf_go(Item & item, bool result); void MakeTextIf(Item & item); void MakeTextFor(Item & item); void MakeItemText(Item & item); void MakeTextContainer(Item & item); void MakeTextNormal(Item & item); void MakeTextDefine(Item & item); void MakeTextFilter(Item & item); void MakeTextEzc(Item & item); void MakeTextReturn(Item & item); void MakeText(Item & item); void MakeEzcOut(Item & item); void Generate(); }; // class Generator template Generator::Generator() : empty_stream() { ppattern = 0; pblocks = 0; pfunctions = 0; pobjects = 0; max_items = 50000; max_for_items = 5000; filter_size = 20; special_chars = false; trim_white = false; skip_new_line = false; is_generator_working = false; stack_size = 300; block_stack_size = 64; ezc_out_stack_size = 16; can_find_in_cache = true; can_use_vars = true; } template Generator::Generator(const Generator & n) { operator=(n); } template Generator & Generator::operator=(const Generator & n) { ppattern = n.ppattern; pblocks = n.pblocks; pfunctions = n.pfunctions; pobjects = n.pobjects; max_items = n.max_items; max_for_items = n.max_for_items; special_chars = n.special_chars; trim_white = n.trim_white; skip_new_line = n.skip_new_line; can_find_in_cache = n.can_find_in_cache; can_use_vars = n.can_use_vars; // filter, stack and block_stack will be auto resized when calling Generate() method filter_size = n.filter_size; stack_size = n.stack_size; block_stack_size = n.block_stack_size; ezc_out_stack_size = n.ezc_out_stack_size; // vars doesn't have to be copied // don't copy filter tab // don't copy stack // don't copy ezc_out_stack_tab // don't copy output_stream and output_stream_map // !! CHECK ME // may copying should be denied when generator is working? // don't copy 'is_generator_working' flag is_generator_working = false; return *this; } template Generator::~Generator() { ClearFilterTab(); ClearForStack(); ClearBlockStack(); ClearEzcOutTab(); } template void Generator::SetCommentary(const char * com_start, const char * com_stop) { PT::UTF8ToWide(com_start, commentary_start); PT::UTF8ToWide(com_stop, commentary_stop); } template void Generator::SetCommentary(const std::string & com_start, const std::string & com_stop) { PT::UTF8ToWide(com_start, commentary_start); PT::UTF8ToWide(com_stop, commentary_stop); } template void Generator::SetCommentary(const wchar_t * com_start, const wchar_t * com_stop) { commentary_start = com_start; commentary_stop = com_stop; } template void Generator::SetCommentary(const std::wstring & com_start, const std::wstring & com_stop) { commentary_start = com_start; commentary_stop = com_stop; } template void Generator::SetPattern(Pattern & pattern) { ppattern = &pattern; } template void Generator::SetBlocks(Blocks & blocks) { pblocks = &blocks; } template void Generator::SetFunctions(Functions & functions) { pfunctions = &functions; } template void Generator::SetObjects(Objects & objects) { pobjects = &objects; } template void Generator::CanUseCache(bool can_use_cache) { can_find_in_cache = can_use_cache; } template void Generator::CanUseVars(bool can_use_variables) { can_use_vars = can_use_variables; } template void Generator::ResizeStack() { if( stack_tab.size() != stack_size ) { if( stack_tab.size() > stack_size ) { for(size_t i=stack_size ; i void Generator::ResizeStreamStack(std::vector & stream_tab, size_t stream_tab_max_size) { if( stream_tab.size() != stream_tab_max_size ) { if( stream_tab.size() < stream_tab_max_size ) { size_t i = stream_tab.size(); stream_tab.resize(stream_tab_max_size); for( ; i void Generator::ResizeFilterTab() { ResizeStreamStack(filter_tab, filter_size); } template void Generator::ResizeEzcOutStack() { ResizeStreamStack(ezc_out_stack_tab, ezc_out_stack_size); } template void Generator::ResizeBlockStack() { if( block_stack_tab.size() != block_stack_size ) { if( block_stack_tab.size() < block_stack_size ) { size_t i = block_stack_tab.size(); block_stack_tab.resize(block_stack_size); for( ; i void Generator::ClearStreamStack(std::vector & stream_tab) { for(size_t i=0 ; i void Generator::ClearFilterTab() { ClearStreamStack(filter_tab); } template void Generator::ClearEzcOutTab() { ClearStreamStack(ezc_out_stack_tab); } template void Generator::ClearBlockStack() { for(size_t i=0 ; i void Generator::ClearForStack() { 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::RemoveStackFunData(Stack & s) { if( s.fun_data && s.auto_remove ) { delete s.fun_data; s.fun_data = 0; } } template template CharType Generator::ToLower(CharType c) { if( c>='A' && c<='Z' ) return c - 'A' + 'a'; return c; } template bool Generator::ConvertToBool(const std::wstring & str) { if( str == L"true" ) return true; return false; } template void Generator::SetMax(size_t max_items_, size_t 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::SetStackSize(size_t new_stack_size) { // the stack will be resized when Generate() method is called stack_size = new_stack_size; } template void Generator::Generate() { if( is_generator_working ) { CreateMsg(L"generator busy"); return; } break_generating = false; current_item = 0; // in the case that there something has left on the stack // from previous call to Generate() // (an exception could have been thrown) ClearForStack(); ResizeFilterTab(); ResizeStack(); ResizeBlockStack(); ResizeEzcOutStack(); filter_index = 0; stack_index = 0; block_stack_index = 0; vars.clear(); if( ppattern ) { try { is_generator_working = true; MakeText( ppattern->item_root ); // !! IMPROVE ME we can print an error message if the stacks are not empty // (some [end] statements has been omited) vars.clear(); is_generator_working = false; } catch(...) { is_generator_working = false; throw; } } } template void Generator::Generate(StreamType & out) { output_stream = &out; output_stream_map = 0; Generate(); } template void Generator::Generate(StreamType & out, OutStreams & out_streams) { output_stream = &out; output_stream_map = &out_streams; Generate(); } template void Generator::Generate(OutStreams & out_streams) { output_stream = 0; output_stream_map = &out_streams; Generate(); } template bool Generator::CheckBlockArgument(Item::Function & item_fun, std::wstring ** variable) { if( item_fun.arg < 0 ) return false; empty_argument.clear(); *variable = &empty_argument; last_res = ConvertToBool(empty_argument); // it's a numeric function -- an argument to a block e.g.: [1] if( block_stack_index > 0 ) { BlockStack & block_stack = block_stack_tab[block_stack_index-1]; if( size_t(item_fun.arg) < block_stack.args.size() ) { *variable = &block_stack.args[item_fun.arg].str; last_res = block_stack.args[item_fun.arg].res; } } return true; } template bool Generator::FindInCache(Item::Function & item_fun, BaseObj ** base_obj, int * method_index, typename Functions::UserFunction ** function, Item ** item_block) { if( can_find_in_cache ) { if( item_fun.base_obj ) { *base_obj = reinterpret_cast * >(item_fun.base_obj); *method_index = item_fun.method_index; return true; } if( item_fun.fun_cache ) { *function = reinterpret_cast::UserFunction*>(item_fun.fun_cache); return true; } if( item_fun.item_block ) { *item_block = item_fun.item_block; return true; } } return false; } template bool Generator::FindInFunctionsAndBlocks(const std::wstring & name, BaseObj ** base_obj, int * method_index, typename Functions::UserFunction ** function, Item ** item_block) { if( pobjects ) { typename Objects::Iterator i = pobjects->Find(name, *method_index); if( i != pobjects->End() ) { *base_obj = *i; return true; } } if( pfunctions ) { typename Functions::Iterator i = pfunctions->Find(name); if( i != pfunctions->End() ) { *function = &i->second; return true; } } if( pblocks ) { Blocks::Iterator i = pblocks->Find(name); if( i != pblocks->End() ) { *item_block = &i->second; return true; } } return false; } template bool Generator::FindInVariables(const std::wstring & name, std::wstring ** variable) { Vars::iterator i = vars.find(name); if( i != vars.end() ) { Var & var = i->second; *variable = &var.str; last_res = var.res; return true; } return false; } template bool Generator::Find(Item::Function & item_fun, BaseObj ** base_obj, int * method_index, typename Functions::UserFunction ** function, Item ** item_block, std::wstring ** variable) { *base_obj = 0; *method_index = -1; *function = 0; *item_block = 0; *variable = 0; if( CheckBlockArgument(item_fun, variable) ) return true; if( FindInCache(item_fun, base_obj, method_index, function, item_block) ) return true; if( FindInFunctionsAndBlocks(item_fun.name, base_obj, method_index, function, item_block) ) return true; if( FindInVariables(item_fun.name, variable) ) return true; CreateUnknownMsg(item_fun.name); return false; } template void Generator::CallFunction(typename Functions::UserFunction * function, FunInfo & info) { info.Clear(); info.is_for = is_generating_for; info.is_if = is_generating_if; info.is_normal = is_generating_normal; info.is_filter = is_generating_filter; info.iter = info.stack.iter; info.stack_tab = &stack_tab[0];//stack_tab.data();///////////////////////////////////////////////////////// info.stack_index = stack_index-1; (*function)(info); last_res = info.res; } template void Generator::CallFunction(typename Functions::UserFunction * function, std::vector & parameters, StreamType & out_stream, const StreamType & in_stream) { if( parameters.empty() ) { FunInfo info(out_stream, parameters, empty, in_stream, stack_tab[stack_index-1], *stack_tab[stack_index-1].item); CallFunction(function, info); } else { FunInfo info(out_stream, parameters, parameters[0].str, in_stream, stack_tab[stack_index-1], *stack_tab[stack_index-1].item); CallFunction(function, info); } } template void Generator::CallObject(BaseObj * base_obj, int method_index, FunInfo & info) { info.Clear(); info.is_for = is_generating_for; info.is_if = is_generating_if; info.is_normal = is_generating_normal; info.is_filter = is_generating_filter; info.iter = info.stack.iter; info.stack_tab = &stack_tab[0];//stack_tab.data();///////////////////////////////////////////////////////// info.stack_index = stack_index-1; base_obj->CallFun(method_index, info); last_res = info.res; } template void Generator::CallObject(BaseObj * base_obj, int method_index, std::vector & parameters, StreamType & out_stream, const StreamType & in_stream) { if( parameters.empty() ) { FunInfo info(out_stream, parameters, empty, in_stream, stack_tab[stack_index-1], *stack_tab[stack_index-1].item); CallObject(base_obj, method_index, info); } else { FunInfo info(out_stream, parameters, parameters[0].str, in_stream, stack_tab[stack_index-1], *stack_tab[stack_index-1].item); CallObject(base_obj, method_index, info); } } template bool Generator::CallBlock(Item & item_block, std::vector & parameters, StreamType & out_stream) { if( block_stack_index >= block_stack_tab.size() ) { CreateMsg(L"Generator exceeded allowed number of blocks"); return false; } StreamType * old_stream = output_stream; BlockStack & block_stack = block_stack_tab[block_stack_index]; block_stack.was_return = false; block_stack.args = parameters; output_stream = block_stack.out_stream; ClearStream(*output_stream); block_stack_index += 1; MakeText(item_block); #ifdef EZC_HAS_SPECIAL_STREAM const std::wstring & str = output_stream->Str(); #else const std::wstring & str = output_stream->str(); #endif out_stream.write(str.c_str(), str.size()); // last_res is set by [return ...] statement or other last evaluated function ClearStream(*output_stream); output_stream = old_stream; block_stack_index -= 1; return true; } // return: true if a function, variable or block was found and called template bool Generator::Call(Item::Function & item_fun, StreamType & out_stream, bool clear_out_stream, const StreamType & in_stream) { BaseObj * base_obj; int method_index; typename Functions::UserFunction * fun; Item * item_block; std::wstring * variable; std::vector parameters; if( clear_out_stream ) ClearStream(out_stream); if( !Find(item_fun, &base_obj, &method_index, &fun, &item_block, &variable) ) return false; parameters.resize(item_fun.parameters.size()); for(size_t i=0 ; ic_str(), variable->size()); return true; } // return: true if a function or variable was found and called template bool Generator::Call(Item::Function & item_fun) { return Call(item_fun, stream_temp1, true, empty_stream); } 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) { if( output_stream ) { 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( output_stream ) { 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 bool Generator::IsWhite(wchar_t c) { // 13 (\r) is from a dos file at the end of a line (\r\n) // 160 is a non-breaking space if( c==' ' || c=='\t' || c==13 || c==160 || c==10 ) return true; return false; } template void Generator::TrimWhite(const wchar_t *& start, const wchar_t *& end) { while( start != end && IsWhite(*start) ) ++start; while( start != end && IsWhite(*(end-1)) ) --end; } template void Generator::SkipWhite(const wchar_t *& str) { while( IsWhite(*str) ) str += 1; } template size_t Generator::StrToSize(const wchar_t * str, const wchar_t ** str_end) { size_t res = 0; SkipWhite(str); // !! IMPROVE ME // overflow is not checked while( *str>='0' && *str<='9' ) { res *= 10; res += *str - '0'; str += 1; } SkipWhite(str); if( str_end ) *str_end = str; return res; } template void Generator::CopyTmpStreamToOutputStreams(Item::Function & fun, StreamType & ezc_out_tmp_stream, StreamType & previous_stream) { if( output_stream_map ) { #ifdef EZC_HAS_SPECIAL_STREAM const std::wstring & str = ezc_out_tmp_stream.Str(); #else const std::wstring & str = ezc_out_tmp_stream.str(); #endif if( !str.empty() ) { previous_stream.write(str.c_str(), str.size()); for(size_t s=0 ; s < fun.parameters.size() ; ++s) { std::wstring & name = fun.parameters[s]->name; auto imap = output_stream_map->streams_map.find(name); if( imap == output_stream_map->streams_map.end() ) { if( output_stream_map->streams_map.size() < output_stream_map->streams_tab.size() ) { /* a new stream from the pool (output_stream_tab) is taken */ StreamType * stream = output_stream_map->streams_tab[ output_stream_map->streams_map.size() ]; output_stream_map->streams_map.insert(std::make_pair(name, stream)); ClearStream(*stream); stream->write(str.c_str(), str.size()); } else { CreateMsg(previous_stream, L"limit of output streams in OutStreams<> has been reached"); } } else { StreamType * stream = imap->second; stream->write(str.c_str(), str.size()); } } } } } template void Generator::CreateMsg(std::wstring & out, const wchar_t * type, const wchar_t * arg) { out = commentary_start; out += L"Ezc runtime error: "; out += type; if( arg ) { out += ' '; out += arg; } out += commentary_stop; } template void Generator::CreateMsg(StreamType & stream, const wchar_t * type, const wchar_t * arg) { CreateMsg(temp_msg, type, arg); stream.write(temp_msg.c_str(), temp_msg.size()); temp_msg.clear(); } template void Generator::CreateMsg(const wchar_t * type, const wchar_t * arg) { if( output_stream ) 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) { is_generating_normal = true; if( output_stream ) { Call(item.function, *output_stream, false, empty_stream); } else { Call(item.function, stream_temp1, false, empty_stream); ClearStream(stream_temp1); } } 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) { is_generating_if = true; if( !Call(item.function) ) return; MakeTextIf_go(item, last_res); } template void Generator::MakeTextFor(Item & item) { stack_tab[stack_index-1].is_for = true; for( ; !break_generating ; stack_tab[stack_index-1].iter += 1 ) { if( stack_tab[stack_index-1].iter >= max_for_items ) { CreateMsg(item.function.name.c_str(), L"function exceeded a limit for a [for] statement"); break; } // is_generating_for will be changed by next call to MakeText() // so we should set it in each iterations is_generating_for = true; Call(item.function, stream_temp1, true, empty_stream); if( !last_res ) break; if( !item.item_tab.empty() ) MakeText( *item.item_tab[0] ); // should be only one item - item_container } } template void Generator::MakeTextDefine(Item & item) { if( !can_use_vars ) { CreateMsg(L"[def] statement not available"); return; } Var & var = vars[item.function.name]; var.str.clear(); var.res = false; for(size_t i=0 ; i < item.function.parameters.size() ; ++i) { if( !item.function.parameters[i]->is_function ) { var.str += item.function.parameters[i]->name; var.res = ConvertToBool(item.function.parameters[i]->name); } else { // it is a function parameter if( Call(*item.function.parameters[i], stream_temp_define, true, empty_stream) ) { #ifdef EZC_HAS_SPECIAL_STREAM var.str += stream_temp_define.Str(); #else var.str += stream_temp_define.str(); #endif var.res = last_res; } else { CreateMsg(L"[def] unknown function/block/variable", item.function.name); } } } } template void Generator::MakeTextFilter(Item & item) { 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 is_generating_filter = true; if( old_stream ) { Call(item.function, *old_stream, false, *output_stream); } else { Call(item.function, stream_temp1, true, *output_stream); ClearStream(stream_temp1); } ClearStream(*output_stream); output_stream = old_stream; filter_index -= 1; } /* although we are using a stack for [etc out] there is no need for the stack now because the output is only a one-level map (not nested structure) but in the future we can use more complicated class like PT::Space and then nested [ezc out] statements can product a better output */ template void Generator::MakeEzcOut(Item & item) { std::vector output_stream_names; StreamType * old_stream; bool stream_added = true; if( ezc_out_stack_index >= ezc_out_stack_tab.size() ) { CreateMsg(L"Generator exceeded allowed number of [ezc out] statements"); return; } /* if we encounter the first ezc_out statement without arguments e.g. [ezc out] or just [out] then we can simply ignore it */ if( item.function.parameters.empty() && ezc_out_stack_index == 0 ) stream_added = false; if( stream_added ) { old_stream = output_stream; output_stream = ezc_out_stack_tab[ezc_out_stack_index]; ClearStream(*output_stream); ezc_out_stack_index += 1; } if( !item.item_tab.empty() ) MakeText( *item.item_tab[0] ); // should be only one item - item_container if( stream_added ) { CopyTmpStreamToOutputStreams(item.function, *output_stream, *old_stream); ClearStream(*output_stream); output_stream = old_stream; ezc_out_stack_index -= 1; } } template void Generator::MakeTextEzc(Item & item) { if( item.function.name == L"out" ) MakeEzcOut(item); // in the future we can use more builtin functions } template void Generator::MakeTextReturn(Item & item) { last_res = false; if( block_stack_index == 0 ) { // the [return] statement is not called from a [block] CreateMsg(L"[return] should be called from a [block]"); return; } BlockStack & block_stack = block_stack_tab[block_stack_index - 1]; block_stack.was_return = true; if( item.has_function ) { // output stream in [return] statement is ignored (we use only the stream produced by the whole block) // this Call() sets last_res which is used later when we return to CallBlock() Call(item.function, stream_temp1, false, empty_stream); ClearStream(stream_temp1); } } template bool Generator::LimitAchieved() { if( break_generating ) return true; if( current_item >= max_items ) { break_generating = true; CreateMsg(L"Generator exceeded allowed number of elements"); return true; } if( stack_index >= stack_tab.size() ) { break_generating = true; CreateMsg(L"Generator exceeded the stack size"); return true; } if( block_stack_index > 0 ) { BlockStack & block_stack = block_stack_tab[block_stack_index - 1]; if( block_stack.was_return ) return true; } return false; } template void Generator::MakeText(Item & item) { if( LimitAchieved() ) return; current_item += 1; stack_index += 1; is_generating_for = false; is_generating_if = false; is_generating_normal = false; is_generating_filter = false; stack_tab[stack_index-1].Clear(); stack_tab[stack_index-1].item = &item; if ( item.type == Item::item_text ) MakeItemText(item); else if( item.type == Item::item_container )MakeTextContainer(item); else if( item.type == Item::item_function ) MakeTextNormal(item); else if( item.type == Item::item_if ) MakeTextIf(item); else if( item.type == Item::item_def ) MakeTextDefine(item); else if( item.type == Item::item_for ) MakeTextFor(item); else if( item.type == Item::item_filter ) MakeTextFilter(item); else if( item.type == Item::item_ezc ) MakeTextEzc(item); else if( item.type == Item::item_return ) MakeTextReturn(item); else if( item.type == Item::item_err ) CreateMsg(L"a wrong directive"); RemoveStackFunData(stack_tab[stack_index-1]); stack_index -=1; } } // namespace Ezc #endif