EvalStatus NodeSquareBracket::evalToStoreInto()
  {
    assert(m_nodeLeft && m_nodeRight);
    UTI nuti = getNodeType();
    if(nuti == Nav)
      return ERROR;

    evalNodeProlog(0); //new current frame pointer

    makeRoomForSlots(1); //always 1 slot for ptr
    EvalStatus evs = m_nodeLeft->evalToStoreInto();
    if(evs != NORMAL)
      {
	evalNodeEpilog();
	return evs;
      }

    UlamValue pluv = m_state.m_nodeEvalStack.popArg();

    makeRoomForNodeType(m_nodeRight->getNodeType()); //offset a constant expression
    evs = m_nodeRight->eval();
    if(evs != NORMAL)
      {
	evalNodeEpilog();
	return evs;
      }

    UlamValue offset = m_state.m_nodeEvalStack.popArg();

    // constant expression only required for array declaration
    u32 offsetdata = offset.getImmediateData(m_state);
    s32 offsetInt = m_state.getUlamTypeByIndex(offset.getUlamValueTypeIdx())->getDataAsCs32(offsetdata);

    UTI auti = pluv.getPtrTargetType();
    UlamType * aut = m_state.getUlamTypeByIndex(auti);
    if(aut->isCustomArray())
      {
	UTI caType = ((UlamTypeClass *) aut)->getCustomArrayType();
	UlamType * caut = m_state.getUlamTypeByIndex(caType);
	u32 pos = pluv.getPtrPos();
	if(caut->getBitSize() > MAXBITSPERINT)
	  pos = 0;
	//  itemUV = UlamValue::makeAtom(caType);
	//else
	//  itemUV = UlamValue::makeImmediate(caType, 0, m_state);  //quietly skip for now XXX

	//use CA type, not the left ident's type
	UlamValue scalarPtr = UlamValue::makePtr(pluv.getPtrSlotIndex(),
						 pluv.getPtrStorage(),
						 caType,
						 m_state.determinePackable(caType), //PACKEDLOADABLE
						 m_state,
						 pos /* base pos of array */
						 );

	//copy result UV to stack, -1 relative to current frame pointer
	assignReturnValuePtrToStack(scalarPtr);
      }
    else
      {
	//adjust pos by offset * len, according to its scalar type
	UlamValue scalarPtr = UlamValue::makeScalarPtr(pluv, m_state);
	if(scalarPtr.incrementPtr(m_state, offsetInt))
	  //copy result UV to stack, -1 relative to current frame pointer
	  assignReturnValuePtrToStack(scalarPtr);
	else
	  {
	    s32 arraysize = m_state.getArraySize(pluv.getPtrTargetType());
	    Symbol * lsymptr;
	    u32 lid = 0;
	    if(getSymbolPtr(lsymptr))
	      lid = lsymptr->getId();

	    std::ostringstream msg;
	    msg << "Array subscript [" << offsetInt << "] exceeds the size (" << arraysize;
	    msg << ") of array '" << m_state.m_pool.getDataAsString(lid).c_str() << "'";
	    MSG(getNodeLocationAsString().c_str(), msg.str().c_str(), ERR);
	    evs = ERROR;
	  }
      }
    evalNodeEpilog();
    return evs;
  } //evalToStoreInto
  // used to select an array element; not for declaration
  UTI NodeSquareBracket::checkAndLabelType()
  {
    assert(m_nodeLeft && m_nodeRight);
    u32 errorCount = 0;
    UTI newType = Nav; //init
    UTI idxuti = Nav;

    UTI leftType = m_nodeLeft->checkAndLabelType();
    bool isCustomArray = false;

    //for example, f.chance[i] where i is local, same as f.func(i);
    NodeBlock * currBlock = m_state.getCurrentBlock();
    m_state.pushCurrentBlockAndDontUseMemberBlock(currBlock); //currblock doesn't change
    UTI rightType = m_nodeRight->checkAndLabelType();
    m_state.popClassContext();

    if(leftType != Nav)
      {
	UlamType * lut = m_state.getUlamTypeByIndex(leftType);
        isCustomArray = lut->isCustomArray();

	if(lut->isScalar())
	  {
	    if(lut->isHolder())
	      {
		std::ostringstream msg;
		msg << "Incomplete Type: " << m_state.getUlamTypeNameBriefByIndex(leftType).c_str();
		msg << " used with " << getName();
		MSG(getNodeLocationAsString().c_str(), msg.str().c_str(), DEBUG);
		errorCount++;
	      }
	    else if(!isCustomArray)
	      {
		std::ostringstream msg;
		msg << "Invalid Type: " << m_state.getUlamTypeNameBriefByIndex(leftType).c_str();
		msg << " used with " << getName();
		MSG(getNodeLocationAsString().c_str(), msg.str().c_str(), ERR);
		errorCount++;
	      }
	    else
	      {
		//must be a custom array; t.f. lhs is a quark!
		assert(lut->getUlamClass() != UC_NOTACLASS);

		// can't substitute a function call node for square brackets to leverage
		// all the overload matching in func call node's c&l, because
		// we ([]) can't tell which side of = we are on, and whether we should
		// be a aref or aset.
		UTI caType = ((UlamTypeClass *) lut)->getCustomArrayType();

		if(!m_state.isComplete(caType))
		  {
		    std::ostringstream msg;
		    msg << "Incomplete Custom Array Type: ";
		    msg << m_state.getUlamTypeNameBriefByIndex(caType).c_str();
		    msg << " used with class: ";
		    msg << m_state.getUlamTypeNameBriefByIndex(leftType).c_str();
		    msg << getName();
		    if(lut->isComplete())
		      MSG(getNodeLocationAsString().c_str(), msg.str().c_str(), ERR);
		    else
		      MSG(getNodeLocationAsString().c_str(), msg.str().c_str(), DEBUG);
		    newType = Nav; //error!
		    errorCount++;
		  }
	      }

	    //set up idxuti..RHS
	    //cant proceed with custom array subscript if lhs is incomplete
	    if(errorCount == 0)
	      {
		if(isCustomArray)
		  {
		    bool hasHazyArgs = false;
		    u32 camatches = ((UlamTypeClass *) lut)->getCustomArrayIndexTypeFor(m_nodeRight, idxuti, hasHazyArgs);
		    if(camatches == 0)
		      {
			std::ostringstream msg;
			msg << "No defined custom array get function with";
			msg << " matching argument type ";
			msg << m_state.getUlamTypeNameBriefByIndex(rightType).c_str();
			msg << "; and cannot be called";
			if(hasHazyArgs)
			  MSG(getNodeLocationAsString().c_str(), msg.str().c_str(), DEBUG);
			else
			  MSG(getNodeLocationAsString().c_str(), msg.str().c_str(), ERR);
			idxuti = Nav; //error!
			errorCount++;
		      }
		    else if(camatches > 1)
		      {
			std::ostringstream msg;
			msg << "Ambiguous matches (" << camatches;
			msg << ") of custom array get function for argument type ";
			msg << m_state.getUlamTypeNameBriefByIndex(rightType).c_str();
			msg << "; Explicit casting required";
			if(hasHazyArgs)
			  MSG(getNodeLocationAsString().c_str(), msg.str().c_str(), DEBUG);
			else
			  MSG(getNodeLocationAsString().c_str(), msg.str().c_str(), ERR);
			idxuti = Nav; //error!
			errorCount++;
		      }
		  }
		else
		  {
		    //not custom array
		    //must be some kind of numeric type: Int, Unsigned, or Unary..of any bit size
		    if(rightType != Nav && !m_state.getUlamTypeByIndex(rightType)->isNumericType())
		      {
			std::ostringstream msg;
			msg << "Array item specifier requires numeric type: ";
			msg << m_state.getUlamTypeNameBriefByIndex(rightType).c_str();
			MSG(getNodeLocationAsString().c_str(), msg.str().c_str(), ERR);
			idxuti = Nav; //error!
			errorCount++;
		      }
		    else
		      idxuti = rightType; //default unless caarray
		  }
	      } //errorcount is zero
	  } //lut is scalar

	if(idxuti != Nav && UlamType::compare(idxuti, rightType, m_state) == UTIC_NOTSAME)
	  {
	    if(m_nodeRight->safeToCastTo(idxuti) == CAST_CLEAR)
	      {
		if(!makeCastingNode(m_nodeRight, idxuti, m_nodeRight))
		  {
		    newType = Nav; //error!
		    errorCount++;
		  }
	      }
	    else
	      {
		newType = Nav; //error!
		errorCount++;
	      }
	  }
      } //lt not nav

    if(errorCount == 0)
      {
	// sq bracket purpose in life is to account for array elements;
	if(isCustomArray)
	  newType = ((UlamTypeClass *) m_state.getUlamTypeByIndex(leftType))->getCustomArrayType();
	else
	  newType = m_state.getUlamTypeAsScalar(leftType);

	// multi-dimensional possible; MP not ok lhs.
	setStoreIntoAble(m_nodeLeft->isStoreIntoAble());
      }

    setNodeType(newType);
    return newType;
  } //checkAndLabelType
  EvalStatus NodeSquareBracket::eval()
  {
    assert(m_nodeLeft && m_nodeRight);
    UTI nuti = getNodeType();
    if(nuti == Nav)
      return ERROR;

    evalNodeProlog(0); //new current frame pointer

    makeRoomForSlots(1); //always 1 slot for ptr
    EvalStatus evs = m_nodeLeft->evalToStoreInto();
    if(evs != NORMAL)
      {
	evalNodeEpilog();
	return evs;
      }

    UlamValue pluv = m_state.m_nodeEvalStack.popArg();
    UTI ltype = pluv.getPtrTargetType();

    //could be a custom array which is a scalar quark. already checked.
    UlamType * lut = m_state.getUlamTypeByIndex(ltype);
    bool isCustomArray = lut->isCustomArray();

    assert(!m_state.isScalar(ltype) || isCustomArray); //already checked, must be array

    makeRoomForNodeType(m_nodeRight->getNodeType()); //offset a constant expression
    evs = m_nodeRight->eval();
    if(evs != NORMAL)
      {
	evalNodeEpilog();
	return evs;
      }

    UlamValue offset = m_state.m_nodeEvalStack.popArg();
    UlamType * offut = m_state.getUlamTypeByIndex(offset.getUlamValueTypeIdx());
    s32 offsetInt = 0;
    if(offut->isNumericType())
      {
	// constant expression only required for array declaration
	s32 arraysize = m_state.getArraySize(ltype);
	u32 offsetdata = offset.getImmediateData(m_state);
	offsetInt = offut->getDataAsCs32(offsetdata);

	if((offsetInt >= arraysize) && !isCustomArray)
	  {
	    Symbol * lsymptr;
	    u32 lid = 0;
	    if(getSymbolPtr(lsymptr))
	      lid = lsymptr->getId();

	    std::ostringstream msg;
	    msg << "Array subscript [" << offsetInt << "] exceeds the size (" << arraysize;
	    msg << ") of array '" << m_state.m_pool.getDataAsString(lid).c_str() << "'";
	    MSG(getNodeLocationAsString().c_str(), msg.str().c_str(), ERR);
	    evalNodeEpilog();
	    return ERROR;
	  }
      }
    else if(!isCustomArray)
      {
	    Symbol * lsymptr;
	    u32 lid = 0;
	    if(getSymbolPtr(lsymptr))
	      lid = lsymptr->getId();

	    std::ostringstream msg;
	    msg << "Array subscript of array '";
	    msg << m_state.m_pool.getDataAsString(lid).c_str();
	    msg << "' requires a numeric type";
	    MSG(getNodeLocationAsString().c_str(), msg.str().c_str(), ERR);
	    evalNodeEpilog();
	    return ERROR;
      }

    assignReturnValueToStack(pluv.getValAt(offsetInt, m_state));
    evalNodeEpilog();
    return NORMAL;
  } //eval