1559 lines
27 KiB
C++
1559 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::HasFileAtBeginning(const char * path, const char * file)
|
|
{
|
|
for(; *path && *file; ++path, ++file)
|
|
{
|
|
if( *path != *file )
|
|
return false;
|
|
}
|
|
|
|
if( *file != 0 )
|
|
return false;
|
|
|
|
// "\" is from a dos path syntax
|
|
if( *path==0 || *path=='\\' || *path=='/' )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
".." is not allowed in the file path
|
|
you cannot go up from your template directory
|
|
*/
|
|
bool Pattern::IsFileCorrect(const char * name)
|
|
{
|
|
while( *name )
|
|
{
|
|
if( HasFileAtBeginning(name, "..") )
|
|
return false;
|
|
|
|
// looking for the next slash of backslash
|
|
while( *name && *name!='\\' && *name!='/' )
|
|
name += 1;
|
|
|
|
// skipping the slash (or backslash)
|
|
if( *name )
|
|
name += 1;
|
|
}
|
|
|
|
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( !IsFileCorrect(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;
|
|
|
|
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;
|
|
|
|
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
|
|
|