ezc/src/ezc.cpp

1537 lines
27 KiB
C++

/*
* 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-2010, 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.
*/
#include "ezc.h"
#ifdef EZC_USE_WINIX_LOGGER
#include "core/log.h"
#endif
namespace Ezc
{
/*
*
* Pattern
*
*
*/
Pattern::Pattern()
{
Clear();
commentary_start = "<!-- ";
commentary_stop = " -->";
allow_include = true;
delete_white_text_items = false;
include_level_max = 100;
}
void Pattern::CreateMsg(std::ostringstream & o, const char * type, const char * arg)
{
o << commentary_start << "ezc: " << type;
if( arg )
o << ' ' << arg << ' ';
o << commentary_stop;
}
std::string Pattern::CreateMsg(const char * type, const char * arg)
{
std::ostringstream buffer;
CreateMsg(buffer, type, arg);
return buffer.str();
}
void Pattern::ParseFile(const std::string & file_name)
{
ParseFile( file_name.c_str() );
}
void Pattern::ParseFile(const char * file_name)
{
item_root.text = file_name;
include_level = 0;
CreateTreeReadIncludeSkipAllowFlag(item_root);
}
void Pattern::ParseString(const std::string & str)
{
ParseString( str.c_str() );
}
void Pattern::ParseString(const char * str)
{
itext = str;
include_level = 0;
CreateTree(item_root);
}
void Pattern::Directory(const char * dir, const char * dir2)
{
directory.clear();
directory2.clear();
if( dir )
directory = dir;
if( dir2 )
directory2 = dir2;
}
void Pattern::Directory(const std::string & dir)
{
directory = dir;
directory2.clear();
}
void Pattern::Directory(const std::string & dir, const std::string & dir2)
{
directory = dir;
directory2 = dir2;
}
void Pattern::AllowInclude(bool allow)
{
allow_include = allow;
}
void Pattern::DeleteWhiteTextItems(bool del)
{
delete_white_text_items = del;
}
void Pattern::SetCommentary(const char * com_start, const char * com_stop)
{
commentary_start = com_start;
commentary_stop = com_stop;
}
void Pattern::SetCommentary(const std::string & com_start, const std::string & com_stop)
{
commentary_start = com_start;
commentary_stop = com_stop;
}
void Pattern::SetIncludeMax(int include_max)
{
include_level_max = include_max;
}
void Pattern::Clear()
{
item_root.Clear();
}
bool Pattern::CheckFileName(const char * name)
{
size_t i;
// we do not allow a slash or backslash in the name
for(i=0 ; name[i]!=0 ; ++i)
if( name[i] == '/' || name[i] == '\\' )
return false;
// "." is not allowed
if( i==1 && name[0]=='.' )
return false;
// ".." is not allowed too
if( i==2 && name[0]=='.' && name[1]=='.' )
return false;
return true;
}
/*
'name' must be a relative path - without a slash or backslash
*/
std::string Pattern::ReadFile(const std::string & name)
{
return ReadFile(name.c_str());
}
/*
'name' must be a relative path - without a slash or backslash
*/
std::string Pattern::ReadFile(const char * name)
{
if( !CheckFileName(name) )
return CreateMsg("incorrect file name:", name);
std::string result;
if( !ReadFileFromDir(directory, name, result) )
if( !ReadFileFromDir(directory2, name, result) )
return CreateMsg("can't open: ", name);
return result;
}
bool Pattern::ReadFileFromDir(const std::string & dir, const char * name, std::string & result)
{
if( dir.empty() )
return false;
std::string file_name(dir);
file_name += '/';
file_name += name;
std::ifstream file(file_name.c_str());
if( !file )
return false;
std::getline(file, result, '\0');
#ifdef EZC_USE_WINIX_LOGGER
log << log3 << "EZC: read pattern: " << file_name << logend;
#endif
return true;
}
int Pattern::ReadCharInText()
{
if( *itext==0 || *itext=='[' )
return -1;
if( *itext == '\\' )
{
if( *(itext+1)=='\\' || *(itext+1)=='[' || *(itext+1)==']' )
++itext;
}
return *(itext++);
}
bool Pattern::IsWhite(int c )
{
// 13 (\r) is from a dos file at the end of a line (\r\n)
// 160 is an unbreakable space
if( c==' ' || c=='\t' || c==13 || c==160 || c==10 )
return true;
return false;
}
void Pattern::SkipWhite()
{
while( IsWhite(*itext) )
++itext;
}
void Pattern::CheckWhiteAndDelete(std::string & s)
{
size_t i;
if( s.empty() )
return;
//for(i=0 ; i<s.size() && (s[i]==10 || IsWhite(s[i])) ; ++i); // !!! skasowac
for(i=0 ; i<s.size() && IsWhite(s[i]) ; ++i);
if( i == s.size() )
s.clear(); // the whole string consists of white characters
}
bool Pattern::IsNameChar(int c)
{
return ((c>='a' && c<='z') ||
(c>='A' && c<='Z') ||
(c>='0' && c<='9') ||
c=='_' || c=='-' || c=='.' || c=='#');
}
bool Pattern::IsDigit(int c)
{
return (c>='0' && c<='9');
}
bool Pattern::IsPositiveNumber(const std::string & str)
{
size_t i;
for(i=0 ; i<str.size() ; ++i)
if( !IsDigit(str[i]) )
return false;
return true;
}
// reading an expression name or a function name
bool Pattern::ReadName(std::string & name)
{
name.clear();
SkipWhite();
while( IsNameChar(*itext) )
{
name += *itext;
++itext;
}
return !name.empty();
}
// string can have a quote character (escaped with a backslash) e.g. "sample text \"with quotes\""
// use \\ to insert one backslash
bool Pattern::ReadString(std::string & str)
{
str.clear();
SkipWhite();
// string is signed by its first quote character (")
if( *itext != '\"' )
return false;
++itext;
while( *itext && *itext!='\"' )
{
if( itext[0]=='\\' && itext[1]=='\"' )
{
str += '\"';
itext += 2;
}
else
if( itext[0]=='\\' && itext[1]=='\\' )
{
str += '\\';
itext += 2;
}
else
{
str += *itext;
itext += 1;
}
}
if( *itext == '\"' )
++itext;
// if there was not a quote at the end we do not report an error
return true;
}
bool Pattern::ReadFunction(Pattern::Item::Function & function)
{
SkipWhite();
function.name.clear();
function.params.clear();
if( !ReadName(function.name) )
return false;
ReadParams(function);
return true;
}
bool Pattern::ReadParams(Pattern::Item::Function & function)
{
std::string temp;
function.params.clear();
while( ReadString(temp) )
function.params.push_back(temp);
return !function.params.empty();
}
bool Pattern::ReadFunctions(Item & item)
{
Item::Function function;
item.functions.clear();
while( ReadFunction(function) )
item.functions.push_back(function);
return !item.functions.empty();
}
void Pattern::CreateTreeReadItemDirectiveCheckEnding(Item & item)
{
SkipWhite();
if( *itext != ']' )
{
item.type = Item::item_err;
while( *itext!=0 && *itext!=']' )
++itext;
}
if( *itext == ']' )
++itext;
}
void Pattern::ReadDirectiveIf(Item & item)
{
item.type = Item::item_if;
ReadFunctions(item);
if( item.functions.size() != 1 )
item.type = Item::item_err;
}
void Pattern::ReadDirectiveIfno(Item & item)
{
item.type = Item::item_ifno;
ReadFunctions(item);
if( item.functions.size() != 1 )
item.type = Item::item_err;
}
void Pattern::ReadDirectiveIfany(Item & item)
{
item.type = Item::item_ifany;
if( !ReadFunctions(item) )
item.type = Item::item_err;
}
void Pattern::ReadDirectiveIfone(Item & item)
{
item.type = Item::item_ifone;
if( !ReadFunctions(item) )
item.type = Item::item_err;
}
void Pattern::ReadDirectiveIfanyno(Item & item)
{
item.type = Item::item_ifanyno;
if( !ReadFunctions(item) )
item.type = Item::item_err;
}
void Pattern::ReadDirectiveIfoneno(Item & item)
{
item.type = Item::item_ifoneno;
if( !ReadFunctions(item) )
item.type = Item::item_err;
}
void Pattern::ReadDirectiveIs(Item & item)
{
item.type = Item::item_is;
ReadFunctions(item);
if( item.functions.size() != 2 )
item.type = Item::item_err;
}
void Pattern::ReadDirectiveIsno(Item & item)
{
item.type = Item::item_isno;
ReadFunctions(item);
if( item.functions.size() != 2 )
item.type = Item::item_err;
}
void Pattern::ReadDirectiveIfindex(Item & item)
{
Item::Function function;
item.type = Item::item_err;
item.functions.clear();
// reading function name
if( !ReadName(function.name) )
return;
item.functions.push_back(function);
// reading: odd, even, first or a number (without quotes)
if( !ReadName(function.name) )
return;
if( function.name != "odd" && function.name != "even" &&
function.name != "first" && !IsPositiveNumber(function.name) )
return;
item.functions.push_back(function);
item.type = Item::item_ifindex;
}
void Pattern::ReadDirectiveFor(Item & item)
{
item.type = Item::item_for;
ReadFunctions(item);
if( item.functions.size() != 1 )
item.type = Item::item_err;
}
void Pattern::ReadDirectiveComment(Item & item)
{
item.type = Item::item_comment;
// skipping the comment
while( *itext && *itext!=']' )
++itext;
}
void Pattern::ReadDirectiveInclude(Item & item)
{
item.type = Item::item_include;
if( !ReadString(item.text) )
item.type = Item::item_err;
}
void Pattern::ReadDirectiveDef(Item & item)
{
item.type = Item::item_err;
ReadFunctions(item);
if( item.functions.size() == 1 )
{
if( item.functions[0].params.size() == 1 )
{
// this is: [def variable "value"]
item.type = Item::item_def;
}
}
else
if( item.functions.size() == 2 )
{
if( item.functions[0].params.empty() && item.functions[1].params.empty())
{
// this is:
// [def variable2 variable1], or
// [def function2 function1]
item.type = Item::item_def;
}
}
}
// user defined directive
void Pattern::ReadDirectiveNormal(const std::string & name, Item & item)
{
Item::Function function;
function.name = name;
ReadParams(function);
item.functions.clear();
item.functions.push_back(function);
item.type = Item::item_normal;
}
void Pattern::CreateTreeReadItemDirective(Item & item)
{
std::string name;
++itext;
ReadName(name);
if ( name == "if" ) ReadDirectiveIf(item);
else if( name == "if-no" ) ReadDirectiveIfno(item);
else if( name == "if-any" ) ReadDirectiveIfany(item);
else if( name == "if-one" ) ReadDirectiveIfone(item);
else if( name == "if-any-no" ) ReadDirectiveIfanyno(item);
else if( name == "if-one-no" ) ReadDirectiveIfoneno(item);
else if( name == "if-index" ) ReadDirectiveIfindex(item);
else if( name == "is" ) ReadDirectiveIs(item);
else if( name == "is-no" ) ReadDirectiveIsno(item);
else if( name == "end" ) item.type = Item::item_end;
else if( name == "else" ) item.type = Item::item_else;
else if( name == "for" ) ReadDirectiveFor(item);
else if( name == "include" ) ReadDirectiveInclude(item);
else if( name == "def" ) ReadDirectiveDef(item);
else if( name == "#" ) ReadDirectiveComment(item);
else
ReadDirectiveNormal(name, item);
CreateTreeReadItemDirectiveCheckEnding(item);
}
void Pattern::CreateTreeReadItemText(Item & item)
{
int c;
while( (c = ReadCharInText()) != -1 )
item.text += c;
if( delete_white_text_items )
CheckWhiteAndDelete(item.text);
item.type = Item::item_text;
}
bool Pattern::CreateTreeReadItem(Item & item)
{
item.Clear();
if( *itext == '[' )
{
CreateTreeReadItemDirective(item);
return true;
}
else
if( *itext )
{
CreateTreeReadItemText(item);
return true;
}
// the end of the string
return false;
}
void Pattern::CreateTreeReadInclude(Item & item)
{
if( !allow_include )
return;
CreateTreeReadIncludeSkipAllowFlag(item);
}
void Pattern::CreateTreeReadIncludeSkipAllowFlag(Item & item)
{
if( item.text.empty() )
return;
if( include_level > include_level_max )
{
#ifdef EZC_USE_WINIX_LOGGER
log << log1 << "EZC: \"include\" directive has reached the maximum level" << logend;
#endif
return;
}
++include_level;
std::string file_text = ReadFile( item.text );
const char * itext_old = itext;
itext = file_text.c_str();
CreateTree(item);
itext = itext_old;
--include_level;
}
void Pattern::CreateTreeReadIf(Item & item)
{
Item * pitem = item.AddItem();
CreateTree(*pitem);
if( pitem->LastItemType() == Item::item_else )
{
pitem->DeleteLastItem();
pitem = item.AddItem();
CreateTree(*pitem);
}
if( pitem->LastItemType() == Item::item_end )
pitem->DeleteLastItem();
}
void Pattern::CreateTreeReadFor(Item & item)
{
Item * pitem = item.AddItem();
CreateTree(*pitem);
if( pitem->LastItemType() == Item::item_end )
pitem->DeleteLastItem();
}
void Pattern::CreateTree(Item & item)
{
item.Clear();
item.type = Item::item_container;
Item item_temp;
while( CreateTreeReadItem(item_temp) )
{
if( item_temp.type == Item::item_comment )
continue;
Item * pitem = item.AddItem(item_temp);
if( item_temp.type==Item::item_end || item_temp.type==Item::item_else )
return;
if( pitem->type == Item::item_if ||
pitem->type == Item::item_ifno ||
pitem->type == Item::item_ifany ||
pitem->type == Item::item_ifone ||
pitem->type == Item::item_ifanyno ||
pitem->type == Item::item_ifoneno ||
pitem->type == Item::item_ifindex ||
pitem->type == Item::item_is ||
pitem->type == Item::item_isno )
CreateTreeReadIf(*pitem);
if( pitem->type == Item::item_for )
CreateTreeReadFor(*pitem);
if( pitem->type == Item::item_include )
CreateTreeReadInclude(*pitem);
}
}
/*
*
* Pattern::Item
*
*
*/
Pattern::Item * Pattern::Item::AddItem(const Pattern::Item * porg)
{
Item * pitem;
if( porg )
pitem = new Item(*porg);
else
pitem = new Item();
item_table.push_back(pitem);
return pitem;
}
Pattern::Item * Pattern::Item::AddItem(const Pattern::Item & porg)
{
return AddItem(&porg);
}
void Pattern::Item::ClearItems()
{
std::vector<Item*>::iterator i = item_table.begin();
for( ; i != item_table.end() ; ++i )
delete *i;
item_table.clear();
}
void Pattern::Item::Clear()
{
ClearItems();
type = item_none;
text.clear();
functions.clear();
}
Pattern::Item::Type Pattern::Item::LastItemType()
{
if( item_table.empty() )
return item_none;
return item_table.back()->type;
}
void Pattern::Item::DeleteLastItem()
{
if( item_table.empty() )
return;
delete item_table.back();
item_table.pop_back();
}
Pattern::Item::Item()
{
type = item_none;
}
Pattern::Item::Item(const Pattern::Item & i) : type(i.type), text(i.text), functions(i.functions)
{
CopyItemTable(i);
}
Pattern::Item & Pattern::Item::operator=(const Pattern::Item & i)
{
type = i.type;
text = i.text;
functions = i.functions;
CopyItemTable(i);
return *this;
}
void Pattern::Item::CopyItemTable(const Pattern::Item & item)
{
std::vector<Item*>::const_iterator i = item.item_table.begin();
for( ; i != item.item_table.end() ; ++i)
AddItem( *i );
}
Pattern::Item::~Item()
{
ClearItems();
}
/*
*
* Info
*
*
*/
void Info::Clear()
{
res = false; // false by default
iter = 0;
}
Info::Info(std::ostringstream & o, std::vector<std::string> & pars, const std::string & p) : out(o), params(pars), par(p)
{
Clear();
}
/*
*
* Functions
*
*
*/
Functions::Function::Function()
{
type = Functions::variable;
user_function = 0;
iter = 0;
is_for = false;
is_running = false;
}
void Functions::Insert(const std::string & key, UserFunction ufunction)
{
Function f;
f.type = function;
f.user_function = ufunction;
functions_table[key] = f;
}
void Functions::Insert(const std::string & key, const char * var)
{
Function f;
f.type = variable;
f.variable = var;
functions_table[key] = f;
}
void Functions::Insert(const std::string & key, const std::string & var)
{
Function f;
f.type = variable;
f.variable = var;
functions_table[key] = f;
}
bool Functions::Find(const std::string & key, Function ** fun)
{
FunctionsTable::iterator i = functions_table.find( key );
if( i == functions_table.end() )
return false;
*fun = &(i->second);
return true;
}
void Functions::Clear()
{
functions_table.clear();
}
/*
*
* Generator
*
*
*/
Generator::Generator()
{
output_stream = 0;
pattern = 0;
functions = 0;
max_items = 50000;
max_for_items = 5000;
}
Generator::Generator(std::ostringstream & o, Pattern & p, Functions & f)
{
output_stream = &o;
pattern = &p;
functions = &f;
max_items = 50000;
max_for_items = 5000;
}
void Generator::Set(std::ostringstream & o, Pattern & p, Functions & f)
{
output_stream = &o;
pattern = &p;
functions = &f;
}
void Generator::Set(std::ostringstream & o)
{
output_stream = &o;
}
void Generator::Set(Pattern & p)
{
pattern = &p;
}
void Generator::Set(Functions & f)
{
functions = &f;
}
void Generator::SetMax(int max_items_, int max_for_items_)
{
max_items = max_items_;
max_for_items = max_for_items_;
}
void Generator::Generate()
{
if( !output_stream || !pattern || !functions )
return;
break_generating = false;
current_item = 0;
MakeText( pattern->item_root );
}
bool Generator::Find(const std::string & key, Functions::Function ** function)
{
if( !functions->Find(key, function) )
{
pattern->CreateMsg(*output_stream, "can't find", key.c_str() );
return false;
}
return true;
}
void Generator::CallUserFunction(Functions::Function * function, Info & info)
{
if( function->is_running )
{
// recurrences are not allowed
pattern->CreateMsg(*output_stream, "the function is being executed" );
return;
}
function->is_running = true;
(function->user_function)(info);
function->is_running = false;
}
void Generator::CallVariable(Functions::Function * function, Info & info)
{
info.res = !function->variable.empty();
}
void Generator::Call(Functions::Function * function, Info & info)
{
info.Clear();
info.iter = function->iter;
if( function->type == Functions::function )
CallUserFunction(function, info);
else
CallVariable(function, info);
}
// return: true if a function or variable was found and called
bool Generator::Call(const std::string & name, Info & info, Functions::Function ** pfun)
{
Functions::Function * function;
if( Find(name, &function) )
{
Call(function, info);
if( pfun )
*pfun = function;
return true;
}
return false;
}
// return: true if a function or variable was found and called
bool Generator::Call(Pattern::Item::Function & function, bool * info_res, Functions::Function ** pfun)
{
bool called;
if( function.params.empty() )
{
Info info(*output_stream, function.params, empty);
called = Call(function.name, info, pfun);
if( info_res )
*info_res = info.res;
}
else
{
Info info(*output_stream, function.params, function.params[0]);
called = Call(function.name, info, pfun);
if( info_res )
*info_res = info.res;
}
return called;
}
void Generator::MakeTextContainer(Pattern::Item & item)
{
std::vector<Pattern::Item*>::iterator i = item.item_table.begin();
for( ; i != item.item_table.end() && !break_generating ; ++i )
MakeText(**i);
}
void Generator::MakeTextNormal(Pattern::Item & item)
{
if( item.functions.size() != 1 )
return;
Functions::Function * pfun;
bool called = Call(item.functions[0], 0, &pfun);
if( called && pfun->type == Functions::variable )
*output_stream << pfun->variable;
}
void Generator::MakeTextIf_go(Pattern::Item & item, bool result)
{
if( result )
{
if( item.item_table.size() > 0 )
MakeText( *item.item_table[0] );
}
else
{
// second element can be (or not -- it's from [else])
if( item.item_table.size() > 1 )
MakeText( *item.item_table[1] );
}
}
void Generator::MakeTextIf(Pattern::Item & item)
{
bool info_res;
if( item.functions.size() != 1 )
return;
if( !Call(item.functions[0], &info_res) )
return;
MakeTextIf_go(item, info_res);
}
void Generator::MakeTextIfno(Pattern::Item & item)
{
bool info_res;
if( item.functions.size() != 1 )
return;
if( !Call(item.functions[0], &info_res) )
return;
MakeTextIf_go(item, !info_res);
}
void Generator::MakeTextIfany(Pattern::Item & item)
{
std::vector<Pattern::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());
}
void Generator::MakeTextIfone(Pattern::Item & item)
{
std::vector<Pattern::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);
}
void Generator::MakeTextIfanyno(Pattern::Item & item)
{
std::vector<Pattern::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);
}
void Generator::MakeTextIfoneno(Pattern::Item & item)
{
std::vector<Pattern::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);
}
void Generator::MakeTextIs(Pattern::Item & item)
{
bool info_res1, info_res2;
bool res = false;
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);
}
void Generator::MakeTextIsno(Pattern::Item & item)
{
bool info_res1, info_res2;
bool res = false;
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);
}
bool Generator::MakeTextIfindexnumber(Pattern::Item & item, Functions::Function * function, bool & result)
{
if( item.functions.size() != 2 )
return false;
const char * number_text = item.functions[1].name.c_str();
char * last_char;
int number = (int)strtol(number_text, &last_char, 10); // !! dodac obsluge bialych znakow na koncu (na poczatku chyba jest od strtol)
if( *last_char != '\0' )
{
pattern->CreateMsg(*output_stream, "if-index: syntax error");
return false;
}
result = (function->iter == number);
return true;
}
void Generator::MakeTextIfindex(Pattern::Item & item)
{
if( item.functions.size() != 2 )
return;
// we actually don't call a function (or variable) here
// but only reading the iterator
Functions::Function * function;
if( !Find(item.functions[0].name, &function) )
return;
bool result = false;
if( item.functions[1].name == "odd" )
{
result = (function->iter & 1) == 1;
}
else
if( item.functions[1].name == "even" )
{
result = (function->iter & 1) == 0;
}
else
if( item.functions[1].name == "first" )
{
result = function->iter == 0;
}
else
{
if( !MakeTextIfindexnumber(item, function, result) )
return;
}
MakeTextIf_go(item, result);
}
void Generator::MakeTextForLoop(Pattern::Item & item, Functions::Function * function)
{
bool info_res;
for( ; !break_generating ; function->iter += 1 )
{
if( function->iter >= max_for_items )
{
pattern->CreateMsg(*output_stream, item.functions[0].name.c_str(),
"function exceeded a limit for a [for] statement");
break;
}
Call(item.functions[0], &info_res);
if( !info_res )
break;
if( !item.item_table.empty() )
MakeText( *item.item_table[0] ); // should be only one item - item_container
}
}
void Generator::MakeTextFor(Pattern::Item & item)
{
if( item.functions.size() != 1 )
return;
Functions::Function * function;
if( !Find(item.functions[0].name, &function) )
return;
if( function->is_for )
{
pattern->CreateMsg(*output_stream, item.functions[0].name.c_str(),
"this function is already used in a [for] statement");
return;
}
function->is_for = true;
function->iter = 0;
MakeTextForLoop(item, function);
function->is_for = false;
function->iter = 0;
}
void Generator::MakeTextDefine(Pattern::Item & item)
{
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 )
{
Functions::Function * function;
if( Find(item.functions[1].name, &function) )
{
if( function->type == Functions::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);
}
}
}
}
void Generator::MakeText(Pattern::Item & item)
{
if( break_generating )
return;
if( ++current_item > max_items )
{
break_generating = true;
pattern->CreateMsg(*output_stream, "Generator exceeded allowed number of elements");
return;
}
if ( item.type == Pattern::Item::item_text ) *output_stream << item.text;
else if( item.type == Pattern::Item::item_container ) MakeTextContainer(item);
else if( item.type == Pattern::Item::item_normal ) MakeTextNormal(item);
else if( item.type == Pattern::Item::item_if ) MakeTextIf(item);
else if( item.type == Pattern::Item::item_ifno ) MakeTextIfno(item);
else if( item.type == Pattern::Item::item_ifany ) MakeTextIfany(item);
else if( item.type == Pattern::Item::item_ifone ) MakeTextIfone(item);
else if( item.type == Pattern::Item::item_ifanyno ) MakeTextIfanyno(item);
else if( item.type == Pattern::Item::item_ifoneno ) MakeTextIfoneno(item);
else if( item.type == Pattern::Item::item_ifindex ) MakeTextIfindex(item);
else if( item.type == Pattern::Item::item_is ) MakeTextIs(item);
else if( item.type == Pattern::Item::item_isno ) MakeTextIsno(item);
else if( item.type == Pattern::Item::item_for ) MakeTextFor(item);
else if( item.type == Pattern::Item::item_def ) MakeTextDefine(item);
else if( item.type == Pattern::Item::item_err )
pattern->CreateMsg(*output_stream, "a wrong directive");
}
} // namespace Ezc