home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
DP Tool Club 17
/
CD_ASCQ_17_101194.iso
/
vrac
/
chkarg.zip
/
CHECKARG.PRG
< prev
next >
Wrap
Text File
|
1994-05-24
|
16KB
|
443 lines
// checkarg.prg
// Copyright (C) 1994 All rights reserved. Paul Long
// With the UDCs in checkarg.ch, these functions automatically perform type
// checking on the arguments passed in function and procedure calls based on
// the modified Hungarian notation of the parameters.
//
// The CACkPm(), CABeg(), and CAEnd() functions are meant to be called by
// the code generated by the UDCs in checkarg.ch, not called explicitly from
// your code. The accessor functions, CASkipAll(), CAChkReq(), CACheckExtra(),
// CAOptChars(), and CAAnyChars(), are meant to be called explicitly by your
// code to modify how or whether arguments are checked.
//
// Use the following commands to compile checkarg.prg and test.prg, link them
// together with Blinker, and run the test program. If you don't use Blinker,
// use whatever syntax is appropriate for your linker.
// clipper checkarg /a/w/n
// clipper test /dDEBUG
// blinker fi test,checkarg
// test
// The test program should display this error message:
// ┌────────────────────────────────────────────┐
// │C is an invalid type for parameter nHowMuch │
// │ of module DoIt, called from TEST/5. │
// │ │
// │ Quit Skip Skip Module SkipAll │
// └────────────────────────────────────────────┘
// Type and modifier characters.
#define SCALAR "bcdlmno" // Must agree with valtype() scalars.
#define ARRAY "a" // Doesn't have to agree w/ valtype(), but should.
// Could have synonyms, e.g., "am" for
// "array" and "matrix."
#define ANY "x" // Can be anything that doesn't conflict.
// Could have synonyms, e.g., "xi" for
// "x" and "indifferent."
#define OPTIONAL "u" // Can be anything that doesn't conflict.
// Could have synonyms, e.g., "ue" for
// "nil" and "elective."
// For Class(y), use methodname: otherwise, procname.
#define MODNAME procname
// Module status regarding checking its parameters.
#define CA_PROCESS 0 // Process this module, including name checking.
#define CA_SKIP 1 // Do not process this module at all.
#define CA_NAMES_CHECKED 2 // Process this module without name checking
// since already done.
// Strict checking, i.e., required vs optional? If CASTRICT is defined on the
// compilation command line, have the default be to perform strict checking.
// This means to only allow arguments with the "optional" modifier character
// in their names to be missing from the call--if the "optional" character is
// missing from the name, the argument is required. The normal default behavior
// is to ignore whether a paremeter name has the "optional" modifier character--
// all arguments are optional.
#ifdef CASTRICT
#undef CASTRICT
#define CASTRICT .t.
#else
#define CASTRICT .f.
#endif
// Check for extra arguments? If CANUMBER is defined on the compilation
// command line, have the default be to make sure that a module is not
// called with extra arguments. The normal default behavior is to not care
// whether extra arguments are present.
#ifdef CANUMBER
#undef CANUMBER
#define CANUMBER .t.
#else
#define CANUMBER .f.
#endif
// Variables that define how arguments are checked.
static lStrict := CASTRICT // Strict checking, i.e., required vs optional?
static lExtra := CANUMBER // Check for extra arguments?
static lSkipAll := .f. // Skip all checking?
static cOpt := OPTIONAL // Character(s) that indicate argument is
// optional.
static cAny := ANY // Character(s) that indicate don't care what
// type an argument is.
static bModName := ; // Block that returns name of module.
{|nAct| MODNAME(nAct)}
// Variables that are only used while processing a module's parameters.
static nModStatus // Status held here for current module.
static nModParms // Number of current module's parameters
// calculated here.
///////////////////////////////////////////////////////////////////////////
// This function is called by the UDCs in the user code to initialize
// variables in preparation for checking a module's parameters.
function CABeg(nuStatus)
nModParms := 0
nModStatus := if(nuStatus == nil, CA_PROCESS, nuStatus)
return nil
///////////////////////////////////////////////////////////////////////////
// This function is called by the UDCs in the user code to wrap up argument
// checking for a module. It updates the module's argument-checking status
// and, if supposed to, displays an error messages if too many arguments were
// passed to this module.
function CAEnd(nArgs, nStatus)
if !lSkipAll .and. nModStatus != CA_SKIP .and. lExtra .and. nArgs > nModParms
BadNo(nArgs - nModParms, 1)
endif
nStatus := if(nModStatus == CA_PROCESS, CA_NAMES_CHECKED, nModStatus)
return nil
///////////////////////////////////////////////////////////////////////////
// This function is called by the UDCs in the user code for each parameter.
// It extracts the type and modifier characters out of the parameter name,
// makes sure that the name is properly formed, and sets up the call to
// ChkType(), where the actual type checking is done.
function CACkPm(cParm, xParm)
local cNext, cType
// Supposed to be checking arguments in general and this module's parameters
// in particular?
if !lSkipAll .and. nModStatus != CA_SKIP
// Used later in CAEnd() to check whether too many arguments were passed.
++nModParms
// Get the type character from parameter name.
cType := left(cParm, 1)
do case
// Does it indicate that the argument should be an array?
case cType $ ARRAY
// Get the subtype character from the parameter name.
cNext := substr(cParm, 2, 1)
// If the "optional" modifier character is present, call ChkType()
// without a subtype and indicating that this is not a required argument.
if cNext $ cOpt
ChkType(cType, "", cParm, xParm, .f., 1)
else
// If base name immediately follows type character or the modifier
// character indicates that this is an array of any type, call
// ChkType() without a subtype and indicating that this is a required
// argument.
if isupper(cNext) .or. cNext $ cAny
ChkType(cType, "", cParm, xParm, .t., 1)
// If modifier character indicates that this is an array of a
// particular type, call ChkType() with the subtype and indicating
// that this is a required argument.
elseif cNext $ SCALAR + ARRAY
ChkType(cType, cNext, cParm, xParm, .t., 1)
else
BadName(cParm, 1)
endif
endif
// Does it indicate that the argument should be one of the other types?
case cType $ SCALAR + cAny
// If the "optional" modifier character is present, call ChkType()
// indicating that this is not a required argument.
cNext := substr(cParm, 2, 1)
if cNext $ cOpt
ChkType(cType, "", cParm, xParm, .f., 1)
else
// If base name immediately follows type character, call ChkType()
// indicating that this is a required argument.
if isupper(cNext)
ChkType(cType, "", cParm, xParm, .t., 1)
else
BadName(cParm, 1)
endif
endif
otherwise
// Doesn't have a valid type character in the parameter name.
BadName(cParm, 1)
endcase
endif
return nil
///////////////////////////////////////////////////////////////////////////
// This function checks whether the parameter named in cParm and whos
// value is in xParm is of one of the types indicated in cType. If cType
// indicates an array type, the subtype in cSubType is also checked against
// the type of the first element of the array. If an argument is missing
// (valtype() is "U") and lRequired is true, an error message is displayed.
// nAct is the activation level of the module whos arguments are being checked.
static function ChkType(cType, cSubType, cParm, xParm, lRequired, nAct)
local cValType := valtype(xParm)
// Increment activation level in order to take the calling of this function
// into consideration.
++nAct
// If not supposed to enforce the required-vs-optional check, ignore the
// setting of lRequired and make all arguments optional.
if !lStrict
lRequired := .f.
endif
// Supposed to be an array?
if cType $ ARRAY
if cValType == "A"
// Print error message if the first element is not the expected type.
if len(xParm) > 0 .and. ;
!empty(cSubType) .and. valtype(xParm[1]) != upper(cSubType)
BadType(cValType + valtype(xParm[1]), cParm, nAct)
endif
else
// This is not an array, but was supposed to be. Print error message
// if the argument is present or is required but not present.
if cValType != "U" .or. lRequired
BadType(cValType, cParm, nAct)
endif
endif
// Can be any type?
elseif cType $ cAny
// Don't perform normal type checking. Just print error message if the
// argument is required but not present.
if lRequired .and. cValType == "U"
BadType(cValType, cParm, nAct)
endif
else
// Print error message if argument is not of the expected scalar type or
// argument is required but not present.
if upper(cType) != cValType .and. (cValType != "U" .or. lRequired)
BadType(cValType, cParm, nAct)
endif
endif
return nil
///////////////////////////////////////////////////////////////////////////
// This function displays an error message indicating that an argument is
// not of the expected type.
static function BadType(cValType, cParm, nAct)
// Increment activation level in order to take the calling of this function
// into consideration.
++nAct
BadSomething(cValType + " is an invalid type for parameter " + cParm + ;
";of module", nAct)
return nil
///////////////////////////////////////////////////////////////////////////
// This function displays an error message indicating that a parameter
// name doesn't follow the naming standards.
static function BadName(cParm, nAct)
// Only report naming error the first time that a module is checked.
if nModStatus != CA_NAMES_CHECKED
// Increment activation level in order to take the calling of this function
// into consideration.
++nAct
BadSomething(cParm + " is an invalid parameter name" + ;
";in module", nAct)
endif
return nil
///////////////////////////////////////////////////////////////////////////
// This function displays an error message indicating that too many arguments
// were passed to the module being checked.
static function BadNo(nExtra, nAct)
// Increment activation level in order to take the calling of this function
// into consideration.
++nAct
BadSomething(ltrim(str(nExtra)) + " extra argument(s) passed" + ;
";to module", nAct)
return nil
///////////////////////////////////////////////////////////////////////////
// This is the common error-handler function. It calls alert() to display
// the error. Depending on which key the user presses from within alert(),
// one of the following occurs:
// the quit command is executed,
// nothing is done (this error is skipped),
// this module is marked so that no further checking will occur on it, or
// no further checking will be performed on any module.
static function BadSomething(cText, nAct)
local nChoice
// Increment activation level in order to take the calling of this function
// into consideration.
++nAct
nChoice := alert(cText + " " + eval(bModName, nAct + 1) + ;
", called from " + CodeLoc(nAct + 1) + ".", ;
{"Quit", "Skip", "Skip Module", "Skip All"})
if nChoice == 1
quit
elseif nChoice == 3
nModStatus := CA_SKIP
elseif nChoice == 4
lSkipAll := .t.
endif
return nil
///////////////////////////////////////////////////////////////////////////
// This function returns a string that indicates from where in the code the
// checked module was called. The module name comes from evaluating the
// block in bModName.
static function CodeLoc(nAct)
++nAct
return eval(bModName, nAct + 1) + "/" + ltrim(str(procline(nAct)))
///////////////////////////////////////////////////////////////////////////
// Accessor functions...
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// This function returns and optionally sets whether to skip type checking.
function CASkipAll(luSkipAll)
local lPrevSkipAll := lSkipAll
if valtype(luSkipAll) == "L"
lSkipAll := luSkipAll
endif
return lPrevSkipAll
///////////////////////////////////////////////////////////////////////////
// This function returns and optionally sets whether to distinguish between
// optional and required arguments. This has to do with the character that
// indicates whether an argument is optional.
function CACheckReq(luStrict)
local lPrevStrict := lStrict
if valtype(luStrict) == "L"
lStrict := luStrict
endif
return lPrevStrict
///////////////////////////////////////////////////////////////////////////
// This function returns and optionally sets whether to check for extra
// arguments in a call to this function.
function CACheckExtra(luExtra)
local lPrevExtra := lExtra
if valtype(luExtra) == "L"
lExtra := luExtra
endif
return lPrevExtra
///////////////////////////////////////////////////////////////////////////
// This function returns and optionally sets what character or characters to
// use to indicate that an argument is optional. The actual check is turned
// on and off with the CACheckReq() function. If more than one character is
// specified, each character can be used individually to indicate an
// optional argument--they are synonyms. For example, "ue" means that
// either "u" or "e" indicates an optional argument, not "ue" together.
function CAOptChars(cuOpt)
local cPrevOpt := cOpt
if valtype(cuOpt) == "C"
cOpt := cuOpt
endif
return cPrevOpt
///////////////////////////////////////////////////////////////////////////
// This function returns and optionally sets what character or characters to
// use to indicate "any type"--no type checking. Like the CAOptChars()
// function, if more than one character is specified, each character can be
// used individually to indicate "any type."
function CAAnyChars(cuAny)
local cPrevAny := cAny
if valtype(cuAny) == "C"
cAny := cuAny
endif
return cPrevAny
///////////////////////////////////////////////////////////////////////////
// This function returns and optionally sets a block that accepts an activation
// level and returns the corresponding module name. This is provided so that
// the Class(y) methodname() function can be used in place of procname(), as in
// {|nAct| methodname(nAct)}.
function CAModName(buModName)
local bPrevModName := bModName
if valtype(buModName) == "B"
bModName := buModName
endif
return bPrevModName