ezc/src/ezc.cpp

919 lines
16 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@slimaczek.pl>
*/
/*
* Copyright (c) 2007, 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"
namespace Ezc
{
std::string CreateMsg(const char * type, const char * arg)
{
std::ostringstream buffer;
buffer << "<!-- ezc: " << type << " ";
if( arg )
buffer << arg << " ";
buffer << "-->";
return buffer.str();
}
/*
*
* Pattern
*
*
*/
void Pattern::ParseFile(std::string file_name)
{
file_name.insert(file_name.begin(), '\"');
item_root.directives.clear();
item_root.directives.push_back( file_name );
CreateTreeReadInclude(item_root);
}
void Pattern::ParseFile(const char * file_name)
{
ParseFile( std::string(file_name) );
}
void Pattern::Directory(const char * d)
{
directory = d;
}
void Pattern::Directory(const std::string & d)
{
directory = d;
}
bool Pattern::CheckFileName(const char * name)
{
// very simple testing -- this path 'some..name' is incorrect as well
// (in the future it can be changed)
for( ; *name ; ++name )
{
if( *name=='.' && *(name+1)=='.' )
return false;
}
return true;
}
std::string Pattern::ReadFile(const char * name)
{
// for security reason we can't open a file which has two dots
// somewhere in its name '..'
if( !CheckFileName(name) )
return CreateMsg("incorrect file name:", name);
std::string file_name;
if( name[0]!='\\' && name[0]!='/' )
{
// name is a relative path
file_name += directory;
if( !file_name.empty() )
file_name += '/';
}
file_name += name;
std::ifstream file(file_name.c_str());
if( !file )
return CreateMsg("can't open:", name);
// or we can give the whole path here (file_name)
else
{
std::string result;
std::getline(file, result, '\0');
return result;
}
}
int Pattern::ReadCharInText()
{
if( *itext==0 || *itext=='[' )
return -1;
if( *itext == '\\' )
{
if( *(itext+1)=='\\' || *(itext+1)=='[' || *(itext+1)==']' )
++itext;
}
return *(itext++);
}
void Pattern::SkipWhiteCharacters()
{
while( *itext==' ' || *itext=='\t' )
++itext;
}
std::string Pattern::ReadDirective()
{
std::string directive;
SkipWhiteCharacters();
while( (*itext>='a' && *itext<='z') ||
(*itext>='A' && *itext<='Z') ||
(*itext>='0' && *itext<='9') ||
*itext=='_' || *itext=='-' || *itext=='.' )
{
directive += *itext;
++itext;
}
return directive;
}
std::string Pattern::ReadString()
{
std::string directive;
SkipWhiteCharacters();
if( *itext != '\"' )
return directive;
// string is signed by its first character equal (")
directive += '\"';
for( ++itext ; *itext && *itext!='\"' && *itext!='\n' ; ++itext )
directive += *itext;
if( *itext != '\"' )
// the second quotation mark (") is missing
directive.clear();
else
++itext;
// the second quotation mark (") we don't add into the 'directive'
return directive;
}
std::string Pattern::ReadDirectiveOrString()
{
SkipWhiteCharacters();
if( *itext == '\"' )
{
// we've got string
return ReadString();
}
else
{
return ReadDirective();
}
}
void Pattern::CreateTreeReadItemDirectiveCheckEnding(Item & item)
{
SkipWhiteCharacters();
if( *itext != ']' )
{
item.type = Item::item_err;
while( *itext!=0 && *itext!=']' )
++itext;
}
if( *itext == ']' )
++itext;
}
void Pattern::ReadDirectiveIfany(Item & item)
{
item.type = Item::item_ifany;
while( true )
{
std::string directive = ReadDirective();
if( directive.empty() )
break;
item.directives.push_back(directive);
}
if( item.directives.empty() )
item.type = Item::item_err;
}
void Pattern::ReadDirectiveIfone(Item & item)
{
ReadDirectiveIfany(item);
if( item.type == Item::item_ifany )
item.type = Item::item_ifone;
}
void Pattern::ReadDirectiveIs(Item & item)
{
item.type = Item::item_is;
std::string directive = ReadDirective();
if( !directive.empty() )
{
item.directives.push_back(directive);
directive = ReadDirectiveOrString();
if( !directive.empty() )
item.directives.push_back(directive);
}
if( item.directives.size() != 2 )
item.type = Item::item_err;
}
void Pattern::ReadDirectiveIfindex(Item & item)
{
item.type = Item::item_ifindex;
std::string directive = ReadDirective();
if( !directive.empty() )
{
item.directives.push_back(directive);
directive = ReadDirective();
if( !directive.empty() )
item.directives.push_back(directive);
}
if( item.directives.size() != 2 )
item.type = Item::item_err;
}
void Pattern::ReadDirectiveFor(Item & item)
{
item.type = Item::item_for;
std::string directive = ReadDirective();
if( !directive.empty() )
item.directives.push_back(directive);
else
item.type = Item::item_err;
}
void Pattern::ReadDirectiveInclude(Item & item)
{
item.type = Item::item_include;
std::string directive = ReadString();
if( !directive.empty() )
item.directives.push_back(directive);
else
item.type = Item::item_err;
}
void Pattern::CreateTreeReadItemDirective(Item & item)
{
++itext;
std::string directive = ReadDirective();
if( directive == "if-any" )
ReadDirectiveIfany(item);
else
if( directive == "if-one" )
ReadDirectiveIfone(item);
else
if( directive == "is" )
ReadDirectiveIs(item);
else
if( directive == "if-index" )
ReadDirectiveIfindex(item);
else
if( directive == "end" )
item.type = Item::item_end;
else
if( directive == "else" )
item.type = Item::item_else;
else
if( directive == "for" )
ReadDirectiveFor(item);
else
if( directive == "include" )
ReadDirectiveInclude(item);
else
{
// user defined
item.directives.push_back(directive);
item.type = Item::item_normal;
}
CreateTreeReadItemDirectiveCheckEnding(item);
}
void Pattern::CreateTreeReadItemText(Item & item)
{
int c;
while( (c = ReadCharInText()) != -1 )
item.text += c;
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( item.directives.empty() || item.directives[0].empty() || item.directives[0][0]!='\"' )
return;
std::string file_text = ReadFile( item.directives[0].c_str()+1 );
const char * itext_copy = itext;
itext = file_text.c_str();
CreateTree(item);
itext = itext_copy;
}
void Pattern::CreateTreeReadIf(Item & item)
{
Item * pitem = item.AddItem();
CreateTree(*pitem);
if( pitem->LastItemType() == Item::item_else )
{
pitem->DeleteLastItem();
pitem = item.AddItem();
CreateTree(*pitem);
}
pitem->DeleteLastItem(); // it deletes [end] from the tree
}
void Pattern::CreateTreeReadFor(Item & item)
{
Item * pitem = item.AddItem();
CreateTree(*pitem);
pitem->DeleteLastItem(); // it deletes [end] from the tree
}
void Pattern::CreateTree(Item & item)
{
item.Clear();
item.type = Item::item_container;
Item item_temp;
while( CreateTreeReadItem(item_temp) )
{
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_ifany ||
pitem->type == Item::item_ifone ||
pitem->type == Item::item_ifindex ||
pitem->type == Item::item_is )
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();
directives.clear();
}
Pattern::Item::ItemType 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.erase( item_table.end() - 1 );
}
Pattern::Item::Item()
{
type = item_none;
}
Pattern::Item::Item(const Pattern::Item & i) : type(i.type), text(i.text), directives(i.directives)
{
CopyItemTable(i);
}
Pattern::Item & Pattern::Item::operator=(const Pattern::Item & i)
{
type = i.type;
text = i.text;
directives = i.directives;
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
*
*
*/
Info::Info()
{
result = false;
iter = 0;
}
/*
*
* Generator
*
*
*/
std::string & Generator::Generate(Pattern & data)
{
otext.clear();
MakeText( data.item_root );
return otext;
}
std::string & Generator::String()
{
return otext;
}
void Generator::Insert(const std::string & key, UserFunction ufunction)
{
UserInfo ui;
ui.user_function = ufunction;
ui.iter = 0;
user_info_table.insert( std::make_pair(key, ui) );
}
void Generator::MakeTextContainer(Pattern::Item & item)
{
std::vector<Pattern::Item*>::iterator i = item.item_table.begin();
for( ; i != item.item_table.end() ; ++i )
MakeText(**i);
}
bool Generator::Find(const std::string & key, UserInfo ** user_info)
{
UserInfoTable::iterator i = user_info_table.find( key );
if( i == user_info_table.end() )
{
otext += CreateMsg("can't find", key.c_str() );
return false;
}
*user_info = &(i->second);
return true;
}
void Generator::MakeTextNormal(Pattern::Item & item)
{
if( item.directives.size() != 1 )
return;
UserInfo * user_info;
if( Find(item.directives[0], &user_info) )
{
Info info;
(user_info->user_function)(info);
otext += info.text;
}
}
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::MakeTextIfany(Pattern::Item & item)
{
std::vector<std::string>::iterator d = item.directives.begin();
unsigned how_many_true = 0;
for( ; d != item.directives.end() ; ++d )
{
UserInfo * user_info;
if( !Find(*d, &user_info) )
return;
Info info;
(user_info->user_function)(info);
if( !info.text.empty() || info.result )
++how_many_true;
}
MakeTextIf_go(item, how_many_true == item.directives.size() );
}
void Generator::MakeTextIfone(Pattern::Item & item)
{
std::vector<std::string>::iterator d = item.directives.begin();
int how_many_true = 0;
for( ; d != item.directives.end() ; ++d )
{
UserInfo * user_info;
if( !Find(*d, &user_info) )
return;
Info info;
(user_info->user_function)(info);
if( !info.text.empty() || info.result )
++how_many_true;
}
MakeTextIf_go(item, how_many_true > 0 );
}
void Generator::MakeTextIs(Pattern::Item & item)
{
if( item.directives.size() != 2 )
return;
UserInfo * user_info;
if( !Find(item.directives[0], &user_info) )
return;
bool result = false;
Info info1, info2;
(user_info->user_function)(info1);
if( !item.directives[1].empty() && item.directives[1][0]=='\"' )
{
if( std::strcmp(info1.text.c_str(), item.directives[1].c_str()+1) == 0 )
result = true;
}
else
{
if( Find(item.directives[1], &user_info) )
{
(user_info->user_function)(info2);
if( info1.result==info2.result && info1.text==info2.text )
result = true;
}
}
MakeTextIf_go(item, result);
}
bool Generator::MakeTextIfindexnumber(Pattern::Item & item, UserInfo * user_info, bool & result)
{
if( item.directives.size() != 2 )
return false;
const char * number_text = item.directives[1].c_str();
char * last_char;
int number = (int)strtol(number_text, &last_char, 10);
if( *last_char == '\0' )
{
if( user_info->iter == number )
result = true;
}
else
{
otext += CreateMsg("if-index: syntax error");
return false;
}
return true;
}
void Generator::MakeTextIfindex(Pattern::Item & item)
{
if( item.directives.size() != 2 )
return;
UserInfo * user_info;
bool result = false;
if( !Find(item.directives[0], &user_info) )
return;
if( item.directives[1] == "odd" )
{
if( ( user_info->iter & 1) == 1 )
result = true;
}
else
if( item.directives[1] == "even" )
{
if( ( user_info->iter & 1) == 0 )
result = true;
}
else
if( item.directives[1] == "first" )
{
if( user_info->iter == 0 )
result = true;
}
else
{
if( !MakeTextIfindexnumber(item, user_info, result) )
return;
}
MakeTextIf_go(item, result);
}
void Generator::MakeTextFor(Pattern::Item & item)
{
if( item.directives.size() != 1 )
return;
UserInfo * user_info;
if( !Find(item.directives[0], &user_info) )
return;
Info info;
user_info->iter = 0;
while( true )
{
(user_info->user_function)(info);
if( info.text.empty() && !info.result )
break;
if( item.item_table.size() > 0 )
MakeText( *item.item_table[0] );
++info.iter;
user_info->iter = info.iter;
}
}
void Generator::MakeText(Pattern::Item & item)
{
switch( item.type )
{
case Pattern::Item::item_text:
otext += item.text;
break;
case Pattern::Item::item_container:
MakeTextContainer(item);
break;
case Pattern::Item::item_normal:
MakeTextNormal(item);
break;
case Pattern::Item::item_ifany:
MakeTextIfany(item);
break;
case Pattern::Item::item_ifone:
MakeTextIfone(item);
break;
case Pattern::Item::item_ifindex:
MakeTextIfindex(item);
break;
case Pattern::Item::item_is:
MakeTextIs(item);
break;
case Pattern::Item::item_for:
MakeTextFor(item);
break;
case Pattern::Item::item_err:
otext += CreateMsg("a wrong directive");
break;
default:
break;
}
}
void Generator::Clear()
{
user_info_table.clear();
otext.clear();
}
} // namespace Ezc