/*
 * Copyright (c) 2002-2006 Samit Basu
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * 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 General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#ifdef HAVE_LLVM
#include "JITVM.hpp"
#endif

#include "Interpreter.hpp"
#include <math.h>
#include <stdio.h>
#include "Exception.hpp"
#include "Math.hpp"
#include "Array.hpp"
#include "Malloc.hpp"
#include "Parser.hpp"
#include "Scanner.hpp"
#include "Token.hpp"
#include "Module.hpp"
#include "File.hpp"
#include "Serialize.hpp"
#include <signal.h>
#include "Class.hpp"
#include "Print.hpp"
#include "MemPtr.hpp"
#include <qapplication.h>
#include <qeventloop.h>
#include <QtCore>
#include <fstream>
#include <stdarg.h>


#ifdef WIN32
#define PATHSEP ";"
#else
#define PATHSEP ":"
#endif

const int max_line_count = 1000000;

#define SaveEndInfo  \
  ArrayReference oldEndRef = endRef; \
  int oldEndCount = endCount; \
  int oldEndTotal = endTotal; 

#define RestoreEndInfo \
  endRef = oldEndRef; \
  endCount = oldEndCount; \
  endTotal = oldEndTotal;  \

QString TildeExpand(string path) {
  if ((path.size() > 0) && (path[0] == '~')) {
    path.erase(path.begin());
    return QDir::homePath() + QString::fromStdString(path);
  }
  return QString::fromStdString(path);
}

void Interpreter::setPath(std::string path) {
  QString pathdata(QString::fromStdString(path));
  QStringList pathset(pathdata.split(PATHSEP,QString::SkipEmptyParts));
  m_userPath.clear();
  for (int i=0;i<pathset.size();i++) 
    if (pathset[i] != ".") {
      QDir tpath(TildeExpand(pathset[i].toStdString()));
      m_userPath << tpath.absolutePath();
    }
  rescanPath();
}

std::string Interpreter::getTotalPath() {
  std::string retpath;
  QStringList totalPath(QStringList() << m_basePath << m_userPath);
  for (int i=0;i<totalPath.size()-1;i++) 
    retpath = retpath + totalPath[i].toStdString() + PATHSEP;
  if (totalPath.size() > 0) 
    retpath = retpath + totalPath[totalPath.size()-1].toStdString();
  return retpath;
}
  
std::string Interpreter::getPath() {
  std::string retpath;
  QStringList totalPath(m_userPath);
  for (int i=0;i<totalPath.size()-1;i++) 
    retpath = retpath + totalPath[i].toStdString() + PATHSEP;
  if (totalPath.size() > 0) 
    retpath = retpath + totalPath[totalPath.size()-1].toStdString();
  return retpath;
}
  
void Interpreter::rescanPath() {
  if (!context) return;
  context->flushTemporaryGlobalFunctions();
  for (int i=0;i<m_basePath.size();i++)
    scanDirectory(m_basePath[i].toStdString(),false,"");
  for (int i=0;i<m_userPath.size();i++)
    scanDirectory(m_userPath[i].toStdString(),false,"");
  // Scan the current working directory.
  scanDirectory(QDir::currentPath().toStdString(),true,"");
  emit CWDChanged();
}
  

void Interpreter::setBasePath(QStringList pth) {
  m_basePath = pth;
}

void Interpreter::setUserPath(QStringList pth) {
  m_userPath = pth;
}
  
static QString mexExtension() {
#ifdef Q_OS_LINUX
  return "fmxglx";
#endif
#ifdef Q_OS_MACX
  return "fmxmac";
#endif
#ifdef Q_OS_WIN32
  return "fmxw32";
#endif
  return "fmx";
}
  
void Interpreter::scanDirectory(std::string scdir, bool tempfunc,
				std::string prefix) {
  QDir dir(QString::fromStdString(scdir));
  dir.setFilter(QDir::Files|QDir::Dirs|QDir::NoDotAndDotDot);
  dir.setNameFilters(QStringList() << "*.m" << "*.p" 
		     << "@*" << "private" << "*."+mexExtension());
  QFileInfoList list(dir.entryInfoList());
  for (unsigned i=0;i<list.size();i++) {
    QFileInfo fileInfo(list.at(i));
    std::string fileSuffix(fileInfo.suffix().toStdString());
    std::string fileBaseName(fileInfo.baseName().toStdString());
    std::string fileAbsoluteFilePath(fileInfo.absoluteFilePath().toStdString());
    if (fileSuffix == "m" || fileSuffix == "M") 
      if (prefix.empty())
	procFileM(fileBaseName,fileAbsoluteFilePath,tempfunc);
      else
	procFileM(prefix + ":" + fileBaseName,fileAbsoluteFilePath,tempfunc);
    else if (fileSuffix == "p" || fileSuffix == "P")
      if (prefix.empty())
	procFileP(fileBaseName,fileAbsoluteFilePath,tempfunc);
      else
	procFileP(prefix + ":" + fileBaseName,fileAbsoluteFilePath,tempfunc);
    else if (fileBaseName[0] == '@')
      scanDirectory(fileAbsoluteFilePath,tempfunc,fileBaseName);
    else if (fileBaseName == "private") 
      scanDirectory(fileAbsoluteFilePath,tempfunc,fileAbsoluteFilePath);
    else
      procFileMex(fileBaseName,fileAbsoluteFilePath,tempfunc);
  }
}
  
void Interpreter::procFileM(std::string fname, std::string fullname, bool tempfunc) {
  MFunctionDef *adef;
  adef = new MFunctionDef();
  adef->name = fname;
  adef->fileName = fullname;
  adef->temporaryFlag = tempfunc;
  context->insertFunction(adef, tempfunc);
}
  
void Interpreter::procFileP(std::string fname, std::string fullname, bool tempfunc) {
  MFunctionDef *adef;
  // Open the file
  try {
    File *f = new File(fullname.c_str(),"rb");
    Serialize *s = new Serialize(f);
    s->handshakeClient();
    s->checkSignature('p',2);
    adef = ThawMFunction(s);
    adef->pcodeFunction = true;
    delete f;
    context->insertFunction(adef, tempfunc);
  } catch (Exception &e) {
  }
}

void Interpreter::procFileMex(std::string fname, std::string fullname, bool tempfunc) {
  MexFunctionDef *adef;
  adef = new MexFunctionDef(fullname);
  adef->name = fname;
  if (adef->LoadSuccessful())
    context->insertFunction((MFunctionDef*)adef,tempfunc);
  else
    delete adef;
}

void Interpreter::RegisterGfxResults(ArrayVector m) {
  mutex.lock();
  gfx_buffer.push_back(m);
  gfxBufferNotEmpty.wakeAll();
  mutex.unlock();
}

void Interpreter::RegisterGfxError(string msg) {
  mutex.lock();
  gfxError = msg;
  gfxErrorOccured = true;
  gfxBufferNotEmpty.wakeAll();
  mutex.unlock();
}

ArrayVector Interpreter::doGraphicsFunction(FuncPtr f, ArrayVector m, int narg_out) {
  gfxErrorOccured = false;
  QMutexLocker lock(&mutex);
  emit doGraphicsCall(this,f,m,narg_out);
  if (!gfxErrorOccured && gfx_buffer.empty()) {
    gfxBufferNotEmpty.wait(&mutex);
  } else {
    qDebug() << "Wha??\r";
  }
  if (gfxErrorOccured) {
    throw Exception(gfxError);
  }
  if (gfx_buffer.empty())
    qDebug() << "Warning! graphics empty on return\r";
  ArrayVector ret(gfx_buffer.front());
  gfx_buffer.erase(gfx_buffer.begin());
  return ret;
}

void Interpreter::setTerminalWidth(int ncols) {
  mutex.lock();
  m_ncols = ncols;
  mutex.unlock();
}

int Interpreter::getTerminalWidth() {
  return m_ncols;
}

std::string TranslateString(std::string x) {
  std::string y(x);
  unsigned int n;
  n = 0;
  while (n<y.size()) {
    if (y[n] == '\n') 
      y.insert(n++,"\r");
    n++;
  }
  return y;
}

void Interpreter::diaryMessage(std::string msg) {
  ofstream diaryFile(m_diaryFilename.c_str(), ios::app);
  diaryFile << msg;
}


void Interpreter::outputMessage(std::string msg) {
  if (m_diaryState) diaryMessage(msg);
  if (m_captureState) 
    m_capture += msg;
  else
    if (m_quietlevel < 2)
      emit outputRawText(TranslateString(msg));
}

void Interpreter::outputMessage(const char* format,...) {
  char buffer[4096];
  va_list ap;
  va_start(ap,format);
  vsnprintf(buffer,4096,format,ap);
  va_end(ap);
  outputMessage(string(buffer));
}

void Interpreter::errorMessage(std::string msg) {
  if (m_diaryState) diaryMessage("Error: " + msg + "\n");
  if (m_captureState) 
    m_capture += "Error: " + msg + "\n";
  else
    if (m_quietlevel < 2)
      emit outputRawText(TranslateString("Error: " + msg + "\r\n"));
}

void Interpreter::warningMessage(std::string msg) {
  if (m_diaryState) diaryMessage("Warning: " + msg + "\n");
  if (m_captureState) 
    m_capture += "Warning: " + msg + "\n";
  else
    if (m_quietlevel < 2)
      emit outputRawText(TranslateString("Warning: " +msg + "\r\n"));
}

void Interpreter::SetContext(int a) {
//   qDebug() << "setting context to line " << (a & 0xffff);
  ip_context = a;
}

static bool isMFile(std::string arg) {
  // Not completely right...
  return (((arg[arg.size()-1] == 'm') ||
	   (arg[arg.size()-1] == 'p')) && 
	  (arg[arg.size()-2] == '.'));
}

std::string TrimFilename(std::string arg) {
  int ndx = arg.rfind(QString(QDir::separator()).toStdString());
  if (ndx>=0)
    arg.erase(0,ndx+1);
  return arg;
}

std::string TrimExtension(std::string arg) {
  if (arg.size() > 2 && arg[arg.size()-2] == '.')
    arg.erase(arg.size()-2,arg.size());
  return arg;
}

static std::string PrivateMangleName(std::string currentFunctionPath, std::string fname) {
  if (currentFunctionPath.empty()) return "";
  // First look to see if we are already a private function
  string separator("/");
  int ndx1 = currentFunctionPath.rfind(separator + "private" + separator);
  if (ndx1>=0) {
    // The current function is already in a private directory
    // In that case, try to find a private function in the same directory
    currentFunctionPath.erase(ndx1+1,currentFunctionPath.size());
    return currentFunctionPath + "private:" + fname;
  }
  int ndx;
  ndx = currentFunctionPath.rfind(separator);
  if (ndx>=0)
    currentFunctionPath.erase(ndx+1,currentFunctionPath.size());
  return currentFunctionPath + "private:" + fname;
}

static std::string LocalMangleName(std::string currentFunctionPath, std::string fname) {
  int ndx = currentFunctionPath.rfind("/");
  if (ndx >= 0)
    currentFunctionPath.erase(ndx,currentFunctionPath.size());
  std::string tmp = currentFunctionPath + "/" + fname;
  //  qDebug() << "Lookup " << QString::fromStdString(tmp);
  return currentFunctionPath + "/" + fname;
}

static std::string NestedMangleName(std::string cfunc, std::string fname) {
  return cfunc + "/" + fname;
}

std::string Interpreter::getVersionString() {
  return std::string("FreeMat v" VERSION);
}

// Run the thread function
void Interpreter::run() {
  if (m_threadFunc) {
    try {
      m_threadFuncRets = m_threadFunc->evaluateFunction(this,m_threadFuncArgs,m_threadNargout);
    } catch (Exception &e) {
      m_threadErrorState = true;      
      lasterr = e.getMessageCopy();
    } catch (InterpreterQuitException &e) {
      m_threadErrorState = true;      
      lasterr = "'quit' called in non-main thread";
    } catch (InterpreterKillException &e) {
      m_kill = false;
    } catch (InterpreterRetallException &e) {
    } catch (exception& e) {
      m_threadErrorState = true;      
      lasterr = "thread crashed!! - you have encountered a bug in FreeMat - please file bug report describing what happened";
    } catch (...) {
      m_threadErrorState = true;      
      lasterr = "thread crashed!! - you have encountered a bug in FreeMat - please file bug report describing what happened";
    }
  }
}

void Interpreter::doCLI() {
  emit CWDChanged();
  if (!m_skipflag)
    sendGreeting();
  try {
    while (1) {
      try {
	evalCLI();
      } catch (InterpreterRetallException) {
	clearStacks();
      } catch (InterpreterReturnException &e) {
      }
    }
  } catch (InterpreterQuitException &e) {
    emit QuitSignal();
  } catch (std::exception& e) {
    emit CrashedSignal();
  } catch (...) {
    emit CrashedSignal();
  }
}

void Interpreter::sendGreeting() {
  outputMessage(" " + getVersionString() + " (build 2820)\n");
  outputMessage(" Copyright (c) 2002-2007 by Samit Basu\n");
  outputMessage(" Licensed under the GNU Public License (GPL)\n");
  outputMessage(" Type <help license> to find out more\n");
  outputMessage("      <helpwin> for online help\n");
  outputMessage("      <pathtool> to set or change your path\n");
}

std::string Interpreter::getLocalMangledName(std::string fname) {
  std::string ret;
  if (isMFile(ip_funcname))
    ret = LocalMangleName(ip_detailname,fname);
  else
    ret = fname;
  return ret;
}

std::string Interpreter::getPrivateMangledName(std::string fname) {
  std::string ret;
  if (isMFile(ip_funcname)) 
    ret = PrivateMangleName(ip_funcname,fname);
  else {
    ret = QDir::currentPath().toStdString() + 
      QString(QDir::separator()).toStdString() + 
      std::string("private:" + fname);
  }
  return ret; 
}

std::string Interpreter::getMFileName() {
  if (isMFile(ip_funcname)) 
    return TrimFilename(TrimExtension(ip_funcname));
  for (int i=cstack.size()-1;i>=0;i--)
    if (isMFile(cstack[i].cname)) 
      return TrimFilename(TrimExtension(cstack[i].cname));
  return std::string("");
}

std::string Interpreter::getInstructionPointerFileName() {
  if (isMFile(ip_funcname)) 
    return ip_funcname;
  return string("");
}

stackentry::stackentry(std::string cntxt, std::string det, 
		       int id, int num, int strp, int stcl) :
  cname(cntxt), detail(det), tokid(id), number(num),
  steptrap(strp), stepcurrentline(stcl)
{
}

stackentry::stackentry() {
}

stackentry::~stackentry() {
}

Array Interpreter::DoBinaryOperator(const tree& t, BinaryFunc fnc, 
				    std::string funcname) {
  Array a(expression(t.first()));
  Array b(expression(t.second()));
  if (!(a.isUserClass() || b.isUserClass())) 
    return fnc(a,b,this);
  return ClassBinaryOperator(a,b,funcname,this);
}

Array Interpreter::DoUnaryOperator(const tree &t, UnaryFunc fnc, 
				   std::string funcname) {
  Array a(expression(t.first()));
  if (!a.isUserClass())
    return fnc(a,this);
  return ClassUnaryOperator(a,funcname,this);
}

void Interpreter::setPrintLimit(int lim) {
  printLimit = lim;
}
  
int Interpreter::getPrintLimit() {
  return(printLimit);
}
 
void Interpreter::clearStacks() {
  //    cname = "base";
  cstack.clear();
  ip_funcname = "base";
  ip_detailname = "base";
  ip_context = 0;
}

//!
//@Module MATRIX Matrix Definitions
//@@Section VARIABLES
//@@Usage
//The matrix is the basic datatype of FreeMat.  Matrices can be
//defined using the following syntax
//@[
//  A = [row_def1;row_def2;...,row_defN]
//@]
//where each row consists of one or more elements, seperated by
//commas
//@[
//  row_defi = element_i1,element_i2,...,element_iM
//@]
//Each element can either be a scalar value or another matrix,
//provided that the resulting matrix definition makes sense.
//In general this means that all of the elements belonging
//to a row have the same number of rows themselves, and that
//all of the row definitions have the same number of columns.
//Matrices are actually special cases of N-dimensional arrays
//where @|N<=2|.  Higher dimensional arrays cannot be constructed
//using the bracket notation described above.  The type of a
//matrix defined in this way (using the bracket notation) is
//determined by examining the types of the elements.  The resulting
//type is chosen so no information is lost on any of the elements
//(or equivalently, by choosing the highest order type from those
//present in the elements).
//@@Examples
//Here is an example of a matrix of @|int32| elements (note that
//untyped integer constants default to type @|int32|).
//@<
//A = [1,2;5,8]
//@>
//Now we define a new matrix by adding a column to the right of
//@|A|, and using float constants.
//@<
//B = [A,[3.2f;5.1f]]
//@>
//Next, we add extend @|B| by adding a row at the bottom.  Note
//how the use of an untyped floating point constant forces the
//result to be of type @|double|
//@<
//C = [B;5.2,1.0,0.0]
//@>
//If we instead add a row of @|complex| values (recall that @|i| is
//a @|complex| constant, not a @|dcomplex| constant)
//@<
//D = [B;2.0f+3.0f*i,i,0.0f]
//@>
//Likewise, but using @|dcomplex| constants
//@<
//E = [B;2.0+3.0*i,i,0.0]
//@>
//Finally, in FreeMat, you can construct matrices with strings
//as contents, but you have to make sure that if the matrix has
//more than one row, that all the strings have the same length.
//@<
//F = ['hello';'there']
//@>
//@@Tests
//@{ test_matcat1.m
//% Check the matcat function
//function test_val = test_matcat1
//a = [1;2];
//b = 3;
//d = 4;
//c = [a,[b;d]];
//test_val = test(c==[1,3;2,4]);
//@}
//@{ test_matcat2.m
//% Check the matcat function with n-dimensional arrays
//function test_val = test_matcat2
//a = 1;
//a(1,1,1) = 1;
//a(1,2,1) = 2;
//a(1,1,2) = 5;
//a(1,2,2) = 6;
//c = [a;a];
//c1 = c(:,:,1);
//c2 = c(:,:,2);
//test_val = test(c1==[1,2;1,2]) & test(c2==[5,6;5,6]);
//@}
//@{ test_matcat3.m
//% Check the matcat function with n-dimensional arrays
//function test_val = test_matcat3
//a = 1;
//a(1,1,1,1,1) = 1;
//a(1,2,1,1,1) = 2;
//a(1,1,1,1,2) = 5;
//a(1,2,1,1,2) = 6;
//c = [a;a];
//c1 = c(:,:,1,1,1);
//c2 = c(:,:,1,1,2);
//test_val = test(c1==[1,2;1,2]) & test(c2==[5,6;5,6]);
//@}
//@{ test_matcat4.m
//% Check the type promotion for the matrix cat function
//function test_val = test_matcat4
//a = [1,2;3.0f,4f+i];
//test_val = test(strcmp(typeof(a),'complex'));
//@}
//@{ test_matcat5.m
//% Check the type promotion for the matrix cat function
//function test_val = test_matcat5
//a = [1,2;3.0f,4.0+i];
//test_val = test(strcmp(typeof(a),'dcomplex'));
//@}
//@{ test_matcat6.m
//% Check the type promotion for the matrix cat function
//function test_val = test_matcat6
//a = [1,2;3.0f,4.0f+i];
//test_val = test(strcmp(typeof(a),'complex'));
//@}
//@{ test_matcat7.m
//% Check that matrix cat works properly with spaces before the continuation
//function test_val = test_matcat7
//a = [1;2;...   
//     3;4; ...
//     5;6];
//b = [1, 2, 3, ...
//     4, 5, 6 ];
//c=[1, 2, ...
//              3];
//test_val = 1;
//@}
//@{ test_vec1.m
//% Check the case of continuation with vectors
//function test_val = test_vec1
//a = [1 2 3 4...
//       5 6 7 8];
//test_val = 1;
//@}
//!
//Works
Array Interpreter::matrixDefinition(const tree &t) {
  ArrayMatrix m;
  if (t.numchildren() == 0) 
    return Array::emptyConstructor();
  for (int i=0;i<t.numchildren();i++) {
    const tree &s(t.child(i));
    ArrayVector n;
    for (int j=0;j<s.numchildren();j++)
      multiexpr(s.child(j),n);
    m.push_back(n);
  }
  // Check if any of the elements are user defined classes
  bool anyuser = false;
  for (int i=0;i<m.size() && !anyuser;i++)
    for (int j=0;j<m[i].size() && !anyuser;j++)
      if (m[i][j].isUserClass()) 
	anyuser = true;
  if (!anyuser)
    return Array::matrixConstructor(m);
  else
    return ClassMatrixConstructor(m,this);
}

//!
//@Module CELL Cell Array Definitions
//@@Section VARIABLES
//@@Usage
//The cell array is a fairly powerful array type that is available
//in FreeMat.  Generally speaking, a cell array is a heterogenous
//array type, meaning that different elements in the array can 
//contain variables of different type (including other cell arrays).
//For those of you familiar with @|C|, it is the equivalent to the
//@|void *| array.  The general syntax for their construction is
//@[
//   A = {row_def1;row_def2;...;row_defN}
//@]
//where each row consists of one or more elements, seperated by
//commas
//@[
//  row_defi = element_i1,element_i2,...,element_iM
//@]
//Each element can be any type of FreeMat variable, including
//matrices, arrays, cell-arrays, structures, strings, etc.  The
//restriction on the definition is that each row must have the
//same number of elements in it.
//@@Examples
//Here is an example of a cell-array that contains a number,
//a string, and an array
//@<
//A = {14,'hello',[1:10]}
//@>
//Note that in the output, the number and string are explicitly
//printed, but the array is summarized.
//We can create a 2-dimensional cell-array by adding another
//row definition
//@<
//B = {pi,i;e,-1}
//@>
//Finally, we create a new cell array by placing @|A| and @|B|
//together
//@<
//C = {A,B}
//@>
//!
//Works
Array Interpreter::cellDefinition(const tree & t) {
  ArrayMatrix m;
  if (t.numchildren() == 0) {
    Array a(Array::emptyConstructor());
    a.promoteType(FM_CELL_ARRAY);
    return a;
  }
  for (int i=0;i<t.numchildren();i++) {
    const tree &s(t.child(i));
    ArrayVector n;
    for (int j=0;j<s.numchildren();j++) 
      multiexpr(s.child(j),n);
    m.push_back(n);
  }
  return Array::cellConstructor(m);
}

Array Interpreter::ShortCutOr(const tree & t) {
  Array a(expression(t.first()));
  Array retval;
  if (!a.isScalar())
    retval = DoBinaryOperator(t,Or,"or");
  else {
    // A is a scalar - is it true?
    a.promoteType(FM_LOGICAL);
    if (*((const logical*)a.getDataPointer()))
      retval = a;
    else 
      retval = DoBinaryOperator(t,Or,"or");
  }
  return retval;
}

Array Interpreter::ShortCutAnd(const tree &t) {
  SetContext(t.context());
  Array a(expression(t.first()));
  SetContext(t.context());
  Array retval;
  if (!a.isScalar()) {
    retval = DoBinaryOperator(t,And,"and");
  } else {
    // A is a scalar - is it false?
    a.promoteType(FM_LOGICAL);
    if (!*((const logical*)a.getDataPointer()))
      retval = a;
    else 
      retval = DoBinaryOperator(t,And,"and");
  }
  return retval;
}

//Works
// Need to take care

ArrayVector Interpreter::handleReindexing(const tree &t, const ArrayVector &p) {
  if (t.numchildren() > 2)
    if (p.size() > 1)
      throw Exception("reindexing of function expressions not allowed when multiple values are returned by the function");
    else {
      Array r;
      if (p.size() == 1)
	r = p[0];
      else
	r = Array::emptyConstructor();
      for (unsigned index = 2;index < t.numchildren();index++) 
	deref(r,t.child(index));
      return ArrayVector() << r;
    }
  else
    return p;
}

void Interpreter::multiexpr(const tree &t, ArrayVector &q, int lhsCount) {
  if (t.is(TOK_VARIABLE)) {
    ArrayReference ptr(context->lookupVariable(t.first().text()));
    if (!ptr.valid()) {
      ArrayVector p;
      functionExpression(t,lhsCount,false,p);
      q += handleReindexing(t,p);
      return;
    }
    if ((ptr->dataClass() == FM_FUNCPTR_ARRAY &&
	 ptr->isScalar()) && (t.numchildren() > 1)) {
      ArrayVector p = FunctionPointerDispatch(*ptr,t.second(),1);
      q += handleReindexing(t,p);
      return;
    }
    if (t.numchildren() == 1) {
      q.push_back(*ptr);
      return;
    }
    if (ptr->isUserClass() && !stopoverload && !inMethodCall(ptr->className().back())) {
      q += ClassRHSExpression(*ptr,t,this);
      return;
    }
    Array r(*ptr);
    for (unsigned index = 1;index < t.numchildren()-1;index++) 
      deref(r,t.child(index));
    SaveEndInfo;
    endRef = &r;
    const tree &s(t.last());
    if (s.is(TOK_PARENS)) {
      ArrayVector m;
      endTotal = s.numchildren();
      if (s.numchildren() == 0)
	q.push_back(r);
      else {
	for (unsigned p = 0;p < s.numchildren(); p++) {
	  endCount = m.size();
	  multiexpr(s.child(p),m);
	}
	subsindex(m);
	if (m.size() == 1)
	  q.push_back(r.getVectorSubset(m.front(),this));
	else
	  q.push_back(r.getNDimSubset(m,this));
      }
    } else if (s.is(TOK_BRACES)) {
      ArrayVector m;
      endTotal = s.numchildren();
      for (unsigned p = 0;p < s.numchildren(); p++) {
	endCount = m.size();
	multiexpr(s.child(p),m);
      }
      subsindex(m);
      if (m.size() == 1)
	q += r.getVectorContentsAsList(m.front(),this);
      else
	q += r.getNDimContentsAsList(m,this);
    } else if (s.is('.')) {
      q += r.getFieldAsList(s.first().text());
    } else if (s.is(TOK_DYN)) {
      string field;
      try {
	Array fname(expression(s.first()));
	field = fname.getContentsAsString();
      } catch (Exception &e) {
	throw Exception("dynamic field reference to structure requires a string argument");
      }
      q += r.getFieldAsList(field);
    }
    RestoreEndInfo;
  } else if (!t.is(TOK_KEYWORD))
    q.push_back(expression(t));
}

Array Interpreter::expression(const tree &t) {
  switch(t.token()) {
  case TOK_VARIABLE: 
    return rhs(t);
  case TOK_INTEGER:
  case TOK_FLOAT:
  case TOK_DOUBLE:
  case TOK_COMPLEX:
  case TOK_DCOMPLEX:
  case TOK_STRING:
    return t.array();
  case TOK_END:
    if (!endRef.valid()) 
      throw Exception("END keyword not allowed for undefined variables");
    if (endTotal == 1)
      return Array::int32Constructor(endRef->getLength());
    else
      return Array::int32Constructor(endRef->getDimensionLength(endCount));
  case ':':
    if (t.numchildren() == 0) {
      return Array::stringConstructor(":");
    } else if (t.first().is(':')) {
      return doubleColon(t);
    } else {
      return unitColon(t);
    }
    break;
  case TOK_MATDEF: 
    return matrixDefinition(t); 
    break;
  case TOK_CELLDEF: 
    return cellDefinition(t); 
    break;
  case '+': 
    return DoBinaryOperator(t,Add,"plus"); 
    break;
  case '-': 
    return DoBinaryOperator(t,Subtract,"minus"); 
    break;
  case '*': 
    return DoBinaryOperator(t,Multiply,"mtimes"); 
    break;
  case '/': 
    return DoBinaryOperator(t,RightDivide,"mrdivide");
    break;
  case '\\': 
    return DoBinaryOperator(t,LeftDivide,"mldivide"); 
    break;
  case TOK_SOR: 
  case '|': 
    return ShortCutOr(t); 
    break;
  case TOK_SAND: 
  case '&': 
    return ShortCutAnd(t); 
    break;
  case '<': 
    return DoBinaryOperator(t,LessThan,"lt"); 
    break;
  case TOK_LE: 
    return DoBinaryOperator(t,LessEquals,"le"); 
    break;
  case '>': 
    return DoBinaryOperator(t,GreaterThan,"gt"); 
    break;
  case TOK_GE: 
    return DoBinaryOperator(t,GreaterEquals,"ge"); 
    break;
  case TOK_EQ: 
    return DoBinaryOperator(t,Equals,"eq"); 
    break;
  case TOK_NE: 
    return DoBinaryOperator(t,NotEquals,"ne"); 
    break;
  case TOK_DOTTIMES: 
    return DoBinaryOperator(t,DotMultiply,"times"); 
    break;
  case TOK_DOTRDIV: 
    return DoBinaryOperator(t,DotRightDivide,"rdivide"); 
    break;
  case TOK_DOTLDIV: 
    return DoBinaryOperator(t,DotLeftDivide,"ldivide"); 
    break;
  case TOK_UNARY_MINUS: 
    return DoUnaryOperator(t,Negate,"uminus"); 
    break;
  case TOK_UNARY_PLUS: 
    return DoUnaryOperator(t,Plus,"uplus"); 
    break;
  case '~': 
    return DoUnaryOperator(t,Not,"not"); 
    break;
  case '^': 
    return DoBinaryOperator(t,Power,"mpower"); 
    break;
  case TOK_DOTPOWER: 
    return DoBinaryOperator(t,DotPower,"power"); 
    break;
  case '\'': 
    return DoUnaryOperator(t,Transpose,"ctranspose"); 
    break;
  case TOK_DOTTRANSPOSE: 
    return DoUnaryOperator(t,DotTranspose,"transpose"); 
    break;
  case '@':
    return FunctionPointer(t);
  default:
    throw Exception("Unrecognized expression!");
  }
}

Array Interpreter::FunctionPointer(const tree &t) {
  if (t.first().is(TOK_ANONYMOUS_FUNC)) {
    AnonymousFunctionDef *q = new AnonymousFunctionDef;
    q->initialize(t.first(),this);
    return Array::funcPtrConstructor(FuncPtr(q));
  } else {
    FuncPtr val;
    if (!lookupFunction(t.first().text(),val))
      throw Exception("unable to resolve " + t.first().text() + 
		      " to a function call");
    return Array::funcPtrConstructor(val);
  }
}

//!
//@Module COLON Index Generation Operator
//@@Section OPERATORS
//@@Usage
//There are two distinct syntaxes for the colon @|:| operator - the two argument form
//@[
//  y = a : c
//@]
//and the three argument form
//@[
//  y = a : b : c
//@]
//The two argument form is exactly equivalent to @|a:1:c|.  The output @|y| is the vector
//\[
//  y = [a,a+b,a+2b,\ldots,a+nb]
//\]
//where @|a+nb <= c|.  There is a third form of the colon operator, the 
//no-argument form used in indexing (see @|indexing| for more details).
//@@Function Internals
//The colon operator turns out to be trickier to implement than one might 
//believe at first, primarily because the floating point versions should
//do the right thing, which is not the obvious behavior.  For example,
//suppose the user issues a three point colon command
//@[
//   y = a : b : c
//@]
//The first question that one might need to answer is: how many points
//in this vector?  If you answered
//\[
//   n = \frac{c-a}{b}+1
//\]
//then you would be doing the straighforward, but not correct thing.
//because a, b, and c are all floating point values, there are errors
//associated with each of the quantities that can lead to n not being
//an integer.  A better way (and the way FreeMat currently does the
//calculation) is to compute the bounding values (for b positive)
//\[
//   n \in \left[\frac{(c-a) \rightarrow 0}{b \rightarrow \infty},
//               \frac{(c-a) \rightarrow \infty}{b \rightarrow 0} \right] + 1
//\]
//where 
//\[
//  x \rightarrow y 
//\]
//means we replace x by the floating point number that is closest to it
//in the direction of y.  Once we have determined the number of points
//we have to compute the intermediate values
//\[
//  [a, a+b, a+2*b, \ldots, a+n*b]
//\]
//but one can readily verify for themselves that this may \emph{not} be
//the same as the vector
//\[
//  \mathrm{fliplr} [c, c-b, c-2*b, \ldots, c-n*b]
//\]
//even for the case where
//\[
//   c = a + n*b
//\]
//for some n.  The reason is that the roundoff in the calculations may
//be different depending on the nature of the sum.  FreeMat uses the
//following strategy to compute the double-colon vector:
//\begin{enumerate}
//\item The value @|n| is computed by taking the floor of the larger 
// value in the interval defined above.
//\item If @|n| falls inside the interval defined above, then it is 
//assumed that the user intended @|c = a + n*b|, and the symmetric 
//algorithm is used.  Otherwise, the nonsymmetric algorithm is used.
//\item The symmetric algorithm computes the vector via
//\[
//  [a, a+b, a+2b,\ldots,c-2b,c-b,c]
//\]
//working symmetrically from both ends of the vector 
//(hence the nomenclature), while the nonsymmetric algorithm computes
//\[
//  [a, a+b ,a+2b,\ldots,a+nb]
//\]
//In practice, the entries are computed by repeated accumulation instead 
//of multiplying the step size by an integer.
//\item The real interval calculation is modified so that we get the
//exact same result with @|a:b:c| and @|c:-b:a| (which basically means
//that instead of moving towards infinity, we move towards the signed 
//infinity where the sign is inherited from @|b|).
//\end{enumerate}
//If you think this is all very obscure, it is.  But without it, you will
//be confronted by mysterious vectors where the last entry is dropped,
//or where the values show progressively larger amounts of accumulated
//roundoff error.
//@@Examples
//Some simple examples of index generation.
//@<
//y = 1:4
//@>
//Now by half-steps:
//@<
//y = 1:.5:4
//@>
//Now going backwards (negative steps)
//@<
//y = 4:-.5:1
//@>
//If the endpoints are the same, one point is generated, regardless of the step size (middle argument)
//@<
//y = 4:1:4
//@>
//If the endpoints define an empty interval, the output is an empty matrix:
//@<
//y = 5:4
//@>
//@@Tests
//@{ test_range1.m
//% Check the range function for integer types positive with 3 args & horizontal concatenation
//function test_val = test_range1
//range = 0:1:3;
//range2 = [0,1,2,3];
//test_val = test(range == range2);
//@}
//@{ test_range2.m
//% Check the range function for integer types positive with 3 args & horizontal concatenation
//function test_val = test_range2
//range = (0:1:3)';
//range2 = [0;1;2;3];
//test_val = test(range == range2);
//@}
//@{ test_range3.m
//% Check the range function for float types with 2 args & horizontal concatenation
//function test_val = test_range3
//range = 0.2f:2.2f;
//range2 = [0.2f,1.2f,2.2f];
//test_val = test(range == range2);
//@}
//@{ test_range4.m
//% Check the range function for double types with 3 args & vertical concatenation
//function test_val = test_range4
//range = 0.45:3.45;
//range2 = [0.45,1.45,2.45,3.45];
//test_val = test(range == range2);
//@}
//@{ test_range5.m
//% Check the range function with negative integer
//function test_val = test_range5
//range = -2:2;
//range2 = [-2,-1,0,1,2];
//test_val = test(range == range2);
//@}
//@{ test_range6.m
//% Check the range function with a negative float
//function test_val = test_range6
//range = -2.f:3.f;
//range2 = [-2.f,-1.f,0.f,1.f,2.f,3.f];
//test_val = test(range == range2);
//@}
//@{ test_range7.m
//% Check the range function with a negative step size
//function test_val = test_range7
//range = 3:-1:-2;
//range2 = [3,2,1,0,-1,-2];
//test_val = test(range == range2);
//@}
//@{ test_range8.m
//% Check the range function with a negative step size
//function test_val = test_range8
//range = 3f:-1f:-2f;
//range2 = [3f,2f,1f,0f,-1f,-2f];
//test_val = test(range == range2);
//@}
//@{ test_range9.m
//% Check the range function with an empty range
//function test_val = test_range9
//range = 3:2;
//test_val = isempty(range);
//@}
//!
//Works
Array Interpreter::unitColon(const tree &t) {
  Array a, b;
  a = expression(t.first());
  b = expression(t.second());
  if (!(a.isUserClass() || b.isUserClass()))
    return UnitColon(a,b);
  else
    return ClassBinaryOperator(a,b,"colon",this);
}

//Works
Array Interpreter::doubleColon(const tree &t) {
  Array a, b, c;
  a = expression(t.first().first());
  b = expression(t.first().second());
  c = expression(t.second());
  if (!(a.isUserClass() || b.isUserClass() || c.isUserClass()))
    return DoubleColon(a,b,c);
  else
    return ClassTrinaryOperator(a,b,c,"colon",this);
}

/**
 * This somewhat strange test is used by the switch statement.
 * If x is a scalar, and we are a scalar, this is an equality
 * test.  If x is a string and we are a string, this is a
 * strcmp test.  If x is a scalar and we are a cell-array, this
 * test is applied on an element-by-element basis, looking for
 * any matches.  If x is a string and we are a cell-array, then
 * this is applied on an element-by-element basis also.
 */
bool Interpreter::testCaseStatement(const tree &t, Array s) {
  int ctxt = t.context();
  Array r(expression(t.first()));
  bool caseMatched = s.testForCaseMatch(r);
  if (caseMatched)
    block(t.second());
  return caseMatched;
}

//!
//@Module TRY-CATCH Try and Catch Statement
//@@Section FLOW
//@@Usage
//The @|try| and @|catch| statements are used for error handling
//and control.  A concept present in @|C++|, the @|try| and @|catch|
//statements are used with two statement blocks as follows
//@[
//   try
//     statements_1
//   catch
//     statements_2
//   end
//@]
//The meaning of this construction is: try to execute @|statements_1|,
//and if any errors occur during the execution, then execute the
//code in @|statements_2|.  An error can either be a FreeMat generated
//error (such as a syntax error in the use of a built in function), or
//an error raised with the @|error| command.
//@@Examples
//Here is an example of a function that uses error control via @|try|
//and @|catch| to check for failures in @|fopen|.
//@{ read_file.m
//function c = read_file(filename)
//try
//   fp = fopen(filename,'r');
//   c = fgetline(fp);
//   fclose(fp);
//catch
//   c = ['could not open file because of error :' lasterr]
//end
//@}
//Now we try it on an example file - first one that does not exist,
//and then on one that we create (so that we know it exists).
//@<
//read_file('this_filename_is_invalid')
//fp = fopen('test_text.txt','w');
//fprintf(fp,'a line of text\n');
//fclose(fp);
//read_file('test_text.txt')
//@>
//!
//Works
void Interpreter::tryStatement(const tree &t) {
  // Turn off autostop for this statement block
  bool autostop_save = autostop;
  autostop = false;
  // Get the state of the IDnum stack and the
  // contextStack and the cnameStack
  int stackdepth;
  stackdepth = cstack.size();
  try {
    block(t.first());
  } catch (Exception &e) {
    while (cstack.size() > stackdepth) popDebug();
    if (t.numchildren()>1) {
      autostop = autostop_save;
      block(t.second().first());
    }
  } 
  autostop = autostop_save;
}


//!
//@Module SWITCH Switch statement
//@@Section FLOW
//@@Usage
//The @|switch| statement is used to selective execute code
//based on the value of either scalar value or a string.
//The general syntax for a @|switch| statement is
//@[
//  switch(expression)
//    case test_expression_1
//      statements
//    case test_expression_2
//      statements
//    otherwise
//      statements
//  end
//@]
//The @|otherwise| clause is optional.  Note that each test
//expression can either be a scalar value, a string to test
//against (if the switch expression is a string), or a 
//@|cell-array| of expressions to test against.  Note that
//unlike @|C| @|switch| statements, the FreeMat @|switch|
//does not have fall-through, meaning that the statements
//associated with the first matching case are executed, and
//then the @|switch| ends.  Also, if the @|switch| expression
//matches multiple @|case| expressions, only the first one
//is executed.
//@@Examples
//Here is an example of a @|switch| expression that tests
//against a string input:
//@{ switch_test.m
//function c = switch_test(a)
//  switch(a)
//    case {'lima beans','root beer'}
//      c = 'food';
//    case {'red','green','blue'}
//      c = 'color';
//    otherwise
//      c = 'not sure';
//  end
//@}
//Now we exercise the switch statements
//@<
//switch_test('root beer')
//switch_test('red')
//switch_test('carpet')
//@>
//@@Tests
//@{ test_switch1.m
//% Test the switch statement with a string argument
//function test_val = test_switch1
//      
//test_val = 0;
//x = 'astring';
//
//switch x
//case 'bstring'
//test_val = 0;
//case 'astring';
//test_val = 1;
//case 'cstring';
//test_val = 0;
//otherwise
//test_val = 0;
//end
//@}
//@{ test_switch2.m
//% Test the switch statement with a string argument and one integer case
//% The type mismatch should not present a problem.
//function test_val = test_switch2
//      
//test_val = 0;
//x = 'astring';
//
//switch x
//case 30
//test_val = 0;
//case 'astring';
//test_val = 1;
//case 'cstring';
//test_val = 0;
//otherwise
//test_val = 0;
//end
//@}
//@{ test_switch3.m
//% Test the switch statement with a scalar argument
//function test_val = test_switch3
//
//test_val = 0;
//x = 1.0f + i;
//z = 1.0 + i;
//switch x
//case 1
//test_val = 0;
//case i
//test_val = 0;
//case z
//test_val = 1;
//case 'astring'
//test_val = 0;
//otherwise
//test_val = 0;
//end
//@}
//@{ test_switch4.m
//% Test the switch statement with a vector argument (should be an error)
//function test_val = test_switch4
//
//x = [1 1];
//test_val = 0;
//try
//switch x
//case 1
//test_val = 0;
//otherwise
//test_val = 0;
//end
//catch
//test_val = 1;
//end
//@}
//@{ test_switch5.m
//% Test the switch statement with a numerical argument
//function test_val = test_switch5
//
//x = 1.2f;
//test_val = 0;
//switch x
//case 1
//test_val = 0;
//case 1.1f
//test_val = 0;
//case 1.2+i;
//test_val = 0;
//case 1.2;
//test_val = 0;
//case 1.2f;
//test_val = 1;
//end
//@}
//@{ test_switch6.m
//% Test the switch statement with a string argument and a cell array
//function test_val = test_switch6
//      
//test_val = 0;
//x = 'astring';
//
//switch x
//case 'bstring'
//test_val = 0;
//case {'dstring',5,'astring'};
//test_val = 1;
//case 'cstring';
//test_val = 0;
//otherwise
//test_val = 0;
//end
//@}
//@{ test_switch7.m
//% Test the switch statement with a numerical argument and a cell array
//function test_val = test_switch7
//      
//test_val = 0;
//x = 5;
//
//switch x
//case 'bstring'
//test_val = 0;
//case {'dstring',5.0,'astring'};
//test_val = 1;
//case 'cstring';
//test_val = 0;
//otherwise
//test_val = 0;
//end
//@}
//@{ test_switch8.m
//% Test the switch statement with a blank line after the switch clause
//function test_val = test_switch8
//   v = '3';
//
//   switch(v)
//
//   case '3'
//     vv = 111;
//   end
//   test_val = 1;
//@}
//!
//Works
void Interpreter::switchStatement(const tree &t) {
  Array switchVal;
  int ctxt = t.context();
  // First, extract the value to perform the switch on.
  switchVal = expression(t.first());
  // Assess its type to determine if this is a scalar switch
  // or a string switch.
  if (!switchVal.isScalar() && !switchVal.isString())
    throw Exception("Switch statements support scalar and string arguments only.");
  unsigned n=1;
  while (n < t.numchildren() && t.child(n).is(TOK_CASE)) {
    if (testCaseStatement(t.child(n),switchVal))
      return;
    n++;
  }
  if (t.last().is(TOK_OTHERWISE))
    block(t.last().first());
}

//!
//@Module IF-ELSEIF-ELSE Conditional Statements
//@@Section FLOW
//@@Usage
//The @|if| and @|else| statements form a control structure for
//conditional execution.  The general syntax involves an @|if|
//test, followed by zero or more @|elseif| clauses, and finally
//an optional @|else| clause:
//@[
//  if conditional_expression_1
//    statements_1
//  elseif conditional_expression_2
//    statements_2
//  elseif conditional_expresiion_3
//    statements_3
//  ...
//  else
//    statements_N
//  end
//@]
//Note that a conditional expression is considered true if 
//the real part of the result of the expression contains
//any non-zero elements (this strange convention is adopted
//for compatibility with MATLAB).
//@@Examples
//Here is an example of a function that uses an @|if| statement
//@{ if_test.m
//function c = if_test(a)
//  if (a == 1)
//     c = 'one';
//  elseif (a==2)
//     c = 'two';
//  elseif (a==3)
//     c = 'three';
//  else
//     c = 'something else';
//  end
//@}
//Some examples of @|if_test| in action:
//@<
//if_test(1)
//if_test(2)
//if_test(3)
//if_test(pi)
//@>
//@@Tests
//@{ test_if1.m
//% Test the if statement
//function test_val = test_if1
//a = 1;
//test_val = 0;
//if (a == 1)
//  test_val = 1;
//end
//@}
//@{ test_if2.m
//% Test the if-else statement
//function test_val = test_if2
//a = 1;
//test_val = 0;
//if (a == 0)
//  test_val = 0;
//else
//  test_val = 1;
//end
//@}
//@{ test_if3.m
//% Test the if-elseif-else statement
//function test_val = test_if3
//a = 1;
//test_val = 0;
//if (a == 0)
//  test_val = 0;
//elseif (a == 3);
//  test_val = 0;
//elseif (a == 1);
//  test_val = 1;
//else
//  test_val = 0;
//end
//@}
//!
//Works
void Interpreter::ifStatement(const tree &t) {
  bool elseifMatched;
  int ctxt = t.context();
    
  bool condtest = !(expression(t.first()).isRealAllZeros());
  if (condtest) {
    block(t.second());
    return;
  } else {
    unsigned n=2;
    while (n < t.numchildren() && t.child(n).is(TOK_ELSEIF)) {
      if (!(expression(t.child(n).first()).isRealAllZeros())) {
	block(t.child(n).second());
	return;
      }
      n++;
    }
  }
  if (t.last().is(TOK_ELSE))
    block(t.last().first());
}

//!
//@Module WHILE While Loop
//@@Section FLOW
//@@Usage
//The @|while| loop executes a set of statements as long as
//a the test condition remains @|true|.  The syntax of a 
//@|while| loop is
//@[
//  while test_expression
//     statements
//  end
//@]
//Note that a conditional expression is considered true if 
//the real part of the result of the expression contains
//any non-zero elements (this strange convention is adopted
//for compatibility with MATLAB).
//@@Examples
//Here is a @|while| loop that adds the integers from @|1|
//to @|100|:
//@<
//accum = 0;
//k=1;
//while (k<100), accum = accum + k; k = k + 1; end
//accum
//@>
//@@Tests
//@{ test_while1.m
//% Test the while statement for normal operation
//function test_val = test_while1
//sum = 0;
//i = 1;
//while (i<=10)
//  sum = sum + i;
//  i = i + 1;
//end
//test_val = test(sum == 55);
//@}
//@{ test_while2.m
//% Test a simple 'while' statement with a continue clause
//function test_val = test_while2
//sum = 0;
//i = 0;
//while (i < 10)
//  i = i + 1;
//  sum = sum + i;
//  if (i==5), continue; end
//  sum = sum - 1;
//end
//test_val = test(sum == 46);
//@}
//@{ test_while3.m
//% Test a simple 'while' statement with a continue clause
//function test_val = test_while3
//sum = 0;
//i = 1;
//while (i < 10)
//  sum = sum + i;
//  if (i==5), break; end
//  i = i + 1;
//end
//test_val = test(sum == 15);
//@}
//@{ testeq.m
//function x = testeq(a,b)
//  if (size(a,1) ~= size(b,1) || size(a,2) ~= size(b,2))
//    x = 0;
//    return;
//  end
//  d = full(a)-full(b);
//  if (strcmp(typeof(d),'double') | strcmp(typeof(d),'dcomplex'))
//    x = isempty(find(abs(d)>10*eps));
//  else
//    x = isempty(find(abs(d)>10*feps));
//  end
//@}
//!
//Works
void Interpreter::whileStatement(const tree &t) {
  int ctxt = t.context();
  tree testCondition(t.first());
  tree codeBlock(t.second());
  bool breakEncountered = false;
  Array condVar(expression(testCondition));
  bool conditionTrue = !condVar.isRealAllZeros();
  context->enterLoop();
  breakEncountered = false;
  while (conditionTrue && !breakEncountered) {
    try {
      block(codeBlock);
    } catch (InterpreterContinueException& e) {
    } catch (InterpreterBreakException& e) {
      breakEncountered = true;
    } catch (InterpreterReturnException& e) {
      context->exitLoop();
      throw;
    } catch (InterpreterRetallException& e) {
      context->exitLoop();
      throw;
    }
    if (!breakEncountered) {
      condVar = expression(testCondition);
      conditionTrue = !condVar.isRealAllZeros();
    } 
  }
  context->exitLoop();
}

//Helper functions for FOR loops.  This template function
//handles the index variable with the correct type.  Reducing
//the net loop time

class ContextLoopLocker {
  Context* m_context;
public:
  ContextLoopLocker(Context* a): m_context(a) {m_context->enterLoop();}
  ~ContextLoopLocker() {m_context->exitLoop();}
};

template <class T>
void ForLoopHelper(const tree &codeBlock, Class indexClass, const T *indexSet, 
		   int count, string indexName, Interpreter *eval) {
  for (int m=0;m<count;m++) {
    Array *vp = eval->getContext()->lookupVariableLocally(indexName);
    if ((!vp) || (vp->dataClass() != indexClass) || (!vp->isScalar())) {
      eval->getContext()->insertVariableLocally(indexName,Array(indexClass,Dimensions(1,1)));
      vp = eval->getContext()->lookupVariableLocally(indexName);
    }
    ((T*) vp->getReadWriteDataPointer())[0] = indexSet[m];
    try {
      eval->block(codeBlock);
    } catch (InterpreterContinueException &e) {
    } catch (InterpreterBreakException &e) {
      break;
    } 
  }
}

template <class T>
void ForLoopHelperComplex(const tree &codeBlock, Class indexClass, 
			  const T *indexSet, int count, 
			  string indexName, Interpreter *eval) {
  for (int m=0;m<count;m++) {
    Array *vp = eval->getContext()->lookupVariableLocally(indexName);
    if ((!vp) || (vp->dataClass() != indexClass) || (!vp->isScalar())) {
      eval->getContext()->insertVariableLocally(indexName,Array(indexClass,Dimensions(1,1)));
      vp = eval->getContext()->lookupVariableLocally(indexName);
    }
    ((T*) vp->getReadWriteDataPointer())[0] = indexSet[2*m];
    ((T*) vp->getReadWriteDataPointer())[1] = indexSet[2*m+1];
    try {
      eval->block(codeBlock);
    } catch (InterpreterContinueException &e) {
    } catch (InterpreterBreakException &e) {
      break;
    } 
  }
}

//!
//@Module FOR For Loop
//@@Section FLOW
//@@Usage
//The @|for| loop executes a set of statements with an 
//index variable looping through each element in a vector.
//The syntax of a @|for| loop is one of the following:
//@[
//  for (variable=expression)
//     statements
//  end
//@]
//Alternately, the parenthesis can be eliminated
//@[
//  for variable=expression
//     statements
//  end
//@]
//or alternately, the index variable can be pre-initialized
//with the vector of values it is going to take:
//@[
//  for variable
//     statements
//  end
//@]
//The third form is essentially equivalent to @|for variable=variable|,
//where @|variable| is both the index variable and the set of values
//over which the for loop executes.  See the examples section for
//an example of this form of the @|for| loop.
//@@Examples
//Here we write @|for| loops to add all the integers from
//@|1| to @|100|.  We will use all three forms of the @|for|
//statement.
//@<
//accum = 0;
//for (i=1:100); accum = accum + i; end
//accum
//@>
//The second form is functionally the same, without the
//extra parenthesis
//@<
//accum = 0;
//for i=1:100; accum = accum + i; end
//accum
//@>
//In the third example, we pre-initialize the loop variable
//with the values it is to take
//@<
//@>
//@@Tests
//@{ test_for1.m
//% Test a simple 'for' statement
//function test_val = test_for1
//sum = 0;
//for i = 1:10;
// sum = sum + i;
//end
//test_val = test(sum == 55);
//@}
//@{ test_for2.m
//% Test a simple 'for' statement with a continue clause
//function test_val = test_for2
//sum = 0;
//for i = 1:10;
// sum = sum + i;
// if (i==5), continue; end
// sum = sum - 1;
//end
//test_val = test(sum == 46);
//@}
//@{ test_for3.m
//% Test a simple 'for' statement with a continue clause
//function test_val = test_for3
//sum = 0;
//for i = 1:10;
// sum = sum + i;
// if (i==5), break; end;
//end
//test_val = test(sum == 15);
//@}
//@{ test_for4.m
//% Test a simple 'for' statement with a return clause
//function test_val = test_for4
//tval = test_for4_assist;
//test_val = test(tval == 15);
//
//function sum = test_for4_assist
//sum = 0;
//for i = 1:10;
// sum = sum + i;
// if (i==5), return; end;
//end
//@}
//!
//Works
void Interpreter::forStatement(const tree &t) {
  Array indexSet;
  string indexVarName;
  int ctxt = t.context();

  // Try to compile this for statement to an instruction stream
  if (jitcontrol) {
#ifdef HAVE_LLVM
    JITVM jit;
    bool success = false;
    try {
      jit.compile(t,this);
      success = true;
    } catch (Exception &e) {
      t.print();
      std::cout << e.getMessageCopy() << "\r\n";
      success = false;
    }
    if (success) {
      jit.run(this);
      return;
    }
#endif
  }

  /* Get the name of the indexing variable */
  if (t.first().is('=')) {
    indexVarName = t.first().first().text();
    /* Evaluate the index set */
    indexSet = expression(t.first().second());
  } else {
    indexVarName = t.first().text();
    ArrayReference ptr(context->lookupVariable(indexVarName));
    if (!ptr.valid()) throw Exception("index variable " + indexVarName + " used in for statement must be defined");
    indexSet = *ptr;
  }
  /* Get the code block */
  tree codeBlock(t.second());
  int elementCount = indexSet.getLength();
  Class loopType(indexSet.dataClass());
  ContextLoopLocker lock(context);
  switch(loopType) {
  case FM_FUNCPTR_ARRAY:
    throw Exception("Function pointer arrays are not supported as for loop index sets");
  case FM_STRUCT_ARRAY:
    throw Exception("Structure arrays are not supported as for loop index sets");
  case FM_CELL_ARRAY:
    ForLoopHelper<Array>(codeBlock,loopType,(const Array*)indexSet.getDataPointer(),
			  elementCount,indexVarName,this);
    break;
  case FM_LOGICAL:
    ForLoopHelper<logical>(codeBlock,loopType,(const logical*)indexSet.getDataPointer(),
			   elementCount,indexVarName,this);
    break;
  case FM_UINT8:
    ForLoopHelper<uint8>(codeBlock,loopType,(const uint8*)indexSet.getDataPointer(),
			 elementCount,indexVarName,this);
    break;
  case FM_INT8:
    ForLoopHelper<int8>(codeBlock,loopType,(const int8*)indexSet.getDataPointer(),
			 elementCount,indexVarName,this);
    break;
  case FM_UINT16:
    ForLoopHelper<uint16>(codeBlock,loopType,(const uint16*)indexSet.getDataPointer(),
			 elementCount,indexVarName,this);
    break;
  case FM_INT16:
    ForLoopHelper<int16>(codeBlock,loopType,(const int16*)indexSet.getDataPointer(),
			 elementCount,indexVarName,this);
    break;
  case FM_UINT32:
    ForLoopHelper<uint32>(codeBlock,loopType,(const uint32*)indexSet.getDataPointer(),
			  elementCount,indexVarName,this);
    break;
  case FM_INT32:
    ForLoopHelper<int32>(codeBlock,loopType,(const int32*)indexSet.getDataPointer(),
			 elementCount,indexVarName,this);
    break;
  case FM_UINT64:
    ForLoopHelper<uint64>(codeBlock,loopType,(const uint64*)indexSet.getDataPointer(),
			  elementCount,indexVarName,this);
    break;
  case FM_INT64:
    ForLoopHelper<int64>(codeBlock,loopType,(const int64*)indexSet.getDataPointer(),
			 elementCount,indexVarName,this);
    break;
  case FM_FLOAT:
    ForLoopHelper<float>(codeBlock,loopType,(const float*)indexSet.getDataPointer(),
			 elementCount,indexVarName,this);
    break;
  case FM_DOUBLE:
    ForLoopHelper<double>(codeBlock,loopType,(const double*)indexSet.getDataPointer(),
			  elementCount,indexVarName,this);
    break;
  case FM_COMPLEX:
    ForLoopHelperComplex<float>(codeBlock,loopType,(const float*)indexSet.getDataPointer(),
				elementCount,indexVarName,this);
    break;
  case FM_DCOMPLEX:
    ForLoopHelperComplex<double>(codeBlock,loopType,(const double*)indexSet.getDataPointer(),
				 elementCount,indexVarName,this);
    break;
  case FM_STRING:
    ForLoopHelper<uint8>(codeBlock,loopType,(const uint8*)indexSet.getDataPointer(),
			 elementCount,indexVarName,this);
    break;
  }
}

//!
//@Module GLOBAL Global Variables
//@@Section VARIABLES
//@@Usage
//Global variables are shared variables that can be
//seen and modified from any function or script that 
//declares them.  The syntax for the @|global| statement
//is
//@[
//  global variable_1 variable_2 ...
//@]
//The @|global| statement must occur before the variables
//appear.
//@@Example
//Here is an example of two functions that use a global
//variable to communicate an array between them.  The
//first function sets the global variable.
//@{ set_global.m
//function set_global(x)
//  global common_array
//  common_array = x;
//@}
//The second function retrieves the value from the global
//variable
//@{ get_global.m
//function x = get_global
//  global common_array
//  x = common_array;
//@}
//Here we exercise the two functions
//@<
//set_global('Hello')
//get_global
//@>
//!
void Interpreter::globalStatement(const tree &t) {
  for (unsigned i=0;i<t.numchildren();i++)
    context->addGlobalVariable(t.child(i).text());
}

//!
//@Module PERSISTENT Persistent Variables
//@@Section VARIABLES
//@@Usage
//Persistent variables are variables whose value persists between
//calls to a function or script.  The general syntax for its
//use is
//@[
//   persistent variable1 variable2 ... variableN
//@]
//The @|persistent| statement must occur before the variable
//is the tagged as persistent.
//@@Example
//Here is an example of a function that counts how many
//times it has been called.
//@{ count_calls.m
//function count_calls
//  persistent ccount
//  if (~exist('ccount')) ccount = 0; end;
//  ccount = ccount + 1;
//  printf('Function has been called %d times\n',ccount);
//@}
//We now call the function several times:
//@<
//for i=1:10; count_calls; end
//@>
//@@Tests
//@{ test_persistent1.m
//% Test the persistent variable declaration
//function test_val = test_persistent1
//accum = 0;
//for (i=1:10)
//  accum = accum + i;
//end
//test_val = test(accum == 55);
//
//function tick = test_persistent1_assist
//persistent count;
//count = count + 1;
//tick = count;
//@}
//@{ test_persistent2.m
//function y = test_persistent2
//persistent a b c d e
//global a1 b1 c1 d1 e1
//y = 1;
//@}
//!
void Interpreter::persistentStatement(const tree &t) {
  for (unsigned i=0;i<t.numchildren();i++)
    context->addPersistentVariable(t.child(i).text());
}

//!
//@Module CONTINUE Continue Execution In Loop
//@@Section FLOW
//@@Usage
//The @|continue| statement is used to change the order of
//execution within a loop.  The @|continue| statement can
//be used inside a @|for| loop or a @|while| loop.  The
//syntax for its use is 
//@[
//   continue
//@]
//inside the body of the loop.  The @|continue| statement
//forces execution to start at the top of the loop with
//the next iteration.  The examples section shows how
//the @|continue| statement works.
//@@Example
//Here is a simple example of using a @|continue| statement.
//We want to sum the integers from @|1| to @|10|, but not
//the number @|5|.  We will use a @|for| loop and a continue
//statement.
//@{ continue_ex.m
//function accum = continue_ex
//  accum = 0;
//  for i=1:10
//    if (i==5)
//      continue;
//    end
//    accum = accum + 1; %skipped if i == 5!
//  end
//@}
//The function is exercised here:
//@<
//continue_ex
//sum([1:4,6:10])
//@>
//@@Tests
//@{ test_continue1.m
//% Test the continuation character with white space after it (bug 1642992)
//function test_val = test_continue1
//test_val = ...    % This should be ignored
//   ...            % So should this
//   1;
//@}
//!

//!
//@Module BREAK Exit Execution In Loop
//@@Section FLOW
//@@Usage
//The @|break| statement is used to exit a loop prematurely.
//It can be used inside a @|for| loop or a @|while| loop.  The
//syntax for its use is
//@[
//   break
//@]
//inside the body of the loop.  The @|break| statement forces
//execution to exit the loop immediately.
//@@Example
//Here is a simple example of how @|break| exits the loop.
//We have a loop that sums integers from @|1| to @|10|, but
//that stops prematurely at @|5| using a @|break|.  We will
//use a @|while| loop.
//@{ break_ex.m
//function accum = break_ex
//  accum = 0;
//  i = 1;
//  while (i<=10) 
//    accum = accum + i;
//    if (i == 5)
//      break;
//    end
//    i = i + 1;
//  end
//@}
//The function is exercised here:
//@<
//break_ex
//sum(1:5)
//@>
//!

//!
//@Module RETURN Return From Function
//@@Section FLOW
//@@Usage
//The @|return| statement is used to immediately return from
//a function, or to return from a @|keyboard| session.  The 
//syntax for its use is
//@[
//  return
//@]
//Inside a function, a @|return| statement causes FreeMat
//to exit the function immediately.  When a @|keyboard| session
//is active, the @|return| statement causes execution to
//resume where the @|keyboard| session started.
//@@Example
//In the first example, we define a function that uses a
//@|return| to exit the function if a certain test condition 
//is satisfied.
//@{ return_func.m
//function ret = return_func(a,b)
//  ret = 'a is greater';
//  if (a > b)
//    return;
//  end
//  ret = 'b is greater';
//  printf('finishing up...\n');
//@}
//Next we exercise the function with a few simple test
//cases:
//@<
//return_func(1,3)
//return_func(5,2)
//@>
//In the second example, we take the function and rewrite
//it to use a @|keyboard| statement inside the @|if| statement.
//@{ return_func2.m
//function ret = return_func2(a,b)
//  if (a > b)
//     ret = 'a is greater';
//     keyboard;
//  else
//     ret = 'b is greater';
//  end
//  printf('finishing up...\n');
//@}
//Now, we call the function with a larger first argument, which
//triggers the @|keyboard| session.  After verifying a few
//values inside the @|keyboard| session, we issue a @|return|
//statement to resume execution.
//@<
//return_func2(2,4)
//return_func2(5,1)
//ret
//a
//b
//return
//@>
//!

//!
//@Module QUIT Quit Program
//@@Section FREEMAT
//@@Usage
//The @|quit| statement is used to immediately exit the FreeMat
//application.  The syntax for its use is
//@[
//   quit
//@]
//!

//!
//@Module RETALL Return From All Keyboard Sessions
//@@Section FLOW
//@@Usage
//The @|retall| statement is used to return to the base workspace
//from a nested @|keyboard| session.  It is equivalent to forcing
//execution to return to the main prompt, regardless of the level
//of nesting of @|keyboard| sessions, or which functions are 
//running.  The syntax is simple
//@[
//   retall
//@]
//The @|retall| is a convenient way to stop debugging.  In the
//process of debugging a complex program or set of functions,
//you may find yourself 5 function calls down into the program
//only to discover the problem.  After fixing it, issueing
//a @|retall| effectively forces FreeMat to exit your program
//and return to the interactive prompt.
//@@Example
//Here we demonstrate an extreme example of @|retall|.  We
//are debugging a recursive function @|self| to calculate the sum
//of the first N integers.  When the function is called,
//a @|keyboard| session is initiated after the function
//has called itself N times.  At this @|keyboard| prompt,
//we issue another call to @|self| and get another @|keyboard|
//prompt, this time with a depth of 2.  A @|retall| statement
//returns us to the top level without executing the remainder
//of either the first or second call to @|self|:
//@{ self.m
//function y = self(n)
//  if (n>1)
//    y = n + self(n-1);
//    printf('y is %d\n',y);
//  else
//    y = 1;
//    printf('y is initialized to one\n');
//    keyboard
//  end
//@}
//@<
//self(4)
//self(6)
//retall
//@>
//!

//!
//@Module KEYBOARD Initiate Interactive Debug Session
//@@Section FLOW
//@@Usage
//The @|keyboard| statement is used to initiate an
//interactive session at a specific point in a function.
//The general syntax for the @|keyboard| statement is
//@[
//   keyboard
//@]
//A @|keyboard| statement can be issued in a @|script|,
//in a @|function|, or from within another @|keyboard| session.
//The result of a @|keyboard| statement is that execution
//of the program is halted, and you are given a prompt
//of the form:
//@[
// [scope,n] -->
//@]
//where @|scope| is the current scope of execution (either
//the name of the function we are executing, or @|base| otherwise).
//And @|n| is the depth of the @|keyboard| session. If, for example,
//we are in a @|keyboard| session, and we call a function that issues
//another @|keyboard| session, the depth of that second session 
//will be one higher.  Put another way, @|n| is the number of @|return|
//statements you have to issue to get back to the base workspace.
//Incidentally, a @|return| is how you exit the @|keyboard| session
//and resume execution of the program from where it left off.  A
//@|retall| can be used to shortcut execution and return to the base
//workspace.
//
//The @|keyboard| statement is an excellent tool for debugging
//FreeMat code, and along with @|eval| provide a unique set of
//capabilities not usually found in compiled environments.  Indeed,
//the @|keyboard| statement is equivalent to a debugger breakpoint in 
//more traditional environments, but with significantly more inspection
//power.
//@@Example
//Here we demonstrate a two-level @|keyboard| situation.  We have
//a simple function that calls @|keyboard| internally:
//@{ key_one.m
//function c = key_one(a,b)
//c = a + b;
//keyboard
//@}
//Now, we execute the function from the base workspace, and
//at the @|keyboard| prompt, we call it again.  This action
//puts us at depth 2.  We can confirm that we are in the second
//invocation of the function by examining the arguments.  We
//then issue two @|return| statements to return to the base
//workspace.
//@<
//key_one(1,2)
//key_one(5,7)
//a
//b
//c
//return
//a
//b
//c
//return
//@>
//!


void Interpreter::doDebugCycle() {
  depth++;
  try {
    evalCLI();
  } catch (InterpreterContinueException& e) {
  } catch (InterpreterBreakException& e) {
  } catch (InterpreterReturnException& e) {
  }
  depth--;
}

void Interpreter::displayArray(Array b) {
  // Check for a user defined class
  FuncPtr val;
  if (b.isUserClass() && ClassResolveFunction(this,b,"display",val)) {
    if (val->updateCode(this)) refreshBreakpoints();
    ArrayVector args(singleArrayVector(b));
    ArrayVector retvec(val->evaluateFunction(this,args,1));
  } else
    PrintArrayClassic(b,printLimit,this);
}

//Works
void Interpreter::expressionStatement(const tree &s, bool printIt) {
  ArrayVector m;
  if (!s.is(TOK_EXPR)) throw Exception("Unexpected statement type!");
  tree t(s.first());
  // There is a special case to consider here - when a 
  // function call is made as a statement, we do not require
  // that the function have an output.
  Array b;
  ArrayReference ptr;
  if (t.is(TOK_VARIABLE)) {
    ptr = context->lookupVariable(t.first().text());
    if (!ptr.valid()) {
      functionExpression(t,0,true,m);
      m = handleReindexing(t,m);
      bool emptyOutput = false;
      if (m.size() == 0) {
	b = Array::emptyConstructor();
	emptyOutput = true;
      } else 
	b = m[0];
      if (printIt && (!emptyOutput)) {
	outputMessage(std::string("\nans = \n"));
	displayArray(b);
      }
    } else {
      multiexpr(t,m);
      if (m.size() == 0)
	b = Array::emptyConstructor();
      else {
	b = m[0];
	if (printIt) {
	  outputMessage(std::string("\nans = \n"));
	  for (int j=0;j<m.size();j++) {
	    char buffer[1000];
	    if (m.size() > 1) {
	      sprintf(buffer,"\n%d of %d:\n",j+1,m.size());
	      outputMessage(std::string(buffer));
	    }
	    displayArray(m[j]);
	  }
	}
      }
    }
  } else {
    b = expression(t);
    if (printIt) {
      outputMessage(std::string("\nans = \n"));
      displayArray(b);
    } 
  }
  context->insertVariable("ans",b);
}

void Interpreter::multiassign(ArrayReference r, const tree &s, ArrayVector &data) {
  SaveEndInfo;
  endRef = r;
  if (s.is(TOK_PARENS)) {
    ArrayVector m;
    endTotal = s.numchildren();
    if (s.numchildren() == 0) 
      throw Exception("The expression A() is not legal unless A is a function");
    for (unsigned p = 0; p < s.numchildren(); p++) {
      endCount = m.size();
      multiexpr(s.child(p),m);
    }
    subsindex(m);
    if (m.size() == 1)
      r->setVectorSubset(m[0],data[0],this);
    else
      r->setNDimSubset(m,data[0],this);
    data.pop_front();
  } else if (s.is(TOK_BRACES)) {
    ArrayVector m;
    endTotal = s.numchildren();
    for (unsigned p = 0; p < s.numchildren(); p++) {
      endCount = m.size();
      multiexpr(s.child(p),m);
    }
    subsindex(m);
    if (m.size() == 1)
      r->setVectorContentsAsList(m[0],data,this);
    else
      r->setNDimContentsAsList(m,data,this);
  } else if (s.is('.')) {
    r->setFieldAsList(s.first().text(),data);
  } else if (s.is(TOK_DYN)) {
    string field;
    try {
      Array fname(expression(s.first()));
      field = fname.getContentsAsString();
    } catch (Exception &e) {
      throw Exception("dynamic field reference to structure requires a string argument");
    }
    r->setFieldAsList(field,data);
  }
  RestoreEndInfo;
}

void Interpreter::assign(ArrayReference r, const tree &s, Array &data) {
  SaveEndInfo;
  endRef = r;  
  if (s.is(TOK_PARENS)) {
    ArrayVector m;
    endTotal = s.numchildren();
    if (s.numchildren() == 0)
      throw Exception("The expression A() is not legal unless A is a function");
    for (unsigned p = 0; p < s.numchildren(); p++) {
      endCount = m.size();
      multiexpr(s.child(p),m);
    }
    subsindex(m);
    if (m.size() == 1)
      r->setVectorSubset(m[0],data,this);
    else
      r->setNDimSubset(m,data,this);
  } else if (s.is(TOK_BRACES)) {
    ArrayVector datav(singleArrayVector(data));
    ArrayVector m;
    endTotal = s.numchildren();
    for (unsigned p = 0; p < s.numchildren(); p++) {
      endCount = m.size();
      multiexpr(s.child(p),m);
    }
    subsindex(m);
    if (m.size() == 1)
      r->setVectorContentsAsList(m[0],datav,this);
    else
      r->setNDimContentsAsList(m,datav,this);
  } else if (s.is('.')) {
    ArrayVector datav(singleArrayVector(data));
    r->setFieldAsList(s.first().text(),datav);
  } else if (s.is(TOK_DYN)) {
    string field;
    try {
      Array fname(expression(s.first()));
      field = fname.getContentsAsString();
    } catch (Exception &e) {
      throw Exception("dynamic field reference to structure requires a string argument");
    }
    ArrayVector datav(singleArrayVector(data));
    r->setFieldAsList(field,datav);
  }
  RestoreEndInfo;
}


ArrayReference Interpreter::createVariable(string name) {
  // Are we in a nested scope?
  if (!context->isCurrentScopeNested() || context->variableLocalToCurrentScope(name)) {
    // if not, just create a local variable in the current scope, and move on.
    context->insertVariable(name,Array::emptyConstructor());
  } else {
    // if so - walk up the scope chain until we are no longer nested
    string localScopeName = context->scopeName();
    context->bypassScope(1);
    while (context->currentScopeNests(localScopeName))
      context->bypassScope(1);
    context->restoreScope(1);
    // We wre now pointing to the highest scope that contains the present
    // (nested) scope.  Now, we start walking down the chain looking for
    // someone who accesses this variable
    while (!context->currentScopeVariableAccessed(name) &&
	   context->scopeName() != localScopeName) 
      context->restoreScope(1);
    // Either we are back in the local scope, or we are pointing to
    // a scope that (at least theoretically) accesses a variable with 
    // the given name.
    context->insertVariable(name,Array::emptyConstructor());
    context->restoreBypassedScopes();
  } 
  ArrayReference np(context->lookupVariable(name));
  if (!np.valid()) 
    throw Exception("error creating variable name " + name + 
		    " with scope " + context->scopeName());
  return np;
}

//Works
    // The case of a(1,2).foo.goo{3} = rhs
    // The tree looks like this:
    // Variable
    //    Identifier
    //    ()
    //      etc
    //    .
    //      foo
    //    .
    //      goo
    //    {}
    //      3
    //
    // We have to do:
    //   a1 = id       data = id
    //   a2 = id(etc)  stack[0] = id(etc)
    //   a3 = a2.foo   stack[1] = stack[0].foo
    //   a4 = a3.goo   stack[2] = stack[1].goo
    //   a3{3} = rhs   data{3} = rhs
    //   a2.foo = a3   
    //   id(etc) = a2;

//!
//@Module ASSIGN Making assignments
//@@Section ARRAY
//@@Usage
//FreeMat assignments take a number of different forms, depending 
//on the type of the variable you want to make an assignment to.
//For numerical arrays and strings, the form of an assignment
//is either
//@[
//  a(ndx) = val
//@]
//where @|ndx| is a set of vector indexing coordinates.  This means
//that the values @|ndx| takes reference the elements of @|a| in column
//order.  So, if, for example @|a| is an @|N x M| matrix, the first column
//has vector indices @|1,2,...,N|, and the second column has indices
//@|N+1,N+2,...,2N|, and so on.  Alternately, you can use multi-dimensional
//indexing to make an assignment:
//@[
//  a(ndx_1,ndx_2,..,ndx_m) = val
//@]
//where each indexing expression @|ndx_i| corresponds to the @|i-th| dimension
//of @|a|.  In both cases, (vector or multi-dimensional indexing), the
//right hand side @|val| must either be a scalar, an empty matrix, or of the
//same size as the indices.  If @|val| is an empty matrix, the assignment acts
//like a delete.  Note that the type of @|a| may be modified by the assignment.
//So, for example, assigning a @|double| value to an element of a @|float| 
//array @|a| will cause the array @|a| to become @|double|.  
//
//For cell arrays, the above forms of assignment will still work, but only
//if @|val| is also a cell array.  If you want to assign the contents of
//a cell in a cell array, you must use one of the two following forms, either
//@[
//  a{ndx} = val
//@]
//or
//@[
//  a{ndx_1,ndx_2,...,ndx_m} = val
//@]
//which will modify the contents of the cell.
//@@Tests
//@{ test_assign1.m
//% Test the assignment of a value to a cell array as a vector-contents
//function test_val = test_assign1
//a = {1,2,3,4,5};
//a{5} = -1;
//test_val = test(a{5} == -1);
//@}
//@{ test_assign2.m
//% Test the assignment of a value to a cell array as a vector-contents.
//function test_val = test_assign2
//a = {1,2,3,4,5};
//test_val = 0;
//try
//a{5:6} = -1;
//catch
//test_val = 1;
//end
//@}
//@{ test_assign3.m
//% Test the assignment of a value to a cell-array using content-based n-dim indexing
//function test_val = test_assign3
//a = {1,2;4,5};
//test_val = 0;
//try
//a{2,1:2} = -1;
//catch
//test_val = 1;
//end
//@}
//@{ test_assign4.m
//% Test the assignment of a value to a cell array as an n-dim indexing
//function test_val = test_assign4
//a = {1,2,3;4,5,6};
//a{2,3} = -1;
//test_val = test(a{6} == -1);
//@}
//@{ test_assign5.m
//% Test the field assignment with a scalar struct
//function test_val = test_assign5
//a = struct('foo',5);
//a.foo = -1;
//test_val = test(a.foo == -1);
//@}
//@{ test_assign6.m
//% Test the field assignment with a vector struct (should cause an error)
//function test_val = test_assign6
//a = struct('foo',{5,7});
//test_val = 0;
//try
//a.foo = -1;
//catch
//test_val = 1;
//end
//@}
//@{ test_assign7.m
//% Test the empty assignment for the empty cell array
//function test_val = test_assign7
//a = {};
//test_val = isempty(a);
//@}
//@{ test_assign8.m
//% Test the empty assignment for the empty array
//function test_val = test_assign8
//a = [];
//test_val = isempty(a);
//@}
//@{ test_assign9.m
//% Test for reassign bug in repmat
//function t = test_assign9
//a = {1,2,3};
//b = repmat(a,2);
//b = 'good';
//t = 1;
//@}
//@{ test_assign10.m
//% Test for multiple assign with structures
//function test_val = test_assign10
//a = zeros(3,4);
//[m.x m.y] = size(a);
//test_val = (m.x == 3) & (m.y == 4);
//@}
//@{ test_assign11.m
//% Test for multiple assign with arrays
//function test_val = test_assign11
//a = zeros(3,4);
//[b(1), b(2)] = size(a);
//test_val = (b(1) == 3) & (b(2) == 4);
//@}
//@{ test_assign12.m
//% Test for column assignment bug with complex arrays
//function test_val = test_assign12
//    A = [1+i 3-i 5+3i
//         7-i 8+9i 6+i
//         9   8-i   5i];
//    B = A;
//    A(:,2) = 0;
//    B(1:3,2) = 0;
//    test_val = all(A==B);
//@}
//@{ test_assign13.m
//% Test for assignment of empties to an empty
//function test_val = test_assign13
//    A = [];
//    A([],[],[]) = [];
//    test_val = 1;
//@}
//@{ test_assign14.m
//% Test for auto sizing of assignment to undefined variables
//function test_val = test_assign14
//   r = [2,3;3,4];
//   a(2,:,:) = r;
//   b = zeros(2,2,2);
//   b(2,:,:) = r;
//   test_val = all(a(:) == b(:)) && all(size(a) == size(b));
//@}
//@{ test_assign15.m
//% Test for error on illegal (incomplete) assign to empty variables
//function test_val = test_assign15
//   a = [2,3;4,5];
//   test_val = 0;
//   try
//     c(2,:) = a;
//   catch
//     test_val = 1;
//   end
//@}
//@{ test_sparse56.m
//% Test DeleteSparseMatrix function
//function x = test_sparse56
//xi = sparse_test_mat('int32',100);
//xf = sparse_test_mat('float',100);
//xd = sparse_test_mat('double',100);
//xc = sparse_test_mat('complex',100);
//xz = sparse_test_mat('dcomplex',100);
//xi = [];
//xf = [];
//xd = [];
//xc = [];
//xz = [];
//x = isempty(xi) & isempty(xf) & isempty(xd) & isempty(xc) & isempty(xz);
//@}
//@{ test_sparse63.m
//% Test sparse matrix array vector-subset extraction
//function x = test_sparse63
//[yi1,zi1] = sparse_test_mat('int32',300,400);
//[yf1,zf1] = sparse_test_mat('float',300,400);
//[yd1,zd1] = sparse_test_mat('double',300,400);
//[yc1,zc1] = sparse_test_mat('complex',300,400);
//[yz1,zz1] = sparse_test_mat('dcomplex',300,400);
//ndx = randi(ones(1500,1),300*400*ones(1500,1));
//a1 = yi1(ndx); b1 = zi1(ndx);
//a2 = yf1(ndx); b2 = zf1(ndx);
//a3 = yd1(ndx); b3 = zd1(ndx);
//a4 = yc1(ndx); b4 = zc1(ndx);
//a5 = yz1(ndx); b5 = zz1(ndx);
//x = testeq(a1,b1) & testeq(a2,b2) & testeq(a3,b3) & testeq(a4,b4) & testeq(a5,b5);
//ndx = [];
//a1 = yi1(ndx); b1 = zi1(ndx);
//a2 = yf1(ndx); b2 = zf1(ndx);
//a3 = yd1(ndx); b3 = zd1(ndx);
//a4 = yc1(ndx); b4 = zc1(ndx);
//a5 = yz1(ndx); b5 = zz1(ndx);
//x = x & testeq(a1,b1) & testeq(a2,b2) & testeq(a3,b3) & testeq(a4,b4) & testeq(a5,b5);
//@}
//@{ test_sparse64.m
//% Test sparse matrix array ndim-subset extraction
//function x = test_sparse64
//[yi1,zi1] = sparse_test_mat('int32',300,400);
//[yf1,zf1] = sparse_test_mat('float',300,400);
//[yd1,zd1] = sparse_test_mat('double',300,400);
//[yc1,zc1] = sparse_test_mat('complex',300,400);
//[yz1,zz1] = sparse_test_mat('dcomplex',300,400);
//row_ndx = randi(ones(150,1),300*ones(150,1));
//col_ndx = randi(ones(150,1),400*ones(150,1));
//a1 = yi1(row_ndx,col_ndx); b1 = zi1(row_ndx,col_ndx);
//a2 = yf1(row_ndx,col_ndx); b2 = zf1(row_ndx,col_ndx);
//a3 = yd1(row_ndx,col_ndx); b3 = zd1(row_ndx,col_ndx);
//a4 = yc1(row_ndx,col_ndx); b4 = zc1(row_ndx,col_ndx);
//a5 = yz1(row_ndx,col_ndx); b5 = zz1(row_ndx,col_ndx);
//x = testeq(a1,b1) & testeq(a2,b2) & testeq(a3,b3) & testeq(a4,b4) & testeq(a5,b5);
//row_ndx = [];
//a1 = yi1(row_ndx,col_ndx); b1 = zi1(row_ndx,col_ndx);
//a2 = yf1(row_ndx,col_ndx); b2 = zf1(row_ndx,col_ndx);
//a3 = yd1(row_ndx,col_ndx); b3 = zd1(row_ndx,col_ndx);
//a4 = yc1(row_ndx,col_ndx); b4 = zc1(row_ndx,col_ndx);
//a5 = yz1(row_ndx,col_ndx); b5 = zz1(row_ndx,col_ndx);
//x = x & testeq(a1,b1) & testeq(a2,b2) & testeq(a3,b3) & testeq(a4,b4) & testeq(a5,b5);
//col_ndx = [];
//a1 = yi1(row_ndx,col_ndx); b1 = zi1(row_ndx,col_ndx);
//a2 = yf1(row_ndx,col_ndx); b2 = zf1(row_ndx,col_ndx);
//a3 = yd1(row_ndx,col_ndx); b3 = zd1(row_ndx,col_ndx);
//a4 = yc1(row_ndx,col_ndx); b4 = zc1(row_ndx,col_ndx);
//a5 = yz1(row_ndx,col_ndx); b5 = zz1(row_ndx,col_ndx);
//x = x & testeq(a1,b1) & testeq(a2,b2) & testeq(a3,b3) & testeq(a4,b4) & testeq(a5,b5);
//@}
//@{ test_sparse65.m
//% Test sparse matrix array vector-subset assignment
//function x = test_sparse65
//[yi1,zi1] = sparse_test_mat('int32',300,400);
//[yf1,zf1] = sparse_test_mat('float',300,400);
//[yd1,zd1] = sparse_test_mat('double',300,400);
//[yc1,zc1] = sparse_test_mat('complex',300,400);
//[yz1,zz1] = sparse_test_mat('dcomplex',300,400);
//ndx = randi(ones(1500,1),300*400*ones(1500,1));
//gi = int32(100*randn(1500,1));
//gf = float(randn(1500,1));
//gd = randn(1500,1);
//gc = complex(randn(1500,1)+i*randn(1500,1));
//gz = dcomplex(randn(1500,1)+i*randn(1500,1));
//yi1(ndx) = gi; zi1(ndx) = gi;
//yf1(ndx) = gf; zf1(ndx) = gf;
//yd1(ndx) = gd; zd1(ndx) = gd;
//yc1(ndx) = gc; zc1(ndx) = gc;
//yz1(ndx) = gz; zz1(ndx) = gz;
//% Cannot use testeq because if we set the same element to two
//% different values, the result is different for full versus sparse
//% matrices
//x = testeq2(yi1,zi1,ndx) & testeq2(yf1,zf1,ndx) & testeq2(yd1,zd1,ndx) & testeq2(yc1,zc1,ndx) & testeq2(yz1,zz1,ndx);
//
//function n = length(x)
//n = prod(size(x));
//
//function x = testeq2(a,b,ndx)
//k = find(a-b);
//x = 1;
//for i = 1:length(k)
//  x = x & length(find(ndx == k(i)));
//end
//@}
//@{ test_sparse66.m
//% Test sparse matrix array ndim-subset assignment
//function x = test_sparse66
//[yi1,zi1] = sparse_test_mat('int32',300,400);
//[yf1,zf1] = sparse_test_mat('float',300,400);
//[yd1,zd1] = sparse_test_mat('double',300,400);
//[yc1,zc1] = sparse_test_mat('complex',300,400);
//[yz1,zz1] = sparse_test_mat('dcomplex',300,400);
//ndxr = randi(ones(100,1),300*ones(100,1));
//ndxc = randi(ones(100,1),400*ones(100,1));
//gi = int32(100*randn(100,100));
//gf = float(randn(100,100));
//gd = randn(100,100);
//gc = complex(randn(100,100)+i*randn(100,100));
//gz = dcomplex(randn(100,100)+i*randn(100,100));
//yi1(ndxr,ndxc) = gi; zi1(ndxr,ndxc) = gi;
//yf1(ndxr,ndxc) = gf; zf1(ndxr,ndxc) = gf;
//yd1(ndxr,ndxc) = gd; zd1(ndxr,ndxc) = gd;
//yc1(ndxr,ndxc) = gc; zc1(ndxr,ndxc) = gc;
//yz1(ndxr,ndxc) = gz; zz1(ndxr,ndxc) = gz;
//x = testeq(yi1,zi1) & testeq(yf1,zf1) & testeq(yd1,zd1) & testeq(yc1,zc1) & testeq(yz1,zz1);
//@}
//@{ test_sparse67.m
//% Test sparse matrix individual element retrieval
//function x = test_sparse67
//[yi1,zi1] = sparse_test_mat('int32',300,400);
//[yf1,zf1] = sparse_test_mat('float',300,400);
//[yd1,zd1] = sparse_test_mat('double',300,400);
//[yc1,zc1] = sparse_test_mat('complex',300,400);
//[yz1,zz1] = sparse_test_mat('dcomplex',300,400);
//x = testeq(yi1(150,200),zi1(150,200)) & testeq(yf1(150,200),zf1(150,200)) & testeq(yd1(150,200),zd1(150,200)) & testeq(yc1(150,200),zc1(150,200)) & testeq(yz1(150,200),zz1(150,200));
//@}
//@{ test_sparse71.m
//% Test sparse matrix array column deletion
//function x = test_sparse71
//[yi1,zi1] = sparse_test_mat('int32',300,400);
//[yf1,zf1] = sparse_test_mat('float',300,400);
//[yd1,zd1] = sparse_test_mat('double',300,400);
//[yc1,zc1] = sparse_test_mat('complex',300,400);
//[yz1,zz1] = sparse_test_mat('dcomplex',300,400);
//ndxc = randi(ones(100,1),400*ones(100,1));
//yi1(:,ndxc) = [];
//yf1(:,ndxc) = [];
//yd1(:,ndxc) = [];
//yc1(:,ndxc) = [];
//yz1(:,ndxc) = [];
//zi1(:,ndxc) = [];
//zf1(:,ndxc) = [];
//zd1(:,ndxc) = [];
//zc1(:,ndxc) = [];
//zz1(:,ndxc) = [];
//x = testeq(yi1,zi1) & testeq(yf1,zf1) & testeq(yd1,zd1) & testeq(yc1,zc1) & testeq(yz1,zz1);
//@}
//@{ test_sparse72.m
//% Test sparse matrix array row deletion
//function x = test_sparse72
//[yi1,zi1] = sparse_test_mat('int32',300,400);
//[yf1,zf1] = sparse_test_mat('float',300,400);
//[yd1,zd1] = sparse_test_mat('double',300,400);
//[yc1,zc1] = sparse_test_mat('complex',300,400);
//[yz1,zz1] = sparse_test_mat('dcomplex',300,400);
//ndxr = randi(ones(100,1),300*ones(100,1));
//yi1(ndxr,:) = [];
//yf1(ndxr,:) = [];
//yd1(ndxr,:) = [];
//yc1(ndxr,:) = [];
//yz1(ndxr,:) = [];
//zi1(ndxr,:) = [];
//zf1(ndxr,:) = [];
//zd1(ndxr,:) = [];
//zc1(ndxr,:) = [];
//zz1(ndxr,:) = [];
//x = testeq(yi1,zi1) & testeq(yf1,zf1) & testeq(yd1,zd1) & testeq(yc1,zc1) & testeq(yz1,zz1);
//@}
//@{ test_sparse73.m
//% Test sparse matrix array vector deletion
//function x = test_sparse73
//[yi1,zi1] = sparse_test_mat('int32',300,400);
//[yf1,zf1] = sparse_test_mat('float',300,400);
//[yd1,zd1] = sparse_test_mat('double',300,400);
//[yc1,zc1] = sparse_test_mat('complex',300,400);
//[yz1,zz1] = sparse_test_mat('dcomplex',300,400);
//ndxr = randi(ones(200,1),400*300*ones(200,1));
//yi1(ndxr) = [];
//yf1(ndxr) = [];
//yd1(ndxr) = [];
//yc1(ndxr) = [];
//yz1(ndxr) = [];
//zi1(ndxr) = [];
//zf1(ndxr) = [];
//zd1(ndxr) = [];
//zc1(ndxr) = [];
//zz1(ndxr) = [];
//x = testeq(yi1,zi1.') & testeq(yf1,zf1.') & testeq(yd1,zd1.') & testeq(yc1,zc1.') & testeq(yz1,zz1.');
//@}
//@{ test_sparse81.m
//function x = test_sparse81
//A = sparse(rand(10));
//C = [];
//D = [C,A];
//x = testeq(D,A);
//@}
//@{ test_sparse83.m
//function x = test_sparse83
//  A = sparse(rand(10));
//  B = sparse([]);
//  C = [B,B,B;B,B,B,A,B];
//  x = testeq(C,A);
//@}
//!
void Interpreter::assignment(const tree &var, bool printIt, Array &b) {
  string name(var.first().text());
  ArrayReference ptr(context->lookupVariable(name));
  if (!ptr.valid()) 
    ptr = createVariable(name);
  if (var.numchildren() == 1) {
    ptr->setValue(b);
  } else if (ptr->isUserClass() && 
	     !inMethodCall(ptr->className().back()) && 
	     !stopoverload) {
    ClassAssignExpression(ptr,var,b,this);
  } else if (var.numchildren() == 2)
    assign(ptr,var.second(),b);
  else {
    ArrayVector stack;
    Array data(*ptr);
    int varCount = var.numchildren();
    for (int index=1;index<varCount-1;index++) {
      if (!data.isEmpty()) {
	try {
	  deref(data,var.child(index));
	} catch (Exception &e) {
	  data = Array::emptyConstructor();
	}
      }
      stack.push_back(data);
    }
    assign(&data,var.child(varCount-1),b);
    Array rhs(data);
    if (stack.size() > 0) {
      stack.pop_back();
      int ptr = 0;
      while (stack.size() > 0) {
	data = stack.back();
	assign(&data,var.child(varCount-2-ptr),rhs);
	rhs = data;
	stack.pop_back();
	ptr++;
      }
    }
    assign(ptr,var.child(1),rhs);
  }
  if (printIt) {
    outputMessage("\n");
    outputMessage(name);
    outputMessage(" = \n");
    displayArray(*ptr);
  }
}

void Interpreter::processBreakpoints(const tree &t) {
  for (int i=0;i<bpStack.size();i++) {
    if ((bpStack[i].cname == ip_funcname) && 
	((ip_context & 0xffff) == bpStack[i].tokid)) {
      doDebugCycle();
      SetContext(t.context());
    }
  }
  if (tracetrap > 0) {
    if (((ip_context) & 0xffff) != tracecurrentline) {
      tracetrap--;
      if (tracetrap == 0)
	doDebugCycle();
    }
  }
  if (steptrap > 0) {
    if (((ip_context) & 0xffff) != stepcurrentline) {
      steptrap--;
      if (steptrap == 0)
	doDebugCycle();
    }
  }
}

void Interpreter::statementType(const tree &t, bool printIt) {
  // check the debug flag
  SetContext(t.context());
  processBreakpoints(t);
  switch(t.token()) {
  case '=': 
    {
      Array b(expression(t.second()));
      assignment(t.first(),printIt,b);
    }
    break;
  case TOK_MULTI:
    multiFunctionCall(t,printIt);
    break;
  case TOK_SPECIAL:
    specialFunctionCall(t,printIt);
    break;
  case TOK_FOR:
    forStatement(t);
    break;
  case TOK_WHILE:
    whileStatement(t);
    break;
  case TOK_IF:
    ifStatement(t);
    break;
  case TOK_BREAK:
    if (context->inLoop()) 
      throw InterpreterBreakException();
    break;
  case TOK_CONTINUE:
    if (context->inLoop()) 
      throw InterpreterContinueException();
    break;
  case TOK_DBSTEP:
    //    qDebug() << "**********************DBStep";
    dbstepStatement(t);
    emit RefreshBPLists();
    throw InterpreterReturnException();
    break;
  case TOK_DBTRACE:
    //    qDebug() << "**********************DBTrace";
    dbtraceStatement(t);
    emit RefreshBPLists();
    throw InterpreterReturnException();
    break;
  case TOK_RETURN:
    throw InterpreterReturnException();
    break;
  case TOK_SWITCH:
    switchStatement(t);
    break;
  case TOK_TRY:
    tryStatement(t);
    break;
  case TOK_QUIT:
    throw InterpreterQuitException();
    break;
  case TOK_RETALL:
    throw InterpreterRetallException();
    break;
  case TOK_KEYBOARD:
    doDebugCycle();
    break;
  case TOK_GLOBAL:
    globalStatement(t);
    break;
  case TOK_PERSISTENT:
    persistentStatement(t);
    break;
  case TOK_EXPR:
    expressionStatement(t,printIt);
    break;
  case TOK_NEST_FUNC:
    break;
  default:
    throw Exception("Unrecognized statement type");
  }
}


//Trapping at the statement level is much better! - two
//problems... try/catch and multiline statements (i.e.,atell.m)
//The try-catch one is easy, I think...  When a try occurs,
//we capture the stack depth... if an exception occurs, we 
//unwind the stack to this depth..
//The second one is trickier - suppose we have a conditional
//statement
//if (a == 3)
//    bfunc
//else
//    cfunc
//end
//this is represented in the parse tree as a single construct...
//

// 
//Works
void Interpreter::statement(const tree &t) {
  try {
    if (t.is(TOK_QSTATEMENT))
      statementType(t.first(),false);
    else if (t.is(TOK_STATEMENT))
      statementType(t.first(),m_quietlevel == 0);
    else
      throw Exception("Unexpected statement type!\n");
  } catch (Exception& e) {
    if (autostop && !InCLI) {
      errorCount++;
      char buffer[4096];
      e.printMe(this);
      stackTrace(true);
      doDebugCycle();
    } else  {
      if (!e.wasHandled() && !InCLI) {
	stackTrace(true);
	e.markAsHandled();
      }
      throw;
    }
  }
}

//Works
void Interpreter::block(const tree &t) {
  try {
    const treeVector &statements(t.children());
    for (treeVector::const_iterator i=statements.begin();
	 i!=statements.end();i++) {
      if (m_kill)
	throw InterpreterKillException();
      if (m_interrupt) {
	outputMessage("Interrupt (ctrl-b) encountered\n");
	stackTrace(true);
	m_interrupt = false;
	doDebugCycle();
      } else 
	statement(*i);
    }
  } catch (Exception &e) {
    lasterr = e.getMessageCopy();
    throw;
  }
}

// I think this is too complicated.... there should be an easier way
// Works
int Interpreter::countLeftHandSides(const tree &t) {
  Array lhs;
  ArrayReference ptr(context->lookupVariable(t.first().text()));
  if (!ptr.valid())
    lhs = Array::emptyConstructor();
  else
    lhs = *ptr;
  if (t.numchildren() == 1) return 1;
  if (t.last().is(TOK_PARENS)) return 1;
  for (unsigned index = 1;index < t.numchildren()-1;index++) {
    try {
      deref(lhs,t.child(index));
    } catch (Exception& e) {
      lhs = Array::emptyConstructor();
    }
  }
  if (t.last().is(TOK_BRACES)) {
    const tree &s(t.last());
    ArrayVector m;
    for (unsigned p = 0; p < s.numchildren(); p++) 
      multiexpr(s.child(p),m);
    subsindex(m);
    if (m.size() == 0)
      throw Exception("Expected indexing expression!");
    if (m.size() == 1) {
      // m[0] should have only one element...
      if (isColonOperator(m[0]))
	return (lhs.getLength());
      m[0].toOrdinalType(this);
      return (m[0].getLength());
    } else {
      int i=0;
      int outputCount=1;
      while (i<m.size()) {
	if (isColonOperator(m[i])) 
	  outputCount *= lhs.getDimensionLength(i);
	else {
	  m[i].toOrdinalType(this);
	  outputCount *= m[i].getLength();
	}
	i++;
      }
      return (outputCount);
    }
  }
  if (t.last().is('.')) 
    return std::max(1,lhs.getLength());
  return 1;
}

Array Interpreter::AllColonReference(Array v, int index, int count) {
  if (v.isUserClass()) return Array::emptyConstructor();
  return Array::stringConstructor(":");
}
  
//test
void Interpreter::specialFunctionCall(const tree &t, bool printIt) {
  tree fAST;
  ArrayVector m;
  stringVector args;
  for (unsigned index=0;index < t.numchildren();index++) 
    args.push_back(t.child(index).text());
  if (args.empty()) return;
  ArrayVector n;
  for (int i=1;i<args.size();i++)
    n.push_back(Array::stringConstructor(args[i].c_str()));
  FuncPtr val;
  if (!lookupFunction(args[0],val,n))
    throw Exception("unable to resolve " + args[0] + " to a function call");
  if (val->updateCode(this)) refreshBreakpoints();
  bool CLIFlagsave = InCLI;
  InCLI = false;
  try {
    if (!val->graphicsFunction)
      m = val->evaluateFunction(this,n,0);
    else
      m = doGraphicsFunction(val,n,0);
  } catch(Exception& e) {
    InCLI = CLIFlagsave;
    throw;
  }
  InCLI = CLIFlagsave;
}
 
void Interpreter::setBreakpoint(stackentry bp, bool enableFlag) {
  FuncPtr val;
  bool isFun = lookupFunction(bp.detail,val);
  if (!isFun) {
    warningMessage(string("unable to find function ") + 
		   bp.detail + " to set breakpoint");
    return;
  }
  if (val->type() != FM_M_FUNCTION) {
    warningMessage("function " + bp.detail + 
		   " is not an m-file, and does not support breakpoints");
    return;
  }
  //  try {
  //     //    ((MFunctionDef*)val)->SetBreakpoint(bp.tokid,enableFlag);
  //   } catch (Exception &e) {
  //     e.printMe(this);
  //   }
}
 
void Interpreter::addBreakpoint(stackentry bp) {
  bpStack.push_back(bp);
  refreshBreakpoints();
  emit RefreshBPLists();
}

void Interpreter::refreshBreakpoints() {
  for (int i=0;i<bpStack.size();i++)
    setBreakpoint(bpStack[i],true);
}

//Some notes on the multifunction call...  This one is pretty complicated, and the current logic is hardly transparent.  Assume we have an expression of the form:
//
//[expr1 expr2 ... exprn] = fcall
//
//where fcall is a function call (obviously).  Now, we want to determine how many output arguments fcall should have.  There are several interesting cases to consider:
//
//expr_i is an existing numeric variable -- lhscount += 1
//
//expr_i is an existing cell array -- lhscount += size(expr_i)
//
//expr_i is an existing struct array -- lhscount += size(expr_i)
//
//expr_i does not exist -- lhscount += 1
//
//Where this will fail is in one case.  If expr_i is a cell reference for a variable that does not exist, and has a sized argument, something like
//[eg{1:3}]
//in which case the lhscount += 3, even though eg does not exist. 
// WORKS
void Interpreter::multiFunctionCall(const tree &t, bool printIt) {
  ArrayVector m;
  treeVector s;
  Array c;
  int lhsCount;

  if (!t.first().is(TOK_BRACKETS))
    throw Exception("Illegal left hand side in multifunction expression");
  s = t.first().children();
  // We have to make multiple passes through the LHS part of the AST.
  // The first pass is to count how many function outputs are actually
  // being requested. 
  // Calculate how many lhs objects there are
  lhsCount = 0;
  for (unsigned index=0;index<s.size();index++) 
    lhsCount += countLeftHandSides(s[index]);

  multiexpr(t.second(),m,lhsCount);

  unsigned index;
  for (index=0;(index<s.size()) && (m.size() > 0);index++) {
    const tree &var(s[index]);
    string name(var.first().text());
    ArrayReference ptr(context->lookupVariable(name));
    if (!ptr.valid()) 
      ptr = createVariable(name);
    if (ptr->isUserClass() && 
	!inMethodCall(ptr->className().back()) && 
	!stopoverload) {
      ClassAssignExpression(ptr,t,m.front(),this);
      m.pop_front();
      return;
    }
    if (var.numchildren() == 1) {
      ptr->setValue(m.front());
      m.pop_front();
    } else if (var.numchildren() == 2)
      multiassign(ptr,var.second(),m);
    else {
      ArrayVector stack;
      Array data(*ptr);
      int varCount = var.numchildren();
      for (int index=1;index<varCount-1;index++) {
	if (!data.isEmpty()) {
	  try {
	    deref(data,var.child(index));
	  } catch (Exception &e) {
	    data = Array::emptyConstructor();
	  }
	}
	stack.push_back(data);
      }
      multiassign(&data,var.child(varCount-1),m);
      Array rhs(data);
      if (stack.size() > 0) {
	stack.pop_back();
	int ptr = 0;
	while (stack.size() > 0) {
	  data = stack.back();
	  assign(&data,var.child(varCount-2-ptr),rhs);
	  rhs = data;
	  stack.pop_back();
	  ptr++;
	}
      }
      assign(ptr,var.child(1),rhs);
    }
    if (printIt) {
      outputMessage(name);
      outputMessage(" = \n");
      displayArray(*ptr);
    }
  }
  if (index < s.size())
    warningMessage("one or more outputs not assigned in call.");
}

int getArgumentIndex(stringVector list, std::string t) {
  bool foundArg = false;
  std::string q;
  uint32 i;
  i = 0;
  while (i<list.size() && !foundArg) {
    q = list[i];
    if (q[0] == '&')
      q.erase(0,1);
    foundArg = (q == t);
    if (!foundArg) i++;
  }
  if (foundArg)
    return i;
  else
    return -1;
}

//!
//@Module FUNCTION Function Declarations
//@@Section FUNCTIONS
//@@Usage
//There are several forms for function declarations in FreeMat.
//The most general syntax for a function declaration is the 
//following:
//@[
//  function [out_1,...,out_M,varargout] = fname(in_1,...,in_N,varargin)
//@]
//where @|out_i| are the output parameters, @|in_i| are the input
//parameters, and @|varargout| and @|varargin| are special keywords
//used for functions that have variable inputs or outputs.  For 
//functions with a fixed number of input or output parameters, the 
//syntax is somewhat simpler:
//@[
//  function [out_1,...,out_M] = fname(in_1,...,in_N)
//@]
//Note that functions that have no return arguments can omit
//the return argument list (of @|out_i|) and the equals sign:
//@[
//  function fname(in_1,...,in_N)
//@]
//Likewise, a function with no arguments can eliminate the list
//of parameters in the declaration:
//@[
//  function [out_1,...,out_M] = fname
//@]
//Functions that return only a single value can omit the brackets
//@[
//  function out_1 = fname(in_1,...,in_N)
//@]
//
//In the body of the function @|in_i| are initialized with the
//values passed when the function is called.  Also, the function
//must assign values for @|out_i| to pass values to the caller.
//Note that by default, FreeMat passes arguments by value, meaning
//that if we modify the contents of @|in_i| inside the function,
//it has no effect on any variables used by the caller.  Arguments
//can be passed by reference by prepending an ampersand @|&|
//before the name of the input, e.g.
//@[
//  function [out1,...,out_M] = fname(in_1,&in_2,in_3,...,in_N)
//@]
//in which case @|in_2| is passed by reference and not by value.
//Also, FreeMat works like @|C| in that the caller does not have
//to supply the full list of arguments.  Also, when @|keywords|
//(see help @|keywords|) are used, an arbitrary subset of the 
//parameters may be unspecified. To assist in deciphering 
//the exact parameters that were passed,
//FreeMat also defines two variables inside the function context:
//@|nargin| and @|nargout|, which provide the number of input
//and output parameters of the caller, respectively. See help for 
//@|nargin| and @|nargout| for more details.  In some 
//circumstances, it is necessary to have functions that
//take a variable number of arguments, or that return a variable
//number of results.  In these cases, the last argument to the 
//parameter list is the special argument @|varargin|.  Inside
//the function, @|varargin| is a cell-array that contains
//all arguments passed to the function that have not already
//been accounted for.  Similarly, the function can create a
//cell array named @|varargout| for variable length output lists.
//See help @|varargin| and @|varargout| for more details.
//
//The function name @|fname| can be any legal FreeMat identifier.
//Functions are stored in files with the @|.m| extension.  Note
//that the name of the file (and not the function name @|fname| 
//used in the declaration) is how the function appears in FreeMat.
//So, for example, if the file is named @|foo.m|, but the declaration
//uses @|bar| for the name of the function, in FreeMat, it will 
//still appear as function @|foo|.  Note that this is only true
//for the first function that appears in a @|.m| file.  Additional
//functions that appear after the first function are known as
//@|helper functions| or @|local| functions.  These are functions that
//can only be called by other functions in the same @|.m| file.  Furthermore
//the names of these helper functions are determined by their declaration
//and not by the name of the @|.m| file.  An example of using
//helper functions is included in the examples.
//
//Another important feature of functions, as opposed to, say @|scripts|,
//is that they have their own @|scope|.  That means that variables
//defined or modified inside a function do not affect the scope of the
//caller.  That means that a function can freely define and use variables
//without unintentionally using a variable name reserved elsewhere.  The
//flip side of this fact is that functions are harder to debug than
//scripts without using the @|keyboard| function, because the intermediate
//calculations used in the function are not available once the function
//exits.
//@@Examples
//Here is an example of a trivial function that adds its
//first argument to twice its second argument:
//@{ addtest.m
//function c = addtest(a,b)
//  c = a + 2*b;
//@}
//@<
//addtest(1,3)
//addtest(3,0)
//@>
//Suppose, however, we want to replace the value of the first 
//argument by the computed sum.  A first attempt at doing so
//has no effect:
//@{ addtest2.m
//function addtest2(a,b)
//  a = a + 2*b;
//@}
//@<
//arg1 = 1
//arg2 = 3
//addtest2(arg1,arg2)
//arg1
//arg2
//@>
//The values of @|arg1| and @|arg2| are unchanged, because they are
//passed by value, so that any changes to @|a| and @|b| inside 
//the function do not affect @|arg1| and @|arg2|.  We can change
//that by passing the first argument by reference:
//@{ addtest3.m
//function addtest3(&a,b)
//  a = a + 2*b
//@}
//Note that it is now illegal to pass a literal value for @|a| when
//calling @|addtest3|:
//@<1
//addtest3(3,4)
//addtest3(arg1,arg2)
//arg1
//arg2
//@>
//The first example fails because we cannot pass a literal like the
//number @|3| by reference.  However, the second call succeeds, and
//note that @|arg1| has now changed.  Note: please be careful when
//passing by reference - this feature is not available in MATLAB
//and you must be clear that you are using it.
//
//As variable argument and return functions are covered elsewhere,
//as are keywords, we include one final example that demonstrates
//the use of helper functions, or local functions, where
//multiple function declarations occur in the same file.
//@{ euclidlength.m
//function y = foo(x,y)
//  square_me(x);
//  square_me(y);
//  y = sqrt(x+y);
//
//function square_me(&t)
//  t = t^2;
//@}
//@<
//euclidlength(3,4)
//euclidlength(2,0)
//@>
//@@Tests
//@{ test_call1.m
//% test the multiple left-hand side call with a pre-existing cell-array
//function test_val = test_call1
//d1 = {1,2};
//d2 = {5};
//d3 = {6};
//[d1,d2,d3] = test_call1_assist;
//test_val = test(d1 == 1) & test(d2 == 4) & test(d3 == 3);
//
//function [a,b,c] = test_call1_assist
//a = 1;
//b = 4;
//c = 3;
//@}
//@{ test_call2.m
//% test the multiple left-hand side call with a pre-existing cell-array
//function test_val = test_call2
//[d1{1:3}] = test_call2_assist;
//test_val = test(d1{1} == 1) & test(d1{2} == 4) & test(d1{3} == 3);
//
//function [a,b,c] = test_call2_assist
//a = 1;
//b = 4;
//c = 3;
//@}
//@{ test_call3.m
//% test calls with pass-by-reference
//function test_val = test_call3
//d1 = 3;
//test_call3_assist(d1);
//test_val = test(d1 == 1);
//
//function test_call3_assist(&x)
//x = 1;
//@}
//@{ test_call4.m
//% test calls with pass-by-reference
//function test_val = test_call4
//d1.foo = 3;
//test_call4_assist(d1.foo);
//test_val = test(d1.foo == 1);
//
//function test_call4_assist(&x)
//x = 1;
//@}
//@{ test_fcall1.m
//% Check to RHS function calls with multiple return values (b989865)
//function test_val = test_fcall1
//try 
//b = 1:10;
//B = addthem(min(b),max(b));
//test_val = test(B == 11);
//catch
//test_val = 0;
//end
//
//function c = addthem(a,b)
//c = a + b;
//@}
//@{ test_fcall2.m
//% Check function declaration with two return args
//function test_val = test_fcall2
//try
//   [a,b] = fc2fuc(1,3,2);
//   test_val = test(a == 4);
//catch
//   test_val = 0;
//end
//
//function [a b] = fc2fuc(w,x,y)
//  a = w+x;
//  b = y;
//@}
//!


//!
//@Module ANONYMOUS Anonymous Functions
//@@Section FUNCTIONS
//@@Usage
//Anonymous functions are simple, nameless functions that can be defined
//anywhere (in a script, function, or at the prompt).  They are intended
//to supplant @|inline| functions.  The syntax for an anonymous function
//is simple:
//@[
//   y = @(arg1,arg2,...,argn) expression
//@]
//where @|arg1,arg2,...,argn| is a list of valid identifiers that define
//the arguments to the function, and @|expression| is the expression to
//compute in the function.  The returned value @|y| is a function handle
//for the anonymous function that can then be used to evaluate the expression.
//Note that @|y| will capture the value of variables that are not indicated
//in the argument list from the current scope or workspace at the time
//it is defined.  So, for example, consider the simple anonymous function
//definition
//@[
//   y = @(x) a*(x+b)
//@]
//In order for this definition to work, the variables @|a| and @|b| need to
//be defined in the current workspace.  Whatever value they have is captured
//in the function handle @|y|.  To change the values of @|a| and @|b| in the
//anonymous function, you must recreate the handle using another call.  See
//the examples section for more information.  In order to use the anonymous
//function, you can use it just like any other function handle.  For example,
//@[
//   p = y(3)
//   p = y()
//   p = feval(y,3)
//@]
//are all examples of using the @|y| anonymous function to perform a calculation.
//@@Examples
//Here are some examples of using an anonymous function
//@<
//a = 2; b = 4;    % define a and b (slope and intercept)
//y = @(x) a*x+b   % create the anonymous function
//y(2)             % evaluate it for x = 2
//a = 5; b = 7;    % change a and b
//y(2)             % the value did not change!  because a=2,b=4 are captured in y
//y = @(x) a*x+b   % recreate the function
//y(2)             % now the new values are used
//@>
//!

//!
//@Module KEYWORDS Function Keywords
//@@Section FUNCTIONS
//@@Usage
//A feature of IDL that FreeMat has adopted is a modified
//form of @|keywords|.  The purpose of @|keywords| is to 
//allow you to call a function with the arguments to the
//function specified in an arbitrary order.  To specify
//the syntax of @|keywords|, suppose there is a function 
//with prototype
//@[
//  function [out_1,...,out_M] = foo(in_1,...,in_N)
//@]
//Then the general syntax for calling function @|foo| using keywords
//is
//@[
//  foo(val_1, val_2, /in_k=3)
//@]
//which is exactly equivalent to
//@[
//  foo(val_1, val_2, [], [], ..., [], 3),
//@]
//where the 3 is passed as the k-th argument, or alternately,
//@[
//  foo(val_1, val_2, /in_k)
//@]
//which is exactly equivalent to
//@[
//  foo(val_1, val_2, [], [], ..., [], logical(1)),
//@]
//Note that you can even pass reference arguments using keywords.
//@@Example
//The most common use of keywords is in controlling options for
//functions.  For example, the following function takes a number
//of binary options that control its behavior.  For example,
//consider the following function with two arguments and two
//options.  The function has been written to properly use and
//handle keywords.  The result is much cleaner than the MATLAB
//approach involving testing all possible values of @|nargin|,
//and forcing explicit empty brackets for don't care parameters.
//@{ keyfunc.m
//function c = keyfunc(a,b,operation,printit)
//  if (~isset('a') | ~isset('b')) 
//    error('keyfunc requires at least the first two 2 arguments'); 
//  end;
//  if (~isset('operation'))
//    % user did not define the operation, default to '+'
//    operation = '+';
//  end
//  if (~isset('printit'))
//    % user did not specify the printit flag, default is false
//    printit = 0;
//  end
//  % simple operation...
//  eval(['c = a ' operation ' b;']);
//  if (printit) 
//    printf('%f %s %f = %f\n',a,operation,b,c);
//  end
//@}
//Now some examples of how this function can be called using
//@|keywords|.
//@<1
//keyfunc(1,3)                % specify a and b, defaults for the others
//keyfunc(1,3,/printit)       % specify printit is true
//keyfunc(/operation='-',2,3) % assigns a=2, b=3
//keyfunc(4,/operation='*',/printit) % error as b is unspecified
//@>
//!

//!
//@Module VARARGIN Variable Input Arguments
//@@Section FUNCTIONS
//@@Usage
//FreeMat functions can take a variable number of input arguments
//by setting the last argument in the argument list to @|varargin|.
//This special keyword indicates that all arguments to the
//function (beyond the last non-@|varargin| keyword) are assigned
//to a cell array named @|varargin| available to the function.
//Variable argument functions are usually used when writing 
//driver functions, i.e., functions that need to pass arguments
//to another function.  The general syntax for a function that
//takes a variable number of arguments is
//@[
//  function [out_1,...,out_M] = fname(in_1,..,in_M,varargin)
//@]
//Inside the function body, @|varargin| collects the arguments 
//to @|fname| that are not assigned to the @|in_k|.
//@@Example
//Here is a simple wrapper to @|feval| that demonstrates the
//use of variable arguments functions.
//@{ wrapcall.m
//function wrapcall(fname,varargin)
//  feval(fname,varargin{:});
//@}
//Now we show a call of the @|wrapcall| function with a number
//of arguments
//@<
//wrapcall('printf','%f...%f\n',pi,e)
//@>
//A more serious driver routine could, for example, optimize
//a one dimensional function that takes a number of auxilliary
//parameters that are passed through @|varargin|.
//!

//!
//@Module VARARGOUT Variable Output Arguments
//@@Section FUNCTIONS
//@@Usage
//FreeMat functions can return a variable number of output arguments
//by setting the last argument in the argument list to @|varargout|.
//This special keyword indicates that the number of return values
//is variable.  The general syntax for a function that returns
//a variable number of outputs is
//@[
//  function [out_1,...,out_M,varargout] = fname(in_1,...,in_M)
//@]
//The function is responsible for ensuring that @|varargout| is
//a cell array that contains the values to assign to the outputs
//beyond @|out_M|.  Generally, variable output functions use
//@|nargout| to figure out how many outputs have been requested.
//@@Example
//This is a function that returns a varying number of values
//depending on the value of the argument.
//@{ varoutfunc.m
//function [varargout] = varoutfunc
//  switch(nargout)
//    case 1
//      varargout = {'one of one'};
//    case 2
//      varargout = {'one of two','two of two'};
//    case 3
//      varargout = {'one of three','two of three','three of three'};
//  end
//@}
//Here are some examples of exercising @|varoutfunc|:
//@<
//[c1] = varoutfunc
//[c1,c2] = varoutfunc
//[c1,c2,c3] = varoutfunc
//@>
//!

//!
//@Module SCRIPT Script Files
//@@Section FUNCTIONS
//@@Usage
//A script is a sequence of FreeMat commands contained in a
//@|.m| file.  When the script is called (via the name of the
//file), the effect is the same as if the commands inside the
//script file were issued one at a time from the keyboard.
//Unlike @|function| files (which have the same extension,
//but have a @|function| declaration), script files share
//the same environment as their callers.  Hence, assignments,
//etc, made inside a script are visible to the caller (which
//is not the case for functions.
//@@Example
//Here is an example of a script that makes some simple 
//assignments and @|printf| statements.
//@{ tscript.m
//a = 13;
//printf('a is %d\n',a);
//b = a + 32
//@}
//If we execute the script and then look at the defined variables
//@<
//tscript
//who
//@>
//we see that @|a| and @|b| are defined appropriately.
//!

//!
//@Module NARGIN Number of Input Arguments
//@@Section FUNCTIONS
//@@Usage
//The special variable @|nargin| is defined inside of all
//functions.  It indicates how many arguments were passed
//to the function when it was called.  FreeMat allows for
//fewer arguments to be passed to a function than were declared,
//and @|nargin|, along with @|isset| can be used to determine
//exactly what subset of the arguments were defined.
//There is no syntax for the use of @|nargin| - it is 
//automatically defined inside the function body.
//@@Example
//Here is a function that is declared to take five 
//arguments, and that simply prints the value of @|nargin|
//each time it is called.
//@{ nargintest.m
//function nargintest(a1,a2,a3,a4,a5)
//  printf('nargin = %d\n',nargin);
//@}
//@<
//nargintest(3);
//nargintest(3,'h');
//nargintest(3,'h',1.34);
//nargintest(3,'h',1.34,pi,e);
//@>
//!

//!
//@Module NARGOUT Number of Output Arguments
//@@Section FUNCTIONS
//@@Usage
//The special variable @|nargout| is defined inside of all
//functions.  It indicates how many return values were requested from
//the function when it was called.  FreeMat allows for
//fewer return values to be requested from a function than were declared,
//and @|nargout| can be used to determine exactly what subset of 
//the functions outputs are required.  There is no syntax for 
//the use of @|nargout| - it is automatically defined inside 
//the function body.
//@@Example
//Here is a function that is declared to return five 
//values, and that simply prints the value of @|nargout|
//each time it is called.
//@{ nargouttest.m
//function [a1,a2,a3,a4,a5] = nargouttest
//  printf('nargout = %d\n',nargout);
//  a1 = 1; a2 = 2; a3 = 3; a4 = 4; a5 = 5;
//@}
//@<
//a1 = nargouttest
//[a1,a2] = nargouttest
//[a1,a2,a3] = nargouttest
//[a1,a2,a3,a4,a5] = nargouttest
//@>
//!
  
//!
//@Module SPECIAL Special Calling Syntax
//@@Section FUNCTIONS
//@@Usage
//To reduce the effort to call certain functions, FreeMat supports
//a special calling syntax for functions that take string arguments.
//In particular, the three following syntaxes are equivalent, with
//one caveat:
//@[
//   functionname('arg1','arg2',...,'argn')
//@]
//or the parenthesis and commas can be removed
//@[
//   functionname 'arg1' 'arg2' ... 'argn'
//@]
//The quotes are also optional (providing, of course, that the
//argument strings have no spaces in them)
//@[
//   functionname arg1 arg2 ... argn
//@]
//This special syntax enables you to type @|hold on| instead of
//the more cumbersome @|hold('on')|.  The caveat is that FreeMat
//currently only recognizes the special calling syntax as the
//first statement on a line of input.  Thus, the following construction
//@[
//  for i=1:10; plot(vec(i)); hold on; end
//@]
//would not work.  This limitation may be removed in a future
//version.
//@@Example
//Here is a function that takes two string arguments and
//returns the concatenation of them.
//@{ strcattest.m
//function strcattest(str1,str2)
//  str3 = [str1,str2];
//  printf('str1 = %s, str2 = %s, str3 = %s\n',str1,str2,str3);
//@}
//We call @|strcattest| using all three syntaxes.
//@<
//strcattest('hi','ho')
//strcattest 'hi' 'ho'
//strcattest hi ho
//@>
//!
void Interpreter::collectKeywords(const tree &q, ArrayVector &keyvals,
				  treeVector &keyexpr, stringVector &keywords) {
  // Search for the keyword uses - 
  // To handle keywords, we make one pass through the arguments,
  // recording a list of keywords used and using ::expression to
  // evaluate their values. 
  for (unsigned index=0;index < q.numchildren();index++) {
    if (q.child(index).is(TOK_KEYWORD)) {
      keywords.push_back(q.child(index).first().text());
      if (q.child(index).numchildren() > 1) {
	keyvals.push_back(expression(q.child(index).second()));
	keyexpr.push_back(q.child(index).second());
      } else {
	keyvals.push_back(Array::logicalConstructor(true));
	keyexpr.push_back(tree());
      }
    }
  }
}

int* Interpreter::sortKeywords(ArrayVector &m, stringVector &keywords,
			       stringVector arguments, ArrayVector keyvals) {
  // If keywords were used, we have to permute the
  // entries of the arrayvector to the correct order.
  int *keywordNdx = new int[keywords.size()];
  int maxndx;
  maxndx = 0;
  // Map each keyword to an argument number
  for (int i=0;i<keywords.size();i++) {
    int ndx;
    ndx = getArgumentIndex(arguments,keywords[i]);
    if (ndx == -1)
      throw Exception("out-of-order argument /" + keywords[i] + " is not defined in the called function!");
    keywordNdx[i] = ndx;
    if (ndx > maxndx) maxndx = ndx;
  }
  // Next, we have to determine how many "holes" there are
  // in the argument list - we get the maximum list
  int holes;
  holes = maxndx + 1 - keywords.size();
  // At this point, holes is the number of missing arguments
  // If holes > m.size(), then the total number of arguments
  // is just maxndx+1.  Otherwise, its 
  // maxndx+1+(m.size() - holes)
  int totalCount;
  if (holes > m.size())
    totalCount = maxndx+1;
  else
    totalCount = maxndx+1+(m.size() - holes);
  // Next, we allocate a vector to hold the values
  ArrayVector toFill;
  for (int i=0;i<totalCount;i++)
    toFill.push_back(Array());
  //  ArrayVector toFill(totalCount);
  bool *filled = new bool[totalCount];
  int *argTypeMap = new int[totalCount];
  for (int i=0;i<totalCount;i++) {
    filled[i] = false;
    argTypeMap[i] = -1;
  }
  // Finally...
  // Copy the keyword values in
  for (int i=0;i<keywords.size();i++) {
    toFill[keywordNdx[i]] = keyvals[i];
    filled[keywordNdx[i]] = true;
    argTypeMap[keywordNdx[i]] = i;
  }
  // Fill out the rest of the values from m
  int n = 0;
  int p = 0;
  while (n < m.size()) {
    if (!filled[p]) {
      toFill[p] = m[n];
      filled[p] = true;
      argTypeMap[p] = -2;
      n++;
    } 
    p++;
  }
  // Finally, fill in empty matrices for the
  // remaining arguments
  for (int i=0;i<totalCount;i++)
    if (!filled[i])
      toFill[i] = Array::emptyConstructor();
  // Clean up
  delete[] filled;
  delete[] keywordNdx;
  // Reassign
  m = toFill;
  return argTypeMap;
}

// arguments is exactly what it should be - the vector of arguments
// m is vector of argument values
// keywords is the list of values passed as keywords
// keyexpr is the   
void Interpreter::handlePassByReference(const tree &q, stringVector arguments,
					ArrayVector m,stringVector keywords, 
					treeVector keyexpr, int* argTypeMap) {
  tree p;
  // M functions can modify their arguments
  int maxsearch = m.size(); 
  if (maxsearch > arguments.size()) maxsearch = arguments.size();
  int qindx = 0;
  for (int i=0;i<maxsearch;i++) {
    // Was this argument passed out of order?
    if ((keywords.size() > 0) && (argTypeMap[i] == -1)) continue;
    if ((keywords.size() > 0) && (argTypeMap[i] >=0)) {
      p = keyexpr[argTypeMap[i]];
    } else {
      p = q.second().child(qindx);
      qindx++;
      if (qindx >= q.second().numchildren())
	qindx = q.second().numchildren()-1;
    }
    std::string args(arguments[i]);
    if (args[0] == '&') {
      args.erase(0,1);
      // This argument was passed by reference
      if (!p.valid() || !(p.is(TOK_VARIABLE)))
	throw Exception("Must have lvalue in argument passed by reference");
      assignment(p,false,m[i]);
    }
  }
}

static ArrayVector mergeVecs(ArrayVector a, ArrayVector b) {
  for (int i=0;i<b.size();i++)
    a.push_back(b[i]);
  return a;
}

//Test
void Interpreter::functionExpression(const tree &t, 
				     int narg_out, 
				     bool outputOptional,
				     ArrayVector &output) {
  ArrayVector m, n;
  stringVector keywords;
  ArrayVector keyvals;
  treeVector keyexpr;
  int i;
  FuncPtr funcDef;
  int* argTypeMap;
  bool CLIFlagsave;
  CLIFlagsave = InCLI;
  int ctxt = t.context();

    
  try {
    // Because of the introduction of user-defined classes, we have to 
    // first evaluate the keywords and the arguments, before we know
    // which function to call.
    // First, check for arguments
    if ((t.numchildren()>=2) && t.second().is(TOK_PARENS)) {
      // Collect keywords
      collectKeywords(t.second(),keyvals,keyexpr,keywords);
      // Evaluate function arguments
      try {
	const tree &s(t.second());
	for (unsigned p=0;p<s.numchildren();p++)
	  multiexpr(s.child(p),m);
      } catch (Exception &e) {
	// Transmute the error message about illegal use of ':'
	// into one about undefined variables.  Its crufty,
	// but it works.
	if (e.matches("Illegal use of the ':' operator"))
	  throw Exception("Undefined variable " + t.text());
	else
	  throw;
      }
    } 
    // Now that the arguments have been evaluated, we have to 
    // find the dominant class
    if (!lookupFunction(t.first().text(),funcDef,m))
      throw Exception("Undefined function or variable " + 
		      t.first().text());
    if (funcDef->updateCode(this)) refreshBreakpoints();
    if (funcDef->scriptFlag) {
      if (t.numchildren()>=2)
	throw Exception(std::string("Cannot use arguments in a call to a script."));
      if ((narg_out > 0) && !outputOptional)
	throw Exception(std::string("Cannot assign outputs in a call to a script."));
      CLIFlagsave = InCLI;
      InCLI = false;
      pushDebug(((MFunctionDef*)funcDef)->fileName,
		((MFunctionDef*)funcDef)->name);
      block(((MFunctionDef*)funcDef)->code);
      if ((steptrap >= 1) && (ip_detailname == stepname)) {
	if ((cstack.size() > 0) && (cstack.back().cname != "Eval")) {
	  warningMessage("dbstep beyond end of script " + stepname +
			 ".\n Setting single step mode for " + 
			 cstack.back().detail);
	  stepname = cstack.back().detail;
	} else
	  steptrap = 0;
      }
      popDebug();
      InCLI = CLIFlagsave;
    } else {
      // We can now adjust the keywords (because we know the argument list)
      // Apply keyword mapping
      if (!keywords.empty()) 
	argTypeMap = sortKeywords(m,keywords,funcDef->arguments,keyvals);
      else
	argTypeMap = NULL;
      if ((funcDef->inputArgCount() >= 0) && 
	  (m.size() > funcDef->inputArgCount()))
	throw Exception(std::string("Too many inputs to function ")+t.first().text());
      if ((funcDef->outputArgCount() >= 0) && 
	  (narg_out > funcDef->outputArgCount() && !outputOptional))
	throw Exception(std::string("Too many outputs to function ")+t.first().text());
      CLIFlagsave = InCLI;
      InCLI = false;
      if (!funcDef->graphicsFunction)
	n = funcDef->evaluateFunction(this,m,narg_out);
      else
	n = doGraphicsFunction(funcDef,m,narg_out);
      if ((steptrap >= 1) && (funcDef->name == stepname)) {
	if ((cstack.size() > 0) && (ip_funcname != "Eval")) {
	  warningMessage("dbstep beyond end of function " + stepname +
			 ".\n Setting single step mode for " + 
			 ip_detailname);
	  stepname = ip_detailname;
	} else
	  steptrap = 0;
      }
      InCLI = CLIFlagsave;
      // Check for any pass by reference
      if (t.haschildren() && (funcDef->arguments.size() > 0)) 
	handlePassByReference(t,funcDef->arguments,m,keywords,keyexpr,argTypeMap);
    }
    // Some routines (e.g., min and max) will return more outputs
    // than were actually requested... so here we have to trim 
    // any elements received that we didn't ask for.
    // preserve one output if we were called as an expression (for ans)
    if (outputOptional) narg_out = (narg_out == 0) ? 1 : narg_out;
    while (n.size() > narg_out)
      n.pop_back();
    output += n;
  } catch (Exception& e) {
    InCLI = CLIFlagsave;
    throw;
  } catch (InterpreterRetallException& e) {
    throw;
  }
}

void Interpreter::toggleBP(QString fname, int lineNumber) {
  if (isBPSet(fname,lineNumber)) {
    string fname_string(fname.toStdString());
    for (int i=0;i<bpStack.size();i++) 
      if ((bpStack[i].cname == fname_string) &&
	  ((bpStack[i].tokid & 0xffff) == lineNumber)) {
	//	qDebug() << "Deleting bp " << i << " w/number " << bpStack[i].number << "";
	deleteBreakpoint(bpStack[i].number);
	return;
      }
  } else {
    addBreakpoint(fname.toStdString(),lineNumber);
  }    
}

MFunctionDef* Interpreter::lookupFullPath(string fname) {
  stringVector allFuncs(context->listAllFunctions());
  FuncPtr val;
  for (int i=0;i<allFuncs.size();i++) {
    bool isFun = context->lookupFunction(allFuncs[i],val);
    if (val->type() == FM_M_FUNCTION) {
      MFunctionDef *mptr;
      mptr = (MFunctionDef *) val;
      if (mptr->fileName ==  fname) return mptr;
    }
  }
  return NULL;
}

static int bpList = 1;
// Add a breakpoint - name is used to track to a full filename
void Interpreter::addBreakpoint(string name, int line) {
  //  qDebug() << "Add bp " << QString::fromStdString(name) << " : " << line << "";
  FuncPtr val;
  // Map the name argument to a full file name.
  string fullFileName;
  if (context->lookupFunction(name,val) && (val->type() == FM_M_FUNCTION))
    fullFileName = ((MFunctionDef*) val)->fileName;
  else
    fullFileName = name;
  // Get a list of all functions
  stringVector allFuncs(context->listAllFunctions());
  // We make one pass through the functions, and update 
  // those functions that belong to the given filename
  for (int i=0;i<allFuncs.size();i++) {
    bool isFun = context->lookupFunction(allFuncs[i],val);
    if (val->type() == FM_M_FUNCTION) {
      MFunctionDef *mptr = (MFunctionDef *) val;
      if (mptr->fileName == fullFileName)
	mptr->updateCode(this);
    }
  }
  // Refresh the list of all functions
  allFuncs = context->listAllFunctions();
  // Search through the list for any function defined  - for each function,
  // record the line number closest to it
  MemBlock<int> line_dist_block(allFuncs.size());
  int *line_dist = &line_dist_block;
  for (int i=0;i<allFuncs.size();i++) line_dist[i] = 2*max_line_count;
  for (int i=0;i<allFuncs.size();i++) {
    bool isFun = context->lookupFunction(allFuncs[i],val);
    if (val->type() == FM_M_FUNCTION) {
      MFunctionDef *mptr = (MFunctionDef *) val;
      if (mptr->fileName == fullFileName) {
	try {
	  int dline = mptr->ClosestLine(line);
	  line_dist[i] = dline;
	} catch (Exception& e) {
	}
      }
    }
  }
  // Second pass through it - find the function with a line number closest to the
  // desired one, but not less than it
  int best_func = -1;
  int best_dist = 2*max_line_count;
  for (int i=0;i<allFuncs.size();i++) {
    if ((line_dist[i] >= line) && ((line_dist[i]-line) < best_dist)) {
      best_func = i;
      best_dist = line_dist[i]-line;
    }
  }
  if (best_dist > max_line_count)
    warningMessage(std::string("Cannot set breakpoint at line ")+line+" of file "+name + ".  \r\nThis can be caused by an illegal line number, or a function that is not on the path or in the current directory.");
  else {
    addBreakpoint(stackentry(fullFileName,allFuncs[best_func],best_dist+line,bpList++));
  }
}

bool Interpreter::isBPSet(QString fname, int lineNumber) {
  string fname_string(fname.toStdString());
  for (int i=0;i<bpStack.size();i++) 
    if ((bpStack[i].cname == fname_string) &&
	((bpStack[i].tokid & 0xffff) == lineNumber)) return true;
  return false;
}

bool Interpreter::isInstructionPointer(QString fname, int lineNumber) {
  return ((fname.toStdString() == ip_funcname) && ((lineNumber == (ip_context & 0xffff)) || ((lineNumber == 1) && ((ip_context & 0xffff) == 0))));
}

void Interpreter::listBreakpoints() {
  for (int i=0;i<bpStack.size();i++) {
    //    if (bpStack[i].number > 0) {
    char buffer[2048];
    snprintf(buffer,2048,"%d   %s line %d\n",bpStack[i].number,
	     bpStack[i].cname.c_str(),bpStack[i].tokid & 0xffff);
    outputMessage(buffer);
    //    }
  }
}

void Interpreter::deleteBreakpoint(int number) {
  for (int i=0;i<bpStack.size();i++) 
    if (bpStack[i].number == number) {
      //      setBreakpoint(bpStack[i],false);
      bpStack.erase(bpStack.begin()+i);
      emit RefreshBPLists();
      return;
    } 
  warningMessage("Unable to delete specified breakpoint (does not exist)");
  emit RefreshBPLists();
  return;
}

void Interpreter::stackTrace(bool includeCurrent) {
  for (int i=0;i<cstack.size();i++) {
    std::string cname_trim(TrimExtension(TrimFilename(cstack[i].cname)));
    outputMessage(string("In ") + cname_trim + "("
		  + cstack[i].detail + ") on line " +
		  (cstack[i].tokid & 0x0000FFFF) + "\r\n");
  }
  if (includeCurrent) {
    std::string ip_trim(TrimExtension(TrimFilename(ip_funcname)));
    outputMessage(string("In ") + ip_trim + "("
		  + ip_detailname + ") on line " +
		  (ip_context & 0x0000FFFF) + "\r\n");
  }
}

bool Interpreter::inMethodCall(std::string classname) {
  if (ip_detailname.empty()) return false;
  if (ip_detailname[0] != '@') return false;
  return (ip_detailname.compare(1,classname.size(),classname)==0);
}

void Interpreter::pushDebug(std::string fname, std::string detail) {
  cstack.push_back(stackentry(ip_funcname,ip_detailname,
			      ip_context,0,steptrap,
			      stepcurrentline));
  //  qDebug() << "Push Debug: " << QString::fromStdString(fname) << ",";
  //  qDebug() << QString::fromStdString(detail) << "";
  ip_funcname = fname;
  ip_detailname = detail;
  ip_context = 0;
  steptrap = 0;
  stepcurrentline = 0;
}

void Interpreter::popDebug() {
  if (!cstack.empty()) {
    //    qDebug() << "Pop Debug";
    ip_funcname = cstack.back().cname;
    ip_detailname = cstack.back().detail;
    ip_context = cstack.back().tokid;
    steptrap = cstack.back().steptrap;
    stepcurrentline = cstack.back().stepcurrentline;
    cstack.pop_back();
  } else
    outputMessage("IDERROR\n");
}

bool Interpreter::isUserClassDefined(std::string classname) {
  UserClass *ret;
  return (classTable.findSymbol(classname)!=NULL);
}
  
UserClass Interpreter::lookupUserClass(std::string classname) {
  return(*(classTable.findSymbol(classname)));
}

void Interpreter::registerUserClass(std::string classname, UserClass cdata) {
  classTable.insertSymbol(classname,cdata);
}

void Interpreter::clearUserClasses() {
  classTable = SymbolTable<UserClass>();
}

bool Interpreter::lookupFunction(std::string funcName, FuncPtr& val) {
  ArrayVector dummy;
  return(lookupFunction(funcName,val,dummy));
}

bool IsNestedName(std::string basename) {
  return (basename.rfind("/") >= 0);
}

std::string StripNestLevel(std::string basename) {
  int ndx = basename.rfind("/");
  if (ndx >= 0)
    basename.erase(ndx,basename.size());
  else
    basename = "";
  return basename;
}

// Look up a function by name.  Use the arguments (if available) to assist
// in resolving method calls for objects
bool Interpreter::lookupFunction(std::string funcName, FuncPtr& val, 
				 ArrayVector &args, bool disableOverload) {
  int passcount = 0;
  while(passcount < 2) {
    // This is the order for function dispatch according to the Matlab manual
    // Nested functions - not explicitly listed in the Matlab manual, but 
    // I figure they have the highest priority in the current scope.
    if (isMFile(ip_funcname) &&
	(context->lookupFunction(NestedMangleName(ip_detailname,funcName),val)))
      return true;
    // Not a nested function of the current scope.  We have to look for nested
    // functions of all parent scopes. Sigh.
    if (context->isCurrentScopeNested()) {
      std::string basename = ip_detailname;
      while (!basename.empty()) {
	if (context->lookupFunction(NestedMangleName(basename,funcName),val))
	  return true;
	basename = StripNestLevel(basename);
      }
    }
    // Subfunctions
    if (isMFile(ip_funcname) && 
	(context->lookupFunction(getLocalMangledName(funcName),val)))
      return true;
    // Private functions
    // Not sure if you have to be an M-file in the current directory
    // to access a private function...
    if (context->lookupFunction(getPrivateMangledName(funcName),val))
      return true;
    // Class constructor functions
    if (context->lookupFunction(ClassMangleName(funcName,funcName),val))
      return true;
    if (!(disableOverload || stopoverload)) {
      // Look for a class method
      // Are any of the arguments classes?
      bool anyClasses = false;
      int i=0;
      while ((!anyClasses) && (i < args.size())) {
	anyClasses = args[i].isUserClass();
	if (!anyClasses) i++;
      }
      // Yes, try and resolve the call to a method
      if (anyClasses && ClassResolveFunction(this,args[i],funcName,val))
	return true;
    }
    if (context->lookupFunction(funcName,val)) {
      //      qDebug() << "Lookup " << QString::fromStdString(funcName);
      return true;
    }
    if (passcount == 0)
      rescanPath();
    passcount++;
  }
  return false;
}

//!
//@Module Function Handles
//@@Section VARIABLES
//@@Usage
//Starting with version 1.11, FreeMat now supports @|function handles|,
//or @|function pointers|.  A @|function handle| is an alias for a function
//or script that is stored in a variable.  First, the way to assign
//a function handle is to use the notation
//@[
//    handle = @func
//@]
//where @|func| is the name to point to.  The function @|func| must exist
//at the time we make the call.  It can be a local function (i.e., a
//subfunction).  To use the @|handle|, we can either pass it to @|feval|
//via 
//@[
//   [x,y] = feval(handle,arg1,arg2).
//@]
//Alternately, you can the function directly using the notation
//@[
//   [x,y] = handle(arg1,arg2)
//@]
//!
//Works
ArrayVector Interpreter::FunctionPointerDispatch(Array r, const tree &args, 
						 int narg_out) {
  const FunctionDef** dp;
  bool CLIFlagsave;
  dp = (const FunctionDef**) r.getDataPointer();
  FunctionDef* fun = (FunctionDef*) dp[0];
  if (!fun) return ArrayVector();
  if (!args.is(TOK_PARENS))
    throw Exception("Expected either '()' or function arguments inside parenthesis");
  ArrayVector m;
  for (unsigned p = 0; p< args.numchildren(); p++)
    multiexpr(args.child(p),m);
  ArrayVector n;
  if (fun->updateCode(this)) refreshBreakpoints();
  if (fun->scriptFlag) {
    if (!m.empty())
      throw Exception(std::string("Cannot use arguments in a call to a script."));
    CLIFlagsave = InCLI;
    InCLI = false;
    pushDebug(((MFunctionDef*)fun)->fileName,
	      ((MFunctionDef*)fun)->name);
    try {
      block(((MFunctionDef*)fun)->code);
    } catch (InterpreterReturnException& e) {
    }
    popDebug();
    InCLI = CLIFlagsave;
  } else {
    CLIFlagsave = InCLI;
    InCLI = false;
    //HACK!
    int narg_out = 1;
    n = fun->evaluateFunction(this,m,narg_out);
    InCLI = CLIFlagsave;
  }
  return n;
}

//!
//@Module INDEXING Indexing Expressions
//@@Section VARIABLES
//@@Usage
//There are three classes of indexing expressions available 
//in FreeMat: @|()|, @|{}|, and @|.|  Each is explained below
//in some detail, and with its own example section.
//@@Array Indexing
//We start with array indexing @|()|,
//which is the most general indexing expression, and can be
//used on any array.  There are two general forms for the 
//indexing expression - the N-dimensional form, for which 
//the general syntax is
//@[
//  variable(index_1,index_2,...,index_n)
//@]
//and the vector form, for which the general syntax is
//@[
//  variable(index)
//@]
//Here each index expression is either a scalar, a range
//of integer values, or the special token @|:|, which is
//shorthand for @|1:end|.  The keyword @|end|, when included
//in an indexing expression, is assigned the length of the 
//array in that dimension.  The concept is easier to demonstrate
//than explain.  Consider the following examples:
//@<
//A = zeros(4)
//B = float(randn(2))
//A(2:3,2:3) = B
//@>
//Here the array indexing was used on the left hand side only.
//It can also be used for right hand side indexing, as in
//@<
//C = A(2:3,1:end)
//@>
//Note that we used the @|end| keyword to avoid having to know
//that @|A| has 4 columns.  Of course, we could also use the 
//@|:| token instead:
//@<
//C = A(2:3,:)
//@>
//An extremely useful example of @|:| with array indexing is for
//slicing.  Suppose we have a 3-D array, that is @|2 x 2 x 3|,
//and we want to set the middle slice:
//@<
//D = zeros(2,2,3)
//D(:,:,2) = int32(10*rand(2,2))
//@>
//In another level of nuance, the assignment expression will
//automatically fill in the indexed rectangle on the left using
//data from the right hand side, as long as the lengths match.
//So we can take a vector and roll it into a matrix using this
//approach:
//@<
//A = zeros(4)
//v = [1;2;3;4]
//A(2:3,2:3) = v
//@>
//
//The N-dimensional form of the variable index is limited
//to accessing only (hyper-) rectangular regions of the 
//array.  You cannot, for example, use it to access only
//the diagonal elements of the array.  To do that, you use
//the second form of the array access (or a loop).  The
//vector form treats an arbitrary N-dimensional array as though
//it were a column vector.  You can then access arbitrary 
//subsets of the arrays elements (for example, through a @|find|
//expression) efficiently.  Note that in vector form, the @|end|
//keyword takes the meaning of the total length of the array
//(defined as the product of its dimensions), as opposed to the
//size along the first dimension.
//@@Cell Indexing
//The second form of indexing operates, to a large extent, in
//the same manner as the array indexing, but it is by no means
//interchangable.  As the name implies, @|cell|-indexing applies
//only to @|cell| arrays.  For those familiar with @|C|, cell-
//indexing is equivalent to pointer derefencing in @|C|.  First,
//the syntax:
//@[
//  variable{index_1,index_2,...,index_n}
//@]
//and the vector form, for which the general syntax is
//@[
//  variable{index}
//@]
//The rules and interpretation for N-dimensional and vector indexing
//are identical to @|()|, so we will describe only the differences.
//In simple terms, applying @|()| to a cell-array returns another
//cell array that is a subset of the original array.  On the other
//hand, applying @|{}| to a cell-array returns the contents of that
//cell array.  A simple example makes the difference quite clear:
//@<
//A = {1, 'hello', [1:4]}
//A(1:2)
//A{1:2}
//@>
//You may be surprised by the response to the last line.  The output
//is multiple assignments to @|ans|!.  The output of a cell-array
//dereference can be used anywhere a list of expressions is required.
//This includes arguments and returns for function calls, matrix
//construction, etc.  Here is an example of using cell-arrays to pass
//parameters to a function:
//@<
//A = {[1,3,0],[5,2,7]}
//max(A{1:end})
//@>
//And here, cell-arrays are used to capture the return.
//@<
//[K{1:2}] = max(randn(1,4))
//@>
//Here, cell-arrays are used in the matrix construction process:
//@<
//C = [A{1};A{2}]
//@>
//Note that this form of indexing is used to implement variable
//length arguments to function.  See @|varargin| and @|varargout|
//for more details.
//@@Structure Indexing
//The third form of indexing is structure indexing.  It can only
//be applied to structure arrays, and has the general syntax
//@[
//  variable.fieldname
//@]
//where @|fieldname| is one of the fields on the structure.  Note that
//in FreeMat, fields are allocated dynamically, so if you reference
//a field that does not exist in an assignment, it is created automatically
//for you.  If variable is an array, then the result of the @|.| 
//reference is an expression list, exactly like the @|{}| operator.  Hence,
//we can use structure indexing in a simple fashion:
//@<
//clear A
//A.color = 'blue'
//B = A.color
//@>
//Or in more complicated ways using expression lists for function arguments
//@<
//clear A
//A(1).maxargs = [1,6,7,3]
//A(2).maxargs = [5,2,9,0]
//max(A.maxargs)
//@>
//or to store function outputs
//@<
//clear A
//A(1).maxreturn = [];
//A(2).maxreturn = [];
//[A.maxreturn] = max(randn(1,4))
//@>
//FreeMat now also supports the so called dynamic-field indexing 
//expressions.  In this mode, the fieldname is supplied through 
//an expression instead of being explicitly provided.  For example,
//suppose we have a set of structure indexed by color,
//@<
//x.red = 430;
//x.green = 240;
//x.blue = 53;
//x.yello = 105
//@>
//Then we can index into the structure @|x| using a dynamic field
//reference:
//@<
//y = 'green'
//a = x.(y)
//@>
//Note that the indexing expression has to resolve to a string for
//dynamic field indexing to work.
//@@Complex Indexing
//The indexing expressions described above can be freely combined
//to affect complicated indexing expressions.  Here is an example
//that exercises all three indexing expressions in one assignment.
//@<
//Z{3}.foo(2) = pi
//@>
//From this statement, FreeMat infers that Z is a cell-array of 
//length 3, that the third element is a structure array (with one
//element), and that this structure array contains a field named
//'foo' with two double elements, the second of which is assigned
//a value of pi.
//@@Tests
//@{ test_subset1.m
//% Test the vector subset operator
//function test_val = test_subset1
//a = [1,2,3,4;5,6,7,8];
//b = [3;4;5];
//c = a(b);
//test_val = test(c==[2;6;3]);
//@}
//@{ test_subset2.m
//% Test the vector subset operator in an error condition
//function test_val = test_subset2
//a = [1,2,3,4];
//b = [4,5,6];
//test_val = 0;
//try
//c = a(b);
//catch
//test_val = 1;
//end
//@}
//@{ test_subset3.m
//% Test the vector subset with a cell array
//function test_val = test_subset3
//a = {1,2,3,4;5,6,7,8};
//b = [3;4];
//c = a(b);
//test_val = test((c{1} == 2) & (c{2} == 6));
//@}
//@{ test_subset4.m
//% Test the vector subset with a struct array
//function test_val = test_subset4
//a = struct('foo',4,'goo',{1,2,3,4});
//c = a(3);
//test_val = test((c.foo == 4) & (c.goo == 3));
//@}
//@{ test_subset5.m
//% Test the vector subset with cell array without contents-based addressing
//function test_val = test_subset5
//a = {1,2,3,4;5,6,7,8};
//b = a(2);
//test_val = test(strcmp(typeof(b),'cell')) & test(b{1} == 5);
//@}
//@{ test_subset6.m
//% Test the ndim subset with a regular vector
//function test_val = test_subset6
//a = [1,2,3;4,5,6;7,8,9];
//b = a(1:2,2:3);
//c = [2,3;5,6];
//test_val = test(b == c);
//@}
//@{ test_subset7.m
//% Test the ndim subset with a cell-array vector
//function test_val = test_subset7
//a = {1,2,3;4,5,6;7,8,9};
//b = a(2,2:3);
//test_val = test(strcmp(typeof(b),'cell')) & test((b{1} == 5) & (b{2} == 6));
//@}
//@{ test_subset8.m
//% Test the ndim subset with a cell-array vector
//function test_val = test_subset8
//a = {1,2,3;4,5,6;7,8,9};
//c = struct('foo',a);
//b = c(2,2:3);
//test_val = test(strcmp(typeof(b),'struct')) & ...
//               test((b(1).foo == 5) & (b(2).foo == 6));
//@}
//@{ test_subset9.m
//% Test the cell vector subset with contents-based addressing
//function test_val = test_subset9
//  a = {5,8;10,3};
//  sm = test_subset9_assist(a{1:3});
//  test_val = test(sm == 23);
//
//function a = test_subset9_assist(b,c,d)
//  a = b + c + d;
//@}
//@{ test_subset10.m
//% Test the cell vector subset with contents-based addressing
//function test_val = test_subset10
//  a = {5,8;10,3};
//  sm = test_subset10_assist(a{2,1:2});
//  test_val = test(sm == 13);
//
//function a = test_subset10_assist(b,c)
//  a = b + c;
//@}
//@{ test_subset11.m
//% Test the vector subset assign function
//function test_val = test_subset11
//a = [1,2,3;4,5,6];
//a(3:4) = [8,9];
//test_val = test(a == [1,8,3;4,9,6]);
//@}
//@{ test_subset12.m
//% Test the ndim subset assign function
//function test_val = test_subset12
//a = [1,2,3;4,5,6];
//a(:,2) = [8,9];
//test_val = test(a == [1,8,3;4,9,6]);
//@}
//@{ test_subset13.m
//% Test the ndim subset contents-based assign function
//function test_val = test_subset13
//a = {1,2,3;4,5,6};
//a{4} = 'goo';
//test_val = test(strcmp(a{2,2},'goo'));
//@}
//@{ test_subset14.m
//% Test the vector contents-based assign for multiple values
//function test_val = test_subset14
//a = {0,0,0,0};
//[a{1:4}] = test_subset14_assist;
//test_val = test(strcmp(typeof(a),'cell')) & ...
//    (a{1} == 1) & (a{2} == 2) & (a{3} == 3) & (a{4} == 4);
//
//function [a,b,c,d] = test_subset14_assist
//  a = 1;
//  b = 2;
//  c = 3;
//  d = 4;
//@}
//@{ test_subset15.m
//% Test the ndim contents-based assign for multiple values
//function test_val = test_subset15
//a = {0,0;0,0};
//[a{1:2,1:2}] = test_subset15_assist;
//test_val = test(strcmp(typeof(a),'cell')) & ...
//    (a{1} == 1) & (a{2} == 2) & (a{3} == 3) & (a{4} == 4);
//
//function [a,b,c,d] = test_subset15_assist
//  a = 1;
//  b = 2;
//  c = 3;
//  d = 4;
//@}
//@{ test_subset16.m
//% Test the ndim contents-based assign for multiple values - with
//% too many outputs
//function test_val = test_subset16
//a = {0,0;0,0};
//test_val = 0;
//try
//  [a{1:2,1:2}] = test_subset16_assist
//catch
//  test_val = 1;
//end
//
//function [a,b,c] = test_subset16_assist
//  a = 1;
//  b = 2;
//  c = 3;
//@}
//@{ test_subset17.m
//% Test the subset assign with an empty variable
//function test_val = test_subset17
//a = [];
//a(:) = [];
//test_val = 1;
//@}
//@{ test_subset18.m
//% Test the subset assign with an empty variable
//function test_val = test_subset18
//a = [];
//b = 1:4;
//a(:) = b;
//test_val = (a(1) == 1) & (a(2) == 2) & (a(3) == 3) & (a(4) == 4);
//@}
//@{ test_subset19.m
//% Torture test for subset indexing
//function test_val = test_subset19
//a1 = 5;
//A{3}(2).foo(2) = a1;
//test_val = test(strcmp(typeof(A),'cell')) & ...
//    test(strcmp(typeof(A{3}),'struct')) & ...
//    test(strcmp(typeof(A{3}(2).foo),'int32'));
//@}
//@{ test_subset20.m
//function x = test_subset20
//  a = [];
//  b = rand(14,1);
//  a(:,1) = b;
//  x = testeq(a,b);
//@}
//@{ test_subset21.m
//function x = test_subset21
//  a = zeros(4,4,5);
//  p = randn(4,4,1);
//  a(:,:,end) = p;
//  x = 1;
//@}
//!


// This has a few shortcomings that prevent it from being
// 100% correct.  
//
//   1.  subsindex is not called for argument
//       expressions of user-defined classes.
//   2.  "end" no longer works.
//
// To fix "end", we should use a source transformation technique.
// The original tree looks like this
//
//   variable
//      -> t
//      -> ()
//          -> 2
//          -> end
//
// This should be translated into:
//
//   _t = end(t,2)
//
// This is done in Transform.cpp...  
//
// This does not cover:
//    Function pointers
//    subsindex
//
// 
 
//
// 
void Interpreter::deref(Array &r, const tree &s) {
  SaveEndInfo;
  endRef = &r;
  if (s.is(TOK_PARENS)) {
    ArrayVector m;
    endTotal = s.numchildren();
    if (s.numchildren() == 0) {
      r = r;
    } else {
      for (unsigned p = 0; p < s.numchildren(); p++) {
	endCount = m.size();
	multiexpr(s.child(p),m);
      }
      subsindex(m);
      if (m.size() == 1)
	r = r.getVectorSubset(m[0],this);
      else
	r = r.getNDimSubset(m,this);
    }
  } else if (s.is(TOK_BRACES)) {
    ArrayVector m;
    endTotal = s.numchildren();
    for (unsigned p = 0; p < s.numchildren(); p++) {
      endCount = m.size();
      multiexpr(s.child(p),m);
    }
    subsindex(m);
    if (m.size() == 1)
      r = r.getVectorContents(m[0],this);
    else
      r = r.getNDimContents(m,this);
  } else if (s.is('.')) {
    r = r.getField(s.first().text());
  } else if (s.is(TOK_DYN)) {
    string field;
    try {
      Array fname(expression(s.first()));
      field = fname.getContentsAsString();
    } catch (Exception &e) {
      throw Exception("dynamic field reference to structure requires a string argument");
    }
    r = r.getField(field);
  }
  RestoreEndInfo;
}
 
 Array Interpreter::rhs(const tree &t) {
   ArrayReference ptr(context->lookupVariable(t.first().text()));
   if (!ptr.valid()) {
     ArrayVector m;
     functionExpression(t,1,false,m);
     m = handleReindexing(t,m);
     if (m.size() >= 1)
       return m[0];
     else
       return Array::emptyConstructor();
   }
   if ((ptr->dataClass() == FM_FUNCPTR_ARRAY &&
	ptr->isScalar()) && (t.numchildren() > 1)) {
     ArrayVector m(FunctionPointerDispatch(*ptr,t.second(),1));
     m = handleReindexing(t,m);
     if (m.size() >= 1)
       return m[0];
     else
       return Array::emptyConstructor();
   }
   if (t.numchildren() == 1)
     return *ptr;
   if (ptr->isUserClass() && !stopoverload && !inMethodCall(ptr->className().back())) {
     ArrayVector m(ClassRHSExpression(*ptr,t,this));
     m = handleReindexing(t,m);
     if (m.size() >= 1)
       return m[0];
     else
       return Array::emptyConstructor();
   }
   Array r(*ptr);
   for (unsigned index = 1;index < t.numchildren();index++) 
     deref(r,t.child(index));
   return r;
 }

  
int Interpreter::getErrorCount() {
  int retval = errorCount;
  errorCount = 0;
  return retval;
}

Interpreter::Interpreter(Context* aContext) {
  errorCount = 0;
  lasterr = string("");
  context = aContext;
  depth = 0;
  printLimit = 1000;
  autostop = false;
  jitcontrol = false;
  InCLI = false;
  stopoverload = false;
  m_skipflag = false;
  clearStacks();
  steptrap = 0;
  stepcurrentline = 0;
  tracetrap = 0;
  tracecurrentline = 0;
  endRef = NULL;
  m_interrupt = false;
  m_kill = false;
  m_diaryState = false;
  m_diaryFilename = "diary";
  m_captureState = false;
  m_capture = "";
  m_quietlevel = 0;
}

Interpreter::~Interpreter() {
  delete context;
}


bool Interpreter::getStopOverload() {
  return stopoverload;
}

void Interpreter::setStopOverload(bool flag) {
  stopoverload = flag;
}

// We want dbstep(n) to cause us to advance n statements and then
// stop.  we execute statement-->set step trap,

void Interpreter::dbstepStatement(const tree &t) {
  int lines = 1;
  if (t.haschildren()) {
    Array lval(expression(t.first()));
    lines = lval.getContentsAsIntegerScalar();
  }
  // Get the current function
  if (cstack.size() < 1) throw Exception("cannot dbstep unless inside an M-function");
  stackentry bp(cstack[cstack.size()-1]);
  FuncPtr val;
  if (bp.detail == "base") return;
  if (!lookupFunction(bp.detail,val)) {
    warningMessage(string("unable to find function ") + bp.detail + " to single step");
    return;
  }
  cstack[cstack.size()-1].steptrap = lines;
  cstack[cstack.size()-1].stepcurrentline = bp.tokid & 0xffff;
//   qDebug() << "setting dbstep trap to current line " << 
//     cstack[cstack.size()-1].stepcurrentline << 
//     " with wait of " << lines << " lines";
}

void Interpreter::dbtraceStatement(const tree &t) {
  int lines = 1;
  if (t.haschildren()) {
    Array lval(expression(t.first()));
    lines = lval.getContentsAsIntegerScalar();
  }
  // Get the current function
  if (cstack.size() < 1) throw Exception("cannot dbtrace unless inside an M-function");
  stackentry bp(cstack[cstack.size()-1]);
  FuncPtr val;
  if (bp.detail == "base") return;
  if (!lookupFunction(bp.detail,val)) {
    warningMessage(string("unable to find function ") + bp.detail + " to single step");
    return;
  }
  tracetrap = lines;
  tracecurrentline = bp.tokid & 0xffff;
//   qDebug() << "setting dbtrace trap to current line " << 
//     tracecurrentline << " with wait of " << lines << " lines";
}

static string EvalPrep(string line) {
  string buf1 = line;
  if (buf1[buf1.size()-1] == '\n')
    buf1.erase(buf1.end()-1);
  if (buf1[buf1.size()-1] == '\r')
    buf1.erase(buf1.end()-1);
  if (buf1.size() > 20)
    buf1 = string(buf1,0,20) + "...";
  return buf1;
}

void Interpreter::ExecuteLine(std::string txt) {
  mutex.lock();
  cmd_buffer.push_back(txt);
  bufferNotEmpty.wakeAll();
  mutex.unlock();
  if (m_diaryState) diaryMessage(txt);
}

//PORT
void Interpreter::evaluateString(string line, bool propogateExceptions) {
  tree t;
  m_interrupt = false;
  Scanner S(line,"");
  Parser P(S);
  try{
    t = P.Process();
    if (!t.is(TOK_SCRIPT))
      throw Exception("Function definition unexpected!");
    t = t.first();
  } catch(Exception &e) {
    if (propogateExceptions)
      throw;
    errorCount++;
    e.printMe(this);
    return;
  }
  try {
    pushDebug("Eval",EvalPrep(line));
    try {
      block(t);
    } catch (InterpreterReturnException& e) {
      if (depth > 0) {
	popDebug();
	throw;
      }
    } catch (InterpreterQuitException& e) {
      popDebug();
      throw;
    } catch (InterpreterRetallException& e) {
      popDebug();
      throw;
    }
  } catch(Exception &e) {
    if (propogateExceptions) {
      popDebug();
      throw;
    }
    errorCount++;
    e.printMe(this);
  }
  popDebug();
}
  
string Interpreter::getLastErrorString() {
  return lasterr;
}

void Interpreter::setLastErrorString(string txt) {
  lasterr = txt;
}

void Interpreter::setGreetingFlag(bool skip) {
  m_skipflag = skip;
}

bool NeedsMoreInput(Interpreter *eval, string txt) {
  // Check for ... or an open []
  try {
    Scanner S(txt,"");
    while (!S.Next().Is(TOK_EOF))
      S.Consume();
    if (S.InContinuationState() || S.InBracket()) return true;
  } catch (Exception &e) {
  }
  try {
    Scanner S(txt,"");
    Parser P(S);
    tree root = P.Process();
    return false;
  } catch (Exception &e) {
    if (e.getMessageCopy().substr(0,13) == "Expecting end") {
      return true;
    }
  }
  return false;
}

void Interpreter::sleepMilliseconds(unsigned long msecs) {
  QThread::msleep(msecs);
}

string Interpreter::getLine(string prompt) {
  emit SetPrompt(prompt);
  if (m_diaryState) diaryMessage(prompt);
  string retstring;
  emit EnableRepaint();
  mutex.lock();
  if (cmd_buffer.empty())
    bufferNotEmpty.wait(&mutex);
  retstring = cmd_buffer.front();
  cmd_buffer.erase(cmd_buffer.begin());
  mutex.unlock();
  emit DisableRepaint();
  return retstring;
}

// This is a "generic" CLI routine.  The user interface (non-debug)
// version of this is "docli"
void Interpreter::evalCLI() {
  char prompt[150];
  bool rootCLI;

  if ((depth == 0) || (cstack.size() == 0)) {
    snprintf(prompt,150,"--> ");
    rootCLI = true;
  } else {
    snprintf(prompt,150,"[%s,%d]--> ",ip_detailname.c_str(),
	     ip_context & 0xffff);
    rootCLI = false;
  }
  while(1) {
    if (rootCLI) {
      tracetrap = 0;
      steptrap = 0;
    }
    if (m_captureState) 
      m_capture += prompt;
    else {
      emit SetPrompt(prompt);
      if (m_diaryState) diaryMessage(prompt);
    }
    //     qDebug() << "IP: " << QString::fromStdString(ip_detailname) << ", " << (ip_context & 0xffff) << "";
    emit ShowActiveLine();
    string cmdset;
    std::string cmdline;
    emit EnableRepaint();
    mutex.lock();
    while ((cmdset.empty() || 
	    NeedsMoreInput(this,cmdset)) && (!m_interrupt)) {
      if (cmd_buffer.empty())
	bufferNotEmpty.wait(&mutex);
      cmdline = cmd_buffer.front();
      cmd_buffer.erase(cmd_buffer.begin());
      cmdset += cmdline;
      if (m_captureState) 
	m_capture += cmdline;
    }
    mutex.unlock();
    emit DisableRepaint();
    if (m_interrupt) {
      m_interrupt = false;
      continue;
    }
    int stackdepth = cstack.size();
    InCLI = true;
    evaluateString(cmdset);
    while (cstack.size() > stackdepth) cstack.pop_back();
  }
}

  
//
// Convert a list of variable into indexing expressions
//  - for user defined classes, we call subsindex for 
//  - the object
Array Interpreter::subsindex(const Array &m) {
  if (m.isUserClass() && !stopoverload) {
    Array t(ClassUnaryOperator(m,"subsindex",this));
    t.promoteType(FM_UINT32);
    return Add(t,Array::uint32Constructor(1),this);
  }
  return m;
}

 void Interpreter::subsindex(ArrayVector& m) {
   for (unsigned p=0;p<m.size();p++)
     m[p] = subsindex(m[p]);
 }


syntax highlighted by Code2HTML, v. 0.9.1