/* * This file is a part of EZC -- Easy templating in C++ * and is distributed under the (new) BSD licence. * Author: Tomasz Sowa */ /* * 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 = ""; 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='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 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::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::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 & 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::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::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::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::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::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