/*
* 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
*
*/
#include "Class.hpp"
#include "Context.hpp"
// some behavioral observations on classes.
// The first call to "class" is the definitive one.
// The exact order of the structure fieldnames must be the same
// for all objects
// The list of parent objects must also be the same for all objects
// So, classes are stored as the following:
// class UserClass {
// stringVector fieldNames;
// stringVector parentClasses;
// }
// Also, somewhere we require a table that
// tracks the hierarchy relationship of the classes.
//
// To Do:
// change grepping code to look for classes
// change function eval code to handle classes
//
// These are both done. Next is the issue of parent classes.
// What does it mean when we have one or more parent classes?
// The structure is simple enough (simply add a new field with
// the name of the parent class). But what about methods?
// When we call a method of the parent class on the current class
// what does it get passed?
//
// The answer:
// Suppose g is of class1, and inherits class2, and class3.
// Then g has fields class2 and class3.
// When we call
// method(g), where method is a member function of class2, then
// effectively, the output is the same as
// method(g.class2)
// p = g
// p.class2 = method(g.class2)
// Odd - that's not quite right... it must be more complicated than that
// Class related issues
// rhs subscripting
// assignment
//
// What about function pointers? - done
//
// Need overload capability for
// And
// Or
// Uplus
// a(s1,s2,...,sn) - subsref
// a(s1, ..., sn) - subsasgn
// b(a) - subsindex
// [a b] - horzcat
// [a; b] - vertcat
// Colon
// end (!)
//
// More ideas on overloading classes...
//
// What happens when the parent classes are different sizes - resolved
// force parent classes to be the same size as the created object
//
// In c++, polymorphism is done through the notion of a pointer and
// type casting. But we can't do exactly the same thing... Because
// when we type-cast, only methods and fields from the type-cast
// object are present...
//
// What we want is
// a.class1.class2.foo = 32
// In this case, a is of some class (e.g., class3). But we want to
// call some method on a that belongs to class2. now, inside the
// method, we want something like
// x.foo = 32
// but _x_ has to be tagged with prefix information, because _x_ is
// really of class class3. The tag has to be on the object because
// if there are multiple arguments to the function, they can be
// typecast at different levels. Also, it tracks only the _instance_
// of the array, not the core array itself. So the information has
// to be tagged on the array somehow.
//
// One idea is to replace the class name with the class path. So if
// a is of type class3, but we want to access it as a type class2,
// we "cast" it to type class3:class1:class2. Then, when accessing
// members of "a", we use the class list to determine the indexing
// sequence. This casting operation can be done at the dispatch
// level. Because the "struct" operation simply strips the class name
// from the object, it will still return the intact data array.
//
// To track precedence...
// 1. Assume that inheritance and precedence do not interact (only the
// outermost class determines precedence).
// 2. For each class, a list of superior classes is provided.
// 3. A inf B <--> B sup A
// Precedence is then a simple question of testing interactions.
UserClass::UserClass() {
}
UserClass::UserClass(rvstring fields, rvstring parents) :
fieldNames(fields), parentClasses(parents) {
}
bool UserClass::matchClass(UserClass test) {
return (((fieldNames) == (test.fieldNames)) &&
((parentClasses) == (test.parentClasses)));
}
UserClass::~UserClass() {
}
rvstring UserClass::getParentClasses() {
return parentClasses;
}
Array ClassAux(Array s, std::string classname, rvstring parentNames,
ArrayVector parents, Interpreter* eval) {
UserClass newclass(s.fieldNames(),parentNames);
if (s.dataClass() != FM_STRUCT_ARRAY)
throw Exception("first argument to 'class' function must be a structure");
// Check to see if this class has already been registered
if (!eval->isUserClassDefined(classname)) {
// new class... register it
eval->registerUserClass(classname,newclass);
} else {
// existing class... make sure we match it
UserClass eclass(eval->lookupUserClass(classname));
if (!eclass.matchClass(newclass))
throw Exception("fieldnames, and parent objects must match registered class. Use 'clear classes' to reset this information.");
}
// Set up the new structure array. We do this by constructing a set of fieldnames
// that includes fields for the parent classes... To resolve - what happens
// if the parent arrays are different sizes than the current class.
rvstring newfields(s.fieldNames());
// We should check for duplicates!
for (int i=0;i<parentNames.size();i++)
newfields.push_back(parentNames.at(i));
// Now check to make sure all of the parent objects are the same size
// as the source object
for (int i=0;i<parents.size();i++)
if (!s.dimensions().equals(parents[i].dimensions()))
throw Exception("parent object much match dimensions of the structure used to make the object");
// Finally, we can construct the new structure object.
Array* dp = (Array *) Array::allocateArray(FM_STRUCT_ARRAY,s.getLength(),newfields);
const Array* sp = (const Array*) s.getDataPointer();
// Now we copy in the data from the original structure
int oldFieldCount(s.fieldNames().size());
int newFieldCount(newfields.size());
int arrayLength(s.getLength());
for (int i=0;i<arrayLength;i++)
for (int j=0;j<oldFieldCount;j++) {
dp[i*newFieldCount+j] = sp[i*oldFieldCount+j];
}
// Now we copy in the data from the parent objects
for (int j=0;j<parents.size();j++)
for (int i=0;i<arrayLength;i++) {
Array ndx(Array::int32Constructor(i+1));
dp[i*newFieldCount+oldFieldCount+j] = parents[j].getVectorSubset(ndx,eval);
}
// return a new object with the specified properties
Array retval(FM_STRUCT_ARRAY,s.dimensions(),dp,false,newfields);
rvstring cp;
cp.push_back(classname);
retval.setClassName(cp);
return retval;
}
Array ClassOneArgFunction(Array x) {
if (x.isUserClass()) {
std::string p(x.className().back());
return Array::stringConstructor(x.className().back());
} else {
switch (x.dataClass()) {
case FM_CELL_ARRAY:
return Array::stringConstructor("cell");
case FM_STRUCT_ARRAY:
return Array::stringConstructor("struct");
case FM_LOGICAL:
return Array::stringConstructor("logical");
case FM_UINT8:
return Array::stringConstructor("uint8");
case FM_INT8:
return Array::stringConstructor("int8");
case FM_UINT16:
return Array::stringConstructor("uint16");
case FM_INT16:
return Array::stringConstructor("int16");
case FM_UINT32:
return Array::stringConstructor("uint32");
case FM_INT32:
return Array::stringConstructor("int32");
case FM_UINT64:
return Array::stringConstructor("uint64");
case FM_INT64:
return Array::stringConstructor("int64");
case FM_FLOAT:
return Array::stringConstructor("float");
case FM_DOUBLE:
return Array::stringConstructor("double");
case FM_COMPLEX:
return Array::stringConstructor("complex");
case FM_DCOMPLEX:
return Array::stringConstructor("dcomplex");
case FM_STRING:
return Array::stringConstructor("string");
case FM_FUNCPTR_ARRAY:
return Array::stringConstructor("function handle");
default:
throw Exception("Unrecognized class!");
}
}
}
//!
//@Module CLASS Class Support Function
//@@Section CLASS
//@@Usage
//There are several uses for the @|class| function. The first
//version takes a single argument, and returns the class of
//that variable. The syntax for this form is
//@[
// classname = class(variable)
//@]
//and it returns a string containing the name of the class for
//@|variable|. The second form of the class function is used
//to construct an object of a specific type based on a structure
//which contains data elements for the class. The syntax for
//this version is
//@[
// classvar = class(template, classname, parent1, parent2,...)
//@]
//This should be called inside the constructor for the class.
//The resulting class will be of the type @|classname|, and will
//be derived from @|parent1|, @|parent2|, etc. The @|template|
//argument should be a structure array that contains the members
//of the class. See the @|constructors| help for some details
//on how to use the @|class| function. Note that if the
//@|template| argument is an empty structure matrix, then the
//resulting variable has no fields beyond those inherited from
//the parent classes.
//!
//!
//@Module CONSTRUCTORS Class Constructors
//@@Section CLASS
//@@Usage
//When designing a constructor for a FreeMat class, you should
//design the constructor to take a certain form. The following
//is the code for the sample @|mat| object
//@[
//function p = mat(a)
// if (nargin == 0)
// p.c = [];
// p = class(p,'mat');
// elseif isa(a,'mat')
// p = a;
// else
// p.c = a;
// p = class(p,'mat');
// end
//@]
//Generally speaking when it is provided with zero arguments, the
//constructor returns a default version of the class using a template
//structure with the right fields populated with default values. If
//the constructor is given a single argument that matches the class
//we are trying to construct, the constructor passes through the
//argument. This form of the constructor is used for type conversion.
//In particular,
//@[
// p = mat(a)
//@]
//guarantees that @|p| is an array of class @|mat|. The last form
//of the constructor builds a class object given the input. The
//meaning of this form depends on what makes sense for your class.
//For example, for a polynomial class, you may want to pass in the
//coefficients of the polynomial.
//!
//!
//@Module HORZCAT Overloaded Horizontal Concatenation
//@@Section CLASS
//@@Usage
//This is a method for a class that is invoked to concatenate two or more
//variables of the same class type together. Besides being called
//when you invoke
//@[
// c = horzcat(a,b,c)
//@]
//when @|a| is a class, it is also called for
//@[
// c = [a,b,c]
//@]
//when one of these variables is a class. The exact meaning of
//horizontal concatenation depends on the class you have designed.
//!
//!
//@Module VERTCAT Overloaded Vertical Concatenation
//@@Section CLASS
//@@Usage
//This is a method for a class that is invoked to concatenate two or more
//variables of the same class type together. Besides being called when
//you invoke
//@[
// c = vertcat(a,b,c)
//@]
//when @|a| is a class, it is also called for
//@[
// c = [a;b;c]
//@]
//when one of the variables is a class. The exact meaning of
//vertical concatenation depends on the class you have designed.
//!
//!
//@Module OR Overloaded Logical Or Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked to combine two variables using a
//logical or operator, and is invoked when you call
//@[
// c = or(a,b)
//@]
//or for
//@[
// c = a | b
//@]
//!
//!
//@Module AND Overloaded Logical And Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked to combine two variables using a
//logical and operator, and is invoked when you call
//@[
// c = and(a,b)
//@]
//or for
//@[
// c = a & b
//@]
//!
//!
//@Module LT Overloaded Less Than Comparison Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked to compare two variables using a
//less than comparison operator, and is invoked when you call
//@[
// c = lt(a,b)
//@]
//or for
//@[
// c = a < b
//@]
//!
//!
//@Module GT Overloaded Greater Than Comparison Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked to combine two variables using a
//greater than comparison operator, and is invoked when you call
//@[
// c = gt(a,b)
//@]
//or for
//@[
// c = a > b
//@]
//!
//!
//@Module LE Overloaded Less-Than-Equals Comparison Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked to compare two variables using a
//less than or equals comparison operator, and is invoked when you call
//@[
// c = le(a,b)
//@]
//or for
//@[
// c = a <= b
//@]
//!
//!
//@Module GE Overloaded Greater-Than-Equals Comparison Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked to combine two variables using a
//greater than or equals comparison operator, and is invoked when you call
//@[
// c = ge(a,b)
//@]
//or for
//@[
// c = a >= b
//@]
//!
//!
//@Module EQ Overloaded Equals Comparison Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked to combine two variables using an
//equals comparison operator, and is invoked when you call
//@[
// c = eq(a,b)
//@]
//or for
//@[
// c = a == b
//@]
//!
//!
//@Module NE Overloaded Not-Equals Comparison Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked to combine two variables using a
//not-equals comparison operator, and is invoked when you call
//@[
// c = ne(a,b)
//@]
//or for
//@[
// c = a != b
//@]
//!
//!
//@Module PLUS Overloaded Addition Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when two variables are added
//and is invoked when you call
//@[
// c = plus(a,b)
//@]
//or for
//@[
// c = a + b
//@]
//!
//!
//@Module MINUS Overloaded Addition Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when two variables are subtracted
//and is invoked when you call
//@[
// c = minus(a,b)
//@]
//or for
//@[
// c = a - b
//@]
//!
//!
//@Module MTIMES Overloaded Matrix Multiplication Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when two variables are multiplied
//using the matrix operator and is invoked when you call
//@[
// c = mtimes(a,b)
//@]
//or for
//@[
// c = a * b
//@]
//!
//!
//@Module TIMES Overloaded Multiplication Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when two variables are multiplied
//and is invoked when you call
//@[
// c = times(a,b)
//@]
//or for
//@[
// c = a .* b
//@]
//!
//!
//@Module RDIVIDE Overloaded Right Divide Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when two variables are divided
//and is invoked when you call
//@[
// c = rdivide(a,b)
//@]
//or for
//@[
// c = a ./ b
//@]
//!
//!
//@Module LDIVIDE Overloaded Left Divide Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when two variables are divided
//and is invoked when you call
//@[
// c = ldivide(a,b)
//@]
//or for
//@[
// c = a .\ b
//@]
//!
//!
//@Module MRDIVIDE Overloaded Matrix Right Divide Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when two variables are divided
//using the matrix divide operator, and is invoked when you call
//@[
// c = mrdivide(a,b)
//@]
//or for
//@[
// c = a / b
//@]
//!
//!
//@Module MLDIVIDE Overloaded Matrix Left Divide Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when two variables are divided
//using the matrix (left) divide operator, and is invoked when
//you call
//@[
// c = mldivide(a,b)
//@]
//or for
//@[
// c = a \ b
//@]
//!
//!
//@Module UMINUS Overloaded Unary Minus Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when a variable is negated,
//and is invoked when you call
//@[
// c = uminus(a)
//@]
//or for
//@[
// c = -a
//@]
//!
//!
//@Module UPLUS Overloaded Unary Plus Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when a variable is preceeded by a "+",
//and is invoked when you call
//@[
// c = uplus(a)
//@]
//or for
//@[
// c = +a
//@]
//!
//!
//@Module NOT Overloaded Logical Not Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when a variable is logically
//inverted, and is invoked when you call
//@[
// c = not(a)
//@]
//or for
//@[
// c = ~a
//@]
//!
//!
//@Module MPOWER Overloaded Matrix Power Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when one variable is raised
//to another variable using the matrix power operator, and
//is invoked when you call
//@[
// c = mpower(a,b)
//@]
//or
//@[
// c = a^b
//@]
//!
//!
//@Module POWER Overloaded Power Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when one variable is raised
//to another variable using the dot-power operator, and is
//invoked when you call
//@[
// c = power(a,b)
//@]
//or
//@[
// c = a.^b
//@]
//!
//!
//@Module CTRANSPOSE Overloaded Conjugate Transpose Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when a variable has the
//conjugate transpose operator method applied, and is invoked
//when you call
//@[
// c = ctranspose(a)
//@]
//or
//@[
/// c = a'
//@]
//!
//!
//@Module TRANSPOSE Overloaded Transpose Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked when a variable has the
//transpose operator method applied, and is invoked
//when you call
//@[
// c = transpose(a)
//@]
//or
//@[
/// c = a.'
//@]
//!
//!
//@Module COLON Overloaded Colon Operator
//@@Section CLASS
//@@Usage
//This is a method that is invoked in one of two forms, either
//the two argument version
//@[
// c = colon(a,b)
//@]
//which is also called using the notation
//@[
// c = a:b
//@]
//and the three argument version
//@[
// d = colon(a,b,c)
//@]
//which is also called using the notation
//@[
// d = a:b:c
//@]
//!
//!
//@Module SUBSREF Overloaded Class Indexing
//@@Section CLASS
//@@Usage
//This method is called for expressions of the form
//@[
// c = a(b), c = a{b}, c = a.b
//@]
//and overloading the @|subsref| method allows you
//to define the meaning of these expressions for
//objects of class @|a|. These expressions are
//mapped to a call of the form
//@[
// b = subsref(a,s)
//@]
//where @|s| is a structure array with two fields. The
//first field is
//\begin{itemize}
//\item @|type| is a string containing either @|'()'| or
// @|'{}'| or @|'.'| depending on the form of the call.
//\item @|subs| is a cell array or string containing the
// the subscript information.
//\end{itemize}
//When multiple indexing experssions are combined together
//such as @|b = a(5).foo{:}|, the @|s| array contains
//the following entries
//@[
// s(1).type = '()' s(1).subs = {5}
// s(2).type = '.' s(2).subs = 'foo'
// s(3).type = '{}' s(3).subs = ':'
//@]
//!
//!
//@Module SUBSASGN Overloaded Class Assignment
//@@Section CLASS
//@@Usage
//This method is called for expressions of the form
//@[
// a(b) = c, a{b} = c, a.b = c
//@]
//and overloading the @|subsasgn| method can allow you
//to define the meaning of these expressions for
//objects of class @|a|. These expressions are mapped
//to a call of the form
//@[
// a = subsasgn(a,s,b)
//@]
//where @|s| is a structure array with two fields. The
//first field is
//\begin{itemize}
//\item @|type| is a string containing either @|'()'| or
// @|'{}'| or @|'.'| depending on the form of the call.
//\item @|subs| is a cell array or string containing the
// the subscript information.
//\end{itemize}
//When multiple indexing experssions are combined together
//such as @|a(5).foo{:} = b|, the @|s| array contains
//the following entries
//@[
// s(1).type = '()' s(1).subs = {5}
// s(2).type = '.' s(2).subs = 'foo'
// s(3).type = '{}' s(3).subs = ':'
//@]
//!
//!
//@Module SUBSINDEX Overloaded Class Indexing
//@@Section CLASS
//@@Usage
//This method is called for classes in the expressions
//of the form
//@[
// c = subsindex(a)
//@]
//where @|a| is an object, and @|c| is an index vector.
//It is also called for
//@[
// c = b(a)
//@]
//in which case @|subsindex(a)| must return a vector containing
//integers between @|0| and @|N-1| where @|N| is the number
//of elements in the vector @|b|.
//!
ArrayVector ClassFunction(int nargout, const ArrayVector& arg,
Interpreter* eval) {
if (arg.size() == 0)
throw Exception("class function requires at least one argument");
if (arg.size() == 1)
return singleArrayVector(ClassOneArgFunction(arg[0]));
ArrayVector parents;
rvstring parentNames;
for (int i=2;i<arg.size();i++) {
Array parent(arg[i]);
if (!parent.isUserClass())
throw Exception("parent objects must be user defined classes");
parents.push_back(parent);
parentNames.push_back(parent.className().back());
}
Array sval(arg[0]);
Array classname(arg[1]);
return singleArrayVector(ClassAux(sval,classname.getContentsAsString(),
parentNames,parents,eval));
}
void LoadClassFunction(Context* context) {
SpecialFunctionDef *sfdef = new SpecialFunctionDef;
sfdef->retCount = 1;
sfdef->argCount = -1;
sfdef->name = "class";
sfdef->fptr = ClassFunction;
context->insertFunction(sfdef,false);
}
std::vector<int> MarkUserClasses(ArrayVector t) {
std::vector<int> set;
for (int j=0;j<t.size();j++)
if (t[j].isUserClass()) set.push_back(j);
return set;
}
bool ClassSearchOverload(Interpreter* eval, ArrayVector t,
std::vector<int> userset, FuncPtr &val,
std::string name) {
bool overload = false;
int k = 0;
while (k<userset.size() && !overload) {
overload = eval->getContext()->lookupFunction(ClassMangleName(t[userset[k]].className().back(),name),val);
if (!overload) k++;
}
if (!overload)
throw Exception(std::string("Unable to find overloaded '") + name + "' for user defined classes");
}
Array ClassMatrixConstructor(ArrayMatrix m, Interpreter* eval) {
// Process the rows...
// If a row has no user defined classes, then
// use the standard matrixConstructor
ArrayVector rows;
for (int i=0;i<m.size();i++) {
// check this row - make a list of columns that are
// user classes
std::vector<int> userset(MarkUserClasses(m[i]));
if (userset.empty()) {
ArrayMatrix n;
n.push_back(m[i]);
rows.push_back(Array::matrixConstructor(n));
} else {
FuncPtr val;
bool horzcat_overload = ClassSearchOverload(eval,m[i],userset,
val,"horzcat");
// scan through the list of user defined classes - look
// for one that has "horzcat" overloaded
val->updateCode(eval);
ArrayVector p;
p = val->evaluateFunction(eval,m[i],1);
if (!p.empty())
rows.push_back(p[0]);
else {
eval->warningMessage("'horzcat' called for user defined class and it returned nothing. Substituting empty array for result.");
rows.push_back(Array::emptyConstructor());
}
}
}
// Check for a singleton - handle with special case
if (rows.size() == 1)
return rows[0];
// At this point we have a vector arrays that have to vertically
// concatenated. There may not be any objects in it, so we have
// to rescan.
std::vector<int> userset(MarkUserClasses(rows));
if (userset.empty()) {
// OK - we don't have any user-defined classes anymore,
// so we want to call matrix constructor, which needs
// an ArrayMatrix instead of an ArrayVector.
ArrayMatrix ref;
for (int i=0;i<rows.size();i++)
ref.push_back(singleArrayVector(rows[i]));
return Array::matrixConstructor(ref);
} else {
// We do have a user defined class - look for a vertcat defined
FuncPtr val;
bool vertcat_overload = ClassSearchOverload(eval,rows,userset,
val,"vertcat");
val->updateCode(eval);
ArrayVector p;
p = val->evaluateFunction(eval,rows,1);
if (!p.empty())
return p[0];
else
return Array::emptyConstructor();
}
return Array::emptyConstructor();
}
Array ClassUnaryOperator(Array a, std::string funcname,
Interpreter* eval) {
FuncPtr val;
ArrayVector m, n;
if (eval->getContext()->lookupFunction(ClassMangleName(a.className().back(),funcname),val)) {
val->updateCode(eval);
m.push_back(a);
n = val->evaluateFunction(eval,m,1);
if (!n.empty())
return n[0];
else
return Array::emptyConstructor();
}
throw Exception("Unable to find a definition of " + funcname + " for arguments of class " + a.className().back());
}
bool ClassResolveFunction(Interpreter* eval, Array& args, std::string funcName, FuncPtr& val) {
Context *context = eval->getContext();
// First try to resolve to a method of the base class
if (context->lookupFunction(ClassMangleName(args.className().back(),funcName),val)) {
return true;
}
UserClass eclass(eval->lookupUserClass(args.className().back()));
rvstring parentClasses(eclass.getParentClasses());
// Now check the parent classes
for (int i=0;i<parentClasses.size();i++) {
if (context->lookupFunction(ClassMangleName(parentClasses.at(i),funcName),val)) {
rvstring argClass(args.className());
argClass.push_back(parentClasses.at(i));
args.setClassName(argClass);
return true;
}
}
// Nothing matched, return
return false;
}
void printClassNames(Array a) {
rvstring classname(a.className());
for (int i=0;i<classname.size();i++)
std::cout << "class " << classname.at(i) << "\r\n";
}
// TODO - add "inferiorto", etc and class precedence
Array ClassBiOp(Array a, Array b, FuncPtr val, Interpreter *eval) {
val->updateCode(eval);
ArrayVector m, n;
m.push_back(a); m.push_back(b);
n = val->evaluateFunction(eval,m,1);
if (!n.empty())
return n[0];
else
return Array::emptyConstructor();
}
Array ClassTriOp(Array a, Array b, Array c, FuncPtr val, Interpreter *eval) {
val->updateCode(eval);
ArrayVector m, n;
m.push_back(a); m.push_back(b); m.push_back(c);
n = val->evaluateFunction(eval,m,1);
if (!n.empty())
return n[0];
else
return Array::emptyConstructor();
}
Array ClassTrinaryOperator(Array a, Array b, Array c, std::string funcname,
Interpreter* eval) {
FuncPtr val;
if (a.isUserClass()) {
if (eval->getContext()->lookupFunction(ClassMangleName(a.className().back(),funcname),val))
return ClassTriOp(a,b,c,val,eval);
throw Exception("Unable to find a definition of " + funcname + " for arguments of class " + a.className().back());
} else if (b.isUserClass()) {
if (eval->getContext()->lookupFunction(ClassMangleName(b.className().back(),funcname),val))
return ClassTriOp(a,b,c,val,eval);
throw Exception("Unable to find a definition of " + funcname + " for arguments of class " + b.className().back());
} else if (c.isUserClass()) {
if (eval->getContext()->lookupFunction(ClassMangleName(c.className().back(),funcname),val))
return ClassTriOp(a,b,c,val,eval);
throw Exception("Unable to find a definition of " + funcname + " for arguments of class " + b.className().back());
}
}
Array ClassBinaryOperator(Array a, Array b, std::string funcname,
Interpreter* eval) {
FuncPtr val;
if (a.isUserClass()) {
if (eval->getContext()->lookupFunction(ClassMangleName(a.className().back(),funcname),val))
return ClassBiOp(a,b,val,eval);
throw Exception("Unable to find a definition of " + funcname + " for arguments of class " + a.className().back());
} else if (b.isUserClass()) {
if (eval->getContext()->lookupFunction(ClassMangleName(b.className().back(),funcname),val))
return ClassBiOp(a,b,val,eval);
throw Exception("Unable to find a definition of " + funcname + " for arguments of class " + b.className().back());
}
}
// void AdjustColonCalls(ArrayVector& m, treeVector t) {
// for (unsigned index=0;index < t.size();index++)
// if (t[index].is(':'))
// m[index] = Array::stringConstructor(":");
// }
Array IndexExpressionToStruct(Interpreter* eval, const tree &t, Array r) {
ArrayVector struct_args;
ArrayVector rv;
Array rsave(r);
rvstring fNames;
fNames.push_back("type");
fNames.push_back("subs");
for (unsigned index=1;index < t.numchildren();index++) {
if (!rv.empty())
throw Exception("Cannot reindex an expression that returns multiple values.");
if (t.child(index).is(TOK_PARENS)) {
ArrayVector m;
const tree &s(t.child(index));
for (unsigned p=0;p<s.numchildren();p++)
eval->multiexpr(s.child(p),m);
eval->subsindex(m);
// m = eval->varExpressionList(t[index].children(),r);
// // Scan through the expressions... adjust for "colon" calls
// AdjustColonCalls(m,t[index].children());
if (m.size() == 0)
throw Exception("Expected indexing expression!");
// Take the arguments and push them into a cell array...
ArrayMatrix q; q.push_back(m);
struct_args.push_back(Array::stringConstructor("()"));
struct_args.push_back(Array::cellConstructor(q));
}
if (t.child(index).is(TOK_BRACES)) {
ArrayVector m;
const tree &s(t.child(index));
for (unsigned p=0;p<s.numchildren();p++)
eval->multiexpr(s.child(p),m);
eval->subsindex(m);
// m = eval->varExpressionList(t[index].children(),r);
// AdjustColonCalls(m,t[index].children());
if (m.size() == 0)
throw Exception("Expected indexing expression!");
// Take the arguments and push them into a cell array...
ArrayMatrix q; q.push_back(m);
struct_args.push_back(Array::stringConstructor("{}"));
struct_args.push_back(Array::cellConstructor(q));
}
if (t.child(index).is('.')) {
struct_args.push_back(Array::stringConstructor("."));
struct_args.push_back(Array::stringConstructor(t.child(index).first().text()));
}
}
int cnt = struct_args.size()/2;
Array *cp = (Array*) Array::allocateArray(FM_STRUCT_ARRAY,cnt,fNames);
for (int i=0;i<2*cnt;i++)
cp[i] = struct_args[i];
return Array(FM_STRUCT_ARRAY,Dimensions(cnt,1),cp,false,fNames);
}
ArrayVector ClassSubsrefCall(Interpreter* eval, const tree &t, Array r, FuncPtr val) {
ArrayVector p;
p.push_back(r);
p.push_back(IndexExpressionToStruct(eval,t, r));
val->updateCode(eval);
ArrayVector n = val->evaluateFunction(eval,p,1);
return n;
}
// What is special here... Need to be able to do field indexing
//
ArrayVector ClassRHSExpression(Array r, const tree &t, Interpreter* eval) {
tree s;
Array q;
Array n, p;
int peerCnt;
int dims;
bool isVar;
bool isFun;
FuncPtr val;
// Try and look up subsref, _unless_ we are already in a method
// of this class
if (!eval->inMethodCall(r.className().back()))
if (ClassResolveFunction(eval,r,"subsref",val)) {
// Overloaded subsref case
return ClassSubsrefCall(eval,t,r,val);
}
ArrayVector rv;
for (unsigned index=1;index < t.numchildren();index++) {
if (!rv.empty())
throw Exception("Cannot reindex an expression that returns multiple values.");
if (t.child(index).is(TOK_PARENS)) {
ArrayVector m;
const tree &s(t.child(index));
for (unsigned p=0;p<s.numchildren();p++)
eval->multiexpr(s.child(p),m);
eval->subsindex(m);
// m = eval->varExpressionList(t.child(index).children(),r);
if (m.size() == 0)
throw Exception("Expected indexing expression!");
else if (m.size() == 1) {
q = r.getVectorSubset(m[0],eval);
r = q;
} else {
q = r.getNDimSubset(m,eval);
r = q;
}
}
if (t.child(index).is(TOK_BRACES)) {
ArrayVector m;
const tree &s(t.child(index));
for (unsigned p=0;p<s.numchildren();p++)
eval->multiexpr(s.child(p),m);
eval->subsindex(m);
// m = eval->varExpressionList(t.child(index).children(),r);
if (m.size() == 0)
throw Exception("Expected indexing expression!");
else if (m.size() == 1)
rv = r.getVectorContentsAsList(m[0],eval);
else
rv = r.getNDimContentsAsList(m,eval);
if (rv.size() == 1) {
r = rv[0];
rv = ArrayVector();
} else if (rv.size() == 0) {
throw Exception("Empty expression!");
r = Array::emptyConstructor();
}
}
if (t.child(index).is('.')) {
// This is where the classname chain comes into being.
rvstring className(r.className());
for (int i=1;i<className.size();i++) {
rv = r.getFieldAsList(className.at(i));
r = rv[0];
}
rv = r.getFieldAsList(t.child(index).first().text());
if (rv.size() <= 1) {
r = rv[0];
rv = ArrayVector();
}
}
if (t.child(index).is(TOK_DYN)) {
string field;
try {
Array fname(eval->expression(t.child(index).first()));
field = fname.getContentsAsString();
} catch (Exception &e) {
throw Exception("dynamic field reference to structure requires a string argument");
}
rv = r.getFieldAsList(field);
if (rv.size() <= 1) {
r = rv[0];
rv = ArrayVector();
}
}
}
if (rv.empty())
rv.push_back(r);
return rv;
}
void ClassAssignExpression(ArrayReference dst, const tree &t, const Array& value, Interpreter* eval) {
FuncPtr val;
if (!ClassResolveFunction(eval,*dst,"subsasgn",val))
throw Exception("The method 'subsasgn' is not defined for objects of class " +
dst->className().back());
ArrayVector p;
p.push_back(*dst);
p.push_back(IndexExpressionToStruct(eval,t, *dst));
p.push_back(value);
val->updateCode(eval);
bool overload(eval->getStopOverload());
eval->setStopOverload(true);
ArrayVector n = val->evaluateFunction(eval,p,1);
eval->setStopOverload(overload);
if (!n.empty())
*dst = n[0];
else
eval->warningMessage(std::string("'subsasgn' for class ") + dst->className().back() + std::string(" did not return a value... operation has no effect."));
}
// Ideally, this would be the only place where the class name is mangled.
// However, currently, the same op is repeated in the Interface implementation
// code.
std::string ClassMangleName(std::string className, std::string funcName) {
return "@" + className + ":" + funcName;
}
syntax highlighted by Code2HTML, v. 0.9.1