diff --git a/include/common.h b/include/common.h index 05fa772..a931960 100644 --- a/include/common.h +++ b/include/common.h @@ -255,11 +255,12 @@ public: ObjectList* PushBack(ObjectBase* p) {vals->push_back(p); return this;} }; - typedef ObjectBase* (*Func)(const ObjectList*); +typedef int (*ModuleInitFunc)(const void*); extern "C" { EXPORT void RegisterFunction(const std::string& name, Func func); +EXPORT int LoadModule(const std::string& name, const void* p, const std::string& modname=""); } #endif \ No newline at end of file diff --git a/include/globals.h b/include/globals.h index c993ec9..f1309d8 100644 --- a/include/globals.h +++ b/include/globals.h @@ -1,8 +1,9 @@ #ifndef GLOBALS_H #define GLOBALS_H +#include "dlfcn.h" #include -#include #include +#include #include "object.h" // Variables definitions @@ -20,6 +21,10 @@ extern G_toType G_tosave; // List of objects to print extern G_toType G_toprint; +// Loaded modules +typedef std::vector G_libsType; +extern G_libsType G_libs; + void ClearGlobals(); bool Save(const ObjectList* input); diff --git a/include/parser.h b/include/parser.h index 720fab0..5568c54 100644 --- a/include/parser.h +++ b/include/parser.h @@ -1,7 +1,7 @@ #ifndef PARSER_PARSER_H #define PARSER_PARSER_H -#include #include +#include #include // State of lexical parser (filename and position in file) @@ -15,6 +15,7 @@ struct lexical_state // Container for "static" parameters of lexical parser struct lexical_extra { + std::string includedirs,moduledirs; struct lexical_state state; unsigned int maxinclevel; int retcode; diff --git a/src/Makefile b/src/Makefile index 7dc7589..cf63006 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,7 @@ OPTFLAGS=-O2 -flto -g EXPORTFLAGS=-fvisibility=hidden -fpic -Wl,--export-dynamic CPPFLAGS=-std=gnu++11 -I../include -LIBSFLAGS=-lpthread +LIBSFLAGS=-ldl -lpthread WARNFLAGS=-Wall CFLAGS=$(OPTFLAGS) $(EXPORTFLAGS) $(WARNFLAGS) $(CPPFLAGS) diff --git a/src/globals.cpp b/src/globals.cpp index f767340..a13a03a 100644 --- a/src/globals.cpp +++ b/src/globals.cpp @@ -1,6 +1,5 @@ #include "globals.h" - // Variables definitions G_varsType G_vars; @@ -13,17 +12,94 @@ G_toType G_tosave; // List of objects to print G_toType G_toprint; +// Loaded modules +G_libsType G_libs; + void ClearGlobals() { for(auto& it:G_vars) delete it.second; for(auto& it:G_tosave) delete it; for(auto& it:G_toprint) delete it; + for(auto& it:G_libs) dlclose(it); G_vars.clear(); G_tosave.clear(); G_toprint.clear(); } + +int LoadModule(const std::string& name, const void* p, const std::string& modname) +{ + const std::string spath=*(reinterpret_cast(p)); + + // Load module + void* handle; + void* initfunc; + std::string initname; + + if(0!=modname.size()) initname=modname; + else + { + // Remove directory name, if present + initname=name.substr((name.rfind('/')!=std::string::npos)?(name.rfind('/')+1):0,std::string::npos); + // Remove ".so" on the end of string + if(initname.rfind(".so")!=std::string::npos) initname.erase(initname.rfind(".so"),std::string::npos); + } + initname+="_module_init"; + + // Check if module is statically linked or already loaded: dlopen'ed the main program + handle=dlopen(0,RTLD_LAZY|RTLD_GLOBAL); + if(0==handle) + { + COUT(ERROR)<(initfunc))(p); + if(0!=ret) COUT(ERROR)< REAL %token BOOL @@ -82,8 +83,25 @@ inline void conferror(const YYLTYPE& locp, yyscan_t sc, const std::string& str) input: %empty {COUT(DEBUG)<<"Empty input\n";} | input line {COUT(DEBUG)<<" input line\n";} + | input dirline {COUT(DEBUG)<<" input dirline\n";} ; +dirline: + DIR_INCLUDEPATH STRING {COUT(DEBUG)<<" DIR_INCLUDEPATH STRING\n"; confget_extra(scanner)->includedirs+=":"+*$2+(('/'==$2->back())?"":"/"); delete $2;} + | DIR_MODULEPATH STRING {COUT(DEBUG)<<" DIR_MODULEPATH STRING\n"; confget_extra(scanner)->moduledirs+=":"+*$2+(('/'==$2->back())?"":"/"); delete $2;} + | DIR_USE STRING {COUT(DEBUG)<<" DIR_USE STRING\n"; + int ret=LoadModule(*$2,&confget_extra(scanner)->moduledirs); + if(0!=ret) conferror(@1,scanner," fail to load module "+(*$2)); + delete $2; + if(0!=ret) YYABORT; + } + | DIR_USE STRING DIR_AS STRING {COUT(DEBUG)<<" DIR_USE STRING DIR_AS STRING\n"; + int ret=LoadModule(*$2,&confget_extra(scanner)->moduledirs,*$4); + if(0!=ret) conferror(@1,scanner," fail to load module "+(*$2)); + delete $2; delete $4; + if(0!=ret) YYABORT; + } + line: NAME ASSIGN object ENDL {COUT(DEBUG)<<" NAME ASSIGN object ENDL\n"; if(G_vars.count(*$1)!=0) delete G_vars[*$1]; G_vars[*$1]=$3; delete $1;} | NAME ASSIGN list ENDL {COUT(DEBUG)<<" NAME ASSIGN list ENDL\n"; if(G_vars.count(*$1)!=0) delete G_vars[*$1]; G_vars[*$1]=$3; delete $1;} diff --git a/src/parser/lexical.l b/src/parser/lexical.l index a39b3ba..22dfd9a 100644 --- a/src/parser/lexical.l +++ b/src/parser/lexical.l @@ -13,8 +13,10 @@ %x PSTRING %x PARSE %x INCLUDE +%x PREPROC %{ +#include #include "common.h" #include "parser.h" // flex use register keyword, but it deprecated in C++11. @@ -41,30 +43,55 @@ std::string str; %} %% -@include[ \t]*\" yy_push_state(INCLUDE,yyscanner); str.erase(); yynextsym; +@[Ii][Nn][Cc][Ll][Uu][Dd][Ee][ \t]*\" yy_push_state(INCLUDE,yyscanner); str.erase(); yynextsym; +@ BEGIN(PREPROC); yynextsym; +[a-zA-Z]* { + yynextsym; str=yytext; transform(str.begin(),str.end(),str.begin(),::tolower); + if("use"==str) return DIR_USE; + if("includepath"==str) return DIR_INCLUDEPATH; + if("modulepath"==str) return DIR_MODULEPATH; + if("as"==str) return DIR_AS; + COUT(WARNING)<\" { if(yyextra->state.inclevel>=yyextra->maxinclevel) yyerrormessage("maximal include level reached",-1); FILE* fd; + std::string fullname; // yyextra->curdir is directory with currently scanned file // !!!NONPORTABLE!!! - if('/'==str[0]) fd=fopen(str.c_str(),"r"); // absolute path + fullname=str; + if('/'==fullname[0]) fd=fopen(fullname.c_str(),"r"); // absolute path else { - // first, try to search file in working directory - fd=fopen(str.c_str(),"r"); - // if fail, try to search in directory with currently scanned file - if(fd==0) fd=fopen((yyextra->state.curdir+str).c_str(),"r"); + // first, try to search in directory with currently scanned file + fullname=yyextra->state.curdir+str; + fd=fopen(fullname.c_str(),"r"); + // if fail, try to search file in includedirs + if(0==fd) + { + std::string curpath; + size_t bpos=0,epos; + do + { + epos=yyextra->includedirs.find(':',bpos); + curpath=yyextra->includedirs.substr(bpos,(std::string::npos==epos)?epos:(epos-bpos)); + fullname=curpath+str; + fd=fopen(fullname.c_str(),"r"); + if(0!=fd) break; + bpos=epos+1; + } while(std::string::npos!=epos); + } } - if(fd==0) yyerrormessage("can't open file "+str,-1); + if(0==fd) yyerrormessage("can't open file "+str,-1); yynextsym; yyextra->fds.push(fd); yyextra->states.push(yyextra->state); COUT(DEBUG)<state.inclevel++; - yyextra->ParsePath(str); + yyextra->ParsePath(fullname); yyextra->state.curline=1; yyextra->state.curpos=yyextra->state.curoffset=0; yylloc_param->filename=yyextra->state.curdir+yyextra->state.filename; @@ -86,15 +113,16 @@ std::string str; , COUT(MOREDEBUG)<<" DELIM()"; setyyllocp; yynextsym; return DELIM; [a-zA-Z][a-zA-Z0-9_]* COUT(MOREDEBUG)<<" IDENTIFIER("<str=new std::string(yytext); setyyllocp; yynextsym; return IDENTIFIER; \. COUT(MOREDEBUG)<<" DOT()"; setyyllocp; yynextsym; return yytext[0]; -[ \t]+ yynextsym; +[ \t]+ yynextsym; \n yynextline; +\n BEGIN(0); yynextline; \#.* yynextsym; -\" BEGIN(PSTRING); str.erase(); yyextra->states.push(yyextra->state); yynextsym; -. yyerrormessage(std::string("unknown symbol ")+yytext+" at position "+std::to_string(yyextra->state.curpos),-1); +\" yy_push_state(PSTRING,yyscanner); str.erase(); yyextra->states.push(yyextra->state); yynextsym; +. yyerrormessage(std::string("unknown symbol ")+yytext+" at position "+std::to_string(yyextra->state.curpos),-1); \\\\ str+='\\'; yynextsym; \\\" str+='\"'; yynextsym; \n str+=yytext[0]; yynextline; -\" BEGIN(PARSE); COUT(MOREDEBUG)<<" STRING("<str=new std::string(str); {struct lexical_state prstate=yyextra->states.top(); yyextra->states.pop(); yylloc_param->first_line=prstate.curline; yylloc_param->last_line=yyextra->state.curline; yylloc_param->first_column=prstate.curpos; yylloc_param->last_column=yyextra->state.curpos+yyleng;}; yynextsym; return STRING; +\" yy_pop_state(yyscanner); COUT(MOREDEBUG)<<" STRING("<str=new std::string(str); {struct lexical_state prstate=yyextra->states.top(); yyextra->states.pop(); yylloc_param->first_line=prstate.curline; yylloc_param->last_line=yyextra->state.curline; yylloc_param->first_column=prstate.curpos; yylloc_param->last_column=yyextra->state.curpos+yyleng;}; yynextsym; return STRING; . str+=yytext[0]; yynextsym; <> yyextra->states.pop(); yyerrormessage("unclosed quote",-1); <> yypop_buffer_state(yyscanner); if(0!=YY_CURRENT_BUFFER) {yyextra->state=yyextra->states.top(); yyextra->states.pop(); fclose(yyextra->fds.top()); yyextra->fds.pop(); yylloc_param->filename=yyextra->state.curdir+yyextra->state.filename;} else yyterminate(0);