/* * This file is a part of EZC -- Easy templating in C++ library * and is distributed under the BSD 3-Clause licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2007-2018, 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 "patternparser.h" #include "convert/convert.h" namespace Ezc { PatternParser::PatternParser() { allow_include = true; pblocks = 0; include_level_max = 100; delete_white_text_items = false; program_mode = false; log = nullptr; } void PatternParser::Directory(const char * dir, const char * dir2) { directory.clear(); directory2.clear(); if( dir ) PT::UTF8ToWide(dir, directory); if( dir2 ) PT::UTF8ToWide(dir2, directory2); } void PatternParser::Directory(const std::string & dir) { PT::UTF8ToWide(dir, directory); directory2.clear(); } void PatternParser::Directory(const std::string & dir, const std::string & dir2) { PT::UTF8ToWide(dir, directory); PT::UTF8ToWide(dir2, directory2); } void PatternParser::Directory(const wchar_t * dir, const wchar_t * dir2) { directory.clear(); directory2.clear(); if( dir ) directory = dir; if( dir2 ) directory2 = dir2; } void PatternParser::Directory(const std::wstring & dir) { directory = dir; directory2.clear(); } void PatternParser::Directory(const std::wstring & dir, const std::wstring & dir2) { directory = dir; directory2 = dir2; } void PatternParser::SetBlocks(Blocks & blocks) { pblocks = &blocks; } void PatternParser::SetCommentary(const char * com_start, const char * com_stop) { PT::UTF8ToWide(com_start, commentary_start); PT::UTF8ToWide(com_stop, commentary_stop); } void PatternParser::SetCommentary(const std::string & com_start, const std::string & com_stop) { PT::UTF8ToWide(com_start, commentary_start); PT::UTF8ToWide(com_stop, commentary_stop); } void PatternParser::SetCommentary(const wchar_t * com_start, const wchar_t * com_stop) { commentary_start = com_start; commentary_stop = com_stop; } void PatternParser::SetCommentary(const std::wstring & com_start, const std::wstring & com_stop) { commentary_start = com_start; commentary_stop = com_stop; } void PatternParser::SetProgramMode(bool program_mode) { this->program_mode = program_mode; } void PatternParser::SetLogger(PT::Log * log) { this->log = log; } void PatternParser::CreateMsg(std::wstring & out, const wchar_t * type, const wchar_t * arg) { out = commentary_start; out += L"Ezc: "; out += type; if( arg ) { out += ' '; out += arg; } out += commentary_stop; } void PatternParser::ParseFile(const std::string & file_name, Pattern & pattern) { ParseFile(file_name.c_str(), pattern); } void PatternParser::ParseFile(const char * file_name, Pattern & pattern) { pat = &pattern; PT::UTF8ToWide(file_name, pat->item_root.file_name); include_level = 0; CreateTreeReadIncludeSkipAllowFlag(pat->item_root); } void PatternParser::ParseFile(const std::wstring & file_name, Pattern & pattern) { ParseFile(file_name.c_str(), pattern); } void PatternParser::ParseFile(const wchar_t * file_name, Pattern & pattern) { pat = &pattern; pat->item_root.file_name = file_name; include_level = 0; CreateTreeReadIncludeSkipAllowFlag(pat->item_root); } void PatternParser::ParseString(const char * str, Pattern & pattern) { PT::UTF8ToWide(str, string_content); ParseString(string_content.c_str(), pattern); string_content.clear(); } void PatternParser::ParseString(const std::string & str, Pattern & pattern) { ParseString(str.c_str(), pattern); } void PatternParser::ParseString(const wchar_t * str, Pattern & pattern) { pat = &pattern; itext = str; include_level = 0; pat->item_root.Clear(); CreateTreeContainer(pat->item_root); } void PatternParser::ParseString(const std::wstring & str, Pattern & pattern) { ParseString(str.c_str(), pattern); } void PatternParser::AllowInclude(bool allow) { allow_include = allow; } void PatternParser::DeleteWhiteTextItems(bool del) { delete_white_text_items = del; } void PatternParser::SetIncludeMax(int include_max) { include_level_max = include_max; } bool PatternParser::HasFileAtBeginning(const wchar_t * path, const wchar_t * 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 PatternParser::IsFileCorrect(const wchar_t * name) { while( *name ) { if( HasFileAtBeginning(name, L"..") ) 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 */ void PatternParser::ReadFile(const std::wstring & name, std::wstring & result) { ReadFile(name.c_str(), result); } /* 'name' must be a relative path - without a slash or backslash */ void PatternParser::ReadFile(const wchar_t * name, std::wstring & result) { if( !IsFileCorrect(name) ) { CreateMsg(result, L"incorrect file name: ", name); } else { result.clear(); if( !ReadFileFromDir(directory, name, result) ) if( !ReadFileFromDir(directory2, name, result) ) CreateMsg(result, L"can't open: ", name); } } bool PatternParser::ReadFileFromDir(const std::wstring & dir, const wchar_t * name, std::wstring & result) { if( dir.empty() ) return false; file_name = dir; file_name += '/'; file_name += name; PT::WideToUTF8(file_name, afile_name); std::ifstream file(afile_name.c_str()); if( !file ) { file_name.clear(); afile_name.clear(); return false; } if( log ) { if( include_level <= 1 ) (*log) << PT::Log::log4 << "Ezc: reading pattern: " << afile_name << PT::Log::logend; else (*log) << PT::Log::log4 << " including pattern: " << afile_name << PT::Log::logend; } ReadFile(file, result); file_name.clear(); afile_name.clear(); return true; } void PatternParser::ReadFile(std::ifstream & file, std::wstring & result) { PT::UTF8ToWide(file, result); } int PatternParser::ReadCharInText() { if( *itext==0 || *itext=='[' ) return -1; if( *itext == '\\' ) { if( *(itext+1)=='\\' || *(itext+1)=='[' || *(itext+1)==']' ) ++itext; } return *(itext++); } bool PatternParser::IsWhite(wchar_t c) { // 13 (\r) is from a dos file at the end of a line (\r\n) // 160 is a non-breaking space if( c==' ' || c=='\t' || c==13 || c==160 || c==10 ) return true; return false; } void PatternParser::SkipWhite() { while( IsWhite(*itext) ) ++itext; } void PatternParser::SkipOneStatement() { size_t count = 1; while( *itext != 0 && count > 0 ) { if( *itext == '[' ) count += 1; else if( *itext == ']' ) count -= 1; itext += 1; } } void PatternParser::CheckWhiteAndDelete(std::wstring & 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 PatternParser::IsDigit(wchar_t c) { return (c>='0' && c<='9'); } bool PatternParser::IsPositiveNumber(const std::wstring & str) { size_t i; for(i=0 ; i(c); if( delete_white_text_items ) CheckWhiteAndDelete(item.text); item.type = Item::item_text; } void PatternParser::CreateTreeReadDirectiveExpression(Item & item, bool is_statement) { int brackets_counter = is_statement ? 1 : 0; while( *itext ) { wchar_t c = *itext; if( c == 10 || c == 13 ) c = ' '; if( c == ';' && brackets_counter == 0 ) { itext += 1; return; // end of normal expression (not in a statement such as 'for' or 'if') } if( c == '(' ) { brackets_counter += 1; } if( c == ')' ) { brackets_counter -= 1; if( is_statement && brackets_counter == 0 ) { itext += 1; return; // end of statement expression } } if( !IsWhite(c) || item.text.empty() || !IsWhite(item.text.back()) ) item.text += c; itext += 1; } } bool PatternParser::CreateTreeCheckProgramDirective(Item & item) { const wchar_t * old_itext = itext; if( PT::IsSubStringNoCasep(L"if", itext) ) { itext += 2; SkipWhite(); if( *itext == '(' ) { itext += 1; item.type = Item::item_if; return true; } } if( PT::IsSubStringNoCasep(L"while", itext) ) { itext += 5; SkipWhite(); if( *itext == '(' ) { itext += 1; item.type = Item::item_for; return true; } } itext = old_itext; return false; } bool PatternParser::CreateTreeReadExpression(Item & item) { SkipWhite(); if( *itext == 0 ) return false; if( *itext == '{' ) { item.type = Item::item_container; itext += 1; return true; } if( *itext == '}' ) { item.type = Item::item_end; itext += 1; return true; } if( CreateTreeCheckProgramDirective(item) ) { CreateTreeReadDirectiveExpression(item, true); } else { item.type = Item::item_function; CreateTreeReadDirectiveExpression(item, false); } return true; } bool PatternParser::CreateTreeReadItem(Item & item) { item.Clear(); if( program_mode ) { return CreateTreeReadExpression(item); } else { if( *itext == '[' ) { CreateTreeReadItemDirective(item); return true; } else if( *itext ) { CreateTreeReadItemText(item); return true; } } // the end of the string return false; } void PatternParser::CreateTreeReadInclude(Item & item) { if( allow_include ) { CreateTreeReadIncludeSkipAllowFlag(item); } else { if( log ) { (*log) << PT::Log::log2 << "Ezc: \"include\" directive is not allowed" << PT::Log::logend; } } } void PatternParser::CreateTreeReadIncludeSkipAllowFlag(Item & item) { if( item.file_name.empty() ) return; if( include_level > include_level_max ) { if( log ) { (*log) << PT::Log::log1 << "Ezc: \"include\" directive has reached the maximum level" << PT::Log::logend; } return; } ++include_level; std::wstring file_text; // this temporary object must not be global (includes can be nested) ReadFile(item.file_name, file_text); const wchar_t * itext_old = itext; itext = file_text.c_str(); item.Clear(); CreateTreeContainer(item); itext = itext_old; --include_level; } void PatternParser::CreateTreeReadIf(Item & item) { Item * pitem = item.AddItem(); pitem->Clear(); if( program_mode ) CreateTree(*pitem); else CreateTreeContainer(*pitem); if( program_mode ) { SkipWhite(); if( PT::IsSubStringNoCasep(L"else", itext) ) { itext += 4; pitem = item.AddItem(); CreateTree(*pitem); } } else { if( pitem->LastItemType() == Item::item_else ) { pitem->DeleteLastItem(); pitem = item.AddItem(); CreateTreeContainer(*pitem); } } if( pitem->LastItemType() == Item::item_end ) pitem->DeleteLastItem(); } void PatternParser::CreateTreeReadBlock(Item & item) { Item item_block; CreateTreeContainer(item_block); if( item_block.LastItemType() == Item::item_end ) item_block.DeleteLastItem(); //if( pblocks && item.functions.size()==1 ) if( pblocks ) pblocks->Insert(item.function.name, item_block); } void PatternParser::CreateTreeReadFor(Item & item) { Item * pitem = item.AddItem(); if( program_mode ) CreateTree(*pitem); else CreateTreeContainer(*pitem); if( pitem->LastItemType() == Item::item_end ) pitem->DeleteLastItem(); } bool PatternParser::CreateTree(Item & item) { do { if( !CreateTreeReadItem(item) ) { return false; } if( item.type == Item::item_block ) CreateTreeReadBlock(item); } while( item.type == Item::item_comment || item.type == Item::item_block ); // such container can be read in program mode if( item.type == Item::item_container ) CreateTreeContainer(item); if( item.type == Item::item_if ) CreateTreeReadIf(item); // CHECK ME is it correct to check item_filter and item_ezc here and call CreateTreeReadFor? if( item.type == Item::item_for || item.type == Item::item_filter || item.type == Item::item_ezc ) CreateTreeReadFor(item); if( item.type == Item::item_include ) CreateTreeReadInclude(item); return true; } void PatternParser::CreateTreeContainer(Item & item) { bool item_read_correctly; Item * pitem; item.type = Item::item_container; do { pitem = item.AddItem(); item_read_correctly = CreateTree(*pitem); } while( item_read_correctly && pitem->type != Item::item_end && pitem->type != Item::item_else); if( !item_read_correctly ) item.DeleteLastItem(); } } // namespace Ezc