ICCItem *CCodeChain::EvaluateArgs (CEvalContext *pCtx, ICCItem *pArgs, const CString &sArgValidation) // EvaluateArgs // // Evaluate arguments and validate their types { ICCItem *pArg; ICCItem *pNew; ICCItem *pError; CCLinkedList *pEvalList; char *pValidation; int i; BOOL bNoEval; // If the argument list if quoted, then it means that the arguments // have already been evaluated. This happens if we've been called by // (apply). bNoEval = pArgs->IsQuoted(); // Create a list to hold the results pNew = CreateLinkedList(); if (pNew->IsError()) return pNew; pEvalList = dynamic_cast<CCLinkedList *>(pNew); // Start parsing at the beginning pValidation = sArgValidation.GetPointer(); // If there is a '*' in the validation, figure out // how many arguments it represents int iVarArgs = Max(0, pArgs->GetCount() - (sArgValidation.GetLength() - 1)); // Loop over each argument for (i = 0; i < pArgs->GetCount(); i++) { ICCItem *pResult; pArg = pArgs->GetElement(i); // If we're processing variable args, see if we're done if (*pValidation == '*') { if (iVarArgs == 0) pValidation++; else iVarArgs--; } // Evaluate the item. If the arg is 'q' or 'u' then we // don't evaluate it. if (bNoEval || *pValidation == 'q' || *pValidation == 'u') pResult = pArg->Reference(); // If the arg is 'c' then we don't evaluate unless it is // a lambda expression (or an identifier) else if (*pValidation == 'c' && !pArg->IsLambdaExpression() && !pArg->IsIdentifier()) pResult = pArg->Reference(); // Evaluate else { pResult = Eval(pCtx, pArg); // We don't want to return on error because some functions // might want to pass errors around if (*pValidation != 'v' && *pValidation != '*') { if (pResult->IsError()) { pEvalList->Discard(this); return pResult; } } } // Check to see if the item is valid switch (*pValidation) { // We expect a function... case 'f': { if (!pResult->IsFunction()) { pError = CreateError(LITERAL("Function expected"), pResult); pResult->Discard(this); pEvalList->Discard(this); return pError; } break; } // We expect a numeral... // // NOTE: We treat integer the same a numeral because it's not always // clear to the user when they've created a double or an integer. // It is up to the actual function to use the integer or double // value appropriately. case 'i': case 'n': { if (!pResult->IsDouble() && !pResult->IsInteger()) { pError = CreateError(LITERAL("Numeral expected"), pResult); pResult->Discard(this); pEvalList->Discard(this); return pError; } break; } // We expect a double... case 'd': { if (!pResult->IsDouble()) { pError = CreateError(LITERAL("Double expected"), pResult); pResult->Discard(this); pEvalList->Discard(this); return pError; } break; } // We expect a vEctor... case 'e': { if (!(pResult->GetValueType() == ICCItem::Vector)) { pError = CreateError(LITERAL("Vector expected"), pResult); pResult->Discard(this); pEvalList->Discard(this); return pError; } break; } // We expect a linked list case 'k': { if (pResult->GetClass()->GetObjID() != OBJID_CCLINKEDLIST) { pError = CreateError(LITERAL("Linked-list expected"), pResult); pResult->Discard(this); pEvalList->Discard(this); return pError; } break; } // We expect a list case 'l': { if (!pResult->IsList()) { pError = CreateError(LITERAL("List expected"), pResult); pResult->Discard(this); pEvalList->Discard(this); return pError; } break; } // We expect an identifier case 's': case 'q': { if (!pResult->IsIdentifier()) { pError = CreateError(LITERAL("Identifier expected"), pResult); pResult->Discard(this); pEvalList->Discard(this); return pError; } break; } // We expect an atom table case 'x': { if (!pResult->IsAtomTable()) { pError = CreateError(LITERAL("Atom table expected"), pResult); pResult->Discard(this); pEvalList->Discard(this); return pError; } break; } // We expect a symbol table case 'y': { if (!pResult->IsSymbolTable()) { pError = CreateError(LITERAL("Symbol table expected"), pResult); pResult->Discard(this); pEvalList->Discard(this); return pError; } break; } // We expect anything case 'c': case 'u': case 'v': break; // We expect any number of anythings... case '*': break; // Too many arguments case '\0': { pError = CreateError(LITERAL("Too many arguments"), NULL); pResult->Discard(this); pEvalList->Discard(this); return pError; } default: ASSERT(FALSE); } // Add the result to the list pEvalList->Append(*this, pResult); pResult->Discard(this); // Next validation sequence (note that *pValidation can never // be '\0' because we return above if we find it) if (*pValidation != '*') pValidation++; } // Make sure we have enough arguments if (*pValidation != '\0' && *pValidation != '*') { pError = CreateError(LITERAL("Insufficient arguments"), NULL); pEvalList->Discard(this); return pError; } // Return the evaluation list return pEvalList; }
ICCItem *fnApply (CEvalContext *pCtx, ICCItem *pArguments, DWORD dwData) // fnApply // // Applies the given parameter list to the lambda expression // // (apply exp arg1 arg2 ... argn list) { CCodeChain *pCC = pCtx->pCC; ICCItem *pArgs; ICCItem *pResult; ICCItem *pFunction; ICCItem *pLast; CCLinkedList *pList; int i; // Evaluate the arguments and validate them pArgs = pCC->EvaluateArgs(pCtx, pArguments, CONSTLIT("v*")); if (pArgs->IsError()) return pArgs; // We better have at least two arguments if (pArgs->GetCount() < 2) { pArgs->Discard(pCC); return pCC->CreateError(CONSTLIT("apply needs a function and a list of arguments."), NULL); } // The last argument better be a list pLast = pArgs->GetElement(pArgs->GetCount() - 1); if (!pLast->IsList()) { pArgs->Discard(pCC); return pCC->CreateError(CONSTLIT("Last argument for apply must be a list."), NULL); } // The first argument is the function pFunction = pArgs->Head(pCC); // Create a new list to store the arguments in pResult = pCC->CreateLinkedList(); if (pResult->IsError()) { pArgs->Discard(pCC); return pResult; } pList = (CCLinkedList *)pResult; // Add each of the arguments except the last for (i = 1; i < pArgs->GetCount() - 1; i++) { pList->Append(pCC, pArgs->GetElement(i), &pResult); if (pResult->IsError()) { pList->Discard(pCC); pArgs->Discard(pCC); return pResult; } pResult->Discard(pCC); } // Add each of the elements of the last list for (i = 0; i < pLast->GetCount(); i++) { pList->Append(pCC, pLast->GetElement(i), &pResult); if (pResult->IsError()) { pList->Discard(pCC); pArgs->Discard(pCC); return pResult; } pResult->Discard(pCC); } // Set the literal flag to indicate that the arguments should // not be evaluated. pList->SetQuoted(); // Execute the function if (pFunction->IsFunction()) pResult = pFunction->Execute(pCtx, pList); else pResult = pFunction->Reference(); pList->Discard(pCC); // Done pArgs->Discard(pCC); return pResult; }
ICCItem *CCodeChain::Eval (CEvalContext *pEvalCtx, ICCItem *pItem) // Eval // // Evaluates the given item and returns a result { // Errors always evaluate to themselves if (pItem->IsError()) return pItem->Reference(); // If this item is quoted, then return an unquoted item if (pItem->IsQuoted()) { // If this is a literal symbol table then we need to evaluate its // values. if (pItem->IsSymbolTable()) return EvalLiteralStruct(pEvalCtx, pItem); // HACK: We clone the item so that when we try to modify a literal list we // mody a copy instead of the original. else { ICCItem *pResult = pItem->Clone(this); pResult->ClearQuoted(); return pResult; } } // Evaluate differently depending on whether or not // this is an atom or a list. If it is an atom, either return // the value or look up the atom in a symbol table. If the item // is a list, try to evaluate as a function else if (pItem->IsIdentifier()) return Lookup(pEvalCtx, pItem); // If this is an expression (a list with multiple terms) then we // try to evaluate it. else if (pItem->IsExpression()) { ICCItem *pFunctionName; ICCItem *pFunction; ICCItem *pArgs; ICCItem *pResult; // The first element of the list is the function pFunctionName = pItem->Head(this); // We must have a first element since this is a list (but not Nil) ASSERT(pFunctionName); // If this is an identifier, then look up the function // in the global symbols if (pFunctionName->IsIdentifier()) pFunction = LookupFunction(pEvalCtx, pFunctionName); // Otherwise, evaluate it else pFunction = Eval(pEvalCtx, pFunctionName); // If we get an error, return it if (pFunction->IsError()) return pFunction; // Make sure this is a function if (!pFunction->IsFunction()) { pFunction->Discard(this); return CreateError(LITERAL("Function name expected"), pFunctionName); } // Get the arguments pArgs = pItem->Tail(this); // Do it pResult = pFunction->Execute(pEvalCtx, pArgs); // Handle error by appending the function call that failed if (pResult->IsError()) { CCString *pError = dynamic_cast<CCString *>(pResult); if (pError) { CString sError = pError->GetValue(); if (!sError.IsBlank()) { char *pPos = sError.GetASCIIZPointer() + sError.GetLength() - 1; if (*pPos != '#') { sError.Append(strPatternSubst(CONSTLIT(" ### %s ###"), pItem->Print(this))); pError->SetValue(sError); } } } } // Done pFunction->Discard(this); pArgs->Discard(this); return pResult; } // Anything else is a literal so we return it. else return pItem->Reference(); }