<commit_before>/*
    Copyright 2008 Brain Research Institute, Melbourne, Australia

    Written by J-Donald Tournier, 27/06/08.

    This file is part of MRtrix.

    MRtrix is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MRtrix is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with MRtrix.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "command.h"
#include "math/math.h"
#include "image.h"
#include "file/nifti1_utils.h"

using namespace MR;
using namespace App;

void usage ()
{
  DESCRIPTION
  + "edit transformations."

  + "This command's function is to either convert the transformation matrix provided "
    "by FSL's flirt command to a format usable in MRtrix or to interpolate between "
    "two transformation files";

  ARGUMENTS
  + Argument ("input", "input transformation matrix").type_file_in ()
  + Argument ("output", "the output transformation matrix.").type_file_out ();

  OPTIONS
    + Option ("flirt_import",
        "Convert a transformation matrix produced by FSL's flirt command into a format usable by MRtrix. "
        "You'll need to provide as additional arguments the save NIfTI images that were passed to flirt "
        "with the -in and -ref options.")
    + Argument ("in").type_image_in ()
    + Argument ("ref").type_image_in ()

    + Option ("interpolate",
        "Create interpolated transformation matrix between input (t=0) and input2 (t=1). "
        "Based on matrix decomposition with linear interpolation of "
        " translation, rotation and stretch described in "
        " Shoemake, K., Hill, M., & Duff, T. (1992). Matrix Animation and Polar Decomposition. "
        " Matrix, 92, 258-264. doi:10.1.1.56.1336")
    + Argument ("input2").type_file_in ()
    + Argument ("t").type_float ();
}

transform_type get_flirt_transform (const Header& header)
{
  std::vector<size_t> axes;
  transform_type nifti_transform = File::NIfTI::adjust_transform (header, axes);
  if (nifti_transform.matrix().determinant() < 0.0)
    return nifti_transform;
  transform_type coord_switch;
  coord_switch.setIdentity();
  coord_switch(0,0) = -1.0f;
  coord_switch(0,3) = (header.size(axes[0])-1) * header.spacing(axes[0]);
  return nifti_transform * coord_switch;
}

template <typename T> int sgn(T val) {
    return (T(0) < val) - (val < T(0));
}

void run ()
{
  auto flirt_opt = get_options ("flirt_import");
  auto interp_opt = get_options ("interpolate");

  if (flirt_opt.size() && !interp_opt.size()) {
    transform_type transform = load_transform<float> (argument[0]);
    if(transform.matrix().determinant() == float(0.0))
        WARN ("Transformation matrix determinant is zero. Replace hex with plain text numbers.");

    auto src_header = Header::open (flirt_opt[0][0]);
    transform_type src_flirt_to_scanner = get_flirt_transform (src_header);

    auto dest_header = Header::open (flirt_opt[0][1]);
    transform_type dest_flirt_to_scanner = get_flirt_transform (dest_header);

    transform_type forward_transform = dest_flirt_to_scanner * transform * src_flirt_to_scanner.inverse();
    if (((forward_transform.matrix().array() != forward_transform.matrix().array())).any())
      WARN ("NAN in transformation.");
    save_transform (forward_transform.inverse(), argument[1]);
  } else if(interp_opt.size() && !flirt_opt.size()) {
    transform_type transform1 = load_transform<double> (argument[0]);
    transform_type transform2 = load_transform<double> (interp_opt[0][0]);
    transform_type transform_out;
    double t = interp_opt[0][1];
    if (t < 0.0 || t > 1.0)
      throw Exception ("t has to be in the interval [0,1]");

    Eigen::MatrixXd M1 = transform1.linear();
    Eigen::MatrixXd M2 = transform2.linear();
    if (sgn(M1.determinant()) != sgn(M2.determinant()))
      WARN("transformation determinants have different signs");

    // transform1.computeRotationScaling (R1, S1);
    Eigen::Matrix3d R1 = transform1.rotation(); // SVD based, polar decomposition should be faster [Higham86]
    Eigen::Matrix3d R2 = transform2.rotation();
    Eigen::Quaterniond Q1(R1);
    Eigen::Quaterniond Q2(R2);
    Eigen::Quaterniond Qout;

    // get stretch (shear becomes roation and stretch)
    Eigen::MatrixXd S1 = R1.transpose() * M1;
    Eigen::MatrixXd S2 = R2.transpose() * M2;
    if (!M1.isApprox(R1*S1))
      WARN ("M1 matrix decomposition might have failed");
    if (!M2.isApprox(R2*S2))
      WARN ("M2 matrix decomposition might have failed");

    transform_out.translation() = ((1.0 - t) * transform1.translation() + t * transform2.translation());
    Qout = Q1.slerp(t, Q2);
    transform_out.linear() = Qout * ((1 - t) * S1 + t * S2);
    INFO("\n"+str(transform_out.matrix().format(
      Eigen::IOFormat(Eigen::FullPrecision, 0, ", ", ",\n", "[", "]", "[", "]"))));
    save_transform (transform_out, argument[1]);
  } else {
    throw Exception ("you must specify either -flirt_import or -interpolate option");
  }
}<commit_msg>transformcalc: added options for freesurfer. moved firt argument into options<commit_after>/*
    Copyright 2008 Brain Research Institute, Melbourne, Australia

    Written by J-Donald Tournier, 27/06/08.

    This file is part of MRtrix.

    MRtrix is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    MRtrix is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with MRtrix.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "command.h"
#include "math/math.h"
#include "image.h"
#include "file/nifti1_utils.h"
#include "transform.h"

using namespace MR;
using namespace App;

void usage ()
{
  DESCRIPTION
  + "edit transformations."

  + "This command's function is to either convert the transformation matrix provided "
    "by FSL's flirt command to a format usable in MRtrix or to interpolate between "
    "two transformation files";

  ARGUMENTS
  + Argument ("output", "the output transformation matrix.").type_file_out ();

  OPTIONS
    + Option ("flirt_import",
        "Convert a transformation matrix produced by FSL's flirt command into a format usable by MRtrix. "
        "You'll need to provide as additional arguments the save NIfTI images that were passed to flirt "
        "with the -in and -ref options.")
    + Argument ("input", "input transformation matrix").type_file_in ()
    + Argument ("in").type_image_in ()
    + Argument ("ref").type_image_in ()

    + Option ("surfer_vox2vox",
        "Convert a transformation matrix produced by FSL's flirt command into a format usable by MRtrix. "
        "You'll need to provide as additional arguments the save NIfTI images that were passed to flirt "
        "with the -in and -ref options.")
    + Argument ("vox2vox", "input transformation matrix").type_file_in ()
    + Argument ("mov").type_image_in ()
    + Argument ("dst").type_image_in ()

    + Option ("surfer_fromheader",
        "Convert a transformation matrix produced by FSL's flirt command into a format usable by MRtrix. "
        "You'll need to provide as additional arguments the save NIfTI images that were passed to flirt "
        "with the -in and -ref options.")
    + Argument ("mov").type_image_in ()
    + Argument ("mapmovhdr").type_image_in ()

    + Option ("interpolate",
        "Create interpolated transformation matrix between input (t=0) and input2 (t=1). "
        "Based on matrix decomposition with linear interpolation of "
        " translation, rotation and stretch described in "
        " Shoemake, K., Hill, M., & Duff, T. (1992). Matrix Animation and Polar Decomposition. "
        " Matrix, 92, 258-264. doi:10.1.1.56.1336")
    + Argument ("input", "input transformation matrix").type_file_in ()
    + Argument ("input2").type_file_in ()
    + Argument ("t").type_float ();
}

transform_type get_flirt_transform (const Header& header)
{
  std::vector<size_t> axes;
  transform_type nifti_transform = File::NIfTI::adjust_transform (header, axes);
  if (nifti_transform.matrix().determinant() < 0.0)
    return nifti_transform;
  transform_type coord_switch;
  coord_switch.setIdentity();
  coord_switch(0,0) = -1.0f;
  coord_switch(0,3) = (header.size(axes[0])-1) * header.spacing(axes[0]);
  return nifti_transform * coord_switch;
}

transform_type get_surfer_transform (const Header& header) {
  // TODO
  return transform_type();
}

//! read matrix data into a 2D vector \a filename
template <class ValueType = default_type>
  transform_type parse_surfer_transform (const std::string& filename) {
    std::ifstream stream (filename, std::ios_base::in | std::ios_base::binary);
    std::vector<std::vector<ValueType>> V;
    std::string sbuf;
    std::string file_version;
    while (getline (stream, sbuf)) {
      if (sbuf.substr (0,1) == "#")
        continue;
      if (sbuf.find("1 4 4") != std::string::npos)
        break;
      if (sbuf.find("type") == std::string::npos)
        continue;
      file_version = sbuf.substr (sbuf.find_first_of ('=')+1);
    }
    if (file_version.empty())
      throw Exception ("not a surfer transformation");
    // so far we only understand vox2vox transformations
    // vox2vox = inverse(colrowslice_to_xyz(moving))*M*colrowslice_to_xyz(target)
    if (stoi(file_version)!=0)
      throw Exception ("not a vox2vox transformation");

    for (auto i = 0; i<4 && getline (stream, sbuf); ++i){
      // sbuf = strip (sbuf.substr (0, sbuf.find_first_of ('type')));
      V.push_back (std::vector<ValueType>());
      const auto elements = MR::split (sbuf, " ,;\t", true);
      for (const auto& entry : elements)
        V.back().push_back (to<ValueType> (entry));
      if (V.size() > 1)
        if (V.back().size() != V[0].size())
          throw Exception ("uneven rows in matrix");
    }
    if (stream.bad())
      throw Exception (strerror (errno));
    if (!V.size())
      throw Exception ("no data in file");

    transform_type M;
    for (ssize_t i = 0; i < 3; i++)
      for (ssize_t j = 0; j < 4; j++)
        M(i,j) = V[i][j];
    return M;
  }

template <typename T> int sgn(T val) {
    return (T(0) < val) - (val < T(0));
}

void run ()
{
  auto flirt_opt = get_options ("flirt_import");
  auto interp_opt = get_options ("interpolate");
  auto surfer_vox2vox_opt = get_options ("surfer_vox2vox");
  auto surfer_fromheader_opt = get_options ("surfer_fromheader");

  size_t options = 0;
  if (flirt_opt.size())
    options++;
  if (interp_opt.size())
    options++;
  if (surfer_fromheader_opt.size())
    options++;
  if (surfer_vox2vox_opt.size())
    options++;
  if (options != 1)
    throw Exception ("You must specify one option");

  if(surfer_fromheader_opt.size()){
    auto orig_header = Header::open (surfer_fromheader_opt[0][0]);
    auto modified_header = Header::open (surfer_fromheader_opt[0][1]);

    transform_type forward_transform = Transform(orig_header).scanner2voxel * Transform(modified_header).voxel2scanner;
    save_transform (forward_transform.inverse(), argument[0]);
  }

  if(surfer_vox2vox_opt.size()){
    auto vox2vox = parse_surfer_transform (surfer_vox2vox_opt[0][0]);

    auto src_header = Header::open (surfer_vox2vox_opt[0][0]);
    auto dest_header = Header::open (surfer_vox2vox_opt[0][1]);

    auto transform_source = Transform(src_header); // .transform();
    auto transform_dest = Transform(dest_header); //dest_header.transform();

    VAR(transform_source.voxel2scanner.matrix());
    VAR(transform_dest.voxel2scanner.matrix());
    VAR(vox2vox.matrix());

    auto forward_transform = transform_dest.voxel2scanner *
        /*swap axis,negation?*/    vox2vox *
                              transform_source.scanner2voxel.inverse();
    throw Exception ("FIXME: not implemented");
    save_transform (forward_transform.inverse(), argument[0]);
  }

  if (flirt_opt.size()) {
    transform_type transform = load_transform<float> (flirt_opt[0][0]);
    if(transform.matrix().determinant() == float(0.0))
        WARN ("Transformation matrix determinant is zero. Replace hex with plain text numbers.");

    auto src_header = Header::open (flirt_opt[0][1]);
    transform_type src_flirt_to_scanner = get_flirt_transform (src_header);

    auto dest_header = Header::open (flirt_opt[0][2]);
    transform_type dest_flirt_to_scanner = get_flirt_transform (dest_header);

    transform_type forward_transform = dest_flirt_to_scanner * transform * src_flirt_to_scanner.inverse();
    if (((forward_transform.matrix().array() != forward_transform.matrix().array())).any())
      WARN ("NAN in transformation.");
    save_transform (forward_transform.inverse(), argument[0]);
  }
  if(interp_opt.size()) {
    transform_type transform1 = load_transform<double> (interp_opt[0][0]);
    transform_type transform2 = load_transform<double> (interp_opt[0][1]);
    transform_type transform_out;
    double t = interp_opt[0][2];
    if (t < 0.0 || t > 1.0)
      throw Exception ("t has to be in the interval [0,1]");

    Eigen::MatrixXd M1 = transform1.linear();
    Eigen::MatrixXd M2 = transform2.linear();
    if (sgn(M1.determinant()) != sgn(M2.determinant()))
      WARN("transformation determinants have different signs");

    // transform1.computeRotationScaling (R1, S1);
    Eigen::Matrix3d R1 = transform1.rotation(); // SVD based, polar decomposition should be faster [Higham86]
    Eigen::Matrix3d R2 = transform2.rotation();
    Eigen::Quaterniond Q1(R1);
    Eigen::Quaterniond Q2(R2);
    Eigen::Quaterniond Qout;

    // get stretch (shear becomes roation and stretch)
    Eigen::MatrixXd S1 = R1.transpose() * M1;
    Eigen::MatrixXd S2 = R2.transpose() * M2;
    if (!M1.isApprox(R1*S1))
      WARN ("M1 matrix decomposition might have failed");
    if (!M2.isApprox(R2*S2))
      WARN ("M2 matrix decomposition might have failed");

    transform_out.translation() = ((1.0 - t) * transform1.translation() + t * transform2.translation());
    Qout = Q1.slerp(t, Q2);
    transform_out.linear() = Qout * ((1 - t) * S1 + t * S2);
    INFO("\n"+str(transform_out.matrix().format(
      Eigen::IOFormat(Eigen::FullPrecision, 0, ", ", ",\n", "[", "]", "[", "]"))));
    save_transform (transform_out, argument[0]);
  }
}<|endoftext|>


<commit_before>#include "angort.h"
#include "cycle.h"

namespace angort {

Type *Type::head=NULL;
static bool createTypesDone=false;

int  GarbageCollected::globalCount=0;

//#define tdprintf printf
#define tdprintf if(0)printf

const char *Type::toString(bool *allocated,const Value *v) const{
    char buf[128];
    snprintf(buf,128,"<TYPE %s:%p>",name,v->v.s);
    *allocated=true;
    return strdup(buf);
}

float Type::toFloat(const Value *v) const{
    throw BadConversionException(v->t->name,Types::tFloat->name);
}

int Type::toInt(const Value *v) const {
    throw BadConversionException(v->t->name,Types::tInteger->name);
}
long Type::toLong(const Value *v) const {
    throw BadConversionException(v->t->name,Types::tLong->name);
}

void Type::createIterator(Value *dest,Value *src){
    Iterator<Value *> *i = makeIterator(src);
    i->first();
    Types::tIter->set(dest,src,i);
}

void Type::clone(Value *out,const Value *in,bool deep){
    // default action is to just copy the value; collections
    // need to do more.
    out->copy(in);
}

void Type::add(const char *_name,const char *_id){
    if(getByName(_name))
        throw Exception().set("type already exists: %s",name);

    const unsigned char *n = (const unsigned char *)_id;
    id = n[0]+(n[1]<<8)+(n[2]<<16)+(n[3]<<24);
    name = _name;
    next = head;
    head = this;

    // create the binop ID
    static int binopIDCount=0;
    binopID = binopIDCount++;

    // normally symbols are generated after the static initialisation
    // but before anything else, in createTypes(). This is to make sure
    // the symbol system has been initialised.
    // If this type is being added later, say by a plugin, we need to
    // do it here.

    if(createTypesDone){
        nameSymb = SymbolType::getSymbol(name);
    }
}

Type *Type::getByID(const char *_id){
    const unsigned char *n = (const unsigned char *)_id;
    uint32_t i = n[0]+(n[1]<<8)+(n[2]<<16)+(n[3]<<24);

    for(Type *t = head;t;t=t->next)
        if(t->id == i)return t;
    return NULL;
}

void Type::registerBinop(Type *rhs, int opcode, BinopFunction f){
    uint32_t key = (rhs->binopID << 16) + opcode;
    BinopFunction *ptr = binops.set(key);
    *ptr = f;
}

bool Type::binop(Angort *a,int opcode,Value *lhs,Value *rhs){
    BinopFunction *f = lhs->t->getBinop(rhs->t,opcode);
    if(!f)
        return false;
    (**f)(a,lhs,rhs);
    return true;
}

char *BlockAllocType::allocate(Value *v,int len,Type *type){
    v->clr();
    BlockAllocHeader *h = (BlockAllocHeader *)malloc(len+sizeof(BlockAllocHeader));
    h->refct=1;
    v->v.block = h;
    v->t = type;
    return (char *)(h+1);
}

int Type::getIndexOfContainedItem(Value *v,Value *item){
    Iterator<Value *> *iter = makeIterator(v); // will throw for non-iterables
    int i=0;
    for(iter->first();!iter->isDone();iter->next()){
        if(iter->current()->equalForHashTable(item)){
            delete iter;
            return i;
        }
        i++;
    }
    delete iter;
    return -1;
}

const char *BlockAllocType::getData(const Value *v) const{
    return (const char *)(v->v.block+1);
}

void BlockAllocType::incRef(Value *v){
    BlockAllocHeader *h = v->v.block;
    h->refct++;
    if(!h->refct)
        throw RUNT("reference count too large");
}

void BlockAllocType::decRef(Value *v){
    BlockAllocHeader *h = v->v.block;
    h->refct--;
    if(h->refct==0){
        free(h);
    }
}

void GCType::incRef(Value *v){
    v->v.gc->incRefCt();
    tdprintf("incrementing ref count of %s:%p, now %d\n",name,v->v.gc,v->v.gc->refct);
}

void GCType::decRef(Value *v){
    bool b = v->v.gc->decRefCt();
    tdprintf("decrementing ref count of %s:%p, now %d\n",name,v->v.gc,v->v.gc->refct);
    if(b){
        tdprintf("  AND DELETING %s:%p\n",name,v->v.gc);
        delete v->v.gc;
    }
}

Iterator<class Value *> *GCType::makeKeyIterator(Value *v){
    return v->v.gc->makeKeyIterator();
}
Iterator<class Value *> *GCType::makeValueIterator(Value *v){
    return v->v.gc->makeValueIterator();
}

GarbageCollected *GCType::getGC(Value *v){
    return v->v.gc;
}

GarbageCollected::GarbageCollected(){
    refct=0;
    globalCount++;
    CycleDetector::getInstance()->add(this);
}

GarbageCollected::~GarbageCollected(){
    globalCount--;
    CycleDetector::getInstance()->remove(this);
}

/*
 *
 * New types go down here
 *
 */

static NoneType _tNone;
NoneType *Types::tNone= &_tNone;

static Type _tDeleted; // has to be added by hand in createTypes
Type *Types::tDeleted= &_tDeleted;

static IntegerType _Int;
IntegerType *Types::tInteger = &_Int;

static FloatType _Float;
FloatType *Types::tFloat = &_Float;

static StringType _String;
StringType *Types::tString = &_String;

static CodeType _Code;
CodeType *Types::tCode = &_Code;

static ClosureType _Closure;
ClosureType *Types::tClosure = &_Closure;

static RangeType<int> _IRange;
RangeType<int> *Types::tIRange = &_IRange;
static RangeType<float> _FRange;
RangeType<float> *Types::tFRange = &_FRange;

static ListType _List;
ListType *Types::tList = &_List;

static HashType _Hash;
HashType *Types::tHash = &_Hash;

static SymbolType _Symbol;
SymbolType *Types::tSymbol = &_Symbol;

static NativeType _Native;
NativeType *Types::tNative = &_Native;

static PropType _Prop;
PropType *Types::tProp = &_Prop;

static LongType _Long;
LongType *Types::tLong = &_Long;

static IteratorType _Iterator;
IteratorType *Types::tIter = &_Iterator;

void Types::createTypes(){
    tDeleted->add("DELETED","DELE");

    // because of the undefined execution order of static heap
    // objects, we set up all the type names here rather than in
    // "add" to make sure the symbol system is already up.

    // IF you add types later in plugins, the system should detect
    // this.

    for(Type *p = Type::head;p;p=p->next){
        p->nameSymb = SymbolType::getSymbol(p->name);
    }
    createTypesDone = true;

}
}
<commit_msg>workaround for running out of string refcount space - strings which get a refct of 0xffff will "stick" and never be deleted. Ugh.<commit_after>#include "angort.h"
#include "cycle.h"

namespace angort {

Type *Type::head=NULL;
static bool createTypesDone=false;

int  GarbageCollected::globalCount=0;

//#define tdprintf printf
#define tdprintf if(0)printf

const char *Type::toString(bool *allocated,const Value *v) const{
    char buf[128];
    snprintf(buf,128,"<TYPE %s:%p>",name,v->v.s);
    *allocated=true;
    return strdup(buf);
}

float Type::toFloat(const Value *v) const{
    throw BadConversionException(v->t->name,Types::tFloat->name);
}

int Type::toInt(const Value *v) const {
    throw BadConversionException(v->t->name,Types::tInteger->name);
}
long Type::toLong(const Value *v) const {
    throw BadConversionException(v->t->name,Types::tLong->name);
}

void Type::createIterator(Value *dest,Value *src){
    Iterator<Value *> *i = makeIterator(src);
    i->first();
    Types::tIter->set(dest,src,i);
}

void Type::clone(Value *out,const Value *in,bool deep){
    // default action is to just copy the value; collections
    // need to do more.
    out->copy(in);
}

void Type::add(const char *_name,const char *_id){
    if(getByName(_name))
        throw Exception().set("type already exists: %s",name);

    const unsigned char *n = (const unsigned char *)_id;
    id = n[0]+(n[1]<<8)+(n[2]<<16)+(n[3]<<24);
    name = _name;
    next = head;
    head = this;

    // create the binop ID
    static int binopIDCount=0;
    binopID = binopIDCount++;

    // normally symbols are generated after the static initialisation
    // but before anything else, in createTypes(). This is to make sure
    // the symbol system has been initialised.
    // If this type is being added later, say by a plugin, we need to
    // do it here.

    if(createTypesDone){
        nameSymb = SymbolType::getSymbol(name);
    }
}

Type *Type::getByID(const char *_id){
    const unsigned char *n = (const unsigned char *)_id;
    uint32_t i = n[0]+(n[1]<<8)+(n[2]<<16)+(n[3]<<24);

    for(Type *t = head;t;t=t->next)
        if(t->id == i)return t;
    return NULL;
}

void Type::registerBinop(Type *rhs, int opcode, BinopFunction f){
    uint32_t key = (rhs->binopID << 16) + opcode;
    BinopFunction *ptr = binops.set(key);
    *ptr = f;
}

bool Type::binop(Angort *a,int opcode,Value *lhs,Value *rhs){
    BinopFunction *f = lhs->t->getBinop(rhs->t,opcode);
    if(!f)
        return false;
    (**f)(a,lhs,rhs);
    return true;
}

char *BlockAllocType::allocate(Value *v,int len,Type *type){
    v->clr();
    BlockAllocHeader *h = (BlockAllocHeader *)malloc(len+sizeof(BlockAllocHeader));
    h->refct=1;
    v->v.block = h;
    v->t = type;
    return (char *)(h+1);
}

int Type::getIndexOfContainedItem(Value *v,Value *item){
    Iterator<Value *> *iter = makeIterator(v); // will throw for non-iterables
    int i=0;
    for(iter->first();!iter->isDone();iter->next()){
        if(iter->current()->equalForHashTable(item)){
            delete iter;
            return i;
        }
        i++;
    }
    delete iter;
    return -1;
}

const char *BlockAllocType::getData(const Value *v) const{
    return (const char *)(v->v.block+1);
}

void BlockAllocType::incRef(Value *v){
    BlockAllocHeader *h = v->v.block;
    h->refct++;
    tdprintf("INCREF STR to %d: %p%s\n",h->refct,getData(v),getData(v));
    if(!h->refct)
        h->refct=0xffff; // MAX REFCOUNT is never freed!
//        throw RUNT("reference count too large");
}

void BlockAllocType::decRef(Value *v){
    BlockAllocHeader *h = v->v.block;
    if(h->refct!=0xffff)h->refct--; // MAX REFCOUNT is never freed!
    tdprintf("DECREF STR to %d: %p%s\n",h->refct,getData(v),getData(v));
    if(h->refct==0){
        free(h);
    }
}

void GCType::incRef(Value *v){
    v->v.gc->incRefCt();
    tdprintf("incrementing ref count of %s:%p, now %d\n",name,v->v.gc,v->v.gc->refct);
}

void GCType::decRef(Value *v){
    bool b = v->v.gc->decRefCt();
    tdprintf("decrementing ref count of %s:%p, now %d\n",name,v->v.gc,v->v.gc->refct);
    if(b){
        tdprintf("  AND DELETING %s:%p\n",name,v->v.gc);
        delete v->v.gc;
    }
}

Iterator<class Value *> *GCType::makeKeyIterator(Value *v){
    return v->v.gc->makeKeyIterator();
}
Iterator<class Value *> *GCType::makeValueIterator(Value *v){
    return v->v.gc->makeValueIterator();
}

GarbageCollected *GCType::getGC(Value *v){
    return v->v.gc;
}

GarbageCollected::GarbageCollected(){
    refct=0;
    globalCount++;
    CycleDetector::getInstance()->add(this);
}

GarbageCollected::~GarbageCollected(){
    globalCount--;
    CycleDetector::getInstance()->remove(this);
}

/*
 *
 * New types go down here
 *
 */

static NoneType _tNone;
NoneType *Types::tNone= &_tNone;

static Type _tDeleted; // has to be added by hand in createTypes
Type *Types::tDeleted= &_tDeleted;

static IntegerType _Int;
IntegerType *Types::tInteger = &_Int;

static FloatType _Float;
FloatType *Types::tFloat = &_Float;

static StringType _String;
StringType *Types::tString = &_String;

static CodeType _Code;
CodeType *Types::tCode = &_Code;

static ClosureType _Closure;
ClosureType *Types::tClosure = &_Closure;

static RangeType<int> _IRange;
RangeType<int> *Types::tIRange = &_IRange;
static RangeType<float> _FRange;
RangeType<float> *Types::tFRange = &_FRange;

static ListType _List;
ListType *Types::tList = &_List;

static HashType _Hash;
HashType *Types::tHash = &_Hash;

static SymbolType _Symbol;
SymbolType *Types::tSymbol = &_Symbol;

static NativeType _Native;
NativeType *Types::tNative = &_Native;

static PropType _Prop;
PropType *Types::tProp = &_Prop;

static LongType _Long;
LongType *Types::tLong = &_Long;

static IteratorType _Iterator;
IteratorType *Types::tIter = &_Iterator;

void Types::createTypes(){
    tDeleted->add("DELETED","DELE");

    // because of the undefined execution order of static heap
    // objects, we set up all the type names here rather than in
    // "add" to make sure the symbol system is already up.

    // IF you add types later in plugins, the system should detect
    // this.

    for(Type *p = Type::head;p;p=p->next){
        p->nameSymb = SymbolType::getSymbol(p->name);
    }
    createTypesDone = true;

}
}
<|endoftext|>


<commit_before>// Ouzel by Elviss Strazdins

#ifndef OUZEL_MATH_MATHUTILS_HPP
#define OUZEL_MATH_MATHUTILS_HPP

#include <cstdint>
#include <cmath>
#include <limits>
#include <type_traits>

namespace ouzel
{
    template <typename T>
    constexpr auto lerp(const T v0, const T v1, const T t) noexcept
    {
        return (T(1) - t) * v0 + t * v1;
    }

    template <typename T>
    constexpr auto smoothStep(const T a, const T b, const T t) noexcept
    {
        return lerp(a, b, t * t * (T(3) - T(2) * t));
    }

    template <typename T> constexpr auto degToRad(const T x) noexcept
    {
        return x * T(0.01745329251994329576);
    }

    template <typename T> constexpr auto radToDeg(const T x) noexcept
    {
        return x * T(57.2957795130823208767);
    }

    template <typename T>
    constexpr auto isNearlyEqual(const T a, const T b,
                                 const T tolerance = std::numeric_limits<T>::min()) noexcept
    {
        return (a - b) <= tolerance && (a - b) >= -tolerance;
    }
}

#endif // OUZEL_MATH_MATHUTILS_HPP
<commit_msg>Remove isNearlyEqual<commit_after>// Ouzel by Elviss Strazdins

#ifndef OUZEL_MATH_MATHUTILS_HPP
#define OUZEL_MATH_MATHUTILS_HPP

#include <cstdint>
#include <cmath>
#include <limits>
#include <type_traits>

namespace ouzel
{
    template <typename T>
    constexpr auto lerp(const T v0, const T v1, const T t) noexcept
    {
        return (T(1) - t) * v0 + t * v1;
    }

    template <typename T>
    constexpr auto smoothStep(const T a, const T b, const T t) noexcept
    {
        return lerp(a, b, t * t * (T(3) - T(2) * t));
    }

    template <typename T> constexpr auto degToRad(const T x) noexcept
    {
        return x * T(0.01745329251994329576);
    }

    template <typename T> constexpr auto radToDeg(const T x) noexcept
    {
        return x * T(57.2957795130823208767);
    }
}

#endif // OUZEL_MATH_MATHUTILS_HPP
<|endoftext|>


<commit_before>/******************************************************************************
** Copyright (c) 2014-2015, Intel Corporation                                **
** All rights reserved.                                                      **
**                                                                           **
** Redistribution and use in source and binary forms, with or without        **
** modification, are permitted provided that the following conditions        **
** are met:                                                                  **
** 1. Redistributions of source code must retain the above copyright         **
**    notice, this list of conditions and the following disclaimer.          **
** 2. Redistributions in binary form must reproduce the above copyright      **
**    notice, this list of conditions and the following disclaimer in the    **
**    documentation and/or other materials provided with the distribution.   **
** 3. Neither the name of the copyright holder nor the names of its          **
**    contributors may be used to endorse or promote products derived        **
**    from this software without specific prior written permission.          **
**                                                                           **
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       **
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         **
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR     **
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT      **
** HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,    **
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  **
** TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR    **
** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    **
** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      **
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        **
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              **
******************************************************************************/
/* Hans Pabst (Intel Corp.)
******************************************************************************/
#include <libxstream_begin.h>
#include <stdexcept>
#include <algorithm>
#include <cstdio>
#if defined(_OPENMP)
# include <omp.h>
#endif
#include <libxstream_end.h>

//#define COPY_NO_SYNC

int main(int argc, char* argv[])
{
  try {
    const bool copyin = 1 < argc ? ('o' != *argv[1]) : true;
#if defined(_OPENMP)
    const int nthreads = std::min(std::max(2 < argc ? std::atoi(argv[2]) : 1, 1), omp_get_max_threads());
#else
    const int nthreads = std::min(std::max(2 < argc ? std::atoi(argv[2]) : 1, 1), 1);
    LIBXSTREAM_PRINT0(1, "OpenMP support needed for performance results!");
    libxstream_use_sink(&nthreads);
#endif
    const int nstreams = std::min(std::max(3 < argc ? std::atoi(argv[3]) : 1, 1), LIBXSTREAM_MAX_NSTREAMS);
    const size_t maxsize = static_cast<size_t>(std::min(std::max(4 < argc ? std::atoi(argv[4]) : 2048, 1), 8192)) * (1 << 20), minsize = 8;
    int minrepeat = std::min(std::max(5 < argc ? std::atoi(argv[5]) :    8, 2), 128);
    int maxrepeat = std::min(std::max(6 < argc ? std::atoi(argv[6]) : 8192, minrepeat), 32768);

    size_t ndevices = 0;
    if (LIBXSTREAM_ERROR_NONE != libxstream_get_ndevices(&ndevices) || 0 == ndevices) {
      throw std::runtime_error("no device found!");
    }

    int steps_repeat = 0, steps_size = 0;
    for (int nrepeat = minrepeat; nrepeat <= maxrepeat; nrepeat <<= 1) ++steps_repeat;
    for (size_t size = minsize; size <= maxsize; size <<= 1) ++steps_size;
    const int stride = 0 < steps_repeat ? (steps_size / steps_repeat + 1) : 1;

    struct {
      libxstream_stream* stream;
      void *mem_hst, *mem_dev;
    } copy[LIBXSTREAM_MAX_NSTREAMS];
    for (int i = 0; i < nstreams; ++i) {
      char name[128];
      LIBXSTREAM_SNPRINTF(name, sizeof(name), "Stream %i", i + 1);
      LIBXSTREAM_CHECK_CALL_THROW(libxstream_stream_create(&copy[i].stream, 0, 0, name));
      if (copyin) {
        if (0 == i) {
          LIBXSTREAM_CHECK_CALL_THROW(libxstream_mem_allocate(-1/*host*/, &copy[i].mem_hst, maxsize, 0));
        }
        else {
          copy[i].mem_hst = copy[0].mem_hst;
        }
        LIBXSTREAM_CHECK_CALL_THROW(libxstream_mem_allocate(0/*device*/, &copy[i].mem_dev, maxsize, 0));
      }
      else { // copy-out
        LIBXSTREAM_CHECK_CALL_THROW(libxstream_mem_allocate(-1/*host*/, &copy[i].mem_hst, maxsize, 0));
        if (0 == i) {
          LIBXSTREAM_CHECK_CALL_THROW(libxstream_mem_allocate(0/*device*/, &copy[i].mem_dev, maxsize, 0));
        }
        else {
          copy[i].mem_dev = copy[0].mem_dev;
        }
      }
    }

    int n = 1;
    double maxval = 0, sumval = 0, lnsval = 0, duration = 0;
    for (size_t size = minsize; size <= maxsize; size <<= 1, ++n) {
      if (0 == (n % stride)) {
        maxrepeat >>= 1;
      }

#if defined(_OPENMP)
      const double start = omp_get_wtime();
#     pragma omp parallel for num_threads(nthreads) schedule(dynamic)
#endif
      for (int i = 0; i < maxrepeat; ++i) {
        const int j = i % nstreams;
        if (copyin) {
          LIBXSTREAM_CHECK_CALL_ASSERT(libxstream_memcpy_h2d(copy[j].mem_hst, copy[j].mem_dev, size, copy[j].stream));
        }
        else {
          LIBXSTREAM_CHECK_CALL_ASSERT(libxstream_memcpy_d2h(copy[j].mem_dev, copy[j].mem_hst, size, copy[j].stream));
        }

#if !defined(COPY_NO_SYNC)
        const int k = (j + 1) % nstreams;
        LIBXSTREAM_CHECK_CALL_ASSERT(libxstream_stream_sync(copy[k].stream));
#endif
      }

      // sync all streams to complete any pending work
      LIBXSTREAM_CHECK_CALL_THROW(libxstream_stream_sync(0));

#if defined(_OPENMP)
      const double iduration = omp_get_wtime() - start;
      fprintf(stdout, "%lu Byte x %i: ", static_cast<unsigned long>(size), maxrepeat);
      if (0 < iduration) {
        const double bandwidth = (1.0 * size * maxrepeat) / ((1ul << 20) * iduration);
        fprintf(stdout, "%.1f MB/s\n", bandwidth);
        maxval = std::max(maxval, bandwidth);
        sumval += bandwidth;
        lnsval += std::log(bandwidth);
        duration += iduration;
      }
      else {
        fprintf(stdout, "-\n");
      }
      fflush(stdout);
#endif
    }

    fprintf(stdout, "\n");
    if (1 < n) {
      fprintf(stdout, "Finished after %.0f s\n", duration);
      fprintf(stdout, "max: %.0f MB/s\n", maxval);
      fprintf(stdout, "avg: %.0f MB/s\n", sumval / (n - 1));
      fprintf(stdout, "geo: %.0f MB/s\n", std::exp(lnsval / (n - 1)));
    }
    else {
      fprintf(stdout, "Finished\n");
    }
  }
  catch(const std::exception& e) {
    fprintf(stderr, "Error: %s\n", e.what());
    return EXIT_FAILURE;
  }
  catch(...) {
    fprintf(stderr, "Error: unknown exception caught!\n");
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}
<commit_msg>Code cleanup.<commit_after>/******************************************************************************
** Copyright (c) 2014-2015, Intel Corporation                                **
** All rights reserved.                                                      **
**                                                                           **
** Redistribution and use in source and binary forms, with or without        **
** modification, are permitted provided that the following conditions        **
** are met:                                                                  **
** 1. Redistributions of source code must retain the above copyright         **
**    notice, this list of conditions and the following disclaimer.          **
** 2. Redistributions in binary form must reproduce the above copyright      **
**    notice, this list of conditions and the following disclaimer in the    **
**    documentation and/or other materials provided with the distribution.   **
** 3. Neither the name of the copyright holder nor the names of its          **
**    contributors may be used to endorse or promote products derived        **
**    from this software without specific prior written permission.          **
**                                                                           **
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       **
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         **
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR     **
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT      **
** HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,    **
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED  **
** TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR    **
** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    **
** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING      **
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS        **
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.              **
******************************************************************************/
/* Hans Pabst (Intel Corp.)
******************************************************************************/
#include <libxstream_begin.h>
#include <stdexcept>
#include <algorithm>
#include <cstdio>
#if defined(_OPENMP)
# include <omp.h>
#endif
#include <libxstream_end.h>

//#define COPY_NO_SYNC

int main(int argc, char* argv[])
{
  try {
    const bool copyin = 1 < argc ? ('o' != *argv[1]) : true;
#if defined(_OPENMP)
    const int nthreads = std::min(std::max(2 < argc ? std::atoi(argv[2]) : 1, 1), omp_get_max_threads());
#else
    const int nthreads = std::min(std::max(2 < argc ? std::atoi(argv[2]) : 1, 1), 1);
    LIBXSTREAM_PRINT0(1, "OpenMP support needed for performance results!");
    libxstream_use_sink(&nthreads);
#endif
    const int nstreams = std::min(std::max(3 < argc ? std::atoi(argv[3]) : 1, 1), LIBXSTREAM_MAX_NSTREAMS);
    const size_t maxsize = static_cast<size_t>(std::min(std::max(4 < argc ? std::atoi(argv[4]) : 2048, 1), 8192)) * (1 << 20), minsize = 8;
    const int minrepeat = std::min(std::max(5 < argc ? std::atoi(argv[5]) :    8, 2), 128);
    const int maxrepeat = std::min(std::max(6 < argc ? std::atoi(argv[6]) : 8192, minrepeat), 32768);

    size_t ndevices = 0;
    if (LIBXSTREAM_ERROR_NONE != libxstream_get_ndevices(&ndevices) || 0 == ndevices) {
      throw std::runtime_error("no device found!");
    }

    int steps_repeat = 0, steps_size = 0;
    for (int nrepeat = minrepeat; nrepeat <= maxrepeat; nrepeat <<= 1) ++steps_repeat;
    for (size_t size = minsize; size <= maxsize; size <<= 1) ++steps_size;
    const int stride = 0 < steps_repeat ? (steps_size / steps_repeat + 1) : 1;

    struct {
      libxstream_stream* stream;
      void *mem_hst, *mem_dev;
    } copy[LIBXSTREAM_MAX_NSTREAMS];
    for (int i = 0; i < nstreams; ++i) {
      char name[128];
      LIBXSTREAM_SNPRINTF(name, sizeof(name), "Stream %i", i + 1);
      LIBXSTREAM_CHECK_CALL_THROW(libxstream_stream_create(&copy[i].stream, 0, 0, name));
      if (copyin) {
        if (0 == i) {
          LIBXSTREAM_CHECK_CALL_THROW(libxstream_mem_allocate(-1/*host*/, &copy[i].mem_hst, maxsize, 0));
        }
        else {
          copy[i].mem_hst = copy[0].mem_hst;
        }
        LIBXSTREAM_CHECK_CALL_THROW(libxstream_mem_allocate(0/*device*/, &copy[i].mem_dev, maxsize, 0));
      }
      else { // copy-out
        LIBXSTREAM_CHECK_CALL_THROW(libxstream_mem_allocate(-1/*host*/, &copy[i].mem_hst, maxsize, 0));
        if (0 == i) {
          LIBXSTREAM_CHECK_CALL_THROW(libxstream_mem_allocate(0/*device*/, &copy[i].mem_dev, maxsize, 0));
        }
        else {
          copy[i].mem_dev = copy[0].mem_dev;
        }
      }
    }

    int n = 1, nrepeat = maxrepeat;
    double maxval = 0, sumval = 0, lnsval = 0, duration = 0;
    for (size_t size = minsize; size <= maxsize; size <<= 1, ++n) {
      if (0 == (n % stride)) {
        nrepeat >>= 1;
      }

#if defined(_OPENMP)
      const double start = omp_get_wtime();
#     pragma omp parallel for num_threads(nthreads) schedule(dynamic)
#endif
      for (int i = 0; i < nrepeat; ++i) {
        const int j = i % nstreams;
        if (copyin) {
          LIBXSTREAM_CHECK_CALL_ASSERT(libxstream_memcpy_h2d(copy[j].mem_hst, copy[j].mem_dev, size, copy[j].stream));
        }
        else {
          LIBXSTREAM_CHECK_CALL_ASSERT(libxstream_memcpy_d2h(copy[j].mem_dev, copy[j].mem_hst, size, copy[j].stream));
        }

#if !defined(COPY_NO_SYNC)
        const int k = (j + 1) % nstreams;
        LIBXSTREAM_CHECK_CALL_ASSERT(libxstream_stream_sync(copy[k].stream));
#endif
      }

      // sync all streams to complete any pending work
      LIBXSTREAM_CHECK_CALL_THROW(libxstream_stream_sync(0));

#if defined(_OPENMP)
      const double iduration = omp_get_wtime() - start;
      fprintf(stdout, "%lu Byte x %i: ", static_cast<unsigned long>(size), nrepeat);
      if (0 < iduration) {
        const double bandwidth = (1.0 * size * nrepeat) / ((1ul << 20) * iduration);
        fprintf(stdout, "%.1f MB/s\n", bandwidth);
        maxval = std::max(maxval, bandwidth);
        sumval += bandwidth;
        lnsval += std::log(bandwidth);
        duration += iduration;
      }
      else {
        fprintf(stdout, "-\n");
      }
      fflush(stdout);
#endif
    }

    fprintf(stdout, "\n");
    if (1 < n) {
      fprintf(stdout, "Finished after %.0f s\n", duration);
      fprintf(stdout, "max: %.0f MB/s\n", maxval);
      fprintf(stdout, "avg: %.0f MB/s\n", sumval / (n - 1));
      fprintf(stdout, "geo: %.0f MB/s\n", std::exp(lnsval / (n - 1)));
    }
    else {
      fprintf(stdout, "Finished\n");
    }
  }
  catch(const std::exception& e) {
    fprintf(stderr, "Error: %s\n", e.what());
    return EXIT_FAILURE;
  }
  catch(...) {
    fprintf(stderr, "Error: unknown exception caught!\n");
    return EXIT_FAILURE;
  }

  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>// @(#)root/core/meta:$Id$
// Author: Paul Russo   30/07/2012

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TClingMethodArgInfo                                                  //
//                                                                      //
// Emulation of the CINT MethodInfo class.                              //
//                                                                      //
// The CINT C++ interpreter provides an interface to metadata about     //
// the arguments to a function through the MethodArgInfo class.  This   //
// class provides the same functionality, using an interface as close   //
// as possible to MethodArgInfo but the typedef metadata comes from     //
// the Clang C++ compiler, not CINT.                                    //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TClingMethodArgInfo.h"

#include "TClingMethodInfo.h"
#include "Property.h"
#include "TClingProperty.h"
#include "TClingTypeInfo.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/Type.h"

#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"

#include <string>

bool TClingMethodArgInfo::IsValid() const
{
   if (!fMethodInfo || !fMethodInfo->IsValid()) {
      return false;
   }
   int numParams = static_cast<int>(fMethodInfo->GetMethodDecl()->getNumParams());
   return (fIdx > -1) && (fIdx < numParams);
}

int TClingMethodArgInfo::Next()
{
   ++fIdx;
   return IsValid();
}

long TClingMethodArgInfo::Property() const
{
   if (!IsValid()) {
      return 0L;
   }
   long property = 0L;
   const clang::FunctionDecl *fd = fMethodInfo->GetMethodDecl();
   const clang::ParmVarDecl *pvd = fd->getParamDecl(fIdx);
   if (pvd->hasDefaultArg() || pvd->hasInheritedDefaultArg()) {
      property |= G__BIT_ISDEFAULT;
   }
   clang::QualType qt = pvd->getOriginalType().getCanonicalType();
   if (qt.isConstQualified()) {
      property |= G__BIT_ISCONSTANT;
   }
   while (1) {
      if (qt->isArrayType()) {
         qt = llvm::cast<clang::ArrayType>(qt)->getElementType();
         continue;
      }
      else if (qt->isReferenceType()) {
         property |= G__BIT_ISREFERENCE;
         qt = llvm::cast<clang::ReferenceType>(qt)->getPointeeType();
         continue;
      }
      else if (qt->isPointerType()) {
         property |= G__BIT_ISPOINTER;
         if (qt.isConstQualified()) {
            property |= G__BIT_ISPCONSTANT;
         }
         qt = llvm::cast<clang::PointerType>(qt)->getPointeeType();
         continue;
      }
      else if (qt->isMemberPointerType()) {
         qt = llvm::cast<clang::MemberPointerType>(qt)->getPointeeType();
         continue;
      }
      break;
   }
   if (qt.isConstQualified()) {
      property |= G__BIT_ISCONSTANT;
   }
   return property;
}

const char *TClingMethodArgInfo::DefaultValue() const
{
   if (!IsValid()) {
      return 0;
   }
   const clang::FunctionDecl *fd = fMethodInfo->GetMethodDecl();
   const clang::ParmVarDecl *pvd = fd->getParamDecl(fIdx);
   const clang::Expr *expr = pvd->getDefaultArg();
   clang::ASTContext &context = pvd->getASTContext();
   clang::PrintingPolicy policy(context.getPrintingPolicy());
   static std::string buf;
   buf.clear();
   llvm::raw_string_ostream out(buf);
   if (expr) {
      bool implicitInit = false;
      if (const clang::CXXConstructExpr *construct =
               llvm::dyn_cast<clang::CXXConstructExpr>(expr)) {
         implicitInit = (pvd->getInitStyle() == clang::VarDecl::CallInit) &&
                        (construct->getNumArgs() == 0) &&
                        !construct->isListInitialization();
      }
      if (!implicitInit) {
         if (pvd->getInitStyle() == clang::VarDecl::CallInit) {
            //out << "(";
         }
         else if (pvd->getInitStyle() == clang::VarDecl::CInit) {
            //out << " = ";
         }
         expr->printPretty(out, 0, policy, /*Indentation=*/0);
         if (pvd->getInitStyle() == clang::VarDecl::CallInit) {
            //out << ")";
         }
         out.flush();
      }
   }
   return buf.c_str();
}

const char *TClingMethodArgInfo::Name() const
{
   if (!IsValid()) {
      return 0;
   }
   const clang::FunctionDecl *fd = fMethodInfo->GetMethodDecl();
   const clang::ParmVarDecl *pvd = fd->getParamDecl(fIdx);
   static std::string buf;
   buf.clear();
   clang::PrintingPolicy policy(pvd->getASTContext().getPrintingPolicy());
   pvd->getNameForDiagnostic(buf, policy, /*Qualified=*/true);
   return buf.c_str();
}

const TClingTypeInfo *TClingMethodArgInfo::Type() const
{
   static TClingTypeInfo ti(fInterp);
   if (!IsValid()) {
      return &ti;
   }
   const clang::FunctionDecl *fd = fMethodInfo->GetMethodDecl();
   const clang::ParmVarDecl *pvd = fd->getParamDecl(fIdx);
   clang::QualType qt = pvd->getOriginalType();
   ti.Init(qt);
   return &ti;
}

const char *TClingMethodArgInfo::TypeName() const
{
   if (!IsValid()) {
      return 0;
   }
   return Type()->Name();
}

<commit_msg>Be CINT-compatible: non-default arguments return NULL as DefaultValue()<commit_after>// @(#)root/core/meta:$Id$
// Author: Paul Russo   30/07/2012

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TClingMethodArgInfo                                                  //
//                                                                      //
// Emulation of the CINT MethodInfo class.                              //
//                                                                      //
// The CINT C++ interpreter provides an interface to metadata about     //
// the arguments to a function through the MethodArgInfo class.  This   //
// class provides the same functionality, using an interface as close   //
// as possible to MethodArgInfo but the typedef metadata comes from     //
// the Clang C++ compiler, not CINT.                                    //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TClingMethodArgInfo.h"

#include "TClingMethodInfo.h"
#include "Property.h"
#include "TClingProperty.h"
#include "TClingTypeInfo.h"

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/Type.h"

#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"

#include <string>

bool TClingMethodArgInfo::IsValid() const
{
   if (!fMethodInfo || !fMethodInfo->IsValid()) {
      return false;
   }
   int numParams = static_cast<int>(fMethodInfo->GetMethodDecl()->getNumParams());
   return (fIdx > -1) && (fIdx < numParams);
}

int TClingMethodArgInfo::Next()
{
   ++fIdx;
   return IsValid();
}

long TClingMethodArgInfo::Property() const
{
   if (!IsValid()) {
      return 0L;
   }
   long property = 0L;
   const clang::FunctionDecl *fd = fMethodInfo->GetMethodDecl();
   const clang::ParmVarDecl *pvd = fd->getParamDecl(fIdx);
   if (pvd->hasDefaultArg() || pvd->hasInheritedDefaultArg()) {
      property |= G__BIT_ISDEFAULT;
   }
   clang::QualType qt = pvd->getOriginalType().getCanonicalType();
   if (qt.isConstQualified()) {
      property |= G__BIT_ISCONSTANT;
   }
   while (1) {
      if (qt->isArrayType()) {
         qt = llvm::cast<clang::ArrayType>(qt)->getElementType();
         continue;
      }
      else if (qt->isReferenceType()) {
         property |= G__BIT_ISREFERENCE;
         qt = llvm::cast<clang::ReferenceType>(qt)->getPointeeType();
         continue;
      }
      else if (qt->isPointerType()) {
         property |= G__BIT_ISPOINTER;
         if (qt.isConstQualified()) {
            property |= G__BIT_ISPCONSTANT;
         }
         qt = llvm::cast<clang::PointerType>(qt)->getPointeeType();
         continue;
      }
      else if (qt->isMemberPointerType()) {
         qt = llvm::cast<clang::MemberPointerType>(qt)->getPointeeType();
         continue;
      }
      break;
   }
   if (qt.isConstQualified()) {
      property |= G__BIT_ISCONSTANT;
   }
   return property;
}

const char *TClingMethodArgInfo::DefaultValue() const
{
   if (!IsValid()) {
      return 0;
   }
   const clang::FunctionDecl *fd = fMethodInfo->GetMethodDecl();
   const clang::ParmVarDecl *pvd = fd->getParamDecl(fIdx);
   const clang::Expr *expr = pvd->getDefaultArg();
   clang::ASTContext &context = pvd->getASTContext();
   clang::PrintingPolicy policy(context.getPrintingPolicy());
   static std::string buf;
   buf.clear();
   llvm::raw_string_ostream out(buf);
   if (!expr) {
      // CINT returned NULL for non-defaulted args.
      return 0;
   }
   bool implicitInit = false;
   if (const clang::CXXConstructExpr *construct =
       llvm::dyn_cast<clang::CXXConstructExpr>(expr)) {
      implicitInit = (pvd->getInitStyle() == clang::VarDecl::CallInit) &&
         (construct->getNumArgs() == 0) &&
         !construct->isListInitialization();
   }
   if (!implicitInit) {
      if (pvd->getInitStyle() == clang::VarDecl::CallInit) {
         //out << "(";
      }
      else if (pvd->getInitStyle() == clang::VarDecl::CInit) {
         //out << " = ";
      }
      expr->printPretty(out, 0, policy, /*Indentation=*/0);
      if (pvd->getInitStyle() == clang::VarDecl::CallInit) {
         //out << ")";
      }
      out.flush();
   }
   return buf.c_str();
}

const char *TClingMethodArgInfo::Name() const
{
   if (!IsValid()) {
      return 0;
   }
   const clang::FunctionDecl *fd = fMethodInfo->GetMethodDecl();
   const clang::ParmVarDecl *pvd = fd->getParamDecl(fIdx);
   static std::string buf;
   buf.clear();
   clang::PrintingPolicy policy(pvd->getASTContext().getPrintingPolicy());
   pvd->getNameForDiagnostic(buf, policy, /*Qualified=*/true);
   return buf.c_str();
}

const TClingTypeInfo *TClingMethodArgInfo::Type() const
{
   static TClingTypeInfo ti(fInterp);
   if (!IsValid()) {
      return &ti;
   }
   const clang::FunctionDecl *fd = fMethodInfo->GetMethodDecl();
   const clang::ParmVarDecl *pvd = fd->getParamDecl(fIdx);
   clang::QualType qt = pvd->getOriginalType();
   ti.Init(qt);
   return &ti;
}

const char *TClingMethodArgInfo::TypeName() const
{
   if (!IsValid()) {
      return 0;
   }
   return Type()->Name();
}

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <znc/Chan.h>
#include <znc/FileUtils.h>
#include <znc/IRCSock.h>
#include <znc/Modules.h>
#include <znc/Nick.h>
#include <znc/User.h>
#include <znc/znc.h>

#include "modperl/module.h"
#include "modperl/swigperlrun.h"
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>

#if defined(__APPLE__) && defined(__MACH__)
#include <crt_externs.h>  // for _NSGetEnviron
#endif

#include "modperl/pstring.h"

using std::set;
using std::vector;

// Allows perl to load .so files when needed by .pm
// For example, it needs to load ZNC.so
extern "C" {
void boot_DynaLoader(pTHX_ CV* cv);
static void xs_init(pTHX) {
    newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
}
}

class CModPerl : public CModule {
    PerlInterpreter* m_pPerl;

  public:
    MODCONSTRUCTOR(CModPerl) { m_pPerl = nullptr; }

#define PSTART   \
    dSP;         \
    I32 ax;      \
    int ret = 0; \
    ENTER;       \
    SAVETMPS;    \
    PUSHMARK(SP)
#define PCALL(name)                        \
    PUTBACK;                               \
    ret = call_pv(name, G_EVAL | G_ARRAY); \
    SPAGAIN;                               \
    SP -= ret;                             \
    ax = (SP - PL_stack_base) + 1
#define PEND  \
    ax += 0;  \
    PUTBACK;  \
    FREETMPS; \
    LEAVE
#define PUSH_STR(s) XPUSHs(PString(s).GetSV())
#define PUSH_PTR(type, p)                                                  \
    XPUSHs(SWIG_NewInstanceObj(const_cast<type>(p), SWIG_TypeQuery(#type), \
                               SWIG_SHADOW))

    bool OnLoad(const CString& sArgsi, CString& sMessage) override {
        CString sModPath, sTmp;
        if (!CModules::FindModPath("modperl/startup.pl", sModPath, sTmp)) {
            sMessage = "startup.pl not found.";
            return false;
        }
        sTmp = CDir::ChangeDir(sModPath, "..");
        int argc = 6;
        char* pArgv[] = {"",
                         "-T",
                         "-w",
                         "-I",
                         const_cast<char*>(sTmp.c_str()),
                         const_cast<char*>(sModPath.c_str()),
                         nullptr};
        char** argv = pArgv;
        char*** const pEnviron =
#if defined(__APPLE__) && defined(__MACH__)
            _NSGetEnviron();
#else
            &environ;
#endif
        PERL_SYS_INIT3(&argc, &argv, pEnviron);
        m_pPerl = perl_alloc();
        perl_construct(m_pPerl);
        if (perl_parse(m_pPerl, xs_init, argc, argv, *pEnviron)) {
            sMessage = "Can't initialize perl. ";
            if (SvTRUE(ERRSV)) {
                sMessage += PString(ERRSV);
            }
            perl_free(m_pPerl);
            PERL_SYS_TERM();
            m_pPerl = nullptr;
            DEBUG(__PRETTY_FUNCTION__ << " can't init perl");
            return false;
        }
        PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
        PSTART;
        PCALL("ZNC::Core::Init");
        PEND;
        return true;
    }

    EModRet OnModuleLoading(const CString& sModName, const CString& sArgs,
                            CModInfo::EModuleType eType, bool& bSuccess,
                            CString& sRetMsg) override {
        EModRet result = HALT;
        PSTART;
        PUSH_STR(sModName);
        PUSH_STR(sArgs);
        mXPUSHi(eType);
        PUSH_PTR(CUser*, GetUser());
        PUSH_PTR(CIRCNetwork*, GetNetwork());
        PCALL("ZNC::Core::LoadModule");

        if (SvTRUE(ERRSV)) {
            sRetMsg = PString(ERRSV);
            bSuccess = false;
            result = HALT;
            DEBUG("Perl ZNC::Core::LoadModule died: " << sRetMsg);
        } else if (ret < 1 || 2 < ret) {
            sRetMsg = "Error: Perl ZNC::Core::LoadModule returned " +
                      CString(ret) + " values.";
            bSuccess = false;
            result = HALT;
        } else {
            ELoadPerlMod eLPM = static_cast<ELoadPerlMod>(SvUV(ST(0)));
            if (Perl_NotFound == eLPM) {
                result = CONTINUE;  // Not a Perl module
            } else {
                sRetMsg = PString(ST(1));
                result = HALT;
                bSuccess = eLPM == Perl_Loaded;
            }
        }

        PEND;
        return result;
    }

    EModRet OnModuleUnloading(CModule* pModule, bool& bSuccess,
                              CString& sRetMsg) override {
        CPerlModule* pMod = AsPerlModule(pModule);
        if (pMod) {
            EModRet result = HALT;
            CString sModName = pMod->GetModName();
            PSTART;
            XPUSHs(pMod->GetPerlObj());
            PCALL("ZNC::Core::UnloadModule");
            if (SvTRUE(ERRSV)) {
                bSuccess = false;
                sRetMsg = PString(ERRSV);
            } else if (ret < 1 || 2 < ret) {
                sRetMsg = "Error: Perl ZNC::Core::UnloadModule returned " +
                          CString(ret) + " values.";
                bSuccess = false;
                result = HALT;
            } else {
                int bUnloaded = SvUV(ST(0));
                if (bUnloaded) {
                    bSuccess = true;
                    sRetMsg = "Module [" + sModName + "] unloaded";
                    result = HALT;
                } else {
                    result = CONTINUE;  // module wasn't loaded by modperl.
                                        // Perhaps a module-provider written in
                                        // perl did that.
                }
            }
            PEND;
            DEBUG(__PRETTY_FUNCTION__ << " " << sRetMsg);
            return result;
        }
        return CONTINUE;
    }

    EModRet OnGetModInfo(CModInfo& ModInfo, const CString& sModule,
                         bool& bSuccess, CString& sRetMsg) override {
        PSTART;
        PUSH_STR(sModule);
        PUSH_PTR(CModInfo*, &ModInfo);
        PCALL("ZNC::Core::GetModInfo");
        EModRet result = CONTINUE;
        if (SvTRUE(ERRSV)) {
            bSuccess = false;
            sRetMsg = PString(ERRSV);
            DEBUG("Perl ZNC::Core::GetModInfo died: " << sRetMsg);
        } else if (0 < ret) {
            switch (static_cast<ELoadPerlMod>(SvUV(ST(0)))) {
                case Perl_NotFound:
                    result = CONTINUE;
                    break;
                case Perl_Loaded:
                    result = HALT;
                    if (1 == ret) {
                        bSuccess = true;
                    } else {
                        bSuccess = false;
                        sRetMsg = "Something weird happened";
                    }
                    break;
                case Perl_LoadError:
                    result = HALT;
                    bSuccess = false;
                    if (2 == ret) {
                        sRetMsg = PString(ST(1));
                    } else {
                        sRetMsg = "Something weird happened";
                    }
            }
        } else {
            result = HALT;
            bSuccess = false;
            sRetMsg = "Something weird happened";
        }
        PEND;
        return result;
    }

    void OnGetAvailableMods(set<CModInfo>& ssMods,
                            CModInfo::EModuleType eType) override {
        unsigned int a = 0;
        CDir Dir;

        CModules::ModDirList dirs = CModules::GetModDirs();

        while (!dirs.empty()) {
            Dir.FillByWildcard(dirs.front().first, "*.pm");
            dirs.pop();

            for (a = 0; a < Dir.size(); a++) {
                CFile& File = *Dir[a];
                CString sName = File.GetShortName();
                CString sPath = File.GetLongName();
                CModInfo ModInfo;
                sName.RightChomp(3);
                PSTART;
                PUSH_STR(sPath);
                PUSH_STR(sName);
                PUSH_PTR(CModInfo*, &ModInfo);
                PCALL("ZNC::Core::ModInfoByPath");
                if (SvTRUE(ERRSV)) {
                    DEBUG(__PRETTY_FUNCTION__ << ": " << sPath << ": "
                                              << PString(ERRSV));
                } else if (ModInfo.SupportsType(eType)) {
                    ssMods.insert(ModInfo);
                }
                PEND;
            }
        }
    }

    ~CModPerl() override {
        if (m_pPerl) {
            PSTART;
            PCALL("ZNC::Core::UnloadAll");
            PEND;
            perl_destruct(m_pPerl);
            perl_free(m_pPerl);
            PERL_SYS_TERM();
        }
    }
};

#include "modperl/perlfunctions.cpp"

VWebSubPages& CPerlModule::GetSubPages() {
    VWebSubPages* result = _GetSubPages();
    if (!result) {
        return CModule::GetSubPages();
    }
    return *result;
}

void CPerlTimer::RunJob() {
    CPerlModule* pMod = AsPerlModule(GetModule());
    if (pMod) {
        PSTART;
        XPUSHs(GetPerlObj());
        PCALL("ZNC::Core::CallTimer");
        PEND;
    }
}

CPerlTimer::~CPerlTimer() {
    CPerlModule* pMod = AsPerlModule(GetModule());
    if (pMod) {
        PSTART;
        XPUSHs(sv_2mortal(m_perlObj));
        PCALL("ZNC::Core::RemoveTimer");
        PEND;
    }
}

#define SOCKSTART \
    PSTART;       \
    XPUSHs(GetPerlObj())
#define SOCKCBCHECK(OnSuccess)                                  \
    PCALL("ZNC::Core::CallSocket");                             \
    if (SvTRUE(ERRSV)) {                                        \
        Close();                                                \
        DEBUG("Perl socket hook died with: " + PString(ERRSV)); \
    } else {                                                    \
        OnSuccess;                                              \
    }                                                           \
    PEND
#define CBSOCK(Func)                                   \
    void CPerlSocket::Func() {                         \
        CPerlModule* pMod = AsPerlModule(GetModule()); \
        if (pMod) {                                    \
            SOCKSTART;                                 \
            PUSH_STR("On" #Func);                      \
            SOCKCBCHECK();                             \
        }                                              \
    }
CBSOCK(Connected);
CBSOCK(Disconnected);
CBSOCK(Timeout);
CBSOCK(ConnectionRefused);

void CPerlSocket::ReadData(const char* data, size_t len) {
    CPerlModule* pMod = AsPerlModule(GetModule());
    if (pMod) {
        SOCKSTART;
        PUSH_STR("OnReadData");
        XPUSHs(sv_2mortal(newSVpvn(data, len)));
        mXPUSHi(len);
        SOCKCBCHECK();
    }
}
void CPerlSocket::ReadLine(const CString& sLine) {
    CPerlModule* pMod = AsPerlModule(GetModule());
    if (pMod) {
        SOCKSTART;
        PUSH_STR("OnReadLine");
        PUSH_STR(sLine);
        SOCKCBCHECK();
    }
}
Csock* CPerlSocket::GetSockObj(const CString& sHost, unsigned short uPort) {
    CPerlModule* pMod = AsPerlModule(GetModule());
    Csock* result = nullptr;
    if (pMod) {
        SOCKSTART;
        PUSH_STR("_Accepted");
        PUSH_STR(sHost);
        mXPUSHi(uPort);
        SOCKCBCHECK(result = SvToPtr<CPerlSocket>("CPerlSocket*")(ST(0)););
    }
    return result;
}

CPerlSocket::~CPerlSocket() {
    CPerlModule* pMod = AsPerlModule(GetModule());
    if (pMod) {
        PSTART;
        XPUSHs(sv_2mortal(m_perlObj));
        PCALL("ZNC::Core::RemoveSocket");
        PEND;
    }
}

template <>
void TModInfo<CModPerl>(CModInfo& Info) {
    Info.SetWikiPage("modperl");
}

GLOBALMODULEDEFS(CModPerl, "Loads perl scripts as ZNC modules")
<commit_msg>Modperl: ZNC::Core::Init is not used anymore for a long time.<commit_after>/*
 * Copyright (C) 2004-2017 ZNC, see the NOTICE file for details.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <znc/Chan.h>
#include <znc/FileUtils.h>
#include <znc/IRCSock.h>
#include <znc/Modules.h>
#include <znc/Nick.h>
#include <znc/User.h>
#include <znc/znc.h>

#include "modperl/module.h"
#include "modperl/swigperlrun.h"
#include <EXTERN.h>
#include <perl.h>
#include <XSUB.h>

#if defined(__APPLE__) && defined(__MACH__)
#include <crt_externs.h>  // for _NSGetEnviron
#endif

#include "modperl/pstring.h"

using std::set;
using std::vector;

// Allows perl to load .so files when needed by .pm
// For example, it needs to load ZNC.so
extern "C" {
void boot_DynaLoader(pTHX_ CV* cv);
static void xs_init(pTHX) {
    newXS("DynaLoader::boot_DynaLoader", boot_DynaLoader, __FILE__);
}
}

class CModPerl : public CModule {
    PerlInterpreter* m_pPerl;

  public:
    MODCONSTRUCTOR(CModPerl) { m_pPerl = nullptr; }

#define PSTART   \
    dSP;         \
    I32 ax;      \
    int ret = 0; \
    ENTER;       \
    SAVETMPS;    \
    PUSHMARK(SP)
#define PCALL(name)                        \
    PUTBACK;                               \
    ret = call_pv(name, G_EVAL | G_ARRAY); \
    SPAGAIN;                               \
    SP -= ret;                             \
    ax = (SP - PL_stack_base) + 1
#define PEND  \
    ax += 0;  \
    PUTBACK;  \
    FREETMPS; \
    LEAVE
#define PUSH_STR(s) XPUSHs(PString(s).GetSV())
#define PUSH_PTR(type, p)                                                  \
    XPUSHs(SWIG_NewInstanceObj(const_cast<type>(p), SWIG_TypeQuery(#type), \
                               SWIG_SHADOW))

    bool OnLoad(const CString& sArgsi, CString& sMessage) override {
        CString sModPath, sTmp;
        if (!CModules::FindModPath("modperl/startup.pl", sModPath, sTmp)) {
            sMessage = "startup.pl not found.";
            return false;
        }
        sTmp = CDir::ChangeDir(sModPath, "..");
        int argc = 6;
        char* pArgv[] = {"",
                         "-T",
                         "-w",
                         "-I",
                         const_cast<char*>(sTmp.c_str()),
                         const_cast<char*>(sModPath.c_str()),
                         nullptr};
        char** argv = pArgv;
        char*** const pEnviron =
#if defined(__APPLE__) && defined(__MACH__)
            _NSGetEnviron();
#else
            &environ;
#endif
        PERL_SYS_INIT3(&argc, &argv, pEnviron);
        m_pPerl = perl_alloc();
        perl_construct(m_pPerl);
        if (perl_parse(m_pPerl, xs_init, argc, argv, *pEnviron)) {
            sMessage = "Can't initialize perl. ";
            if (SvTRUE(ERRSV)) {
                sMessage += PString(ERRSV);
            }
            perl_free(m_pPerl);
            PERL_SYS_TERM();
            m_pPerl = nullptr;
            DEBUG(__PRETTY_FUNCTION__ << " can't init perl");
            return false;
        }
        PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
        return true;
    }

    EModRet OnModuleLoading(const CString& sModName, const CString& sArgs,
                            CModInfo::EModuleType eType, bool& bSuccess,
                            CString& sRetMsg) override {
        EModRet result = HALT;
        PSTART;
        PUSH_STR(sModName);
        PUSH_STR(sArgs);
        mXPUSHi(eType);
        PUSH_PTR(CUser*, GetUser());
        PUSH_PTR(CIRCNetwork*, GetNetwork());
        PCALL("ZNC::Core::LoadModule");

        if (SvTRUE(ERRSV)) {
            sRetMsg = PString(ERRSV);
            bSuccess = false;
            result = HALT;
            DEBUG("Perl ZNC::Core::LoadModule died: " << sRetMsg);
        } else if (ret < 1 || 2 < ret) {
            sRetMsg = "Error: Perl ZNC::Core::LoadModule returned " +
                      CString(ret) + " values.";
            bSuccess = false;
            result = HALT;
        } else {
            ELoadPerlMod eLPM = static_cast<ELoadPerlMod>(SvUV(ST(0)));
            if (Perl_NotFound == eLPM) {
                result = CONTINUE;  // Not a Perl module
            } else {
                sRetMsg = PString(ST(1));
                result = HALT;
                bSuccess = eLPM == Perl_Loaded;
            }
        }

        PEND;
        return result;
    }

    EModRet OnModuleUnloading(CModule* pModule, bool& bSuccess,
                              CString& sRetMsg) override {
        CPerlModule* pMod = AsPerlModule(pModule);
        if (pMod) {
            EModRet result = HALT;
            CString sModName = pMod->GetModName();
            PSTART;
            XPUSHs(pMod->GetPerlObj());
            PCALL("ZNC::Core::UnloadModule");
            if (SvTRUE(ERRSV)) {
                bSuccess = false;
                sRetMsg = PString(ERRSV);
            } else if (ret < 1 || 2 < ret) {
                sRetMsg = "Error: Perl ZNC::Core::UnloadModule returned " +
                          CString(ret) + " values.";
                bSuccess = false;
                result = HALT;
            } else {
                int bUnloaded = SvUV(ST(0));
                if (bUnloaded) {
                    bSuccess = true;
                    sRetMsg = "Module [" + sModName + "] unloaded";
                    result = HALT;
                } else {
                    result = CONTINUE;  // module wasn't loaded by modperl.
                                        // Perhaps a module-provider written in
                                        // perl did that.
                }
            }
            PEND;
            DEBUG(__PRETTY_FUNCTION__ << " " << sRetMsg);
            return result;
        }
        return CONTINUE;
    }

    EModRet OnGetModInfo(CModInfo& ModInfo, const CString& sModule,
                         bool& bSuccess, CString& sRetMsg) override {
        PSTART;
        PUSH_STR(sModule);
        PUSH_PTR(CModInfo*, &ModInfo);
        PCALL("ZNC::Core::GetModInfo");
        EModRet result = CONTINUE;
        if (SvTRUE(ERRSV)) {
            bSuccess = false;
            sRetMsg = PString(ERRSV);
            DEBUG("Perl ZNC::Core::GetModInfo died: " << sRetMsg);
        } else if (0 < ret) {
            switch (static_cast<ELoadPerlMod>(SvUV(ST(0)))) {
                case Perl_NotFound:
                    result = CONTINUE;
                    break;
                case Perl_Loaded:
                    result = HALT;
                    if (1 == ret) {
                        bSuccess = true;
                    } else {
                        bSuccess = false;
                        sRetMsg = "Something weird happened";
                    }
                    break;
                case Perl_LoadError:
                    result = HALT;
                    bSuccess = false;
                    if (2 == ret) {
                        sRetMsg = PString(ST(1));
                    } else {
                        sRetMsg = "Something weird happened";
                    }
            }
        } else {
            result = HALT;
            bSuccess = false;
            sRetMsg = "Something weird happened";
        }
        PEND;
        return result;
    }

    void OnGetAvailableMods(set<CModInfo>& ssMods,
                            CModInfo::EModuleType eType) override {
        unsigned int a = 0;
        CDir Dir;

        CModules::ModDirList dirs = CModules::GetModDirs();

        while (!dirs.empty()) {
            Dir.FillByWildcard(dirs.front().first, "*.pm");
            dirs.pop();

            for (a = 0; a < Dir.size(); a++) {
                CFile& File = *Dir[a];
                CString sName = File.GetShortName();
                CString sPath = File.GetLongName();
                CModInfo ModInfo;
                sName.RightChomp(3);
                PSTART;
                PUSH_STR(sPath);
                PUSH_STR(sName);
                PUSH_PTR(CModInfo*, &ModInfo);
                PCALL("ZNC::Core::ModInfoByPath");
                if (SvTRUE(ERRSV)) {
                    DEBUG(__PRETTY_FUNCTION__ << ": " << sPath << ": "
                                              << PString(ERRSV));
                } else if (ModInfo.SupportsType(eType)) {
                    ssMods.insert(ModInfo);
                }
                PEND;
            }
        }
    }

    ~CModPerl() override {
        if (m_pPerl) {
            PSTART;
            PCALL("ZNC::Core::UnloadAll");
            PEND;
            perl_destruct(m_pPerl);
            perl_free(m_pPerl);
            PERL_SYS_TERM();
        }
    }
};

#include "modperl/perlfunctions.cpp"

VWebSubPages& CPerlModule::GetSubPages() {
    VWebSubPages* result = _GetSubPages();
    if (!result) {
        return CModule::GetSubPages();
    }
    return *result;
}

void CPerlTimer::RunJob() {
    CPerlModule* pMod = AsPerlModule(GetModule());
    if (pMod) {
        PSTART;
        XPUSHs(GetPerlObj());
        PCALL("ZNC::Core::CallTimer");
        PEND;
    }
}

CPerlTimer::~CPerlTimer() {
    CPerlModule* pMod = AsPerlModule(GetModule());
    if (pMod) {
        PSTART;
        XPUSHs(sv_2mortal(m_perlObj));
        PCALL("ZNC::Core::RemoveTimer");
        PEND;
    }
}

#define SOCKSTART \
    PSTART;       \
    XPUSHs(GetPerlObj())
#define SOCKCBCHECK(OnSuccess)                                  \
    PCALL("ZNC::Core::CallSocket");                             \
    if (SvTRUE(ERRSV)) {                                        \
        Close();                                                \
        DEBUG("Perl socket hook died with: " + PString(ERRSV)); \
    } else {                                                    \
        OnSuccess;                                              \
    }                                                           \
    PEND
#define CBSOCK(Func)                                   \
    void CPerlSocket::Func() {                         \
        CPerlModule* pMod = AsPerlModule(GetModule()); \
        if (pMod) {                                    \
            SOCKSTART;                                 \
            PUSH_STR("On" #Func);                      \
            SOCKCBCHECK();                             \
        }                                              \
    }
CBSOCK(Connected);
CBSOCK(Disconnected);
CBSOCK(Timeout);
CBSOCK(ConnectionRefused);

void CPerlSocket::ReadData(const char* data, size_t len) {
    CPerlModule* pMod = AsPerlModule(GetModule());
    if (pMod) {
        SOCKSTART;
        PUSH_STR("OnReadData");
        XPUSHs(sv_2mortal(newSVpvn(data, len)));
        mXPUSHi(len);
        SOCKCBCHECK();
    }
}
void CPerlSocket::ReadLine(const CString& sLine) {
    CPerlModule* pMod = AsPerlModule(GetModule());
    if (pMod) {
        SOCKSTART;
        PUSH_STR("OnReadLine");
        PUSH_STR(sLine);
        SOCKCBCHECK();
    }
}
Csock* CPerlSocket::GetSockObj(const CString& sHost, unsigned short uPort) {
    CPerlModule* pMod = AsPerlModule(GetModule());
    Csock* result = nullptr;
    if (pMod) {
        SOCKSTART;
        PUSH_STR("_Accepted");
        PUSH_STR(sHost);
        mXPUSHi(uPort);
        SOCKCBCHECK(result = SvToPtr<CPerlSocket>("CPerlSocket*")(ST(0)););
    }
    return result;
}

CPerlSocket::~CPerlSocket() {
    CPerlModule* pMod = AsPerlModule(GetModule());
    if (pMod) {
        PSTART;
        XPUSHs(sv_2mortal(m_perlObj));
        PCALL("ZNC::Core::RemoveSocket");
        PEND;
    }
}

template <>
void TModInfo<CModPerl>(CModInfo& Info) {
    Info.SetWikiPage("modperl");
}

GLOBALMODULEDEFS(CModPerl, "Loads perl scripts as ZNC modules")
<|endoftext|>


<commit_before>/*
 * VariableList.cc
 *
 * Copyright (C) 2009, 2014, 2015 Linas Vepstas
 *
 * Author: Linas Vepstas <linasvepstas@gmail.com>  January 2009
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the
 * exceptions at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/atoms/base/ClassServer.h>
#include <opencog/atoms/TypeNode.h>

#include "VariableList.h"

using namespace opencog;

void VariableList::validate_vardecl(const HandleSeq& oset)
{
	for (const Handle& h: oset)
	{
		Type t = h->getType();
		if (VARIABLE_NODE == t or GLOB_NODE == t)
		{
			_varlist.varset.insert(h);    // tree (unordered)
			_varlist.varseq.emplace_back(h); // vector (ordered)
		}
		else if (TYPED_VARIABLE_LINK == t)
		{
			get_vartype(h);
		}
		else
			throw InvalidParamException(TRACE_INFO,
				"Expected a VariableNode or a TypedVariableLink, got: %s",
					classserver().getTypeName(t).c_str());
	}
	build_index();
}

VariableList::VariableList(const HandleSeq& oset,
                       TruthValuePtr tv, AttentionValuePtr av)
	: Link(VARIABLE_LIST, oset, tv, av)
{
	validate_vardecl(oset);
}

VariableList::VariableList(Type t, const HandleSeq& oset,
                       TruthValuePtr tv, AttentionValuePtr av)
	: Link(t, oset, tv, av)
{
	// derived classes have a different initialization order
	if (VARIABLE_LIST != t) return;
	validate_vardecl(oset);
}

VariableList::VariableList(Link &l)
	: Link(l)
{
	// Type must be as expected
	Type tscope = l.getType();
	if (not classserver().isA(tscope, VARIABLE_LIST))
	{
		const std::string& tname = classserver().getTypeName(tscope);
		throw InvalidParamException(TRACE_INFO,
			"Expecting a VariableList, got %s", tname.c_str());
	}

	// Dervided types have a different initialization sequence
	if (VARIABLE_LIST != tscope) return;
	validate_vardecl(_outgoing);
}

/* ================================================================= */
/**
 * Extract the variable type(s) from a TypedVariableLink
 *
 * The call is expecting htypelink to point to one of the two
 * following structures:
 *
 *    TypedVariableLink
 *       VariableNode "$some_var_name"
 *       TypeNode  "ConceptNode"
 *
 * or
 *
 *    TypedVariableLink
 *       VariableNode "$some_var_name"
 *       TypeChoice
 *          TypeNode  "ConceptNode"
 *          TypeNode  "NumberNode"
 *          TypeNode  "WordNode"
 *
 * or possibly types that are SignatureLink's or FuyzzyLink's or
 * polymorphic combinations thereof: e.g. the following is valid:
 *
 *    TypedVariableLink
 *       VariableNode "$some_var_name"
 *       TypeChoice
 *          TypeNode  "ConceptNode"
 *          SignatureLink
 *              InheritanceLink
 *                 PredicateNode "foobar"
 *                 TypeNode  "ListLink"
 *          FuzzyLink
 *              InheritanceLink
 *                 ConceptNode "animal"
 *                 ConceptNode "tiger"
 *
 * In either case, the variable itself is appended to "vset",
 * and the list of allowed types are associated with the variable
 * via the map "typemap".
 */
void VariableList::get_vartype(const Handle& htypelink)
{
	const std::vector<Handle>& oset = htypelink->getOutgoingSet();
	if (2 != oset.size())
	{
		throw InvalidParamException(TRACE_INFO,
			"TypedVariableLink has wrong size, got %lu", oset.size());
	}

	Handle varname = oset[0];
	Handle vartype = oset[1];

	// The vartype is either a single type name, or a list of typenames.
	Type t = vartype->getType();
	if (TYPE_NODE == t)
	{
		Type vt = TypeNodeCast(vartype)->get_value();
		if (vt != ATOM)  // Atom type is same as untyped.
		{
			std::set<Type> ts = {vt};
			_varlist._simple_typemap.insert({varname, ts});
		}
	}
	else if (TYPE_CHOICE == t)
	{
		std::set<Type> typeset;
		std::set<Handle> deepset;
		std::set<Handle> fuzzset;

		const HandleSeq& tset = vartype->getOutgoingSet();
		size_t tss = tset.size();
		for (size_t i=0; i<tss; i++)
		{
			Handle ht(tset[i]);
			Type var_type = ht->getType();
			if (TYPE_NODE == var_type)
			{
				Type vt = TypeNodeCast(ht)->get_value();
				if (ATOM != vt) typeset.insert(vt);
			}
			else if (SIGNATURE_LINK == var_type)
			{
				const HandleSeq& sig = ht->getOutgoingSet();
				if (1 != sig.size())
					throw SyntaxException(TRACE_INFO,
						"Unexpected contents in SignatureLink\n"
						"Expected arity==1, got %s", vartype->toString().c_str());

				deepset.insert(ht);
			}
			else if (FUZZY_LINK == var_type)
			{
				const HandleSeq& fuz = ht->getOutgoingSet();
				if (1 != fuz.size())
					throw SyntaxException(TRACE_INFO,
						"Unexpected contents in FuzzyLink\n"
						"Expected arity==1, got %s", vartype->toString().c_str());

				fuzzset.insert(ht);
			}
			else
			{
				throw InvalidParamException(TRACE_INFO,
					"VariableChoice has unexpected content:\n"
					"Expected TypeNode, got %s",
					    classserver().getTypeName(ht->getType()).c_str());
			}
		}

		if (0 < typeset.size())
			_varlist._simple_typemap.insert({varname, typeset});
		if (0 < deepset.size())
			_varlist._deep_typemap.insert({varname, deepset});
		if (0 < fuzzset.size())
			_varlist._fuzzy_typemap.insert({varname, fuzzset});
	}
	else if (SIGNATURE_LINK == t)
	{
		const HandleSeq& tset = vartype->getOutgoingSet();
		if (1 != tset.size())
			throw SyntaxException(TRACE_INFO,
				"Unexpected contents in SignatureLink\n"
				"Expected arity==1, got %s", vartype->toString().c_str());

		std::set<Handle> ts;
		ts.insert(vartype);
		_varlist._deep_typemap.insert({varname, ts});
	}
	else if (FUZZY_LINK == t)
	{
		const HandleSeq& tset = vartype->getOutgoingSet();
		if (1 != tset.size())
			throw SyntaxException(TRACE_INFO,
				"Unexpected contents in FuzzyLink\n"
				"Expected arity==1, got %s", vartype->toString().c_str());

		std::set<Handle> ts;
		ts.insert(vartype);
		_varlist._fuzzy_typemap.insert({varname, ts});
	}
	else
	{
		throw SyntaxException(TRACE_INFO,
			"Unexpected contents in TypedVariableLink\n"
			"Expected type specifier (e.g. TypeNode, TypeChoice, etc.), got %s",
			classserver().getTypeName(t).c_str());
	}

	_varlist.varset.insert(varname);
	_varlist.varseq.emplace_back(varname);
}

/* ================================================================= */
/**
 * Validate variable declarations for syntax correctness.
 *
 * This will check to make sure that a set of variable declarations are
 * of a reasonable form. Thus, for example, a structure similar to the
 * below is expected.
 *
 *       VariableList
 *          VariableNode "$var0"
 *          VariableNode "$var1"
 *          TypedVariableLink
 *             VariableNode "$var2"
 *             TypeNode  "ConceptNode"
 *          TypedVariableLink
 *             VariableNode "$var3"
 *             TypeChoice
 *                 TypeNode  "PredicateNode"
 *                 TypeNode  "GroundedPredicateNode"
 *
 * As a side-effect, the variables and type restrictions are unpacked.
 */
void VariableList::validate_vardecl(const Handle& hdecls)
{
	// Expecting the declaration list to be either a single
	// variable, or a list of variable declarations
	Type tdecls = hdecls->getType();
	if (VARIABLE_NODE == tdecls or GLOB_NODE == tdecls)
	{
		_varlist.varset.insert(hdecls);
		_varlist.varseq.emplace_back(hdecls);
	}
	else if (TYPED_VARIABLE_LINK == tdecls)
	{
		get_vartype(hdecls);
	}
	else if (VARIABLE_LIST == tdecls)
	{
		// The list of variable declarations should be .. a list of
		// variables! Make sure its as expected.
		const std::vector<Handle>& dset = hdecls->getOutgoingSet();
		validate_vardecl(dset);
	}
	else
	{
		throw InvalidParamException(TRACE_INFO,
			"Expected a VariableList holding variable declarations");
	}
	build_index();
}

/* ================================================================= */
/**
 * Build the index from variable name, to its ordinal number.
 * The index is needed for variable substitution, i.e. for the
 * substitute method below.  The specific sequence order of the
 * variables is essential for making substitution work.
 */
void VariableList::build_index(void)
{
	if (0 < _varlist.index.size()) return;
	size_t sz = _varlist.varseq.size();
	for (size_t i=0; i<sz; i++)
	{
		_varlist.index.insert(std::pair<Handle, unsigned int>(_varlist.varseq[i], i));
	}
}

/* ===================== END OF FILE ===================== */
<commit_msg>Unbundle defined types<commit_after>/*
 * VariableList.cc
 *
 * Copyright (C) 2009, 2014, 2015 Linas Vepstas
 *
 * Author: Linas Vepstas <linasvepstas@gmail.com>  January 2009
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the
 * exceptions at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/atoms/base/ClassServer.h>
#include <opencog/atoms/TypeNode.h>
#include <opencog/atoms/core/DefineLink.h>

#include "VariableList.h"

using namespace opencog;

void VariableList::validate_vardecl(const HandleSeq& oset)
{
	for (const Handle& h: oset)
	{
		Type t = h->getType();
		if (VARIABLE_NODE == t or GLOB_NODE == t)
		{
			_varlist.varset.insert(h);    // tree (unordered)
			_varlist.varseq.emplace_back(h); // vector (ordered)
		}
		else if (TYPED_VARIABLE_LINK == t)
		{
			get_vartype(h);
		}
		else
			throw InvalidParamException(TRACE_INFO,
				"Expected a VariableNode or a TypedVariableLink, got: %s",
					classserver().getTypeName(t).c_str());
	}
	build_index();
}

VariableList::VariableList(const HandleSeq& oset,
                       TruthValuePtr tv, AttentionValuePtr av)
	: Link(VARIABLE_LIST, oset, tv, av)
{
	validate_vardecl(oset);
}

VariableList::VariableList(Type t, const HandleSeq& oset,
                       TruthValuePtr tv, AttentionValuePtr av)
	: Link(t, oset, tv, av)
{
	// derived classes have a different initialization order
	if (VARIABLE_LIST != t) return;
	validate_vardecl(oset);
}

VariableList::VariableList(Link &l)
	: Link(l)
{
	// Type must be as expected
	Type tscope = l.getType();
	if (not classserver().isA(tscope, VARIABLE_LIST))
	{
		const std::string& tname = classserver().getTypeName(tscope);
		throw InvalidParamException(TRACE_INFO,
			"Expecting a VariableList, got %s", tname.c_str());
	}

	// Dervided types have a different initialization sequence
	if (VARIABLE_LIST != tscope) return;
	validate_vardecl(_outgoing);
}

/* ================================================================= */
/**
 * Extract the variable type(s) from a TypedVariableLink
 *
 * The call is expecting htypelink to point to one of the two
 * following structures:
 *
 *    TypedVariableLink
 *       VariableNode "$some_var_name"
 *       TypeNode  "ConceptNode"
 *
 * or
 *
 *    TypedVariableLink
 *       VariableNode "$some_var_name"
 *       TypeChoice
 *          TypeNode  "ConceptNode"
 *          TypeNode  "NumberNode"
 *          TypeNode  "WordNode"
 *
 * or possibly types that are SignatureLink's or FuyzzyLink's or
 * polymorphic combinations thereof: e.g. the following is valid:
 *
 *    TypedVariableLink
 *       VariableNode "$some_var_name"
 *       TypeChoice
 *          TypeNode  "ConceptNode"
 *          SignatureLink
 *              InheritanceLink
 *                 PredicateNode "foobar"
 *                 TypeNode  "ListLink"
 *          FuzzyLink
 *              InheritanceLink
 *                 ConceptNode "animal"
 *                 ConceptNode "tiger"
 *
 * In either case, the variable itself is appended to "vset",
 * and the list of allowed types are associated with the variable
 * via the map "typemap".
 */
void VariableList::get_vartype(const Handle& htypelink)
{
	const std::vector<Handle>& oset = htypelink->getOutgoingSet();
	if (2 != oset.size())
	{
		throw InvalidParamException(TRACE_INFO,
			"TypedVariableLink has wrong size, got %lu", oset.size());
	}

	Handle varname(oset[0]);
	Handle vartype(oset[1]);

	// If its a defined type, unbundle it.
	Type t = vartype->getType();
	if (DEFINED_TYPE_NODE == t)
	{
		vartype = DefineLink::get_definition(vartype);
		t = vartype->getType();
	}

	// The vartype is either a single type name, or a list of typenames.
	if (TYPE_NODE == t)
	{
		Type vt = TypeNodeCast(vartype)->get_value();
		if (vt != ATOM)  // Atom type is same as untyped.
		{
			std::set<Type> ts = {vt};
			_varlist._simple_typemap.insert({varname, ts});
		}
	}
	else if (TYPE_CHOICE == t)
	{
		std::set<Type> typeset;
		std::set<Handle> deepset;
		std::set<Handle> fuzzset;

		const HandleSeq& tset = vartype->getOutgoingSet();
		size_t tss = tset.size();
		for (size_t i=0; i<tss; i++)
		{
			Handle ht(tset[i]);
			Type var_type = ht->getType();
			if (TYPE_NODE == var_type)
			{
				Type vt = TypeNodeCast(ht)->get_value();
				if (ATOM != vt) typeset.insert(vt);
			}
			else if (SIGNATURE_LINK == var_type)
			{
				const HandleSeq& sig = ht->getOutgoingSet();
				if (1 != sig.size())
					throw SyntaxException(TRACE_INFO,
						"Unexpected contents in SignatureLink\n"
						"Expected arity==1, got %s", vartype->toString().c_str());

				deepset.insert(ht);
			}
			else if (FUZZY_LINK == var_type)
			{
				const HandleSeq& fuz = ht->getOutgoingSet();
				if (1 != fuz.size())
					throw SyntaxException(TRACE_INFO,
						"Unexpected contents in FuzzyLink\n"
						"Expected arity==1, got %s", vartype->toString().c_str());

				fuzzset.insert(ht);
			}
			else
			{
				throw InvalidParamException(TRACE_INFO,
					"VariableChoice has unexpected content:\n"
					"Expected TypeNode, got %s",
					    classserver().getTypeName(ht->getType()).c_str());
			}
		}

		if (0 < typeset.size())
			_varlist._simple_typemap.insert({varname, typeset});
		if (0 < deepset.size())
			_varlist._deep_typemap.insert({varname, deepset});
		if (0 < fuzzset.size())
			_varlist._fuzzy_typemap.insert({varname, fuzzset});
	}
	else if (SIGNATURE_LINK == t)
	{
		const HandleSeq& tset = vartype->getOutgoingSet();
		if (1 != tset.size())
			throw SyntaxException(TRACE_INFO,
				"Unexpected contents in SignatureLink\n"
				"Expected arity==1, got %s", vartype->toString().c_str());

		std::set<Handle> ts;
		ts.insert(vartype);
		_varlist._deep_typemap.insert({varname, ts});
	}
	else if (FUZZY_LINK == t)
	{
		const HandleSeq& tset = vartype->getOutgoingSet();
		if (1 != tset.size())
			throw SyntaxException(TRACE_INFO,
				"Unexpected contents in FuzzyLink\n"
				"Expected arity==1, got %s", vartype->toString().c_str());

		std::set<Handle> ts;
		ts.insert(vartype);
		_varlist._fuzzy_typemap.insert({varname, ts});
	}
	else
	{
		throw SyntaxException(TRACE_INFO,
			"Unexpected contents in TypedVariableLink\n"
			"Expected type specifier (e.g. TypeNode, TypeChoice, etc.), got %s",
			classserver().getTypeName(t).c_str());
	}

	_varlist.varset.insert(varname);
	_varlist.varseq.emplace_back(varname);
}

/* ================================================================= */
/**
 * Validate variable declarations for syntax correctness.
 *
 * This will check to make sure that a set of variable declarations are
 * of a reasonable form. Thus, for example, a structure similar to the
 * below is expected.
 *
 *       VariableList
 *          VariableNode "$var0"
 *          VariableNode "$var1"
 *          TypedVariableLink
 *             VariableNode "$var2"
 *             TypeNode  "ConceptNode"
 *          TypedVariableLink
 *             VariableNode "$var3"
 *             TypeChoice
 *                 TypeNode  "PredicateNode"
 *                 TypeNode  "GroundedPredicateNode"
 *
 * As a side-effect, the variables and type restrictions are unpacked.
 */
void VariableList::validate_vardecl(const Handle& hdecls)
{
	// Expecting the declaration list to be either a single
	// variable, or a list of variable declarations
	Type tdecls = hdecls->getType();
	if (VARIABLE_NODE == tdecls or GLOB_NODE == tdecls)
	{
		_varlist.varset.insert(hdecls);
		_varlist.varseq.emplace_back(hdecls);
	}
	else if (TYPED_VARIABLE_LINK == tdecls)
	{
		get_vartype(hdecls);
	}
	else if (VARIABLE_LIST == tdecls)
	{
		// The list of variable declarations should be .. a list of
		// variables! Make sure its as expected.
		const std::vector<Handle>& dset = hdecls->getOutgoingSet();
		validate_vardecl(dset);
	}
	else
	{
		throw InvalidParamException(TRACE_INFO,
			"Expected a VariableList holding variable declarations");
	}
	build_index();
}

/* ================================================================= */
/**
 * Build the index from variable name, to its ordinal number.
 * The index is needed for variable substitution, i.e. for the
 * substitute method below.  The specific sequence order of the
 * variables is essential for making substitution work.
 */
void VariableList::build_index(void)
{
	if (0 < _varlist.index.size()) return;
	size_t sz = _varlist.varseq.size();
	for (size_t i=0; i<sz; i++)
	{
		_varlist.index.insert(std::pair<Handle, unsigned int>(_varlist.varseq[i], i));
	}
}

/* ===================== END OF FILE ===================== */
<|endoftext|>