/* * 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-2022, 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 #include #include #include "blocks.h" #include "pattern.h" #include "functions.h" #include "objects.h" #include "outstreams.h" #include "expressionparser.h" #include "log/log.h" #include "utf8/utf8.h" #include "vars.h" #ifdef EZC_HAS_MORM_LIBRARY #include "model.h" #endif 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 SetVariables(Vars & variables); void SetLogger(pt::Log & logger); #ifdef EZC_HAS_MORM_LIBRARY //void SetModels(Models & models); #endif 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 variables: [def ...] statement // true by default void CanUseVars(bool can_use_variables); void SetProgramMode(bool program_mode); void SetExpressionParser(ExpressionParser * expression_parser); // the main methods for generating void Generate(StreamType & out); void Generate(StreamType & out, OutStreams & out_streams); void Generate(OutStreams & out_streams); private: struct FindHelperOld { //const std::wstring * fun_name; //BaseObj * base_obj; //int method_index; //typename Functions::UserFunction * function; //Item * item_block; Var * variable; #ifdef EZC_HAS_MORM_LIBRARY Var * wrapper; size_t field_index; #endif FindHelperOld() { //fun_name = nullptr; //base_obj = nullptr; //method_index = -1; //function = nullptr; //item_block = nullptr; variable = nullptr; #ifdef EZC_HAS_MORM_LIBRARY wrapper = nullptr; field_index = 0; #endif } }; // RENAMEME struct FindHelper { const std::wstring & name; std::vector & fields; size_t field_index; std::vector> & parameters; Var * input; Var * result; bool found; const std::wstring * previous_name; size_t nested_calls; Var * current_var; Var * previous_var; Var * previous_result; FindHelper( const std::wstring & name, std::vector & fields, std::vector> & parameters ): name(name), fields(fields), parameters(parameters) { field_index = 0; found = false; previous_name = nullptr; nested_calls = 0; input = nullptr; result = nullptr; current_var = nullptr; previous_var = nullptr; previous_result = nullptr; } // rename me to current_field() const std::wstring & current_name() { return (field_index == 0)? name : fields[field_index - 1]; } const std::wstring & next_field() { return fields[field_index]; } bool is_last_field() { return field_index == fields.size(); } bool is_next_field() { return field_index < fields.size(); } }; 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; #ifdef EZC_HAS_MORM_LIBRARY //Models * pmodels; #endif Vars * pvars; pt::Log * plog; // pointer to the output streams map (can be null) // output stream will be created when [ezc frame "stream_name"] statement is found OutStreams * output_frames_streams; // 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_frames_stack_index; size_t ezc_frames_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_frame_stack_tab; StreamType empty_stream; // 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; //StreamType 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_if_def; bool is_generating_if_not_def; 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; bool can_use_vars; bool program_mode; size_t max_nested_calls; ExpressionParser * expression_parser; void ResizeStreamStack(std::vector & stream_tab, size_t stream_tab_max_size); void ResizeFilterTab(); void ResizeStack(); void ResizeBlockStack(); void ResizeEzcFrameStack(); void ClearStreamStack(std::vector & stream_tab); void ClearFilterTab(); void ClearForStack(); void ClearBlockStack(); void ClearEzcFrameTab(); void ClearStream(StreamType & str); void CopyStreamToString(StreamType & src_stream, std::wstring & dst_string); void CopyStream(StreamType & src_stream, StreamType & dst_stream); void CopyStream(pt::WTextStream & src_stream, StreamType & dst_stream, bool should_escape); void RemoveStackFunData(Stack & sitem); bool ConvertToBool(const std::wstring & str); template CharType ToLower(CharType c); bool IsTestingFunctionExistence(); bool CheckBlockArgument(int arg_index, FindHelperOld & find_helper); #ifdef EZC_HAS_MORM_LIBRARY //bool FindInModels(FindHelperOld & find_helper); #endif void PrepareEnvStruct(Env & info); void CallFunction(typename Functions::UserFunction & function, Env & info); void CallFunction(typename Functions::UserFunction & function, FindHelper & find_helper); bool CallBlock(Item & item_block, Var & result, std::vector> & parameters); void PrintDate(pt::Date & date, FindHelper & find_helper); //bool PrintDatePart(pt::Date & date, const std::wstring & field, std::vector> & parameters, Var & result); //bool CallDate(pt::Date & date, const std::wstring & name, std::vector & fields, size_t & field_index, std::vector> & parameters, Var & result); void PrintLastSpaceField(pt::Space & space, FindHelper & find_helper); //void CallSpaceObjectForLastField(pt::Space & space, FindHelper & find_helper); //pt::Space * CallSpaceObjectForMiddleField(const std::wstring & root_space_name, std::vector & fields, size_t field_index, pt::Space * space); void CallSpaceTableForLastField(morm::SpaceWrapper & space_wrapper, pt::Space & space, FindHelper & find_helper, size_t model_wrapper_space_table_index); pt::Space * CallSpaceTableForMiddleField(morm::SpaceWrapper & space_wrapper, pt::Space & space, FindHelper & find_helper, size_t model_wrapper_space_table_index); bool CallSpaceWrapper(morm::SpaceWrapper & space_wrapper, FindHelper & find_helper); bool CallSpace(pt::Space & root_space, FindHelper & find_helper); #ifdef EZC_HAS_MORM_LIBRARY bool CallModelField(morm::Model & model, FindHelper & find_helper); // bool CallModel(morm::Model & model, const std::wstring & name, std::vector & fields, size_t field_index, // std::vector> & parameters, Var & result); // void FindLastModelWrapper(FindHelperOld & find_helper, std::vector & fields); // bool CallWrapper(FindHelperOld & find_helper, std::vector & fields, Var & result, std::vector> & parameters, const StreamType & in_stream); #endif Var * FindInScope(const std::wstring & name); void FindVariable(FindHelper & find_helper); void EvaluateVariable(FindHelper & find_helper); bool EvaluateModel(FindHelper & find_helper); bool EvaluateModelWrapper(FindHelper & find_helper); void EvaluateFunction(typename Var::UserFunction user_function, FindHelper & find_helper); // bool CallVariable(FindHelper & findxxx // const std::wstring & name, // std::vector & fields, // size_t & field_index, // Var & result, // std::vector> & parameters // ); bool Call(Item::Function & item_fun, const std::wstring * fun_name, Var & result, StreamType & in_stream); bool Call(Item::Function & item_fun, Var & result); 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); bool HasParam(std::vector> & parameters, const wchar_t * param1, const wchar_t * param2 = nullptr); bool ShouldMakeSpaceDump(std::vector> & parameters); bool ShouldMakeJsonDump(std::vector> & parameters); bool IsPrettyPrint(std::vector> & parameters); void DumpSpaceIfNeeded(pt::Space & space, FindHelper & find_helper); void DumpModelIfNeeded(morm::Model & model, Var & result, std::vector> & parameters); void CopyTmpStreamToOutputStreams(Item::Function & fun, StreamType & ezc_out_tmp_stream, StreamType & previous_stream); void CreateMsg(const wchar_t * type, const std::wstring & model_name, std::vector & fields, size_t how_many_fields_print, const wchar_t * arg = nullptr, const wchar_t * arg2 = nullptr, const wchar_t * arg3 = nullptr); void CreateMsg(const wchar_t * type, const std::wstring & model_name, std::vector & fields, const wchar_t * arg = nullptr, const wchar_t * arg2 = nullptr, const wchar_t * arg3 = nullptr); void CreateMsg(const wchar_t * type, const wchar_t * arg = nullptr); void CreateMsg(const std::wstring & type, const std::wstring & arg); void CreateMsg(const std::wstring & type); bool LimitAchieved(); void EvaluateProgramNode(Item & item); void MakeTextIf_go(Item & item, bool result); void MakeTextIf(Item & item); void MakeTextIfDef(Item & item); void MakeTextIfNotDef(Item & item); void MakeTextFor(Item & item); void MakeItemText(Item & item); void MakeTextContainer(Item & item); void MakeTextFunction(Item & item); void MakeTextDefine(Item & item, Var & var); void MakeTextDefine(Item & item); void MakeTextDefineIfNotSet(Item & item); void MakeTextLet(Item & item, Var & var); void MakeTextLet(Item & item); void MakeTextLetIfNotSet(Item & item); void MakeTextFilter(Item & item); void MakeTextEzc(Item & item); void MakeTextReturn(Item & item); void MakeText(Item & item); void MakeEzcFrame(Item & item); void Generate(); }; // class Generator template Generator::Generator() : empty_stream() { ppattern = nullptr; pvars = nullptr; plog = nullptr; #ifdef EZC_HAS_MORM_LIBRARY //pmodels = nullptr; #endif 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_frames_stack_size = 16; can_use_vars = true; expression_parser = nullptr; program_mode = false; max_nested_calls = 64; } template Generator::Generator(const Generator & n) { operator=(n); } template Generator & Generator::operator=(const Generator & n) { ppattern = n.ppattern; pvars = n.pvars; plog = n.plog; #ifdef EZC_HAS_MORM_LIBRARY //pmodels = n.pmodels; #endif 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_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_frames_stack_size = n.ezc_frames_stack_size; expression_parser = n.expression_parser; program_mode = n.program_mode; max_nested_calls = n.max_nested_calls; // vars doesn't have to be copied // don't copy filter tab // don't copy stack // don't copy ezc_frame_stack_tab // don't copy output_stream and output_frames_streams // !! 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(); ClearEzcFrameTab(); } template void Generator::SetPattern(Pattern & pattern) { ppattern = &pattern; } template void Generator::SetVariables(Vars & variables) { pvars = &variables; } template void Generator::SetLogger(pt::Log & logger) { plog = &logger; } #ifdef EZC_HAS_MORM_LIBRARY //template //void Generator::SetModels(Models & models) //{ // pmodels = ⊧ //} #endif template void Generator::CanUseVars(bool can_use_variables) { can_use_vars = can_use_variables; } template void Generator::SetProgramMode(bool set_program_mode) { this->program_mode = set_program_mode; } template void Generator::SetExpressionParser(ExpressionParser * expression_parser) { this->expression_parser = expression_parser; } 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::ResizeEzcFrameStack() { ResizeStreamStack(ezc_frame_stack_tab, ezc_frames_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::ClearEzcFrameTab() { ClearStreamStack(ezc_frame_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) { if constexpr(is_pikotools_stream) { str.clear(); } else { str.str(L""); } } template void Generator::CopyStreamToString(StreamType & src_stream, std::wstring & dst_string) { if constexpr (sizeof(wchar_t) == sizeof(typename StreamType::char_type)) { if constexpr(is_pikotools_stream) { src_stream.to_str(dst_string); } else { dst_string = src_stream.str(); } } else { if constexpr(is_pikotools_stream) { src_stream.to_str(dst_string); } else { std::string tmp = src_stream.str(); pt::utf8_to_wide(tmp, dst_string); } } } template void Generator::CopyStream(StreamType & src_stream, StreamType & dst_stream) { if constexpr(is_pikotools_stream) { dst_stream << src_stream; } else { dst_stream << src_stream.str(); } } template void Generator::CopyStream(pt::WTextStream & src_stream, StreamType & dst_stream, bool should_escape) { if constexpr(is_autoescape_stream) { dst_stream.Escape(should_escape); dst_stream << src_stream; } else { if constexpr(is_pikotools_stream) { dst_stream << src_stream; } else { if constexpr(sizeof(char) == sizeof(typename StreamType::char_type)) { wide_stream_to_utf8(src_stream, dst_stream, false); } else { dst_stream << src_stream.to_str(); } } } } 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) { return !str.empty(); } 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(); ResizeEzcFrameStack(); filter_index = 0; stack_index = 0; ezc_frames_stack_index = 0; block_stack_index = 0; 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) is_generator_working = false; } catch(...) { is_generator_working = false; throw; } } } template void Generator::Generate(StreamType & out) { output_stream = &out; output_frames_streams = nullptr; Generate(); } template void Generator::Generate(StreamType & out, OutStreams & out_streams) { output_stream = &out; output_frames_streams = &out_streams; Generate(); } template void Generator::Generate(OutStreams & out_streams) { output_stream = nullptr; output_frames_streams = &out_streams; Generate(); } template bool Generator::IsTestingFunctionExistence() { return is_generating_if_def || is_generating_if_not_def; } template bool Generator::CheckBlockArgument(int arg_index, FindHelperOld & find_helper) { if( arg_index < 0 ) return false; // 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(arg_index) < block_stack.args.size() ) { find_helper.variable = &block_stack.args[arg_index]; return true; } } return false; } #ifdef EZC_HAS_MORM_LIBRARY //template //bool Generator::FindInModels(FindHelperOld & find_helper) //{ // if( pmodels ) // { // morm::Wrapper * m = pmodels->Find(*find_helper.fun_name); // // if( m ) // { // find_helper.wrapper = m; // return true; // } // } // // return false; //} #endif template void Generator::PrepareEnvStruct(Env & info) { info.clear(); info.is_for = is_generating_for; info.is_if = is_generating_if; info.is_if_def = is_generating_if_def; info.is_if_not_def = is_generating_if_not_def; info.is_normal = is_generating_normal; info.is_filter = is_generating_filter; info.iter = info.stack.iter; info.stack_tab = stack_tab.data(); info.stack_index = stack_index-1; } template void Generator::CallFunction(typename Functions::UserFunction & function, Env & info) { PrepareEnvStruct(info); (function)(info); } template void Generator::CallFunction( typename Functions::UserFunction & function, FindHelper & find_helper) { if( !IsTestingFunctionExistence() ) { // if( find_helper.parameters.empty() ) { Env info(*find_helper.result, find_helper.parameters, *find_helper.input, stack_tab[stack_index-1], *stack_tab[stack_index-1].item); CallFunction(function, info); } // else // { // Env info(result, parameters, in_stream, stack_tab[stack_index-1], *stack_tab[stack_index-1].item); // CallFunction(function, info); // } } } template bool Generator::HasParam(std::vector> & parameters, const wchar_t * param1, const wchar_t * param2) { for(Var & var : parameters) { if( var.is_equal(param1) || (param2 && var.is_equal(param2)) ) { return true; } } return false; } template bool Generator::ShouldMakeSpaceDump(std::vector> & parameters) { return HasParam(parameters, L"dump", L"dump_to_space"); } template bool Generator::ShouldMakeJsonDump(std::vector> & parameters) { return HasParam(parameters, L"dump_to_json"); } template bool Generator::IsPrettyPrint(std::vector> & parameters) { return HasParam(parameters, L"pretty"); } template void Generator::DumpSpaceIfNeeded(pt::Space & space, FindHelper & find_helper) { bool dump_space = ShouldMakeSpaceDump(find_helper.parameters); bool dump_json = ShouldMakeJsonDump(find_helper.parameters); if( dump_space || dump_json ) { bool pretty = IsPrettyPrint(find_helper.parameters); if( dump_space ) { space.serialize_to_space_stream(find_helper.result->stream, pretty); find_helper.result->type = Var::TYPE_STREAM; } else if( dump_json ) { space.serialize_to_json_stream(find_helper.result->stream, pretty); find_helper.result->type = Var::TYPE_STREAM; } } } template void Generator::DumpModelIfNeeded( morm::Model & model, Var & result, std::vector> & parameters) { bool dump_space = ShouldMakeSpaceDump(parameters); bool dump_json = ShouldMakeJsonDump(parameters); if( dump_space || dump_json ) { //bool pretty = IsPrettyPrint(parameters); if( dump_space ) { // not implemented yet } else if( dump_json ) { // IMPLEMENT ME // depends on the model_connector (flat connector) // need to be made in a different way pt::TextStream temp_str; model.to_text(temp_str, false, false); result.stream << temp_str; // what about escaping here? why Model::to_text() don't take WTextStream? result.type = Var::TYPE_STREAM; //out_stream << temp_str; } } } template void Generator::PrintDate( pt::Date & date, FindHelper & find_helper) { bool is_roman = HasParam(find_helper.parameters, L"roman"); bool is_no_sec = HasParam(find_helper.parameters, L"no_sec"); bool only_date = HasParam(find_helper.parameters, L"only_date"); bool only_time = HasParam(find_helper.parameters, L"only_time"); if( only_date ) { find_helper.result->type = Var::TYPE_STREAM; date.SerializeYearMonthDay(find_helper.result->stream, is_roman); } else if( only_time ) { find_helper.result->type = Var::TYPE_STREAM; if( is_no_sec ) date.SerializeHourMin(find_helper.result->stream); else date.SerializeHourMinSec(find_helper.result->stream); } else { find_helper.result->type = Var::TYPE_STREAM; date.Serialize(find_helper.result->stream, is_roman, !is_no_sec); } } /* * these can be made as a simple function (e.g. from winix) * as a first argument let they take pt::Date */ /* template bool Generator::PrintDatePart( pt::Date & date, const std::wstring & field, std::vector> & parameters, Var & result) { bool is_test = IsTestingFunctionExistence(); if( field == L"year" ) { if( !is_test ) { result.type = Var::TYPE_STREAM; result.stream << date.year; } } else if( field == L"month" ) { if( !is_test ) { bool is_roman = HasParam(parameters, L"roman"); result.type = Var::TYPE_STREAM; if( is_roman ) pt::Date::SerializeMonthAsRoman(result.stream, date.month); else result.stream << date.month; } } else if( field == L"day" ) { if( !is_test ) { result.type = Var::TYPE_STREAM; result.stream << date.day; } } else if( field == L"hour" ) { if( !is_test ) { result.type = Var::TYPE_STREAM; result.stream << date.hour; } } else if( field == L"min" ) { if( !is_test ) { result.type = Var::TYPE_STREAM; result.stream << date.min; } } else if( field == L"sec" ) { if( !is_test ) { result.type = Var::TYPE_STREAM; result.stream << date.sec; } } else { return false; } return true; } */ /* template bool Generator::CallDate( pt::Date & date, const std::wstring & name, std::vector & fields, size_t field_index, std::vector> & parameters, Var & result) { bool found = true; bool all_fields_known = (field_index == fields.size()); if( all_fields_known ) { PrintDate(date, parameters, result); } else { found = PrintDatePart(date, fields[field_index], result, parameters); // if( found ) // field_index += 1; // { // if( !IsTestingFunctionExistence() ) // { // CreateMsg(L"cannot find ", *find_helper.fun_name, fields, L", unknown property ", fields[find_helper.field_index].c_str(), L" of date object"); // } // // found = false; // } } return found; } */ //template //void Generator::CallSpaceObjectForLastField(pt::Space & space, FindHelper & find_helper) //{ // if( !IsTestingFunctionExistence() ) // { // // CHECKME should we convert the last value to last_res? // DumpSpaceIfNeeded(space, find_helper); // } //} //template //pt::Space * Generator::CallSpaceObjectForMiddleField(const std::wstring & root_space_name, std::vector & fields, size_t field_index, pt::Space * space) //{ // std::wstring & next_field = fields[field_index]; // space = space->get_space(next_field); // // if( !space && !IsTestingFunctionExistence() ) // { // CreateMsg(L"cannot find space field: ", root_space_name, fields, field_index + 1); // } // // return space; //} template void Generator::CallSpaceTableForLastField( morm::SpaceWrapper & space_wrapper, pt::Space & space, FindHelper & find_helper, size_t model_wrapper_space_table_index) { pt::Space::TableType * table = space.get_table(); if( is_generating_for ) { // we are in [for...]statement, increment iterator and check the range space_wrapper.increment_iterator(model_wrapper_space_table_index, table->size()); space_wrapper.invalidate_iterators(model_wrapper_space_table_index + 1); size_t iterator_value = space_wrapper.get_space_iterator_value(model_wrapper_space_table_index); bool loop_status = (iterator_value < table->size()); //space_wrapper.set_last_for_loop_status(for_status); find_helper.result->set(loop_status); } else { find_helper.result->set(space, false); } if( !IsTestingFunctionExistence() && find_helper.is_last_field() ) { DumpSpaceIfNeeded(space, find_helper); if( HasParam(find_helper.parameters, L"index") ) { find_helper.result->stream << space_wrapper.get_space_iterator_value(model_wrapper_space_table_index); find_helper.result->type = Var::TYPE_STREAM; } if( HasParam(find_helper.parameters, L"index-one") ) { find_helper.result->stream << (space_wrapper.get_space_iterator_value(model_wrapper_space_table_index) + 1); find_helper.result->type = Var::TYPE_STREAM; } } } template pt::Space * Generator::CallSpaceTableForMiddleField( morm::SpaceWrapper & space_wrapper, pt::Space & space_table, FindHelper & find_helper, size_t model_wrapper_space_table_index) { pt::Space::TableType * table = space_table.get_table(); pt::Space * space = nullptr; // check the current iterator, if it is correct then select the item from the table size_t iterator_value = space_wrapper.get_space_iterator_value(model_wrapper_space_table_index); if( iterator_value < table->size() ) { space = (*table)[iterator_value]; } else { // this message can be print even for [if-def...] //CreateMsg(L"space table: ", root_space_name, fields, field_index, L" is not initialized, have you forgotten to use [for...] statement?"); space = nullptr; } return space; } template void Generator::PrintLastSpaceField( pt::Space & space, FindHelper & find_helper) { bool no_escape = HasParam(find_helper.parameters, L"raw", L"noescape"); // FIXME what about escaping here? if( no_escape ) { //pt::WTextStream str; space.serialize_to_string(find_helper.result->stream); find_helper.result->type = Var::TYPE_STREAM; //CopyStream(str, out_stream, false); } else { // space->serialize_to_string(out_stream); space.serialize_to_string(find_helper.result->stream); find_helper.result->type = Var::TYPE_STREAM; } } template bool Generator::CallSpaceWrapper( morm::SpaceWrapper & space_wrapper, FindHelper & find_helper) { pt::Space * space = space_wrapper.get_space(); size_t model_wrapper_space_table_index = 0; while( find_helper.is_next_field() ) { if( space->is_object() ) { const std::wstring & next_field = find_helper.next_field(); pt::Space * next_space = space->get_space(next_field); if( next_space ) { find_helper.field_index += 1; space = next_space; } else { // may this is a global function so return true return true; } } else if( space->is_table() ) { if( model_wrapper_space_table_index < space_wrapper.space_indices_table_size() ) { const std::wstring & next_field = find_helper.next_field(); if( next_field == L"first" ) { find_helper.field_index += 1; if( space->table_size() > 0 ) { find_helper.result->set(*space->value.value_table.front(), false); find_helper.found = true; return true; } else { CreateMsg(L"first is used but the table is empty"); // IMPROVE the warning message return false; } } else if( next_field == L"last" ) { find_helper.field_index += 1; if( space->table_size() > 0 ) { find_helper.result->set(*space->value.value_table.back(), false); find_helper.found = true; return true; } else { CreateMsg(L"last is used but the table is empty"); // IMPROVE the warning message return false; } } pt::Space * next_space = CallSpaceTableForMiddleField(space_wrapper, *space, find_helper, model_wrapper_space_table_index); if( !next_space ) { // a [for ...] was not used beforehand // return true, this can be a global method such as 'size' find_helper.result->set(*space, false); find_helper.found = true; return true; } // put a constant somewhere if( next_field == L"val" ) { find_helper.field_index += 1; } else if( next_field == L"index" ) { find_helper.field_index += 1; size_t iterator_value = space_wrapper.get_space_iterator_value(model_wrapper_space_table_index); find_helper.result->set(static_cast(iterator_value)); find_helper.found = true; return true; } else if( next_field == L"index-one" ) { find_helper.field_index += 1; size_t iterator_value = space_wrapper.get_space_iterator_value(model_wrapper_space_table_index); find_helper.result->set(static_cast(iterator_value + 1)); find_helper.found = true; return true; } else if( next_field == L"is_first" ) { find_helper.field_index += 1; size_t iterator_value = space_wrapper.get_space_iterator_value(model_wrapper_space_table_index); find_helper.result->set(iterator_value == 0); find_helper.found = true; return true; } else if( next_field == L"is_last" ) { find_helper.field_index += 1; size_t iterator_value = space_wrapper.get_space_iterator_value(model_wrapper_space_table_index); find_helper.result->set(iterator_value + 1 == space->table_size()); find_helper.found = true; return true; } else { space = next_space; break; } space = next_space; } else { CreateMsg(L"", find_helper.name, find_helper.fields, find_helper.field_index, L" exceeded the maximum number of fields for a space object"); return false; } } else { break; } model_wrapper_space_table_index += 1; } find_helper.found = true; if( space->is_table() ) { CallSpaceTableForLastField(space_wrapper, *space, find_helper, model_wrapper_space_table_index); } else if( !IsTestingFunctionExistence() ) { //PrintLastSpaceField(*space, find_helper); find_helper.result->set(*space, false); // WARNING here a new space wrapper will be created, need to invastigate if( find_helper.is_last_field() ) // find_index will be incremented and the result is always false here DumpSpaceIfNeeded(*space, find_helper); } return true; } template bool Generator::CallSpace( pt::Space & root_space, FindHelper & find_helper) { pt::Space * space = &root_space; while( find_helper.field_index < find_helper.fields.size() + 1) { if( space->is_object() ) { const std::wstring & current_field = find_helper.fields[find_helper.field_index - 1]; pt::Space * next_space = space->get_space(current_field); if( next_space ) { find_helper.field_index += 1; space = next_space; } else { return true; } } else if( space->is_table() ) { //CreateMsg(L"you cannot iterate through space table when the space is added without a wrapper"); // IMPROVE the message //return false; find_helper.result->set(*space, false); return true; } else { break; } } find_helper.found = true; if( space->is_table() ) { // CreateMsg(L"you cannot iterate through space table when the space is added without a wrapper"); // IMPROVE the message // return false; find_helper.result->set(*space, false); return true; } else if( !IsTestingFunctionExistence() ) { //PrintLastSpaceField(*space, find_helper); find_helper.result->set(*space, false); if( find_helper.is_last_field() ) // !! find_index was incremented and here find_index is greater than fields.size(), this returns false DumpSpaceIfNeeded(*space, find_helper); } return true; } #ifdef EZC_HAS_MORM_LIBRARY template bool Generator::CallModelField( morm::Model & model, FindHelper & find_helper) { /* * if 'field' is a POD type then 'str' will be used in get_raw_value() * if 'field' is a getter method with pt::Stream then 'str' will be used too * if 'field' is a getter method which takes Env<> then out_stream will be used and 'str' will be empty * */ //pt::WTextStream str; bool found = false; Env info(*find_helper.result, find_helper.parameters, *find_helper.input, stack_tab[stack_index-1], *stack_tab[stack_index-1].item); found = model.get_raw_value(nullptr, find_helper.current_name().c_str(), nullptr, info); // if( found && !str.empty()) // { // bool no_escape = HasParam(parameters, L"raw", L"noescape"); // CopyStream(str, result.stream, !no_escape); // result.type = Var::TYPE_STREAM; // } return found; } /* template bool Generator::CallModel( morm::Model & model, const std::wstring & name, std::vector & fields, size_t field_index, std::vector> & parameters, Var & result) { bool found = true; if( find_helper.field_index == fields.size() ) { // all fields items are models or models containers if( !IsTestingFunctionExistence() ) { DumpModelIfNeeded(model, result, parameters); } } else if( find_helper.field_index + 1 == fields.size() ) { // last field is not a model nor a models container if( !CallModelField(model, fields[find_helper.field_index], result, parameters, in_stream) ) { if( !IsTestingFunctionExistence() ) { CreateMsg(L"cannot find ", *find_helper.fun_name, fields, L", unknown property ", fields[find_helper.field_index].c_str(), L" of a model object"); } found = false; } } else { if( !IsTestingFunctionExistence() ) { CreateMsg(L"cannot find ", *find_helper.fun_name, fields, L", ", fields[find_helper.field_index].c_str(), L" is not a model nor a model container nor a space"); } found = false; } return found; } */ /* template void Generator::FindLastModelWrapper( Var & var_parent, std::vector & fields, size_t & field_index) { Var * var = &var_parent; for( ; field_index < fields.size() && var->has_model_object() ; ++field_index) { std::wstring & field = fields[field_index]; Var * child_var = var->find_child(field); if( !child_var ) { morm::Model * model = nullptr; if( var->model ) { model = var->model; } else if( var->model_container_wrapper ) { model = var->model_container_wrapper->get_model(); // this can return null for lists/vectors in a case when the iterator is not pointing to a valid item } if( model ) { // IMPROVE ME there'll be only one method to call a model morm::Wrapper new_wrapper = model->get_wrapper(nullptr, field.c_str()); if( new_wrapper.has_object() ) { Var child; if( new_wrapper.model ) child.set(*new_wrapper.model); if( new_wrapper.model_container_wrapper ) child.set(*new_wrapper.model_container_wrapper); if( new_wrapper.date ) child.set(*new_wrapper.date); if( new_wrapper.space_wrapper ) child.set(*new_wrapper.space_wrapper); child_var = var->add_child(field, new_wrapper); } ////////////////////////////////////////////////////////////////////// } } if( !child_var ) break; var = child_var; } } */ /* template bool Generator::CallWrapper( FindHelperOld & find_helper, std::vector & fields, Var & result, std::vector> & parameters, const StreamType & in_stream) { bool found = true; //last_res = false; FindLastModelWrapper(find_helper, fields); // if: // helper.field_index == fields.size() - all fields items are either models, models containers, space or a date // helper.field_index == fields.size()-1 - only the last field is not known // helper.field_index < fields.size()-1 - there are more not known fields bool all_fields_known = (find_helper.field_index == fields.size()); bool last_field_not_known = (find_helper.field_index + 1 == fields.size()); // if( find_helper.wrapper->space_wrapper ) // { // found = CallSpace(find_helper, fields, result, parameters); // } if( find_helper.wrapper->date ) { found = CallDate(find_helper, fields, result, parameters); } if( find_helper.wrapper->model ) { found = CallModel(*find_helper.wrapper->model, find_helper, fields, result, parameters, in_stream); } if( find_helper.wrapper->model_container_wrapper ) { morm::Model * model = find_helper.wrapper->model_container_wrapper->get_model(); if( model ) { found = CallModel(*model, find_helper, fields, result, parameters, in_stream); } else { if( last_field_not_known ) { // can be printed even for [if-def...] CreateMsg(L"model ", *find_helper.fun_name, fields, L" is not initialized, have you forgotten to use [for ...] statement?"); found = false; } } if( all_fields_known ) { if( is_generating_for ) { find_helper.wrapper->model_container_wrapper->increment_iterator(); find_helper.wrapper->clear_childs(); //last_res = find_helper.wrapper->model_container_wrapper->is_iterator_correct(); } else { // for [if-def...] last_res will be set later //last_res = !find_helper.wrapper->model_container_wrapper->is_container_empty(); } } } return found; } */ #endif template bool Generator::CallBlock(Item & item_block, Var & result, std::vector> & parameters) { if( block_stack_index >= block_stack_tab.size() ) { CreateMsg(L"Generator exceeded allowed number of blocks"); return false; } if( !IsTestingFunctionExistence() ) { 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); CopyStream(*output_stream, result.stream); result.type = Var::TYPE_STREAM; // last_res is set by [return ...] statement or other last evaluated function // FIXME what about result now? ClearStream(*output_stream); output_stream = old_stream; block_stack_index -= 1; } return true; } //template //bool Generator::CallVariable(FindHelper & find_helper) // //const std::wstring & name, std::vector & fields, size_t & field_index, Var & result, std::vector> & parameters) //{ // Var * variable = FindVariable(find_helper); // bool status = false; // // if( variable ) // { // // here we cannot pass 'name' but the last field instead? // status = EvaluateVariable(name, *variable, fields, field_index, parameters, result); // } // // return status; //} template Var * Generator::FindInScope(const std::wstring & name) { // here we should look in scopes (not implemented yet) if( pvars ) { return pvars->find(name); } return nullptr; } template void Generator::FindVariable(FindHelper & find_helper) { find_helper.field_index = 0; find_helper.previous_result = nullptr; find_helper.previous_var = nullptr; find_helper.previous_name = nullptr; find_helper.nested_calls = 0; Var * origin_result = find_helper.result; std::vector> results; results.resize(find_helper.fields.size() + 1); origin_result->clear(); do { find_helper.result = find_helper.is_last_field() ? origin_result : &results[find_helper.field_index]; EvaluateVariable(find_helper); if( find_helper.found ) { if( find_helper.result->type != Var::TYPE_MODEL ) // TYPE_SPACE here too? { find_helper.input = find_helper.result; } if( find_helper.field_index == 0 || find_helper.field_index - 1 < find_helper.fields.size() ) find_helper.previous_name = &find_helper.current_name(); else find_helper.previous_name = nullptr; find_helper.field_index += 1; // for space if( find_helper.field_index >= find_helper.fields.size() + 1 && find_helper.result != origin_result ) { *origin_result = *find_helper.result; } } else { if( find_helper.is_last_field() ) // find_index was incremented and is greater than fields.size(), this returns false { find_helper.result->clear(); } } } while( find_helper.found && find_helper.field_index < find_helper.fields.size() + 1 ); } template void Generator::EvaluateVariable(FindHelper & find_helper) { bool was_child_found = false; find_helper.found = false; find_helper.current_var = nullptr; const std::wstring & name = find_helper.current_name(); if( find_helper.previous_var ) { find_helper.current_var = find_helper.previous_var->find_child(name); find_helper.found = was_child_found = (find_helper.current_var != nullptr); if( find_helper.current_var ) *find_helper.result = *find_helper.current_var; } if( !find_helper.found && find_helper.previous_name && find_helper.previous_result ) { if( find_helper.previous_result->type == Var::TYPE_MODEL ) { // let functions return false to immediately return // and find_helper can be either true or false if( !EvaluateModel(find_helper) ) return; // what about if EvaluateModel returns another model or a space? we need a loop here? } else if( find_helper.previous_result->type == Var::TYPE_SPACE ) { if( !CallSpace(*find_helper.previous_result->space, find_helper) ) return; } } // find_helper.field_index could have been changed by EvaluateModelField or CallSpace if( find_helper.field_index < find_helper.fields.size() + 1 ) // add 'has_current_field()' to FindHelper { const std::wstring & name = find_helper.current_name(); if( !find_helper.found ) { find_helper.result->clear(); // is this clear here needed? find_helper.current_var = FindInScope(name); find_helper.found = (find_helper.current_var != nullptr); if( find_helper.current_var ) *find_helper.result = *find_helper.current_var; if( find_helper.found ) { if( find_helper.current_var->type == Var::TYPE_FUNCTION && find_helper.current_var->user_function ) { EvaluateFunction(find_helper.current_var->user_function, find_helper); } else if( find_helper.current_var->type == Var::TYPE_DATE && find_helper.current_var->date ) { // move me below (where TYPE_MODEL_CONTAINER_WRAPPER is used) if( find_helper.is_last_field() ) PrintDate(*find_helper.current_var->date, find_helper); } } } if( find_helper.found ) { if( find_helper.result->type == Var::Type::TYPE_VOID && !find_helper.result->stream.empty() ) { find_helper.result->type = Var::Type::TYPE_STREAM; } if( find_helper.previous_var && !was_child_found ) { // may only model_container_wrapper and space_wrapper here? //find_helper.result->type == Var::TYPE_MODEL || if( find_helper.result->type == Var::TYPE_MODEL_CONTAINER_WRAPPER || find_helper.result->type == Var::TYPE_SPACE_WRAPPER || find_helper.result->type == Var::TYPE_FUNCTION ) { find_helper.previous_var->add_child(name, *find_helper.result); // this result_child probably should be set as find_helper.current_var } } if( find_helper.result->type == Var::TYPE_MODEL_CONTAINER_WRAPPER ) { if( !EvaluateModelWrapper(find_helper) ) return; } else if( find_helper.result->type == Var::TYPE_SPACE_WRAPPER ) { if( !CallSpaceWrapper(*find_helper.result->space_wrapper, find_helper) ) return; } find_helper.previous_var = find_helper.current_var; find_helper.previous_result = find_helper.result; } else { find_helper.previous_var = nullptr; find_helper.previous_result = nullptr; } } } template bool Generator::EvaluateModel(FindHelper & find_helper) { find_helper.found = CallModelField(*find_helper.previous_result->model, find_helper); if( find_helper.found ) { if( find_helper.result->type == Var::TYPE_FUNCTION && find_helper.result->user_function ) { EvaluateFunction(find_helper.result->user_function, find_helper); if( !find_helper.found ) return false; // maximum nested calls exceeded } } return true; } template bool Generator::EvaluateModelWrapper(FindHelper & find_helper) { if( find_helper.is_last_field() ) { if( is_generating_for ) { find_helper.result->model_container_wrapper->increment_iterator(); bool loop_status = find_helper.result->model_container_wrapper->is_iterator_correct(); find_helper.result->set(loop_status); // may create a second bool status only for loops? // find_helper.result was copied from currrent_var so it doesn't have childs here...(childs are not copied) if( find_helper.current_var ) find_helper.current_var->clear_childs(); // but it only clears child in the first level, is it correct? // childs from different vars will not be cleared } } else if( find_helper.is_next_field() ) { const std::wstring & next_field = find_helper.next_field(); // make a constant somewhere if( next_field == L"val" ) { find_helper.field_index += 1; morm::Model * model = find_helper.result->model_container_wrapper->get_model(); if( model ) { find_helper.result->set(*model); find_helper.found = true; } else { // put some log (no [for] statement) // CreateMsg(L"model ", name, fields, L" is not initialized, have you forgotten to use [for ...] statement?"); find_helper.result->clear(); find_helper.found = false; return false; } } } return true; } template void Generator::EvaluateFunction(typename Var::UserFunction user_function, FindHelper & find_helper) { bool call_again; do { call_again = false; CallFunction(user_function, find_helper); find_helper.found = true; if( find_helper.result->type == Var::TYPE_FUNCTION && find_helper.result->user_function ) { user_function = find_helper.result->user_function; find_helper.result->clear(); find_helper.nested_calls += 1; if( find_helper.nested_calls <= max_nested_calls ) { call_again = true; } else { CreateMsg(L"Generator exceeded max nested calls"); find_helper.found = false; } } } while( call_again ); } /* template bool Generator::CallVariable( std::vector & fields, size_t & field_index, Var & variable, Var & result, std::vector> & parameters, const StreamType & in_stream) { if( variable.type == Var::TYPE_FUNCTION ) { if( variable.user_function ) { CallFunction(variable.user_function, result, parameters, in_stream); return true; } else { // put some log that variable.user_function is null? return false; } } else if( variable.type == Var::TYPE_MODEL ) { if( variable.model ) { return XXXRenameMe(variable, fields, field_index, result, parameters, in_stream); } else { // put some log that model is null? return false; } } else { if( !IsTestingFunctionExistence() ) { result = variable; //variable.serialize_to(out_stream); //out_stream << variable.str; //last_res = variable.res; } return true; } } */ /* * fun_name can be null, it is used only with [let ...] statements * and if not null then means: as a funcion name we are not using item_fun.name but fun_name * * return: true if a function, variable or block was found and called (evaluated) */ template bool Generator::Call( Item::Function & item_fun, const std::wstring * fun_name, Var & result, StreamType & in_stream) { std::vector> parameters; // FIXME where to set this? // if constexpr(is_autoescape_stream) // out_stream.Escape(true); const std::wstring * name = (fun_name)? fun_name : &item_fun.name; Var * var = nullptr; if( pvars ) { var = pvars->find(*name); } if( !var ) { if( !IsTestingFunctionExistence() ) CreateMsg(name->c_str(), L"is not defined"); return false; } parameters.resize(item_fun.parameters.size()); for(size_t i=0 ; i empty_input; // fix me for filters empty_input.type = Var::TYPE_STREAM; empty_input.stream = in_stream; // IMPROVEME don't copy here streams, let Var::stream be a pointer? FindHelper find_helper(*name, item_fun.fields, parameters); find_helper.result = &result; find_helper.input = &empty_input; FindVariable(find_helper); return find_helper.found; } // return: true if a function or variable was found and called template bool Generator::Call(Item::Function & item_fun, Var & result) { //return Call(item_fun, nullptr, stream_temp1, true, result, empty_stream); empty_stream.clear(); return Call(item_fun, nullptr, result, 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_frames_streams ) { CopyStream(ezc_out_tmp_stream, previous_stream); for(size_t s=0 ; s < fun.parameters.size() ; ++s) { std::wstring & name = fun.parameters[s]->name; auto imap = output_frames_streams->streams_map.find(name); if( imap == output_frames_streams->streams_map.end() ) { if( output_frames_streams->streams_map.size() < output_frames_streams->streams_tab.size() ) { /* a new stream from the pool (output_stream_tab) is taken */ StreamType * stream = output_frames_streams->streams_tab[ output_frames_streams->streams_map.size() ]; output_frames_streams->streams_map.insert(std::make_pair(name, stream)); ClearStream(*stream); CopyStream(ezc_out_tmp_stream, *stream); } else { CreateMsg(L"limit of output streams in OutStreams<> has been reached"); } } else { StreamType * stream = imap->second; CopyStream(ezc_out_tmp_stream, *stream); } } } } template void Generator::CreateMsg(const wchar_t * type, const wchar_t * arg) { if( plog ) { (*plog) << pt::Log::log2 << "Ezc: " << type; if( arg ) { (*plog) << ' ' << arg; } (*plog) << pt::Log::logend; } } template void Generator::CreateMsg(const wchar_t * type, const std::wstring & model_name, std::vector & fields, size_t how_many_fields_print, const wchar_t * arg, const wchar_t * arg2, const wchar_t * arg3) { if( plog ) { (*plog) << pt::Log::log2 << L"Ezc: " << type << model_name; for(size_t i=0 ; i < how_many_fields_print && i < fields.size() ; ++i) { (*plog) << '.' << fields[i]; } if( arg ) { (*plog) << arg; } if( arg2 ) { (*plog) << arg2; } if( arg3 ) { (*plog) << arg3; } (*plog) << pt::Log::logend; } } template void Generator::CreateMsg(const wchar_t * type, const std::wstring & model_name, std::vector & fields, const wchar_t * arg, const wchar_t * arg2, const wchar_t * arg3) { CreateMsg(type, model_name, fields, fields.size(), arg, arg2, arg3); } 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::EvaluateProgramNode(Item & item) { if( output_stream ) { if( item.type == Item::item_function ) *output_stream << " expression: " << item.text; if( item.type == Item::item_if ) *output_stream << " if: " << item.text; if( item.type == Item::item_for ) *output_stream << " for: " << item.text; } //last_res = false; if( expression_parser ) { if( expression_parser->Parse(item.text) ) { //last_res = expression_parser->LastResultToBool(); if( output_stream ) { *output_stream << " -> " << expression_parser->LastResult(); } } else { if( output_stream ) { *output_stream << " -> syntax error when evaluating expression: error code: " << (int)expression_parser->LastError(); } } } if( output_stream ) { *output_stream << "\n"; } } 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::MakeTextFunction(Item & item) { is_generating_normal = true; if( program_mode ) { EvaluateProgramNode(item); } else { Var result; empty_stream.clear(); if( Call(item.function, nullptr, result, empty_stream) ) { if( output_stream ) { if( result.type == Var::TYPE_MODEL_CONTAINER_WRAPPER ) { // IMPROVEME add model_container serialized as a table // add a method 'serialize_to' to ModelContainerWrapper } else if( result.type == Var::TYPE_MODEL ) { // IMPROVEME get rid of the tmp object pt::TextStream str_tmp; result.model->to_text(str_tmp); *output_stream << str_tmp; // what about escaping here? } else { result.serialize_to(*output_stream); } } } /* if( output_stream ) { //Call(item.function, nullptr, *output_stream, false, result, empty_stream); Call(item.function, nullptr, result, empty_stream); result.serialize_to(*output_stream); } else { //Call(item.function, nullptr, stream_temp1, false, result, empty_stream); Call(item.function, nullptr, result, 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; Var result; if( program_mode ) { EvaluateProgramNode(item); } else { if( !Call(item.function, result) ) return; } MakeTextIf_go(item, result.to_bool()); } template void Generator::MakeTextIfDef(Item & item) { is_generating_if_def = true; Var result; if( program_mode ) { //CHECKME //EvaluateProgramNode(item); } else { Call(item.function, result); } MakeTextIf_go(item, result.to_bool()); } template void Generator::MakeTextIfNotDef(Item & item) { is_generating_if_not_def = true; Var result; if( program_mode ) { //CHECKME //EvaluateProgramNode(item); } else { Call(item.function, result); } MakeTextIf_go(item, !result.to_bool()); } template void Generator::MakeTextFor(Item & item) { stack_tab[stack_index-1].is_for = true; Var result; 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; result.clear(); if( program_mode ) { EvaluateProgramNode(item); } else { //Call(item.function, nullptr, stream_temp1, true, result, empty_stream); empty_stream.clear(); Call(item.function, nullptr, result, empty_stream); } if( !result.to_bool() ) break; // if( result.type == Var::Type::TYPE_MODEL_CONTAINER_WRAPPER ) // { // if( !result.model_container_wrapper->is_iterator_correct() ) // break; // } // else // if( result.type == Var::Type::TYPE_SPACE_WRAPPER ) // { // if( !result.to_bool() ) // break; // } // else // { // break; // } if( !item.item_tab.empty() ) MakeText( *item.item_tab[0] ); // should be only one item } } template void Generator::MakeTextDefine(Item & item, Var & var) { // var.str.clear(); // var.res = ConvertToBool(var.str); // var.is_function = false; var.clear(); if( item.function.parameters.empty() ) { return; } if( item.function.parameters.size() > 1 ) { CreateMsg(L"[def] can have only one parameter"); return; } Item::Function & fun = *item.function.parameters[0]; if( fun.is_function ) { // call function //if( Call(fun, nullptr, stream_temp_define, true, var, empty_stream) ) empty_stream.clear(); if( Call(fun, nullptr, var, empty_stream) ) { //CopyStreamToString(stream_temp_define, var.str); //var.res = last_res; } else { CreateMsg(L"[def] unknown function/block/variable", item.function.name); } } else { // var.str = fun.name; // var.res = ConvertToBool(fun.name); var.set(fun.name); } } template void Generator::MakeTextDefine(Item & item) { return; /* if( !can_use_vars || !pvars ) { CreateMsg(L"[def] statement not available"); return; } Var & var = (*pvars)[item.function.name]; MakeTextDefine(item, var); */ } template void Generator::MakeTextDefineIfNotSet(Item & item) { return; /* if( !can_use_vars || !pvars ) { CreateMsg(L"[def?] statement not available"); return; } Vars::iterator vi = pvars->find(item.function.name); if( vi == pvars->end() ) { Var & var = (*pvars)[item.function.name]; MakeTextDefine(item, var); } */ } template void Generator::MakeTextLet(Item & item, Var & var) { // var.str.clear(); // var.res = ConvertToBool(var.str); // var.is_function = true; if( item.function.parameters.empty() ) { //var.is_function = false; return; } if( item.function.parameters.size() > 1 ) { CreateMsg(L"[let] can have only one parameter"); return; } Item::Function & fun = *item.function.parameters[0]; if( fun.is_function ) var.set_function(fun.name); else var.set(fun.name); // var.str = fun.name; // var.is_function = fun.is_function; // if( !fun.is_function ) // { // var.res = ConvertToBool(var.str); // } } template void Generator::MakeTextLet(Item & item) { return; /* if( !can_use_vars || !pvars ) { CreateMsg(L"[let] statement not available"); return; } Var & var = (*pvars)[item.function.name]; MakeTextLet(item, var); */ } template void Generator::MakeTextLetIfNotSet(Item & item) { return; /* if( !can_use_vars || !pvars ) { CreateMsg(L"[let?] statement not available"); return; } Vars::iterator vi = pvars->find(item.function.name); if( vi == pvars->end() ) { Var & var = (*pvars)[item.function.name]; MakeTextLet(item, var); } */ } template void Generator::MakeTextFilter(Item & item) { if( filter_index >= filter_tab.size() ) { CreateMsg(L"Generator exceeded allowed number of filters"); return; } Var result; 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, nullptr, *old_stream, false, result, *output_stream); Call(item.function, nullptr, result, *output_stream); result.serialize_to(*old_stream); } else { // Call(item.function, nullptr, stream_temp1, true, result, *output_stream); // ClearStream(stream_temp1); Call(item.function, nullptr, result, *output_stream); } ClearStream(*output_stream); output_stream = old_stream; filter_index -= 1; } /* although we are using a stack for [etc frame] 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 frame] statements can product a better output */ template void Generator::MakeEzcFrame(Item & item) { std::vector output_stream_names; StreamType * old_stream; bool stream_added = true; if( ezc_frames_stack_index >= ezc_frame_stack_tab.size() ) { CreateMsg(L"Generator exceeded allowed number of [ezc frame] statements"); return; } /* if we encounter the first ezc frame statement without arguments e.g. [ezc frame] or just [frame] then we can simply ignore it */ if( item.function.parameters.empty() && ezc_frames_stack_index == 0 ) stream_added = false; if( stream_added ) { old_stream = output_stream; output_stream = ezc_frame_stack_tab[ezc_frames_stack_index]; ClearStream(*output_stream); ezc_frames_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_frames_stack_index -= 1; } } template void Generator::MakeTextEzc(Item & item) { if( item.function.name == L"frame" ) { MakeEzcFrame(item); } else if( item.function.name == L"clear_all_white_nodes" || item.function.name == L"trim_text_nodes" ) { // text nodes are already cleared/trimmed by PatternParser MakeTextContainer(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 ) { Var result; // 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, nullptr, stream_temp1, false, result, empty_stream); //ClearStream(stream_temp1); empty_stream.clear(); Call(item.function, nullptr, result, empty_stream); } } 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_if_def = false; is_generating_if_not_def = 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 ) MakeTextFunction(item); else if( item.type == Item::item_if ) MakeTextIf(item); else if( item.type == Item::item_if_def ) MakeTextIfDef(item); else if( item.type == Item::item_if_not_def ) MakeTextIfNotDef(item); else if( item.type == Item::item_def ) MakeTextDefine(item); else if( item.type == Item::item_def_if_not_set ) MakeTextDefineIfNotSet(item); else if( item.type == Item::item_let ) MakeTextLet(item); else if( item.type == Item::item_let_if_not_set ) MakeTextLetIfNotSet(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