Skip to content
Snippets Groups Projects
Forked from gmsh / gmsh
14131 commits behind the upstream repository.
LuaBindings.cpp 13.64 KiB
// Gmsh - Copyright (C) 1997-2010 C. Geuzaine, J.-F. Remacle
//
// See the LICENSE.txt file for license information. Please report all
// bugs and problems to <gmsh@geuz.org>.
//
// Contributor(s):
//   Jonathan Lambrechts
//

#include "GmshConfig.h"

#if defined(HAVE_LUA)
#include <iostream>
#include <string>
#include "Gmsh.h"
#include "Context.h"
#include "MVertex.h"
#include "MElement.h"
#include "MTriangle.h"
#include "MQuadrangle.h"
#include "MPrism.h"
#include "MLine.h"
#include "GFace.h"
#include "DivideAndConquer.h"
#include "Bindings.h"
#include "luaFunction.h"
#include "function.h"
#include "GModel.h"
#include "Bindings.h"
#include "GmshMessage.h"
#include "Options.h"
#include "polynomialBasis.h"
#include "Gauss.h"

#if defined(HAVE_OPENGL)
#include "drawContext.h"
#endif

#if defined(HAVE_SOLVER)
#include "elasticitySolver.h"
#include "linearSystem.h"
#include "linearSystemCSR.h"
#endif

#if defined(HAVE_POST)
#include "PView.h"
#include "PViewData.h"
#include "PViewFactory.h"
#endif

extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

#if defined(HAVE_READLINE)
#include "readline.h"
#include "history.h"
#endif

//trivial class to bind options
class gmshOptions {
  public:
  gmshOptions(){}
  static void colorSet(std::string category, int index, std::string name, int value)
  {
    GmshSetOption(category, name, (unsigned int)(value), index);
  }
  static int colorGet(std::string category, int index, std::string name)
  {
    unsigned int value;
    GmshGetOption(category, name, value, index);
    return value;
  }
  static double numberGet(std::string category, int index, std::string name)
  {
    double value;
    GmshGetOption(category, name, value, index);
    return value;
  }
  static void numberSet(std::string category, int index, std::string name, double value)
  {
    GmshSetOption(category, name, value, index);
  }
  static std::string stringGet(std::string category, int index, std::string name)
  {
    std::string value;
    GmshGetOption(category, name, value, index);
    return value;
  }
  static void stringSet(std::string category, int index, std::string name, double value)
  {
    GmshSetOption(category, name, value, index);
  }
  static void initOptions(){
    ReInitOptions(0);
    InitOptionsGUI(0);
  }

  static void registerBindings(binding *b)
  {
    classBinding *cb = b->addClass<gmshOptions>("gmshOptions");
    cb->setDescription("access the gmsh option database");
    methodBinding *mb;
    mb = cb->addMethod("colorSet", &gmshOptions::colorSet);
    mb->setDescription("set the value of a color (unsigned int) option. This is "
                       "equivalent to category[index].name = value");
    mb->setArgNames("category", "index", "name", "value", NULL);
    mb = cb->addMethod("colorGet", &gmshOptions::colorGet);
    mb->setDescription("return the value of a color (unsigned int) option. This "
                       "is equivalent to category[index].name");
    mb->setArgNames("category", "index", "name", NULL);
    mb = cb->addMethod("numberSet", &gmshOptions::numberSet);
    mb->setDescription("set the value of a number option. This is equivalent "
                       "to category[index].name = value");
    mb->setArgNames("category", "index", "name", "value", NULL);
    mb = cb->addMethod("numberGet", &gmshOptions::numberGet);
    mb->setDescription("return the value of a number option. This is equivalent "
                       "to category[index].name");
    mb->setArgNames("category", "index", "name", NULL);
    mb = cb->addMethod("srtingSet", &gmshOptions::stringSet);
    mb->setDescription("set the value of a string option. This is equivalent "
                       "to category[index].name = \"value\"");
    mb->setArgNames("category", "index", "name", "value", NULL);
    mb = cb->addMethod("srtingGet", &gmshOptions::stringGet);
    mb->setDescription("return the value of a string option. This is equivalent "
                       "to category[index].name");
    mb->setArgNames("category", "index", "name", NULL);

    mb = cb->addMethod("initOptions", &gmshOptions::initOptions);
    mb->setDescription("Re-initialize option file");
    mb->setArgNames(NULL);

    mb = cb->setConstructor<gmshOptions>();
    mb->setDescription("an instance of gmshOptions is needed to access the database");
  }
};

static void reportErrors(lua_State *L, int status)
{
  if ( status!=0 ) {
    std::cerr << "-- " << lua_tostring(L, -1) << std::endl;
    lua_pop(L, 1); // remove error message
    printf("exit now\n");
    exit(1); //we need this for automatic test
  }
}

static const char *colorRed = "\033[1;31m";
static const char *colorGreen = "\033[1;32m";
static const char *colorBlue = "\033[1;34m";
static const char *colorDefault = "\033[0m";
static const char *colorBold = "\033[1m";

static void printMethod(std::string name, luaMethodBinding *mb, bool isConstructor=false)
{
  std::vector<std::string> argTypeNames;
  mb->getArgTypeNames(argTypeNames);
  std::cout << "  ";
  if(!isConstructor && name!="delete")
    std::cout << colorBold<<argTypeNames[0];
  std::cout << colorBlue << " " << name << colorDefault << colorBold << " (";
  int count = 0;
  for(unsigned int i = 1; i < argTypeNames.size(); i++){
    if(argTypeNames[i] == "-1")
      continue;
    if(count != 0)
      std::cout << ", ";
    std::cout << colorBold<<argTypeNames[i] << colorDefault;
    if((int)mb->getArgNames().size() > count)
      std::cout << " " << mb->getArgNames()[count];
    count++;
  }
  std::cout << colorBold << ")\n" << colorDefault;
  const std::string description = mb->getDescription();
  std::cout << (description.empty() ? "no help available" : description) << "\n";
}

static void listMethods(classBinding *cb)
{
  if(cb->methods.size())
    std::cout << colorGreen << "Methods from " << cb->getClassName() 
              << colorDefault<< "\n";
  for(std::map<std::string, luaMethodBinding *>::iterator it = cb->methods.begin();
      it != cb->methods.end(); it++){
    printMethod(it->first, it->second);
  }
  if(cb->getParent())
    listMethods(cb->getParent());
}

static int luaHelp (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";
    if(cb->getConstructor()){
      std::cout << colorGreen << "Constructor" << colorDefault << "\n";
      printMethod(className,cb->getConstructor(), true);
      std::cout << "\n";
    }
    listMethods(cb);
    std::cout << "\n";
    if(cb->children.size()){
      std::cout << colorGreen << "Children of " << cb->getClassName() 
                << colorDefault << "\n";
      for(std::set<classBinding *>::iterator it = cb->children.begin(); 
          it != cb->children.end(); it++){
        std::cout << "  " << colorBlue << (*it)->getClassName() << colorDefault << " : ";
        const std::string description = (*it)->getDescription();
        std::cout << (description.empty() ? "no help available" : description) << "\n";
      }
      std::cout << "\n";
    }
  }
  return 0;
}

static int luaSave (lua_State *L)
{
#if defined(HAVE_READLINE)
  const char *filename = luaL_checkstring(L, 1);
  write_history(filename);
#endif
  return 0;
}

static int luaClear(lua_State *L)
{
#if defined(HAVE_READLINE)
  clear_history();
#endif
  return 0;
}

static int luaRefresh(lua_State *L)
{
#if defined(HAVE_OPENGL)
  drawContext::global()->draw();
#endif
  return 0;
}

int binding::readFile(const char *filename)
{
  int lock = CTX::instance()->lock;
  CTX::instance()->lock = 0;
  checkDocCompleteness();
  int s = luaL_loadfile(L, filename);
  if (s == 0) {
    Msg::Info("lua executes %s", filename);
    s = lua_pcall(L, 0, LUA_MULTRET, 0);
  }
  reportErrors(L, s);
  lua_close(L);
  CTX::instance()->lock = lock;
  return (s == 0);
}

static int countInArguments(const std::vector<std::string> &types)
{
  int c = 0;
  for(unsigned int i = 1; i < types.size(); i++)
    c += (types[i] != "-1");
  return c;
}

void binding::checkDocCompleteness()
{
  int nBad = 0;
  for(std::map<std::string, classBinding *>::iterator cb = classes.begin();
      cb!=classes.end();cb++) {
    if(cb->second->getDescription().empty()){
      Msg::Error("binded class %s has no description.", cb->first.c_str());
      nBad++;
    }
    luaMethodBinding *constructor = cb->second->getConstructor();
    if(constructor){
      if(constructor->getDescription().empty()){
        Msg::Error("binded constructor of class %s has no description.", 
                   cb->first.c_str());
        nBad++;
      }
      std::vector<std::string> argTypeNames;
      constructor->getArgTypeNames(argTypeNames);
      int nTypeArg = countInArguments(argTypeNames);
      int nDocArg = constructor->getArgNames().size();
      if(nTypeArg != nDocArg){
        Msg::Error("binded constructor of class %s takes %i arguments but %i "
                   "are documented.", cb->first.c_str(), nTypeArg, nDocArg);
        nBad++;
      }
    }
    for(std::map<std::string,luaMethodBinding*>::iterator mb = cb->second->methods.begin();
        mb != cb->second->methods.end(); mb++){
      if(mb->second->getDescription().empty()){
        Msg::Error("binded method %s.%s has no description.", cb->first.c_str(),
                   mb->first.c_str());
        nBad++;
      }
      std::vector<std::string> argTypeNames;
      mb->second->getArgTypeNames(argTypeNames);
      int nTypeArg = countInArguments(argTypeNames);
      int nDocArg = mb->second->getArgNames().size();
      if(nTypeArg != nDocArg){
        Msg::Error("binded method %s.%s takes %i arguments but %i are documented.",
          cb->first.c_str(),mb->first.c_str(), nTypeArg, nDocArg);
        nBad++;
      }
    }
  }
  if(nBad != 0){
    Msg::Error("Bindings documentation is not complete (%i error(s)). To enforce "
               "documentation completeness, I will exit now. Please complete the "
               "documentation and run Gmsh again ;-)", nBad);
    Msg::Exit(1);
  }
}

void binding::interactiveSession()
{
  int lock = CTX::instance()->lock;
  CTX::instance()->lock = 0;
  checkDocCompleteness();

  Msg::Info("Starting interactive lua session, press Ctrl-D to exit"); 
#if defined(HAVE_READLINE)
  using_history();
  while (const char *line=readline("lua> ")){
    char *expansion = NULL;
    int r=history_expand((char*)line, &expansion);
    if(r)
      std::cout << expansion << "\n";
    if((r == 0 || r == 1) && expansion){
      add_history(expansion);
      reportErrors(L, luaL_dostring(L, expansion));
    }
    if(expansion)
      free(expansion);
  }
  clear_history();
#else
  std::string line;
  while(std::cout << "lua> ", getline(std::cin, line)){
    reportErrors(L, luaL_dostring(L, line.c_str()));
  }
#endif
  CTX::instance()->lock = lock;
}

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);
  luaopen_io(L);
  luaopen_string(L);
  luaopen_math(L);
  luaopen_debug(L);
  */
  luaL_openlibs(L);

  lua_register(L, "help",luaHelp);
  lua_register(L, "saveHistory", luaSave);
  lua_register(L, "clearHistory", luaClear);
  lua_register(L, "refreshGraphics", luaRefresh);

  //  lua_pushcfunction(L, luaopen_io);
  //  lua_call(L, 0, 0);

  // Register Lua bindings
  DocRecord::registerBindings(this);
  GEntity::registerBindings(this);
  GVertex::registerBindings(this);
  GEdge::registerBindings(this);
  GFace::registerBindings(this);
  GRegion::registerBindings(this);
  GModel::registerBindings(this);
  MElement::registerBindings(this);
  MVertex::registerBindings(this);
  MTriangle::registerBindings(this);
  MPrism::registerBindings(this);
  MQuadrangle::registerBindings(this);
  MLine::registerBindings(this);
  fullMatrix<double>::registerBindings(this);
  gmshOptions::registerBindings(this);
  Msg::registerBindings(this);
  polynomialBasis::registerBindings(this);
  gaussIntegration::registerBindings(this);
#if defined(HAVE_SOLVER)
  function::registerBindings(this);
  linearSystem<double>::registerBindings(this);
  linearSystemCSRGmm<double>::registerBindings(this);
  elasticitySolverRegisterBindings(this); 
#endif
#if defined(HAVE_POST)
  PView::registerBindings(this);
  PViewData::registerBindings(this);
  PViewFactory::registerBindings(this);
#endif
}

binding *binding::_instance=NULL;

binding::~binding()
{
  for (std::map<std::string,classBinding *>::iterator it = classes.begin(); 
       it != classes.end(); it++) {
    delete it->second;
  }
}

void *binding::checkudata_with_inheritance (lua_State *L, int ud, const char *tname) {
  void *p = lua_touserdata(L, ud);
  if (!p)
    return NULL;
  lua_getglobal(L, tname);
  if (ud<0) ud--;
  int depth = 1;
  while (luaL_getmetafield (L, ud, "__index")) {
    depth ++;
    if (lua_rawequal(L,-1, -depth) ) {
      lua_pop(L, depth);
      return p;
    }
    ud = -1;
  }
  lua_pop(L, depth);
  return NULL;
}

#endif