ezc/src/generator.h

3294 lines
87 KiB
C++

/*
* This file is a part of EZC -- Easy templating in C++ library
* and is distributed under the BSD 3-Clause licence.
* Author: Tomasz Sowa <t.sowa@ttmath.org>
*/
/*
* 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 <sstream>
#include <fstream>
#include <vector>
#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 StreamType, bool is_pikotools_stream = false, bool is_autoescape_stream = false>
class Generator
{
public:
Generator();
Generator(const Generator & n);
Generator & operator=(const Generator & n);
~Generator();
void SetPattern(Pattern & pattern);
void SetVariables(Vars<StreamType> & 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<StreamType, is_pikotools_stream> & out_streams);
void Generate(OutStreams<StreamType, is_pikotools_stream> & out_streams);
private:
struct FindHelperOld
{
//const std::wstring * fun_name;
//BaseObj<StreamType> * base_obj;
//int method_index;
//typename Functions<StreamType>::UserFunction * function;
//Item * item_block;
Var<StreamType> * variable;
#ifdef EZC_HAS_MORM_LIBRARY
Var<StreamType> * 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<std::wstring> & fields;
size_t field_index;
std::vector<Var<StreamType>> & parameters;
Var<StreamType> * input;
Var<StreamType> * result;
bool found;
const std::wstring * previous_name;
size_t nested_calls;
Var<StreamType> * current_var;
Var<StreamType> * previous_var;
Var<StreamType> * previous_result;
FindHelper(
const std::wstring & name,
std::vector<std::wstring> & fields,
std::vector<Var<StreamType>> & 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<Var<StreamType>> args;
StreamType * out_stream;
bool was_return;
};
std::vector<BlockStack> 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<StreamType> * 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<StreamType, is_pikotools_stream> * 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<StreamType*> filter_tab;
std::vector<StreamType*> 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> stack_tab;
bool can_use_vars;
bool program_mode;
size_t max_nested_calls;
ExpressionParser * expression_parser;
void ResizeStreamStack(std::vector<StreamType*> & stream_tab, size_t stream_tab_max_size);
void ResizeFilterTab();
void ResizeStack();
void ResizeBlockStack();
void ResizeEzcFrameStack();
void ClearStreamStack(std::vector<StreamType*> & 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<class CharType>
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<StreamType> & info);
void CallFunction(typename Functions<StreamType>::UserFunction & function, Env<StreamType> & info);
void CallFunction(typename Functions<StreamType>::UserFunction & function, FindHelper & find_helper);
bool CallBlock(Item & item_block,
Var<StreamType> & result,
std::vector<Var<StreamType>> & parameters);
void PrintDate(pt::Date & date, FindHelper & find_helper);
//bool PrintDatePart(pt::Date & date, const std::wstring & field, std::vector<Var<StreamType>> & parameters, Var<StreamType> & result);
//bool CallDate(pt::Date & date, const std::wstring & name, std::vector<std::wstring> & fields, size_t & field_index, std::vector<Var<StreamType>> & parameters, Var<StreamType> & 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<std::wstring> & 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<std::wstring> & fields, size_t field_index,
// std::vector<Var<StreamType>> & parameters, Var<StreamType> & result);
// void FindLastModelWrapper(FindHelperOld & find_helper, std::vector<std::wstring> & fields);
// bool CallWrapper(FindHelperOld & find_helper, std::vector<std::wstring> & fields, Var<StreamType> & result, std::vector<Var<StreamType>> & parameters, const StreamType & in_stream);
#endif
Var<StreamType> * 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<StreamType>::UserFunction user_function, FindHelper & find_helper);
// bool CallVariable(FindHelper & findxxx
// const std::wstring & name,
// std::vector<std::wstring> & fields,
// size_t & field_index,
// Var<StreamType> & result,
// std::vector<Var<StreamType>> & parameters
// );
bool Call(Item::Function & item_fun,
const std::wstring * fun_name,
Var<StreamType> & result,
StreamType & in_stream);
bool Call(Item::Function & item_fun, Var<StreamType> & 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<Var<StreamType>> & parameters, const wchar_t * param1, const wchar_t * param2 = nullptr);
bool ShouldMakeSpaceDump(std::vector<Var<StreamType>> & parameters);
bool ShouldMakeJsonDump(std::vector<Var<StreamType>> & parameters);
bool IsPrettyPrint(std::vector<Var<StreamType>> & parameters);
void DumpSpaceIfNeeded(pt::Space & space, FindHelper & find_helper);
void DumpModelIfNeeded(morm::Model & model, Var<StreamType> & result, std::vector<Var<StreamType>> & 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<std::wstring> & 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<std::wstring> & 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<StreamType> & var);
void MakeTextDefine(Item & item);
void MakeTextDefineIfNotSet(Item & item);
void MakeTextLet(Item & item, Var<StreamType> & 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::Generator(const Generator<StreamType, is_pikotools_stream, is_autoescape_stream> & n)
{
operator=(n);
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
Generator<StreamType, is_pikotools_stream, is_autoescape_stream> &
Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::operator=(const Generator<StreamType, is_pikotools_stream, is_autoescape_stream> & 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::~Generator()
{
ClearFilterTab();
ClearForStack();
ClearBlockStack();
ClearEzcFrameTab();
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::SetPattern(Pattern & pattern)
{
ppattern = &pattern;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::SetVariables(Vars<StreamType> & variables)
{
pvars = &variables;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::SetLogger(pt::Log & logger)
{
plog = &logger;
}
#ifdef EZC_HAS_MORM_LIBRARY
//template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
//void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::SetModels(Models & models)
//{
// pmodels = &models;
//}
#endif
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CanUseVars(bool can_use_variables)
{
can_use_vars = can_use_variables;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::SetProgramMode(bool set_program_mode)
{
this->program_mode = set_program_mode;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::SetExpressionParser(ExpressionParser * expression_parser)
{
this->expression_parser = expression_parser;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ResizeStack()
{
if( stack_tab.size() != stack_size )
{
if( stack_tab.size() > stack_size )
{
for(size_t i=stack_size ; i<stack_tab.size() ; ++i)
RemoveStackFunData(stack_tab[i]);
}
stack_tab.resize(stack_size);
}
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ResizeStreamStack(std::vector<StreamType*> & 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<stream_tab.size() ; ++i)
stream_tab[i] = new StreamType();
}
else
{
for(size_t i=stream_tab_max_size ; i<stream_tab.size() ; ++i)
delete stream_tab[i];
stream_tab.resize(stream_tab_max_size);
}
}
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ResizeFilterTab()
{
ResizeStreamStack(filter_tab, filter_size);
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ResizeEzcFrameStack()
{
ResizeStreamStack(ezc_frame_stack_tab, ezc_frames_stack_size);
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<block_stack_tab.size() ; ++i)
block_stack_tab[i].out_stream = new StreamType();
}
else
{
for(size_t i=block_stack_size ; i<block_stack_tab.size() ; ++i)
delete block_stack_tab[i].out_stream;
block_stack_tab.resize(block_stack_size);
}
}
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ClearStreamStack(std::vector<StreamType*> & stream_tab)
{
for(size_t i=0 ; i<stream_tab.size() ; ++i)
delete stream_tab[i];
stream_tab.clear();
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ClearFilterTab()
{
ClearStreamStack(filter_tab);
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ClearEzcFrameTab()
{
ClearStreamStack(ezc_frame_stack_tab);
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ClearBlockStack()
{
for(size_t i=0 ; i<block_stack_tab.size() ; ++i)
delete block_stack_tab[i].out_stream;
block_stack_tab.clear();
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ClearForStack()
{
for(size_t i=0 ; i<stack_tab.size() ; ++i)
RemoveStackFunData(stack_tab[i]);
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ClearStream(StreamType & str)
{
if constexpr(is_pikotools_stream)
{
str.clear();
}
else
{
str.str(L"");
}
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CopyStream(StreamType & src_stream, StreamType & dst_stream)
{
if constexpr(is_pikotools_stream)
{
dst_stream << src_stream;
}
else
{
dst_stream << src_stream.str();
}
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::RemoveStackFunData(Stack & s)
{
if( s.fun_data && s.auto_remove )
{
delete s.fun_data;
s.fun_data = 0;
}
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
template<class CharType>
CharType Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ToLower(CharType c)
{
if( c>='A' && c<='Z' )
return c - 'A' + 'a';
return c;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ConvertToBool(const std::wstring & str)
{
return !str.empty();
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::SetMax(size_t max_items_, size_t max_for_items_)
{
max_items = max_items_;
max_for_items = max_for_items_;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::RecognizeSpecialChars(bool spec)
{
special_chars = spec;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::TrimWhite(bool trim)
{
trim_white = trim;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::SkipNewLine(bool skip)
{
skip_new_line = skip;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::SetMaxFilters(size_t new_len)
{
// the table will be resized when Generate() method is called
filter_size = new_len;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::SetStackSize(size_t new_stack_size)
{
// the stack will be resized when Generate() method is called
stack_size = new_stack_size;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::Generate(StreamType & out)
{
output_stream = &out;
output_frames_streams = nullptr;
Generate();
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::Generate(StreamType & out, OutStreams<StreamType, is_pikotools_stream> & out_streams)
{
output_stream = &out;
output_frames_streams = &out_streams;
Generate();
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::Generate(OutStreams<StreamType, is_pikotools_stream> & out_streams)
{
output_stream = nullptr;
output_frames_streams = &out_streams;
Generate();
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::IsTestingFunctionExistence()
{
return is_generating_if_def || is_generating_if_not_def;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
//bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::PrepareEnvStruct(Env<StreamType> & 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CallFunction(typename Functions<StreamType>::UserFunction & function, Env<StreamType> & info)
{
PrepareEnvStruct(info);
(function)(info);
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CallFunction(
typename Functions<StreamType>::UserFunction & function, FindHelper & find_helper)
{
if( !IsTestingFunctionExistence() )
{
// if( find_helper.parameters.empty() )
{
Env<StreamType> 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<StreamType> info(result, parameters, in_stream, stack_tab[stack_index-1], *stack_tab[stack_index-1].item);
// CallFunction(function, info);
// }
}
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::HasParam(std::vector<Var<StreamType>> & parameters, const wchar_t * param1, const wchar_t * param2)
{
for(Var<StreamType> & var : parameters)
{
if( var.is_equal(param1) || (param2 && var.is_equal(param2)) )
{
return true;
}
}
return false;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ShouldMakeSpaceDump(std::vector<Var<StreamType>> & parameters)
{
return HasParam(parameters, L"dump", L"dump_to_space");
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::ShouldMakeJsonDump(std::vector<Var<StreamType>> & parameters)
{
return HasParam(parameters, L"dump_to_json");
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::IsPrettyPrint(std::vector<Var<StreamType>> & parameters)
{
return HasParam(parameters, L"pretty");
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<StreamType>::TYPE_STREAM;
}
else
if( dump_json )
{
space.serialize_to_json_stream(find_helper.result->stream, pretty);
find_helper.result->type = Var<StreamType>::TYPE_STREAM;
}
}
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::DumpModelIfNeeded(
morm::Model & model, Var<StreamType> & result, std::vector<Var<StreamType>> & 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<StreamType>::TYPE_STREAM;
//out_stream << temp_str;
}
}
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<StreamType>::TYPE_STREAM;
date.SerializeYearMonthDay(find_helper.result->stream, is_roman);
}
else
if( only_time )
{
find_helper.result->type = Var<StreamType>::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<StreamType>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::PrintDatePart(
pt::Date & date, const std::wstring & field, std::vector<Var<StreamType>> & parameters, Var<StreamType> & result)
{
bool is_test = IsTestingFunctionExistence();
if( field == L"year" )
{
if( !is_test )
{
result.type = Var<StreamType>::TYPE_STREAM;
result.stream << date.year;
}
}
else
if( field == L"month" )
{
if( !is_test )
{
bool is_roman = HasParam(parameters, L"roman");
result.type = Var<StreamType>::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<StreamType>::TYPE_STREAM;
result.stream << date.day;
}
}
else
if( field == L"hour" )
{
if( !is_test )
{
result.type = Var<StreamType>::TYPE_STREAM;
result.stream << date.hour;
}
}
else
if( field == L"min" )
{
if( !is_test )
{
result.type = Var<StreamType>::TYPE_STREAM;
result.stream << date.min;
}
}
else
if( field == L"sec" )
{
if( !is_test )
{
result.type = Var<StreamType>::TYPE_STREAM;
result.stream << date.sec;
}
}
else
{
return false;
}
return true;
}
*/
/*
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CallDate(
pt::Date & date, const std::wstring & name, std::vector<std::wstring> & fields, size_t field_index, std::vector<Var<StreamType>> & parameters, Var<StreamType> & 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
//void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CallSpaceObjectForLastField(pt::Space & space, FindHelper & find_helper)
//{
// if( !IsTestingFunctionExistence() )
// {
// // CHECKME should we convert the last value to last_res?
// DumpSpaceIfNeeded(space, find_helper);
// }
//}
//template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
//pt::Space * Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CallSpaceObjectForMiddleField(const std::wstring & root_space_name, std::vector<std::wstring> & 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<StreamType>::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<StreamType>::TYPE_STREAM;
}
}
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
pt::Space * Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<StreamType>::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<StreamType>::TYPE_STREAM;
}
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<unsigned long long>(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<unsigned long long>(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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<StreamType> 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<StreamType>::TYPE_STREAM;
// }
return found;
}
/*
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CallModel(
morm::Model & model, const std::wstring & name, std::vector<std::wstring> & fields, size_t field_index,
std::vector<Var<StreamType>> & parameters, Var<StreamType> & 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::FindLastModelWrapper(
Var<StreamType> & var_parent, std::vector<std::wstring> & fields, size_t & field_index)
{
Var<StreamType> * var = &var_parent;
for( ; field_index < fields.size() && var->has_model_object() ; ++field_index)
{
std::wstring & field = fields[field_index];
Var<StreamType> * 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<StreamType> 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CallWrapper(
FindHelperOld & find_helper, std::vector<std::wstring> & fields,
Var<StreamType> & result, std::vector<Var<StreamType>> & 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CallBlock(Item & item_block,
Var<StreamType> & result,
std::vector<Var<StreamType>> & 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<StreamType>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
//bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CallVariable(FindHelper & find_helper)
// //const std::wstring & name, std::vector<std::wstring> & fields, size_t & field_index, Var<StreamType> & result, std::vector<Var<StreamType>> & parameters)
//{
// Var<StreamType> * 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
Var<StreamType> * Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::FindInScope(const std::wstring & name)
{
// here we should look in scopes (not implemented yet)
if( pvars )
{
return pvars->find(name);
}
return nullptr;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<StreamType> * origin_result = find_helper.result;
std::vector<Var<StreamType>> 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<StreamType>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<StreamType>::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<StreamType>::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<StreamType>::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<StreamType>::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<StreamType>::Type::TYPE_VOID && !find_helper.result->stream.empty() )
{
find_helper.result->type = Var<StreamType>::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<StreamType>::TYPE_MODEL ||
if(
find_helper.result->type == Var<StreamType>::TYPE_MODEL_CONTAINER_WRAPPER ||
find_helper.result->type == Var<StreamType>::TYPE_SPACE_WRAPPER ||
find_helper.result->type == Var<StreamType>::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<StreamType>::TYPE_MODEL_CONTAINER_WRAPPER )
{
if( !EvaluateModelWrapper(find_helper) )
return;
}
else
if( find_helper.result->type == Var<StreamType>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<StreamType>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::EvaluateFunction(typename Var<StreamType>::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<StreamType>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CallVariable(
std::vector<std::wstring> & fields, size_t & field_index, Var<StreamType> & variable, Var<StreamType> & result, std::vector<Var<StreamType>> & parameters, const StreamType & in_stream)
{
if( variable.type == Var<StreamType>::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<StreamType>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::Call(
Item::Function & item_fun,
const std::wstring * fun_name,
Var<StreamType> & result,
StreamType & in_stream)
{
std::vector<Var<StreamType>> 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<StreamType> * 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<item_fun.parameters.size() ; ++i)
{
Item::Function & fun_child = *item_fun.parameters[i];
if( fun_child.is_function )
{
empty_stream.clear();
Call(fun_child, nullptr, parameters[i], empty_stream);
}
else
{
// currently in Patterns we can define only strings
parameters[i].set(fun_child.name);
}
}
Var<StreamType> empty_input; // fix me for filters
empty_input.type = Var<StreamType>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::Call(Item::Function & item_fun, Var<StreamType> & result)
{
//return Call(item_fun, nullptr, stream_temp1, true, result, empty_stream);
empty_stream.clear();
return Call(item_fun, nullptr, result, empty_stream);
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
wchar_t Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
const wchar_t * Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::TrimWhite(const wchar_t *& start, const wchar_t *& end)
{
while( start != end && IsWhite(*start) )
++start;
while( start != end && IsWhite(*(end-1)) )
--end;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::SkipWhite(const wchar_t *& str)
{
while( IsWhite(*str) )
str += 1;
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
size_t Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CreateMsg(const wchar_t * type, const std::wstring & model_name, std::vector<std::wstring> & 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CreateMsg(const wchar_t * type, const std::wstring & model_name, std::vector<std::wstring> & fields,
const wchar_t * arg, const wchar_t * arg2, const wchar_t * arg3)
{
CreateMsg(type, model_name, fields, fields.size(), arg, arg2, arg3);
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CreateMsg(const std::wstring & type, const std::wstring & arg)
{
CreateMsg(type.c_str(), arg.c_str());
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::CreateMsg(const std::wstring & type)
{
CreateMsg(type.c_str());
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::MakeTextContainer(Item & item)
{
std::vector<Item*>::iterator i = item.item_tab.begin();
for( ; i != item.item_tab.end() && !break_generating ; ++i )
MakeText(**i);
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::MakeTextFunction(Item & item)
{
is_generating_normal = true;
if( program_mode )
{
EvaluateProgramNode(item);
}
else
{
Var<StreamType> result;
empty_stream.clear();
if( Call(item.function, nullptr, result, empty_stream) )
{
if( output_stream )
{
if( result.type == Var<StreamType>::TYPE_MODEL_CONTAINER_WRAPPER )
{
// IMPROVEME add model_container serialized as a table
// add a method 'serialize_to' to ModelContainerWrapper
}
else
if( result.type == Var<StreamType>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::MakeTextIf(Item & item)
{
is_generating_if = true;
Var<StreamType> result;
if( program_mode )
{
EvaluateProgramNode(item);
}
else
{
if( !Call(item.function, result) )
return;
}
MakeTextIf_go(item, result.to_bool());
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::MakeTextIfDef(Item & item)
{
is_generating_if_def = true;
Var<StreamType> result;
if( program_mode )
{
//CHECKME
//EvaluateProgramNode(item);
}
else
{
Call(item.function, result);
}
MakeTextIf_go(item, result.to_bool());
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::MakeTextIfNotDef(Item & item)
{
is_generating_if_not_def = true;
Var<StreamType> result;
if( program_mode )
{
//CHECKME
//EvaluateProgramNode(item);
}
else
{
Call(item.function, result);
}
MakeTextIf_go(item, !result.to_bool());
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::MakeTextFor(Item & item)
{
stack_tab[stack_index-1].is_for = true;
Var<StreamType> 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<StreamType>::Type::TYPE_MODEL_CONTAINER_WRAPPER )
// {
// if( !result.model_container_wrapper->is_iterator_correct() )
// break;
// }
// else
// if( result.type == Var<StreamType>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::MakeTextDefine(Item & item, Var<StreamType> & 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::MakeTextDefine(Item & item)
{
return;
/*
if( !can_use_vars || !pvars )
{
CreateMsg(L"[def] statement not available");
return;
}
Var<StreamType> & var = (*pvars)[item.function.name];
MakeTextDefine(item, var);
*/
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<StreamType> & var = (*pvars)[item.function.name];
MakeTextDefine(item, var);
}
*/
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::MakeTextLet(Item & item, Var<StreamType> & 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::MakeTextLet(Item & item)
{
return;
/*
if( !can_use_vars || !pvars )
{
CreateMsg(L"[let] statement not available");
return;
}
Var<StreamType> & var = (*pvars)[item.function.name];
MakeTextLet(item, var);
*/
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<StreamType> & var = (*pvars)[item.function.name];
MakeTextLet(item, var);
}
*/
}
template<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::MakeTextFilter(Item & item)
{
if( filter_index >= filter_tab.size() )
{
CreateMsg(L"Generator exceeded allowed number of filters");
return;
}
Var<StreamType> 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::MakeEzcFrame(Item & item)
{
std::vector<std::wstring*> 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<StreamType> 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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
bool Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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<class StreamType, bool is_pikotools_stream, bool is_autoescape_stream>
void Generator<StreamType, is_pikotools_stream, is_autoescape_stream>::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