/**************************************************************************** ** ** This file is part of GAP, a system for computational discrete algebra. ** ** Copyright of GAP belongs to its developers, whose names are too numerous ** to list here. Please refer to the COPYRIGHT file for details. ** ** SPDX-License-Identifier: GPL-2.0-or-later ** ** This file contains the functions of the function interpreter package. ** ** The function interpreter package contains the executors for procedure ** calls, the evaluators for function calls, the evaluator for function ** expressions, and the handlers for the execution of function bodies. ** ** It uses the function call mechanism defined by the calls package.
*/
Int IncRecursionDepth(void)
{ int depth = ++(FuncsState()->RecursionDepth); return depth;
}
void DecRecursionDepth(void)
{
FuncsState()->RecursionDepth--; /* FIXME: According to a comment in the function RecursionDepthTrap below, RecursionDepth can become "slightly" negative. This needs some investigation. GAP_ASSERT(FuncsState()->RecursionDepth >= 0);
*/
}
Int GetRecursionDepth(void)
{ return FuncsState()->RecursionDepth;
}
/**************************************************************************** ** *F ExecProccall0args(<call>) . execute a procedure call with 0 arguments *F ExecProccall1args(<call>) . execute a procedure call with 1 arguments *F ExecProccall2args(<call>) . execute a procedure call with 2 arguments *F ExecProccall3args(<call>) . execute a procedure call with 3 arguments *F ExecProccall4args(<call>) . execute a procedure call with 4 arguments *F ExecProccall5args(<call>) . execute a procedure call with 5 arguments *F ExecProccall6args(<call>) . execute a procedure call with 6 arguments *F ExecProccallXargs(<call>) . execute a procedure call with more arguments ** ** 'ExecProccall<i>args' executes a procedure call to the function ** 'FUNC_CALL(<call>)' with the arguments 'ARGI_CALL(<call>,1)' to ** 'ARGI_CALL(<call>,<i>)'. It discards the value returned by the function ** and returns the statement execution status (as per EXEC_STAT, q.v.) ** resulting from the procedure call, which in fact is always 0.
*/
// evaluate the function
func = EVAL_EXPR( FUNC_CALL( call ) );
// evaluate the arguments if (nr <= 6 && TNUM_OBJ(func) == T_FUNCTION) { for (UInt i = 1; i <= nr; i++) {
a[i - 1] = EVAL_EXPR(ARGI_CALL(call, i));
}
} else {
UInt realNr = NARG_SIZE_CALL(SIZE_STAT(call));
args = NEW_PLIST(T_PLIST, realNr);
SET_LEN_PLIST(args, realNr); for (UInt i = 1; i <= realNr; i++) {
Obj argi = EVAL_EXPR(ARGI_CALL(call, i));
SET_ELM_PLIST(args, i, argi);
CHANGED_BAG(args);
}
}
if (opts) {
CALL_1ARGS(PushOptions, EVAL_EXPR(opts));
}
// call the function
SET_BRK_CALL_TO( call ); if (TNUM_OBJ(func) != T_FUNCTION) {
result = DoOperation2Args(CallFuncListOper, func, args);
} else { switch (nr) { case 0:
result = CALL_0ARGS(func); break; case 1:
result = CALL_1ARGS(func, a[0]); break; case 2:
result = CALL_2ARGS(func, a[0], a[1]); break; case 3:
result = CALL_3ARGS(func, a[0], a[1], a[2]); break; case 4:
result = CALL_4ARGS(func, a[0], a[1], a[2], a[3]); break; case 5:
result = CALL_5ARGS(func, a[0], a[1], a[2], a[3], a[4]); break; case 6:
result = CALL_6ARGS(func, a[0], a[1], a[2], a[3], a[4], a[5]); break; default:
result = CALL_XARGS(func, args);
}
} if (STATE(UserHasQuit) || STATE(UserHasQUIT)) { // the function must have called READ() and the user quit from a break loop // inside it; or a file containing a `QUIT` statement was read at the top // execution level (e.g. in init.g, before the primary REPL starts) after // which the function was called, and now we are returning from that
GAP_THROW();
}
if (!ignoreResult && result == 0) {
ErrorMayQuit("Function Calls: must return a value", 0, 0);
}
if (opts) {
CALL_0ARGS(PopOptions);
}
return result;
}
/**************************************************************************** ** *F ExecProccallOpts( <call> ). . execute a procedure call with options ** ** Calls with options are wrapped in an outer statement, which is ** handled here
*/
static ExecStatus ExecProccallXargs(Stat call)
{ // pass in 7 (instead of NARG_SIZE_CALL(SIZE_STAT(call))) // to allow the compiler to perform better optimizations // (as we know that the number of arguments is >= 7 here)
EvalOrExecCall(1, 7, call, 0); return STATUS_END;
}
/**************************************************************************** ** *F EvalFunccallOpts( <call> ). . evaluate a function call with options ** ** Calls with options are wrapped in an outer statement, which is ** handled here
*/
/**************************************************************************** ** *F EvalFunccall0args(<call>) . . execute a function call with 0 arguments *F EvalFunccall1args(<call>) . . execute a function call with 1 arguments *F EvalFunccall2args(<call>) . . execute a function call with 2 arguments *F EvalFunccall3args(<call>) . . execute a function call with 3 arguments *F EvalFunccall4args(<call>) . . execute a function call with 4 arguments *F EvalFunccall5args(<call>) . . execute a function call with 5 arguments *F EvalFunccall6args(<call>) . . execute a function call with 6 arguments *F EvalFunccallXargs(<call>) . . execute a function call with more arguments ** ** 'EvalFunccall<i>args' executes a function call to the function ** 'FUNC_CALL(<call>)' with the arguments 'ARGI_CALL(<call>,1)' to ** 'ARGI_CALL(<call>,<i>)'. It returns the value returned by the function.
*/
static Obj EvalFunccallXargs(Expr call)
{ // pass in 7 (instead of NARG_SIZE_CALL(SIZE_EXPR(call))) // to allow the compiler to perform better optimizations // (as we know that the number of arguments is >= 7 here) return EvalOrExecCall(0, 7, call, 0);
}
/**************************************************************************** ** *F DoExecFunc0args(<func>) . . . . interpret a function with 0 arguments *F DoExecFunc1args(<func>,<arg1>) . interpret a function with 1 arguments *F DoExecFunc2args(<func>,<arg1>...) interpret a function with 2 arguments *F DoExecFunc3args(<func>,<arg1>...) interpret a function with 3 arguments *F DoExecFunc4args(<func>,<arg1>...) interpret a function with 4 arguments *F DoExecFunc5args(<func>,<arg1>...) interpret a function with 5 arguments *F DoExecFunc6args(<func>,<arg1>...) interpret a function with 6 arguments *F DoExecFuncXargs(<func>,<args>) . interpret a function with more arguments ** ** 'DoExecFunc<i>args' interprets the function <func> that expects <i> ** arguments with the <i> actual argument <arg1>, <arg2>, and so on. If the ** function expects more than 4 arguments the actual arguments are passed in ** the plain list <args>. ** ** 'DoExecFunc<i>args' is the handler for interpreted functions expecting ** <i> arguments. ** ** 'DoExecFunc<i>args' first switches to a new values bag. Then it enters ** the arguments <arg1>, <arg2>, and so on in this new values bag. Then it ** executes the function body. After that it switches back to the old ** values bag. ** ** Note that these functions are never called directly, they are only called ** through the function call mechanism. ** ** The following functions implement the recursion depth control. **
*/
UInt RecursionTrapInterval;
void RecursionDepthTrap( void )
{ Int recursionDepth; /* in interactive work the RecursionDepth could become slightly negative * when quit-ting a higher level brk-loop to a lower level one. * Therefore we don't do anything if RecursionDepth <= 0
*/ if (GetRecursionDepth() > 0) {
recursionDepth = GetRecursionDepth();
SetRecursionDepth(0);
ErrorReturnVoid("recursion depth trap (%d)", (Int)recursionDepth, 0, "you may 'return;'");
SetRecursionDepth(recursionDepth);
}
}
#define REMEMBER_LOCKSTACK() \ int lockSP = TLS(lockStackPointer)
#define CLEAR_LOCK_STACK() \ if (lockSP != TLS(lockStackPointer)) \
PopRegionLocks(lockSP)
#endif
#ifdef HPCGAP
staticvoid LockFuncArgs(Obj func, Int narg, const Obj * args)
{ Int i; int count = 0;
LockMode * mode = alloca(narg * sizeof(int));
UChar *locks = CHARS_STRING(LCKS_FUNC(func));
Obj *objects = alloca(narg * sizeof(Obj)); for (i=0; i<narg; i++) {
Obj obj = args[i]; switch (locks[i]) { case LOCK_QUAL_READONLY: if (CheckReadAccess(obj)) break;
mode[count] = LOCK_MODE_READONLY;
objects[count] = obj;
count++; break; case LOCK_QUAL_READWRITE: if (CheckWriteAccess(obj)) break;
mode[count] = LOCK_MODE_READWRITE;
objects[count] = obj;
count++; break;
}
} if (count && LockObjects(count, objects, mode) < 0)
ErrorMayQuit("Cannot lock arguments of atomic function", 0, 0); /* Push at least one region so that we can tell that we are inside
* an atomic function. */ if (!count)
PushRegionLock((Region *) 0);
}
#endif
static ALWAYS_INLINE Obj DoExecFunc(Obj func, Int narg, const Obj *arg)
{
Bag oldLvars; // old values bag
Obj result;
CHECK_RECURSION_BEFORE
#ifdef HPCGAP
REMEMBER_LOCKSTACK(); if (LCKS_FUNC(func))
LockFuncArgs(func, narg, arg); #endif
// switch to a new values bag
oldLvars = SWITCH_TO_NEW_LVARS(func, narg, NLOC_FUNC(func));
// enter the arguments for (Int i = 0; i < narg; i++)
ASS_LVAR( i+1, arg[i] );
// execute the statement sequence
result = EXEC_CURR_FUNC(); #ifdef HPCGAP
CLEAR_LOCK_STACK(); #endif
// switch back to the old values bag
SWITCH_TO_OLD_LVARS_AND_FREE( oldLvars );
// switch to a new values bag
oldLvars = SWITCH_TO_NEW_LVARS(func, named + 1, NLOC_FUNC(func));
// enter the arguments for (i = 1; i <= named; i++) {
ASS_LVAR(i, ELM_PLIST(args,i));
} for (i = named+1; i <= len; i++) {
SET_ELM_PLIST(args, i-named, ELM_PLIST(args,i));
}
SET_LEN_PLIST(args, len-named);
ASS_LVAR(named+1, args);
// execute the statement sequence
result = EXEC_CURR_FUNC(); #ifdef HPCGAP
CLEAR_LOCK_STACK(); #endif
// switch back to the old values bag
SWITCH_TO_OLD_LVARS_AND_FREE( oldLvars );
CHECK_RECURSION_AFTER
return result;
}
/**************************************************************************** ** *F MakeFunction(<fexp>) . . . . . . . . . . . . . . . . . . make a function ** ** 'MakeFunction' makes a function from the function expression bag <fexp>.
*/
Obj MakeFunction (
Obj fexp )
{
Obj func; // function, result
ObjFunc hdlr; // handler
// make the function
func = NewFunction( NAME_FUNC( fexp ),
NARG_FUNC( fexp ), NAMS_FUNC( fexp ),
hdlr );
// install the things an interpreted function needs
SET_NLOC_FUNC( func, NLOC_FUNC( fexp ) );
SET_BODY_FUNC( func, BODY_FUNC( fexp ) );
SET_ENVI_FUNC( func, STATE(CurrLVars) );
MakeHighVars(STATE(CurrLVars)); #ifdef HPCGAP
SET_LCKS_FUNC( func, LCKS_FUNC( fexp ) ); #endif
// return the function return func;
}
/**************************************************************************** ** *F EvalFuncExpr(<expr>) . . . evaluate a function expression to a function ** ** 'EvalFuncExpr' evaluates the function expression <expr> to a function.
*/ static Obj EvalFuncExpr(Expr expr)
{ // get the function expression bag
Obj fexp = GET_VALUE_FROM_CURRENT_BODY(READ_EXPR(expr, 0));
// and make the function return MakeFunction( fexp );
}
/**************************************************************************** ** *F PrintFuncExpr(<expr>) . . . . . . . . . . . . print a function expression ** ** 'PrintFuncExpr' prints a function expression.
*/ staticvoid PrintFuncExpr(Expr expr)
{ // get the function expression bag
Obj fexp = GET_VALUE_FROM_CURRENT_BODY(READ_EXPR(expr, 0));
PrintObj( fexp );
}
/**************************************************************************** ** *F PrintFunccall(<call>) . . . . . . . . . . . . . . . print a function call ** ** 'PrintFunccall' prints a function call.
*/ staticvoid PrintFunccall1 (
Expr call )
{
UInt i; // loop variable
// print the expression that should evaluate to a function
Pr("%2>", 0, 0);
PrintExpr( FUNC_CALL(call) );
// print the opening parenthesis
Pr("%<( %>", 0, 0);
// print the expressions that evaluate to the actual arguments for ( i = 1; i <= NARG_SIZE_CALL( SIZE_EXPR(call) ); i++ ) {
PrintExpr( ARGI_CALL(call,i) ); if ( i != NARG_SIZE_CALL( SIZE_EXPR(call) ) ) {
Pr("%<, %>", 0, 0);
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.