changed: the interface in this version is not the same as previous

added: class Functions, you can define your own set of functions and then
       put them into the Generator
added: variables
       parser is able to parse the "def" directive:
       [def variable "value"]
       [def variable another_variable]
       [def variable another_function]
       variables are put into a Functions object
added: class Info has a reference to std::ostringstream (out)
added: limit for a "for" directive
       limit for the whole tree (when genereting)
       this is to protect for a case when someone makes an infinite loop
changed: many small changes


git-svn-id: svn://ttmath.org/publicrep/ezc/trunk@88 e52654a7-88a9-db11-a3e9-0013d4bc506e
This commit is contained in:
Tomasz Sowa 2008-12-10 01:46:10 +00:00
parent bf585d3716
commit 54be9c0489
2 changed files with 438 additions and 163 deletions

View File

@ -5,7 +5,7 @@
*/ */
/* /*
* Copyright (c) 2007, Tomasz Sowa * Copyright (c) 2007-2008, Tomasz Sowa
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -41,21 +41,29 @@ namespace Ezc
{ {
void CreateMsg(std::ostringstream & o, const char * type, const char * arg)
{
o << "<!-- ezc: " << type << " ";
if( arg )
o << arg << " ";
o << "-->";
}
std::string CreateMsg(const char * type, const char * arg) std::string CreateMsg(const char * type, const char * arg)
{ {
std::ostringstream buffer; std::ostringstream buffer;
buffer << "<!-- ezc: " << type << " "; CreateMsg(buffer, type, arg);
if( arg )
buffer << arg << " ";
buffer << "-->";
return buffer.str(); return buffer.str();
} }
/* /*
this method splits the input file name into a directory and a file name this method splits the input file name into a directory and a file name
e.g e.g
@ -64,37 +72,37 @@ return buffer.str();
dir = "/this/is/dummy" dir = "/this/is/dummy"
file = "file.ezc" file = "file.ezc"
*/ */
void SplitUnixDirectory(const char * name, std::string & dir, std::string & file) void SplitUnixDirectory(const char * name, std::string & dir, std::string & file)
{ {
dir.clear(); dir.clear();
file.clear(); file.clear();
int i; int i;
for( i=0 ; name[i] != 0 ; ++i ); for( i=0 ; name[i] != 0 ; ++i );
if( i == 0 ) if( i == 0 )
return; return;
for( --i ; i>=0 && name[i]!='\\' && name[i]!='/' ; --i ); for( --i ; i>=0 && name[i]!='\\' && name[i]!='/' ; --i );
if( i < 0 ) if( i < 0 )
{ {
file.assign(name); file.assign(name);
} }
else else
{ {
if( i == 0 ) if( i == 0 )
// we're leaving one '/' in the directory // we're leaving one '/' in the directory
// (we have only the root directory) // (we have only the root directory)
dir.assign(name, 1); dir.assign(name, 1);
else else
dir.assign(name, i); dir.assign(name, i);
file.assign(name + i + 1 ); file.assign(name + i + 1 );
} }
return; return;
} }
void SplitUnixDirectory(const std::string & name, std::string & dir, std::string & file) void SplitUnixDirectory(const std::string & name, std::string & dir, std::string & file)
{ {
@ -234,7 +242,9 @@ return directive;
std::string Pattern::ReadString()
std::string Pattern::ReadString(bool skip_first_quote)
{ {
std::string directive; std::string directive;
@ -244,7 +254,8 @@ std::string directive;
return directive; return directive;
// string is signed by its first character equal (") // string is signed by its first character equal (")
directive += '\"'; if( !skip_first_quote )
directive += '\"';
for( ++itext ; *itext && *itext!='\"' && *itext!='\n' ; ++itext ) for( ++itext ; *itext && *itext!='\"' && *itext!='\n' ; ++itext )
directive += *itext; directive += *itext;
@ -397,6 +408,27 @@ void Pattern::ReadDirectiveInclude(Item & item)
} }
void Pattern::ReadDirectiveDef(Item & item)
{
item.type = Item::item_def;
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::CreateTreeReadItemDirective(Item & item) void Pattern::CreateTreeReadItemDirective(Item & item)
{ {
++itext; ++itext;
@ -427,6 +459,9 @@ void Pattern::CreateTreeReadItemDirective(Item & item)
if( directive == "include" ) if( directive == "include" )
ReadDirectiveInclude(item); ReadDirectiveInclude(item);
else else
if( directive == "def" )
ReadDirectiveDef(item);
else
if( directive == "#" ) if( directive == "#" )
ReadDirectiveComment(item); ReadDirectiveComment(item);
else else
@ -658,13 +693,108 @@ Pattern::Item::~Item()
* *
*/ */
Info::Info() void Info::Clear()
{ {
// default settings indicate 'false'
result = false; result = false;
iter = 0; out_string.clear();
iter = 0;
} }
Info::Info(std::ostringstream & o) : out(o)
{
Clear();
}
bool Info::IsTrue() const
{
if( result || !out_string.empty() )
return true;
return false;
}
bool Info::IsFalse() const
{
return !IsTrue();
}
/*
*
* 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
@ -674,70 +804,114 @@ Info::Info()
std::string & Generator::Generate(Pattern & data) bool Generator::Find(const std::string & key, Functions::Function ** function)
{ {
otext.clear(); if( !functions.Find(key, function) )
MakeText( data.item_root ); {
CreateMsg(output_stream, "can't find", key.c_str() );
return otext; return false;
}
return true;
} }
std::string & Generator::String()
void Generator::CallUserFunction(Functions::Function * function, Info & info)
{ {
return otext; // przetestowac to jeszcze !!!! (wczesniej sprawdzenie bylo w Find) !!
if( function->is_running )
{
// recurrences are not allowed
CreateMsg(output_stream, "the function is currently running" );
return;
}
function->is_running = true;
(function->user_function)(info);
function->is_running = false;
} }
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::CallVariable(Functions::Function * function, Info & info)
{
info.out_string = function->variable;
} }
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;
}
Generator::Generator(std::ostringstream & o, Pattern & p, Functions & f) : output_stream(o), pattern(p), functions(f), info1(o), info2(o)
{
}
void Generator::Generate()
{
loop = 0;
MakeText( pattern.item_root );
}
void Generator::MakeTextContainer(Pattern::Item & item) void Generator::MakeTextContainer(Pattern::Item & item)
{ {
std::vector<Pattern::Item*>::iterator i = item.item_table.begin(); std::vector<Pattern::Item*>::iterator i = item.item_table.begin();
for( ; i != item.item_table.end() ; ++i ) 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() ); MakeText(**i);
return false;
if( loop < 0 )
break;
} }
*user_info = &(i->second);
return true;
} }
void Generator::MakeTextNormal(Pattern::Item & item) void Generator::MakeTextNormal(Pattern::Item & item)
{ {
if( item.directives.size() != 1 ) if( item.directives.size() != 1 )
return; return;
UserInfo * user_info; Call(item.directives[0], info1);
if( Find(item.directives[0], &user_info) ) if( !info1.out_string.empty() )
{ output_stream << info1.out_string;
Info info;
(user_info->user_function)(info);
otext += info.text;
}
} }
@ -764,18 +938,11 @@ void Generator::MakeTextIfany(Pattern::Item & item)
for( ; d != item.directives.end() ; ++d ) for( ; d != item.directives.end() ; ++d )
{ {
UserInfo * user_info; if( !Call(*d, info1) )
if( !Find(*d, &user_info) )
return; return;
Info info; if( info1.IsTrue() )
(user_info->user_function)(info);
if( !info.text.empty() || info.result )
++how_many_true; ++how_many_true;
} }
MakeTextIf_go(item, how_many_true == item.directives.size() ); MakeTextIf_go(item, how_many_true == item.directives.size() );
@ -789,15 +956,7 @@ void Generator::MakeTextIfone(Pattern::Item & item)
for( ; d != item.directives.end() ; ++d ) for( ; d != item.directives.end() ; ++d )
{ {
UserInfo * user_info; if( Call(*d, info1) && info1.IsTrue() )
if( !Find(*d, &user_info) )
return;
Info info;
(user_info->user_function)(info);
if( !info.text.empty() || info.result )
++how_many_true; ++how_many_true;
} }
@ -805,32 +964,28 @@ void Generator::MakeTextIfone(Pattern::Item & item)
} }
void Generator::MakeTextIs(Pattern::Item & item) void Generator::MakeTextIs(Pattern::Item & item)
{ {
if( item.directives.size() != 2 ) if( item.directives.size() != 2 )
return; return;
UserInfo * user_info; if( !Call(item.directives[0], info1) )
if( !Find(item.directives[0], &user_info) )
return; return;
bool result = false; bool result = false;
Info info1, info2;
(user_info->user_function)(info1);
if( !item.directives[1].empty() && item.directives[1][0]=='\"' ) if( !item.directives[1].empty() && item.directives[1][0]=='\"' )
{ {
if( std::strcmp(info1.text.c_str(), item.directives[1].c_str()+1) == 0 ) // second directive is a string
if( std::strcmp(info1.out_string.c_str(), item.directives[1].c_str()+1) == 0 )
result = true; result = true;
} }
else else
{ {
if( Find(item.directives[1], &user_info) ) if( Call(item.directives[1], info2) )
{ {
(user_info->user_function)(info2); if( info1.result==info2.result && info1.out_string==info2.out_string )
if( info1.result==info2.result && info1.text==info2.text )
result = true; result = true;
} }
} }
@ -839,7 +994,7 @@ void Generator::MakeTextIs(Pattern::Item & item)
} }
bool Generator::MakeTextIfindexnumber(Pattern::Item & item, UserInfo * user_info, bool & result) bool Generator::MakeTextIfindexnumber(Pattern::Item & item, Functions::Function * function, bool & result)
{ {
if( item.directives.size() != 2 ) if( item.directives.size() != 2 )
return false; return false;
@ -851,12 +1006,14 @@ bool Generator::MakeTextIfindexnumber(Pattern::Item & item, UserInfo * user_info
if( *last_char == '\0' ) if( *last_char == '\0' )
{ {
if( user_info->iter == number ) if( function->iter == number )
result = true; result = true;
// we don't have to set result as false (false is default)
} }
else else
{ {
otext += CreateMsg("if-index: syntax error"); CreateMsg(output_stream, "if-index: syntax error");
return false; return false;
} }
@ -870,33 +1027,37 @@ void Generator::MakeTextIfindex(Pattern::Item & item)
if( item.directives.size() != 2 ) if( item.directives.size() != 2 )
return; return;
UserInfo * user_info; // we actually don't call a function (or variable) here
// but only reading an iteration index
Functions::Function * function;
if( !Find(item.directives[0], &function) )
return;
bool result = false; bool result = false;
if( !Find(item.directives[0], &user_info) )
return;
if( item.directives[1] == "odd" ) if( item.directives[1] == "odd" )
{ {
if( ( user_info->iter & 1) == 1 ) if( (function->iter & 1) == 1 )
result = true; result = true;
} }
else else
if( item.directives[1] == "even" ) if( item.directives[1] == "even" )
{ {
if( ( user_info->iter & 1) == 0 ) if( (function->iter & 1) == 0 )
result = true; result = true;
} }
else else
if( item.directives[1] == "first" ) if( item.directives[1] == "first" )
{ {
if( user_info->iter == 0 ) if( function->iter == 0 )
result = true; result = true;
} }
else else
{ {
if( !MakeTextIfindexnumber(item, user_info, result) ) if( !MakeTextIfindexnumber(item, function, result) )
return; return;
} }
@ -904,42 +1065,94 @@ void Generator::MakeTextIfindex(Pattern::Item & item)
} }
void Generator::MakeTextFor(Pattern::Item & item) void Generator::MakeTextFor(Pattern::Item & item)
{ {
const int max_loop = 3000;
if( item.directives.size() != 1 ) if( item.directives.size() != 1 )
return; return;
UserInfo * user_info; Functions::Function * function;
if( !Find(item.directives[0], &user_info) ) if( !Find(item.directives[0], &function) )
return; return;
if( function->is_for )
Info info;
user_info->iter = 0;
while( true )
{ {
(user_info->user_function)(info); CreateMsg(output_stream, item.directives[0].c_str(), "this function is already used in a [for] statement");
return;
}
for( function->is_for = true, function->iter=0 ; loop != -1 ; ++function->iter )
{
if( function->iter >= max_loop )
{
CreateMsg(output_stream, item.directives[0].c_str(), "function exceeded a limit for a [for] statement");
break;
}
Call(function, info1);
if( info.text.empty() && !info.result ) if( info1.IsFalse() )
break; break;
if( item.item_table.size() > 0 ) if( item.item_table.size() > 0 )
MakeText( *item.item_table[0] ); MakeText( *item.item_table[0] );
}
function->is_for = false;
function->iter = 0;
}
++info.iter;
user_info->iter = info.iter;
void Generator::MakeTextDefine(Pattern::Item & item)
{
if( item.directives.size() != 2 )
return;
if( !item.directives[1].empty() && item.directives[1][0]=='\"' )
{
// second directive is a string
functions.Insert(item.directives[0], item.directives[1].c_str() + 1);
}
else
{
Functions::Function * function;
if( Find(item.directives[1], &function) )
{
if( function->type == Functions::function )
functions.Insert(item.directives[0], function->user_function);
else
functions.Insert(item.directives[0], function->variable);
}
} }
} }
void Generator::MakeText(Pattern::Item & item) void Generator::MakeText(Pattern::Item & item)
{ {
const int max_loop = 10000;
if( loop == -1 )
return;
if( ++loop > max_loop )
{
loop = -1;
CreateMsg(output_stream, "Generator exceeded allowed number of elements");
return;
}
switch( item.type ) switch( item.type )
{ {
case Pattern::Item::item_text: case Pattern::Item::item_text:
otext += item.text; output_stream << item.text;
break; break;
case Pattern::Item::item_container: case Pattern::Item::item_container:
MakeTextContainer(item); MakeTextContainer(item);
@ -962,8 +1175,11 @@ void Generator::MakeText(Pattern::Item & item)
case Pattern::Item::item_for: case Pattern::Item::item_for:
MakeTextFor(item); MakeTextFor(item);
break; break;
case Pattern::Item::item_def:
MakeTextDefine(item);
break;
case Pattern::Item::item_err: case Pattern::Item::item_err:
otext += CreateMsg("a wrong directive"); CreateMsg(output_stream, "a wrong directive");
break; break;
default: default:
break; break;
@ -971,12 +1187,5 @@ void Generator::MakeText(Pattern::Item & item)
} }
void Generator::Clear()
{
user_info_table.clear();
otext.clear();
}
} // namespace Ezc } // namespace Ezc

114
src/ezc.h
View File

@ -5,7 +5,7 @@
*/ */
/* /*
* Copyright (c) 2007, Tomasz Sowa * Copyright (c) 2007-2008, Tomasz Sowa
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -56,10 +56,14 @@
namespace Ezc namespace Ezc
{ {
void CreateMsg(std::ostringstream & o, const char * type, const char * arg = 0);
std::string CreateMsg(const char * type, const char * arg = 0); std::string CreateMsg(const char * type, const char * arg = 0);
void SplitUnixDirectory(const char * name, std::string & dir, std::string & file); void SplitUnixDirectory(const char * name, std::string & dir, std::string & file);
void SplitUnixDirectory(const std::string & name, std::string & dir, std::string & file); void SplitUnixDirectory(const std::string & name, std::string & dir, std::string & file);
class Pattern class Pattern
{ {
@ -71,13 +75,17 @@ public:
struct Item struct Item
{ {
// change the name to 'Type'
enum ItemType enum ItemType
{ {
item_none, item_container, item_text, item_ifany, item_for, item_none, item_container, item_text, item_ifany, item_for,
item_else, item_end, item_err, item_normal, item_ifindex, item_else, item_end, item_err, item_normal, item_ifindex,
item_include, item_is, item_ifone, item_comment item_include, item_is, item_ifone, item_comment, item_def
}; };
ItemType type; ItemType type;
std::string text; std::string text;
std::vector<Item*> item_table; std::vector<Item*> item_table;
@ -111,7 +119,7 @@ private:
void SkipWhiteCharacters(); void SkipWhiteCharacters();
std::string ReadDirective(); std::string ReadDirective();
std::string ReadString(); std::string ReadString(bool skip_first_quote = false);
std::string ReadDirectiveOrString(); std::string ReadDirectiveOrString();
void CreateTreeReadItemDirectiveCheckEnding(Item & item); void CreateTreeReadItemDirectiveCheckEnding(Item & item);
@ -123,6 +131,7 @@ private:
void ReadDirectiveFor(Item & item); void ReadDirectiveFor(Item & item);
void ReadDirectiveComment(Item & item); void ReadDirectiveComment(Item & item);
void ReadDirectiveInclude(Item & item); void ReadDirectiveInclude(Item & item);
void ReadDirectiveDef(Item & item);
void CreateTreeReadItemDirective(Item & item); void CreateTreeReadItemDirective(Item & item);
@ -138,45 +147,101 @@ private:
}; // Pattern }; // Pattern
struct Info struct Info
{ {
std::string text; // this variables you can set in your function
std::ostringstream & out;
std::string out_string;
bool result; bool result;
// this is set by Generator
// normally is 0
// in a [for] statement it indicates the number of the current iteration (the first is 0)
int iter; int iter;
Info();
Info(std::ostringstream & o);
void Clear();
bool IsTrue() const;
bool IsFalse() const;
}; };
class Functions
{
public:
typedef void (*UserFunction)(Info &);
enum Type { function, variable };
struct Function
{
Type type;
UserFunction user_function;
std::string variable;
int iter;
bool is_for; // true if is used by a [for] statement
bool is_running; // true if this function (if is) is currently running
Function();
};
void Insert(const std::string & key, UserFunction ufunction); // inserting a function
void Insert(const std::string & key, const char * var); // inserting a variable
void Insert(const std::string & key, const std::string & var); // inserting a variable
bool Find(const std::string & key, Function ** fun);
void Clear();
private:
typedef std::map<std::string, Function> FunctionsTable;
FunctionsTable functions_table;
};
class Generator class Generator
{ {
private:
typedef void (*UserFunction)(Info &);
struct UserInfo
{
UserFunction user_function;
int iter;
};
typedef std::map<std::string, UserInfo> UserInfoTable;
public: public:
void Insert(const std::string & key, UserFunction ufunction); Generator(std::ostringstream &, Pattern & data, Functions & functions);
std::string & Generate(Pattern & data); void Generate();
std::string & String();
void Clear();
private: private:
UserInfoTable user_info_table; std::ostringstream & output_stream;
std::string otext; Pattern & pattern;
Functions & functions;
bool Find(const std::string & key, UserInfo ** user_info);
Info info1, info2;
int loop;
bool Find(const std::string & key, Functions::Function ** function);
bool Call(const std::string & name, Info & info, Functions::Function ** pfun = 0);
void Call(Functions::Function * function, Info & info);
void CallUserFunction(Functions::Function * function, Info & info);
void CallVariable(Functions::Function * function, Info & info);
void MakeTextIf_go(Pattern::Item & item, bool result); void MakeTextIf_go(Pattern::Item & item, bool result);
bool MakeTextIfindexnumber(Pattern::Item & item, UserInfo * user_info, bool & result); bool MakeTextIfindexnumber(Pattern::Item & item, Functions::Function * function, bool & result);
void MakeTextIfany(Pattern::Item & item); void MakeTextIfany(Pattern::Item & item);
void MakeTextIfone(Pattern::Item & item); void MakeTextIfone(Pattern::Item & item);
void MakeTextIfindex(Pattern::Item & item); void MakeTextIfindex(Pattern::Item & item);
@ -184,6 +249,7 @@ private:
void MakeTextContainer(Pattern::Item & item); void MakeTextContainer(Pattern::Item & item);
void MakeTextNormal(Pattern::Item & item); void MakeTextNormal(Pattern::Item & item);
void MakeTextIs(Pattern::Item & item); void MakeTextIs(Pattern::Item & item);
void MakeTextDefine(Pattern::Item & item);
void MakeText(Pattern::Item & item); void MakeText(Pattern::Item & item);
}; // Generator }; // Generator