ezc/src/generator.h

1919 lines
42 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-2014, Tomasz Sowa
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name Tomasz Sowa nor the names of contributors to this
* project may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef headerfile_ezc_generator
#define headerfile_ezc_generator
#include "blocks.h"
#include "pattern.h"
#include "functions.h"
#include <sstream>
#include <fstream>
#include <vector>
namespace Ezc
{
/*
StreamType
we use only method write(const wchar_t * str, size_t len) from the stream
*/
template<class StreamType>
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<StreamType> & functions);
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);
// do not print anything to stream stream_index
// useful when using Generate() method with StreamType tables
// this is a small optimization if we know that a stream with stream_index will not be used later
// set stream_index to -1 to turn off (default)
void SkipStream(int stream_index);
// set whether or not we can use cache for functions or blocks
// true by default
void CanUseCache(bool can_use_cache);
// set whether or not we can use variables: [def ...] statement
// true by default
void CanUseVars(bool can_use_variables);
// the main methods for generating
void Generate(StreamType & o);
void Generate(std::vector<StreamType> & o);
void Generate(std::vector<StreamType*> & o);
void SetCommentary(const char * com_start, const char * com_stop);
void SetCommentary(const std::string & com_start, const std::string & com_stop);
void SetCommentary(const wchar_t * com_start, const wchar_t * com_stop);
void SetCommentary(const std::wstring & com_start, const std::wstring & com_stop);
private:
// variables set
typedef std::map<std::wstring, Var> Vars;
Vars vars;
Var temp_var;
struct BlockStack
{
std::vector<Var> 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
// if we are printing only to one stream e.g. [ezc output "3"]
// then output_stream is pointing directly to that stream
// but if we are printing to more than one stream e.g. [ezc output "0" "2" "5"]
// then output_stream is pointing to output_tmp_stream and at the end
// output_tmp_stream is copied to the correct streams
// output_stream can be null e.g. [ezc output] without arguments
// in such a case we do not print anything (but any functions are evaluated)
StreamType * output_stream;
Pattern * ppattern;
Blocks * pblocks;
Functions<StreamType> * pfunctions;
// output_tmp_stream is used when outputting to more than one stream
// (first we output to output_tmp_stream and then the content is copied
// to the correct streams)
StreamType output_tmp_stream;
// pointers to the output streams
std::vector<StreamType*> output_stream_tab;
// indices to output_stream_tab
// they are set by [put stream ...] keyword
// at the beginning there is only one index created automatically - "0"
std::vector<size_t> output_stream_index;
// if has_skip_stream_index is true then when reading stream indices from [put stream ...]
// a stream with skip_stream_index index will be ignored
bool has_skip_stream_index;
size_t skip_stream_index;
// 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 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;
const StreamType empty_stream;
// used in [0] [1] [2] when there is no such argument defined
std::wstring empty_argument;
// temporary streams used in [if..] [is...] or [for...]
// or if output_stream is null and an ezc function should be called
StreamType stream_temp1, stream_temp2, 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_is;
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;
std::wstring commentary_start, commentary_stop;
bool can_use_vars;
bool can_find_in_cache;
void ResizeFilterTab();
void ResizeStack();
void ResizeBlockStack();
void ClearFilterTab();
void ClearForStack();
void ClearBlockStack();
void ClearStream(StreamType & str);
void RemoveStackFunData(Stack & sitem);
template<class CharType>
CharType ToLower(CharType c);
bool AreStreamsEqual(StreamType & str1, StreamType & str2);
bool FindInCache(const Item::Function & item_fun,
typename Functions<StreamType>::UserFunction ** function,
Item ** item_block,
std::wstring ** variable);
bool FindInFunctionsAndBlocks(const std::wstring & name,
typename Functions<StreamType>::UserFunction ** function,
Item ** item_block);
bool FindInVariables(const std::wstring & name,
std::wstring ** variable);
bool Find(const Item::Function & item_fun,
typename Functions<StreamType>::UserFunction ** function,
Item ** item_block,
std::wstring ** variable);
void CallFunction(typename Functions<StreamType>::UserFunction * function,
FunInfo<StreamType> & info);
void CallFunction(typename Functions<StreamType>::UserFunction * function,
std::vector<Var> & parameters,
StreamType & out_stream,
const StreamType & in_stream);
bool CallBlock(Item & item_block,
std::vector<Var> & parameters,
StreamType & out_stream);
bool Call(Item::Function & item_fun,
StreamType & out_stream,
bool clear_out_stream,
const StreamType & in_stream);
bool Call(Item::Function & item_fun);
wchar_t CreateSpecialChar(wchar_t c);
const wchar_t * PrintSpecialChar(const wchar_t * start, const wchar_t * end);
void PrintSpecialText(const wchar_t * start, const wchar_t * end);
void PrintNormalText(const wchar_t * start, const wchar_t * end);
bool IsWhite(wchar_t c);
void TrimWhite(const wchar_t *& start, const wchar_t *& end);
void SkipWhite(const wchar_t *& str);
size_t StrToSize(const wchar_t * str, const wchar_t ** str_end = 0);
bool LookForLastForStatement(size_t & last_for_index);
void WriteTmpStreamToStreams();
void CreateMsg(std::wstring & out, const wchar_t * type, const wchar_t * arg = 0);
void CreateMsg(const wchar_t * type, const wchar_t * arg = 0);
void CreateMsg(const std::wstring & type, const std::wstring & arg);
void CreateMsg(const std::wstring & type);
void CreateUnknownMsg(const std::wstring & fun);
bool LimitAchieved();
void MakeTextIf_go(Item & item, bool result);
bool MakeTextIfindexnumber(Item & item, size_t for_index, bool & result);
void MakeTextIf(Item & item);
void MakeTextIfno(Item & item);
void MakeTextIfany(Item & item);
void MakeTextIfone(Item & item);
void MakeTextIfanyno(Item & item);
void MakeTextIfoneno(Item & item);
void MakeTextIfindex(Item & item);
void MakeTextFor(Item & item);
void MakeItemText(Item & item);
void MakeTextContainer(Item & item);
void MakeTextNormal(Item & item);
void MakeTextIs(Item & item, bool equal);
void MakeTextIs(Item & item);
void MakeTextIsno(Item & item);
void MakeTextDefine(Item & item);
void MakeTextFilter(Item & item);
void MakeTextEzc(Item & item);
void MakeTextReturn(Item & item);
void MakeText(Item & item);
void MakeEzcStream(Item::Function & fun);
void Generate();
}; // class Generator
template<class StreamType>
Generator<StreamType>::Generator() : empty_stream()
{
output_stream = 0;
ppattern = 0;
pblocks = 0;
pfunctions = 0;
max_items = 50000;
max_for_items = 5000;
filter_size = 20;
special_chars = false;
trim_white = false;
skip_new_line = false;
is_generator_working = false;
stack_size = 300;
block_stack_size = 64;
has_skip_stream_index = false;
can_find_in_cache = true;
can_use_vars = true;
}
template<class StreamType>
Generator<StreamType>::Generator(const Generator<StreamType> & n)
{
operator=(n);
}
template<class StreamType>
Generator<StreamType> & Generator<StreamType>::operator=(const Generator<StreamType> & n)
{
// !! CHECK ME may output_streams should not be copied?
output_stream_tab = n.output_stream_tab;
output_stream_index = n.output_stream_index;
output_tmp_stream = n.output_tmp_stream;
output_stream = n.output_stream;
ppattern = n.ppattern;
pblocks = n.pblocks;
pfunctions = n.pfunctions;
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;
has_skip_stream_index = n.has_skip_stream_index;
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;
// vars doesn't have to be copied
// don't copy filter tab
// don't copy stack
// !! 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>
Generator<StreamType>::~Generator()
{
ClearFilterTab();
ClearForStack();
ClearBlockStack();
}
template<class StreamType>
void Generator<StreamType>::SetCommentary(const char * com_start, const char * com_stop)
{
PT::UTF8ToWide(com_start, commentary_start);
PT::UTF8ToWide(com_stop, commentary_stop);
}
template<class StreamType>
void Generator<StreamType>::SetCommentary(const std::string & com_start, const std::string & com_stop)
{
PT::UTF8ToWide(com_start, commentary_start);
PT::UTF8ToWide(com_stop, commentary_stop);
}
template<class StreamType>
void Generator<StreamType>::SetCommentary(const wchar_t * com_start, const wchar_t * com_stop)
{
commentary_start = com_start;
commentary_stop = com_stop;
}
template<class StreamType>
void Generator<StreamType>::SetCommentary(const std::wstring & com_start, const std::wstring & com_stop)
{
commentary_start = com_start;
commentary_stop = com_stop;
}
template<class StreamType>
void Generator<StreamType>::SetPattern(Pattern & pattern)
{
ppattern = &pattern;
}
template<class StreamType>
void Generator<StreamType>::SetBlocks(Blocks & blocks)
{
pblocks = &blocks;
}
template<class StreamType>
void Generator<StreamType>::SetFunctions(Functions<StreamType> & functions)
{
pfunctions = &functions;
}
template<class StreamType>
void Generator<StreamType>::CanUseCache(bool can_use_cache)
{
can_find_in_cache = can_use_cache;
}
template<class StreamType>
void Generator<StreamType>::CanUseVars(bool can_use_variables)
{
can_use_vars = can_use_variables;
}
template<class StreamType>
void Generator<StreamType>::ResizeFilterTab()
{
if( filter_tab.size() != filter_size )
{
if( filter_tab.size() < filter_size )
{
for(size_t i=filter_tab.size() ; i<filter_size ; ++i)
filter_tab.push_back( new StreamType() );
}
else
{
for(size_t i=filter_size ; i<filter_tab.size() ; ++i)
delete filter_tab[i];
filter_tab.resize(filter_size);
}
}
}
template<class StreamType>
void Generator<StreamType>::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>
void Generator<StreamType>::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>
void Generator<StreamType>::ClearFilterTab()
{
for(size_t i=0 ; i<filter_tab.size() ; ++i)
delete filter_tab[i];
filter_tab.clear();
}
template<class StreamType>
void Generator<StreamType>::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>
void Generator<StreamType>::ClearForStack()
{
for(size_t i=0 ; i<stack_tab.size() ; ++i)
RemoveStackFunData(stack_tab[i]);
}
template<class StreamType>
void Generator<StreamType>::ClearStream(StreamType & str)
{
#ifdef EZC_HAS_SPECIAL_STREAM
str.Clear();
#else
str.str(L"");
#endif
}
template<class StreamType>
void Generator<StreamType>::RemoveStackFunData(Stack & s)
{
if( s.fun_data && s.auto_remove )
{
delete s.fun_data;
s.fun_data = 0;
}
}
template<class StreamType>
void Generator<StreamType>::SkipStream(int stream_index)
{
if( stream_index < 0 )
{
has_skip_stream_index = false;
}
else
{
has_skip_stream_index = true;
skip_stream_index = stream_index;
}
}
template<class StreamType>
template<class CharType>
CharType Generator<StreamType>::ToLower(CharType c)
{
if( c>='A' && c<='Z' )
return c - 'A' + 'a';
return c;
}
template<class StreamType>
bool Generator<StreamType>::AreStreamsEqual(StreamType & str1, StreamType & str2)
{
#ifdef EZC_HAS_SPECIAL_STREAM
return str1.Str() == str2.Str();
#else
return str1.str() == str2.str();
#endif
}
template<class StreamType>
void Generator<StreamType>::SetMax(size_t max_items_, size_t max_for_items_)
{
max_items = max_items_;
max_for_items = max_for_items_;
}
template<class StreamType>
void Generator<StreamType>::RecognizeSpecialChars(bool spec)
{
special_chars = spec;
}
template<class StreamType>
void Generator<StreamType>::TrimWhite(bool trim)
{
trim_white = trim;
}
template<class StreamType>
void Generator<StreamType>::SkipNewLine(bool skip)
{
skip_new_line = skip;
}
template<class StreamType>
void Generator<StreamType>::SetMaxFilters(size_t new_len)
{
// the table will be resized when Generate() method is called
filter_size = new_len;
}
template<class StreamType>
void Generator<StreamType>::SetStackSize(size_t new_stack_size)
{
// the stack will be resized when Generate() method is called
stack_size = new_stack_size;
}
template<class StreamType>
void Generator<StreamType>::Generate()
{
if( is_generator_working )
{
CreateMsg(L"generator busy");
return;
}
output_stream = 0;
output_stream_index.clear();
ClearStream(output_tmp_stream);
if( !output_stream_tab.empty() )
{
if( !has_skip_stream_index || skip_stream_index != 0 )
{
output_stream_index.push_back(0);
output_stream = output_stream_tab[0];
}
}
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();
filter_index = 0;
stack_index = 0;
block_stack_index = 0;
vars.clear();
if( ppattern )
{
try
{
is_generator_working = true;
MakeText( ppattern->item_root );
if( output_stream_index.size() > 1 )
WriteTmpStreamToStreams();
vars.clear();
temp_var.str.clear();
is_generator_working = false;
}
catch(...)
{
is_generator_working = false;
throw;
}
}
}
template<class StreamType>
void Generator<StreamType>::Generate(StreamType & o)
{
output_stream_tab.resize(1);
output_stream_tab[0] = &o;
Generate();
}
template<class StreamType>
void Generator<StreamType>::Generate(std::vector<StreamType> & o)
{
output_stream_tab.resize(o.size());
for(size_t i=0 ; i<o.size() ; ++i)
output_stream_tab[i] = &o[i];
Generate();
}
template<class StreamType>
void Generator<StreamType>::Generate(std::vector<StreamType*> & o)
{
output_stream_tab.resize(o.size());
for(size_t i=0 ; i<o.size() ; ++i)
output_stream_tab[i] = o[i];
Generate();
}
template<class StreamType>
bool Generator<StreamType>::FindInCache(const Item::Function & item_fun,
typename Functions<StreamType>::UserFunction ** function,
Item ** item_block,
std::wstring ** variable)
{
if( item_fun.arg >= 0 )
{
empty_argument.clear();
*variable = &empty_argument;
// it's a numeric function -- an argument to a block e.g.: [1]
if( block_stack_index > 0 )
{
BlockStack & block_stack = block_stack_tab[block_stack_index-1];
if( size_t(item_fun.arg) < block_stack.args.size() )
{
*variable = &block_stack.args[item_fun.arg].str;
last_res = block_stack.args[item_fun.arg].res;
}
}
return true;
}
if( can_find_in_cache )
{
if( item_fun.fun_cache )
{
*function = reinterpret_cast<typename Functions<StreamType>::UserFunction*>(item_fun.fun_cache);
return true;
}
if( item_fun.item_block )
{
*item_block = item_fun.item_block;
return true;
}
}
return false;
}
template<class StreamType>
bool Generator<StreamType>::FindInFunctionsAndBlocks(const std::wstring & name,
typename Functions<StreamType>::UserFunction ** function,
Item ** item_block)
{
if( pfunctions )
{
typename Functions<StreamType>::Iterator i = pfunctions->Find(name);
if( i != pfunctions->End() )
{
*function = &i->second;
return true;
}
}
if( pblocks )
{
Blocks::Iterator i = pblocks->Find(name);
if( i != pblocks->End() )
{
*item_block = &i->second;
return true;
}
}
return false;
}
template<class StreamType>
bool Generator<StreamType>::FindInVariables(const std::wstring & name,
std::wstring ** variable)
{
Vars::iterator i = vars.find(name);
if( i != vars.end() )
{
Var & var = i->second;
*variable = &var.str;
last_res = var.res;
return true;
}
return false;
}
template<class StreamType>
bool Generator<StreamType>::Find(const Item::Function & item_fun,
typename Functions<StreamType>::UserFunction ** function,
Item ** item_block,
std::wstring ** variable)
{
*function = 0;
*item_block = 0;
*variable = 0;
if( FindInCache(item_fun, function, item_block, variable) )
return true;
if( FindInFunctionsAndBlocks(item_fun.name, function, item_block) )
return true;
if( FindInVariables(item_fun.name, variable) )
return true;
CreateUnknownMsg(item_fun.name);
return false;
}
template<class StreamType>
void Generator<StreamType>::CallFunction(typename Functions<StreamType>::UserFunction * function, FunInfo<StreamType> & info)
{
info.Clear();
info.is_for = is_generating_for;
info.is_if = is_generating_if;
info.is_is = is_generating_is;
info.is_normal = is_generating_normal;
info.is_filter = is_generating_filter;
info.iter = info.stack.iter;
(*function)(info);
last_res = info.res;
}
template<class StreamType>
void Generator<StreamType>::CallFunction(typename Functions<StreamType>::UserFunction * function,
std::vector<Var> & parameters,
StreamType & out_stream,
const StreamType & in_stream)
{
if( parameters.empty() )
{
FunInfo<StreamType> info(out_stream, parameters, empty, in_stream, stack_tab[stack_index-1]);
CallFunction(function, info);
}
else
{
FunInfo<StreamType> info(out_stream, parameters, parameters[0].str, in_stream, stack_tab[stack_index-1]);
CallFunction(function, info);
}
}
template<class StreamType>
bool Generator<StreamType>::CallBlock(Item & item_block,
std::vector<Var> & parameters,
StreamType & out_stream)
{
if( block_stack_index >= block_stack_tab.size() )
{
CreateMsg(L"Generator exceeded allowed number of blocks");
return false;
}
StreamType * old_stream = output_stream;
BlockStack & block_stack = block_stack_tab[block_stack_index];
block_stack.was_return = false;
block_stack.args = parameters;
output_stream = block_stack.out_stream;
ClearStream(*output_stream);
block_stack_index += 1;
MakeText(item_block);
#ifdef EZC_HAS_SPECIAL_STREAM
const std::wstring & str = output_stream->Str();
#else
const std::wstring & str = output_stream->str();
#endif
out_stream.write(str.c_str(), str.size());
// last_res is set by [return ...] statement
ClearStream(*output_stream);
output_stream = old_stream;
block_stack_index -= 1;
return true;
}
// return: true if a function or variable was found and called
template<class StreamType>
bool Generator<StreamType>::Call(Item::Function & item_fun,
StreamType & out_stream,
bool clear_out_stream,
const StreamType & in_stream)
{
typename Functions<StreamType>::UserFunction * fun;
Item * item_block;
std::wstring * variable;
std::vector<Var> parameters;
if( clear_out_stream )
ClearStream(out_stream);
if( !Find(item_fun, &fun, &item_block, &variable) )
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.name.empty() )
{
StreamType local_temp_stream;
Call(fun_child, local_temp_stream, true, empty_stream);
#ifdef EZC_HAS_SPECIAL_STREAM
parameters[i].str = local_temp_stream.Str();
#else
parameters[i].str = local_temp_stream.str();
#endif
parameters[i].res = last_res;
}
else
{
parameters[i].str = fun_child.par;
parameters[i].res = !fun_child.par.empty();
}
}
if( fun )
CallFunction(fun, parameters, out_stream, in_stream);
else
if( item_block )
return CallBlock(*item_block, parameters, out_stream);
else
if( variable )
out_stream.write(variable->c_str(), variable->size());
return true;
}
// return: true if a function or variable was found and called
template<class StreamType>
bool Generator<StreamType>::Call(Item::Function & item_fun)
{
return Call(item_fun, stream_temp1, true, empty_stream);
}
template<class StreamType>
wchar_t Generator<StreamType>::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>
const wchar_t * Generator<StreamType>::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>
void Generator<StreamType>::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>
void Generator<StreamType>::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 Generator<StreamType>::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>
void Generator<StreamType>::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>
void Generator<StreamType>::SkipWhite(const wchar_t *& str)
{
while( IsWhite(*str) )
str += 1;
}
template<class StreamType>
size_t Generator<StreamType>::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>
void Generator<StreamType>::WriteTmpStreamToStreams()
{
#ifdef EZC_HAS_SPECIAL_STREAM
const std::wstring & str = output_tmp_stream.Str();
#else
const std::wstring & str = output_tmp_stream.str();
#endif
if( !str.empty() )
{
for(size_t s = 0 ; s < output_stream_index.size() ; ++s)
{
size_t i = output_stream_index[s];
if( i < output_stream_tab.size() )
output_stream_tab[i]->write(str.c_str(), str.size());
}
ClearStream(output_tmp_stream);
}
}
template<class StreamType>
void Generator<StreamType>::CreateMsg(std::wstring & out, const wchar_t * type, const wchar_t * arg)
{
out = commentary_start;
out += L"Ezc runtime error: ";
out += type;
if( arg )
{
out += ' ';
out += arg;
}
out += commentary_stop;
}
template<class StreamType>
void Generator<StreamType>::CreateMsg(const wchar_t * type, const wchar_t * arg)
{
if( output_stream )
{
CreateMsg(temp_msg, type, arg);
output_stream->write(temp_msg.c_str(), temp_msg.size());
}
}
template<class StreamType>
void Generator<StreamType>::CreateMsg(const std::wstring & type, const std::wstring & arg)
{
CreateMsg(type.c_str(), arg.c_str());
}
template<class StreamType>
void Generator<StreamType>::CreateMsg(const std::wstring & type)
{
CreateMsg(type.c_str());
}
template<class StreamType>
void Generator<StreamType>::CreateUnknownMsg(const std::wstring & fun)
{
CreateMsg(L"unknown function", fun.c_str());
}
template<class StreamType>
void Generator<StreamType>::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>
void Generator<StreamType>::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>
void Generator<StreamType>::MakeTextNormal(Item & item)
{
if( item.functions.size() != 1 )
return;
is_generating_normal = true;
if( output_stream )
{
Call(item.functions[0], *output_stream, false, empty_stream);
}
else
{
Call(item.functions[0], stream_temp1, false, empty_stream);
ClearStream(stream_temp1);
}
}
template<class StreamType>
void Generator<StreamType>::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>
void Generator<StreamType>::MakeTextIf(Item & item)
{
if( item.functions.size() != 1 )
return;
is_generating_if = true;
if( !Call(item.functions[0]) )
return;
MakeTextIf_go(item, last_res);
}
template<class StreamType>
void Generator<StreamType>::MakeTextIfno(Item & item)
{
if( item.functions.size() != 1 )
return;
is_generating_if = true;
if( !Call(item.functions[0]) )
return;
MakeTextIf_go(item, !last_res);
}
template<class StreamType>
void Generator<StreamType>::MakeTextIfany(Item & item)
{
std::vector<typename Item::Function>::iterator d = item.functions.begin();
unsigned how_many_true = 0;
is_generating_if = true;
for( ; d != item.functions.end() ; ++d )
{
if( !Call(*d) )
return;
if( last_res )
++how_many_true;
}
MakeTextIf_go(item, how_many_true == item.functions.size());
}
template<class StreamType>
void Generator<StreamType>::MakeTextIfone(Item & item)
{
std::vector<typename Item::Function>::iterator d = item.functions.begin();
unsigned how_many_true = 0;
is_generating_if = true;
for( ; d != item.functions.end() ; ++d )
{
if( Call(*d) )
{
if( last_res )
{
++how_many_true;
break; // there is no sense to go through all functions
}
}
}
MakeTextIf_go(item, how_many_true > 0);
}
template<class StreamType>
void Generator<StreamType>::MakeTextIfanyno(Item & item)
{
std::vector<typename Item::Function>::iterator d = item.functions.begin();
unsigned how_many_true = 0;
is_generating_if = true;
for( ; d != item.functions.end() ; ++d )
{
if( !Call(*d) )
return;
if( last_res )
++how_many_true;
}
MakeTextIf_go(item, how_many_true == 0);
}
template<class StreamType>
void Generator<StreamType>::MakeTextIfoneno(Item & item)
{
std::vector<typename Item::Function>::iterator d = item.functions.begin();
unsigned how_many_false = 0;
is_generating_if = true;
for( ; d != item.functions.end() ; ++d )
{
if( Call(*d) )
{
if( !last_res )
{
++how_many_false;
break;
}
}
}
MakeTextIf_go(item, how_many_false > 0);
}
template<class StreamType>
void Generator<StreamType>::MakeTextIs(Item & item, bool equal)
{
if( item.functions.size() != 2 )
return;
is_generating_is = true;
if( !Call(item.functions[0], stream_temp1, true, empty_stream) )
return;
if( !Call(item.functions[1], stream_temp2, true, empty_stream) )
return;
bool res = AreStreamsEqual(stream_temp1, stream_temp2);
if( !equal )
res = !res;
MakeTextIf_go(item, res);
}
template<class StreamType>
void Generator<StreamType>::MakeTextIs(Item & item)
{
MakeTextIs(item, true);
}
template<class StreamType>
void Generator<StreamType>::MakeTextIsno(Item & item)
{
MakeTextIs(item, false);
}
template<class StreamType>
bool Generator<StreamType>::LookForLastForStatement(size_t & last_for_index)
{
last_for_index = stack_index;
while( last_for_index-- > 0 )
{
if( stack_tab[last_for_index].is_for )
return true;
}
return false;
}
template<class StreamType>
bool Generator<StreamType>::MakeTextIfindexnumber(Item & item, size_t for_index, bool & result)
{
if( item.functions.size() != 1 )
return false;
const wchar_t * number_text = item.functions[0].name.c_str();
const wchar_t * last_char;
size_t number = StrToSize(number_text, &last_char);
if( *last_char != '\0' )
{
CreateMsg(L"if-index: syntax error");
return false;
}
result = (stack_tab[for_index].iter == number);
return true;
}
template<class StreamType>
void Generator<StreamType>::MakeTextIfindex(Item & item)
{
size_t for_index;
if( item.functions.size() != 1 || !LookForLastForStatement(for_index) )
return;
bool result = false;
if( item.functions[0].name == L"odd" )
{
result = (stack_tab[for_index].iter & 1) == 1;
}
else
if( item.functions[0].name == L"even" )
{
result = (stack_tab[for_index].iter & 1) == 0;
}
else
if( item.functions[0].name == L"first" )
{
result = stack_tab[for_index].iter == 0;
}
else
{
if( !MakeTextIfindexnumber(item, for_index, result) )
return;
}
MakeTextIf_go(item, result);
}
template<class StreamType>
void Generator<StreamType>::MakeTextFor(Item & item)
{
if( item.functions.size() != 1 )
return;
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.functions[0].name.c_str(),
L"function exceeded a limit for a [for] statement");
break;
}
// is_generating_for will be changed by next call to MakeText()
// so we should set it in each iterations
is_generating_for = true;
Call(item.functions[0], stream_temp1, true, empty_stream);
if( !last_res )
break;
if( !item.item_tab.empty() )
MakeText( *item.item_tab[0] ); // should be only one item - item_container
}
}
template<class StreamType>
void Generator<StreamType>::MakeTextDefine(Item & item)
{
Item::Function * pfun = 0;
if( !can_use_vars )
{
CreateMsg(L"[def] statement not available");
return;
}
if( item.functions.size() == 1 &&
item.functions[0].parameters.size() == 1 )
{
if( item.functions[0].parameters[0]->name.empty() )
{
// it is [def name "string"]
temp_var.str = item.functions[0].parameters[0]->par;
temp_var.res = !temp_var.str.empty();
vars[item.functions[0].name] = temp_var;
}
else
{
// it is [def name [function "funpar"]]
pfun = item.functions[0].parameters[0];
}
}
if( item.functions.size() == 2 )
{
// is is [def name function "funpar"]
pfun = &item.functions[1];
}
if( pfun )
{
if( Call(*pfun, stream_temp_define, true, empty_stream) )
{
#ifdef EZC_HAS_SPECIAL_STREAM
temp_var.str = stream_temp_define.Str();
#else
temp_var.str = stream_temp_define.str();
#endif
temp_var.res = last_res;
vars[item.functions[0].name] = temp_var;
}
else
{
CreateMsg(L"[def] unknown function/block/variable", pfun->name);
}
}
}
template<class StreamType>
void Generator<StreamType>::MakeTextFilter(Item & item)
{
if( item.functions.size() != 1 )
return;
typename Functions<StreamType>::UserFunction * function;
Item * item_block;
std::wstring * variable;
if( !Find(item.functions[0], &function, &item_block, &variable) )
return;
// IMPROVE ME
// what about blocks and variables?
// may when there is a block we can get the whole content from filter
// and put it as the first argument to the block?
if( !function )
return;
if( filter_index >= filter_tab.size() )
{
CreateMsg(L"Generator exceeded allowed number of filters");
return;
}
StreamType * old_stream = output_stream;
output_stream = filter_tab[filter_index];
ClearStream(*output_stream);
filter_index += 1;
if( !item.item_tab.empty() )
MakeText( *item.item_tab[0] ); // should be only one item - item_container
is_generating_filter = true;
if( old_stream )
{
//Call(function, item.functions[0].params, *old_stream, false, *output_stream);
Call(item.functions[0], *old_stream, false, *output_stream);
}
else
{
//Call(function, item.functions[0].params, stream_temp1, true, *output_stream);
Call(item.functions[0], stream_temp1, true, *output_stream);
ClearStream(stream_temp1);
}
ClearStream(*output_stream);
output_stream = old_stream;
filter_index -= 1;
}
template<class StreamType>
void Generator<StreamType>::MakeEzcStream(Item::Function & fun)
{
if( output_stream_index.size() > 1 )
WriteTmpStreamToStreams();
output_stream_index.clear();
for(size_t i=0 ; i<fun.parameters.size() ; ++i)
{
size_t index = StrToSize(fun.parameters[i]->par.c_str()); // !! CHECK ME or ->name ?
if( index < output_stream_tab.size() )
{
if( !has_skip_stream_index || skip_stream_index != index )
output_stream_index.push_back(index);
}
// we do not print any information if the index is out of range
// because we do not know to which stream is should be printed
}
if( output_stream_index.size() > 1 )
{
ClearStream(output_tmp_stream);
output_stream = &output_tmp_stream;
}
else
if( output_stream_index.size() == 1 )
{
size_t index = output_stream_index[0];
output_stream = output_stream_tab[index];
}
else
{
output_stream = 0;
}
}
template<class StreamType>
void Generator<StreamType>::MakeTextEzc(Item & item)
{
for(size_t i=0 ; i<item.functions.size() ; ++i)
{
if( item.functions[i].name == L"stream" )
MakeEzcStream(item.functions[i]);
else
CreateMsg(L"incorrect argument to [ezc] statement");
// in the future we can use more builtin functions
}
}
template<class StreamType>
void Generator<StreamType>::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.functions.empty() )
{
// output stream in [return] statement is ignored
// this Call() sets last_res which is used later when we return to CallBlock()
Call(item.functions[0], stream_temp1, false, empty_stream);
ClearStream(stream_temp1);
}
}
template<class StreamType>
bool Generator<StreamType>::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>
void Generator<StreamType>::MakeText(Item & item)
{
if( LimitAchieved() )
return;
current_item += 1;
stack_index += 1;
is_generating_for = false;
is_generating_if = false;
is_generating_is = false;
is_generating_normal = false;
is_generating_filter = false;
stack_tab[stack_index-1].Clear();
if ( item.type == Item::item_text ) MakeItemText(item);
else if( item.type == Item::item_container )MakeTextContainer(item);
else if( item.type == Item::item_normal ) MakeTextNormal(item);
else if( item.type == Item::item_if ) MakeTextIf(item);
else if( item.type == Item::item_ifno ) MakeTextIfno(item);
else if( item.type == Item::item_ifany ) MakeTextIfany(item);
else if( item.type == Item::item_ifone ) MakeTextIfone(item);
else if( item.type == Item::item_ifanyno ) MakeTextIfanyno(item);
else if( item.type == Item::item_ifoneno ) MakeTextIfoneno(item);
else if( item.type == Item::item_ifindex ) MakeTextIfindex(item);
else if( item.type == Item::item_is ) MakeTextIs(item);
else if( item.type == Item::item_isno ) MakeTextIsno(item);
else if( item.type == Item::item_def ) MakeTextDefine(item);
else if( item.type == Item::item_for ) MakeTextFor(item);
else if( item.type == Item::item_filter ) MakeTextFilter(item);
else if( item.type == Item::item_ezc ) MakeTextEzc(item);
else if( item.type == Item::item_return ) MakeTextReturn(item);
else if( item.type == Item::item_err )
CreateMsg(L"a wrong directive");
RemoveStackFunData(stack_tab[stack_index-1]);
stack_index -=1;
}
} // namespace Ezc
#endif