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);