From 29c7163d9be7758159dcf8367ac361b209245d0d Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Fri, 14 May 2021 21:02:27 +0200 Subject: [PATCH 1/5] updated to the new pikotools api (api2021 branch) --- src/patternparser.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/patternparser.cpp b/src/patternparser.cpp index a507ef4..7d08f65 100644 --- a/src/patternparser.cpp +++ b/src/patternparser.cpp @@ -935,7 +935,7 @@ bool PatternParser::CreateTreeCheckProgramDirective(Item & item) { const wchar_t * old_itext = itext; - if( PT::IsSubStringNoCasep(L"if", itext) ) + if( PT::is_substr_nc(L"if", itext) ) { itext += 2; SkipWhite(); @@ -948,7 +948,7 @@ bool PatternParser::CreateTreeCheckProgramDirective(Item & item) } } - if( PT::IsSubStringNoCasep(L"while", itext) ) + if( PT::is_substr_nc(L"while", itext) ) { itext += 5; SkipWhite(); @@ -1095,7 +1095,7 @@ void PatternParser::CreateTreeReadIf(Item & item) { SkipWhite(); - if( PT::IsSubStringNoCasep(L"else", itext) ) + if( PT::is_substr_nc(L"else", itext) ) { itext += 4; pitem = item.AddItem(); From d1a15ad15383548f3d619ded967b34df84a6cf8a Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Fri, 14 May 2021 21:02:56 +0200 Subject: [PATCH 2/5] make depend --- src/Makefile.dep | 38 ++++---------------------------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/src/Makefile.dep b/src/Makefile.dep index 918967a..1bcb86d 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep @@ -1,38 +1,8 @@ # DO NOT DELETE -blocks.o: blocks.h item.h cache.h functions.h ../../pikotools/utf8/utf8.h -blocks.o: ../../pikotools/textstream/textstream.h -blocks.o: ../../pikotools/space/space.h ../../pikotools/textstream/types.h -blocks.o: ../../pikotools/date/date.h ../../pikotools/convert/inttostr.h -blocks.o: ../../pikotools/membuffer/membuffer.h -blocks.o: ../../pikotools/textstream/types.h funinfo.h objects.h -cache.o: cache.h item.h functions.h ../../pikotools/utf8/utf8.h -cache.o: ../../pikotools/textstream/textstream.h -cache.o: ../../pikotools/space/space.h ../../pikotools/textstream/types.h -cache.o: ../../pikotools/date/date.h ../../pikotools/convert/inttostr.h -cache.o: ../../pikotools/membuffer/membuffer.h -cache.o: ../../pikotools/textstream/types.h funinfo.h objects.h blocks.h +blocks.o: blocks.h item.h cache.h functions.h funinfo.h objects.h +cache.o: cache.h item.h functions.h funinfo.h objects.h blocks.h item.o: item.h -pattern.o: pattern.h item.h cache.h functions.h ../../pikotools/utf8/utf8.h -pattern.o: ../../pikotools/textstream/textstream.h -pattern.o: ../../pikotools/space/space.h ../../pikotools/textstream/types.h -pattern.o: ../../pikotools/date/date.h ../../pikotools/convert/inttostr.h -pattern.o: ../../pikotools/membuffer/membuffer.h -pattern.o: ../../pikotools/textstream/types.h funinfo.h objects.h blocks.h +pattern.o: pattern.h item.h cache.h functions.h funinfo.h objects.h blocks.h patternparser.o: patternparser.h blocks.h item.h cache.h functions.h -patternparser.o: ../../pikotools/utf8/utf8.h -patternparser.o: ../../pikotools/textstream/textstream.h -patternparser.o: ../../pikotools/space/space.h -patternparser.o: ../../pikotools/textstream/types.h -patternparser.o: ../../pikotools/date/date.h -patternparser.o: ../../pikotools/convert/inttostr.h -patternparser.o: ../../pikotools/membuffer/membuffer.h -patternparser.o: ../../pikotools/textstream/types.h funinfo.h objects.h -patternparser.o: pattern.h ../../pikotools/log/log.h -patternparser.o: ../../pikotools/log/filelog.h -patternparser.o: ../../pikotools/convert/convert.h -patternparser.o: ../../pikotools/convert/inttostr.h -patternparser.o: ../../pikotools/convert/patternreplacer.h -patternparser.o: ../../pikotools/convert/strtoint.h -patternparser.o: ../../pikotools/convert/text.h -patternparser.o: ../../pikotools/convert/misc.h +patternparser.o: funinfo.h objects.h pattern.h From cd1c24d0a5234aefe23a8dfaa27201cf298d4d68 Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Thu, 20 May 2021 16:12:41 +0200 Subject: [PATCH 3/5] updated to new pikotools api: namespace PT renamed to pt --- src/functions.h | 2 +- src/generator.h | 10 +++++----- src/patternparser.cpp | 42 +++++++++++++++++++++--------------------- src/patternparser.h | 4 ++-- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/functions.h b/src/functions.h index 8889470..6b9f6a7 100644 --- a/src/functions.h +++ b/src/functions.h @@ -88,7 +88,7 @@ private: template void Functions::Insert(const char * key, UserFunction ufunction) { - PT::UTF8ToWide(key, temp_key); + pt::UTF8ToWide(key, temp_key); functions_tab[temp_key] = ufunction; temp_key.clear(); } diff --git a/src/generator.h b/src/generator.h index 0e82b64..e023af9 100644 --- a/src/generator.h +++ b/src/generator.h @@ -429,8 +429,8 @@ Generator::~Generator() template void Generator::SetCommentary(const char * com_start, const char * com_stop) { - PT::UTF8ToWide(com_start, commentary_start); - PT::UTF8ToWide(com_stop, commentary_stop); + pt::UTF8ToWide(com_start, commentary_start); + pt::UTF8ToWide(com_stop, commentary_stop); } @@ -438,8 +438,8 @@ void Generator::SetCommentary(const char * com_start, const char * c template void Generator::SetCommentary(const std::string & com_start, const std::string & com_stop) { - PT::UTF8ToWide(com_start, commentary_start); - PT::UTF8ToWide(com_stop, commentary_stop); + pt::UTF8ToWide(com_start, commentary_start); + pt::UTF8ToWide(com_stop, commentary_stop); } @@ -1703,7 +1703,7 @@ void Generator::MakeTextFilter(Item & item) /* although we are using a stack for [etc out] there is no need for the stack now because the output is only a one-level map (not nested structure) - but in the future we can use more complicated class like PT::Space + but in the future we can use more complicated class like pt::Space and then nested [ezc out] statements can product a better output */ template diff --git a/src/patternparser.cpp b/src/patternparser.cpp index 7d08f65..bd6d682 100644 --- a/src/patternparser.cpp +++ b/src/patternparser.cpp @@ -64,17 +64,17 @@ void PatternParser::Directory(const char * dir, const char * dir2) directory2.clear(); if( dir ) - PT::UTF8ToWide(dir, directory); + pt::UTF8ToWide(dir, directory); if( dir2 ) - PT::UTF8ToWide(dir2, directory2); + pt::UTF8ToWide(dir2, directory2); } void PatternParser::Directory(const std::string & dir) { - PT::UTF8ToWide(dir, directory); + pt::UTF8ToWide(dir, directory); directory2.clear(); } @@ -82,8 +82,8 @@ void PatternParser::Directory(const std::string & dir) void PatternParser::Directory(const std::string & dir, const std::string & dir2) { - PT::UTF8ToWide(dir, directory); - PT::UTF8ToWide(dir2, directory2); + pt::UTF8ToWide(dir, directory); + pt::UTF8ToWide(dir2, directory2); } @@ -126,16 +126,16 @@ void PatternParser::SetBlocks(Blocks & blocks) void PatternParser::SetCommentary(const char * com_start, const char * com_stop) { - PT::UTF8ToWide(com_start, commentary_start); - PT::UTF8ToWide(com_stop, commentary_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); + pt::UTF8ToWide(com_start, commentary_start); + pt::UTF8ToWide(com_stop, commentary_stop); } @@ -161,7 +161,7 @@ void PatternParser::SetProgramMode(bool program_mode) } -void PatternParser::SetLogger(PT::Log * log) +void PatternParser::SetLogger(pt::Log * log) { this->log = log; } @@ -195,7 +195,7 @@ void PatternParser::ParseFile(const std::string & file_name, Pattern & pattern) void PatternParser::ParseFile(const char * file_name, Pattern & pattern) { pat = &pattern; - PT::UTF8ToWide(file_name, pat->item_root.file_name); + pt::UTF8ToWide(file_name, pat->item_root.file_name); include_level = 0; CreateTreeReadIncludeSkipAllowFlag(pat->item_root); @@ -225,7 +225,7 @@ void PatternParser::ParseFile(const wchar_t * file_name, Pattern & pattern) void PatternParser::ParseString(const char * str, Pattern & pattern) { - PT::UTF8ToWide(str, string_content); + pt::UTF8ToWide(str, string_content); ParseString(string_content.c_str(), pattern); string_content.clear(); } @@ -372,7 +372,7 @@ bool PatternParser::ReadFileFromDir(const std::wstring & dir, const wchar_t * na file_name = dir; file_name += '/'; file_name += name; - PT::WideToUTF8(file_name, afile_name); + pt::WideToUTF8(file_name, afile_name); std::ifstream file(afile_name.c_str()); @@ -386,9 +386,9 @@ bool PatternParser::ReadFileFromDir(const std::wstring & dir, const wchar_t * na if( log ) { if( include_level <= 1 ) - (*log) << PT::Log::log4 << "Ezc: reading pattern: " << afile_name << PT::Log::logend; + (*log) << pt::Log::log4 << "Ezc: reading pattern: " << afile_name << pt::Log::logend; else - (*log) << PT::Log::log4 << " including pattern: " << afile_name << PT::Log::logend; + (*log) << pt::Log::log4 << " including pattern: " << afile_name << pt::Log::logend; } ReadFile(file, result); @@ -403,7 +403,7 @@ return true; void PatternParser::ReadFile(std::ifstream & file, std::wstring & result) { - PT::UTF8ToWide(file, result); + pt::UTF8ToWide(file, result); } @@ -935,7 +935,7 @@ bool PatternParser::CreateTreeCheckProgramDirective(Item & item) { const wchar_t * old_itext = itext; - if( PT::is_substr_nc(L"if", itext) ) + if( pt::is_substr_nc(L"if", itext) ) { itext += 2; SkipWhite(); @@ -948,7 +948,7 @@ bool PatternParser::CreateTreeCheckProgramDirective(Item & item) } } - if( PT::is_substr_nc(L"while", itext) ) + if( pt::is_substr_nc(L"while", itext) ) { itext += 5; SkipWhite(); @@ -1042,7 +1042,7 @@ void PatternParser::CreateTreeReadInclude(Item & item) { if( log ) { - (*log) << PT::Log::log2 << "Ezc: \"include\" directive is not allowed" << PT::Log::logend; + (*log) << pt::Log::log2 << "Ezc: \"include\" directive is not allowed" << pt::Log::logend; } } } @@ -1059,7 +1059,7 @@ void PatternParser::CreateTreeReadIncludeSkipAllowFlag(Item & item) { if( log ) { - (*log) << PT::Log::log1 << "Ezc: \"include\" directive has reached the maximum level" << PT::Log::logend; + (*log) << pt::Log::log1 << "Ezc: \"include\" directive has reached the maximum level" << pt::Log::logend; } return; @@ -1095,7 +1095,7 @@ void PatternParser::CreateTreeReadIf(Item & item) { SkipWhite(); - if( PT::is_substr_nc(L"else", itext) ) + if( pt::is_substr_nc(L"else", itext) ) { itext += 4; pitem = item.AddItem(); diff --git a/src/patternparser.h b/src/patternparser.h index 4a29ef2..211b446 100644 --- a/src/patternparser.h +++ b/src/patternparser.h @@ -89,7 +89,7 @@ public: void SetProgramMode(bool program_mode); - void SetLogger(PT::Log * log); + void SetLogger(pt::Log * log); private: @@ -138,7 +138,7 @@ private: bool program_mode; - PT::Log * log; + pt::Log * log; void CreateMsg(std::wstring & out, const wchar_t * type, const wchar_t * arg = 0); From 6f6df9524ca7090e60c27855d5e3119a3f02b37f Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Fri, 21 May 2021 00:32:05 +0200 Subject: [PATCH 4/5] updated to the new pikotools api: changed utf8 functions PascalCase to snake_case --- src/functions.h | 2 +- src/generator.h | 8 ++++---- src/objects.h | 6 +++--- src/patternparser.cpp | 26 +++++++++++++------------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/functions.h b/src/functions.h index 6b9f6a7..451a3d2 100644 --- a/src/functions.h +++ b/src/functions.h @@ -88,7 +88,7 @@ private: template void Functions::Insert(const char * key, UserFunction ufunction) { - pt::UTF8ToWide(key, temp_key); + pt::utf8_to_wide(key, temp_key); functions_tab[temp_key] = ufunction; temp_key.clear(); } diff --git a/src/generator.h b/src/generator.h index e023af9..a6c7a4d 100644 --- a/src/generator.h +++ b/src/generator.h @@ -429,8 +429,8 @@ Generator::~Generator() template void Generator::SetCommentary(const char * com_start, const char * com_stop) { - pt::UTF8ToWide(com_start, commentary_start); - pt::UTF8ToWide(com_stop, commentary_stop); + pt::utf8_to_wide(com_start, commentary_start); + pt::utf8_to_wide(com_stop, commentary_stop); } @@ -438,8 +438,8 @@ void Generator::SetCommentary(const char * com_start, const char * c template void Generator::SetCommentary(const std::string & com_start, const std::string & com_stop) { - pt::UTF8ToWide(com_start, commentary_start); - pt::UTF8ToWide(com_stop, commentary_stop); + pt::utf8_to_wide(com_start, commentary_start); + pt::utf8_to_wide(com_stop, commentary_stop); } diff --git a/src/objects.h b/src/objects.h index 5ec14ee..d8e36ad 100644 --- a/src/objects.h +++ b/src/objects.h @@ -155,7 +155,7 @@ protected: \ { \ EzcTemplateMethod item; \ \ - PT::UTF8ToWide(name, item.fun_name); \ + pt::utf8_to_wide(name, item.fun_name); \ item.fun = fun; \ \ ezc_template_method_tab.push_back(item); \ @@ -165,7 +165,7 @@ protected: \ { \ EzcTemplateMethod item; \ \ - PT::UTF8ToWide(name, item.fun_name); \ + pt::utf8_to_wide(name, item.fun_name); \ item.fun = fun; \ \ ezc_template_method_tab.push_back(item); \ @@ -294,7 +294,7 @@ size_t Objects::Size() const template void Objects::Clear() { - return obj_tab.clear(); + obj_tab.clear(); } diff --git a/src/patternparser.cpp b/src/patternparser.cpp index bd6d682..b055bc6 100644 --- a/src/patternparser.cpp +++ b/src/patternparser.cpp @@ -64,17 +64,17 @@ void PatternParser::Directory(const char * dir, const char * dir2) directory2.clear(); if( dir ) - pt::UTF8ToWide(dir, directory); + pt::utf8_to_wide(dir, directory); if( dir2 ) - pt::UTF8ToWide(dir2, directory2); + pt::utf8_to_wide(dir2, directory2); } void PatternParser::Directory(const std::string & dir) { - pt::UTF8ToWide(dir, directory); + pt::utf8_to_wide(dir, directory); directory2.clear(); } @@ -82,8 +82,8 @@ void PatternParser::Directory(const std::string & dir) void PatternParser::Directory(const std::string & dir, const std::string & dir2) { - pt::UTF8ToWide(dir, directory); - pt::UTF8ToWide(dir2, directory2); + pt::utf8_to_wide(dir, directory); + pt::utf8_to_wide(dir2, directory2); } @@ -126,16 +126,16 @@ void PatternParser::SetBlocks(Blocks & blocks) void PatternParser::SetCommentary(const char * com_start, const char * com_stop) { - pt::UTF8ToWide(com_start, commentary_start); - pt::UTF8ToWide(com_stop, commentary_stop); + pt::utf8_to_wide(com_start, commentary_start); + pt::utf8_to_wide(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); + pt::utf8_to_wide(com_start, commentary_start); + pt::utf8_to_wide(com_stop, commentary_stop); } @@ -195,7 +195,7 @@ void PatternParser::ParseFile(const std::string & file_name, Pattern & pattern) void PatternParser::ParseFile(const char * file_name, Pattern & pattern) { pat = &pattern; - pt::UTF8ToWide(file_name, pat->item_root.file_name); + pt::utf8_to_wide(file_name, pat->item_root.file_name); include_level = 0; CreateTreeReadIncludeSkipAllowFlag(pat->item_root); @@ -225,7 +225,7 @@ void PatternParser::ParseFile(const wchar_t * file_name, Pattern & pattern) void PatternParser::ParseString(const char * str, Pattern & pattern) { - pt::UTF8ToWide(str, string_content); + pt::utf8_to_wide(str, string_content); ParseString(string_content.c_str(), pattern); string_content.clear(); } @@ -372,7 +372,7 @@ bool PatternParser::ReadFileFromDir(const std::wstring & dir, const wchar_t * na file_name = dir; file_name += '/'; file_name += name; - pt::WideToUTF8(file_name, afile_name); + pt::wide_to_utf8(file_name, afile_name); std::ifstream file(afile_name.c_str()); @@ -403,7 +403,7 @@ return true; void PatternParser::ReadFile(std::ifstream & file, std::wstring & result) { - pt::UTF8ToWide(file, result); + pt::utf8_to_wide(file, result); } From 0ac8e05c047669fa8afcbdd5aa0c1cec7ba522cb Mon Sep 17 00:00:00 2001 From: Tomasz Sowa Date: Sun, 23 May 2021 10:02:51 +0200 Subject: [PATCH 5/5] changed semantic of [def] statements: - [def] is used to define a variable (like before) but if we assign a string such as [def var "string"] the string is evaluated to bool in a different way: empty string is false, not empty string is true - added [def?] statement - similar like [def] but define a variable only if such a variable is not already defined - added [let] statement - similar like [def] but with lazy evaluation (it is an alias to a function) if it is called to assign a string e.g. [let var "string"] then this has the same meaning like [def] - an alias is only created when we assign a function e.g. [let var my_function] both [def] and [let] are using the same Vars object - added [let?] - make an alias but only if such a variable is not already defined - a std::map of variables moved outside of Generator added method void SetVariables(Vars & variables); - fixed: a result status was not correctly propagated when evaluating [def] statements, this was in Call(...) function: last_res from variables was set in Find() but later was overwritten by Call(...) called for parameters (recursively) --- src/Makefile | 4 +- src/funinfo.h | 17 +++ src/generator.h | 337 ++++++++++++++++++++++++++++++------------ src/item.h | 5 +- src/patternparser.cpp | 97 +++++++++++- src/patternparser.h | 5 +- 6 files changed, 356 insertions(+), 109 deletions(-) diff --git a/src/Makefile b/src/Makefile index b5957b6..d42c6db 100644 --- a/src/Makefile +++ b/src/Makefile @@ -20,13 +20,13 @@ $(libname): $(o) %.o: %.cpp - $(CXX) -c $(CXXFLAGS) -I$(GLOBAL_WORKING_DIR)/pikotools $< + $(CXX) -c $(CXXFLAGS) -I$(GLOBAL_WORKING_DIR)/pikotools/src $< depend: # !! IMPROVE ME # as Ezc is a different project we rather shoudn't use '-I$(global_relative_working_dir)/pikotools' here? - makedepend -Y. -I$(global_relative_working_dir)/pikotools -f- *.cpp > Makefile.dep + makedepend -Y. -I$(global_relative_working_dir)/pikotools/src -f- *.cpp > Makefile.dep echo -n "o = " > Makefile.o.dep ls -1 *.cpp | xargs -I foo echo -n foo " " | sed -E "s/([^\.]*)\.cpp[ ]/\1\.o/g" >> Makefile.o.dep diff --git a/src/funinfo.h b/src/funinfo.h index 2ecc90f..18e5a44 100644 --- a/src/funinfo.h +++ b/src/funinfo.h @@ -40,6 +40,7 @@ #define headerfile_ezc_funinfo #include +#include #include "item.h" @@ -52,11 +53,27 @@ namespace Ezc */ struct Var { + /* + * if true then means 'str' is a function name and should be called (res is ignored) + * + * if false then means 'str' is a string value and res is a boolean value + */ + bool is_function; + std::wstring str; // a string value bool res; // a boolean value + + Var() + { + res = false; + is_function = false; + } }; +typedef std::map Vars; + + /* a base class for your own function data class diff --git a/src/generator.h b/src/generator.h index a6c7a4d..a3ae991 100644 --- a/src/generator.h +++ b/src/generator.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2007-2018, Tomasz Sowa + * Copyright (c) 2007-2021, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -73,6 +73,7 @@ public: void SetBlocks(Blocks & blocks); void SetFunctions(Functions & functions); void SetObjects(Objects & objects); + void SetVariables(Vars & variables); // [def] and [let] void SetMax(size_t max_items_, size_t max_for_items_); @@ -131,11 +132,6 @@ public: private: - // variables set - typedef std::map Vars; - Vars vars; - - struct BlockStack { std::vector args; @@ -155,6 +151,7 @@ private: Blocks * pblocks; Functions * pfunctions; Objects * pobjects; + Vars * pvars; // pointer to the output streams map (can be null) // output stream will be created when [ezc out "stream_name"] statement is found @@ -185,9 +182,6 @@ private: std::vector ezc_out_stack_tab; const StreamType empty_stream; - // used in [0] [1] [2] when there is no such argument defined - std::wstring empty_argument; - // temporary streams used in [if..] [for...] or [def ...] // or if output_stream is null and an ezc function should be called StreamType stream_temp1, stream_temp_define; @@ -242,7 +236,7 @@ private: CharType ToLower(CharType c); - bool CheckBlockArgument(Item::Function & item_fun, std::wstring ** variable); + bool CheckBlockArgument(int arg_index, Var ** variable); bool FindInCache(Item::Function & item_fun, BaseObj ** base_obj, @@ -257,15 +251,16 @@ private: Item ** item_block); bool FindInVariables(const std::wstring & name, - std::wstring ** variable); + Var ** variable); bool Find(Item::Function & item_fun, + std::wstring * fun_name, BaseObj ** base_obj, int * method_index, typename Functions::UserFunction ** function, Item ** item_block, - std::wstring ** variable); + Var ** variable); void CallFunction(typename Functions::UserFunction * function, FunInfo & info); @@ -289,8 +284,14 @@ private: StreamType & out_stream, const StreamType & in_stream); + bool CallVariable(Item::Function & item_fun, + Var * variable, + std::vector & parameters, + StreamType & out_stream, + const StreamType & in_stream); bool Call(Item::Function & item_fun, + std::wstring * fun_name, StreamType & out_stream, bool clear_out_stream, const StreamType & in_stream); @@ -324,7 +325,12 @@ private: void MakeItemText(Item & item); void MakeTextContainer(Item & item); void MakeTextNormal(Item & item); + void MakeTextDefine(Item & item, Var & var); void MakeTextDefine(Item & item); + void MakeTextDefineIfNotSet(Item & item); + void MakeTextLet(Item & item, Var & var); + void MakeTextLet(Item & item); + void MakeTextLetIfNotSet(Item & item); void MakeTextFilter(Item & item); void MakeTextEzc(Item & item); void MakeTextReturn(Item & item); @@ -349,6 +355,7 @@ Generator::Generator() : empty_stream() pblocks = 0; pfunctions = 0; pobjects = 0; + pvars = 0; max_items = 50000; max_for_items = 5000; @@ -382,6 +389,7 @@ Generator & Generator::operator=(const Generator::SetObjects(Objects & objects) pobjects = &objects; } + +template +void Generator::SetVariables(Vars & variables) +{ + pvars = &variables; +} + + template void Generator::CanUseCache(bool can_use_cache) { @@ -679,10 +695,7 @@ return c; template bool Generator::ConvertToBool(const std::wstring & str) { - if( str == L"true" ) - return true; - -return false; + return !str.empty(); } @@ -756,7 +769,6 @@ void Generator::Generate() filter_index = 0; stack_index = 0; block_stack_index = 0; - vars.clear(); if( ppattern ) { @@ -766,7 +778,6 @@ void Generator::Generate() MakeText( ppattern->item_root ); // !! IMPROVE ME we can print an error message if the stacks are not empty // (some [end] statements has been omited) - vars.clear(); is_generator_working = false; } catch(...) @@ -808,28 +819,24 @@ void Generator::Generate(OutStreams & out_streams) template -bool Generator::CheckBlockArgument(Item::Function & item_fun, std::wstring ** variable) +bool Generator::CheckBlockArgument(int arg_index, Var ** variable) { - if( item_fun.arg < 0 ) + if( arg_index < 0 ) return false; - empty_argument.clear(); - *variable = &empty_argument; - last_res = ConvertToBool(empty_argument); - // it's a numeric function -- an argument to a block e.g.: [1] if( block_stack_index > 0 ) { BlockStack & block_stack = block_stack_tab[block_stack_index-1]; - if( size_t(item_fun.arg) < block_stack.args.size() ) + if( size_t(arg_index) < block_stack.args.size() ) { - *variable = &block_stack.args[item_fun.arg].str; - last_res = block_stack.args[item_fun.arg].res; + *variable = &block_stack.args[arg_index]; + return true; } } - return true; + return false; } @@ -914,50 +921,58 @@ return false; template bool Generator::FindInVariables(const std::wstring & name, - std::wstring ** variable) + Var ** variable) { - Vars::iterator i = vars.find(name); - - if( i != vars.end() ) + if( pvars ) { - Var & var = i->second; + Vars::iterator i = pvars->find(name); - *variable = &var.str; - last_res = var.res; - return true; + if( i != pvars->end() ) + { + *variable = &(i->second); + return true; + } } -return false; + return false; } +/* + * fun_name can be null, it is used only with [let ...] statements + * and if not null then means: as a funcion name we are not using item_fun.name but fun_name + */ template bool Generator::Find(Item::Function & item_fun, + std::wstring * fun_name, BaseObj ** base_obj, int * method_index, typename Functions::UserFunction ** function, Item ** item_block, - std::wstring ** variable) + Var ** variable) { - *base_obj = 0; + *base_obj = nullptr; *method_index = -1; - *function = 0; - *item_block = 0; - *variable = 0; + *function = nullptr; + *item_block = nullptr; + *variable = nullptr; - if( CheckBlockArgument(item_fun, variable) ) + if( CheckBlockArgument(item_fun.arg, variable) ) return true; if( FindInCache(item_fun, base_obj, method_index, function, item_block) ) return true; - if( FindInFunctionsAndBlocks(item_fun.name, base_obj, method_index, function, item_block) ) + if( !fun_name ) + fun_name = &item_fun.name; + + if( FindInFunctionsAndBlocks(*fun_name, base_obj, method_index, function, item_block) ) return true; - if( FindInVariables(item_fun.name, variable) ) + if( FindInVariables(*fun_name, variable) ) return true; - CreateUnknownMsg(item_fun.name); + CreateUnknownMsg(*fun_name); return false; } @@ -1089,9 +1104,32 @@ return true; } -// return: true if a function, variable or block was found and called + +template +bool Generator::CallVariable(Item::Function & item_fun, Var * variable, std::vector & parameters, StreamType & out_stream, const StreamType & in_stream) +{ + if( variable->is_function ) + { + return Call(item_fun, &variable->str, out_stream, false, in_stream); + } + else + { + out_stream << variable->str; + last_res = variable->res; + return true; + } +} + + +/* + * fun_name can be null, it is used only with [let ...] statements + * and if not null then means: as a funcion name we are not using item_fun.name but fun_name + * + * return: true if a function, variable or block was found and called (evaluated) + */ template bool Generator::Call(Item::Function & item_fun, + std::wstring * fun_name, StreamType & out_stream, bool clear_out_stream, const StreamType & in_stream) @@ -1100,13 +1138,13 @@ BaseObj * base_obj; int method_index; typename Functions::UserFunction * fun; Item * item_block; -std::wstring * variable; +Var * variable; std::vector parameters; if( clear_out_stream ) ClearStream(out_stream); - if( !Find(item_fun, &base_obj, &method_index, &fun, &item_block, &variable) ) + if( !Find(item_fun, fun_name, &base_obj, &method_index, &fun, &item_block, &variable) ) return false; parameters.resize(item_fun.parameters.size()); @@ -1118,7 +1156,7 @@ std::vector parameters; if( fun_child.is_function ) { StreamType local_temp_stream; - Call(fun_child, local_temp_stream, true, empty_stream); + Call(fun_child, nullptr, local_temp_stream, true, empty_stream); #ifdef EZC_HAS_SPECIAL_STREAM parameters[i].str = local_temp_stream.Str(); @@ -1145,9 +1183,9 @@ std::vector parameters; return CallBlock(*item_block, parameters, out_stream); else if( variable ) - out_stream.write(variable->c_str(), variable->size()); + return CallVariable(item_fun, variable, parameters, out_stream, in_stream); -return true; + return true; } @@ -1156,7 +1194,7 @@ return true; template bool Generator::Call(Item::Function & item_fun) { - return Call(item_fun, stream_temp1, true, empty_stream); + return Call(item_fun, nullptr, stream_temp1, true, empty_stream); } @@ -1529,11 +1567,11 @@ void Generator::MakeTextNormal(Item & item) { if( output_stream ) { - Call(item.function, *output_stream, false, empty_stream); + Call(item.function, nullptr, *output_stream, false, empty_stream); } else { - Call(item.function, stream_temp1, false, empty_stream); + Call(item.function, nullptr, stream_temp1, false, empty_stream); ClearStream(stream_temp1); } } @@ -1604,7 +1642,7 @@ void Generator::MakeTextFor(Item & item) } else { - Call(item.function, stream_temp1, true, empty_stream); + Call(item.function, nullptr, stream_temp1, true, empty_stream); } if( !last_res ) @@ -1616,51 +1654,151 @@ void Generator::MakeTextFor(Item & item) } +template +void Generator::MakeTextDefine(Item & item, Var & var) +{ + var.str.clear(); + var.res = ConvertToBool(var.str); + var.is_function = false; + + if( item.function.parameters.empty() ) + { + return; + } + + if( item.function.parameters.size() > 1 ) + { + CreateMsg(L"[def] can have only one parameter"); + return; + } + + Item::Function & fun = *item.function.parameters[0]; + + if( fun.is_function ) + { + // call function + if( Call(fun, nullptr, stream_temp_define, true, empty_stream) ) + { + #ifdef EZC_HAS_SPECIAL_STREAM + var.str += stream_temp_define.Str(); + #else + var.str += stream_temp_define.str(); + #endif + + var.res = last_res; + } + else + { + CreateMsg(L"[def] unknown function/block/variable", item.function.name); + } + } + else + { + var.str = fun.name; + var.res = ConvertToBool(fun.name); + } +} + template void Generator::MakeTextDefine(Item & item) { - if( !can_use_vars ) + if( !can_use_vars || !pvars ) { CreateMsg(L"[def] statement not available"); return; } - Var & var = vars[item.function.name]; - var.str.clear(); - var.res = false; - - for(size_t i=0 ; i < item.function.parameters.size() ; ++i) - { - if( !item.function.parameters[i]->is_function ) - { - var.str += item.function.parameters[i]->name; - var.res = ConvertToBool(item.function.parameters[i]->name); - } - else - { - // it is a function parameter - if( Call(*item.function.parameters[i], stream_temp_define, true, empty_stream) ) - { - #ifdef EZC_HAS_SPECIAL_STREAM - var.str += stream_temp_define.Str(); - #else - var.str += stream_temp_define.str(); - #endif + Var & var = (*pvars)[item.function.name]; + MakeTextDefine(item, var); +} - var.res = last_res; - } - else - { - CreateMsg(L"[def] unknown function/block/variable", item.function.name); - } - } + +template +void Generator::MakeTextDefineIfNotSet(Item & item) +{ + if( !can_use_vars || !pvars ) + { + CreateMsg(L"[def?] statement not available"); + return; + } + + Vars::iterator vi = pvars->find(item.function.name); + + if( vi == pvars->end() ) + { + Var & var = (*pvars)[item.function.name]; + MakeTextDefine(item, var); } } +template +void Generator::MakeTextLet(Item & item, Var & var) +{ + var.str.clear(); + var.res = ConvertToBool(var.str); + var.is_function = true; + + if( item.function.parameters.empty() ) + { + var.is_function = false; + return; + } + + if( item.function.parameters.size() > 1 ) + { + CreateMsg(L"[let] can have only one parameter"); + return; + } + + Item::Function & fun = *item.function.parameters[0]; + var.str = fun.name; + var.is_function = fun.is_function; + + if( !fun.is_function ) + { + var.res = ConvertToBool(var.str); + } +} + + +template +void Generator::MakeTextLet(Item & item) +{ + if( !can_use_vars || !pvars ) + { + CreateMsg(L"[let] statement not available"); + return; + } + + Var & var = (*pvars)[item.function.name]; + MakeTextLet(item, var); +} + + +template +void Generator::MakeTextLetIfNotSet(Item & item) +{ + if( !can_use_vars || !pvars ) + { + CreateMsg(L"[let?] statement not available"); + return; + } + + Vars::iterator vi = pvars->find(item.function.name); + + if( vi == pvars->end() ) + { + Var & var = (*pvars)[item.function.name]; + MakeTextLet(item, var); + } +} + + + template void Generator::MakeTextFilter(Item & item) { @@ -1683,11 +1821,11 @@ void Generator::MakeTextFilter(Item & item) if( old_stream ) { - Call(item.function, *old_stream, false, *output_stream); + Call(item.function, nullptr, *old_stream, false, *output_stream); } else { - Call(item.function, stream_temp1, true, *output_stream); + Call(item.function, nullptr, stream_temp1, true, *output_stream); ClearStream(stream_temp1); } @@ -1777,12 +1915,14 @@ void Generator::MakeTextReturn(Item & item) { // output stream in [return] statement is ignored (we use only the stream produced by the whole block) // this Call() sets last_res which is used later when we return to CallBlock() - Call(item.function, stream_temp1, false, empty_stream); + Call(item.function, nullptr, stream_temp1, false, empty_stream); ClearStream(stream_temp1); } } + + template bool Generator::LimitAchieved() { @@ -1831,15 +1971,18 @@ void Generator::MakeText(Item & item) stack_tab[stack_index-1].Clear(); stack_tab[stack_index-1].item = &item; - if ( item.type == Item::item_text ) MakeItemText(item); - else if( item.type == Item::item_container )MakeTextContainer(item); - else if( item.type == Item::item_function ) MakeTextNormal(item); - else if( item.type == Item::item_if ) MakeTextIf(item); - else if( item.type == Item::item_def ) MakeTextDefine(item); - else if( item.type == Item::item_for ) MakeTextFor(item); - else if( item.type == Item::item_filter ) MakeTextFilter(item); - else if( item.type == Item::item_ezc ) MakeTextEzc(item); - else if( item.type == Item::item_return ) MakeTextReturn(item); + if ( item.type == Item::item_text ) MakeItemText(item); + else if( item.type == Item::item_container ) MakeTextContainer(item); + else if( item.type == Item::item_function ) MakeTextNormal(item); + else if( item.type == Item::item_if ) MakeTextIf(item); + else if( item.type == Item::item_def ) MakeTextDefine(item); + else if( item.type == Item::item_def_if_not_set ) MakeTextDefineIfNotSet(item); + else if( item.type == Item::item_let ) MakeTextLet(item); + else if( item.type == Item::item_let_if_not_set ) MakeTextLetIfNotSet(item); + else if( item.type == Item::item_for ) MakeTextFor(item); + else if( item.type == Item::item_filter ) MakeTextFilter(item); + else if( item.type == Item::item_ezc ) MakeTextEzc(item); + else if( item.type == Item::item_return ) MakeTextReturn(item); else if( item.type == Item::item_err ) CreateMsg(L"a wrong directive"); diff --git a/src/item.h b/src/item.h index c2fb49a..90f2d82 100644 --- a/src/item.h +++ b/src/item.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2007-2018, Tomasz Sowa + * Copyright (c) 2007-2021, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -55,7 +55,8 @@ struct Item { item_none, item_container, item_text, item_function, item_if, item_for, item_else, item_end, item_err, item_include, item_comment, - item_def, item_filter, item_ezc, item_block, item_return + item_def, item_def_if_not_set, item_let, item_let_if_not_set, + item_filter, item_ezc, item_block, item_return }; diff --git a/src/patternparser.cpp b/src/patternparser.cpp index b055bc6..9d541d8 100644 --- a/src/patternparser.cpp +++ b/src/patternparser.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2007-2018, Tomasz Sowa + * Copyright (c) 2007-2021, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -486,7 +486,7 @@ bool PatternParser::IsNameChar(wchar_t c) return ((c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || - c=='_' || c=='-' || c=='.' || c=='#'); + c=='_' || c=='-' || c=='.' || c=='#' || c=='?'); } @@ -784,10 +784,85 @@ void PatternParser::ReadDirectiveInclude(Item & item) void PatternParser::ReadDirectiveDef(Item & item) { item.type = Item::item_def; - ReadFunction(item); + + if( ReadFunction(item) ) + { + if( item.function.parameters.size() > 1 ) + item.type = Item::item_err; + } } +void PatternParser::ReadDirectiveDefIfNotSet(Item & item) +{ + item.type = Item::item_def_if_not_set; + + if( ReadFunction(item) ) + { + if( item.function.parameters.size() > 1 ) + item.type = Item::item_err; + } +} + + +void PatternParser::ReadDirectiveLet(Item & item) +{ + item.type = Item::item_let; + + if( ReadFunction(item) ) + { + std::vector & parameters = item.function.parameters; + + if( parameters.size() > 1 ) + { + item.type = Item::item_err; + item.function.Clear(); + } + else + if( parameters.size() == 1 && parameters[0]->is_function && !parameters[0]->parameters.empty() ) + { + /* + * if the first parameter in [let] is a function e.g. [let variable function_name] (here the first parameter is function_name) + * then the function cannot have parameters itselt (because it is not evaluated here) + * this is only an alias + */ + item.type = Item::item_err; + item.function.Clear(); + } + } +} + + +void PatternParser::ReadDirectiveLetIfNotSet(Item & item) +{ + item.type = Item::item_let_if_not_set; + + if( ReadFunction(item) ) + { + std::vector & parameters = item.function.parameters; + + if( parameters.size() > 1 ) + { + item.type = Item::item_err; + item.function.Clear(); + } + else + if( parameters.size() == 1 && parameters[0]->is_function && !parameters[0]->parameters.empty() ) + { + /* + * if the first parameter in [let] is a function e.g. [let variable function_name] (here the first parameter is function_name) + * then the function cannot have parameters itselt (because it is not evaluated here) + * this is only an alias + */ + item.type = Item::item_err; + item.function.Clear(); + } + } +} + + + + void PatternParser::ReadDirectiveFilter(Item & item) { item.type = Item::item_filter; @@ -825,11 +900,16 @@ void PatternParser::ReadDirectiveOut(Item & item) void PatternParser::ReadDirectiveBlock(Item & item) { item.type = Item::item_block; - ReadFunction(item); - // only one function without arguments - if( !item.function.parameters.empty() ) - item.type = Item::item_err; + if( ReadFunction(item) ) + { + // only one function without arguments + if( !item.function.parameters.empty() ) + { + item.type = Item::item_err; + item.function.Clear(); + } + } } @@ -859,6 +939,9 @@ std::wstring name; else if( name == L"for" ) ReadDirectiveFor(item); else if( name == L"include" ) ReadDirectiveInclude(item); else if( name == L"def" ) ReadDirectiveDef(item); + else if( name == L"def?" ) ReadDirectiveDefIfNotSet(item); + else if( name == L"let" ) ReadDirectiveLet(item); + else if( name == L"let?" ) ReadDirectiveLetIfNotSet(item); else if( name == L"filter" ) ReadDirectiveFilter(item); else if( name == L"ezc" ) ReadDirectiveEzc(item); else if( name == L"out" ) ReadDirectiveOut(item); diff --git a/src/patternparser.h b/src/patternparser.h index 211b446..6a9cd9d 100644 --- a/src/patternparser.h +++ b/src/patternparser.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2007-2018, Tomasz Sowa + * Copyright (c) 2007-2021, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -175,6 +175,9 @@ private: void ReadDirectiveComment(Item & item); void ReadDirectiveInclude(Item & item); void ReadDirectiveDef(Item & item); + void ReadDirectiveDefIfNotSet(Item & item); + void ReadDirectiveLet(Item & item); + void ReadDirectiveLetIfNotSet(Item & item); void ReadDirectiveFilter(Item & item); void ReadDirectiveEzc(Item & item); void ReadDirectiveOut(Item & item);