ezc/src/generator.h

971 lines
21 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-2011, 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>
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();
void SetMax(int max_items_, int 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);
// 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;
int current_item;
int max_items;
int max_for_items;
bool special_chars;
bool trim_white;
bool skip_new_line;
// an empty string for info objects
// when there is no any parameters
const std::wstring empty;
// a stack item for 'for' statements
// currently only one item (iteration)
struct StackItem
{
int iter;
};
// a stack for 'for' statements
std::vector<StackItem> stack_tab;
bool Find(const Item::Function & item_fun, typename Functions<StreamType>::Function ** function);
void Call(typename Functions<StreamType>::Function * function, FunInfo<StreamType> & info);
bool Call(Item::Function & function, bool * info_res = 0, typename Functions<StreamType>::Function ** pfun = 0);
void Call(typename Functions<StreamType>::Function * function, std::vector<std::wstring> & params, bool * info_res = 0);
void CallUserFunction(typename Functions<StreamType>::Function * function, FunInfo<StreamType> & info);
void CallVariable(typename Functions<StreamType>::Function * function, FunInfo<StreamType> & info);
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);
int StrToInt(const wchar_t * str, const wchar_t ** str_end);
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);
void MakeTextIsno(Item & item);
void MakeTextDefine(Item & item);
void MakeText(Item & item);
}; // class Generator
template<class StreamType>
Generator<StreamType>::Generator()
{
output_stream = 0;
pattern = 0;
functions = 0;
max_items = 50000;
max_for_items = 5000;
special_chars = false;
trim_white = false;
skip_new_line = false;
}
template<class StreamType>
void Generator<StreamType>::SetMax(int max_items_, int 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>::Generate(StreamType & o, Pattern & p, Functions<StreamType> & f)
{
output_stream = &o;
pattern = &p;
functions = &f;
break_generating = false;
current_item = 0;
MakeText( pattern->item_root );
}
template<class StreamType>
void Generator<StreamType>::Generate(StreamType & o, Pattern & p)
{
output_stream = &o;
pattern = &p;
functions = 0;
break_generating = false;
current_item = 0;
MakeText( pattern->item_root );
}
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>::CallUserFunction(typename Functions<StreamType>::Function * function, FunInfo<StreamType> & info)
{
if( function->is_running )
{
// recurrences are not allowed
CreateMsg(L"the function is being executed");
return;
}
function->is_running = true;
(function->user_function)(info);
function->is_running = false;
}
template<class StreamType>
void Generator<StreamType>::CallVariable(typename Functions<StreamType>::Function * function, FunInfo<StreamType> & info)
{
info.res = !function->variable.empty();
}
template<class StreamType>
void Generator<StreamType>::Call(typename Functions<StreamType>::Function * function, FunInfo<StreamType> & info)
{
info.Clear();
if( stack_tab.empty() )
info.iter = 0;
else
info.iter = stack_tab.back().iter;
if( function->type == Functions<StreamType>::function )
CallUserFunction(function, info);
else
CallVariable(function, info);
}
// return: true if a function or variable was found and called
template<class StreamType>
bool Generator<StreamType>::Call(Item::Function & item_fun, bool * info_res, typename Functions<StreamType>::Function ** pfun)
{
typename Functions<StreamType>::Function * fun;
bool called = false;
if( item_fun.params.empty() )
{
FunInfo<StreamType> info(*output_stream, item_fun.params, empty);
if( Find(item_fun, &fun) )
{
Call(fun, info);
called = true;
}
if( info_res )
*info_res = info.res;
}
else
{
FunInfo<StreamType> info(*output_stream, item_fun.params, item_fun.params[0]);
if( Find(item_fun, &fun) )
{
Call(fun, info);
called = true;
}
if( info_res )
*info_res = info.res;
}
if( called && pfun )
*pfun = fun;
return called;
}
// it calls the function pointed by pfun
template<class StreamType>
void Generator<StreamType>::Call(typename Functions<StreamType>::Function * function, std::vector<std::wstring> & params, bool * info_res)
{
if( params.empty() )
{
FunInfo<StreamType> info(*output_stream, params, empty);
Call(function, info);
if( info_res )
*info_res = info.res;
}
else
{
FunInfo<StreamType> info(*output_stream, params, params[0]);
Call(function, info);
if( info_res )
*info_res = info.res;
}
}
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>
int Generator<StreamType>::StrToInt(const wchar_t * str, const wchar_t ** str_end)
{
int res = 0;
SkipWhite(str);
// 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(const wchar_t * type, const wchar_t * arg)
{
pattern->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;
typename Functions<StreamType>::Function * pfun;
bool called = Call(item.functions[0], 0, &pfun);
if( called && pfun->type == Functions<StreamType>::variable )
(*output_stream) << pfun->variable;
}
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)
{
bool info_res;
if( item.functions.size() != 1 )
return;
if( !Call(item.functions[0], &info_res) )
return;
MakeTextIf_go(item, info_res);
}
template<class StreamType>
void Generator<StreamType>::MakeTextIfno(Item & item)
{
bool info_res;
if( item.functions.size() != 1 )
return;
if( !Call(item.functions[0], &info_res) )
return;
MakeTextIf_go(item, !info_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;
bool info_res;
for( ; d != item.functions.end() ; ++d )
{
if( !Call(*d, &info_res) )
return;
if( info_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;
bool info_res;
for( ; d != item.functions.end() ; ++d )
{
if( Call(*d, &info_res) && info_res )
{
// there is no sense to go through all functions
++how_many_true;
break;
}
}
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;
bool info_res;
for( ; d != item.functions.end() ; ++d )
{
if( !Call(*d, &info_res) )
return;
if( info_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;
bool info_res;
for( ; d != item.functions.end() ; ++d )
{
if( Call(*d, &info_res) && !info_res )
++how_many_false;
}
MakeTextIf_go(item, how_many_false > 0);
}
template<class StreamType>
void Generator<StreamType>::MakeTextIs(Item & item)
{
bool info_res1, info_res2;
if( item.functions.size() != 2 )
return;
if( !Call(item.functions[0], &info_res1) )
return;
if( !Call(item.functions[1], &info_res2) )
return;
MakeTextIf_go(item, info_res1 == info_res2);
}
template<class StreamType>
void Generator<StreamType>::MakeTextIsno(Item & item)
{
bool info_res1, info_res2;
if( item.functions.size() != 2 )
return;
if( !Call(item.functions[0], &info_res1) )
return;
if( !Call(item.functions[1], &info_res2) )
return;
MakeTextIf_go(item, info_res1 != info_res2);
}
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;
int number = StrToInt(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)
{
bool info_res;
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;
}
Call(function, item.functions[0].params, &info_res);
if( !info_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;
StackItem s;
s.iter = 0;
stack_tab.push_back(s);
MakeTextForLoop(item, function);
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>::MakeText(Item & item)
{
if( break_generating )
return;
if( ++current_item > max_items )
{
break_generating = true;
CreateMsg(L"Generator exceeded allowed number of elements");
return;
}
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_err )
CreateMsg(L"a wrong directive");
}
} // namespace Ezc
#endif