/* * This file is a part of EZC -- Easy templating in C++ library * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2007-2024, 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: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. 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. * * 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 HOLDER 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 "blocks.h" #include "pattern.h" #include "functions.h" #include "objects.h" #include "outstreams.h" #include "expressionparser.h" #include "models.h" #include "log/log.h" #ifdef EZC_HAS_MORM_LIBRARY #include "model.h" #endif namespace Ezc { 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 SetVariables(Vals & variables); // [def] and [let] 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 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); void SetProgramMode(bool program_mode); void SetExpressionParser(ExpressionParser * expression_parser); // some optimization when generating the output // if provided frames then only such frames will be generated // call OnlyFrames(nullptr) to disable it (default) void OnlyFrames(const std::vector & frames); void OnlyFrames(const std::vector * frames); // some optimization when generating the output // whether or not the main stream should be produced (true by default) void UseMainStream(bool use_main_stream); // the main methods for generating void Generate(pt::Stream & out); void Generate(pt::Stream & out, OutStreams & out_streams); //void Generate(OutStreams & out_streams); private: struct FindHelper { FindHelper( Item::Function & item_fun, const std::wstring & fun_name, std::vector & fields, Val & baseval, std::vector & parameters, Val & result, pt::Stream & out_stream, const pt::Stream & in_stream) : item_fun(item_fun), fun_name(fun_name), fields(fields), baseval(&baseval), currentval(&baseval), parameters(parameters), result(result), out_stream(out_stream), in_stream(in_stream) { #ifdef EZC_HAS_MORM_LIBRARY field_index = 0; #endif } FindHelper(const FindHelper &) = delete; // RENAMEME void set_base_val(Val * val) { baseval = val; currentval = val; } void set_base_val(Val & val) { set_base_val(&val); } Item::Function & item_fun; const std::wstring & fun_name; std::vector & fields; Val * baseval; Val * currentval; std::vector & parameters; Val & result; pt::Stream & out_stream; const pt::Stream & in_stream; // if: // field_index == size() - all fields items are either models, models containers, space or a date // field_index == size()-1 - only the last field is not known // field_index < size()-1 - there are more not known fields bool all_fields_known() { return (field_index == fields.size()); } bool last_field_not_known() { return (field_index + 1 == fields.size()); } //std::wstring * fun_name; //BaseObj * base_obj; //int method_index; //typename Functions::UserFunction * function; //Item * item_block; //Val * variable; #ifdef EZC_HAS_MORM_LIBRARY //morm::Wrapper * wrapper; size_t field_index; #endif // FindHelper() // { //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 //} }; struct BlockStack { std::vector args; pt::Stream * 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 pt::Stream argument passsed to Generate method) pt::Stream * output_stream; pt::Stream * main_stream; // it is pointing to the main stream (to the pt::Stream argument passsed to Generate method) Pattern * ppattern; Blocks * pblocks; Functions * pfunctions; Objects * pobjects; #ifdef EZC_HAS_MORM_LIBRARY Models * pmodels; #endif Vals * pvals; 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; const pt::Stream * empty_stream; // temporary streams used in [if..] [for...] or [def ...] // or if output_stream is null and an ezc function should be called pt::Stream * 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_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 can_find_in_cache; bool program_mode; const std::vector * only_frames; bool is_output_stream_allowed; bool use_main_stream; ExpressionParser * expression_parser; void InitializeTmpStreams(); 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 ClearTmpStreams(); void ClearStream(pt::Stream & str); void CopyStreamToString(pt::Stream & src_stream, std::wstring & dst_string); void CopyStream(pt::Stream & src_stream, pt::Stream & dst_stream); void CopyStream(pt::WTextStream & src_stream, pt::Stream & dst_stream, bool should_escape); void RemoveStackFunData(Stack & sitem); bool ConvertToBool(const std::wstring & str); template CharType ToLower(CharType c); bool IsTestingFunctionExistence(); bool CheckBlockArgument(FindHelper & find_helper); bool FindInCache(Item::Function & item_fun, FindHelper & find_helper); #ifdef EZC_HAS_MORM_LIBRARY bool FindInModels(FindHelper & find_helper); #endif bool FindInFunctions(FindHelper & find_helper); bool FindInBlocks(FindHelper & find_helper); bool FindInVariables(FindHelper & find_helper); bool Find(FindHelper & find_helper); void PrepareEnvStruct(Env & env); void CallFunction(typename Functions::UserFunction & function, Env & env); void CallFunction(FindHelper & find_helper); bool CallBlock(FindHelper & find_helper); bool CallPodType(FindHelper & find_helper); void CallObject(BaseObj & base_obj, int method_index, Env & info); void PrintDate(FindHelper & find_helper, pt::Date * date); bool PrintDatePart(FindHelper & find_helper, pt::Date * date); bool CallDate(FindHelper & find_helper); void PrintLastSpaceField(FindHelper & find_helper, pt::Space * space); void CallSpaceObjectForLastField(FindHelper & find_helper, pt::Space * space); pt::Space * CallSpaceObjectForMiddleField(FindHelper & find_helper, pt::Space * space); void CallSpaceTableForLastField(FindHelper & find_helper, morm::SpaceWrapper & space_wrapper, pt::Space * space, size_t model_wrapper_space_table_index); pt::Space * CallSpaceTableForMiddleField(FindHelper & find_helper, morm::SpaceWrapper & space_wrapper, pt::Space * space, size_t model_wrapper_space_table_index); bool CallSpace(FindHelper & find_helper); #ifdef EZC_HAS_MORM_LIBRARY bool CallModelField(FindHelper & find_helper, morm::Model & model); bool CallModel(FindHelper & find_helper, morm::Model & model); void FindLastModelWrapper(FindHelper & find_helper); bool CallModel(FindHelper & find_helper); bool CallModelContainerWrapper(FindHelper & find_helper); #endif void CallObject(BaseObj & base_obj, int method_index, std::vector & parameters, Val & result, pt::Stream & out_stream, const pt::Stream & in_stream); // bool CallVariable(Item::Function & item_fun, // Val & variable, // std::vector & parameters, // Val & result, // pt::Stream & out_stream, // const pt::Stream & in_stream); void EvaluateParameters(FindHelper & find_helper); bool ReduceFields(FindHelper & find_helper); bool CallValue(FindHelper & find_helper); void CleanupParameters(std::vector & parameters); bool Call(Item::Function & item_fun, std::wstring * fun_name, Val & result, pt::Stream & out_stream, const pt::Stream & in_stream); 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(FindHelper & find_helper, pt::Space * space); void DumpModelIfNeeded(FindHelper & find_helper, morm::Model & model); pt::Stream * FindAddOutputStream(const std::wstring & name); void CopyTmpStreamToOutputStreams(Item::Function & fun, pt::Stream & ezc_out_tmp_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); bool HasAskForSuchFrame(Item::Function & fun); bool HasAskForSuchFrame(const std::wstring & name); pt::Stream * GetNewStreamForFrame(); void DecrementFramesStackIndex(); 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, Val & val); void MakeTextDefine(Item & item); void MakeTextDefineIfNotSet(Item & item); void MakeTextLet(Item & item, Val & val); 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 MakeEzcFrame2(Item & item); void MakeEzcFrame(Item & item); void Generate(); }; // class Generator Generator::Generator() : empty_stream() { ppattern = nullptr; pblocks = nullptr; pfunctions = nullptr; pobjects = nullptr; pvals = 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_find_in_cache = true; can_use_vars = true; expression_parser = nullptr; program_mode = false; only_frames = nullptr; is_output_stream_allowed = true; use_main_stream = true; } Generator::Generator(const Generator & n) { operator=(n); } Generator & Generator::operator=(const Generator & n) { ppattern = n.ppattern; pblocks = n.pblocks; pfunctions = n.pfunctions; pobjects = n.pobjects; pvals = n.pvals; 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_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_frames_stack_size = n.ezc_frames_stack_size; expression_parser= n.expression_parser; program_mode = n.program_mode; only_frames = n.only_frames; is_output_stream_allowed = n.is_output_stream_allowed; use_main_stream = n.use_main_stream; // 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 // don't copy tmp 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; } Generator::~Generator() { ClearFilterTab(); ClearForStack(); ClearBlockStack(); ClearEzcFrameTab(); } void Generator::SetPattern(Pattern & pattern) { ppattern = &pattern; } void Generator::SetBlocks(Blocks & blocks) { pblocks = &blocks; } void Generator::SetFunctions(Functions & functions) { pfunctions = &functions; } void Generator::SetObjects(Objects & objects) { pobjects = &objects; } void Generator::SetVariables(Vals & variables) { pvals = &variables; } void Generator::SetLogger(pt::Log & logger) { plog = &logger; } #ifdef EZC_HAS_MORM_LIBRARY void Generator::SetModels(Models & models) { pmodels = ⊧ } #endif void Generator::CanUseCache(bool can_use_cache) { can_find_in_cache = can_use_cache; } void Generator::CanUseVars(bool can_use_variables) { can_use_vars = can_use_variables; } void Generator::SetProgramMode(bool set_program_mode) { this->program_mode = set_program_mode; } void Generator::SetExpressionParser(ExpressionParser * expression_parser) { this->expression_parser = expression_parser; } void Generator::OnlyFrames(const std::vector & frames) { this->only_frames = &frames; } void Generator::OnlyFrames(const std::vector * frames) { this->only_frames = frames; } void Generator::UseMainStream(bool use_main_stream) { this->use_main_stream = use_main_stream; } void Generator::ResizeStack() { if( stack_tab.size() != stack_size ) { if( stack_tab.size() > stack_size ) { for(size_t i=stack_size ; i & 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( ; inew_empty(); } else { for(size_t i=stream_tab_max_size ; inew_empty(); } else { for(size_t i=block_stack_size ; i & stream_tab) { for(size_t i=0 ; i CharType Generator::ToLower(CharType c) { if( c>='A' && c<='Z' ) return c - 'A' + 'a'; return c; } bool Generator::ConvertToBool(const std::wstring & str) { return !str.empty(); } void Generator::SetMax(size_t max_items_, size_t max_for_items_) { max_items = max_items_; max_for_items = max_for_items_; } void Generator::RecognizeSpecialChars(bool spec) { special_chars = spec; } void Generator::TrimWhite(bool trim) { trim_white = trim; } void Generator::SkipNewLine(bool skip) { skip_new_line = skip; } void Generator::SetMaxFilters(size_t new_len) { // the table will be resized when Generate() method is called filter_size = new_len; } void Generator::SetStackSize(size_t new_stack_size) { // the stack will be resized when Generate() method is called stack_size = new_stack_size; } 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(); InitializeTmpStreams(); filter_index = 0; stack_index = 0; ezc_frames_stack_index = 0; is_output_stream_allowed = use_main_stream; 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; } } } void Generator::Generate(pt::Stream & out) { main_stream = &out; output_stream = &out; output_frames_streams = nullptr; Generate(); } void Generator::Generate(pt::Stream & out, OutStreams & out_streams) { main_stream = &out; output_stream = &out; output_frames_streams = &out_streams; Generate(); } void Generator::InitializeTmpStreams() { empty_stream = main_stream->new_empty(); stream_temp1 = main_stream->new_empty(); stream_temp_define = main_stream->new_empty(); } // void Generator::Generate(OutStreams & out_streams) // { // output_stream = nullptr; // output_frames_streams = &out_streams; // Generate(); // } bool Generator::IsTestingFunctionExistence() { return is_generating_if_def || is_generating_if_not_def; } bool Generator::CheckBlockArgument(FindHelper & find_helper) { if( find_helper.item_fun.arg < 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(find_helper.item_fun.arg) < block_stack.args.size() ) { find_helper.set_base_val(block_stack.args[find_helper.item_fun.arg]); return true; } } return false; } bool Generator::FindInCache(Item::Function & item_fun, FindHelper & find_helper) { /* if( can_find_in_cache ) { if( item_fun.base_obj ) { find_helper.base_obj = reinterpret_cast(item_fun.base_obj); find_helper.method_index = item_fun.method_index; return true; } if( item_fun.fun_cache ) { find_helper.function = reinterpret_cast(item_fun.fun_cache); return true; } if( item_fun.item_block ) { find_helper.item_block = item_fun.item_block; return true; } } */ return false; } #ifdef EZC_HAS_MORM_LIBRARY bool Generator::FindInModels(FindHelper & find_helper) { bool found = false; if( pmodels ) { Val * val = pmodels->Find(find_helper.fun_name); if( val ) { find_helper.set_base_val(val); found = true; } } return found; } #endif // IMPROVEME split me into two functions bool Generator::FindInFunctions(FindHelper & find_helper) { if( pfunctions ) { typename Functions::Iterator i = pfunctions->Find(find_helper.fun_name); if( i != pfunctions->End() ) { find_helper.baseval->set(i->second); return true; } } return false; } bool Generator::FindInBlocks(FindHelper & find_helper) { if( pblocks ) { Blocks::Iterator i = pblocks->Find(find_helper.fun_name); if( i != pblocks->End() ) { find_helper.baseval->set_pointer_to(&i->second); return true; } } return false; } bool Generator::FindInVariables(FindHelper & find_helper) { if( pvals ) { Vals::iterator i = pvals->find(find_helper.fun_name); if( i != pvals->end() ) { find_helper.set_base_val(i->second); return true; } } return false; } /* * 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 */ // give me a better name, FindWhat.... //bool Generator::Find(Item::Function & item_fun, FindHelper & find_helper) bool Generator::Find(FindHelper & find_helper) { if( CheckBlockArgument(find_helper) ) return true; #ifdef EZC_HAS_MORM_LIBRARY if( FindInModels(find_helper) ) return true; if( !find_helper.item_fun.fields.empty() && !IsTestingFunctionExistence() ) { CreateMsg(L"unknown model", find_helper.fun_name.c_str()); return false; } #endif // Cache will be removed in the future // if( FindInCache(item_fun, find_helper) ) // return true; if( FindInFunctions(find_helper) ) return true; if( FindInBlocks(find_helper) ) return true; if( FindInVariables(find_helper) ) return true; if( !IsTestingFunctionExistence() ) CreateMsg(L"unknown function", find_helper.fun_name.c_str()); return false; } void Generator::PrepareEnvStruct(Env & env) { env.Clear(); env.is_for = is_generating_for; env.is_if = is_generating_if; env.is_if_def = is_generating_if_def; env.is_if_not_def = is_generating_if_not_def; env.is_normal = is_generating_normal; env.is_filter = is_generating_filter; env.iter = env.stack.iter; env.stack_tab = stack_tab.data(); env.stack_index = stack_index-1; } void Generator::CallFunction(typename Functions::UserFunction & function, Env & env) { PrepareEnvStruct(env); (function)(env); //last_res = env.res; } void Generator::CallFunction(FindHelper & find_helper) { if( !IsTestingFunctionExistence() ) { if( find_helper.baseval->type == Val::TYPE_FUNCTION && find_helper.baseval->pointer ) { using UserFunction = void (*)(Env &); UserFunction user_function = reinterpret_cast(find_helper.baseval->pointer); Env env(find_helper.out_stream, find_helper.parameters, find_helper.result, find_helper.in_stream, stack_tab[stack_index-1], *stack_tab[stack_index-1].item); CallFunction(user_function, env); } } } void Generator::CallObject(BaseObj & base_obj, int method_index, Env & info) { PrepareEnvStruct(info); base_obj.CallFun(method_index, info); //last_res = info.res; } bool Generator::HasParam(std::vector & parameters, const wchar_t * param1, const wchar_t * param2) { for(Val & val : parameters) { if( val.is_equal_string(param1) || (param2 && val.is_equal_string(param2)) ) { return true; } } return false; } bool Generator::ShouldMakeSpaceDump(std::vector & parameters) { return HasParam(parameters, L"dump", L"dump_to_space"); } bool Generator::ShouldMakeJsonDump(std::vector & parameters) { return HasParam(parameters, L"dump_to_json"); } bool Generator::IsPrettyPrint(std::vector & parameters) { return HasParam(parameters, L"pretty"); } void Generator::DumpSpaceIfNeeded(FindHelper & find_helper, pt::Space * space) { 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.out_stream, pretty); } else if( dump_json ) { space->serialize_to_json_stream(find_helper.out_stream, pretty); } } } void Generator::DumpModelIfNeeded(FindHelper & find_helper, morm::Model & model) { bool dump_space = ShouldMakeSpaceDump(find_helper.parameters); bool dump_json = ShouldMakeJsonDump(find_helper.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); // find_helper.out_stream << temp_str; model.to_text(find_helper.out_stream, false, false); } } } void Generator::PrintDate(FindHelper & find_helper, pt::Date * date) { 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 ) { date->SerializeYearMonthDay(find_helper.out_stream, is_roman); } else if( only_time ) { if( is_no_sec ) date->SerializeHourMin(find_helper.out_stream); else date->SerializeHourMinSec(find_helper.out_stream); } else { date->Serialize(find_helper.out_stream, is_roman, !is_no_sec); } } bool Generator::PrintDatePart(FindHelper & find_helper, pt::Date * date) { bool is_test = IsTestingFunctionExistence(); std::wstring & field = find_helper.fields[find_helper.field_index]; if( field == L"year" ) { if( !is_test ) find_helper.out_stream << date->year; } else if( field == L"month" ) { if( !is_test ) { bool is_roman = HasParam(find_helper.parameters, L"roman"); if( is_roman ) pt::Date::SerializeMonthAsRoman(find_helper.out_stream, date->month); else find_helper.out_stream << date->month; } } else if( field == L"day" ) { if( !is_test ) find_helper.out_stream << date->day; } else if( field == L"hour" ) { if( !is_test ) find_helper.out_stream << date->hour; } else if( field == L"min" ) { if( !is_test ) find_helper.out_stream << date->min; } else if( field == L"sec" ) { if( !is_test ) find_helper.out_stream << date->sec; } else { return false; } return true; } bool Generator::CallDate(FindHelper & find_helper) { bool found = true; bool all_fields_known = (find_helper.field_index == find_helper.fields.size()); bool last_field_not_known = (find_helper.field_index + 1 == find_helper.fields.size()); pt::Date * date = nullptr; if( find_helper.currentval->type == Val::TYPE_DATE && find_helper.currentval->pointer ) { date = reinterpret_cast(find_helper.currentval->pointer); } if( all_fields_known ) { if( date ) { PrintDate(find_helper, date); } } else if( last_field_not_known ) { if( date ) { if( !PrintDatePart(find_helper, date) ) { if( !IsTestingFunctionExistence() ) { CreateMsg(L"cannot find ", find_helper.fun_name, find_helper.fields, L", unknown property ", find_helper.fields[find_helper.field_index].c_str(), L" of a Date object"); } found = false; } } } else { if( !IsTestingFunctionExistence() ) { CreateMsg(L"cannot find ", find_helper.fun_name, find_helper.fields, L", ", find_helper.fields[find_helper.field_index].c_str(), L" is not a model nor a model container nor a space"); } found = false; } return found; } void Generator::CallSpaceObjectForLastField(FindHelper & find_helper, pt::Space * space) { if( IsTestingFunctionExistence() ) { find_helper.result.set(true); //last_res = true; } else { DumpSpaceIfNeeded(find_helper, space); //last_res = space->object_size() > 0; } } pt::Space * Generator::CallSpaceObjectForMiddleField(FindHelper & find_helper, pt::Space * space) { std::wstring & next_field = find_helper.fields[find_helper.field_index]; space = space->get_space(next_field); if( !space && !IsTestingFunctionExistence() ) { CreateMsg(L"cannot find a Space field: ", find_helper.fun_name, find_helper.fields, find_helper.field_index + 1); } return space; } void Generator::CallSpaceTableForLastField( FindHelper & find_helper, morm::SpaceWrapper & space_wrapper, pt::Space * space, 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); //last_res = (iterator_value < table->size()); } else { // we are not in [for..], it can be for example [if...], return true if the table is not empty if( IsTestingFunctionExistence() ) { find_helper.result.set(true); //last_res = true; } else { //last_res = !table->empty(); } } if( !IsTestingFunctionExistence() ) { DumpSpaceIfNeeded(find_helper, space); if( HasParam(find_helper.parameters, L"index") ) { find_helper.out_stream << space_wrapper.get_space_iterator_value(model_wrapper_space_table_index); } if( HasParam(find_helper.parameters, L"index-one") ) { find_helper.out_stream << (space_wrapper.get_space_iterator_value(model_wrapper_space_table_index) + 1); } } } pt::Space * Generator::CallSpaceTableForMiddleField(FindHelper & find_helper, morm::SpaceWrapper & space_wrapper, pt::Space * space, size_t model_wrapper_space_table_index) { pt::Space::TableType * table = space->get_table(); // 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: ", find_helper.fun_name, find_helper.fields, find_helper.field_index, L" is not initialized, have you forgotten to use [for...] statement?"); space = nullptr; } return space; } void Generator::PrintLastSpaceField(FindHelper & find_helper, pt::Space * space) { bool no_escape = HasParam(find_helper.parameters, L"raw", L"noescape"); if( no_escape ) { pt::WTextStream str; space->serialize_to_string(str); CopyStream(str, find_helper.out_stream, false); } else { space->serialize_to_string(find_helper.out_stream); } } bool Generator::CallSpace(FindHelper & find_helper) { morm::SpaceWrapper * space_wrapper = reinterpret_cast(find_helper.currentval->pointer); pt::Space * space = space_wrapper->get_space(); //last_res = false; size_t field_index = find_helper.field_index; size_t model_wrapper_space_table_index = 0; /* * * we are iterating through such objects: * [root_space, fields[field_index], fields[field_index+1], fields[field_index+2], fields[field_index+3] ...] * the first item is the root_space with a find_helper.fun_name name * */ while( (field_index < find_helper.fields.size() + 1) && space ) { bool is_last_field = (field_index == find_helper.fields.size()); if( space->is_object() ) { if( is_last_field ) { CallSpaceObjectForLastField(find_helper, space); } else { space = CallSpaceObjectForMiddleField(find_helper, space); } field_index += 1; } else if( space->is_table() ) { if( model_wrapper_space_table_index < space_wrapper->space_indices_table_size() ) { if( is_last_field ) { CallSpaceTableForLastField(find_helper, *space_wrapper, space, model_wrapper_space_table_index); field_index += 1; } else { space = CallSpaceTableForMiddleField(find_helper, *space_wrapper, space, model_wrapper_space_table_index); // increment field_index only if next_field is 'this' and space is not an object if( space && !space->is_object() ) { std::wstring & next_field = find_helper.fields[field_index]; if( next_field == L"this" ) { field_index += 1; } } } } else { CreateMsg(L"", find_helper.fun_name, find_helper.fields, field_index, L" exceeded the maximum number of fields for a space object"); space = nullptr; } } else { if( is_last_field ) { if( IsTestingFunctionExistence() ) { //last_res = true; } else { PrintLastSpaceField(find_helper, space); //last_res = space->to_bool(); } field_index += 1; } else { if( !IsTestingFunctionExistence() ) { CreateMsg(L"", find_helper.fun_name, find_helper.fields, field_index, L" of a space is not an object nor a table"); } space = nullptr; } } model_wrapper_space_table_index += 1; } return space != nullptr; } #ifdef EZC_HAS_MORM_LIBRARY bool Generator::CallModelField(FindHelper & find_helper, morm::Model & model) { bool found = false; const std::wstring & field = find_helper.fields[find_helper.field_index]; bool no_escape = HasParam(find_helper.parameters, L"raw", L"noescape"); find_helper.result.output_stream->escape_input(!no_escape); if( find_helper.parameters.empty() ) { // what about an empty input parameter? Env env(find_helper.out_stream, find_helper.parameters, find_helper.result, find_helper.in_stream, stack_tab[stack_index-1], *stack_tab[stack_index-1].item); PrepareEnvStruct(env); found = model.get_raw_value(nullptr, field.c_str(), nullptr, env, find_helper.out_stream, false); } else { Env env(find_helper.out_stream, find_helper.parameters, find_helper.result, find_helper.in_stream, stack_tab[stack_index-1], *stack_tab[stack_index-1].item); PrepareEnvStruct(env); found = model.get_raw_value(nullptr, field.c_str(), nullptr, env, find_helper.out_stream, false); } if( found ) { // set type to TYPE_OUTPUT_STREAM? // find_helper.result.type == } return found; } bool Generator::CallModel(FindHelper & find_helper, morm::Model & model) { bool found = true; if( find_helper.field_index == find_helper.fields.size() ) { // all fields items are models or models containers // at the moment we do not set last_res // we don't know whether the last object has properties (fields) or not if( !IsTestingFunctionExistence() ) { DumpModelIfNeeded(find_helper, model); } } else if( find_helper.field_index + 1 == find_helper.fields.size() ) { // last field is not a model nor a models container if( !CallModelField(find_helper, model) ) { if( !IsTestingFunctionExistence() ) { CreateMsg(L"cannot find ", find_helper.fun_name, find_helper.fields, L", unknown property ", find_helper.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, find_helper.fields, L", ", find_helper.fields[find_helper.field_index].c_str(), L" is not a model nor a model container nor a space"); } found = false; } return found; } void Generator::FindLastModelWrapper(FindHelper & find_helper) { for(find_helper.field_index = 0 ; find_helper.field_index < find_helper.fields.size() && find_helper.currentval->has_model_object() ; ++find_helper.field_index) { std::wstring & field = find_helper.fields[find_helper.field_index]; Val * child_wrapper = find_helper.currentval->find_child(field); if( !child_wrapper ) { morm::Model * model = nullptr; if( find_helper.currentval->pointer ) { if( find_helper.currentval->type == Val::TYPE_MODEL ) { model = reinterpret_cast(find_helper.currentval->pointer); } else if( find_helper.currentval->type == Val::TYPE_MODEL_CONTAINER_WRAPPER ) { morm::ModelContainerWrapper * model_container_wrapper = reinterpret_cast(find_helper.currentval->pointer); model = 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 ) { Val val = model->get_ezc_val(nullptr, field.c_str()); if( val.has_object() ) { child_wrapper = find_helper.currentval->add_child(field, val); } } } if( !child_wrapper ) break; find_helper.currentval = child_wrapper; find_helper.currentval->set_output_stream(find_helper.out_stream); } } bool Generator::CallModel(FindHelper & find_helper) { morm::Model * model = reinterpret_cast(find_helper.currentval->pointer); return CallModel(find_helper, *model); } bool Generator::CallModelContainerWrapper(FindHelper & find_helper) { bool found = true; //last_res = false; morm::ModelContainerWrapper * model_container_wrapper = reinterpret_cast(find_helper.currentval->pointer); morm::Model * model = model_container_wrapper->get_model(); if( model ) { found = CallModel(find_helper, *model); } else { if( find_helper.last_field_not_known() ) { // can be printed even for [if-def...] CreateMsg(L"model ", find_helper.fun_name, find_helper.fields, L" is not initialized, have you forgotten to use [for ...] statement?"); found = false; } } if( find_helper.all_fields_known() ) { if( is_generating_for ) { model_container_wrapper->increment_iterator(); find_helper.currentval->clear_childs(); find_helper.result.set(model_container_wrapper->is_iterator_correct()); //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(); find_helper.result.set(model_container_wrapper->is_container_empty()); } } return found; } #endif void Generator::CallObject(BaseObj & base_obj, int method_index, std::vector & parameters, Val & result, pt::Stream & out_stream, const pt::Stream & in_stream) { if( !IsTestingFunctionExistence() ) { Env env(out_stream, parameters, result, in_stream, stack_tab[stack_index-1], *stack_tab[stack_index-1].item); CallObject(base_obj, method_index, env); } } bool Generator::CallBlock(FindHelper & find_helper) { if( block_stack_index >= block_stack_tab.size() ) { CreateMsg(L"Generator exceeded allowed number of blocks"); return false; } if( !IsTestingFunctionExistence() ) { pt::Stream * old_stream = output_stream; BlockStack & block_stack = block_stack_tab[block_stack_index]; block_stack.was_return = false; block_stack.args = find_helper.parameters; output_stream = block_stack.out_stream; ClearStream(*output_stream); block_stack_index += 1; Item * item_block = reinterpret_cast(find_helper.currentval->pointer); MakeText(*item_block); //CopyStream(*output_stream, find_helper.out_stream); // last_res is set by [return ...] statement or other last evaluated function IMPROVE ME now it is changed? ClearStream(*output_stream); output_stream = old_stream; block_stack_index -= 1; } return true; } // bool Generator::CallVariable(Item::Function & item_fun, Var & variable, std::vector & parameters, Val & result, pt::Stream & out_stream, const pt::Stream & in_stream) // { // if( variable.is_function ) // { // return Call(item_fun, &variable.str, result, out_stream, in_stream); // } // else // { // if( !IsTestingFunctionExistence() ) // { // out_stream << variable.str; // last_res = variable.res; // } // return true; // } // } bool Generator::CallPodType(FindHelper & find_helper) { if( !IsTestingFunctionExistence() ) { find_helper.currentval->put_pod_type_to_stream(); } return true; } void Generator::EvaluateParameters(FindHelper & find_helper) { find_helper.parameters.resize(find_helper.item_fun.parameters.size()); for(size_t i=0 ; i < find_helper.item_fun.parameters.size() ; ++i) { pt::Stream * tmp_stream = main_stream->new_empty(); tmp_stream->escape_input(true); find_helper.parameters[i].set_output_stream(tmp_stream); Item::Function & fun_child = *find_helper.item_fun.parameters[i]; if( fun_child.is_function ) { Call(fun_child, nullptr, find_helper.parameters[i], *tmp_stream, *empty_stream); //CopyStreamToString(*local_temp_stream, parameters[i].str); //parameters[i].res = last_res; } else { // parameters[i].str = fun_child.name; // parameters[i].res = ConvertToBool(fun_child.name); find_helper.parameters[i] << fun_child.name; } } } // give me a better name bool Generator::ReduceFields(FindHelper & find_helper) { bool reduced = true; if( find_helper.baseval->has_model_object() ) { #ifdef EZC_HAS_MORM_LIBRARY FindLastModelWrapper(find_helper); #endif } else { // space ? } return true; } bool Generator::CallValue(FindHelper & find_helper) { if( find_helper.baseval->pointer && find_helper.currentval->pointer ) { #ifdef EZC_HAS_MORM_LIBRARY if( find_helper.currentval->type == Val::TYPE_MODEL ) { //return CallWrapper(find_helper); return CallModel(find_helper); } else if( find_helper.currentval->type == Val::TYPE_MODEL_CONTAINER_WRAPPER ) return CallModelContainerWrapper(find_helper); else if( find_helper.currentval->type == Val::TYPE_DATE ) //return CallWrapper(find_helper); return CallDate(find_helper); else if( find_helper.currentval->type == Val::TYPE_SPACE_WRAPPER ) return CallSpace(find_helper); //return CallWrapper(find_helper); else #endif // if( find_helper.base_obj ) // OBJECTS WILL BE REMOVED // CallObject(*find_helper.base_obj, find_helper.method_index, parameters, result, out_stream, in_stream); //else if( find_helper.currentval->type == Val::TYPE_FUNCTION ) { CallFunction(find_helper); return true; } else if( find_helper.currentval->type == Val::TYPE_ITEM_BLOCK ) return CallBlock(find_helper); // else // if( find_helper.variable ) // return CallVariable(item_fun, *find_helper.variable, parameters, result, out_stream, in_stream); if( find_helper.currentval->has_pod_type() ) return CallPodType(find_helper); } return false; } void Generator::CleanupParameters(std::vector & parameters) { for(Val & val : parameters) { delete val.output_stream; val.output_stream = nullptr; } } /* * 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) */ bool Generator::Call(Item::Function & item_fun, std::wstring * fun_name, Val & result, pt::Stream & out_stream, const pt::Stream & in_stream) { Val current(&out_stream); std::vector parameters; if( !fun_name ) fun_name = &item_fun.name; FindHelper find_helper(item_fun, *fun_name, item_fun.fields, current, parameters, result, out_stream, in_stream); // if( clear_out_stream ) // ClearStream(out_stream); if( !Find(find_helper) ) return false; out_stream.escape_input(true); EvaluateParameters(find_helper); ReduceFields(find_helper); bool status = false; try { status = CallValue(find_helper); } catch(...) { // rethrow or just generate a log? CleanupParameters(parameters); return false; } CleanupParameters(parameters); return status; } 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" 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; } 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); } } } 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); } } } 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; } void Generator::TrimWhite(const wchar_t *& start, const wchar_t *& end) { while( start != end && IsWhite(*start) ) ++start; while( start != end && IsWhite(*(end-1)) ) --end; } void Generator::SkipWhite(const wchar_t *& str) { while( IsWhite(*str) ) str += 1; } 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; } pt::Stream * Generator::FindAddOutputStream(const std::wstring & name) { pt::Stream * stream = nullptr; if( output_frames_streams ) { 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 */ 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); } else { CreateMsg(L"limit of output streams in OutStreams<> has been reached"); } } else { stream = imap->second; } } return stream; } void Generator::CopyTmpStreamToOutputStreams(Item::Function & fun, pt::Stream & ezc_out_tmp_stream) { if( output_frames_streams ) { for(size_t s=0 ; s < fun.parameters.size() ; ++s) { std::wstring & name = fun.parameters[s]->name; if( HasAskForSuchFrame(name) ) { pt::Stream * stream = FindAddOutputStream(name); if( stream ) { CopyStream(ezc_out_tmp_stream, *stream); } } } } } 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; } } 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; } } 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); } void Generator::CreateMsg(const std::wstring & type, const std::wstring & arg) { CreateMsg(type.c_str(), arg.c_str()); } void Generator::CreateMsg(const std::wstring & type) { CreateMsg(type.c_str()); } 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"; } } bool Generator::HasAskForSuchFrame(Item::Function & fun) { if( only_frames ) { for(size_t s=0 ; s < fun.parameters.size() ; ++s) { std::wstring & name = fun.parameters[s]->name; for(size_t f=0 ; f < only_frames->size() ; ++f) { const std::wstring & frame = (*only_frames)[f]; if( name == frame ) return true; } } return false; } else { return true; } } bool Generator::HasAskForSuchFrame(const std::wstring & name) { if( only_frames ) { for(size_t f=0 ; f < only_frames->size() ; ++f) { const std::wstring & frame = (*only_frames)[f]; if( name == frame ) return true; } return false; } else { return true; } } void Generator::MakeItemText(Item & item) { if( is_output_stream_allowed ) { 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); } } void Generator::MakeTextContainer(Item & item) { std::vector::iterator i = item.item_tab.begin(); for( ; i != item.item_tab.end() && !break_generating ; ++i ) MakeText(**i); } void Generator::MakeTextFunction(Item & item) { is_generating_normal = true; if( program_mode ) { EvaluateProgramNode(item); } else { if( output_stream && is_output_stream_allowed ) { Val result(output_stream); // does we need both seperate: result and output_stream? if( Call(item.function, nullptr, result, *output_stream, *empty_stream) ) { output_stream->escape_input(true); result.serialize_to(*output_stream); } } else { Val result(stream_temp1); Call(item.function, nullptr, result, *stream_temp1, *empty_stream); } } } 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] ); } } void Generator::MakeTextIf(Item & item) { Val result(stream_temp1); is_generating_if = true; if( program_mode ) { // is it ok now? EvaluateProgramNode(item); } else { bool status = Call(item.function, nullptr, result, *stream_temp1, *empty_stream); ClearStream(*stream_temp1); if( !status ) return; } MakeTextIf_go(item, result.to_bool(false)); } void Generator::MakeTextIfDef(Item & item) { is_generating_if_def = true; if( program_mode ) { //CHECKME //EvaluateProgramNode(item); } else { //last_res = Call(item.function); } //MakeTextIf_go(item, last_res); } void Generator::MakeTextIfNotDef(Item & item) { is_generating_if_not_def = true; if( program_mode ) { //CHECKME //EvaluateProgramNode(item); } else { //last_res = !Call(item.function); } //MakeTextIf_go(item, last_res); } void Generator::MakeTextFor(Item & item) { Val result(stream_temp1); 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; if( program_mode ) { EvaluateProgramNode(item); } else { if( !Call(item.function, nullptr, result, *stream_temp1, *empty_stream) ) return; } if( !result.to_bool(true) ) break; if( !item.item_tab.empty() ) MakeText( *item.item_tab[0] ); // should be only one item } } void Generator::MakeTextDefine(Item & item, Val & val) { /* val.str.clear(); val.res = ConvertToBool(val.str); var.is_function = false; 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, *empty_stream) ) { CopyStreamToString(*stream_temp_define, val.str); val.res = last_res; } else { CreateMsg(L"[def] unknown function/block/variable", item.function.name); } } else { val.str = fun.name; val.res = ConvertToBool(fun.name); } */ } void Generator::MakeTextDefine(Item & item) { if( !can_use_vars || !pvals ) { CreateMsg(L"[def] statement not available"); return; } Val & val = (*pvals)[item.function.name]; MakeTextDefine(item, val); } void Generator::MakeTextDefineIfNotSet(Item & item) { if( !can_use_vars || !pvals ) { CreateMsg(L"[def?] statement not available"); return; } Vals::iterator vi = pvals->find(item.function.name); if( vi == pvals->end() ) { Val & val = (*pvals)[item.function.name]; MakeTextDefine(item, val); } } void Generator::MakeTextLet(Item & item, Val & val) { /* val.str.clear(); val.res = ConvertToBool(val.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]; val.str = fun.name; var.is_function = fun.is_function; if( !fun.is_function ) { val.res = ConvertToBool(val.str); } */ } void Generator::MakeTextLet(Item & item) { if( !can_use_vars || !pvals ) { CreateMsg(L"[let] statement not available"); return; } Val & val = (*pvals)[item.function.name]; MakeTextLet(item, val); } void Generator::MakeTextLetIfNotSet(Item & item) { if( !can_use_vars || !pvals ) { CreateMsg(L"[let?] statement not available"); return; } Vals::iterator vi = pvals->find(item.function.name); if( vi == pvals->end() ) { Val & val = (*pvals)[item.function.name]; MakeTextLet(item, val); } } void Generator::MakeTextFilter(Item & item) { /* if( filter_index >= filter_tab.size() ) { CreateMsg(L"Generator exceeded allowed number of filters"); return; } pt::Stream * 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 && is_output_stream_allowed ) { Call(item.function, nullptr, *old_stream, false, *output_stream); } else { Call(item.function, nullptr, *stream_temp1, true, *output_stream); ClearStream(*stream_temp1); } ClearStream(*output_stream); output_stream = old_stream; filter_index -= 1; */ } pt::Stream * Generator::GetNewStreamForFrame() { if( ezc_frames_stack_index >= ezc_frame_stack_tab.size() ) { CreateMsg(L"Generator exceeded allowed number of [ezc frame] statements"); return nullptr; } pt::Stream * stream = ezc_frame_stack_tab[ezc_frames_stack_index]; ClearStream(*stream); ezc_frames_stack_index += 1; return stream; } void Generator::DecrementFramesStackIndex() { if( ezc_frames_stack_index > 0 ) { ezc_frames_stack_index -= 1; } } void Generator::MakeEzcFrame2(Item & item) { pt::Stream * old_stream = output_stream; bool old_is_output_stream_allowed = is_output_stream_allowed; bool put_directly = false; bool is_current_frame_allowed = HasAskForSuchFrame(item.function); if( !is_output_stream_allowed && is_current_frame_allowed ) { is_output_stream_allowed = true; } if( !old_is_output_stream_allowed && is_current_frame_allowed && item.function.parameters.size() == 1) { /* * some optimization, put directly to the frame stream */ put_directly = true; output_stream = FindAddOutputStream(item.function.parameters[0]->name); } else if( old_is_output_stream_allowed && !is_current_frame_allowed ) { /* * some optimization, put directly to the old stream */ put_directly = true; // do not modify output_stream here } else { output_stream = GetNewStreamForFrame(); } if( output_stream ) { if( !item.item_tab.empty() ) MakeText( *item.item_tab[0] ); // should be only one item - item_container if( !put_directly ) { if( old_is_output_stream_allowed ) { CopyStream(*output_stream, *old_stream); } if( is_current_frame_allowed ) { CopyTmpStreamToOutputStreams(item.function, *output_stream); } ClearStream(*output_stream); DecrementFramesStackIndex(); } } is_output_stream_allowed = old_is_output_stream_allowed; output_stream = old_stream; } void Generator::MakeEzcFrame(Item & item) { /* 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 ) { if( !item.item_tab.empty() ) MakeText( *item.item_tab[0] ); // should be only one item - item_container } else { MakeEzcFrame2(item); } } 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 } 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, nullptr, *stream_temp1, false, *empty_stream); // ClearStream(*stream_temp1); } } 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; } 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