Back to APL Language

Details

Each APL operator has its own class based on the Operator class. Each operator must implement scalar monadic and dyadic (one parameter or two parameter) methods but may used default implementions for matrix methods that call the scalar methods. Macros are used to simplify the operator class declarations.For example, the class for the Multiplication operator is declared like this:

OPERATOR(Multiplication) INT_INT FLOAT_FLOAT BOOL_BOOL INT FLOAT BOOL
OPERATOR_END

This specifies that the Multiplication operator implements scalar monadic and dyadic methods for the integer, float, and boolean data types. The INT_INT macro resolves to;

#define INT_INT void ScalarScalar(Int left, Int right, Int &result, bool &exception);

This method is implemented as:

void Multiplication::ScalarScalar(Int left, Int right, Int &result, bool &exception)
{
result = left * right; if (IsIntegerOverflow()) exception = true;
}

This method is called by a generic dispatcher, which is used by the matrix operators:

template <typename _T1_, typename _T2_, typename _T3_> void MatrixMatrixT(_T1_ left, _T2_ right, _T3_ *result, bool &exception, Operator &op)
{
Index n = op.m_LeftNumberOfElements;
while (n-- > 0 && !exception)
op.ScalarScalar(static_cast<_T3_>(*left++), static_cast<_T3_>(*right++), *result++, exception);
}

Type promotion is provided by the dispatchers:

void Operator::DispatchScalarScalar(Value &left, Value &right, Value &result)
{
bool exception(false);
ConformTypes(left, right);
switch (left.ValueType())
{
case BoolT:
      Bool b;
     ScalarScalar(left.GetBool(), right.GetBool(), b, exception);
        if (exception)
   {
         left.ConvertTo(IntT);
           right.ConvertTo(IntT);
           exception = false;
           SetupScalarScalar(left, right, result, IntT);
        } else
        {
           result.SetBool(b);
           break;
        }
        // Fall through
    case IntT:
         result.Type = IntT;
         ScalarScalar(left.GetInt(), right.GetInt(), *result.GetInts(), exception);
        if (exception)
        {
          exception = false;
           SetupScalarScalar(left, right, result, FloatT);
           ScalarScalar(left.GetInt(), right.GetInt(), *result.GetFloats(), exception);
           if (exception)
             THROW_BoundsError;
         }
         break;
     case FloatT:
        result.Type = FloatT; ScalarScalar(left.GetFloat(), right.GetFloat(), *result.GetFloats(), exception);
         if (exception)
            THROW_BoundsError;
         break;
    default:
      THROW_DomainError;
 }
}

Non-mathematical operators generally must implement more methods to provide specific functionality for combinations of inputs that can't be built up from primitives.