diff --git a/CMakeLists.txt b/CMakeLists.txt index 965e76c746cf6254b6aba9f08f6c3f7bde9b6d73..b1a28eb40ac2cf3fc4cca83c88d71c6683511527 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ option(ENABLE_PARSER "Build the GEO file parser" ON) option(ENABLE_PETSC "Enable PETSc linear algebra solvers" ON) option(ENABLE_POST "Build the post-processing module" ON) option(ENABLE_QT "Build QT GUI" OFF) +option(ENABLE_READLINE "Enable Readline in Lua prompt" ON) option(ENABLE_SLEPC "Enable SLEPc eigensolvers" ON) option(ENABLE_SOLVER "Enable solver components" ON) option(ENABLE_TAUCS "Enable Taucs linear algebra solver" ON) @@ -667,6 +668,21 @@ if(ENABLE_OSMESA) endif(OSMESA_LIB) endif(ENABLE_OSMESA) +if(HAVE_LUA) + if(ENABLE_READLINE) + find_library(READLINE_LIB readline PATH_SUFFIXES lib) + if(READLINE_LIB) + find_path(READLINE_INC "readline.h" PATH_SUFFIXES src readline include) + if(READLINE_INC) + set(HAVE_READLINE TRUE) + list(APPEND CONFIG_OPTIONS "Readline") + list(APPEND EXTERNAL_LIBRARIES ${READLINE_LIB}) + list(APPEND EXTERNAL_INCLUDES ${READLINE_INC}) + endif(READLINE_INC) + endif(READLINE_LIB) + endif(ENABLE_READLINE) +endif(HAVE_LUA) + include(CheckFunctionExists) check_function_exists(vsnprintf HAVE_VSNPRINTF) if(NOT HAVE_VSNPRINTF) @@ -1003,4 +1019,5 @@ message("Run 'ccmake ${CMAKE_SOURCE_DIR}' to fine-tune the configuration.") message("") mark_as_advanced(BISON FLEX GMP_LIB GMSH_EXTRA_VERSION HDF5_LIB MAKEINFO - MED_LIB OCC_INC SZ_LIB TAUCS_LIB LUA_LIB TEXI2PDF) + MED_LIB OCC_INC SZ_LIB TAUCS_LIB LUA_LIB TEXI2PDF + READLINE_LIB) diff --git a/Common/Bindings.h b/Common/Bindings.h index c0d2f0368ce4d2a8c4934ed958c6e1756d029bce..0b254c188180ee00381285c693b1f4fb0b03d464 100644 --- a/Common/Bindings.h +++ b/Common/Bindings.h @@ -6,9 +6,11 @@ #include "GmshConfig.h" class methodBinding{ + std::string _description; public: void setArgNames(std::string arg1, ...){} - void setDescription(std::string description){} + void setDescription(std::string description){_description=description;} + inline const std::string getDescription() const {return _description;} }; #if defined(HAVE_LUA) @@ -17,4 +19,5 @@ class methodBinding{ #include "DummyBindings.h" #endif + #endif diff --git a/Common/CommandLine.cpp b/Common/CommandLine.cpp index 4838a6826f2faaf1f50d5594ac8d52b889e29a90..5268a3148653124b18f7145947c62dd075efe57b 100644 --- a/Common/CommandLine.cpp +++ b/Common/CommandLine.cpp @@ -96,6 +96,9 @@ void PrintUsage(const char *name) Msg::Direct(" - Parse input files, then exit"); #if defined(HAVE_FLTK) Msg::Direct(" -a, -g, -m, -s, -p Start in automatic, geometry, mesh, solver or post-processing mode"); +#endif +#if defined(HAVE_LUA) + Msg::Direct(" -lua Start an interactive lua session"); #endif Msg::Direct(" -pid Print process id on stdout"); Msg::Direct(" -listen Always listen to incoming connection requests"); @@ -603,6 +606,12 @@ void GetOptions(int argc, char *argv[]) Msg::Fatal("Missing argument"); } #endif +#if defined (HAVE_LUA) + else if(!strcmp(argv[i] + 1, "lua")) { + i++; + CTX::instance()->batch = -4; + } +#endif #if defined(__APPLE__) else if(!strncmp(argv[i] + 1, "psn", 3)) { // The Mac Finder launches programs with a special command diff --git a/Common/Context.h b/Common/Context.h index 7571f953e7611f81bc61f4d9c9e9985366583078..720f5b3ccfe4994e050cabe7375a27d3df304a4a 100644 --- a/Common/Context.h +++ b/Common/Context.h @@ -92,11 +92,11 @@ class CTX { int fileChooserPosition[2]; // use the system menu bar on Mac OS X? int systemMenuBar; - // batch mode (-3: server daemon, -2: check coherence, -1: write + // batch mode (-4: lua session, -3: server daemon, -2: check coherence, -1: write // geo, 0: full gfx, 1: 1D mesh, 2: 2D mesh, 3: 3D mesh, 4: adapt // mesh, 5: refine mesh) int batch; - // batch operations to appy after meshing (1: partition mesh) + // batch operations to apply after meshing (1: partition mesh) int batchAfterMesh; // initial menu (0: automatic, 1: geom, 2: mesh, 3: solver, 4: post) int initialContext; diff --git a/Common/Gmsh.cpp b/Common/Gmsh.cpp index 33b7760072985671db783397b8dac0c24a23a966..cbcb48aa248e40e56061e086b7361d30c50a0c82 100644 --- a/Common/Gmsh.cpp +++ b/Common/Gmsh.cpp @@ -28,6 +28,10 @@ #include "PluginManager.h" #endif +#if defined(HAVE_LUA) +#include "LuaBindings.h" +#endif + int GmshInitialize(int argc, char **argv) { // we need at least one model during option parsing @@ -141,6 +145,12 @@ int GmshBatch() } #endif +#if defined(HAVE_LUA) + if(CTX::instance()->batch == -4){ + binding::instance()->interactiveSession(); + } + else +#endif if(CTX::instance()->batch == -3){ GmshRemote(); } diff --git a/Common/GmshConfig.h.in b/Common/GmshConfig.h.in index 245960ab20f22ce30901e2f7b1f02284db09591f..97c42ba0b6ac0d49c95c38a9528055e6e146bc0b 100644 --- a/Common/GmshConfig.h.in +++ b/Common/GmshConfig.h.in @@ -38,6 +38,7 @@ #cmakedefine HAVE_PETSC #cmakedefine HAVE_POST #cmakedefine HAVE_QT +#cmakedefine HAVE_READLINE #cmakedefine HAVE_SLEPC #cmakedefine HAVE_SOLVER #cmakedefine HAVE_TAUCS diff --git a/Common/LuaBindings.cpp b/Common/LuaBindings.cpp index b88a153fc3bc5ca46ab661c16b863e392c3eb902..5b8c729213d5efc7a2310f26850b7e4fed24ce2a 100644 --- a/Common/LuaBindings.cpp +++ b/Common/LuaBindings.cpp @@ -1,6 +1,7 @@ #include "GmshConfig.h" #ifdef HAVE_LUA #include <iostream> +#include <string> #include "Gmsh.h" #include "Bindings.h" #include "dgSystemOfEquations.h" @@ -16,16 +17,130 @@ extern "C" { #include "lualib.h" #include "lauxlib.h" } -void report_errors(lua_State *L, int status) + +#ifdef HAVE_READLINE +#include "readline.h" +#include "history.h" +#endif + +static void report_errors(lua_State *L, int status) { if ( status!=0 ) { std::cerr << "-- " << lua_tostring(L, -1) << std::endl; lua_pop(L, 1); // remove error message } } -int read_lua(const char *filename) + +const char *colorRed = "\033[1;31m"; +const char *colorGreen = "\033[1;32m"; +const char *colorBlue = "\033[1;34m"; +const char *colorDefault = "\033[0m"; +const char *colorBold = "\033[1m"; + +static void list_methods(classBinding *cb){ + if(cb->methods.size()) + std::cout<<colorBold<<"Methods from "<<cb->getClassName()<<colorDefault<<"\n"; + for(std::map<std::string,luaMethodBinding *>::iterator it = cb->methods.begin(); it!=cb->methods.end(); it++){ + std::cout<<colorBlue<<" "<<it->first<<colorDefault<<" : "; + const std::string description=it->second->getDescription(); + std::cout<<(description.empty()?"no help available":description) <<"\n"; + } + if(cb->getParent()) + list_methods(cb->getParent()); +} + +static int lua_help (lua_State *L){ + int argc = lua_gettop(L); + binding *b = binding::instance(); + if (argc==0){ + std::cout<<"this is the gmsh help\n"; + for(std::map<std::string,classBinding *>::iterator it = b->classes.begin(); it!=b->classes.end(); it++){ + if(it->second->getParent()) + continue; + std::cout<<colorBlue<<" "<<it->first<<colorDefault<<" : "; + const std::string description=it->second->getDescription(); + std::cout<<(description.empty()?"no help available":description) <<"\n"; + } + }else{ + const char *className = luaL_checkstring(L,1); + if(b->classes.find(className)==b->classes.end()){ + std::cout<<"Class "<<colorBold<<className<<colorDefault<<" does not exist.\n"; + return 0; + } + classBinding *cb = b->classes[className]; + std::cout<<"\n"<<colorRed<<className<<colorDefault<<"\n"; + const std::string description=cb->getDescription(); + std::cout<<(description.empty()?"no help available":description) <<"\n"; + std::cout<<"\n"; + list_methods(cb); + std::cout<<"\n"; + if(cb->children.size()){ + std::cout<<colorBold<<"Children of "<<cb->getClassName()<<colorDefault<<"\n"; + for(std::set<classBinding *>::iterator it = cb->children.begin(); it!=cb->children.end(); it++){ + std::cout<<" "<<colorGreen<<(*it)->getClassName()<<colorDefault<<" : "; + const std::string description=(*it)->getDescription(); + std::cout<<(description.empty()?"no help available":description) <<"\n"; + } + std::cout<<"\n"; + } + } + return 0; +} +#ifdef HAVE_READLINE +static int lua_save (lua_State *L){ + const char *filename = luaL_checkstring(L,1); + write_history(filename); + return 0; +} +static int lua_clear (lua_State *L){ + clear_history(); + return 0; +} +#endif + +int binding::readFile(const char *filename) { - lua_State *L = lua_open(); + int s = luaL_loadfile(L, filename); + if ( s==0 ) { + Msg::Info("lua executes %s\n",filename); + s = lua_pcall(L, 0, LUA_MULTRET, 0); + } + report_errors(L, s); + lua_close(L); + return (s==0); +} + +void binding::interactiveSession(){ + Msg::Info("Starting interactive lua session, press Ctrl-D to exit"); + #ifdef HAVE_READLINE + using_history(); + while (const char *line=readline("lua> ")){ + char *expansion; + int r=history_expand((char*)line,&expansion); + if(r) + std::cout<<expansion<<"\n"; + if(r==0 || r==1){ + add_history(expansion); + report_errors(L, luaL_dostring(L, expansion)); + } + free(expansion); + } + clear_history(); + #else + while(std::string line, + std::cout<<"lua> ", + getline(std::cin,line) ){ + report_errors(L, luaL_dostring(L, line.c_str())); + } + #endif +} + +binding::binding(){ + if(_instance){ + Msg::Error("Only one instance of lua bindings is allowed"); + } + _instance=this; + L = lua_open(); luaopen_base(L); luaopen_table(L); luaopen_os(L); @@ -33,32 +148,26 @@ int read_lua(const char *filename) luaopen_string(L); luaopen_math(L); luaopen_debug(L); - binding b; - b.L=L; + + lua_register(L,"help",lua_help); + #ifdef HAVE_READLINE + lua_register(L,"saveHistory",lua_save); + lua_register(L,"clearHistory",lua_clear); + #endif // Register Lua bindings - GModel::registerBindings(&b); - dgSystemOfEquations::registerBindings(&b); - dgBoundaryCondition::registerBindings(&b); - dgConservationLaw::registerBindings(&b); - dgConservationLawShallowWater2dRegisterBindings(&b); - dgConservationLawWaveEquationRegisterBindings(&b); - dgConservationLawAdvectionRegisterBindings(&b); - dgPerfectGasLaw2dRegisterBindings(&b); - fullMatrix<double>::registerBindings(&b); - function::registerBindings(&b); - functionLua::registerBindings(&b); + GModel::registerBindings(this); + dgSystemOfEquations::registerBindings(this); + dgBoundaryCondition::registerBindings(this); + dgConservationLaw::registerBindings(this); + dgConservationLawShallowWater2dRegisterBindings(this); + dgConservationLawWaveEquationRegisterBindings(this); + dgConservationLawAdvectionRegisterBindings(this); + dgPerfectGasLaw2dRegisterBindings(this); + fullMatrix<double>::registerBindings(this); + function::registerBindings(this); + functionLua::registerBindings(this); function::registerDefaultFunctions(); - - int s = luaL_loadfile(L, filename); - - if ( s==0 ) { - printf("lua executes %s\n",filename); - s = lua_pcall(L, 0, LUA_MULTRET, 0); - } - report_errors(L, s); - - lua_close(L); - return (s==0); } +binding *binding::_instance=NULL; #endif diff --git a/Common/LuaBindings.h b/Common/LuaBindings.h index 96756a0b5445c3bc3372f442359158bc7ebd74ad..290387e0bc576083568332d65d602741b8ef282c 100644 --- a/Common/LuaBindings.h +++ b/Common/LuaBindings.h @@ -2,14 +2,28 @@ #define _LUA_BINDINGS_H_ #include "Bindings.h" #ifdef HAVE_LUA -int read_lua(const char *filename); +#include "BindingsDocTemplates.h" extern "C" { #include "lua.h" #include "lauxlib.h" } #include <vector> +#include <set> +class classBinding; +class binding { + static binding *_instance; + public: + inline static binding *instance(){return _instance ? _instance : new binding();} + lua_State *L; + int readFile(const char *filename); + void interactiveSession(); + binding(); + std::map<std::string,classBinding *> classes; + template<class t> + classBinding *addClass(std::string className); +}; /*** store a unique static class name for each binded class ***/ template <typename type> @@ -302,12 +316,15 @@ template <typename cb> class methodBindingT:public luaMethodBinding { public: cb _f; - methodBindingT(const std::string luaname,cb f):luaMethodBinding(luaname){ + methodBindingT(const std::string luaname,cb f):luaMethodBinding(luaname) { _f=f; } int call (lua_State *L) { return luaCall(L,_f); } + void getArgNames(std::string &returnTypeName, std::vector<std::string> &argTypeName) { + getArgNames(_f,returnTypeName,argTypeName); + } }; template <typename tObj, typename t0=void, typename t1=void , typename t2=void, typename t3=void> @@ -357,10 +374,9 @@ class constructorBindingT<tObj,t0,t1,t2,void>:public luaMethodBinding { } }; - class classBinding { std::string _className; - lua_State *_L; + binding *_b; static int callMethod(lua_State *L) { return static_cast<luaMethodBinding*>(lua_touserdata(L, lua_upvalueindex(1)))->call(L); } @@ -381,19 +397,25 @@ class classBinding { return 1; } void setConstructorLuaMethod(luaMethodBinding *constructor){ - lua_getglobal(_L,_className.c_str()); - int methods = lua_gettop(_L); - lua_getmetatable(_L,methods); - int mt = lua_gettop(_L); - lua_pushlightuserdata(_L,(void*)constructor); - lua_pushcclosure(_L, callMethod, 1); - lua_setfield(_L, mt,"__call"); - lua_pop(_L,2); - } + lua_State *L = _b->L; + lua_getglobal(L,_className.c_str()); + int methods = lua_gettop(L); + lua_getmetatable(L,methods); + int mt = lua_gettop(L); + lua_pushlightuserdata(L,(void*)constructor); + lua_pushcclosure(L, callMethod, 1); + lua_setfield(L, mt,"__call"); + lua_pop(L,2); + } + //for the doc + std::string _description; + classBinding *_parent; public: + std::set<classBinding *> children; // get userdata from Lua stack and return pointer to T object - classBinding(lua_State *L, std::string name){ - _L=L; + classBinding(binding *b, std::string name){ + _b=b; + lua_State *L = _b->L; _className=name; // there are 3 tables involved : @@ -423,30 +445,42 @@ public: } template<typename parentType> void setParentClass(){ + if(_parent) + Msg::Error("Multiple inheritance not implemented in lua bindings for class %s",_className.c_str()); std::string parentClassName=className<parentType>::get(); - lua_getglobal(_L,_className.c_str()); - lua_getmetatable(_L,-1); - int mt=lua_gettop(_L); - lua_getglobal(_L,parentClassName.c_str()); - lua_setfield(_L,mt,"__index");// mt.__index = global[_parentClassName] // this is the inheritance bit - lua_pop(_L,2); + _parent = _b->classes[parentClassName]; + _parent->children.insert(this); + lua_getglobal(_b->L,_className.c_str()); + lua_getmetatable(_b->L,-1); + int mt=lua_gettop(_b->L); + lua_getglobal(_b->L,parentClassName.c_str()); + lua_setfield(_b->L,mt,"__index");// mt.__index = global[_parentClassName] // this is the inheritance bit + lua_pop(_b->L,2); + } + + void setDescription(std::string description){ + _description = description; } - void setDescription(std::string description){}; + inline const std::string getDescription()const {return _description;}; + inline classBinding *getParent()const {return _parent;}; + std::map<std::string, luaMethodBinding *> methods; template <typename cb> methodBinding *addMethod(std::string n, cb f){ luaMethodBinding *mb = new methodBindingT<cb>(n,f); + methods[n]=mb; + lua_State *L=_b->L; - lua_getglobal(_L,_className.c_str()); - int methods=lua_gettop(_L); + lua_getglobal(L,_className.c_str()); + int methods=lua_gettop(L); /*int (*lc)(lua_State *,cb)=(int(*)(lua_State*,cb))luaCall; - lua_pushlightuserdata(_L, (void*)lc); - lua_pushlightuserdata(_L, (void*)f); - lua_pushcclosure(_L, callMethod2, 2);*/ - lua_pushlightuserdata(_L, (void*)mb); - lua_pushcclosure(_L, callMethod, 1); - lua_setfield(_L,methods, n.c_str()); //className.name = callMethod(mb) - lua_pop(_L,1); + lua_pushlightuserdata(L, (void*)lc); + lua_pushlightuserdata(L, (void*)f); + lua_pushcclosure(L, callMethod2, 2);*/ + lua_pushlightuserdata(L, (void*)mb); + lua_pushcclosure(L, callMethod, 1); + lua_setfield(L,methods, n.c_str()); //className.name = callMethod(mb) + lua_pop(L,1); return mb; } template <typename tObj, typename t0, typename t1, typename t2, typename t3 > @@ -479,19 +513,15 @@ public: setConstructorLuaMethod(constructorLua); return constructorLua; } + inline const std::string getClassName()const {return _className;} }; -class binding { - public: - lua_State *L; - template<class t> - classBinding *addClass(std::string className); -}; template<typename t> classBinding *binding::addClass(std::string name){ className<t>::set(name); - classBinding *cb=new classBinding(L,name); + classBinding *cb=new classBinding(this,name); + classes[name]=cb; return cb; } #endif diff --git a/Common/OpenFile.cpp b/Common/OpenFile.cpp index 4f7b19aa25a50f10fcef345ac0b8aea4985ea87c..fdb7b72b35e8533da1cdc27271578e1e366aeeff 100644 --- a/Common/OpenFile.cpp +++ b/Common/OpenFile.cpp @@ -332,7 +332,7 @@ int MergeFile(std::string fileName, bool warnIfMissing) #endif #if defined(HAVE_LUA) else if(ext == ".lua" || ext == ".LUA") { - status = read_lua(fileName.c_str()); + status = binding::instance()->readFile(fileName.c_str()); } #endif else { diff --git a/doc/gmsh.1 b/doc/gmsh.1 index 0c644ace787b5882175d3e9033e2d62abe6b17c9..f4dd4cc2b2bf94f63da099b694ffbcd0403eb234 100644 --- a/doc/gmsh.1 +++ b/doc/gmsh.1 @@ -120,6 +120,9 @@ parse input files, then exit. .B \-a, \-g, \-m, \-s, \-p start in automatic, geometry, mesh, solver or post-processing mode. .TP 4 +.B \-lua +start an interactive lua session. +.TP 4 .B \-pid print pid on stdout. .TP 4 diff --git a/doc/texinfo/gmsh.texi b/doc/texinfo/gmsh.texi index 1efa6f92191e8670189f7d19a386a851b9478653..31503f03f605763c57cb2a3964f1284e785bb565 100644 --- a/doc/texinfo/gmsh.texi +++ b/doc/texinfo/gmsh.texi @@ -915,6 +915,8 @@ Specify display Parse input files, then exit @item -a, -g, -m, -s, -p Start in automatic, geometry, mesh, solver or post-processing mode +@item -lua +Start an interactive lua session @item -pid Print pid on stdout @item -listen