1286 lines
27 KiB
C++
Executable File
1286 lines
27 KiB
C++
Executable File
/*
|
|
* This file is a part of EZC -- Easy templating in C++
|
|
* and is distributed under the (new) BSD licence.
|
|
* Author: Tomasz Sowa <t.sowa@ttmath.org>
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2007-2012, 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 "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 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);
|
|
|
|
// the main methods for generating
|
|
void Generate(StreamType & o, Pattern & p, Functions<StreamType> & f);
|
|
|
|
// this method is used only if the pattern has cached functions
|
|
void Generate(StreamType & o, Pattern & p);
|
|
|
|
|
|
private:
|
|
|
|
StreamType * output_stream;
|
|
Pattern * pattern;
|
|
Functions<StreamType> * functions;
|
|
|
|
// 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 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;
|
|
|
|
// temporary streams used in [if..] [is...] or [for...]
|
|
StreamType stream_temp1, stream_temp2;
|
|
|
|
// whether last function has put its content in case_sensitive (set FunInfo::case_sensitive)
|
|
bool last_case_sensitive;
|
|
|
|
// 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 item for [for] statements
|
|
// currently only one item (iterator)
|
|
struct StackItem
|
|
{
|
|
size_t iter;
|
|
FunData * fun_data;
|
|
|
|
StackItem()
|
|
{
|
|
iter = 0;
|
|
fun_data = 0;
|
|
}
|
|
};
|
|
|
|
// a stack for [for] statements
|
|
std::vector<StackItem> stack_tab;
|
|
|
|
|
|
void ResizeFilterTab();
|
|
void ClearFilterTab();
|
|
void ClearForStack();
|
|
void ClearStream(StreamType & str);
|
|
|
|
template<class CharType>
|
|
CharType ToLower(CharType c);
|
|
|
|
template<class CharType>
|
|
bool StrCmpNc(CharType * str1, CharType * str2);
|
|
|
|
bool AreStreamsEqual(StreamType & str1, StreamType & str2, bool case_sensitive);
|
|
|
|
|
|
bool Find(const Item::Function & item_fun, typename Functions<StreamType>::Function ** function);
|
|
|
|
void Call(typename Functions<StreamType>::Function * function, FunInfo<StreamType> & info);
|
|
void Call(typename Functions<StreamType>::Function * function, std::vector<std::wstring> & params,
|
|
StreamType & out_stream, bool clear_out_stream, const StreamType & in_stream);
|
|
|
|
bool Call(Item::Function & item_fun, StreamType & out_stream, bool clear_out_stream);
|
|
bool Call(Item::Function & function);
|
|
|
|
|
|
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);
|
|
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);
|
|
|
|
void CreateMsg(StreamType & 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);
|
|
|
|
void MakeTextIf_go(Item & item, bool result);
|
|
bool MakeTextIfindexnumber(Item & item, 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 MakeTextForLoop(Item & item, typename Functions<StreamType>::Function * function);
|
|
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 MakeText(Item & item);
|
|
|
|
void Generate(StreamType * o, Pattern * p, Functions<StreamType> * f);
|
|
|
|
}; // class Generator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
Generator<StreamType>::Generator()
|
|
{
|
|
output_stream = 0;
|
|
pattern = 0;
|
|
functions = 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;
|
|
}
|
|
|
|
|
|
template<class StreamType>
|
|
Generator<StreamType>::Generator(const Generator<StreamType> & n)
|
|
{
|
|
operator=(n);
|
|
}
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
Generator<StreamType> & Generator<StreamType>::operator=(const Generator<StreamType> & n)
|
|
{
|
|
output_stream = n.output_stream;
|
|
pattern = n.pattern;
|
|
functions = n.functions;
|
|
|
|
max_items = n.max_items;
|
|
max_for_items = n.max_for_items;
|
|
filter_size = n.filter_size;
|
|
special_chars = n.special_chars;
|
|
trim_white = n.trim_white;
|
|
skip_new_line = n.skip_new_line;
|
|
|
|
// don't copy filter tab
|
|
ClearFilterTab();
|
|
|
|
// don't copy [for] stack
|
|
ClearForStack();
|
|
|
|
// don't copy 'is_generator_working' flag
|
|
is_generator_working = false;
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
Generator<StreamType>::~Generator()
|
|
{
|
|
ClearFilterTab();
|
|
ClearForStack();
|
|
}
|
|
|
|
|
|
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>::ClearFilterTab()
|
|
{
|
|
for(size_t i=0 ; i<filter_tab.size() ; ++i)
|
|
delete filter_tab[i];
|
|
|
|
filter_tab.clear();
|
|
}
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::ClearForStack()
|
|
{
|
|
for(size_t i=0 ; i<stack_tab.size() ; ++i)
|
|
delete stack_tab[i].fun_data;
|
|
|
|
stack_tab.clear();
|
|
}
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::ClearStream(StreamType & str)
|
|
{
|
|
#ifdef EZC_HAS_SPECIAL_STREAM
|
|
str.Clear();
|
|
#else
|
|
str.str(L"");
|
|
#endif
|
|
}
|
|
|
|
|
|
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>
|
|
template<class CharType>
|
|
bool Generator<StreamType>::StrCmpNc(CharType * str1, CharType * str2)
|
|
{
|
|
for( ; ToLower(*str1) == ToLower(*str2) ; ++str1, ++str2)
|
|
if( *str1 == 0 )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
template<class StreamType>
|
|
bool Generator<StreamType>::AreStreamsEqual(StreamType & str1, StreamType & str2, bool case_sensitive)
|
|
{
|
|
if( case_sensitive )
|
|
{
|
|
#ifdef EZC_HAS_SPECIAL_STREAM
|
|
return str1.Str() == str2.Str();
|
|
#else
|
|
return str1.str() == str2.str();
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef EZC_HAS_SPECIAL_STREAM
|
|
return StrCmpNc(str1.CStr(), str2.CStr());
|
|
#else
|
|
return StrCmpNc(str1.str().c_str(), str2.str().c_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>::Generate(StreamType * o, Pattern * p, Functions<StreamType> * f)
|
|
{
|
|
if( is_generator_working )
|
|
{
|
|
if( o )
|
|
CreateMsg(*o, L"generator busy");
|
|
|
|
return;
|
|
}
|
|
|
|
output_stream = o;
|
|
pattern = p;
|
|
functions = f;
|
|
|
|
break_generating = false;
|
|
current_item = 0;
|
|
ClearForStack();
|
|
ResizeFilterTab();
|
|
filter_index = 0;
|
|
|
|
if( output_stream && pattern )
|
|
{
|
|
try
|
|
{
|
|
is_generator_working = true;
|
|
MakeText( pattern->item_root );
|
|
is_generator_working = false;
|
|
}
|
|
catch(...)
|
|
{
|
|
is_generator_working = false;
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::Generate(StreamType & o, Pattern & p, Functions<StreamType> & f)
|
|
{
|
|
Generate(&o, &p, &f);
|
|
}
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::Generate(StreamType & o, Pattern & p)
|
|
{
|
|
Generate(&o, &p, 0);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
bool Generator<StreamType>::Find(const Item::Function & item_fun, typename Functions<StreamType>::Function ** function)
|
|
{
|
|
if( !functions )
|
|
{
|
|
if( item_fun.fun_cache )
|
|
{
|
|
*function = reinterpret_cast<typename Functions<StreamType>::Function*>(item_fun.fun_cache);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
CreateUnknownMsg(item_fun.name);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if( !functions->Find(item_fun.name, function) )
|
|
{
|
|
CreateUnknownMsg(item_fun.name);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::Call(typename Functions<StreamType>::Function * 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;
|
|
|
|
if( info.is_for )
|
|
{
|
|
if( !stack_tab.empty() )
|
|
info.iter = stack_tab.back().iter;
|
|
|
|
if( stack_tab.size() >= 2 )
|
|
info.last_iter = stack_tab[stack_tab.size()-2].iter;
|
|
}
|
|
else
|
|
{
|
|
if( !stack_tab.empty() )
|
|
info.last_iter = stack_tab.back().iter;
|
|
}
|
|
|
|
if( !stack_tab.empty() )
|
|
info.fun_data = stack_tab.back().fun_data;
|
|
|
|
if( stack_tab.size() >= 2 )
|
|
info.last_fun_data = stack_tab[stack_tab.size()-2].fun_data;
|
|
|
|
if( function->type == Functions<StreamType>::function )
|
|
{
|
|
(function->user_function)(info);
|
|
}
|
|
else
|
|
{
|
|
info.out << function->variable;
|
|
info.res = !function->variable.empty();
|
|
}
|
|
|
|
if( info.fun_data )
|
|
{
|
|
if( !stack_tab.empty() )
|
|
{
|
|
if( stack_tab.back().fun_data != info.fun_data )
|
|
{
|
|
delete stack_tab.back().fun_data;
|
|
stack_tab.back().fun_data = info.fun_data;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delete info.fun_data;
|
|
}
|
|
}
|
|
|
|
last_res = info.res;
|
|
last_case_sensitive = info.case_sensitive;
|
|
}
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::Call(typename Functions<StreamType>::Function * function,
|
|
std::vector<std::wstring> & params,
|
|
StreamType & out_stream,
|
|
bool clear_out_stream,
|
|
const StreamType & in_stream)
|
|
{
|
|
if( clear_out_stream )
|
|
ClearStream(out_stream);
|
|
|
|
if( params.empty() )
|
|
{
|
|
FunInfo<StreamType> info(out_stream, params, empty, in_stream);
|
|
Call(function, info);
|
|
}
|
|
else
|
|
{
|
|
FunInfo<StreamType> info(out_stream, params, params[0], in_stream);
|
|
Call(function, info);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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)
|
|
{
|
|
typename Functions<StreamType>::Function * fun;
|
|
|
|
if( !Find(item_fun, &fun) )
|
|
return false;
|
|
|
|
Call(fun, item_fun.params, out_stream, clear_out_stream, empty_stream);
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
{
|
|
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( 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>
|
|
void Generator<StreamType>::TrimWhite(const wchar_t *& start, const wchar_t *& end)
|
|
{
|
|
while( start != end && pattern->IsWhite(*start) )
|
|
++start;
|
|
|
|
while( start != end && pattern->IsWhite(*(end-1)) )
|
|
--end;
|
|
}
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::SkipWhite(const wchar_t *& str)
|
|
{
|
|
while( pattern->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);
|
|
*str_end = str;
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::CreateMsg(StreamType & out, const wchar_t * type, const wchar_t * arg)
|
|
{
|
|
pattern->CreateMsg(temp_msg, type, arg);
|
|
out.write(temp_msg.c_str(), temp_msg.size());
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::CreateMsg(const wchar_t * type, const wchar_t * arg)
|
|
{
|
|
CreateMsg(*output_stream, type, arg);
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
Call(item.functions[0], *output_stream, false);
|
|
}
|
|
|
|
|
|
|
|
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 one of these two functions has set case_sensitive to false
|
|
// then we compare in case insensitive mode
|
|
bool case_sensitive = true;
|
|
|
|
if( !Call(item.functions[0], stream_temp1, true) )
|
|
return;
|
|
|
|
if( !last_case_sensitive )
|
|
case_sensitive = false;
|
|
|
|
if( !Call(item.functions[1], stream_temp2, true) )
|
|
return;
|
|
|
|
if( !last_case_sensitive )
|
|
case_sensitive = false;
|
|
|
|
bool res = AreStreamsEqual(stream_temp1, stream_temp2, case_sensitive);
|
|
|
|
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>::MakeTextIfindexnumber(Item & item, bool & result)
|
|
{
|
|
if( item.functions.size() != 1 || stack_tab.empty() )
|
|
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.back().iter == number);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::MakeTextIfindex(Item & item)
|
|
{
|
|
if( item.functions.size() != 1 )
|
|
return;
|
|
|
|
bool result = false;
|
|
|
|
if( !stack_tab.empty() )
|
|
{
|
|
if( item.functions[0].name == L"odd" )
|
|
{
|
|
result = (stack_tab.back().iter & 1) == 1;
|
|
}
|
|
else
|
|
if( item.functions[0].name == L"even" )
|
|
{
|
|
result = (stack_tab.back().iter & 1) == 0;
|
|
}
|
|
else
|
|
if( item.functions[0].name == L"first" )
|
|
{
|
|
result = stack_tab.back().iter == 0;
|
|
}
|
|
else
|
|
{
|
|
if( !MakeTextIfindexnumber(item, result) )
|
|
return;
|
|
}
|
|
}
|
|
|
|
MakeTextIf_go(item, result);
|
|
}
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::MakeTextForLoop(Item & item, typename Functions<StreamType>::Function * function)
|
|
{
|
|
for( ; !break_generating ; stack_tab.back().iter += 1 )
|
|
{
|
|
if( stack_tab.back().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(function, item.functions[0].params, 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>::MakeTextFor(Item & item)
|
|
{
|
|
if( item.functions.size() != 1 )
|
|
return;
|
|
|
|
typename Functions<StreamType>::Function * function;
|
|
|
|
if( !Find(item.functions[0], &function) )
|
|
return;
|
|
|
|
stack_tab.push_back(StackItem());
|
|
|
|
MakeTextForLoop(item, function);
|
|
|
|
delete stack_tab.back().fun_data;
|
|
stack_tab.erase(--stack_tab.end());
|
|
}
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::MakeTextDefine(Item & item)
|
|
{
|
|
if( !functions )
|
|
{
|
|
CreateMsg(L"[def] statement is not available when used without functions set");
|
|
return;
|
|
}
|
|
|
|
if( item.functions.size() == 1 )
|
|
{
|
|
// inserting a new variable
|
|
if( item.functions[0].params.size() == 1 )
|
|
functions->Insert(item.functions[0].name, item.functions[0].params[0]);
|
|
}
|
|
else
|
|
if( item.functions.size() == 2 )
|
|
{
|
|
typename Functions<StreamType>::Function * function;
|
|
|
|
if( Find(item.functions[1], &function) )
|
|
{
|
|
if( function->type == Functions<StreamType>::function )
|
|
{
|
|
// inserting a new function
|
|
functions->Insert(item.functions[0].name, function->user_function);
|
|
}
|
|
else
|
|
{
|
|
// inserting a new variable (the value is copied)
|
|
functions->Insert(item.functions[0].name, function->variable);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::MakeTextFilter(Item & item)
|
|
{
|
|
if( item.functions.size() != 1 )
|
|
return;
|
|
|
|
typename Functions<StreamType>::Function * function;
|
|
|
|
if( !Find(item.functions[0], &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;
|
|
Call(function, item.functions[0].params, *old_stream, false, *output_stream);
|
|
|
|
output_stream = old_stream;
|
|
filter_index -= 1;
|
|
}
|
|
|
|
|
|
|
|
template<class StreamType>
|
|
void Generator<StreamType>::MakeText(Item & item)
|
|
{
|
|
if( break_generating )
|
|
return;
|
|
|
|
if( ++current_item > max_items )
|
|
{
|
|
break_generating = true;
|
|
CreateMsg(L"Generator exceeded allowed number of elements");
|
|
return;
|
|
}
|
|
|
|
is_generating_for = false;
|
|
is_generating_if = false;
|
|
is_generating_is = false;
|
|
is_generating_normal = false;
|
|
is_generating_filter = false;
|
|
|
|
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_for ) MakeTextFor(item);
|
|
else if( item.type == Item::item_def ) MakeTextDefine(item);
|
|
else if( item.type == Item::item_filter ) MakeTextFilter(item);
|
|
else if( item.type == Item::item_err )
|
|
CreateMsg(L"a wrong directive");
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Ezc
|
|
|
|
|
|
#endif
|