diff --git a/src/ezc.cpp b/src/ezc.cpp index 2b5da31..4fcbf8f 100644 --- a/src/ezc.cpp +++ b/src/ezc.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2007, Tomasz Sowa + * Copyright (c) 2007-2008, Tomasz Sowa * All rights reserved. * * 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 << ""; +} + + std::string CreateMsg(const char * type, const char * arg) { std::ostringstream buffer; - buffer << ""; + CreateMsg(buffer, type, arg); return buffer.str(); } + + /* this method splits the input file name into a directory and a file name e.g @@ -64,37 +72,37 @@ return buffer.str(); dir = "/this/is/dummy" file = "file.ezc" */ -void SplitUnixDirectory(const char * name, std::string & dir, std::string & file) -{ - dir.clear(); - file.clear(); - - int i; - for( i=0 ; name[i] != 0 ; ++i ); - - if( i == 0 ) - return; - - for( --i ; i>=0 && name[i]!='\\' && name[i]!='/' ; --i ); - - if( i < 0 ) - { - file.assign(name); - } - else - { - if( i == 0 ) - // we're leaving one '/' in the directory - // (we have only the root directory) - dir.assign(name, 1); - else - dir.assign(name, i); - - file.assign(name + i + 1 ); - } - -return; -} +void SplitUnixDirectory(const char * name, std::string & dir, std::string & file) +{ + dir.clear(); + file.clear(); + + int i; + for( i=0 ; name[i] != 0 ; ++i ); + + if( i == 0 ) + return; + + for( --i ; i>=0 && name[i]!='\\' && name[i]!='/' ; --i ); + + if( i < 0 ) + { + file.assign(name); + } + else + { + if( i == 0 ) + // we're leaving one '/' in the directory + // (we have only the root directory) + dir.assign(name, 1); + else + dir.assign(name, i); + + file.assign(name + i + 1 ); + } + +return; +} 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; @@ -244,7 +254,8 @@ std::string directive; return directive; // string is signed by its first character equal (") - directive += '\"'; + if( !skip_first_quote ) + directive += '\"'; for( ++itext ; *itext && *itext!='\"' && *itext!='\n' ; ++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) { ++itext; @@ -427,6 +459,9 @@ void Pattern::CreateTreeReadItemDirective(Item & item) if( directive == "include" ) ReadDirectiveInclude(item); else + if( directive == "def" ) + ReadDirectiveDef(item); + else if( directive == "#" ) ReadDirectiveComment(item); else @@ -658,13 +693,108 @@ Pattern::Item::~Item() * */ -Info::Info() +void Info::Clear() { + // default settings indicate '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 @@ -674,70 +804,114 @@ Info::Info() -std::string & Generator::Generate(Pattern & data) +bool Generator::Find(const std::string & key, Functions::Function ** function) { - otext.clear(); - MakeText( data.item_root ); - -return otext; + if( !functions.Find(key, function) ) + { + CreateMsg(output_stream, "can't find", key.c_str() ); + 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) { std::vector::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; + MakeText(**i); + + if( loop < 0 ) + break; } - - *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; - } + Call(item.directives[0], info1); + + if( !info1.out_string.empty() ) + output_stream << info1.out_string; } @@ -764,18 +938,11 @@ void Generator::MakeTextIfany(Pattern::Item & item) for( ; d != item.directives.end() ; ++d ) { - UserInfo * user_info; - - if( !Find(*d, &user_info) ) + if( !Call(*d, info1) ) return; - - Info info; - (user_info->user_function)(info); - - if( !info.text.empty() || info.result ) + if( info1.IsTrue() ) ++how_many_true; - } 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 ) { - UserInfo * user_info; - - if( !Find(*d, &user_info) ) - return; - - Info info; - (user_info->user_function)(info); - - if( !info.text.empty() || info.result ) + if( Call(*d, info1) && info1.IsTrue() ) ++how_many_true; } @@ -805,32 +964,28 @@ void Generator::MakeTextIfone(Pattern::Item & item) } + void Generator::MakeTextIs(Pattern::Item & item) { if( item.directives.size() != 2 ) return; - UserInfo * user_info; - - if( !Find(item.directives[0], &user_info) ) + if( !Call(item.directives[0], info1) ) 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 ) + // second directive is a string + if( std::strcmp(info1.out_string.c_str(), item.directives[1].c_str()+1) == 0 ) result = true; } 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.text==info2.text ) + if( info1.result==info2.result && info1.out_string==info2.out_string ) 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 ) return false; @@ -851,12 +1006,14 @@ bool Generator::MakeTextIfindexnumber(Pattern::Item & item, UserInfo * user_info if( *last_char == '\0' ) { - if( user_info->iter == number ) + if( function->iter == number ) result = true; + + // we don't have to set result as false (false is default) } else { - otext += CreateMsg("if-index: syntax error"); + CreateMsg(output_stream, "if-index: syntax error"); return false; } @@ -870,33 +1027,37 @@ void Generator::MakeTextIfindex(Pattern::Item & item) if( item.directives.size() != 2 ) 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; - if( !Find(item.directives[0], &user_info) ) - return; - if( item.directives[1] == "odd" ) { - if( ( user_info->iter & 1) == 1 ) + if( (function->iter & 1) == 1 ) result = true; } else if( item.directives[1] == "even" ) { - if( ( user_info->iter & 1) == 0 ) + if( (function->iter & 1) == 0 ) result = true; } else if( item.directives[1] == "first" ) { - if( user_info->iter == 0 ) + if( function->iter == 0 ) result = true; } else { - if( !MakeTextIfindexnumber(item, user_info, result) ) + if( !MakeTextIfindexnumber(item, function, result) ) return; } @@ -904,42 +1065,94 @@ void Generator::MakeTextIfindex(Pattern::Item & item) } + void Generator::MakeTextFor(Pattern::Item & item) { + const int max_loop = 3000; + if( item.directives.size() != 1 ) return; - UserInfo * user_info; + Functions::Function * function; - if( !Find(item.directives[0], &user_info) ) + if( !Find(item.directives[0], &function) ) return; - - Info info; - user_info->iter = 0; - - while( true ) + if( function->is_for ) { - (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; if( item.item_table.size() > 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) { + 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 ) { case Pattern::Item::item_text: - otext += item.text; + output_stream << item.text; break; case Pattern::Item::item_container: MakeTextContainer(item); @@ -962,8 +1175,11 @@ void Generator::MakeText(Pattern::Item & item) case Pattern::Item::item_for: MakeTextFor(item); break; + case Pattern::Item::item_def: + MakeTextDefine(item); + break; case Pattern::Item::item_err: - otext += CreateMsg("a wrong directive"); + CreateMsg(output_stream, "a wrong directive"); break; default: break; @@ -971,12 +1187,5 @@ void Generator::MakeText(Pattern::Item & item) } -void Generator::Clear() -{ - user_info_table.clear(); - otext.clear(); -} - - } // namespace Ezc diff --git a/src/ezc.h b/src/ezc.h index a762fdd..00a5300 100644 --- a/src/ezc.h +++ b/src/ezc.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2007, Tomasz Sowa + * Copyright (c) 2007-2008, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -56,10 +56,14 @@ namespace Ezc { +void CreateMsg(std::ostringstream & o, 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 std::string & name, std::string & dir, std::string & file); + + + class Pattern { @@ -71,13 +75,17 @@ public: struct Item { + // change the name to 'Type' enum ItemType { item_none, item_container, item_text, item_ifany, item_for, 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; std::string text; std::vector item_table; @@ -111,7 +119,7 @@ private: void SkipWhiteCharacters(); std::string ReadDirective(); - std::string ReadString(); + std::string ReadString(bool skip_first_quote = false); std::string ReadDirectiveOrString(); void CreateTreeReadItemDirectiveCheckEnding(Item & item); @@ -123,6 +131,7 @@ private: void ReadDirectiveFor(Item & item); void ReadDirectiveComment(Item & item); void ReadDirectiveInclude(Item & item); + void ReadDirectiveDef(Item & item); void CreateTreeReadItemDirective(Item & item); @@ -138,45 +147,101 @@ private: }; // Pattern + + + struct Info { - std::string text; + // this variables you can set in your function + std::ostringstream & out; + std::string out_string; 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; - 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 FunctionsTable; + FunctionsTable functions_table; +}; + + + + + + + class Generator { -private: - typedef void (*UserFunction)(Info &); - - struct UserInfo - { - UserFunction user_function; - int iter; - }; - - typedef std::map UserInfoTable; public: - void Insert(const std::string & key, UserFunction ufunction); - std::string & Generate(Pattern & data); - std::string & String(); - void Clear(); + Generator(std::ostringstream &, Pattern & data, Functions & functions); + void Generate(); private: - UserInfoTable user_info_table; - std::string otext; - - bool Find(const std::string & key, UserInfo ** user_info); + std::ostringstream & output_stream; + Pattern & pattern; + Functions & functions; + + 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); - 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 MakeTextIfone(Pattern::Item & item); void MakeTextIfindex(Pattern::Item & item); @@ -184,6 +249,7 @@ private: void MakeTextContainer(Pattern::Item & item); void MakeTextNormal(Pattern::Item & item); void MakeTextIs(Pattern::Item & item); + void MakeTextDefine(Pattern::Item & item); void MakeText(Pattern::Item & item); }; // Generator